天天看點

從位元組碼角度分析Byte類型變量b++和++b

轉載自:http://www.cnblogs.com/nailperry/p/4780354.html

1. 下面是一到Java筆試題:

從位元組碼角度分析Byte類型變量b++和++b
1 public class Test2
 2 {
 3     public void add(Byte b)
 4     {
 5         b = b++;
 6     }
 7     public void test()
 8     {
 9         Byte a = 127;
10         Byte b = 127;
11         add(++a);
12         System.out.print(a + " ");
13         add(b);
14         System.out.print(b + "");
15     }
16 }      
從位元組碼角度分析Byte類型變量b++和++b

2. 為友善分析起見,将列印的語句去掉,如下:

從位元組碼角度分析Byte類型變量b++和++b
1     public void add(Byte b)
 2     {
 3         b = b++;
 4     }
 5     public void test()
 6     {
 7         Byte a = 127;
 8         Byte b = 127;
 9         add(++a);
10         add(b);
11     }      
從位元組碼角度分析Byte類型變量b++和++b

3. 将上述代碼反編譯,得到如下位元組碼:

從位元組碼角度分析Byte類型變量b++和++b
1 public void add(java.lang.Byte);
 2     Code:
 3        0: aload_1
 4        1: astore_2
 5        2: aload_1
 6        3: invokevirtual #2                  // Method java/lang/Byte.byteValue:(
 7 )B
 8        6: iconst_1
 9        7: iadd
10        8: i2b
11        9: invokestatic  #3                  // Method java/lang/Byte.valueOf:(B)
12 Ljava/lang/Byte;
13       12: dup
14       13: astore_1
15       14: astore_3
16       15: aload_2
17       16: astore_1
18       17: return
19 
20 public void test();
21     Code:
22        0: bipush        127
23        2: invokestatic  #3                  // Method java/lang/Byte.valueOf:(B)
24 Ljava/lang/Byte;
25        5: astore_1
26        6: bipush        127
27        8: invokestatic  #3                  // Method java/lang/Byte.valueOf:(B)
28 Ljava/lang/Byte;
29       11: astore_2
30       12: aload_0
31       13: aload_1
32       14: invokevirtual #2                  // Method java/lang/Byte.byteValue:(
33 )B
34       17: iconst_1
35       18: iadd
36       19: i2b
37       20: invokestatic  #3                  // Method java/lang/Byte.valueOf:(B)
38 Ljava/lang/Byte;
39       23: dup
40       24: astore_1
41       25: invokevirtual #4                  // Method add:(Ljava/lang/Byte;)V
42       28: aload_0
43       29: aload_2
44       30: invokevirtual #4                  // Method add:(Ljava/lang/Byte;)V
45       33: return
46 }      
從位元組碼角度分析Byte類型變量b++和++b

 4. 位元組碼很長,看着發怵,不用怕,我們将位元組碼分成兩部分:add方法和test方法。

 5. 我們先來看add方法:

從位元組碼角度分析Byte類型變量b++和++b
1 add方法局部變量表
 2 下标:  0         1                2                    3
 3 标記: this   形參Byte b   Byte型臨時變量tmp     Byte型臨時變量tmp2
 4 值  :          -128             -128                  -127
 5 public void add(java.lang.Byte);
 6     Code:
 7        0: aload_1          // 局部變量表中下标為1的引用型局部變量b進棧      
 8        1: astore_2         // 将棧頂數值指派給局部變量表中下标為2的引用型局部變量tmp,棧頂數值出棧。
 9        2: aload_1           // 局部變量表中下标為1的引用型局部變量b進棧 
10        3: invokevirtual #2 // 自動拆箱,通路棧頂元素b,調用執行個體方法b.byteValue擷取b所指Byte
11                            // 對象的value值-128,并壓棧
12        6: iconst_1           // int型常量值1進棧
13        7: iadd               // 依次彈出棧頂兩int型數值1(0000 0001)、-128(1000 0000)
14                            //(byte類型自動轉型為int類型)相加,并将結果-127(1000 0001)進棧
15        8: i2b               // 棧頂int值-127(1000 0001)出棧,強轉成byte值-127(1000 0001),并且結果進棧
16        9: invokestatic  #3 // 自動裝箱:通路棧頂元素,作為函數實參傳入靜态方法Byte.valueOf(byte),
17                            // 傳回value值為-127的Byte對象的位址,并壓棧
18       12: dup               // 複制棧頂數值,并且複制值進棧
19       13: astore_1           // 将棧頂數值指派給局部變量表中下标為1的引用型局部變量b,棧頂數值出棧。此時b為-127
20       14: astore_3           // 将棧頂數值指派給局部變量表中下标為3的引用型局部變量tmp2,棧頂數值出棧。此時tmp2為-127
21       15: aload_2           // 局部變量表中下标為2的引用型局部變量tmp進棧,即-128入棧  
22       16: astore_1         // 将棧頂數值指派給局部變量表中下标為1的引用型局部變量b,棧頂數值出棧。此時b為-128
23       17: return      
從位元組碼角度分析Byte類型變量b++和++b

