問:euqals()函數是用來作什麼的? java
答:equals()函數能夠用來檢查一個對象與調用這個equals()的這個對象是否相等。 數組
問:爲何不用「==」運算符來判斷兩個對象是否相等呢? ide
答:雖然「==」運算符能夠比較兩個數據是否相等,可是要來比較對象的話,恐怕達不到預期的結果。就是說,「==」經過是否引用了同一個對象來判斷兩個對象是否相等,這被稱爲「引用相等」。這個運算符不能經過比較兩個對象的內容來判斷它們是否是邏輯上的相等。 函數
問:使用Object類的equals()方法能夠用來作什麼樣的對比? flex
答:Object類默認的eqauls()函數進行比較的依據是:調用它的對象和傳入的對象的引用是否相等。也就是說,默認的equals()進行的是引用比較。若是兩個引用是相同的,equals()函數返回true;不然,返回false. ui
問:覆蓋equals()函數的時候要遵照那些規則? this
答:覆蓋equals()函數的時候須要遵照的規則在Oracle官方的文檔中都有申明: spa
問:能提供一個正確覆蓋equals()的示例嗎? 對象
答:固然,請看代碼清單8。 繼承
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
classEmployee
{
privateString name;
privateintage;
Employee(String name,intage)
{
this.name = name;
this.age = age;
}
@Override
publicbooleanequals(Object o)
{
if(!(oinstanceofEmployee))
returnfalse;
Employee e = (Employee) o;
returne.getName().equals(name) && e.getAge() == age;
}
String getName()
{
returnname;
}
intgetAge()
{
returnage;
}
}
publicclassEqualityDemo
{
publicstaticvoidmain(String[] args)
{
Employee e1 =newEmployee("John Doe",29);
Employee e2 =newEmployee("Jane Doe",33);
Employee e3 =newEmployee("John Doe",29);
Employee e4 =newEmployee("John Doe",27+2);
// 驗證自反性。
System.out.printf("Demonstrating reflexivity...%n%n");
System.out.printf("e1.equals(e1): %b%n", e1.equals(e1));
// 驗證對稱性。
System.out.printf("%nDemonstrating symmetry...%n%n");
System.out.printf("e1.equals(e2): %b%n", e1.equals(e2));
System.out.printf("e2.equals(e1): %b%n", e2.equals(e1));
System.out.printf("e1.equals(e3): %b%n", e1.equals(e3));
System.out.printf("e3.equals(e1): %b%n", e3.equals(e1));
System.out.printf("e2.equals(e3): %b%n", e2.equals(e3));
System.out.printf("e3.equals(e2): %b%n", e3.equals(e2));
// 驗證傳遞性。
System.out.printf("%nDemonstrating transitivity...%n%n");
System.out.printf("e1.equals(e3): %b%n", e1.equals(e3));
System.out.printf("e3.equals(e4): %b%n", e3.equals(e4));
System.out.printf("e1.equals(e4): %b%n", e1.equals(e4));
// 驗證一致性。
System.out.printf("%nDemonstrating consistency...%n%n");
for(inti =0; i <5; i++)
{
System.out.printf("e1.equals(e2): %b%n", e1.equals(e2));
System.out.printf("e1.equals(e3): %b%n", e1.equals(e3));
}
// 驗證傳入非空集合時,返回值爲false。
System.out.printf("%nDemonstrating null check...%n%n");
System.out.printf("e1.equals(null): %b%n", e1.equals(null));
}
}
|
代碼清單8聲明瞭一個包含名字、年齡成員變量的Employee對象。這個對象覆蓋了equals()函數來對Employee對象進行適當的對比。
ps:覆蓋hashCode()函數
當覆蓋equals()函數的時候,就至關於覆蓋了hashCode()函數,我將在下篇文章討論hashCode()的時候詳細說明。
equals()函數首先要檢查傳入的確實是一個Employee對象。若是不是,返回false。這個檢查是靠instanceof運算來判斷的,當傳入null值的時候,一樣也返回false。所以,遵照了「對於任意的非空引用值x,x.equals(null)必須返回假」這個規則。
這樣,咱們就保證了傳入的對象是Employee類型。由於以前的instanceof判斷保證了傳入值必須是Employee類型的對象,因此在這裏咱們就沒必要擔憂拋出ClassCastException異常了。接下來,equals()方法對兩個對象的name和age的值進行了比較。
編譯(javac EqualityDemo.java)並運行(java EqualityDemo)代碼清單8,你能夠看到如下輸出結果:
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
|
Demonstrating reflexivity...
e1.equals(e1):true
Demonstrating symmetry...
e1.equals(e2):false
e2.equals(e1):false
e1.equals(e3):true
e3.equals(e1):true
e2.equals(e3):false
e3.equals(e2):false
Demonstrating transitivity...
e1.equals(e3):true
e3.equals(e4):true
e1.equals(e4):true
Demonstrating consistency...
e1.equals(e2):false
e1.equals(e3):true
e1.equals(e2):false
e1.equals(e3):true
e1.equals(e2):false
e1.equals(e3):true
e1.equals(e2):false
e1.equals(e3):true
e1.equals(e2):false
e1.equals(e3):true
Demonstrating null check...
e1.equals(null):false
|
equals()和繼承
當Employee類被繼承的時候,代碼清單8就存在一些問題。例如,SaleRep類繼承了Employee類,這個類中也有基於字符串類型的變量,equals()能夠對其進行比較。假設你建立的Employee對象和SaleRep對象都有相同的「名字」和「年齡」。可是,SaleRep中仍是添加了一些內容。
假設你在Employee對象中調用equals()方法而且傳入了一個SaleRep對象。因爲SaleRep對象繼承了Employee,也是一種Employee的對象,instanceof判斷會經過,而且執行equals()方法來判斷名字和年齡。由於這兩個對象有徹底相同的名字和年齡,因此equals()方法返回true。若是拿SaleRep對象中Employee的部分來和Employee比較的話,返回true值是正確的,可是,若是拿整個SaleRep對象來和Employee對象比較,返回true值就不妥了。
如今假設在SaleRep對象中調用equals()方法並將Employee傳入。由於Employee不是SaleRep類型的對象(不然的話,你能夠訪問Employee對象中不存在的Region域,這會致使虛擬機崩潰),沒法經過instanceof判斷,equals()方法返回false。綜上,equals()在一種判斷中爲true卻在另外一判斷中爲false,違背了「對稱性原則」。
Joshua Bloch在《Effective Java Programming Language Guide》第七版中指出:咱們沒法擴展可被實例化的類(例如Employee)並向其中增長一個域(如Region域),而同時維持equals()方法的對稱性。儘管也有辦法來維持對稱性,但代價是破壞傳遞性。Bloch指出解決這個問題須要在繼承上支持組合:不是讓SaleRep來擴展Employee,SaleRep應該引用一個私有的Employee值。得到更多信息能夠參考Bloch的書。
問:可使用equals()函數來判斷兩個數組是否相等嗎?
答:能夠調用equals()函數來比較數組的引用是否相等。可是,因爲在數組對象中沒法覆蓋equals(),因此只能對數組的引用進行比較,由於不是很經常使用。參見代碼清單9。
1
2
3
4
5
6
7
8
9
10
11
|
publicclassEqualityDemo
{
publicstaticvoidmain(String[] args)
{
intx[] = {1,2,3};
inty[] = {1,2,3};
System.out.printf("x.equals(x): %b%n", x.equals(x));
System.out.printf("x.equals(y): %b%n", x.equals(y));
}
}
|
代碼清單9的main()函數中聲明瞭一對類型與內容徹底相等的數組。而後嘗試對第一個數組和它本身、第一個數組和第二個數組分別進行比較。因爲equals()對數組來講比較的僅僅是引用,而不比較內容,因此x.equals(x)返回true(由於自反性——一個對象與它本身相等),可是x.equals(y)返回false。
編譯(javac EqualityDemo.java) 並運行(java EqualityDemo)代碼清單9,你將會看到如下輸出結果:
1
2
|
x.equals(x):true
x.equals(y):false
|
若是你想要比較的是兩個數組的內容,也不要絕望。 可使用java.util.Arrays 類中聲明的 static boolean deepEquals(Object[] a1, Object[] a2) 方法來實現。代碼清單10演示了這個方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
importjava.util.Arrays;
publicclassEqualityDemo
{
publicstaticvoidmain(String[] args)
{
Integer x[] = {1,2,3};
Integer y[] = {1,2,3};
Integer z[] = {3,2,1};
System.out.printf("x.equals(x): %b%n", Arrays.deepEquals(x, x));
System.out.printf("x.equals(y): %b%n", Arrays.deepEquals(x, y));
System.out.printf("x.equals(z): %b%n", Arrays.deepEquals(x, z));
}
}
|
因爲deepEquals()方法要求傳入的數組元素必須是對象,因此以前在代碼清單9中的元素類型要從int[]改成Integer[]。Java語言的自動封裝特性會把integer常量轉換成Integer對象存放在數組中。接下來要將數組傳入到deepEquals()就是小事一樁了。
編譯(javac EqualityDemo.java)並運行(java EqualityDemo)代碼清單10,你將看到如下輸出結果。
1
2
3
|
x.equals(x):true
x.equals(y):true
x.equals(z):false
|
用deepEquals()方法比較的相等是「深度」的相等:這要求每一個元素對象所包含的的成員、對象相等。成員對象若是還包含了對象,也要相等,以此類推,纔算是「相等」(另外,兩個空的數組引用也是「深度」的相等,所以Arrays.deepEquals(null, null)返回true)。