1. 程式人生 > >Java學習筆記 (十五) 自動拆箱與自動裝箱

Java學習筆記 (十五) 自動拆箱與自動裝箱

什麼是自動拆裝箱

  • 自動裝箱: 就是將基本資料型別自動轉換成對應的包裝類.
  • 自動拆箱:就是將包裝類自動轉換成對應的基本資料型別。
For example :
  Integer a=1;  //自動裝箱
  int b=a;     //自動拆箱

基本資料型別與包裝類對應關係,如下:

基本資料型別 包裝類
boolean Boolean
byte Byte
char Character
float Float
int Integer
long Long
short Short
double Double

自動拆裝箱是怎麼實現的

我們可以通過反編譯去看他是如何實現的,反編譯前程式碼:

    public static  void main(String[]args){

        Integer integer=1; //裝箱
        int i=integer; //拆箱
        Double dou=2.0; //裝箱
        double dou2=dou; //拆箱
        Float f=3f;//裝箱
        float f2=f;//拆箱
        Byte b=4;//裝箱
        byte
b2=b;//拆箱 Long lo=5l;//裝箱 long lo2=lo;//拆箱 Character character=6;//裝箱 char c2=character;//拆箱 Boolean bl=false;//裝箱 boolean bl2=bl;//拆箱 Short s=8;//裝箱 short s2=s;//拆箱 }

反編譯後代碼:
這裡寫圖片描述

從上面反編譯後的程式碼可以看出,自動裝箱都是通過包裝類的valueOf()方法來實現的.自動拆箱都是通過包裝類物件的**Value()來實現的。

包裝類比較資料大小是自動拆箱進行比較的嗎

原始碼:

        Integer a=1;
        System.out.println(a==1?"等於":"不等於");
        Boolean bool=false;
        System.out.println(bool?"真":"假");

反編譯後:
這裡寫圖片描述
可以看到,包裝類進行比較運算,是先進行拆箱成基本資料型別,然後進行運算的。

自動拆裝箱有什麼用

我從官網偷了個例子:

List<Integer> li = new ArrayList<>();
for (int i = 1; i < 50; i += 2)
    li.add(i);

為什麼上面的程式碼不報錯呢,因為編譯時程式碼自動轉換了一下,如下:

List<Integer> li = new ArrayList<>();
for (int i = 1; i < 50; i += 2)
    li.add(Integer.valueOf(i));

所以 li集合裡面存的實際上是Interger物件,而不是基礎資料型別。

  • 第一,方便使用,包裝類中有很多方法,比如轉換成其他資料型別的方法,等等
  • 第二, 多型,我們都知道,所有的類都繼承了Object ,而基本資料型別沒有繼承,所有使用包裝類可以使用多型這個特性,就像上面那個例子。

自動拆裝的疑惑?

一、包裝類物件為什麼會相等

看,a==b 為true,c==d 為false

        Integer a=2;
        Integer b=2;

        Integer c=128;
        Integer d=128;

        System.out.println(a==b);  //true
        System.out.println(c==d);  //false

運算元都是物件,“==” 比較的是引用地址,都是自動裝箱。應該都是false才是,為什麼是a==b是true呢?
我們知道,自動裝箱是通過valueOf()方法來實現的,原始碼如下:

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

可以看出來,數值大小在low到high之間返回的是快取中的物件,超出這個範圍則是new 一個返回。low= -128 ;high如果不指定的話值為127;
很顯然,2在這個範圍,所以Integer.valueOf(2)的時候,返回的是快取的物件,所以a和b其實是一個物件所以相等。
其他幾個包裝類中;Double與Float是直接new 一個物件返回的;Long,Short,Character與Integer類似,而Byte則是直接返回快取中的物件,想一下Byte的取值範圍就明白了;Boolean比較特殊,一共就兩個值,如下:

這裡寫圖片描述

二、什麼時候會進行自動拆裝箱

看一段我從某位大佬那裡借鑑(偷)過來的程式碼:

        Integer a = 1;
        Integer b = 2;
        Integer c = 3;

        Long g = 3L;
        Long h = 2L;

        System.out.println(c==(a+b)); //1. true
        System.out.println(g==(a+b)); //2. true
        System.out.println(c.equals(a+b));//3. true

        System.out.println(g.equals(a+b)); //4. false
        System.out.println(g.equals(a+h));//5. true

仔細想下,為什麼會這樣呢?我來分析下,如果您認為我分析有誤或者有疑問歡迎聯絡我。

  • 1、為什麼c==(a+b) 為true?
    是因為,a+b 是進行算術運算,上文提到過,算術運算時,會拆箱成基本資料型別進行運算,那麼a+b的結果也是基本資料型別,而包裝類型別進行==比較時,如果有一方是基本資料型別,那麼包裝類型別會進行拆箱。所以c==(a+b) 比較的是數值是否相等。 結果為true
  • 2、為什麼g==(a+b) 為true?
    同1。
  • 3、c.equals(a+b) 為什麼為 true?
    首先放一段Integer.eqquals()的原始碼:
    public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }

看了上面的原始碼就很好理解了,a和b都是Integer型別,拆箱算術運算結束後結果也是int 型別,然後equals需要的是物件,所以這裡會進行裝箱,還是Integer型別,然後型別判斷結果為true,比較的是數值,所以結果為true。
- 4、g.equals(a+b) 為什麼是false呢?
g是Long型別,我們看下Long的equals()方法;

    public boolean equals(Object obj) {
        if (obj instanceof Long) {
            return value == ((Long)obj).longValue();
        }
        return false;
    }

看了原始碼很容易就知道為什麼了,上面說過a+b結果為int型別,在這裡自動裝箱也是對應的Integer型別,Integer 型別不屬於Long型別,結果為false,直接返回false,
- 5、g.equals(a+h)為什麼為true?
相信你已經很明白了為什麼了。

下面放出反編譯後的程式碼,很容易就看出哪裡進行了拆箱裝箱。

這裡寫圖片描述

參考資料: