1. i++和++i的基本概念

在几乎所有的命令式编程语言中,必然都会有 i++ 和 ++i 这种语法。有些语言中 i++ 和 ++i 既可以作为左值又可以作为右值,笔者专门测试了一下,在 Java 语言中,这两条语句都只能作为右值,而不能作为左值。同时,它们都可以作为独立的一条指令执行。

关于 i++ 和 ++i 的区别,稍微有经验的程序员都或多或少都是了解的,为了文章的完整性,本文也通过实例来简单地解释一下。

案例 1:

/** * @author pcwl * @description i++ 和 ++i 详解 */public class Test {    public static void main(String[] args) {        int i= 1;        int j = i++;        System.out.println("j = " + j);        System.out.println("i = " + i);    }}
运行结果:

案例 2:
public class Test {    public static void main(String[] args) {        int i= 1;        int j = ++i;        System.out.println("j = " + j);        System.out.println("i = " + i);    }}
运行结果:

上面的例子中可以看到,无论是 i++ 和 ++i 指令,对于 i 变量本身来说是没有任何区别的,指令执行的结果都是 i 变量的值加 1。而对于 j 来说前 ++ 和后 ++ 结果却不一样了。
int i = 1;int j = i++; // 先将i的原始值(1)赋值给变量j(1),然后i变量的值加1int j = ++i; // 先将i变量的值加1,然后将i的当前值(2)赋值给变量j(2)

2. i++ 和 ++i 的实现原理

