在我的理解中,单个对象只有在应用程序即将终止时才会销毁。所以在C ++中我编写了一个Singleton类来记录我的应用程序,在Singleton logger的析构函数中,我记录了我的应用程序终止时的时间。事情在C ++中完美运作。
现在我想在Java中使用相同的记录器,因为在java中没有析构函数,所以我为该单例记录器实现了finalize方法。但似乎finalize方法实际上永远不会被调用。
所以,我在我的代码中的某处添加System.runFinalizersOnExit(true);
行(虽然我知道它已被弃用)并且每次在应用程序终止之前调用finalize方法。但还是有问题!如果我尝试在finalize方法中写入任何文件,它不起作用,虽然System.out没有任何问题! :(
你们可以帮我解决这个问题吗?以下是我尝试做的示例代码:
Singleton Logger类:
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");
}
}
答案 0 :(得分: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
答案 1 :(得分:2)
Effective Java 2nd Edition:第7项:避免使用终结器
终结者是不可预测的,通常是危险的,而且通常是不必要的。它们的使用会导致不稳定的行为,性能不佳和可移植性问题。终结器的有效用途很少,但根据经验,你应该避免使用终结器。
[...]声称保证最终确定的唯一方法是
System.runFinalizersOnExit
及其邪恶双胞胎Runtime.runFinalizersOnExit
。这些方法存在致命缺陷,已被弃用。
答案 2 :(得分:2)
正如其他答案所说,使用finalize被认为是不好的做法。当VM关闭时发生某些事情的“官方”方式是使用关闭挂钩。
关闭挂钩是您创建的Thread对象,但不启动。你用它注册 Runtime.getRuntime().addShutdownHook(Thread)并在VM关闭时调用它。
答案 3 :(得分:1)
Josh Bloch在他的“Effective Java”一书中写道,在终结者中做任何事都是不好的做法。
您可以选择在关闭应用程序时手动执行此操作:
System.exit(0)
),请在那里调用该代码答案 4 :(得分: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通常不是一个好习惯,应该避免使用。
答案 5 :(得分:0)
我建议您不要尝试使用自己的日志记录框架,而是查看许多预先存在的Java日志框架之一。我使用log4j,但有很多选择!您将获得更稳定的代码,更少的调试工作,并可能更快的性能。而且,他们不需要棘手的终结者行为。