Java問答:終極父類(2)—上篇

我以前發佈了關於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:實踐finalize()

代碼清單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的這篇文章能夠做爲一個很好的參考手冊。

相關文章
相關標籤/搜索