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

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

  1. 局部变量表
  2. 操作数栈
  3. 动态链接
  4. 方法返回地址 这里i++、 ++i涉及到的就是局部变量表和操作数栈。具体信息可参考:《Java虚拟机规范》

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

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

1
2
3
4
5
6
7
8
class Scratch {

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

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

1
javac scratch.java

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

1
javap -c Scratch > scratch.txt

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Compiled from "scratch.java"
class Scratch {
Scratch();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()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,可以发现对应的是:

1
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到底有什么不同

https://jingzhouzhao.github.io/archives/ade4c276.html

作者

太阳当空赵先生

发布于

2019-08-20

更新于

2022-02-22

许可协议

评论