java finalize方法的使用

劍西樓發表於2017-03-21

JAVA程式設計思想》:

Java提供finalize()方法,垃圾回收器準備釋放記憶體的時候,會先呼叫finalize()

          (1).物件不一定會被回收。

       (2).垃圾回收不是解構函式。

       (3).垃圾回收只與記憶體有關。

       (4).垃圾回收和finalize()都是靠不住的,只要JVM還沒有快到耗盡記憶體的地步,它是不會浪費時間進行垃圾回收的。

有時當撤消一個物件時,需要完成一些操作。例如,如果一個物件正在處理的是非Java 資源,如檔案控制程式碼或window 字元字型,這時你要確認在一個物件被撤消以前要保證這些資源被釋放。為處理這樣的狀況,Java 提供了被稱為收尾(finalization )的機制。使用該機制你可以定義一些特殊的操作,這些操作在一個物件將要被垃圾回收程式釋放時執行。

要給一個類增加收尾(finalizer ),你只要定義finalize ( ) 方法即可。Java 回收該類的一個物件時,就會呼叫這個方法。在finalize ( )方法中,你要指定在一個物件被撤消前必須執行的操作。垃圾回收週期性地執行,檢查物件不再被執行狀態引用或間接地通過其他物件引用。就在物件被釋放之前,Java 執行系統呼叫該物件的finalize( ) 方法。

finalize()方法的通用格式如下:

protected void finalize( )
{
// finalization code here
}

其中,關鍵字protected是防止在該類之外定義的程式碼訪問finalize()識別符號。該識別符號和其他識別符號將在第7章中解釋。

理解finalize( ) 正好在垃圾回收以前被呼叫非常重要。例如當一個物件超出了它的作用域時,finalize( ) 並不被呼叫。這意味著你不可能知道何時——甚至是否——finalize( ) 被呼叫。因此,你的程式應該提供其他的方法來釋放由物件使用的系統資源,而不能依靠finalize( ) 來完成程式的正常操作。

注意:如果你熟悉C ,那你知道C 允許你為一個類定義一個撤消函式(destructor ),它在物件正好出作用域之前被呼叫。Java不支援這個想法也不提供撤消函式。finalize() 方法只和撤消函式的功能接近。當你對Java 有豐富經驗時,你將看到因為Java使用垃圾回收子系統,幾乎沒有必要使用撤消函式。

理解finalize()-解構函式的替代者

by Tim Gooch

在許多方面,Java 類似於 C++。Java 的語法非常類似於 C++,Java 有類、方法和資料成員;Java 的類有建構函式; Java 有異常處理。

但是,如果你使用過 C++ 會發現 Java 也丟掉一些可能是你熟悉的特性。這些特性之一就是解構函式。取代使用解構函式,Java 支援finalize() 方法。

在本文中,我們將描述 finalize() 與 C++ 解構函式的區別。另外,我們將建立一個簡單的 Applet 來演示 finalize() 是如何工作的。

最終的界限

與 Java 不同,C++ 支援區域性物件(基於棧)和全域性物件(基於堆)。因為這一雙重支援,C++ 也提供了自動構造和析構,這導致了對建構函式和解構函式的呼叫,(對於堆物件)就是記憶體的分配和釋放。

在 Java 中,所有物件都駐留在堆記憶體,因此區域性物件就不存在。結果,Java 的設計者覺得不需要解構函式(象 C++ 中所實現的)。

取而代之,Java 定義了一個特殊的方法叫做finalize() ,它提供了 C++ 解構函式的一些功能。但是,finalize() 並不完全與 C++ 的解構函式一樣,並可以假設它會導致一系列的問題。finalize() 方法作用的一個關鍵元素是 Java 的垃圾回收器。

垃圾回收器

在 C/C++、Pascal和其他幾種多種用途的程式語言中,開發者有責任在記憶體管理上發揮積極的作用。例如,如果你為一個物件或資料結構分配了記憶體,那麼當你不再使用它時必須釋放掉該記憶體。

在 Java 中,當你建立一個物件時,Java 虛擬機器(JVM)為該物件分配記憶體、呼叫建構函式並開始跟蹤你使用的物件。當你停止使用一個物件(就是說,當沒有對該物件有效的引用時),JVM 通過垃圾回收器將該物件標記為釋放狀態。

當垃圾回收器將要釋放一個物件的記憶體時,它呼叫該物件的finalize() 方法(如果該物件定義了此方法)。垃圾回收器以獨立的低優先順序的方式執行,只有當其他執行緒掛起等待該記憶體釋放的情況出現時,它才開始執行釋放物件的記憶體。(事實上,你可以呼叫System.gc() 方法強制垃圾回收器來釋放這些物件的記憶體。)

在以上的描述中,有一些重要的事情需要注意。首先,只有當垃圾回收器釋放該物件的記憶體時,才會執行finalize()。如果在 Applet 或應用程式退出之前垃圾回收器沒有釋放記憶體,垃圾回收器將不會呼叫finalize()。

其次,除非垃圾回收器認為你的 Applet 或應用程式需要額外的記憶體,否則它不會試圖釋放不再使用的物件的記憶體。換句話說,這是完全可能的:一個 Applet 給少量的物件分配記憶體,沒有造成嚴重的記憶體需求,於是垃圾回收器沒有釋放這些物件的記憶體就退出了。

顯然,如果你為某個物件定義了finalize() 方法,JVM 可能不會呼叫它,因為垃圾回收器不曾釋放過那些物件的記憶體。呼叫System.gc() 也不會起作用,因為它僅僅是給 JVM 一個建議而不是命令。

finalize() 有什麼優點呢?

如果finalize() 不是解構函式,JVM 不一定會呼叫它,你可能會疑惑它是否在任何情況下都有好處。事實上,在 Java 1.0 中它並沒有太多的優點。

根據 Java 文件,finalize() 是一個用於釋放非 Java 資源的方法。但是,JVM 有很大的可能不呼叫物件的finalize() 方法,因此很難證明使用該方法釋放資源是有效的。

Java 1.1 通過提供一個System.runFinalizersOnExit() 方法部分地解決了這個問題。(不要將這個方法與 Java 1.0 中的System.runFinalizations() 方法相混淆。)不象System.gc() 方法那樣,System.runFinalizersOnExit() 方法並不立即試圖啟動垃圾回收器。而是當應用程式或 Applet 退出時,它呼叫每個物件的finalize() 方法。

正如你可能猜測的那樣,通過呼叫System.runFinalizersOnExit() 方法強制垃圾回收器清除所有獨立物件的記憶體,當清除程式碼執行時可能會引起明顯的延遲。現在建立一個示例 Applet 來演示 Java 垃圾回收器和finalize() 方法是如何相互作用的。

回收垃圾

通過使用Java Applet Wizard 建立一個新的 Applet 開始。當提示這樣做時,輸入final_things作為 Applet 名,並選擇不要生成原始檔註釋。

接下來,在Java Applet Wizard 進行第三步,不要選擇多執行緒選項。在第五步之前,根據需要修改 Applet 的描述。

當你單擊Finish 後,Applet Wizard 將生成一個新的工作空間,併為該專案建立預設的 Java 檔案。從列表 A 中選擇適當的程式碼輸入(我們已經突出顯示了你需要輸入的程式碼)。

當你完成程式碼的輸入後,配置Internet 瀏覽器將System.out 的輸出資訊寫到Javalog.txt 檔案中。(在IE 選項對話方塊的高階頁面中選擇起用 Java Logging。)

編譯並執行該 Applet。然後,等待 Applet 執行(你將在狀態列中看到 Applet 已啟動的資訊),退出瀏覽器,並開啟Javalog.txt 檔案。你將會發現類似於下列行的資訊:

        1000 things constructed

        0 things finalized

正如你能夠看到的那樣,建立了1,000個物件仍然沒有迫使垃圾回收器開始回收空間,即使在 Applet 退出時也沒有物件被使用。

現在,刪除在stop() 方法第一行中的註釋符以起用System.gc() 方法。再次編譯並執行該 Applet ,等待 Applet 完成執行,並退出瀏覽器。當你再次開啟Javalog.txt 檔案,你將看到下列行:

        1000 things constructed

        963 things finalized

這次,垃圾回收器認為大多數物件未被使用,並將它們回收。按順序,當垃圾回收器開始釋放這些物件的記憶體時,JVM 呼叫它們的finalize() 方法。

繼承finalize()?

順便,如果你在類中定義了finalize() ,它將不會自動呼叫基類中的方法。在我們討論了finalize() 與 C++ 的解構函式的不同點後,對這個結論不會驚訝,因為為某個類定製的清除程式碼另一個類不一定會需要。

如果你決定要通過派生一個類的finalize() 方法來呼叫基類中的finalize() 方法,你可以象其他繼承方法一樣處理。

        protected void finalize()

        {

          super.finalize();

          // other finalization code...

        }

