一:a++
public static void main(String[] args) {
int a = 0;
int b = 0;
b = a++;
System.out.println("a="+a+";b="+b);
}
//执行结果:a=1;b=0
问题:
针对以上代码,在第一眼看到时,我的结论是:a=1;b=1,但实际执行结果却是a=1;b=0。于是就想弄清楚什么原因导致的结果不一致。
代码解读:
在解释这个问题之前,我们需要知道一些字节码指令的使用
接下来,我们看下这段代码的一个执行过程:
整个执行过程解释:
第一步:iconst_0,(0表示常量的值)其含义是将int类型常量压入操作数栈
第二步:istore_1,其含义是将操作数栈顶的值(即:0)弹出,并赋值给局部变量1
至此,完成了 int a = 0 的执行
第三步&第四步: 与第一步,第二步一样,处理int b = 0;
第五步:iload_1,其含义是将局部变量1的值压入操作数栈中
第六步:iinc 1 by 1,重点:表示将局部变量1的值加1
第七步:istore_2,与步骤二一样,表示将操作数栈顶的值弹出,并赋值给局部变量2
后续是对System.out.println("a="+a+";b="+b);这个输出的执行,这里就不做解释了。
最终我们得到结果是:a=1;b=0
原因分析:
通过这个代码执行过程的解释,可以看出,在第五步时,将局部变量表中变量1的值放入操作数栈,此时操作数栈顶的值为0,随后第六步将局部变量1的值加1,局部变量1的值变为了1,最后在第七步将操作数栈顶的值0赋值给了局部变量2,因此最终得到的结果是局部变量1的值为1,局部变量2的值为0。内存状态如下图:
结论:
至此,已经明白了,其实是因为在执行a++时先将a赋值给了b,然后才执行了后边的++操作,并且是在局部变量表中进行的++操作。
二:++a
public static void main(String[] args) {
int a = 0;
int b = 0;
b = ++a;
System.out.println("a="+a+";b="+b);
}
//执行结果:a=1;b=1
代码解读:
整个执行过程解释:
第一步:iconst_0,(0表示常量的值)其含义是将int类型常量压入操作数栈
第二步:istore_1,其含义是将操作数栈顶的值(即:0)弹出,并赋值给局部变量1
第三步&第四步: 与第一步,第二步一样,处理int b = 0;
前四步与a++一样,都是执行变量的定义操作。
第五步:iinc 1 by 1,重点:表示将局部变量1的值加1
第六步: iload_1,其含义是将局部变量1的值压入操作数栈中(此时局部变量1的值为1)
第七步:istore_2,与步骤二一样,表示将操作数栈顶的值弹出,并赋值给局部变量2
后续是对System.out.println("a="+a+";b="+b);这个输出的执行,这里就不做解释了。
最终我们得到结果是:a=1;b=1
原因分析:
通过这个代码执行过程的解释,可以看出,在第五步时,开始对局部变量1进行自增的操作,随后在第六步才将局部变量1的值压入操作数栈顶,此时操作数栈顶的值为1,最后在第七步将操作数栈顶的值1赋值给了局部变量2,因此最终得到的结果是局部变量1的值为1,局部变量2的值为1。内存状态如下图:
结论:
至此,已经明白了,相比a++而言,++a是先进行了数值运算,然后将其值赋值给了b,而非a++的先进行“赋值”。
三:a+1:
问题:
在这里我有个疑问,为什么在执行a++或者++a时,不是在操作数栈里边进行的计算,而是直接在局部变量表中进行的处理?那我们执行a+1或者1+a时是怎样的一个过程呢?
public static void main(String[] args) {
int a = 0;
int b = 0;
b = a+1;
System.out.println("a="+a+";b="+b);
}
//执行结果:a=0;b=1
代码解读:
整个执行过程解释:
第一步:iconst_0,(0表示常量的值)其含义是将int类型常量压入操作数栈
第二步:istore_1,其含义是将操作数栈顶的值(即:0)弹出,并赋值给局部变量1
第三步&第四步: 与第一步,第二步一样,处理int b = 0;
前四步与前边一样,都是执行变量的定义操作。
第五步:iload_1,其含义是将局部变量1的值压入操作数栈中(此时局部变量1的值为0)
第六步:iconst_1,表示将常量值1(也就是我们代码中a+1的那个1)压入操作数栈
第七步:iadd,表示将操作数栈中的数据进行求和处理,并且结果会被压入操作数栈顶
第八步:istore_2,表示将操作数栈顶的值(即上一步求得的和)弹出,并赋值给局部变量2
后续是对System.out.println("a="+a+";b="+b);这个输出的执行,这里就不做解释了。
最终我们得到结果是:a=0;b=1
原因分析:
在使用a+1/1+a时,可以发现这次的加1是先将局部变量值和常量值都压入操作数栈经由指令“iadd”处理,并将最终结果压入操作数栈顶。
为什么执行过程不一样?
这就要看a++/++a对应的字节码指令“iinc”与a+1/1+a的字节码指令“iadd”的区别了,如下表(谷歌翻译):
指令 | 操作 | 格式 | 对操作数栈影响 | 描述 |
iinc | 按常量递增局部变量 | iinc 索引 常量 | 无 | 索引是一个无符号字节,它必须是当前帧的局部变量数组的索引( 第2.6 节)。const是立即有符号字节。index处的局部变量必须包含一个int . 值const首先符号扩展为 a int ,然后index处的局部变量 增加该数量 |
iadd | add int | iadd | ..., value1, value2 → ..., result | value1和value2 都必须是 type 结果是真实数学结果的低 32 位,采用足够宽的二进制补码格式,表示为 type 的值 |
对比可发现,iinc指令是直接对变量进行递增,对操作数栈是没有任何操作的,而iadd是需要对在操作数栈里边进行计算的,目前就先了解到这里吧。至于为什么两个指令的处理逻辑不一样,感兴趣的小伙伴欢迎补充。
最后:
以上就是我对a++/++a的简单理解,如果有不正确的地方欢迎指点,感谢!