企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
### [终结条件](https://lingcoder.gitee.io/onjava8/#/book/06-Housekeeping?id=%e7%bb%88%e7%bb%93%e6%9d%a1%e4%bb%b6) 通常,不能指望`finalize()`,你必须创建其他的"清理"方法,并明确地调用它们。所以看起来,`finalize()`只对大部分程序员很难用到的一些晦涩内存清理里有用了。但是,`finalize()`还有一个有趣的用法,它不依赖于每次都要对`finalize()`进行调用,这就是对象终结条件的验证。 当对某个对象不感兴趣时——也就是它将被清理了,这个对象应该处于某种状态,这种状态下它占用的内存可以被安全地释放掉。例如,如果对象代表了一个打开的文件,在对象被垃圾回收之前程序员应该关闭这个文件。只要对象中存在没有被适当清理的部分,程序就存在很隐晦的 bug。`finalize()`可以用来最终发现这个情况,尽管它并不总是被调用。如果某次`finalize()`的动作使得 bug 被发现,那么就可以据此找出问题所在——这才是人们真正关心的。以下是个简单的例子,示范了`finalize()`的可能使用方式: ~~~ // housekeeping/TerminationCondition.java // Using finalize() to detect a object that // hasn't been properly cleaned up import onjava.*; class Book { boolean checkedOut = false; Book(boolean checkOut) { checkedOut = checkOut; } void checkIn() { checkedOut = false; } @Override protected void finalize() throws Throwable { if (checkedOut) { System.out.println("Error: checked out"); } // Normally, you'll also do this: // super.finalize(); // Call the base-class version } } public class TerminationCondition { public static void main(String[] args) { Book novel = new Book(true); // Proper cleanup: novel.checkIn(); // Drop the reference, forget to clean up: new Book(true); // Force garbage collection & finalization: System.gc(); new Nap(1); // One second delay } } ~~~ 输出: ~~~ Error: checked out ~~~ 本例的终结条件是:所有的**Book**对象在被垃圾回收之前必须被登记。但在`main()`方法中,有一本书没有登记。要是没有`finalize()`方法来验证终结条件,将会很难发现这个 bug。 你可能注意到使用了`@Override`。`@`意味着这是一个注解,注解是关于代码的额外信息。在这里,该注解告诉编译器这不是偶然地重定义在每个对象中都存在的`finalize()`方法——程序员知道自己在做什么。编译器确保你没有拼错方法名,而且确保那个方法存在于基类中。注解也是对读者的提醒,`@Override`在 Java 5 引入,在 Java 7 中改善,本书通篇会出现。 注意,`System.gc()`用于强制进行终结动作。但是即使不这么做,只要重复地执行程序(假设程序将分配大量的存储空间而导致垃圾回收动作的执行),最终也能找出错误的**Book**对象。 你应该总是假设基类版本的`finalize()`也要做一些重要的事情,使用**super**调用它,就像在`Book.finalize()`中看到的那样。本例中,它被注释掉了,因为它需要进行异常处理,而我们到现在还没有涉及到。