Java finalize方法使用

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來自動完成。如果你手動使用,則可以使用上面的方法。

舉例說明:

[java] view plaincopy
  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. }  


結果運行:
[java] view plaincopy
  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()的工作就是回收這部分的內存。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章