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个槽位
    示例:
java 复制代码
public int add(int a, int b) {
    int c = a + b;
    return c;
}

这个方法的局部变量表可能如下:

|----|------------|
| 槽位 | 内容 |
| 0 | this(对象引用) |
| 1 | a |
| 2 | b |
| 3 | c |

1.3 操作数栈

  • 是一个后进先出(LIFO)栈,用于临时存放操作数、计算中间结果
  • 所有字节码指令操作的"数据",都要先从局部变量表加载进栈,再在栈中操作
  • 每个方法调用都会创建一个新的栈帧,包含一个新的操作数栈
    示例:
java 复制代码
int a = 3;
int b = 4;
int c = a + b;

字节码执行过程(假设槽位1=3,槽位2=4):

java 复制代码
iload_1       // 操作数栈: 3
iload_2       // 操作数栈: 3, 4
iadd          // 操作数栈: 7
istore_3      // 局部变量槽3 = 7

二、简单示例

java 复制代码
public class Test {
    public static void main(String[] args) {
        int i = 1;
        int a = i++; // 后缀递增
        int b = ++i; // 前缀递增
    }
}

通过Javap 反编译字节码:

java 复制代码
javac Test.java
javap -c Test

输出(关键部分)如下:

java 复制代码
0: iconst_1         // 将常量1压入栈
1: istore_1         // i = 1

2: iload_1          // 将i的值加载进操作数栈(i = 1)
3: iinc 1, 1        // i = i + 1,此时 i = 2
6: istore_2         // 把栈顶旧值(1)赋值给 a
//结果 a = 1,i = 2

7: 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 |

三、深入示例

示例代码:

java 复制代码
public class Test {
    public static void main(String[] args) {
        int i = 1;
        int a = i++ + i++;  // 表达式1
        int b = i++ + ++i;  // 表达式2

        System.out.println("a = " + a);
        System.out.println("b = " + b);
    }
}

编译并反编译(javac Test.java + javap -c Test):

java 复制代码
 0: iconst_1         // i = 1
 1: istore_1

 2: iload_1          // 把 i=1 压入栈顶 → 用于 i++ 的结果
 3: iinc 1, 1        // i++:i 变为 2
 6: iload_1          // 把 i=2 压入栈顶 → 第二次 i++
 7: iinc 1, 1        // i++:i 变为 3
10: iadd             // 1 + 2
11: istore_2         // a = 3

12: 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

  1. iload_1 → 把 i 的当前值 1 压入操作数栈(用于加法的左值
  2. iinc 1, 1 → i 自增为 2
  3. iload_1 → 把 i 的当前值 2 压入操作数栈(用于加法的右值
  4. iinc 1, 1 → i 自增为 3
  5. iadd → 1 + 2 = 3
  6. 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

  1. iload_1 → 压入 i 的当前值 3(用于 i++)
  2. iinc 1, 1 → i++,i 变为 4
  3. iinc 1, 1 → ++i,i 再变为 5
  4. iload_1 → 压入 i 的当前值 5(用于 ++i)
  5. iadd → 3 + 5 = 8
  6. 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 |

相关推荐
做一位快乐的码农4 分钟前
基于springboot的在线考试系统/考试信息管理平台
java·struts·spring·eclipse·tomcat·maven·hibernate
创码小奇客8 分钟前
Spring Boot 集成 Talos:打造智能调参系统,让模型性能自动飙升
java·spring boot·trae
kyranhan13 分钟前
C#程序本地运行正常,通过网络下载报错:FileLoadException:“未能加载文件或程序集“xxx.dll”或它的某一个依赖项。
开发语言·c#·wpf
涡能增压发动积40 分钟前
Browser-Use Agent使用初体验
人工智能·后端·python
探索java1 小时前
Spring lookup-method实现原理深度解析
java·后端·spring
lxsy1 小时前
spring-ai-alibaba 之 graph 槽点
java·后端·spring·吐槽·ai-alibaba
重生之我是Java开发战士1 小时前
【C语言】结构体详解
c语言·开发语言
码事漫谈1 小时前
深入解析线程同步中WaitForSingleObject的超时问题
后端
码事漫谈1 小时前
C++多线程同步:深入理解互斥量与事件机制
后端
先鱼鲨生1 小时前
gtest框架的安装与使用
开发语言·apache