Конструкция try-with-resources в Java - Вопросы
Всего: 6 вопросов
1. Что такое try-with-resources в Java и чем он лучше закрытия ресурса в блоке finally?
Что такое try-with-resources в Java и чем он лучше закрытия ресурса в блоке finally?
try-with-resources — форма оператора try (Java 7), в которой ресурсы объявляются в круглых скобках после try и закрываются автоматически: close() вызывается при выходе из блока try и при обычном завершении, и при исключении. Ресурсом может быть объект, класс которого реализует AutoCloseable.
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
}
По сравнению с try-finally: не нужно писать вызов close() вручную; несколько ресурсов объявляются в одних скобках вместо вложенных try-finally; главное — исключение из close() не затирает исходное. В try-finally, если упали и readLine(), и close(), наружу уходит только исключение из close(), а настоящая причина сбоя теряется. В try-with-resources наружу уходит исключение из блока try, а исключение из close() добавляется к нему как подавленное.
2. Как объявить несколько ресурсов в try-with-resources и в каком порядке они закрываются?
Как объявить несколько ресурсов в try-with-resources и в каком порядке они закрываются?
Ресурсы перечисляются в одних скобках через точку с запятой. Открываются они слева направо, а закрываются в обратном порядке: последний открытый закрывается первым.
try (FileReader fr = new FileReader(path);
BufferedReader br = new BufferedReader(fr)) {
return br.readLine();
} // br is closed first, then fr
Если конструктор второго ресурса выбросит исключение, уже открытый первый ресурс всё равно будет закрыт. Поэтому обёртки над потоками лучше объявлять отдельными ресурсами: в записи new ObjectInputStream(new FileInputStream(f)) внутренний FileInputStream не является ресурсом и останется открытым, если конструктор ObjectInputStream выбросит StreamCorruptedException. Повторное закрытие (BufferedReader.close() уже закрыл FileReader) безопасно: для Closeable повторный вызов close() не должен давать эффекта.
3. Чем отличаются интерфейсы AutoCloseable и Closeable и что может быть ресурсом в try-with-resources?
Чем отличаются интерфейсы AutoCloseable и Closeable и что может быть ресурсом в try-with-resources?
Ресурсом может быть только объект, класс которого реализует java.lang.AutoCloseable: потоки ввода-вывода, сокеты, JDBC-соединения, Scanner, собственные классы.
AutoCloseable— пакетjava.lang, Java 7, методvoid close() throws Exception; повторный вызовclose()может иметь побочные эффекты.Closeable— пакетjava.io, Java 5, с Java 7 наследуетAutoCloseable; методvoid close() throws IOException; повторный вызовclose()должен ничего не делать (идемпотентность).
Так как Closeable расширяет AutoCloseable, все потоки из java.io подходят автоматически. В собственном ресурсе стоит сужать исключение в close(): объявить throws IOException или не объявлять проверяемых исключений вовсе — тогда вызывающему коду не придётся ловить общий Exception.
4. Что такое подавленные исключения в try-with-resources и как их получить через getSuppressed()?
Что такое подавленные исключения в try-with-resources и как их получить через getSuppressed()?
Если в блоке try выброшено исключение, а затем close() тоже выбрасывает исключение, наружу уходит исключение из блока try, а исключение из close() прикрепляется к нему методом addSuppressed(). Оно не теряется: метод Throwable.getSuppressed() возвращает массив Throwable[] с подавленными исключениями.
try (FaultyResource r = new FaultyResource()) { // close() throws IllegalStateException
throw new RuntimeException("try failed");
} catch (RuntimeException e) {
System.out.println(e.getMessage()); // try failed
for (Throwable s : e.getSuppressed()) {
System.out.println(s.getMessage()); // message from close()
}
}
В аналогичном коде с try-finally наружу ушло бы исключение из close(), а getSuppressed().length был бы равен 0. Отдельно в лог подавленные исключения не попадают, но e.printStackTrace() и большинство логгеров выводят их внутри стек-трейса основного исключения строкой Suppressed: ....
5. В каком порядке выполняются close(), catch и finally в try-with-resources?
В каком порядке выполняются close(), catch и finally в try-with-resources?
Сначала закрываются все ресурсы (в обратном порядке объявления), затем выполняется catch, последним — finally.
try (Resource r = new Resource("R")) { // prints "open R" / "close R"
throw new IllegalArgumentException("fail");
} catch (IllegalArgumentException e) {
System.out.println("catch: " + e.getMessage());
} finally {
System.out.println("finally");
}
// open R
// close R
// catch: fail
// finally
Следствия: в catch и finally ресурс уже закрыт и вне области видимости, а исключение, выброшенное при закрытии, тоже можно поймать в этом catch. Если исключение выброшено при создании ресурса (например, FileNotFoundException в конструкторе FileReader), тело блока try не выполняется, закрывать нечего, и управление сразу переходит в подходящий catch.
6. Что изменилось в try-with-resources в Java 9?
Что изменилось в try-with-resources в Java 9?
Начиная с Java 9 в скобках try-with-resources можно указать уже существующую переменную, если она final или effectively final (после инициализации ей ничего не присваивается). В Java 7 и 8 ресурс нужно было обязательно объявлять внутри скобок, даже если объект уже был создан.
// Java 7/8
try (BufferedReader br = reader) {
return br.readLine();
}
// Java 9+
try (reader) {
return reader.readLine();
}
Если переменной где-либо в методе присваивается новое значение, try (reader) не скомпилируется: переменная перестаёт быть effectively final.