我一直在阅读很多关于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,所以他们只是删除它引用的东西,耸耸肩然后离开。

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

其他回答

编辑:好吧,这真的不管用。我实现了它,并认为如果它有时失败了,这对我来说是可以的,但它甚至没有调用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);
}

为了强调以上答案中的一点:终结器将在单独的GC线程上执行。我听说过一个大型的Sun演示,开发人员在一些终结器中添加了一个小睡眠,并故意让一个原本花哨的3D演示崩溃。

最好避免,可能的例外是test-env诊断。

Eckel的《Thinking in Java》在这方面有一个很好的章节。

作为旁注:

覆盖finalize()的对象由垃圾回收器进行特殊处理。通常,在对象不再在作用域中后,在收集周期中立即销毁对象。但是,可终结对象被移到队列中,在队列中,独立的终结线程将耗尽队列并在每个对象上运行finalize()方法。一旦finalize()方法终止,对象就可以在下一个循环中进行垃圾收集了。

源代码:finalize()在java-9上已弃用

class MyObject {
    Test main;

    public MyObject(Test t) {    
        main = t; 
    }

    protected void finalize() {
        main.ref = this; // let instance become reachable again
        System.out.println("This is finalize"); //test finalize run only once
    }
}

class Test {
    MyObject ref;

    public static void main(String[] args) {
        Test test = new Test();
        test.ref = new MyObject(test);
        test.ref = null; //MyObject become unreachable,finalize will be invoked
        System.gc(); 
        if (test.ref != null) System.out.println("MyObject still alive!");  
    }
}

====================================

结果:

This is finalize

MyObject still alive!

=====================================

所以你可以在finalize方法中使一个不可达的实例可达。

当编写将被其他开发人员使用的代码时,需要调用某种“清理”方法来释放资源。有时那些其他开发人员忘记调用您的清理(或关闭,或销毁,或其他)方法。为了避免可能的资源泄漏,您可以检查finalize方法,以确保该方法被调用,如果没有调用,您可以自己调用它。

许多数据库驱动程序在它们的Statement和Connection实现中这样做,以提供一点安全性,防止开发人员忘记调用close。