《深入理解java虛擬機》第二版 57頁
對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類裡發現
根據註釋可以看出來,System是由虛擬機自動調用的。
在initializeSystemClass 方法中發現調用了Version對象的init靜態方法
而Version類裡 laucher_name是私有靜態字符串常量
因此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
從而更加證實了我們的猜測。
再遇到類似問題的時候,希望大家可以多從源碼角度去追本溯源,能夠多分享出來。
閱讀更多 明明如月學長 的文章