程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> Java自動裝箱與拆箱的分析

Java自動裝箱與拆箱的分析

編輯:JAVA綜合教程

Java自動裝箱與拆箱的分析


??自動裝箱與拆箱機制在實際使用中非常常見,不過也特別容易出錯,博主在面對下面一道題的時候自信滿滿,可還是沒有能夠全對,所以寫下這篇博文,給自己對自動裝箱與拆箱機制做一下知識鞏固,也給各位朋友做一下參考。
??首先有這樣一道題,給出下面代碼的輸出結果:

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()方法不處理數據轉型的關系,博主建議在實際編碼中要盡量避免這樣使用自動裝箱與拆箱機制。

   

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved