В чём разница между AutoCloseable и Closeable?
Closeable — это частный случай AutoCloseable только для I/O операций.
Junior Level
Краткий ответ
Closeable — это частный случай AutoCloseable только для I/O операций.
AutoCloseable (java.lang)
└── Closeable (java.io)
└── FileInputStream, BufferedReader, etc.
Основные отличия
| Характеристика | Closeable | AutoCloseable |
|---|---|---|
| Пакет | java.io |
java.lang |
| Исключение | throws IOException |
throws Exception |
| Назначение | Только I/O | Любые ресурсы |
Примеры
// Closeable — только I/O
try (FileInputStream fis = new FileInputStream("file.txt")) {
fis.read();
} // close() бросает IOException
// AutoCloseable — любой ресурс
try (Connection conn = dataSource.getConnection()) {
conn.createStatement().executeQuery("SELECT 1");
} // close() бросает SQLException
Какой использовать
- Для файлов, потоков —
Closeable(уже реализован в JDK) - Для своих ресурсов (БД, транзакции) —
AutoCloseable
Middle Level
Иерархия (с Java 7)
Closeable наследует AutoCloseable. Это сделано для обратной совместимости — все старые I/O классы работают в try-with-resources.
Разница в контрактах
Идемпотентность:
Closeable– Javadoc требует идемпотентности: “если поток уже закрыт, вызов не имеет эффекта”AutoCloseable– Javadoc рекомендует: “настоятельно рекомендуется…”- На практике оба должны быть идемпотентными.
Причина: некоторые ресурсы (транзакции) могут выбрасывать исключение при попытке закрыть уже закрытую транзакцию.
Flushable & Closeable
Многие Closeable классы также реализуют Flushable:
Flushable – интерфейс с методом flush(). Flush = сбросить буферизованные данные. BufferedOutputStream.close() сначала вызывает flush(), чтобы данные из буфера записались на диск.
public class BufferedOutputStream extends FilterOutputStream
implements Flushable, Closeable {
public void close() throws IOException {
flush(); // Сбрасывает буфер перед закрытием
out.close();
}
}
При закрытии close() обычно вызывает flush(). Но это зависит от реализации.
Exception Narrowing
При реализации AutoCloseable для своих сервисов, убирайте throws Exception:
// Плохо
public class MyService implements AutoCloseable {
public void close() throws Exception { }
}
// Хорошо
public class MyService implements AutoCloseable {
public void close() { } // Без throws — чисто и безопасно
}
Что реализовывать в своём классе
- Closeable – если ваш класс – I/O поток (работает с байтами/символами)
- AutoCloseable – если ваш класс – любой другой ресурс (БД-соединение, транзакция, блокировка)
Senior Level
Suppressed Exceptions Priority
При закрытии нескольких ресурсов в TWR, порядок – LIFO (Last In, First Out). Исключение от самого внутреннего ресурса добавляется последним в suppressed список.
// При закрытии A, B, C (в этом порядке), если все три упали: // A – основное исключение // B и C – suppressed (порядок: B затем C, LIFO)
Interrupted Close
Если close() блокирующий (закрытие сетевого пула) и поток прерывается — InterruptedException. TWR не умеет магически обрабатывать прерывания — всё равно нужен catch.
Static Analysis
instanceof Closeable также означает instanceof AutoCloseable. Но не наоборот!
Edge Cases
- Транзакционные ресурсы —
close()может означать “commit”. Повторный вызов должен быть безопасен - Shared ресурсы — если ресурс используется несколькими компонентами,
close()одного не должен ломать других
Диагностика
В Highload-системах утечка дескрипторов файлов из-за неправильного выбора между этими интерфейсами в кастомных обёртках — одна из самых частых причин падения JVM через несколько дней работы.
lsof -p <pid>— покажет открытые файловые дескрипторыjstack— покажет заблокированные потоки вclose()- Метрики — отслеживайте количество открытых соединений через Micrometer
🎯 Шпаргалка для интервью
Обязательно знать:
Closeableнаследуется отAutoCloseable(иерархия:AutoCloseable←Closeable)Closeableвjava.io, бросаетIOException;AutoCloseableвjava.lang, бросаетExceptionCloseable— только для I/O потоков;AutoCloseable— для любых ресурсов (БД, транзакции, блокировки)- Оба должны быть идемпотентными, но для
Closeableэто строгое требование Javadoc - При реализации сужайте тип исключения или убирайте
throwsполностью - Порядок закрытия в TWR — LIFO (последний открытым закрывается первым)
Частые уточняющие вопросы:
- Какой интерфейс реализовывать в своём классе? —
Closeableесли это I/O поток,AutoCloseableдля всего остального - Что такое Exception Narrowing? — При реализации
AutoCloseableзаменяйтеthrows Exceptionна конкретный тип или убирайте - Почему
CloseableбросаетIOException? — Исторически: создан для I/O операций, гдеIOException— единственная ожидаемая ошибка - Что будет если закрыть общий ресурс из двух компонентов? — Второй вызов должен быть безопасным (идемпотентность); иначе — баг архитектуры
Красные флаги (НЕ говорить):
- “Это одно и то же” — У них разные пакеты, контракты и назначения
- “AutoCloseable бросает только RuntimeException” — Бросает
Exception(checked), но вы можете сузить - “Порядок закрытия не важен” — LIFO критичен: самый внешний ресурс закрывается последним
Связанные темы:
- [[11. Что такое AutoCloseable интерфейс]]
- [[9. Что такое try-with-resources]]
- [[10. Какие требования к ресурсам в try-with-resources]]
- [[23. Что такое suppressed exceptions]]
- [[18. Что такое оборачивание (wrapping) исключений]]