Java ++i 与 i++ 底层原理
一、概念解释
1.1 指令含义
- ICONST_x :将常量x压到操作数栈中
- ISTORE_x :将操作数栈顶元素写入到本地变量表第x+1位置
- IINC m n :将本地变量表中第m+1位置进行加n操作
- LINENUMBER :这个就是标注我们的行号
- ILOAD_x :将局部变量表第x+1位置元素加入操作数栈中,和ISTORE相反
1.2 局部变量表
- 是一个数组,用于存储方法中所有的局部变量,包括:
- 方法参数
- 方法内部定义的局部变量(int、Object、boolean 等)
- 编译器临时变量(如:中间值、i++的旧值)
- 每个变量占用 1 或 2 个槽(slot):
- int, float, reference → 占1个槽位
- long, double → 占2个槽位
示例:
public int add(int a, int b) {int c = a + b;return c;
}
这个方法的局部变量表可能如下:
槽位 | 内容 |
0 | this(对象引用) |
1 | a |
2 | b |
3 | c |
1.3 操作数栈
- 是一个后进先出(LIFO)栈,用于临时存放操作数、计算中间结果
- 所有字节码指令操作的“数据”,都要先从局部变量表加载进栈,再在栈中操作
- 每个方法调用都会创建一个新的栈帧,包含一个新的操作数栈
示例:
int a = 3;
int b = 4;
int c = a + b;
字节码执行过程(假设槽位1=3,槽位2=4):
iload_1 // 操作数栈: 3
iload_2 // 操作数栈: 3, 4
iadd // 操作数栈: 7
istore_3 // 局部变量槽3 = 7
二、简单示例
public class Test {public static void main(String[] args) {int i = 1;int a = i++; // 后缀递增int b = ++i; // 前缀递增}
}
通过Javap 反编译字节码:
javac Test.java
javap -c Test
输出(关键部分)如下:
0: iconst_1 // 将常量1压入栈
1: istore_1 // i = 12: iload_1 // 将i的值加载进操作数栈(i = 1)
3: iinc 1, 1 // i = i + 1,此时 i = 2
6: istore_2 // 把栈顶旧值(1)赋值给 a
//结果 a = 1,i = 27: iinc 1, 1 // i = i + 1,此时 i = 3
10: iload_1 // 把新值加载进栈(i = 3)
11: istore_3 // b = 3
//结果 b = 3,i = 3
分析区别:
表达式 | 步骤 | 字节码行为 | 结果 |
a = i++ | 先取值后自增 | iload_1(先把i原值放进操作数栈) iinc(i加1,此时 i = 2) istore_2(把栈顶旧值赋值给 a) | a = 1,i = 2 |
b = ++i | 先自增后取值 | iinc(i先加1,此时 i = 3) iload_1(把新值加载进栈) istore_3(把栈顶值赋给b) | b = 3,i = 3 |
三、深入示例
示例代码:
public class Test {public static void main(String[] args) {int i = 1;int a = i++ + i++; // 表达式1int b = i++ + ++i; // 表达式2System.out.println("a = " + a);System.out.println("b = " + b);}
}
编译并反编译(javac Test.java + javap -c Test):
0: iconst_1 // i = 11: istore_12: iload_1 // 把 i=1 压入栈顶 → 用于 i++ 的结果3: iinc 1, 1 // i++:i 变为 26: iload_1 // 把 i=2 压入栈顶 → 第二次 i++7: iinc 1, 1 // i++:i 变为 3
10: iadd // 1 + 2
11: istore_2 // a = 312: iload_1 // i=3,压栈(用于 i++ 的结果)
13: iinc 1, 1 // i++:i = 4
16: iinc 1, 1 // ++i:i = 5
19: iload_1 // i=5,再压栈
20: iadd // 3 + 5
21: istore_3 // b = 8
3.1 表达式 1:int a = i++ + i++
初始:i = 1
- iload_1 → 把 i 的当前值 1 压入操作数栈(用于加法的左值)
- iinc 1, 1 → i 自增为 2
- iload_1 → 把 i 的当前值 2 压入操作数栈(用于加法的右值)
- iinc 1, 1 → i 自增为 3
- iadd → 1 + 2 = 3
- istore_2 → 存入变量 a
最终结果:
- i = 3
- a = 3
int a = i++ + i++ 的操作数栈变化示意图:
步骤 | i 值 | 操作数栈 | 说明 |
初始 | 1 | ||
iload_1 | 1 | 1 | 压入 i(用于计算) |
iinc | 2 | 1 | i++ |
iload_1 | 2 | 1, 2 | 再压入 i(计算) |
iinc | 3 | 1, 2 | i++ |
iadd | 3 | 3 | 1 + 2 |
istore_2 | 3 | a = 3 |
3.2 表达式 2:int b = i++ + ++i
此时 i = 3
- iload_1 → 压入 i 的当前值 3(用于 i++)
- iinc 1, 1 → i++,i 变为 4
- iinc 1, 1 → ++i,i 再变为 5
- iload_1 → 压入 i 的当前值 5(用于 ++i)
- iadd → 3 + 5 = 8
- istore_3 → 存入变量 b
最终结果:
- i = 5
- b = 8
int b = i++ + ++i 的操作数栈变化示意图:
步骤 | i 值 | 操作数栈 | 说明 |
初始 | 3 | ||
iload_1 | 3 | 3 | i++ 左值 |
iinc | 4 | 3 | i++ |
iinc | 5 | 3 | ++i |
iload_1 | 5 | 3, 5 | ++i 右值 |
iadd | 5 | 8 | 3 + 5 |
istore_3 | 5 | b = 8 |