2010-05-29 37 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,但有很多選擇!您將獲得更穩定的代碼,更少的調試工作以及更快的性能。而且,他們不需要棘手的終結者行爲。