小偉剛畢業時面的第一家公司就被面試官給問住了,記憶尤深啊...
如何理解Java中的自動拆箱和自動裝箱?
自動拆箱?自動裝箱?什麼鬼,聽都沒聽過啊,這...這..知識盲區...
回到家後小偉趕緊查資料,我透,這不就是問基本型別跟封裝型別嗎,面試官整啥名詞呢...
別問結果,問就是沒過。
1、 什麼是自動裝箱,自動拆箱
定義:基本資料型別和包裝類之間可以自動地相互轉換
理解:裝箱就是自動將基本資料型別轉換為封裝型別,拆箱就是自動將封裝型別轉換為基本資料型別。
我們知道,Java中提供了四大類基本資料型別,分別是:整數、浮點數、字元型和布林型,其中:
- 整數包含:byte、int、short、long
- 浮點數包含:float、double
- 字元型別:char
- 布林型別:boolean
基本資料型別相信大家一定很熟悉了吧,來來來,說說他們的取值範圍~
資料型別 | 取值範圍 |
---|---|
byte | -128 ~ 127 |
short | -32786 ~ 32767 |
int | -4294967296 ~ 4294967295 |
long | -2^64^ ~ 2^64^ -1 |
float | 3.4e-038 ~ 3.4e+038 |
double | 1.7e-308 ~ 1.7e+308 |
char | \u0000 ~ \uffff |
boolean | true 、false |
日常開發中,靠這些基本資料型別
幾乎能夠滿足我們的需求,但是基本型別終究不是物件,往重了說不滿足物件導向的開發思想,往輕了說就是使用不方便。怎麼講?例如做一些資料型別轉換,獲取int資料型別的取值範圍等等。
我們知道,類的優點在於它可以定義成員變數、成員方法,提供豐富便利的功能,因此Java在JDK1.0的時候就設計了基本資料型別的包裝類,而在JDK1.5中引入了新特性:自動裝箱和拆箱。
我們來看一下基本型別跟封裝型別之間的對應關係:
資料型別 | 封裝類 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
2、 使用包裝型別後的便捷
我們以上邊提到的資料型別轉換
為例,看看使用包裝型別後的便捷性。
小偉在資料庫中存放商品庫存用的是 varchar
型別來儲存的,所以在程式碼中的實體與之對應的是 String
,那麼問題來了,既然是庫存,那麼勢必就要用到加減乘除之類的運算,所以就需要先轉換成 數值型別(int\long\float等)來運算,我們看一下通過包裝類是如何快速轉換的「int\long\float」:
public class Test {
public static void main(String[] args) {
// 資料庫中的商品數量 number
String number = "666";
// 藉助封裝了 Integer 轉換為 int
int intVal = Integer.valueOf(number);
// 藉助封裝了 Float 轉換為 float
float floatVal = Float.valueOf(number);
// 藉助封裝了 Long 轉換為 long
long longVal = Long.valueOf(number);
// 依次輸出三個值的內容
System.out.println("int="+intVal);
System.out.println("floatVal="+floatVal);
System.out.println("longVal="+longVal);
}
}
3、 落實自動裝箱、拆箱
看完了包裝型別的便捷性後,我們再來落實到自動裝箱、自動拆箱上...
怎麼就自動裝箱,自動拆箱了呢?
上一段程式碼,看看哪是自動裝箱跟自動拆箱:
// 自動裝箱
1. Integer a = 100;
// 自動拆箱
2. int b = a;
自動裝箱,相當於Java編譯器替我們執行了 Integer.valueOf(XXX);
自動拆箱,相當於Java編譯器替我們執行了Integer.intValue(XXX);
我們證實一下,首先通過 javac 編譯得到 class 檔案,接著反編譯看看:
指令為:javap -c class檔名
,得到下圖所示:
看完編譯器替我們做的,接下來我們再通過原始碼看看,首先是自動裝箱 valueOf()
方法:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
我們可以看到,首先是if
方法, 對傳入的int
數值進行判斷,如果 i >= -128
且i <= 127
那麼就會從IntegerCache
快取中獲取指定數字的封裝類,如果不存在則 new 出一個新的封裝類,關於 IntegerCache
,其內部實現了一個Integer
的靜態常量陣列,在類載入的時候,執行static
靜態塊進行初始化-128~127
之間的Integer
物件,存放到cache
陣列中,cache
屬於常量,存放在java的方法區中,對方法區不太瞭解的小夥伴可以先留空,後面我會單獨水一篇的~
額外補充一下:上邊我們只看了Integer封裝類的自動裝箱方法,從方法中我們瞭解了在
-128~127
之間使用了快取,那麼是不是意味著別的封裝類也是這樣呢?其實不是的,首先Integer使用快取原因是該區間會被經常使用到,且數量個數比較確定,就256個值,所以為了提高效率,防止每次自動裝箱都建立一次物件例項,然後就你懂得~,而double、float浮點型是沒有使用快取的,因為小數點的原因,所以在這個區間範圍內個數是比較泛的,即不適合快取,沒有意義。
我們通過一段程式碼看看這個快取的效果吧:
public class Test2 {
public static void main(String[] args) {
Integer a = 100;
Integer b = 100;
Integer c = 200;
Integer d = 200;
System.out.println(a==b); // 列印true
System.out.println(a==b); // 列印false
}
}
接著再來看自動拆箱 intValue()
方法:
private final int value;
public int intValue() {
return value;
}
這個方法就比較簡單了,呼叫時直接返回了基本資料型別的 value 值。
至此我們看完了自動裝箱、自動拆箱,以Integer為例我們知道了使用 valueOf() 方法實現裝箱,使用 intValue() 方法實現拆箱,接下來我們再結合幾行程式碼重新回顧一下:
1. Integer a = new Integer(100);
2. Integer b = 100;
3. b+=100;
-
第一行程式碼:new 了一個 Integer 物件例項,將 int 型別的資料傳入包裝成了 Integer 型別。
-
第二行程式碼:首先我們知道 100 是 int 型別的,但是等待複製的 b 是 Integer 型別,此時就用到了自動裝箱,
b = Integer.valueOf(100)
,將100包裝成包裝類了「通過反編譯驗證」 -
第三行程式碼:用到了自動裝箱+自動拆箱,
b = b + 100 = Integer.intValye(b) + 100
此時計算結果得到的應該是 int 型別的 b,但是 b 又被限定了是 Integer 型別,所以就又要用到Integet.valueOf()
自動裝箱。
4、 上才藝
才藝一:如何理解Java中的自動拆箱和自動裝箱?
答:自動裝箱就是將基本資料型別自動轉換為封裝型別,自動拆箱是將封裝型別自動轉換為基本資料型別。
才藝二:能說一下是通過哪些方法實現自動拆箱、裝箱的嗎?
答:以Integer為例,使用Integer.valueOf()
方法實現裝箱,使用Integer.intValue()
方法實現拆箱。
推薦閱讀: