最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中的==、equals與hashCode區(qū)別與聯(lián)系最佳實踐

 更新時間:2025年09月23日 09:37:32   作者:猿究院-陸昱澤  
在Java開發(fā)中,==、equals和hashCode是三個高頻出現(xiàn)的概念,也是初學(xué)者最容易混淆的知識點(diǎn),本文將從底層原理出發(fā),全面解析三者的區(qū)別、聯(lián)系及最佳實踐,幫你徹底理清它們的使用場景,感興趣的朋友一起看看吧

在Java開發(fā)中,==、equalshashCode是三個高頻出現(xiàn)的概念,也是初學(xué)者最容易混淆的知識點(diǎn)。它們看似簡單,卻蘊(yùn)含著Java對象模型和哈希表設(shè)計的深層邏輯。本文將從底層原理出發(fā),全面解析三者的區(qū)別、聯(lián)系及最佳實踐,幫你徹底理清它們的使用場景。

一、==:比較的是"身份"還是"內(nèi)容"?

==是Java中的運(yùn)算符,用于比較兩個變量的值。但它的行為會因比較的類型不同而產(chǎn)生差異,核心區(qū)別在于基本數(shù)據(jù)類型引用數(shù)據(jù)類型的比較邏輯。

1. 基本數(shù)據(jù)類型:比較"值"本身

Java中的基本數(shù)據(jù)類型(byteshort、int、longfloat、double、char、boolean)直接存儲值,不存在"引用"的概念。因此,==比較的是兩個變量存儲的實際值是否相等。

int a = 10;
int b = 10;
System.out.println(a == b); // true(值相同)
char c1 = 'A';
char c2 = 65; // 'A'的ASCII碼是65
System.out.println(c1 == c2); // true(值相同)

2. 引用數(shù)據(jù)類型:比較"內(nèi)存地址"

引用數(shù)據(jù)類型(如String、Object、自定義類等)的變量存儲的是對象在堆內(nèi)存中的地址(引用)。因此,==比較的是兩個變量是否指向同一個對象(即內(nèi)存地址是否相同)。

String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2); // false(兩個不同的對象,地址不同)
String s3 = s1;
System.out.println(s1 == s3); // true(s3和s1指向同一個對象)

關(guān)鍵結(jié)論
==對于基本類型是"值比較",對于引用類型是"地址比較"(判斷是否為同一對象)。

二、equals:對象內(nèi)容的比較器

equalsObject類定義的方法,用于判斷兩個對象是否"相等"。與==不同,equals的邏輯可以由開發(fā)者自定義,默認(rèn)行為與==一致,但多數(shù)類會重寫它以實現(xiàn)"內(nèi)容比較"。

1. Object類的默認(rèn)equals實現(xiàn)

Object類中equals的源碼如下:

public boolean equals(Object obj) {
    return (this == obj); // 本質(zhì)是用==比較,即比較內(nèi)存地址
}

這意味著:如果一個類沒有重寫equals,那么它的equals方法與==完全等價,比較的是對象的內(nèi)存地址。

class Person {
    private String name;
    // 省略構(gòu)造方法和getter
}
Person p1 = new Person("張三");
Person p2 = new Person("張三");
System.out.println(p1.equals(p2)); // false(未重寫equals,等價于==)

2. 重寫equals:實現(xiàn)"內(nèi)容相等"

實際開發(fā)中,我們通常認(rèn)為"內(nèi)容相同的對象應(yīng)該相等"(如兩個String的字符序列相同即為相等)。因此,許多Java內(nèi)置類(如StringInteger、List)都重寫了equals方法。

例1:String類的equals實現(xiàn)

String重寫的equals用于比較字符序列是否相同:

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true; // 同一對象,直接返回true
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) { // 逐個比較字符
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

使用示例:

String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1.equals(s2)); // true(內(nèi)容相同)
System.out.println(s1 == s2);      // false(地址不同)

例2:自定義類重寫equals

重寫equals需遵循等價關(guān)系的規(guī)則(否則會導(dǎo)致邏輯混亂):

  • 自反性x.equals(x)必須返回true
  • 對稱性:若x.equals(y)true,則y.equals(x)也必須為true
  • 傳遞性:若x.equals(y)y.equals(z)true,則x.equals(z)必須為true;
  • 一致性:多次調(diào)用x.equals(y),結(jié)果應(yīng)保持一致;
  • 非空性x.equals(null)必須返回false

正確重寫Person類的equals示例:

class Person {
    private String name;
    private int age;
    // 構(gòu)造方法、getter省略
    @Override
    public boolean equals(Object o) {
        if (this == o) return true; // 同一對象,直接返回true
        if (o == null || getClass() != o.getClass()) return false; // 類型不同或null,返回false
        Person person = (Person) o;
        // 比較關(guān)鍵屬性(name和age都相同才認(rèn)為相等)
        return age == person.age && Objects.equals(name, person.name);
    }
}

關(guān)鍵結(jié)論
equals的默認(rèn)行為是比較對象地址(與==一致),但可通過重寫實現(xiàn)"內(nèi)容比較",其邏輯由開發(fā)者定義(通?;趯ο蟮年P(guān)鍵屬性)。

三、hashCode:哈希表的"定位器"

hashCodeObject類的另一個方法,返回一個int類型的哈希碼(散列值)。它的核心作用是輔助哈希表(如HashMap、HashSet)快速定位對象,是哈希表高效運(yùn)作的基礎(chǔ)。

1. hashCode的本質(zhì)與作用

哈希碼是對象的"數(shù)字指紋",由對象的內(nèi)部狀態(tài)計算得出。在哈希表中,它的作用是:

  • 快速確定對象在哈希表中的存儲位置(通過哈希碼計算"桶位");
  • 減少equals的調(diào)用次數(shù)(先通過哈希碼篩選,再用equals精確比較)。

Object類中hashCode的默認(rèn)實現(xiàn)是根據(jù)對象的內(nèi)存地址計算哈希碼(不同對象的哈希碼通常不同),但子類可以重寫它。

2. 重寫hashCode的規(guī)則

equals類似,hashCode也需要遵循一定的規(guī)則,尤其是當(dāng)類重寫了equals時:

  1. 一致性:同一對象多次調(diào)用hashCode(),必須返回相同的整數(shù)(對象狀態(tài)未修改時);
  2. 等價性:若a.equals(b) == true,則a.hashCode()必須等于b.hashCode();
  3. 非必須等價:若a.equals(b) == false,a.hashCode()b.hashCode()可以相等(即允許哈希沖突)。

為什么規(guī)則2如此重要?
如果兩個對象equals返回truehashCode不同,在哈希表中會被分配到不同的桶位,導(dǎo)致哈希表認(rèn)為它們是不同的對象,從而破壞哈希表的邏輯(如HashSet中出現(xiàn)重復(fù)元素)。

3. 如何正確重寫hashCode?

重寫hashCode的核心原則是:根據(jù)equals中用于比較的所有屬性計算哈希碼,確保"相等的對象有相同的哈希碼"。

Java提供了Objects.hash()工具方法,可便捷地生成哈希碼(內(nèi)部通過組合各屬性的哈希值實現(xiàn))。

為前面的Person類重寫hashCode

@Override
public int hashCode() {
    // 基于name和age計算哈希碼(與equals中比較的屬性一致)
    return Objects.hash(name, age);
}

Objects.hash()的簡化原理:

public static int hash(Object... values) {
    int result = 1;
    for (Object element : values) {
        result = 31 * result + (element == null ? 0 : element.hashCode());
    }
    return result;
}

選擇31作為乘數(shù)的原因:31是質(zhì)數(shù),且31 * i = (i << 5) - i,可通過移位運(yùn)算高效計算。

四、==、equals與hashCode的關(guān)聯(lián)關(guān)系

三者并非孤立存在,尤其是equalshashCode,在哈希表場景中存在強(qiáng)關(guān)聯(lián),我們可以用一句話總結(jié):

==判斷是否為同一對象;equals判斷內(nèi)容是否相等;hashCode輔助哈希表快速查找,且必須與equals保持邏輯一致。

具體關(guān)聯(lián)如下:

1. ==與equals的關(guān)系

  • a == btrue,則a.equals(b)一定為true(同一對象,內(nèi)容必然相同);
  • a.equals(b)true,a == b不一定為true(內(nèi)容相同的不同對象)。

