首頁  >  文章  >  Java  >  Java 的自動裝箱與拆箱源碼分析

Java 的自動裝箱與拆箱源碼分析

WBOY
WBOY轉載
2023-05-09 12:10:111254瀏覽

    什麼叫裝箱 & 拆箱?

    將int基本型別轉換為Integer包裝型別的過程叫做裝箱,反之叫拆箱。

    先看一段程式碼

    public static void main(String[] args) {
        Integer a = 127, b = 127;
        Integer c = 128, d= 128;
        System.out.println(a == b); // true
        System.out.println(c == d); // false
    }

    不知道還有沒有人不知道這段程式碼出現true和false的原因。由此我們引出了Java裝箱的這個操作。我們帶著疑問去進行分析。

    裝箱(valueOf())

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

    我們可以發現,在最開始有一個判斷,如果這個值的範圍在[-128,127]之間,那麼就從這個快取( Integer數組)中取,如果不在這個範圍那麼直接new一個。

    為什麼要有[-128,127]的快取?

    我說說的理解,因為在我們的業務中,可能存在各種狀態和標識等Integer類型的字段,這些值一般都是0,1,2,3之類的,而且出現的比較頻繁,如果沒有緩存,那麼就需要頻繁的new對象,然後再釋放,就非常消耗內存空間,所以對於這個緩存就出現了,可以極大的幫助我們優化一些空間上的浪費。

    為什麼是[-128,127]?

    這個我看了一下,具體為什麼這裡就不詳說了,主要還是依賴電腦基礎知識,在你了解了什麼是原碼、反碼、補碼。就很容易知道為什麼是這個範圍區間了。

    這個值也是可以透過啟動參數來更改的。

    -XX:AutoBoxCacheMax=(size)

    自動裝箱帶來的效能問題

    那麼看到現在你應該要明白上面程式碼出現不同結果的原因了,那你有沒有想過,例如我們業務中一個for循環中,出現了統計數據類似這樣的操作,如果存在自動裝箱,那麼會出現什麼問題?我們來看下面一段程式碼。

    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        Integer count = 0;
        // int count = 0;
        for (int i = 0; i < 5000000; i++) {
            count += i;
        }
        System.out.println("计算时长:" + (System.currentTimeMillis() - startTime) + " ms");
    }
    
    // 执行结果:
    // Integer 计算时长:51 ms
    // int 计算时长:6 ms

    那麼透過執行結果可以明顯的發現自動裝箱頻繁的new物件、分配內存,造成時間和空間上的效能損耗。

    小總結

    透過上面的原始碼閱讀和測試分析,我們可以得出結論,我們平時在進行計算統計,或者方法入參的時候,應該盡量的避免這種類型轉換的問題。來提升我們整個程式碼的執行效率。

    拆箱(intValue)

    拆箱整體沒有什麼複雜的邏輯,直接傳回這個數值的基本型別。

    補充:自動裝箱、拆箱總是會發生嗎?

    其實不一定。看下面的一段範例程式碼,輸出結果已被註解在輸出語句後面。

    public static void main(String[] args) {
    // TODO 自动生成的方法存根
    Integer a = 1;
    Integer b = 2;
    Integer c = 3;
    Integer d = 3;
    Integer e = 321;
    Integer f = 321;
    Long g = 3L;
    System.out.println(c==d);//true
    //包装类的==在没有遇到算术运算的情况下不会自动拆箱
    System.out.println(e==f);//false
    System.out.println(c==(a+b));//true
    System.out.println(c.equals(a+b));//true
    System.out.println(g==(a+b));//true
    //equals方法不会处理数据转型关系
    System.out.println(g.equals(a+b));//false
    }

    發生自動裝箱、拆箱的情況如下:

    自動裝箱:基本型別賦值給包裝型別。如:Integer i1 = 1;

    自動拆箱:

    1. #包裝類型賦值給基本型別。如:int i2 = new Integer(1);

    2. int型別與Integer型別比較。 int類型與Integer類型比較如果值相等則結果總是為true。

    3. Integer類型遇到算術運算

    #但是為什麼在上例中,System.out.println(c==d);與System.out.println(e==f);輸出的結果不一樣呢?

    主要是因為Integer.valueOf()方法。 Integer的部分原始碼貼在下面:

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

    IntegerCache 是Integer的靜態內部類,valueOf()是包裝方法。從原始碼可以看出,cache是​​快取數組,當valueOf()方法的入參i在[-128,127]區間內,就會傳回快取數組中的Integer值,否則會重新new一個Integer。

    這就是System.out.println(c==d);與System.out.println(e==f);輸出結果不同的原因。 c和d在快取區間內,所以回傳的是同一個引用;而e和f不在快取區間內,回傳的都是new Integer,已經不是同一個引用。

    以上是Java 的自動裝箱與拆箱源碼分析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

    陳述:
    本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除