Какие требования к ресурсам в try-with-resources?
Ресурс обязан реализовывать интерфейс java.lang.AutoCloseable. Без этого его нельзя использовать в try-with-resources.
Junior Level
Главное требование
Ресурс обязан реализовывать интерфейс java.lang.AutoCloseable. Без этого его нельзя использовать в try-with-resources.
// Работает — FileInputStream реализует AutoCloseable
try (FileInputStream fis = new FileInputStream("file.txt")) {
fis.read();
}
// Не скомпилируется — String не реализует AutoCloseable
try (String s = "hello") { } // Ошибка компиляции
Какие классы реализуют AutoCloseable
- Все
InputStream/OutputStream— файлы, сеть - Все
Reader/Writer— текст Connection,Statement,ResultSet— база данныхScanner,Formatter— утилиты
Несколько ресурсов
try (FileInputStream fis = new FileInputStream("in.txt");
FileOutputStream fos = new FileOutputStream("out.txt")) {
// Оба закроются автоматически
}
Java 9+ — внешние переменные
FileInputStream fis = createStream();
try (fis) { // fis должен быть effectively final
fis.read();
}
Когда НЕ класть ресурс в try-with-resources
- Ресурс управляется контейнером (Spring @Bean) – фреймворк сам закроет
- Соединение из пула (HikariCP) – пул управляет lifecycle за вас
- Нужно передать ресурс дальше – return из метода, TWR закроет до возврата
Middle Level
AutoCloseable vs Closeable
AutoCloseable (Java 7, java.lang):
void close() throws Exception- Универсальный — для любых ресурсов
Closeable (Java 5, java.io):
void close() throws IOException- Только для I/O
- Наследует
AutoCloseable
Для try-with-resources подходит любой из них.
Риск утечки при инициализации
// БЕЗОПАСНО — если B упадёт, A закроется
try (A a = new A(); B b = new B(a)) { }
// ОПАСНО — если B упадёт, A утечёт
try (B b = new B(new A())) { }
// A создан, но try не имеет на него ссылки
// Разбор утечки: new A() создал объект. new B(a) бросил исключение.
// A создан, но переменная в TWR -- это B. У TWR нет ссылки на A,
// поэтому close() для A не вызовется. Решение:
// try (A a = new A(); B b = new B(a)) { ... }
Идемпотентность close()
Метод close() должен быть идемпотентным — повторный вызов не должен выбрасывать исключение.
Идемпотентность – свойство операции: повторный вызов даёт тот же результат, что и первый. close() должен быть идемпотентным: первый вызов закрывает ресурс, второй – ничего не делает (не бросает исключение).
public void close() {
if (closed.compareAndSet(false, true)) {
// реальное освобождение ресурсов
}
}
Требования к переменным (Java 9+)
Ресурс должен быть final или effectively final. Компилятор создаёт локальную копию ссылки — это гарантирует, что если другой поток изменит внешнюю переменную, TWR всё равно закроет оригинальный объект.
effectively final = переменная не меняется после инициализации, даже если ключевое слово final не написано. Компилятор видит, что вы не меняете переменную, и разрешает использовать в TWR.
Senior Level
Under the Hood: компилятор создаёт копию
При использовании внешней переменной в try(resource), компилятор создаёт локальную копию ссылки. Это гарантирует потокобезопасность закрытия.
Бросание исключений из close()
Если close() бросает исключение:
- Если в
tryошибок не было — исключение изclose()станет основным - Если в
tryуже было исключение — изclose()добавится вsuppressed
Interrupted close
В сетевых операциях close() может заблокироваться. Устанавливайте тайм-ауты, иначе поток “зависнет” на стадии закрытия ресурсов.
Object Allocation и GC
try-with-resources — создание объектов. При открытии/закрытии тысяч ресурсов в секунду это создаёт давление на GC (Young Generation). Используйте пулы ресурсов (HikariCP для БД), которые реализуют AutoCloseable, но вместо закрытия возвращают объект в пул.
Edge Cases
- Ресурс не открылся — если конструктор бросил исключение, TWR не будет закрывать (нечего закрывать)
- Частичная инициализация — если конструктор открыл нативный ресурс, но упал до возврата — утечка. Делайте конструкторы лёгкими.
Диагностика
- Resource Leak Detection — IDE подсвечивает
AutoCloseableклассы, не использованные в TWR. Не игнорируйте. jstackanalysis — много потоков вBLOCKED/WAITINGвclose()— проверьте дедлоки между ресурсами.
🎯 Шпаргалка для интервью
Обязательно знать:
- Ресурс обязан реализовывать
java.lang.AutoCloseable AutoCloseable(Java 7,java.lang) —close() throws Exception;Closeable(Java 5,java.io) —close() throws IOException- Метод
close()должен быть идемпотентным — повторный вызов не бросает исключение - Ресурсы объявляйте отдельно:
try (A a = new A(); B b = new B(a))— иначе утечка - Java 9+: внешние переменные должны быть
effectively final - Компилятор создаёт локальную копию ссылки — гарантирует потокобезопасность закрытия
- Если
close()бросает при наличии ошибки вtry— добавляется вsuppressed - Пулы ресурсов (HikariCP) реализуют AutoCloseable, но возвращают объект в пул, а не закрывают
Частые уточняющие вопросы:
- В чём разница AutoCloseable и Closeable? — AutoCloseable универсальнее (throws Exception), Closeable только для I/O (throws IOException)
- Что такое идемпотентность close()? — Первый вызов закрывает ресурс, второй — ничего не делает
- Почему вложенное создание ресурсов опасно? —
new B(new A())— если B упадёт, у TWR нет ссылки на A, он утечёт - Что происходит при массовом открытии/закрытии ресурсов? — Давление на GC; используйте пулы ресурсов
Красные флаги (НЕ говорить):
- “Close() может бросать исключение каждый раз” — должен быть идемпотентным
- “Я не объявляю ресурсы отдельно, чтобы короче написать” — это вызывает утечки
- “TWR закрывает соединение из пула” — пул управляет lifecycle сам, возврат != закрытие
Связанные темы:
- [[Что такое try-with-resources]]
- [[Что такое AutoCloseable интерфейс]]
- [[В чём разница между AutoCloseable и Closeable]]
- [[Что такое suppressed exceptions]]
- [[Какие исключения нужно обрабатывать обязательно]]