Java中判斷對(duì)象是否相等的equals()方法使用教程
Object類中的equals方法用于檢測(cè)一個(gè)對(duì)象是否等于另一個(gè)對(duì)象。在Object類中,這個(gè)方法判斷兩個(gè)對(duì)象是否具有相同的引用,如果兩個(gè)對(duì)象具有相同的引用,它們一定是相等的。從這點(diǎn)上看,將其作為默認(rèn)操作也是合乎情理的。然而,對(duì)于多數(shù)類類說(shuō),這種判斷并沒(méi)有什么意義,例如,采用這種方式比較兩個(gè)PrintStream是否相等就完全沒(méi)有意義。然而,經(jīng)常需要檢測(cè)兩個(gè)對(duì)象狀態(tài)的相等性,如果兩個(gè)對(duì)象的狀態(tài)相等,就認(rèn)為這兩個(gè)對(duì)象是相等的。所以一般在自定義類中都要重寫(xiě)equals比較。
下面給出編寫(xiě)一個(gè)完美equals()方法的建議:
(1)顯式參數(shù)命名為otherObject,稍后需要將轉(zhuǎn)換成一個(gè)叫other的變量
(2)檢測(cè)this與otherObject是否引用同一個(gè)對(duì)象:
if(this==otherObject) return true;
這條語(yǔ)句只是一個(gè)優(yōu)化。實(shí)際上,這是一種經(jīng)常采用的形式。因?yàn)橛?jì)算這個(gè)等式要比一個(gè)一個(gè)地比較類中的域所付出的代價(jià)小的多。
(3)檢測(cè)otherObject是否為null,如果為null,返回false。這項(xiàng)檢測(cè)是很必要的。
if(otherObject==null) return false;
(4)比較this和otherObject是否屬于同一個(gè)類,如果equals的語(yǔ)義在每個(gè)子類中有所改變,就使用getClass()檢測(cè),它將自己作為目標(biāo)類
if(getClass()!=otherObject.getClass()) return false;
如果所有的子類都擁有同一的語(yǔ)義,就使用instanceof檢測(cè)
if(!(otherObject instanceof ClassName)) return false;
(5)將otherObject轉(zhuǎn)換為相應(yīng)類型的變量:
ClassName other=(ClassName)otherObject;
(6)現(xiàn)在開(kāi)始對(duì)所有需要比較的域進(jìn)行比較。使用==比較基本類型域,使用equals比較對(duì)象域。如果所有域都匹配,就返回true,否則返回false;
return field1==other.field1&&field2.equals(other.field2)
如果在子類中重新定義equals,就要在其中包含調(diào)用super.equals(other)。如果檢測(cè)失敗,就不可能相等。如果超類中的域相等,就比較子類中的實(shí)例域。
對(duì)于數(shù)組類型的域,可以使用靜態(tài)的Arrays.equals方法檢測(cè)相應(yīng)的元素是否相等。
來(lái)看幾個(gè)字符串比較例子:
String a = "abc"; String b = "abc"; String c = new String("abc"); String d = new String("abc"); System.out.println(a == b); // true 因?yàn)镴AVA中字符串常量是共享的,只有一個(gè)拷貝 System.out.println(a == c); // false a和c屬于2個(gè)不同的對(duì)象 System.out.println(a.equals(c)); // true 由于String對(duì)象的equals方法比較的是對(duì)象中的值,所以返回true。(和Object的equals方法不同) System.out.println(c==d); // false c和d雖然對(duì)象內(nèi)的值相同,但屬于2個(gè)不同的對(duì)象,所以不相等 System.out.println(c.equals(d)); // true
簡(jiǎn)單的說(shuō),當(dāng)比較字符串常量時(shí),等于和equals返回的結(jié)果一樣,當(dāng)想比較字符串對(duì)象的值時(shí)用equals。
看一個(gè)equals的使用例子:
package chapter05.EqualsTest; import java.util.*; public class EqualsTest { public static void main(String[] args) { Employee alice1 = new Employee("Alice Adams", 75000, 1987, 12, 15); Employee alice2 = alice1; // reference the same object Employee alice3 = new Employee("Alice Adams", 75000, 1987, 12, 15); Employee bob = new Employee("Bob Brandson", 50000, 1989, 10, 1); System.out.println("alice1 == alice2: " + (alice1 == alice2)); System.out.println("alice1 == alice3: " + (alice1 == alice3)); System.out.println("alice1.equals(alice3): " + (alice1.equals(alice3))); System.out.println("alice1.equals(bob): " + (alice1.equals(bob))); System.out.println(bob.toString()); } } class Employee { public Employee(String n, double s, int year, int month, int day) { name = n; salary = s; GregorianCalendar calendar = new GregorianCalendar(year, month, day); hireDay = calendar.getTime(); } public String getName() { return name; } public double getSalary() { return salary; } public Date getHireDay() { return hireDay; } public void raiseSalary(double byPercent) { double raise = salary * byPercent / 100; salary += raise; } @Override public boolean equals(Object otherObject) { // a quick test to see if the objects are identical if (this == otherObject) return true; // must return false if the explicit parameter is null if (otherObject == null) return false; // if the classed don't match,they can't be equal if (getClass() != otherObject.getClass()) return false; // now we know otherObject is a non-null Employee Employee other = (Employee) otherObject; // test whether the fields hava identical values return name.equals(other.name) && salary == other.salary && hireDay.equals(other.hireDay); } @Override public int hashCode() { return 7 * name.hashCode() + 11 * new Double(salary).hashCode() + 13 * hireDay.hashCode(); } @Override public String toString() { return getClass().getName() + "[name=" + name + ",salary=" + salary + ",hireDay=" + hireDay + "]"; } private String name; private double salary; private Date hireDay; } class Manager extends Employee { public Manager(String n, double s, int year, int month, int day) { super(n, s, year, month, day); bouns = 0; } @Override public double getSalary() { double baseSalary = super.getSalary(); return baseSalary + bouns; } public void setBouns(double b) { bouns = b; } @Override public boolean equals(Object otherObject) { if (!super.equals(otherObject)) return false; Manager other = (Manager) otherObject; // super equals checked that this and other belong to the same class return bouns == other.bouns; } @Override public int hashCode() { return super.hashCode() + 17 * new Double(bouns).hashCode(); } @Override public String toString() { return super.toString() + "[bouns=" + bouns + "]"; } private double bouns; }
深入
下面根據(jù)“類是否覆蓋equals()方法”,將它分為2類。
(1) 若某個(gè)類沒(méi)有覆蓋equals()方法,當(dāng)它的通過(guò)equals()比較兩個(gè)對(duì)象時(shí),實(shí)際上是比較兩個(gè)對(duì)象是不是同一個(gè)對(duì)象。這時(shí),等價(jià)于通過(guò)“==”去比較這兩個(gè)對(duì)象。
(2) 我們可以覆蓋類的equals()方法,來(lái)讓equals()通過(guò)其它方式比較兩個(gè)對(duì)象是否相等。通常的做法是:若兩個(gè)對(duì)象的內(nèi)容相等,則equals()方法返回true;否則,返回fasle。
下面,舉例對(duì)上面的2種情況進(jìn)行說(shuō)明。
1. “沒(méi)有覆蓋equals()方法”的情況
代碼如下 (EqualsTest1.java):
import java.util.*; import java.lang.Comparable; /** * @desc equals()的測(cè)試程序。 */ public class EqualsTest1{ public static void main(String[] args) { // 新建2個(gè)相同內(nèi)容的Person對(duì)象, // 再用equals比較它們是否相等 Person p1 = new Person("eee", 100); Person p2 = new Person("eee", 100); System.out.printf("%s\n", p1.equals(p2)); } /** * @desc Person類。 */ private static class Person { int age; String name; public Person(String name, int age) { this.name = name; this.age = age; } public String toString() { return name + " - " +age; } } }
運(yùn)行結(jié)果:
結(jié)果分析
我們通過(guò) p1.equals(p2) 來(lái)“比較p1和p2是否相等時(shí)”。實(shí)際上,調(diào)用的Object.java的equals()方法,即調(diào)用的 (p1==p2) 。它是比較“p1和p2是否是同一個(gè)對(duì)象”。
而由 p1 和 p2 的定義可知,它們雖然內(nèi)容相同;但它們是兩個(gè)不同的對(duì)象!因此,返回結(jié)果是false。
2. "覆蓋equals()方法"的情況
我們修改上面的EqualsTest1.java:覆蓋equals()方法。
代碼如下 (EqualsTest2.java):
import java.util.*; import java.lang.Comparable; /** * @desc equals()的測(cè)試程序。 */ public class EqualsTest2{ public static void main(String[] args) { // 新建2個(gè)相同內(nèi)容的Person對(duì)象, // 再用equals比較它們是否相等 Person p1 = new Person("eee", 100); Person p2 = new Person("eee", 100); System.out.printf("%s\n", p1.equals(p2)); } /** * @desc Person類。 */ private static class Person { int age; String name; public Person(String name, int age) { this.name = name; this.age = age; } public String toString() { return name + " - " +age; } /** * @desc 覆蓋equals方法 */ @Override public boolean equals(Object obj){ if(obj == null){ return false; } //如果是同一個(gè)對(duì)象返回true,反之返回false if(this == obj){ return true; } //判斷是否類型相同 if(this.getClass() != obj.getClass()){ return false; } Person person = (Person)obj; return name.equals(person.name) && age==person.age; } } }
運(yùn)行結(jié)果:
結(jié)果分析:
我們?cè)贓qualsTest2.java 中重寫(xiě)了Person的equals()函數(shù):當(dāng)兩個(gè)Person對(duì)象的 name 和 age 都相等,則返回true。
因此,運(yùn)行結(jié)果返回true。
講到這里,順便說(shuō)一下java對(duì)equals()的要求。有以下幾點(diǎn):
對(duì)稱性:如果x.equals(y)返回是"true",那么y.equals(x)也應(yīng)該返回是"true"。
反射性:x.equals(x)必須返回是"true"。
類推性:如果x.equals(y)返回是"true",而且y.equals(z)返回是"true",那么z.equals(x)也應(yīng)該返回是"true"。
一致性:如果x.equals(y)返回是"true",只要x和y內(nèi)容一直不變,不管你重復(fù)x.equals(y)多少次,返回都是"true"。
非空性,x.equals(null),永遠(yuǎn)返回是"false";x.equals(和x不同類型的對(duì)象)永遠(yuǎn)返回是"false"。
現(xiàn)在,再回顧一下equals()的作用:判斷兩個(gè)對(duì)象是否相等。當(dāng)我們重寫(xiě)equals()的時(shí)候,可千萬(wàn)不好將它的作用給改變了!
equals() 與 == 的區(qū)別是什么?
== : 它的作用是判斷兩個(gè)對(duì)象的地址是不是相等。即,判斷兩個(gè)對(duì)象是不是同一個(gè)對(duì)象。
equals() : 它的作用也是判斷兩個(gè)對(duì)象是否相等。但它一般有兩種使用情況(前面第1部分已詳細(xì)介紹過(guò)):
情況1,類沒(méi)有覆蓋equals()方法。則通過(guò)equals()比較該類的兩個(gè)對(duì)象時(shí),等價(jià)于通過(guò)“==”比較這兩個(gè)對(duì)象。
情況2,類覆蓋了equals()方法。一般,我們都覆蓋equals()方法來(lái)兩個(gè)對(duì)象的內(nèi)容相等;若它們的內(nèi)容相等,則返回true(即,認(rèn)為這兩個(gè)對(duì)象相等)。
下面,通過(guò)示例比較它們的區(qū)別。
代碼如下:
import java.util.*; import java.lang.Comparable; /** * @desc equals()的測(cè)試程序。 */ public class EqualsTest3{ public static void main(String[] args) { // 新建2個(gè)相同內(nèi)容的Person對(duì)象, // 再用equals比較它們是否相等 Person p1 = new Person("eee", 100); Person p2 = new Person("eee", 100); System.out.printf("p1.equals(p2) : %s\n", p1.equals(p2)); System.out.printf("p1==p2 : %s\n", p1==p2); } /** * @desc Person類。 */ private static class Person { int age; String name; public Person(String name, int age) { this.name = name; this.age = age; } public String toString() { return name + " - " +age; } /** * @desc 覆蓋equals方法 */ @Override public boolean equals(Object obj){ if(obj == null){ return false; } //如果是同一個(gè)對(duì)象返回true,反之返回false if(this == obj){ return true; } //判斷是否類型相同 if(this.getClass() != obj.getClass()){ return false; } Person person = (Person)obj; return name.equals(person.name) && age==person.age; } } }
運(yùn)行結(jié)果:
p1.equals(p2) : true p1==p2 : false
結(jié)果分析:
在EqualsTest3.java 中:
(1) p1.equals(p2)
這是判斷p1和p2的內(nèi)容是否相等。因?yàn)镻erson覆蓋equals()方法,而這個(gè)equals()是用來(lái)判斷p1和p2的內(nèi)容是否相等,恰恰p1和p2的內(nèi)容又相等;因此,返回true。
(2) p1==p2
這是判斷p1和p2是否是同一個(gè)對(duì)象。由于它們是各自新建的兩個(gè)Person對(duì)象;因此,返回false。
相關(guān)文章
MyBatis關(guān)于二級(jí)緩存問(wèn)題
本篇文章主要介紹了MyBatis關(guān)于二級(jí)緩存問(wèn)題,二級(jí)緩存是Mapper級(jí)別的緩存,多個(gè)sqlSession操作同一個(gè)Mapper,其二級(jí)緩存是可以共享的。2017-03-03Spring IOC推導(dǎo)與DI構(gòu)造器注入超詳細(xì)講解
這篇文章主要介紹了Spring IOC推導(dǎo)與DI構(gòu)造器注入,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2023-02-02詳解java中finalize的實(shí)現(xiàn)與相應(yīng)的執(zhí)行過(guò)程
在常規(guī)的java書(shū)籍中,即會(huì)描述 object的finalize方法是用于一些特殊的對(duì)象在回收之前再做一些掃尾的工作,但是并沒(méi)有說(shuō)明此是如何實(shí)現(xiàn)的.本篇從java的角度(不涉及jvm以及c++),有需要的朋友們可以參考借鑒。2016-09-09基于Java實(shí)現(xiàn)簡(jiǎn)單的時(shí)序數(shù)據(jù)壓縮算法
這篇文章主要為大家詳細(xì)介紹了如何利用Java語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單易懂的時(shí)序數(shù)據(jù)壓縮算法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2022-06-06Java的this關(guān)鍵字的使用與方法的重載相關(guān)知識(shí)
這篇文章主要介紹了Java的this關(guān)鍵字的使用與方法的重載相關(guān)知識(shí),是Java入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-09-09數(shù)據(jù)同步利器DataX簡(jiǎn)介及如何使用
DataX?是阿里云?DataWorks數(shù)據(jù)集成?的開(kāi)源版本,使用Java?語(yǔ)言編寫(xiě),在阿里巴巴集團(tuán)內(nèi)被廣泛使用的離線數(shù)據(jù)同步工具/平臺(tái),今天給大家分享一個(gè)阿里開(kāi)源的數(shù)據(jù)同步工具DataX,在Github擁有14.8k的star,非常受歡迎2024-02-02java ArrayList和Vector的區(qū)別詳解
這篇文章主要介紹了java ArrayList和Vector的區(qū)別詳解的相關(guān)資料,并附簡(jiǎn)單實(shí)例代碼,需要的朋友可以參考下2016-11-11