有没有Java的析构函数?

有没有Java的析构函数? 我似乎无法find任何文件。 如果没有,我怎样才能达到同样的效果?

为了使我的问题更具体,我正在编写一个处理数据的应用程序,并且规范说应该有一个“重置”button,使应用程序恢复到刚刚启动的状态。 但是,除非应用程序已closures或按下重置button,否则所有数据都必须“有效”。

通常是C / C ++程序员,我认为这将是微不足道的实施。 (因此,我计划最后实现它。)我组织我的程序,使所有“可重置”对象将在同一个类中,以便按下重置button时可以销毁所有“实时”对象。

我在想,如果我所做的只是解引用数据,并等待垃圾收集器收集它们,如果我的用户重复input数据并按下重置button,是不是会有内存泄漏? 我也在想,因为Java作为一种语言已经相当成熟,所以应该有一种方法来防止这种情况发生,或者优雅地解决这个问题。

由于Java是垃圾收集语言,因此无法预测何时(甚至是否)某个对象将被销毁。 因此,不存在直接的析构函数。

有一个名为finalize的inheritance方法,但这完全是由垃圾收集器决定的。 因此,对于需要明确整理的类,约定是定义一个close方法,并且只使用finalize来进行完整性检查(即,如果close没有被调用,现在就执行并logging一个错误)。

有一个问题引起了最近定稿的深入讨论 ,所以如果需要的话应该提供更多的深度…

不,没有破坏者在这里。 原因是所有的Java对象都是堆分配和垃圾收集。 没有明确的释放(即C ++的删除操作符)没有明智的方法来实现真正的析构函数。

Java确实支持终结器,但是它们仅仅被用作为持有本地资源(如套接字,文件句柄,窗口句柄等)句柄的对象的保护措施。当垃圾收集器收集没有终结器的对象时,它只会标记内存地区是免费的,就是这样。 当对象有一个终结器时,它首先被复制到一个临时位置(记住,我们在这里进行垃圾回收),然后将它排入一个待定位队列,然后一个终结器线程以非常低的优先级轮询队列并运行终结器。

当应用程序退出时,JVM会停止而不等待挂起的对象被终止,所以几乎不能保证终结器将永远运行。

如果您使用Java 7,请查看try-with-resources语句。 例如:

 try (BufferedReader br = new BufferedReader(new FileReader(path))) { System.out.println(br.readLine()); } catch (Exception e) { ... } finally { ... } 

这里不再需要的资源在BufferedReader.close()方法中被释放。 您可以创build自己的实现AutoCloseable的类,并以类似的方式使用它。

这个陈述比在代码结构化方面的finalize更有限,但是同时它使得代码更易于理解和维护。 另外,不能保证在应用程序的运行时间内调用finalize方法。

应该避免使用finalize()方法。 他们不是一个可靠的资源清理机制,并可能通过滥用垃圾收集器来引起问题。

