美团一面问我i++跟++i的区别是什么

news/2024/11/18 11:42:13/文章来源:https://www.cnblogs.com/daimzh/p/18199375

美团一面问我i++跟++i的区别是什么

面试官:“i++跟++i的区别是什么?”

我:“i++是先使用然后再执行+1的操作,++i是先执行+1的操作然后再去使用i”

面试官:“那你看看下面这段代码,运行结果是什么?”

public static void main(String[] args) {int j = 0;for (int i = 0; i < 10; i++) {j = (j++);}System.out.println(j);
}

我:“我猜他肯定不是10”

面试官:

我:“哈哈.....,开个玩笑,结果为0啦”

面试官:“为什么呢?”

我:“简单来说的话,j++这个表达式每次返回的都是0,所以最终结果就是0”

对应前文提到过的:i++这种写法是先使用,再执行+1操作,如果不理解请暂停多思考思考

面试官:“小伙子不错,那你能从更底层的角度讲一讲为什么嘛?”

首先我们知道,JVM的运行时数据区域是分为好几块的,具体分布如下图所示:

现在我们主要关注其中的虚拟机栈,关于虚拟机栈,我们需要了解的是:

  1. Java虚拟机栈是由一个个栈帧组成,线程在执行一个方法时,便会向栈中放入一个栈帧。
  2. 每一个方法所对应的栈帧又包含了以下几个部分
    • 局部变量表
    • 操作数栈
    • .........

其中的局部变量表存放了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用。

局部变量表的最小存储单元为Slot(槽),其中64位长度的long和double类型的数据会占用2个Slot,其余的数据类型只占用1个。因此可以直接通过下标来进行数据访问

操作数栈对于数据的存储跟局部变量表是一样的,但是跟局部变量表不同的是,操作数栈对于数据的访问不是通过下标而是通过标准的栈操作来进行的(压入与弹出)

数据的计算是由CPU完成的,弹栈的目的就是将数据压入到CPU中

接下来我们分析下面这段代码在字节码层面的执行过程:

// 为方便阅读将对应代码也放到这里
public static void main(String[] args) {int j = 0;for (int i = 0; i < 10; i++) {j = (j++);}System.out.println(j);
}

我们进入到这段代码编译好的.class文件目录下执行:javap -c xxx.class,得到其字节码如下:

  public static void main(java.lang.String[]);Code:0: iconst_0    // 将常数0压入到操作数栈顶1: istore_1    // 将操作数栈顶元素弹出并压入到局部变量表中1号槽位,也就是j=02: iconst_0    // 将常数0压入到操作数栈顶3: istore_2	  // 将操作数栈顶元素弹出并压入到局部变量表中2号槽位,也就是i=04: iload_2     // 将2号槽位的元素压入操作数栈顶5: bipush        10   // 将常数10压入到操作数栈顶,此时操作数栈中有两个数(常数10,以及i)7: if_icmpge     21	 // 比较操作数栈中的两个数,如果i>=10,跳转到第21行10: iload_1			 // 将局部变量表中的1号槽位的元素压入到操作数栈顶,就是将j=0压入操作数栈顶11: iinc          1, 1 // 将局部变量表中的1号元素自增1,此时局部变量表中的j=114: istore_1			 // 将操作数栈顶的元素(此时栈顶元素为0)弹出并赋值给局部变量表中的1号							      槽位(一号槽位本来已经完成自增了,但是又被赋值成了0)15: iinc          2, 1 // 将局部变量表中的2号槽位的元素自增1,此时局部变量表中的2号元素值为1,也就是i=118: goto          4	 // 第一次循环结束,跳转到第四行继续循环21: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;24: iload_125: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V28: return

我们着重关注第10,11,14行字节码指令,用图表示如下:

可以看到本来局部变量表中的j已经完成了自增(iinc指令是直接对局部变量进行自增),但是在进行赋值时是将操作数栈中的数据弹出,但是操作数栈的数据并没有经过计算,所以每次自增的结果都被覆盖了,最终结果就是0。

我们平常说的i++是先使用,然后再自增,而++i是先自增再使用。这个到底怎么理解呢?如果站在JVM的层次来讲的话,应该这样说:

  1. i++是先被操作数栈拿去用了(先执行的load指令),然后再在局部变量表中完成了自增,但是操作数栈中还是自增前的值
  2. 而++1是先在局部变量表中完成了自增(先执行innc指令),然后再被load进了操作数栈,所以操作数栈中保存的是自增后的值

这就是它们的根本区别。

关于i++的执行过程,我这里也给出一个程序及编译后的结果

public static void main(String[] args) {int i = 0;i = ++i;System.out.println(i);
}
>  0 iconst_0
>  1 istore_1
>  2 iinc 1 by 1
>  5 iload_1
>  6 istore_1
>  7 getstatic #2 <java/lang/System.out : Ljava/io/PrintStream;>
> 10 iload_1
> 11 invokevirtual #3 <java/io/PrintStream.println : (I)V>
> 14 return

大家可以自行分析


作者简介

大三退学,创业、求职、自考,一路升级

7年it从业经验,多个开源社区contributor

半自由职业,新时代数字游民

自媒体创业,专注分享成长路上的所悟所得

长期探索 个人成长职业发展副业探索

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

成功实现FaceTime数据筛选,FaceTime蓝号检测,检测是否开通FaceTime功能的实现原理

FaceTime是苹果公司iOS和macOS(以前称Mac OS X或OS X)内置的一款视频通话软件,通过Wi-Fi或者蜂窝数据接入互联网,在两个装有FaceTime的设备之间实现视频通话。其要求通话双方均具有装有FaceTime的苹果设备,苹果ID以及可接入互联网的3G/4G/5G或者Wi-Fi网络。一、Windows电脑…

一文掌握:数据湖是什么?可不是数据仓库

一、什么是数据湖 数据湖(Data Lake)是指一个大型数据存储和处理系统,它能够存储各种类型和格式的数据,包括结构化数据、半结构化数据和非结构化数据。数据湖的目的是为了让企业可以更好地管理和利用大量的数据,以便进行数据分析、机器学习等工作。数据湖通常采用分布式计…

Django markdown 实现

一、安装markdown插件 pip install django-mdeditor pip install markdown二、在 settings 配置文件 INSTALLED_APPS 中添加 mdeditor:INSTALLED_APPS = [...mdeditor,]三、插入图片没有地址,针对django3.0+修改 frame 配置,settings.py设置如下: X_FRAME_OPTIONS = SAMEORI…

一款基于C#开发的通讯调试工具(支持Modbus RTU、MQTT调试)

前言 今天大姚给大家分享一款基于C#、WPF、Prism、MaterialDesign、HandyControl开发的通讯调试工具(支持Modbus RTU、MQTT调试,界面色彩丰富):Wu.CommTool。 工具特点工具界面色彩丰富。 支持Modbus RTU、MQTT服务器、MQTT客户端。 Modbus RTU自动解析数据帧。 智能防粘包…

20240518模拟赛

C240518A. 传送门(portal) 构造一个图使得点\(1\)到\(2\)的最短路正好有\(k\)条,使构造出的图点的个数\(N\le n_5\) 考虑\(k=2^t\)那么可以轻松构造出如下的图对于其他的情况可以考虑二进制拆分,如\(k=10\)时为了,使最短路长度固定加入点\(9\)对\(k=10^9\),只需构造\(80\)个…

『手撕Vue-CLI』添加帮助和版本号

前言 经过上一篇『手撕Vue-CLI』编码规范检查之后,手撕 Vue-CLI 已经进阶到了代码规范检查这一步,已经将基本的工程搭建好了,然后代码规范约束也已经加入了,并且将 nue-cli 指令绑定到了全局当中,可以在任何地方使用了。 正文 接下来这篇文章呢,就要来实现一下大多数的命…

ASE180N08-ASEMI低压N沟道MOS管ASE180N08

ASE180N08-ASEMI低压N沟道MOS管ASE180N08编辑:ll ASE180N08-ASEMI低压N沟道MOS管ASE180N08 型号:ASE180N08 品牌:ASEMI 批号:2024+ 沟道:N沟道 导通内阻RDS(ON)Max:4.0mΩ 启动电压:2V-4V 最大漏源电流(Id):180A 漏源击穿电压(VRM):80V 正向电压:1.3V 特性:低…

垂直关系转化思维导图

线线、线面、面面垂直关系转化思维导图前言 使用方法:如果想得到更好的显示效果,可以点击全屏按钮,已经实现电脑端、手机端的适配,效果很好;电视端没有实现适配,Ipad端的适配没有测试; 思维结构图全屏 相关说明 内容继续编辑完善中,源文件存放在 draw.io 上。

C++学习----make

基本规则:touch main.c add.c sub.c add.h sub.h #新建以上文件 main函数: int main(void) {return 0; } Makefile文件: main:main.o add.o sub.ogcc -Wall -g main.o add.o sub.o -o main main.o:main.cgcc -Wall -g -c main.c -o main.o add.o:add.c add.hgcc -Wall -g -c …

logstash

遇到的问题:环境: 配置:input {beats {port=>5044codec=>plain{charset=>"UTF-8"}} }filter {mutate {remove_field => ["host","input","@timestamp","ecs","tags","agent","@ve…

2024.5.18 杂题

2024.5.18 杂题 「SMOI-R1」Apple 两个操作,修改元素,求子集和。 高位前缀和不会。考虑朴素 dp 转移 设 \(f[i]\) 表示二进制下长度为 \(n\) 的数前 \(i\) 位为 \(1\),后边为 \(0\) 的子集和。理论来说可以转移,但是比较麻烦,考虑优化状态,\(f[i][j]\) 表示前 \(\frac{n}…