Що таке інтерфейс AutoCloseable?
AutoCloseable з'явився в Java 7 спеціально для try-with-resources. Closeable (Java 5) був лише для I/O потоків. AutoCloseable -- у java.lang (без імпортів), щоб будь-який ресурс...
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 наполегливо рекомендує, повторний виклик не повинен падати
Пов’язані теми:
- [[В чому різниця між AutoCloseable та Closeable]]
- [[Що таке try-with-resources]]
- [[Які вимоги до ресурсів в try-with-resources]]
- [[Що таке обгортання винятків (wrapping)]]
- [[Що станеться, якщо в блоку finally теж виникне виняток]]