Що таке 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]]