淺談JVM 底層解析 i++和 ++i 區別
一、前言
如果隻用普通的知識解釋i++和++i的話
i++ 先將i賦值再++
++i 先++再賦值
但是這簡單的回答並不能入吸引面試官的眼球,如果用java字節碼指令分析則效果完全不同
二、代碼實現
public class OperandStackTest { /** 程序員面試過程中, 常見的i++和++i 的區別 */ public static void add(){ //第1類問題: int i1 = 10; i1++; System.out.println("i1 =" + i1);//11 int i2 = 10; ++i2; System.out.println("i2 =" + i2);//11 //第2類問題: int i3 = 10; int i4 = i3++; System.out.println("i3 =" + i3);//11 System.out.println("i4 =" + i4);//10 int i5 = 10; int i6 = ++i5; System.out.println("i5 =" + i5);//11 System.out.println("i6 =" + i6);//11 //第3類問題: int i7 = 10; i7 = i7++; System.out.println("i7 =" + i7);//10 int i8 = 10; i8 = ++i8; System.out.println("i8 =" + i8);//11 //第4類問題: int i9 = 10; int i10 = i9++ + ++i9;//10+12 System.out.println("i9 =" + i9);//12 System.out.println("i10 =" + i10);//22 } public static void main(String[] args) { add(); } }
運行結果
i1 = 11 i2 = 11 i3 = 11 i4 = 10 i5 = 11 i6 = 11 i7 = 10 i8 = 11 i9 = 12 i10 = 22
三、字節碼指令
通過javap -v out目錄下的class文件名 在終端運行得到如下結果
public static void add(); descriptor: ()V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=10, args_size=0 0: bipush 10 2: istore_0 3: iinc 0, 1 6: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 9: iload_0 10: invokevirtual #5 // Method java/io/PrintStream.println:(I)V 13: bipush 10 15: istore_1 16: iinc 1, 1 19: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 22: iload_1 23: invokevirtual #5 // Method java/io/PrintStream.println:(I)V 26: bipush 10 28: istore_2 29: iload_2 30: iinc 2, 1 33: istore_3 34: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 37: iload_2 38: invokevirtual #5 // Method java/io/PrintStream.println:(I)V 41: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 44: iload_3 45: invokevirtual #5 // Method java/io/PrintStream.println:(I)V 48: bipush 10 50: istore 4 52: iinc 4, 1 55: iload 4 57: istore 5 59: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 62: iload 4 64: invokevirtual #5 // Method java/io/PrintStream.println:(I)V 67: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 70: iload 5 72: invokevirtual #5 // Method java/io/PrintStream.println:(I)V 75: bipush 10 77: istore 6 79: iload 6 81: iinc 6, 1 84: istore 6 86: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 89: iload 6 91: invokevirtual #5 // Method java/io/PrintStream.println:(I)V 94: bipush 10 96: istore 7 98: iinc 7, 1 101: iload 7 103: istore 7 105: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 108: iload 7 110: invokevirtual #5 // Method java/io/PrintStream.println:(I)V 113: bipush 10 115: istore 8 117: iload 8 119: iinc 8, 1 122: iinc 8, 1 125: iload 8 127: iadd 128: istore 9 130: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 133: iload 8 135: invokevirtual #5 // Method java/io/PrintStream.println:(I)V 138: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 141: iload 9 143: invokevirtual #5 // Method java/io/PrintStream.println:(I)V 146: return
四、字節碼解析
1. 第一類問題
//第1類問題: int i1 = 10; i1++; System.out.println("i1 =" + i1);//11 int i2 = 10; ++i2; System.out.println("i2 =" + i2);//11
對應字節碼指令為
0: bipush 10 2: istore_0 3: iinc 0, 1 6: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 9: iload_0 10: invokevirtual #5 // Method java/io/PrintStream.println:(I)V 13: bipush 10 15: istore_1 16: iinc 1, 1 19: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 22: iload_1 23: invokevirtual #5 // Method java/io/PrintStream.println:(I)V
先將i1的值為10入棧(bipush),然後將int類型的值從棧中存到局部變量表0的位置,然後執行iinc將0位置的值+1,然後將局部變量表0位置的數入棧執行輸出操作
所以i1的值為11
先將i2的值為10入棧(bipush),然後將int類型的值從棧中存到局部變量表1的位置,然後執行iinc將1位置的值+1,然後將局部變量表1位置的數入棧執行輸出操作
所以i2的值為11
由於沒有賦值操作,區別不大
2. 第二類問題
//第2類問題: int i3 = 10; int i4 = i3++; System.out.println("i3 =" + i3);//11 System.out.println("i4 =" + i4);//10 int i5 = 10; int i6 = ++i5; System.out.println("i5 =" + i5);//11 System.out.println("i6 =" + i6);//11
對應字節碼為
26: bipush 10 28: istore_2 29: iload_2 30: iinc 2, 1 33: istore_3 34: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 37: iload_2 38: invokevirtual #5 // Method java/io/PrintStream.println:(I)V 41: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 44: iload_3 45: invokevirtual #5 // Method java/io/PrintStream.println:(I)V 48: bipush 10 50: istore 4 52: iinc 4, 1 55: iload 4 57: istore 5 59: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 62: iload 4 64: invokevirtual #5 // Method java/io/PrintStream.println:(I)V 67: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 70: iload 5 72: invokevirtual #5 // Method java/io/PrintStream.println:(I)V
先將i3入棧存儲到局部變量表2的位置,然後將它入棧,執行iinc將2位置的值加一,i4存儲到局部表量表3的位置
所以i3是11,i4還是10
將i5入棧存儲到局部變量表4的位置,由於是++i所以先iinc將4位置的值加一,然後將局部變量表4的值入棧,執行賦值操作
所以i5、i6都是11
3. 第三類問題
//第3類問題: int i7 = 10; i7 = i7++; System.out.println("i7 =" + i7);//10 int i8 = 10; i8 = ++i8; System.out.println("i8 =" + i8);//11
對應字節碼
75: bipush 10 77: istore 6 79: iload 6 81: iinc 6, 1 84: istore 6 86: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 89: iload 6 91: invokevirtual #5 // Method java/io/PrintStream.println:(I)V 94: bipush 10 96: istore 7 98: iinc 7, 1 101: iload 7 103: istore 7 105: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 108: iload 7 110: invokevirtual #5 // Method java/io/PrintStream.println:(I)V
先將i7入棧,然後存到局部變量表6的位置,先把i6入棧,然後把6處的值加一,由於又將這個值存儲到局部變量表6處,所以產生覆蓋又把值變為10
所以i7為10
而++i不會產生覆蓋先執行加一然後再把值入棧,在賦值給局部變量表中
所以i8為11
4. 第四類問題
//第4類問題: int i9 = 10; int i10 = i9++ + ++i9;//10+12 System.out.println("i9 =" + i9);//12 System.out.println("i10 =" + i10);//22
對應字節碼為
113: bipush 10 115: istore 8 117: iload 8 119: iinc 8, 1 122: iinc 8, 1 125: iload 8 127: iadd 128: istore 9 130: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 133: iload 8 135: invokevirtual #5 // Method java/io/PrintStream.println:(I)V 138: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 141: iload 9 143: invokevirtual #5 // Method java/io/PrintStream.println:(I)V 146: return
先將i9=10入棧,然後存在局部變量表8的位置
int i10 = i9++ + ++i9;
先iload將8位置的i9入棧然後執行iinc將8處的i9加一,然後執行++i9,在將8處的i9加一
此時i9=10+1+1為12
然後將8位置的i9入棧,執行add將棧中的兩i9相加,得到的值存儲到局部變量表9的位置
所以i10=10+12(i9++後還是10,++i9後是12,因為執行瞭兩次iinc操作)
然後調用虛方法和靜態方法,在將9處的值入棧執行輸出語句
到此這篇關於淺談JVM 底層解析 i++和 ++i 區別的文章就介紹到這瞭,更多相關JVM 底層解析 i++和 ++i 區別內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Java 匯編JVM編寫jasmin程序的操作方法
- java中i = i++和i =++i的深入講解
- Java虛擬機運行時棧的棧幀
- 詳細圖解Java中字符串的初始化
- Java11 中基於嵌套關系的訪問控制優化問題