java 分析 i++ 和 ++i 区别 以及 i = i++ + ++i + i++ + ++i jvm字节码执行过程

本文详细分析了i=i+++++i+i+++++i代码在JVM中的字节码执行过程,解释了i++和++i的区别,确定了最终i的值,并探讨了代码执行顺序及字节码对应流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文以 “i = i++ + ++i + i++ + ++i” 为例 分析 其字节码的执行过程。其中主要解决4个问题

1. i++ 和 ++i 有什么区别,在jvm字节码层面又有什么区别
2. “i = i++ + ++i + i++ + ++i” 最后 i 的值是多少
3. “i = i++ + ++i + i++ + ++i” 这串代码的执行顺序(是从右向左顺序执行,还是像这样"i = (i++ + ++i) + (i++ + ++i)"的执行顺序呢)
4. “i = i++ + ++i + i++ + ++i” 这串代码对应的jvm 字节码 解析和对应的执行流程解析

测试代码:

package com.example.all6.iii;
public class ITest {
	public static void main(String[] args){
	    int i = 1;
	    i = i++ + ++i + i++ + ++i;
	    System.out.println(i);
	}
}

运行 javap -v -p ITest.class 得到的字节码

...... 省略了
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: iconst_1
         1: istore_1
         2: iload_1
         3: iinc          1, 1
         6: iinc          1, 1
         9: iload_1
        10: iadd
        11: iload_1
        12: iinc          1, 1
        15: iadd
        16: iinc          1, 1
        19: iload_1
        20: iadd
        21: istore_1
        22: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        25: iload_1
        26: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        29: return
      LineNumberTable:
        这也省略..... 
}
SourceFile: "ITest.java"

借个图先:
在这里插入图片描述

开始 第四个问题(4. “i = i++ + ++i + i++ + ++i” 这串代码对应的jvm 字节码 解析和对应的执行流程解析):
字节码一共 0 - 29 行, 主要分析0-21行内容,分析每一行的含义和对应的内存的值的变化,首先 图说一下 方法执行过程中 jvm操作数栈 和 局部变量表 的初始情况 如下图所示:
1
此时的 局部变量表里的i 还没有赋值 , 操作数栈里也没有数据 。this 默认占局部变量表 0 的位置。i 占局部变量表1 的位置(下边的图 好像都忘了 标注了 - -!)。只要知道 变量 i 在局部变量表1的位置

0: iconst_1 : 把 数值 “1” 压入操作数栈栈顶 。此时 操作数栈 栈顶的值是 1
在这里插入图片描述
1: istore_1 : 操作数栈 栈顶元素出栈 并存入 局部变量表 位置1 。即相当于给i 赋值为1.i = 1。
在这里插入图片描述
2: iload_1 : 将局部变量表位置1 的值 压入操作数栈栈顶
在这里插入图片描述
3: iinc 1, 1 : “iinc x,n” 意为 局部变量表x位置上自增n。这里指 局部变量表 1 位置上的数值 自增1。即此时 i 的值为2 。 操作数栈没有变化
在这里插入图片描述
6: iinc 1, 1 : 同上 ,此时 i 的值 变为3 。 操作数栈 仍没有变化
在这里插入图片描述
9: iload_1 : 把局部变量表1位置的数值 压入栈顶,注意,此时的i 因为两次自增已经变成了 3 。
在这里插入图片描述
10: iadd : 把操作数栈 栈顶 和 次栈顶两个元素弹出 相加,再压入栈顶。就是 先弹出3,再弹出1 ,把 1 和 3 相加 。1+3=4 ,把结果 4 再次压入操作数栈 栈顶。此时的 局部变量表没有变化。
在这里插入图片描述
11: iload_1 : 把局部变量表1位置的数值 压入栈顶。即 把数值3 压入操作数栈到栈顶位置。此时操作数栈 发生了变化 。看图:
在这里插入图片描述
12: iinc 1, 1 : 局部变量表 1的位置上的数值加1 。 即 i 的值由3 变成4 。
在这里插入图片描述
15: iadd : 将此时的操作数栈的 临近栈顶的两个元素弹出,即3和4 分别弹出,然后相加 得7 。再压入栈顶。
在这里插入图片描述
16: iinc 1, 1 : 局部变量表1的位置自加1。即 4+1=5。i的值变为5。
在这里插入图片描述
19: iload_1 : 将局部变量表 1 位置的数值压入操作数栈的栈顶位置。注意 此时的 i 的值 已经是 5 了。
在这里插入图片描述
20: iadd : 将操作数栈 临近栈顶的两个元素弹出相加,讲结果压入栈顶。即 先弹出5 再弹出7 ,把 5和7 相加 得到12 ,再压入 操作数栈。此时栈顶的值变成了12。
在这里插入图片描述
21: istore_1 : 将操作数栈 栈顶的元素弹出,赋值给局部变量表1位置。即 把 操作数栈栈顶 元素12 弹出,赋值给 i 。相当于 最后给 i 赋值 i=12 。
在这里插入图片描述
到此为止 0 - 21 行字节码都一一解析完成了 。剩下的
22: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
25: iload_1
26: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
29: return
这些 无非是 调用打印函数 和 方法结束返回。

