1. 程式人生 > >裝箱與拆箱

裝箱與拆箱

對象 nal important 原因 比較 運算符 空指針異常 ros 相同

裝箱與拆箱

什麽是裝箱與拆箱

描述

語言描述,裝箱就是自動將基本數據類型轉換為包裝器類型;拆箱就是自動將包裝器類型轉換為基本數據類型。

代碼描述就是:

    Integer integer = 100;  //自動裝箱
    int i = integer;  //自動拆箱

基本技術類型對應的包裝器類型表:

數據類型包裝器類型
int(4字節) Integer
byte(1字節) Byte
short(2字節) Short
long(8字節) Long
float(4字節) Float
double(8字節) Double
char(2字節) Character
boolean(未定) Boolean

如何實現裝箱與拆箱

裝箱與拆箱的代碼


public class IntegerAndInt {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Integer integer = 100; //自動裝箱
        int i = integer; //自動拆箱
    }

}

反編譯class文件

技術分享圖片
從反編譯得到的字節碼內容可以看出,在裝箱的時候自動調用了Integer的valueOf(int)方法。而在拆箱的時候自動調用的是Integer的intValue方法。

因此,裝箱過程是通過調用包裝器的valueOf方法實現的,而拆箱過程是通過調用包裝器的xxxValue方法實現的。

valueOf方法

 public static Integer valueOf(int i) {
     return  i >= 128 || i < -128 ? new Integer(i) : SMALL_VALUES[i + 128];
 }

首先判斷數值大小,如果數值大於等於128或者小於-128,則創建一個Integer對象返回,否則,則返回SMALL_VALUES的i+128的數值。

接著,查看以下Integer的構造函數:

private final int value;

public Integer(int value) {
    this.value = value;
}

public Integer(String string) throws NumberFormatException {
    this(parseInt(string));
}

Integer定義了一個value變量,創建一個Integer對象時,就會給這個變量初始化。第二個傳入的是一個String變量,它會先把它轉換成一個int值,然後進行初始化。

再看一下SMALL_VALUES[i+128]是什麽:

private static final Integer[] SMALL_VALUES = new Integer[256];

SMALL_VALUES是一個靜態的Integer數組對象,也就是說valueOf返回的都是一個Integer對象。
通過分析可以看到裝箱的過程會創建對應的對象,這個會消耗內存,所以裝箱的過程會增加內存的消耗,影響性能。

initValue方法

 @Override
 public int intValue() {
     return value;
 }

intValue方法直接返回了value值。

裝箱與拆箱需要註意的一些問題

例子一

public class Main{
    public static void main(String[] args) {
        Integer i1 = 66, i2 = 66, i3 = 166, i4 = 166;
        System.out.println(i1 == i2);//true
        System.out.println(i3 == i4);//false
       }
}

看上面的代碼可以發現,兩個比較的結果不相同,再結合上面的裝箱原理,128~-127的裝箱是直接返回的SMALL_VALUES數組中存儲的值,所以i1和i2的裝箱結果返回的是同一個變量,所以是相等的,而i3和i4是返回的新創建的變量,兩個變量是不同的,所以不相等。

例子二

Integer a = new Integer(6);
Integer b = 6; // 將6自動裝箱成Integer類型
int c = 6;
System.out.println(a == b); // false 兩個引用沒有引用同一對象
System.out.println(a == c); // true a自動拆箱成int類型再和c比較

a是一個創建的Integer對象,而b是自動裝箱,是SMALL_VALUES數組中存儲的值,所以a和b是不同的對象,不相等,而c是int類型的值,a與c相比是會先拆箱為int類型的6,兩個6數值相比,故相等。

例子三

    Integer i1 = new Integer(6);
    Integer i2 = 6;
    System.out.println("i1.equals(i2):"+(i1.equals(i2))); //true

會發現同樣的對象,==與equals的結果是不同,先看一下equals源碼:

 @Override
 public boolean equals(Object o) {
     return (o instanceof Integer) && (((Integer) o).value == value);
 }

發現equals方法是比較value值相同的,比較的內容的本身。

例子四

Integer num1 = 100;
int num2 = 100;
Long num3 = 200L;
System.out.println(num1+num2); //200
System.out.println(num3 == (num1+num2)); //true
System.out.println(num3.equals(num1+num2)); //false
System.out.println(num3 == (num1+num2)); //true

當一個基本類型數據域封裝類進行==、+、-、*、/運算時,會將封裝類進行拆箱,對基礎數據類型進行運算。
而num3.equals(num1+num2)為false的原因是,num1+num2的類型不是Long,所以為false。
Long的equals方法:

 @Override
 public boolean equals(Object o) {
     return (o instanceof Long) && (((Long) o).value == value);
 }

而(num3 (num1+num2))為true,當運算符的兩個操作數都是包裝器類型的引用,則是比較指向的是否是同一個對象,而如果其中有一個操作數是表達式(即包含算術運算)則比較的是數值(即會觸發自動拆箱的過程)。

例子五

  Integer integer=null;
  int i=integer;

上面的代碼可以通過編譯,但是在運行時,就會拋出空指針異常。integer是Integer類型的對愛選哪個,可以指向null,但是對integer進行拆箱的時候,就是對一個null對象調用valueOf方發,所以會拋出空指針異常。

註意

Integer、Short、Byte、Charater、Long這幾個類的valueOf方法的實現是類似的,Double和Float的valueOf方法的實現是類似的,是沒有想Integer一樣的SMALL_VALUE數組的。
Double的valueOf方法:

public static Double valueOf(double d) {
    return new Double(d);
}

Float的valueOf方法:

public static Float valueOf(String s) throws NumberFormatException {
    return new Float(FloatingDecimal.getThreadLocalInstance().readJavaFormatString(s).floatValue());
}

還有,Boolean的valueOf方法:

public static Boolean valueOf(boolean b) {
    return (b ? TRUE : FALSE);
}

所以不管對象是不是同一個,只要對象的值時相同的,就是相等的。

5.總結

Java通過自動裝箱和拆箱的機制,節省了部分內存開銷和創建對象的開銷(Integer的128~-127常用值節省開支),提高了效率同時簡化了代碼,不用每次需要程序員轉換類型。在比較數值相等時,盡量使用equals()方法。

參考文章

https://www.cnblogs.com/wang-yaz/p/8516151.html 詳解Java的自動裝箱與拆箱(Autoboxing and unboxing)
https://www.cnblogs.com/dolphin0520/p/3780005.html 深入剖析Java中的裝箱和拆箱

裝箱與拆箱