a=a++结果为什么没有加1,而a=++a结果加了1?

一: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处的局部变量 增加该数量
iaddadd intiadd

..., value1value2 →

..., result

value1value2 都必须是 type int。这些值从操作数堆栈中弹出。int 结果value1 + value2。_ 结果被压操作数堆栈。

结果是真实数学结果的低 32 位,采用足够宽的二进制补码格式,表示为 type 的值int。如果发生溢出,则结果的符号可能与两个值的数学和的符号不同。

尽管可能发生溢出,但执行iadd 指令永远不会引发运行时异常。

对比可发现,iinc指令是直接对变量进行递增,对操作数栈是没有任何操作的,而iadd是需要对在操作数栈里边进行计算的,目前就先了解到这里吧。至于为什么两个指令的处理逻辑不一样,感兴趣的小伙伴欢迎补充。

最后:

以上就是我对a++/++a的简单理解,如果有不正确的地方欢迎指点,感谢!

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值