java重寫equals方法需要注意的幾點


為什么equals()方法要重寫?

判斷兩個對象在邏輯上是否相等,如根據類的成員變量來判斷兩個類的實例是否相等,而繼承Object中的equals方法只能判斷兩個引用變量是否是同一個對象。這樣我們往往需要重寫equals()方法。

我們向一個沒有重復對象的集合中添加元素時,集合中存放的往往是對象,我們需要先判斷集合中是否存在已知對象,這樣就必須重寫equals方法。

怎樣重寫equals()方法?

重寫equals方法的要求:
1、自反性:對於任何非空引用x,x.equals(x)應該返回true。
2、對稱性:對於任何引用x和y,如果x.equals(y)返回true,那么y.equals(x)也應該返回true。
3、傳遞性:對於任何引用x、y和z,如果x.equals(y)返回true,y.equals(z)返回true,那么x.equals(z)也應該返回true。
4、一致性:如果x和y引用的對象沒有發生變化,那么反復調用x.equals(y)應該返回同樣的結果。
5、非空性:對於任意非空引用x,x.equals(null)應該返回false。

 

1、自反性原則

    在JavaBean中,經常會覆寫equals方法,從而根據實際業務情況來判斷兩個對象是否相等,比如我們寫一個person類,根據姓名來判斷兩個person類實例對象是否相等。代碼如下:

public class Person {
    private String name;
 
    public Person(String name){
        this.name = name;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Person) {
            Person person= (Person) obj;
            return name.equalsIgnoreCase(person.getName().trim());
        }
        return false;
    }
    public static void main(String[] args){
        Person p1=new Person("張三");
        Person p2=new Person("張三    ");
        List<Person> list = new ArrayList<Person>();
        list.add(p1);
        list.add(p2);
        System.out.println("是否包含張三:"+list.contains(p1));
        System.out.println("是否包含張三:"+list.contains(p2));
    }
}

    list中含有這個生成的person對象,結果應該為true,但是實際結果:    這里考慮了字符串空格的問題,去除前后的空格。

    是否包含張三:true

    是否包含張三:false

    第二個為什么會是false呢?原因在於list中檢查是否含有元素時是通過調用對象的equals方法來判斷的,也就是說 contains(p2)傳遞進去會依次執行p2.equals(p1)、p2.equals(p2),只要一個返回true,結果就是true。但是這里p2.equals(p2)返回的是false?由於我們對字符前后進行了空格的切割造成p2.equals(p2)的比較實際上是:“張三   ”.equals(“張三”),一個有空格,一個沒有空格就出錯了。

    這個違背了equals的自反性原則:對於任何非空引用x,x.equals(x)應該返回true。

    這里只要去掉trim方法就可以解決。

2、對稱性原則

    上面這個例子,還並不是很好,如果我們傳入null值,會怎么樣呢?增加一條語句:Person p2=new Person(null);

結果:

1
2
是否包含張三:true
Exception in thread "main" java.lang.NullPointerException

原因在執行p2.equals(p1)時,由於p2的name是一個null值,所以調用name.equalsIgnoreCase()方法時就會報空指針異常。

這是在覆寫equals方法時沒有遵循對稱性原則:對於任何應用x,y的情形,如果想x.equals(y)返回true,那么y.equals(x),也應該返回true。

應該在equals方法里加上是否為null值的判斷:

@Override
    public boolean equals(Object obj) {
        if (obj instanceof Person) {
            Person person= (Person) obj;
            if (person.getName() == null || name == null) {
                return false;
            }else{
                return name.equalsIgnoreCase(person.getName());
            }
        }
        return false;
    }

3、傳遞性原則  

現在我們有一個Employee類繼承自person類:

public class Employee extends Person{
    private int id;
 
 
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public Employee(String name,int id) {
        super(name);
        this.id = id;
        // TODO Auto-generated constructor stub
    }
    @Override
    public boolean equals(Object obj) {
        if(obj instanceof Employee){
            Employee e = (Employee)obj;
            return super.equals(obj) && e.getId() == id;
        }
        return super.equals(obj);
    }
 
    public static void main(String[] args){
        Employee e1=new Employee("張三",12);
        Employee e2=new Employee("張三",123);
        Person p1 = new Person("張三");
 
        System.out.println(p1.equals(e1));
        System.out.println(p1.equals(e2));
        System.out.println(e1.equals(e2));
    }
}

  只有在name和ID都相同的情況下才是同一個員工,避免同名同姓的。在main里定義了,兩個員工和一個社會閑雜人員,雖然同名同姓但肯定不是同一個人。運行結果應該三個都是false才對。但是:

true

true

false

    p1盡然等於e1,也等於e2,不是同一個類的實例也相等了?因為p1.equals(e1)是調用父類的equals方法進行判斷的它使用instanceof關鍵字檢查e1是否是person的實例,由於employee和person是繼承關系,結果就是true了。但是放過來就不成立,e1,e2就不等於p1,這也是違反對稱性原則的一個典型案例。

   e1竟然不等於e2?e1.equals(e2)調用的是Employee的equals方法,不僅要判斷姓名相同還有判斷工號相同,兩者的工號不同,不相等時對的。但是p1等於e1,也等於e2,e1卻不等於e2,這里就存在矛盾,等式不傳遞是因為違反了equals的傳遞性原則:對於實例對象x、y、z;如果x.equals(y)返回true,y.equals(z)返回true,那么x.equals(z)也應該返回true。

    上述情況會發生是因為父類使用instanceof關鍵字(是否是這個特定類或者是它的子類的一個實例),用來判斷是否是一個類的實例對象的,這很容易讓子類“鑽空子”。想要解決也很簡單,使用getClass進行類型的判斷,person類的equals方法修改如下:

@Override
    public boolean equals(Object obj) {
        if (obj != null && obj.getClass() == this.getClass()) {
            Person person= (Person) obj;
            if (person.getName() == null || name == null) {
                return false;
            }else{
                return name.equalsIgnoreCase(person.getName());
            }
        }
        return false;
    }


4、必須覆寫hashCode方法
這樣結果就是三個false。

覆寫equals方法就必須覆寫hashCode方法,這是Javaer都知道的。原因就是HashMap的底層處理機制是以數組的方式保存map條目的,這其中的關鍵是這個數組下標的處理機制:依據傳入元素的hashCode方法的返回值決定其數組的下標,如果該數組位置上已經有了map條目,且與傳入的鍵值相等則不處理,若不相等則覆蓋;如果數組位置沒有條目,則插入,並加入到map條目的鏈表中。同理檢查鍵是否存在也是根據哈希嗎確定文職,然后遍歷查找鍵值的。

那么對象的hashCode方法返回的是什么呢?他是一個對象的哈希碼,是有Object類的本地方法生成的,確保每個對象有一個哈希碼。

具體的可以看另一篇博文:http://www.cnblogs.com/silence-hust/p/4510574.html

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM