在jvm中,涉及到变量运算时,会发生压栈弹栈等动作,但如果仅仅是自增和自减,那么jvm会直接在本地变量中实现。
public class JavaTest{
public static void main(String[] args){
int a=1,b=1,c=1,d=1;
a++;
++b;
c = c++;
d=++d;
System.out.println(a+"..."+b+"..."+c+"..."+d);
}
}
这段代码很简单,运行结果为2,2,1,2。
对于这样的结果大家可能都觉得是对的,因为c=c++的时候会先执行赋值操作,再进行自增,所以c等于1。但是c进行赋值之后,自增的操作也是c本身,为什么c这个变量不等于自增后的数值呢?
我们使用javap来将这段代码进行解析,结果如下:
F:\>javap -c JavaTest
Compiled from "JavaTest.java"
public class JavaTest {
public JavaTest();
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_1
1: istore_1
2: iconst_1
3: istore_2
4: iconst_1
5: istore_3
6: iconst_1
7: istore 4
9: iinc 1, 1
12: iinc 2, 1
15: iload_3
16: iinc 3, 1
19: istore_3
20: iinc 4, 1
23: iload 4
25: istore 4
27: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
30: new #3 // class java/lang/StringBuilder
33: dup
34: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
37: iload_1
38: invokevirtual #5 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
41: ldc #6 // String ...
43: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
46: iload_2
47: invokevirtual #5 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
50: ldc #6 // String ...
52: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
55: iload_3
56: invokevirtual #5 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
59: ldc #6 // String ...
61: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
64: iload 4
66: invokevirtual #5 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
69: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
72: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
75: return
}
下面我们来分析一下,前面0-7可以看出是定义了4个变量,分别进行压栈和弹栈操作,将变量最终放入到本地变量中。9这个操作就对应于代码中的a++,可以看出没有进行压栈和弹栈,只是执行了iinc,也就是自增操作,所以这个时候,本地变量中a就等于2。12对应于代码中的++b,情况和9类似。c=c++对应于15-19,从这里可以看出,jvm先执行了压栈,将c压入栈顶,然后执行自增操作,所以这个时候,栈顶的c为1,本地变量c为2,但是在这个时候,19又将栈顶的c弹栈并赋值给本地变量c了,所以本地变量c最终为1。d=++d和上面原理是一致的。
通过对代码的汇编码的分析,我们可以知道c=c++为什么等于自增前的c了,其实不是因为先进行了赋值,自增后的数据没有再次赋值,而是由于自增后被原数据给覆盖了。