i++与++i在JVM层面上的分析

我们都知道,i++是先取值后自增,++i是先自增后取值,但是项目这个题目你真的会吗?

下面是看某培训机构分析的视频中一道面试题,请问i,j,k的值一次为多少?

public class Increment {

	public static void main(String[] args) {
		int i=1;
		i=i++;
		int j=i++;
		int k=i+ ++i *i++;
		System.out.println(i);
		System.out.println(j);
		System.out.println(k);
	}
}

//答案是4   1   11(我和我的小伙伴都惊呆了,自己的答案一个都不对啊.......)

下面是自己再看完视频以及查阅资料后的相关整理,要从JVM层面去分析需要先了解一下相应的JVM知识

JVM相关知识
  • JVM(HotSpot)的运行时数据区可以分为程序计数器、Java虚拟机栈+本地方法栈(HotSpot把这两个合二为一,这个是我们这里的重点)、Java堆、方法区、运行时常量池。直接内存等。
  • JVM是基于栈(这里就是指Java虚拟机栈)的执行引擎,栈中存在许多栈帧(一个方法对应一个栈帧,一个栈帧的入栈到出栈对应一个方法的执行到结束)
  • 栈帧中存储了方法的局部变量表、操作数栈、 动态连接、方法返回地址
  • 局部变量表中可以简单的理解问数组,其中存储了方法中定义的局部变量,如果是基本数据类型变量,则变量中存放的就是值,如果是引用数据类型变量,其中存放的是值在堆中的地址
  • 操作数栈是典型的栈结构(后进先出),采用压栈与出栈操作,主要用于辅助指令的执行
  • 自增 自减操作都是直接修改变量的值,不需要经过操作数栈,操作数栈中仅仅保存临时结果
  • 字节码指令不管执行顺序如何,都是=右边从左往右加载进操作数栈中
分析开始

首先使用javap -c Increment .class得到程序的反编译文件

Compiled from "Increment.java"
public class com.zkq.test.Increment {
  public com.zkq.test.Increment();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."":()V
       4: return

//这里开始是main方法的字节码文件
  public static void main(java.lang.String[]);
    Code:
    
    /**
      *  编号0-1是int i=1的字节码指令
      *
      */
        //把int型变量1推送到操作数栈的栈顶
       0: iconst_1
       //把栈顶int型数值(1)存入局部变量表中的第2个本地变量(第一个本地变量用于保存this)        
       1: istore_1



    /**
      *  编号2 3 6是i=i++的字节码指令,从执行上可以看出来i的值是1
      *
      */
       //把局部变量表中的第二个本地变量(int 类型的1)推送到栈顶
       2: iload_1
       //对局部变量表中i的值进行+1操作,i的值变为2(也就是自增操作)
       3: iinc          1, 1
       //把栈顶int型数值(1)存入局部变量表中的第2个本地变量(也就是i的位置)        
       6: istore_1



    /**
      *  编号7 8 12是int j=i++;的字节码指令,这系列指令与i++的指令很类似
      *
      */
      //把局部变量表中的第二个本地变量(int 类型的1)推送到栈顶
       7: iload_1
       //对局部变量表中i的值进行+1操作,i的值变为2(也就是自增操作)
       8: iinc          1, 1
      //把栈顶int型数值(1)存入局部变量表中的第3个本地变量(也就是j的位置),经过这步,j的值为1,i的值变为2
      11: istore_2



    /**
      *  编号7 8是int k=i+ ++i *i++;的字节码指令,虽然=右边的先执行*操作,但是是按照从左到右依次加载进操作数栈中
      *
      */
      //这里是把i加载进操作数栈中
      //经过12后 ,此时操作数栈中从栈顶到栈底依次为 2
      12: iload_1
      //13 16是把++i加载进操作数栈中,因为是++i,所有先执行13的自增(自增后i的值变为3),后加载i到操作数栈顶
      //经过13 16后,此时操作数栈中从栈顶到栈底元素依次为3 2
      13: iinc          1, 1
      16: iload_1
      //17 18是把i++加载进操作数栈中,因为是i++,所有先把i(3)加载到操作数栈顶,然后对局部变量表中i的值自增1
      //经过17 18后,此时操作数栈中从栈顶到栈底元素依次为3 3 2
      17: iload_1
      18: iinc          1, 1
     //把栈顶的两个元素弹出进行乘法运算即3*3=9,然后重新把运算结构压回栈顶
     //经过21后,此时操作数栈中从栈顶到栈底元素依次为9 2
      21: imul
      //把栈顶的两个元素出栈,进行加法操作即9+2=11,然后把运算结构重新压回栈顶
      //经过21后,此时操作数栈中只有一个元素为11
      22: iadd
      //把栈顶int型数值(11)存入局部变量表中的第4个本地变量(也就是k的位置)
      23: istore_3
      

       //下面都是一个输出语句
      24: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
      27: iload_1
      28: invokevirtual #22                 // Method java/io/PrintStream.println:(I)V
      31: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
      34: iload_2
      35: invokevirtual #22                 // Method java/io/PrintStream.println:(I)V
      38: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
      41: iload_3
      42: invokevirtual #22                 // Method java/io/PrintStream.println:(I)V
      45: return
}

你可能感兴趣的:(java)