try-with-resources
Некоторые вещи, которые открывает программа, обязательно нужно закрыть: файл, соединение с базой данных, сетевое подключение. Незакрытый файл держит занятыми ресурсы системы, а записанное в него может так и не попасть на диск. Закрывать такие вещи можно в finally, но этот код длинный, и его часто забывают написать.
Вспомните дверь из урока о finally. Теперь представьте дверь с доводчиком — пружиной, которая закрывает её сама. Вы проходите, а дверь закрывается без вашей помощи. Java умеет работать с такими вещами так же: особый try закрывает их сам.
public class Main {
public static void main(String[] args) {
try (Door door = new Door("A")) {
System.out.println("inside " + door.name());
}
System.out.println("after");
}
}
record Door(String name) implements AutoCloseable {
@Override
public void close() {
System.out.println("close " + name);
}
}Программа печатает:
inside A
close A
afterРесурс и AutoCloseable
Ресурс — объект, который после работы нужно закрыть. Давайте разберём программу по шагам:
record Door(String name) implements AutoCloseable— наша дверь-ресурс.AutoCloseable— интерфейс из стандартной библиотеки с одним методомclose(), аDoorего реализует.try (Door door = new Door("A"))— ресурс объявлен в круглых скобках сразу послеtry.- Тело блока печатает
inside A. - На выходе из блока Java сама вызывает
door.close(), и печатаетсяclose A. - Затем выполняется код после
tryи печатаетafter.
try-with-resources — это try с ресурсами в скобках: при выходе из блока он вызывает close() у каждого из них. Компилятор превращает такой try в try и finally с вызовом close() — тот самый код, который пришлось бы писать вручную.
Ресурсом может быть только объект класса, который реализует AutoCloseable. В интерфейсе метод close() объявлен с throws Exception. Наша Door ничего не бросает и throws не пишет, поэтому catch здесь не нужен.
На собеседовании спрашивают: что такое try-with-resources и что нужно ресурсу? Ответ: блок, который сам закрывает ресурсы при любом выходе, а ресурс должен реализовать AutoCloseable.
Несколько ресурсов
Ресурсы в скобках разделяют точкой с запятой:
try (Door a = new Door("A"); Door b = new Door("B")) {
System.out.println("inside " + a.name() + b.name());
}inside AB
close B
close AРесурсы закрываются в обратном порядке: последний открытый — первым. Так и с дверьми: вы вошли через A, потом через B, а на выходе сначала проходите B. Поэтому ресурс, открытый позже, может пользоваться открытым раньше: пока он закрывается, тот ещё работает.
Исключение внутри try
Ресурс закрывается при любом выходе из блока, в том числе по исключению:
try (Door door = new Door("C")) {
System.out.println("inside " + door.name());
Integer.parseInt("x");
} catch (NumberFormatException e) {
System.out.println("caught");
}inside C
close C
caughtИсключение прервало тело блока, но дверь всё равно закрылась. И закрылась раньше, чем выполнился catch: сначала Java закрывает ресурсы, а потом ищет, кто ответит на тревогу.
Здесь картинка с доводчиком неточна. Настоящая дверь закрывается сразу, как только вы прошли. Ресурс закрывается только в конце всего блока try, а внутри блока с ним можно работать.
Частая ошибка
Метод close() у двери написали, а интерфейс забыли:
record Door(String name) { // ✗
public void close() {
System.out.println("close " + name);
}
}error: incompatible types: try-with-resources not applicable to variable typeПрограмма с try (Door door = new Door("A")) не компилируется, хотя метод close() у двери есть. Компилятор смотрит не на метод, а на тип: в скобки try можно поставить только AutoCloseable. Ошибку он показывает в строке с try и добавляет Door cannot be converted to AutoCloseable, а исправлять нужно заголовок записи: record Door(String name) implements AutoCloseable.
Главное
- try-with-resources сам вызывает
close()у каждого ресурса при любом выходе из блока. - Ресурс — объект класса, который реализует
AutoCloseable. - Несколько ресурсов закрываются в обратном порядке: последний открытый — первым.
- При исключении ресурсы закрываются раньше, чем выполняется
catch.