接下来让我们深入到编译后的字节码层面上来了解 i++ 和 ++i 的实现原理,为了方便对比,将这两个指令分别放在 2 个不同的方法中执行,源代码如下:
/** * @author pcwl * @description i++ 和 ++i 详解 */public class Test {    public void testIPlus() {        int i = 0;        int j = i++;    }    public void testPlusI() {        int i = 0;        int j = ++i;    }}
将上面的源代码编译之后,使用 javap 命令查看编译生成的代码(忽略次要代码)如下:
...{  ...  public void testIPlus();    descriptor: ()V    flags: ACC_PUBLIC    Code:      stack=1, locals=3, args_size=1         0: iconst_0               // 生成整数0         1: istore_1               // 将整数0赋值给1号存储单元(即变量i)         2: iload_1                // 将1号存储单元的值加载到数据栈(此时 i=0,栈顶值为0)         3: iinc          1, 1     // 1号存储单元的值+1(此时 i=1)         6: istore_2               // 将数据栈顶的值(0)取出来赋值给2号存储单元(即变量j,此时i=1,j=0)         7: return                 // 返回时:i=1,j=0      LineNumberTable:        line 4: 0        line 5: 2        line 6: 7  public void testPlusI();    descriptor: ()V    flags: ACC_PUBLIC    Code:      stack=1, locals=3, args_size=1         0: iconst_0                // 生成整数0         1: istore_1                // 将整数0赋值给1号存储单元(即变量i)         2: iinc          1, 1      // 1号存储单元的值+1(此时 i=1)         5: iload_1                 // 将1号存储单元的值加载到数据栈(此时 i=1,栈顶值为1)         6: istore_2                // 将数据栈顶的值(1)取出来赋值给2号存储单元(即变量j,此时i=1,j=1)         7: return                  // 返回时:i=1,j=1      LineNumberTable:        line 9: 0        line 10: 2        line 11: 7}...
可以从上面的字节码文件看出,造成结果不同的原因就是:“1 号存储单元的值加 1 的操作”和“将 1 号存储单元的值加载到数据栈”的先后顺序造成的。如果前者在后者之前,则结果就是 1,反之则为 0。

3. i++ 和 ++i 使用的一些坑

i++ 和 ++i 在一些特殊场景下可能会产生意想不到的结果,本节介绍两种会导致结果混乱的使用场景,并剖析其原因。

01

案例1:i=i++导致的结果异常 

public class Test {    public static void main(String[] args) {        int i = 0;        i = i++;        System.out.println("i = " + i);   // 0    }}
运行结果:

正常来讲,执行的结果应该是:i = 1,实际结果却是:i = 0,这多少会让人有些诧异。为什么会出现这种情况呢?我们来从编码后的代码中找答案。上面的代码编译后的核心代码如下:

0: iconst_0                   // 生成整数01: istore_1                   // 将整数0赋值给1号存储单元(即变量i,i=0)2: iload_1                    // 将1号存储单元的值加载到数据栈(此时 i=0,栈顶值为0)3: iinc          1, 1         // 1号存储单元的值+1(此时 i=1)6: istore_1                   // 将数据栈顶的值(0)取出来赋值给1号存储单元(即变量i,此时i=0)7: getstatic      #16         // 下面是打印到控制台指令10: new           #22              13: dup14: ldc           #24                16: invokespecial #26                19: iload_120: invokevirtual #29               23: invokevirtual #33                26: invokevirtual #37                29: return
从编码指令可以看出,i 被栈顶值所覆盖,导致最终 i 的值仍然是 i 的初始值。 无论重复多少次 i = i++ 操作,最终 i 的值都是其初始值。
实际上: i++ 有中间缓存变量,,i = i++ 等价于:
temp = i;i = i + 1;i = temp;

所以 i 不变, 依然是0。

02

案例2 

public class Test {    public static void main(String[] args) {        int i = 0;        i++;        System.out.println("i = " + i);   // 1    }}
运行结果:

和上面上面的两端代码中唯一的差别就是 i++ 的结果有没有赋值给 i ,但是输出的 i 的结果一个加了1,而1个没有加。 这是为什么呢?我们看下编译的字节码文件:
0: iconst_0              // 生成整数0                1: istore_1              // 将整数0赋值给1号存储单元(即变量i,i=0)2: iinc          1, 1    // 1号存储单元的值+1(此时 i=1)5: getstatic     #2                  8: new           #3                  11: dup12: invokespecial #4                  15: ldc           #5                  17: invokevirtual #6                  20: iload_121: invokevirtual #7                  24: invokevirtual #8                  27: invokevirtual #9                  30: return

--------  END  ---------

最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。关注公众号并回复 888 领取,更多内容陆续奉上   



i++和++i的联系与区别!超详细原理分析!相关推荐

  1. 支持向量机(SVM)----超详细原理分析讲解

    文章目录 支持向量机(SVM) 直观的本质理解 几个基础概念 决策超平面的求解(SVM模型的推导) 最大硬间隔的寻找与公式构建 拉格朗日乘数法的应用 使用对偶问题求解 一个小例子(求解决策超平面与决策 ...

  2. mysql left/right join算法效率分析_mysql left join,right join,inner join超详细用法分析

    MySQL left join,right join,inner join超详细用法分析 下面是例子分析 表A记录如下: aID        aNum 1           a20050111 2 ...

  3. JAVA使用HttpClient模拟登录正方教务系统,爬取学籍信息和课程表成绩等,超详细登录分析和代码注解

    目录 前言 分析 代码实现 第一次GET POST登录 第二次Get 第三次GET 第四次GET 第五次GET 测试 完整代码 前言 最近在做一个APP,需要获取我们学校--武汉纺织大学皇家停水断电断 ...

  4. Kickstart无人值守安装系统(含DHCP超详细原理)

    前言: 为啥要用无人值守安装系统?很简单的答案!就两个! 一个是方便日常工作,另一个就是可以用来装逼! 常规装系统的办法有哪些? 光盘安装系统===>一个服务器DVD内置光驱百千块,百台服务器都 ...

  5. yield和return的区别-- 超详细

    首先比较下return 与 yield的区别: return:在程序函数中返回某个值,返回之后函数不在继续执行,彻底结束. yield: 带有yield的函数是一个迭代器,函数返回某个值时,会停留在某 ...

  6. 【路径规划】Dijkstra算法——超详细原理图解

    Dijkstra算法详解 1. Dijkstra算法原理  1.1. 有向图的Dijkstra算法  1.2. 无向图和栅格网络的拓展   1.2.1. 无向图   1.2.2. 栅格网络 2. Di ...

  7. eMMC工作模式 - 超详细原理讲解

    博主福利:100G+电子设计学习资源包! http://mp.weixin.qq.com/mp/homepage?__biz=MzU3OTczMzk5Mg==&hid=7&sn=ad5 ...

  8. 『 高达 购物车案例 』jQuery + Java Script 全功能实现【超详细 代码分析】

  9. v-model获取值与.value取值的区别(v-model原理分析)

    vue中使用v-model在表单元素上创建双向数据绑定,在官方文档中简单的提到了它的本质只是一个语法糖,在单向数据绑定的基础上,增加了监听用户输入事件并更新数据的功能: 对,它本质上只是一个语法糖,但 ...

最新文章

  1. LInux main.cpp 编码问题 导致影响后面的内容
  2. java httpclient访问webservice_java通过HttpClient方式和HttpURLConnection方式调用WebService接口...
  3. numpy的array合并-【老鱼学numpy】
  4. 艾伟:MOSS 2007 项目的开发步骤
  5. 如何使用 Istio 进行多集群部署管理(一)
  6. iOS开发  plist字段列表,很全
  7. python关于路径需使用的方法笔记
  8. locktty锁终端的方法
  9. 6代u笔记本完美支持win7_Z170等六代主板装WIN7后USB不能用实测超简单解决教程
  10. python的三种取整方式_python3.6 numpy 数组的多种取整方式
  11. Hackintool 3.4.7中文版 (黑苹果必备工具箱神器)
  12. Python通过selenium操作edge浏览器
  13. Justice 「未见系列 2」随夏而至,总有一款让你心动的配色!
  14. 百度分享不支持https的解决方案
  15. 总结整理Echarts双y轴曲线图(全)
  16. html5 3d自动,html5 3D微信头像自动抽奖代码
  17. Spring Boot DAY03 配置文件的注入
  18. Hung-Yi Lee homework[7]: Network Compression
  19. Idea插件开发之Gradle
  20. (全栈旅行足迹地图打卡网站 0-1)-旅行足迹文章记录(项目完结)-15(WebGIS Vue-js-go-mysql)

热门文章

  1. NetBIOS 介绍
  2. AC5 AC6 CMSIS符号对照表
  3. 【docker系列】使用非root用户安装及启动docker(rootless模式运行)
  4. 密码学ECC算法梳理总结
  5. 继续干IT的十个理由
  6. 2021年材料员-岗位技能(材料员)报名考试及材料员-岗位技能(材料员)考试报名
  7. 从功能到测开,阿里巴巴软件测试面经大揭秘,看看大厂的技术栈
  8. EA使用(一): 绘制ER图
  9. 协议离婚时夫妻共同债务如何处理
  10. win10 Powershell与CMD模式切换