
本文详解如何正确使用for循环遍历字符串,将每个英文句号(.)和感叹号(!)安全替换为"!!",避免因字符串动态修改导致的索引错位问题,并提供高效、可读性强的实现方案。
原始代码的问题根源在于:在for循环中直接修改正在遍历的字符串 text。由于Java中String是不可变对象,每次执行 text = front + "!!" + back 都会创建一个新字符串,导致后续 text.length() 和字符索引发生偏移——例如,原字符串中第5位是.,替换后字符串变长,原第6位字符被“推”到第8位,而循环变量 i 仍按旧长度递增,造成跳过字符、逻辑错乱,甚至 StringIndexOutOfBoundsException 异常(尤其当 i+1 超出新字符串长度时),最终可能输出空字符串或结果不全。
正确的做法是分离“读取”与“构建”:遍历原字符串只负责读取每个字符,而将转换后的结果累积到一个可变容器中。推荐使用 StringBuilder——它专为高效字符串拼接设计,时间复杂度为 O(n),远优于反复字符串拼接的 O(n²)。
以下是符合题目要求(必须使用for循环)的优化实现:
public static String replace(String text) {
StringBuilder sb = new StringBuilder(text.length()); // 预分配容量,提升性能
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
if (c == '.' || c == '!') {
sb.append("!!");
} else {
sb.append(c);
}
}
return sb.toString();
}✅ 关键改进说明:
立即学习“Java免费学习笔记(深入)”;
- 使用 charAt(i) 直接获取单个字符,比 substring(i, i+1) 更高效且语义清晰;
- StringBuilder 独立于原字符串,遍历索引 i 始终对应原始字符串位置,完全规避了修改源数据引发的逻辑错误;
- 预设 StringBuilder 初始容量(text.length()),减少内部数组扩容次数,进一步优化性能。
⚠️ 注意事项:
- 切勿在循环中用 text = text.replaceFirst(...) 或 text = text.substring(...) + ... 修改原字符串——这本质上仍是“边读边改”,同样危险;
- 若需处理更多标点(如 ?, ,, ;),只需扩展if条件:if (c == '.' || c == '!' || c == '?');
- 此方案严格满足“必须使用for循环”的约束,同时兼顾健壮性与可维护性。
作为补充,若项目允许使用正则表达式(非循环方式),一行代码即可完成:
return text.replaceAll("[.!]", "!!");该写法简洁、声明式,但不符合本题强制for循环的要求,仅作对比参考。
综上,核心原则是:对不可变字符串做批量替换时,永远采用“遍历输入 → 构建输出”的两阶段模式,而非就地修改。这是Java字符串处理中最基础也最关键的实践规范。










