《深入理解java虛擬機》String.intern()探究

《深入理解java虛擬機》第二版 57頁

《深入理解java虛擬機》String.intern()探究

對String.intern()返回引用的測試代碼如下:

《深入理解java虛擬機》String.intern()探究

結果是 :

true

false

可能很多人覺得這個結果很奇怪,在這裡我們進行深入地探究。


書中寫道,如果JDK1.6會返回兩個false,JDK1.7運行則會返回一個true一個false。

因為JDK1.6中,intern()方法會把首次遇到的字符串實例複製到永久代中,返回的也是永久代中這個字符串的實例的引用,而StringBulder創建的字符串實例在Java堆上,所以必然不是同一個引用,將返回false。

在JDK1.7中,intern()的實現不會在複製實例,只是在常量池中記錄首次出現的實例引用,因此返回的是引用和由StringBuilder.toString()創建的那個字符串實例是同一個。

str2的比較返回false因為"java"這個字符串在執行StringBuilder.toString()之前已經出現過,字符串常量池中已經有它的引用了,不符合“首次出現”的原則,而“計算機軟件”這個字符串是首次出現,因此返回true。


那麼就有疑問了,這個“java”字符串在哪裡出現過呢?顯然並不是直接出現在這個類裡面。

我們分別打開String 、StringBuilder和System類的源碼看看有啥發現,其中System類裡發現

《深入理解java虛擬機》String.intern()探究

根據註釋可以看出來,System是由虛擬機自動調用的。

《深入理解java虛擬機》String.intern()探究

在initializeSystemClass 方法中發現調用了Version對象的init靜態方法

《深入理解java虛擬機》String.intern()探究

而Version類裡 laucher_name是私有靜態字符串常量

《深入理解java虛擬機》String.intern()探究

因此sun.misc.Version 類會在JDK類庫的初始化過程中被加載並初始化,而在初始化時它需要對靜態常量字段根據指定的常量值(ConstantValue)做默認初始化,此時被 sun.misc.Version.launcher 靜態常量字段所引用的"java"字符串字面量就被intern到HotSpot VM的字符串常量池——StringTable裡了。

因此我們修改一下代碼:

String str2 = new StringBuilder("Java(TM) SE ").append("Runtime Environment").toString();System.out.println(str2.intern() == str2) 

發現結果還是false

從而更加證實了我們的猜測。


再遇到類似問題的時候,希望大家可以多從源碼角度去追本溯源,能夠多分享出來。


分享到:


相關文章: