一文搞懂Java中的包裝類與基本數據類型

一、基本數據類型與包裝類

基本數據類型與包裝類是Java中的基礎知識與面試題中的常考題目。
常見面試題問法如下:int和Integer有什麼區別?基本數據類型和基本數據類型包裝類有什麼異同?爲什麼有基本數據類型還要有基本數據類型包裝類?

Java是面向對象的程序設計語言,講究的是萬物皆對象的理念。而基本數據類型在某些場景下無法使用,包裝類可以向操作其它類一樣簡單操作對“基本數據類型進行操作”;
包裝類提供了更多更實用的方法,如hashCode方法,equals方法以及valueOf方法等等,功能比基本數據類型豐富。從Java 5開始引入了自動裝箱/拆箱機制,使得二者可以相互轉換。
Java 爲每個原始類型提供了包裝類型:

  • 原始類型: boolean,char,byte,short,int,long,float,double
  • 包裝類型:Boolean,Character,Byte,Short,Integer,Long,Float,Double
    基本數據類型與基本數據類型比較
    1)重寫了hashcode和equals方法還有其它比較實用的方法,如valueOf、parseInt、parseDouble、toHexString等數值的常用進制轉換方法等等。(使用包裝類型進行值比較時,建議使用equals方法,而不是使用==去進行值比較,這是因爲包裝類型中對一個範圍的數字使用了一種類似類似緩存池的東西,下面的例子將會介紹)
    2)包裝類可以爲泛型參數,而基本數據類型不可以。例如我們定義一個List集合時,可以使用包裝類型,但是卻無法使用基本數據類型。
    3)包裝數據類型都實現了Serializable接口,所以可以序列化和反序列化。
    擴展:(1)把對象轉換爲字節序列的過程稱爲對象的序列化;把字節序列恢復爲對象的過程稱爲對象的反序列化。
    (2)對象的序列化主要有兩種用途:第一種用途:把對象的字節序列永久地保存到硬盤上,通常存放在一個文件中;第二種用途:在網絡上傳送對象的字節序列。
    4)基本數據類型的默認值與基本數據類型的包裝類的默認值不同。包裝類因爲是類,所以包裝類的默認值爲null;基本數據類型,byte、short、int、long爲0,boolean默認爲false,float和double爲0.0,char類型默認爲空(不是null哦)
    光說不練假把式,請看如下程序:
public class DataTest {
    public static void main(String[] args) {
        // 定義了一個字符串,字符串的內容爲256
        String numberString = "252";
        // Integer中的parseInt方法返回值類型爲Integer
        // 之所以能用int類型去接,是因爲自動拆箱,將包裝類型拆箱爲基本數據類型
        int number = Integer.parseInt(numberString);
        // number 爲基本數據類型,進行加4操作,返回的應該是基本數據類型中的int型
        // 之所以能用Integer包裝類型去接,是因爲自動裝箱,將int數據類型自動裝箱
        Integer hexInteger = number + 4;
        // 可以對包裝類型賦值像給基本數據類型中的int賦值一樣,自動裝箱
        // int自動裝箱調用了Integer中的valueOf方法(請注意此方法)
        // 下面等同於 Integer equalsInteger = Integer.valueOf(256);
        Integer equalsInteger = 256;
        // 這裏很容易理解,==比較的是兩個對象的地址,hexInteger和equalsInteger
        // 是兩個不同的Integer對象,他們的地址是不同的,==比較結果是false
        // 比較結果爲false,但是如果我們將數換爲64呢?==比較的結果如何???
        System.out.println(hexInteger == equalsInteger);
        // 包裝類型都重寫了equals方法,所以這裏比較的是兩個對象的值內容       System.out.println(hexInteger.equals(equalsInteger));
        // 將數字轉化爲16進制字符串
        System.out.println(Integer.toHexString(hexInteger));
    }
}

程序運行結果如下:
在這裏插入圖片描述
在上述程序示例中的一個問題,如果我們換成64呢?結果還是一樣嗎?

public class DataTest {
    public static void main(String[] args) {
        // 定義了一個字符串,字符串的內容爲60
        String numberString = "60";
        // Integer中的parseInt方法返回值類型爲Integer
        // 之所以能用int類型去接,是因爲自動拆箱,將包裝類型拆箱爲基本數據類型
        int number = Integer.parseInt(numberString);
        // number 爲基本數據類型,進行加4操作,返回的應該是基本數據類型中的int型
        // 之所以能用Integer包裝類型去接,是因爲自動裝箱,將int數據類型自動裝箱
        Integer hexInteger = number + 4;
        // 可以對包裝類型賦值像給基本數據類型中的int賦值一樣,自動裝箱
        // int自動裝箱調用了Integer中的valueOf方法(請注意此方法)
        Integer equalsInteger = 64;
        // 比較結果爲false,但是如果我們將數換爲64呢?==比較的結果如何???
        System.out.println(hexInteger == equalsInteger);
        // 包裝類型都重寫了equals方法,所以這裏比較的是兩個對象的值內容
   System.out.println(hexInteger.equals(equalsInteger));
        // 將數字轉化爲16進制字符串     System.out.println(Integer.toHexString(hexInteger));
    }
}

程序輸出結果如下:
在這裏插入圖片描述
這裏改爲64以後,爲什麼==比較也是true了呢?
這裏就需要對裝箱的實現以及裝箱的概念有所瞭解。當我們給一個Integer對象賦一個int值的時候,會調用Integer類的靜態方法valueOf,看看valueOf方法的源碼就知道了,valueOf方法源碼如下:

public static Integer valueOf(int i) {
	if (i >= IntegerCache.low && i <= IntegerCache.high)
	return IntegerCache.cache[i + (-IntegerCache.low)];
	return new Integer(i);
}

這裏面有一個IntegerCache是Integer的內部類,其代碼如下:

/**
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage.  The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/

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) {
	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() {}
}

簡單的說,如果整型字面量的值在-128到127之間,那麼不會new新的Integer對象,而是直接引用常量池中的Integer對象,超出這個範圍的數值纔會真正的new一個對象出來。所以上面的兩段程序的輸出結果是不一樣的。
擴展:基本數據類型包裝類中的Byte、Short、Integer、Long的高頻緩存範圍爲-128到127;Character的高頻緩存爲0到127;Float、Double沒有高頻緩存區。Integer是唯一一個可以修改高頻緩存範圍的包裝類。通過在VM optons中如下設置:-XX:AutoBoxCacheMax=8866 即修改緩存最大值爲8866。
再看如下程序:

public class DataTest {
    public static void main(String[] args) {
        int a = 100;
        Integer b = new Integer(100);
        Integer c = new Integer(100);
        System.out.println(a == b);
        System.out.println(a == c );
        System.out.println(b == c);
        System.out.println(b.equals(a));
        System.out.println(c.equals(a));
        System.out.println(b.equals(c));
    }
}

程序運行結果如下:
在這裏插入圖片描述
a與b、a與c使用兩個=比較,會將Integer包裝類自動拆箱爲基本數據類型中的int,進行值比較與a與b、a與c、b與c使用equals(包裝類重新了equals方法)方法一樣進行的都是值比較,所以是true;而b與c使用==進行比較的結果卻爲false,這是因爲new出來的對象不會使用高頻緩存範圍的數值,是先創建對象,這兩個對象是不同的對象,所以地址是不同的,返回false;(當然這麼寫代碼,如果你的編程軟件安裝了阿里代碼開發檢測工具的時候是會有黃色警告的)

二、在程序中選擇包裝類還是基本類的原則有哪些?

包裝類型比基本數據類型的應用範圍更廣,同時提供了很多方法,很方便,一般情況下確定是使用基本數據類型還是包裝類型原則如下:
1)所有的POJO類屬性必須使用包裝類;
2)RPC中的方法返回值和參數必須使用包裝類;
3)所有局部變量推薦使用基本數據類型;

三、泛型可以爲基本類型嗎?爲什麼?

泛型不能使用基本數據類型。泛型在 JVM(Java虛擬機)編譯的時候會類型檫除,比如代碼 List list 在 JVM 編譯的時候會轉換爲 List list ,因爲泛型是在 JDK 5 時提供的,而 JVM 的類型檫除是爲了兼容以前代碼的一個折中方案,類型檫除之後就變成了 Object,而 Object 不能存儲基本數據類型,但可以使用基本數據類型對應的包裝類,所以像 List《int》 list 這樣的代碼是不被允許的,編譯器階段會檢查報錯,而 List《Integer》 list 是被允許的。

微信公衆號:【爲碼以夢】不斷更文中,有興趣的小夥伴關注支持一下吧!
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章