總結一下上述過程,核心步驟為b = b++;分為三步:參考:http://blog.csdn.net/brooksychen/article/details/1624753

①把變量b的值取出來,放在一個臨時變量裡(我們先記作tmp);

②把變量b的值進行自加操作;

③把臨時變量tmp的值作為自增運算前b的值使用,在本題中就是給變量b指派。

到此可得出結論,add方法隻是個擺設,沒有任何作用,不修改實參的值。

 6. 搞懂了add方法,我們接下來分析test方法:

這裡需要說明兩點:

(1)由于Byte類緩存了[-128,127]之間的Byte對象,故當傳入的實參byte相同時,通過Byte.valueOf(byte)傳回的對象是同一個對象,詳見Byte源碼。

(2)如果是執行個體方法(非static),那麼局部變量表的第0位索引的Slot預設是用于傳遞方法所屬對象執行個體的引用,在方法中通過this通路。詳見:http://wangwengcn.iteye.com/blog/1622195

從位元組碼角度分析Byte類型變量b++和++b
1 test方法局部變量表
 2 下标:  0         1                2                    
 3 标記: this   形參Byte a   Byte型臨時變量b 
 4 值  :          -128             127        
 5 public void test();
 6     Code:
 7        0: bipush        127        // 将一個byte型常量值推送至操作數棧棧頂
 8        2: invokestatic  #3        // 自動裝箱:通路棧頂元素,作為函數實參傳入靜态方法Byte.valueOf(byte),
 9                                 // 傳回value值為127的Byte對象的位址,并壓棧
10        5: astore_1                // 将棧頂數值指派給局部變量表中下标為1的引用型局部變量a,棧頂數值出棧。此時a為127
11        6: bipush        127        // 将一個byte型常量值推送至操作數棧棧頂
12        8: invokestatic  #3        // 自動裝箱:通路棧頂元素,作為函數實參傳入靜态方法Byte.valueOf(byte),
13                                 // 傳回value值為127的Byte對象的位址,并壓棧。這裡需要說明一點,
14                                 // 由于Byte類緩存了[-128,127]之間的Byte對象,故當傳入的實參byte相同時,
15                                 // 通過Byte.valueOf(byte)傳回的對象是同一個對象,詳見Byte源碼。
16       11: astore_2                // 将棧頂數值指派給局部變量表中下标為2的引用型局部變量b,棧頂數值出棧。此時b為127
17       12: aload_0                // 局部變量表中下标為0的引用型局部變量進棧,即this,加載this主要是為了下面通過this調用add方法。  
18       13: aload_1                // 局部變量表中下标為1的引用型局部變量a進棧  
19       14: invokevirtual #2      // 自動拆箱,通路棧頂元素a,調用執行個體方法a.byteValue擷取a所指Byte
20                                 // 對象的value值127,并壓棧
21       17: iconst_1                // int型常量值1進棧
22       18: iadd                    // 依次彈出棧頂兩int型數值1(0000 0001)、127(0111 1111)
23                                 //(byte類型自動轉型為int類型)相加,并将結果128(1000 0000)進棧
24       19: i2b                    // 棧頂int值128(1000 0000)出棧,強轉成byte值-128(1000 0000),并且結果進棧
25       20: invokestatic  #3      // 自動裝箱:通路棧頂元素,作為函數實參傳入靜态方法Byte.valueOf(byte),
26                                 // 傳回value值為-128的Byte對象的位址,并壓棧
27       23: dup                    // 複制棧頂數值,并且複制值進棧
28       24: astore_1                // 将棧頂數值指派給局部變量表中下标為1的引用型局部變量a,棧頂數值出棧。此時a為-128
29       25: invokevirtual #4      // 調用執行個體方法add:(Byte),傳入的實參為棧頂元素,也即a的拷貝,前面已經分析過了,該調用不改變a的對象值
30                                 // 該執行個體方法的調用需要通路棧中的兩個參數,一個是實參,也即a的拷貝,一個是在第12步入棧的this。
31       28: aload_0                // 局部變量表中下标為0的引用型局部變量進棧,即this,加載this主要是為了下面通過this調用add方法。
32       29: aload_2                // 局部變量表中下标為2的引用型局部變量b進棧  
33       30: invokevirtual #4      // 調用執行個體方法add:(Byte),傳入的實參為棧頂元素,也即b,前面已經分析過了,該調用不改變b的對象值
34                                 // 該執行個體方法的調用需要通路棧中的兩個參數,一個是實參,也即b,一個是在第28步入棧的this。
35       33: return                // 函數執行到最後,b所指對象的值沒有改變,仍為127。
36 }      
從位元組碼角度分析Byte類型變量b++和++b

7. 綜合以上分析,原問題的輸出為-128 127

8. 小結:

通過以上分析,我們發現該題綜合考察了Byte自動拆/裝箱、Byte對象緩存、Java編譯器對i=i++的特殊處理等等,相當有難度呀。

繼續閱讀