核心方法揭秘)
當(dāng)我們寫一個(gè)類的時(shí)候都會(huì)對(duì)Java.lang.類的一些重要方法進(jìn)行重寫這些方法包含(),(),(),(),clone(),wait(),()/() 這八個(gè)方法。一 ()方法1.何時(shí)需要對(duì)代碼方法進(jìn)行重寫?當(dāng)一個(gè)類里面, 它自己有自己特有的那種邏輯相等的概念, 然后這個(gè)邏輯相等的概念呢, 是不同于對(duì)象身份的概念的。2.設(shè)計(jì)()使用操作符來檢查實(shí)參是否為正確的類型。對(duì)于該類當(dāng)中的每一個(gè)關(guān)鍵域, 需要針對(duì)實(shí)里面所提供的對(duì)應(yīng)的那個(gè)域的數(shù)值情況, 去仔細(xì)地與目前所持有的這個(gè)對(duì)象內(nèi)部所對(duì)應(yīng)的這些域的數(shù)值之間展開檢查處理。對(duì)于那些, 不屬于float類型以及類型的原語類型字段來說, 應(yīng)該采用相等符號(hào)進(jìn)行數(shù)值上的比對(duì)操作, 以確保它們的值是完全一致的。針對(duì)對(duì)象引用領(lǐng)域, 將遞歸調(diào)用方法進(jìn)行處理。針對(duì)float這個(gè)類型的字段, 采取辦法是先調(diào)用Float點(diǎn)括號(hào)來把它轉(zhuǎn)換成整數(shù)類型, 之后再使用雙等號(hào)來進(jìn)行比較操作。對(duì)于域這個(gè)概念, 我們是先借助點(diǎn)括號(hào)的方法把它轉(zhuǎn)換成為長(zhǎng)整型數(shù)據(jù), 接著再去執(zhí)行相等性檢查操作來比較它們的值。針對(duì)數(shù)組類型這一種數(shù)據(jù)領(lǐng)域的具體情況, 需要去調(diào)用對(duì)應(yīng)的那個(gè)方法。二 ()方法1.當(dāng)進(jìn)行改寫某個(gè)內(nèi)容的操作的時(shí)候, 往往總是要針對(duì)另一個(gè)方面或者內(nèi)容來進(jìn)行相應(yīng)的改寫處理工作。參考改寫后的類定義中的方法, 邏輯上可能認(rèn)為兩個(gè)完全不同的實(shí)例是相等的, 可要是用某個(gè)特定的.方法來判斷的話, 它們就僅僅是兩個(gè)分開的對(duì)象而已, 這樣一來, 就違背了這樣的規(guī)定, 就是相等的東西, 它們的散列碼也得一樣才行。2.設(shè)計(jì)()將某個(gè)不是零的一個(gè)常數(shù)數(shù)值, 比如說17這一個(gè)數(shù)字, 給保存到一個(gè)int類型的一個(gè)變量里面去。對(duì)于該對(duì)象里面的每一個(gè)關(guān)鍵的域 f 來說, 這里所說的關(guān)鍵的域 f, 意思就是在具體的方法里面會(huì)被考慮到的那一個(gè)具體的字段。這個(gè)類型的計(jì)算方法是將表達(dá)式 f?? 0 : 1 進(jìn)行計(jì)算。對(duì)于byte型數(shù)據(jù), 對(duì)于char型數(shù)據(jù), 對(duì)于short型數(shù)據(jù), 都需要執(zhí)行轉(zhuǎn)換為int型的計(jì)算。這是一個(gè)long類型的數(shù)據(jù), 計(jì)算的過程是先對(duì)它進(jìn)行強(qiáng)制轉(zhuǎn)換變?yōu)閕nt類型, 然后把這個(gè)數(shù)據(jù)和它的右移32位之后的結(jié)果按位異或。這是一個(gè)float浮點(diǎn)數(shù)類型, 在計(jì)算的時(shí)候需要使用Float方法。這種類型, 進(jìn)行計(jì)算操作, 最終獲取到一個(gè)長(zhǎng)整型的數(shù)值結(jié)果, 隨后再次執(zhí)行該步驟。對(duì)象引用, 遞歸調(diào)用該對(duì)象的所屬類的成員方法。數(shù)組域?qū)ζ渲忻總€(gè)元素調(diào)用它的方法。把通過上面步驟計(jì)算得到的那個(gè)散列碼, 存到名為c的那個(gè)整型變量里面去, 然后接下來去執(zhí)行等于三十七乘以之前某個(gè)值再加上這個(gè)c的那一步計(jì)算操作。執(zhí)行返回這一動(dòng)作。三 ()方法關(guān)于我們所使用的各種方法, 這些方法在我們?nèi)粘I畹母鱾€(gè)領(lǐng)域里面都被普遍應(yīng)用著, 可以說是占據(jù)了一個(gè)極其重要位置的小型知識(shí)點(diǎn)類別, 這里我就給大家做一個(gè)大體上面的講解和說明。我們翻閱了API這一文檔, 查看那個(gè)名稱為java.lang.類的這些方法, 它們?cè)敿?xì)的描述是這樣子寫著的:()這是關(guān)于這一事項(xiàng)的一個(gè)方面。在里面提到的時(shí)候, 有這樣一種情況, 那就是“這個(gè)”被提及的這個(gè)內(nèi)容。這一個(gè)東西本應(yīng)該是屬于某一個(gè)特定對(duì)象的, 但是大家普遍認(rèn)為這對(duì)于任意一個(gè)對(duì)象來說都是很容易去閱讀的這樣一個(gè)狀態(tài)。因此可以說是有必要讓所有的相關(guān)方都參與到這一件事兒的處理過程中來。The for class a of the name of the class of which the is an , the at-sign , and the of the hash code of the . In other words, this a equal to the value of:首先(), 然后()接著是()最后是。(())。:這是一個(gè)a的部分的。我們對(duì)上述內(nèi)容進(jìn)行大致性的解析與翻譯處理。該函數(shù)會(huì)返回一個(gè)字符串, 這個(gè)字符串是能夠代表當(dāng)前對(duì)象的。通常來說, 這個(gè)方法返回的是對(duì)象的“文本表達(dá)式”。這種最終獲得的結(jié)果形式是非常簡(jiǎn)潔的。但是, 這樣并不是那種方便人們?nèi)ラ喿x的信息展示方式。因此, 這里鄭重建議大家, 在您自己去編寫子類的時(shí)候, 最好對(duì)這一方法進(jìn)行重新定義與實(shí)現(xiàn)。對(duì)于這個(gè)類來講, 方法返回值是由所屬類的類的名稱、一個(gè)符號(hào)名為的字符、還有這個(gè)對(duì)象的哈希值的沒有符號(hào)的十六進(jìn)制的表達(dá)形式所共同組成的。換句話說, 這個(gè)方法返回來的這些字符串和下面的這個(gè)方法所返回的那些數(shù)值是完全等同的。這個(gè)表達(dá)式看起來是缺失了具體的函數(shù)名或者變量名稱。返回把那個(gè)對(duì)象的字符串樣子給表現(xiàn)出來。隨后, 我們不妨將目光轉(zhuǎn)向 java.lang. 類中所包含的方法部分, 深入考察一下那些內(nèi)容在程度上是否具有相同之處呢。()This (which is a !) is .by:in:in class:the .我們依舊還是打算進(jìn)行一下翻譯這件事的吧。這個(gè)對(duì)象自己呀, 它已經(jīng)就是一個(gè)字符串了, 那它也就是那個(gè)返回值。說明接口中的方法重寫重寫了中的方法返回那個(gè)句子就是它自己本身所包含的那個(gè)字符串。我們可以看到, 這是一個(gè)類的實(shí)例繼承自另一個(gè)類, 并且對(duì)這個(gè)方法進(jìn)行了重寫操作, 也就是說它擁有了屬于自己的一套具體的邏輯處理方式, 該方法的作用是把調(diào)用者自身轉(zhuǎn)換為一個(gè)字符串形式的內(nèi)容并返回回去, 由于這個(gè)對(duì)象在被當(dāng)作字符串使用時(shí)本身就已經(jīng)具備字符串類型的特征或者說是本質(zhì)上就是字符串類型的一個(gè)具體表現(xiàn), 所以在后續(xù)的操作當(dāng)中或者說在內(nèi)存的管理環(huán)節(jié)里頭, 就完全沒必要去再去生成或者引用一大堆毫無意義的哈希碼值了。為了把這個(gè)事情說得更清楚一些, 我們?cè)谡f明的這段內(nèi)容中可以看出來, 中方法它就是接口的一個(gè)方法, 所以呢, 我們現(xiàn)在就去查看一下 java.lang 這個(gè)接口里面的方法的定義情況是怎樣的。()a the in this in the same order as this . The of the will be the of this .:in class:這是一個(gè)of this of。咱們還是把它翻譯一下。返回值是一個(gè)字符串對(duì)象, 該對(duì)象里面裝著跟這個(gè)序列的順序一樣的所有字符, 這個(gè)字符串的長(zhǎng)度, 就是那個(gè)序列的長(zhǎng)度。重寫的返回這個(gè)字符串是由該序列里面的特定的字符所組成的。第四點(diǎn), 是關(guān)于那個(gè)名為clone這個(gè)方法的內(nèi)容。針對(duì)克隆, 也就是那個(gè)英文單詞叫Clone的事情, Java它有一些那個(gè)限制。1、被克隆的類必須由它來自己去實(shí)現(xiàn)那個(gè)接口, 這樣做是為了表示 .clone() 這個(gè)方法能夠?qū)@一個(gè)類的實(shí)例進(jìn)行字段的復(fù)制, 而且這個(gè)操作是合法的。這個(gè)接口實(shí)際上是一個(gè)標(biāo)識(shí)用的接口, 它里面沒有任何接口方法。2、那些需要去實(shí)現(xiàn)對(duì)應(yīng)的接口的這些類, 它們都應(yīng)該把自己的.clone這個(gè)方法給重新實(shí)現(xiàn)一遍, 也就是要進(jìn)行重寫操作, 至于說它原本的那個(gè)狀態(tài), 是處于受保護(hù)的一個(gè)權(quán)限級(jí)別的。3、在Java.lang.包之中, 那個(gè)被叫做克隆的方法, 它的具體定義是這樣的:clone()請(qǐng)創(chuàng)建并返回那個(gè)對(duì)象的一個(gè)副本, 這是一個(gè)受保護(hù)的方法, 只有在同一個(gè)包里面才可以看到它。按照大家一直以來的習(xí)慣, 返回出來的那個(gè)對(duì)象, 需要去調(diào)用一下 super.clone 這個(gè)方法來得到它。當(dāng)你在執(zhí)行淺拷貝操作的時(shí)候, 意味著你只是在復(fù)制你的該類本身的對(duì)象而你并沒有在該類的屬性之中所包含的其他類對(duì)象的層面上進(jìn)行拷貝動(dòng)作。所謂深拷貝, 這個(gè)意思是指對(duì)包含類自己本身以及屬性類在內(nèi)的所有類都進(jìn)行一個(gè)完整的拷貝操作。把話說的再簡(jiǎn)單一些, 其實(shí)就是淺拷貝操作后產(chǎn)生的那兩個(gè)對(duì)象里的屬性依然去指向同一個(gè)類, 而深拷貝則是讓它們?nèi)慷甲兊帽舜霜?dú)立了。也就是說深拷貝這一行為連帶著把那些關(guān)聯(lián)關(guān)系也給一并拷貝了過來。我們可以把它簡(jiǎn)單地理解為在深拷貝里面會(huì)存在那些數(shù)據(jù)類型中包含了類類型的變量這種情況, 這是因?yàn)樵?java 這門語言里傳遞參數(shù)的形式是基于傳值方式來進(jìn)行操作的。這五個(gè)方面, 其涉及的方法主要是這樣的:大家所知道的是, 這個(gè)方法屬于垃圾回收器運(yùn)行機(jī)制中一個(gè)部分。這個(gè)方法是在垃圾回收器處理它相關(guān)聯(lián)的那些對(duì)象的時(shí)候被觸發(fā)的, 如果在執(zhí)行這個(gè)方法的運(yùn)行期間發(fā)生了那些程序本身沒有辦法進(jìn)行捕獲的異常的情況, 那么垃圾回收機(jī)制就會(huì)停止對(duì)這個(gè)特定對(duì)象的后續(xù)清理工作, 并且這種發(fā)生的異常情況將被系統(tǒng)直接忽略掉, 一直到下一次垃圾回收機(jī)制啟動(dòng)并對(duì)這個(gè)仍然存在的對(duì)象進(jìn)行清理操作的時(shí)候, 這個(gè)方法才會(huì)被重新調(diào)用一遍。請(qǐng)您看一下下邊給出的這幾個(gè)小例子。Java代碼public final class FinallyTest {// 重寫finalize()方法protected void finalize() throws Throwable {System.out.println(執(zhí)行了finalize()方法);}public static void main(String[] args) {FinallyTest ft new FinallyTest();ft null;System.gc();}}運(yùn)行結(jié)果如下? 執(zhí)行了()方法在這個(gè)程序當(dāng)中, 調(diào)用了屬于java.lang.這個(gè)類里面的gc()這個(gè)方法, 由此導(dǎo)致了垃圾回收GC操作被執(zhí)行。當(dāng)垃圾回收器在處理ft這個(gè)對(duì)象的時(shí)候, 調(diào)用了該對(duì)象的某個(gè)方法, 正是由于這個(gè)原因, 所以我們才會(huì)看到上文提到的輸出結(jié)果。實(shí)際上, 調(diào)用.gc()和下面所寫的那一行代碼在效果上是一樣的:Java代碼.().gc();調(diào)用某個(gè)方法的作用, 說白了, 就是給垃圾收集器提個(gè)建議。意思是想讓垃圾收集器趕緊啟動(dòng)起來, 好把那些沒用的對(duì)象清理出去, 從而釋放出占用的內(nèi)存空間。不過, 垃圾收集器到底什么時(shí)候啟動(dòng), 這個(gè)并不是板上釘釘、必須馬上辦的事, 而是得由 JAVA虛擬機(jī)來拿主意決定。這就導(dǎo)致了這么一種情況: 在 JAVA虛擬機(jī)一直運(yùn)行著直到它最終停止運(yùn)行這段時(shí)間里, 有些對(duì)象的該方法可能連一次都沒有被運(yùn)行過。那么問題來了, 究竟該怎么去保證每個(gè)對(duì)象的這個(gè)方法, 都能在 JAVA虛擬機(jī)徹底停止運(yùn)行之前, 一定被調(diào)用成功? 答案就是, 我們可以去調(diào)用類的另一個(gè)方法。Java代碼這是一個(gè)函數(shù)聲明, 函數(shù)名為 void , 參數(shù)為 value , 大括號(hào)內(nèi)有其他代碼。public static void runFinalizersOnExit(boolean value) {//other code}給這個(gè)方法傳入 true 這個(gè)值, 就可以保證當(dāng) Java 虛擬機(jī)停止運(yùn)行的時(shí)候, 對(duì)象的 () 方法一定會(huì)被運(yùn)行。不過遺憾的是, 這個(gè)方法是不安全的。它會(huì)導(dǎo)致有用的對(duì)象 () 被錯(cuò)誤地調(diào)用。因此, 目前大家已經(jīng)不贊成使用這種方法了。由于這個(gè)類屬于某種特定的類別, 所以所有的類都擁有這個(gè)方法, 并且該類的任意子類都可以對(duì)這一方法進(jìn)行重寫操作, 在這個(gè)重寫的過程里面可以進(jìn)行系統(tǒng)資源的釋放動(dòng)作或者是執(zhí)行其他的一些清理工作, 例如關(guān)閉輸入流和輸出流等。六 wait()/()/()在一般情況下, 多個(gè)線程彼此之間是需要協(xié)調(diào)工作的。舉個(gè)具體的例子來講, 瀏覽器里負(fù)責(zé)進(jìn)行圖片顯示的這樣一個(gè)線程, 它如果想要去執(zhí)行顯示圖片的具體任務(wù), 那么就必須等待另一個(gè)下載線程, 這個(gè)下載線程得先把該張圖片給下載完成再說。倘若此時(shí)圖片還沒有全部都下載完畢的話, 那就可以選擇暫時(shí)性暫停, 等到相關(guān)任務(wù)徹底完成了之后, 再去發(fā)出“圖片已經(jīng)準(zhǔn)備充足, 能夠展示”這樣的通知, 緊接著再繼續(xù)往下執(zhí)行后續(xù)的操作。說白了, 上面的邏輯就是這樣的, 如果條件不滿足, 那就去等著, 什么時(shí)候條件滿足了呢, 那個(gè)正在等這個(gè)條件的線程, 就會(huì)被人叫醒。在Java語言里面, 這個(gè)法子能夠?qū)崿F(xiàn), 是靠那個(gè)wait方法以及相關(guān)的等待機(jī)制來完成的, 并且這個(gè)等待的法子, 和鎖這個(gè)東西, 關(guān)系是非常非常緊密的。打個(gè)比方來說:(obj) { while(!) { obj.wait(); } obj.(); }synchronized(obj) {while(!condition) {obj.wait();}obj.doSomething();}當(dāng)線程A獲取了obj鎖之后, 它發(fā)現(xiàn)當(dāng)前條件不滿足情況, 所以沒辦法繼續(xù)執(zhí)行后續(xù)的處理步驟, 緊接著線程A就調(diào)用了wait()方法來進(jìn)行等待操作。然后來看另一個(gè)線程B的情況, 如果在某個(gè)時(shí)段內(nèi), 線程B對(duì)某些特定的條件做出了更改動(dòng)作, 使得原先那個(gè)讓線程A無法滿足的條件現(xiàn)在變得滿足了話, 那么這個(gè)時(shí)候就可以觸發(fā)機(jī)制來喚醒線程A:(obj)等于true, 然后調(diào)用obj的一個(gè)方法。synchronized(obj) {condition true;obj.notify();}有一個(gè)概念是大家需要加以關(guān)注的。在調(diào)用obj.wait()這個(gè)方法之前, 是一定要先得到obj這個(gè)對(duì)象上的鎖的, 所以, 相關(guān)的操作代碼必須是被包裹在針對(duì)obj對(duì)象的 代碼塊里面去執(zhí)行的。在線程A成功調(diào)用obj.wait這個(gè)方法之后, 線程A就把obj的鎖給交出來了, 如果沒有這一步, 線程B就沒法拿到obj的鎖, 那么線程B也就沒法在obj的大括號(hào)所包含的代碼段里面把線程A叫醒。等到obj.wait這個(gè)方法把結(jié)果返回回來的時(shí)候, 線程A還得再次去把obj的鎖給爭(zhēng)下來, 拿到了這把鎖之后, 它才能接著往下干活。如果A1以及A2和A3都處在了調(diào)用obj.wait()的狀態(tài)之中, 那么當(dāng)B去調(diào)用()的時(shí)候, 它只能把A1、A2或者A3這三個(gè)當(dāng)中的某一個(gè)給喚醒掉, 至于具體到底是哪一個(gè)被喚醒了, 這個(gè)要由JVM來最終決定。通過調(diào)用obj.方法可以一次性喚醒A1、A2和A3這三個(gè)線程, 不過若想繼續(xù)執(zhí)行緊隨obj.wait方法之后的后續(xù)代碼語句, 線程必須是成功獲取到obj對(duì)象鎖的那個(gè)線程, 正因如此, 在A1、A2與A3之中僅有其中任意一個(gè)線程才有資格獲得該對(duì)象鎖并把事情做完, 比方說假定是A1搶到了鎖, 那么其余的線程就都得等著, 等到A1把對(duì)obj對(duì)象鎖的使用權(quán)交出去之后, 它們才又接著往下走。當(dāng)程序里的B去訪問那個(gè)obj對(duì)象的時(shí)候, 也就是在運(yùn)行B調(diào)用了obj這個(gè)相關(guān)操作的時(shí)候, 此時(shí)B手里是正拿著obj這個(gè)鎖的, 所以, 雖然A1、A2還有A3這三個(gè)家伙已經(jīng)被喚醒弄好了, 可是它們還是沒辦法拿到obj這把鎖來使用, 直到過了一陣子, 到了B退出了它當(dāng)前占據(jù)的那一塊代碼范圍以后, 才松勁了把obj這塊鎖給放出去了, 這個(gè)時(shí)候, A1、A2和A3當(dāng)中挑一個(gè)出來才有機(jī)會(huì)把這個(gè)鎖給抓住了然后繼續(xù)跑它的程序。