Object類分析(equals和hashCode知識(shí)點(diǎn))

所有的java對(duì)象都隱式繼承了Object類對(duì)象。所有的java對(duì)象都擁有Object默認(rèn)的方法。

public final native Class<?>getClass();//返回字節(jié)碼文件對(duì)象 java反射實(shí)現(xiàn)方式之一
 public native int hashCode();
public boolean equals(Object obj)
protected Object clone();
public String toString();
public final void notify();
public final void notifyAll();
public final void wait(long timeout);
protected void finalized();

1.equals和hashCode

查看完文檔以后我們查看源碼,發(fā)現(xiàn)hashCode是由native關(guān)鍵字修飾,equals方法則是直接使用==比較了內(nèi)存地址。

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

equals和hashCode用來(lái)標(biāo)識(shí)對(duì)象,可以在非排序的情況下比較兩個(gè)對(duì)象是否相等(對(duì)象數(shù)組中可以使用比較器)。

那么我們仔細(xì)關(guān)注一下hashCode源碼所給出的注釋

   /**
     * Returns a hash code value for the object. This method is
     * supported for the benefit of hash tables such as those provided by
     * {@link java.util.HashMap}.
     * <p>
     * The general contract of {@code hashCode} is:
     * <ul>
     * <li>Whenever it is invoked on the same object more than once during
     *     an execution of a Java application, the {@code hashCode} method
     *     must consistently return the same integer, provided no information
     *     used in {@code equals} comparisons on the object is modified.
     *     This integer need not remain consistent from one execution of an
     *     application to another execution of the same application.
     * <li>If two objects are equal according to the {@code equals(Object)}
     *     method, then calling the {@code hashCode} method on each of
     *     the two objects must produce the same integer result.
     * <li>It is <em>not</em> required that if two objects are unequal
     *     according to the {@link java.lang.Object#equals(java.lang.Object)}
     *     method, then calling the {@code hashCode} method on each of the
     *     two objects must produce distinct integer results.  However, the
     *     programmer should be aware that producing distinct integer results
     *     for unequal objects may improve the performance of hash tables.
     * </ul>
     * <p>
     * As much as is reasonably practical, the hashCode method defined by
     * class {@code Object} does return distinct integers for distinct
     * objects. (This is typically implemented by converting the internal
     * address of the object into an integer, but this implementation
     * technique is not required by the
     * Java&trade; programming language.)
  1. 返回一個(gè)哈希值給對(duì)象,這個(gè)方法有益于底層結(jié)構(gòu)是哈希表的結(jié)構(gòu)對(duì)象,例如HashMap
  2. 如果兩個(gè)對(duì)象的equals的結(jié)果是相等的,則調(diào)用hashCode返回的int也必須是相同的
  3. 如果兩個(gè)對(duì)象的equals不相等,hashCode可以不相等,但是如果equasl不相等,hashCode也不相等的話,有利于提高散列性能。(Map集合和Set集合底層判斷重復(fù)的時(shí)候,先判斷hashCode是否相等,如相等,再判斷key的equals是否相等,一旦短路與&&生效,會(huì)大大提高程序執(zhí)行效率。)
  4. hashCode默認(rèn)是由對(duì)象的地址轉(zhuǎn)換而來(lái),同時(shí)根據(jù)不同的對(duì)象轉(zhuǎn)成不同的hash值(但這種實(shí)現(xiàn)不是java語(yǔ)言要求的 所以我們常常重寫(xiě)它)

適用場(chǎng)景:我們?cè)谶m用自定義對(duì)象作為Map/Set鍵時(shí),為了區(qū)分不同對(duì)象,必須重寫(xiě)hashCode和equals。

延申:equals和==的區(qū)別:
對(duì)于基本類型而言,==比較的是內(nèi)容。
對(duì)于引用數(shù)據(jù)類型而言,==比較的是地址。
對(duì)于引用數(shù)據(jù)類型(String,Integer,date等),重寫(xiě)了equals和hashCode的,比較的就是內(nèi)容。
對(duì)于引用數(shù)據(jù)類型,沒(méi)有重寫(xiě)equals和hashCode的,適用的還是Object的equals方法,比較的是地址。

再延申:那我們就去看一看String重寫(xiě)的equals源碼吧:

 public boolean equals(Object anObject) {
        if (this == anObject) {//先判斷地址是否相等,地址相等直接返回
            return true;
        }
        if (anObject instanceof String) {//判斷是否是String類,為了向下轉(zhuǎn)型的安全
            String anotherString = (String)anObject;
            int n = value.length;//value就是本String轉(zhuǎn)化成的字符數(shù)組
            if (n == anotherString.value.length) {//比較兩者的字符數(shù)組是否相等
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {//從前往后一個(gè)字符一個(gè)字符比較
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

OK,那么我們知道了,String先是判斷兩個(gè)對(duì)象的地址是否相等,然后向下轉(zhuǎn)型,再轉(zhuǎn)成字符數(shù)組,從后向前遍歷比較。

2.toString

    /**
     * Returns a string representation of the object. In general, the
     * {@code toString} method returns a string that
     * "textually represents" this object. The result should
     * be a concise but informative representation that is easy for a
     * person to read.
     * It is recommended that all subclasses override this method.
     * <p>
     * The {@code toString} method for class {@code Object}
     * returns a string consisting of the name of the class of which the
     * object is an instance, the at-sign character `{@code @}', and
     * the unsigned hexadecimal representation of the hash code of the
     * object. In other words, this method returns a string equal to the
     * value of:
     * <blockquote>
     * <pre>
     * getClass().getName() + '@' + Integer.toHexString(hashCode())
     * </pre></blockquote>
     *
     * @return  a string representation of the object.
     */
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
  1. 用文本方式標(biāo)識(shí)一個(gè)對(duì)象
  2. Object默認(rèn)返回的是 字節(jié)碼文件的名稱+@+一個(gè)內(nèi)存地址的int映射

