基本描述:
(1)Object類位於java.lang包中,java.lang包包含着Java最基礎和核心的類,在編譯時會自動導入;
(2)Object類是所有Java類的祖先。每個類都使用 Object 作為超類。所有對象(包括數組)都實現這個類的方法。可以使用類型為Object的變量指向任意類型的對象
Object的主要方法介紹:
package java.lang;
public class Object {
/* 一個本地方法,具體是用C(C++)在DLL中實現的,然后通過JNI調用。*/
private static native void registerNatives();
/* 對象初始化時自動調用此方法*/
static {
registerNatives();
}
/* 返回此 Object 的運行時類。*/
public final native Class<?> getClass();
public native int hashCode();
hashCode()方法:
hash值:Java中的hashCode方法就是根據一定的規則將與對象相關的信息(比如對象的存儲地址,對象的字段等)映射成一個數值,這個數值稱作為散列值。
情景:考慮一種情況,當向集合中插入對象時,如何判別在集合中是否已經存在該對象了?(注意:集合中不允許重復的元素存在)。
大多數人都會想到調用equals方法來逐個進行比較,這個方法確實可行。但是如果集合中已經存在一萬條數據或者更多的數據,如果采用equals方法去逐一比較,效率必然是一個問題。此時hashCode方法的作用就體現出來了,當集合要添加新的對象時,先調用這個對象的hashCode方法,得到對應的hashcode值。實際上在HashMap的具體實現中會用一個table保存已經存進去的對象的hashcode值,如果table中沒有該hashcode值,它就可以直接存進去,不用再進行任何比較了;如果存在該hashcode值, 就調用它的equals方法與新元素進行比較,相同的話就不存了,不相同就散列其它的地址。
重寫hashCode()方法的基本規則:
· 在程序運行過程中,同一個對象多次調用hashCode()方法應該返回相同的值。
· 當兩個對象通過equals()方法比較返回true時,則兩個對象的hashCode()方法返回相等的值。
· 對象用作equals()方法比較標准的Field,都應該用來計算hashCode值。
Object本地實現的hashCode()方法計算的值是底層代碼的實現,采用多種計算參數,返回的並不一定是對象的(虛擬)內存地址,具體取決於運行時庫和JVM的具體實現。
public boolean equals(Object obj) {
return (this == obj);
}
equals()方法:比較兩個對象是否相等
我們知道所有的對象都擁有標識(內存地址)和狀態(數據),同時“==”比較兩個對象的的內存地址,所以說使用Object的equals()方法是比較兩個對象的內存地址是否相等,即若object1.equals(object2)為true,則表示equals1和equals2實際上是引用同一個對象。雖然有時候Object的equals()方法可以滿足我們一些基本的要求,但是我們必須要清楚我們很大部分時間都是進行兩個對象的比較,這個時候Object的equals()方法就不可以了,實際上JDK中,String、Math等封裝類都對equals()方法進行了重寫。
protected native Object clone() throws CloneNotSupportedException;
clone()方法:快速創建一個已有對象的副本
第一:Object類的clone()方法是一個native方法,native方法的效率一般來說都是遠高於Java中的非native方法。這也解釋了為什么要用Object中clone()方法而不是先new一個類,然后把原始對象中的信息復制到新對象中,雖然這也實現了clone功能。
第二:Object類中的 clone()方法被protected修飾符修飾。這也意味着如果要應用 clone()方 法,必須繼承Object類。
第三:Object.clone()方法返回一個Object對象。我們必須進行強制類型轉換才能得到我們需要的類型。
克隆的步驟:1:創建一個對象; 2:將原有對象的數據導入到新創建的數據中。
clone方法首先會判對象是否實現了Cloneable接口,若無則拋出CloneNotSupportedException, 最后會調用internalClone. intervalClone是一個native方法,一般來說native方法的執行效率高於非native方法。
復制對象 or 復制引用
Person p = new Person(23, "zhang"); Person p1 = p; System.out.println(p); System.out.println(p1); 打印出來: com.pansoft.zhangjg.testclone.Person@2f9ee1ac com.pansoft.zhangjg.testclone.Person@2f9ee1ac

可已看出,打印的地址值是相同的,既然地址都是相同的,那么肯定是同一個對象。p和p1只是引用而已,他們都指向了一個相同的對象Person(23, "zhang") 。 可以把這種現象叫做引用的復制。
Person p = new Person(23, "zhang"); Person p1 = (Person) p.clone(); System.out.println(p); System.out.println(p1); 打印出: com.pansoft.zhangjg.testclone.Person@2f9ee1ac com.pansoft.zhangjg.testclone.Person@67f1fba0

從打印結果可以看出,兩個對象的地址是不同的,也就是說創建了新的對象, 而不是把原對象的地址賦給了一個新的引用變量:
深拷貝 or 淺拷貝
public class Person implements Cloneable{
private int age ;
private String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public Person() {}
public int getAge() {
return age;
}
public String getName() {
return name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return (Person)super.clone();
}
}
Person中有兩個成員變量,分別是name和age, name是String類型, age是int類型

由於age是基本數據類型, 那么對它的拷貝沒有什么疑議,直接將一個4字節的整數值拷貝過來就行。但是name是String類型的, 它只是一個引用, 指向一個真正的String對象,那么對它的拷貝有兩種方式: 直接將源對象中的name的引用值拷貝給新對象的name字段, 或者是根據原Person對象中的name指向的字符串對象創建一個新的相同的字符串對象,將這個新字符串對象的引用賦給新拷貝的Person對象的name字段。這兩種拷貝方式分別叫做淺拷貝和深拷貝。
通過下面代碼進行驗證。如果兩個Person對象的name的地址值相同, 說明兩個對象的name都指向同一個String對象, 也就是淺拷貝, 而如果兩個對象的name的地址值不同, 那么就說明指向不同的String對象, 也就是在拷貝Person對象的時候, 同時拷貝了name引用的String對象, 也就是深拷貝。
Person p = new Person(23, "zhang");
Person p1 = (Person) p.clone();
String result = p.getName() == p1.getName()
? "clone是淺拷貝的" : "clone是深拷貝的";
System.out.println(result);
打印出:
clone是淺拷貝的
對於對象的淺拷貝和深拷貝還有更深的細節,可以參考:詳解Java中的clone方法 -- 原型模式
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
toString()方法:toString 方法會返回一個“以文本方式表示”此對象的字符串。結果應是一個簡明但易於讀懂的信息表達式。建議所有子類都重寫此方法。
Object 類的 toString 方法返回一個字符串,該字符串由類名(對象是該類的一個實例)、at 標記符“@”和此對象哈希碼的無符號十六進制表示組成。
/*喚醒在此對象監視器上等待的單個線程。*/
public final native void notify();
/*喚醒在此對象監視器上等待的所有線程。*/
public final native void notifyAll();
/*在其他線程調用此對象的 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);
}
/*在其他線程調用此對象的 notify() 方法或 notifyAll() 方法前,導致當前線程等待。換句話說,此方法的行為就好像它僅執行 wait(0) 調用一樣。
當前線程必須擁有此對象監視器。該線程發布對此監視器的所有權並等待,直到其他線程通過調用 notify 方法,或 notifyAll 方法通知在此對象的監視器上等待的線程醒來。然后該線程將等到重新獲得對監視器的所有權后才能繼續執行。*/
public final void wait() throws InterruptedException {
wait(0);
}
protected void finalize() throws Throwable { }
finalize()方法:垃圾回收器准備釋放內存的時候,會先調用finalize()。
(1). 對象不一定會被回收。
(2).垃圾回收不是析構函數。
(3).垃圾回收只與內存有關。
(4).垃圾回收和finalize()都是靠不住的,只要JVM還沒有快到耗盡內存的地步,它是不會浪費時間進行垃圾回收的。
