「OpenJdk-11 源碼-系列」 : Object

Object

在 Java 中 Object 類是全部類的祖先類,Object 沒有定義屬性,一共有13個方法。其它全部的子類都會繼承這些方法。java

構造函數

registerNatives()

private static native void registerNatives();
 static {
        registerNatives();
 }
複製代碼

在 Java 中,用 native 關鍵字修飾的函數代表該方法的實現並非在Java中去完成,而是由C/C++去完成,並被編譯成了.dll,由Java去調用ios

registerNatives()方法的主要做用則是將C/C++中的方法映射到 Java 中的 native方法,實現方法命名的解耦。多線程

構造函數 public Object()

@HotSpotIntrinsicCandidate
 public Object() {}
複製代碼

@HotSpotIntrinsicCandidate註解,該註解是特定於Java虛擬機的註解。經過該註解表示的方法可能( 但不保證 )經過HotSpot VM本身來寫彙編或IR編譯器來實現該方法以提供性能。 它表示註釋的方法可能(但不能保證)由HotSpot虛擬機內在化。若是HotSpot VM用手寫彙編和/或手寫編譯器IR(編譯器自己)替換註釋的方法以提升性能,則方法是內在的。 也就是說雖然外面看到的在JDK9中weakCompareAndSet和compareAndSet底層依舊是調用了同樣的代碼,可是不排除HotSpot VM會手動來實現weakCompareAndSet真正含義的功能的可能性函數

通常建立對象的時候直接使用 new className(Args) 來建立一個新的對象。而在類的定義過程當中,對於未定義構造函數的類,那麼它就會默認繼承Object的無參構造函數,若是定了一個或多個構造函數,那麼就須要把無參構造函數方法也寫上。性能

方法

public final native Class<?> getClass()

@HotSpotIntrinsicCandidate
 public final native Class<?> getClass();
複製代碼

getClass返回運行時當前對象的類對象。在 Java 中,類是對具備一組相同特徵或行爲的實例的抽象進行描述。而類對象則是對的特徵和行爲進行描述(即類的名稱,屬性,方法...)。也就是說經過獲取到類對象,則能夠獲取到該類的全部屬性,方法等。this

public native int hashCode()

@HotSpotIntrinsicCandidate
public native int hashCode();
複製代碼

hashCode 返回當前對象的哈希碼。hashCode遵照如下三個約定spa

  • 在 Java 程序運行期間,對同一個對象屢次調用hashCode,那麼它們的返回值須要是一致的。(前提:沒有對對象進行修改)
  • 若是兩個對象相等(調用equals()方法),那麼這兩個對象的 hashCode也是同樣
  • 兩個對象調用hashCode方法返回的哈希碼相等,這兩個對象不必定相等

也便是說,調用equals方法返回值相等,那麼調用hashCode方法返回值也必定相等。因此,在重寫euqlas方法以後,必定要重寫hashCode方法。.net

那麼判斷對象是否先等能夠直接用equals來判斷,爲何還須要hashCode方法呢?線程

其實hashCode方法的一個主要做用是爲了加強哈希表的性能。好比:咱們知道Set集合不能存在相同的兩個對象,那麼該怎麼判斷兩個對象是否相同呢?若是沒有hashCode,那麼就須要進行遍從來逐一判斷。那麼有hashCode,咱們就能夠計算出即將要加入集合的對象的hashCode,而後查看集合中對應的位置上是否有對象便可。code

public boolean equals(Object obj)

public boolean equals(Object obj) {
   return (this == obj);
}
    
複製代碼

equals()用於判斷兩個對象是否相等。根據 Object 的實現,能夠看到判斷的依據是看兩個對象的引用地址是否相等。

而通常咱們會用另一種方式來判斷是否相等。即==,==表示的是兩個變量值是否相等(基礎類型的值在內存地址中存儲的是值)

那麼咱們想要判斷是否相等:

  • 若是是基礎類型,就能夠直接用==來判斷
  • 若是是引用類型,那麼就須要經過equals方法來判斷(在實際業務中,通常會重寫equals方法)

須要注意的一點是String也是引用類型,咱們判斷String的時候是直接使用的equals方法,而按照默認的equals實現,建立兩個具備相同值的String對象,那麼equals返回的應該是false

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String aString = (String)anObject;
        if (coder() == aString.coder()) {
            return isLatin1() ? StringLatin1.equals(value, aString.value)
                              : StringUTF16.equals(value, aString.value);
        }
    }
    return false;
}
複製代碼

public String toString()

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

toString()返回該對象的字符串表示。在使用 System.out.printLn(obj)的時候,其內部也是調用的toString方法。能夠按需重寫toString方法。

protected native Object clone()

protected native Object clone() throws CloneNotSupportedException;
複製代碼

clone()方法返回的是當前對象的引用,指向的是新clone出來的對象,此對象和原對象佔用不一樣的堆空間。

clone方法的正確調用須要實現 cloneable 接口,若是沒有實現該接口,那麼子類調用父類的 clone方法則會拋出CloneNotSupportedException異常

Cloneable接口僅僅是一個表示接口,接口自己不包含任何方法,用來指示Object.clone()能夠合法的被子類引用所調用。

1. 使用

先看一段代碼

public class CloneTest {  
  
    public static void main(String[] args) {  
        Object o1 = new Object();  
        try {
            Object clone = o1.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }  
  
}  
複製代碼

執行這段段代碼會拋出The method clone() from the type Object is not visible異常。緣由是clone 方法是被 protected修飾的,也就是說被protected修飾的屬性和方法,在同一包下或在不一樣包下的子類能夠訪問。顯然,CloneTestObject不在同一包下,不過按照字面意思,CloneTest會默認繼承Object,因此即便在不一樣的包下,應該也是能夠訪問的纔對。那麼問題就出如今「在不一樣包下的子類能夠訪問」這句話上:

不一樣包中的子類能夠訪問: 是指當兩個類不在同一個包中的時候,繼承自父類的子類內部且主調(調用者)爲子類的引用時才能訪問父類用protected修飾的成員(屬性/方法)。 在子類內部,主調爲父類的引用時並不能訪問此protected修飾的成員。(super關鍵字除外)

也就是說在子類中想要調用父類的protected方法,能夠

  • 在子類中重寫父類的方法
  • 在子類中經過super.methodName()來調用父類方法

2. 淺拷貝&深拷貝

淺拷貝: 淺拷貝是按位拷貝對象,它會建立一個新對象,這個對象有着原始對象屬性值的一份精確拷貝。若是屬性是基本類型,拷貝的就是基本類型的值;若是屬性是引用類型,拷貝的就是內存地址。 深拷貝: 深拷貝會拷貝全部的屬性,並拷貝屬性指向的動態分配的內存。當對象和它所引用的對象一塊兒拷貝時即發生深拷貝。深拷貝相比於淺拷貝速度較慢而且花銷較大。

對於淺拷貝來講,若是含有引用類型,那麼修改其中一個對象的引用值,那麼會影響到另一個對象。按層級來講,淺拷貝只拷貝了第一層。對於默認的clone實現是淺拷貝。若是想要實現深拷貝,能夠

  • 對對象進行序列化
  • 重寫clone方法
//序列化實現深拷貝

public class CloneUtils {
    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T clone(T obj){
        T cloneObj = null;
        try {
            //寫入字節流
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ObjectOutputStream obs = new ObjectOutputStream(out);
            obs.writeObject(obj);
            obs.close();
            
            //分配內存,寫入原始對象,生成新對象
            ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(ios);
            //返回生成的新對象
            cloneObj = (T) ois.readObject();
            ois.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return cloneObj;
    }
}

public class Person implements Serializable{
    private static final long serialVersionUID = 2631590509760908280L;
}

public class CloneTest {
    public static void main(String[] args) {
        Person person =  new Person();
        Person person1 =  CloneUtils.clone(person);     
    }
}


參考:https://blog.csdn.net/chenssy/article/details/12952063
複製代碼

protected void finalize()

protected void finalize() throws Throwable {}
複製代碼

finalize()方法主要與 Java 垃圾回收機制有關,JVM準備對此對形象所佔用的內存空間進行垃圾回收前,將被調用。因此此方法並非由咱們主動去調用的。

wait()/notify/notifyAll

可先看java 多線程嚐鮮。後續會專門講多線程相關源碼。

相關文章
相關標籤/搜索