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

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

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

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


当前回答

就我个人而言,除了在一种罕见的情况下,我几乎从不使用finalize():我创建了一个自定义泛型类型集合,并编写了一个自定义finalize()方法,它执行以下操作:

public void finalize() throws Throwable {
    super.finalize();
    if (destructiveFinalize) {
        T item;
        for (int i = 0, l = length(); i < l; i++) {
            item = get(i);
            if (item == null) {
                continue;
            }
            if (item instanceof Window) {
                ((Window) get(i)).dispose();
            }
            if (item instanceof CompleteObject) {
                ((CompleteObject) get(i)).finalize();
            }
            set(i, null);
        }
    }
}

(CompleteObject是我做的一个接口,让你指定你已经实现了很少实现的对象方法,如#finalize(), #hashCode()和#clone()))

因此,使用姐妹方法# setdestructivelyfinalizer(布尔值),使用我的集合的程序可以(帮助)确保销毁对该集合的引用也会销毁对其内容的引用,并处理可能在无意中保持JVM活动的任何窗口。我也考虑过停止任何线程,但这打开了一个全新的蠕虫罐头。

其他回答

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

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

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

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

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

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

我不知道你是怎么想的,但是…

itsadok@laptop ~/jdk1.6.0_02/src/
$ find . -name "*.java" | xargs grep "void finalize()" | wc -l
41

所以我猜《太阳报》找到了一些(他们认为)应该使用它的案例。

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

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

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

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