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

news2024/12/22 15:24:11

美团一面问我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”

面试官:

b1b3a3d3d57c6afcc44b9cd191830e77.jpeg

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

f8794a63117c03399e5b262e6942c83f.jpeg

面试官:“为什么呢?”

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

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

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

4d47378c14f5a56761699787aae6b154.png

首先我们知道,JVM的运行时数据区域是分为好几块的,具体分布如下图所示:1cb99584fa4b90b453259b26ed8938a6.png现在我们主要关注其中的虚拟机栈,关于虚拟机栈,我们需要了解的是:

  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=0
       2: iconst_0    // 将常数0压入到操作数栈顶
       3: istore_2   // 将操作数栈顶元素弹出并压入到局部变量表中2号槽位,也就是i=0
       4: 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=1

      14: istore_1    // 将操作数栈顶的元素(此时栈顶元素为0)弹出并赋值给局部变量表中的1号             槽位(一号槽位本来已经完成自增了,但是又被赋值成了0)
      
      15: iinc          2, 1 // 将局部变量表中的2号槽位的元素自增1,此时局部变量表中的2号元素值为1,也就是i=1
      
      18: goto          4  // 第一次循环结束,跳转到第四行继续循环
      21: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      24: iload_1
      25: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
      28: return

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

44029247cc876f23d3b46d5d085fc1da.png

可以看到本来局部变量表中的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

大家可以自行分析

作者简介:

大三选择从大学退学,创业3年失败进入it行业,又从高中学历开始自考,从大专考到本科,主打一个自作自受,人生升级打怪专家。

7年it从业经验,4年基础架构及中间件开发,多个开源社区contributor。

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

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

长期探索 个人成长职业发展自媒体创业副业探索

b14062d07b3c078d03bdaa4cbe503b00.png

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微机控制电子式万能试验机WDW-20B

一.项目简介&#xff1a; 国内微机控制电子式万能试验机起步于90年代初&#xff0c;为提高企业产品的技术水平&#xff0c;公司先后引进国外先进技术&#xff0c;使公司的产品技术水平跃上了一个新的台阶。 二.使用领域&#xff1a; 该产品广泛用于金属、非金属材料的拉、压…

快充插线板怎么选?我的办公搭子是它!

最近我入手了一款倍思65W氮化镓快充插线板,不得不说真的是我的办公好搭子。在这里跟大家分享一下使用体验,希望能给正在挑选快充插线板的你一些参考。 首先,这款插线板的外观真的很讨喜。纯白色的长方体造型,简约而不失时尚感,放在办公桌上非常百搭。而且,它的体积小巧,长度比我…

024.反转链表

给定单链表的头节点 head &#xff0c;请反转链表&#xff0c;并返回反转后的链表的头节点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1]示例 2&#xff1a; 输入&#xff1a;head [1,2] 输出&#xff1a;[2,1]示例 3&#xff1a; 输…

如何从 Mac 上清空的垃圾箱中恢复已删除的文件

在 Mac 上删除的文件将被移至垃圾箱并保留 30 天&#xff0c;然后才会被永久删除。然而&#xff0c;许多 Mac 用户可能会意外清空垃圾箱&#xff0c;而没有意识到其中包含重要文件。本指南包含从清空的废纸篓中恢复 Mac 上已删除文件的所有有效方法。 当您意识到自己不小心清空…

最有效的学习方法是:费曼学习法

最有效的学习方法&#xff1a;费曼学习法 最有效的学习方法是什么&#xff1f;就叫做费曼学习法。 其实也就是为什么给别人讲课的人&#xff0c;自己进步的也很快&#xff0c;那些做知识付费赛道的人&#xff0c;自己成长的也很迅速。 费曼学习法&#xff0c;包含4个步骤&…

番外篇 | 利用PyQt5+YOLOv5来搭建目标检测系统(附可视化界面+功能介绍+源代码)

前言:Hello大家好,我是小哥谈。PyQt5是一个Python绑定的Qt库,是用于创建图形用户界面(GUI)和其他应用程序组件的工具包。PyQt5提供了许多GUI元素,如按钮、文本框、标签等,也提供了许多Qt的功能,如网络、数据库、XML等。通过PyQt5可以在Python中使用Qt的丰富功能和强大的工…

以太网技术介绍

随着通信和计算机技术的不断发展&#xff0c;无论是骨干网还是接入网&#xff0c;以太网都已成为应用场景最多&#xff0c;应用范围最广泛的技术之一。对于初次应用以太网的读者&#xff0c;本文主要给出以太网技术的基础知识&#xff0c;并对以太网涉及的部分协议进行简要说明…

大米自动化生产线揭秘:包装设备选择与维护之道

在现代化的大米生产过程中&#xff0c;自动化生产线的应用已经越来越广泛。其中&#xff0c;包装设备作为生产线上的重要一环&#xff0c;其选择与维护直接关系到产品的质量和生产效率。与星派一起探讨大米自动化生产线中包装设备的选择与维护之道。 一、包装设备的选择 在选择…

