Java編程中的equals方法使用全解
通過(guò)下面的例子掌握equals的用法
package cn.galc.test; public class TestEquals { public static void main(String[] args) { /** * 這里使用構(gòu)造方法Cat()在堆內(nèi)存里面new出了兩只貓, * 這兩只貓的color,weight,height都是一樣的, * 但c1和c2卻永遠(yuǎn)不會(huì)相等,這是因?yàn)閏1和c2分別為堆內(nèi)存里面兩只貓的引用對(duì)象, * 里面裝著可以找到這兩只貓的地址,但由于兩只貓?jiān)诙褍?nèi)存里面存儲(chǔ)在兩個(gè)不同的空間里面, * 所以c1和c2分別裝著不同的地址,因此c1和c2永遠(yuǎn)不會(huì)相等。 */ Cat c1 = new Cat(1, 1, 1); Cat c2 = new Cat(1, 1, 1); System.out.println("c1==c2的結(jié)果是:"+(c1==c2));//false System.out.println("c1.equals(c2)的結(jié)果是:"+c1.equals(c2));//false } } class Cat { int color, weight, height; public Cat(int color, int weight, int height) { this.color = color; this.weight = weight; this.height = height; } }
畫出內(nèi)存分析圖分析c1和c2比較的結(jié)果
程序:
Cat c1 = new Cat(1,1,1); Cat c2 = new Cat(1,1,1);
執(zhí)行完之后內(nèi)存之中的布局如下圖所示,
c1指向一個(gè)對(duì)象,c2也指向一個(gè)對(duì)象,c1和c2里面裝著的是這兩只Cat對(duì)象在堆內(nèi)存里面存儲(chǔ)的地址,由于這兩只Cat對(duì)象分別位于不同的存儲(chǔ)空間,因此c1和c2里面裝著的地址肯定不相等,因此c1和c2這兩個(gè)引用對(duì)象也肯定不相等。因此執(zhí)行:“System.out.println(c1==c2);”打印出來(lái)的結(jié)果肯定是false。因此你new出來(lái)了兩個(gè)對(duì)象,你放心,這兩個(gè)對(duì)象的引用永遠(yuǎn)不一樣,一樣的話就會(huì)把其中一個(gè)給覆蓋掉了,這個(gè)可不成。c1是不是等于c2比較的是c1和c2這兩個(gè)引用里面裝著的內(nèi)容,因?yàn)閚ew出來(lái)的兩個(gè)對(duì)象的它們的引用永遠(yuǎn)不一樣,因此c1和c2這兩個(gè)引用的內(nèi)容也永遠(yuǎn)不一樣,因此c1永遠(yuǎn)不可能等于c2。因此通過(guò)比較兩個(gè)對(duì)象的引用是永遠(yuǎn)無(wú)法使得兩個(gè)對(duì)象相等的,一模一樣的。
要想判斷兩個(gè)對(duì)象是否相等,不能通過(guò)比較兩個(gè)對(duì)象的引用是否相等,這是永遠(yuǎn)都得不到相等的結(jié)果的,因?yàn)閮蓚€(gè)對(duì)象的引用永遠(yuǎn)不會(huì)相等,所以正確的比較方法是直接比較這兩個(gè)對(duì)象,比較這兩個(gè)對(duì)象的實(shí)質(zhì)是不是一樣的,即這兩個(gè)對(duì)象里面的內(nèi)容是不是相同的,通過(guò)比較這兩個(gè)對(duì)象的屬性值是否相同而決定這兩個(gè)對(duì)象是否相等。
Object類提供了一個(gè)equals()方法來(lái)比較兩個(gè)對(duì)象的內(nèi)容是否相同,因此我們可以采用這個(gè)方法去比較兩個(gè)對(duì)象是否在邏輯上“相等”。如:c1.equals(c2);這里是調(diào)用從Object類繼承下來(lái)的equals()方法,通過(guò)查閱API文檔得到Object類里的equals方法的定義如下:
public boolean equals(Object obj)
在Object這個(gè)類里面提供的Equals()方法默認(rèn)的實(shí)現(xiàn)是比較當(dāng)前對(duì)象的引用和你要比較的那個(gè)引用它們指向的是否是同一個(gè)對(duì)象,即和“c1==c2”這種寫法是一樣的,“c1.equals(c2)”與“c1==c2”是完全等價(jià)的。因此直接使用繼承下來(lái)的equals()方法也是無(wú)法直接比較兩個(gè)對(duì)象的內(nèi)容是否相同的,為此,我們必須得重寫equals()方法,改變這個(gè)方法默認(rèn)的實(shí)現(xiàn)。
下面在Cat類里面重寫這個(gè)繼承下來(lái)的equals()方法:
class Cat { int color, weight, height; public Cat(int color, int weight, int height) { this.color = color; this.weight = weight; this.height = height; } /** * 這里是重寫相等從Object類繼承下來(lái)的equals()方法,改變這個(gè)方法默認(rèn)的實(shí)現(xiàn), * 通過(guò)我們自己定義的實(shí)現(xiàn)來(lái)判斷決定兩個(gè)對(duì)象在邏輯上是否相等。 * 這里我們定義如果兩只貓的color,weight,height都相同, * 那么我們就認(rèn)為這兩只貓?jiān)谶壿嬌鲜且荒R粯拥?,即這兩只貓是“相等”的。 */ public boolean equals(Object obj){ if (obj==null){ return false; } else{ /** * instanceof是對(duì)象運(yùn)算符。 * 對(duì)象運(yùn)算符用來(lái)測(cè)定一個(gè)對(duì)象是否屬于某個(gè)指定類或指定的子類的實(shí)例。 * 對(duì)象運(yùn)算符是一個(gè)組合單詞instanceof。 * 該運(yùn)算符是一個(gè)雙目運(yùn)算符,其左邊的表達(dá)式是一個(gè)對(duì)象,右邊的表達(dá)式是一個(gè)類, * 如果左邊的對(duì)象是右邊的類創(chuàng)建的對(duì)象,則運(yùn)算結(jié)果為true,否則為false。 */ if (obj instanceof Cat){ Cat c = (Cat)obj; if (c.color==this.color && c.weight==this.weight && c.height==this.height){ return true; } } } return false; } }
此時(shí)在再main方法里面執(zhí)行打印的命令:
public static void main(String[] args) { /** * 這里使用構(gòu)造方法Cat()在堆內(nèi)存里面new出了兩只貓, * 這兩只貓的color,weight,height都是一樣的, * 但c1和c2卻永遠(yuǎn)不會(huì)相等,這是因?yàn)閏1和c2分別為堆內(nèi)存里面兩只貓的引用對(duì)象, * 里面裝著可以找到這兩只貓的地址,但由于兩只貓?jiān)诙褍?nèi)存里面存儲(chǔ)在兩個(gè)不同的空間里面, * 所以c1和c2分別裝著不同的地址,因此c1和c2永遠(yuǎn)不會(huì)相等。 */ Cat c1 = new Cat(1, 1, 1); Cat c2 = new Cat(1, 1, 1); System.out.println("c1==c2的結(jié)果是:"+(c1==c2));//false System.out.println("c1.equals(c2)的結(jié)果是:"+c1.equals(c2));//true }
這一次得到的結(jié)果就與上次沒(méi)有重寫equals()方法時(shí)得到的結(jié)果就不一樣了:
“System.out.println(c1 == c2);”打印出來(lái)的結(jié)果依然是false,因?yàn)檫@里是比較兩個(gè)對(duì)象的引用里面的內(nèi)容,這兩個(gè)引用里面的內(nèi)容當(dāng)然不相等,而且永遠(yuǎn)不會(huì)相等,所以打印出來(lái)的結(jié)果肯定是false。
“System.out.println(c1.equals(c2));”打印出來(lái)的結(jié)果為true,因?yàn)槲覀冊(cè)贑at類里面重寫了equals()方法,改變了這個(gè)方法默認(rèn)的實(shí)現(xiàn),我們把方法的實(shí)現(xiàn)改為只要這個(gè)兩個(gè)對(duì)象是真的存在,并且都是貓,并且它們的顏色(color),身高(height)和體重(weight)都相同,那么這兩只貓?jiān)谶壿嬌暇褪且荒R粯拥?,是完全相同的兩只貓,即這兩只貓是“相等”的。所以這里打印出來(lái)的結(jié)果是true。
那么如何比較兩個(gè)字符串對(duì)象是否相等?
看下面的例子:
public class TestEquals { public static void main(String args[]){ String s1 = new String("hello"); String s2 = new String("hello"); System.out.println("s1 == s2的結(jié)果是:"+(s1 == s2));//false System.out.println("s1.equals(s2)的結(jié)果是:"+s1.equals(s2));//true } }
這一次是比較兩個(gè)字符串對(duì)象是否相等:
System.out.println(s1 == s2);
打印出來(lái)的結(jié)果依然是fase,因?yàn)檫@里比較的是s1和s2兩個(gè)字符串對(duì)象的引用,兩個(gè)對(duì)象的引用永遠(yuǎn)不會(huì)相等,所以打印出來(lái)的結(jié)果為false。
System.out.println(s1.equals(s2));
打印出來(lái)的結(jié)果為true,因?yàn)樵赟tring類里面重寫了從Object類繼承(所有的類都是從Object類繼承下來(lái),String類當(dāng)然也不例外,從父類繼承下來(lái)就擁有了父類的一切屬性與方法,所以Sting類里面也有equals()方法,并且還把這個(gè)繼承下來(lái)的equals()方法重寫了)下來(lái)的equals()方法,改變了這個(gè)方法默認(rèn)的實(shí)現(xiàn),
在String類里面是這樣重寫equals()方法的實(shí)現(xiàn)的:用當(dāng)前的這個(gè)字符串對(duì)象和指定的字符串對(duì)象比較,指定的字符串對(duì)象不能為空并且這個(gè)對(duì)象的字符序列和當(dāng)前這個(gè)字符串對(duì)象的字符串序列一樣,如果這些條件都滿足,那么這兩個(gè)字符串對(duì)象就是相等的。
因此這里的s2已經(jīng)滿足了條件,所以打印出來(lái)的結(jié)果是true。
以后在某一個(gè)類里面比較兩個(gè)對(duì)象是否相等時(shí),首先去API文檔里面查找這個(gè)類是否重寫了從Object類繼承下來(lái)的equals()方法。如果重寫了equals()方法,那么在比較兩個(gè)對(duì)象是否相等時(shí)調(diào)用的就是重寫以后的equals()方法,如果沒(méi)有重寫,那么調(diào)用時(shí)就是直接調(diào)用從Object類里面的繼承下來(lái)的那個(gè)equals()方法,并且采用equals()方法默認(rèn)的實(shí)現(xiàn)去比較兩個(gè)對(duì)象是否相等。因此每一個(gè)類都可以根據(jù)需要對(duì)從Object類繼承下來(lái)的equals()方法進(jìn)行重寫。
對(duì)于在API文檔里面找某個(gè)類,如果一個(gè)類不用引入包就可以直接使用,那么這個(gè)類肯定是在java.lang這個(gè)包里面,如這里的String類,直接就可以使用了,所以String類一定是在java.lang這個(gè)包里面。使用某個(gè)類時(shí)看這個(gè)類引入的是哪個(gè)包,然后就去這個(gè)包里面找這個(gè)類,不用引入包的類一定是位于java.lang里面,直接去java.lang里面找就可以了。
一般我們?cè)谠O(shè)計(jì)一個(gè)類時(shí),需要重寫父類的equals方法,在重寫這個(gè)方法時(shí),需要按照以下幾個(gè)規(guī)則設(shè)計(jì):
1、自反性:對(duì)任意引用值X,x.equals(x)的返回值一定為true.
2、對(duì)稱性:對(duì)于任何引用值x,y,當(dāng)且僅當(dāng)y.equals(x)返回值為true時(shí),x.equals(y)的返回值一定為true;
3、傳遞性:如果x.equals(y)=true, y.equals(z)=true,則x.equals(z)=true
4、一致性:如果參與比較的對(duì)象沒(méi)任何改變,則對(duì)象比較的結(jié)果也不應(yīng)該有任何改變
5、非空性:任何非空的引用值X,x.equals(null)的返回值一定為false
例如:
public class People { private String firstName; private String lastName; private int age; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; People other = (People) obj; if (age != other.age) return false; if (firstName == null) { if (other.firstName != null) return false; } else if (!firstName.equals(other.firstName)) return false; if (lastName == null) { if (other.lastName != null) return false; } else if (!lastName.equals(other.lastName)) return false; return true; } }
在這個(gè)例子中,我們規(guī)定一個(gè)人,如果姓、名和年齡相同,則就是同一個(gè)人。當(dāng)然你也可以再增加其他屬性,比如必須身份證號(hào)相同,才能判定為同一個(gè)人,則你可以在equals方法中增加對(duì)身份證號(hào)的判斷!
總結(jié):比較兩個(gè)對(duì)象是否相等,我們采用equals()方法,判斷兩個(gè)對(duì)象是否相等的條件是由我們重寫equals()方法的實(shí)現(xiàn)后定義的,這樣就可以比較靈活地使用equals()方法在不同的類里面比較位于同一類下的兩個(gè)對(duì)象是否相等了。
相關(guān)文章
java判斷某個(gè)點(diǎn)是否在所畫多邊形/圓形內(nèi)
這篇文章主要為大家詳細(xì)介紹了java判斷某個(gè)點(diǎn)是否在所畫多邊形或圓形內(nèi)的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05JavaWeb中HttpSession中表單的重復(fù)提交示例
這篇文章主要介紹了JavaWeb中HttpSession中表單的重復(fù)提交,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-03-03java 實(shí)現(xiàn)讀取txt文本數(shù)據(jù)并以數(shù)組形式一行一行取值
今天小編就為大家分享一篇java 實(shí)現(xiàn)讀取txt文本數(shù)據(jù)并以數(shù)組形式一行一行取值,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-07-07Java 淺復(fù)制和深復(fù)制的實(shí)例詳解
這篇文章主要介紹了Java 淺復(fù)制和深復(fù)制的實(shí)例詳解的相關(guān)資料,這里提供實(shí)例幫助大家學(xué)習(xí)理解這部分內(nèi)容,需要的朋友可以參考下2017-08-08單元測(cè)試 @mock與@SpringBootTest的使用
這篇文章主要介紹了單元測(cè)試 @mock與@SpringBootTest的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10