P46 面试重灾区:JVM 常量池与 intern() 的谎言
面试题:字符串常量池是什么?new String("abc") 创建了几个对象?intern() 到底干什么?
1. 几个基础概念
- 字符串常量池(String Pool):存放字符串字面量和 intern 的字符串,JDK 7+ 移到堆中;
- 字面量
"abc":编译期放入 class 文件常量池,类加载后放入运行时常量池/字符串常量池; - new String("abc"):运行时在堆里创建一个新对象。
2. 经典问题:new String("abc") 创建几个对象
java
String s = new String("abc");答案分情况:
- "abc" 第一次出现:创建 2 个对象(字符串常量池里的
"abc"+ 堆上的 new 对象); - "abc" 已在常量池:只创建 1 个对象(堆上的 new 对象)。
注意:两个对象的值相同但引用不同,s == "abc" 为 false。
3. intern() 到底是什么(视频说的"谎言")
java
String s = new String("abc");
String t = s.intern(); // 把 s 的值放进常量池,返回常量池引用intern() 的行为:
- 若常量池中已有相同内容的字符串 → 返回常量池中那个引用(堆里的 s 保持原样);
- 若没有 → JDK7+ 会把 s 的引用登记到常量池(不是复制一份),并返回该引用。
4. 常见误区("谎言")
误区 1:"intern() 会在常量池中复制一份"
→ JDK 7 后,堆字符串 intern 时是把堆中对象的引用放进常量池,并没有复制出第二个字符串对象。
误区 2:"常量池里的字符串一定在方法区"
→ JDK 7 起字符串常量池已移到堆中(运行时常量池仍在方法区/元空间,但字符串常量池在堆)。
误区 3:"new String 一定创建两个对象"
→ 取决于字面量是否首次出现。
误区 4:"intern() 一定省内存"
→ 对大量重复且生命周期长的字符串,intern 能去重省内存;但如果字符串千变万化,常量池本身也会膨胀,还可能拖慢 GC。
5. 常见考题
java
String a = "ab";
String b = new String("ab");
String c = b.intern();
System.out.println(a == b); // false
System.out.println(a == c); // true
System.out.println(b == c); // false(b 还是堆对象)一句话总结
字符串常量池 JDK7+ 在堆中;new String("abc") 首次创建 2 个对象、否则 1 个;intern() 是"把引用登记进常量池并返回常量池引用",不是复制;理解这些才能答对 == 比较和内存语义。