Java
中没有
C++的析构函数(destructor)的概念。析构函数是一种在对象被销毁时可以被
自动调用的函数。其原因可能是因为在 Java 中,我们的习惯只是忘掉而不是销毁对象,并
且让垃圾回收器在必要时释放其内存。
通常这样做是好事,但有时你的类可能要在其生命周期内执行一些必需的清除活动。正如我
们在第四章中所提到的那样,你并不知道垃圾回收器何时将会被调用,或者它是否将被调用。
因此,如果你想要某个类清除一些东西,就必须显式地编写一个特殊方法来做这件事,并要
确保客户端程序员知晓他们必须要调用这一方法。就像第九章(异常处理)所描述的那样,
其首要任务就是,你必须将这一清除动作置于 finally 字句之中,以预防异常的出现。
请思考一下下面这个能在屏幕上绘制图案的计算机辅助设计系统示例:
//: c06:CADSystem.java
// Ensuring proper cleanup.
package c06;
import com.bruceeckel.simpletest.*;
import java.util.*;
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");
}
void dispose() {
System.out.println("Erasing Circle");
super.dispose();
}
}
class Triangle extends Shape {
Triangle(int i) {
super(i);
System.out.println("Drawing Triangle");
}
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);
}
void dispose() {
System.out.println("Erasing Line: "+ start+ ", "+ end);
super.dispose();
}
}
public class CADSystem extends Shape {
private static Test monitor = new Test();
private Circle c;
private Triangle t;
private Line[] lines = new Line[5];
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");
}
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();
}
monitor.expect(new String[] {
"Shape constructor",
"Shape constructor",
"Drawing Line: 0, 0",
"Shape constructor",
"Drawing Line: 1, 1",
"Shape constructor",
"Drawing Line: 2, 4",
"Shape constructor",
"Drawing Line: 3, 9",
"Shape constructor",
"Drawing Line: 4, 16",
"Shape constructor",
"Drawing Circle",
"Shape constructor",
"Drawing Triangle",
"Combined constructor",
"CADSystem.dispose()",
"Erasing Triangle",
"Shape dispose",
"Erasing Circle",
"Shape dispose",
"Erasing Line: 4, 16",
"Shape dispose",
"Erasing Line: 3, 9",
"Shape dispose",
"Erasing Line: 2, 4",
"Shape dispose",
"Erasing Line: 1, 1",
"Shape dispose",
"Erasing Line: 0, 0",
"Shape dispose",
"Shape dispose"
});
}
} ///:~
此系统中的每件事物都是某种 Shape(Shape 自身就是一种 Object,因为 Shape 继承自根类
Object)。每个类都重载 Shape 的 dispose( )方法,并运用 super 来调用该方法的基类版本。尽
管对象生命期中任何被调用的方法都可以做执行一些必需的清除工作,但是 Circle、Triangle
和 Line 这些特定的 Shape 类仍然都带有可以进行“绘制”的构造器。每个类都有自己的
dispose( )方法来将未存于内存之中的东西恢复到对象存在之前的状态。
在 main( )中,你将看到 try 和 finally 这两个新的关键字,我们将在第九章对它们进行正式介
绍。关键字 try 表示,下面的块(用一组大括号括起来的范围)是所谓的保护区(guarded region),
这意味着它需要被特殊处理。其中一项特殊处理就是无论 try 块是怎样退出的,保护区后的
finally 子句中的代码总是要被执行的。这里 finally 子句表示的是“无论发生什么事,一定要
为 x 调用 dispose( )”。这在第九章中,我们将对这些关键字作全面的解释。
请注意,在你的清除方法中,你还必须注意对基类和成员对象的清除方法的调用顺序,以防
某个子对象(subobject)依赖于另一个子对象情形的发生。一般而言,你采用的形式应该与
C++编译器在其析构函数上所施加的形式相同:首先,执行类的所有特定的清除动作,其顺
序同生成顺序相反(通常这就要求“基类”元素仍旧存活),然后,就如我们所示范的那样,
调用基类的清除方法。
许多情况下 ,“清除”并不是问题,你仅需要让垃圾回收器完成该动作就行。但当你必须亲
自处理此事时,你就得多做努力并多加小心。因为,一旦涉及垃圾回收,你能够信赖的事就
不会很多了。垃圾回收器可能永远也无法被调用,即使被调用,它也可能以任何它想要的顺
序来回收对象。最好的办法是除了内存以外,不要依赖垃圾回收器去做任何事。如果你需要
自动调用的函数。其原因可能是因为在 Java 中,我们的习惯只是忘掉而不是销毁对象,并
且让垃圾回收器在必要时释放其内存。
通常这样做是好事,但有时你的类可能要在其生命周期内执行一些必需的清除活动。正如我
们在第四章中所提到的那样,你并不知道垃圾回收器何时将会被调用,或者它是否将被调用。
因此,如果你想要某个类清除一些东西,就必须显式地编写一个特殊方法来做这件事,并要
确保客户端程序员知晓他们必须要调用这一方法。就像第九章(异常处理)所描述的那样,
其首要任务就是,你必须将这一清除动作置于 finally 字句之中,以预防异常的出现。
请思考一下下面这个能在屏幕上绘制图案的计算机辅助设计系统示例:
//: c06:CADSystem.java
// Ensuring proper cleanup.
package c06;
import com.bruceeckel.simpletest.*;
import java.util.*;
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");
}
void dispose() {
System.out.println("Erasing Circle");
super.dispose();
}
}
class Triangle extends Shape {
Triangle(int i) {
super(i);
System.out.println("Drawing Triangle");
}
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);
}
void dispose() {
System.out.println("Erasing Line: "+ start+ ", "+ end);
super.dispose();
}
}
public class CADSystem extends Shape {
private static Test monitor = new Test();
private Circle c;
private Triangle t;
private Line[] lines = new Line[5];
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");
}
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();
}
monitor.expect(new String[] {
"Shape constructor",
"Shape constructor",
"Drawing Line: 0, 0",
"Shape constructor",
"Drawing Line: 1, 1",
"Shape constructor",
"Drawing Line: 2, 4",
"Shape constructor",
"Drawing Line: 3, 9",
"Shape constructor",
"Drawing Line: 4, 16",
"Shape constructor",
"Drawing Circle",
"Shape constructor",
"Drawing Triangle",
"Combined constructor",
"CADSystem.dispose()",
"Erasing Triangle",
"Shape dispose",
"Erasing Circle",
"Shape dispose",
"Erasing Line: 4, 16",
"Shape dispose",
"Erasing Line: 3, 9",
"Shape dispose",
"Erasing Line: 2, 4",
"Shape dispose",
"Erasing Line: 1, 1",
"Shape dispose",
"Erasing Line: 0, 0",
"Shape dispose",
"Shape dispose"
});
}
} ///:~
此系统中的每件事物都是某种 Shape(Shape 自身就是一种 Object,因为 Shape 继承自根类
Object)。每个类都重载 Shape 的 dispose( )方法,并运用 super 来调用该方法的基类版本。尽
管对象生命期中任何被调用的方法都可以做执行一些必需的清除工作,但是 Circle、Triangle
和 Line 这些特定的 Shape 类仍然都带有可以进行“绘制”的构造器。每个类都有自己的
dispose( )方法来将未存于内存之中的东西恢复到对象存在之前的状态。
在 main( )中,你将看到 try 和 finally 这两个新的关键字,我们将在第九章对它们进行正式介
绍。关键字 try 表示,下面的块(用一组大括号括起来的范围)是所谓的保护区(guarded region),
这意味着它需要被特殊处理。其中一项特殊处理就是无论 try 块是怎样退出的,保护区后的
finally 子句中的代码总是要被执行的。这里 finally 子句表示的是“无论发生什么事,一定要
为 x 调用 dispose( )”。这在第九章中,我们将对这些关键字作全面的解释。
请注意,在你的清除方法中,你还必须注意对基类和成员对象的清除方法的调用顺序,以防
某个子对象(subobject)依赖于另一个子对象情形的发生。一般而言,你采用的形式应该与
C++编译器在其析构函数上所施加的形式相同:首先,执行类的所有特定的清除动作,其顺
序同生成顺序相反(通常这就要求“基类”元素仍旧存活),然后,就如我们所示范的那样,
调用基类的清除方法。
许多情况下 ,“清除”并不是问题,你仅需要让垃圾回收器完成该动作就行。但当你必须亲
自处理此事时,你就得多做努力并多加小心。因为,一旦涉及垃圾回收,你能够信赖的事就
不会很多了。垃圾回收器可能永远也无法被调用,即使被调用,它也可能以任何它想要的顺
序来回收对象。最好的办法是除了内存以外,不要依赖垃圾回收器去做任何事。如果你需要
进行清除,最好是编写你自己的清除方法,但不要依赖 finalize( )。