自動裝箱就是Java自動將原始類型值轉換成對應的對象,比如將int的變量轉換成Integer對象,這個過程叫做裝箱,反之將Integer對象轉換成int類型值,這個過程叫做拆箱。因為這里的裝箱和拆箱是自動進行的非人為轉換,所以就稱作為自動裝箱和拆箱。原始類型byte, short, char, int, long, float, double 和 boolean 對應的封裝類為Byte, Short, Character, Integer, Long, Float, Double, Boolean。
下面例子是自動裝箱和拆箱帶來的疑惑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
public class Test { public static void main(String[] args) { test(); } public static void test() { int i = 40 ; int i0 = 40 ; Integer i1 = 40 ; Integer i2 = 40 ; Integer i3 = 0 ; Integer i4 = new Integer( 40 ); Integer i5 = new Integer( 40 ); Integer i6 = new Integer( 0 ); Double d1= 1.0 ; Double d2= 1.0 ; System.out.println( "i=i0\t" + (i == i0)); System.out.println( "i1=i2\t" + (i1 == i2)); System.out.println( "i1=i2+i3\t" + (i1 == i2 + i3)); System.out.println( "i4=i5\t" + (i4 == i5)); System.out.println( "i4=i5+i6\t" + (i4 == i5 + i6)); System.out.println( "d1=d2\t" + (d1==d2)); System.out.println(); } } |
請看下面的輸出結果跟你預期的一樣嗎?
輸出的結果:
i=i0 true
i1=i2 true
i1=i2+i3 true
i4=i5 false
i4=i5+i6 true
d1=d2 false
為什么會這樣?帶著疑問繼續往下看。
自動裝箱和拆箱的原理
自動裝箱時編譯器調用valueOf將原始類型值轉換成對象,同時自動拆箱時,編譯器通過調用類似intValue(),doubleValue()這類的方法將對象轉換成原始類型值。
明白自動裝箱和拆箱的原理后,我們帶著上面的疑問進行分析下Integer的自動裝箱的實現源碼。如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
public static Integer valueOf( int i) { //判斷i是否在-128和127之間,如果不在此范圍,則從IntegerCache中獲取包裝類的實例。否則new一個新實例 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[]; //靜態方法,類加載的時候進行初始化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 ) { try { 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 ); } catch ( NumberFormatException nfe) { // If the property cannot be parsed into an int, ignore it. } } high = h; cache = new Integer[(high - low) + 1 ]; int j = low; for ( int k = 0 ; k < cache.length; k++) cache[k] = new Integer(j++); // range [-128, 127] must be interned (JLS7 5.1.7) assert IntegerCache.high >= 127 ; } private IntegerCache() {} } |
Integer i1 = 40; 自動裝箱,相當于調用了Integer.valueOf(40);方法。
首先判斷i值是否在-128和127之間,如果在-128和127之間則直接從IntegerCache.cache緩存中獲取指定數字的包裝類;不存在則new出一個新的包裝類。
IntegerCache內部實現了一個Integer的靜態常量數組,在類加載的時候,執行static靜態塊進行初始化-128到127之間的Integer對象,存放到cache數組中。cache屬于常量,存放在java的方法區中。
接著看下面是java8種基本類型的自動裝箱代碼實現。如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
//boolean原生類型自動裝箱成Boolean public static Boolean valueOf( boolean b) { return (b ? TRUE : FALSE); } //byte原生類型自動裝箱成Byte public static Byte valueOf( byte b) { final int offset = 128 ; return ByteCache.cache[( int )b + offset]; } //byte原生類型自動裝箱成Byte public static Short valueOf( short s) { final int offset = 128 ; int sAsInt = s; if (sAsInt >= - 128 && sAsInt <= 127 ) { // must cache return ShortCache.cache[sAsInt + offset]; } return new Short(s); } //char原生類型自動裝箱成Character public static Character valueOf( char c) { if (c <= 127 ) { // must cache return CharacterCache.cache[( int )c]; } return new Character(c); } //int原生類型自動裝箱成Integer public static Integer valueOf( int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); } //int原生類型自動裝箱成Long public static Long valueOf( long l) { final int offset = 128 ; if (l >= - 128 && l <= 127 ) { // will cache return LongCache.cache[( int )l + offset]; } return new Long(l); } //double原生類型自動裝箱成Double public static Double valueOf( double d) { return new Double(d); } //float原生類型自動裝箱成Float public static Float valueOf( float f) { return new Float(f); } |
通過分析源碼發現,只有double和float的自動裝箱代碼沒有使用緩存,每次都是new 新的對象,其它的6種基本類型都使用了緩存策略。
使用緩存策略是因為,緩存的這些對象都是經常使用到的(如字符、-128至127之間的數字),防止每次自動裝箱都創建一此對象的實例。
而double、float是浮點型的,沒有特別的熱的(經常使用到的)數據的,緩存效果沒有其它幾種類型使用效率高。
下面在看下裝箱和拆箱問題解惑。
1
2
3
4
5
6
7
8
9
10
11
12
|
//1、這個沒解釋的就是true System.out.println( "i=i0\t" + (i == i0)); //true //2、int值只要在-128和127之間的自動裝箱對象都從緩存中獲取的,所以為true System.out.println( "i1=i2\t" + (i1 == i2)); //true //3、涉及到數字的計算,就必須先拆箱成int再做加法運算,所以不管他們的值是否在-128和127之間,只要數字一樣就為true System.out.println( "i1=i2+i3\t" + (i1 == i2 + i3)); //true //比較的是對象內存地址,所以為false System.out.println( "i4=i5\t" + (i4 == i5)); //false //5、同第3條解釋,拆箱做加法運算,對比的是數字,所以為true System.out.println( "i4=i5+i6\t" + (i4 == i5 + i6)); //true //double的裝箱操作沒有使用緩存,每次都是new Double,所以false System.out.println( "d1=d2\t" + (d1==d2)); //false |
相信你看到這就應該能明白上面的程序輸出的結果為什么是true,false了,只要掌握原理,類似的問題就迎刃而解了,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:http://www.jianshu.com/p/0ce2279c5691?utm_source=tuicool&utm_medium=referral