Java自增
本文分為以下部分:
- 栗子
- 栗子解釋
- 來點復雜的
- 位元組碼解讀
- 總結
栗子
java存在一種神奇的運算子,++,自增1,但是經常分不清楚 i++ 和++i 兩者的區別,雖然最后結果可能都是 i+1,但是在不同場景使用有不同效果,先上一段代碼,
public class IncreaseTest {
public static void main(String[] args) {
int i = 10;
int j = i++;
System.out.println(j);
int k = ++i;
System.out.println(k);
}
}
看著腦袋都大,感覺 j、k 最后值都一樣,實際上不一樣,在講解原理之前,先簡單說明一下底層東西,
區域變數表
oracle java 區域變數表 中解釋

其中第二段解釋,byte、char、short、int 等基本資料型別值會存在區域變數表中,
運算元堆疊
oracle java 運算元堆疊 中解釋

簡單理解就是 主要用于保存計算程序的中間結果,同時作為計算程序中變數臨時的存盤空間,就是用于計算等操作,
舉個簡單的栗子簡單解釋上面兩個東西
/**
* 運算元堆疊壓入 10 這個值
* 然后將 10 保存到本地變數表賦值給 i
* j同理
*
* 在進行 k = i + j 的操作
* 運算元堆疊從本地變數表中讀取 i 的值壓入運算元堆疊
* 運算元堆疊從本地變數表中讀取 j 的值壓入運算元堆疊
* 然后對運算元堆疊中的兩個值進行相加操作
* 將結果保存到本地變數表同時賦值給 k
* 最后輸出 本地變數表中 讀取的資料
*/
public static void main(String[] args) {
int i = 10;
int j = 20;
int k = i + j;
System.out.println(k);
}
圖例如下:

栗子解釋
回歸正題,最初的栗子,在計算 j 的時候,是先將 i 的本地變數表的值取出來壓入運算元堆疊,然后再進行 ++ 、賦值等操作,那是先賦值還是先 ++ 呢,其實已經不重要了,因為 ++ 操作操作的是本地變數表的值,而不是運算元堆疊中的值,所以當 i 的值已經壓入運算元堆疊后,那么運算元堆疊中的值已經是 10 了,就算本地變數表的值再變化,不會改變運算元堆疊中的值,所以 j 為 10,本地變數表中 i 為11,(事實上是i的值取出來壓入運算元堆疊,然后i的本地變數表進行+1操作,然后運算元堆疊賦值給j)

然后第二步,進行的是 int k = ++i;
首先看到的是 ++,所以操作的是本地變數表 i+1 ,然后再將 i 的本地變數表值壓入運算元堆疊,再賦值給 k,

所以 i++ ,是先將 i 的值壓入運算元堆疊,再將本地變數表中 i 的值 +1 ,再將運算元堆疊中的數值賦值給要賦值的物件,
++i ,是先將本地變數表中 i 的值 +1,再壓入運算元堆疊中,再進行賦值給物件等操作,
簡記:先++就是先+1,后++就是后+1,
來點復雜的
public class IncreaseTest2 {
public static void main(String[] args) {
int i = 10;
i = i++;
System.out.println(i);
i = ++i;
System.out.println(i);
}
}
首先看 i = i++;
通過簡單的栗子,我們知道,當 i++ 再后面時,為后 ++;那么是 i 先賦值給自己 ,再 +1?
當然不是,仔細看上方賦值給 j 的圖會發現,先本地變數 +1 ,再進行賦值,

然后再看 ++i,和第一個栗子差不多

位元組碼解讀
第一個栗子
在終端中使用 javap -v -p xxx.class 檔案可以看到該class檔案的位元組碼檔案,由于不好閱讀,所以追加覆寫到 test1.txt 檔案中,(采用idea的 jclasslib 插件也可以)

在檔案中找到熟悉的public static void main,下面即為代碼的位元組碼,我貼一部分

感覺有點晦澀難懂,我一一解釋吧,
bipush 將數值(當前10位byte,-128—127均為byte)壓入運算元堆疊,
istore 將運算元堆疊堆疊頂的值彈出回傳給本地變數表(保存到本地變數),
iload 從本地變數表中讀取數值壓入運算元堆疊,
iinc 本地變數表中的值+1
0: bipush 10 //運算元堆疊壓入10
2: istore_1 //將10存盤到本地變數i (這里1表示i,可以從檔案下方LocalVariableTable查看)
3: iload_1 //讀取i的值 10 壓入運算元堆疊
4: iinc 1, 1 //本地變數中 i 的值 +1(運算元堆疊值不變,依舊為10)
7: istore_2 //運算元堆疊堆疊頂(10)的值彈出回傳本地變數 j ,所以 j 為10
11: iload_2 //有個列印輸出流,需要讀取 j 的值,洗掉了相應位元組碼
15: iinc 1, 1 //本地變數 i 的值 +1(原本為11,現在變為12)
18: iload_1 //讀取i的值 12 壓入運算元堆疊
19: istore_3 //保存至 k ,所以 k 為12
第二個栗子
使用同樣方法查看位元組碼

其實和第一個栗子一樣,可以自行理解,
特栗
public class IncreaseTest3 {
public static void main(String[] args) {
int i = 10;
i = (i++) * (++i);
//120
System.out.println(i);
}
}
其實再理解上方位元組碼或者圖解,這個自然而然容易理解了
圖解

位元組碼大概描述就是
iload //10 入運算元堆疊
iinc //本地變數 +1 (運算元堆疊中依舊為10)
innc //本地變數繼續 +1(此時為12)
iload //12 入運算元堆疊
//然后進行乘法得到120
istore
總結
i++ ,是先將 i 的值壓入運算元堆疊,再將本地變數表中 i 的值 +1 ,最后運算元堆疊的數進行操作,
++i ,是先將本地變數表中 i 的值 +1,再壓入運算元堆疊中,最后運算元堆疊的數進行操作,
簡單記憶:先++就是先+1,后++就是后+1,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/458204.html
標籤:Java
