字符串常量池(String Pool)
本质:JVM 里一块专门存字符串字面量 的缓存区域,目的:复用字符串对象,减少重复创建,节省内存 。
JDK1.6:常量池在永久代;JDK1.7+ 移到堆内存中。
1.什么会进常量池
✅ 1.直接写双引号字面量,编译期就放入常量池
java
String s1 = "hello";
String s2 = "hello";
编译时发现 "hello",先去常量池找:
- 如果没有:在常量池创建
hello对象 - 如果已经存在:直接返回常量池里对象的引用,不再新建
所以 s1 和 s2 指向同一个对象,s1==s2→ true
✅ 2.编译期可以算出来的常量拼接(常量折叠)
java
String a = "he"+"llo";
// 编译器直接优化成 String a = "hello"; 直接进常量池
两个都是字面量,编译就能算出结果,结果放入常量池。
❌ 不会进常量池(在堆上新建普通对象)
① new String() 一定在堆创建对象
java
String s = new String("hello");
流程:
"hello"字面量,先放到常量池;new String()在堆中再创建一份新的字符串对象;- s 指向堆的对象,不是直接指向常量池对象。
java
String s1 = "hello";
String s2 = new String("hello");
System.out.println(s1 == s2); // false,地址不一样
② 变量拼接,运行时才计算,不会进常量池
java
String x = "he";
String y = "llo";
String z = x + y; // x、y是变量,运行时拼接,在堆产生新对象,不进常量池
z == "hello" → false
intern() 方法:手动把字符串放进常量池
str.intern()
如果常量池已经有这个字符串:返回常量池引用;
如果常量池没有:把该字符串复制到常量池,返回常量池引用。
java
String s = new String("abc").intern();
快速总结
- 只有编译期就能确定的字符串字面量,才会进入字符串常量池;
new String(xxx)、运行时变量相加,生成的字符串存在堆,不在常量池;- 常量池作用:避免大量重复字符串,节约内存;
- ⚠️业务代码千万不要用 == 判断字符串相等 ,因为只有常量池场景
==才true,数据库、接口返回、运行时拼接的字符串不走常量池,==直接出错。
极简记忆
"abc"写死字面量 → 常量池new String("abc")→ 堆a+b变量拼接 → 堆.intern()→ 手动入常量池
小面试题
java
String s1 = "hello";
String s2 = new String("hello");
String s3 = s2.intern();
System.out.println(s1 == s2); // false
System.out.println(s1 == s3); // true