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

爲何equals()方法要重寫?html

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

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

怎樣重寫equals()方法?ide

重寫equals方法的要求:
一、自反性:對於任何非空引用x,x.equals(x)應該返回true。
二、對稱性:對於任何引用x和y,若是x.equals(y)返回true,那麼y.equals(x)也應該返回true。
三、傳遞性:對於任何引用x、y和z,若是x.equals(y)返回true,y.equals(z)返回true,那麼x.equals(z)也應該返回true。
四、一致性:若是x和y引用的對象沒有發生變化,那麼反覆調用x.equals(y)應該返回一樣的結果。
五、非空性:對於任意非空引用x,x.equals(null)應該返回false。this

 

一、自反性原則spa

    在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,可是實際結果:    這裏考慮了字符串空格的問題,去除先後的空格。code

    是否包含張三:truehtm

    是否包含張三: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方法就能夠解決。

二、對稱性原則

    上面這個例子,還並非很好,若是咱們傳入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;
    }

三、傳遞性原則  

如今咱們有一個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;
    }


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

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

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

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

相關文章
相關標籤/搜索