Java: запись файла в метод финализации - PullRequest
3 голосов
/ 29 мая 2010

В моем понимании одноэлементный объект будет уничтожен только тогда, когда приложение собирается завершить работу. Поэтому в C ++ я пишу класс Singleton для регистрации моего приложения, а в деструкторе этого журнала Singleton я записываю время, когда мое приложение было закрыто. Вещи отлично работали в C ++.


Теперь я хочу иметь тот же логгер в Java, так как в Java нет деструктора, поэтому я реализовал метод finalize для этого одноэлементного логгера. Но кажется, что метод finalize никогда не вызывается. Итак, я добавляю строку System.runFinalizersOnExit(true); где-то в моем коде (хотя я знаю, что она устарела) и этот метод 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) {
        }
    }
 }

Main:

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

Ответы [ 6 ]

3 голосов
/ 29 мая 2010

Это один из тех любопытных вопросов. Примечание: я бы не стал писать свой собственный класс Logging - посмотрите на log4j или что-то в этом роде ...

Как уже говорилось, я бы не стал использовать finalize.

...

Исходя из того, что вы говорите, ваша цель - просто запустить что-то, когда программа остановится.

Я бы вместо этого зарегистрировал отключение ...

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

2 голосов
/ 29 мая 2010

Как говорят другие ответы, использование финализации считается плохой практикой. «Официальный» способ добиться чего-либо, когда виртуальная машина выключается, - это использовать shutdown hook .

Хук завершения работы - это объект Thread, который вы создаете, но не запускаете. Вы регистрируете это с Runtime.getRuntime (). AddShutdownHook (Thread) , и он будет вызываться при выключении виртуальной машины.

2 голосов
/ 29 мая 2010

Effective Java 2nd Edition: пункт 7. Избегайте финализаторов

Финализаторы непредсказуемы, часто опасны и, как правило, не нужны . Их использование может вызвать нестабильное поведение, плохую производительность и проблемы с переносимостью. У финализаторов мало действительных применений, [...] но, как правило, следует избегать финализаторов.

[...] Единственные методы, которые утверждают, что гарантируют завершение, это System.runFinalizersOnExit и его злой близнец Runtime.runFinalizersOnExit. Эти методы имеют серьезные недостатки и устарели.

1 голос
/ 29 мая 2010

Джош Блох писал в своей книге «Эффективная Java», что в финализаторах делать что-либо плохая практика.

Вы можете сделать это вручную, когда закрываете приложение:

  • если вы закроете его (с помощью System.exit(0)), назовите этот код там
  • если пользователь закрывает его, добавьте слушателя и вызовите его там.
0 голосов
/ 29 мая 2010

Я бы посоветовал вместо того, чтобы пытаться развернуть собственную структуру ведения журналов, вы воспользуетесь одной из многих уже существующих сред ведения журналов Java. Я использую log4j , но есть много вариантов! Вы получите более стабильный код, меньше отладочной работы и, возможно, более высокую производительность. И им не нужно хитрое поведение финализатора.

0 голосов
/ 29 мая 2010

Помимо того факта, что использование 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, как правило, не является хорошей практикой, и ее следует избегать.

...