深入理解Java中的自增

2023-11-07

引言

i++和++i的区别在学习程序设计的时候应该就已经学过:一个是用完再加,一个是加完再用。那么考虑一下下面的代码:

int i = 0;
for (int j = 0; j < 100; j++) 
    i = i++;

这个运行完,i的值应该是多少呢?

深入理解自增

上面的代码运行完,i的值不是100,也不是101,而是0!这个结果的原因搜一下,会看到一个新的专业术语中间缓存变量i = i++这行代码相当于:

int tmp = i;
i++;
i = tmp;

单单知道这个也没用,你只是以后遇到i = i++知道它是怎么处理的,但是换一个i = ++i + i++,这个又是怎么运算的呢?

所以我们看一下经过javac编译后的int i = 0; i = i++字节码到底是什么样子的,用javap -verbose命令得到:

 0: iconst_0            常数0入栈
 1: istore_1            将栈顶元素移入本地变量1号位置存储
 2: iload_1             本地变量1号位置入栈
 3: iinc          1, 1  将本地变量1号位置加1
 6: istore_1            栈顶元素移入本地变量1号位置
 7: return              返回

对于JVM来说,最初的Java代码就是执行这7行字节码,每一行的意思我在后面都解释了,比较简单,主要是要理解,对于一个方法来说,都相当于在栈里压入了一个栈帧,在其中会有局部变量区操作栈,前者就是存储局部变量的,后者是存操作数的(符合栈的规律,栈顶元素先出)

这里写图片描述

PC寄存器就是记录当前运行到哪一行指令了。

再比较一下i = ++i的字节码:

 0: iconst_0
 1: istore_1
 2: iinc          1, 1
 5: iload_1
 6: istore_1
 7: return

字节码对应的操作其实都一样,只不过是顺序不一样。

i++是将局部变量区的值先load到栈中,再对局部变量区的值进行增加,而++i则是先对局部变量区的值进行增加,之后再load到栈中。这也很好的解释了为什么一个是用完再加,一个是加完再用

再看看i = ++i + i++的字节码:

 0: iconst_0
 1: istore_1
 2: iinc          1, 1
 5: iload_1
 6: iload_1
 7: iinc          1, 1
10: iadd                取栈顶的两个元素相加,再把结果压入栈
11: istore_1
12: return

是不是也是一样的,只是iinc与iload的顺序不一样而已。所以以后再遇到这种情况,只要自己画一个操作栈,一个局部变量区,结果就显而易见了。

static的自增

    static int i;
    public static void main(String[] str) {
        i = ++i + i++;
    }

对于这一段代码,它的字节码是:

    0: getstatic     #2                  // Field i:I
    3: iconst_1
    4: iadd
    5: dup                               取当前栈顶元素复制一份,并压入栈中
    6: putstatic     #2                  // Field i:I
    9: getstatic     #2                  // Field i:I
   12: dup
   13: iconst_1
   14: iadd
   15: putstatic     #2                  // Field i:I
   18: iadd
   19: putstatic     #2                  // Field i:I
   22: return

没有用到iinc,用了dup和iadd替代了。getstatic和putstatic分别是获取指定类的实例域,并压入栈以及为指定的类的静态域赋值。

对于++i,对应的字节码是先iadd,再dup,然后putstatic,这样dup的值就是加后值,而对于i++,对应的字节码是先dup,再iadd,再putstatic,这样dup的值就是原始的值。

虽然类静态变量和局部变量的字节码对应的操作不一样,但是结果都是一样的

练习

public static void main(String[] str) {
        int i = 0;
        i = i++ + ++i;
        int j = 0;
        j = ++j + j++ + j++ + j++;
        int k = 0;
        k = k++ + k++ + k++ + ++k;
        int h = 0;
        h = ++h + ++h;
        int p1 = 0, p2 = 0;
        int q1 = 0, q2 = 0;
        q1 = ++p1;
        q2 = p2++;

        StringBuilder builder = new StringBuilder();
        builder.append("i:").append(i).append("\n")
                .append("j:").append(j).append("\n")
                .append("k:").append(k).append("\n")
                .append("h:").append(h).append("\n")
                .append("p1:").append(p1).append("\n")
                .append("p2:").append(p2).append("\n")
                .append("q1:").append(q1).append("\n")
                .append("q2:").append(q2);
        System.out.println(builder.toString());
    }

