Вопрос 11 · Раздел 7

Что такое AutoCloseable интерфейс?

AutoCloseable появился в Java 7 специально для try-with-resources. Closeable (Java 5) был только для I/O потоков. AutoCloseable -- в java.lang (без импортов), чтобы любой ресурс...

Версии по языкам: English Russian Ukrainian

Junior Level

Определение

java.lang.AutoCloseable — это интерфейс с одним методом close(), который вызывается автоматически при выходе из блока try-with-resources.

AutoCloseable появился в Java 7 специально для try-with-resources. Closeable (Java 5) был только для I/O потоков. AutoCloseable – в java.lang (без импортов), чтобы любой ресурс мог стать «закрываемым».

public interface AutoCloseable {
    void close() throws Exception;
}

Как использовать

// FileInputStream реализует AutoCloseable
try (FileInputStream fis = new FileInputStream("file.txt")) {
    fis.read();
} // fis.close() вызовется автоматически

Какие классы его реализуют

  • InputStream / OutputStream — файлы, сеть
  • Connection / Statement — база данных
  • Reader / Writer — текст
  • Scanner — утилиты

Зачем нужен

Чтобы не забывать закрывать ресурсы. До Java 7 приходилось писать finally с close() вручную.


Middle Level

Почему в java.lang?

Это фундаментальный интерфейс. Его вынесли в java.lang, чтобы он был доступен везде без импортов — на уровне Runnable или Iterable.

Правильная реализация close()

1. Сужение типа исключения: Интерфейс объявляет throws Exception. Переопределяйте с конкретным типом:

public class MyResource implements AutoCloseable {
    @Override
    public void close() throws IOException { // конкретнее
        // закрытие
    }
}

Или вообще без throws, если метод безопасен.

2. Паттерн “Safe Close”: Если ресурс владеет несколькими под-ресурсами:

public void close() throws Exception {
    try {
        subResource1.close();
    } finally {
        subResource2.close(); // закроется даже если первый упал
    }
}

3. Идемпотентность: Повторный вызов close() не должен выбрасывать исключение.

Garbage Collector и AutoCloseable

AutoCloseable не заменяет управление памятью. Ресурс (файл, сокет) — это нативный дескриптор в ОС. Если не вызвать close(), дескриптор “висит” пока GC не соберёт объект.

Это ведёт к ошибке “Too many open files”.


Три подхода к управлению ресурсами

Механизм Когда срабатывает Гарантия Рекомендация
TWR При выходе из блока ✅ Гарантирует Основной способ
Cleaner Когда GC решит ⚠️ Страховка Фолбэк, если забыли close()
finalize() Когда GC решит ❌ Непредсказуем Устарел (deprecated в Java 9)

Senior Level

Under the Hood: нативные дескрипторы

Ресурс — это не Java-объект, а дескриптор операционной системы. Дескриптор – число (int), которое ОС выдаёт при открытии файла/сокета. Java-объект – лишь обёртка. GC видит обёртку, но не видит нативный ресурс в ОС. Без явного close() дескриптор утечёт, даже если GC собрал Java-объект. GC не видит нативные ресурсы. Без close() дескриптор утекает, даже если Java-объект собран.

Atomic State в многопоточных средах

public class MyResource implements AutoCloseable {
    private final AtomicBoolean closed = new AtomicBoolean(false);
    
    public void close() {
        if (closed.compareAndSet(false, true)) {
            // реальное освобождение ресурсов
        }
    }
}

Два потока не должны закрыть один сокет одновременно — неопределённое поведение на уровне нативного драйвера.

Бросание исключений в конструкторе

Если конструктор бросил исключение после открытия нативного ресурса, но до возврата объекта — утечка:

public class MyResource implements AutoCloseable {
    private final NativeHandle handle;
    
    public MyResource(String path) {
        this.handle = openNative(path); // открыли
        if (!validate(path)) {
            throw new IllegalArgumentException(); // handle утек!
        }
    }
}

Решение: лёгкие конструкторы, тяжёлое открытие — под try-with-resources.

Cleaner вместо Finalizer (Java 9+)

finalize() deprecated. Для “страховки” ресурсов используйте java.lang.ref.Cleaner:

public class MyResource implements AutoCloseable {
    private static final Cleaner cleaner = Cleaner.create();
    private final Cleaner.Cleanable cleanable;

    public MyResource() {
        this.cleanable = cleaner.register(this, () -> {
            // последний рубеж — если не вызвали close()
            System.err.println("Resource not closed properly!");
        });
    }

    public void close() {
        cleanable.clean();
    }
}
// Cleaner запускается GC. Когда на MyResource больше нет сильных ссылок,
// GC отмечает объект для очистки. Cleaner выполнит лямбду в отдельном потоке.
// Это НЕ замена TWR -- GC может сработать через минуты или даже часы.
// Cleaner -- страховка, если забыли close().

Это лишь страховка, не отменяющая важности TWR.

Диагностика

  • SonarLint Rule — “Resources should be closed”. Проверяет иерархию типов.
  • jcmd VM.native_memory — мониторинг нативной памяти
  • Утечка дескрипторов — частая причина падения JVM через дни работы

🎯 Шпаргалка для интервью

Обязательно знать:

  • AutoCloseable появился в Java 7 специально для try-with-resources
  • Единственный метод: void close() throws Exception
  • Находится в java.lang — доступен без импортов, как Runnable
  • close() должен быть идемпотентным (повторный вызов безопасен)
  • GC не видит нативные дескрипторы — без close() утечка даже при собранном объекте
  • Сужайте throws Exception до конкретного типа или убирайте совсем
  • Cleaner (Java 9+) — страховка, но НЕ замена TWR

Частые уточняющие вопросы:

  • Почему в java.lang, а не в java.io? — Чтобы любой класс мог стать закрываемым без импортов, на уровне Object
  • Что будет если не закрыть ресурс? — Утечка нативных дескрипторов → “Too many open files” → падение JVM
  • Может ли close() бросить исключение? — Да, поэтому в multi-resource TWR исключения подавляются (suppressed)
  • Чем Cleaner лучше finalize()? — Cleaner предсказуемее, finalize() deprecated в Java 9

Красные флаги (НЕ говорить):

  • “GC сам закроет ресурсы” — GC собирает Java-объект, но не нативный дескриптор
  • “AutoCloseable только для I/O” — Это Closeable; AutoCloseable для любых ресурсов
  • “Можно не делать close() идемпотентным” — Javadoc настоятельно рекомендует, повторный вызов не должен падать

Связанные темы:

  • [[12. В чём разница между AutoCloseable и Closeable]]
  • [[9. Что такое try-with-resources]]
  • [[10. Какие требования к ресурсам в try-with-resources]]
  • [[18. Что такое оборачивание (wrapping) исключений]]
  • [[22. Что произойдёт, если в блоке finally тоже возникнет исключение]]