Синтаксис пробного использования ресурсов Java 7 (также известный как блок ARM ( Автоматическое управление ресурсами )) хорош, короток и понятен при использовании только одного AutoCloseableресурса. Однако я не уверен, какова правильная идиома, когда мне нужно объявить несколько ресурсов, которые зависят друг от друга, например a FileWriterи a, BufferedWriterкоторые обертывают его. Конечно, этот вопрос касается любого случая, когда некоторые AutoCloseableресурсы упакованы, а не только эти два конкретных класса.
Я придумал три следующих варианта:
1)
Наивная идиома, которую я видел, состоит в объявлении только оболочки верхнего уровня в переменной, управляемой ARM:
static void printToFile1(String text, File file) {
try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
bw.write(text);
} catch (IOException ex) {
// handle ex
}
}
Это красиво и коротко, но оно сломано. Поскольку базовый FileWriterобъект не объявлен в переменной, он никогда не будет закрыт непосредственно в сгенерированном finallyблоке. Он будет закрыт только через closeметод упаковки BufferedWriter. Проблема заключается в том, что если исключение выдается из bwконструктора, оно closeне будет вызываться и, следовательно, базовое FileWriter не будет закрыто .
2)
static void printToFile2(String text, File file) {
try (FileWriter fw = new FileWriter(file);
BufferedWriter bw = new BufferedWriter(fw)) {
bw.write(text);
} catch (IOException ex) {
// handle ex
}
}
Здесь и базовый, и ресурс обёртывания объявляются в переменных, управляемых ARM, поэтому они оба обязательно будут закрыты, но базовый fw.close() будет вызываться дважды : не только напрямую, но и через обтекание bw.close().
Это не должно быть проблемой для этих двух конкретных классов, которые оба реализуют Closeable(что является подтипом AutoCloseable), чей контракт гласит, что closeразрешены множественные вызовы :
Закрывает этот поток и освобождает любые системные ресурсы, связанные с ним. Если поток уже закрыт, то вызов этого метода не имеет никакого эффекта.
Однако в общем случае у меня могут быть ресурсы, которые реализуют только AutoCloseable(и не реализуют Closeable), что не гарантирует, что их closeможно будет вызывать несколько раз:
Обратите внимание, что в отличие от метода закрытия java.io.Closeable, этот метод закрытия не обязательно должен быть идемпотентным. Другими словами, вызов этого метода close более одного раза может иметь некоторый видимый побочный эффект, в отличие от Closeable.close, который не должен иметь эффекта при вызове более одного раза. Однако разработчикам этого интерфейса настоятельно рекомендуется сделать их близкие методы идемпотентными.
3)
static void printToFile3(String text, File file) {
try (FileWriter fw = new FileWriter(file)) {
BufferedWriter bw = new BufferedWriter(fw);
bw.write(text);
} catch (IOException ex) {
// handle ex
}
}
Эта версия должна быть теоретически правильной, потому что только она fwпредставляет реальный ресурс, который необходимо очистить. Сам по bwсебе ресурс не содержит никаких ресурсов, он только делегирует его fw, поэтому его должно быть достаточно только для закрытия базового объекта fw.
С другой стороны, синтаксис немного нерегулярный, а также, Eclipse выдает предупреждение, которое я считаю ложной тревогой, но это все еще предупреждение, с которым нужно иметь дело:
Утечка ресурсов: «bw» никогда не закрывается
Итак, какой подход пойти? Или я пропустил какую-то другую идиому, которая является правильной ?
public BufferedWriter(Writer out, int sz)могу бросить IllegalArgumentException. Кроме того, я могу расширить BufferedWriter классом, который будет генерировать что-то из его конструктора, или создать любую пользовательскую оболочку, которая мне нужна.
BufferedWriterКонструктор может легко бросить исключение. OutOfMemoryErrorвероятно, является наиболее распространенным, поскольку он выделяет достаточный объем памяти для буфера (хотя это может означать, что вы хотите перезапустить весь процесс). / Вы должны flushсвой контекстуальный, BufferedWriterесли вы не близко и хотите сохранить содержание ( как правило , только случае , не исключение). FileWriterПодбирает то, что происходит в кодировке файла по умолчанию - лучше быть явным.