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

Java中如何正確重寫(xiě)equals方法

 更新時(shí)間:2021年10月06日 11:59:05   作者:寧海沒(méi)有七號(hào)公園  
Object類中equals方法比較的是兩個(gè)對(duì)象的引用地址,只有對(duì)象的引用地址指向同一個(gè)地址時(shí),才認(rèn)為這兩個(gè)地址是相等的,否則這兩個(gè)對(duì)象就不相等

重寫(xiě)equals方法的正確打開(kāi)方式

正文開(kāi)始@Assassin

1. 什么是equals方法?

我們首先得知道,Object類Java中所有類的父類(超類/基類),也就是說(shuō),在Java中,所有的類都是默認(rèn)繼承自Object類的,換言之,Object類中所實(shí)現(xiàn)的方法我們都可以直接拿來(lái)用。而equals方法便是Object類所實(shí)現(xiàn)的眾多方法之一。

以下截圖自Java11 API

Object類的所有方法:

1.1 equals方法:

  • equals:是Object類中的方法,只能判斷引用類型,等下可以帶大伙看看jdk源碼
  • 默認(rèn)判斷的是地址是否相等(因?yàn)橐妙愋妥兞康讓颖举|(zhì)就是來(lái)存儲(chǔ)對(duì)象地址的,有C/C++知識(shí)的小伙伴應(yīng)該很了解 ),在子類中往往會(huì)重寫(xiě)該方法,用于判斷對(duì)象的內(nèi)容是否相等。比如等下會(huì)簡(jiǎn)單了解到的IntegerString(在IDEA里看源碼實(shí)現(xiàn) )

2. 為什么要重寫(xiě)equals方法?

我們有Object類實(shí)現(xiàn)的equals方法能用不就行了?為啥還得重寫(xiě)equals方法呢?這就要看看Object類equals方法實(shí)現(xiàn)機(jī)制了。

我們可以清楚地看到,Object類equals方法底層是用 == 來(lái)實(shí)現(xiàn)的,也就是說(shuō)它的用法跟我們平常用來(lái)比較基本數(shù)據(jù)類型的 == 用法一致。我們首先來(lái)看一下 == 的語(yǔ)法:

  • == 只能用來(lái)比較基本數(shù)據(jù)類型是否相等,也就是單純的值比較;
  • == 在比較浮點(diǎn)數(shù)的時(shí)候也可能存在失效的情況,這是因?yàn)楦↑c(diǎn)數(shù)的存儲(chǔ)機(jī)制跟整型家族不一樣,浮點(diǎn)數(shù)本身就不能表示一個(gè)精確的值(具體原因可自行查看IEEE 754規(guī)則,這里不再展開(kāi))

所以我們?cè)趩渭兊倪M(jìn)行基本數(shù)據(jù)類型的值比較時(shí)可以用 == ,而比較引用數(shù)據(jù)類型就不能這么做,前面有提到,引用數(shù)據(jù)類型本質(zhì)上是來(lái)引用/存儲(chǔ)對(duì)象的地址的,所有你完全可以把它當(dāng)做C/C++的指針來(lái)看待(這里杠一句說(shuō)Java沒(méi)有指針的,個(gè)人覺(jué)得只是叫法不同罷了
注: 不要把Java引用跟C++引用搞混了,C++引用其實(shí)是指針常量,即int* const,這也是C++的引用只能作為一個(gè)變量的別名的原因。

2.1 舉個(gè)例子吧~

比較兩個(gè)int時(shí)可以直接用 == ,當(dāng)它們相等時(shí)結(jié)果為true,而當(dāng)new了兩個(gè)屬性完全一樣的對(duì)象時(shí),再用 == 來(lái)進(jìn)行比較就會(huì)出現(xiàn)錯(cuò)誤,如我們所見(jiàn),明明我們應(yīng)該想要得到true的,結(jié)果卻是false
源碼:

運(yùn)行結(jié)果:

到這里,我們應(yīng)該大致清楚為啥要在比較對(duì)象時(shí)重寫(xiě)equals方法了,因?yàn)?code>Object類提供給我們的不好使~~

3. 分析equals源碼:

在進(jìn)行重寫(xiě)之前,我們依舊來(lái)看看Java API中的定義:
public boolean equals​(Object obj)
作用:指示某個(gè)其他對(duì)象是否“等于”此對(duì)象。

equals方法在非null對(duì)象引用上實(shí)現(xiàn)等價(jià)關(guān)系:

  • 自反性 :對(duì)于任何非空的參考值x , x.equals(x)應(yīng)該返回true 。
  • 對(duì)稱性 :對(duì)于任何非空引用值xyx.equals(y)應(yīng)該返回true當(dāng)且僅當(dāng)y.equals(x)回報(bào)true 。
  • 傳遞性 :對(duì)于任何非空引用值xyz ,如果x.equals(y)回報(bào)true個(gè)y.equals(z)回報(bào)true ,然后x.equals(z)應(yīng)該返回true 。
  • 一致性 :對(duì)于任何非空引用值xy ,多次調(diào)用x.equals(y)始終返回true或始終返回false ,前提是未修改對(duì)象上的equals比較中使用的信息。
  • 對(duì)于任何非空的參考值x , x.equals(null)應(yīng)該返回false。

類Object的equals方法實(shí)現(xiàn)了對(duì)象上最具區(qū)別的可能等價(jià)關(guān)系; 也就是說(shuō),對(duì)于任何非空引用值x和y ,當(dāng)且僅當(dāng)x和y引用同一對(duì)象( x == y具有值true )時(shí),此方法返回true 。

注意:通常需要在重寫(xiě)此方法時(shí)覆蓋hashCode方法,以便維護(hù)hashCode方法的常規(guī)協(xié)定,該方法聲明相等對(duì)象必須具有相等的哈希代碼。

接下來(lái)看看String類中重寫(xiě)的equals方法和Integer類中重寫(xiě)的equals方法:

//String類equals源代碼:
public boolean equals(Object anObject) {
        if (this == anObject) {
            return 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;
    }

簡(jiǎn)單解讀一下就是當(dāng)對(duì)比的是同一個(gè)對(duì)象時(shí),直接返回true,提高效率。當(dāng)傳進(jìn)來(lái)的對(duì)象是當(dāng)前類的實(shí)例時(shí),進(jìn)入進(jìn)一步的判斷,一個(gè)for循環(huán)依次遍歷字符串每一個(gè)字符,只要有一個(gè)字符不同就返回false。

繼續(xù)來(lái)看看Integer類的equals源代碼:

//Integer類的equals源代碼:
 public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }

Integer類equals源碼簡(jiǎn)單許多,只要傳入的對(duì)象是當(dāng)前類的實(shí)例,就進(jìn)行進(jìn)一步的判斷:當(dāng)它們的值相等時(shí),就返回true,不相等就返回false。

這里還是來(lái)實(shí)際演示一下⑧,就以Integer類為例:

很明顯,我們知道Integer類重寫(xiě)了equals方法且是引用類型。當(dāng)直接用 == 來(lái)比較引用類型變量時(shí),結(jié)果是false,而用equals判斷結(jié)果為true。這便很好地說(shuō)明了重寫(xiě)equals方法的必要性。String類大伙自己驗(yàn)證一哈⑧。

4. 正確重寫(xiě)equals方法:

(先說(shuō)結(jié)論,getClass()instanceof更安全)

到這里,我們基本把equals方法的各種源碼都分析了一遍,接下來(lái)就是我們自己要來(lái)實(shí)現(xiàn)equals方法了。

這里提供兩個(gè)比較常見(jiàn)的equals重寫(xiě)方法:

  • instanceof實(shí)現(xiàn)重寫(xiě)equals方法
  • getClass實(shí)現(xiàn)重寫(xiě)equals方法

假設(shè)有此場(chǎng)景:

在已經(jīng)創(chuàng)建好的長(zhǎng)方形類中重寫(xiě)Object類中的equals方法為當(dāng)長(zhǎng)方形的長(zhǎng)和寬相等時(shí),返回TRUE,同時(shí)重寫(xiě)hashCode方法,重寫(xiě)toString方法為顯示長(zhǎng)方形的長(zhǎng)寬信息。并測(cè)試類。

package com.test10_04;

import java.util.Objects;

class Rectangle {
    private double length;
    private double wide;

    public Rectangle() {
        //空實(shí)現(xiàn)
    }
    public Rectangle(double length, double wide) {
        setLength(length);
        setWide(wide);
    }

    public double getLength() {
        return length;
    }

    public void setLength(double length) {
        assert length > 0.0 : "您的輸入有誤,長(zhǎng)方形的長(zhǎng)不能小于0";
        this.length = length;
    }

    public double getWide() {
        return wide;
    }

    public void setWide(double wide) {
        assert wide > 0.0 : "您的輸入有誤,長(zhǎng)方形的寬不能小于0";
        this.wide = wide;
    }

    public double area() {
        return this.length * this.wide;

    }
    public double circumference() {
        return 2 * (this.wide + this.length);
    }

    public boolean equals(Object obj) {
        if (this == obj) { //判斷一下如果是同一個(gè)對(duì)象直接返回true,提高效率
            return true;
        }
        if (obj == null || obj.getClass() != this.getClass()) { //如果傳進(jìn)來(lái)的對(duì)象為null或者二者為不同類,直接返回false
            return false;
        }
        //也可以以下方法:
//        if (obj == null || !(obj instanceof Rectangle)) { //如果傳進(jìn)來(lái)的對(duì)象為null或者二者為不同類,直接返回false
//            return false;
//        }
        Rectangle rectangle = (Rectangle) obj; //向下轉(zhuǎn)型
        //比較長(zhǎng)寬是否相等,注意:浮點(diǎn)數(shù)的比較不能簡(jiǎn)單地用==,會(huì)有精度的誤差,用Math.abs或者Double.compare
        return Double.compare(rectangle.length, length) == 0 && Double.compare(rectangle.wide, wide) == 0;
    }

    public int hashCode() { //重寫(xiě)equals的同時(shí)也要重寫(xiě)hashCode,因?yàn)橥粚?duì)象的hashCode永遠(yuǎn)相等
        return Objects.hash(length, wide); //調(diào)用Objects類,這是Object類的子類
    }

    public String toString() {
        return "Rectangle{" + "length=" + length + ", wide=" + wide + '}';
    }
}

public class TestDemo {
    public static void main(String[] args) {

        Rectangle rectangle1 = new Rectangle(3.0, 2.0);
        Rectangle rectangle2 = new Rectangle(3.0, 2.0);
        
        System.out.println(rectangle1.equals(rectangle2));
        System.out.println("rectangle1哈希碼:" + rectangle1.hashCode() +
                "\nrectangle2哈希碼:" + rectangle2.hashCode());
        System.out.println("toString打印信息:" + rectangle1.toString());
    }
}

具體實(shí)現(xiàn)思路在代碼中講的很清楚了,我們這里重點(diǎn)分析一下getClassinstanceof兩種實(shí)現(xiàn)方法的優(yōu)缺點(diǎn):

將代碼邏輯簡(jiǎn)化一下:
我們就重點(diǎn)看這段簡(jiǎn)單的代碼

//getClass()版本
public class Student {
	private String name;
	
    public void setName(String name) {
        this.name = name;
    }
	@Override
	public boolean equals(Object object){
		if (object == this)
			return true;
		// 使用getClass()判斷對(duì)象是否屬于該類
		if (object == null || object.getClass() != getClass())
			return false;
		Student student = (Student)object;
		return name != null && name.equals(student.name);
}

//instanceof版本
public class Student {
	private String name;
	
    public void setName(String name) {
       this.name = name;
   }
	@Override
	public boolean equals(Object object){
		if (object == this)
			return true;
		// 通過(guò)instanceof來(lái)判斷對(duì)象是否屬于類
		if (object == null || !(object instanceof Student))
			return false;
		Student student = (Student)object;
		return name!=null && name.equals(student.name);
	}
}

事實(shí)上兩種方案都是有效的,區(qū)別就是getClass()限制了對(duì)象只能是同一個(gè)類,而instanceof卻允許對(duì)象是同一個(gè)類或其子類,這樣equals方法就變成了父類與子類也可進(jìn)行equals操作了,這時(shí)候如果子類重定義了equals方法,那么就可能變成父類對(duì)象equlas子類對(duì)象為true,但是子類對(duì)象equlas父類對(duì)象就為false了,如下所示:

class GoodStudent extends Student {

    @Override
    public boolean equals(Object object) {
        return false;
    }

