2.1 包裝類的作用
(1)基本數據類型的存在意義
我們都知道在Java語言中,new一個對象存儲在堆裏,我們通過棧中的引用來使用這些對象。但是對於經常用到的一系列類型如int、boolean…如果我們用new將其存儲在堆裏就不是很高效——特別是簡單的小的變量。所以,同C++一樣Java也採用了相似的做法,決定基本數據類型不是用new關鍵字來創建,而是直接將變量的值存儲在棧中,方法執行時創建,結束時銷燬,因此更加高效。所以基本數據類型的存在意義是爲了運行效率;
(2)爲什麼要引入包裝類
Java是一門面向對象的編程語言,但是Java中的基本數據類型卻不是面向對象的,並不具有對象的性質,這在實際生活中存在很多的不便。爲了讓基本類型也具有對象的特徵,就出現了包裝類型,它相當於將基本類型“包裝起來”,使得具有了面向對象的性質,並且爲其添加了屬性和方法,豐富了基本類型的操作,方便涉及到對象的操作。所以,引入包裝類是爲了方便對基本類型進行操作;
在使用集合類型時,就一定要使用包裝類型,因爲容器都是裝object的,基本數據類型顯然不適用。
2.2 包裝類的原理
2.2.1 包裝類
Java設計當初就提供類8種基本數據類型及對應的8 種包裝類(封裝類);
基本數據類型 | 包裝類 |
---|---|
byte | Byte |
boolean | Boolean |
short | Short |
char | Character |
int | Integer |
long | Long |
float | Float |
double | Double |
2.2.2 裝箱和拆箱
下面均以int,Integer爲例進行講解;
- 裝箱:基本數據類型轉換爲包裝類;裝箱有兩種方法:
//裝箱兩種方法
Integer i1 = new Integer(8);
Integer i2 = Integer.valueOf(8);
// 自動裝箱,會隱式調用valueOf方法
Integer i3 = 8;
fun(8);
void fun( Integer a){
//...
}
- 拆箱:包裝類轉換爲基本數據類型;拆箱方法:
//拆箱方法
int i5 = i3.intValue();
//自動拆箱,會隱式調用intValue方法
int i4 = i3;
test(i3);
void test(int a){
//...
}
2.2.3 緩存池
上一節說到,裝箱有兩種方法:new Integer(8)
和 Integer.valueOf(8)
;他們的區別在於:
new Integer(8)
每次調用創建一個新對象;Integer.valueOf(8)
使用緩存池中的對象,多次調用會取得同一個對象的引用(可複用)。
下面看一下Integer的源碼,分析一下valueOf和緩存池;
valueOf() 方法的實現比較簡單,就是先判斷值是否在緩存池中,如果在的話就直接返回緩存池的內容。
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];//1
return new Integer(i);//2
}
Integer i6 = 127;//自動裝箱,值在緩存池範圍內,調用valueOf,執行1,i6是緩存池單元的引用(地址);
Integer i7 = 128;//自動裝箱,值在緩存池範圍外,調用valueOf,執行2,最終調用newInteger,i7是堆單元的引用;
在 Java 8 中,Integer 緩存池的大小默認爲 -128~127,和byte範圍相同;
private static class IntegerCache {
static final int low = -128;//Integer緩存池最小值
static final int high;//Integer緩存池最大值
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;//Integer緩存池最大值
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;//Integer緩存池最大值
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() {}
}
2.3.4 封裝類對象的比較
封裝類對象的比較強制使用equals(源自阿里編碼規範);
public class Test {
public static void main(String[] args) {
Integer i1 = new Integer(127);
Integer i2 = Integer.valueOf(127);
Integer i3 = 127;
Integer i4 = 127;
Integer i5 = 128;
Integer i6 = 128;
int i7 = 127;
System.Out.println(i1 == i2); //1false;
System.Out.println(i2 == i3); //2true
System.Out.println(i3 == i4); //3true
System.Out.println(i5 == i6); //4false
System.Out.println(i5.equals(i6)); //5true
System.Out.println(i1 == i7);//true
System.Out.println(i3 == i7);//true
}
}
首先聲明==和equals的區別:
- equals比較對象的內容(所有屬性值);
- ==如果是基本數據類型比較值,如果是對象比較內存地址;
(1)i1是堆中新開闢一塊內存的地址;i2是堆中緩存池某一單元的地址;1輸出false;
(2)自動裝箱,值在緩存池範圍內,調用valueOf,i2、i3和i4是同一緩存池單元的引用(地址);2,3輸出true;
(3)自動裝箱,值在緩存池範圍外,valueOf最終調用newInteger,i5,i6都創建了一個新對象,倆對象內容相同,地址不同;4false ,5true ;
(3) System.Out.println(i1 == i7);
編譯成 System.Out.println(i1.intValue() == i7);
所以輸出true;System.Out.println(i3 == i7);
同理輸出true;
2.2.5 String和Integer相互轉換
- String to Integer:
Intrger.parseInt(string);
- Integer to String:
Integer.toString();
2.3 基本類型和包裝類的區別
-
存儲位置:基本類型變量值存儲在棧中,而包裝類型是將對象放存儲在堆中,然後通過引用來使用;
-
默認初值:基本類型的默認初始如int爲0,boolean爲false,而包裝類型的默認初始爲null;
-
作用不同:基本數據類型的存在意義是爲了運行效率;而引入包裝類是爲了方便對基本類型進行操作;
2.3 包裝類使用場景
- 泛型(包括容器的泛型參數)只能使用包裝類;
- 某個允許爲null的屬性只能使用包裝類;
以下三條源自阿里編碼規範:
- 所有POJO類(簡單Java類,只有屬性、構造器、seter、geter的類)屬性;
- RPC(遠程方法調用)方法返回值和參數必須使用包裝數據類型;
- 推薦所有的局部變量使用基本數據類型,避免頻繁GC;
ps:在一個ORM中存在一個Student類和Student類表,有一個JavaBean 類Student類中有一個表示學生考試分數的屬性score,相對應Student類表中有一個表示學生考試分數的字段score;如果用Integer而不用int定義這個屬性,一次考試,學生可能沒考,值是null,也可能考了,但考了0分,值是0,這兩個表達的狀態明顯不一樣
2.4 包裝類存在的問題
- 效率低;
由於使用包裝類會創建新對象,而且若在循環中使用包裝類會頻繁GC,效率比較低;所以在循環中儘量使用基本數據類型;所以阿里編碼規範推薦所有的局部變量使用基本數據類型;
Integer sum = 0;
for(int i=0; i<1000; i++){
sum+=i;
}
- 拆箱可能會產生空指針異常;
Object obj = null;//1
int i = (Integer)obj;//2
2處自動拆箱會調用obj.intValue();
方法,從而產生空指針異常;
參考: