最近到廣州某互聯網公司面試,當時面試官問假設有兩個字符串String a="abc",String b = "abc";問輸出a==b是true仍是false。我當時堅決果斷答了true,而後根據字符串常量池的知識點結合jvm的內存模型講解,然而他卻跟我說是false,說這是最基本的問題。我當時一臉懵逼,跟他討論了很長時間,後來發現他是錯的,他說a,b兩個變量是存在棧中,這兩個引用是不同的,只不過它們指向的內容是同樣的。由於他畢竟工做了好幾年,我當時也質疑個人知識點,因此當時也接受了他的答案。回來上機實踐查資料求證以後,說明我是對的,具體內容整理以下。首先先上三份代碼:面試
第一段:jvm
public static void main(String[] args) { String s1 = "hello"; String s2 = "hello"; String s3 = "he" + "llo"; String s4 = "hel" + new String("lo"); String s5 = new String("hello"); String s6 = s5.intern(); String s7 = "h"; String s8 = "ello"; String s9 = s7 + s8; System.out.println(s1==s2);//true System.out.println(s1==s3);//true System.out.println(s1==s4);//false System.out.println(s1==s9);//false System.out.println(s4==s5);//false System.out.println(s1==s6);//true }
在jdk1.6,1.7,1.8下運行的結果爲:優化
System.out.println(s1==s2);//true
System.out.println(s1==s3);//true
System.out.println(s1==s4);//false
System.out.println(s1==s9);//false
System.out.println(s4==s5);//false
System.out.println(s1==s6);//truespa
第二段:設計
public static void main(String[] args) { String s1 = new String("hello"); String intern1 = s1.intern(); String s2 = "hello"; System.out.println(s1 == s2); String s3 = new String("hello") + new String("hello"); String intern3 = s3.intern(); String s4 = "hellohello"; System.out.println(s3 == s4); }
在jdk1.6下運行的結果爲:code
false,false對象
在jdk1.7,1.8下運行的結果爲:blog
false,true內存
第三段:字符串
public static void main(String[] args) { String s1 = new String("hello"); String s2 = "hello"; String intern1 = s1.intern(); System.out.println(s1 == s2); String s3 = new String("hello") + new String("hello"); String s4 = "hellohello"; String intern3 = s3.intern(); System.out.println(s3 == s4); }
在jdk1.6下運行的結果爲:
false,false
在jdk1.7,1.8下運行的結果爲:
false,false
第一段代碼:
String類的final修飾的,以字面量的形式建立String變量時,jvm會在編譯期間就把該字面量(「hello」)放到字符串常量池中,由Java程序啓動的時候就已經加載到內存中了。這個字符串常量池的特色就是有且只有一份相同的字面量,若是有其它相同的字面量,jvm則返回這個字面量的引用,若是沒有相同的字面量,則在字符串常量池建立這個字面量並返回它的引用。因爲s2指向的字面量「hello」在常量池中已經存在了(s1先於s2),因而jvm就返回這個字面量綁定的引用,因此s1==s2。s3中字面量的拼接其實就是「hello」,jvm在編譯期間就已經對它進行優化,因此s1和s3也是相等的。s4中的new String("lo")生成了兩個對象,"lo","new String("lo")","lo"存在字符串常量池,"new String("lo")"存在堆中,String s4 = "hel" + new String("lo")實質上是兩個對象的相加,編譯器不會進行優化,相加的結果存在堆中,而s1存在字符串常量池中,固然不相等。s1==s9的原理同樣。s4==s5兩個相加的結果都在堆中,不用說,確定不相等。s1==s6中,s5.intern()方法能使一個位於堆中的字符串在運行期間動態地加入到字符串常量池中(字符串常量池的內容是程序啓動的時候就已經加載好了),若是字符串常量池中有該對象對應的字面量,則返回該字面量在字符串常量池中的引用,不然,建立複製一份該字面量到字符串常量池並返回它的引用。所以s1==s6輸出true。
第二段代碼:
jdk1.6下字符串常量池是在永久區中,是與堆徹底獨立的兩個空間,s1指向堆中的內容,s2指向字符串常量池中的內容,二者固然不同,s1.intern1()將字面量加入字符串常量池中,因爲字符串常量池中已經存在該字面量,因此返回該字面量的惟一引用,intern1==s2就輸出true。
jdk1.7,1.8下字符串常量池已經轉移到堆中了,是堆中的一部份內容,jvm設計人員對intern()進行了一些修改,當執行s3.intern()時,jvm再也不把s3對應的字面量複製一份到字符串常量池中,而是在字符串常量池中存儲一份s3的引用,這個引用指向堆中的字面量,當運行到String s4 = "hellohello"時,發現字符串常量池已經存在一個指向堆中該字面量的引用,則返回這個引用,而這個引用就是s3。因此s3==s4輸出true。
第三段代碼:
jdk1.6下輸出false,false,原理和上面同樣。
jdk1.7,1.8下輸出false,false,s3指向堆內容,接着s4="hellohello"發現字符串常量池中沒有該字面量,建立並返回該引用。s3,s4一個在堆,一個在字符串常量池,是兩個獨立的引用,因此s3==s4輸出false。
2018-03-17 05:44:23