Откройте два ресурса в одном try-with-resources и определите порядок их закрытия
Resource печатает каждое открытие и каждое закрытие. Допишите run() так, чтобы ресурс с именем A и ресурс с именем B открывались в одном заголовке try-with-resources, сначала A, и оба закрывались автоматически по выходе из блока.
Ограничения:
- один заголовок
try, держащий оба ресурса, — безfinallyи без ручного вызоваclose() - затем назовите ровно четыре строки, которые программа печатает, по порядку
class Resource implements AutoCloseable {
private final String name;
Resource(String name) { this.name = name; System.out.println("open " + name); }
@Override public void close() { System.out.println("close " + name); }
}
void run() {
// ваш код здесь
}
Допишите реализацию.
Объявите оба ресурса в одном заголовке try через точку с запятой. Закрываются они в порядке, обратном объявлению, поэтому программа печатает open A, open B, close B, close A. Обратный порядок гарантирован языком: поздний ресурс часто построен поверх раннего. Каждый объявленный ресурс закрывается, даже если предыдущий close() бросил исключение, — этот сбой не теряется, а записывается в пробрасываемое исключение как подавленный.
- ✗Ждать закрытия ресурсов в порядке объявления, а не в обратном
- ✗Считать, что упавший
close()одного ресурса оставит остальные незакрытыми - ✗Добавлять
finallyс ручнымclose()поверх try-with-resources
- →Почему язык закрывает поздний ресурс раньше, а не ранний?
- →Куда девается сбой
close(), если телоtryуже бросило исключение?
Решение
void run() {
try (Resource a = new Resource("A");
Resource b = new Resource("B")) {
// работа с a и b
}
}
Вывод:
open A
open B
close B
close A
Почему обратный порядок. Ресурсы в заголовке try — это последовательность объявлений, и поздний ресурс часто построен поверх раннего (BufferedWriter поверх FileWriter, PreparedStatement поверх Connection). Закрывать надо от зависимого к базовому, иначе поздний ресурс попытается доработать поверх уже закрытого. Поэтому язык фиксирует именно обратный порядок — это не деталь реализации, а гарантия JLS.
Закрываются все. Компилятор разворачивает заголовок во вложенные try/finally, поэтому даже если close() у B бросит исключение, close() у A всё равно будет вызван. Первый сбой пробрасывается, второй прикрепляется к нему как подавленное исключение и достаётся через getSuppressed() — ни один не теряется молча.