【Java】深入理解i++、++i

先看两个例子

示例一

public class Test {
    public static void main(String[] args) {
        int i = 0;
        int j = i++;

        System.out.println("i=" + i);
        System.out.println("j=" + j);
    }
}

示例二 

public class Test {
    public static void main(String[] args) {
        int i = 0;
        i = i++;
        int j = i++;

        System.out.println("i=" + i);
        System.out.println("j=" + j);
    }
}

结果都是

i=1
j=0

怎么解释?我猜你会说:“i先赋值给j,再自增”

额。。。可以这么理解,但代码在实际执行过程中不是这样的。因为在运算符优先级中,++优先于=,所以++会先运行,赋值后运行。

知识储备

这里简单说明,详细解释可网上查阅

1、运算符优先级:++优先于=,所以++会先运行,赋值后运行。

2、Java虚拟机的栈帧

        局部变量表:用于存放临时变量,++是在这里面操作的

        操作数栈:先进后出,用于数据操作,如加、减、乘与除的计算等等

        动态链接:本文用不到,无视

        方法返回地址:本文用不到,无视

3、Java虚拟机字节码指令

iconst_<i>:将数据<i>压入【操作数栈】,这里的i是数值

注:

iconst_<i>:i的取值范围是[-1,5]

bipush:[-128,127]

sipush:[-32768,32767]

ldc :[-2147483648,2147483647]

istore_<i>:将【操作数栈】栈顶的数据弹出(弹出后,栈里没有了),并存到【局部变量表】的<i>位置

注:静态变量i从0开始,非静态从1开始,所以后面代码里看到的是1开始的

iload_<i>:将【局部变量表】的<i>位置的数据,加载压入到【操作数栈】

iinc          <i>, <j>:将【局部变量表】的<i>位置的数据,加上<j> 

注:iinc时,【操作数栈】没有变化

下面进入正题

先用jdk自带的工具把【示例一代码】反编译成字节码。

获取class文件

javac Test.java

反汇编,得到字节码

javap -c Test.class

得到字节码代码 

public class com.test.Test {
  public com.test.Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_0
       1: istore_1
       2: iload_1
       3: iinc          1, 1
       6: istore_2
       7: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      10: new           #3                  // class java/lang/StringBuilder
      13: dup
      14: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      17: ldc           #5                  // String i=
      19: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      22: iload_1
      23: invokevirtual #7                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      26: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      29: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      32: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      35: new           #3                  // class java/lang/StringBuilder
      38: dup
      39: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      42: ldc           #10                 // String j=
      44: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      47: iload_2
      48: invokevirtual #7                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      51: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      54: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      57: return
}

解读前5行

0: iconst_0                》》常量0入【操作数栈】
1: istore_1                 》》将【操作数栈】栈顶的数据弹出,并存到【局部变量表】1的位置,此时i=0
2: iload_1                  》》将【局部变量表】1位置的数据,压入【操作数栈】,此时,栈顶数据是0
3: iinc          1, 1        》》将【局部变量表】1位置的数据,加1,此时i=1
6: istore_2                》》将【操作数栈】栈顶的数据弹出,并存到【局部变量表】2的位置,此时j=0

图解

示例二的字节码代码

public class com.test.Test {
  public com.test.Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_0
       1: istore_1
       2: iload_1
       3: iinc          1, 1
       6: istore_1
       7: iload_1
       8: iinc          1, 1
      11: istore_2
      12: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      15: new           #3                  // class java/lang/StringBuilder
      18: dup
      19: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      22: ldc           #5                  // String i=
      24: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      27: iload_1
      28: invokevirtual #7                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      31: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      34: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      37: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      40: new           #3                  // class java/lang/StringBuilder
      43: dup
      44: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      47: ldc           #10                 // String j=
      49: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      52: iload_2
      53: invokevirtual #7                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      56: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      59: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      62: return
}

 直接上图解

再来个++i的示例

源码

public class Test {
    public static void main(String[] args) {
        int i = 0;
        int j = ++i;

        System.out.println("i=" + i);
        System.out.println("j=" + j);
    }
}

字节码

