1. 程式人生 > >Java原始碼分析——Object類解析,一切類的源頭

Java原始碼分析——Object類解析,一切類的源頭

    Java語言類的起始點源於Object類,其它類都直接或間接繼承Oject類,可以說Object類是一切類的源頭。下面來談下Object類的內部的編碼以及簡單的闡述下其中的native本地的方法。
    在Java程式執行的時候,我們不免會想到java中的類怎麼進入jvm裡面執行,也就是怎麼載入進jvm的,在Object類裡面有如下定義:

 private static native void registerNatives();     
    static {     
        registerNatives
(); }

    registerNatives方法就是負責這一功能的,它呼叫了Openjdk的本地方法,將該類註冊進jvm中,在Openjdk裡面registerNatives方法是由c++來實現的,程式碼如下

Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)
{
    (*env)->RegisterNatives(env, cls,
                            methods, sizeof(methods)/sizeof(methods[0]));
}

    接下來是getClass方法,該方法的作用是來獲取Class類的例項,在這裡其實是獲取該類的完整名字,什麼叫完整的名字呢?比如說Object類,那麼它的完整名字是java.lang.Object。要注意的是它也是呼叫的本地的方法,還需要注意的是該方法的字首由final屬性,也就是說,該方法不可以被重寫:

public final native Class<?> getClass();

    我們可以從其原生代碼當中看出,當該類物件為空時,丟擲異常,不為空則返回該類的完整名:

Java_java_lang_Object_getClass(JNIEnv *env, jobject this)
{
    if (this == NULL) {
        JNU_ThrowNullPointerException(env, NULL);
        return 0;
    } else {
        return (*env)-&gt;GetObjectClass(env, this);
    }
}

    接著就是雜湊編碼了:

public native int hashCode(); 

    每個類都有其特定的雜湊編碼,我們可以通過兩個物件的雜湊編碼是否相同來判斷其是否是一個類的物件。Java中的hashCode的常規協定是:

  1. 在 Java 應用程式執行期間,在對同一物件多次呼叫 hashCode 方法時,必須一致地返回相同的整數,前提是將物件進行 equals 比較時所用的資訊沒有被修改。從某一應用程式的一次執行到同一應用程式的另一次執行,該整數無需保持一致。
  2. 如果根據 equals(Object) 方法,兩個物件是相等的,那麼對這兩個物件中的每個物件呼叫 hashCode 方法都必須生成相同的整數結果。
  3. 如果根據 equals(java.lang.Object) 方法,兩個物件不相等,那麼對這兩個物件中的任一物件上呼叫 hashCode 方法不 要求一定生成不同的整數結果。但是,程式設計師應該意識到,為不相等的物件生成不同整數結果可以提高雜湊表的效能。

    equals是個比較有趣的方法,初學者如果一不小心就會弄錯,原由是對java底層的原始碼不熟悉的結果,Object類中的equals用來判斷物件的引用是否相等,而其它類都是在判斷引用是否相等的前提下再去執行其它的程式碼:

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

    clone方法是一個本地的方法,用來複制一個物件,這種複製常常被我們稱為淺拷貝,因為它只複製了物件的引用,兩個引用指向的是同一個地址,須注意的是,它是protected方法,也就是說要使用它的話只能被繼承一次,也就是在自己自定義的類上進行拷貝,因為這樣是預設繼承Object方法的,其次需注意的是,使用它需要實現一個標記藉口,Cloneable該接口裡面沒有任何方法,只是純標記介面,不實現會拋CloneNotSupportedException異常:

protected native Object clone() throws CloneNotSupportedException;    

    toString是用來返回類中資料的,當想要返回某些資料展示給外界時,重寫該方法就可以了:

public String toString() {     
    return getClass().getName() + "@" + Integer.toHexString(hashCode());     
    }    

    接下來就是執行緒有關的操作了,呼叫了本地方法:

  /*喚醒在此物件監視器上等待的單個執行緒。*/    
    public final native void notify(); 

    喚醒在此物件監視器上等待的所有執行緒:

   public final native void notifyAll();  

    在其他執行緒呼叫此物件的 notify() 方法或 notifyAll() 方法前,導致當前執行緒等待。換句話說,此方法的行為就好像它僅執行 wait(0) 呼叫一樣。當前執行緒必須擁有此物件監視器。該執行緒釋出對此監視器的所有權並等待,直到其他執行緒通過呼叫 notify 方法,或 notifyAll 方法通知在此物件的監視器上等待的執行緒醒來。然後該執行緒將等到重新獲得對監視器的所有權後才能繼續執行:

   public final void wait() throws InterruptedException {     
   wait(0);     
   }     

    在其他執行緒呼叫此物件的 notify() 方法或 notifyAll() 方法,或者超過指定的時間量前,導致當前執行緒等待:

 public final native void wait(long timeout) throws InterruptedException; 

    在其他執行緒呼叫此物件的 notify() 方法或 notifyAll() 方法,或者其他某個執行緒中斷當前執行緒,或者已超過某個實際時間量前,導致當前執行緒等待:

 public final void wait(long timeout, int nanos) throws InterruptedException {     
        if (timeout < 0) {     
            throw new IllegalArgumentException("timeout value is negative");     
        }     
        if (nanos < 0 || nanos > 999999) {     
            throw new IllegalArgumentException(     
                "nanosecond timeout value out of range");     
        }     
    if (nanos >= 500000 || (nanos != 0 && timeout == 0)) {     
        timeout++;     
    }     
    wait(timeout);     
    } 

    最後是關於垃圾回收器的方法,當GC確定不存在該物件的更多引用時,會讓物件的垃圾回收器呼叫此方法,程式設計師一般不會使用該方法:

 protected void finalize() throws Throwable { }    

    ps:所有的類都會繼承Object類,只不過在程式中不會寫出來。