1. 程式人生 > >自動裝箱和自動拆箱(慎用“==”)

自動裝箱和自動拆箱(慎用“==”)

先來看一段非常“簡單”的程式碼

public static void main(String[] args) {
		Integer i1 = 100, i2 = 100, i3 = 130, i4 = 130;
		System.out.println(i1 == i2);
		System.out.println(i3 == i4);
	}

如果你覺得輸出的是兩個ture,哪我覺得你還需要再學習一下Inreger的原始碼(雖然一開始是我也是這樣的)。

實際 輸出結果是這樣的

true
false

為什麼會出現這樣的結果呢?我們可以看到上面的程式碼4個變數都是Integer的引用,所以輸出的==運算比較的不是Integer值而是Integer引用。裝箱的本質是什麼呢?當我們給一個Integer物件賦一個int值的時候,會呼叫Integer類的靜態方法valueOf,我們看一看valueOf方法就知道為什麼會有這樣的結果了。

 /**
     * Returns an {@code Integer} instance representing the specified
     * {@code int} value.  If a new {@code Integer} instance is not
     * required, this method should generally be used in preference to
     * the constructor {@link #Integer(int)}, as this method is likely
     * to yield significantly better space and time performance by
     * caching frequently requested values.
     *
     * This method will always cache values in the range -128 to 127,
     * inclusive, and may cache other values outside of this range.
     *
     * @param  i an {@code int} value.
     * @return an {@code Integer} instance representing {@code i}.
     * @since  1.5
     */
    public static Integer valueOf(int i) {//是一個靜態方法 IntegerCache是一個內部類
        assert IntegerCache.high >= 127;//斷言  參考http://lavasoft.blog.51cto.com/62575/43735/
        if (i >= IntegerCache.low && i <= IntegerCache.high)//如果i大於對於IntegerCache.low()且i小於等於IntegerCache.high
            return IntegerCache.cache[i + (-IntegerCache.low)];//直接從快取取出來
        return new Integer(i);//新建立一個Integer物件
    }

從上面的程式碼中我們可以看出Integer維持了一個快取系統,如果在快取的範圍內直接取出來就好了,如果不在的就要建立新的Integer物件。但是具體快取範圍是什麼的,我們在深入進去看看:

 /**
     * Cache to support the object identity semantics of autoboxing for values between
     * -128 and 127 (inclusive) as required by JLS.
     *
     * The cache is initialized on first usage.  The size of the cache
     * may be controlled by the -XX:AutoBoxCacheMax=<size> option.
     * During VM initialization, java.lang.Integer.IntegerCache.high property
     * may be set and saved in the private system properties in the
     * sun.misc.VM class.
     */

    private static class IntegerCache {//靜態類哦
        static final int low = -128;//最小值是-128
        static final int high;//最高
        static final Integer cache[];//快取陣列  這三個都final,不可修改的

        static {//靜態程式碼塊   靜態程式碼塊會比改造方法先執行
            // high value may be configured by property
            int h = 127;//預設的
            String integerCacheHighPropValue =//定義一個String 
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");//取得設定的值
            if (integerCacheHighPropValue != null) {//如果設定了就用設定的值
                int i = parseInt(integerCacheHighPropValue);//把String轉換為int
                i = Math.max(i, 127);//獲得i和127的更大的一個,其實是不能小與預設的
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low));//如果取的小的那個,不能超過Integer的最大值+low
            }
            high = h;//最大值為127

            cache = new Integer[(high - low) + 1];//建立快取陣列大小
            int j = low;//最小值
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);//快取初始化
        }

        private IntegerCache() {}//私有構造
    }

這樣問題就一目瞭然;因為i3、i4的值是130已經 超過了127所以一進行自動裝箱結果自然是false。

總結:

簡單一點說,裝箱就是 自動將基本資料型別轉換為包裝器型別;拆箱就是 自動將包裝器型別轉換為基本資料型別。
下表是基本資料型別對應的包裝器型別:

int(4位元組)	Integer
byte(1位元組)	Byte
short(2位元組)	Short
long(8位元組)	Long
float(4位元組)	Float
double(8位元組)	Double
char(2位元組)	Character
boolean(未定)	Boolean

他們都會都會存在類似的問題,具體原因自己去看原始碼。

擴充套件一個非常基礎的問題(java中==和equals的區別)

java中的資料型別,可分為兩類:

1.基本資料型別,也稱原始資料型別。byte,short,char,int,long,float,double,boolean
他們之間的比較,應用雙等號(),比較的是他們的值。
2.複合資料型別(類)
當他們用(
)進行比較的時候,比較的是他們在記憶體中的存放地址,所以,除非是同一個new出來的物件,他們的比較後的結果為true,否則比較後結果為false。 JAVA當中所有的類都是繼承於Object這個基類的,在Object中的基類中定義了一個equals的方法,這個方法的初始行為是比較物件的記憶體地 址,但在一些類庫當中這個方法被覆蓋掉了,如String,Integer,Date在這些類當中equals有其自身的實現,而不再是比較類在堆記憶體中的存放地址了。
對於複合資料型別之間進行equals比較,在沒有覆寫equals方法的情況下,他們之間的比較還是基於他們在記憶體中的存放位置的地址值的,因為Object的equals方法也是用雙等號()進行比較的,所以比較後的結果跟雙等號()的結果相同。

小問題大bug

public static void main(String[] args) {
		int i1=100;
		int i2=200;
		Integer a=i1;
		Integer b=i1;
		Integer c=i2;
		Integer d=i2;
		if(a==b) {
			System.out.println("成功執行");
		}
		
		if(c==d) {
			System.out.println("能成功執行嗎");
		}
	}

如果在平常的專案中出現像上面一樣的邏輯判斷最後出現的bug我覺得是非常難找出來的。。。。。。