鍍金池/ 教程/ Java/ equals() 方法總結(jié)
Java 集合細(xì)節(jié)(四):保持 compareTo 和 equals 同步
Iterator
使用序列化實(shí)現(xiàn)對(duì)象的拷貝
fail-fast 機(jī)制
關(guān)鍵字 final
Vector
HashTable
Java 集合細(xì)節(jié)(一):請(qǐng)為集合指定初始容量
強(qiáng)制類(lèi)型轉(zhuǎn)換
數(shù)組之一:認(rèn)識(shí) JAVA 數(shù)組
Java 集合細(xì)節(jié)(三):subList 的缺陷
hashCode
ArrayList
數(shù)組之二
List 總結(jié)
LinkedList
Java 提高篇(九)—–實(shí)現(xiàn)多重繼承
Java 的四舍五入
關(guān)鍵字 static
理解 Java 的三大特性之多態(tài)
抽象類(lèi)與接口
集合大家族
異常(二)
Java 集合細(xì)節(jié)(二):asList 的缺陷
Map 總結(jié)
TreeSet
equals() 方法總結(jié)
Java 提高篇(十)—–詳解匿名內(nèi)部類(lèi)
HashMap
Stack
詳解內(nèi)部類(lèi)
TreeMap
異常(一)
詳解 Java 定時(shí)任務(wù)
HashSet
字符串
理解 Java 的三大特性之繼承
理解 Java 的三大特性之封裝
代碼塊

equals() 方法總結(jié)

equals()

超類(lèi) Object 中有這個(gè) equals() 方法,該方法主要用于比較兩個(gè)對(duì)象是否相等。該方法的源碼如下:


    public boolean equals(Object obj) {
        return (this == obj);
        }

我們知道所有的對(duì)象都擁有標(biāo)識(shí)(內(nèi)存地址)和狀態(tài)(數(shù)據(jù)),同時(shí)“==”比較兩個(gè)對(duì)象的的內(nèi)存地址,所以說(shuō)使用 Object 的 equals() 方法是比較兩個(gè)對(duì)象的內(nèi)存地址是否相等,即若 object1.equals(object2) 為 true,則表示 equals1 和 equals2 實(shí)際上是引用同一個(gè)對(duì)象。雖然有時(shí)候 Object 的 equals() 方法可以滿足我們一些基本的要求,但是我們必須要清楚我們很大部分時(shí)間都是進(jìn)行兩個(gè)對(duì)象的比較,這個(gè)時(shí)候 Object 的 equals() 方法就不可以了,實(shí)際上 JDK 中,String、Math 等封裝類(lèi)都對(duì) equals() 方法進(jìn)行了重寫(xiě)。下面是 String 的 equals() 方法:


    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = count;
            if (n == anotherString.count) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = offset;
            int j = anotherString.offset;
            while (n-- != 0) {
                if (v1[i++] != v2[j++])
                return false;
            }
            return true;
            }
        }
        return false;
        }

對(duì)于這個(gè)代碼段:if (v1[i++] != v2[j++])return false;我們可以非常清晰的看到 String 的 equals() 方法是進(jìn)行內(nèi)容比較,而不是引用比較。至于其他的封裝類(lèi)都差不多。

在 Java 規(guī)范中,它對(duì) equals() 方法的使用必須要遵循如下幾個(gè)規(guī)則:

equals 方法在非空對(duì)象引用上實(shí)現(xiàn)相等關(guān)系:

1、自反性:對(duì)于任何非空引用值 x,x.equals(x) 都應(yīng)返回 true。

2、對(duì)稱(chēng)性:對(duì)于任何非空引用值 x 和 y,當(dāng)且僅當(dāng) y.equals(x) 返回 true 時(shí),x.equals(y) 才應(yīng)返回 true。

3、傳遞性:對(duì)于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 應(yīng)返回 true。

4、一致性:對(duì)于任何非空引用值 x 和 y,多次調(diào)用 x.equals(y) 始終返回 true 或始終返回 false,前提是對(duì)象上 equals 比較中所用的信息沒(méi)有被修改。

5、對(duì)于任何非空引用值 x,x.equals(null) 都應(yīng)返回 false。

對(duì)于上面幾個(gè)規(guī)則,我們?cè)谑褂玫倪^(guò)程中最好遵守,否則會(huì)出現(xiàn)意想不到的錯(cuò)誤。

在 java 中進(jìn)行比較,我們需要根據(jù)比較的類(lèi)型來(lái)選擇合適的比較方式:

1) 對(duì)象域,使用 equals 方法 。

2) 類(lèi)型安全的枚舉,使用 equals 或== 。

3) 可能為 null 的對(duì)象域 : 使用 == 和 equals 。

4) 數(shù)組域 : 使用 Arrays.equals 。

5)除 float 和 double 外的原始數(shù)據(jù)類(lèi)型 : 使用 == 。

