亚洲国产日韩欧美一区二区三区,精品亚洲国产成人av在线,国产99视频精品免视看7,99国产精品久久久久久久成人热,欧美日韩亚洲国产综合乱

JVM-String比較-字節(jié)碼分析

原創(chuàng) 2016-11-05 13:53:13 480
摘要:一道String字符串比較問題引發(fā)的字節(jié)碼分析public class a {     public static void main(String[] args)throws Exception{       &nbs

一道String字符串比較問題引發(fā)的字節(jié)碼分析

public class a {
    public static void main(String[] args)throws Exception{
        
    }
    public static void aa(){
        String s1="a";//內(nèi)存在方法區(qū)的常量池
        String s2="b";//內(nèi)存在方法區(qū)的常量池
        String s12 = "ab";//內(nèi)存在方法區(qū)的常量池
        String s3 = s1 + s2;//s3的內(nèi)存所在???
        p(s3==s12);//false
    }
    public static void bb(){
        String s1="a"+"b";//s1的內(nèi)存所在???
        String s2 = "ab";//內(nèi)存在方法區(qū)的常量池
        p(s1==s2);//true
    }public static void p(Object obj){
        System.out.println(obj);
    }
}

這是我們經(jīng)常碰到的煩人的String比較問題,要得到答案,就要弄清楚aa()方法中的s3的內(nèi)存在哪里?,和bb()方法中的s1的內(nèi)存在哪里?

不多說,貼上a.class文件反編譯的字節(jié)碼指令:

首先是 aa()方法:

public static void aa();
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=4, args_size=0                                                  共4個本地變量空間
         0: ldc           #3                  // String a                            將字符串"a"從常量池中推送至棧頂
         2: astore_0                                                                  將棧頂引用類型(即字符串"a")存入第一個本地變量
         3: ldc           #4                  // String b                              將字符串"b"從常量池推送至棧頂
         5: astore_1                                                                  將棧頂引用類型(即字符串"b")存入第二個本地變量
         6: ldc           #5                  // String ab                             將字符串"ab"從常量池推送至棧頂
         8: astore_2                                                                  將棧頂引用類型(即字符串"ab")存入第三個本地變量
         9: new           #6                  // class java/lang/StringBuilder         創(chuàng)建StringBuilder對象sb,并將引用值壓入棧頂
        12: dup                                                                       復(fù)制棧頂數(shù)值,并將復(fù)制值壓入棧頂
        13: invokespecial #7                  // Method java/lang/StringBuilder.       調(diào)用對象的初始化方法
"<init>":()V
        16: aload_0                                                                   將第一個本地變量(即字符串"a")推送至棧頂
        17: invokevirtual #8                  // Method java/lang/StringBuilder.       調(diào)用實例方法sb.append("a");
append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        20: aload_1                                                                   將第二個本地變量(即字符串"b")推送至棧頂
        21: invokevirtual #8                  // Method java/lang/StringBuilder.       調(diào)用實例方法sb.append("b");
append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        24: invokevirtual #9                  // Method java/lang/StringBuilder.       調(diào)用實例方法sb.toString(),并將結(jié)果【Java堆地址】放在棧頂
toString:()Ljava/lang/String;
        27: astore_3                                                                   將棧頂引用類型(即堆地址)存入第四個本地變量                                                            
        28: aload_3                                                                    將第四個本地變量(即堆地址)推送至棧頂
        29: aload_2                                                                    將第三個本地變量(即字符串"ab")推送至棧頂
        30: if_acmpne     37                                                           比較棧頂兩引用數(shù)值,結(jié)果不同跳轉(zhuǎn)(當(dāng)然不同啦)
        33: iconst_1
        34: goto          38
        37: iconst_0                                                                   將int類型 0 壓入棧頂
        38: invokestatic  #10                 // Method java/lang/Boolean.valueO       調(diào)用靜態(tài)方法Boolean.valueOf();實現(xiàn)基本數(shù)據(jù)類型->包裝類型自動轉(zhuǎn)換
f:(Z)Ljava/lang/Boolean;
        41: invokestatic  #11                 // Method p:(Ljava/lang/Object;)V        調(diào)用靜態(tài)方法p(false);//輸出false
        44: return                                                                     從當(dāng)前方法返回void

 針對其中的一些解釋:(下面的數(shù)字是字節(jié)碼偏移量)

24       為何在sb.toString()我說的是【堆地址】,大家看源碼就知道了。

//這是StringBuilder的源碼,返回的是堆上的字符串地址
public String toString() {
    return new String(value, 0, count);
}

所以在aa()方法中,s3的內(nèi)存其實在Java堆上,s12在方法區(qū)的常量池上,所以兩者不相等。

 

37      boolean到底分配幾個字節(jié),在這里大家可以看到。

如果為true,編譯器翻譯的字節(jié)碼是iconst_1,意思將int類型1存入棧頂,所以單個引用boolean值時,分配4個字節(jié),和int相同。(數(shù)組boolean沒測試,不清楚)

如果為false,編譯器翻譯的字節(jié)碼是iconst_0,意思將int類型0存入棧頂。

 

38      在這里我們還能看到自動類型轉(zhuǎn)換的身影,這里是基本數(shù)據(jù)類型boolean->包裝類Boolean的自動類型轉(zhuǎn)換,實際調(diào)用的就是Boolean.valueOf()靜態(tài)方法,這是因為下面的p()方法里面需要的是Object引用類型,所以進(jìn)行了自動類型轉(zhuǎn)換。

 

然后是 bb()方法:

public static void bb();
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=0                                                   兩個本地變量空間
         0: ldc           #5                  // String ab                             將字符串"ab"從常量池中推送至棧頂
         2: astore_0                                                                   將棧頂引用類型(字符串"ab")存入第一個本地變量
         3: ldc           #5                  // String ab                             將字符串"ab"從常量池中推送至棧頂
         5: astore_1                                                                   將棧頂引用類型(字符串"ab")存入第一個本地變量
         6: aload_0                                                                    將第一個本地變量("ab")推送至棧頂
         7: aload_1                                                                    將第二個本地變量("ab")推送至棧頂
         8: if_acmpne     15                                                           比較棧頂兩引用類型數(shù)值,結(jié)果不同跳轉(zhuǎn)(這里當(dāng)然相同啦)
        11: iconst_1                                                                   將int類型 1 推送至棧頂
        12: goto          16                                                           無條件跳轉(zhuǎn)到16字節(jié)碼偏移量
        15: iconst_0
        16: invokestatic  #10                 // Method java/lang/Boolean.valueO       調(diào)用靜態(tài)方法Boolean.valueOf();并將返回的Boolean類型的true壓入棧頂
f:(Z)Ljava/lang/Boolean;                                            
        19: invokestatic  #11                 // Method p:(Ljava/lang/Object;)V        調(diào)用靜態(tài)方法p(true);輸出true
        22: return                                                                     從當(dāng)前方法返回void

針對其中的一些解釋:(下面的數(shù)字是字節(jié)碼偏移量)

0           大家看到了吧,編譯器看到String a="aa"+"bb";會自動合并,將"aabb"存入常量池,并返回地址。所以答案為true。


發(fā)佈手記

熱門詞條