一、什么是封裝類?
Java中存在基礎數據類型,但是在某些情況下,我們要對基礎數據類型進行對象的操作,例如,集合中只能存在對象,而不能存在基礎數據類型,於是便出現了包裝器類。包裝器類型就是對基本數據類型進行了封裝,使之成為一個對象,每一個基本數據類型都對應一種包裝器類型。
二、什么是裝箱與拆箱
將基本數據類型變為包裝器類,便是裝箱,將包裝器類轉為基本數據類型就是拆箱。相面以Integer為例:
public static void main(String[] args){ //自動裝箱 Integer a = 100; //自動拆箱 int b = a; }
實際上,Integer在裝箱過程中調用了Integer中的valueOf()方法,拆箱為int時調用了Integer中的intValue()方法,源碼如下:
public static Integer valueOf(int i) { assert IntegerCache.high >= 127; if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
public int intValue() { return value; }
這便是裝箱與拆箱的底層執行過程,其它包裝器類類似。
三、自動裝箱與拆箱中的一些問題
問題1:
public class Main { public static void main(String[] args) { Integer i1 = 100; Integer i2 = 100; Integer i3 = 200; Integer i4 = 200; System.out.println(i1==i2); //true System.out.println(i3==i4); //false } }
通過分析valueOf()方法我們知道,當值為-128到127之間時,返回return IntegerCache.cache[i + (-IntegerCache.low)];當不在這個區間時,返回return new Integer(i);那么IntegerCache.cache[i + (-IntegerCache.low)]又是什么呢?繼續追蹤源碼:
private static class IntegerCache { static final int low = -128; static final int high; static final Integer 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) { 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); } high = h; cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); } private IntegerCache() {} }
從上面代碼可以看出Integer類中提前就初始化了一個Integer數組,范圍為-128到127。回到上面的題目:i1==i2=100返回true是因為范圍屬於-128到127,直接返回cache[]數組中的對象,屬於同一個對象,==運算符比較對象,從而返回true,而200超過了這個范圍,直接返回new Integer(),因此屬於兩個不同的對象,結果為false。
問題2:
public class Main { public static void main(String[] args) { Double i1 = 100.0; Double i2 = 100.0; Double i3 = 200.0; Double i4 = 200.0; System.out.println(i1==i2); //false System.out.println(i3==i4); //false } }
上面的代碼中的結果與Integer類型的結果大相徑庭,原因還得從源碼中找。
public static Double valueOf(double d) { return new Double(d); }
因此返回false理所當然。
總結一下:
Integer派別:Integer、Short、Byte、Character、Long這幾個類的valueOf方法的實現是類似的。
Double派別:Double、Float的valueOf方法的實現是類似的。每次都返回不同的對象。
Integer派別的范圍:
問題3:
public class Main { public static void main(String[] args) { Boolean i1 = false; Boolean i2 = false; Boolean i3 = true; Boolean i4 = true; System.out.println(i1==i2);//true System.out.println(i3==i4);//true } }
以上代碼又是林外一種情況,繼續追蹤源碼
public static Boolean valueOf(boolean b) { return (b ? TRUE : FALSE); }
/** * The {@code Boolean} object corresponding to the primitive * value {@code true}. */ public static final Boolean TRUE = new Boolean(true); /** * The {@code Boolean} object corresponding to the primitive * value {@code false}. */ public static final Boolean FALSE = new Boolean(false);
分析可知,TRUE和FALSE是兩個final對象,因此i1、i2和i3、i4始終指向同一個對象的地址,因此返回true。
問題4:
public static void main(String[] args) { Integer num1 = 400; int num2 = 400; System.out.println(num1 == num2); //true System.out.println(num1.equals(num2)); //true }
可見num1 == num2進行了拆箱操作。equals源碼如下:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
我們知道equal比較的是內容本身,並且我們也可以看到equal的參數是一個Object對象,我們傳入的是一個int類型,所以首先會進行裝箱,然后比較,之所以返回true,是由於它比較的是對象里面的value值。
問題5:
public static void main(String[] args) { Integer num1 = 100; int num2 = 100; Long num3 = 200l; System.out.println(num1 + num2); //200 System.out.println(num3 == (num1 + num2)); //true System.out.println(num3.equals(num1 + num2)); //false }
當一個基礎數據類型與封裝類進行==、+、-、*、/運算時,會將封裝類進行拆箱,對基礎數據類型進行運算。
對於num3.equals(num1 + num2)為false的原因很簡單,我們還是根據代碼實現來說明:
public boolean equals(Object obj) { if (obj instanceof Long) { return value == ((Long)obj).longValue(); } return false; }
它必須滿足兩個條件才為true:
1、類型相同
2、內容相同
上面返回false的原因就是類型不同。
問題6:
public static void main(String[] args) { int num1 = 100; int num2 = 200; long mum3 = 300; System.out.println(mum3 == (num1 + num2)); //true }
當 “==”運算符的兩個操作數都是包裝器類型的引用,則是比較指向的是否是同一個對象,而如果其中有一個操作數是表達式(即包含算術運算)則比較的是數值(即會觸發自動拆箱的過程)。
問題7:
public static void main(String[] args) { Integer integer100=null; int int100=integer100; }
這兩行代碼是完全合法的,完全能夠通過編譯的,但是在運行時,就會拋出空指針異常。其中,integer100為Integer類型的對象,它當然可以指向null。但在第二行時,就會對integer100進行拆箱,也就是對一個null對象執行intValue()方法,當然會拋出空指針異常。所以,有拆箱操作時一定要特別注意封裝類對象是否為null。