一、關(guān)于拆箱和裝箱
裝箱 就是自動將基本數(shù)據(jù)類型轉(zhuǎn)換為包裝器類型;拆箱 就是自動將包裝器類型轉(zhuǎn)換為基本數(shù)據(jù)類型
| 基本數(shù)據(jù)類型 | 包裝器類型 |
|---|---|
| int(4子節(jié)) | Integer |
| byte(1子節(jié)) | Byte |
| short(2子節(jié)) | Short |
| long(8子節(jié)) | Long |
| float(4子節(jié)) | Float |
| double(8子節(jié)) | Double |
| char(2子節(jié)) | Character |
| boolean(未定) | Boolean |
具體是怎么進行裝箱的呢?
如果需要生成一個數(shù)值的對象,那么只需要調(diào)用
Integer a = 100; //裝箱
這個過程就會根據(jù)數(shù)值創(chuàng)建對應(yīng)的 Integer 對象這就是裝箱
而拆箱呢,直接使用
int b = a; //拆箱
這個時候就是拆箱
我們用一個例子來反編譯一下,看看整個過程調(diào)用了那些方法
public class IntegerTest {
public static void main(String[] args) {
Integer a = 100;
int b = a;
}
}
反編譯之后的結(jié)果如下:
從反編譯的結(jié)果可以看到,在裝箱的時候,調(diào)用了 Integer 的 valueOf(int i) 方法;而在拆箱的時候,則調(diào)用了 Integer 的 intValue() 方法
我試了一下其他的一些基本類型
public class IntegerTest {
public static void main(String[] args) {
Double d = 200d;
double f = d;
Short aa = 1;
short bb = aa;
Long l = 10000000l;
long ll = l;
}
}
經(jīng)過反編譯
果不其然,都調(diào)用了各自包裝類型的 valueOf(xxx) 方法和 xxxValue() 方法
因此可以總結(jié)一下:
裝箱過程就是通過包裝器的 valueOf() 方法實現(xiàn)的,而拆箱過程是通過調(diào)用包裝器的 xxxValue() 方法實現(xiàn)的
二、關(guān)于 Integer.valueOf
由于 Integer 的這個方法蘊含著一些密碼,因此我們拿源代碼來分析一下
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
IntegerCache 的源碼如下
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
通過源碼可以看到,在 Interger 的 valueOf 方法中,如果數(shù)值在 [-127, 128] 范圍的時候,就會去 IntegerCache.cache 這個數(shù)組尋找有沒有存在的 Integer 對象的引用,如果有,則直接返回對象的引用,如果沒有(超過了范圍),就新建一個 Integer 對象

這個是 cache 數(shù)組中已經(jīng)存在的對象,只是一部分,數(shù)組下標范圍為 [0,255],對應(yīng)數(shù)值范圍是 [-128,127]
三、相關(guān)面試題
1.輸出以下結(jié)果
Integer a = 100;
Integer b = 100;
Integer a1 = 200;
Integer b1 = 200;
Integer a2 = new Integer(100);
a == b;
a1 == b1;
a == a2;
結(jié)果: true、false、false
a 和 b 的數(shù)值為100的時候,因為 cache 數(shù)組中已經(jīng)存在對象,因此可以直接取出;
a 和 b 的數(shù)值為200時,因為 cache 數(shù)組中沒有相應(yīng)的對象,因此只能 new 一個 Integer 對象,此時 a1 和 b1 分別指向不同的對象;
a 和 a2 比較的時候,由于 a 調(diào)用 valueOf() 進行自動裝箱,而 a2 已經(jīng)是 Integer 對象,相當(dāng)于兩個不同對象之間的比較,因為地址不同,所以肯定不等
需要注意的是,這整個過程中,都沒有用到拆箱的過程,可以通過反編譯進行驗證

