36

出于好奇,

为什么将finalize()方法的访问修饰符设为protected. 为什么不能呢public?有人可以解释一下这背后的任何具体原因吗?

另外,我知道该finalize()方法只被调用一次。如果我在内部程序中调用它两次,会发生什么?垃圾收集器会再次调用它吗?

private void dummyCall() {
    try {
        finalize();
        finalize();
    } catch (Throwable e) {
        e.printStackTrace();//NOT REACHES EXCEPTION
    }
}
4

9 回答 9

26

我用另一个问题回答你的问题:

为什么finalize方法不应该受到保护?

一般来说,您应该尽量保持隐私。这就是封装的全部意义所在。否则,你可以做一切 publicfinalize不能private(因为派生类应该能够访问它以便能够覆盖它),所以至少应该是,protected但是为什么在不希望的时候给予更多的访问权限呢?


在更仔细地阅读了您的评论之后,我想我现在明白了您的主要观点。我认为您的观点是,由于一切都源自java.lang.Object并因此访问其protected成员,因此与. 就个人而言,我认为这是 Java 的设计缺陷。这在 C# 中确实是固定的。问题不在于为什么受到保护。没关系。真正的问题是您不应该能够通过基类类型的对象引用来调用基类中的受保护方法。Eric Lippert 有一篇博客文章讨论了为什么允许对受保护的成员进行这种访问是一个坏主意,在这个问题的 Stack Overflow 上进一步阐述了这一点java.lang.Objectpublicprotectedfinalize

于 2010-02-18T19:17:07.177 回答
23

为什么 finalize() 方法的访问修饰符被设置为受保护。为什么不能公开?

它不是公开的,因为它不应该被 JVM 以外的任何人调用。但是,它必须受到保护,以便它可以被需要为其定义行为的子类覆盖。

如果我在我的程序中调用它两次,内部发生了什么?

你可以随心所欲地调用它,毕竟它只是一种方法。但是,很像public static void main(String [] args),它对 JVM 有特殊的意义

垃圾收集器会再次调用它吗?

是的

于 2010-02-18T19:19:12.583 回答
12
  • finalize 仅由 gc 调用,因此不需要公共访问
  • finalize 保证只被 gc调用一次,你自己调用它会破坏这个保证,因为 gc 不会知道它。
  • 任何重写类都可以公开 finalize,我认为由于上述原因,这很糟糕
  • finalize 不应包含太多代码,因为 finalize 抛出的任何异常都可能杀死 gc 的终结器线程。

反对 finalize()

  • 管理本机资源或任何需要调用 dispose() 或 close() 的资源可能会导致难以发现错误,因为它们只会在 jvm 内存不足时释放,您应该手动释放资源。Finalize 只能用于调试资源泄漏或手动管理资源工作量太大的情况。
  • finalize 将在 gc 的附加线程中调用,并可能导致资源锁定等问题。
  • 像 WeakReference 和 ReferenceQueue 这样的引用类是处理清理的另一种(相当复杂的)方法,并且可能与本机资源的 finalize() 有相同的问题。

当心以上陈述中的错误,我有点累了:-)

于 2010-02-18T20:04:26.310 回答
3

查看讨论它的此链接。

基本上,它是最有意义的private,因为它只能由 JVM(垃圾收集器)调用。但是为了允许子类调用父finalize()方法作为它的一部分finalize(),它必须是protected

编辑- 只是一般警告 - 通常不鼓励使用 finalize() 方法,因为没有办法确保它会被调用。虽然这并不意味着你永远不会有机会使用它 - 它是只是很少见。)

于 2010-02-18T19:18:53.350 回答
3

关于finalize()仅被调用一次的部分仅适用于来自 GC 的调用。您可以将对象想象为具有隐藏标志“finalize()被 GC 调用”,并且 GC 检查该标志以了解如何处理该对象。旗帜不会受到您自己手动调用的任何影响finalize()

在完成时,请阅读Hans Boehm(以垃圾收集方面的工作而闻名)的这篇文章。这是关于最终确定的大开眼界;特别是,Boehm 解释了为什么最终确定必须是异步的。一个推论是,虽然定稿是一个强大的工具,但它很少是给定工作的正确工具。

于 2010-02-18T21:00:19.827 回答
2

它不是public(或默认访问),因为当对象被垃圾收集时,它应该由 JVM 在内部调用 - 它应该被其他任何东西调用。这并不是private因为它要被覆盖并且你不能覆盖私有方法。

如果我在我的程序中调用它两次,内部发生了什么?垃圾收集器会再次调用它吗?

可能是的,但是很难想象这样的场景会有任何意义——关键finalize()是在对象被垃圾收集时进行清理。而且它甚至没有那么好,所以它真的是你应该完全避免而不是尝试的东西。

于 2010-02-18T19:21:14.193 回答
1

finalize() 仅由 JVM 用于在收集对象时清理资源。一个类定义在集合上应该采取什么行动是合理的,为此它可能需要访问 super.finalize()。外部进程调用 finalize() 并没有真正意义,因为外部进程无法控制何时收集对象。

于 2010-02-18T19:21:35.797 回答
1

另外,我知道 finalize() 方法只被调用一次。如果我在我的程序中调用它两次,内部发生了什么?

您可能在对 C++ ~destructors 的印象下问这个问题。在 java 中的 finalize() 方法没有做任何魔法(比如清除内存)。它应该由垃圾收集器调用。但反之亦然。

我建议您阅读 Joshua Bloch 的“Effective Java”中的相应章节。它说使用终结器是一种不好的做法,可能会导致性能和其他问题,并且只有几种情况应该使用它们。本章从以下几个词开始:

终结器是不可预测的,通常很危险,而且通常是不必要的。

于 2010-02-18T19:23:53.353 回答
1

我认为受保护的原因finalize可能是它被JDK中的某些类覆盖,而那些被覆盖的方法被JVM调用。

于 2012-05-06T11:19:25.903 回答