这个工作效果符合预期,我认为在Java中,
finalize()
方法并没有受到任何不同于其他方法的特殊待遇。可能有点不同的是,
finalize()
方法通常只会被JVM垃圾回收器本身调用,如
JavaDoc所述:
当垃圾回收确定没有更多对该对象的引用时,由垃圾回收器在对象上调用。
此外,请注意Josh Bloch在
Effective Java中强烈反对使用finalizers:
Finalizers是不可预测的、常常危险的,而且一般是不必要的。它们的使用可能会导致不稳定的行为、性能差和可移植性问题。Finalizers有一些有效的用途...但作为一个经验法则,你应该避免使用finalizers。
考虑以下类似于您的示例的示例:
一个拥有覆盖了
finalize()
方法的基类。
public abstract class BaseClass {
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("BaseClass finalisation occured");
}
}
一个未覆盖finalize方法的子类:
public class SubClass extends BaseClass {
public void foo() {
System.out.println("SubClass Foo'd");
}
}
还需要一个带有基本主方法以运行所有内容的驱动程序类:
public class Driver {
public static void main(String[] args) {
SubClass sc = new SubClass();
sc.foo();
sc = null;
System.gc();
}
}
我们得到的输出如下:
SubClass Foo'd
BaseClass finalisation occured
Java方法查找的过程(非常简单的表述)是,在当前类中查找任何方法,如果没有,则向上遍历类层次结构,直到找到所需的方法。在上面的例子中,当在
SubClass
对象上调用
foo()
方法时,
SubClass
类包含方法定义,因此使用该实现,并且不会再往上遍历类层次结构。当调用
finalize()
方法(因为请求了
System.gc()
),该方法将首先在
SubClass
类中查找,但由于其没有包含
finalize()
的实现,所以会搜索其父类(
BaseClass
)。
BaseClass
包含
finalize()
的实现,因此使用该实现,并将一行打印到stdout。
现在考虑一个再次覆盖finalize()
的子子类:
public class OverridenSubClass extends SubClass {
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("Overriden finalize called, which calls super's finalize first");
}
}
以下是略作修改的Driver
类:
public class Driver {
public static void main(String[] args) {
OverridenSubClass sc = new OverridenSubClass();
sc.foo();
System.out.println(sc.toString());
sc = null;
System.gc();
System.exit(0);
}
}
这会产生以下输出:
SubClass Foo'd
finalize.OverridenSubClass@7150bd4d
BaseClass finalisation occured
Overriden finalize called, which calls initial finalize first
希望这符合预期。需要注意的有:
- 我们没有在任何类中覆盖
toString()
方法,因此使用Object.toString()
实现。
- 变量
sc
的类型并不决定所使用的方法实现 - 而是由sc
引用的实际对象类型决定。