第7条:避免使用终结方法
终结方法(finalize)通常是不可预测的,它不是C++中析构器(destructors)的对应物,Java中一旦对象变的不可达,垃圾回收器会自动回收与该对象相关的存储空间,不需要程序员做专门的工作。C++析构器也可以用来回收其他非内存资源,在Java中,通常用try-finally来完成。
终结方法的缺点:
1.不能保证被及时执行,注重时间的任务不应该由终结方法完成,比如用终结方法关闭已经打开的文件,因为文件描述符是有限的资源,如果大量文件保留在打开状态,当程序不能再打开文件,可能会运行失败,还有,如果是以写方式打开文件,这时候文件就不应该再被读或者写了,只有等待被写的文件关闭,表示写完,才可继续进行另外的操作,如果终结方法迟迟不执行,那么整个文件在已经写完,但还没关闭的情况下,无法被访问,大大降低性能。
2.即使地执行终结方法是回收算法的一个主要功能,由于不同的JVM的回收算法实现大相庭径,使用终结方法可能会丧失平台无关性。
3.Java语言规范不保证终结方法会被执行,当一个程序终止,某些无法访问的对象的终结方法没有被执行是完全有可能的,比如,依赖终结方法来释放数据库上的永久锁,可能会导致锁永远不会被释放。
如果类的对象中封装的资源确实需要终止,应该怎么做。
提供一个显式的终结方法。意思是,我们不去覆盖finalize方法,而是自己编写一个终止资源的方法。比如,java.io.FileInputStream的close方法。当使用完这个FileInputStream对象时,显式调用close() 来回收资源。
终结方法的好处:
1.当对象的所有者忘记调用显式终结方法时,终结方法可以充当"安全网"。
来看看FileInputStream是怎么做的:
public void close() throws IOException { synchronized (closeLock) { if (closed) { return; } closed = true; } if (channel != null) { channel.close(); } fd.closeAll(new Closeable() { public void close() throws IOException { close0(); } }); } protected void finalize() throws IOException { if ((fd != null) && (fd != FileDescriptor.in)) { /* if fd is shared, the references in FileDescriptor * will ensure that finalizer is only called when * safe to do so. All references using the fd have * become unreachable. We can call close() */ close(); } }
可以看到FileInputStream还是有覆盖finalize方法的,而里面做的就是调用close方法,这是为了当对象持有者忘记调用close方法,在finalize方法中为它做调用close的事,这就是“安全网”的意思。
2.终结方法的守卫者。把终结方法放在一个匿名的类,该匿名类的唯一用途就是终结它的外围实例。外围实例持有对终结方法守卫者的唯一实例,这意味着当外围实例是不可达时,这个终结方法守卫者也是不可达的了,垃圾回收器回收外围实例的同时也会回收终结方法守卫者的实例,而终结方法守卫者的finalize方法就把外围实例的资源释放掉,就好像是终结方法是外围实例的一个方法一样。
来看看java.util.Timer的终结方法守卫者:
public void cancel() { synchronized(queue) { thread.newTasksMayBeScheduled = false; queue.clear(); queue.notify(); // In case queue was already empty. } } private final Object threadReaper = new Object() { protected void finalize() throws Throwable { synchronized(queue) { thread.newTasksMayBeScheduled = false; queue.notify(); // In case queue is empty. } } };
cancel方法是Timer提供的显式终止方法,threadReaper是一个私有变量,保证除了实例本身外,没有对它的引用,它覆盖finalize方法,实现与cancel方法一样的功能。