Java String不可变性是JVM内存与性能优化的基石:支撑字符串常量池安全复用、JDK9紧凑字符串动态编码切换、hashCode缓存免重复计算,并倒逼高频拼接使用StringBuilder。

Java String 的不可变性不是为“禁止修改”而存在,而是 JVM 在底层做内存与性能优化的基石——它让虚拟机敢复用、敢缓存、敢压缩,而不是每次都要分配新空间。
字符串常量池:靠不可变性实现安全共享
JVM 在堆中维护字符串常量池(JDK 7+ 后移入堆),当遇到字面量如 "hello" 时,会先查池中是否已有相同内容的对象。若有,直接复用其引用;若无,才新建并登记。
- 因为 String 不可变,a = "abc" 和 b = "abc" 共享同一对象是安全的——没人能偷偷把 a 改成 "xyz",从而影响 b
- new String("abc") 绕过池,在堆中新建对象,但它的不可变性仍保障自身状态稳定,不干扰池中其他对象
- "abc".intern() 能安全返回池中已有引用,前提是池里那个对象的内容永远不变
JDK9+ 紧凑字符串:不可变性支撑动态编码切换
JDK9 起,String 内部从 char[] 改为 byte[] + coder(Latin-1 或 UTF-16 标识)。对纯 ASCII 字符(如英文、数字、常见符号),每个字符只占 1 字节,内存直降约 50%。
- 含中文等非 Latin-1 字符时,自动回退到 2 字节/字符模式,完全透明
- 不可变性确保 value(byte[])和 coder 两个 final 字段组合后语义恒定——结构可适配,内容不漂移
- 开发者无需改代码,length()、charAt() 行为完全一致
哈希值缓存:一次计算,永久有效
String 内部有 private int hash 字段。首次调用 hashCode() 时计算并缓存,之后直接返回。
- 作为 HashMap 的 key 时,避免每次查找都重新遍历字符数组算 hash
- 在配置项缓存、路由表等大量字符串 key 场景下,性能提升明显
- 若 String 可变,hash 缓存会失效,甚至引发 Map 数据错乱
拼接场景的权衡:不可变性带来 GC 压力,也催生 StringBuilder
“修改即新建”的逻辑虽带来复用与缓存红利,但也导致临时对象爆炸。
- 循环内写 str += "x",每轮都生成新 String,10 万次 → 10 万个中间对象 → Minor GC 加重
- 编译器对字面量拼接(如 "a" + "b")自动优化为常量并入池;一旦掺入变量,就退化为运行期堆上创建
- 高频拼接必须用 StringBuilder(单线程)或 StringBuffer(多线程),它们内部用可变数组,构造完再调 toString() 得到安全不可变对象
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











