Что такое try-with-resources?
Раньше нужно было вручную закрывать ресурсы в finally:
Junior Level
Определение
Try-with-resources (TWR) — это конструкция Java 7+. Минимальная версия: Java 7. Для Java 6 и ниже — используйте finally. В Android — API 19+ (KitKat). Автоматически закрывает ресурсы после выхода из блока try.
Зачем нужен
Раньше нужно было вручную закрывать ресурсы в finally:
// Старый подход (до Java 7)
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader("file.txt"));
br.readLine();
} finally {
if (br != null) br.close(); // Руками!
}
Теперь автоматически:
// Современный подход (Java 7+)
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
br.readLine();
} // br.close() вызовется автоматически!
Какие ресурсы можно использовать
Любые, реализующие AutoCloseable:
FileInputStream,BufferedReader— файлыConnection,Statement— база данныхSocket— сеть
Несколько ресурсов
try (FileInputStream fis = new FileInputStream("in.txt");
FileOutputStream fos = new FileOutputStream("out.txt")) {
// Оба закроются автоматически
}
Когда НЕ использовать try-with-resources
- Фабричные методы – создаёте ресурс и возвращаете вызывающему (TWR закроет его до return)
- Ресурс живёт дольше одного метода – lifecycle управляется извне
- Пулированные ресурсы (HikariCP connection) – возврат в пул != закрытие, пул сам управляет
Middle Level
Как это работает
Компилятор разворачивает TWR в код с finally и обработкой suppressed исключений:
// Ваш код
try (Resource r = new Resource()) {
r.doWork();
}
// Компилятор создаёт этот шаблон, чтобы:
// (1) гарантировать закрытие ресурса
// (2) не потерять исключение из close() если в try уже была ошибка
// -- оно добавляется в suppressed-список primaryException
// Разворачивается в:
Resource r = new Resource();
Throwable primaryException = null;
try {
r.doWork();
} catch (Throwable t) {
primaryException = t;
throw t;
} finally {
if (r != null) {
if (primaryException != null) {
try { r.close(); }
catch (Throwable t) { primaryException.addSuppressed(t); }
} else {
r.close();
}
}
}
Suppressed Exceptions
Если в try упало исключение #1, а при close() — исключение #2:
- #1 остаётся основным
- #2 добавляется как suppressed (подавленное)
- Вы получаете полную картину:
Error A (Suppressed: Error B)
Получить suppressed: exception.getSuppressed()
Эффект ловушки вложенных ресурсов
// ОПАСНО — FileWriter может утечь!
try (BufferedWriter writer = new BufferedWriter(new FileWriter("file.txt"))) {
// Если конструктор BufferedWriter упадёт, FileWriter НЕ закроется
}
// БЕЗОПАСНО — каждый ресурс отдельно
try (FileWriter fw = new FileWriter("file.txt");
BufferedWriter writer = new BufferedWriter(fw)) {
writer.write("data");
}
Java 9+ — effectively final
Можно использовать внешние переменные:
BufferedReader br = createReader();
try (br) { // br — effectively final
br.readLine();
}
Senior Level
Порядок закрытия (LIFO)
Ресурсы закрываются в порядке, обратном их объявлению:
LIFO (Last In, First Out) – последний открытым, первый закрытым. Как стопка тарелок: последнюю положил – первую берёшь. Для ресурсов это значит: самый «внутренний» ресурс закрывается первым.
try (Socket socket = new Socket("localhost", 8080);
OutputStream os = socket.getOutputStream();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os))) {
// Закроются: writer → os → socket
}
Это критично для стеков потоков. Если закрыть Socket раньше Stream, Stream упадёт при попытке flush.
Null Resources
Если ресурс в скобках инициализирован как null, TWR просто проигнорирует его — NPE не будет.
Производительность: Suppressed Overhead
При закрытии 1000 ресурсов и массовом сбое, объект основного исключения содержит огромный список suppressed. Это может ударить по памяти.
Edge Cases
- Catch/Finally порядок — ваши
catchиfinallyвыполняются после закрытия ресурсов - Внутри
catchресурсы уже недоступны (закрыты) - Компилятор создаёт скрытую локальную переменную для внешних ресурсов (Java 9+)
Диагностика
Throwable.getSuppressed()— всегда проверяйте этот массив при проблемах с инфраструктурой- Static Analysis — SonarLint находит ресурсы, созданные внутри
tryблока, но не в скобках javap -v— посмотрите сгенерированный байт-код для понимания механизма
🎯 Шпаргалка для интервью
Обязательно знать:
- Try-with-resources (TWR) — Java 7+, автоматически закрывает ресурсы после выхода из
try - Ресурс обязан реализовывать
AutoCloseable - Компилятор разворачивает TWR в
finallyс обработкой suppressed исключений - Suppressed exceptions: если в
tryиclose()упали два — #1 основное, #2 suppressed - Порядок закрытия — LIFO (последний открытым, первый закрытым)
- Эффект ловушки вложенных ресурсов:
new B(new A())— если B упадёт, A утечёт - Java 9+ поддерживает effectively final внешние переменные
- Null ресурс в TWR просто игнорируется — NPE не будет
Частые уточняющие вопросы:
- Почему вложенные ресурсы опасны? — Если конструктор внешнего ресурса упадёт, внутренний утечёт; объявляйте каждый отдельно
- В каком порядке закрываются ресурсы? — LIFO: последний объявленным закрывается первым
- Что будет если ресурс = null? — TWR проигнорирует его, NPE не возникнет
- Когда НЕ использовать TWR? — Фабричные методы (возврат из метода), пулированные ресурсы, ресурс живёт дольше метода
Красные флаги (НЕ говорить):
- “Я создаю ресурсы внутри try, но не в скобках” — они не закроются автоматически
- “При массовом сбое suppressed исключения не важны” — они могут содержать критичную диагностику
- “TWR работает только с InputStream” — работает с любым AutoCloseable
Связанные темы:
- [[Какие требования к ресурсам в try-with-resources]]
- [[Что такое AutoCloseable интерфейс]]
- [[В чём разница между AutoCloseable и Closeable]]
- [[Что такое suppressed exceptions]]
- [[Гарантируется ли выполнение блока finally]]