java中string的不可变性旨在支持内存优化:通过字符串常量池复用对象、jdk9起采用byte[]+coder实现紧凑存储(ascii字符内存减半)、哈希值缓存提升hashmap性能,并要求拼接场景使用stringbuilder避免gc压力。

Java中String的不可变性不是为了“限制操作”,而是为内存优化铺路——它让JVM能安全地复用、缓存、压缩字符串数据,而不是盲目分配新空间。
字符串常量池:复用而非新建
当你写 String s = "hello",JVM不会每次都分配新对象。它先查常量池:如果已有内容为"hello"的字符串,就直接返回其引用;没有才创建并登记。这避免了重复字符串在堆中堆积。
- 双引号字面量(如
"abc")自动进池,共享引用 -
new String("abc")总是在堆中新建对象,即使池里已有相同内容 -
"abc".intern()可主动将堆中字符串纳入常量池,或获取池中已有引用
紧凑字符串:JDK9起的内存减半策略
JDK9把String内部的 char[] value 换成了 byte[] value + 编码标识(coder 字段)。对纯ASCII字符(如英文、数字),每个字符只占1字节,而非原先的2字节,内存占用直降约50%。
- 该优化完全透明,开发者无需改代码
- 若字符串含中文等UTF-16字符,仍用2字节/字符,自动回退到传统模式
- 不可变性保障了这种动态编码切换的安全性——内容不变,结构可适配
哈希值缓存:一次计算,永久复用
String重写了hashCode(),内部用 private int hash 缓存结果。因为内容永不改变,哈希值算一次就够了。
- 作为HashMap的key时,避免每次查找都重新计算hash
- 尤其在大量字符串Key的Map场景(如配置项缓存、路由表),性能提升明显
- 这个缓存依赖不可变性——如果String可变,hash就可能失效,引发数据错乱
GC压力与拼接陷阱:不可变性的代价与应对
不可变性虽优化了复用和安全,但“修改即新建”会带来临时对象爆炸。比如循环拼接10万次,String会生成10万个中间对象,加重GC负担。
- 简单拼接(≤3个字符串)用
+没问题,编译器会自动优化为StringBuilder - 循环内拼接、动态构建长字符串,必须用
StringBuilder(单线程)或StringBuffer(多线程) - 注意
substring()在JDK7以前会共享原数组,导致内存泄漏;JDK7+已改为复制子串,更安全但略增开销
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











