Gymterview
junior

Что такое try-with-resources и как правильно закрывать ресурсы?

try-with-resources — это конструкция языка Java (Java 7+), которая автоматически закрывает ресурсы, реализующие интерфейс AutoCloseable, при выходе из блока try.

Аналогия из жизни: try-with-resources — это как автоматическое выключение света при выходе из комнаты. Вы не забудете выключить свет (закрыть ресурс), даже если уходите в спешке (исключение в try-блоке).

AutoCloseable и Closeable

Пример
public interface AutoCloseable {
    void close() throws Exception;
}

public interface Closeable extends AutoCloseable {
    void close() throws IOException;
}

Все потоки IO (InputStream, OutputStream, Reader, Writer), каналы NIO (Channel), соединения с БД (Connection, Statement, ResultSet) реализуют один из этих интерфейсов.

Базовый синтаксис

Пример
// До Java 7 — громоздко и подвержено ошибкам
InputStream is = null;
try {
    is = new FileInputStream("file.txt");
    // чтение
} finally {
    if (is != null) { is.close(); } // может поглотить исключение из try
}

// Java 7+ — лаконично и безопасно
try (InputStream is = new FileInputStream("file.txt")) {
    // чтение
} // is.close() вызывается автоматически

Несколько ресурсов

Ресурсы закрываются в обратном порядке объявления:

Пример
try (FileInputStream fis = new FileInputStream("input.txt");
     BufferedInputStream bis = new BufferedInputStream(fis);
     FileOutputStream fos = new FileOutputStream("output.txt")) {
    bis.transferTo(fos);
}
// Порядок закрытия: fos -> bis -> fis

Effectively final переменные (Java 9+)

Пример
BufferedReader reader = new BufferedReader(new FileReader("file.txt"));
try (reader) { // reader — effectively final
    String line = reader.readLine();
}

Подавленные исключения (Suppressed Exceptions)

Если исключение возникает и в блоке try, и в методе close(), исключение из close() подавляется и добавляется к основному:

Пример
try (ProblematicResource resource = new ProblematicResource()) {
    resource.doWork(); // бросает RuntimeException
} catch (RuntimeException e) {
    e.getSuppressed(); // содержит исключение из close()
}

Частые ошибки

  • Забывают, что Stream от Files.lines() тоже реализует AutoCloseable и требует try-with-resources.
  • Закрывают обёртку и внутренний поток отдельно — при закрытии обёртки (BufferedInputStream) внутренний поток закрывается автоматически.
  • Используют try-with-resources без catch и считают, что исключения обработаны — конструкция только закрывает ресурсы, но не обрабатывает исключения.
  • В старом коде с finally исключение в close() может поглотить основное исключение из try.

На собеседовании: объясните механизм: ресурсы реализуют AutoCloseable, закрываются автоматически при выходе из try, в обратном порядке объявления. Упомяните suppressed exceptions и effectively final (Java 9+).