Java的finalize()方法在使用gc()方法后没有被调用?

3
为了演示Java中finalize()方法的工作原理,该方法在对象即将销毁时被调用。我编写了以下程序。
class counterTest{
    public static int count;
    public counterTest(){
        count++;
    }
}

public class finalize {

    public static void main(String args[]){

        counterTest obj1=new counterTest();
        System.out.println("Number of objects :" + counterTest.count);
        counterTest obj2=new counterTest();
        System.out.println("Number of objects :" + counterTest.count);
        Runtime rs=Runtime.getRuntime();
        obj1=null;
        obj2=null;
        rs.gc();
    }
    protected void finalize(){
        System.out.println("Program about to terminate");
        counterTest.count--;
        System.out.println("Number of objects :" + counterTest.count);
    }
}

我期望的输出结果应该是这样的:
Number of objects :1 
Number of objects :2 
Program about to terminate 
Number of objects :1 
Program about to terminate 
Number of objects :0

但我只得到了前两行的内容。因为我将对象引用设置为null,然后调用gc()方法,所以我希望在finalize()方法中编写的语句应该被显示。这是否意味着我们无法保证每次使用gc()方法时都会调用finalize()方法。


3
调用垃圾回收并不保证会执行finalize方法,它只是建议执行。 - gevorg
2
@Sai 请查看Java代码规范 - catch23
可能是如何在Java中强制进行垃圾回收?的重复问题。 - gevorg
请查看J. Bloch的《Effective Java》第二版,第7项,第27页,它对finalizers进行了很好的解释。 - Vladimir Vagaytsev
2个回答

2
首先,你的代码没有 finalize 类的实例(也称为对象)。main 方法是静态的,没有实例。
但即使你有一个实例,在 Java 中垃圾回收和调用 finalize() 方法并不是非常确定或保证的。它不像 C++ 中的析构函数。因此,即使你将 finalize() 方法添加到 counterTest 类中,并创建了一些实例,也不能保证它会被调用。
如果您想要类似于使用析构函数的行为,您需要围绕 try-with-resources 模式设计代码。然后,AutoCloseable 接口的 close() 方法 扮演析构函数的角色。
附注:在 Java 中,使用所谓的帕斯卡命名法作为类名有一个非常强的约定,因此你的类应该命名为 CounterTestFinalize

2

您的 finalize 方法应该在 counterTest 中,并且它将“可能”被调用。您实际上从未创建“finalize”类的实例。因此,您的 finalize 方法从未有机会执行。

这是更新后的代码,应该可以按照您的期望工作:

class counterTest{
    public static int count;
    public counterTest(){
        count++;
    }

    protected void finalize(){
        System.out.println("Program about to terminate");
        counterTest.count--;
        System.out.println("Number of objects :" + counterTest.count);
    }
}

public class Finalize {

    public static void main(String args[]){

        counterTest obj1=new counterTest();
        System.out.println("Number of objects :" + counterTest.count);
        counterTest obj2=new counterTest();
        System.out.println("Number of objects :" + counterTest.count);
        Runtime rs=Runtime.getRuntime();
        obj1=null;
        obj2=null;
        rs.gc();
    }

}

应该注意,"finalize"方法不应该被覆盖,因为它们是不可靠的。你永远不知道垃圾回收器何时会收集特定的对象,所以依赖它来关闭数据库连接或执行其他类似的操作是不可取的。

谢谢,现在它可以工作了。但是为什么finalize()方法应该在我们创建对象的类中呢?我的意思是,如果我们有10个类,那么每个类中都需要有一个finalize()方法吗? - Sai Sankalp
1
可能被调用。没有任何保证会调用GC,更不用说终结了。 - user207421
1
@SaiSankalp 这没有意义。每个类只能有一个 finalize() 方法。 - user207421
@EJP,这就是我所想的。因此,我们无法保证每次将对象引用设置为null时都会调用gc(),我是对的吗? - Sai Sankalp
在Java中,@Sai finalize()方法“实际上”从未被用于做任何事情。大多数情况下是垃圾收集器在使用它们。没有人期望您会在10个不同的类中覆盖10个finalize方法。在我职业生涯中,大多数情况下看到有人重写finalize()方法都是错误的。 - Alexander Petrov
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接