Java有析构函数吗?我好像找不到任何关于这个的文件。如果没有,我怎样才能达到同样的效果?
为了使我的问题更具体,我正在编写一个处理数据的应用程序,规范说应该有一个“重置”按钮,使应用程序恢复到最初的启动状态。但是,除非应用程序关闭或按下重置按钮,否则所有数据必须是“活的”。
作为一个通常的C/ c++程序员,我认为这是微不足道的实现。(因此我打算最后实现它。)我构造了我的程序,使所有“可重置”的对象都在同一个类中,这样当按下重置按钮时,我就可以销毁所有“活动”对象。
我在想,如果我所做的只是解除对数据的引用,并等待垃圾收集器收集它们,如果我的用户重复输入数据并按下重置按钮,是否会出现内存泄漏?我还在想,既然Java作为一种语言相当成熟,应该有一种方法来防止这种情况发生或优雅地解决这个问题。
随着Java 1.7的发布,现在有了使用try-with-resources块的额外选项。例如,
public class Closeable implements AutoCloseable {
@Override
public void close() {
System.out.println("closing...");
}
public static void main(String[] args) {
try (Closeable c = new Closeable()) {
System.out.println("trying...");
throw new Exception("throwing...");
}
catch (Exception e) {
System.out.println("catching...");
}
finally {
System.out.println("finalizing...");
}
}
}
如果执行这个类,则在try块剩余时,在catch块和finally块执行之前,将执行c.close()。与finalize()方法不同,close()保证被执行。但是,不需要在finally子句中显式地执行它。
想想最初的问题……我认为我们可以从所有其他学习过的答案中得出结论,也可以从Bloch的《有效Java》中得出结论,第7项“避免终结器”,以一种不适合Java语言的方式寻求合理问题的解决方案……
... OP实际上想要的是将所有需要重置的对象保存在一种“playpen”中,而所有其他不可重置的对象只能通过某种访问器对象引用该对象,这难道不是一个非常明显的解决方案吗?
然后当你需要“重置”时,你断开现有的游戏笔并创建一个新的游戏笔:游戏笔中所有的对象都被抛到一边,永远不会返回,有一天会被GC收集。
如果这些对象中的任何一个是可关闭的(或者不是,但有一个关闭方法),你可以在它们被创建(可能打开)时将它们放在游戏笔的一个袋子中,访问器在切断游戏笔之前的最后一个动作将是通过所有的关闭对象关闭它们……?
代码可能看起来像这样:
accessor.getPlaypen().closeCloseables();
accessor.setPlaypen( new Playpen() );
closeCloseables可能是一个阻塞方法,可能涉及一个闩锁(例如CountdownLatch),以处理(并等待)任何特定于Playpen的线程中的任何可运行对象/可调用对象在适当的时候结束,特别是在JavaFX线程中。
首先,请注意,由于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(),它看起来像析构函数,但行为并不像析构函数。这通常不是一个好的选择。
不,这里没有析构函数。原因是所有Java对象都是堆分配和垃圾收集。没有显式的释放(即c++的delete操作符),就没有实现真正析构函数的合理方法。
Java does support finalizers, but they are meant to be used only as a safeguard for objects holding a handle to native resources like sockets, file handles, window handles, etc. When the garbage collector collects an object without a finalizer it simply marks the memory region as free and that's it. When the object has a finalizer, it's first copied into a temporary location (remember, we're garbage collecting here), then it's enqueued into a waiting-to-be-finalized queue and then a Finalizer thread polls the queue with very low priority and runs the finalizer.
当应用程序退出时,JVM停止而不等待挂起的对象完成,因此实际上不能保证终结器将运行。