只要是會java的都知道 和—操作符的用法,如
int i = 1;
int j = i ;
int k = i;
結果i為3,j為1,k為3。
那如下程式碼:
int j = 0;
for (int i = 0; i < 100; i ) {
j = j ;
}
System.out.println(j);
輸出結果又是多少呢?100?0?
正確答案是0。為什麼呢?
要想搞明白這個問題,那來看看這段程式碼生成的位元組碼:
0: iconst_0
1: istore_1
2: iconst_0
3: istore_2
4: goto 15
7: iload_1
8: iinc 1, 1
11: istore_1
12: iinc 2, 1
15: iload_2
16: bipush 100
18: if_icmplt 7
21: getstatic #16; //Field java/lang/System.out:Ljava/io/PrintStream;
24: iload_1
25: invokevirtual #22; //Method java/io/PrintStream.println:(I)V
28: return
j = j ;的位元組碼為:7: iload_1
8: iinc 1, 1
11: istore_1
iload_1 意思是把區域性變數表中位置1的變數取出來放到運算元棧中;
iinc 1, 1 這是執行j 操作,把區域性變數表中位置1的變數加1;
istore_1 這是把運算元棧頂的值彈出放到區域性變數表位置1的變數中。
問題就在innc這個指令,指令格式為:
innc vindex const
這個指令只能操作int型別的變數,有兩個運算元,第一個運算元vindex指示區域性變數在區域性表中的位置索引,第二個運算元const表示要相加的整型常量。如
innc 2 100
表示把區域性變數表中位置2的變數加上100。
具體的過程是,把區域性變數表中位置2的變數取出來,加上100後,然後再放回到區域性變數表中。
而const這個常量值的範圍是-128~127,如果超出這個範圍,使用指令iinc_w。如:
int x = 1;
x = x -128;
x = x -129;
x = x 127;
x = x 128;
生成位元組碼如下:
0: iconst_1
1: istore_1
2: iinc 1, -128 //x = x -128;
5: iinc_w 1, -129 //x = x -129;
11: iinc 1, 127 //x = x 127;
14: iinc_w 1, 128 //x = x 128;
20: return
上面把innc指令的基本意思說清楚了,現在是最重要的一點:
innc指令操作的是區域性變數表中的變數,而不是當前運算元棧棧頂的資料(iinc指令實現有沒有用到運算元棧已經不重要了)。
上面j = j ;的位元組碼
7: iload_1
8: iinc 1, 1
11: istore_1
iload_1 先把j的值取出來放到棧頂,此時值為0,執行iinc 1, 1這個指令時,是操作的區域性變數表中變數(值為0),把它加1,此時值為1,istore_1指令把當前運算元棧頂的值(還是0),又放回區域性變數表位置1的變數中,那區域性變數表位置1的變數的值又從1變為0了。
所以,不管怎麼迴圈,j的值永遠是0。
下面說說i 和 i的問題:
都知道i 是先使用i的值,再把i的值加1;而 i是先把i的值加1,再使用i的值。
但是實際上是怎麼回事呢?其實i 和 i都是使用iinc vindex, 1指令,區別在於,i 是先把區域性變數取出來放到運算元棧頂,再把區域性變數表中的變數值加1,而 i是先把區域性變數表中的變數值加1,再把區域性變數取出來放到運算元棧頂。如下程式碼:
int j = 0;
int x = j ;
x = j;
位元組碼如下:
0: iconst_0 //把常量0放到運算元棧頂
1: istore_1 //int j = 0;
2: iload_1 //先取出j的值0
3: iinc 1, 1 //j ,把區域性變數表中j的值加1,j=1,此時運算元棧頂的值還是0
6: istore_2 //把運算元棧頂的值0放加區域性變數表中,此時x=0
7: iinc 1, 1 // j,先把區域性變數表中j的值加1,此時j=2
10: iload_1 //取出j的值2
11: istore_2 //x=2
12: return //方法返回
以上說的都是 操作符,–的操作也是一樣的,就不再多說。
到現在終於把這個簡單的 和–徹底的搞明白了-:)