JVM筆記:i++ 和++i的字節碼原理詳解

前言

一個關於i++和++i的原理詳解,對字節碼不太熟悉的能夠先了解下字節碼相關知識,由於從字節碼角度更能理解二者的原理。bash

案例

int i = 0, j = 0;
        i = i++;
        j =++ j;
        System.out.println("args = [" + i + "]");
        System.out.println("args = [" + j + "]");
輸出結果:
args = [0]
args = [1]
複製代碼

對於i++,++i可能你們的第一反應的理解是:前者先運算再自增賦值,後者先自增賦值在運算。那麼上面那個例子爲何會得出這個結果,下面查看其字節碼,輸出的我就忽略不展現了。post

0: iconst_0
         1: istore_1
         2: iconst_0
         3: istore_2
         4: iload_1
         5: iinc          1, 1
         8: istore_1
         9: iinc          2, 1
        12: iload_2
        13: istore_2
複製代碼

0 ~ 3行很容易理解,在局部變量保存i和j 分別設值爲0。注意看4 ~ 8和9 ~ 13,他們分別表明的是i=i++j=++j。他們之間的差異就在於load指令一個在自增指令`iinc``前,一個在後學習

這裏說一下iinc指令,它是int類型的局部變量自增指令(將後者數據加到前者下標的int類型局部變量中)。能夠看到這個指令和下一條指令之間左邊的行號是少了兩行的,那是由於這個指令顯示的是一條,可是幹了三件事,讀取該值,自增長一,將自增後的值保存回局部變量。spa

下面開始分析,執行i=i++操做時(4 ~ 8),首先iload_1使局部變量i(0)入棧,而後執行iinc注意,執行完iinc後局部變量表中的i是已經變成1了,iinc並非吃乾飯的,可是前一條指令入棧的值沒變,仍是0,而後緊接着對i作等號賦值操做的istore_1指令將棧頂的0又保存回了局部變量1(i)中,因此局部變量表中的值變化是:0 ~ 1 ~ 0;3d

不一樣的是,執行j=j++操做時(9 ~ 13),先自增後局部變量表中j的值已經變成了1,而後執行=號賦值操做,將++j這一整個iinc後的結果也就是局部變量表2中的值入棧(iload_2),而後賦值給j(istore_2)。code

從上面的理解中能夠看到俗稱的:先運算在賦值和先賦值在運算,其原理就在於iinc指令是否先於局部變量的入棧賦值操做,下面再給幾個拓展例子看看。對象

例一:
       int i = 0, j = 0;
        i=i++ + i++;
        j= ++j + j++; 
例二:
       int i = 0, j = 0;
        j=i++ + i++;

例三:
        int i = 0, j = 0;
        j=i++ + ++i;

例四:
       int i = 0, j = 0;
        j=i++ + ++j;
複製代碼

上面我不列答案,你們能夠本身先猜想是多少,而後在看看本身分析的對不對。get

虛假的分割線-------------------------------------------------------------------

例一:第一個自增和前面的步驟同樣,入棧i而後自增iinc,可是第二個自增會從新把i從局部變量表入棧,此時i通過前面自增已經爲1了,而後第二個自增後再賦值,結果就是i=0+1且此時局部變量表中的i值爲2(這是等於號賦值前的值,賦值後又從新保存變爲1了)。 j的運算我就簡要說一下了,和前面相似:先iinc自增運算(差別),而後j(1)入棧,而後出棧保存,再入棧j(1)入棧,自增運算,而後相加。結果是j=1+1,且此時局部變量表中的j爲2,下面是字節碼:string

4: iload_1
         5: iinc          1, 1
         8: iload_1
         9: iinc          1, 1
        12: iadd
        13: istore_1
        14: iinc          2, 1
        17: iload_2
        18: iload_2
        19: iinc          2, 1
        22: iadd
        23: istore_2
複製代碼

例二:例二和例一不一樣的是最後的賦值對象從自身變成了其餘人,因此這裏和例一i運算解析是如出一轍的,只不過是最後括號內那句話中的賦值對象變成了j,因此此時i=2,j=1class

例三:例三和例一j運算解析也是相似的,一樣改變的是最後的賦值對象`,因此此時i=2,j=2

例四:例四雖然有點小不一樣,可是其原理仍是同樣的,先入棧i再自增運算i++,此時棧中的i爲,0,變量表中的i爲1;而後自增運算++j,在入棧j,相加得出結果再保存回變量表,因此結果就是j=0+1,這裏得出的j=1是相加運算得出的j=1,並非自增指令得出的j=1,好比此時i爲4的話,結果就是i=5,j=5

4: iload_1
      5: iinc          1, 1
      8: iinc          2, 1
      11: iload_2
      12: iadd
      13: istore_2
複製代碼

總結

其實之前也看過i++和++i區別的文章,可是看過不就又忘了,最近在學習JVM相關知識,結果字節碼對這一原理有了更深的瞭解,起碼在腦子裏這個知識點的保質期會久一點。

相關文章
相關標籤/搜索