??自動裝箱與拆箱機制在實際使用中非常常見,不過也特別容易出錯,博主在面對下面一道題的時候自信滿滿,可還是沒有能夠全對,所以寫下這篇博文,給自己對自動裝箱與拆箱機制做一下知識鞏固,也給各位朋友做一下參考。
??首先有這樣一道題,給出下面代碼的輸出結果:
public class AutoBoxing
{
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;
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));
}
}
??運行結果:
true
false
true
true
true
false
??如果你看到這邊,答案都正確,而且沒有絲毫的疑問,那麼對於你來說這篇博文就此結束了,如果沒有,請繼續翻閱。
??首先從最基礎的開始講起,首先通過反編譯來看一看自動裝箱和拆箱的過程:
??首先看如下一段程序:
public class AutoBoxing2
{
public static void main(String[] args)
{
Integer a = 1;
Integer b = 2;
Integer c = a+b;
}
}
??反編譯結果為:(如果對於java反編譯不太了解的朋友可以先看一下《通過Java反編譯揭開一些問題的真相》)
public jvm.optimize.AutoBoxing2();
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
LineNumberTable:
line 3: 0
public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
0: iconst_1
1: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
4: astore_1
5: iconst_2
6: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
9: astore_2
10: aload_1
11: invokevirtual #3 // Method java/lang/Integer.intValue:()I
14: aload_2
15: invokevirtual #3 // Method java/lang/Integer.intValue:()I
18: iadd
19: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
22: astore_3
23: return
LineNumberTable:
line 8: 0
line 9: 5
line 10: 10
line 11: 23
??可以看到Integer a=1實際上自動裝箱為Integer a = Integer.valueOf(1),而在進行a+b的時候可以看到進行了自動拆箱,將a拆箱為Integer.intValue();然後將a和b的int值相加,相加之後有進行了自動裝箱:Integer c=Integer.valueOf(3).
??接下來我們就可以上面題目中給出的 System.out.println(c==d);和System.out.println(e==f);他們分別的結果為true和false。
??知道Integer會緩存-128至127的朋友估計這兩條語句的輸出結果都能答對。
??如果沒有答對,請看解析:
??Integer c=3;會自動裝箱為Integer c = Integer.valueOf(3),那麼看一下valueOf方法的源碼:
public static Integer valueOf(int i) {
assert IntegerCache.high >= 127;
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
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) {
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);
}
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() {}
}
??可以看到實際上Integer會緩存-128值127的內容,如果值在這個區間之內(比如c和d),那麼就會返回IntegerCache中的引用,所以Integer c= Integer d = IntegerCache.cache[3+(–128)] = IntegerCache.cache[131], c和d是相等的。
??但是如果超過這個區間,比如e和f,則Integer e = new Integer(321); Integer f = new Integer(321);new出來的自然是在堆中新開辟的內存,兩份地址不同,自然e和f不同,也就是如果遇到這樣的情況:
Integer m = new Integer(2);
Integer n = new Integer(2);
System.out.println(m==n);
??那麼輸出的結果是false(如果Integer m=2; Intger n=2則m和n相同)
??接著再說System.out.println(c==(a+b));
??我們看如下代碼:
Integer a = 1;
Integer b = 2;
Integer c = 3;
System.out.println(c==(a+b));
??反編譯之後:
0: iconst_1
1: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
4: astore_1
5: iconst_2
6: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
9: astore_2
10: iconst_3
11: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
14: astore_3
15: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
18: aload_3
19: invokevirtual #4 // Method java/lang/Integer.intValue:()I
22: aload_1
23: invokevirtual #4 // Method java/lang/Integer.intValue:()I
26: aload_2
27: invokevirtual #4 // Method java/lang/Integer.intValue:()I
30: iadd
31: if_icmpne 38
34: iconst_1
35: goto 39
38: iconst_0
39: invokevirtual #5 // Method java/io/PrintStream.println:(Z)V
42: return
??可以看到實際在c==(a+b)的時候是執行拆箱機制,實際上就是在運算3==2+1,當然就是true咯。
??繼續說明: System.out.println(c.equals(a+b));
??同樣看一下c.equals(a+b)反編譯的結果(篇幅限制,只截取部分相關的結果):
19: aload_1
20: invokevirtual #4 // Method java/lang/Integer.intValue:()I
23: aload_2
24: invokevirtual #4 // Method java/lang/Integer.intValue:()I
27: iadd
28: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
31: invokevirtual #5 // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z
??可以看到a+b先拆箱為int再相加之後再裝箱為Integer型與c進行equals比較,那麼我們再看一下equals()方法的源碼:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
??通過查看源碼可知此條語句的輸出結果為true。
??最後來看一下System.out.println(g==(a+b));和System.out.println(g.equals(a+b));兩條語句。
??System.out.println(g==(a+b));由前面的推論可知最後g拆箱為long型,a+b為int型,基礎類型int可以自動升級為long,所以輸出為true。
??對於System.out.println(g.equals(a+b));可以看一下Long的equals()方法。
public boolean equals(Object obj) {
if (obj instanceof Long) {
return value == ((Long)obj).longValue();
}
return false;
}
??對於(a+b)來說是Integer類型,所以返回false.
??鑒於包裝類的“==”運算在不遇到算術運算的情況下不會自動拆箱,以及它們equals()方法不處理數據轉型的關系,博主建議在實際編碼中要盡量避免這樣使用自動裝箱與拆箱機制。