finalize()有一个有趣的用法,它并不依赖于每次都要对finalize()进行调用,这就是对象终结条件的验证。
当对某个对象不再感兴趣----也就是它可以被清理了,这个对象应该处于某种状态,使它占用的内存可以被安全地释放。
例如,要是对象代表了一个打开的文件,在对象被回收前程序员应该关闭这个文件。只要对象中存在没有被适当清理的
部分,程序就存在很隐晦的缺陷。finalize()可以用来最终发现这种情况----尽管它并不总是被调用。如果某次finalize()的
动作使得缺陷被发现,那么就可以找出问题所在 ,这才是人们真正关心的。
举个例子:
package test;
public class Tank {
boolean status=false;//false为空,true为满
Tank(boolean stat){
status=stat;
}
void empty(){
status=false;
}
protected void finalize(){
if(status)
System.out.println("error");
}
}
package test;
public class Test {
public static void main(String args[]){
Tank tk1=new Tank(true);
tk1.empty();
//Tank tk2=new Tank(true);
new Tank(true); //对象一创建就成为垃圾,因为没有引用指向它
System.gc();
}
}
程序运行结果如图:
本例的终结条件是:所有的Tank对象在被当做垃圾回收前都应该置为空,但是在main方法中,由于程序员的错误,有一个tank被置为满。
要是没有finalize()来验证终结条件,将很难发现这种缺陷。
注意,System.gc()用于强制进行终结动作即垃圾回收动作。而如果不强制这么做,假设在之后的程序中由于分配大量的存储空间而导致垃圾回收动作的
执行,也能找到错误的Tank对象,即执行finalize()方法。
而需要注意的一点是在上面源代码中我注释了Tank tk2=new Tank(true);假如不把它注释,而是把new Tank(true);注释会出现怎么样的情况呢?即源代码如下:
package test;
public class Test {
public static void main(String args[]){
Tank tk1=new Tank(true);
tk1.empty();
Tank tk2=new Tank(true);
//new Tank(true);
System.gc();
}
}
程序运行结果如图:
不会执行finalize()方法,即不会打印错误信息。这是为什么呢?
是由于System.gc()的运行机制有关,准确来说是java的垃圾回收机制有关。
第一种情况new Tank(true);只是创建一个对象,并没有引用指向它,所以该对象的引用计数为0,即这个对象一创建出来就成为垃圾,所以调用System.gc()会执行回收动作。
而第二种情况Tank tk2=new Tank(true);是创建一个对象,并且让tk2指向该对象,所以该对象的引用计数为1,即还有引用指向该对象,所以不需要被回收,调用System.gc()并不会执行回收动作,因为没有垃圾,所以程序结果自然为没有打印任何东西。
最后是System.gc()方法的源代码解释:
Runs the garbage collector.
Calling the gc
method suggests that the Java Virtual Machine expend effort toward recycling unused objects in order to make the memory they currently occupy available for quick reuse. When control returns from the method call, the Java Virtual Machine has made a best effort to reclaim space from all discarded objects.
即调用垃圾回收器。调用gc方法表明Java虚拟机尽最大努力去回收未使用的对象从而使目前可用的内存得到快速重复利用。当控制器执行这个方法调用时,java虚拟机尽最大努力去回收废弃对象的空间。