關于String.intern()的一個疑惑

今天看《深入理解Java虛擬機》,看到第二章關于String.intern()的測試的時候,有這樣一個例子

public static void main(String[] args) {    
    String str1 = new StringBuilder("計算機").append( "軟件" ).toString();
    System.out.println(str1.intern()==str1);
    String str2=new StringBuilder("ja").append( "va" ).toString();  
    System.out.println(str2.intern()==str2);    
}

在Jdk1.6的時候均返回false,這個容易理解,因為intern()方法會把首次遇到的字符串實例復制到永久代中,而new StringBuilder創建出來的對象是在堆上的,所以str1.intern()拿出來的對象跟新創建的對象不相等。

而在JDK1.7上,第一個true,第二個flase.
第一個返回true的原因是 JDK1.7等虛擬機的intern()實現不會復制實例,而是在常量池中記錄首次出現的實例引用,因此第一個返回的是true,這里也沒有問題。

至于第二個返回false的例子,書上的解析是

java這個字符串在執行StringBuilder.toString()之前已經出現過,字符串常量池中早已有它的引用。所以返回false

最初的猜想是'java'看起來像個保留字,是不是在JVM啟動的時候已經寫到常量池里了,類似的還有'main'、'int'、'float'。
于是測試了下以下例子

    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);

進一步驗證了自己的猜想。


那么繼續往下扒,然后被我在知乎上找到了R大的回答https://www.zhihu.com/question/51102308/answer/124441115

答案中的意思大致是
JVM在初始化的過程中主動觸發java.lang.System的加載和初始化,過程中會調用到initializeSystemClass()方法,進一步調用sun.misc.Version.init()。

` private static void initializeSystemClass() {
    ...
    sun.misc.Version.init();
    ...
}

但是源碼里面并沒有這個類,原因在于這個類是根據模板類動態編譯生成的,模板如下:

http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/tip/src/share/classes/sun/misc/Version.java.template

也就是說當編譯jvm的時候,會根據Version.java.template這個模版動態生成version的java類,填入常量后這個類大概長這樣

圖來源于上面的知乎答案

在調用sun.misc.Version.init()時會進一步初始化launcher_name,這時會把'java'這個字符串常量的引用存到StringTable里。這就可以解析為什么

`String str2=new StringBuilder("ja").append( "va" ).toString(); 
System.out.println(str2.intern()==str2);`

返回false了,因為'java'這個字符串實例的引用 在JVM初始化的時候就保存到常量池了,跟new出來的StringBuilder對象不是同一個。

至于其他的int、float的情況請看后面的參考資料。

注:StringTable并不是常量池,里面保存的是引用,本例中存的是'java'這個字符串實例的引用


參考資料:
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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 緣起 開始介紹 intern()方法前,先看一個簡單的 Java程序吧!下面是一段 Java代碼,代碼內容比較簡單...
    LilacZiyun閱讀 2,755評論 6 17
  • 一、JVM內幕:Java虛擬機詳解(java se 7規范) 直接上圖,再逐步解釋。 上圖顯示的組件分兩個章節解釋...
    屈小勇閱讀 1,900評論 6 22
  • ??需要說明的一點是,這篇文章是以《深入理解Java虛擬機》第二版這本書為基礎的,這里假設大家已經了解了JVM的運...
    Geeks_Liu閱讀 14,076評論 5 44
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,869評論 18 139
  • 今天和閔老師,一個網友,兩個人聊天,使我明白一個至深的道理,定位,我的人生缺少定位。 今天和一個網友聊天,聊著聊著...
    韋哥說道閱讀 185評論 0 0