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

前言

一个关于i++和++i的原理详解,对字节码不太熟悉的能够先了解下字节码相关知识,由于从字节码角度更能理解二者的原理。bash

案例

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可能你们的第一反应的理解是:前者先运算再自增赋值,后者先自增赋值在运算。那么上面那个例子为何会得出这个结果,下面查看其字节码,输出的我就忽略不展现了。post

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类型局部变量中)。能够看到这个指令和下一条指令之间左边的行号是少了两行的,那是由于这个指令显示的是一条,可是干了三件事,读取该值,自增长一,将自增后的值保存回局部变量。spa

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

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

从上面的理解中能够看到俗称的:先运算在赋值和先赋值在运算,其原理就在于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;
复制代码

上面我不列答案,你们能够本身先猜想是多少,而后在看看本身分析的对不对。get

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

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

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=1class

例三:例三和例一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相关知识,结果字节码对这一原理有了更深的了解,起码在脑子里这个知识点的保质期会久一点。

相关文章
相关标签/搜索