轉載自:http://www.cnblogs.com/nailperry/p/4780354.html
1. 下面是一到Java筆試題:
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 }
2. 為友善分析起見,将列印的語句去掉,如下:
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 }
3. 将上述代碼反編譯,得到如下位元組碼:
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 }
4. 位元組碼很長,看着發怵,不用怕,我們将位元組碼分成兩部分:add方法和test方法。
5. 我們先來看add方法:
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
總結一下上述過程,核心步驟為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
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 }
7. 綜合以上分析,原問題的輸出為-128 127
8. 小結:
通過以上分析,我們發現該題綜合考察了Byte自動拆/裝箱、Byte對象緩存、Java編譯器對i=i++的特殊處理等等,相當有難度呀。