代码结果是:

i:2
j:7
k:7
h:3
p1:1
p2:1
q1:1
q2:0

如果答案都是正确的那么就差不多可以过关了,如果有什么问题再自己去javap去研究研究字节码吧。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

深入理解Java中的自增 的相关文章

  • 按键时关闭 ModalWindow

    我希望能够在用户按下某个键 在我的例子中是 ESC 时关闭 ModalWindow 我有一个用于按键的 Javascript 侦听器 它调用取消按钮 ID 的单击事件 jQuery modalWindowInfo closeButtonId
  • 在 Java 中克隆对象 [3 个问题]

    这样做会调用Asub的clone方法吗 或者Asub深度克隆是否正确 如果没有的话 有没有办法通过这种方法对Asub进行深度克隆呢 abstract class Top extends TopMost protected Object cl
  • 不同帐户上的 Spring Boot、JmsListener 和 SQS 队列

    我正在尝试开发一个 Spring Boot 1 5 应用程序 该应用程序需要侦听来自两个不同 AWS 帐户的 SQS 队列 是否可以使用 JmsListener 注解创建监听器 我已检查权限是否正确 我可以使用 getQueueUrl 获取
  • 为 java 游戏创建交互式 GUI

    大家好 我正在创建一个类似于 java 中的 farmville 的游戏 我只是想知道如何实现用户通常单击以与游戏客户端交互的交互式对象 按钮 我不想使用 swing 库 通用 Windows 看起来像对象 我想为我的按钮导入自定义图像 并
  • 动态选择端口号?

    在 Java 中 我需要获取端口号以在同一程序的多个实例之间进行通信 现在 我可以简单地选择一些固定的数字并使用它 但我想知道是否有一种方法可以动态选择端口号 这样我就不必打扰我的用户设置端口号 这是我的一个想法 其工作原理如下 有一个固定
  • 如何在java中将一个数组列表替换为另一个不同大小的数组列表

    我有两个大小不同的数组列表 如何从此替换 ArrayList
  • 在 Jar 文件中运行 ANT build.xml 文件

    我需要使用存储在 jar 文件中的 build xml 文件运行 ANT 构建 该 jar 文件在类路径中可用 是否可以在不分解 jar 文件并将 build xml 保存到本地目录的情况下做到这一点 如果是的话我该怎么办呢 Update
  • java.lang.IllegalStateException:应用程序 PagerAdapter 更改了适配器的内容,而没有调用 PagerAdapter#notifyDataSetChanged android

    我正在尝试使用静态类将值传递给视图 而不是使用意图 因为我必须传递大量数据 有时我会收到此错误 但无法找出主要原因是什么 Error java lang IllegalStateException The application s Pag
  • 没有 Spring 的自定义 Prometheus 指标

    我需要为 Web 应用程序提供自定义指标 问题是我不能使用 Spring 但我必须使用 jax rs 端点 要求非常简单 想象一下 您有一个包含键值对的映射 其中键是指标名称 值是一个简单的整数 它是一个计数器 代码会是这样的 public
  • 将流转换为 IntStream

    我有一种感觉 我在这里错过了一些东西 我发现自己做了以下事情 private static int getHighestValue Map
  • 帮助将图像从 Servlet 获取到 JSP 页面 [重复]

    这个问题在这里已经有答案了 我目前必须生成一个显示字符串文本的图像 我需要在 Servlet 上制作此图像 然后以某种方式将图像传递到 JSP 页面 以便它可以显示它 我试图避免保存图像 而是以某种方式将图像流式传输到 JSP 自从我开始寻
  • Hibernate 的 PersistentSet 不使用 hashCode/equals 的自定义实现

    所以我有一本实体书 public class Book private String id private String name private String description private Image coverImage pr
  • volatile、final 和synchronized 安全发布的区别

    给定一个带有变量 x 的 A 类 变量 x 在类构造函数中设置 A x 77 我们想将 x 发布到其他线程 考虑以下 3 种变量 x 线程安全 发布的情况 1 x is final 2 x is volatile 3 x 设定为同步块 sy
  • tomcat 中受密码保护的应用程序

    我正在使用 JSP Servlet 开发一个Web应用程序 并且我使用了Tomcat 7 0 33 as a web container 所以我的要求是tomcat中的每个应用程序都会password像受保护的manager applica
  • 最新的 Hibernate 和 Derby:无法建立 JDBC 连接

    我正在尝试创建一个使用 Hibernate 连接到 Derby 数据库的准系统项目 我正在使用 Hibernate 和 Derby 的最新版本 但我得到的是通用的Unable to make JDBC Connection error 这是
  • 干净构建 Java 命令行

    我正在使用命令行编译使用 eclipse 编写的项目 如下所示 javac file java 然后运行 java file args here 我将如何运行干净的构建或编译 每当我重新编译时 除非删除所有内容 否则更改不会受到影响 cla
  • 如何使用mockito模拟构建器

    我有一个建造者 class Builder private String name private String address public Builder setName String name this name name retur
  • 包 javax.el 不存在

    我正在使用 jre6 eclipse 并导入 javax el 错误 包 javax el 不存在 javac 导入 javax el 过来 这不应该是java的一部分吗 谁能告诉我为什么会这样 谢谢 米 EL 统一表达语言 是 Java
  • 长轮询会冻结浏览器并阻止其他 ajax 请求

    我正在尝试在我的中实现长轮询Spring MVC Web 应用程序 http static springsource org spring docs 2 0 x reference mvc html但在 4 5 个连续 AJAX 请求后它会
  • 使用 CXF-RS 组件时,为什么我们使用 而不是普通的

    作为后续这个问题 https stackoverflow com questions 20598199 对于如何正确使用CXF RS组件我还是有点困惑 我很困惑为什么我们需要

