程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 理解finalize()-析構函數替代者

理解finalize()-析構函數替代者

編輯:關於JAVA

在許多方面,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 塊(或類似的機制)來實現。

列表 A: final_things.Java

import Java.applet.*;

import Java.awt.*;

class thing

{

public static int thingcount = 0;

public static int thingfinal = 0;

public thing()

{

++thingcount;

}

protected void finalize()

{

++thingfinal;

}

}

public class final_things extends Applet

{

public final_things()

{

}

public String getAppletInfo()

{

return "Name: final_thing\r\n" +

"Author: Tim Gooch\r\n" +

"Created with Microsoft " +

"Visual J++ Version 1.1";

}

public void init()

{

resize(320, 240);

}

public void destroy()

{

}

public void paint(Graphics g)

{

g.drawString("Created with Microsoft" +

"Visual J++ Version 1.1", 10, 20);

}

public void start()

{

while(thing.thingfinal < 1)

{

new thing();

}

}

public void stop()

{

// System.gc();

System.out.println(thing.thingcount +

" things constructed");

System.out.println(thing.thingfinal +

" things finalized");

}

}

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved