一.什麼是裝箱?什麼是拆箱?
裝箱:
在Java SE5之前,如果要生成一個數值爲10的Integer對象,必須這樣進行:
Integer i = new Integer(10);
而在從Java SE5開始就提供了自動裝箱的特性,如果要生成一個數值爲10的Integer對象
Integer i = 10;
這個過程中會自動根據數值創建對應的 Integer對象,這就是裝箱。
拆箱:
就是自動將包裝器類型轉換爲基本數據類型:、
Integer i = 10; //裝箱
int n = i; //拆箱
int(4字節) | Integer |
byte(1字節) | Byte |
short(2字節) | Short |
long(8字節) | Long |
float(4字節) | Float |
double(8字節) | Double |
char(2字節) | Character |
boolean(未定) | Boolean |
二.裝箱和拆箱是如何實現的
上一小節瞭解裝箱的基本概念之後,這一小節來了解一下裝箱和拆箱是如何實現的。
我們就以Interger類爲例,下面看一段代碼:
public class Main {
public static void main(String[] args) {
Integer i = 10;
int n = i;
}
}
從反編譯得到的字節碼內容可以看出,在裝箱的時候自動調用的是Integer的valueOf(int)方法。而在拆箱的時候自動調用的是Integer的intValue方法。
其他的也類似,比如Double、Character,不相信的朋友可以自己手動嘗試一下。
因此可以用一句話總結裝箱和拆箱的實現過程:
裝箱過程是通過調用包裝器的valueOf方法實現的,而拆箱過程是通過調用包裝器的 xxxValue方法實現的。(xxx代表對應的基本數據類型)。
三.面試中相關的問題
public class Main {
public static void main(String[] args) {
Integer i1 = 100;
Integer i2 = 100;
Integer i3 = 200;
Integer i4 = 200;
System.out.println(i1==i2);
System.out.println(i3==i4);
}
}
輸出的會是
true
false
原因:Integer的valueOf方法的具體實現:
public static Integer valueOf(int i) {
if(i >= -128 && i <= IntegerCache.high)
return IntegerCache.cache[i + 128];
else
return new Integer(i);
}
在通過valueOf方法創建Integer對象的時候,如果數值在[-128,127]之間,便返回指向IntegerCache.cache中已經存在的對象的引用;否則創建一個新的Integer對象。
public class Main {
public static void main(String[] args) {
Double i1 = 100.0;
Double i2 = 100.0;
Double i3 = 200.0;
Double i4 = 200.0;
System.out.println(i1==i2);
System.out.println(i3==i4);
}
}
輸出的是:
false
false
原因:Double類的valueOf方法會採用與Integer類的valueOf方法不同的實現。很簡單:在某個範圍內的整型數值的個數是有限的,而浮點數卻不是。
注意,Integer、Short、Byte、Character、Long這幾個類的valueOf方法的實現是類似的。
Double、Float的valueOf方法的實現是類似的。
public static void main(String[] args) {
Boolean i1 = false;
Boolean i2 = false;
Boolean i3 = true;
Boolean i4 = true;
System.out.println(i1==i2);
System.out.println(i3==i4);
}
談談Integer i = new Integer(xxx)和Integer i =xxx;這兩種方式的區別。
當然,這個題目屬於比較寬泛類型的。但是要點一定要答上,我總結一下主要有以下這兩點區別:
1)第一種方式不會觸發自動裝箱的過程;而第二種方式會觸發;
2)在執行效率和資源佔用上的區別。第二種方式的執行效率和資源佔用在一般性情況下要優於第一種情況(注意這並不是絕對的)。
public static void main(String[] args) {
Integer a = 1;
Integer b = 2;
Integer c = 3;
Integer d = 3;
Integer e = 321;
Integer f = 321;
Long g = 3L;
Long h = 2L;
System.out.println(c==d);
System.out.println(e==f);
System.out.println(c==(a+b));
System.out.println(c.equals(a+b));
System.out.println(g==(a+b));
System.out.println(g.equals(a+b));
System.out.println(g.equals(a+h));
}
輸出:
true false true true true false true
這裏面需要注意的是:當 "=="運算符的兩個操作數都是 包裝器類型的引用,則是比較指向的是否是同一個對象,而如果其中有一個操作數是表達式(即包含算術運算)則比較的是數值(即會觸發自動拆箱的過程)。另外,對於包裝器類型,equals方法並不會進行類型轉換。
原因:第三句由於 a+b包含了算術運算,因此會觸發自動拆箱過程(會調用intValue方法),因此它們比較的是數值是否相等。而對於c.equals(a+b)會先觸發自動拆箱過程,再觸發自動裝箱過程,也就是說a+b,會先各自調用intValue方法,得到了加法運算後的數值之後,便調用Integer.valueOf方法,再進行equals比較。同理對於後面的也是這樣,不過要注意倒數第二個和最後一個輸出的結果(如果數值是int類型的,裝箱過程調用的是Integer.valueOf;如果是long類型的,裝箱調用的Long.valueOf方法