[JAVA] Java物件導向之包裝類,拆箱、裝箱

老夫不正經發表於2020-03-29

Java物件導向之包裝類,拆箱、裝箱

包裝類,拆箱、裝箱——一切皆物件

為何要包裝類

  1. 在物件導向中,”一切皆物件”,但基本資料型別的特殊存在不太符合這一理念,物件導向面向得並不純粹,因為基本型別變數並不是物件;
  2. 涉及進位制間的轉換的演算法,資料型別間的基本操作;如果都要我們來實現,那工作量就太大了;
  3. Java的集合框架並不支援基本資料型別的儲存,只支援物件儲存;

故此,針對Java基本資料型別封裝了包裝類,每一個基本型別都有一個對應的包裝類,以下是詳情:

基本資料型別的包裝類

八大基本資料型別的包裝類都使用final修飾,都是最終類,都不能被繼承。

// Byte

public final class Byte extends Number implements Comparable<Byte> { }

// Character

public final class Character implements java.io.Serializable, Comparable<Character> { }

// Short

public final class Short extends Number implements Comparable<Short> { }

// Integer

public final class Integer extends Number implements Comparable<Integer> { }

// Long

public final class Long extends Number implements Comparable<Long> { }

// Float

public final class Float extends Number implements Comparable<Float> { }

// Double

public final class Double extends Number implements Comparable<Double> { }

// Boolean

public final class Boolean implements java.io.Serializable, Comparable<Boolean> { }

複製程式碼

拆箱和裝箱

裝箱:把基本型別資料轉成對應的包裝類物件。

方式一:


Integer i = Integer.value(13);
複製程式碼

方式二:


Integer i = new Integer(13);
複製程式碼

拆箱:把包裝類物件轉成對應的基本資料型別資料。


int value = i.intValue();
複製程式碼

自動裝箱(Autoboxing)和自動拆箱(AutoUnboxing)

在Java 5之前的版本中,基本資料型別和包裝類之間的轉換是需要手動進行的,但Sun公司從Java5開始提供了的自動裝箱(Autoboxing)和自動拆箱(AutoUnboxing)操作 ;

  • 自動裝箱:可以把一個基本型別變數直接賦給對應的包裝型別變數。

Integer i = 13;
複製程式碼
  • 自動拆箱:允許把包裝類物件直接賦給對應的基本資料型別變數。

Integer i = new Integer(13);

Int j = i;
複製程式碼

自動裝箱和自動拆箱,也是一個語法糖/編譯器級別新特性,在底層依然是手動裝箱、拆箱操作;但是在裝箱操作中使用的是Integer.valueOf()方法,而不是直接new Integer();其他的幾個包裝類也是如此,裝箱操作中使用的是各自的valueOf()方法。

自動裝箱和自動拆箱的反編譯效果

switch 對包裝類的支援

switch支援的基本資料型別:byte,short,char,int;也支援對應的包裝類。因為在底層,switch中會對包裝類做手動拆箱操作。

switch 中包裝類的支援

考慮下面的語句:


Object obj = 17;
複製程式碼

在上述程式碼語句中有如下的操作:

  1. 自動裝箱: Integer i = 17;
  2. 引用的自動型別轉換,把子類物件賦給父類變數: Object obj = i; 因為Object類的父類;

因此,Object可以接受一切資料型別的值;Object陣列:Object[]該陣列可以裝一切資料型別。


Object\[\] arr = {“A”,12,3.14,true}; // 這是完全可行的
複製程式碼

包裝類的常用操作方法(以Integer為例):

1. 包裝類中的常量:

  • MAX_VALUE :最大值
  • MIN_VALUE :最小值
  • SIZE :變數在記憶體中儲存資料佔多少位
  • TYPE :對應的基本型別

2. 包裝類的構造器:建立包裝類物件,

/\*\* Integer 構造器原始碼 \*\*/

// 接收int型別資料構建Integer物件

public Integer(int value) {

this.value = value;

}

// 接受字串資料構建Integer物件

public Integer(String s) throws NumberFormatException {

this.value = parseInt(s, 10);

}
複製程式碼

其他的幾個包裝型別也是這樣的規律,具體實現檢視原始碼即可。

3. 基本型別和包裝型別的轉換(裝箱和拆箱)

裝箱:


