美文网首页
JVM笔记:i++ 和++i的字节码原理详解

JVM笔记:i++ 和++i的字节码原理详解

作者: BigX | 来源:发表于2019-10-31 14:59 被阅读0次

    前言

    一个关于i++和++i的原理详解,对字节码不太熟悉的可以先了解下字节码相关知识,因为从字节码角度更能理解两者的原理。

    案例

      int i = 0, j = 0;
            i = i++;
            j =++ j;
            System.out.println("args = [" + i + "]");
            System.out.println("args = [" + j + "]");
    输出结果:
    args = [0]
    args = [1]
    

    对于i++,++i可能大家的第一反应的理解是:前者先运算再自增赋值,后者先自增赋值在运算。那么上面那个例子为什么会得出这个结果,下面查看其字节码,输出的我就忽略不展示了。

             0: iconst_0
             1: istore_1
             2: iconst_0
             3: istore_2
             4: iload_1
             5: iinc          1, 1
             8: istore_1
             9: iinc          2, 1
            12: iload_2
            13: istore_2
    

    0 ~ 3行很容易理解,在局部变量保存i和j 分别设值为0。注意看4 ~ 8和9 ~ 13,他们分别代表的是i=i++j=++j。他们之间的差别就在于load指令一个在自增指令`iinc``前,一个在后

    这里说一下iinc指令,它是int类型的局部变量自增指令(将后者数据加到前者下标的int类型局部变量中)。可以看到这个指令和下一条指令之间左边的行号是少了两行的,那是因为这个指令显示的是一条,但是干了三件事,读取该值,自增加一,将自增后的值保存回局部变量。

    下面开始分析,执行i=i++操作时(4 ~ 8),首先iload_1使局部变量i(0)入栈,然后执行iinc注意,执行完iinc后局部变量表中的i是已经变成1了,iinc并不是吃干饭的,但是前一条指令入栈的值没变,还是0,然后紧接着对i做等号赋值操作的istore_1指令将栈顶的0又保存回了局部变量1(i)中,所以局部变量表中的值变化是:0 ~ 1 ~ 0;

    不同的是,执行j=j++操作时(9 ~ 13),先自增后局部变量表中j的值已经变成了1,然后执行=号赋值操作,将++j这一整个iinc后的结果也就是局部变量表2中的值入栈(iload_2),然后赋值给j(istore_2)。

    从上面的理解中可以看到俗称的:先运算在赋值和先赋值在运算,其原理就在于iinc指令是否先于局部变量的入栈赋值操作,下面再给几个拓展例子看看。

    例一:
           int i = 0, j = 0;
            i=i++ + i++;
            j= ++j + j++; 
    例二:
           int i = 0, j = 0;
            j=i++ + i++;
    
    例三:
            int i = 0, j = 0;
            j=i++ + ++i;
    
    例四:
           int i = 0, j = 0;
            j=i++ + ++j;
    

    上面我不列答案,大家可以自己先猜测是多少,然后在看看自己分析的对不对。

    虚假的分割线-------------------------------------------------------------------

    例一:第一个自增和前面的步骤一样,入栈i然后自增iinc,但是第二个自增会重新把i从局部变量表入栈,此时i经过前面自增已经为1了,然后第二个自增后再赋值,结果就是i=0+1且此时局部变量表中的i值为2(这是等于号赋值前的值,赋值后又重新保存变为1了)。
    j的运算我就简要说一下了,和前面类似:先iinc自增运算(差异),然后j(1)入栈,然后出栈保存,再入栈j(1)入栈,自增运算,然后相加。结果是j=1+1,且此时局部变量表中的j为2,下面是字节码:

            4: iload_1
             5: iinc          1, 1
             8: iload_1
             9: iinc          1, 1
            12: iadd
            13: istore_1
            14: iinc          2, 1
            17: iload_2
            18: iload_2
            19: iinc          2, 1
            22: iadd
            23: istore_2
    

    例二:例二和例一不同的是最后的赋值对象从自身变成了其他人,所以这里和例一i运算解析是一模一样的,只不过是最后括号内那句话中的赋值对象变成了j,所以此时i=2,j=1

    例三:例三和例一j运算解析也是类似的,同样改变的是最后的赋值对象`,所以此时i=2,j=2

    例四:例四虽然有点小不同,但是其原理还是一样的,先入栈i再自增运算i++,此时栈中的i为,0,变量表中的i为1;然后自增运算++j,在入栈j,相加得出结果再保存回变量表,所以结果就是j=0+1,这里得出的j=1是相加运算得出的j=1,并不是自增指令得出的j=1,比如此时i为4的话,结果就是i=5,j=5

             4: iload_1
             5: iinc          1, 1
             8: iinc          2, 1
            11: iload_2
            12: iadd
            13: istore_2
    

    总结

    其实以前也看过i++和++i区别的文章,但是看过不就又忘了,最近在学习JVM相关知识,结果字节码对这一原理有了更深的了解,起码在脑子里这个知识点的保质期会久一点。

    相关文章

      网友评论

          本文标题:JVM笔记:i++ 和++i的字节码原理详解

          本文链接:https://www.haomeiwen.com/subject/sggzvctx.html