    public static void main(String[] args) {
        GoodStudent son = new GoodStudent();
        Student father = new Student();

        son.setName("test");
        father.setName("test");

		// 當(dāng)使用instance of時(shí)
        System.out.println(son.equals(father)); // 這里為false
        System.out.println(father.equals(son)); // 這里為true

		// 當(dāng)使用getClass()時(shí)
        System.out.println(son.equals(father)); // 這里為false
        System.out.println(father.equals(son)); // 這里為false	
    }
}

注意看這里用的是getClass()

返回值兩個(gè)都是false,符合我們的預(yù)期,(連類都不一樣那肯定得為false?。?/p>

而換成instanceof試試看咯:

運(yùn)行結(jié)果:一個(gè)為true一個(gè)為false,很明顯出現(xiàn)問(wèn)題了。

這里的原因如下:
instanceof的語(yǔ)法是這樣的:
當(dāng)一個(gè)對(duì)象為一個(gè)類的實(shí)例時(shí),結(jié)果才為true。但它還有一個(gè)特點(diǎn)就是,如果當(dāng)這個(gè)對(duì)象時(shí)其子類的實(shí)例時(shí),結(jié)果也會(huì)為true。這便導(dǎo)致了上述的bug。也就是說(shuō)當(dāng)比較的兩個(gè)對(duì)象,他們的類是父子關(guān)系時(shí),instanceof可能會(huì)出現(xiàn)問(wèn)題。**需要深究的小伙伴可以自己去了解一哈,所以在這里建議在實(shí)現(xiàn)重寫(xiě)equals方法時(shí),盡量使用getClass來(lái)實(shí)現(xiàn)。

在重寫(xiě)equals方法的同時(shí)需要重寫(xiě)hashCode方法,具體原因可能后續(xù)會(huì)講到~~

到此這篇關(guān)于Java中如何正確重寫(xiě)equals方法的文章就介紹到這了,更多相關(guān)Java 重寫(xiě) equals方法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java面試題之HashSet的實(shí)現(xiàn)原理

    Java面試題之HashSet的實(shí)現(xiàn)原理

    這篇文章主要介紹了Java面試題之HashSet的實(shí)現(xiàn)原理,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-01-01
  • Spring Boot整合Spring Data Jpa代碼實(shí)例

    Spring Boot整合Spring Data Jpa代碼實(shí)例

    這篇文章主要介紹了Spring Boot整合Spring Data Jpa代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • Java使用J4L識(shí)別驗(yàn)證碼的操作方法

    Java使用J4L識(shí)別驗(yàn)證碼的操作方法

    這篇文章主要介紹了Java使用J4L識(shí)別驗(yàn)證碼的操作方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-02-02
  • Spring Security認(rèn)證提供程序示例詳解

    Spring Security認(rèn)證提供程序示例詳解

    這篇文章主要給大家介紹了關(guān)于Spring Security認(rèn)證提供程序的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Spring Security具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-05-05
  • 基于SpringBoot+Redis實(shí)現(xiàn)一個(gè)簡(jiǎn)單的限流器

    基于SpringBoot+Redis實(shí)現(xiàn)一個(gè)簡(jiǎn)單的限流器

    在Spring?Boot中使用Redis和過(guò)濾器實(shí)現(xiàn)請(qǐng)求限流,過(guò)濾器將在每個(gè)請(qǐng)求到達(dá)時(shí)檢查請(qǐng)求頻率,并根據(jù)設(shè)定的閾值進(jìn)行限制,這樣可以保護(hù)您的應(yīng)用程序免受惡意請(qǐng)求或高并發(fā)請(qǐng)求的影響,本文我們通過(guò)Spring?Boot?+Redis?實(shí)現(xiàn)一個(gè)輕量級(jí)的消息隊(duì)列,需要的朋友可以參考下
    2023-08-08
  • Java生成UUID的常用方式示例代碼

    Java生成UUID的常用方式示例代碼

    UUID保證對(duì)在同一時(shí)空中的所有機(jī)器都是唯一的,通常平臺(tái)會(huì)提供生成的API,按照開(kāi)放軟件基金會(huì)(OSF)制定的標(biāo)準(zhǔn)計(jì)算,用到了以太網(wǎng)卡地址、納秒級(jí)時(shí)間、芯片ID碼和許多可能的數(shù)字,下面這篇文章主要給大家介紹了關(guān)于Java生成UUID的常用方式,需要的朋友可以參考下
    2023-05-05
  • 在IDEA中配置Maven鏡像的最新方法

    在IDEA中配置Maven鏡像的最新方法

    這篇文章主要給大家介紹了關(guān)于在IDEA中配置Maven鏡像的最新方法,Maven是一個(gè)流行的Java項(xiàng)目構(gòu)建工具,它依賴于互聯(lián)網(wǎng)上的Mave中央倉(cāng)庫(kù)來(lái)下載和管理項(xiàng)目依賴庫(kù),文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2023-11-11
  • java如何實(shí)現(xiàn)post請(qǐng)求webservice服務(wù)端

    java如何實(shí)現(xiàn)post請(qǐng)求webservice服務(wù)端

    這篇文章主要介紹了java如何實(shí)現(xiàn)post請(qǐng)求webservice服務(wù)端,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Java map 優(yōu)雅的元素遍歷方式說(shuō)明

    Java map 優(yōu)雅的元素遍歷方式說(shuō)明

    這篇文章主要介紹了Java map 優(yōu)雅的元素遍歷方式說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-10-10
  • 使用Mybatis-plus實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)表的內(nèi)部字段進(jìn)行比較

    使用Mybatis-plus實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)表的內(nèi)部字段進(jìn)行比較

    這篇文章主要介紹了使用Mybatis-plus實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)表的內(nèi)部字段進(jìn)行比較方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07

最新評(píng)論

巫山县| 和硕县| 库尔勒市| 芒康县| 左权县| 土默特左旗| 三明市| 蒙城县| 凌海市| 宝坻区| 塔城市| 阿拉尔市| 工布江达县| 乌审旗| 克拉玛依市| 馆陶县| 得荣县| 金寨县| 哈密市| 枣庄市| 壶关县| 涪陵区| 巴林左旗| 库尔勒市| 仁怀市| 大宁县| 洪江市| 顺平县| 紫金县| 怀安县| 张家港市| 蓬安县| 伊通| 洞头县| 郧西县| 马龙县| 杭锦后旗| 梅州市| 资中县| 江口县| 通州区|