i++是先算后加,++i是先加后算,这个在我们学习循环就已经知道的东西,到底蕴含了什么呢?
我们来看一段代码:
int i1=10;int i2=i1++;int i3=10;int i4=++i3;int i5 = 10;i5 = i5++;int i6=10;i6 = ++i6;int i7=10;int i8 = i7++ + ++i7;i7=10;int i9 = ++i7 + i7++;
稍稍有点眼花缭乱,没关系,很简单,认真看看就能看懂,那么他们输出什么呢?
i1=:11i2=:10i3=:11i4=:11 //直到i4大家都应该懂i5=:10 // i5以后可能有点难度i6=:11i7=:12i8=:22i9=:22
要解释i5,i6,i8,i9,需要从java的字节码角度去探索。
>> 栈结构
我们知道,java虚拟机中对象一般在堆中,而常量运算一般在栈中(这里是简略描述,真实比这个复杂)。
上面的所有运算都是在栈中完成的,栈的基本单位是栈帧,而一个方法所占用的空间就是一个栈帧,上面只有一个main方法,所以占用一个栈帧。
每个栈帧对应一个方法,包含五个内容,但是由于我们这道题只涉及局部变量表和操作数栈,我们只看这两个:
>> 字节码
现在我们知道,我们的代码将会在栈帧中的局部变量表和操作数栈中发生事情,那如何发生?,就是通过字节码指令运行,字节码由java代码编译而来,在idea中安装jclasslib插件即可查看。
先了解以下4行代码:
0 bipush 10 //将10压入操作数栈2 istore_1 //将栈顶的数保存到局部变量表的1位置处3 iload_1 //将局部变量表1位置处的值取出4 iinc 1 by 1 //将局部变量表中1位置处的值自增1
好,准备工作做完了,现在开始分析。
前6个:
int i1=10;int i2=i1++;int i3=10;int i4=++i3;int i5 = 10;i5 = i5++;int i6=10;i6 = ++i6;
字节码如下,字节码前面的数字表示的是地址,不是代码行数。
0 bipush 10 //压入10到栈中2 istore_1 //将栈顶的10保存到局部变量表的1处(简称i1)3 iload_1 // 复制i1并取出4 iinc 1 by 1 //自增位于局部变量表中的i1 7 istore_2 //将取出的i1放到局部变量表的2处(简称i2)//注意,这里是先取出再自增,取出的i1还是原值,所以i2依旧为108 bipush 1010 istore_311 iinc 3 by 1 //这里是先自增再取出14 iload_315 istore 4 //i4为1017 bipush 1019 istore 5 21 iload 5 //i5先取出23 iinc 5 by 1 //局部变量表处再自增26 istore 5 //又将取出的值存回去,所以先取出的10覆盖了自增的1128 bipush 1030 istore 632 iinc 6 by 1 //i6先自增再取出35 iload 637 istore 6 // 所以取出的是11,覆盖的也是11
最后两个:
int i7=10;int i8 = i7++ + ++i7;i7=10;int i9 = ++i7 + i7++;
首先我们明确,自增操作符++的优先级高于运算符+号,而且是从左到右运算。
来看字节码:
39 bipush 10 41 istore 7 43 iload 7 45 iinc 7 by 1 48 iinc 7 by 1 51 iload 7 53 iadd 54 istore 8 56 iinc 7 by 1 59 iload 7 61 iload 7 63 iinc 7 by 1 66 iadd 67 istore 9
这就是i7++与++i7的字节码混合,如图
i7++中局部变量表处自增时,由于没有执行=号,所以没有存回去,这个时候,轮到了++i7,++i7执行了自增后,实际上局部变量表中的i7此时已经是12了。
所以23行 iload 7的值为10,但是26行iload 7的值为12,综合下来,还是22。
至于i9,一样的道理,可以自己探索哦~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!