2012-11-01 4 views
2

Представьте, что MyOpenedFile - это что-то, обертывающее файл с открытыми потоками. Теперь предположим, этот код:Есть ли чистый способ предоставить объекты объектов Java с уничтожением?

// method in an Util class 
static void safeClose(MyOpenedFile f) { 
    if (f != null) { 
    try { 
     f.close(); 
    } catch(IOException ex) { /* add logging or console output */ } 
    } 
} 

Фактический метод вопрос:

void doSomeFileOperation(...) throws IOException, ... { 
    MyOpenedFile f1 = null; 
    MyOpenedFile f2 = null; 
    try { 

     /* method's "business logic" code beings */ 
     f1 = new MyOpenedFile(...); 
     //do stuff 
     f2 = new MyOpenedFile(...); 
     // do stuff 
     f1.close(); f1 = null; 
     // do stuff with f1 closed 
     f2.close(); f2 = null; 
     // do stuff with f2 closed 
     /* method's "business logic" code ends */ 

    } finally { 
     Util.safeClose(f1); f1 = null; 
     Util.safeClose(f2); f2 = null; 
    } 
} 

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

Итак, есть лучше/красивее/уборщик способ обернуть выше части бизнес-логики кода, так что любые исключения получить размножают, но оба файла f1 и f2 закрываются (или по крайней мере близко предпринимается на обоих, даже если он не получится)?

Также ответы, указывающие на любые библиотеки с открытым исходным кодом, такие как Apache Commons, обеспечивая приятные обертки.

+0

Кстати, ваш пример кода имеет ошибку в нем, что будет на самом деле привести вас к утечке дескрипторов файлов, так как вы установите f1 = нуль, прежде чем закрыть его. – Chii

+0

@Chii Нет, это не так, насколько я вижу. Там 'close' или' safeClose' * перед * все 'null' назначения. – hyde

+0

Почему вы дважды нажимаете 'close' на файл (в конце концов и в конце)? – home

ответ

4

Файл является оболочкой для String, которая содержит имя файла, которое может или не может существовать. Он не имеет гражданства, поэтому вам не нужно его закрывать.

Ресурс вам нужно закрыть это FileInputStream или BufferedReader, и вы можете закрыть это неявно с ARM в Java 7

try(BufferedReader br = new BufferedReader(new FileReader(file))) { 

} 

Это закроет br whent он блокирует выходы.

http://www.oracle.com/technetwork/articles/java/trywithresources-401775.html

+1

Я решил принять ваш ответ за то, что был самым точным, и с хорошим примером кода ИМО (иногда маленьким красивым), спасибо. Теперь все, что остается для меня, - это выяснить, могу ли я использовать Java7 для фактического ...: - / – hyde

0

Опция, которая автоматически приходит мне в голову: Отделите код, который обрабатывает файлы, из кода, который выполняет любую обработку. Таким образом, вы можете инкапсулировать неприятный код, который обрабатывает операции открытия, закрытия и обработки исключений.

Другой бит, что образец у вас есть действительно много лишних, ненужных шагов

void doSomeFileOperation(...) throws IOException, ... { 
    File f1 = null; 
    File f2 = null; 
    try { 
     f1 = new File(...); 
     f2 = new File(...); 
     // callback to another class/method that does the real work 
    } finally { 
     Util.safeClose(f1); 
     Util.safeClose(f2); 
    } 
} 

Вам не нужно устанавливать экземпляры файлов в нуль. Если вы попытаетесь использовать их, вы получите исключение.

Я не удивляюсь, какой File объект, который вы используете. Стандартный класс File в java не имеет метода close().

+0

Я изначально думал то же самое, но недостаток в том, что требование, что '// делать вещи с f1 закрыто', больше не сохраняется. Вам нужно будет передать два разных обратных вызова, и поэтому это не устранило проблему. – Chii

+0

Да, в зависимости от точного бизнес-кода, очистка может быть оптимизирована. Но это приводит к * разному * шаблону кода для разных случаев, что может быть еще хуже. – hyde

1

Посмотрите на The try-with-resources Statement, который закроет ресурсы после завершения блока try.

Класс File, который вы используете, кажется, не java.io.File, потому что он не имеет никакого метода close(). В этом случае убедитесь, что ваш собственный класс File реализует Closeable, чтобы он работал с ARM.

try (FileInputStream f1 = new FileInputStream("test1.txt"); 
    FileInputStream f2 = new FileInputStream("test2.txt")) { 
    // Some code 
} catch (FileNotFoundException e) { 
    e.printStackTrace(); 
} catch (IOException e) { 
    e.printStackTrace(); 
} 
+0

Спасибо, указав на вещь «Файл», я изменил имя класса и добавил комментарий к вопросу, объясняющий логику имени пользовательского класса. – hyde

+1

Один nitpick, больше похожий на обычное разглагольствование, не направленное на вас: Я думаю, что такие блоки catch должны использоваться очень редко. Почти всегда плохая идея - просто поймать исключение, зарегистрировать его и продолжить, как будто ничего не произошло. Если исключение можно безопасно игнорировать, запись в него просто беспорядка. Если нужно предпринять действие, то это должно быть больше, чем просто его регистрация (например, всплывающее диалоговое окно в графическом приложении). В большинстве случаев исключение должно допускаться для распространения, иногда как причина другого более значимого исключения. Простое ведение журнала полезно в основном, когда * programmer * не уверен в правильности дела. – hyde

1

Вам не нужно закрывать файлы (которые являются представлением файлов в файловой системе), как упомянуто здесь:

Do I need to close files I perform File.getName() on?

Я предполагаю, что вы просите больше о файловых потоках/читателях?

В этом случае Java 7 имеет хорошую новую особенность: http://www.vineetmanohar.com/2011/03/java-7-try-with-auto-closable-resources/

Если вы работаете на более старую версию Java я просто держать его просто с этим:

void doSomeFileOperation(...) throws IOException, ... { 
    FileInputStream f1 = null; 
    FileInputStream f2 = null; 
    try { 

    // do stuff 

    } finally { 
    Util.safeClose(f1); 
    Util.safeClose(f2); 
    } 
}