终结方法带来的问题:
1.终结方法( finalizer)通常是不可预测的,也是很危险的,一般情况下是不必要的。使用终结方法会导致行为不稳定、降低性能,以及可移植性问题。当然,终结方法也有其可用之处,但我们还是应该避免使用。
终结方法的缺点在于不能保证会被及时的执行。
2.不要被System.gc和System.runFinalization这两个方法所诱惑,它们确实增加了终结方法被执行的机会,但它们并不保证终结方法一定会被执行。
3.使用终结方法会有非常严重的性能损失。会增加对象创建和销毁的时间(大约430倍,原作者机器的性能)
终结方法合理用法:
1.充当“安全网”。
当对象的所有者忘记调用显示终止方法时,保证对象能够被释放(晚释放总比不释放好)。
FileInputStream、FileOutputStream、Timer和Connection这四个类都具有终结方法,当它们的终止方法未被调用时,这些终结方法就充当了安全网。
2.与本地对等体(native peer)有关。
本地对等体是一个本地对象,本地代码是java方法的实施是由并非java代码提供。在定义一个native method时,并不提供实现体(有些像定义一个java interface),因为其实现体是由非java语言在外面实现的,所以垃圾回收器不知道本地对等体的存在。
注意
1.“终结方法链(finalizer chaining)”并不会被自动执行。如果类(不是Object)有终结方法,并且子类覆盖了终结方法,子类的终结方法必须手工调用超类的终结方法。
public class Parent {
@Override
protected void finalize() throws Throwable {
try {
System.out.println("parent finalize ");
}finally {
super.finalize();
}
}
}
public class Son extends Parent {
@Override
protected void finalize() throws Throwable {
try {
System.out.println("son finalize");
}finally {
super.finalize();
}
}
}
2.采用try-finally的方式来调用超类终结方法,即使子类在终结过程中抛出异常,父类依旧可以被终结。
3.终结方法守卫者(Finalizer Guradian)。
如果子类实现者覆盖了超类的终结方法,但是忘记了调用超类的终结方法,那么超类的终结方法永远不会被调用到。防范这种情况的方法是,创建一个匿名内部类,类的终结方法写在匿名内部类(参考第22条)的方法中,这样做的代价就是为每个对象都创建了一个附加对象。
//Finalizer Guardian idiom
private final Object finalizeGuardian =new Object() {
@Override
protected void finalize() throws Throwable {
//Finalizer outer Parent object
try {
System.out.println("parent finalize Guardian");
}finally {
super.finalize();
}
}
//Remainder omitted
};
总结:
除非用作安全网,或者是为了终止非关键的本地资源,否则请不要使用终结方法。使用总结方法一定要记得调用super.finalize()。如果是用作安全网,一定要记录终结方法的非法用法。考虑使用终结方法守卫者(匿名内部类中实现终结方法),以确保子类的终结方法未能调用super.finalize()时,终结方法也会执行。