总结一下
第四个问题(“i = i++ + ++i + i++ + ++i” 这串代码对应的jvm 字节码 解析和对应的执行流程解析) 相信 仔细看完的朋友 一定能理解 这串代码的执行流程了。

第二问题 (“i = i++ + ++i + i++ + ++i” 最后 i 的值是多少) 12 。对是 12 ,不信你debug试试。为啥 看上边。

第一个问题(i++ 和 ++i 有什么区别,在jvm字节码层面又有什么区别
通俗的来讲 “i++” 是先赋值 , 再相加 。“++i” 是先相加再赋值。
通过字节码来理解 无论是 “i++” 还是“++i” 都会调用 “iinc 1, 1” 这指令 完成自身加1 的操作。关键取决于这个指令后续的指令是什么。

  • 如果是“iload_1” 即将局部变量表位置1 的值 压入操作数栈栈顶。因为此时的i的值已经完成自身加1,这是取的i 的值 或者说以后参与运算的i的值 都是i的最新的值。即 是 2。
  • 如果后续指令不是“iload_1” 或者 “istore_1” 。就是说虽然局部变量表i的值完成自增。但是 并没有进入操作数栈参与运算。此时操作数栈 栈顶元素 很可能还是 没有自增前的 值。

至于这两种情况,到底谁“i++” ,谁是“++i”呢 。第1种情况 有后续指令的是“++i”,第二种情况则是“i++”。
为什么 ? 两段代码证明

package com.example.all6.iii;
public class ITest {
	public static void main(String[] args){
	    int i = 1;
	    int j = 1;
	    //i = i++ + ++i + i++ + ++i;
		i = i++;
		j = ++j;
	    System.out.println(i); // 1
		System.out.println(j); // 2
	}
}

第三个问题(“i = i++ + ++i + i++ + ++i” 这串代码的执行顺序(是从右向左顺序执行,还是像这样"i = (i++ + ++i) + (i++ + ++i)"的执行顺序呢)
观察这串代码 可以发现有 4个 “++” 操作 和 3 个“+” 操作。
先看图:
在这里插入图片描述
从图中 可以推断出 ,字节码 6行和9行 组合是一个“++i” , 16行和19行组合 是一个 “++i” , 剩下的 3行 和 12 行 自然 就是 “i++”了。
先按照 代码从右向左执行顺序来 解读一下
在这里插入图片描述
根据字节码从上向下的执行顺序 ,代码从右向左的执行顺序。 得到推测到这样的结果:
5 → 7 → 6 → 1 → 4 → 3 → 2 , 什么乱七八糟的 , 整理一下 i = ((5 + 7) + 1 ) + 3 ,再翻译一下
i = ((i++ + ++i)+ i++ ) + ++i 。

大概能推测出 “i++” 指令的优先级 要比 “++i” 高。或者 “i++” 总比 “i++” 先执行。
至于为什么不是 从右向左的执行顺序 或者 (i++ + ++i) + (i++ + ++i) 这样的执行顺序呢?

应该是 指令重排导致的 (猜的 没有验证过)。

好了,以上就是全部内容 。四个问题 都基本 给出了对应的解释。
推荐 一篇不错的文章 https://www.jianshu.com/p/c11f98269b00

最后 如果有错误,欢迎批评指正

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值