除了允許你控制是否執行清除操作外,這個技術還使你可以控制當前類的finalize() 方法何時執行。

結論

然而有益的是,Java 的自動垃圾回收器不會失去平衡。作為便利的代價,你不得不放棄對系統資源釋放的控制。不象 C++ 中的解構函式,Java Applet 不會自動執行你的類中的finalize() 方法。事實上,如果你正在使用 Java 1.0,即使你試圖強制它呼叫finalize() 方法,也不能確保將呼叫它。

因此,你不應當依靠finalize() 來執行你的 Applet 和應用程式的資源清除工作。取而代之,你應當明確的清除那些資源或建立一個try...finally 塊(或類似的機制)來實現。



finalize方法是與Java程式設計中的垃圾回收器有關係。即:當一個物件變成一個垃圾物件的時候,如果此物件的記憶體被回收,那麼就可以呼叫系統中定義的finalize方法來完成

當然,Java的記憶體回收可以由JVM來自動完成。如果你手動使用,則可以使用上面的方法。

舉例說明:

  1. public class FinalizationDemo {  
  2.     public static void main(String[] args) {  
  3.         Cake c1 = new Cake(1);  
  4.         Cake c2 = new Cake(2);  
  5.         Cake c3 = new Cake(3);  
  6.           
  7.         c2 = c3 = null;  
  8.         System.gc(); //Invoke the Java garbage collector  
  9.     }  
  10. }  
  11.   
  12. class Cake extends Object {  
  13.     private int id;  
  14.     public Cake(int id) {  
  15.         this.id = id;  
  16.         System.out.println("Cake Object " + id + "is created");  
  17.     }  
  18.       
  19.     protected void finalize() throws java.lang.Throwable {  
  20.         super.finalize();  
  21.         System.out.println("Cake Object " + id + "is disposed");  
  22.     }  
  23. }  


結果執行:
  1. C:\1>java FinalizationDemo  
  2. Cake Object 1is created  
  3. Cake Object 2is created  
  4. Cake Object 3is created  
  5. Cake Object 3is disposed  
  6. Cake Object 2is disposed  


final

修飾符(關鍵字)如果一個類被宣告為final,意味著它不能再派生出新的子類,不能作為父類被繼承。因此一個類不能既被宣告為 abstract的,又被宣告為final的。將變數或方法宣告為final,可以保證它們在使用中不被改變。被宣告為final的變數必須在宣告時給定初值,而在以後的引用中只能讀取,不可修改。被宣告為final的方法也同樣只能使用,不能過載。 


 

finally

異常處理時提供 finally 塊來執行任何清除操作。如果丟擲一個異常,那麼相匹配的 catch 子句就會執行,然後控制就會進入 finally 塊(如果有的話)。一般異常處理塊需要。


 

finalize

方法名。Java 技術允許使用 finalize() 方法在垃圾收集器將物件從記憶體中清除出去之前做必要的清理工作。這個方法是由垃圾收集器在確定這個物件沒有被引用時對這個物件呼叫的。它是在 Object 類中定義的,因此所有的類都繼承了它。子類覆蓋 finalize() 方法以整理系統資源或者執行其他清理工作。finalize() 方法是在垃圾收集器刪除物件之前對這個物件呼叫的。 

Java中所有類都從Object類中繼承finalize()方法。

當垃圾回收器(garbage colector)決定回收某物件時,就會執行該物件的finalize()方法。值得C++程式設計師注意的是,finalize()方法並不能等同與解構函式。Java中是沒有解構函式的。C++的解構函式是在物件消亡時執行的。由於C++沒有垃圾回收,物件空間手動回收,所以一旦物件用不到時,程式設計師就應當把它delete()掉。所以解構函式中經常做一些檔案儲存之類的收尾工作。但是在Java中很不幸,如果記憶體總是充足的,那麼垃圾回收可能永遠不會進行,也就是說filalize()可能永遠不被執行,顯然指望它做收尾工作是靠不住的。

那麼finalize()究竟是做什麼的呢?它最主要的用途是回收特殊渠道申請的記憶體。Java程式有垃圾回收器,所以一般情況下記憶體問題不用程式設計師操心。但有一種JNI(Java Native Interface)呼叫non-Java程式(C或C++),finalize()的工作就是回收這部分的記憶體。

僅供學習參考,原文出自:http://blog.csdn.NET/carolzhang8406/article/details/6705831

相關文章