🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
### [保证适当的清理](https://lingcoder.gitee.io/onjava8/#/book/08-Reuse?id=%e4%bf%9d%e8%af%81%e9%80%82%e5%bd%93%e7%9a%84%e6%b8%85%e7%90%86) Java 没有 C++ 中析构函数的概念,析构函数是在对象被销毁时自动调用的方法。原因可能是,在Java中,通常是忘掉而不是销毁对象,从而允许垃圾收集器根据需要回收内存。通常这是可以的,但是有时你的类可能在其生命周期中执行一些需要清理的活动。初始化和清理章节提到,你无法知道垃圾收集器何时会被调用,甚至它是否会被调用。因此,如果你想为类清理一些东西,必须显式地编写一个特殊的方法来完成它,并确保客户端程序员知道他们必须调用这个方法。最重要的是——正如在"异常"章节中描述的——你必须通过在 \*\*finally \*\*子句中放置此类清理来防止异常。 请考虑一个在屏幕上绘制图片的计算机辅助设计系统的例子: ~~~ // reuse/CADSystem.java // (c)2017 MindView LLC: see Copyright.txt // We make no guarantees that this code is fit for any purpose. // Visit http://OnJava8.com for more book information. // Ensuring proper cleanup // {java reuse.CADSystem} package reuse; class Shape { Shape(int i) { System.out.println("Shape constructor"); } void dispose() { System.out.println("Shape dispose"); } } class Circle extends Shape { Circle(int i) { super(i); System.out.println("Drawing Circle"); } @Override void dispose() { System.out.println("Erasing Circle"); super.dispose(); } } class Triangle extends Shape { Triangle(int i) { super(i); System.out.println("Drawing Triangle"); } @Override void dispose() { System.out.println("Erasing Triangle"); super.dispose(); } } class Line extends Shape { private int start, end; Line(int start, int end) { super(start); this.start = start; this.end = end; System.out.println( "Drawing Line: " + start + ", " + end); } @Override void dispose() { System.out.println( "Erasing Line: " + start + ", " + end); super.dispose(); } } public class CADSystem extends Shape { private Circle c; private Triangle t; private Line[] lines = new Line[3]; public CADSystem(int i) { super(i + 1); for(int j = 0; j < lines.length; j++) lines[j] = new Line(j, j*j); c = new Circle(1); t = new Triangle(1); System.out.println("Combined constructor"); } @Override public void dispose() { System.out.println("CADSystem.dispose()"); // The order of cleanup is the reverse // of the order of initialization: t.dispose(); c.dispose(); for(int i = lines.length - 1; i >= 0; i--) lines[i].dispose(); super.dispose(); } public static void main(String[] args) { CADSystem x = new CADSystem(47); try { // Code and exception handling... } finally { x.dispose(); } } } /* Output: Shape constructor Shape constructor Drawing Line: 0, 0 Shape constructor Drawing Line: 1, 1 Shape constructor Drawing Line: 2, 4 Shape constructor Drawing Circle Shape constructor Drawing Triangle Combined constructor CADSystem.dispose() Erasing Triangle Shape dispose Erasing Circle Shape dispose Erasing Line: 2, 4 Shape dispose Erasing Line: 1, 1 Shape dispose Erasing Line: 0, 0 Shape dispose Shape dispose */ ~~~ 这个系统中的所有东西都是某种**Shape**(它本身是一种**Object**,因为它是从根类隐式继承的) 。除了使用**super**调用该方法的基类版本外,每个类还覆盖`dispose()`方法。特定的**Shape**类——**Circle**、**Triangle**和**Line**,都有 “draw” 构造函数,尽管在对象的生命周期中调用的任何方法都可以负责做一些需要清理的事情。每个类都有自己的`dispose()`方法来将非内存的内容恢复到对象存在之前的状态。 在`main()`中,有两个关键字是你以前没有见过的,在"异常"一章之前不会详细解释:**try**和**finally**。**try**关键字表示后面的块 (用花括号分隔 )是一个受保护的区域,这意味着它得到了特殊处理。其中一个特殊处理是,无论**try**块如何退出,在这个保护区域之后的**finally**子句中的代码总是被执行。(通过异常处理,可以用许多不同寻常的方式留下**try**块。)这里,**finally**子句的意思是,“无论发生什么,始终调用`x.dispose()`。” 在清理方法 (在本例中是`dispose()`) 中,还必须注意基类和成员对象清理方法的调用顺序,以防一个子对象依赖于另一个子对象。首先,按与创建的相反顺序执行特定于类的所有清理工作。(一般来说,这要求基类元素仍然是可访问的。) 然后调用基类清理方法,如这所示。 在很多情况下,清理问题不是问题;你只需要让垃圾收集器来完成这项工作。但是,当你必须执行显式清理时,就需要多做努力,更加细心,因为在垃圾收集方面没有什么可以依赖的。可能永远不会调用垃圾收集器。如果调用,它可以按照它想要的任何顺序回收对象。除了内存回收外,你不能依赖垃圾收集来做任何事情。如果希望进行清理,可以使用自己的清理方法,不要使用`finalize()`。