2010-05-29 74 views
3

在我的理解中,一个单例对象只会在应用程序即将终止时才销毁。所以在C++中,我编写了一个Singleton类来记录我的应用程序,并在该Singleton记录器的析构函数中记录了我的应用程序终止的时间。事情在C++中完美运行。Java:在finalize方法上写文件


现在我想在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"); 
    } 
} 

回答

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

+0

看起来我有点慢。有人已经说过了.. – laher 2010-05-29 18:54:39

+0

感谢:D 我只是把它做完,没有任何定稿! 有人已经说过了,但你给了链接,这更有帮助。 感谢你和所有人:) – sowrov 2010-05-29 19:03:31

1

Josh Bloch在他的书“Effective Java”中写道,在终结器中做任何事都是不好的习惯。

你的选择是当你关闭应用程序手动执行此操作:

  • ,如果你关闭它(与System.exit(0)),调用代码中有
  • 如果用户关闭它,添加一个监听器,并调用它。
2

有效的Java第二版:第7项:避免终结

终结是不可预知的,往往是危险的,一般不需要。它们的使用可能会导致不稳定的行为,糟糕的性能和便携性问题。终结者没有多少有效用途,但作为一个经验法则,你应该避免终结者。

[...]声称保证最终确定的唯一方法是System.runFinalizersOnExit及其邪恶双胞胎,Runtime.runFinalizersOnExit。这些方法是致命的缺陷,并已被弃用。

2

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

关闭挂钩是您创建但未启动的线程对象。您在 Runtime.getRuntime().addShutdownHook(Thread)中注册它,并在VM关闭时调用它。

+0

+1 - 要知道,有办法,一个JVM可以在不运行关闭挂接的情况下终止**。例如,一个JVM崩溃或一个“kill -9”就可以做到这一点,硬件故障,电源故障或陨石撞击也是如此。 – 2010-05-30 01:28:23

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通常不是一个好的做法,应该避免。

0

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