理解JDK1.5的自動裝箱拆箱
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 = newInteger(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的自動裝箱拆箱