Java equals 和 == 徹底解析

圖片描述

今天朋友忽然問到一個問題:html

兩個對象使用 x.equals(y)判斷結果爲 true時,兩個對象的 hashCode能夠不一樣嗎?

在Java編程中,判斷兩個對象是否相等經常使用equals()或是==,可是其中的區別和原理可能不少人並不徹底清楚。今天就藉着上面這個問題來看看equals()==的區別和原理。java

1. 數據類型與==的含義

Java中的數據類型分爲基本數據類型和引用數據類型:編程

  1. 基本類型:編程語言中內置的最小粒度的數據類型。它包括四大類八種類型數組

    • 4種整數類型:byte、short、int、long
    • 2種浮點數類型:float、double
    • 1種字符類型:char
    • 1種布爾類型:boolean
  2. 引用類型:引用也叫句柄,引用類型,是編程語言中定義的在句柄中存放着實際內容所在地址的地址值的一種數據形式編程語言

    • 接口
    • 數組
  • 對於基本類型來講,== 比較的是它們的值
  • 對於引用類型來講,== 比較的是它們在內存中存放的地址(堆內存地址)

例:ide

public void test(){
    int num1 = 100;
    int num2 = 100;

    String str1 = "James";
    String str2 = "James";

    String str3 = new String("James");
    String str4 = new String("James");

    System.out.println("num1 == num2 : " + (num1 == num2));
    System.out.println("str1 address : " + System.identityHashCode(str1) + ";\nstr2 address : " + System.identityHashCode(str1) + ";\nstr1 == str2 : " + (str1 == str2));
    System.out.println("str3 address : " + System.identityHashCode(str3) + ";\nstr4 address : " + System.identityHashCode(str4) + ";\nstr3 == str4 : " + (str3 == str4));
}

運行上面的代碼,能夠獲得如下結果:測試

num1 == num2 : true

str1 address : 1174290147;
str2 address : 1174290147;
str1 == str2 : true

str3 address : 1289696681;
str4 address : 1285044316;
str3 == str4 : false

能夠看到str1和str2的內存地址都是1174290147,因此使用==判斷爲true,可是str3和str4的地址是不一樣的,因此判斷爲falsethis

2. equals() 方法解析

在Java語言中,全部類都是繼承於Object這個超類的,在這個類中也有一個equals()方法,那麼咱們先來看一下這個方法。spa

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

能夠看得出,這個方法很簡單,就是比較對象的內存地址的。因此在對象沒有重寫這個方法時,默認使用此方法,即比較對象的內存地址值。可是相似於String、Integer等類均已重寫了equals()。下面以String爲例。code

public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = length();
            if (n == anotherString.length()) {
                int i = 0;
                while (n-- != 0) {
                    if (charAt(i) != anotherString.charAt(i))
                            return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

很明顯,String的equals()方法僅僅是對比它的 數據值,而不是對象的 內存地址

String 爲例測試一下。

public void test() {

    String str1 = "James";
    String str2 = "James";

    String str3 = new String("James");
    String str4 = new String("James");

    System.out.println("str1 address : " + System.identityHashCode(str1) + ";\nstr2 address : " + System.identityHashCode(str1) + ";\nstr1.equals(str2) : " + str1.equals(str2));
    System.out.println("str3 address : " + System.identityHashCode(str3) + ";\nstr4 address : " + System.identityHashCode(str4) + ";\nstr3.equals(str4) : " + str3.equals(str4));
}

結果爲:

str1 address : 1174290147;
str2 address : 1174290147;
str1.equals(str2) : true

str3 address : 1289696681;
str4 address : 1285044316;
str3.equals(str4) : true

能夠發現無論對象的內存地址是否相同並不影響其結果,因此String類型比較的是 數據值, 而不是 內存地址值

因此總結一下equals()== 的區別:

  1. ==

    • 基本類型:對比它們的值是否相等
    • 引用類型:對比它們的內存地址是否相等
  2. equals()

    • 基本類型:使用==進行比較
    • 引用類型:默認狀況下,對比它們的地址是否相等;若是equals()方法被重寫,則根據重寫的要求來比較。

3. equals() 與 hashCode()

在詳細的瞭解了==equals()的做用和區別後,如今來研究一下以前的那個問題:

兩個對象使用 x.equals(y)判斷結果爲 true時,兩個對象的 hashCode能夠不一樣嗎?

首先咱們須要知道hashCode究竟是什麼?仍是從Object這個超類來看一下。

public int hashCode() {
    return identityHashCode(this); // 此處返回對象的內存地址值
}

代碼也很簡單,看來默認狀況下,hashCode就等於對象的 內存地址值(注:System.identityHashCode(Object obj)方法用於獲取對象的內存地址,以前的樣例代碼中有使用)。和equals()方法同樣重寫以後,hashCode()方法方法也是能夠被重寫的,並且二者通常狀況下都是成對出現。

簡單測試一下String類型重寫hashCode()方法以後有什麼變化。

public void test() {
    String str1 = "James";
    System.out.println("str1 address : " + System.identityHashCode(str1) + "\nstr1 hashCode : " + str1.hashCode());
}

結果爲:

str1 address : 1174290147
str1 hashCode : 71338276

很明顯,hashCode 已經不是內存地址了。

那麼總結一下:

  • equals():默認狀況下比較的是對象的 內存地址值,被重寫後按照重寫要求進行比較,通常是比較對象的 數據值
  • hashCode(): 默認狀況下爲對象的 內存地址值,被重寫後按照重寫要求生成新的值。

到此對於剛開始提出的問題應該很好解決了。對於這兩個對象,只要咱們重寫equals()方法,就能夠比較對象的 數據值,而不重寫hashCode()方法,此時兩個對象的 hashCode 就默認爲內存地址值了,只要將兩個對象指向不一樣的地址便可。

驗證環節,先建立一個類:

public class CustomBean {
    private String name;
    private int age;

    public CustomBean(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        CustomBean that = (CustomBean) o;
        return age == that.age &&
                Objects.equals(name, that.name);
    }

    // @Override
    // public int hashCode() {
    //     return Objects.hash(name, age);
    // }
}

建立測試方法:

@Test
public void test() {
    CustomBean x = new CustomBean("James", 18);
    CustomBean y = new CustomBean("James", 18);

    System.out.println("x.hashCode: " + x.hashCode());
    System.out.println("x address : " + System.identityHashCode(x));
    System.out.println("y.hashCode: " + y.hashCode());
    System.out.println("x address : " + System.identityHashCode(y));

    System.out.println("x and y is equals : " + x.equals(y));
}

運行結果爲:

x.hashCode: 1174290147
x address : 1174290147

y.hashCode: 1289696681
x address : 1289696681

x and y is equals : true

很明顯,這就是問題中所描述的那種狀況:兩個對象使用x.equals(y)判斷結果爲true時,兩個對象的hashCode不相同。

4. 總結

至此,==equals()的區別及做用,equals()hashCode的關係及使用已經瞭解清楚了。下面再總結一下:

對於equals()== 的區別:

  1. ==

    • 基本類型:對比它們的值是否相等
    • 引用類型:對比它們的內存地址是否相等
  2. equals()

    • 基本類型:使用==進行比較
    • 引用類型:默認狀況下,對比它們的地址是否相等;若是equals()方法被重寫,則根據重寫的要求來比較

對於equals()hashCode()的關係:

根據Object超類中的文檔說明,equals()hashCode()兩個方法應該 同進同退。上面的例子只是舉例說明存在那種狀況,但那並非一個很好的應用。

  • 因此必定要記住equals()hashCode()兩個方法應該 同進同退
  • 因此必定要記住equals()hashCode()兩個方法應該 同進同退
  • 因此必定要記住equals()hashCode()兩個方法應該 同進同退

重要的事情說三遍。

歡迎您關注個人博客主頁: James Blog

閱讀原文

相關文章
相關標籤/搜索