在java 8+中,单次拼接推荐用+(编译器优化为stringbuilder),concat()仅适合小字符串;循环拼接必须用stringbuilder;split()默认丢弃末尾空串,需传-1保留;replace()用于字面量替换,replaceall()和replacefirst()走正则;substring()在jdk7u6+已修复内存泄漏,但大字符串截取仍需谨慎。

String.concat() 和 + 拼接到底谁更快?
在 Java 8+ 中,用 + 拼接字符串(非循环场景)会被编译器自动优化为 StringBuilder.append(),性能和 concat() 差不多;但 concat() 只支持拼一个字符串,且内部会判断长度做数组复制,小字符串快,大字符串反而多一次拷贝。
常见错误现象:循环里写 str += "x",每次创建新对象,O(n²) 时间复杂度。
- 单次拼接两个字符串,
+、concat()、StringBuilder.append()都可,推荐+(语义清晰) - 循环拼接或拼多个,必须用
StringBuilder,别碰+或concat() - JDK 9+ 字符串底层改用
byte[]存储,concat()对 Latin-1 编码字符串有优化,但业务代码无需感知
split() 的坑:为什么空字符串不见了?
split() 默认丢弃末尾空字符串——这是它最常被误用的点。比如 "a,b,c,".split(",") 返回长度为 3 的数组,最后一个空串没了。
使用场景:解析 CSV 片段、路径分段、简单协议字段拆分。
立即学习“Java免费学习笔记(深入)”;
- 要保留所有分割结果(包括开头、结尾空串),传负数限制参数:
str.split(",", -1) - 正则特殊字符如
.、*、+必须转义,"a.b.c".split("\.")才对 - 想按固定字符串切(非正则),用
String.split(Pattern.quote(delimiter))更安全 - 性能上,
split()每次都编译正则,高频调用建议预编译Pattern
replace()、replaceAll()、replaceFirst() 怎么选?
三个方法名字像,行为完全不同:replace() 是字面量替换(不走正则),后两者是正则替换。用错就出诡异结果。
常见错误现象:str.replace(".", "x") 把所有字符都替成 x(因为 . 在正则里是通配符),其实该用 replace() 或转义。
- 只换固定子串(比如把
"old"换成"new"),无条件用replace() - 需要正则能力(如“所有数字”“开头空格”),才用
replaceAll();注意$、在 replacement 字符串里有特殊含义,要双写:"\$1" -
replaceFirst()就是replaceAll()的单次版,别为了“只换一次”硬套正则 - 如果只是删掉某子串,
replace("xxx", "")比replaceAll("xxx", "")更快更安全
substring() 的索引越界和内存泄漏风险
substring() 在 JDK 7u6 之前会共享原字符串的 char[],导致小字符串持有了大字符串的全部内容,GC 不掉——这问题虽已修复,但新坑还在。
常见错误现象:从超长日志里 substring(0, 100) 提取摘要,结果整个日志对象一直驻留堆中(JDK 7u6+ 已改为复制新数组,但开发者容易忽略这点)。
- 明确知道源字符串很大、只取一小段时,主动触发复制:
new String(str.substring(a, b)) - 检查索引是否合法:
beginIndex必须 ≥ 0,endIndex≤str.length(),否则抛StringIndexOutOfBoundsException - 空字符串
""调用substring(0, 0)合法,返回自身;但substring(1, 0)会直接报错 - Java 12+ 加了
describeConstable()等方法,但日常开发几乎用不到,别被文档带偏
String 不可变,所以每个操作都可能新建对象;真正要注意的不是“哪个方法快”,而是“哪次调用让 GC 压力突然变大”。尤其是日志处理、JSON 解析、模板渲染这些场景,substring 和 split 最容易悄悄吃掉内存。










