一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Java中如何正確重寫equals方法

Java中如何正確重寫equals方法

2022-01-26 12:42寧海沒有七號公園 Java教程

Object類中equals方法比較的是兩個對象的引用地址,只有對象的引用地址指向同一個地址時,才認為這兩個地址是相等的,否則這兩個對象就不相等

重寫equals方法的正確打開方式

正文開始@Assassin

 

1. 什么是equals方法?

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

以下截圖自Java11 API

Java中如何正確重寫equals方法

Object類的所有方法:

Java中如何正確重寫equals方法

1.1 equals方法:

  • equals:是Object類中的方法,只能判斷引用類型,等下可以帶大伙看看jdk源碼
  • 默認判斷的是地址是否相等(因為引用類型變量底層本質就是來存儲對象地址的,有C/C++知識的小伙伴應該很了解 ),在子類中往往會重寫該方法,用于判斷對象的內容是否相等。比如等下會簡單了解到的IntegerString(在IDEA里看源碼實現 )

 

2. 為什么要重寫equals方法?

我們有Object類實現的equals方法能用不就行了?為啥還得重寫equals方法呢?這就要看看Object類equals方法實現機制了。

Java中如何正確重寫equals方法

我們可以清楚地看到,Object類equals方法底層是用 == 來實現的,也就是說它的用法跟我們平常用來比較基本數據類型的 == 用法一致。我們首先來看一下 == 的語法:

  • == 只能用來比較基本數據類型是否相等,也就是單純的值比較;
  • == 在比較浮點數的時候也可能存在失效的情況,這是因為浮點數的存儲機制跟整型家族不一樣,浮點數本身就不能表示一個精確的值(具體原因可自行查看IEEE 754規則,這里不再展開)

所以我們在單純的進行基本數據類型的值比較時可以用 == ,而比較引用數據類型就不能這么做,前面有提到,引用數據類型本質上是來引用/存儲對象的地址的,所有你完全可以把它當做C/C++的指針來看待(這里杠一句說Java沒有指針的,個人覺得只是叫法不同罷了
注: 不要把Java引用跟C++引用搞混了,C++引用其實是指針常量,即int* const,這也是C++的引用只能作為一個變量的別名的原因。

2.1 舉個例子吧~

比較兩個int時可以直接用 == ,當它們相等時結果為true,而當new了兩個屬性完全一樣的對象時,再用 == 來進行比較就會出現錯誤,如我們所見,明明我們應該想要得到true的,結果卻是false
源碼:

Java中如何正確重寫equals方法

運行結果:

Java中如何正確重寫equals方法

到這里,我們應該大致清楚為啥要在比較對象時重寫equals方法了,因為Object類提供給我們的不好使~~

 

3. 分析equals源碼:

在進行重寫之前,我們依舊來看看Java API中的定義:
public boolean equals?(Object obj)
作用:指示某個其他對象是否“等于”此對象。

equals方法在非null對象引用上實現等價關系:

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

類Object的equals方法實現了對象上最具區別的可能等價關系; 也就是說,對于任何非空引用值x和y ,當且僅當x和y引用同一對象( x == y具有值true )時,此方法返回true 。

注意:通常需要在重寫此方法時覆蓋hashCode方法,以便維護hashCode方法的常規協定,該方法聲明相等對象必須具有相等的哈希代碼。

Java中如何正確重寫equals方法

接下來看看String類中重寫的equals方法和Integer類中重寫的equals方法:

Java中如何正確重寫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;
  }

簡單解讀一下就是當對比的是同一個對象時,直接返回true,提高效率。當傳進來的對象是當前類的實例時,進入進一步的判斷,一個for循環依次遍歷字符串每一個字符,只要有一個字符不同就返回false

繼續來看看Integer類的equals源代碼:

Java中如何正確重寫equals方法

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

Integer類equals源碼簡單許多,只要傳入的對象是當前類的實例,就進行進一步的判斷:當它們的值相等時,就返回true,不相等就返回false

這里還是來實際演示一下⑧,就以Integer類為例:

Java中如何正確重寫equals方法

很明顯,我們知道Integer類重寫了equals方法且是引用類型。當直接用 == 來比較引用類型變量時,結果是false,而用equals判斷結果為true。這便很好地說明了重寫equals方法的必要性。String類大伙自己驗證一哈⑧。

 

4. 正確重寫equals方法:

(先說結論,getClass()instanceof更安全)

到這里,我們基本把equals方法的各種源碼都分析了一遍,接下來就是我們自己要來實現equals方法了。

這里提供兩個比較常見的equals重寫方法:

  • instanceof實現重寫equals方法
  • getClass實現重寫equals方法

假設有此場景:

在已經創建好的長方形類中重寫Object類中的equals方法為當長方形的長和寬相等時,返回TRUE,同時重寫hashCode方法,重寫toString方法為顯示長方形的長寬信息。并測試類。

package com.test10_04;

import java.util.Objects;

class Rectangle {
  private double length;
  private double wide;

  public Rectangle() {
      //空實現
  }
  public Rectangle(double length, double wide) {
      setLength(length);
      setWide(wide);
  }

  public double getLength() {
      return length;
  }

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

  public double getWide() {
      return wide;
  }

