我以前發佈了關於java.lang.Object類及其方法的一系列文章。在介紹了Object以後,咱們又探究了clone()和euqals()方法。在這篇文章中,咱們將繼續討論Object中的finalize()、getClass()和hashCode()方法。 html
問: finalize()方法是用來作什麼的? java
答: finalize()方法能夠被子類對象所覆蓋,而後做爲一個終結者,當GC被調用的時候完成最後的清理工做(例如釋放系統資源之類)。這就是終止。默認的finalize()方法什麼也不作,當被調用時直接返回。 數組
對於任何一個對象,它的finalize()方法都不會被JVM執行兩次。若是你想讓一個對象可以被再次調用的話(例如,分配它的引用給一個靜態變量),注意當這個對象已經被GC回收的時候,finalize()方法不會被調用第二次。 安全
問: 有人說要避免使用finalize()方法,這是真的嗎? ide
答: 一般來說,你應該儘可能避免使用finalize()。相對於其餘JVM實現,終結器被調用的狀況較少——多是由於終結器線程的優先級別較低的緣由。若是你依靠終結器來關閉文件或者其餘系統資源,可能會將資源耗盡,當程序試圖打開一個新的文件或者新的系統資源的時候可能會崩潰,就由於這個緩慢的終結器。 ui
問: 應該使用什麼來替代終結器? this
答: 提供一個明確的用來銷燬這個對象的方法(例如,java.io.FileInputStream的void close()方法),而且在代碼中使用try - finally結構來調用這個方法,以確保不管有沒有異常從try中拋出,都會銷燬這個對象。參考下面釋放鎖的代碼: spa
1
2
3
4
5
6
7
8
9
10
|
Lock l = ...;// ... is a placeholder for the actual lock-acquisition code
l.lock();
try
{
// access the resource protected by this lock
}
finally
{
l.unlock();
}
|
這段代碼保證了不管try是正常結束仍是拋出異常都會釋放鎖。 線程
問: 什麼狀況下適合使用終結器? code
答: 終結器能夠做爲一個安全保障,以防止聲明的終結方法(像是java.io.FileOutputStream對象的close()方法或者java.util.concurrent.Lock對象的Lock()方法)沒有被調用。萬一這種狀況出現,終結器能夠在最後被調用,釋放臨街資源。
問: 怎麼寫finalize()?
答: 能夠遵循下面這個模式寫finalize()方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
@Override
protectedvoidfinalize()throwsThrowable
{
try
{
// Finalize the subclass state.
// ...
}
finally
{
super.finalize();
}
}
|
子類終結器通常會經過調用父類的終結器來實現。當被調用時,先執行try模塊,而後再在對應的finally中調用super.finalize();這就保證了不管try會不會拋出異常父類都會被銷燬。
問: 若是finalize()拋出異常會怎樣?
答: 當finalize()拋出異常的時候會被忽略。並且,對象的終結將在此中止,致使對象處在一種不肯定的狀態。若是另外一個進程試圖使用這個對象的話,將產生不肯定的結果。一般拋出異常將會致使線程終止併產生一個提示信息,可是從finalize()中拋出異常就不會。
問: 我想實踐一下finalize()方法,能提供一個範例嗎?
答: 參考代碼清單1.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
classLargeObject
{
byte[] memory =newbyte[1024*1024*4];
@Override
protectedvoidfinalize()throwsException
{
System.out.println("finalized");
}
}
publicclassFinalizeDemo
{
publicstaticvoidmain(String[] args)
{
while(true)
newLargeObject();
}
}
|
代碼清單1中的代碼寫了一個FinalizeDemo程序,重複地對largeObject類實例化。每個Largeobject對象將產生4M的數組。在這種狀況下,因爲沒有指向該對象的引用,因此LargeObject對象將被GC回收。
GC會調用對象的finalize()方法來回收對象。LargeObject重載的finalize()方法被調用的時候會想標準輸出流打印一條信息。它沒有調用父類的finalize()方法,由於它的父類是Object,即父類的finalize()方法什麼也不作。
編譯(javac FinalizeDemo.java)並運行(java FinalizeDemo)代碼清單1.當我在個人環境下(64位win7平臺)使用JDK7u6來編譯運行的時候,我看到一列finalized的信息。可是在JDK8的環境下時,在幾行finalized以後拋出了java.lang.OutOfMemoryError。
由於finalize()方法對於虛擬機來講不是輕量級的程序,因此不能保證你必定會在你的環境下觀察到輸出信息。
問: getClass()方法是用來作什麼的?
答: 經過getClass()方法能夠獲得一個和這個類有關的java.lang.Class對象。返回的Class對象是一個被static synchronized方法封裝的表明這個類的對象;例如,static sychronized void foo(){}。這也是指向反射API。由於調用getClass()的對象的類是在內存中的,保證了類型安全。
問: 還有其餘方法獲得Class對象嗎?
答: 獲取Class對象的方法有兩種。可使用類字面常量,它的名字和類型相同,後綴爲.class;例如,Account.class。另一種就是調用Class的forName()方法。類字面常量更加簡潔,而且編譯器強制類型安全;若是找不到指定的類編譯就不會經過。經過forname()能夠動態地經過指定包名載入任意類型地引用。可是,不能保證類型安全,可能會致使Runtime異常。
問: 實現equals()方法的時候,getClass()和instanceof哪個更好?
答: 使用getClass()仍是instanceof的話題一直都是Java社區爭論的熱點,Angelika Langer的Secrets of equals – Part 1這片文章能夠幫助你作出選擇。關於正確覆蓋equals()方法(例如保證對稱性)的討論,Lang的這篇文章能夠做爲一個很好的參考手冊。