Java的i = i++与i=++i的去呗从JVM指令讲解图文并茂,一文必懂

简介: Java的i = i++与i=++i的去呗从JVM指令讲解图文并茂,一文必懂

一、Java的i = i++深入JVM指令讲解 图文并茂 看了必懂

先看一段简单的代码

package com.zhou.jvm.runtimedataAreainstructionset;
/**
 * @author zhouyanxiang
 * @create 2020-08-2020/8/5-19:55
 */
public class TestPlusPlus {
    public static void main(String[] args) {
        int i = 8;
        i = i++;
        System.out.println(i);
    }
}

最后程序打印的结果是8不是9

那么这个是为什么呢?我们从JVM指令来看

编译这个类之后我们用jclasslib插件show bytecode with jclasslib,可以看到如下

我把里面的Bytecode单独拿出来,粘在这里

0 bipush 8
 2 istore_1
 3 iload_1
 4 iinc 1 by 1
 7 istore_1
 8 getstatic #2 <java/lang/System.out>
11 iload_1
12 invokevirtual #3 <java/io/PrintStream.println>
15 return

接下来一条一条指令的解析

1. bipush 8

第一步,bipush指令具体的含义就是把一个int类型的数据压入操作数栈。具体的可以参照官网链接,如下图所示把8压入了操作数栈

2. istore_1

第二步,istore_1就是将操作数栈的数字弹出栈并写入局部变量表当中建立的一个index值为1的索引的变量,这里程序的i变量此时就为8,完成了int i = 8的操作。

3. iload_1

第三步,iload_1指令就是将局部变量表中的值再次压入操作数栈当中去。所以此时操作数栈里面的值此时为8

4. iinc 1 by 1

第四步,iinc 1 by 1就是将局部变量表中索引值为1的变量加一,就是完成了i++这一步操作,此时局部变量表中的i为9

5. istore_1

第五步,istore_1就是将操作数栈的数字弹出栈并写入局部变量表当中建立的一个index值为1的索引的变量,这里程序的i变量此时就为8,完成了int i = i++的操作。

6. iload_1

因此最后输出的就是8

二、换成i=++i的JVM指令研究

package com.zhou.jvm.runtimedataAreainstructionset;
/**
 * @author zhouyanxiang
 * @create 2020-08-2020/8/5-19:55
 */
public class TestPlusPlus {
    public static void main(String[] args) {
        int i = 8;
        i = ++i;
        System.out.println(i);
    }
}

这个类的Bytecode

i=++i的bytecode

0 bipush 8
 2 istore_1
 3 iinc 1 by 1
 6 iload_1
 7 istore_1
 8 getstatic #2 <java/lang/System.out>
11 iload_1
12 invokevirtual #3 <java/io/PrintStream.println>
15 return

把i = i++的bytecode拿出来对比

0 bipush 8
 2 istore_1
 3 iload_1
 4 iinc 1 by 1
 7 istore_1
 8 getstatic #2 <java/lang/System.out>
11 iload_1
12 invokevirtual #3 <java/io/PrintStream.println>
15 return

可以看出两者之间的区别在于 iload_1 和 iinc 1 by 1的顺序问题

i=++i的是先 iinc 1 by 1再 iload_1,就是说先对局部变量表的变量进行自增再把这个自增以后的值压入操作数栈,自然这个操作数栈的数据已经+1了

i=i++的是先iload_1 再iinc 1 by 1,就是说先把局部变量表中的变量的值压入操作数栈,再进行自增操作,所以这个对于后续的istore_1操作有影响,使得这个最终的局部变量表中的数据还是之前没有加1的那个数值。