2.輸出以下代碼運行的結(jié)果
Double i1 = 100.0;
Double i2 = 100.0;
Double i3 = 200.0;
Double i4 = 200.0;
i1 == i2
i3 == i4
結(jié)果:false、false。
為什么呢?由于 Double 在進行裝箱的時候也會調(diào)用 valueOf() 這個方法,因此看一下源代碼
public static Double valueOf(double d) {
return new Double(d);
}
public Double(double value) {
this.value = value;
}
可以看到,Double 并沒有 Integer 的冠軍機制,而是直接返回了一個新的 Double 對象,因此以上的四個引用都是指向不同的對象的,所以不同
PS
為什么 Double 類的 valueOf() 方法會采用與 Integer 類的 valueOf() 方法不同的實現(xiàn)呢?
很簡單:在某個范圍內(nèi)的整型數(shù)值的個數(shù)是有限的,而浮點數(shù)卻不是。
我又查看了其他幾種數(shù)據(jù)類型的 valueOf 源碼
其中,==Double 和 Float 沒有緩存機制,都是直接返回新的對象== ;==Integer、Short、Byte、Character 有緩存機制==
3.輸出以下代碼運行的結(jié)果
Boolean b1 = false;
Boolean b2 = false;
Boolean b3 = true;
Boolean b4 = true;
b1 == b2
b3 == b4
b1 == b3
結(jié)果:true、true、false
為什么會這樣呢?看一下 Boolean 的 valueOf() 的源代碼
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
其中的 TRUE 和 FALSE,代表兩個靜態(tài)成員屬性
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
因此可以知道了,每次傳入的 true 或 false,都是指向同一個 Boolean 對象,因此他們的引用肯定相同了
4.輸出以下代碼運行的結(jié)果
Integer a = 10;
Integer b = 20;
Integer c = 30;
Integer d = 30;
Integer e = 300;
Integer f = 300;
Integer g = a + b;
c == d;
e == f;
c == g;
c == (a + b);
c.equals(a + b);
結(jié)果:true、false、true、true、true
先注意這句話:當(dāng) "=="運算符的兩個操作數(shù)都是 包裝器類型的引用,則是比較指向的是否是同一個對象,而如果其中有一個操作數(shù)是表達式(即包含算術(shù)運算)則比較的是數(shù)值(即會觸發(fā)自動拆箱的過程)
前兩個沒什么好說的
第三個:由于運用了算術(shù)運算符(+),因此右邊的算式在進行計算之前會先調(diào)用 intVal() 方法進行拆箱,在進行相加,然后得到的結(jié)果30之后,由于前面是 Integer g,因此還會再調(diào)用 valueOf() 方法進行裝箱。由于此時 cache() 數(shù)組中已經(jīng)有了 30 了,因此直接指向緩存池中的 30 這個 Integer 對象。此時 c == g 比較的還是對象的地址是否相同

第四個:由于右邊是 a+b,包含算術(shù)運算,因此會調(diào)用 intVal() 方法,將左邊的 c 進行拆箱,之后又分別對 a 和 b 進行拆箱,即一共調(diào)用了三次拆箱過程,最后比較的是數(shù)值大小,而不是地址

第五個:equals() 方法會先除非自動拆箱,然后在觸發(fā)自動裝箱。a + b 的時候,會先調(diào)用兩次 intVal() 進行拆箱,然后再調(diào)用 valueOf() 方法進行裝箱,此時再用 equals() 方法比較。由于都是 Integer,且數(shù)值相等,則返回 true

5.輸出以下代碼運行的結(jié)果
Integer a = 10;
Integer b = 20;
Long g = 30L;
Long h = 20L;
g == (a + b);
g.equals(a + b);
g.equals(a + h);
結(jié)果:true、false、true
和上面一樣,同樣是使用拆箱,但是需要注意的是,Integer 類型調(diào)用 Integer.valueOf 進行拆箱,而 Long 類型調(diào)用 Long.valueOf 進行拆箱
最后一個結(jié)果是 true,因為 a + h 會使小的精度轉(zhuǎn)為大的精度,最終的 30 是 Long 類型的,因此結(jié)果是 true