5

我有一个带有 finalize() 方法的伸缩构造函数的超类。为了防止子类忘记调用 super.finalize,我编写了一个像这样的终结器守护程序(EJ Item 7)。

public class Super {

    public Super() {}

    {
        Object finalizerGuardian = new Object() {
            @Override
            protected void finalize() {
                System.out.println("Finalizer guardian finalize");
                Super.this.finalize();
            }
        };
    }

    protected void finalize() {
        System.out.println("Super finalize");
    }

}

这是一个示例子类——

public class Sub extends Super {

    public Sub() {}

    protected void finalize() {
        System.out.println("Sub finalize");
    }

    public static void main(String[] args)
            throws InterruptedException {
        if (1 == 1) {
            Sub s1 = new Sub();
        }
        Runtime.getRuntime().gc();
        Thread.sleep(10000);
    }
}

当 s1 对象超出范围时,终结器监护人的 finalize() 被调用,我从子类的 finalize 方法中获取 SYSO,但从不从 super 的 finalize 中获取。

我很困惑。我从根本上误解了什么吗?

免责声明:我意识到终结器是危险的,不可取的,等等。仍然试图理解这里的问题。

4

4 回答 4

8

有效的 Java 终结器监护人应该自己执行必要的终结逻辑(例如,调用一些Super执行实际终结的方法)而不是调用该finalize()方法,因为在您的情况下Super.this.finalize();实际上是从子类调用被覆盖的方法。

另请注意,终结器监护人应该是该类的一个字段:

public class Super {
    private final Object finalizerGuardian = new Object() {
            @Override
            protected void finalize() {
                Super.this.doFinalize();
            }
    };

    private void doFinalize() {
        System.out.println("Super finalize");
    }
}
于 2011-08-12T14:35:23.833 回答
2

您正在重载Super.finalize(). Sub这就是为什么它没有被调用。

因此,当您在 " finalizerGuardian" 呼叫时,Super.this.finalize();您实际上是在呼叫Sub.finalize()

于 2011-08-12T14:36:22.850 回答
2

正如其他人已经说过动态调度是你的问题。但是据我所知,您的代码中还有另一个主要错误:您的 finalizerGuardian 仅在初始化块内定义,这意味着一旦对象被初始化,该对象就会超出范围并且可以被 GC。

即即使你解决了你的第一个问题(通过在你的类中定义一个处理终结的东西的最终方法),你仍然需要将 finalizerGuardian 保存在一个实例变量中。

于 2011-08-12T14:39:12.787 回答
2

Super.finalize()没有被调用,因为 Sub 覆盖它。尝试添加一个方法并从终结器监护人Super._finalize()调用它。Super.finalize()

另外,我认为finalizerGuardian需要是一个领域Super。否则,即使 Super 对象仍然是强可达的,它也会被垃圾回收。

于 2011-08-12T14:39:49.133 回答