Java工具類—包裝類

Java工具類——包裝類

咱們都知道,JDK 其實給咱們提供了不少不少 Java 開發者已經寫好的現成的類,他們其實均可以理解成工具類,好比咱們常見的集合類,日期相關的類,數學相關的類等等,有了這些工具類,你會發現它能很大程度的幫你節省時間,能很方便的實現你的需求。固然,沒有這些包,你也能實現你的需求,可是你須要時間,今天咱們主要是來學習一下包裝類。java

1、包裝類介紹

一、爲何須要包裝類?

咱們知道 Java 語言是一個面向對象的編程語言,可是 Java 中的基本數據類型卻不是面向對象的,可是咱們在實際使用中常常須要將基本數據類型轉換成對象,便於操做,好比,集合的操做中,這時,咱們就須要將基本類型數據轉化成對象,因此就出現了包裝類。程序員

二、包裝類是什麼呢?

包裝類,顧名思義就是將什麼通過包裝的類,那麼是將什麼包裝起來的呢,顯然這裏是將基本類型包裝起來的類。包裝類的做用就是將基本類型轉成對象,將基本類型做爲對象來處理。面試

Java 中咱們知道,基本數據類型有8個,因此對應的包裝類也是8個,包裝類就是基本類型名稱首字母大寫。但Integer 和 Character 例外,它們顯示全稱,以下面表格所示:編程

基本數據類型 對應包裝類
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean

2、包裝類的繼承關係

經過閱讀 Java8 的 API 官方文檔或者看源代碼咱們能夠得知8個包裝類的繼承關係以下:api

經過以上的繼承關係圖,咱們其實能夠這樣記憶,包裝類裏面有6個與數字相關的都是繼承自 Number 類,而其他兩個不是與數字相關的都是默認繼承 Object 類。經過看 API 官方文檔,咱們還能夠得知這8個包裝類都實現了Serializable , Comparable 接口。好比下圖的 Integer 類數組

public final class Integer extends Number implements Comparable<Integer> {}複製代碼

3、包裝類的使用方法(基本操做)

接下來關於包裝類的講解我就講Integer包裝類,其餘的都依此類推,用法和操做都是差很少的,只是名字不同而已。緩存

一、包裝類的構造方法

8個包裝類都有帶本身對應類型參數的構造方法,其中8個包裝類中除了Character還有構造方法重載,參數是String類型的。架構

Integer one = new Integer(666);
Integer two = new Integer("666");複製代碼

二、包裝類的自動拆裝箱

在瞭解自動拆裝箱以前,咱們得先知道什麼是拆箱和裝箱。其實拆裝箱主要應對基本類型與包裝類型的相互轉換問題。oracle

  • 裝箱:將基本類型轉換成包裝類型的過程叫作裝箱。編程語言

  • 拆箱:將包裝類型轉換成基本類型的過程叫作拆箱。

其實,在 JDK1.5 版本以前,是沒有自動拆裝箱的,開發人員要手動進行裝拆箱:

//手動裝箱,也就是將基本類型10轉換爲引用類型
Integer integer = new Integer(10);
//或者
Integer integer1 = Integer.valueOf(10);

//手動拆箱,也就是將引用類型轉換爲基本類型
int num = integer.intValue();複製代碼

而在在 JDK1.5 版本以後,爲了減小開發人員的工做,提供了自動裝箱與自動拆箱的功能。實現了自動拆箱和自動裝箱,以下方代碼所示:

//自動裝箱
Integer one = 1;
//自動拆箱
int two = one + 10;複製代碼

其實以上兩種方式本質上是同樣得,只不過一個是自動實現了,一個是手動實現了。至於自動拆裝箱具體怎麼實現的我這裏不作深刻研究。

4、包裝類的緩存機制

咱們首先來看看如下代碼,例1:

public static void main(String[] args) {
  Integer i1 = 100;
  Integer i2 = 100;
  Integer i3 = new Integer(100);
  Integer i4 = new Integer(100);
  System.out.println(i1 == i2);//true
  System.out.println(i1 == i3);//false
  System.out.println(i3 == i4);//false
  System.out.println(i1.equals(i2));//true
  System.out.println(i1.equals(i3));//true
  System.out.println(i3.equals(i4));//true
}複製代碼