随机推荐

  • GameofMir引擎架设传奇服务器【2:登录器配置】

    工具 1 gom引擎自带的登录器 这里没有自带的登录器 游戏运行不了 里面带有一些密码和UI相关的文件 2 绿盟配置器 当然也可以用其他的 这里使用的绿盟的免费版本 都解压后开始配置 这里进入绿盟的文件夹 把这几个文件复制到gom自带的登录
  • C关键字volatile

    其实我想看的 想做笔记的就是就是那个汇编例子 看来汇编例子有助于了解啊 以前听人说过 高手都能将每一句C语言对应一句汇编语言 C语言关键字volatile 1 C语言关键字volatile C语言关键字volatile 注意它是用来修饰变量
  • 【maven】scalac:error while loading <root>,Error accessing

    1 概述 今天运行flink程序 编译时报如下错误 意思是访问maven下载的jar包出错 但是这个jar包已经下载好了 于是我进入jar包所在的路径 发现该包的大小只有1KB 感觉是下载有问题 于是删除该jar包 然后在pom中reimp
  • 5款linux下的笔记软件

    Evernote是一个很好的笔记软件 但是却没有linux版本 下面介绍5款linux下最好的笔记软件 1 Springseed Springseed 是 Jono Cooper 和 Michael Harker 一起开发的 是一个使用非常
  • Python手册(Standard Library)--datetime+time+calendar

    文章目录 datetime datetime timedelta datetime date datetime datetime datetime time python中时间日期格式化符号 time time struct time 时间
  • transformer论文_Transformer相关论文阅读笔记

    最近在使用Transformer模型跑任务 正好加入了同学组织的一个暑期文献打卡群 于是决定假期每天读一篇文献 主要关于近两年对于Transformer模型应用的论文 阅读论文的笔记分享给大家 7月18日 R transformer Rec
  • 安装pytorch-metric-learning

    https github com KevinMusgrave pytorch metric learning pytorch metric learning的github官方网址 安装pytorch metric learning 1 直接
  • vite遇见跨域怎么解决

    1 找到vite config js 2 找到defineConfig下面的server proxy 代理名称 target 地址 changeOrigin true rewrite path gt path replace 代理名称 3
  • 数字分类 C语言

    给定一系列正整数 请按要求对数字进行分类 并输出以下 5 个数字 A1 能被 5 整除的数字中所有偶数的和 A2 将被 5 除后余 1 的数字按给出顺序进行交错求和 即计算 n1 n2 n3 n4 A3 被 5 除后余 2 的数字的个数 A
  • Element-ui使用Select多选框如何给value属性绑定对象类型

    如何给select多选下拉框的value属性绑定对象类型时怎么做 现在select标签处有一个value key属性绑定值必须是为option标签中v for遍历的数组中的元素 该元素值必须具有唯一性 在option属性key处绑定值为数组
  • 分类算法及其应用场景

    单一的分类方法主要包括 LR逻辑回归 SVM支持向量机 DT决策树 NB朴素贝叶斯 NN人工神经网络 K 近邻 集成学习算法 基于Bagging和Boosting算法思想 RF随机森林 GBDT Adaboost XGboost
  • 使用vim编写并编译运行C++程序并添加代码补全等功能

    听说使用vim写程序效率非常高 今天满怀一腔热血准备把vim整利索了 写篇笔记记录一下防止以后忘了 vim功能很多 我也只能是解决我现在的需求 以后遇到更多的需求再扩充 如果读者有什么建议和问题欢迎留言讨论 vim 编写一个C 程序并保存
  • Java中实现文件上传下载的三种解决方案(推荐)

    前言 文件上传是一个老生常谈的话题了 在文件相对比较小的情况下 可以直接把文件转化为字节流上传到服务器 但在文件比较大的情况下 用普通的方式进行上传 这可不是一个好的办法 毕竟很少有人会忍受 当文件上传到一半中断后 继续上传却只能重头开始上
  • 我的 2020 总结:跌宕起伏

    文章目录 复盘与展望 复盘与展望 2020总结 2021计划 个人 生理健康 55kg前半年熬夜较多眼睛干涩 眼睑有障碍 经常热敷毛巾 蒸汽眼罩滴眼药水 坚持锻炼 俯卧撑 开合跳 心理健康 6月份左右申请过劳动仲裁 迟迟拿不到钱比较着急 找
  • 初步学习MapReduce编程——编程实现文件合并和去重操作

    对于两个输入文件 即文件A和文件B 编写MapReduce程序 对两个文件进行合并 并剔除其中重复的内容 得到一个新的输出文件C 数据放TXT文件时 不能多出数据外的空行光标 不然运行程序时会显示错误 For input string 直接
  • From System Services Freezing to System Server Shutdown in Android: All You Need ....阅读报告

    From System Services Freezing to System Server Shutdown in Android All You Need Is a Loop in an App 阅读笔记 概述 这篇文章从安卓系统进程S
  • angular指令和管道_Angular单元测试开发人员指南—第2部分(服务,管道,指令)

    angular指令和管道 Explore how to write test cases for shared services Http services pipes and attribute directives in Angular
  • SpringBoot常用注解

    实体类常用注解 Data 注在类上 提供类的get set equals hashCode canEqual toString方法 AllArgsConstructor 注在类上 提供类的全参构造 NoArgsConstructor 注在类
  • Linux基础命令-date设置时间

    Linux基础命令 history历史记录 文章目录 前言 一 date命令的介绍 二 语法及参数 2 1 用help或man查看语法 2 2 常用参数 三 参考实例 3 1 以默认格式输出系统当前的时间和日期 3 2 按照 年 月 日 的
  • 深入理解Java中的自增

    引言 i 和 i的区别在学习程序设计的时候应该就已经学过 一个是用完再加 一个是加完再用 那么考虑一下下面的代码 int i 0 for int j 0 j lt 100 j i i 这个运行完 i的值应该是多少呢 深入理解自增 上面的代码