问题
我们都知道:
- ++a,是先增后用
- a++,是先用后增
那么做一些下面这道题。
public static void main(String argc[]){
int a=0;
a=a++;
System.out.println(a);
}
由于a++是先用后增,于是a的值0先用,赋给a为0,然后a后增,此时a的值为1。所以结果就为1了。
相信很多人都会得出1这个结果,可结果是:0
在开始解析之前,我们先说一下为什么。
- ++a,先增后用,增是直接操作占局部变量表的,后用是将变量a的值放入操作数栈中。对应与
iinc
指令。 - a++,先用后增。先用是把a的值放到操作数栈中,后增是将局部变量表中a的值加一。
解析
a++
public static void main(String argc[]){
int a=0;
a=a++;
System.out.println(a);
}
使用javap -verbose
反汇编的结果。
0: iconst_0
1: istore_1
2: iload_1 // 先用
3: iinc 1, 1 // 后加
6: istore_1 //赋值算符=
7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
10: iload_1
11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
14: return
每一个函数的调用都伴随这一个栈帧的入栈与出栈。栈帧中有操作数栈、局部变量表等结构。栈帧结构可以参考虚拟机字节码执行引擎——运行时栈帧结构
- iconst_0。将一个常量0添加到操作数栈中。
- istore_1。将一个操作数栈顶数
0
拿出来放到局部变量表第1个slot槽中。
- iload_1。将局部变量表中槽1的局部变量的值
0
放到操作数栈中。
- iinc 1, 1。将局部变量表中槽1的值加1,变量a的值为
1
。对应的是a++
指令。
- istore_1。将操作数栈顶元素
0
拿出来放到局部变量表中槽1位置,意思是a的值1
被覆盖了。其实这条指令对应与a=a++中的=
++a
++a代码。
class A{
public static void main(String argc[]){
int a=0;
a=++a;
System.out.println(a);//1
}
}
使用javap -verbose
反汇编的结果。
0: iconst_0
1: istore_1
2: iinc 1, 1 // 先加
5: iload_1 // 后用
6: istore_1 // 算符=
7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
10: iload_1
11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
14: return
例子
public static void main(String argc[]){
int j=0;
for(int i=0;i<100;++i){
j=j++;
}
System.out.println(j);
}
这个例子的值为0
总结
++a。先增有用。增是直接操作局部变量表中的a,让a的值加1(iinc操作的不是操作数栈,而是局部变量表)。用是将局部变量表中的a的值放入操作数栈定。
a++。先用后增。用是将局部变量表中a的值放入操作数栈顶,增是将局部变量表中a的值加一。