自動裝箱和拆箱定義
自動裝箱:把基本類型用其包裝類替代,使其具有對象的特性??梢哉{(diào)用toString()、hashCode()、getClass()、equals()等方法。編譯器調(diào)用的是valueOf這個方法即:
Integer a = Integer.valueOf(4);
自動拆箱:將包裝類轉(zhuǎn)換為基本類型。
由于裝箱和拆箱是自動進行的非人為轉(zhuǎn)換,所以就稱作為自動裝箱和拆箱。編譯器調(diào)用的是intValue方法即:
int a = new Integer(4).intValue
原始類型:byte,short,char,int,long,float,double,boolean
封裝類:Byte,Short,Character,Integer,Long,Float,Double,Boolean
發(fā)生時機
當出現(xiàn)賦值運算、算術(shù)表達式、方法調(diào)用等情況時,會觸發(fā)自動裝箱/拆箱操作,舉例如下
Integer a = 1;
Integer b = 2;
Long c = 3L;
System.out.println(c == (a+b));
System.out.println(c.equals(a+b));
輸出結(jié)果如下:
true
false
分析如下:
c==(a+b),算數(shù)表達式先運算a+b拆箱操作,得到數(shù)值為3。Long與int比較,會自動拆箱,因此最終是3==3,得到值為true
c.equals(a+b),a+b拆箱操作,得到數(shù)值為3int類型。Long與int進行equals,由于不是同一個對象,因此會返回false。
第二種情況可以查看源碼,在Long對象源碼中,equals首頁需要判斷是否是同一對象
public boolean equals(Object obj) {
if (obj instanceof Long) {
return this.value == (Long)obj;
} else {
return false;
}
}
包裝類緩存
查看包裝類源碼,對于byte short int long char boolean這些類型的包裝類都實現(xiàn)了一個字節(jié)的緩存,float double這兩種類型沒有緩存。因此在緩存范圍內(nèi)的包裝類,使用==時實際是同一對象
Integer i1 = 127;
Integer i2 = 127;
Integer i3 = 128;
Integer i4 = 128;
System.out.println(i1 == i2); //true 滿足緩存訪問
System.out.println(i3 == i4);//false 在緩存范圍之外
Double d1 = 127.0;
Double d2 = 127.0;
Double d3 = 128.0;
Double d4 = 128.0;
System.out.println(d1 == d2);//false 沒有緩存
System.out.println(d3 == d4);//false 沒有緩存
實現(xiàn)包裝類緩存源碼如下:
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer[] cache;
static Integer[] archivedCache;
private IntegerCache() {
}
static {
int h = 127;
String integerCacheHighPropValue = VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
int size;
if (integerCacheHighPropValue != null) {
try {
size = Integer.parseInt(integerCacheHighPropValue);
size = Math.max(size, 127);
h = Math.min(size, 2147483518);
} catch (NumberFormatException var6) {
}
}
high = h;
VM.initializeFromArchive(Integer.IntegerCache.class);
size = high - -128 + 1;
if (archivedCache == null || size > archivedCache.length) {
Integer[] c = new Integer[size];
int j = -128;
for(int k = 0; k < c.length; ++k) {
c[k] = new Integer(j++);
}
archivedCache = c;
}
cache = archivedCache;
assert high >= 127;
}
}
這里使用了享元模式
包裝類的意義
其實這個問題才是最關(guān)鍵的,一個知識點的存在的意義才是我們更深入了解它的關(guān)鍵,才能在使用時因地制宜
1、如果你想在方法體內(nèi)更新primitive類型即原始類型的值,必須要使用primitive對應(yīng)的object,因為前者使用的值傳遞,后者使用的是引用傳遞
2、java.util內(nèi)操作的都是對象,如果沒有PWC,會讓程序員在使用這些工具類操作原始類型時編寫額外的代碼
3、Java提供的集合框架中的數(shù)據(jù)結(jié)構(gòu),比如ArrayList和Vector,也是只能操作對象,理由和第二點相似
4、多線程中也必須使用對象來完成各種同步操作
5、從設(shè)計理念上,在Java中,萬物皆對象,為原始類型設(shè)計出與之匹配的對象類型,更能讓編程體驗與審計理念融為一體
所以基于上述五點考慮,包裝類的存在是有積極意義的