从 JVM 层面理解 i++ 和 ++i 的真正区别!

共 13849字,需浏览 28分钟

 ·

2021-09-12 15:36

上一篇:深夜看了张一鸣的微博,让我越想越后怕

前言

如果只用普通的知识解释i++和++i的话

  • i++ 先将i赋值再++
  • ++i 先++再赋值

但是这简单的回答并不能入吸引面试官的眼球,如果用java字节码指令分析则效果完全不同。

代码实现

public class OperandStackTest {
/**
    程序员面试过程中, 常见的i++和++i 的区别
     */
    public static void add(){
        //第1类问题:
        int i1 = 10;
        i1++;
        System.out.println(i1);//11

        int i2 = 10;
        ++i2;
        System.out.println(i2);//11

        //第2类问题:
        int i3 = 10;
        int i4 = i3++;
        System.out.println(i3);//11
        System.out.println(i4);//10

        int i5 = 10;
        int i6 = ++i5;
        System.out.println(i5);//11
        System.out.println(i6);//11

        //第3类问题:
        int i7 = 10;
        i7 = i7++;
        System.out.println(i7);//10

        int i8 = 10;
        i8 = ++i8;
        System.out.println(i8);//11

        //第4类问题:
        int i9 = 10;
        int i10 = i9++ + ++i9;//10+12
        System.out.println(i9);//12
        System.out.println(i10);//22
    }

    public static void main(String[] args) {
        add();
    }
}
运行结果

字节码指令

通过javap -v out目录下的class文件名 在终端运行得到如下结果

 public static void add();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=10, args_size=0
         0: bipush        10
         2: istore_0
         3: iinc          0, 1
         6: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
         9: iload_0
        10: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        13: bipush        10
        15: istore_1
        16: iinc          1, 1
        19: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        22: iload_1
        23: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        26: bipush        10
        28: istore_2
        29: iload_2
        30: iinc          2, 1
        33: istore_3
        34: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        37: iload_2
        38: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        41: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        44: iload_3
        45: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        48: bipush        10
        50: istore        4
        52: iinc          4, 1
        55: iload         4
        57: istore        5
        59: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        62: iload         4
        64: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        67: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        70: iload         5
        72: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        75: bipush        10
        77: istore        6
        79: iload         6
        81: iinc          6, 1
        84: istore        6
        86: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
        89: iload         6
        91: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        94: bipush        10
        96: istore        7
        98: iinc          7, 1
       101: iload         7
       103: istore        7
       105: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
       108: iload         7
       110: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
       113: bipush        10
       115: istore        8
       117: iload         8
       119: iinc          8, 1
       122: iinc          8, 1
       125: iload         8
       127: iadd
       128: istore        9
       130: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
       133: iload         8
       135: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
       138: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
       141: iload         9
       143: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
       146: return

解释以上运行结果

第一类问题

对应的指令为

先将i1的值为10入栈(bipush),然后将int类型的值从栈中存到局部变量表0的位置,然后执行iinc将0位置的值+1,然后将局部变量表0位置的数入栈执行输出操作

所以i1的值为11

先将i2的值为10入栈(bipush),然后将int类型的值从栈中存到局部变量表1的位置,然后执行iinc将1位置的值+1,然后将局部变量表1位置的数入栈执行输出操作

所以i2的值为11

总结

由于没有赋值操作,区别不大。

第二类问题


先将i3入栈存储到局部变量表2的位置,然后将它入栈,执行iinc将2位置的值加一,i4存储到局部表量表3的位置


所以i3是11,i4还是10

将i5入栈存储到局部变量表4的位置,由于是++i所以先iinc将4位置的值加一,然后将局部变量表4的值入栈,执行赋值操作,所以都是11。另外,Java 系列面试题和答案全部整理好了,微信搜索互联网架构师,在后台发送:2T,可以在线阅读。

第三类问题

对应字节码

先将i7入栈,然后存到局部变量表6的位置,先把i6入栈,然后把6处的值加一,由于又将这个值存储到局部变量表6处,所以产生覆盖又把值变为10。

而++i不会产生覆盖先执行加一然后再把值入栈,在赋值给局部变量表中,所以i8为11。

第四类问题

先将i9=10入栈,然后存在局部变量表8的位置
int i10 = i9++ + ++i9;
先iload将8位置的i9入栈然后执行iinc将8处的i9加一,然后执行++i9,在将8处的i9加一

此时i9=10+1+1为12,然后将8位置的i9入栈,执行add将栈中的两i9相加,得到的值存储到局部变量表9的位置

所以i10=10+12(i9++后还是10,++i9后是12,因为执行了两次iinc操作)

然后调用虚方法和静态方法,在将9处的值入栈执行输出语句。

原文链接:https://blog.csdn.net/demo_yo/article/details/118269423

感谢您的阅读,也欢迎您发表关于这篇文章的任何建议,关注我,技术不迷茫!小编到你上高速。

    · END ·
最后,关注公众号互联网架构师,在后台回复:2T,可以获取我整理的 Java 系列面试题和答案,非常齐全


正文结束


推荐阅读 ↓↓↓

1.不认命,从10年流水线工人,到谷歌上班的程序媛,一位湖南妹子的励志故事

2.如何才能成为优秀的架构师?

3.从零开始搭建创业公司后台技术栈

4.程序员一般可以从什么平台接私活?

5.37岁程序员被裁,120天没找到工作,无奈去小公司,结果懵了...

6.IntelliJ IDEA 2019.3 首个最新访问版本发布,新特性抢先看

7.这封“领导痛批95后下属”的邮件,句句扎心!

8.15张图看懂瞎忙和高效的区别!

一个人学习、工作很迷茫?


点击「阅读原文」加入我们的小圈子!

浏览 36
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报