通過反編譯看Java String及intern內幕,intern內幕

來源:互聯網
上載者:User

通過反編譯看Java String及intern內幕,intern內幕

一、字串問題

  字串在我們平時的編碼工作中其實用的非常多,並且用起來也比較簡單,所以很少有人對其做特別深入的研究。倒是面試或者筆試的時候,往往會涉及比較深入和難度大一點的問題。我在招聘的時候也偶爾會問應聘者相關的問題,倒不是說一定要回答的特別正確和深入,通常問這些問題的目的有兩個,第一是考察對 JAVA 基礎知識的瞭解程度,第二是考察應聘者對技術的態度。

  我們看看以下程式會輸出什麼結果?如果你能正確的回答每一道題,並且清楚其原因,那本文對你就沒什麼太大的意義。如果回答不正確或者不是很清楚其原理,那就仔細看看以下的分析,本文應該能協助你清楚的理解每段程式的結果及輸出該結果的深層次原因。

程式碼片段一:

package com.paddx.test.string;public class StringTest {    public static void main(String[] args) {        String str1 = "string";        String str2 = new String("string");        String str3 = str2.intern();        System.out.println(str1==str2);//#1        System.out.println(str1==str3);//#2    }}

 程式碼片段二:

package com.paddx.test.string;
public class StringTest01 {    public static void main(String[] args) {        String baseStr = "baseStr";        final String baseFinalStr = "baseStr";        String str1 = "baseStr01";        String str2 = "baseStr"+"01";        String str3 = baseStr + "01";        String str4 = baseFinalStr+"01";        String str5 = new String("baseStr01").intern();        System.out.println(str1 == str2);//#3        System.out.println(str1 == str3);//#4        System.out.println(str1 == str4);//#5        System.out.println(str1 == str5);//#6    }}

程式碼片段三(1): 

package com.paddx.test.string;
  public class InternTest { public static void main(String[] args) { String str2 = new String("str")+new String("01"); str2.intern(); String str1 = "str01"; System.out.println(str2==str1);//#7 }}

 程式碼片段三(2):

package com.paddx.test.string;public class InternTest01 {    public static void main(String[] args) {        String str1 = "str01";        String str2 = new String("str")+new String("01");        str2.intern();        System.out.println(str2 == str1);//#8    }}

 為了方便描述,我對上述代碼的輸出結果由#1~#8進行了編碼,下文中藍色字型部分即為結果。

二、字串深入分析

  1、程式碼片段一分析

  字串不屬於基本類型,但是可以像基本類型一樣,直接通過字面量賦值,當然也可以通過new來產生一個字串對象。不過通過字面量賦值的方式和new的方式產生字串有本質的區別:

可以通過 intern() 方法來將字串添加的常量池中,並返回指向該常量的引用。

現在我們應該能很清楚程式碼片段一的結果了:

結果 #1:因為str1指向的是字串中的常量,str2是在堆中產生的對象,所以str1==str2返回false。

結果 #2:str2調用intern方法,會將str2中值(“string”)複製到常量池中,但是常量池中已經存在該字串(即str1指向的字串),所以直接返回該字串的引用,因此str1==str2返回true。

以下運行程式碼片段一的代碼的結果:

 2、程式碼片段二分析

對於程式碼片段二的結果,還是通過反編譯StringTest01.class檔案比較容易理解:

 常量池內容(部分):

結果#3:str1==str2 肯定會返回true,因為str1和str2都指向常量池中的同一引用地址。所以其實在JAVA 1.6之後,常量字串的“+”操作,編譯階段直接會合成為一個字串。

13: new           #4:產生StringBuilder的執行個體。

16: dup        :複製13產生對象的引用並壓入棧中。

17: invokespecial #5:調用常量池中的第五項,即StringBuilder.<init>方法。

以上三條指令的作用是產生一個StringBuilder的對象。

20: aload_1  :載入第一個參數的值,即"baseStr"

21: invokevirtual #6 :調用StringBuilder對象的append方法。

24: ldc           #7:載入常量池中的第七項("01")到棧中。

26: invokevirtual #6:調用StringBuilder.append方法。

29: invokevirtual #8:調用StringBuilder.toString方法。

32: astore        5:將29中的結果引用賦值改第五個局部變數,即對變數str3的賦值。

結果 #4:因為str3實際上是stringBuilder.append()產生的結果,所以與str1不相等,結果返回false。

34: ldc           #3:載入常量池中的第三項("baseStr01")到棧中。

36: astore        6:將34中的引用賦值給第六個局部變數,即str4="baseStr01";

結果 #5 :因為str1和str4指向的都是常量池中的第三項,所以str1==str4返回true。這裡我們還能發現一個現象,對於final欄位,編譯期直接進行了常量替換,而對於非final欄位則是在運行期進行賦值處理的。

38: new           #9:建立String對象

41: dup               :複製引用並壓如棧中。

42: ldc           #3:載入常量池中的第三項("baseStr01")到棧中。

44: invokespecial #10:調用String."<init>"方法,並傳42步驟中的引用作為參數傳入該方法。

47: invokevirtual #11:調用String.intern方法。

從38到41的對應的源碼就是new String("baseStr01").intern()。

50: astore        7:將47步返回的結果賦值給變數7,即str5指向baseStr01在常量池中的位置。

結果 #6 :因為str5和str1都指向的都是常量池中的同一個字串,所以str1==str5返回true。

運行程式碼片段二,輸出結果如下: 

 3、程式碼片段三解析:

 對於程式碼片段三,在 JDK 1.6 和 JDK 1.7中的運行結果不同。我們先看一下運行結果,然後再來解釋其原因:

 JDK 1.6 下的運行結果:

結果 #7:在第一種情況下,因為常量池中沒有“str01”這個字串,所以會在常量池中產生一個對堆中的“str01”的引用,而在進行字面量賦值的時候,常量池中已經存在,所以直接返回該引用即可,因此str1和str2都指向堆中的字串,返回true。

結果 #8:調換位置以後,因為在進行字面量賦值(String str1 = "str01")的時候,常量池中不存在,所以str1指向的常量池中的位置,而str2指向的是堆中的對象,再進行intern方法時,對str1和str2已經沒有影響了,所以返回false。

 

三、常見面試題解答

有了對以上的知識的瞭解,我們現在再來看常見的面試或筆試題就很簡單了:

 

Q:String s = new String("xyz"),建立了幾個String Object? 

A:兩個,常量池中的"xyz"和堆中對象。

 

Q:下列程式的輸出結果:

String s1 = “abc”;
String s2 = “abc”;
System.out.println(s1 == s2);

A:true,均指向常量池中對象。

 

Q:下列程式的輸出結果:

String s1 = new String(“abc”);
String s2 = new String(“abc”);
System.out.println(s1 == s2);

A:false,兩個引用指向堆中的不同對象。

 

Q:下列程式的輸出結果:

String s1 = “abc”;
String s2 = “a”;
String s3 = “bc”;
String s4 = s2 + s3;
System.out.println(s1 == s4);

A:false,因為s2+s3實際上是使用StringBuilder.append來完成,會產生不同的對象。

 

Q:下列程式的輸出結果:

String s1 = “abc”;
final String s2 = “a”;
final String s3 = “bc”;
String s4 = s2 + s3;
System.out.println(s1 == s4);

A:true,因為final變數在編譯後會直接替換成對應的值,所以實際上等於s4="a"+"bc",而這種情況下,編譯器會直接合并為s4="abc",所以最終s1==s4。

 

Q:下列程式的輸出結果:

String s = new String("abc");
String s1 = "abc";
String s2 = new String("abc");

System.out.println(s == s1.intern());
System.out.println(s == s2.intern());
System.out.println(s1 == s2.intern());

A:false,false,true,具體原因參考第二部分內容。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.