例如:

String s1 = "hello";
String s2 = "hello"; // 常量池復(fù)用,s1和s2指向同一對象
System.out.println(s1 == s2);      // true
System.out.println(s1.equals(s2)); // true
String s3 = new String("hello");
System.out.println(s1 == s3);      // false(不同對象)
System.out.println(s1.equals(s3)); // true(內(nèi)容相同)

2. equals與hashCode的強(qiáng)制約束

這是開發(fā)中最容易出錯的點(diǎn),必須牢記:

  • a.equals(b) = true,則a.hashCode()必須等于b.hashCode()(否則哈希表會出錯);
  • a.hashCode() = b.hashCode(),a.equals(b)可能為false(哈希沖突是允許的)。

反例(違反約束會導(dǎo)致的問題):

class BadPerson {
    private String name;
    public BadPerson(String name) { this.name = name; }
    // 只重寫equals,未重寫hashCode
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        BadPerson badPerson = (BadPerson) o;
        return Objects.equals(name, badPerson.name);
    }
}
// 測試代碼
public class Test {
    public static void main(String[] args) {
        BadPerson p1 = new BadPerson("張三");
        BadPerson p2 = new BadPerson("張三");
        System.out.println(p1.equals(p2)); // true(內(nèi)容相同)
        System.out.println(p1.hashCode() == p2.hashCode()); // false(哈希碼不同,違反約束)
        // 放入HashSet
        Set<BadPerson> set = new HashSet<>();
        set.add(p1);
        set.add(p2);
        System.out.println(set.size()); // 2(錯誤!因為p1和p2應(yīng)該被視為相同元素)
    }
}

原因:HashSet判斷元素是否重復(fù)時,先通過hashCode定位桶位,再用equals比較。由于p1p2哈希碼不同,會被放入不同桶位,equals即使返回true也不會被視為重復(fù)元素。

五、常見誤區(qū)與最佳實踐

了解了三者的原理后,我們需要規(guī)避一些常見錯誤,掌握實際開發(fā)中的最佳實踐。

1. 誤區(qū)1:用==比較字符串內(nèi)容

很多初學(xué)者會犯這樣的錯誤:

String s1 = "hello";
String s2 = new String("hello");
if (s1 == s2) { ... } // 錯誤!此處比較的是地址,而非內(nèi)容

正確做法:字符串內(nèi)容比較必須用equals

if (s1.equals(s2)) { ... } // 正確,比較內(nèi)容
// 避免空指針異常的寫法(當(dāng)s1可能為null時)
if (Objects.equals(s1, s2)) { ... }

2. 誤區(qū)2:重寫equals時不重寫hashCode

如前文反例所示,這會導(dǎo)致哈希表(HashMap、HashSet等)工作異常。牢記:重寫equals必須同時重寫hashCode,且兩者基于相同的屬性計算。

3. 誤區(qū)3:認(rèn)為hashCode相等的對象一定相等

哈希碼相等只是"可能相等",而非"一定相等"。例如:

// 兩個不同的字符串,可能有相同的哈希碼(哈希沖突)
String str1 = "Aa";
String str2 = "BB";
System.out.println(str1.hashCode()); // 2112
System.out.println(str2.hashCode()); // 2112
System.out.println(str1.equals(str2)); // false

因此,在哈希表中,hashCode僅用于初步篩選,最終必須通過equals確認(rèn)是否相等。

4. 最佳實踐總結(jié)

場景

正確做法

錯誤做法

比較基本類型值

使用==

試圖用equals(基本類型沒有該方法)

比較引用類型內(nèi)容

使用equals(需確保已重寫)

使用==(比較地址而非內(nèi)容)

重寫equals

同時重寫hashCode,基于相同屬性計算

只重寫equals,忽略hashCode

避免equals空指針異常

使用Objects.equals(a, b)

直接調(diào)用a.equals(b)(當(dāng)a可能為null時)

判斷對象是否為同一實例

使用==

使用equals(可能被重寫,結(jié)果不可靠)

六、總結(jié)

==、equalshashCode是Java對象比較的三大核心工具,它們的設(shè)計體現(xiàn)了Java對"身份"與"內(nèi)容"的嚴(yán)格區(qū)分,以及哈希表高效運(yùn)作的底層邏輯:

  • ==:基本類型比"值",引用類型比"地址";
  • equals:默認(rèn)比"地址",重寫后可比"內(nèi)容",需遵循等價關(guān)系;
  • hashCode:對象的"數(shù)字指紋",輔助哈希表定位,必須與equals保持一致。

到此這篇關(guān)于Java中的==、equals與hashCode區(qū)別與聯(lián)系最佳實踐的文章就介紹到這了,更多相關(guān)java == equals hashCode內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

  • 詳解Java 虛擬機(jī)垃圾收集機(jī)制

    詳解Java 虛擬機(jī)垃圾收集機(jī)制

    這篇文章主要介紹了Java 虛擬機(jī)垃圾收集機(jī)制的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)Java虛擬機(jī)的相關(guān)知識,感興趣的朋友可以了解下
    2020-12-12
  • Java數(shù)據(jù)結(jié)構(gòu)與算法之循環(huán)隊列的實現(xiàn)

    Java數(shù)據(jù)結(jié)構(gòu)與算法之循環(huán)隊列的實現(xiàn)

    循環(huán)隊列 (Circular Queue) 是一種特殊的隊列。循環(huán)隊列解決了隊列出隊時需要將所有數(shù)據(jù)前移一位的問題。本文將帶大家詳細(xì)了解循環(huán)隊列如何實現(xiàn),需要的朋友可以參考一下
    2021-12-12
  • SpringBoot整合logback的示例代碼

    SpringBoot整合logback的示例代碼

    Logback是由log4j創(chuàng)始人設(shè)計的又一個開源日志組件,logback分為三個模塊,在文章開頭給大家介紹的很明確,接下來通過本文重點(diǎn)介紹下SpringBoot整合logback的方法,需要的朋友可以參考下
    2022-04-04
  • Spring?Security安全框架之記住我功能

    Spring?Security安全框架之記住我功能

    這篇文章主要介紹了Spring?Security安全框架之記住我,本次就來探究如何實現(xiàn)這種自動登錄、記住我的功能,通過實例代碼圖文相結(jié)合給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-02-02
  • SpringMVC多個文件上傳及上傳后立即顯示圖片功能

    SpringMVC多個文件上傳及上傳后立即顯示圖片功能

    這篇文章主要介紹了SpringMVC多個文件上傳及上傳后立即顯示圖片功能,非常不錯,具有參考借鑒價值功能,需要的朋友可以參考下
    2017-10-10
  • Spring Security UserDetails實現(xiàn)原理詳解

    Spring Security UserDetails實現(xiàn)原理詳解

    這篇文章主要介紹了Spring Security UserDetails實現(xiàn)原理詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-09-09
  • Spring Boot 連接LDAP的方法

    Spring Boot 連接LDAP的方法

    這篇文章主要介紹了Spring Boot 連接LDAP的方法,僅僅涉及基本的使用ODM來快速實現(xiàn)LDAP增刪改查操作。具有一定的參考價值,有興趣的可以了解一下
    2017-12-12
  • Java實現(xiàn)簡單的RPC框架的示例代碼

    Java實現(xiàn)簡單的RPC框架的示例代碼

    本篇文章主要介紹了Java實現(xiàn)簡單的RPC框架的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-11-11
  • IDEA檢查項目的jdk版本需要看的地方

    IDEA檢查項目的jdk版本需要看的地方

    這篇文章主要介紹了IDEA檢查項目的jdk版本需要看的地方,文中通過圖文介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-06-06
  • 最新評論

    林口县| 大足县| 合肥市| 大英县| 二连浩特市| 华蓥市| 沙河市| 德保县| 平阳县| 盖州市| 钟山县| 如东县| 常山县| 峨眉山市| 潼南县| 小金县| 高尔夫| 长春市| 宣化县| 景泰县| 八宿县| 兴宁市| 宝兴县| 都昌县| 广河县| 保德县| 基隆市| 得荣县| 乐至县| 吉林省| 石河子市| 莱州市| 蓬安县| 彭泽县| 安化县| 岐山县| 博湖县| 通道| 承德县| 乌鲁木齐市| 五峰|