从字节码角度看待i++和++i

首先从一个示例代码开始:

public class test {
    
    

    public static void main(String[] args) {
    
    
        int i = 0;
        int j = 0;
        j = (i++) + (i++) + (i++);
        i = (++j) + (++j) + (++j);
        System.out.println("i = "+i+",j = "+j);
    }

}

该段代码的执行结果是 i = 15,j = 6。

通过使用jclasslib插件,查看编译后的字节码。

在查看字节码中方法执行过程前,首先了解两个概念,局部变量表和操作数栈。

局部变量表:用于存储方法执行过程使用到的形参变量+局部变量的一个表格。如果有this对象,先存储this,再存储形参,然后存储方法内部的局部变量。注意:局部变量表的长度并不等于以上所有这些值的数量,如果某些代码块内部的局部变量的生命周期结束,则在局部变量表中可以被取代。

操作数栈:用于执行基本的加减乘除操作。

方法执行流程
0 iconst_0 // 从常量池中取0,放到操作数栈中
1 istore_1 // 从操作数栈中弹出栈顶的0,存储到局部变量表索引为1的位置,即i=0
2 iconst_0 // 从常量池中取0,放到操作数栈中
3 istore_2 // 从操作数栈中弹出栈顶的0,存储到局部变量表索引为2的位置,即j=0
4 iload_1 // 将局部变量索引位置为1的值,加载到操作数栈中,即栈顶值为0
5 iinc 1 by 1 //将局部变量表中的第一个位置的变量增加1,此时i=1
8 iload_1 //将局部变量表索引位置为1的值,加载到操作数栈中,即栈顶值为1
9 iinc 1 by 1 //将局部变量表中的第一个位置的变量增加1,此时i=2
12 iadd // 对操作数栈中栈顶2个元素相加,再入栈,即栈顶值为0+1=1
13 iload_1 // 将局部变量表索引位置为1的值,加载到操作数栈中,即栈顶值为2
14 iinc 1 by 1 // 将局部变量表中的第一个位置的变量增加1,此时i=3
17 iadd // 对操作数栈中栈顶2个元素相加,再入栈,即栈顶值为1+2=3
18 istore_2 //将操作数栈顶的值3,存储到局部变量表索引为2的位置,即j=3
19 iinc 2 by 1 //将局部变量表索引位置为2的值增加1,此时j=3+1=4
22 iload_2 // 将局部变量表索引位置为2的值,加载到操作数栈中,即栈顶值为4
23 iinc 2 by 1 //将局部变量表索引位置为2的值增加1,此时j=4+1=5
26 iload_2 // 将局部变量表索引位置为2的值,加载到操作数栈中,即栈顶值为5
27 iadd // 对操作数栈中栈顶2个元素相加,再入栈,即栈顶值为4+5=9
28 iinc 2 by 1//将局部变量表索引位置为2的值增加1,此时j=5+1=6
31 iload_2 // 将局部变量表索引位置为2的值,加载到操作数栈中,即栈顶值为6
32 iadd // 对操作数栈中栈顶2个元素相加,再入栈,即栈顶值为9+6=15
33 istore_1 // 将操作数栈顶值15,存储到索引为1的位置,即i=15
34 getstatic #2 <java/lang/System.out>
37 iload_1
38 iload_2
39 invokedynamic #3 <makeConcatWithConstants, BootstrapMethods #0>
44 invokevirtual #4 <java/io/PrintStream.println>
47 return

总结
①执行i++,会先将局部变量表中的值加载到操作数栈中,然后再对局部变量表中的值自增1;
②执行++i,会先将局部变量表中的值自增1,再加载到操作数栈中。

猜你喜欢

转载自blog.csdn.net/Longstar_L/article/details/108583343