今天看《深入理解Java虛擬機(jī)》甲喝,看到第二章關(guān)于String.intern()的測(cè)試的時(shí)候唱星,有這樣一個(gè)例子
public static void main(String[] args) {
String str1 = new StringBuilder("計(jì)算機(jī)").append( "軟件" ).toString();
System.out.println(str1.intern()==str1);
String str2=new StringBuilder("ja").append( "va" ).toString();
System.out.println(str2.intern()==str2);
}
在Jdk1.6的時(shí)候均返回false,這個(gè)容易理解跌造,因?yàn)閕ntern()方法會(huì)把首次遇到的字符串實(shí)例復(fù)制到永久代中杆怕,而new StringBuilder創(chuàng)建出來(lái)的對(duì)象是在堆上的族购,所以str1.intern()拿出來(lái)的對(duì)象跟新創(chuàng)建的對(duì)象不相等。
而在JDK1.7上陵珍,第一個(gè)true寝杖,第二個(gè)flase.
第一個(gè)返回true的原因是 JDK1.7等虛擬機(jī)的intern()實(shí)現(xiàn)不會(huì)復(fù)制實(shí)例,而是在常量池中記錄首次出現(xiàn)的實(shí)例引用互纯,因此第一個(gè)返回的是true瑟幕,這里也沒(méi)有問(wèn)題。
至于第二個(gè)返回false的例子留潦,書(shū)上的解析是
java這個(gè)字符串在執(zhí)行StringBuilder.toString()之前已經(jīng)出現(xiàn)過(guò)只盹,字符串常量池中早已有它的引用。所以返回false
最初的猜想是'java'看起來(lái)像個(gè)保留字兔院,是不是在JVM啟動(dòng)的時(shí)候已經(jīng)寫(xiě)到常量池里了殖卑,類似的還有'main'、'int'坊萝、'float'孵稽。
于是測(cè)試了下以下例子
String str1 = new StringBuilder("jc").append( "vc" ).toString();//true
System.out.println(str1.intern()==str1);
String str2=new StringBuilder("mai").append( "n" ).toString(); //false
System.out.println(str2.intern()==str2);
String str3=new StringBuilder("in").append( "t" ).toString(); //flase
System.out.println(str3.intern()==str3);
String str4=new StringBuilder("flo").append( "at" ).toString(); //flase
System.out.println(str4.intern()==str4);
進(jìn)一步驗(yàn)證了自己的猜想。
那么繼續(xù)往下扒十偶,然后被我在知乎上找到了R大的回答https://www.zhihu.com/question/51102308/answer/124441115
答案中的意思大致是
JVM在初始化的過(guò)程中主動(dòng)觸發(fā)java.lang.System的加載和初始化菩鲜,過(guò)程中會(huì)調(diào)用到initializeSystemClass()方法,進(jìn)一步調(diào)用sun.misc.Version.init()扯键。
` private static void initializeSystemClass() {
...
sun.misc.Version.init();
...
}
但是源碼里面并沒(méi)有這個(gè)類睦袖,原因在于這個(gè)類是根據(jù)模板類動(dòng)態(tài)編譯生成的,模板如下:
http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/tip/src/share/classes/sun/misc/Version.java.template
也就是說(shuō)當(dāng)編譯jvm的時(shí)候荣刑,會(huì)根據(jù)Version.java.template這個(gè)模版動(dòng)態(tài)生成version的java類馅笙,填入常量后這個(gè)類大概長(zhǎng)這樣
在調(diào)用sun.misc.Version.init()時(shí)會(huì)進(jìn)一步初始化launcher_name,這時(shí)會(huì)把'java'這個(gè)字符串常量的引用存到StringTable里。這就可以解析為什么
`String str2=new StringBuilder("ja").append( "va" ).toString();
System.out.println(str2.intern()==str2);`
返回false了厉亏,因?yàn)?java'這個(gè)字符串實(shí)例的引用 在JVM初始化的時(shí)候就保存到常量池了董习,跟new出來(lái)的StringBuilder對(duì)象不是同一個(gè)。
至于其他的int爱只、float的情況請(qǐng)看后面的參考資料皿淋。
注:StringTable并不是常量池,里面保存的是引用恬试,本例中存的是'java'這個(gè)字符串實(shí)例的引用
參考資料:
1.https://www.zhihu.com/question/57124207/answer/151835713
2.https://www.zhihu.com/question/51102308/answer/124441115
3.http://blog.csdn.net/raintungli/article/details/38595573