1. 程式人生 > >理解JDK1.5的自動裝箱拆箱

理解JDK1.5的自動裝箱拆箱

特性 ava 創建 幫我 max 循環 assert rally jdk1

JDK1.5的升級引入了裝箱和拆箱概念,簡單說就是為了簡化書寫。

  JDK1.5之前,創建Integer對象是需要這麽寫的 Integer i = new Integer("3");

  JDK1.5之後,有了自動裝箱,創建Integer對象時,我們可以這樣寫 Integer i = 5;

1 int num = 3;
2 num = num + 4;
3 //這樣寫在JDK1.5中是沒有問題的 4 Integer x = 3; 5 x = x + 4;

  這樣以來Integer就擁有了和 int 基本類型一樣的能力。表面看起來對象變的可以直接進行運算,這對編程來說方便了很多。

裝箱:

  由 Integer x = new Integer(3); 簡化成:Integer i = 3; 可以理解為java自動幫我們做了 x = new Integer(3)的操作。

拆箱:

  x = x + 4; 對象是不能直接用來進行運算的,所以java會自動的做拆箱的操作,把(x+4)中的 x 自動拆箱(也就是調用Integer的intValue()方法,把x轉換為int類型進行運算),當運算完成之後會把運算結果再次使用裝箱賦值給 x 。【整個表達式先拆箱運算完成之後再裝箱】

Integer的特殊之處

1 Integer a = new Integer(127);
2 Integer b = new
Integer(127); 3 System.out.println(a==b);//false 4 System.out.println(a.equals(b));//true 5 6 Integer c = 127;//JDK1.5以後,自動裝箱如果裝箱的是一個字節那麽該數據會被共享 不會重新開辟空間 7 Integer d = 127; 8 System.out.println(c==d);//true 9 System.out.println(c.equals(d));//true

  通過測試上面代碼的測試結果看出,JDK1.5以後Integer自動裝箱的數據如果是一個字節,那麽該數據會被共享,不會重新開辟新的空間。

  這就與我們上面說的裝箱拆箱操作相矛盾了,如果Integer x = 3 真的代替了 Integer x = new Integer(3); 這裏已經使用了new,怎麽可能會不開辟新的空間呢?

  如果不開辟空間的話,那麽共享數據總要存在於一塊共享空間之內,難道會像字符串那樣維護了一個常量池?

 1     /**
 2      * Cache to support the object identity semantics of autoboxing for values between
 3      * -128 and 127 (inclusive) as required by JLS.
 4      *
 5      * The cache is initialized on first usage.  The size of the cache
 6      * may be controlled by the -XX:AutoBoxCacheMax=<size> option.
 7      * During VM initialization, java.lang.Integer.IntegerCache.high property
 8      * may be set and saved in the private system properties in the
 9      * sun.misc.VM class.
10      */
11 
12     private static class IntegerCache {
13         static final int low = -128;
14         static final int high;
15         static final Integer cache[];
16 
17         static {
18             // high value may be configured by property
19             int h = 127;
20             String integerCacheHighPropValue =
21                 sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
22             if (integerCacheHighPropValue != null) {
23                 int i = parseInt(integerCacheHighPropValue);
24                 i = Math.max(i, 127);
25                 // Maximum array size is Integer.MAX_VALUE
26                 h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
27             }
28             high = h;
29 
30             cache = new Integer[(high - low) + 1];
31             int j = low;
32             for(int k = 0; k < cache.length; k++)
33                 cache[k] = new Integer(j++);
34         }
35 
36         private IntegerCache() {}
37     }

  通過查找源碼發現,原來Integer類內部維護了一個靜態內部類,這是JDK1.5中,為 Integer 的操作引入了一個新的特性,用來節省內存和提高性能。整型對象在內部實現中通過使用相同的對象引用實現了緩存和重用。

  Javadoc 詳細的說明這個類是用來實現緩存支持,並支持 -128 到 127 之間的自動裝箱過程。最大值 127 可以通過 JVM 的啟動參數 -XX:AutoBoxCacheMax=size 修改。 緩存通過一個 for 循環實現。從小到大的創建盡可能多的整數並存儲在一個名為 cache 的整數數組中。這個緩存會在 Integer 類第一次被使用的時候被初始化出來。以後,就可以使用緩存中包含的實例對象,而不是創建一個新的實例(在自動裝箱的情況下)。

  通過查看Integer的構造方法發現,沒有一個構造方法會從IntegerCache中取值,很顯然自動裝箱的操作並不是通過new Integer()來完成的。應該是通過其他的方法!

 1     /**
 2      * Returns an {@code Integer} instance representing the specified
 3      * {@code int} value.  If a new {@code Integer} instance is not
 4      * required, this method should generally be used in preference to
 5      * the constructor {@link #Integer(int)}, as this method is likely
 6      * to yield significantly better space and time performance by
 7      * caching frequently requested values.
 8      *
 9      * This method will always cache values in the range -128 to 127,
10      * inclusive, and may cache other values outside of this range.
11      *
12      * @param  i an {@code int} value.
13      * @return an {@code Integer} instance representing {@code i}.
14      * @since  1.5
15      */
16     public static Integer valueOf(int i) {
17         assert IntegerCache.high >= 127;
18         if (i >= IntegerCache.low && i <= IntegerCache.high)
19             return IntegerCache.cache[i + (-IntegerCache.low)];
20         return new Integer(i);
21     }

  通過查看源碼發現valueOf(int i)【只有這一個方法,其他的重載方法未操作IntegerCache】中出現了對IntegerCache的操作。看源碼我們了解到如果傳入的-128<i<127 就會從IntegerCache的cache數組中取出共享數據,如果不在這個範圍之內則使用new Integer(i)。

  由此也就徹底明白了裝箱拆箱的內幕!

總結:

  > 裝箱使用的是valueOf(int i)

  > 拆箱使用的是intValue();

其實這種緩存行為不僅適用於Integer對象,針對所有整數類型的類都有類似的緩存機制。

有 ByteCache 用於緩存 Byte 對象

有 ShortCache 用於緩存 Short 對象

有 LongCache 用於緩存 Long 對象

有 CharacterCache 用於緩存 Character 對象

Byte、Short、Long有固定範圍:-128~127,對於 Character範圍是:0~127。

除了Integer可以通過參數改變範圍外,其他對象都不可以。

理解JDK1.5的自動裝箱拆箱