Java try / catch / окончательная передовая практика при приобретении / закрытии ресурсов

Во время работы над школьным проектом я написал следующий код:

FileOutputStream fos; ObjectOutputStream oos; try { fos = new FileOutputStream(file); oos = new ObjectOutputStream(fos); oos.writeObject(shapes); } catch (FileNotFoundException ex) { // complain to user } catch (IOException ex) { // notify user } finally { if (oos != null) oos.close(); if (fos != null) fos.close(); } 

Проблема в том, что Netbeans сообщает мне, что строки resource.close() генерируют IOException и поэтому должны быть либо пойманы, либо объявлены. Он также жалуется, что oos и fos еще не могут быть инициализированы (несмотря на нулевые проверки).

Это кажется немного странным, видя, как все дело в том, чтобы остановить IOException прямо там.

Мое исправление коленного сустава заключается в следующем:

 } finally { try { if (oos != null) oos.close(); if (fos != null) fos.close(); } catch (IOException ex) { } } 

Но в глубине души это беспокоит меня и чувствует себя грязным.

Я пришел из фона C #, где я просто использовал бы using блок, поэтому я не уверен, что «правильный» способ справиться с этим.

Каков правильный способ справиться с этой проблемой?

Если вы пытаетесь поймать и сообщить обо всех исключениях в источнике, лучшим решением будет следующее:

 ObjectOutputStream oos = null; try { oos = new ObjectOutputStream(new FileOutputStream(file)); oos.writeObject(shapes); oos.flush(); } catch (FileNotFoundException ex) { // complain to user } catch (IOException ex) { // notify user } finally { if (oos != null) { try { oos.close(); } catch (IOException ex) { // ignore ... any significant errors should already have been // reported via an IOException from the final flush. } } } 

Заметки:

  • Стандартные streamи оболочек Java, читатели и писатели распространяются close и flush к их обернутым streamам и т. Д. Таким образом, вам нужно только закрыть или очистить внешнюю оболочку.
  • objective сброса явно в конце блока try заключается в том, что (реальный) обработчик для IOException получает вид сбоя записи 1 .
  • Когда вы выполняете закрытие или завершение streamа вывода, существует вероятность «один раз в синей луне», что исключение будет выбрано из-за ошибок диска или полной файловой системы. Вы не должны раздавить это исключение! ,

Если вам часто приходится «закрывать возможно пустой stream, игнорируя IOExceptions», тогда вы можете написать себе вспомогательный метод следующим образом:

 public void closeQuietly(Closeable closeable) { if (closeable != null) { try { closeable.close(); } catch (IOException ex) { // ignore } } } 

то вы можете заменить предыдущий блок finally следующим образом:

 } finally { closeQuietly(oos); } 

(Еще один ответ указывает, что метод closeQuietly уже доступен в библиотеке Apache Commons … если вы не возражаете добавить зависимость от своего проекта для метода с 10 строками. UPDATE : обратите внимание, что эти методы устарели в версии 2.6 API.)

Но будьте осторожны, если вы используете только closeQuietly в streamах, где исключения IO действительно неактуальны.

1 – Это не обязательно при использовании try-with-resources.


По вопросу flush() и close() о которых люди спрашивают:

  • Стандартные «фильтрующие» и «буферизованные» выходные streamи и писатели имеют контракт API, который утверждает, что close() заставляет сбросить все буферизованные выходные данные. Вы должны обнаружить, что все остальные (стандартные) classы вывода, которые выполняют буферизацию вывода, будут вести себя одинаково. Таким образом, для стандартного classа избыточно вызывать flush() непосредственно перед close() .
  • Для пользовательских и сторонних classов вам необходимо исследовать (например, прочитать javadoc, посмотреть код), но любой метод close() , который не очищает буферизованные данные, может быть нарушен .
  • Наконец, возникает вопрос, что делает flush() . Что говорит javadoc, это (для OutputStream …)

    Если предполагаемый пункт назначения этого streamа является абстракцией, предоставляемой базовой операционной системой, например файлом, то очистка streamа гарантирует, что только байты, ранее записанные в stream, передаются в операционную систему для записи; это не гарантирует, что они фактически записаны на физическое устройство, такое как дисковод.

    Итак … если вы надеетесь / предположите, что вызов flush() гарантирует, что ваши данные будут сохраняться, вы ошибаетесь! (Если вам нужно что-то делать, посмотрите на метод FileChannel.force …)


С другой стороны, если вы можете использовать Java 7 или более позднюю версию, лучшим решением будет использование новых «try-in-resources», как описано в ответе @Mike Clark.

Если вы не используете Java 7 или более поздней версии для своего нового кода, вы, вероятно, находитесь в дыре и копаете глубже.

Текущая передовая практика для try / catch /, наконец, включающая объекты, которые могут быть закрыты (например, файлы), заключается в использовании инструкции try-with-resource от Java 7, например:

 try (FileReader reader = new FileReader("ex.txt")) { System.out.println((char)reader.read()); } catch (IOException ioe) { ioe.printStackTrace(); } 

В этом случае FileReader автоматически закрывается в конце инструкции try, без необходимости закрывать его в явном блоке finally. Здесь есть несколько примеров:

http://ppkwok.blogspot.com/2012/11/java-cafe-2-try-with-resources.html

Официальное описание Java:

http://docs.oracle.com/javase/7/docs/technotes/guides/language/try-with-resources.html

Java 7 добавит автоматические блоки управления ресурсами . Они очень похожи на использование C #.

Джош Блох написал техническое предложение , которое я настоятельно рекомендую прочитать. Не только потому, что он даст вам преимущество на предстоящей языковой функции Java 7, а потому, что спецификация мотивирует необходимость такой конструкции, и при этом иллюстрирует, как писать правильный код даже в отсутствие ARM.

Вот пример кода Аскера, переведенный в форму ARM:

 try (FileOutputStream fos = new FileOutputStream(file); ObjectOutputStream oos = new ObjectOutputStream(fos)) { oos.writeObject(shapes); } catch (FileNotFoundException ex) { // handle the file not being found } catch (IOException ex) { // handle some I/O problem } 

Обычно у меня есть небольшой class IOUtil с таким методом, как:

 public static void close(Closeable c) { if (c != null) { try { c.close(); } catch (IOException e) { // ignore or log } } } 

Как насчет этих парней? Нет нулевой проверки, не удивительно. После выхода все очищается.

 try { final FileOutputStream fos = new FileOutputStream(file); try { final ObjectOutputStream oos = new ObjectOutputStream(fos); try { oos.writeObject(shapes); oos.flush(); } catch(IOException ioe) { // notify user of important exception } finally { oos.close(); } } finally { fos.close(); } } catch (FileNotFoundException ex) { // complain to user } catch (IOException ex) { // notify user } 

К сожалению, поддержка языкового уровня отсутствует. Но есть много библиотек, которые делают это простым. Проверьте библиотеку commons-io. Или современный google-guava @ http://guava-libraries.googlecode.com/svn/trunk/javadoc/index.html

Ты делаешь это правильно. Мне это тоже надоедает. Вы должны инициализировать эти streamи явным образом – это обычное соглашение. Все, что вы можете сделать, это присоединиться к клубу и хотеть using .

Не прямой ответ на ваш вопрос, но это печальный факт, что, поскольку, finally и catch связаны с try людей думать, что они принадлежат друг другу. Лучший дизайн для блоков try – это либо catch либо, finally но не оба.

В этом случае ваши комментарии подсказывают, что что-то не так. Почему в методе, связанном с файлом IO, мы жалуемся на что-либо пользователю. Мы могли бы работать на сервере где-то с очевидным пользователем.

Таким образом, код, который вы вышеприведённый, должен иметь, finally потерпеть неудачу изящно, когда все пойдет не так. Он не обладает способностью разумно справляться с ошибками, поэтому ваш catch где-то выше в цепочке вызовов.

  • android.view.InflateException: двоичная строка XML-файла # 12: ошибка раздувания classа
  • Доступ к ресурсам JAR
  • Программно изменить значение цветового ресурса, полученного из ответа API
  • Загрузите ресурс, содержащийся в банке
  • Файл Java Jar: используйте ошибки ресурса: URI не является иерархическим
  • Доступ к сетевому ресурсу из VBScript, например, FileSystemObject
  • Чтение файла ресурсов изнутри банки
  • Загружать конкретную строку языка из ресурса?
  • Как я могу прочитать файл ресурсов из файла jar java?
  • Как ссылаться на ресурс CSS / JS / image в шаблоне Facelets?
  • Как правильно получить изображение из папки «Ресурсы» в NetBeans
  • Давайте будем гением компьютера.