Integer i1 = new Integer(13); // 方式一,每次都會建立新物件,不推薦

Integer i2 = Integer.valueOf(13); // 方式二,推薦,底層使用了快取。
複製程式碼

拆箱:


int val = i1.intValue();
複製程式碼

4. String和基本型別/包裝型別之間的轉換操作

把String轉換為包裝類型別:


方式1:包裝類.valueOf(String str):

Integer i = Integer.valueOf(“13”);


方式2: new 包裝類(String str):

Integer i= new Integer(“13”);
複製程式碼

把包裝類物件轉換為String.


String str = 物件.toString(); // 不止包裝類物件,其他任何物件都可以使用toString()轉換;
複製程式碼

把基本資料型別轉換為String:


String str = 13 + "";
複製程式碼

把String轉換為基本資料型別:


parseXxx(String s) : xxx表示8大基本資料型別,如:

String input=”12345”;

int value = Integer.parseInt(input);
複製程式碼

5. 對於Boolean來說,無論是使用new Boolean(“”); 還是Boolean.valueOf(“”), 只有使用true/TRUE會被認為是true,其他都是false。


Boolean b1 = new Boolean("true"); // true

Boolean b1 = new Boolean("TRUE"); // true

Boolean b1 = new Boolean("sjsj"); // false
複製程式碼

包裝類中的快取設計

在包裝類中提供了快取設計,會對一定範圍內的資料作快取,如果資料在範圍內,會優先從快取中取資料,超出範圍才會建立新物件;Byte、Short、Integer、Long:快取[-128,127]區間的資料;Character:快取[0,127]區間的資料;包裝類中的快取設計,也稱為享元模式。

快取設計會在包裝類中的valueOf()方法中實現,所以才會推薦使用valueOf()方法來實現拆箱操作,如下是Integer類的valueOf()原始碼:

valueOf()

再檢視快取實現細節:

IntegerCache

通過檢視原始碼可知,JVM會對-128 到 127之間的做快取,如果你的變數值在這個範圍內,就會優先從快取中取資料,否則就會建立新物件。當然這個快取區間也是可是設定的。

那麼以下這個例子就可以解釋了:

public static void main(String\[\] args) {

    Integer i1 = Integer.valueOf(13);
    Integer i2 = Integer.valueOf(13);

    System.out.println(i1 == i2);
    // 輸出為true。因為13在\[-128, 127\]之間,但是並沒有建立新物件

    Integer i3 = Integer.valueOf(129);
    Integer i4 = Integer.valueOf(129);

    System.out.println(i3 == i4);
    // 輸出為false, 因為129不在\[-128, 127\]之間,是使用new Integer()建立了新物件,故比較為false

    Integer i5 = 129;
    Integer i6 = 129;

    System.out.println(i5 == i6); // 輸出為false
    System.out.println(i5.equals(i6));

    // 輸出為true,建議:如果物件包裝類物件的值作比較,應選用包裝類的equals方法。

}
複製程式碼

我們再來看Integer的equals方法的實現原始碼:

Integer的equals方法

可以發現,包裝類在比較時會將包裝型別拆箱為基本資料型別,並使用==做比較。

包裝型別和基本資料型別的區別

包裝型別和基本資料型別的區別(以Integer與int的區別為例):

1. 預設值:

  • int的預設值是0。
  • Integer的預設值為null。Integer既可以表示null,又可以表示0。

2. 包裝類中提供了該型別相關的很多演算法操作方法:

*   static String toBinaryString(int i) :把十進位制轉換為二進位制
*   static String toOctalString(int i) : :把十進位制轉換為八進位制
*   static String toHexString(int i) : :把十進位制轉換為十六進位制
複製程式碼

3. 在集合框架中,只能儲存物件型別,不能儲存基本資料型別值。

4. Integer和int並不是相同的資料型別,儘管值是相同的。Integer是一個類,可以例項化為物件,但int只是一個基本資料型別。

5. 在JVM中,基本型別變數儲存在棧中的,而包裝型別物件存放於堆中。

其實,包裝類就是把基本資料類物件化,包裝類是基本資料型別的超集;在開發中,建議成員變數優先使用包裝型別,區域性變數優先考慮基本資料型別。

完結。老夫雖不正經,但老夫一身的才華

相關文章