如果您需要在对象中释放呼叫,请释放资源,请使用明确的方法调用。 这个约定可以在现有的API中看到(例如Closeable , Graphics.dispose() , Widget.dispose() ),通常通过try / finally调用。

 Resource r = new Resource(); try { //work } finally { r.dispose(); } 

尝试使用处置对象应该抛出一个运行时exception(请参阅IllegalStateException )。


编辑:

我在想,如果我所做的只是解引用数据,并等待垃圾收集器收集它们,如果我的用户重复input数据并按下重置button,是不是会有内存泄漏?

一般来说,所有你需要做的是解除对象的引用 – 至less,这是它应该工作的方式。 如果您担心垃圾收集,请查看Java SE 6 HotSpot [tm]虚拟机垃圾收集调整 (或您的JVM版本的等效文档)。

随着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块被留下时,在catchfinally块被执行之前, c.close()将被执行。 与finalize()方法不同, close()保证被执行。 但是,在finally子句中没有必要显式执行它。

我完全同意其他答案,说不依靠最终确定的执行。

除了try-catch-finally块之外,您还可以使用Runtime#addShutdownHook (在Java 1.6中引入)来执行程序中的最终清理。

这与析构函数是不一样的 ,但是可以实现一个closures钩子,它具有注册的监听器对象,可以调用清理方法(closures持久数据库连接,移除文件locking等) – 通常在析构函数 。 再次 – 这不是一个替代构造函数,但在某些情况下,你可以用这个方法来达到想要的function。

这样做的好处就是解开了你的程序的其余部分。

不, java.lang.Object#finalize是最接近你可以得到的。

但是,如果(如果)被调用,则不能保证。
请参阅: java.lang.Runtime#runFinalizersOnExit(boolean)

首先请注意,由于Java是垃圾收集的,因此很less需要做任何关于对象破坏的事情。 首先是因为你通常没有任何pipe理资源可以免费,其次是因为你无法预测什么时候会发生什么事情,因此,一旦没有人再使用我的对象,就不适合你需要发生的事情”。

你可以在使用java.lang.ref.PhantomReference销毁一个对象之后得到通知(实际上,它已经被销毁了,可能会有些不准确,但是如果一个对它的幻影引用排队了,那么它就不能被恢复了,这通常相当于一样的东西)。 常用的是:

  • 把需要被破坏的资源分离出来放到另一个帮助对象中(注意,如果你所做的只是closures一个连接,这是一个常见的情况,你不需要写一个新的类:在这种情况下,要closures的连接将是“帮助对象”)。
  • 当你创build你的主对象的时候,还要为它创build一个PhantomReference。 要么引用新的帮助对象,要么build立一个从PhantomReference对象到相应的帮助对象的映射。
  • 在收集主对象之后,PhantomReference被排队(或者更确切地说它可能是排队的 – 就像终结者一样,不能保证永远不会有,比如如果虚拟机退出,那么它就不会等待)。 确保你正在处理它的队列(在一个特殊的线程或不时)。 由于对辅助对象的强烈引用,辅助对象还没有被收集。 因此,在助手对象上做任何你喜欢的清理工作,然后放弃PhantomReference,帮助者最终也会被收集起来。

还有finalize(),它看起来像一个析构函数,但不像一个。 这通常不是一个好的select。

我很抱歉,如果这从主要议题,但java.util.Timer(SE6)文档说:

“在最后一次对Timer对象的实时引用消失并且所有未完成的任务已经完成执行之后,计时器的任务执行线程将优雅地终止(并且成为垃圾收集的对象),但是这可能需要很长的时间。任务执行线程不作为守护进程线程运行,所以它能够保持应用程序不终止,如果调用者想要快速终止一个定时器的任务执行线程,调用者应该调用定时器的取消方法…“

我想在拥有Timer的class上取消最后的参考(或之前的immeditalesky)。 这里一个可靠的析构函数可以为我做到这一点。 上面的评论表明,最后是一个不好的select,但有没有一个优雅的解决scheme? “能够保留终止应用程序”的业务不具吸引力。

如果只是记忆你担心,不要。 只要相信GC就是一份体面的工作。 我实际上看到了一些关于它的效率如此之高,以至于在某些情况下,创build小型对象的堆可能比使用大型数组更好。

我同意大部分的答案。

你不应该完全依靠finalizeShutdownHook

敲定

  1. 当这个finalize()方法被调用时,JVM不能保证。

  2. finalize()只会被GC线程调用一次,如果对象从finalize方法中复位,finalize将不会被再次调用。

  3. 在您的应用程序中,您可能有一些活动对象,垃圾收集永远不会被调用。

  4. GC线程会忽略finalize方法引发的任何exception

  5. System.runFinalization(true)Runtime.getRuntime().runFinalization(true)方法增加了调用finalize()方法的可能性,但现在这两个方法已被弃用。 这些方法由于缺乏线程安全性和可能的​​死锁创build而非常危险。

shutdownHooks

 public void addShutdownHook(Thread hook) 

注册一个新的虚拟机closures挂钩。

Java虚拟机响应两种事件closures:

  1. 程序正常退出,最后一个非守护进程线程退出或退出(等效于System.exit)方法被调用时,或
  2. 响应用户中断(例如键入^ C)或系统范围的事件(如用户注销或系统closures)而终止虚拟机。
  3. closures钩子只是一个初始化但未启动的线程。 当虚拟机开始其closures序列时,它将以某些未指定的顺序启动所有已注册的closures挂钩,并让它们同时运行。 当所有钩子都完成后,它将运行所有未被引用的终结器,如果退出时已经启用。
  4. 最后,虚拟机将停止。 请注意,守护进程线程将在closures序列期间继续运行,如果通过调用exit方法启动closures,守护进程线程也将继续运行。
  5. 关机挂钩也应该尽快完成工作。 当一个程序调用exit时,期望的是虚拟机会及时closures并退出。

    但即使是oracle文档也引用了这个

在极less数情况下,虚拟机可能会中止,即停止运行而不干净地closures

当虚拟机在外部终止时会发生这种情况,例如Unix上的SIGKILL信号或Microsoft Windows上的TerminateProcess调用。 如果本机方法出错,例如破坏内部数据结构或尝试访问不存在的内存,则虚拟机也可能中止。 如果虚拟机中止,则不能保证是否将运行任何closures挂钩。

结论 :正确使用try{} catch{} finally{}块,并在finally(}块中释放关键资源。释放finally{}块中的资源时,捕获ExceptionThrowable

finalize()函数是析构函数。

但是,它不应该正常使用,因为它是在GC之后调用的并且不能确定何时会发生(如果曾经发生过)。

此外,需要多个GC来释放具有finalize()的对象。

您应该尝试使用try {} finally {}语句清理代码中的逻辑位置!

也许你可以使用一个try … finally块来最终确定你正在使用该对象的控制stream中的对象。 当然,它不会自动发生,但在C ++中也不会销毁。 你经常看到finally块中的资源closures。

在Java中最接近的析构函数是finalize()方法。 与传统的析构函数最大的区别是,你不能确定什么时候会被调用,因为这是垃圾回收器的责任。 我强烈build议在使用之前仔细阅读这些内容,因为典型的文件句柄的RAIA模式等不能与finalize()一起使用。

虽然Java的GC技术已经有了相当的进步,但你仍然需要注意你的引用。 许多似乎微不足道的引用模式的实例,实际上就是引擎盖下的老鼠巢。

从你的post来看,这听起来并不像你想要实现一个重置方法的对象重用的目的(true?)。 你的对象是否需要清理任何其他types的资源(即,必须closures的stream,必须返回的任何池或借来的对象)? 如果你唯一担心的是内存释放,那么我会重新考虑我的对象结构,并试图validation我的对象是自我包含的结构,将在GC时间进行清理。

如果您正在编写Java Applet,则可以覆盖Applet“destroy()”方法。 它是…

  * Called by the browser or applet viewer to inform * this applet that it is being reclaimed and that it should destroy * any resources that it has allocated. The stop() method * will always be called before destroy(). 

显然不是想要的,但可能是其他人正在寻找的东西。

考虑到原来的问题,我认为我们可以从其他所有的答案中得出结论,也可以从布洛赫基本的有效的Java第 7项“避免终结者”得出一个合理的问题的解决scheme,对Java语言来说是不恰当的:

…并不是一个非常明显的解决scheme来实现OP实际需要的是将所有需要重置的对象保留在某种“围栏”中,所有其他不可重置的对象只能通过某种方式将其引用存取对象的…

然后,当您需要“重置”时,您将断开现有的围栏并重新build立一个围栏:围栏中的所有对象网都会被抛弃,永远不会返回,并且有一天会被GC收集。

如果这些对象中的任何一个都是可Closeable (或者不是,但是有一个close方法),那么当它们被创build(并且可能被打开)时,可以将它们放入围栏中的Bag中,并且在切断围栏之前最后一次访问器的行为是要通过closures他们所有closures…?

代码可能看起来像这样:

 accessor.getPlaypen().closeCloseables(); accessor.setPlaypen( new Playpen() ); 

closeCloseables可能是一个阻塞方法,可能涉及一个锁存器(例如CountdownLatch )来处理(并等待适当的)任何特定于Playpen线程中的任何Runnables / Callables ,以便适当地结束,特别是在JavaFX线程中。

在龙目岛有一个@Cleanup注释,大部分类似于C ++析构函数:

 @Cleanup ResourceClass resource = new ResourceClass(); 

在处理时(在编译时),当执行离开variables的范围时,Lombok插入适当的try-finally块,以便调用resource.close() 。 你也可以明确指定另一个释放资源的方法,例如resource.dispose()

 @Cleanup("dispose") ResourceClass resource = new ResourceClass(); 

我曾经主要处理C ++,这也是我的search析构函数。 我现在用了很多JAVA。 我做了什么,也许不是每个人的最好情况,但我实现了我自己的析构函数通过重置所有值为0或默认通过函数。

例:

 public myDestructor() { variableA = 0; //INT variableB = 0.0; //DOUBLE & FLOAT variableC = "NO NAME ENTERED"; //TEXT & STRING variableD = false; //BOOL } 

理想情况下,这不适用于所有情况,但只要没有大量的全局variables,它就会工作。

我知道我不是最好的Java程序员,但它似乎为我工作。