相关文章
|
2月前
|
存储 算法 Java
惊!Java程序员必看:JVM调优揭秘,堆溢出、栈溢出如何巧妙化解?
【8月更文挑战第29天】在Java领域,JVM是代码运行的基础,但需适当调优以发挥最佳性能。本文探讨了JVM中常见的堆溢出和栈溢出问题及其解决方法。堆溢出发生在堆空间不足时,可通过增加堆空间、优化代码及释放对象解决;栈溢出则因递归调用过深或线程过多引起,调整栈大小、优化算法和使用线程池可有效应对。通过合理配置和调优JVM,可确保Java应用稳定高效运行。
111 4
|
19天前
|
存储 缓存 监控
【Java面试题汇总】JVM篇(2023版)
JVM内存模型、双亲委派模型、类加载机制、内存溢出、垃圾回收机制、内存泄漏、垃圾回收流程、垃圾回收器、G1、CMS、JVM调优
【Java面试题汇总】JVM篇(2023版)
|
1月前
|
安全 前端开发 Java
浅析JVM invokedynamic指令与Java Lambda语法的深度融合
在Java的演进历程中,Lambda表达式无疑是Java 8引入的一项革命性特性,它极大地简化了函数式编程在Java中的应用,使得代码更加简洁、易于阅读和维护。而这一切的背后,JVM的invokedynamic指令功不可没。本文将深入探讨invokedynamic指令的工作原理及其与Java Lambda语法的紧密联系,带您领略这一技术背后的奥秘。
19 1
|
2月前
|
C# 开发者 Windows
震撼发布:全面解析WPF中的打印功能——从基础设置到高级定制,带你一步步实现直接打印文档的完整流程,让你的WPF应用程序瞬间升级,掌握这一技能,轻松应对各种打印需求,彻底告别打印难题!
【8月更文挑战第31天】打印功能在许多WPF应用中不可或缺,尤其在需要生成纸质文档时。WPF提供了强大的打印支持,通过`PrintDialog`等类简化了打印集成。本文将详细介绍如何在WPF应用中实现直接打印文档的功能,并通过具体示例代码展示其实现过程。
133 0
|
2月前
|
数据库 C# 开发者
WPF开发者必读:揭秘ADO.NET与Entity Framework数据库交互秘籍,轻松实现企业级应用!
【8月更文挑战第31天】在现代软件开发中,WPF 与数据库的交互对于构建企业级应用至关重要。本文介绍了如何利用 ADO.NET 和 Entity Framework 在 WPF 应用中访问和操作数据库。ADO.NET 是 .NET Framework 中用于访问各类数据库(如 SQL Server、MySQL 等)的类库;Entity Framework 则是一种 ORM 框架,支持面向对象的数据操作。文章通过示例展示了如何在 WPF 应用中集成这两种技术,提高开发效率。
41 0
|
2月前
|
缓存 前端开发 Java
浅析JVM invokedynamic指令与Java Lambda语法
【8月更文挑战第27天】在Java的演进历程中,invokedynamic指令的引入和Lambda表达式的出现无疑是两大重要里程碑。它们不仅深刻改变了Java的开发模式和性能表现,还极大地推动了Java在函数式编程和动态语言支持方面的进步。本文将从技术角度浅析JVM中的invokedynamic指令及其与Java Lambda语法的紧密联系。
41 0
|
5天前
|
安全 Java 调度
Java编程时多线程操作单核服务器可以不加锁吗?
Java编程时多线程操作单核服务器可以不加锁吗?
18 2
|
9天前
|
存储 缓存 Java
java线程内存模型底层实现原理
java线程内存模型底层实现原理
java线程内存模型底层实现原理
|
13天前
|
缓存 Java 应用服务中间件
Java虚拟线程探究与性能解析
本文主要介绍了阿里云在Java-虚拟-线程任务中的新进展和技术细节。
|
11天前
|
Java 开发者
Java中的多线程基础与应用
【9月更文挑战第22天】在Java的世界中,多线程是一块基石,它支撑着现代并发编程的大厦。本文将深入浅出地介绍Java中多线程的基本概念、创建方法以及常见的应用场景,帮助读者理解并掌握这一核心技术。
下一篇
无影云桌面