《JAVA編程思想》: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
}this
其中,關鍵字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]
結果運行:
[java]
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()的工做就是回收這部分的內存。