首頁  >  文章  >  Java  >  Java中的裝箱與拆箱深入理解

Java中的裝箱與拆箱深入理解

高洛峰
高洛峰原創
2017-01-24 14:03:451424瀏覽

自動裝箱和拆箱問題是Java中一個老生常談的問題了,今天我們就來一些看一下裝箱和拆箱中的若干問題。本文先講述裝箱和拆箱最基本的東西,再來看一下面試筆試中常遇到的與裝箱、拆箱相關的問題。

一.什麼是裝箱?什麼是拆箱?

在前面的文章中提到,Java為每種基本資料類型都提供了對應的包裝器類型,至於為什麼會為每種基本資料類型提供包裝器類型在此不進行闡述,有興趣的朋友可查閱相關資料。在Java SE5之前,如果要產生一個數值為10的Integer對象,必須這樣進行:

Integer i = new Integer(10);

而在從Java SE5開始就提供了自動裝箱的特性,如果要產生一個數值為10的Integer對象,只需要這樣就可以了:

Integer i = 10;

這個過程中會自動根據數值建立對應的Integer對象,這就是裝箱。

那什麼是拆箱呢?顧名思義,跟裝箱對應,就是自動將包裝器類型轉換為基本資料類型:

Integer i = 10; //装箱
int n = i; //拆箱

簡單一點說,裝箱就是自動將基本資料型別轉換為包裝器型別;拆箱就是自動將包裝器型別轉換為基本資料型態。

下表是基本資料類型對應的包裝器類型:

Java中的裝箱與拆箱深入理解

二.裝箱和拆箱是如何實現的

上一小節了解裝箱的基本概念之後,這一小節來了解一下裝箱和拆箱是如何實現的。

我們就以Interger類為例,下面看一段程式碼:

public class Main {
public static void main(String[] args) {
Integer i = 10;
int n = i;
}
}

   

反編譯class檔案之後得到如下內容:

Java中的裝箱與拆箱深入理解


?在裝箱的時候自動呼叫的是Integer的valueOf(int)方法。而在拆箱的時候自動呼叫的是Integer的intValue方法。


其他的也類似,例如Double、Character,不相信的朋友可以自己手動嘗試。


因此可以用一句話總結裝箱和拆箱的實現過程:


裝箱過程是透過呼叫包裝器的valueOf方法實現的,而拆箱過程是透過呼叫包裝器的 xxxValue方法實現的。 (xxx代表對應的基本資料型態)。


三.面試中相關的問題


雖然大多數人對裝箱和拆箱的概念都清楚,但是在面試和筆試中遇到了與裝箱和拆箱的問題卻不一定會答得上來。以下列舉一些常見的與裝箱/拆箱有關的面試題。

1.下面這段程式碼的輸出結果是什麼?

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);
}
}

   


也許有些朋友會說都會輸出false,或是也有朋友會說都會輸出true。但事實上輸出結果是:


tru​​e

false

為什麼會出現這樣的結果?輸出結果顯示i1和i2指向的是同一個對象,而i3和i4指向的是不同的對象。此時只需一看原始碼便知究竟,下面這段程式碼是Integer的valueOf方法的具體實作:

public static Integer valueOf(int i) {
if(i >= -128 && i <= IntegerCache.high)
return IntegerCache.cache[i + 128];
else
return new Integer(i);
}

   

而IntegerCache類的實作為:

private static class IntegerCache {
static final int high;
static final Integer cache[];
static {
final int low = -128;
// high value may be configured by property
int h = 127;
if (integerCacheHighPropValue != null) {
// Use Long.decode here to avoid invoking methods that
// require Integer&#39;s autoboxing cache to be initialized
int i = Long.decode(integerCacheHighPropValue).intValue();
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - -low);
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
}
private IntegerCache() {}
}

而IntegerCache類的實作為:

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);
}
}

而其中IntegerCache類的實作為:

public class Main {
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);
}
}

程式碼可以看出,在透過valueOf方法建立Integer物件的時候,如果數值在[-128,127]之間,便傳回指向IntegerCache.cache中已經存在的物件的參考;否則建立一個新的Integer物件。


上面的程式碼中i1和i2的數值為100,因此會直接從cache中取已經存在的對象,所以i1和i2指向的是同一個對象,而i3和i4則是分別指向不同的對象。

2.下面這段程式碼的輸出結果是什麼?

public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}

   


也許有的朋友會認為跟上面一道題目的輸出結果相同,但事實上卻不是。實際輸出結果為:


false

false

至於具體為什麼,讀者可以去查看Double類的valueOf的實作。

Java中的裝箱與拆箱深入理解


在這裡只解釋一下為什麼Double類別的valueOf方法會採用與Integer類別的valueOf方法不同的實作。很簡單:在某個範圍內的整數數值的數量是有限的,而浮點數卻不是。


注意,Integer、Short、Byte、Character、Long這幾個類別的valueOf方法的實作是類似的。


Double、Float的valueOf方法的實作是類似的。

3.下面這段程式碼輸出結果是:

public static final Boolean TRUE = new Boolean(true);
/**
* The <code>Boolean</code> object corresponding to the primitive
* value <code>false</code>.
*/
public static final Boolean FALSE = new Boolean(false);

   


輸出結果是:


tru​​e

true🎜輸出結果是:🎜🎜🎜tru​​e🎜true🎜輸出結果是:🎜🎜🎜tru​​e🎜true

至于为什么是这个结果,同样地,看了Boolean类的源码也会一目了然。下面是Boolean的valueOf方法的具体实现:

public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}

   

而其中的 TRUE 和FALSE又是什么呢?在Boolean中定义了2个静态成员属性:

public static final Boolean TRUE = new Boolean(true);
/**
* The <code>Boolean</code> object corresponding to the primitive
* value <code>false</code>.
*/
public static final Boolean FALSE = new Boolean(false);

   

至此,大家应该明白了为何上面输出的结果都是true了。

4.谈谈Integer i = new Integer(xxx)和Integer i =xxx;这两种方式的区别。

当然,这个题目属于比较宽泛类型的。但是要点一定要答上,我总结一下主要有以下这两点区别:

1)第一种方式不会触发自动装箱的过程;而第二种方式会触发;

2)在执行效率和资源占用上的区别。第二种方式的执行效率和资源占用在一般性情况下要优于第一种情况(注意这并不是绝对的)。

5.下面程序的输出结果是什么?

public class Main {
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));
}
}

   

先别看输出结果,读者自己想一下这段代码的输出结果是什么。这里面需要注意的是:当 “==”运算符的两个操作数都是 包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。另外,对于包装器类型,equals方法并不会进行类型转换。明白了这2点之后,上面的输出结果便一目了然:

true
false
true
true
true
false
true

第一个和第二个输出结果没有什么疑问。第三句由于 a+b包含了算术运算,因此会触发自动拆箱过程(会调用intValue方法),因此它们比较的是数值是否相等。而对于c.equals(a+b)会先触发自动拆箱过程,再触发自动装箱过程,也就是说a+b,会先各自调用intValue方法,得到了加法运算后的数值之后,便调用Integer.valueOf方法,再进行equals比较。同理对于后面的也是这样,不过要注意倒数第二个和最后一个输出的结果(如果数值是int类型的,装箱过程调用的是Integer.valueOf;如果是long类型的,装箱调用的Long.valueOf方法)。

以上所述是小编给大家介绍的Java中的裝箱與拆箱深入理解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对PHP中文网站的支持!

更多Java中的裝箱與拆箱深入理解相关文章请关注PHP中文网!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn