Java变量自增表达式 i = i++ 的底层逻辑(简述)


前言

很多老师告诉我们,i = i++ 的运算过程是 temp = i; i ++; i = temp; 所以i的值不变。但我总觉得这个temp的出现有些莫名其妙。所以在网上检索之后,把大佬们的解释做了一点总结和简化,权当拾人牙慧。

要搞懂 i = i++ 我们先要简单认识两个东西:局部变量表操作数栈

  • 操作数栈:一个临时的存储空间, 主要用于保存计算过程的变量和中间结果,
  • 局部变量表:也是一个临时的存储空间,它用于保存函数的参数以及局部变量。
  • 对于本文章而言,初学者可以把它看做两个桶:一个用来装i的值(局部变量表),另一个用来装中间过程中用到的数(操作数栈)

正文

了解了以上内容,就可以对i=i++进行理解了。不过在此之前,我们先看看单独的i++ 和 ++i的区别:

i++ 和 ++i

i++ 和 ++i,它们实际上是直接在局部变量表里修改变量的值,原地修改,不需要经过操作数栈。所以,作为语句单独使用时,没有区别。

public static void main(String[] args) {
int i = 0;
i++; // 在局部变量表直接自增
++i; // 在局部变量表直接自增,没有区别
}

那么当情况是 i = i++时; 为什么结果就是i的值不变呢?

i = i++

public static void main(String[] args) {
int i = 0;
i = i++; // i值不变
}

这就是操作数栈参与的结果,上面代码的执行过程,实际上是这样的:

  • 执行 int i = 0;

    • 把0这个常数放到操作数栈中
    • 从操作数栈顶取出常数0,然后存储到局部变量表的索引为1的位置(局部变量表[1]),这个位置就代表i的值(因为局部变量表里有args 和 i 两个元素,args的索引是0)。
  • 执行 i = i++;
    • 计算机首先看见右侧表达式中的i, 所以它把局部变量表[1]的值取出,压入操作数栈。
    • 计算机又看见了符号“++”,于是把局部变量表[1]进行自增
    • 然后计算机看见 “=” ,所以对等号左边的i进行赋值,重点来了
      • 这里赋的是哪个值呢? -- 操作数栈里的值,0。
      • 那么赋值到哪里呢? -- 局部变量表[1]。直接覆盖了自增的结果,也就是说,刚刚的自增操作,增了个寂寞。
    • 所以我们就知道了:由于刚刚是“先压栈,再自增”,所以栈里的值还是原始值,最后又覆盖回去了。
  • 同理,我们也就知道它和 i = ++i 的不同之处在哪里了
    • 计算机这次首先看见的是“++”符号,而不是i, 所以它这次先把局部变量表[1]进行自增
    • 然后计算机才看见了i,此时才把局部变量表[1]的值取出,压入操作数栈,因此,栈里的值也变成了1,最后覆盖回去就是1。

以上顺序的变化,实际上在JVM编译后的字节码文件中能够直观地看到,但是初学者对字节码很陌生,所以采用了以上的描述方式。字节码的区别其实更加直观,如下:

  • i = i++ 的执行顺序:
iconst_0  # 把int常量0压入操作数栈
istore_1 # 把操作数栈顶的值"0"存储到局部变量表[1]
iload_1 # 局部变量表[1]的值压入操作数栈顶,此时操作数栈顶为0
iinc 1 1 # 将局部变量表[1]的值,加上1, 所以此时i的值变成1
istore_1 # 将操作数栈顶的值存储到局部变量表[1], 用0覆盖了1.
  • i = ++i 的执行顺序:
iconst_0  # 同上
istore_1 # 同上
iinc 1 1 # 将局部变量表[1]的值,加上1, 此时i的值变成1
iload_1 # 局部变量表[1]的值压入操作数栈顶,此时操作数栈顶为1
istore_1 # 将操作数栈顶的值存储到局部变量表[1], 用1覆盖了1.

这个问题的进阶还有 k = i + ++i * i++ 参考文章:https://blog.csdn.net/See_Star/article/details/125206538


作者: 练块儿的程序员

出处:https://www.cnblogs.com/sunyujun16

本文版权归作者和博客园共有,欢迎转载,但必须保留此段声明,且在文章页面明显位置给出原文链接, 如有问题,可邮件sunyujun16@163.com咨询.

