字符串常量池的位置
- JDK 6及之前,字符串常量池位于方法区
- JDK 7及之后,字符串常量池位于堆空间
字符串常量池常量的来源
- class文件的静态常量池,主要包含文本字面量和被
final
修饰的字符串常量 - 运行时通过
String
类的intern
方法动态添加
字符串常量池数据结构
字符串常量池底层为一个定长的Hashtable
,管理的是字符串的引用。字符串的对象本身一定在堆空间。
intern方法
调用intern
方法,会判断常量池引用指向的对象里面有没有与当前字符串值相等的对象。如果有,则返回常量池相应的引用。如果没有,则将当前字符串的引用放入常量池,并返回。
字符串常量池的一些例子
字面量在类加载时进入常量池
String s1 = "Hello";
String s2 = "Hello";
// s1 == s2 true
编译器优化,常量折叠,在类加载时进入常量池
String s1 = "Hello";
String s2 = "He" + "llo";
// s1 == s2 true
实际编译后相当于
String s1 = "Hello";
String s2 = "Hello";
// s1 == s2 true
运行时确定的字符串与常量池无关
String s1 = "Hello";
String s2 = "Hel" + new String("lo");
String s3 = new String("Hello");
String s4Pre = "Hel";
String s4Post = "lo";
String s4 = s4Pre + s4Post;
// s1 == s2 false
// s1 == s3 false
// s1 == s4 false
使用常量池已经存在的字符串
String s1 = "Hello";
String s2 = new String("Hello").intern();
// s1 == s2 true
往常量池中加入本来不存在的字符串
String s1 = new String("He") + new String("llo");
String s2 = s1.intern();
// s1 == s2 true
编译器优化,final变量
public class Test {
public static final String s1Pre = "He";
public static final String s1Post = "llo";
public static void main(String[] args) {
String s1 = s1Pre + s1Post;
String s2 = "Hello";
// s1 == s2 true
}
}
总结
只有文本字面量以及能被编译器编译时优化的等效文本字面量,会隐式进入字符串常量池。其它情况下,进入字符串常量池需要显式调用String
的intern
方法,并且要满足字符串常量池本来不存在该值的字符串的条件
PREVIOUSJava中String类的+运算符重载
NEXTJava方法签名