使用javap深入理解Java整型常量和整型變數的區別

i042416發表於2018-08-28

我下圖程式碼第五行和第九行分別定義了一個整型變數和一個整型常量:

static final int number1 = 512;

static int number3 = 545;

Java程式設計師都知道兩者的區別。

使用javap深入理解Java整型常量和整型變數的區別

下面我們就用javap將.class檔案反編譯出來然後深入研究Java裡整型變數和整型常量的區別。

使用命令列javap -c constant.ConstantFolding檢視.class檔案反編譯出來的位元組碼:

使用javap深入理解Java整型常量和整型變數的區別

結果:

使用javap深入理解Java整型常量和整型變數的區別

這些位元組碼指令的說明,在wikipedia裡有說明:

wiki:  https://en.wikipedia.org/wiki/Java_bytecode_instruction_listings

我們們Java程式設計師不需要把它們都背下來,只需要把這個網頁收藏起來,要用的時候當成字典來用就行:

使用javap深入理解Java整型常量和整型變數的區別

sipush 545: 將整數545放置到棧上

putstatic #16:

將棧上的值545賦給當前類的靜態欄位裡。

使用javap深入理解Java整型常量和整型變數的區別

那麼putstatic #16裡的#16代表什麼含義?

我們再用javap -v 引數反編譯,就能看到這個類的常量池(Constant pool). 大家看下圖藍色高亮的一行:

constant/ConstantFolding.number3:I

說明#16代表類constant.ConstantFolding的成員number3,型別為I。

使用javap深入理解Java整型常量和整型變數的區別

至此,這兩行位元組碼指令聯合起來,實際對應了我們寫的Java程式碼:

static int number3 = 545;

我們繼續分析javap反編譯出來的位元組碼。

使用javap深入理解Java整型常量和整型變數的區別

aload_0: 將序號為0的本地變數的引入載入到棧上

invokespecial: 呼叫物件例項上的成員方法,如果有返回值,方法的返回值儲存到棧上。具體呼叫的方法由#標識,可在常量池中查詢到對應的方法名。

ldc: 將常量池上代號為#<數字>的常量的值從常量池載入到棧上。

我們從下圖的常量池列表能發現,序號為#29的常量318976正是整型常量number1(512)和整型常量(623)的積。由此可以看出, number1 * number2這個表示式,因為參與運算的兩個運算元通過STATIC和FINAL修飾成為了整型常量,因此其積在編譯期就能得到,所以編譯器在編譯時就計算出來,儲存在變數池裡,序號為#29。

使用javap深入理解Java整型常量和整型變數的區別

那麼整型變數做乘法運算,對應的位元組碼又是什麼樣的呢?

從下圖序號為3的code開始:

getstatic #16: 將類的靜態成員#16載入到棧上。#16對應的成員為number3,值為545。

getstatic #18: 將類的靜態成員#18載入到棧上。#18對應的成員為number4,值為619。

imul: 執行棧上兩個整數的乘法運算。

istore_2: 將結果儲存到區域性變數2裡。

此時,我們Java程式碼裡的int product2 = number3 * number4就執行完了。

大家看到的剩下的藍色位元組碼,都對應了下面這行列印語句。

System.out.println("Value: " + product1 + " , " + product2);

從這些位元組碼也能看出,Java裡我們直接用加號進行字串拼接操作,Java編譯器在編譯時,自動使用了StringBuilder進行優化。

既然整型變數的乘積需要列印出來,因此位元組碼的iload_2將之前用istore_2儲存在區域性變數2中的計算結果又載入到棧上,這樣乘積結果最後就能輸出了。

使用javap深入理解Java整型常量和整型變數的區別

希望通過這個簡單的例子,大家能學會用javap去深入理解一些Java和JVM的細節

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/24475491/viewspace-2212997/,如需轉載,請註明出處,否則將追究法律責任。

相關文章