  public void setWide(double wide) {
      assert wide > 0.0 : "您的輸入有誤,長方形的寬不能小于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) { //判斷一下如果是同一個對象直接返回true,提高效率
          return true;
      }
      if (obj == null || obj.getClass() != this.getClass()) { //如果傳進來的對象為null或者二者為不同類,直接返回false
          return false;
      }
      //也可以以下方法:
//        if (obj == null || !(obj instanceof Rectangle)) { //如果傳進來的對象為null或者二者為不同類,直接返回false
//            return false;
//        }
      Rectangle rectangle = (Rectangle) obj; //向下轉型
      //比較長寬是否相等,注意:浮點數的比較不能簡單地用==,會有精度的誤差,用Math.abs或者Double.compare
      return Double.compare(rectangle.length, length) == 0 && Double.compare(rectangle.wide, wide) == 0;
  }

  public int hashCode() { //重寫equals的同時也要重寫hashCode,因為同一對象的hashCode永遠相等
      return Objects.hash(length, wide); //調用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());
  }
}

具體實現思路在代碼中講的很清楚了,我們這里重點分析一下getClassinstanceof兩種實現方法的優缺點:

Java中如何正確重寫equals方法

將代碼邏輯簡化一下:
我們就重點看這段簡單的代碼

//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()判斷對象是否屬于該類
		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;
		// 通過instanceof來判斷對象是否屬于類
		if (object == null || !(object instanceof Student))
			return false;
		Student student = (Student)object;
		return name!=null && name.equals(student.name);
	}
}

事實上兩種方案都是有效的,區別就是getClass()限制了對象只能是同一個類,而instanceof卻允許對象是同一個類或其子類,這樣equals方法就變成了父類與子類也可進行equals操作了,這時候如果子類重定義了equals方法,那么就可能變成父類對象equlas子類對象為true,但是子類對象equlas父類對象就為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");

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

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

注意看這里用的是getClass()

Java中如何正確重寫equals方法

返回值兩個都是false,符合我們的預期,(連類都不一樣那肯定得為false啊)

Java中如何正確重寫equals方法

而換成instanceof試試看咯:

Java中如何正確重寫equals方法

運行結果:一個為true一個為false,很明顯出現問題了。

Java中如何正確重寫equals方法

這里的原因如下:
instanceof的語法是這樣的:
當一個對象為一個類的實例時,結果才為true。但它還有一個特點就是,如果當這個對象時其子類的實例時,結果也會為true。這便導致了上述的bug。也就是說當比較的兩個對象,他們的類是父子關系時,instanceof可能會出現問題。**需要深究的小伙伴可以自己去了解一哈,所以在這里建議在實現重寫equals方法時,盡量使用getClass來實現。

在重寫equals方法的同時需要重寫hashCode方法,具體原因可能后續會講到~~

到此這篇關于Java中如何正確重寫equals方法的文章就介紹到這了,更多相關Java 重寫 equals方法內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://blog.csdn.net/b_ingram/article/details/120614766

延伸 · 閱讀

精彩推薦
  • Java教程Java BufferWriter寫文件寫不進去或缺失數據的解決

    Java BufferWriter寫文件寫不進去或缺失數據的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進去或缺失數據的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發項目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程xml與Java對象的轉換詳解

    xml與Java對象的轉換詳解

    這篇文章主要介紹了xml與Java對象的轉換詳解的相關資料,需要的朋友可以參考下...

    Java教程網2942020-09-17
  • Java教程Java實現搶紅包功能

    Java實現搶紅包功能

    這篇文章主要為大家詳細介紹了Java實現搶紅包功能,采用多線程模擬多人同時搶紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關于小米推送Java代碼,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧...

    富貴穩中求8032021-07-12
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經有好久沒有升過級了。升級完畢重啟之后,突然發現好多錯誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程Java8中Stream使用的一個注意事項

    Java8中Stream使用的一個注意事項

    最近在工作中發現了對于集合操作轉換的神器,java8新特性 stream,但在使用中遇到了一個非常重要的注意點,所以這篇文章主要給大家介紹了關于Java8中S...

    阿杜7482021-02-04
主站蜘蛛池模板: 继的朋友无遮漫画免费观看73 | 欧美国产日本高清不卡 | 亚洲精品国精品久久99热 | 国产午夜精品理论片 | 耽美调教高h | 福利片福利一区二区三区 | 四虎导航 | 俄罗斯男男激情1069gay | 亚洲成人免费 | 天天摸天天爽视频69视频 | 午夜精品久久久久久久2023 | 亚洲欧美国产在线 | 男同巨黄gay小说好爽 | 日日免费视频 | 亚洲人成网站在线观看播放青青 | www.av在线免费观看 | 雪恋电影完整版免费观看 | 免费观看在线观看 | 双性总裁被调教1v1 双性双根 | 香蕉久久一区二区三区啪啪 | 九九99热久久精品在线6 | 69成人网| 国产精品毛片久久久久久久 | 欧美成人tv | 亚洲高清免费在线观看 | 草草视频免费在线观看 | 精品日韩一区 | 色婷婷婷婷 | 免费大片 | 国产精品国产国产aⅴ | 午夜精品网站 | 玩50岁四川熟女大白屁股直播 | 日韩在线二区全免费 | 成人男女啪啪免费观看网站 | 香蕉eeww99国产精选播放 | 四虎在线精品免费高清在线 | 丰满岳乱妇在线观看视频国产 | 韩国理论三级在线观看视频 | 日韩在线二区全免费 | 嫩草成人影院 | 精品国产一区二区在线观看 |