天天看點

Integer類型比較大小【詳解】

面試中經常被問的一個問題就是Integer類型的對象的比較問題,今天我們就來一探究竟

有如下代碼:問輸出是什麼?

public static void main(String[] args) {
        Integer a = 128;
        Integer b = 128;

        Integer c = 1;
        Integer d = 1;

        System.out.println(a==b);
        System.out.println(a.equals(b));
        System.out.println(c==d);
        System.out.println(c.equals(d))
}
           
//輸出:
false
true
true
true
           

1、equals()

首先我們看比較容易了解的

a.equals(b)

c.equals(d)

,我們看下源碼,主要涉及代碼如下:

private final int value;
public boolean equals(Object obj) {
       if (obj instanceof Integer) {
           return value == ((Integer)obj).intValue();
       }
       return false;
   }
   public int intValue() {
       return value;
   }
           

可以看到Integer中重寫了

equals

方法,其中就是對于int類型值的比較,是以自然傳回true

System.out.println(a.equals(b));//等價于128==128
 System.out.println(c.equals(d));//等價于1==1
           

2、==

下面的

==

比較就比較麻煩了,其實也不麻煩,隻是涉及到自動拆箱和Integer對象緩存,

我們先來看一下位元組碼:javap -v 反編譯一下,找到

main

方法對用的位元組碼如下:

public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=5, args_size=1
         0: sipush        128
         3: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
         6: astore_1
         7: sipush        128
        10: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        13: astore_2
        14: iconst_1
        15: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        18: astore_3
        19: iconst_1
        20: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        23: astore        4
        25: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
        28: aload_1
        29: aload_2
        30: if_acmpne     37
        33: iconst_1
        34: goto          38
        37: iconst_0
        38: invokevirtual #4                  // Method java/io/PrintStream.println:(Z)V
        41: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
        44: aload_1
        45: aload_2
        46: invokevirtual #5                  // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z
        49: invokevirtual #4                  // Method java/io/PrintStream.println:(Z)V
        52: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
        55: aload_3
        56: aload         4
        58: if_acmpne     65
        61: iconst_1
        62: goto          66
        65: iconst_0
        66: invokevirtual #4                  // Method java/io/PrintStream.println:(Z)V
        69: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
        72: aload_3
        73: aload         4
        75: invokevirtual #5                  // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z
        78: invokevirtual #4                  // Method java/io/PrintStream.println:(Z)V
        81: return
           

通過位元組碼可以看到,從0-23是在初始化了4個變量,同時存儲之前自動調用了

java/lang/Integer.valueOf:(I)Ljava/lang/Integer;

進行裝箱,25-38對應代碼

System.out.println(a==b);

25: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
 28: aload_1							//加載128
 29: aload_2							//加載128
 30: if_acmpne     37					//比較兩個Integer引用是否是!=,成立執行33行,将1寫入操作數棧,不成立跳轉到37,将0寫入操作數棧
 33: iconst_1
 34: goto          38
 37: iconst_0
 38: invokevirtual #4                  // Method java/io/PrintStream.println:(Z)V//列印30行執行後的結果0或1對應false或true

           

問題就出現這裡,通過輸出的結果我們可以看到a和b引用的是不同的Integer,這個可以了解,因為我們就是定義了兩個Integer;但是c和d引用的卻是同一個Integer,那為什麼會這樣呢?

public static void main(String[] args) {
        Integer a = 128;
        Integer b = 128;
        Integer c = 1;
        Integer d = 1;
        System.out.println(a==b);
        System.out.println(a.equals(b));
        System.out.println(c==d);
        System.out.println(c.equals(d))
}
//輸出:
false
true
true
true
           

玄機應該是在建立對象的時候,編譯器悄悄的幹了點什麼,幹了什麼呢?其實就是自動裝箱,我們找到自動裝箱執行的方法

Integer.valueOf

的源代碼如下:

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

可以看到,當

i

[IntegerCache.low, IntegerCache.high]

中時,沒有new新的Integer對象,是以我們在找到

IntegerCache

一探究竟,源代碼如下:

/**
  * 緩存以支援自動裝箱的對象辨別語義
  * JLS 要求的 -128 和 127(含)。
  *
  * 緩存在第一次使用時初始化。 緩存大小
  * 可以由 {@code -XX:AutoBoxCacheMax=<size>} 選項控制。
  * 在 VM 初始化期間,java.lang.Integer.IntegerCache.high 屬性
  * 可以在私有系統屬性中設定和儲存
  * sun.misc.VM 類。
  */
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) {//如果自己配置了就用自己配置的,同時下面代碼保證了,自己配置的值要大于127才生效
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);//大于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;//沒有自己配置就用預設128

			//建立緩存數組
            cache = new Integer[(high - low) + 1];
            int j = low;
            //緩存數組中全部填充值Integer對象
            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;//最大值必須大于127,否則報錯。防止自己配置錯誤
        }

        private IntegerCache() {}
    }
           

代碼中都加了注解,應該很好了解。

看到這裡應該就很明白了:得出結論

(1)

System.out.println(a==b);

中,因為128>127是以,在自動裝箱執行

valueOf

方法時時并沒有進入if語句,而是建立的新的Integer對象,是以顯示a、b是兩個對象的引用,是以輸出false。

(2)同理

System.out.println(c==d);

中,1<127,是以進入if語句塊,c、d對象都是

IntegerCache.cache[1]

引用的對象,是以自然是相等的!輸出true。

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}
           
public static void main(String[] args) {
        Integer a = 128;
        Integer b = 128;
        Integer c = 1;
        Integer d = 1;
        System.out.println(a==b);
        System.out.println(a.equals(b));
        System.out.println(c==d);
        System.out.println(c.equals(d))
}
//輸出:
false
true
true
true
           

到了這裡其實還沒有完:如下代碼會輸出什麼呢?

public static void main(String[] args) {
        Integer e = new Integer(128);
        Integer f = new Integer(128);
        Integer g = new Integer(1);
        Integer h = new Integer(1);
        
        System.out.println(e==f);
        System.out.println(e.equals(f));
        System.out.println(g==h);
        System.out.println(g.equals(h)); 
    }
           

使用new建立對象是不會觸發自動裝箱操作的,是以預測會列印false、true、false、true,執行結果如下,和預料的一樣。

false
true
false
true
           

到這裡所有關于Integer對象的比較問題就都說清楚了,但是還會有a與e比較、c與g比較的問題,這些衍生問題就很容易了。

最後再總結一下:==比較的位址,equals方法得看代碼怎麼寫(一般是值的比較)

that is all!!!