构造函数抛出异常后是否可以调用finalize方法?

18

是否有关于使用finalize()来清理对象是否会在该对象的构造函数抛出异常时执行的详细信息。

调用此方法的时间是众所周知的不确定。根据手册:

Java编程语言不保证哪个线程会为任何给定的对象调用finalize方法。但是,保证调用finalize的线程在调用finalize时不会持有任何用户可见的同步锁。如果finalize方法抛出未捕获的异常,则忽略该异常并终止该对象的终结。

http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#finalize%28%29

我无法以这种方式触发finalize方法。 有没有人知道它是否保证不被调用,还是在构造函数未能初始化该对象(抛出异常)后,在某些情况下调用了它。

我之所以这样问,是因为我有一个对象不能被清理两次。 我正在尝试了解在抛出异常之前清理是否安全,或者我必须为finalize()留下标记以有效地跳过并什么也不做。


3
我怀疑这是需要您检查的内容,因为不同的 JVM 可能会有不同的行为。 - Peter Lawrey
使用 finalize 是不好的。 - Louis Wasserman
2
@Peter,JLS非常明确,一旦“[base] Object”的构造函数成功完成,对象就可以被finalize。 - Jason Pyeron
@JasonPyeron 很好的澄清。 - Peter Lawrey
3个回答

12

我的测试显示它可以

public class Test1 {

    Test1() {
        throw new RuntimeException();
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println("finalized");
    }

    public static void main(String[] args) throws Exception {
        try {
            new Test1();
        } catch (RuntimeException e) {
            e.printStackTrace();
        }
        System.gc();
        Thread.sleep(1000);
    }
}

打印

java.lang.RuntimeException
    at test.Test1.<init>(Test1.java:13)
    at test.Test1.main(Test1.java:24)
finalized

它运行在Java HostSpot Client VM 1.7.0_03上。


非常有帮助!您能编辑一下您的答案,说明您在哪个JVM上尝试过吗?我会接受它。非常感谢! - Philip Couling
2
这是因为隐含的super()没有抛出异常。 - Jason Pyeron

10
根据JLS的12.6.1. 实现Finalization节:

对象o在其构造函数调用o上的Object构造函数并且该调用已成功完成(即未引发异常)之前,不可终止。

如果您的构造函数在Object构造函数完成后抛出异常,则您的对象应该是可终止的,因此仍然可以调用finalize()
12.5. 创建新类实例节中,有一个很好的示例步骤,展示了Object构造函数的调用时间。

1
这意味着在类X X(){super();}中,对Object的super()调用已经完成且没有异常。Object类具有粘合代码来注册由new分配的堆分配以进行垃圾回收。 - Jason Pyeron
3
值得一提的是,上述评论(即 finalizable 的定义)首次出现在JLS的第三版(Java 1.5),但是即使没有调用 Object 构造函数,Java 1.5仍允许 finalizer 运行(见 这里)。这个 bug 直到 Java 1.6 才被修复。 - Bass
1
@Bass 还有一个 JVM 选项可启用旧行为,但很遗憾我不记得确切的名称。 - Holger
1
@Holger 谢谢!我相信它是RegisterFinalizersAtInit - Bass

1
为了更清晰地演示:

public class Test1 {

    public static class LifeBoat extends RuntimeException
    {
        private Test1 passenger;
        public Test1 getPassenger(){return passenger;}
        public LifeBoat(Test1 passenger){this.passenger=passenger;}
    }

    Test1() {
        super(); //once this is finished, there is an Object to GC per JLS 12.6.1. 
        throw new LifeBoat(this);
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println("finalized");
    }

    public static void main(String[] args) throws Exception {
        try {
            new Test1();
        } catch (LifeBoat e) {
            Test1 obj;
            obj=e.getPassenger();
            System.out.println(obj);
        }
        System.gc();
        Thread.sleep(1000);
    }
}

打印

java.lang.RuntimeException
    at test.Test1.<init>(Test1.java:13)
    at test.Test1.main(Test1.java:24)
test.Test1@6dc8f3cd
finalized

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接