Java 中爲何設計了包裝類

公衆號最近更新頻率慢了,由於「最近在準備暑期實習」,以前尋思着一邊複習一邊寫文章,一箭雙鵰。後來發現一篇讀起來比較舒服的文章寫出來加上配圖得花上四五個小時甚至更多,但這個知識點我可能半個小時就能複習完了,春招在即,時間比較緊迫,因此最近文章可能改成一週一更或者一週兩更,但願各位理解。另外,有和我同樣在準備暑期實習的小夥伴能夠聯繫我互相交流 😊java

全文脈絡思惟導圖以下:git

1. 爲何須要包裝類

在 Java 中,萬物皆對象,全部的操做都要求用對象的形式進行描述。可是 Java 中除了對象(引用類型)還有八大基本類型,它們不是對象。那麼,爲了把基本類型轉換成對象,最簡單的作法就是「將基本類型做爲一個類的屬性保存起來」,也就是把基本數據類型包裝一下,這也就是包裝類的由來。web

這樣,咱們先本身實現一個簡單的包裝類,以包裝基本類型 int 爲例:面試

// 包裝類 MyInt
public class MyInt {
    private int number; // 基本數據類型
    
    public Int (int number)// 構造函數,傳入基本數據類型
        this.number = number;
    }
    
    public int intValue()// 取得包裝類中的數據
        return this.number;
    }
}

測試一下這個包裝類:算法

public static void main(String[] args) {
    MyInt temp = new Int(100); // 100 是基本數據類型, 將基本數據類型包裝後成爲對象
    int result = temp.intValue(); // 從對象中取得基本數據類型
    System.out.println(result);
}

固然,咱們本身實現的這個包裝類很是簡單,Java 給咱們提供了更完善的內置包裝類:數據庫

基本類型 對應的包裝類(位於 java.lang 包中)
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean

前 6 個類派生於公共的超類 Number,而 Character 和 Boolean 是 Object 的直接子類。編程

來看看包裝類的聲明,以 Integer  爲例:後端

被 final 修飾,也就是說 Java 內置的「包裝類是沒法被繼承的」數組

2. 裝箱與拆箱

OK,如今咱們已經知道了,存在基本數據類型與其對應的包裝類,那麼,他們之間互相的轉換操做就稱爲裝箱與拆箱:緩存

  • 裝箱:將基本數據類型轉換成包裝類(每一個包裝類的構造方法均可以接收各自數據類型的變量)
  • 拆箱:從包裝類之中取出被包裝的基本類型數據(使用包裝類的 xxxValue 方法)

下面以 Integer 爲例,咱們來看看 Java 內置的包裝類是如何進行拆裝箱的:

Integer obj = new Integer(10);  // 自動裝箱
int temp = obj.intValue();   // 自動拆箱

能夠看出,和上面咱們本身寫的包裝類使用方式基本同樣,事實上,Integer 中的這兩個方法其底層實現和咱們上述寫的代碼也是差很少的。

不知道各位發現沒,value 被聲明爲 final 了,也就是說「一旦構造了包裝器,就不容許更改包裝在其中的值」

另外,須要注意的是,這種形式的代碼是 「JDK 1.5 之前」的!!!「JDK 1.5 以後」,Java 設計者爲了方便開發提供了「自動裝箱」「自動拆箱」的機制,而且能夠直接利用包裝類的對象進行數學計算。

仍是以 Integer 爲例咱們來看看自動拆裝箱的過程:

Integer obj = 10;   // 自動裝箱. 基本數據類型 int -> 包裝類 Integer
int temp = obj;   // 自動拆箱. Integer -> int
obj ++; // 直接利用包裝類的對象進行數學計算
System.out.println(temp * obj); 

看見沒有,基本數據類型到包裝類的轉換,不須要像上面同樣使用構造函數,直接 = 就完事兒;一樣的,包裝類到基本數據類型的轉換,也不須要咱們手動調用包裝類的 xxxValue 方法了,直接 = 就能完成拆箱。這也是將它們稱之爲自動的緣由。

咱們來看看這段代碼反編譯後的文件,底層究竟是什麼原理:

Integer obj = Integer.valueOf(10);
int temp = obj.intValue();

能夠看見,自動裝箱的底層原理是調用了包裝類的 valueOf 方法,而自動拆箱的底層調用了包裝類的 intValue() 方法。

3. 不簡單的 Integer.valueOf

咱們上面已經看過了用於自動拆箱的 intValue 方法的源碼,很是簡單。接下來咱來看看用於自動裝箱的 valueOf,其餘包裝類倒沒什麼好說的,不過 Integer 中的這個方法仍是有點東西的:

IntegerCache 又是啥,點進去看看:

IntegerCache 是 Integer 類中的靜態內部類,綜合這兩段代碼,咱們大概也能知道,IntegerCache 其實就是個「緩存」,其中定義了一個緩衝區 cache,用於存儲 Integer 類型的數據,「緩存區間是 [-128, 127]」

回到 valueOf 的源碼:它首先會判斷 int 類型的實參 i 是否在可緩存區間內,若是在,就直接從緩存 IntegerCache 中獲取對應的 Integer 對象;若是不在緩存區間內,則會 new 一個新的 Integer 對象。

結合這個特性,咱們來看一個題目,兩種相似的代碼邏輯,可是卻獲得徹底相反的結果。:

public static void main(String args[]) {
    Integer a1 = 127;
    Integer a2 = 127;
    System.out.println(a1 == a2); // true

    Integer b1 = 128;
    Integer b2 = 128;
    System.out.println(b1 == b2); // false
}

咱們知道,== 擁有兩種應用場景:

  • 對於引用類型來講,判斷的是內存地址是否相等
  • 對於基本類型來講,判斷的是值是否相等

從 a1 開始看,因爲其值在 InterCache 的緩存區間內,因此這個 Integer 對象會被存入緩存。而在建立 a2 的時候,因爲其值和 a1 相等,因此直接從緩存中取出值爲 127 的 Integer 對象給 a2 使用,也就是說,a1 和 a2 這兩個 Integer 的對象引用都指向同一個地址。

對於 b1 和 b2 來講,因爲 128 不在 IntegerCache 的緩存區間內,那就只能本身老老實實開闢空間了,因此 b1 和 b2 指向不一樣的內存地址。

很顯然,因爲 InterCache 緩存機制的存在,可能會讓咱們在編程的時候出現困惑,所以最好使用 .equals 方法來比較 Integer 值是否相等。Integer 重寫了 .equals 方法:

固然,其餘包裝類雖然沒有緩存機制,可是也都重載了 .equals 方法,用於根據值來判斷是否相等。所以,得出結論,「使用 equals 方法來比較兩個包裝類對象的值」

4. Object 類能夠接收全部數據類型

綜上,有了自動拆裝箱機制,基本數據類型能夠自動的被轉爲包裝類,而 Object 是全部類的父類,也就是說,Object 能夠接收全部的數據類型了」(引用類型、基本類型)!!!

不信你能夠試試,直接用 Object 類接收一個基本數據類型 int,徹底是能夠的。

Object obj = 10;
int temp = (Integer) obj;

解釋一下上面這段代碼發生了什麼,下面這張圖很重要,你們仔細看:

5. 包裝類在集合中的普遍使用

其實包裝類最多見的使用就是在集合中,由於集合不容許存儲基本類型的數據,只能存儲引用類型的數據。那若是咱們想要存儲 一、二、3 這樣的基本類型數據怎麼辦?舉個例子,咱們能夠以下聲明一個 Integer對象的數組列表:

ArrayList<Integer> list = new ArrayList<>();

往這個列表中添加 int 型數據:

list.add(3); 

上面這個調用在底層將會發生自動裝箱操做:

list.add (Integer.valueOf(3));

基本數據類型 int 會被轉換成 Integer 對象存入集合中。

咱們再來從這個集合中根據某個下標 i 獲取對應的 Integer 對象,並用基本數據類型 int 接收:

int n = list.get(i);

上面這個調用在底層將會發生自動拆箱操做:

int n = list.get(i).intValue();

6. 數據類型轉換

另外,除了在集合中的普遍應用,包裝類還包含一個重要功能,那就是提供將String型數據變爲基本數據類型的方法,使用幾個表明的類作說明:

Integer

Double

Boolean

這些方法均被 static 標識,也就是說它們被各自對應的全部對象共同維護,直接經過類名訪問該方法。舉個例子:

String str = "10";
int temp = Integer.parseInt(str);// String -> int
System.out.println(temp * 2); // 20

須要特別注意的是:Character 類裏面並不存在字符串變爲字符的方法,由於 String 類中已經有一個 charAt()的方法能夠根據索引取出字符內容。



😁 點擊下方卡片關注公衆號「飛天小牛肉」(專一於分享計算機基礎、Java 基礎和麪試指南的相關原創技術好文,幫助讀者快速掌握高頻重點知識,有的放矢),與小牛肉一塊兒成長、共同進步 

🎉 並向你們強烈推薦我維護的 Gitee 倉庫 「CS-Wiki」(Gitee 推薦項目,目前已 1.0k+ star。致力打造完善的後端知識體系,在技術的路上少走彎路。相比公衆號,該倉庫擁有更健全的知識體系,歡迎給位小夥伴前來交流學習,倉庫地址 https://gitee.com/veal98/CS-Wiki。也可直接下方掃碼訪問


原創不易,讀完有收穫不妨點贊|分享|在看支持

本文分享自微信公衆號 - 飛天小牛肉(CS-Wiki)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索