Java自動拆箱與裝箱

微信公衆號:infoQc
若有問題或建議,請公衆號留言
最近更新:2018-08-19java

包裝類型

     在講解正文以前,我很想問這麼一個問題:"Java爲咱們提供了8種基本數據類型,爲何還須要提供各自的包裝類型呢?"。您可能會以爲這個問題問的很奇怪,可是我以爲仍是值的思考的。
     由於Java是一門面向對象的語言,基本數據類型並不具有對象的性質。而包裝類型則是在基本類型的基礎上,添加了屬性和方法,從而成爲了對象。試想,一個int類型怎麼添加到List集合中?數組

自動拆箱與裝箱

概念:

     首先,咱們來看一個例子:緩存

1Integer number = 10//自動裝箱
2int count = number; //自動拆箱
複製代碼

     基本數據類型自動轉化爲包裝類型時,即爲自動裝箱。
     反之,包裝類型自動轉化爲基本數據類型時,即爲自動拆箱。
     看概念您是否是以爲很簡單,但真的這麼"簡單"嗎?不妨咱們來看兩道題。微信

實操:

  • 題目一:
 1int z = 127;
2Integer a = 127;
3Integer b = Integer.valueOf(127);
4Integer f = new Integer(127);
5Integer g = new Integer(127);
6
7int y = 128;
8Integer c = 128;
9Integer d = Integer.valueOf(128);
10Integer e = new Integer(128);
11Integer h = new Integer(128);
12
13System.out.println("z==a結果:" + (z == a));
14System.out.println("z==b結果:" + (z == b));
15System.out.println("z==f結果:" + (z == f));
16System.out.println("a==b結果:" + (a == b));
17System.out.println("a==f結果:" + (a == f));
18System.out.println("f==g結果:" + (f == g));
19
20System.out.println("y==c結果:" + (y == c));
21System.out.println("y==d結果:" + (y == d));
22System.out.println("y==e結果:" + (y == e));
23System.out.println("c==d結果:" + (c == d));
24System.out.println("c==e結果:" + (c == e));
25System.out.println("e==h結果:" + (e == h));
複製代碼
  • 題目二:
 1Integer i = 1;
2Integer j = 2;
3Integer k = 3;
4Long m = 3L;
5Long n = 2L;
6System.out.println("k==i+j結果:" + (k == i + j));
7System.out.println("k.equals(i+j)結果:" + (k.equals(i + j)));
8System.out.println("m==i+j結果:" + (m == i + j));
9System.out.println("m.equals(i+j)結果:" + (m.equals(i + j)));
10System.out.println("m.equals(i+n)結果:" + (m.equals(i + n)));
複製代碼

思考:

  • 什麼狀況會觸發自動裝箱操做?
         當基本類型賦值給包裝類型引用時,會觸發自動裝箱操做,調用包裝類型的valueOf()方法。
  • 什麼狀況會觸發自動拆箱操做?
         當包裝類型參與運算時會觸發自動拆箱操做,調用包裝類型對應的***Value()方法,例如Integer類爲intValue()。Why?由於運算是基本數據類型要作的事情。

     補充:此處利用java提供的反彙編器javap,查看java編譯器爲咱們生成的字節碼。經過它,咱們能夠對照源代碼和字節碼,來對上述觀點進行論證。app

1Integer number = 10;//自動裝箱
2int number2 = 10;
3Integer number3 = 10;
4System.out.println(number == number2);//自動拆箱
5System.out.println(number + number3);//自動拆箱
6System.out.println(number.equals(number2));//自動裝箱
複製代碼
圖注:infoQc公衆號
圖注:infoQc公衆號

     由圖中能夠看出,執行過程確實如上述代碼末尾註釋,而且自動裝箱調用了valueOf()方法,自動拆箱調用了intValue()方法。

  • Integer的valueOf()和intValue()又是如何實現的呢?

     話很少說,咱們來看下Integer這兩個方法的源碼:spa

