面試題(3)-Object類(lèi)中有哪些方法及作用
Object類(lèi)中有哪些方法及作用
1.Object類(lèi)的定義
Object類(lèi)是所有類(lèi)的父類(lèi),也就是說(shuō)任何一個(gè)類(lèi)在定義時(shí)候如果沒(méi)有明確的繼承一個(gè)父類(lèi)的話(huà),那么它就是Object類(lèi)的子類(lèi)。
由于Java里面的類(lèi)的繼承關(guān)系一直都存在(除了Object類(lèi))
Object類(lèi)是所有類(lèi)的父類(lèi)的好處:利用Object類(lèi)可以接收全部類(lèi)的對(duì)象,因?yàn)榭梢韵蛏献詣?dòng)轉(zhuǎn)型,所以如果不確定參數(shù)的實(shí)際類(lèi)型,那么Object類(lèi)就是最好的選擇,最后強(qiáng)制向下轉(zhuǎn)型為所需類(lèi)型即可。
Object類(lèi)提供了一個(gè)無(wú)參構(gòu)造方法:public Object(),因?yàn)椋侯?lèi)對(duì)象實(shí)例化時(shí),子類(lèi)構(gòu)造方法一定會(huì)默認(rèn)調(diào)用父類(lèi)的無(wú)參構(gòu)造。
2.Object類(lèi)中的方法及作用
1.hashCode方法
在Object代碼中,hashCode是native的,非java代碼實(shí)現(xiàn)。主要原因是它的實(shí)現(xiàn)方法是通過(guò)將對(duì)象在內(nèi)存中所處于的位置轉(zhuǎn)換成數(shù)字,這個(gè)數(shù)字就是hashCode。但是這個(gè)內(nèi)存地址實(shí)際上java程序并不關(guān)心也是不可知的。這個(gè)地址是由JVM維護(hù)并保存的,所以實(shí)現(xiàn)是native的。
在程序運(yùn)行過(guò)程中,同一個(gè)對(duì)象的hashCode無(wú)論執(zhí)行多少次都要保持一致。但是,在程序重啟后同一個(gè)對(duì)象的hashCode不用和之前那次運(yùn)行的hashCode保持一致。但是考慮如果在分布式的情況下,如果對(duì)象作為key,最好還是保證無(wú)論在哪臺(tái)機(jī)器上運(yùn)行多少次,重啟多少次,不同機(jī)器上,同一個(gè)對(duì)象(指的是兩個(gè)equals對(duì)象),的hashCode值都一樣。 例如這里的Object對(duì)于hashCode的實(shí)現(xiàn),在當(dāng)前次運(yùn)行,這個(gè)對(duì)象的存儲(chǔ)地址是不變的。所以hashCode不變,但是程序重啟后就不一定了。
String的hashCode實(shí)現(xiàn):
String就是一種典型的適合在分布式的情況下作為key的存儲(chǔ)對(duì)象。無(wú)論程序何時(shí)在哪里運(yùn)行,同一個(gè)String的hashCode結(jié)果都是一樣的。
2.equals方法
該方法用于比較兩個(gè)對(duì)象,如果這兩個(gè)對(duì)象引用指向的是同一個(gè)對(duì)象,那么返回 true,否則返回 false。一般 equals 和 == 是不一樣的,但是在 Object 中兩者是一樣的。子類(lèi)一般都要重寫(xiě)這個(gè)方法。
String的equals實(shí)現(xiàn):
注意:
如果兩個(gè)對(duì)象是euqal的,那么hashCode要相同
不強(qiáng)制對(duì)于不相等的對(duì)象的hashCode一定要不同。
3.getClass方法
final 方法,獲取對(duì)象的運(yùn)行時(shí) class 對(duì)象,class 對(duì)象就是描述對(duì)象所屬類(lèi)的對(duì)象。這個(gè)方法通常是和 Java 反射機(jī)制搭配使用的。
getClass()和.class的作用與區(qū)別
getClass()是Object類(lèi)的方法,該方法的返回值類(lèi)型是Class類(lèi),通過(guò)getClass()方法可以得到一個(gè)Class類(lèi)的對(duì)象。
.class返回的也是Class類(lèi)型的對(duì)象。所以,如果getClass()和.class返回的內(nèi)容相等,說(shuō)明是同一個(gè)對(duì)象。
既然都可以得到Class的對(duì)象,關(guān)于getClass()和.class的區(qū)別:
getClass()方法,有多態(tài)能力,運(yùn)行時(shí)可以返回子類(lèi)的類(lèi)型信息。
.class是沒(méi)有多態(tài)的,是靜態(tài)解析,編譯時(shí)可以確定類(lèi)型信息。
4.toString方法
該方法用得比較多,一般子類(lèi)都有覆蓋。默認(rèn)的實(shí)現(xiàn)就是class名字@hashcode的值(如果hashCode()方法也是默認(rèn)的話(huà),那么就是如之前所述地址)
5.clone方法
該方法也是一個(gè)native方法, 此方法是保護(hù)方法, 實(shí)現(xiàn)對(duì)象的淺復(fù)制, 只有實(shí)現(xiàn)了Cloneable接口才可以調(diào)用該方法,否則拋出CloneNotSupportedException異常。
默認(rèn)的 clone 方法是淺拷貝。所謂淺拷貝,指的是對(duì)象內(nèi)屬性引用的對(duì)象只會(huì)拷貝引用地址,而不會(huì)將引用的對(duì)象重新分配內(nèi)存。深拷貝則是會(huì)連引用的對(duì)象也重新創(chuàng)建。
主要是JAVA里除了8種基本類(lèi)型傳參數(shù)是值傳遞,其他的類(lèi)對(duì)象傳參數(shù)都是引用傳遞,我們有時(shí)候不希望在方法里將參數(shù)改變,這時(shí)就需要在類(lèi)中復(fù)寫(xiě)clone方法。
6.registerNatives方法
Java有兩種方法:Java方法和本地方法。Java方法是由Java語(yǔ)言編寫(xiě),編譯成字節(jié)碼,存儲(chǔ)在class文件中。本地方法是由其他語(yǔ)言(比如C,C++,或者匯編)編寫(xiě)的,編譯成和處理器相關(guān)的機(jī)器代碼。本地方法保存在動(dòng)態(tài)連接庫(kù)中,格式是各個(gè)平臺(tái)專(zhuān)有的。Java方法是平臺(tái)無(wú)關(guān)的,單本地方法卻不是。運(yùn)行中的Java程序調(diào)用本地方法時(shí),虛擬機(jī)裝載包含這個(gè)本地方法的動(dòng)態(tài)庫(kù),并調(diào)用這個(gè)方法。本地方法是聯(lián)系Java程序和底層主機(jī)操作系統(tǒng)的連接方法。
由此可知,本地方法的實(shí)現(xiàn)是由其他語(yǔ)言編寫(xiě)并保存在動(dòng)態(tài)連接庫(kù)中,因而在java類(lèi)中不需要方法實(shí)現(xiàn)。registerNatives本質(zhì)上就是一個(gè)本地方法,但這又是一個(gè)有別于一般本地方法的本地方法,從方法名我們可以猜測(cè)該方法應(yīng)該是用來(lái)注冊(cè)本地方法的。對(duì),你猜的沒(méi)錯(cuò)。上述代碼的功能就是先定義了registerNatives()方法,然后當(dāng)該類(lèi)被加載的時(shí)候,調(diào)用該方法完成對(duì)該類(lèi)中本地方法的注冊(cè)。這里你可能會(huì)有一些疑惑,比如,到底注冊(cè)了哪些方法?為什么要注冊(cè)?具體又是怎么注冊(cè)的?
我們首先看第一個(gè)問(wèn)題:到底注冊(cè)了哪些方法?細(xì)心的你可能還會(huì)發(fā)現(xiàn),在Object類(lèi)中,除了有registerNatives這個(gè)本地方法之外,還有hashCode()、clone()等本地方法,而在Class類(lèi)中有forName0()這樣的本地方法等等。也就是說(shuō),凡是包含registerNatives()本地方法的類(lèi),同時(shí)也包含了其他本地方法。所以,顯然,當(dāng)包含registerNatives()方法的類(lèi)被加載的時(shí)候,注冊(cè)的方法就是該類(lèi)所包含的除了registerNatives()方法以外的所有本地方法。
7.notify方法和notifyAll方法
final 方法,主要用于喚醒在該對(duì)象上等待的某個(gè)線(xiàn)程或者是所有線(xiàn)程。
8.wait方法
wait方法就是使當(dāng)前線(xiàn)程等待該對(duì)象的鎖,當(dāng)前線(xiàn)程必須是該對(duì)象的擁有者,也就是具有該對(duì)象的鎖。wait()方法一直等待,直到獲得鎖或者被中斷。wait(long timeout)設(shè)定一個(gè)超時(shí)間隔,如果在規(guī)定時(shí)間內(nèi)沒(méi)有獲得鎖就返回。
調(diào)用該方法后當(dāng)前線(xiàn)程進(jìn)入睡眠狀態(tài),直到以下事件發(fā)生。
(1)其他線(xiàn)程調(diào)用了該對(duì)象的notify方法。
(2)其他線(xiàn)程調(diào)用了該對(duì)象的notifyAll方法。
(3)其他線(xiàn)程調(diào)用了interrupt中斷該線(xiàn)程。
(4)時(shí)間間隔到了。
此時(shí)該線(xiàn)程就可以被調(diào)度了,如果是被中斷的話(huà)就拋出一個(gè)InterruptedException異常。
參數(shù)說(shuō)明:
該方法導(dǎo)致當(dāng)前線(xiàn)程等待,直到其他線(xiàn)程調(diào)用此對(duì)象的 notify() 方法或notifyAll()方法,或在指定已經(jīng)過(guò)去的時(shí)間。此方法類(lèi)似于 wait 方法的一個(gè)參數(shù),但它允許更好地控制的時(shí)間等待一個(gè)通知放棄之前的量。實(shí)時(shí)量,以毫微秒計(jì)算,計(jì)算公式如下:
在所有其他方面,這種方法與 wait(long timeout) 做同樣的事情。特別是 wait(0, 0) 表示和 wait(0) 相同。
9.finalize方法
該方法用于釋放資源。因?yàn)闊o(wú)法確定該方法什么時(shí)候被調(diào)用,很少使用。
首先f(wàn)inalize方法是在垃圾回收時(shí),用于確認(rèn)該對(duì)象是否確認(rèn)被回收的一個(gè)標(biāo)記過(guò)程。
確認(rèn)一個(gè)對(duì)象真正被回收需要經(jīng)歷兩次標(biāo)記過(guò)程:
可達(dá)性分析沒(méi)有引用,這是第一次標(biāo)記,是否有必要執(zhí)行finalize方法,如果對(duì)象沒(méi)有重寫(xiě)finalize方法或者finalize方法已經(jīng)被調(diào)用過(guò)了,那么finalize方法就是沒(méi)有必要執(zhí)行的,沒(méi)有必要執(zhí)行finalize方法的對(duì)象就會(huì)被直接回收。如果對(duì)象被判定為有必要執(zhí)行finalize()方法,那么這個(gè)對(duì)象將會(huì)放置在一個(gè)叫做F-Queue的隊(duì)列之中,并在稍后由一個(gè)由虛擬機(jī)創(chuàng)建、低優(yōu)先級(jí)的finalizer線(xiàn)程去執(zhí)行它。這里所謂的“執(zhí)行”是指虛擬機(jī)會(huì)觸發(fā)這個(gè)方法,但并不承諾會(huì)等待它運(yùn)行結(jié)束,這樣做的原因是,如果一個(gè)對(duì)象的finalize()執(zhí)行緩慢,極端情況下死循環(huán),那么就會(huì)導(dǎo)致F-Queue隊(duì)列中其他對(duì)象永久處于等待,甚至導(dǎo)致整個(gè)內(nèi)存回收系統(tǒng)崩潰。
finalize()方法是對(duì)象逃脫死亡命運(yùn)的最后一次機(jī)會(huì),稍后GC將對(duì)F-Queue中的對(duì)象進(jìn)行第二次小規(guī)模的標(biāo)記,如果對(duì)象要在finalize()成功拯救自己——只需要重新與引用鏈上的任何一個(gè)對(duì)象建立關(guān)聯(lián)即可,譬如把this關(guān)鍵字賦值給某個(gè)類(lèi)的變量或者對(duì)象的成員變量,那么第二次標(biāo)記時(shí)它將會(huì)被移出“即將回收”的集合;如果對(duì)象這時(shí)還沒(méi)有逃脫,那么它就真正被回收了。
finalize方法不是一定會(huì)執(zhí)行,只有在該方法被重寫(xiě)的時(shí)候才會(huì)執(zhí)行
inalize方法只會(huì)被執(zhí)行一次
對(duì)象可以在finalize方法中獲得自救,避免自己被垃圾回收,同樣自救也只能一次
不推薦Java程序員調(diào)用該方法,因?yàn)閒inalize方法代價(jià)很大