clone()方法

一般java的賦值是復(fù)制對(duì)象的引用(=),(類初始化狀態(tài)不一)淺拷貝。要實(shí)現(xiàn)深拷貝,(連成員變量初始化狀態(tài)都一樣)成員變量都拷貝出去一份(如果是可變的引用),因而"="是屬于淺拷貝。
所以深拷貝是成員變量(如果是可變的引用)都復(fù)制一份,淺拷貝則是不復(fù)制成員變量。兩者初始化程度不一。

clone用法:

  1. 克隆的對(duì)象要實(shí)現(xiàn)Cloneable接口
  2. 重寫(xiě)clone方法,最好修飾成public

范例:淺拷貝Time

public class Time implements Cloneable{

    //可變成員變量
    private Date date;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

范例:深拷貝Time

public class Time implements Cloneable{

    //可變成員變量
    private Date date;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        //向下轉(zhuǎn)型 拷貝Time對(duì)象
        Time time=(Time)super.clone();

        //拷貝可變的成員變量
        time.date=(Date)date.clone();

        //返回拷貝的對(duì)象
        return time;
    }
}

wait和notify方法

這是線程間通信的API

  1. 無(wú)論是wait、notify還是notifyAll()都需要由監(jiān)聽(tīng)器對(duì)象(鎖對(duì)象)來(lái)進(jìn)行調(diào)用,他們都是在同步代碼塊中調(diào)用的,否則會(huì)拋出異常!
  2. notify()喚醒的是在等待隊(duì)列的某個(gè)線程(不確定會(huì)喚醒哪個(gè)),notifyAll()喚醒的是等待隊(duì)列所有線程
  3. 導(dǎo)致wait()的線程被喚醒可以有4種情況
  • 該線程被中斷
  • wait()時(shí)間到了
  • 被notify()喚醒
  • 被notifyAll()喚醒
  1. 調(diào)用wait()的線程會(huì)釋放掉鎖

一些面試題:
為什么wait和notify在Object方法上?
因?yàn)殒i是對(duì)象鎖,讓線程等待某個(gè)對(duì)象的鎖,應(yīng)該由對(duì)象來(lái)操作

notify方法調(diào)用后,會(huì)發(fā)生什么?
會(huì)喚醒等待隊(duì)列的某個(gè)線程
注:并不會(huì)立刻喚醒,會(huì)等notify的synchronized代碼塊執(zhí)行完之后才會(huì)獲得鎖對(duì)象

Thread.sleep和Object.wait區(qū)別?
sleep不會(huì)釋放對(duì)象鎖的控制,而wait會(huì)。

finalize方法

這是在GC前會(huì)被JVM調(diào)用的方法,用來(lái)對(duì)一些特定的內(nèi)存進(jìn)行GC,一般不重寫(xiě),對(duì)一些JNI操作的gc會(huì)使用。

因而hashCode和equals用于對(duì)象比較,clone用于對(duì)象克隆,toString用于對(duì)象標(biāo)識(shí),notify與wait是對(duì)象鎖相關(guān)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Java8張圖 11、字符串不變性 12、equals()方法、hashCode()方法的區(qū)別 13、...
    Miley_MOJIE閱讀 3,896評(píng)論 0 11
  • Java中的Object類是所有類的父類,它提供了以下11個(gè)方法: public final native Cla...
    CHSmile閱讀 588評(píng)論 0 0
  • 由于時(shí)間倉(cāng)促,有些地方未寫(xiě)完,后面會(huì)繼續(xù)補(bǔ)充.如有不妥之處,歡迎及時(shí)與我溝通. 如果你也是在學(xué)習(xí)java,給你們推...
    分不清java閱讀 2,871評(píng)論 0 15
  • 相關(guān)概念 面向?qū)ο蟮娜齻€(gè)特征 封裝,繼承,多態(tài).這個(gè)應(yīng)該是人人皆知.有時(shí)候也會(huì)加上抽象. 多態(tài)的好處 允許不同類對(duì)...
    東經(jīng)315度閱讀 2,192評(píng)論 0 8
  • Object類位于java.lang包中,java.lang包有最基礎(chǔ)的和核心的類,在編譯時(shí)會(huì)自動(dòng)導(dǎo)入; Obje...
    遇見(jiàn)你的故事閱讀 738評(píng)論 0 0

友情鏈接更多精彩內(nèi)容