Java字符串常量池

字符串常量池的位置

  • 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
    }
}

总结

只有文本字面量以及能被编译器编译时优化的等效文本字面量,会隐式进入字符串常量池。其它情况下,进入字符串常量池需要显式调用Stringintern方法,并且要满足字符串常量池本来不存在该值的字符串的条件