簡體   English   中英

Java:在finalize方法上寫入文件

[英]Java: file write on finalize method

據我了解,單例對象僅在應用程序即將終止時才會銷毀。 因此,在 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");
    }
}

這是那些奇怪的問題之一。 注意:我不會編寫自己的 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

Effective Java 第 2 版:第 7 項:避免使用終結器

終結器是不可預測的,通常是危險的,而且通常是不必要的。 它們的使用會導致行為不穩定、性能不佳和可移植性問題。 終結器幾乎沒有有效用途,[...] 但根據經驗,您應該避免使用終結器。

[...] 唯一聲稱可以保證終結的方法是System.runFinalizersOnExit及其邪惡的孿生兄弟Runtime.runFinalizersOnExit 這些方法存在致命缺陷,已被棄用。

正如其他答案所說,使用 finalize 被認為是不好的做法。 虛擬機關閉時發生某些事情的“官方”方法是使用關閉掛鈎

關閉掛鈎是您創建但不啟動的 Thread 對象。 您使用Runtime.getRuntime().addShutdownHook(Thread)注冊它,它將在 VM 關閉時調用。

Josh Bloch 在他的《Effective Java》一書中寫道,在終結器中做任何事情都是不好的做法。

您可以選擇在關閉應用程序時手動執行此操作:

  • 如果您關閉它(使用System.exit(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 通常不是一個好習慣,應該避免。

我建議您不要嘗試推出自己的日志框架,而是查看許多預先存在的 Java 日志框架之一。 我使用log4j ,但有很多選擇! 您將獲得更穩定的代碼、更少的調試工作以及可能更快的性能。 而且,它們不需要棘手的終結器行為。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM