String不可变性导致每次拼接都新建对象,编译期字面量拼接可优化,但运行时变量拼接必经StringBuilder生成临时String;循环中用+拼接时间复杂度升至O(n²),应改用预设容量的StringBuilder。

Java 中 String 的不可变性意味着每次对字符串内容做修改(包括拼接),都会创建一个新的 String 对象,而原对象保持不变。这种设计在字符串拼接时会直接导致临时对象的频繁生成,尤其在循环或多次拼接场景下,容易引发性能和内存问题。
为什么拼接会产生临时对象
因为 String 内部用 final char[](Java 8 及以前)或 final byte[](Java 9+)存储字符,且没有提供任何修改自身内容的方法。所有看似“修改”的操作(如 +、substring()、replace())都必须返回新对象。
-
"a" + "b"→ 编译期常量优化为"ab"(不产生临时对象) -
str1 + str2(运行时变量)→ 触发StringBuilder.append(),最后调用toString()创建新String - 连续拼接如
s = s + "x"在循环中 → 每次都新建一个String,前一次结果立即变为垃圾
编译器如何“帮忙”,又在哪失效
Java 编译器会对**编译期已知的字符串字面量拼接**做优化(常量折叠),例如:
"Hello" + " " + "World" → 直接编译为 "Hello World",不生成中间对象
但一旦涉及变量(哪怕只是 final String s = "a"; s + "b"),就无法在编译期确定全部内容,JVM 必须在运行时通过 StringBuilder 完成拼接,最终调用 new String(value) 返回新实例 —— 这个 new String(...) 就是那个临时对象。
典型高开销场景:循环内用 + 拼接
以下代码每轮迭代都产生至少一个临时 String:
String result = "";<br>for (String s : list) {<br> result = result + s; // 每次都 new String()!<br>}- 第1次:"" + s₁ → 新字符串 s₁
- 第2次:s₁ + s₂ → 新字符串 s₁s₂(s₁ 成为临时对象,等待回收)
- 第n次:产生长度递增的新字符串,同时丢弃前 n−1 个中间结果
时间复杂度从 O(n) 退化为 O(n²),堆内存压力显著上升。
怎么避免?用 StringBuilder 或 StringBuffer
它们内部使用可变的 char[] 数组,append() 方法直接修改数组内容,仅在必要时扩容,最后调用 toString() 才生成唯一一个目标 String:
StringBuilder sb = new StringBuilder();<br>for (String s : list) {<br> sb.append(s); // 无新 String 对象<br>}<br>String result = sb.toString(); // 只这里 new 一次- 单线程场景优先用
StringBuilder(非同步,更快) - 多线程共享拼接逻辑时才考虑
StringBuffer - 初始化时指定合理容量(如
new StringBuilder(1024)),避免数组多次扩容
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











