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