深入理解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中的自增 的相关文章

  • “java.io.IOException:连接超时”和“SocketTimeoutException:读取超时”之间有什么区别

    如果我设置一个套接字 SoTimeout 并从中读取 当读取时间超过超时限制时 我会收到 SocketTimeoutException 读取超时 这是我的例子中的堆栈 java net SocketTimeoutException Read
  • Guice 忽略注入构造函数参数上的 @Nullable

    我正在使用 Guice v 3 0 并且有一个值被注入到构造函数中 该值可以为 null 因此我在构造函数中使用 Nullable 来自 javax annotations 注释了该参数 public MyClass Parameter1
  • 获取文件的锁

    我想在对特定文件开始 threo read 时获取文件上的锁定 以便其他应用程序无法读取已锁定的文件并希望在线程终止时释放锁定文件 您可以获得一个FileLock https docs oracle com javase 8 docs ap
  • 如何强制jar使用(或jar运行的jvm)utf-8而不是系统的默认编码

    我的Windows默认编码是GBK 而我的Eclipse完全是utf 8编码 因此 在我的 Eclipse 中运行良好的应用程序崩溃了 因为导出为 jar 文件时这些单词变得不可读 我必须在 bat 文件中写入以下行才能运行该应用程序 st
  • 如何将jscrollpane添加到jframe?

    我有以下源代码 有人可以给我建议如何将 jscrollpane 添加到 jframe 上吗 我尝试了几次将其添加到 jframe 但没有任何进展 它甚至没有显示 public class Form3 JFrame jframe new JF
  • 为什么Iterator接口没有add方法

    In IteratorSun 添加了remove 方法来删 除集合中最后访问的元素 为什么没有add方法来向集合中添加新元素 它可能对集合或迭代器产生什么样的副作用 好的 我们开始吧 设计常见问题解答中明确给出了答案 为什么不提供 Iter
  • 如何检测图像是否像素化

    之前有人在 SO 上提出过这样的问题 在Python中检测像素化图像 https stackoverflow com questions 12942365 detecting a pixelated image in python还有关于q
  • 如何通过注解用try-catch包装方法?

    如果应该在方法调用中忽略异常 则可以编写以下内容 public void addEntryIfPresent String key Dto dto try Map
  • org/codehaus/plexus/archiver/jar/JarArchiver(不支持的major.minor版本49.0)-Maven构建错误

    下午大家 我在尝试构建项目时收到上述错误 我很确定这与使用 Java 1 6 编译的 Maven 最新更新有关 而我们尝试构建的项目是 1 4 项目 在此之前的插件工作没有问题 因此我将以下内容添加到 POM xml 文件中以尝试强制使用现
  • 如何从日期中删除毫秒、秒、分钟和小时[重复]

    这个问题在这里已经有答案了 我遇到了一个问题 我想比较两个日期 然而 我只想比较年 月 日 这就是我能想到的 private Date trim Date date Calendar calendar Calendar getInstanc
  • 在 Clojure 中解压缩 zlib 流

    我有一个二进制文件 其内容由zlib compress在Python上 有没有一种简单的方法可以在Clojure中打开和解压缩它 import zlib import json with open data json zlib wb as
  • JAVA中遍历JSON数据

    我是 JSON 新手 我使用 HTTPUrlConnections 并在 JAVA 程序中获得一些响应 响应数据将类似于 data id 1 userId 1 name ABC modified 2014 12 04 created 201
  • IntelliJ 组织导入

    IntelliJ 是否具有类似于 Eclipse 中的组织导入功能 我拥有的是一个 Java 文件 其中多个类缺少导入 例子 package com test public class Foo public Map map public J
  • Lombok @Builder 不创建不可变对象?

    在很多网站上 我看到 lombok Builder 可以用来创建不可变的对象 https www baeldung com lombok builder singular https www baeldung com lombok buil
  • 如何从 Ant 启动聚合 jetty-server JAR?

    背景 免责声明 I have veryJava 经验很少 我们之前在 Ant 构建期间使用了 Jetty 6 的包装版本来处理按需静态内容 JS CSS 图像 HTML 因此我们可以使用 PhantomJS 针对 HTTP 托管环境运行单元
  • 无需登录即可直接从 Alfresco 访问文件/内容

    我的场景是这样的 我有一个使用 ALFRESCO CMS 来显示文件或图像的 Web 应用程序 我正在做的是在 Java servlet 中使用用户名和密码登录 alfresco 并且我可以获得该登录的票证 但我无法使用该票证直接从浏览器访
  • 禁用 Android 菜单组

    我尝试使用以下代码禁用菜单组 但它不起作用 菜单项仍然启用 你能告诉我出了什么问题吗 资源 菜单 menu xml menu menu
  • Hadoop NoSuchMethodError apache.commons.cli

    我在用着hadoop 2 7 2我用 IntelliJ 做了一个 MapReduce 工作 在我的工作中 我正在使用apache commons cli 1 3 1我把库放在罐子里 当我在 Hadoop 集群上使用 MapReduceJob
  • JMS 中的 MessageListener 和 Consumer 有什么区别?

    我是新来的JMS 据我了解Consumers能够从队列 主题中挑选消息 那么为什么你需要一个MessageListener因为Consumers会知道他们什么时候收到消息吗 这样的实际用途是什么MessageListener 编辑 来自Me
  • 使用 JFreeChart 为两个系列设置不同的 y 轴

    我正在使用 JFreeChart 使用折线图绘制两个数据系列 XYSeries 复杂的因素是 其中一个数据系列的 y 值通常远高于第二个数据系列的 y 值 假设第一个系列的 y 值约为数百万数量级 而第二个数据系列的 y 值约为数百万数量级

随机推荐

  • 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的值应该是多少呢 深入理解自增 上面的代码