詳解Java中==和equals()的區別


眾所周知,在 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.lowIntegerCache.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()方法,當調用該方法時執行的是父類Objectequals()方法。而Objectequals()方法通過查看源碼可得知,其實它是比較兩者之間的引用地址。v1 和 v2 盡管對象的內容相同,但是它們分別來自不同的對象,引用地址當然不同,故兩者比較的結果是false

    public boolean equals(Object obj) {
        return (this == obj);
    }

總結:

  • 基本數據類型之間比較,可直接使用==進行比較。
  • 當比較兩個對象引用地址是否為同個地址時,使用==進行比較。
  • 在重寫了equals()的前提下,比較對象內容或者其他自定義的比較方法時,使用equals()比較。

本文參考文章:https://www.cnblogs.com/wang-yaz/p/8516151.html

關於自動裝箱與拆箱的細節可以看看這篇文章,感謝作者提供好的思路和講解。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM