终结方法(finalizer)通常是不可预测的,也是很危险的,一般情况下也是不必要的。
如果子类覆盖了终结函数,子类的终结函数就必须手动调用超类的终结函数。不然超类的终结函数将永远也不会被调用到。而且应该在一个try块中终结子类,并在相应的finally块中调用超类的终结函数。这样做可以保证:即使子类的终结过程抛出异常,超类的终结函数也会得到执行。如下面的代码实例:
// Manual finalizer chaining@Override PRotected void finalize() throws Throwable { try { ... // Finalize subclass state } finally { super.finalize(); }}不能保证会被及时地执行,甚至无法保证终结方法是否会被执行;
依赖于终结方法被执行的时间点的程序行为在不同的JVM中运行的表现可能会截然不同;
使用终结方法会造成严重的性能损失。
既然终结方法有这么多缺点,但是当我们真的需要终结资源时该怎么办,这是需要用显示的终结方法。显示的终止方式通常与try-finally结构结合使用,确保及时终止。如下面的例子:
//try-finally block guarantees execution of termination methodFoo foo = new Foo(...);try { //Do what must be done with foo ...} finally { foo.terminate(); // Explicit termination method}既然终结方法这么不好(第2条),而且在已经有了显示的终结方法了(第3条),那终结方法到底有什么用:
当对象的所有者忘记使用显示终止方式时,终结方法可以充当“安全网(safety net)”,也就是备胎;
第二种合理用法和本地对等体(native peer)相关。
在涉及到JNI编程的时候,我们的普通java对象有的时候需要委托给本地对象,在资源回收的时候只能回收到普通java对象而回收不了被本地代码(C或者C++)托管的对象,所以需要我们在终结方法方法中通过调用本地方法来释放掉这个被本地代码托管的对象。
在第4条中,我们发现终结方法还是有一定的作用的,那么我们使用的时候应该注意什么呢?
使用try-finally
使用终结函数守卫者
在最上面的第一条中,我们知道子类的终结函数就必须手动调用超类的终结函数。所以为了防止忘了子类手动调用超类的终结函数,把终结函数放在一个匿名的类中,该匿名类的唯一用途就是终结它的外围实例(enclosing instance)。该匿名类的单个实例被称为终结函数守卫者(finalizer guardian),外围类的每个实例都会创建这样一个守卫者,对于每一个带有终结函数的非final公有类,都应该考虑使用这种方法。书中的例子:
// Finalizer Guardian idiompublic class Foo { // Sole purpose of this object is to finalize outer Foo object private final Object finalizerGuardian = new Object() { @Override protected void finalize() throws Throwable { ... // Finalize outer Foo object } }; ... // Remainder omitted}总之,除非是作为安全网,或者是为了终止非关键的本地资源,否则请不要使用终结函数。在这些很少见的情况下,既然使用了终结函数,就要记住调用super.finalize。如果用终结函数作为安全网,要记得记录终结函数的非法用法。最后,如果需要把终结函数与公有的非final类关联起来,请考虑使用终结函数守卫者,以确保即使子类的终结函数未能调用super.finalize,该终结函数也会被执行。
新闻热点
疑难解答