面試中經常被問的一個問題就是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!!!