詳解Java的自動(dòng)裝箱與拆箱(Autoboxing and unboxing)
專注于為中小企業(yè)提供成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站建設(shè)服務(wù),電腦端+手機(jī)端+微信端的三站合一,更高效的管理,為中小企業(yè)樺甸免費(fèi)做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動(dòng)了上千余家企業(yè)的穩(wěn)健成長(zhǎng),幫助中小企業(yè)通過(guò)網(wǎng)站建設(shè)實(shí)現(xiàn)規(guī)模擴(kuò)充和轉(zhuǎn)變。
一、什么是自動(dòng)裝箱拆箱?
很簡(jiǎn)單,下面兩句代碼就可以看到裝箱和拆箱過(guò)程
//自動(dòng)裝箱
Integer?total?=?99;
//自定拆箱
int?totalprim?=?total;
簡(jiǎn)單一點(diǎn)說(shuō),裝箱就是自動(dòng)將基本數(shù)據(jù)類型轉(zhuǎn)換為包裝器類型;拆箱就是自動(dòng)將包裝器類型轉(zhuǎn)換為基本數(shù)據(jù)類型。
下面我們來(lái)看看需要裝箱拆箱的類型有哪些:
這個(gè)過(guò)程是自動(dòng)執(zhí)行的,那么我們需要看看它的執(zhí)行過(guò)程:
public?class?Main?{
public?static?void?main(String[]?args)?{
//自動(dòng)裝箱
Integer?total?=?99;
//自定拆箱
int?totalprim?=?total;
}
}
反編譯class文件之后得到如下內(nèi)容:
javap?-c?StringTest
Integer total = 99;?
執(zhí)行上面那句代碼的時(shí)候,系統(tǒng)為我們執(zhí)行了:?
Integer total = Integer.valueOf(99);
int totalprim = total;?
執(zhí)行上面那句代碼的時(shí)候,系統(tǒng)為我們執(zhí)行了:?
int totalprim = total.intValue();
我們現(xiàn)在就以Integer為例,來(lái)分析一下它的源碼:?
1、首先來(lái)看看Integer.valueOf函數(shù)
public?static?Integer?valueOf(int?i)?{
return??i?=?128?||?i??-128???new?Integer(i)?:?SMALL_VALUES[i?+?128];
}
它會(huì)首先判斷i的大?。喝绻鹖小于-128或者大于等于128,就創(chuàng)建一個(gè)Integer對(duì)象,否則執(zhí)行SMALL_VALUES[i + 128]。
首先我們來(lái)看看Integer的構(gòu)造函數(shù):
private?final?int?value;
public?Integer(int?value)?{
this.value?=?value;
}
public?Integer(String?string)?throws?NumberFormatException?{
this(parseInt(string));
}
它里面定義了一個(gè)value變量,創(chuàng)建一個(gè)Integer對(duì)象,就會(huì)給這個(gè)變量初始化。第二個(gè)傳入的是一個(gè)String變量,它會(huì)先把它轉(zhuǎn)換成一個(gè)int值,然后進(jìn)行初始化。
下面看看SMALL_VALUES[i + 128]是什么東西:
private?static?final?Integer[]?SMALL_VALUES?=?new?Integer[256];
它是一個(gè)靜態(tài)的Integer數(shù)組對(duì)象,也就是說(shuō)最終valueOf返回的都是一個(gè)Integer對(duì)象。
所以我們這里可以總結(jié)一點(diǎn):裝箱的過(guò)程會(huì)創(chuàng)建對(duì)應(yīng)的對(duì)象,這個(gè)會(huì)消耗內(nèi)存,所以裝箱的過(guò)程會(huì)增加內(nèi)存的消耗,影響性能。
2、接著看看intValue函數(shù)
@Override
public?int?intValue()?{
return?value;
}
這個(gè)很簡(jiǎn)單,直接返回value值即可。
二、相關(guān)問(wèn)題?
上面我們看到在Integer的構(gòu)造函數(shù)中,它分兩種情況:?
1、i = 128 || i -128 ===== new Integer(i)?
2、i 128 i = -128 ===== SMALL_VALUES[i + 128]
private?static?final?Integer[]?SMALL_VALUES?=?new?Integer[256];
SMALL_VALUES本來(lái)已經(jīng)被創(chuàng)建好,也就是說(shuō)在i = 128 || i -128是會(huì)創(chuàng)建不同的對(duì)象,在i 128 i = -128會(huì)根據(jù)i的值返回已經(jīng)創(chuàng)建好的指定的對(duì)象。
說(shuō)這些可能還不是很明白,下面我們來(lái)舉個(gè)例子吧:
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);??//true
System.out.println(i3==i4);??//false
}
}
代碼的后面,我們可以看到它們的執(zhí)行結(jié)果是不一樣的,為什么,在看看我們上面的說(shuō)明。?
1、i1和i2會(huì)進(jìn)行自動(dòng)裝箱,執(zhí)行了valueOf函數(shù),它們的值在(-128,128]這個(gè)范圍內(nèi),它們會(huì)拿到SMALL_VALUES數(shù)組里面的同一個(gè)對(duì)象SMALL_VALUES[228],它們引用到了同一個(gè)Integer對(duì)象,所以它們肯定是相等的。
2、i3和i4也會(huì)進(jìn)行自動(dòng)裝箱,執(zhí)行了valueOf函數(shù),它們的值大于128,所以會(huì)執(zhí)行new Integer(200),也就是說(shuō)它們會(huì)分別創(chuàng)建兩個(gè)不同的對(duì)象,所以它們肯定不等。
下面我們來(lái)看看另外一個(gè)例子:
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);?//false
System.out.println(i3==i4);?//false
}
}
看看上面的執(zhí)行結(jié)果,跟Integer不一樣,這樣也不必奇怪,因?yàn)樗鼈兊膙alueOf實(shí)現(xiàn)不一樣,結(jié)果肯定不一樣,那為什么它們不統(tǒng)一一下呢??
這個(gè)很好理解,因?yàn)閷?duì)于Integer,在(-128,128]之間只有固定的256個(gè)值,所以為了避免多次創(chuàng)建對(duì)象,我們事先就創(chuàng)建好一個(gè)大小為256的Integer數(shù)組SMALL_VALUES,所以如果值在這個(gè)范圍內(nèi),就可以直接返回我們事先創(chuàng)建好的對(duì)象就可以了。
但是對(duì)于Double類型來(lái)說(shuō),我們就不能這樣做,因?yàn)樗谶@個(gè)范圍內(nèi)個(gè)數(shù)是無(wú)限的。?
總結(jié)一句就是:在某個(gè)范圍內(nèi)的整型數(shù)值的個(gè)數(shù)是有限的,而浮點(diǎn)數(shù)卻不是。
所以在Double里面的做法很直接,就是直接創(chuàng)建一個(gè)對(duì)象,所以每次創(chuàng)建的對(duì)象都不一樣。
public?static?Double?valueOf(double?d)?{
return?new?Double(d);
}
下面我們進(jìn)行一個(gè)歸類:?
Integer派別:Integer、Short、Byte、Character、Long這幾個(gè)類的valueOf方法的實(shí)現(xiàn)是類似的。?
Double派別:Double、Float的valueOf方法的實(shí)現(xiàn)是類似的。每次都返回不同的對(duì)象。
下面對(duì)Integer派別進(jìn)行一個(gè)總結(jié),如下圖:
下面我們來(lái)看看另外一種情況:
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);//true
System.out.println(i3==i4);//true
}
}
可以看到返回的都是true,也就是它們執(zhí)行valueOf返回的都是相同的對(duì)象。
public?static?Boolean?valueOf(boolean?b)?{
return?b???Boolean.TRUE?:?Boolean.FALSE;
}
可以看到它并沒(méi)有創(chuàng)建對(duì)象,因?yàn)樵趦?nèi)部已經(jīng)提前創(chuàng)建好兩個(gè)對(duì)象,因?yàn)樗挥袃煞N情況,這樣也是為了避免重復(fù)創(chuàng)建太多的對(duì)象。
public?static?final?Boolean?TRUE?=?new?Boolean(true);
public?static?final?Boolean?FALSE?=?new?Boolean(false);
上面把幾種情況都介紹到了,下面來(lái)進(jìn)一步討論其他情況。
Integer?num1?=?400;??
int?num2?=?400;??
System.out.println(num1?==?num2);?//true
說(shuō)明num1 == num2進(jìn)行了拆箱操作
Integer?num1?=?100;??
int?num2?=?100;??
System.out.println(num1.equals(num2));??//true
我們先來(lái)看看equals源碼:
@Override
public?boolean?equals(Object?o)?{
return?(o?instanceof?Integer)??(((Integer)?o).value?==?value);
}
我們指定equal比較的是內(nèi)容本身,并且我們也可以看到equal的參數(shù)是一個(gè)Object對(duì)象,我們傳入的是一個(gè)int類型,所以首先會(huì)進(jìn)行裝箱,然后比較,之所以返回true,是由于它比較的是對(duì)象里面的value值。
Integer?num1?=?100;??
int?num2?=?100;??
Long?num3?=?200l;??
System.out.println(num1?+?num2);??//200
System.out.println(num3?==?(num1?+?num2));??//true
System.out.println(num3.equals(num1?+?num2));??//false
1、當(dāng)一個(gè)基礎(chǔ)數(shù)據(jù)類型與封裝類進(jìn)行==、+、-、*、/運(yùn)算時(shí),會(huì)將封裝類進(jìn)行拆箱,對(duì)基礎(chǔ)數(shù)據(jù)類型進(jìn)行運(yùn)算。?
2、對(duì)于num3.equals(num1 + num2)為false的原因很簡(jiǎn)單,我們還是根據(jù)代碼實(shí)現(xiàn)來(lái)說(shuō)明:
@Override
public?boolean?equals(Object?o)?{
return?(o?instanceof?Long)??(((Long)?o).value?==?value);
}
它必須滿足兩個(gè)條件才為true:?
1、類型相同?
2、內(nèi)容相同?
上面返回false的原因就是類型不同。
Integer?num1?=?100;
Ingeger?num2?=?200;
Long?num3?=?300l;
System.out.println(num3?==?(num1?+?num2));?//true
我們來(lái)反編譯一些這個(gè)class文件:javap -c StringTest?
可以看到運(yùn)算的時(shí)候首先對(duì)num3進(jìn)行拆箱(執(zhí)行num3的longValue得到基礎(chǔ)類型為long的值300),然后對(duì)num1和mum2進(jìn)行拆箱(分別執(zhí)行了num1和num2的intValue得到基礎(chǔ)類型為int的值100和200),然后進(jìn)行相關(guān)的基礎(chǔ)運(yùn)算。
我們來(lái)對(duì)基礎(chǔ)類型進(jìn)行一個(gè)測(cè)試:
int?num1?=?100;
int?num2?=?200;
long?mum3?=?300;
System.out.println(num3?==?(num1?+?num2));?//true
上面就說(shuō)明了為什么最上面會(huì)返回true.
所以,當(dāng) “==”運(yùn)算符的兩個(gè)操作數(shù)都是 包裝器類型的引用,則是比較指向的是否是同一個(gè)對(duì)象,而如果其中有一個(gè)操作數(shù)是表達(dá)式(即包含算術(shù)運(yùn)算)則比較的是數(shù)值(即會(huì)觸發(fā)自動(dòng)拆箱的過(guò)程)。
通過(guò)上面的分析我們需要知道兩點(diǎn):?
1、什么時(shí)候會(huì)引發(fā)裝箱和拆箱?
2、裝箱操作會(huì)創(chuàng)建對(duì)象,頻繁的裝箱操作會(huì)消耗許多內(nèi)存,影響性能,所以可以避免裝箱的時(shí)候應(yīng)該盡量避免。
我有一個(gè)微信公眾號(hào),經(jīng)常會(huì)分享一些Java技術(shù)相關(guān)的干貨文章,還有一些學(xué)習(xí)資源。
如果你需要的話,可以用微信搜索“Java團(tuán)長(zhǎng)”或者“javatuanzhang”關(guān)注。
注意Java的自動(dòng)裝箱拆箱功能始于Java 1.5, 如果你的編譯器用的是更早的版本一定會(huì)出錯(cuò),我的eclipse用的1.7,就沒(méi)有錯(cuò)誤。
public?class?MyTest?{
public?static?void?main(String[]?args)?{
int?a?=?new?Integer(12);
Integer?b?=?10;
}
}
jdk5以上才支持autoboxing,應(yīng)該是你的jdk版本不夠吧。
eclipse里,project-properties,搜jdk,看看compiler那里的jdk版本,調(diào)整至1.5以上
你的方法定義在方法里面了。。
給你改了下哈。。
public class AutoboxingInAction {
/**
* @param args
*/
public static void main(String[] args) {
int[] values = { 3, 97, 55, 22, 12345 };
Integer[] objs = new Integer[values.length];
// Call method to cause boxing conversions
for (int i = 0; i values.length; i++) {
objs[i] = boxInteger(values[i]);
}
// Use method to caluse unboxing conversions
for (Integer intObject : objs) {
unboxInteger(intObject);
}
// Method to cause boxing conversion
}
public static Integer boxInteger(Integer obj) {
return obj;
}
// Method to cause unboxing conversion
public static void unboxInteger(int n) {
System.out.println("value = " + n);
}
}
Java包裝類,Wrapper~由于在java中,數(shù)據(jù)類型總共可分為兩大種,基本數(shù)據(jù)類型(值類型)和類類型(引用數(shù)據(jù)類型)?;绢愋偷臄?shù)據(jù)不是對(duì)象,所以對(duì)于要將數(shù)據(jù)類型作為對(duì)象來(lái)使用的情況,java提供了相對(duì)應(yīng)的包裝類。對(duì)于8種數(shù)據(jù)類型的包裝類分別是:
int---Integer
char---Character
float---Float
double---Double
byte---Byte
short---Short
long---Long
boolean--Boolean
所謂裝箱,就是把基本類型用它們相對(duì)應(yīng)的引用類型包起來(lái),使它們可以具有對(duì)象的特質(zhì),如我們可以把int型包裝成Integer類的對(duì)象,或者把double包裝成Double,等等。
所謂拆箱,就是跟裝箱的方向相反,將Integer及Double這樣的引用類型的對(duì)象重新簡(jiǎn)化為值類型的數(shù)據(jù)
J2SE5.0后提供了自動(dòng)裝箱與拆箱的功能,此功能事實(shí)上是編譯器來(lái)幫您的忙,編譯器在編譯時(shí)期依您所編寫的方法,決定是否進(jìn)行裝箱或拆箱動(dòng)作。
自動(dòng)裝箱的過(guò)程:每當(dāng)需要一種類型的對(duì)象時(shí),這種基本類型就自動(dòng)地封裝到與它相同類型的包裝中。
自動(dòng)拆箱的過(guò)程:每當(dāng)需要一個(gè)值時(shí),被裝箱對(duì)象中的值就被自動(dòng)地提取出來(lái),沒(méi)必要再去調(diào)用intValue()和doubleValue()方法。
自動(dòng)裝箱,只需將該值賦給一個(gè)類型包裝器引用,java會(huì)自動(dòng)創(chuàng)建一個(gè)對(duì)象。例如:
Integer i=100; //沒(méi)有通過(guò)使用new來(lái)顯示建立,java自動(dòng)完成。
自動(dòng)拆箱,只需將該對(duì)象值賦給一個(gè)基本類型即可。
例如:
int j=i;
int i = 10;
Integer j =new Integer(i); //手動(dòng)裝箱操作
int k = j.intValue(); //手動(dòng)拆箱操作
int i = 11;
Integer j = i; //自動(dòng)裝箱
int k = j //自動(dòng)拆箱
裝箱與拆箱是C#與JAVA中的概念。。
Object o=i,裝箱操作,基本數(shù)據(jù)類型轉(zhuǎn)換成Object類
(int)o,拆箱操作,Object類轉(zhuǎn)成基本數(shù)據(jù)類型
j.ToString(),i.ToString()裝箱操作,int型轉(zhuǎn)成Integer類,調(diào)用ToString()方法