i++跟++i在JVM字節(jié)碼上的區(qū)別

大家都知道i++跟++i的區(qū)別:

  1. i++是先賦值再運算
  2. ++i是先運算再賦值

那可能很多人沒有寫過i=i++或者i=++i哎垦,這樣的騷語句镜雨,這個時候是什么樣的情況呢纯趋?
可能很多人能想到i=i++,結(jié)果i=0冷离,因為先賦值,這時候值是0纯命,所以i=0西剥。
i=++i最終結(jié)果i=1,因為是先運算+1再賦值亿汞,這時候已經(jīng)是1了瞭空。
那到底是做了什么樣的操作來實現(xiàn)這樣的結(jié)果的呢?我們來追根溯源疗我,正常情況咆畏,我們應(yīng)該先看一下class文件顯示什么東西,那我們寫了幾個最簡單的方法如下:

public class TestI {
    public void testMethod() {
        int i = 0;
        i = i + 2;
    }
    public void testMethodA() {
        int i = 0;
        i = i++;
    }
    public void testMethodB() {
        int i = 0;
        i = ++i;
    }
}

這里為了明白正常的i+1在java中是怎么處理的吴裤,增加了一個i=i+2的對照組旧找。
我們用javac TestI.java編譯成class文件,如下:

    public void testMethod() {
        byte var1 = 0;
        int var2 = var1 + 2;
    }

    public void testMethodA() {
        byte var1 = 0;
        int var2 = var1 + 1;
    }

    public void testMethodB() {
        byte var1 = 0;
        int var2 = var1 + 1;
    }

testMethodA竟然跟testMethodB是一樣的麦牺。Emmm钮蛛,因吹斯聽,應(yīng)該是IDE在翻譯字節(jié)碼的時候沒有看出來這倆的區(qū)別剖膳?那我們還是來看字節(jié)碼好了魏颓。
javap -c TestI.class命令查看字節(jié)碼如下:

這里需要的知識儲備是JVM指令集JVM 棧幀之操作數(shù)棧與局部變量表,默認(rèn)讀者了解不在贅述吱晒。

public class testJava.TestI {
  public testJava.TestI();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void testMethod();
    Code:
       0: iconst_0                          //將int型0壓棧至棧頂
       1: istore_1                          //將棧頂int型數(shù)值存入第1個本地變量
       2: iload_1                           //將第1個int型本地變量壓棧至棧頂
       3: iconst_2                          //將int型2壓棧至棧頂
       4: iadd                              //棧頂兩個數(shù)相加并進(jìn)棧
       5: istore_1                          //將棧頂int型數(shù)值存入第1個本地變量
       6: return                            //從當(dāng)前方法返回void

  public void testMethodA();
    Code:
       0: iconst_0                          //將int型0壓棧至棧頂
       1: istore_1                          //將棧頂int型數(shù)值存入第1個本地變量
       2: iload_1                           //將第1個int型本地變量壓棧至棧頂
       3: iinc          1, 1                //將指定int型變量增加指定值甸饱,可以有兩個變量,分別表示index, const仑濒,index指第index個int型本地變量叹话,const增加的值,所以是第1個本地變量增加1
       6: istore_1                          //將棧頂int型數(shù)值存入第1個本地變量
       7: return                            //從當(dāng)前方法返回void

  public void testMethodB();
    Code:
       0: iconst_0                          //將int型0壓棧至棧頂
       1: istore_1                          //將棧頂int型數(shù)值存入第1個本地變量
       2: iinc          1, 1                //將指定int型變量增加指定值躏精,第1個本地變量增加1
       5: iload_1                           //將第1個int型本地變量壓棧至棧頂
       6: istore_1                          //將棧頂int型數(shù)值存入第1個本地變量
       7: return                            //從當(dāng)前方法返回void
}

我們做了一個最簡單的i=i+2的對照組來看正常情況下這個最簡單的增量操作是長什么樣子的渣刷,它的具體流程是先把i在本地變量表里面初始化出來,再把i的值放到操作數(shù)棧矗烛,再給操作數(shù)棧放一個需要加的2辅柴,然后i跟2相加箩溃,得到的結(jié)果再存到本地變量完成相加操作。
而i = i++或者i = ++i的操作還是跟i=i+1有很大區(qū)別的碌嘀。最大的區(qū)別就是i=i+2是用了iadd涣旨,而i++是用了iinc,iadd是作用在操作數(shù)棧中的股冗,而iinc是直接在本地變量表中直接把變量增加霹陡。
來看i++++i,其實就是iinciload的運行順序的區(qū)別止状,印證了我們之前所說的這兩者的區(qū)別烹棉,我們再贅述一遍兩者的區(qū)別,看是怎么體現(xiàn)出來的:

  1. i++是先賦值再運算
  2. ++i是先運算再賦值

