裝箱(Autoboxing)和拆箱(Unboxing)
wiki
學(xué)習(xí)目標(biāo)
- Why:為什么需要裝箱和拆箱;
- 代碼更加簡潔
- 基本數(shù)據(jù)類型可以使用泛型
- What:什么是裝箱和拆箱;
- How:裝箱和拆箱是怎么實現(xiàn)的插勤?
- 裝箱:包裝類的
valueOf()
方法 - 拆箱:包裝類的
***Value()
方法
- 裝箱:包裝類的
- When:什么時候使用裝箱和拆箱
- Where:在什么地方會使用到裝箱和拆箱
- Other:裝箱和拆箱的優(yōu)點和缺點
- 優(yōu)點:代碼簡潔叹洲,可以使用泛型
- 缺點:裝箱需要創(chuàng)建新的對象,時間會比較長
裝箱和拆箱的定義
- 裝箱(Autoboxing):基本數(shù)據(jù)類型(int绍填,double等)轉(zhuǎn)換為對應(yīng)的包裝類叫做裝箱;
- 基本數(shù)據(jù)類型作為參數(shù)傳遞給一個用對應(yīng)包裝類作為參數(shù)的方法時使用裝箱;
- 將基本數(shù)據(jù)類型賦值給對應(yīng)的包裝類媳溺;
- 拆箱(Unboxing):包裝類轉(zhuǎn)換為對應(yīng)的基本數(shù)據(jù)類型叫做拆箱掀泳;
- 將包裝類作為參數(shù)傳遞給一個用對應(yīng)基本數(shù)據(jù)類型作為參數(shù)的方法時使用裝箱雪隧;
- 將包裝類賦值給對應(yīng)的基本數(shù)據(jù)類型;
Interger i = 10; //裝箱
int num = i; //拆箱
基本數(shù)據(jù)類型和對應(yīng)的包裝類
Primitive Type | Wrapper Class |
---|---|
boolean | Boolean |
byte | Byte |
char | Character |
float | Float |
int | Integer |
long | Long |
short | Short |
double | Double |
為什么需要拆箱和裝箱
-
在泛型中员舵,編譯器會進(jìn)行類型擦除脑沿,將泛型中的類型編譯為Object;對應(yīng)基礎(chǔ)數(shù)據(jù)類型马僻,無法使用泛型庄拇,比如創(chuàng)建列表,即不能出現(xiàn)
List<int>, List<long>
;通過裝箱和拆箱韭邓,可以將int類型加入List<Integer>
中措近,其他類型類似public static void main(String[] args) { int i = 1; List<Integer> list = new ArrayList<Integer>(); list.add(i); }
編譯后代碼
public static void main(String[] args) { int i = 1; ArrayList<Integer> list = new ArrayList<Integer>(); list.add(Integer.valueOf((int)i)); }
裝箱和拆箱是怎么實現(xiàn)的?
public static void main(String[] args)
{
Integer i = 10;
int n = i;
}
-
使用命令
java -jar .\cfr_0_132.jar .\BoxingTest.class --sugarboxing false > BoxingTest.txt
反編譯上述代碼public static void main(String[] args) { Integer i = Integer.valueOf((int)10); int n = i.intValue(); }
裝箱是利用包裝類的
valueOf()
方法女淑;拆箱是利用包裝類的
***Value()
方法瞭郑;
包裝類的valueOf()方法
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
}
-
查看
Integer.valueOf()
方法public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
- 從
valueOf
方法可以看出,如果數(shù)字在[-127,128]
之間鸭你,不會新建Integer
類屈张,會直接返回緩存中已經(jīng)創(chuàng)建的對象的引用;
- 從
public static void main(String[] args)
{
Double d1 = 10.0;
Double d2 = 10.0;
System.out.println(d1 == d2); // false
}
-
查看
Double.valueOf()
方法public Double(double value) { this.value = value; }
- 由于double類型的值不是離散的袱巨,無法使用緩存保存袜茧;
-
Integer、Short瓣窄、Byte笛厦、Character、Long這幾個類的valueOf方法的實現(xiàn)是類似的俺夕;
類型 包裝類緩存范圍 Integer裳凸,Byte,Short劝贸,Long [-128,127] Character [0,127] Double姨谷,F(xiàn)loat 無 Double、Float的valueOf方法的實現(xiàn)是類似的
when and where: 使用裝箱和拆箱
public static void main(String[] args)
{
Integer i1 = 1;
Integer i2 = 2;
Integer i3 = 3;
System.out.println(i3 == (i1 + i2)); // true
Long l1 = 3l;
Long l2 = 2l;
System.out.println(l1 == (i1 + i2)); // true
System.out.println(l1.equals(i1 + i2)); // false
System.out.println(l1.equals(i1 + l2)); // true
}
cfr編譯后的代碼
public static void main(String[] args) {
Integer i1 = Integer.valueOf((int)1);
Integer i2 = Integer.valueOf((int)2);
Integer i3 = Integer.valueOf((int)3);
System.out.println((boolean)(i3.intValue() == i1.intValue() + i2.intValue()));
Long l1 = Long.valueOf((long)3L);
Long l2 = Long.valueOf((long)2L);
System.out.println((boolean)(l1.longValue() == (long)(i1.intValue() + i2.intValue())));
System.out.println((boolean)l1.equals((Object)Integer.valueOf((int)(i1.intValue() + i2.intValue()))));
System.out.println((boolean)l1.equals((Object)Long.valueOf((long)((long)i1.intValue() + l2.longValue()))));
}
- 當(dāng) "=="運算符的兩個操作數(shù)都是包裝器類型的引用映九,則是比較指向的是否是同一個對象梦湘;而如果其中有一個操作數(shù)是表達(dá)式(即包含算術(shù)運算)則比較的是數(shù)值(即會觸發(fā)自動拆箱的過程);
- 包裝器類型中
equals()
方法中并不會進(jìn)行類型轉(zhuǎn)換。
三目運算符中的裝箱和拆箱
public static void main(String[] args)
{
Map<String,Boolean> map = new HashMap<String, Boolean>();
Boolean b = (map!=null ? map.get("test") : false);
}
-
jdk1.7編譯結(jié)果
public static void main(String[] args) { HashMap map = new HashMap(); Boolean b = Boolean.valueOf((boolean)(map != null ?((Boolean)map.get((Object)"test")).booleanValue() : false)); }
- 由于jdk1.7中捌议,第二和第三操作數(shù)為基本類型和對象時哼拔,會將對象拆箱,所有上述代碼執(zhí)行時會報空指針的錯誤瓣颅;
-
jdk1.8編譯結(jié)果
public static void main(String[] args) { HashMap map = new HashMap(); Boolean b = map != null ? (Boolean)map.get((Object)"test") : Boolean.valueOf((boolean)false); }
-
理解要點
- 三目運算符第二個和第三個操作數(shù)的類型不同時返回的類型到底是什么倦逐?
- 第二個和第三個操作數(shù)會進(jìn)行什么操作?
在java1.8中宫补,操作數(shù)共有18中檬姥,基本數(shù)據(jù)類型和對應(yīng)的包裝類共16個,加上null和Object共18個粉怕,所有表達(dá)式返回的數(shù)據(jù)類型組合共有324種情況健民。
-
lub()
的理解least upper bound - basically the closest superclass that they have in common; since null (type "the special null type") can be implicitly converted (widened) to any type, you can consider the special null type to be a "superclass" of any type (class) for the purposes of lub().
public static void main(String[] args) { Map<String,Boolean> map = new HashMap<String, Boolean>(); Boolean flag = true; Integer i = 1; Comparable res = map!=null ? flag : i; System.out.println(res); }
如果第二個操作數(shù)是
Boolean
,第三個操作數(shù)是Integer
贫贝,則三目運算符的返回類型是lub(Boolean,Integer)
// 編譯后的結(jié)果 public static void main(String[] args) { HashMap map = new HashMap(); Boolean flag = Boolean.valueOf((boolean)true); Integer i = Integer.valueOf((int)1); Comparable res = (Comparable)(map != null ? (Comparable<Boolean>)flag : (Comparable<Boolean>)i); System.out.println((Object)res); }
lub
的作用是需找兩個對象的最小共同父類或者父接口荞雏,通過查看Boolean
和Integer
代碼可以得到lub是Comparable
接口public final class Boolean implements java.io.Serializable, Comparable<Boolean>{} public final class Integer extends Number implements Comparable<Integer> {}
-
bnp(..)
: bnp(..) means to apply binary numeric promotion.When an operator applies binary numeric promotion to a pair of operands, each of which must denote a value that is convertible to a numeric type, the following rules apply, in order:
- If any operand is of a reference type, it is subjected to unboxing conversion (§5.1.8).
- Widening primitive conversion (§5.1.2) is applied to convert either or both operands as specified by the following rules:
- If either operand is of type double, the other is converted to double.
- Otherwise, if either operand is of type float, the other is converted to float.
- Otherwise, if either operand is of type long, the other is converted to long.
- Otherwise, both operands are converted to type int.
Binary numeric promotion is performed on the operands of certain operators:
-
T | bnp(..)
:The form "T | bnp(..)" is used where one operand is a constant expression of typeint
and may be representable in type T, where binary numeric promotion is used if the operand is not representable in type T.如果一個操作數(shù)是int的常量(即1,2,...,不是int變量)平酿,并且int常量的范圍在類型T的范圍內(nèi),則返回類型T悦陋,否則返回bnp(int,T)
以第二個操作數(shù)為short蜈彼,第三個操作數(shù)為int為例,返回類型為
short| bnp(short,int)
俺驶,public static void main(String[] args) { // 以操作數(shù)二為short類型幸逆,操作三為int類型為例 Map<String,Boolean> map = new HashMap<String, Boolean>(); short b1 = 1; int i1 = 2; short test = (map!=null ? b1 : 32767); // 編譯不報錯, short類型的范圍是[-32768,32767]暮现,所有32767可以轉(zhuǎn)化為short類型 short test1 = (map!=null ? b1 : 32768); //編譯報錯还绘,32768不可以轉(zhuǎn)化為short類型 short test2 = (map!=null ? b1 : i1); //編譯報錯,i1是符號引用栖袋,在編譯器無法確定具體的值拍顷,無法確定是否可以轉(zhuǎn)化為short類型。 }
-