當咱們修改了值爲200的時候,例2:

public static void main(String[] args) {
  Integer i1 = 200;
  Integer i2 = 200;
  Integer i3 = new Integer(200);
  Integer i4 = new Integer(200);
  System.out.println(i1 == i2);//false
  System.out.println(i1 == i3);//false
  System.out.println(i3 == i4);//false
  System.out.println(i1.equals(i2));//true
  System.out.println(i1.equals(i3));//true
  System.out.println(i3.equals(i4));//true
}複製代碼

經過上面兩端代碼,咱們發現修改了值,第5行代碼的執行結果居然發生了改變,爲何呢?首先,咱們須要明確第1行和第2行代碼其實是實現了自動裝箱的過程,也就是自動實現了 Integer.valueOf 方法,其次,==比較的是地址,而 equals 比較的是值(這裏的 eauals 重寫了,因此比較的是具體的值),因此顯然最後五行代碼的執行結果沒有什麼疑惑的。既然==比較的是地址,例1的第5行代碼爲何會是true呢,這就須要咱們去了解包裝類的緩存機制。

其實看Integer類的源碼咱們能夠發如今第780行有一個私有的靜態內部類,以下:

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() {}
}複製代碼

咱們知道,靜態的內部類是在整個 Integer 加載的時候就已經加載完成了,以上代碼初始化了一個 Integer 類型的叫 cache 的數組,取值範圍是[-128, 127]。緩存機制的做用就是提早實例化相應範圍數值的包裝類對象,只要建立處於緩存範圍的對象,就使用已實例好的對象。從而避免重複建立多個相同的包裝類對象,提升了使用效率。若是咱們用的對象範圍在[-128, 127]以內,就直接去靜態區找對應的對象,若是用的對象的範圍超過了這個範圍,會幫咱們建立一個新的 Integer 對象,其實下面的源代碼就是這個意思:

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

因此 例1 代碼裏,i1 和i2 是100,值的範圍在[-128, 127],因此直接區靜態區找,因此i1和i2指向的地址是同一個,因此 i1==i2;而在例2的代碼裏,i1 和i2 是200,值的範圍不在在[-128, 127],因此分別建立了一個新的對象,放在了堆內存裏,各自指向了不一樣的地址,因此地址都不一樣了,天然 i1 不等於 i2。

經過分析源碼咱們能夠發現,只有 double 和 float 的自動裝箱代碼沒有使用緩存,每次都是 new 新的對象,其它的6種基本類型都使用了緩存策略。 使用緩存策略是由於,緩存的這些對象都是常用到的(如字符、-128至127之間的數字),防止每次自動裝箱都建立一次對象的實例。

5、包裝類和基本數據類型的區別

  • 默認值不一樣

包裝類的默認值是null,而基本數據類型是對應的默認值(好比整型默認值是0,浮點型默認值是0.0)

  • 存儲區域不一樣

基本數據類型是把值保存在棧內存裏,包裝類是把對象放在堆中,而後經過對象的引用來調用他們

  • 傳遞方式不一樣

基本數據類型變量空間裏面存儲的是值,傳遞的也是值,一個改變,另一個不變,而包裝類屬於引用數據類型,變量空間存儲的是地址(引用),傳遞的也是引用,一個變,另一個跟着變。

5、小結

​ 以上就是我對於Java包裝類的我的理解,其實學習這些工具類還有一個更好的學習方式,就是去看官方文檔(API官方文檔地址:docs.oracle.com/javase/8/do…


最後,最近不少小夥伴找我要Linux學習路線圖,因而我根據本身的經驗,利用業餘時間熬夜肝了一個月,整理了一份電子書。不管你是面試仍是自我提高,相信都會對你有幫助!目錄以下:

免費送給你們,只求你們金指給我點個贊!

電子書 | Linux開發學習路線圖

也但願有小夥伴能加入我,把這份電子書作得更完美!

有收穫?但願老鐵們來個三連擊,給更多的人看到這篇文章

推薦閱讀:

相關文章
相關標籤/搜索