3

据我了解,单例对象仅在应用程序即将终止时才会销毁。因此,在 C++ 中,我编写了一个 Singleton 类来记录我的应用程序,并在该 Singleton 记录器的析构函数中记录我的应用程序终止的时间。事情在 C++ 中完美运行。


现在我想在 Java 中使用相同的记录器,因为在 Java 中没有析构函数,所以我为该单例记录器实现了 finalize 方法。但似乎 finalize 方法实际上从未被调用过。因此,我System.runFinalizersOnExit(true);在代码中的某处添加了该行(尽管我知道它已被弃用),并且每次在应用程序终止之前都会调用该 finalize 方法。但是还是有问题!如果我尝试在该 finalize 方法中写入任何文件,它不起作用,尽管 System.out 工作没有任何问题!:(

你们能帮我解决这个问题吗?这是我尝试做的示例代码:

单例记录器类:

public class MyLogger {
    FileWriter writer;
    private MyLogger() {
        try {
            this.writer = new FileWriter("log.txt");
        }
        catch (IOException ex) {
        }
    }

    public static MyLogger getInstance() {
        return MyLoggerHolder.INSTANCE;
    }

    private static class MyLoggerHolder {
        private static final MyLogger INSTANCE = new MyLogger();
    }

    @Override
    protected void finalize () {
        try {
            super.finalize();
            System.out.println("Here"); //worked correctly.
            this.writer.write(new Date().toString()+System.getProperty("line.separator"));
            this.writer.write("End");
            this.writer.flush(); //does not work!
            this.writer.close();
        }
        catch (Throwable ex) {
        }
    }
    public synchronized void log(String str) {
        try {
            this.writer.write(new Date().toString()+System.getProperty("line.separator"));
            this.writer.write(str+"\n");
            this.writer.flush();
        }
        catch (IOException ex) {
        }
    }
 }

主要的:

public class Main {
    public static void main(String[] args) {
        System.runFinalizersOnExit(true);
        MyLogger logger = MyLogger.getInstance();
        logger.log("test");
    }
}
4

6 回答 6

3

这是那些奇怪的问题之一。注意:我不会编写自己的 Logging 类 - 看看 log4j 什么的......

如前所述,我真的不会真正使用 finalize。

...

根据您的说法,您的目标只是在程序停止时运行某些东西。

我会注册一个shutdownhook ...

shutdownhook 是一个用户定义的对象,它扩展了 Thread 类。例如,您可以在记录器中定义一个静态内部类

这是一个例子:

http://www.crazysquirrel.com/computing/java/basics/java-shutdown-hooks.jspx

这是 API 文档:http: //java.sun.com/j2se/1.4.2/docs/api/java/lang/Runtime.html#addShutdownHook%28java.lang.Thread%29

于 2010-05-29T18:53:25.903 回答
2

Effective Java 第 2 版:第 7 项:避免使用终结器

终结器是不可预测的,通常是危险的,而且通常是不必要的。它们的使用会导致行为不稳定、性能不佳和可移植性问题。终结器几乎没有有效用途,[...] 但根据经验,您应该避免使用终结器。

[...] 声称保证最终确定的唯一方法是System.runFinalizersOnExit及其邪恶的孪生兄弟,Runtime.runFinalizersOnExit. 这些方法存在致命缺陷,已被弃用。

于 2010-05-29T18:38:16.030 回答
2

正如其他答案所说,使用 finalize 被认为是不好的做法。虚拟机关闭时发生某些事情的“官方”方法是使用关闭挂钩

关闭挂钩是您创建但不启动的 Thread 对象。您使用 Runtime.getRuntime().addShutdownHook(Thread)注册它,它将在 VM 关闭时调用。

于 2010-05-29T18:43:27.557 回答
1

Josh Bloch 在他的《Effective Java》一书中写道,在终结器中做任何事情都是不好的做法。

您可以选择在关闭应用程序时手动执行此操作:

  • 如果您关闭它(使用System.exit(0)),请在此处调用该代码
  • 如果用户关闭它,添加一个监听器并在那里调用它。
于 2010-05-29T18:37:00.610 回答
0

除了使用 finalize 是不安全的这一事实(因为我确信您已经看到了这一点,因为您使用 System.runFinalizersOnExit 的方法已被弃用)您的 finalize 方法可能会引发异常/错误。在这种情况下,您的异常/错误在您的 catch 语句中被捕获,并且没有对其进行任何处理。更好的方法是:

try{
   System.out.println("Here"); //worked correctly.
   this.writer.write(new Date().toString()+System.getProperty("line.separator"));
   this.writer.write("End");
   this.writer.flush(); //does not work!
   this.writer.close();
}
catch(Throwable e){
   //Log or handle the issue
}
finally{
   super.finalize();
}

话虽如此,在 Java 中使用 finalize 通常不是一个好习惯,应该避免。

于 2010-05-29T18:46:38.260 回答
0

我建议您不要尝试推出自己的日志记录框架,而是查看许多预先存在的 Java 日志记录框架之一。我使用log4j,但有很多选择!您将获得更稳定的代码、更少的调试工作以及可能更快的性能。而且,它们不需要棘手的终结器行为。

于 2010-05-29T18:49:12.647 回答