直接上兩個(gè)網(wǎng)上很經(jīng)典的demo
例子1
int i = 1;
int j = i++;
System.out.println(i);
System.out.println(j);
答案:輸出2和1
直接反編譯class文件,得到前兩行的java指令,分析如下
可以看出來(lái),i++分為兩iload和iinc缭黔,所以j=i++會(huì)先將i壓入操作數(shù)棧,再對(duì)變量i自加蒂破,所以下一步操作從操作數(shù)棧取到的i是自加之前的馏谨。
例子2
int i = 1;
i = i++;
System.out.println(i);
答案:輸出1
再次觀察java指令
所以i變量并不是沒(méi)有自加,而是變量自加后又被操作數(shù)棧中的原始i賦值了附迷,所以i看起來(lái)沒(méi)變化
例子3
接下來(lái)看看++i是什么樣子
int i = 1;
i = ++i;
System.out.println(i);
輸出2
再次觀察java指令
可以看到惧互,i=++i相當(dāng)于iinc和iload哎媚,所以操作數(shù)棧中存儲(chǔ)的是自增之后的i,這樣下次操作用的就是自增之后的i壹哺。
于是乎抄伍,我得到了大學(xué)老師告知的結(jié)論:
i++艘刚,i在語(yǔ)句結(jié)束后自增管宵,當(dāng)前語(yǔ)句使用的是原始i
++i,先對(duì)i進(jìn)行自增攀甚,當(dāng)前語(yǔ)句使用的是自增之后的i
上面的結(jié)論得到了System.out.println(i++)和System.out.printlin(++i)的證實(shí)
但是卻不嚴(yán)謹(jǐn)箩朴,如下面的例子
例子4
int i = 1;
int[] arr = new int[10];
arr[i++] = 10 - i;
System.out.println(arr[1]);
輸出8
按照之前的理解, arr[i++] = 10 - i; 中的i應(yīng)該是1,所以答案是arr[1]=10-1=9啊秋度,截取該行代碼的java指令如下
11: aload_2 #加載arr數(shù)組到操作數(shù)棧
12: iload_1 #加載i到操作數(shù)棧 (arr[i++] = arr[1])
13: iinc 1, 1 #i變量自增(i=2)
16: bipush 10 #加載10到操作數(shù)棧
18: iload_1 #加載i到操作數(shù)棧 (2)
19: isub #執(zhí)行減法 10-2=10-2=8
20: iastore #arr[i]=8
可以看到, 當(dāng)編譯器遇到i++后炸庞,確實(shí)是先iload然后iinc,但是后面又遇到了10-i,這時(shí)再次從變量棧加載i(2)到操作數(shù)棧荚斯,所以10-i = 2埠居;
所以結(jié)論再精確點(diǎn),應(yīng)為
當(dāng)語(yǔ)句中只有一個(gè)i:
i++事期,i在語(yǔ)句結(jié)束后自增滥壕,當(dāng)前語(yǔ)句使用的是原始i
++i,先對(duì)i進(jìn)行自增兽泣,當(dāng)前語(yǔ)句使用的是自增之后的i
當(dāng)語(yǔ)句中有多個(gè)i:
i++绎橘,該表達(dá)式之前的i為原始i,該表達(dá)式為原始i唠倦,該表達(dá)式之后i為自增后的i
++i, 該表達(dá)式之前的i為原始i称鳞,該表達(dá)式為自增后的i,該表達(dá)式之后的i為自增后的i
驗(yàn)證下:
int i = 1;
int[] arr = new int[]{2, 4, 6, 8, 10};
int[] arr1 = new int[5];
arr1[i] = arr[i++] - i; //arr1[1]=arr[1]-2
輸出 [0,2,0,0,0]
int i = 1;
int[] arr = new int[]{2, 4, 6, 8, 10};
int[] arr1 = new int[5];
arr1[i] = arr[++i] - i;// arr1[1]=arr[2]-2
輸出 [0,4,0,0,0]
但是真是不建議在一個(gè)復(fù)雜的語(yǔ)句中使用i++或++i稠鼻,給自己增加難度不說(shuō)冈止,也增加的以后的維護(hù)成本;看似很炫的代碼換來(lái)的是后人的詛咒 ... ...