i++是先iload_1把0這個值推到操作數(shù)幀頂部怯疤,再iinc把本地變量表里面的i做+1操作浆洗,這個操作結(jié)束后意味著這時候在操作數(shù)棧里面代表i的值仍然是+1之前的值也就是0,而其實本地變量表中的值已經(jīng)是1集峦,但是得下一個再iload1的時候才是1代表i出戰(zhàn)伏社。
而++i,正好相反是先iinc做+1操作塔淤,然后再代表i出證摘昌,這時候的值已經(jīng)變成了1。
所以后面執(zhí)行到i=i++還是i=++i的共同代碼i=高蜂,在字節(jié)碼中也就是istore_1聪黎,把這時候棧中代表i出征的值賦值回本地變量表中的i,所以這時候i=i++操作數(shù)棧里面的0覆蓋了本地變量表中的1备恤。

所以i=i++比i++做得多此一舉事情就是多做了一個i=, 把操作數(shù)棧中的0覆蓋了本地變量表中的正確值1.

說一千道一萬挺举,什么都比不上一張圖來的直觀:

i++2.png

那既然i++是先賦值再運算,那我們多做幾次i=i++是不是就好了烘跺,比如加個while循環(huán)湘纵,like below:

    public void testMethodC() {
        int i = 0;
        for(int j = 0;j<100;j++){
            i = i++;
        }
    }

  public void testMethodC();
    Code:
       0: iconst_0                          //將int型0壓棧至棧頂
       1: istore_1                          //將棧頂int型數(shù)值存入第1個本地變量
       2: iconst_0                          //將int型0壓棧至棧頂
       3: istore_2                          //將棧頂int型數(shù)值存入第2個本地變量
       4: iload_2                           //將第2個int型本地變量推送至棧頂
       5: bipush        100                 //將將單字節(jié)的常量值100推送至棧頂
       7: if_icmpge     21                  //棧頂彈出兩個值,比較兩int型數(shù)值大小滤淳,當(dāng)結(jié)果大于等于0時跳轉(zhuǎn)到21
      10: iload_1                           //將第1個int型本地變量推送至棧頂
      11: iinc          1, 1                //將指定int型變量增加指定值梧喷,第1個本地變量增加1
      14: istore_1                          //將棧頂int型數(shù)值存入第1個本地變量
      15: iinc          2, 1                //將指定int型變量增加指定值,第2個本地變量增加1
      18: goto          4                   //跳轉(zhuǎn)到偏移位4
      21: return

其實想一想還是0哈脖咐,因為每次的i都是重新load到操作數(shù)棧的铺敌,之前的+1過來的又會被覆蓋,不管循環(huán)多少次都是一樣的:


while3.png

總結(jié):

1. i=i+1用的是iadd指令屁擅,作用是彈出操作數(shù)棧頂部的兩個值相加并把結(jié)果再壓入棧頂偿凭。
2. i++用的是iinc指令,直接操作的是本地變量表的數(shù)值派歌。
3. i=i++結(jié)果是0是因為本地變量表中i加一了但是操作數(shù)棧中的仍然是0弯囊,最后賦值的時候0把1覆蓋掉了痰哨。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市匾嘱,隨后出現(xiàn)的幾起案子斤斧,更是在濱河造成了極大的恐慌,老刑警劉巖霎烙,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件撬讽,死亡現(xiàn)場離奇詭異,居然都是意外死亡悬垃,警方通過查閱死者的電腦和手機(jī)游昼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來尝蠕,“玉大人酱床,你說我怎么就攤上這事√说瑁” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵昧捷,是天一觀的道長闲昭。 經(jīng)常有香客問我,道長靡挥,這世上最難降的妖魔是什么序矩? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮跋破,結(jié)果婚禮上簸淀,老公的妹妹穿的比我還像新娘。我一直安慰自己毒返,他們只是感情好租幕,可當(dāng)我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著拧簸,像睡著了一般劲绪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上盆赤,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天贾富,我揣著相機(jī)與錄音,去河邊找鬼牺六。 笑死颤枪,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的淑际。 我是一名探鬼主播畏纲,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼扇住,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了霍骄?” 一聲冷哼從身側(cè)響起台囱,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎读整,沒想到半個月后簿训,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡米间,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年强品,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屈糊。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡的榛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出逻锐,到底是詐尸還是另有隱情夫晌,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布昧诱,位于F島的核電站晓淀,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏盏档。R本人自食惡果不足惜凶掰,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蜈亩。 院中可真熱鬧懦窘,春花似錦、人聲如沸稚配。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽道川。三九已至毅戈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間愤惰,已是汗流浹背苇经。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留宦言,地道東北人扇单。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像奠旺,于是被迫代替她去往敵國和親蜘澜。 傳聞我的和親對象是個殘疾皇子施流,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容