Java有析构函数吗?我好像找不到任何关于这个的文件。如果没有,我怎样才能达到同样的效果?
为了使我的问题更具体,我正在编写一个处理数据的应用程序,规范说应该有一个“重置”按钮,使应用程序恢复到最初的启动状态。但是,除非应用程序关闭或按下重置按钮,否则所有数据必须是“活的”。
作为一个通常的C/ c++程序员,我认为这是微不足道的实现。(因此我打算最后实现它。)我构造了我的程序,使所有“可重置”的对象都在同一个类中,这样当按下重置按钮时,我就可以销毁所有“活动”对象。
我在想,如果我所做的只是解除对数据的引用,并等待垃圾收集器收集它们,如果我的用户重复输入数据并按下重置按钮,是否会出现内存泄漏?我还在想,既然Java作为一种语言相当成熟,应该有一种方法来防止这种情况发生或优雅地解决这个问题。
首先,请注意,由于Java是垃圾收集的,因此很少需要对对象销毁做任何事情。首先是因为你通常没有任何托管资源可以释放,其次是因为你无法预测它何时或是否会发生,所以当你需要“一旦没有人再使用我的对象”就发生的事情是不合适的。
你可以使用java.lang.ref.PhantomReference在一个对象被销毁后得到通知(实际上,说它已经被销毁可能有点不准确,但如果对它的幻影引用处于队列中,那么它就不再是可恢复的,这通常是一样的)。常见的用法是:
Separate out the resource(s) in your class that need to be destructed into another helper object (note that if all you're doing is closing a connection, which is a common case, you don't need to write a new class: the connection to be closed would be the "helper object" in that case).
When you create your main object, create also a PhantomReference to it. Either have this refer to the new helper object, or set up a map from PhantomReference objects to their corresponding helper objects.
After the main object is collected, the PhantomReference is queued (or rather it may be queued - like finalizers there is no guarantee it ever will be, for example if the VM exits then it won't wait). Make sure you're processing its queue (either in a special thread or from time to time). Because of the hard reference to the helper object, the helper object has not yet been collected. So do whatever cleanup you like on the helper object, then discard the PhantomReference and the helper will eventually be collected too.
还有finalize(),它看起来像析构函数,但行为并不像析构函数。这通常不是一个好的选择。
这里有很多很好的答案,但还有一些关于为什么应该避免使用finalize()的额外信息。
如果JVM由于System.exit()或Runtime.getRuntime().exit()而退出,终止器将不会默认运行。From Javadoc for Runtime.exit():
虚拟机的关闭顺序由两个阶段组成。在第一阶段,所有已注册的shutdown钩子(如果有的话)将以某种未指定的顺序启动,并允许并发运行,直到它们完成。在第二阶段中,如果启用了退出时终止,则运行所有未调用的终结器。完成此操作后,虚拟机将停止。
您可以调用System.runFinalization(),但它只会“尽最大努力完成所有未完成的终结”——并不能保证。
有一个System.runFinalizersOnExit()方法,但不要使用它——它是不安全的,很久以前就已经弃用了。
应该避免使用finalize()方法。它们不是一种可靠的资源清理机制,滥用它们可能会在垃圾收集器中引起问题。
如果你在对象中需要一个释放调用,比如释放资源,使用显式的方法调用。这个约定可以在现有的api中看到(例如Closeable, Graphics.dispose(), Widget.dispose()),通常通过try/finally调用。
Resource r = new Resource();
try {
//work
} finally {
r.dispose();
}
尝试使用已处理对象应该抛出运行时异常(参见IllegalStateException)。
编辑:
我在想,如果我所做的只是
来解除对数据的引用并等待
垃圾收集器来收集它们,
会不会有内存泄漏,如果我的
用户反复输入数据和
按了重置键?
一般来说,您所需要做的就是取消对对象的引用——至少,这是它应该工作的方式。如果您担心垃圾收集,请查看Java SE 6 HotSpot[tm]虚拟机垃圾收集调优(或您的JVM版本的等效文档)。
我过去主要处理c++,这也是我寻找析构函数的原因。我现在经常使用JAVA。我所做的,可能对每个人来说都不是最好的,但我实现了我自己的析构函数通过将所有值重置为0或通过函数的默认值。
例子:
public myDestructor() {
variableA = 0; //INT
variableB = 0.0; //DOUBLE & FLOAT
variableC = "NO NAME ENTERED"; //TEXT & STRING
variableD = false; //BOOL
}
理想情况下,这并不适用于所有情况,但在有全局变量的情况下,只要你没有大量的全局变量,它就可以工作。
我知道我不是最好的Java程序员,但它似乎对我有用。