解双曲型非线性方程的Harden-Yee算法(TVD格式)

解双曲型非线性方程的Harden-Yee算法 先贴代码&#xff0c;教程后面有空再写 import matplotlib import math matplotlib.use(TkAgg) import numpy as np import matplotlib.pyplot as plt def Phiy(yy,epsi):#phi(y)if abs(yy) > epsi:phiyy abs(yy)else:phiyy (yy*yy…

【机器学习】线性回归:以房价预测为例

线性回归&#xff1a;揭秘房价预测的黑科技 一、引言二、线性回归概述三、房价预测实例数据收集与预处理特征选择与建模模型评估与优化 四、总结与展望 一、引言 在数字化时代&#xff0c;数据科学已成为推动社会进步的重要引擎。其中&#xff0c;线性回归作为数据科学中的基础…

Go 语言并发编程初体验:简洁高效

文章目录 前言GoLang 并发编程基本概念进程与线程线程和协程并行与并发GoLang的协程机制 GoLang 并发实践案例需求传统方式实现使用 goroutines 实现并发goroutine 如何通信channel 使用注意事项 总结 前言 Go语言是谷歌推出的一种的编程语言&#xff0c;可以在不损失应用程序…

语义分割——脑肿瘤图像分割数据集

引言 亲爱的读者们&#xff0c;您是否在寻找某个特定的数据集&#xff0c;用于研究或项目实践&#xff1f;欢迎您在评论区留言&#xff0c;或者通过公众号私信告诉我&#xff0c;您想要的数据集的类型主题。小编会竭尽全力为您寻找&#xff0c;并在找到后第一时间与您分享。 …

leetcode刷题——设计循环链表

题目要求我们设计循环队列&#xff0c;其特点是容量固定&#xff0c;队列循环&#xff0c;如图所示&#xff1a; 这里的队列我们以链表队列举例&#xff0c;对于循环&#xff0c;只需要把尾节点的指针指向头节点。重点是队列的容量固定&#xff1a;如何确定队列是否已满和空&am…

mamba-ssm安装卡着不动

项目中用到Mamba的小伙伴&#xff0c;causal_conv1d和 mamba-ssm两个包&#xff0c;但是会卡在Building wheel for mamba-ssm (setup.py) &#xff1a; 为了探究卡在了building的哪一步&#xff0c;加入–verbose进行显示&#xff1a; pip install mamba-ssm --no-cache-dir -…

[muduo网络库]——muduo库三大核心组件之 Poller/EpollPoller类(剖析muduo网络库核心部分、设计思想)

接着上文&#xff0c;[muduo网络库]——muduo库三大核心组件之Channel类&#xff08;剖析muduo网络库核心部分、设计思想&#xff09;&#xff0c;本章我们来学习muduo网络库中第二大核心组件Poller/EpollPoller类。 先回顾一下三大核心组件之间的关系。 接着我们进入正题。 P…

github删除自己的仓库

测试Github的时候新建了很多仓库&#xff0c;但是后来想删除&#xff0c;找了半天居然没有找到按钮。 我就推测这个删除的功能肯定藏起来了&#xff0c;后来度娘了一下&#xff0c;发现果然在一个比较隐蔽的位置&#xff0c;不知道以后这个功能会不会改到一个比较明显的位置吧…

flutter开发实战-log日志存储zip上传,发送钉钉机器人消息

flutter开发实战-log日志存储zip上传&#xff0c;发送钉钉机器人消息 当我们需要Apk上传的时候&#xff0c;我们需要将日志打包并上传到七牛&#xff0c;上传之后通过钉钉通知我们日志下载地址。 这里我使用的是loggy来处理日志 一、引入loggy日志格式插件 在工程的pubspec.…

Dijkstra求最短路 I:图解 详细代码(图解)

文章目录 题目&#xff1a;Dijkstra求最短路思路伪代码&#xff1a;代码优化优化代码&#xff1a;Java代码 总结 题目&#xff1a;Dijkstra求最短路 给定一个 n个点 m条边的有向图&#xff0c;图中可能存在重边和自环&#xff0c;所有边权均为正值。 请你求出 1号点到 n号点的…

如何打开远程桌面连接?

远程桌面连接是一项强大的功能&#xff0c;它允许我们远程访问其他计算机&#xff0c;并在远程计算机上进行操作。这对于远程办公、技术支持和远程培训等场景非常有用。本文将介绍如何在不同操作系统中打开远程桌面连接。 Windows系统 在Windows操作系统中&#xff0c;打开远程…

实用的Chrome命令 帮你打开Chrome浏览器的隐藏功能

前言 Chrome作为主力浏览器&#xff0c;支持相当丰富的第三方扩展&#xff0c;其实浏览器本身也内置了大量实用的命令。许多实用的功能并没有直接显示在Chrome的菜单上。在这篇文章中&#xff0c;我们将介绍几个实用的chrome:// commands。 通过下面整理的 Chrome 命令&#x…