Object是Java所有類的基類迈嘹,是整個(gè)類繼承結(jié)構(gòu)的頂端碰酝,我們幾乎每次都需要使用到它丐黄。
Object的12種方法分別是:registerNatives()
绊序、getClass()
纷宇、hashCode()
夸盟、equals()
、clone()
像捶、toString()
上陕、wait()
桩砰、wait(long)
、wait(long,int)
释簿、notify()
亚隅、notifyAll()
、finalize()
庶溶。下面對(duì)這些方法做一個(gè)總結(jié)歸納煮纵。
registerNatives()
private static native void registerNatives();
static {
registerNatives();
}
這個(gè)方法是一個(gè)注冊(cè)本地方法,由JVM實(shí)現(xiàn)渐尿,底層是C/C++實(shí)現(xiàn)的醉途,由代碼我們可以發(fā)現(xiàn)一個(gè)問題,為什么要使用靜態(tài)方法砖茸,還要放在靜態(tài)塊中呢隘擎?
原因是類初始化的順序是:
父類–靜態(tài)變量/父類–靜態(tài)初始化塊
子類–靜態(tài)變量/子類–靜態(tài)初始化塊
父類–變量/父類–初始化塊
父類–構(gòu)造器
子類–變量/子類–初始化塊
子類–構(gòu)造器
放入靜態(tài)代碼塊中保證了父類的類變量及方法的初始化一定先于子類,所以當(dāng)子類調(diào)用響應(yīng)native方法凉夯,比如計(jì)算hashCode()時(shí)货葬,一定可以保證能夠調(diào)用到JVM的native方法。
getClass()
public final native Class<?> getClass();
這是一個(gè) public的方法劲够,我們可以直接通過對(duì)象調(diào)用震桶。
類加載的第一階段類的加載就是將 .class文件加載到內(nèi)存,并生成一個(gè) java.lang.Class對(duì)象的過程征绎。 getClass()方法就是獲取這個(gè)對(duì)象蹲姐,這是當(dāng)前類的對(duì)象在運(yùn)行時(shí)類的所有信息的集合。這個(gè)方法是反射三種方式之一(類名.class人柿、對(duì)象.getClass柴墩、Class.forName("全類名")
)。
hashCode()
public native int hashCode();
這是一個(gè) public的方法凫岖,所以子類可以重寫它江咳。這個(gè)方法返回當(dāng)前對(duì)象的 hashCode值,這個(gè)值是一個(gè)整數(shù)范圍內(nèi)的 (-2^31~2^31-1)
數(shù)字哥放。
對(duì)于 hashCode有以下幾點(diǎn)約束:
- 在 Java應(yīng)用程序執(zhí)行期間歼指,在對(duì)同一對(duì)象多次調(diào)用 hashCode 方法時(shí),必須一致地返回相同的整數(shù)甥雕,前提是將對(duì)象進(jìn)行 equals 比較時(shí)所用的信息沒有被修改踩身;
- 如果兩個(gè)對(duì)象 x.equals(y) 方法返回 true,則 x社露、 y這兩個(gè)對(duì)象的 hashCode必須相等惰赋。
- 如果兩個(gè)對(duì)象 x.equals(y) 方法返回 false,則 x、 y這兩個(gè)對(duì)象的 hashCode可以相等也可以不等赁濒。但是轨奄,為不相等的對(duì)象生成不同整數(shù)結(jié)果可以提高哈希表的性能。
- 默認(rèn)的 hashCode是將內(nèi)存地址轉(zhuǎn)換為的 hash值拒炎,重寫過后就是自定義的計(jì)算方式挪拟;也可以通過 System.identityHashCode(Object)來返回原本的 hashCode。
equals()
public boolean equals(Object obj) {
return (this == obj);
}
用于比較當(dāng)前對(duì)象與目標(biāo)對(duì)象是否相等击你,默認(rèn)是比較引用是否指向同一對(duì)象玉组。為 public方法,子類可重寫丁侄。
為什么需要重寫 equals方法惯雳?
因?yàn)槿绻恢貙慹quals方法,當(dāng)將自定義對(duì)象放到 map或者 set中時(shí)鸿摇;如果這時(shí)兩個(gè)對(duì)象的 hashCode相同石景,就會(huì)調(diào)用 equals方法進(jìn)行比較,這個(gè)時(shí)候會(huì)調(diào)用 Object中默認(rèn)的 equals方法拙吉,而默認(rèn)的 equals方法只是比較了兩個(gè)對(duì)象的引用是否指向了同一個(gè)對(duì)象潮孽,顯然大多數(shù)時(shí)候都不會(huì)指向,這樣就會(huì)將重復(fù)對(duì)象存入 map或者 set中筷黔。這就破壞了 map與 set不能存儲(chǔ)重復(fù)對(duì)象的特性往史,會(huì)造成內(nèi)存溢出。
重寫 equals方法的幾條約定:
- 自反性:即 x.equals(x)返回 true佛舱, x不為 null椎例;
- 對(duì)稱性:即 x.equals(y)與 y.equals(x)的結(jié)果相同, x與 y不為 null请祖;
- 傳遞性:即 x.equals(y)結(jié)果為 true, y.equals(z)結(jié)果為 true订歪,則 x.equals(z)結(jié)果也必須為 true;
- 一致性:即 x.equals(y)返回 true或 false损拢,在未更改 equals方法使用的參數(shù)條件下,多次調(diào)用返回的結(jié)果也必須一致撒犀。 x與 y不為 null福压。
- 如果 x不為 null, x.equals(null)返回 false。
clone()
protected native Object clone() throws CloneNotSupportedException;
此方法返回當(dāng)前對(duì)象的一個(gè)副本或舞。
這是一個(gè) protected方法荆姆,提供給子類重寫。但需要實(shí)現(xiàn) Cloneable接口映凳,這是一個(gè)標(biāo)記接口胆筒,如果沒有實(shí)現(xiàn),當(dāng)調(diào)用 object.clone()方法,會(huì)拋出 CloneNotSupportedException仆救。
@Override
protected CloneTest clone() throws CloneNotSupportedException
{
return (CloneTest) super.clone();
}
那如果我們要進(jìn)行深拷貝怎么辦呢抒和?
答案是:如果成員變量是引用類型,想實(shí)現(xiàn)深拷貝彤蔽,則成員變量也要實(shí)現(xiàn) Cloneable接口摧莽,重寫 clone方法。
toString()
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
這是一個(gè) public方法顿痪,子類可重寫镊辕,建議所有子類都重寫 toString方法,默認(rèn)的 toString方法蚁袭,只是將當(dāng)前類的全限定性類名 +@+十六進(jìn)制的 hashCode值征懈。
wait()/ wait(long)/ wait(long,int)
public final native void wait(long timeout) throws InterruptedException;
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 > 0) {
timeout++;
}
wait(timeout);
}
public final void wait() throws InterruptedException {
wait(0);
}
這三個(gè)方法是用來線程間通信用的,作用是阻塞當(dāng)前線程揩悄,等待其他線程調(diào)用 notify()/notifyAll()方法將其喚醒卖哎。這些方法都是 publicfinal的,不可被重寫虏束。
注意:
- 此方法只能在當(dāng)前線程獲取到對(duì)象的鎖監(jiān)視器之后才能調(diào)用棉饶,否則會(huì)拋出 IllegalMonitorStateException異常。
- 調(diào)用 wait方法镇匀,線程會(huì)將鎖監(jiān)視器進(jìn)行釋放照藻;而 Thread.sleep,Thread.yield()并不會(huì)釋放鎖汗侵。
- wait方法會(huì)一直阻塞幸缕,直到其他線程調(diào)用當(dāng)前對(duì)象的 notify()/notifyAll()方法將其喚醒;而 wait(long)是等待給定超時(shí)時(shí)間內(nèi)(單位毫秒)晰韵,如果還沒有調(diào)用 notify()/nofiyAll()會(huì)自動(dòng)喚醒发乔; wait(long,int)如果第二個(gè)參數(shù)大于 0并且小于 999999,則第一個(gè)參數(shù) +1作為超時(shí)時(shí)間雪猪;
notify()/notifyAll()
public final native void notify();
public final native void notifyAll();
前面說了栏尚,如果當(dāng)前線程獲得了當(dāng)前對(duì)象鎖,調(diào)用 wait方法只恨,將鎖釋放并阻塞译仗;這時(shí)另一個(gè)線程獲取到了此對(duì)象鎖,并調(diào)用此對(duì)象的 notify()/notifyAll()方法將之前的線程喚醒官觅。這些方法都是 publicfinal的纵菌,不可被重寫。
publicfinalnativevoidnotify();
隨機(jī)喚醒之前在當(dāng)前對(duì)象上調(diào)用 wait方法的一個(gè)線程
publicfinalnativevoidnotifyAll();
喚醒所有之前在當(dāng)前對(duì)象上調(diào)用 wait方法的線程
注意:調(diào)用 notify()后休涤,阻塞線程被喚醒咱圆,可以參與鎖的競爭,但可能調(diào)用 notify()方法的線程還要繼續(xù)做其他事,鎖并未釋放序苏,所以我們看到的結(jié)果是手幢,無論 notify()是在方法一開始調(diào)用,還是最后調(diào)用杠览,阻塞線程都要等待當(dāng)前線程結(jié)束才能開始弯菊。
finalize
protected void finalize() throws Throwable { }
此方法是在垃圾回收之前,JVM會(huì)調(diào)用此方法來清理資源踱阿。此方法可能會(huì)將對(duì)象重新置為可達(dá)狀態(tài)管钳,導(dǎo)致JVM無法進(jìn)行垃圾回收。永遠(yuǎn)不要主動(dòng)調(diào)用某個(gè)對(duì)象的 finalize()方法软舌,該方法由垃圾回收機(jī)制自己調(diào)用才漆;
感謝codesheep!