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

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

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

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

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

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

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

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

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、自定義類等)的變量存儲(chǔ)的是對(duì)象在堆內(nèi)存中的地址(引用)。因此,==比較的是兩個(gè)變量是否指向同一個(gè)對(duì)象(即內(nèi)存地址是否相同)。

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

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

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

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

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

Object類中equals的源碼如下:

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

這意味著:如果一個(gè)類沒有重寫equals,那么它的equals方法與==完全等價(jià),比較的是對(duì)象的內(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,等價(jià)于==)

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

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

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

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

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true; // 同一對(duì)象,直接返回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) { // 逐個(gè)比較字符
                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需遵循等價(jià)關(guān)系的規(guī)則(否則會(huì)導(dǎo)致邏輯混亂):

  • 自反性x.equals(x)必須返回true
  • 對(duì)稱性:若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; // 同一對(duì)象,直接返回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)行為是比較對(duì)象地址(與==一致),但可通過重寫實(shí)現(xiàn)"內(nèi)容比較",其邏輯由開發(fā)者定義(通?;趯?duì)象的關(guān)鍵屬性)。

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

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

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

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

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

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

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

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

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

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

3. 如何正確重寫hashCode?

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

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

為前面的Person類重寫hashCode

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

Objects.hash()的簡(jiǎn)化原理:

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)算高效計(jì)算。

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

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

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

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

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

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

例如:

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

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

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

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

反例(違反約束會(huì)導(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);
    }
}
// 測(cè)試代碼
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(錯(cuò)誤!因?yàn)閜1和p2應(yīng)該被視為相同元素)
    }
}

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

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

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

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

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

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

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

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

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

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

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

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

// 兩個(gè)不同的字符串,可能有相同的哈希碼(哈希沖突)
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. 最佳實(shí)踐總結(jié)

場(chǎng)景

正確做法

錯(cuò)誤做法

比較基本類型值

使用==

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

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

使用equals(需確保已重寫)

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

重寫equals

同時(shí)重寫hashCode,基于相同屬性計(jì)算

只重寫equals,忽略hashCode

避免equals空指針異常

使用Objects.equals(a, b)

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

判斷對(duì)象是否為同一實(shí)例

使用==

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

六、總結(jié)

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

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

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

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

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

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

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

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

    SpringBoot整合logback的示例代碼

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

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

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

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

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

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

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

    Spring Boot 連接LDAP的方法

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

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

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

    IDEA檢查項(xiàng)目的jdk版本需要看的地方

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

    芦山县| 文登市| 营山县| 柘荣县| 呈贡县| 和林格尔县| 兰州市| 康平县| 荆州市| 镇沅| 利川市| 库伦旗| 开鲁县| 耒阳市| 石林| 晋江市| 温州市| 阿城市| 河曲县| 深水埗区| 班戈县| 侯马市| 贵阳市| 金乡县| 塔城市| 安泽县| 罗江县| 咸宁市| 汉川市| 临澧县| 兴山县| 都安| 卢龙县| 罗平县| 通辽市| 象州县| 洪湖市| 安康市| 壶关县| 廉江市| 西贡区|