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