我一直在阅读很多关于finalize()的Java新手问题,并发现没有人真正清楚地表明finalize()是一种不可靠的清理资源的方法,这有点令人困惑。我看到有人评论说他们用它来清理连接,这真的很可怕,因为唯一接近于保证连接关闭的方法是最后实现try (catch)。

我没有学过CS,但我已经用Java专业编程近十年了,我从来没有见过有人在生产系统中实现finalize()。这并不意味着它没有用处,或者和我一起工作的人一直在做正确的事情。

所以我的问题是,实现finalize()有哪些用例不能通过语言中的另一个进程或语法更可靠地处理?

请提供具体的场景或您的经验,简单地重复Java教科书,或最终确定的预期用途是不够的,因为这不是这个问题的意图。


当前回答

嗯,我曾经用它来清理没有返回到现有池的对象。

它们经常被转来转去,所以不可能知道什么时候能安全地放回泳池。问题在于,它在垃圾收集过程中带来了巨大的损失,远远超过了使用对象池节省的成本。在我拆除整个池,让所有内容都变得动态并完成之前,它已经投入了大约一个月的时间。

其他回答

公认的答案是好的,我只是想补充的是,现在有一种方法来实现finalize的功能,而不实际使用它。

看看“Reference”类。弱引用,幻影引用和软引用。

您可以使用它们来保存对所有对象的引用,但是这个引用ALONE不会停止GC。这样做的好处是,当它被删除时,你可以让它调用一个方法,并且这个方法可以保证被调用。

至于finalize: 我使用finalize一次来了解哪些对象正在被释放。你可以用静态、引用计数等来玩一些巧妙的游戏——但这只是为了分析,但要注意这样的代码(不只是在finalize中,但这是你最有可能看到它的地方):

public void finalize() {
  ref1 = null;
  ref2 = null;
  othercrap = null;
}

这表明某人不知道自己在做什么。像这样的“清理”实际上是不需要的。当类被GC化时,这将自动完成。

如果你在finalize中发现这样的代码,那肯定是编写它的人搞糊涂了。

如果它在其他地方,可能是代码是一个坏模型的有效补丁(一个类存在很长一段时间,由于某种原因,它引用的东西必须在对象GC之前手动释放)。一般来说,这是因为有人忘记删除监听器或其他东西,不知道为什么他们的对象没有被GC,所以他们只是删除它引用的东西,耸耸肩然后离开。

它永远不应该被用来“更快”地清理东西。

您可以将它用作持有外部资源(套接字、文件等)的对象的支持。实现close()方法和需要调用它的文档。

如果检测到close()没有执行,则实现finalize()来执行close()处理。可能会将一些东西转储到stderr,以指出您正在清理有错误的调用者。

它在异常/有bug的情况下提供了额外的安全性。并不是每个调用者每次都会执行正确的try {} finally{}。不幸的是,但在大多数环境中都是如此。

我同意很少有人需要它。正如评论者指出的那样,它带来了GC开销。只有当你在一个长期运行的应用程序中需要“皮带和背带裤”的安全性时才使用。

我看到从Java 9开始,Object.finalize()已弃用!它们将我们指向java.lang.ref.Cleaner和java.lang.ref.PhantomReference作为替代。

您不应该依赖finalize()来为您清理资源。Finalize()在类被垃圾回收之前不会运行。在使用完资源后显式地释放资源要好得多。

编辑:好吧,这真的不管用。我实现了它,并认为如果它有时失败了,这对我来说是可以的,但它甚至没有调用finalize方法一次。

我不是一个专业的程序员,但在我的程序中,我有一个案例,我认为是一个使用finalize()的好例子,那是一个缓存,在它被销毁之前将其内容写入磁盘。因为它没有必要在每次销毁时都执行,它只会加速我的程序,我希望我没有做错。

@Override
public void finalize()
{
    try {saveCache();} catch (Exception e)  {e.printStackTrace();}
}

public void saveCache() throws FileNotFoundException, IOException
{
    ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("temp/cache.tmp"));
    out.writeObject(cache);
}

资源(文件,套接字,流等)需要关闭一旦我们完成他们。它们通常有close()方法,我们通常在try-catch语句的finally部分调用该方法。有时finalize()也可以被少数开发人员使用,但在我看来,这不是一种合适的方式,因为不能保证finalize总是被调用。

在Java 7中,我们有try-with-resources语句,可以这样使用:

try (BufferedReader br = new BufferedReader(new FileReader(path))) {
  // Processing and other logic here.
} catch (Exception e) {
  // log exception
} finally {
  // Just in case we need to do some stuff here.
}

在上面的例子中,try-with-resource将通过调用close()方法自动关闭资源BufferedReader。如果我们愿意,我们也可以在自己的类中实现Closeable,并以类似的方式使用它。在我看来,这似乎更整洁,更容易理解。