面試時經常被問到的問題: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 :先增加後引用

博主後記:

希望看到此篇博文的小夥伴,如果發現有什麼不對的地方,歡迎在下方留言指正!博主一定虛心接受並改正!大家一起共同進步。如果對你有所幫助,可以給博主一個贊👍。

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