面试时经常被问到的问题:i++与++i的区别?图文详解

i++与++i的区别

在说i++与++i的区别之前,我们不妨来看一下下面这几行代码:

这里有四个问题,分别输出的结果是什么?

    /**
     * 程序员面试过程中,常见的 i++ 与 ++i 的区别,这里需要从java的字节码文件进行分析
     */
    public void add(){
        //第一类问题
        int i1 = 10;
        i1++;
        System.out.println(i1);

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

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

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

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

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

        //第四类问题:重点,易错
        int i9 = 10;
        int i10 = i9++ + ++i9;
        System.out.println(i10);

    }

对于大多数人来说,前三个问题的输出都不成问题。关键在于最后一个问题,很多人可能没见过。
在这里插入图片描述

第一,二类问题分析

相信大家都知道,第一类问题的输出都是11,结果相同。因此很好理解。第二类问题也很好理解,输出的结果是10,11.

这里给大家解释一下,在运算中:
i++ :先引用后增加
++i :先增加后引用


i++ :先在i所在的表达式中使用i的当前值,后让i加1
++i :让i先加1,然后在i所在的表达式中使用i的新值

也就是说:
在 int i4 = i3++;这个表达式中,先执行 i4 = i3,把i3的值赋给i4,之后i3才会进行+1操作。
在 int i6 = ++i5;这个表达式中,先执行 i3+1操作,之后在进行把i3的值赋给i4的操作。

第三类问题分析

可能有的人在回答这个问题时,就会不小心掉坑里哦。我猜一下,有的人会说答案是11,11.

其实细心的人就会发现,第三类问题的答案和第二类的没区别,答案依旧是10,11.

我们来分析一下,为什么结果还是一样的呢。我们知道,i++与++i的区别就是一个先引用后增加,一个先增加后引用。如果你理解了这句话,这第三类问题就不攻自破了。他其实就是第二类问题的变形。

只不过不同的是,它把自身作为值的传递和存储对象了。会给我们造成一种错觉。

下面我从java字节码的角度带大家分析一下这个问题:(此处涉及jvm的知识,希望大家集中注意力)

首先,我将这个程序预编译成class类:
下面就是生成的class文件,此处被idea反编译回来了!

    public void add() {
        int i1 = 10;
        int i1 = i1 + 1;
        System.out.println(i1);
        int i2 = 10;
        int i2 = i2 + 1;
        System.out.println(i2);
        int i3 = 10;
        int var12 = i3 + 1;
        System.out.println(i3);
        int i5 = 10;
        int i5 = i5 + 1;
        System.out.println(i5);
        int i7 = 10;
        int var14 = i7 + 1;
        System.out.println(i7);
        int i8 = 10;
        int i8 = i8 + 1;
        System.out.println(i8);
        int i9 = 10;
        int i9 = i9 + 1;
        ++i9;
        int i10 = i9 + i9;
        System.out.println(i10);
    }

其实从这里,我们就可以看出一些头绪。
大家注意看这里:
在这里插入图片描述
此处就已经证实我之前说的,由于i++会先传值,在自增。所以i7会被先传值,而+1的值会被赋给一个新的临时变量存储起来。
而++i则会先自增,再传值,所以编译成class文件后,先自+1,后重新赋值回i8.

从而得到10,11这两个结果!

想要自己动手尝试的小伙伴,记得先将程序编译一下,快捷键是ctrl+shift+F9。然后再打开如下如所示位置即可查看被编译的class文件.
在这里插入图片描述

因此这里我就不用往下分析了。

第四类问题(重难点)

想必很多小伙伴都不明白这一题输出的答案为什么是22,为什么不是21?心里肯定在想出题还能这样出!
在这里插入图片描述

答案马上揭晓。

首先,我们需要了解一个知识点。大家都听说过java程序要想运行起来,必须得在特定的java环境中。那么,这个环境中就由一个叫JVM的虚拟机来进行java程序的一切操作和管理。