6) float 類(lèi)型: 使用 Float.foatToIntBits 轉(zhuǎn)換成 int 類(lèi)型,然后使用==。

7) double 類(lèi)型: 使用 Double.doubleToLongBit 轉(zhuǎn)換成 long 類(lèi)型,然后使用==。

至于6)、7)為什么需要進(jìn)行轉(zhuǎn)換,我們可以參考他們相應(yīng)封裝類(lèi)的 equals() 方法,下面的是 Float 類(lèi)的:


    public boolean equals(Object obj) {
        return (obj instanceof Float)
               && (floatToIntBits(((Float)obj).value) ==   floatToIntBits(value));
        }

原因嘛,里面提到了兩點(diǎn):


    However, there are two exceptions:
    If f1 and f2 both represent
    Float.NaN, then the equals method returns
    true, even though Float.NaN==Float.NaN
    has the value false.
    If <code>f1 represents +0.0f while
    f2 represents -0.0f, or vice
    versa, the equal test has the value
    false, even though 0.0f==-0.0f
    has the value true.

在 equals() 中使用 getClass 進(jìn)行類(lèi)型判斷

我們?cè)诟矊?xiě) equals() 方法時(shí),一般都是推薦使用 getClass 來(lái)進(jìn)行類(lèi)型判斷,不是使用 instanceof。我們都清楚 instanceof 的作用是判斷其左邊對(duì)象是否為其右邊類(lèi)的實(shí)例,返回 boolean 類(lèi)型的數(shù)據(jù)。可以用來(lái)判斷繼承中的子類(lèi)的實(shí)例是否為父類(lèi)的實(shí)現(xiàn)。注意后面這句話:可以用來(lái)判斷繼承中的子類(lèi)的實(shí)例是否為父類(lèi)的實(shí)現(xiàn),正是這句話在作怪。我們先看如下實(shí)例(摘自《高質(zhì)量代碼 改善 Java 程序的 151 個(gè)建議》)。

父類(lèi):Person


    public class Person {
        protected String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Person(String name){
            this.name = name;
        }

        public boolean equals(Object object){
            if(object instanceof Person){
                Person p = (Person) object;
                if(p.getName() == null || name == null){
                    return false;
                }
                else{
                    return name.equalsIgnoreCase(p.getName ());
                }
            }
            return false;
       }
    }

子類(lèi):Employee


    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;
        }

        /**
         * 重寫(xiě)equals()方法
         */
        public boolean equals(Object object){
            if(object instanceof Employee){
                Employee e = (Employee) object;
                return super.equals(object) && e.getId() == id;
            }
            return false;
        }
    }

上面父類(lèi) Person 和子類(lèi) Employee 都重寫(xiě)了 equals(),不過(guò) Employee 比父類(lèi)多了一個(gè)id屬性。測(cè)試程序如下:


    public class Test {
        public static void main(String[] args) {
            Employee e1 = new Employee("chenssy", 23);
            Employee e2 = new Employee("chenssy", 24);
            Person p1 = new Person("chenssy");

            System.out.println(p1.equals(e1));
            System.out.println(p1.equals(e2));
            System.out.println(e1.equals(e2));
        }
    }

上面定義了兩個(gè)員工和一個(gè)普通人,雖然他們同名,但是他們肯定不是同一人,所以按理來(lái)說(shuō)輸出結(jié)果應(yīng)該全部都是 false,但是事與愿違,結(jié)果是:true、true、false。

對(duì)于那 e1!=e2 我們非常容易理解,因?yàn)樗麄儾粌H需要比較 name,還需要比較 ID。但是 p1 即等于 e1 也等于 e2,這是非常奇怪的,因?yàn)?e1、e2 明明是兩個(gè)不同的類(lèi),但為什么會(huì)出現(xiàn)這個(gè)情況?首先 p1.equals(e1),是調(diào)用 p1 的 equals 方法,該方法使用 instanceof 關(guān)鍵字來(lái)檢查 e1 是否為 Person 類(lèi),這里我們?cè)倏纯?instanceof:判斷其左邊對(duì)象是否為其右邊類(lèi)的實(shí)例,也可以用來(lái)判斷繼承中的子類(lèi)的實(shí)例是否為父類(lèi)的實(shí)現(xiàn)。他們兩者存在繼承關(guān)系,肯定會(huì)返回 true 了,而兩者 name 又相同,所以結(jié)果肯定是 true。

所以出現(xiàn)上面的情況就是使用了關(guān)鍵字 instanceof,這是非常容易“專(zhuān)空子”的。故在覆寫(xiě) equals 時(shí)推薦使用 getClass 進(jìn)行類(lèi)型判斷。而不是使用 instanceof。

鞏固基礎(chǔ),提高技術(shù),不懼困難,攀登高峰?。。。。?!