Java 中 int 和 Integer 的區別

一、 簡單介紹

Java 是一門面向對象的編程語言,分爲:基本類型(primitive types)和引用類型(reference types),詳情見博客- 基本類型

1、關係

int 是我們經常使用的基礎數據類型,在我們的使用中經常出現,而Integer在我們的日常工作中也會出現,那麼他們的關係是什麼呢?


Integer 是 int 的包裝類,jdk 1.5以後添加了自動拆裝箱功能。也就是在編譯期可以將int 裝箱爲 Integer ,也可以將Integer 拆箱爲 int ,這裏涉及一個緩存的問題下面會講,

2、注意

雖然自動拆裝箱爲我們勉去了手動進行這些操作的麻煩,但是我們還是要儘量避免無意中的拆裝箱行爲,尤其是在對於性能很敏感的地方。【畢竟創建大量對象和創建大量整數的開銷不一樣,效率差距很大】。

二、 代碼層次理解

1、關於Number

所有基礎數據類型的包裝類都是繼承了Number父類

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

  • Number類代碼:
public abstract class Number implements java.io.Serializable {
    public abstract int intValue();
    public abstract long longValue();
    public abstract float floatValue();
    public abstract double doubleValue();
    public byte byteValue() {
        return (byte)intValue();
    }
    public short shortValue() {
        return (short)intValue();
    }
    private static final long serialVersionUID = -8742448824652078965L;
}

所有的包裝類型都繼承了該類 ,該類又實現了序列化接口。

2、Integer

Integer 實現的時候 內部有成員變量 value 該 變量使用 private final 進行修飾 ,第一點 使用private 是防止我們主動的去獲取到內部的value 值,並進行修改,但是因爲java 提供了反射能力 ,所以我們還是可以獲取到 Integer 內部的 value 所以我們使用final 修飾 直接阻斷這一可能。

Integer 內部提供了緩存機制 就是如果我們獲取的整數在-128 -127 之間的 直接從緩存中取出。

  • private final int value;
  • 使用valueOf()完成了緩存功能:
public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

如果值得範圍在-128 到 127 之間 就從緩衝中去 ,否則 創建新的Integr對象。
IntegerCache 源代碼:

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() {}
    }

3、其他

首先 原始數據類型不是線程安全的 ,如果使用原始數據我們需要結合其他手段纔可以達到線程安全比如: volitale,此外在我們需要有線程安全的場景時建議使用 : Atomic 原子類。【對於float、long這種比較寬的數據類型甚至會出現不能保證更新操作的原子性,可能出現程序讀取到了更新了一半的數值。】。

還有就是 原始數據類型不支持泛型,而泛型中使用的都是引用類型的數據。

原始類型創建 開銷小,而引用類型創建開銷大【因爲需要維護類型 等其他相關信息】。所以我們發現Integer 的緩存也是爲了提高效率 ,提升性能的因爲我們一般使用的數據都在-128 到127 之間,這樣不至於頻繁創建Integer對象 。

三、 深層次理解

1、談一下 對象的組成

對象由三部分組成:對象頭、對象實例、對其填充。

  • 對象頭:

對象頭一般是16個字節,包括兩部分第一部分是:哈希嗎、鎖狀態標誌、線程持有的鎖、偏向鎖ID、GC 分代年齡等,第二部分是:類型指針【對象指向他屬於的類的信息】。

  • 對象實例:

對象實例就是對象存儲的真正有效信息,也是程序中定義各種類型的字段包括父類繼承和子類定義的,這部分的存儲順序會被虛擬機和代碼中的定義順序影響【虛擬機的volatile 指令重排會影響】,

  • 對其填充:

相當於佔位符的作用,因爲內存的使用需要是8字節的倍數。


知道了上面這些 ,我們就應該知道 創建一個對象需要存儲的數據是很多的,所以這就是爲什麼我們要儘量去避免使用包裝類型來替代基礎類型。
創建一萬個 整數 和創建一萬個對象,所佔用的存儲以及效率是有很大區別的。

2、總的談一下

因爲原始類型和Java泛型不能配合使用,所以Java設計了自動拆裝箱這個性能【其實也就是基礎類型和Object之間的轉換】,這樣避免了我們在使用的時候自己去顯示的進行轉換,

對於原始數據我們內存中存的就是一個數,獲取的時候直接獲取到的就是這個數本身,而對於引用類型 ,我們首先要去獲取這個對象的引用 ,然後通過引用地址去找到堆上具體的數,這樣多了一次IO,而IO 是很影響性能的,在加之對象存儲所佔用的空間也大,所以對象的性能 在計算的時候相較於基礎類型是要差許多的,所以爲了彌補引用類型在計算時性能差的問題 引入了緩存比如Integer 引入了 static valueOf() 的緩存機制,其他的如Boolean 、Char 等也都有相應的實現。

所以我們在進行計算的時候,最好能使用原始數據類型的話 儘量使用 原始數據類型, 而 對於我們需要使用泛型的時候 使用引用類型即可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章