在jvm中,虚拟机栈会对方法中的变量和操作进行处理。在虚拟机栈中,有一个局部变量表和操作数栈,他们两个相互配合,完成一个方法的变量赋值,四则运算!

说到这里,估计有的人已经有点懵了。那我就先上结果把!

结论在进行表达式运算时,都是自右向左依次压入栈中
因此,此处的 int i10 = i9++ + ++i9; 相当于先进行 ++i9,在进行 i10 = i9++的操作。
所以,此处的表达式就相当于

i9 = i9 + 1;
i10 = i9+i9;
i9 = i9 + 1;

也就是下图的class文件所表示的这样!
在这里插入图片描述

其实这里的分析只是取巧,真正的分析因该是对他的字节码进行分析。

先附上它的字节码:

  0 bipush 10
  2 istore_0
  3 iinc 0 by 1
  6 getstatic #3 <java/lang/System.out>
  9 iload_0
 10 invokevirtual #4 <java/io/PrintStream.println>
 13 bipush 10
 15 istore_1
 16 iinc 1 by 1
 19 getstatic #3 <java/lang/System.out>
 22 iload_1
 23 invokevirtual #4 <java/io/PrintStream.println>
 26 bipush 10
 28 istore_2
 29 iload_2
 30 iinc 2 by 1
 33 istore_3
 34 getstatic #3 <java/lang/System.out>
 37 iload_3
 38 invokevirtual #4 <java/io/PrintStream.println>
 41 bipush 10
 43 istore 4
 45 iinc 4 by 1
 48 iload 4
 50 istore 5
 52 getstatic #3 <java/lang/System.out>
 55 iload 5
 57 invokevirtual #4 <java/io/PrintStream.println>
 60 bipush 10
 62 istore 6
 64 iload 6
 66 iinc 6 by 1
 69 istore 6
 71 getstatic #3 <java/lang/System.out>
 74 iload 6
 76 invokevirtual #4 <java/io/PrintStream.println>
 79 bipush 10
 81 istore 7
 83 iinc 7 by 1
 86 iload 7
 88 istore 7
 90 getstatic #3 <java/lang/System.out>
 93 iload 7
 95 invokevirtual #4 <java/io/PrintStream.println>
 98 bipush 10
100 istore 8
102 iload 8
104 iinc 8 by 1
107 iinc 8 by 1
110 iload 8
112 iadd
113 istore 9
115 getstatic #3 <java/lang/System.out>
118 iload 9
120 invokevirtual #4 <java/io/PrintStream.println>
123 return

上面就是此程序的字节码文件!

为了便于大家理解,我在这里引用尚硅谷的宋红康老师的一张图大致表示一下:(此处的图并不匹配本程序,只是让大家看一下结构

先解释一下马上要用到的几个操作

  1. bipush 10 :指将10压入操作数栈中
  2. istore 0 : 指将操作数栈中压入的10出栈,并存入局部变量表中的0号地址
  3. iinc 0 by 1 :指当前值+1操作
  4. iload_0 : 指将局部变量表中0号地址的值取出,存入栈中

下面我们要看得是问题四得那一部分指令:

 98 bipush 10
100 istore 8
102 iload 8
104 iinc 8 by 1
107 iinc 8 by 1
110 iload 8
112 iadd
113 istore 9
115 getstatic #3 <java/lang/System.out>
118 iload 9
120 invokevirtual #4 <java/io/PrintStream.println>

下面是大致执行过程:
它首先把10入栈,然后入

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

由于操作得指令比较多,所以博主偷了个懒,把有些指令合并成一张图表示了。希望大家见谅!

以上就是从字节码文件解释为啥输出结果是22,而不是21了。

如果对字节码不理解的,建议就记住一个结论:

表达式运算,先从右开始。因此从右自左入栈!i++ :先引用后增加
++i :先增加后引用

博主后记:

希望看到此篇博文的小伙伴,如果发现有什么不对的地方,欢迎在下方留言指正!博主一定虚心接受并改正!大家一起共同进步。如果对你有所帮助,可以给博主一个赞👍。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章