Java String不可变性不直接损耗性能,但高频修改会引发内存分配增多、GC压力上升等问题;应避免+拼接,改用预设容量的StringBuilder;substring自JDK7u6起改为复制,正则替换宜预编译Pattern;优先使用字面量而非new String;hashCode缓存依赖不可变性,提升HashMap性能。

Java 中 String 的不可变性本身不直接“损耗性能”,但它让每次修改都变成对象创建,从而在高频操作场景下引发内存分配增多、GC 压力上升、CPU 缓存局部性下降等连锁反应。真正关键的不是“不可变”,而是你是否意识到并主动规避了它的副作用。
高频拼接:+ 和 += 是最隐蔽的性能陷阱
表面看是语法糖,底层却暗藏两层开销:每次 s += "x" 都会新建一个 StringBuilder(JDK 7+),再调用其 append + toString,最终生成新 String。10 万次循环拼接,可能产生 10 万个 StringBuilder + 10 万个 String 实例。
- 日志组装、模板渲染、SQL 构建等场景,应显式用 StringBuilder 替代链式 +
- 单线程环境一律选 StringBuilder;仅当多个线程共写同一实例时才考虑 StringBuffer
- 初始化容量可预估:new StringBuilder(256) 比默认 16 容量减少数组扩容次数
substring 等“视图类”操作已不再省资源
JDK 7u6 之前,substring 复用原字符串的 char[],虽省内存但易导致大字符串长期驻留(如从 1MB 日志中取 10 字符,整块数组无法回收);JDK 7u6 起改为独立拷贝,安全但开销更透明——每次调用都触发一次数组复制。
- 若需大量切片且原始字符串极大,考虑用 CharBuffer.wrap() 或直接操作索引,避免无谓复制
- 正则替换(如 replaceAll)本质也是多次 substring + concat,长文本+复杂模式时建议预编译 Pattern 并复用 Matcher
常量池与字面量:免费的内存优化
String 字面量("abc")自动入常量池,复用已有实例;而 new String("abc") 强制堆上新建对象,哪怕内容相同也浪费空间和 GC 负担。
立即学习“Java免费学习笔记(深入)”;
- 配置读取、枚举值、固定响应体等,优先使用字面量赋值
- 必要时用 string.intern() 将运行期字符串纳入常量池,但注意元空间溢出风险(尤其动态生成大量唯一字符串时)
- 避免在循环内对相同字面量反复调用 intern(),它带同步开销
hashCode 缓存:被低估的隐性收益
String 的 hashCode 在首次调用后缓存到私有字段 hash 中,后续直接返回。这使它作为 HashMap 键时,插入/查找无需重复计算哈希——相比可变对象(如未覆写 hashCode 的自定义类),性能提升显著。
- 该缓存依赖不可变性:若 String 可变,每次 get/put 都要重算 hash,HashMap 性能会断崖下跌
- 这也是为什么推荐用 String 而非 StringBuilder 作 Map key:后者无缓存,且本身可变,语义也不合理



















