从字节码角度带你彻底理解i++与++i

先看一段简单的代码:

public static void main(String[] args) {
        int i = 10;
        int a = i++;
    }

下面图片是该代码的部分字节码指令: 其中stack就是操作数栈中的栈的最大深度,locals的值就是局部变量表的大小(从0开始算的);

 看完这个图中的字节码指令后:我们可以知道

  • 运算操作是在操作数栈中完成的,运算完成后会把这个运算的结果存储在局部变量表

  • 在进行i++ 和 ++i 操作是,唯一的区别就是,i++ 操作是先把局部变量表中的值取出来放到数栈中,然后再对局部变量表中的i 进行加一操作,运算操作是在数栈中完成的,比如i起始值为10,然后数栈中的i依然是10,但是局部变量表中的i就变成11了下一次运算来取i就是从局部变量表中取的,为11;

 ++ i 的分析:

 我们从这个字节码指令可以看到:10这个数入栈后,然后把10弹栈存储到局部变量表中的1号槽位,然后接着对局部变量表中的i进行自增操作,此时局部变量表中的i为11,然后再把局部变量表中的i加载到操作数栈中(此时加载到数栈中的i的值是11),所以在运算阶段使用的i就已经是11了;

练习题:

/**
 * @author LJM
 * @create 2022/6/23
 */
public class Demo1 {
    public static void main(String[] args) {
       int a = 10;
       int b = a++ + ++a + a--;
       System.out.println(a);
       System.out.println(b);
    }
}

结果:11 和 34

先简单分析一下:
    a++ :上面我们已经讲过这个的执行情况了,所以在执行 a++ 的时候,局部变量表中的a已经变成11了,但是此时数栈中的第一个a还是10(从局部变量表中加载到栈中,先进的会在栈底)
    然后执行加法运算,运算是在数栈中进行的,此时 ++a 是先对局部变量中的a进行加一,此时局部变量表中的a就是12了,也就是说 10 + 12 + a--   此时局部变量表中的a变成12,a--,先把a=12这个值入栈,然后再把局部变量表中的a变成11, 所以栈中的运算数据是  10+12+12  (此时局部变量表中的a为11) 

可以使用该指令查看class文件的字节码:javap -v Demo1.class

 例题的完全字节码文件:我们来一起读一下(里面全都写了注释,很好理解的^_^):

Classfile /E:/jvm_learn/out/production/jvm_learn/classLoad/Demo1.class
  Last modified 2022-6-23; size 572 bytes  //最后更新时间以及文件的大小
  MD5 checksum 8b9ac16f998833ced786c134b65ee1e2  //使用md5进行签名的矫正
  Compiled from "Demo1.java"     //编译的文件来源
