Java字符串常量池是明确规则的内存复用机制,字面量自动入池、new绕过池但可能预存字面量、intern手动驻留、编译期拼接进池而运行期拼接不进池。

Java 中的字符串常量池不是黑箱,而是一套有明确规则的内存复用机制。它直接影响对象创建数量、内存占用和 == 比较结果,理解它能帮你避开常见陷阱。
字面量赋值自动入池
只要用双引号直接写字符串,比如 String a = "hello",JVM 就会立刻检查常量池:如果已有内容相同的字符串,就返回池中那个对象的引用;如果没有,就新建一个放入池中,并返回该引用。这个过程在类加载阶段就完成,无需手动干预。
所以以下代码中,a 和 b 实际指向堆中同一个对象:
String a = "hello";
String b = "hello";
System.out.println(a == b); // true
立即学习“Java免费学习笔记(深入)”;
new 创建一定绕过常量池(但可能触发池内预存)
使用 new String("hello") 时,JVM 一定会在堆中新建一个 String 对象,不管常量池里有没有 "hello"。
不过注意:这个操作本身会先确保字面量 "hello" 已存在于常量池(若不存在则先放入),再在堆中新建对象。因此:
- 如果这是第一次出现 "hello",则总共创建 2 个对象:1 个进池 + 1 个在堆
- 如果池中已有 "hello",则只在堆中创建 1 个新对象
此时 new 出的对象与池中对象地址不同:a == c 为 false。
intern() 是手动“拉回”池中的桥梁
String.intern() 的作用是:查池 → 有则返回池中引用,无则把当前字符串加入池并返回其引用。
例如:
String c = new String("hello").intern();
String d = "hello";
System.out.println(c == d); // true
这里 c 经过 intern 后,就和 d 共享同一个池中对象了。这个方法适合对动态生成但重复率高的字符串做显式驻留,比如解析配置项、统一状态码等场景。
拼接结果是否进池?看编译期能否确定
编译器会对纯字面量拼接做优化。例如:
String e = "he" + "llo"; // 编译期即确定为 "hello",等价于字面量,进池
String f = "he";
String g = f + "llo"; // 运行期拼接,结果在堆中,不进池(除非显式调用 intern)
所以 e == d 是 true,但 g == d 是 false —— 关键在于拼接是否发生在编译期。


