1public int intValue({
2    return value;
3}
4
5public static Integer valueOf(int i{
6    if (i >= IntegerCache.low && i <= IntegerCache.high)
7        return IntegerCache.cache[i + (-IntegerCache.low)];
8    return new Integer(i);
9}
複製代碼

     intValue()很簡單,返回Integer對象的value值。
     valueOf()判斷當前int值是否在IntegerCache的區間內(-128~127),在則從IntegerCache的cache數組中獲取指定位置的Integer對象,不然建立一個Integer對象,value值爲i。
     抱着打破砂鍋問到底的態度,咱們繼續看下IntegerCache源碼:code

 1private static class IntegerCache {
2    static final int low = -128;
3    static final int high;
4    static final Integer cache[];
5
6    static {
7        // high value may be configured by property
8        int h = 127;
9        String integerCacheHighPropValue =
10            sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
11        if (integerCacheHighPropValue != null) {
12            try {
13                int i = parseInt(integerCacheHighPropValue);
14                i = Math.max(i, 127);
15                // Maximum array size is Integer.MAX_VALUE
16                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
17            } catch( NumberFormatException nfe) {
18                // If the property cannot be parsed into an int, ignore it.
19            }
20        }
21        high = h;
22
23        cache = new Integer[(high - low) + 1];
24        int j = low;
25        for(int k = 0; k < cache.length; k++)
26            cache[k] = new Integer(j++);
27
28        // range [-128, 127] must be interned (JLS7 5.1.7)
29        assert IntegerCache.high >= 127;
30    }
31
32    private IntegerCache() {}
33}
複製代碼

     IntegerCache做爲Integer私有靜態內部類,定義了靜態語句塊,在類加載時會被調用。作什麼呢?按照low和high默認爲-128~127,依次建立對應數值的Integer對象,並放入cache數組中,做爲緩存。經過valueOf()源碼,咱們知道,那裏就是體現它緩存價值的地方。
     補充:緩存裏下限-128不可變,上限默認爲127,能夠經過java -Djava.lang.Integer.IntegerCache.high=***來動態調整。orm

實操分析:

     有了上述的知識儲備,咱們回顧一下以前的兩道題目,是否是容易了不少?cdn

  • 題目一解析:
 1int z = 127;
2Integer a = 127;
3Integer b = Integer.valueOf(127);
4Integer f = new Integer(127);
5Integer g = new Integer(127);
6
7int y = 128;
8Integer c = 128;
9Integer d = Integer.valueOf(128);
10Integer e = new Integer(128);
11Integer h = new Integer(128);
12
13System.out.println("z==a結果:" + (z == a));//true 和基本數據類型作==,比較的是值,故包裝類型會自動拆箱
14System.out.println("z==b結果:" + (z == b));//true 同理,自動拆箱
15System.out.println("z==f結果:" + (z == f));//true 同理,自動拆箱
16System.out.println("a==b結果:" + (a == b));//true 因127在IntegerCache的-128~127範圍內,故採用緩存裏的對象,故兩者引用地址一致
17System.out.println("a==f結果:" + (a == f));//false 對象之間==,比較的是引用,由於a指向IntegerCache裏緩存的對象,f指向的new出來的對象,故引用地址不一樣
18System.out.println("f==g結果:" + (f == g));//false f和g都是本身new出來的對象,故引用地址不一樣
19
20System.out.println("y==c結果:" + (y == c));//true 自動拆箱
21System.out.println("y==d結果:" + (y == d));//true 自動拆箱
22System.out.println("y==e結果:" + (y == e));//true 自動拆箱
23System.out.println("c==d結果:" + (c == d));//false 因128不在IntegerCache的-128~127範圍內,故採用緩存裏的對象,故兩者引用地址一致
24System.out.println("c==e結果:" + (c == e));//false 引用地址不一樣
25System.out.println("e==h結果:" + (e == h));//false 引用地址不一樣
複製代碼
  • 題目二解析:
 1Integer i = 1;
2Integer j = 2;
3Integer k = 3;
4Long m = 3L;
5Long n = 2L;
6
7System.out.println("k==i+j結果:" + (k == i + j));//true 首先+操做是數值操做,故i和j會自動拆箱,結果爲基本類型int 3。而後k自動拆箱,進行值比較3==3
8System.out.println("k.equals(i+j)結果:" + (k.equals(i + j)));//true 首先+操做是數值操做,故i和j會自動拆箱,結果爲基本類型int 3。
9// 而後調用equals(Object o)方法,此時int 3自動裝箱,爲Integer 3。Integer類的equals方法裏比較的是值,故爲true
10
11System.out.println("m==i+j結果:" + (m == i + j));//true 先i和j自動拆箱,再m自動拆箱,比較值。int自動提高爲long,值一致爲true
12System.out.println("m.equals(i+j)結果:" + (m.equals(i + j)));//false 先i和j自動拆箱,equals(Object o)再自動裝箱,爲Integer 3。Long和Integer類型不一致,返回false
13System.out.println("m.equals(i+n)結果:" + (m.equals(i + n)));//true 先i和j自動拆箱,int自動提高爲long,故爲long 3L。再自動裝箱爲Long 3,返回true
複製代碼

Java緩存機制

     Java爲了在自動裝箱時避免每次去建立包裝類型,採用了緩存技術。即在類加載時,初始化必定數量的經常使用數據對象(即常說的熱點數據),放入緩存中,等到使用時直接命中緩存,減小資源浪費。
     經過查看源碼發現,Java提供的8種基本數據類型,除了float和double外,都採用了緩存機制。其實緣由也很簡單,由於浮點型數據,熱點數據並很差肯定,故並未採用。
     最後給出各種型緩存範圍:
       Character: 0~127
       Byte,Short,Integer,Long: -128 到 127
       Boolean: true,false
     你們再遇到相似題目時,就能夠正確的作出判斷啦。對象

相關文章
相關標籤/搜索