通過字節碼分析Java中自動裝箱和拆箱是如何實現的

雲棲號資訊:【點擊查看更多行業資訊
在這裏您可以找到不同行業的第一手的上雲資訊,還在等什麼,快來!


Java中自動裝箱和拆箱

裝箱(Boxing),也稱爲包裝(Wrapper),是在對象中放置原語類型(primitive type)的過程,以便原語(primitive)可以作爲引用對象使用。

這裏的primitive type就是Java裏面的基本類型,所有的基本類型都有一個與之對應的類。例如,Integer類對應基本類型int。

通常,這些類稱爲包裝器(wrapper)。這些對象包裝器類擁有很明顯的名字:Integer、Long、Float、Double、Short、Byte、Character、Void和Boolean(前6個類派生於公共的超類Number)。

對象包裝器類是不可變的,即一旦構造了包裝器,就不允許更改包裝在其中的值。同時,對象包裝器類還是final,因此不能定義它們的子類。

自動裝箱是指通過類型轉換(隱式或顯式)從值類型中獲取引用類型,這部分工作是編譯器幫我們來完成的。

我們看一個常見的例子,比如我們創建一個int類型的ArrayList(因爲ArrayList的泛型是不允許基本類型的,這裏只能使用它們包裝類),我們給ArrayList添加元素,再從裏面獲取元素,一般是這麼寫的:

// int類型的自動裝箱和拆箱
ArrayList<Integer> integerArrayList = new ArrayList<>();
integerArrayList.add(1);
int i = integerArrayList.get(0);

這裏分別觸發了自動裝箱和自動拆箱,這裏的add操作觸發了一次自動裝箱操作,將int轉化爲Integer;接着從ArrayList裏面獲取元素,由於我們的目標變量類型是基本類型int,但獲取到的元素類型是Integer,所以編譯器在這裏幫我們做了拆箱的操作。

通過字節碼查看自動裝箱和自動拆箱是如何實現的

我們經常說自動裝箱、自動拆箱,到底是如何個自動法,我們來一個眼見爲實,通過查看java代碼生成的字節碼來看下編譯器對我們的代碼做了什麼。

查看字節碼的方式

這裏介紹兩種查看字節碼的方式:

  • 第一種,通過javac和javap查看:先通過javac將.java代碼編譯成.class字節碼,然後通過javap分析字節碼。
(base) tinytongtongdeMacBook-Pro% javac TestAutoWrapper.java
(base) tinytongtongdeMacBook-Pro% javap -verbose TestAutoWrapper

這樣你就能看到你的字節碼信息了。

  • 第二種,通過IDE插件ASM Bytecode Outline來查看,具體操作方式見插件說明。

查看自動裝箱和拆箱的字節碼

public static void main(String[] args) {
    // int類型的自動裝箱和拆箱
    ArrayList<Integer> integerArrayList = new ArrayList<>();
    integerArrayList.add(1);// 自動裝箱
    int i = integerArrayList.get(0);// 自動拆箱
}

我們生成這段java代碼的字節碼,核心部分如下:

// access flags 0x9
  public static main([Ljava/lang/String;)V
   ...
   L1
    LINENUMBER 15 L1
    ALOAD 1
    ICONST_1
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
    INVOKEVIRTUAL java/util/ArrayList.add (Ljava/lang/Object;)Z
    POP
   L2
    LINENUMBER 16 L2
    ALOAD 1
    ICONST_0
    INVOKEVIRTUAL java/util/ArrayList.get (I)Ljava/lang/Object;
    CHECKCAST java/lang/Integer
    INVOKEVIRTUAL java/lang/Integer.intValue ()I
    ISTORE 2
   ...

L1部分中的倒數第二行,INVOKEVIRTUAL java/util/ArrayList.add (Ljava/lang/Object;)Z,INVOKEVIRTUAL指令表示一個虛方法調用,這裏具體就是我們java代碼中的integerArrayList.add(1);,自動裝箱發生在哪呢?就在它上面,INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;,INVOKESTATIC表示靜態方法調用,這裏對應的Java語句就是Integer#valueOf()方法。

接下來我們看下自動裝箱對應的字節碼,也就是L2部分,先看INVOKEVIRTUAL java/util/ArrayList.get (I)Ljava/lang/Object;,它對應的java代碼是integerArrayList.get(0),表示從ArrayList裏面獲取到Integer類型對象。自動拆箱發生在下面,就是NVOKEVIRTUAL java/lang/Integer.intValue ()I這條指令,它對應的java方法是Integer#intValue()方法。

看到這裏相信大家對自動裝箱和拆箱有一個比較具體的認識了,說白了就是編譯器會根據情況替我們做一些工作,通過插入字節碼指令來替我們完成裝箱和拆箱操作。int對應的裝箱方法是Integer#valueOf,拆箱方法是Integer#intValue()。

自動裝箱和拆箱的觸發時機

我們接着講下自動裝箱和拆箱的觸發時機,具體如下:

* 進行 = 賦值操作(裝箱或拆箱)
* 進行+,-,*,/混合運算 (拆箱)
* 進行>,<,==比較運算(拆箱)
* 調用equals進行比較(裝箱)
* ArrayList,HashMap等集合類 添加基礎類型數據時(裝箱)

基本類型自動裝箱和拆箱方法總結

1

感興趣的同學可以自己試下各種操作下,自動裝箱和拆箱的表現,查看對應的字節碼即可。

【雲棲號在線課堂】每天都有產品技術專家分享!
課程地址:https://yqh.aliyun.com/live

立即加入社羣,與專家面對面,及時瞭解課程最新動態!
【雲棲號在線課堂 社羣】https://c.tb.cn/F3.Z8gvnK

原文發佈時間:2020-08-04
本文作者:tinyvampirepudge
本文來自:“掘金”,瞭解相關信息可以關注“掘金”

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