眾所周知,在 Java 編程中,程序員通常會使用==
或equals()
來簡單的比較地址,內容是否相等。而這兩者之間的使用區別,對於初學 Java 的同學來說可能會比較迷糊。我將根據下面的幾段示例程序,來對這兩種比較方法進行分析,供大家參考:
private static void method1() {
Integer n1 = new Integer(5);
Integer n2 = new Integer(5);
System.out.println(n1.equals(n2));// true
System.out.println(n1 == n2);// false
}
在method1()
中,第一個的輸出打印結果如我所料,equals()
比較兩者的內容,輸出結果會是true
,但是第二個打印輸出結果,如果是在我剛接觸 Java 時,肯定會毅然決然的認為這條輸出的結果應該是true
。但事實是,盡管n1 和 n2 看起來內容相同,但是程序執行出來的結果卻是false
。
翻閱《 Java 編程思想(第四版)》,在書中有這么一段話:
== 和 != 比較的就是對象的引用。
如果想比較兩個對象的實際內容是否相同,必須使用所有對象都使用的特殊方法 equals() 方法。但這個方法不適用於“基本類型”,基本類型直接使用 == 或 != 即可。
通過書里的這段話我們進行分析,在method1()
方法里,盡管 n1,n2 兩個對象的內容是相等的,但是它們本質上是屬於兩個創建出來的不同的對象。因此,在method1()
中,兩個不同的對象它們所引用的地址當然是不相同的,故method1()
的第二個輸出結果為false
。
private static void method2() {
Integer n3 = 127;
Integer n4 = 127;
Integer n5 = 128;
Integer n6 = 128;
System.out.println(n3 == n4);// true
System.out.println(n5 == n6);// false
}
在method2()
中可以看到,這里的寫法不是通過 new 形式來創建 Integer 對象了 ,而是通過自動裝箱方式創建一個 Integer 對象。何謂自動裝箱,通俗的解釋就是自動將基本數據類型轉換為包裝器類型。記得我在method1()
中寫道:== 比較的就是對象的引用。在這里 n3 ,n4 屬於兩個不同的對象,通過 ==
比較的是兩個對象的引用,理應輸出的結果是false。那么結果為什么是true
呢?
為了便於分析,我們可以僅在代碼里保留一行代碼。
public class Equivalence {
public static void main(String[] args) {
Integer n3 = 127;
}
}
通過編譯該 java 文件后,在控制台窗口中切換到其編譯后輸出的 *.class字節碼
文件的位置,輸入javap -c "文件名.class"
對其進行反編譯。我們來看看保留的這段代碼執行編譯中干了什么事情:
從打印輸出的內容截圖里,我們可以知道系統當使用 Integer 自動裝箱時,系統會幫我們會調用Integer.valueOf()
方法,我們可以查看下該方法的源碼。
public final class Integer extends Number implements Comparable<Integer> {
// ...
public static Integer valueOf(int i) {
// 對傳進來的數值進行比較
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
/**
* Integer的內部類,用於緩存 -128 ~ 127的數值
*/
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() {}
}
}
通過查閱源碼注釋(本文未展示),這個方法是在JDK 1.5
時加入的。當執行Integer.valueOf()
方法時,會將傳進來的數值與IntegerCache.low
和IntegerCache.high
進行比較。為了避免多次創建浪費資源,通過IntegerCache
這個 Integer 的緩存內部類,將-128 ~ 127
的數值緩存起來。
也就是說,當我們使用 Integer 進行自動裝箱操作時,會將裝箱的數值與緩存的數值進行比較,如果在該緩存范圍內,則會返回一個已經創建好的 Integer 對象,反之將會重新創建一個 Integer 對象。這么一來,也就可以解釋的通:為什么 n3 、n4數值相等,n4、n5數值也同樣相等,輸出的結果卻是不同的原因了。
上述內容,我們僅是通過 Integer 包裝類的源碼對==
和equals()
兩種比較方式進行分析。實際上,不同的包裝類,所執行出的效果也不盡相同。若想深入了解,大家可以查看我文末附上的參考文章鏈接。
接下來我們再通過代碼示例,對equals()
進行分析。
public class Equivalence {
public static void main(String[] args) {
method3();
}
private static void method3() {
Value v1 = new Value(100);
Value v2 = new Value(100);
String s1 = new String("smallmin");
String s2 = new String("smallmin");
System.out.println(v1.equals(v2));// false
System.out.println(s1.equals(s2));// true
}
}
class Value {
int i;
public Value(int i) {
super();
this.i = i;
}
}
記得我們在method1()
中講過,比較兩個對象的實際內容是否相同,必須使用所有對象都使用的特殊方法 equals() 方法。那么上面示例代碼中,為什么v1 與 v2 比較輸出結果會是false
,而 s1 與 s2 比較出的結果卻是true
呢?
在method1()
中我們 Integer 對象之間通過equals()
比較以及本例中,String
對象間通過equals()
輸出的比較結果都是true
(對象內容相等的前提下)的原因是,Integer 和 String 等類中,都對equals()
進行了重寫,因文章篇幅原因故不再逐一附上源碼。
而v1 與 v2 是我們自己創建的對象,沒有重寫equals()
方法,當調用該方法時執行的是父類Object
的equals()
方法。而Object
的equals()
方法通過查看源碼可得知,其實它是比較兩者之間的引用地址。v1 和 v2 盡管對象的內容相同,但是它們分別來自不同的對象,引用地址當然不同,故兩者比較的結果是false
。
public boolean equals(Object obj) {
return (this == obj);
}
總結:
- 基本數據類型之間比較,可直接使用
==
進行比較。 - 當比較兩個對象引用地址是否為同個地址時,使用
==
進行比較。 - 在重寫了
equals()
的前提下,比較對象內容或者其他自定義的比較方法時,使用equals()
比較。
本文參考文章:https://www.cnblogs.com/wang-yaz/p/8516151.html
關於自動裝箱與拆箱的細節可以看看這篇文章,感謝作者提供好的思路和講解。