public class classLoad.Demo1     //类的全路径名称
  minor version: 0               //jdk版本号, 52转换成10进制表示为8
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER   //类的访问修饰符
Constant pool:                   //常量池    这个常量池的右边给的注释是jvm查询常量池后最后要表示的结果,这样我们就不需要一个一个对着去查了,就可以大致知道结果了
   #1 = Methodref          #5.#22         // java/lang/Object."<init>":()V  表示的类的构造方法
   #2 = Fieldref           #23.#24        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Methodref          #25.#26        // java/io/PrintStream.println:(I)V
   #4 = Class              #27            // classLoad/Demo1 
   #5 = Class              #28            // java/lang/Object 
   #6 = Utf8               <init>        
   #7 = Utf8               ()V
   #8 = Utf8               Code
   #9 = Utf8               LineNumberTable
  #10 = Utf8               LocalVariableTable
  #11 = Utf8               this
  #12 = Utf8               LclassLoad/Demo1;
  #13 = Utf8               main
  #14 = Utf8               ([Ljava/lang/String;)V
  #15 = Utf8               args
  #16 = Utf8               [Ljava/lang/String;
  #17 = Utf8               a
  #18 = Utf8               I
  #19 = Utf8               b
  #20 = Utf8               SourceFile
  #21 = Utf8               Demo1.java
  #22 = NameAndType        #6:#7          // "<init>":()V
  #23 = Class              #29            // java/lang/System
  #24 = NameAndType        #30:#31        // out:Ljava/io/PrintStream;
  #25 = Class              #32            // java/io/PrintStream
  #26 = NameAndType        #33:#34        // println:(I)V
  #27 = Utf8               classLoad/Demo1
  #28 = Utf8               java/lang/Object
  #29 = Utf8               java/lang/System
  #30 = Utf8               out
  #31 = Utf8               Ljava/io/PrintStream;
  #32 = Utf8               java/io/PrintStream
  #33 = Utf8               println
  #34 = Utf8               (I)V
{
  public classLoad.Demo1();   //构造方法
    descriptor: ()V           //方法的参数信息,v 表示void
    flags: ACC_PUBLIC         //方法的访问修饰符
    Code:                     //具体的代码执行,code这里才是关键
      stack=1, locals=1, args_size=1  //stack的值表示操作数栈的最大深度,locals表示局部变量表的大小,args_size表示参数的长度
         0: aload_0             //开始执行,这个是执行的字节码指令单第一行,把局部变量的第0项args加载到操作数栈
         1: invokespecial #1    // 调用方法,去寻找常量池中的#1,结果为:Method java/lang/Object."<init>":()V  
         4: return              //停止这个方法的执行
      LineNumberTable:          //方法中的属性,对应着一个行号表
        line 7: 0               //前面的行号7表示的是源代码中的行号, 后面的0表示的是字节码中的行号
      LocalVariableTable:       //局部变量表
        Start  Length  Slot  Name   Signature  //分别表示字节码起始范围, length表示作用范围,slot表示的是局部变量表中的槽位号,name表示的是变量名,Signature表示的是局部变量参数的类型
            0       5     0  this   LclassLoad/Demo1;

  public static void main(java.lang.String[]);     //main方法
    descriptor: ([Ljava/lang/String;)V            //方法中的参数类型为string V 表示没有返回值
    flags: ACC_PUBLIC, ACC_STATIC                //main方法的访问修饰符,表示的是public static
    Code:                             //具体的代码执行,code这里才是关键
      stack=2, locals=3, args_size=1  //stack的值表示操作数栈的最大深度,locals表示局部变量表的大小,args_size表示参数的长度
         0: bipush        10          //bipush是把10这个值进行入栈操作
         2: istore_1  		          //把刚刚入栈道10存储到局部变量表的1号槽位
         3: iload_1			          //把局部变量表中1号槽位的数据加载到操作数栈中,此时操作数栈中的a是10 
         4: iinc          1, 1        //第一个1表示的是局部变量表中的1号槽位,第二个1表示的是自增数据的大小,所以这里是把1号槽位中的a进行加1操作,此时局部变量表中的a为11,注意此时操作数栈中的a还是10
         7: iinc          1, 1        //++a的字节指令,对变量表中的a进行加1,此时局部变量表中的a为12
        10: iload_1                   //把局部变量表中1号槽位的数据加载到操作数栈,此时操作数栈中有 10 和 12
        11: iadd                      //进行加法运算  10 + 12  
        12: iload_1                   //把局部变量表中1号槽位的数据加载到操作数栈 此时a还是12;   22  12
        13: iinc          1, -1       //对局部变量表中1号槽位的数据进行减少1操作  11
        16: iadd			          //进行第二个加法操作,虽然此时局部变量表中的a已经变成11了,但是操作数栈中的a还是12,在减1之前就把a=12加载到操作数栈中了
        17: istore_2                  //把运算的结果存储到局部变量表中的2号槽位
        18: getstatic     #2          // Field java/lang/System.out:Ljava/io/PrintStream;
        21: iload_1                   //把1号槽位中的数据加载到操作数栈中,此时a为11
        22: invokevirtual #3          // 执行打印方法 Method java/io/PrintStream.println:(I)V
        25: getstatic     #2          // Field java/lang/System.out:Ljava/io/PrintStream;
        28: iload_2                   //把局部变量表中的2号槽位中的数据加载到操作数栈中
        29: invokevirtual #3          // 执行打印方法 Method java/io/PrintStream.println:(I)V
        32: return                    //main方法执行完毕,退出
      LineNumberTable:
        line 9: 0
        line 10: 3
        line 11: 18
        line 12: 25
        line 13: 32 
      LocalVariableTable:     //局部变量表的数据
        Start  Length  Slot  Name   Signature
            0      33     0  args   [Ljava/lang/String;
            3      30     1     a   I
           18      15     2     b   I
}

猜你喜欢

转载自blog.csdn.net/weixin_53142722/article/details/125434390