從字節碼角度分析裝箱和拆箱

一.拆箱和裝箱的基本介紹

裝箱和拆箱是Java中提供的兩個有用的語法糖。java

裝箱是指將基本數據類型自動轉換爲它的包裝器類型。如int到Integer的轉換。web

拆箱是指將包裝器類型轉換爲對應的基本數據類型。如Integer到int的轉換。shell

如下是一個例子:微信

Integer num1 = 1000;
int num2 = num1;

其中num1是一個Integer類型的對象,這裏對它的賦值操做就是一個裝箱的過程。app

num2是一個基本類型int的變量,用num1給它賦值就是一個拆箱的過程。spa

二.拆箱和裝箱的字節碼實現

下面咱們從字節碼的角度看一下裝箱和拆箱是如何實現的吧。code

那麼如何獲取到Java代碼的字節碼呢?其實咱們在使用javac對Java源代碼進行編譯後獲得的class文件就是其字節碼文件。orm

可是這個class文件是二進制形式存在的,是沒法直接閱讀的。因此還須要另一個命令javap幫咱們把class文件解析爲可讀的形式對象

咱們將如下代碼保存在test.java文件中(這裏省略了類目、main方法等):blog

Integer num1 = 1000;
int num2 = num1;

javap 命令的使用
而後用javac進行編譯,再用javap -v進行反編譯。javap加上-v參數能夠看到較爲全面的信息。

javac test.java
javap -v test

使用javap反編譯後,咱們能夠獲得以下結果:

         0: sipush        1000
         3: invokestatic  #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
         6: astore_1
         7: aload_1
         8: invokevirtual #3// Method java/lang/Integer.intValue:()I
        11: istore_2

這裏的反編譯結果包含6條字節碼指令,弄懂了這些指令,也就清楚了一個普通的拆箱和裝箱的實現原理。

因爲這些指令對應着在棧幀上的各類操做,咱們首先回顧一下棧幀的概念。

棧幀回顧

相信你們對Java運行時數據區域中的Java虛擬機棧不會陌生。

每個Java方法從調用直至執行完成的過程,就對應着一個棧幀在虛擬機棧中入棧和出找的過程。

而在方法的執行過程就是在棧幀上進行各類操做。

這裏咱們主要關注棧幀中的操做數棧和局部變量表。

Java棧幀
Java棧幀

指令執行分析

下面咱們詳細分析下上述6條指令的執行過程。

Java代碼

Integer num1 = 1000;
int num2 = num1;

對應字節碼

         0: sipush        1000
         3: invokestatic  #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
         6: astore_1
         7: aload_1
         8: invokevirtual #3// Method java/lang/Integer.intValue:()I
        11: istore_2

第一句:sipush 1000

sipush 是一個入棧操做,表示把short類型的數字放入棧幀中。執行完這句後棧幀的狀況以下:

sipush 1000
sipush 1000

能夠看到1000這個數字被放到了操做數棧裏。

第二句:invokestatic #2

這一句是調用一個static方法。

那麼具體調用的是哪一個方法呢?

能夠看到後面還有一個#2參數。這個#2能夠理解爲常量池的一個索引。

咱們使用javap -v命令除了輸出了上述代碼對應的字節碼外。還有一個重要的部分是常量池。

本次反編譯的常量池以下圖所示:

在這裏插入圖片描述
在這裏插入圖片描述

能夠看到索引爲#2的位置,存儲的是一個方法描述符,其實能夠直接看到後面的註釋,這個方法就是Integer.valueOf方法。

關於常量池這裏再也不多說,你們暫時知道這回事就行。

回到咱們的主題。咱們如今知道了invokestatic #2這句指令調用的是Integer.valueOf方法,那麼入參是誰呢?

還記得上一條指令幹了什麼事嗎?

對。就是把1000這個數字放到操做數棧的棧頂。棧頂的這個數字1000其實就是Integer.valueOf的入參。

Integer.valueOf的返回值是一個Integer對象。執行完這條指令後,1000出棧,獲得的結果Integer對象入棧。這個Integer對象就是咱們Java代碼裏的num1。

這時,棧幀的狀況以下:

在這裏插入圖片描述
在這裏插入圖片描述

第三句:astore_1

這句表示把操做數棧的棧頂元素彈出,放到局部變量表下標爲1的位置。

執行以後的結果以下:

在這裏插入圖片描述
在這裏插入圖片描述

到這一句執行完,Java代碼中的Integer num1 = 1000,就算執行完了。

裝箱結論

前三句字節碼,完成了裝箱的操做。經過上述分析,咱們知道了整型的裝箱就是調用Integer.valueOf方法實現的。

對於其它的數據類型都是相似的。

後三句字節碼對應int num2 = num1的過程。

第四句:aload_1
表示將局部變量表位置爲1的元素,壓入操做數棧。

這一句執行後結果以下。

aload_1
aload_1

第五句:invokevirtual #3

和第二句化invokestatic相似,這一句也是對方法的調用執行。

只不過這裏調用的是一個示例方法。

參加第二句分析時給出的常量池狀況,這裏調用的方法是Integer的intValue方法。

操做數是上一步壓棧的Integer對象。

執行後結果以下:

在這裏插入圖片描述
在這裏插入圖片描述

第六句:istore_2

將操做數棧的元素存入局部變量表第二個位置。

在這裏插入圖片描述
在這裏插入圖片描述

到這裏就執行完了int num2 = num1

最後局部變量表的1和2的位置就分別存着num1和num2。

拆箱結論

經過後三句字節碼的分析咱們知道了,Integer類型的拆箱是使用Integer.intValue實現的。

對於其它的包裝器類型也是相似的。

三.本文小結

本文介紹了裝箱和拆箱的含義。而後經過javap命令拿到了裝拆箱的字節碼實現。並一句句地分析了這些字節碼。

最後得出了對於整數類型,裝箱是使用Integer.valueOf方法、拆箱是使用Integer.intValue方法來實現的結論。


若是本文對您有幫助,歡迎關注個人原創微信公衆號「Java技術小站」第一時間接收個人更多文章

在這裏插入圖片描述
在這裏插入圖片描述
相關文章
相關標籤/搜索