歡迎支援筆者新作:《深入了解Kafka:核心設計與實踐原理》和《RabbitMQ實戰指南》,同時歡迎關注筆者的微信公衆号:朱小厮的部落格。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsATOfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5yN3YjM0QzNkJDO5cjYkBjZ2ITN4IGMhVjMmFGZxYTOi9CXyAzLclDMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLzM3Lc9CX6MHc0RHaiojIsJye.png)
自動裝箱與拆箱機制在實際使用中非常常見,不過也特别容易出錯,部落客在面對下面一道題的時候自信滿滿,可還是沒有能夠全對,是以寫下這篇博文,給自己對自動裝箱與拆箱機制做一下知識鞏固,也給各位朋友做一下參考。
首先有這樣一道題,給出下面代碼的輸出結果:
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."<init>":()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(cd);和System.out.println(ef);他們分别的結果為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()方法不處理資料轉型的關系,部落客建議在實際編碼中要盡量避免這樣使用自動裝箱與拆箱機制。