从字节码来说明i++与++i到底有什么不同

看字节码之前需要先了解相关概念,如栈帧、操作数栈、局部变量表。
栈帧是JVM中很重要的一个概念,因为JVM是基于栈的架构。一个方法的调用其实就是栈帧入栈出栈的过程。栈顶栈帧就是当前方法调用。
一个栈帧中包含:

  1. 局部变量表
  2. 操作数栈
  3. 动态链接
  4. 方法返回地址

这里i++、 ++i涉及到的就是局部变量表和操作数栈。具体信息可参考:《Java虚拟机规范》

局部变量表存储的是方法的参数以及内部定义的变量的值,操作数栈也是一个栈结构,用来执行方法中的指令。

好了,来看一个代码片段:

class Scratch {

    public static void main(String[] args) {
        int i=0,j=0,m=0;
        j = i++;
        m = ++i;
    }
}

为了不产生其他多过信息,这里只写了关键代码。
首先可以通过javac 将源码编译成class:

javac scratch.java

执行完成后将看到Scratch.class 文件,通过javap命令查看字节码:

javap -c Scratch > scratch.txt

为了方便查看将结果输出到了scratch.txt,打开此文件将看到如下信息:

Compiled from "scratch.java"
class Scratch {
  Scratch();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_0
       1: istore_1
       2: iconst_0
       3: istore_2
       4: iconst_0
       5: istore_3
       6: iload_1
       7: iinc          1, 1
      10: istore_2
      11: iinc          1, 1
      14: iload_1
      15: istore_3
      16: return
}

关注main方法 0-5,可以发现对应的是:

 int i=0,j=0,m=0;

主要是声明变量并初始化为0。我们可以发现下划线后面跟了一个数字,这里应该代表的是变量在局部变量表中的位置。
i:1
j:2
m:3

再关注main方法:6、7、10:
iload_1 表示将局部变量表中位置1的数据放入操作数栈中(这里对应的是i,此时i的值为0)然后pop出来赋值给j。然后再将i自增iinc。最后istore_2存储j到局部变量表中。
操作完成后i=1,j=1。
最后关注main方法:11、14、15:
iinc首先自增i,然后iload_1将局部变量表中位置1的数据放入操作数栈中(这里对应的是i,此时i的值为2)然后pop出来赋值给m。最后istore_3存储m到局部变量表中。
操作完成后i=2,j=1,m=2。

这就是为什么大家都说,i++是先赋值后自增,而++i是先自增后赋值的原因。

这里要说明的是,如果是单独的i++、++i是没有什么区别的。

你可能感兴趣的:(从字节码来说明i++与++i到底有什么不同)