我們都知道 i++ 與 ++i 都是自增操作。大多數(shù)也知道兩種 "先加" 和 "后加的區(qū)別"颂龙。例如下方代碼:
public class Hello{
public static void main(String[] args) {
int i = 0;
System.out.println(i++);
int j = 0;
System.out.println(++j);
}
}
打印出
i = 0
j = 1
可以看到 j
完成了自增,并且打印出 1
∩鹉悖可是 i
確依據(jù)打印出 0
。那么 i
是不是沒有完成自增呢昭躺?改動(dòng)一下上方代碼:
public class Hello{
public static void main(String[] args) {
int i = 0;
System.out.println("i = " + i++);
int j = 0;
System.out.println("j = " + ++j);
System.out.println("i = " + i);
}
}
打印出
i = 0
j = 1
i = 1
可以看到第二次對(duì) i
進(jìn)行讀取的時(shí)候就打印出了自增后的值忌锯。說明 i++
的值晚一步來到了可這是為什么呢?
從 JVM 角度分析 i++
和 ++i
的問題领炫。
我們先簡(jiǎn)化一下上方的代碼偶垮,只用于研究自增問題簡(jiǎn)化代碼如下:
public class Hello{
public static void main(String[] args) {
int i = 0;
System.out.println(i++);
System.out.println(++i);
}
}
我們?cè)?CMD
控制臺(tái)中使用 javac Hello.java
編譯上方代碼。
再使用 javap -c Hello.class
查看編譯過后字節(jié)碼帝洪。
控制臺(tái)打印出:
Compiled from "Hello.java"
public class Hello {
public Hello();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
5: iload_1
6: iinc 1, 1
9: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
12: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
15: iinc 1, 1
18: iload_1
19: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
22: return
}
我們需要觀察 main
方法總的字節(jié)碼就行了似舵。再此之前我想先解釋下 "局部變量表" 和 "操作數(shù)棧" 。
局部變量表:顧名思義葱峡,存放方法中局部變量的地方砚哗。是一個(gè)數(shù)組。
操作數(shù)棧:大多數(shù)的對(duì)數(shù)棧的操作都要先從局部變量表中拿出值放在數(shù)棧中砰奕,在從數(shù)棧中取值進(jìn)行操作蛛芥。是一個(gè)先進(jìn)后出的隊(duì)列。
好了军援,我們?cè)趤斫忉?main
方法中的字節(jié)碼仅淑。
public static void main(java.lang.String[]);
Code:
// 將 i 的值從常量表中取出,放到操作數(shù)棧中胸哥。
0: iconst_0
// 從數(shù)棧中取出值放到局部變量表中的第 1 位涯竟。第 0 位已經(jīng)被 `java.lang.String[]`占用
1: istore_1
// 簡(jiǎn)單理解為初始化 System.out.println()
2: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
// 以下是 i++ 操作
// 從局部變量中取出第一個(gè)值,放入數(shù)棧中
5: iload_1
// 局部變量表中第一位值自增 1
6: iinc 1, 1
// 從操作數(shù)棧中取出值烘嘱,并打印出來昆禽。
9: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
// 以下是 ++i 的操作,你可以對(duì)比上方解釋自己嘗試翻譯下蝇庭。
12: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
15: iinc 1, 1
18: iload_1
19: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
22: return
}