Java String不可变性本身非性能瓶颈,但频繁修改会因不断创建新对象而加剧内存分配与GC压力;应避免循环拼接、substring、replace等操作,改用预估容量的StringBuilder,读多写少场景仍应优先使用String。

Java String 的不可变性本身不是性能问题,但它让“频繁修改”这件事天然代价高昂——每次操作都等于放弃旧对象、申请新内存、触发GC。真正被抛弃的,是直接用 String 做动态拼接、循环替换、反复截取这类场景下的写法,而不是 String 这个类本身。
哪些操作会悄悄创建大量临时对象
所有看似“修改”的方法,底层都返回新实例:
- s += "x":每次执行都新建一个 String,JDK 还会在背后额外建一个 StringBuilder 临时对象再转成 String
- s.substring(10, 20):JDK 7u6 后不再共享原 char 数组,而是拷贝子串,长度越长开销越大
- s.replace("a", "b") 或 s.split(","):输入字符串越长、匹配越复杂,生成的新 String 就越多,尤其在循环里调用时风险集中
为什么 StringBuilder 能扛住高频拼接
它不造新对象,只复用内部 char 数组(或 byte 数组):
- append() 是就地追加,扩容策略清晰(默认 newCapacity = old × 2 + 2),避免频繁复制
- 构造时若预估最终长度(如 new StringBuilder(1024)),可彻底避开中途扩容
- 链式调用(sb.append(a).append(b).append(c))全程只操作一个对象,无中间 String 产生
什么情况下还必须用 String
不可变性带来的优势,在读多写少、共享传递、安全校验等场景中反而不可替代:
立即学习“Java免费学习笔记(深入)”;
- 作为 HashMap 键:hashCode 缓存一次、永久有效,不怕后续误改
- 传参给敏感 API(如 URL、Class.forName、SQL 参数):内容不会被下游方法意外篡改
- 字符串字面量自动进常量池:String a = "hello"; String b = "hello"; 二者指向同一对象,省空间也省比较开销
实战中容易踩的“假优化”坑
不是所有地方换 StringBuilder 都对:
- 单次拼接不用动:String a = "name=" + name + "&id=" + id; 编译器已优化为 StringBuilder,手动改反而啰嗦
- 短字符串小批量操作影响极小:几十次以内,GC 压力几乎可忽略,优先保障代码可读性
- 跨线程共享时别乱用 StringBuilder:它不保证线程安全;若需并发拼接,应选 StringBuffer,或由上层加锁协调



















