int i=0;
i = ++i + i++ + i++ + i++
一、从java定义的语法层面分析
++i 语义为先自增再赋值,所有此时的 i 为相加后的值
i++ 语义为先读取再自增,所以此时的 i 为相加前的值
根据这两个语义进行操作如下:
因为每一个自增操作的变量 i 都是前一个累加过的值,而最后的i++ 此时的自增的值没有后续操作去取,因此自增后的值不会参与计算,所以最后i=1+1+2+3最终结果为7.
二,从编译的字节码分析
i = ++i + i++ + i++ + i++
分析前先把相关的字节码指令解释下:
iconst_0把int类型的常量0加载到栈帧的操作数栈中
istore_1把操作数栈栈顶中int类型的变量值存储到栈帧的局部变量表索引为1的位置。
iinc 1, 1 这个函数有些特殊,它直接把局部变量表的值加1,第一个1是指向局部变量表中索引的位置,第二个1表示给找到的值 加1。
iload_1 把局部变量表中的索引为1的值加载到操作数栈栈顶。
iadd 把栈顶int类型的两个数相加
介绍完,看生成的字节码如下:
Code:
0: iconst_0 //加载int类型的常量值为0,到操作数栈栈顶也就是定义的 int i=0
1: istore_1//吧栈顶的数据存储到局部变量表索引为1的位置
2: iinc 1, 1 //把局部变量表索引为1的值自增1,此时局部变量表中的i=0,加1后等于1
5: iload_1 //把局部变量表索引为1的值加载到操作数栈 栈顶 也就是把1复制到栈顶
6: iload_1//再次把局部变量表索引为1的值加载到操作数栈 栈顶 也就是把1复制到栈顶
7: iinc 1, 1 //此时把局部变量表中索引为1的数加1,也就是把1+1=2,此时的局部变量表中的数为2
10: iadd //把栈顶的两个数相加,此时栈顶的数时两个1,在5和6两步加载的,所以此时相加等于1+1=2,放到栈顶
11: iload_1//把局部变量表下标为1的数值加载到栈顶,此时局部变量表中的值为2.
12: iinc 1, 1//再次把局部变量表中的下标为1的数值加1,也就是2+1=3
15: iadd //把操作数栈中的两个数据相加,此时栈顶数据为10和11两个操作也就是2+2=4,放到栈顶
16: iload_1 //把局部变量表中下标为1的数值复制到栈顶,此时局部变量表中的数据为3,在12步的时候操作的
17: iinc 1, 1//把局部变量表中的数据加1,也就是3+1=4
20: iadd //把栈顶的两个数据相加,此时栈顶的数据为15和16步时放的值,4+3=7,放到栈顶
21: istore_1//把栈顶数据存储到局部变量索引为1的位置,保存后,此时局部变量表中的数值为7
22: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
25: iload_1 //把局部变量表中下标为1的数据加载到栈顶,此时值为7
26: invokevirtual #3 // Method java/io/PrintStream.println:(I)V 调用函数,打印出栈顶数据为7
29: return
此时会有疑问,我们在分析自增表达式时,不知道虚拟机生成字节码的顺序,所以就不会知道生成的结果,因此咱们对照着生成的字节码,来分析:
++i + i++ + i++ + i++
当java遇到++i的时候会先执行iinc再执行iload
当java遇到i++的时候会先执行iload然后再执行iinc
因此++i + i++ + i++ + i++的顺序为:
1、iinc //++
2、iload //i
3、iload //i
4、iinc //++
5、iadd //+ 此处的+为两个变量的相加不是自增的+
6、iload//i
7、iinc//++
8、iadd/+ 此处的+为两个变量的相加不是自增的+
9、iload/i
10、iinc//++
11、iadd//+ 此处的+为两个变量的相加不是自增的+
对比发现,这个顺序跟jvm生成的字节码顺序一致,因此,我们完全可以通过这样的分析来判断这种自增相加问题的结果。
如有疑问欢迎留言,共同讨论,一起进步