第七条:避免使用终结方法(finalizer)
1. 解释
本条的意思是,让你尽量不要在你的类中覆盖finalize方法,然后在在里面写一些释放你的类中资源的语句。
2. 为什么要避免覆盖并使用finalize方法?
(1)finalize方法不能保证它能被及时的执行。
(2)finalize方法甚至都不会被执行。
(3)System.gc和System.runFinalization这两个方法只是能增加finalize方法被调用的几率。
(4)唯一能保证finalize方法被执行的方法有两个,System.runFinalizersOnExit和Runtime.runFinalizersOnExit但是这两个方法已经被弃用。
(5)覆盖并使用终结方法会造成严重的性能损失。
3. 如果类中的资源确实需要被释放,我们应该怎么做?
一般来说,需要释放的资源有线程或者文件还有一下涉及到本地的资源的对象。
我们只需要提供一个public修饰的终止方法,用来释放资源,并要求这类的使用者在不再使用这个类的时候调用这个方法,并且在类中添加一个标志,来标记资源是否已经释放,如果已经被释放了,那这个类中的方法如果在被调用的话就抛出IllegalStateException异常,一个很好的例子就是InputStream和OutputStream。
多说一句,在调用我们自己定义的public修饰的终止方法的时候最好和try—finally一起使用,就像下面这样:
class MyObject{
private boolean isClosed = false;
//public修饰的终止方法
public void close(){
//资源释放操作
...
isClosed = true;
}
}
public static void main(String... args) {
MyObject object = new MyObject();
try{
//在这里面使用object;
...
} finally {
//在这里面关闭object;
object.close();
}
}
4. 什么时候使用终结方法是合理的?
(1)用终结方法充当“安全网”
“安全网”的作用是当我们提供的public修饰的终结方法被在外部忘记调用的时候提供一种安全保障,如下:
class MyObject{
private boolean isClosed = false;
//public修饰的终止方法
public void close(){
//资源释放操作
...
isClosed = true;
}
//安全网
@Overried
protected void finalize() throws Throwable {
try{
close();
} finally {
super.finalize();
}
}
}
(2)第二种合理用法和本地对等体相关,在涉及到JNI编程的时候,我们的普通java对象有的时候需要委托给本地对象,在资源回收的时候只能回收到普通java对象而回收不了被本地代码(C或者C++)托管的对象,所以需要我们在终结方法方法中通过调用本地方法来释放掉这个被本地代码托管的对象。
注:这种方式是面对不包含关键资源的对象,如果包含关键资源同样需要提供一个public修饰的终结方法。
5. 在使用终结方法的时候我们应该注意些什么?
只需要注意一点,那就是确保super.finalize()方法一定会被执行。
确保它一定会被执行的方式有两种:
(1)使用try-finally(像上面的安全网一样);
(2)使用“终结方法守卫”
书中的例子:
public class Foo{
//终结守卫
private final Object finalizerGuardian = new Object{
... //释放Foo中的资源,这里不需要调用super.finalize()
}
}
本条内容记录完毕。