Синтаксис пробних ресурсів 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
конструктора 's, його 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, цей метод закриття не повинен бути ідентичним. Іншими словами, виклик цього методу закриття не один раз може мати деякий видимий побічний ефект, на відміну від 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
підбирає те, що відбувається з кодуванням файлів «за замовчуванням» - краще бути явним.