问题:如何理解 i++ 和 ++i ?
该问题需要用到 Java 类在内存中的存储结构、栈帧、JVM 指令等底层内容。
简单理解,每个方法有独立的栈帧,每个栈帧有独立的内部结构,如下:
- LocalVariableTable(局部变量表,存放方法内的局部变量)
- OperandStack(操作数栈,存放方法内的操作数)
- DynamicLinking(动态链接,此处可简单理解为指针)
- ReturnAddress(返回值地址)
针对 i++ 和 ++i,有以下四种常见情况使用最简单代码描述
- i++ 无赋值
public static void main(String[] args) {
int i = 8;
i++;
System.out.println(i); // 9
}
其字节码如下:
bipush 8 // 将8入栈,即将8压入当前线程的操作数栈
istore_1 // 弹出操作数栈1号位置的数据,并放入局部变量表中,此时操作数栈为空,局部变量表有一个数据
iinc 1 by 1 // 对局部变量表1号位置的数据加一,此时局部变量表中的 i 为9,操作数栈为空
getstatic #2 <java/lang/System.out> // 获取静态变量 System.out
iload_1 // 获取局部变量表1号位置的数据,并压入操作数栈,此时局部变量表和操作数栈中的数据相同,即 i=9
invokevirtual #3 <java/io/PrintStream.println> // 执行 PrintStream.println 输出操作数栈的内容
return // 返回,此时无数据返回
- ++i 无赋值
public static void main(String[] args) {
int i = 8;
++i;
System.out.println(i); // 8
}
其字节码如下:
bipush 8 // 将8入栈,即将8压入当前线程的操作数栈
istore_1 // 弹出操作数栈1号位置的数据,并放入局部变量表中,此时操作数栈为空,局部变量表有一个数据
iinc 1 by 1 // 对局部变量表1号位置的数据加一,此时局部变量表中的 i 为9,操作数栈为空
getstatic #2 <java/lang/System.out>
iload_1 // 获取局部变量表1号位置的数据,并压入操作数栈,此时局部变量表和操作数栈中的数据相同,即 i=9
invokevirtual #3 <java/io/PrintStream.println>
return
- i++ 赋值给 i
public static void main(String[] args) {
int i = 8;
i = i++;
System.out.println(i); // 8
}
其字节码如下:
bipush 8 // 将8入栈,即将8压入当前线程的操作数栈
istore_1 // 弹出操作数栈1号位置的数据,并放入局部变量表中,此时操作数栈为空,局部变量表有一个数据
iload_1 // 获取局部变量表1号位置的数据,并压入操作数栈,此时局部变量表和操作数栈中的数据相同
iinc 1 by 1 // 对局部变量表1号位置的数据加一,此时局部变量表中的 i 为9,操作数栈中的 i 仍为 8
istore_1 // 弹出操作数栈1号位置的数据,并放入局部变量表中,即将操作数栈中的 i=8 覆盖局部变量表中的 i=9,最终操作数栈和局部变量表中的 i 都等于 8
getstatic #2 <java/lang/System.out>
iload_1 // 获取局部变量表1号位置的数据,并压入操作数栈,此时局部变量表和操作数栈中的数据相同,即 i=8
invokevirtual #3 <java/io/PrintStream.println>
return
- ++i 赋值给 i
public static void main(String[] args) {
int i = 8;
i = ++i;
System.out.println(i); // 9
}
其字节码如下:
bipush 8 // 将8入栈,即将8压入当前线程的操作数栈
istore_1 // 弹出操作数栈1号位置的数据,并放入局部变量表中,此时操作数栈为空,局部变量表有一个数据
iinc 1 by 1 // 对局部变量表1号位置的数据加一,此时局部变量表中的 i 为9,操作数栈为空
iload_1 // 获取局部变量表1号位置的数据,并压入操作数栈,此时局部变量表和操作数栈中的数据相同
istore_1 // 弹出操作数栈1号位置的数据,并放入局部变量表中,即将操作数栈中的 i=9 覆盖局部变量表中的 i=9,最终操作数栈和局部变量表中的 i 都等于 9
getstatic #2 <java/lang/System.out>
iload_1 // 获取局部变量表1号位置的数据,并压入操作数栈,此时局部变量表和操作数栈中的数据相同,即 i=9
invokevirtual #3 <java/io/PrintStream.println>
return
综上,通过对比以上四种情况,可以确定:
- 赋值操作永远是从操作数栈向局部变量表中赋值
- 读取操作永远是从局部变量表中获取变量值
- ++ 操作发生在局部变量表中,执行变量操作时才将变量从局部变量表压入操作数栈,执行赋值操作时将变量从操作数栈中取出并对局部变量表中的变量进行赋值
简单记忆(非复杂情况下):
- 单独执行 i++ 或 ++i 的效果相同,下一行命令中的 i 将为 i+1
- 执行
i=i++;时,下一行命令中的 i 值不变 - 执行
i=++i;时,下一行命令中的i=i+1
本文详细解释了Java中i++和i+=运算符的工作原理,涉及栈帧结构、局部变量表和操作数栈,并通过字节码分析展示了四种常见情况。重点强调了赋值操作和读取操作的顺序以及执行流程。

被折叠的 条评论
为什么被折叠?