Java变量自增表达式 i = i++ 的底层逻辑(简述)的更多相关文章

  1. Java——变量自增(++)自减(--)

    //运算符在操作数之后,称为“后增量”.i变量自增,返回自增之前的值;//运算符在操作数之前,称为“前增量”.i变量自增,返回自增之后的值.//自减同理 public static void test ...

  2. Java变量自增和自减运算符的用法

    1.后加加(num++): 先输出运算结果再加加: public static void main(String[] args){ int num=10; int  p1=num++; System. ...

  3. JAVA 变量 数据类型 运算符 知识小结

    ---------------------------------------------------> JAVA 变量 数据类型 运算符 知识小结 <------------------ ...

  4. Lambda 表达式,Java中应用Lambda 表达式

    一.Lambda 表达式 简单来说,编程中提到的 lambda 表达式,通常是在需要一个函数,但是又不想费神去命名一个函数的场合下使用,也就是指匿名函数. 链接:知乎 先举一个普通的 Python 例 ...

  5. Java 终于有 Lambda 表达式啦~Java 8 语言变化——Lambda 表达式和接口类更改【转载】

    原文地址 en cn 下载 Demo Java™ 8 包含一些重要的新的语言功能,为您提供了构建程序的更简单方式.Lambda 表达式 为内联代码块定义一种新语法,其灵活性与匿名内部类一样,但样板文件 ...

  6. java字符串应用之表达式解析器

    一.表达式的组成    1.数字    2.运算符:+ - / * ^ % =    3.圆括号    4.变量二.运算符优先级    由高到低分别为:+-(正负号).^.*/%.+-.=    优先 ...

  7. Java精选笔记_EL表达式

    EL表达式 初始EL EL是一种可以简化JSP页面的表达式,EL表达式的语法非常简单都是以"${"符号开始,以"}"符号结束的 EL表达式是一种简单的数据&qu ...

  8. Java变量声明和赋值

    Java的8种基础类型变量声明,在得到Java 11支持后会有新的语法糖 基础数据类型一共有8种 整数类型:byte.short.int和long 小数类型:float和double 字符类型:cha ...

  9. Java代码三级跳——表达式、语句和代码块

    Java代码三级跳—表达式.语句和代码块 表达式(expression):Java中最基本的一个运算.比如一个加法运算表达式.1+2是一个表达式,a+b也是. 语句(statement):类似于平时说 ...

  10. java基础知识-lambda表达式

    一.什么是lambda? 在Java中,我们可以将一个值赋值给一个Java变量. int aValue = 129; String aString = "hello world"; ...

随机推荐

  1. System.gc 之后到底发生了什么 ?

    本文基于 OpenJDK17 进行讨论 在 JDK NIO 针对堆外内存的分配场景中,我们经常会看到 System.gc 的身影,比如当我们通过 FileChannel#map 对文件进行内存映射的时 ...

  2. Tomcat内存马分析

    前言 自己简单搭建一个Tomcat项目,IDEA里选择JavaEE,勾上web就行了 加个依赖(这样就能找到三个Context了: <dependency> <groupId> ...

  3. 用于多视角人群计数的协同通信图卷积网络 Co-Communication Graph Convolutional Network for Multi-View Crowd Counting

    Multi-Camara Methods Co-Communication Graph Convolutional Network for Multi-View Crowd Counting 论文ur ...

  4. Device Partner平台合作伙伴认证和数据安全保护

     Device Partner平台是面向AIoT产业链伙伴的一站式服务平台,伙伴可以通过平台获取最新的产品.服务与解决方案,实现智能硬件产品的开发.认证.量产和推广等全生命周期的管理,加入Harmon ...

  5. 【直播合集】HDC.Together 2023 精彩回顾!收藏勿错过~

    HDC.Together 2023 主题演讲 万象复兴,热潮澎湃,HarmonyOS 全面进化,迈入新纪元.以创新改变世界,以生态驱动未来.扬帆起航,就在此刻.新版本.新体验.新流量.新商业.新机遇. ...

  6. SQL 转置计算

    转置即旋转数据表的横纵方向,常用来改变数据布局,以便用新的角度观察.有些转置算法比较简单,比如行转列.列转行.双向转置:有些算法变化较多,比如动态转置.转置时跨行计算.关联转置等.这些转置算法对日常工 ...

  7. Pytorch-卷积神经网络CNN之ResNet的Pytorch代码实现

    先说一个小知识,助于理解代码中各个层之间维度是怎么变换的. 卷积函数:一般只用来改变输入数据的维度,例如3维到16维. Conv2d() Conv2d(in_channels:int,out_chan ...

  8. 妙用 drop-shadow 实现线条光影效果

    本文将介绍一种利用 CSS 滤镜 filter 的 drop-shadow(),实现对 HTML 元素及 SVG 元素的部分添加阴影效果,以实现一种酷炫的光影效果,用于各种不同的场景之中.通过本文,你 ...

  9. Docker 内安装 SSH 服务

    有时候我们想把 Docker 环境提供给别人,但是又不想提供宿主机,那么可以在 Docker 容器内运行 SSH 服务,使容器保持运行并提供给对方使用即可,下面我们以 CentOS 7.6 的 Doc ...

  10. log4j2 lookup漏洞修复方法

    2021.12.10凌晨,Apache Log4j远程代码执行漏洞细节被公开,参考链接:https://unit42.paloaltonetworks.com/apache-log4j-vulnera ...