|
![]() |
名片设计 CorelDRAW Illustrator AuotoCAD Painter 其他软件 Photoshop Fireworks Flash |
|
理解finalize()-析构函数替代者 在许多方面,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"); } } 返回类别: 教程 上一教程: 用Java简朴实现文件分割与合并 下一教程: Java代码编写的一般性指导 (转贴) 您可以阅读与"理解finalize()-析构函数替代者"相关的教程: · struts的整体理解 · 递归函数之JAVA演绎 · 有用的字符串函数 · 从C/C++迁移到PHP??判定字符类型的函数 · J2EE初学者到精通必须需要理解的问题 |
![]() ![]() |
快精灵印艺坊 版权所有 |
首页![]() ![]() ![]() ![]() ![]() ![]() ![]() |