public class com.test.Test {
  public com.test.Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_0
       1: istore_1
       2: iinc          1, 1
       5: iload_1
       6: istore_2
       7: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      10: new           #3                  // class java/lang/StringBuilder
      13: dup
      14: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      17: ldc           #5                  // String i=
      19: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      22: iload_1
      23: invokevirtual #7                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      26: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      29: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      32: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      35: new           #3                  // class java/lang/StringBuilder
      38: dup
      39: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      42: ldc           #10                 // String j=
      44: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      47: iload_2
      48: invokevirtual #7                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      51: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      54: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      57: return
}

解读

0: iconst_0                》》常量0入【操作数栈】
1: istore_1                》》将【操作数栈】栈顶的数据弹出,并存到【局部变量表】1的位置,此时i=0
2: iinc          1, 1        》》将【局部变量表】1位置的数据,加1,此时i=1
5: iload_1                》》将【局部变量表】1位置的数据,压入【操作数栈】,此时,栈顶数据是1
6: istore_2                》》将【操作数栈】栈顶的数据弹出,并存到【局部变量表】2的位置,此时j=1

图解

 

 运行结果

i=1
j=1

 希望对你有所帮助!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/260795.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

.Net Core webapi RestFul 统一接口数据返回格式

在RestFul风格盛行的年代&#xff0c;大部分接口都需要一套统一的数据返回格式&#xff0c;那么我们怎么才能保证使用统一的json数据格式返回呢&#xff0c;下面给大家简单介绍一下&#xff1a; 假如我们需要接口统一返回一下数据格式&#xff1a; {"statusCode": …

LEFT JOIN

通過中間表説明 biz_email_sent table1 biz_email table2 biz_email_sent_address 中間表 LEFT JOIN 是 JOIN 左邊的記錄(biz_email_sent id52)全部查出&#xff0c;比如52 的記錄全部查出。 即使中間表se.sa_email_id 在 table2中找不到&#xff0c…

Centos7配置静态ip地址

目录结构 Centos7配置静态ip地址查看网关进入存放ip地址的目录修改ip地址的文件重启网络检查ip地址 Centos7配置静态ip地址 查看网关 编辑–>虚拟网络编辑器–>NAT设置 记住这个网关地址 进入存放ip地址的目录 cd /etc/sysconfig/network-scripts/ 修改ip地址的文件 …

Windows下安装MongoDB实践总结

本文记录Windows环境下的MongoDB安装与使用总结。 【1】官网下载 官网下载地址&#xff1a;Download MongoDB Community Server | MongoDB 这里可以选择下载zip或者msi&#xff0c;zip是解压后自己配置&#xff0c;msi是傻瓜式一键安装。这里我们分别对比进行实践。 【2】ZI…

LeetCode 19 删除链表的倒数第 N 个结点

题目描述 删除链表的倒数第 N 个结点 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5]示例 2&#xff1a; 输入&#xff1a;head [1], n 1…

什么是OEM分区?能不能删除OEM分区?

在某些电脑中&#xff0c;打开磁盘管理时会发现有一个OEM分区&#xff0c;那么这个OEM分区是什么意思呢&#xff1f;能不能删除呢&#xff1f;下面我们就来了解一下。 什么是OEM分区&#xff1f; OEM分区通常是品牌机厂商预装系统或软件以及一键还原的分区。OEM分区中的文件可…

在线更换Proxmox VE超融合集群Ceph OSD磁盘

因为资源紧张的原因&#xff0c;担心一旦关机&#xff0c;虚拟机因为没有空闲的资源而被冻结&#xff0c;以致于不能漂移&#xff0c;导致部分服务停止&#xff0c;只好让机房帮忙热插拔。 幸运的是&#xff0c;插上去能够被系统所识别&#xff08;/dev/sdf就是新插入的硬盘&am…

【MYSQL】-表的操作

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …

acwing-蓝桥杯C++ AB组辅导课Day2-递归习题+递推+二分

感谢梦翔老哥的蓝桥杯C AB组辅导课~ 递归习题&#xff1a; 1.递归实现组合型枚举 题意&#xff1a; 题目要求输出组合枚举&#xff0c;与排列不同&#xff0c;排列具有顺序之分&#xff0c;对于组合来说&#xff0c;是没有顺序之分的&#xff0c;所以[1,2,3]和[3,2,1]被看成同…

PyQt6 QTableWidget表格控件

锋哥原创的PyQt6视频教程&#xff1a; 2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 PyQt6 Python桌面开发 视频教程(无废话版) 玩命更新中~共计50条视频&#xff0c;包括&#xff1a;2024版 PyQt6 Python桌面开发 视频教程(无废话版…

神经网络:优化器和全连接层

SGD&#xff08;随机梯度下降&#xff09; 随机梯度下降的优化算法在科研和工业界是很常用的。 很多理论和工程问题都能转化成对目标函数进行最小化的数学问题。 举个例子&#xff1a;梯度下降&#xff08;Gradient Descent&#xff09;就好比一个人想从高山上奔跑到山谷最低…

23年12月AI烟火识别系统应用案例-北京梅兰芳故居防火系统

AI烟火识别智能视频分析系统在文化遗产保护领域的应用&#xff0c;尤其是在梅兰芳故居防火系统的部署&#xff0c;是现代科技与传统文化保护结合的典范。这篇文章将详细介绍富维烟火识别系统的设计、实施及其在23年12月在北京梅兰芳故居中的应用。 背景介绍 ● 梅兰芳故居的重要…

Ubuntu20.04.2-mate上Lazarus安装与测试

简言 Lazarus采用RAD方式界面开发&#xff0c;一套代码可交差编译出windows、ios、android、solaris、BSD等 各平台运行的程序&#xff0c;在unbuntu的repo中有2.2.0版本可用&#xff0c;在sourceforge上有2.2.6版本和3.0.0的Rolling版可下载安装&#xff0c;但感觉上2.2.0和2…

【实时绘画】comfyUI 实时绘画工作流 - 投屏

原理&#xff1a;&#xff08;可用方案&#xff09; 1&#xff1a;利用DesignDoll&#xff08;人体控制模型)comfyUI实现 实时绘制人物对话场景 2&#xff1a;利用krita投影 实时绘制 场景人物 3&#xff1a;利用posemy.art进行实时绘画 设置有关模型。如果要开启lora&#xff…

C/C++与MySQL:多线程、大并发和异步操作的实践

C/C与MySQL&#xff1a;多线程、大并发和异步操作的实践 在前面的文章中&#xff0c;我们介绍了如何使用C/C调用MYSQL API进行基本的数据库操作。然而&#xff0c;在实际应用中&#xff0c;特别是面对大量用户请求和高并发场景时&#xff0c;单线程的数据库操作往往显得力不从…

内网离线部署Ant Design离线文档离线api antd离线组件api

我们经常会遇到需要在内网开发不方便查看api的尴尬情况&#xff0c;文本用最简单的nginx&#xff0c;让你十分钟部署好本地的离线antd文档&#xff0c;想在哪用在哪用&#xff0c;妈妈在也不用担心我啦 1.x及以下的只要到官网下载gh-pages分支&#xff0c;放到nginx里&#xf…

在centos7.9上安装Jenkins的安装过程

1.jenkins的安装和配置&#xff1a; 安装JDK&#xff1a; yum install -y fontconfig java-11-openjdk # 安装目录&#xff1a;/usr/lib/jvm # fontconfig 是 Linux 系统中用于配置和管理字体的一种工具 下载jenkins安装包&#xff1a; sudo wget -O /etc/yum.repos.d/jenkins…

2017年第六届数学建模国际赛小美赛B题电子邮件中的笔迹分析解题全过程文档及程序

2017年第六届数学建模国际赛小美赛 B题 电子邮件中的笔迹分析 原题再现&#xff1a; 笔迹分析是一种非常特殊的调查形式&#xff0c;用于将人们与书面证据联系起来。在法庭或刑事调查中&#xff0c;通常要求笔迹鉴定人确认笔迹样本是否来自特定的人。由于许多语言证据出现在电…

Ubuntu 常用命令之 tar 命令用法介绍

&#x1f4d1;Linux/Ubuntu 常用命令归类整理 tar 命令在 Ubuntu 系统中是用来打包和解包文件的工具。tar 命令可以将多个文件或目录打包成一个 tar 文件&#xff0c;也可以将 tar 文件解包成原来的文件或目录。 tar 命令的常用参数如下 c&#xff1a;创建一个新的 tar 文件…

记一次渗透测试信息收集(证书+c段+历史漏洞搜索)

目录 一、当资产列表挖掘不出漏洞的时候 二、信息收集之证书信息收集&#xff08;部分方式&#xff09; 三、信息收集之C段信息收集 四、信息收集之某网关RCE 一、当资产列表挖掘不出漏洞的时候 二、信息收集之证书信息收集&#xff08;部分方式&#xff09; Fofa语句&#…