例题:
public static void main(String args[]) {
String s1="a";
String s2="b";
String s3="a"+"b";//编译器会进行优化
String s4=s1+s2; //new了一个StringBuilder方法, 对应着 new StringBuilder().append(s1).append(s2).toString()[toString()方法创建了一个新的变量]
String s5="ab";
String s6=s4.intern();//会进行入池操作,然后返回Stringtable中相应元素的内容
//问
System.out.println(s3==s4);
System.out.println(s3==s5);
System.out.println(s3==s6);
System.out.println(s4==s6);
String x2=new String("c")+new String("d");
String x1="cd";
x2.intern();
//问
System.out.println(x1==x2);
}
结果:
false
true
true
false
false
原理解释:
- 常量池最初存在于编译后的字节码文件中,当加载字节码文件的时候,常量池会被加载到运行时常量池中,但是此时,运行时常量池的常量,还未变成java对象,等到运行到对应的语句时(用到该对象的时候),再加载成对应的java的对象。同时,准备好一块空间(Stringtable),以此时的java 对象为键,在Stringtable中寻找是否有相同的对象。若没有,则放入stringtable中。所以Stringtable每中个常量都是唯一的
- 同时,javac在编译期间会进行优化。常量的结果在编译期间会进行拼接,因为结果已经确定了。但是变量不会在编译期间管理(因为变量的值可能会变)。会通过堆内存存储。Stringtable存储的为常量对象。变量对象,存储在堆内存中。
- intern(),将在堆上开辟的对象存到Stringtable中。如果有,则不会放入:如果没有,则将对象放入到Stringtable中。并返回Stringtable的对象。
以上只是jdk1.8的intern(),在1.8以下,如果Stringtable中没有该对象。会把此对象复制一份,放入Stringtable中,并把Stringtable的对象返回
- Stringtable数据结构相当于一个hashtable,长度固定并且无法扩容。当1.6环境及以下,Stringtable在方法区中;而1.7及以上,Stringtable在堆内存中。
原因?
永久代的垃圾回收效率会很慢(在FULL GC时,才会进行垃圾回收,而Stringtable的更新十分频繁,所以不合适在永久代中,放在堆中可实现Minor GC即可回收)