通過下面的例子掌握equals的用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
package cn.galc.test; public class TestEquals { public static void main(String[] args) { /** * 這里使用構造方法Cat()在堆內存里面new出了兩只貓, * 這兩只貓的color,weight,height都是一樣的, * 但c1和c2卻永遠不會相等,這是因為c1和c2分別為堆內存里面兩只貓的引用對象, * 里面裝著可以找到這兩只貓的地址,但由于兩只貓在堆內存里面存儲在兩個不同的空間里面, * 所以c1和c2分別裝著不同的地址,因此c1和c2永遠不會相等。 */ Cat c1 = new Cat( 1 , 1 , 1 ); Cat c2 = new Cat( 1 , 1 , 1 ); System.out.println( "c1==c2的結果是:" +(c1==c2)); //false System.out.println( "c1.equals(c2)的結果是:" +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; } } |
畫出內存分析圖分析c1和c2比較的結果
程序:
1
2
3
|
Cat c1 = new Cat( 1 , 1 , 1 ); Cat c2 = new Cat( 1 , 1 , 1 ); |
執行完之后內存之中的布局如下圖所示,
c1指向一個對象,c2也指向一個對象,c1和c2里面裝著的是這兩只Cat對象在堆內存里面存儲的地址,由于這兩只Cat對象分別位于不同的存儲空間,因此c1和c2里面裝著的地址肯定不相等,因此c1和c2這兩個引用對象也肯定不相等。因此執行:“System.out.println(c1==c2);”打印出來的結果肯定是false。因此你new出來了兩個對象,你放心,這兩個對象的引用永遠不一樣,一樣的話就會把其中一個給覆蓋掉了,這個可不成。c1是不是等于c2比較的是c1和c2這兩個引用里面裝著的內容,因為new出來的兩個對象的它們的引用永遠不一樣,因此c1和c2這兩個引用的內容也永遠不一樣,因此c1永遠不可能等于c2。因此通過比較兩個對象的引用是永遠無法使得兩個對象相等的,一模一樣的。
要想判斷兩個對象是否相等,不能通過比較兩個對象的引用是否相等,這是永遠都得不到相等的結果的,因為兩個對象的引用永遠不會相等,所以正確的比較方法是直接比較這兩個對象,比較這兩個對象的實質是不是一樣的,即這兩個對象里面的內容是不是相同的,通過比較這兩個對象的屬性值是否相同而決定這兩個對象是否相等。
Object類提供了一個equals()方法來比較兩個對象的內容是否相同,因此我們可以采用這個方法去比較兩個對象是否在邏輯上“相等”。如:c1.equals(c2);這里是調用從Object類繼承下來的equals()方法,通過查閱API文檔得到Object類里的equals方法的定義如下:
1
|
public boolean equals(Object obj) |
在Object這個類里面提供的Equals()方法默認的實現是比較當前對象的引用和你要比較的那個引用它們指向的是否是同一個對象,即和“c1==c2”這種寫法是一樣的,“c1.equals(c2)”與“c1==c2”是完全等價的。因此直接使用繼承下來的equals()方法也是無法直接比較兩個對象的內容是否相同的,為此,我們必須得重寫equals()方法,改變這個方法默認的實現。
下面在Cat類里面重寫這個繼承下來的equals()方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
class Cat { int color, weight, height; public Cat( int color, int weight, int height) { this .color = color; this .weight = weight; this .height = height; } /** * 這里是重寫相等從Object類繼承下來的equals()方法,改變這個方法默認的實現, * 通過我們自己定義的實現來判斷決定兩個對象在邏輯上是否相等。 * 這里我們定義如果兩只貓的color,weight,height都相同, * 那么我們就認為這兩只貓在邏輯上是一模一樣的,即這兩只貓是“相等”的。 */ public boolean equals(Object obj){ if (obj== null ){ return false ; } else { /** * instanceof是對象運算符。 * 對象運算符用來測定一個對象是否屬于某個指定類或指定的子類的實例。 * 對象運算符是一個組合單詞instanceof。 * 該運算符是一個雙目運算符,其左邊的表達式是一個對象,右邊的表達式是一個類, * 如果左邊的對象是右邊的類創建的對象,則運算結果為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 ; } } |
此時在再main方法里面執行打印的命令:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public static void main(String[] args) { /** * 這里使用構造方法Cat()在堆內存里面new出了兩只貓, * 這兩只貓的color,weight,height都是一樣的, * 但c1和c2卻永遠不會相等,這是因為c1和c2分別為堆內存里面兩只貓的引用對象, * 里面裝著可以找到這兩只貓的地址,但由于兩只貓在堆內存里面存儲在兩個不同的空間里面, * 所以c1和c2分別裝著不同的地址,因此c1和c2永遠不會相等。 */ Cat c1 = new Cat( 1 , 1 , 1 ); Cat c2 = new Cat( 1 , 1 , 1 ); System.out.println( "c1==c2的結果是:" +(c1==c2)); //false System.out.println( "c1.equals(c2)的結果是:" +c1.equals(c2)); //true } |
這一次得到的結果就與上次沒有重寫equals()方法時得到的結果就不一樣了:
“System.out.println(c1 == c2);”打印出來的結果依然是false,因為這里是比較兩個對象的引用里面的內容,這兩個引用里面的內容當然不相等,而且永遠不會相等,所以打印出來的結果肯定是false。
“System.out.println(c1.equals(c2));”打印出來的結果為true,因為我們在Cat類里面重寫了equals()方法,改變了這個方法默認的實現,我們把方法的實現改為只要這個兩個對象是真的存在,并且都是貓,并且它們的顏色(color),身高(height)和體重(weight)都相同,那么這兩只貓在邏輯上就是一模一樣的,是完全相同的兩只貓,即這兩只貓是“相等”的。所以這里打印出來的結果是true。
那么如何比較兩個字符串對象是否相等?
看下面的例子:
1
2
3
4
5
6
7
8
9
|
public class TestEquals { public static void main(String args[]){ String s1 = new String( "hello" ); String s2 = new String( "hello" ); System.out.println( "s1 == s2的結果是:" +(s1 == s2)); //false System.out.println( "s1.equals(s2)的結果是:" +s1.equals(s2)); //true } } |
這一次是比較兩個字符串對象是否相等:
1
|
System.out.println(s1 == s2); |
打印出來的結果依然是fase,因為這里比較的是s1和s2兩個字符串對象的引用,兩個對象的引用永遠不會相等,所以打印出來的結果為false。
1
|
System.out.println(s1.equals(s2)); |
打印出來的結果為true,因為在String類里面重寫了從Object類繼承(所有的類都是從Object類繼承下來,String類當然也不例外,從父類繼承下來就擁有了父類的一切屬性與方法,所以Sting類里面也有equals()方法,并且還把這個繼承下來的equals()方法重寫了)下來的equals()方法,改變了這個方法默認的實現,
在String類里面是這樣重寫equals()方法的實現的:用當前的這個字符串對象和指定的字符串對象比較,指定的字符串對象不能為空并且這個對象的字符序列和當前這個字符串對象的字符串序列一樣,如果這些條件都滿足,那么這兩個字符串對象就是相等的。
因此這里的s2已經滿足了條件,所以打印出來的結果是true。
以后在某一個類里面比較兩個對象是否相等時,首先去API文檔里面查找這個類是否重寫了從Object類繼承下來的equals()方法。如果重寫了equals()方法,那么在比較兩個對象是否相等時調用的就是重寫以后的equals()方法,如果沒有重寫,那么調用時就是直接調用從Object類里面的繼承下來的那個equals()方法,并且采用equals()方法默認的實現去比較兩個對象是否相等。因此每一個類都可以根據需要對從Object類繼承下來的equals()方法進行重寫。
對于在API文檔里面找某個類,如果一個類不用引入包就可以直接使用,那么這個類肯定是在java.lang這個包里面,如這里的String類,直接就可以使用了,所以String類一定是在java.lang這個包里面。使用某個類時看這個類引入的是哪個包,然后就去這個包里面找這個類,不用引入包的類一定是位于java.lang里面,直接去java.lang里面找就可以了。
一般我們在設計一個類時,需要重寫父類的equals方法,在重寫這個方法時,需要按照以下幾個規則設計:
1、自反性:對任意引用值X,x.equals(x)的返回值一定為true.
2、對稱性:對于任何引用值x,y,當且僅當y.equals(x)返回值為true時,x.equals(y)的返回值一定為true;
3、傳遞性:如果x.equals(y)=true, y.equals(z)=true,則x.equals(z)=true
4、一致性:如果參與比較的對象沒任何改變,則對象比較的結果也不應該有任何改變
5、非空性:任何非空的引用值X,x.equals(null)的返回值一定為false
例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
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 ; } } |
在這個例子中,我們規定一個人,如果姓、名和年齡相同,則就是同一個人。當然你也可以再增加其他屬性,比如必須身份證號相同,才能判定為同一個人,則你可以在equals方法中增加對身份證號的判斷!
總結:比較兩個對象是否相等,我們采用equals()方法,判斷兩個對象是否相等的條件是由我們重寫equals()方法的實現后定義的,這樣就可以比較靈活地使用equals()方法在不同的類里面比較位于同一類下的兩個對象是否相等了。