string.intern()检查常量池是否存在值相同的字符串,有则返回池中引用,无则将该字符串引用加入池并返回;jdk 7+后池位于堆中,仅登记引用而非复制对象。

String.intern() 的作用不是“强行把堆里字符串塞进字符串常量池”,而是检查常量池中是否存在值相同的字符串,如果有就返回池中引用;如果没有,就把该字符串对象的引用(注意:不是复制一份)加入池中,并返回这个引用。
关键点在于:JDK 7+ 后,字符串常量池被移到堆内存中(不再是永久代),所以 intern() 实际上是往堆里的常量池区域添加引用,而不是“跨内存区域搬运对象”。
✅ 正确理解 intern 的行为
-
intern()永远返回常量池中已存在或刚加入的那个字符串引用; - 如果堆中已有相同内容的字符串(比如通过
new String("abc")创建),调用其intern()会将它的引用登记到常量池(前提是池里还没有"abc"); - 之后再通过字面量
"abc"获取的字符串,就和这个intern()返回的引用完全相等(==)。
String s1 = new String("hello"); // 堆中新建对象
String s2 = s1.intern(); // 池里没有"hello",把s1的引用存入池,返回它
String s3 = "hello"; // 字面量,直接从池取 → 和s2是同一个对象
System.out.println(s2 == s3); // true
System.out.println(s1 == s2); // false(s1是堆中新对象,s2是池引用)
✅ 如何“让堆中字符串入池”?三步操作
- 确保字符串内容在常量池中尚不存在(否则
intern()只是返回已有引用); - 对堆中字符串对象调用
.intern(); - 用返回值替代原引用(因为原引用仍指向堆对象,而
intern()返回的是池中引用)。
String heapStr = new String("world"); // 堆中对象
heapStr = heapStr.intern(); // 入池,并更新引用指向池中
String lit = "world";
System.out.println(heapStr == lit); // true
⚠️ 注意:
intern()是同步方法,高并发频繁调用可能有性能开销;且常量池底层是哈希表,过度使用可能导致内存占用上升或哈希冲突。
✅ 常见误区澄清
❌ “
intern()把字符串对象本身复制到常量池”
→ 错。它只存引用(JDK7+ 后池在堆里,所以只是把堆中已有对象的引用登记进去)。❌ “所有调用
intern()的字符串都会新增进池”
→ 错。如果池里已存在相同equals()的字符串(如已有字面量),就直接返回那个引用,不重复添加。❌ “
intern()能让任意字符串变成编译期常量”
→ 错。它不影响编译期行为,也不让字符串获得final语义或参与编译期优化(如字符串拼接折叠)。
✅ 实用建议
- 适合场景:大量重复字符串(如解析 CSV 的列名、HTTP 头字段名),用
intern()减少内存占用; - 不适合场景:随机唯一字符串(如 UUID、时间戳拼接)、短生命周期字符串(入池反而增加 GC 压力);
- 可配合
String.valueOf()或new String(bytes).intern()对动态构造的字符串做归一化; - 若需批量处理,可先用
Set<string></string>去重,再对每个调用intern(),但通常直接intern()更轻量。
// 示例:统一 HTTP 方法名
String method = parseFromRequest(); // 可能是 new String("GET")
method = method.intern(); // 归一为常量池中标准引用
if (method == "GET") { ... } // 可用 == 安全比较
不复杂但容易忽略细节。











