What is the difference between AutoCloseable and Closeable
The primary differences between java.lang.AutoCloseable and java.io.Closeable lie in their hierarchy, package placement, exception signatures, and architectural scopes:
🟢 Junior Level
The primary differences between java.lang.AutoCloseable and java.io.Closeable lie in their hierarchy, package placement, exception signatures, and architectural scopes:
- Inheritance Hierarchy:
Closeableis a direct sub-interface ofAutoCloseable(public interface Closeable extends AutoCloseable). - Package Placement:
AutoCloseableresides injava.lang(core language primitive, requires no import), whereasCloseableresides injava.io(I/O streaming subsystem). - Exception Contract:
AutoCloseable.close()declaresthrows Exception, whereasCloseable.close()narrows this tothrows IOException. - Domain Scope:
AutoCloseable(Java 7) is a general-purpose abstraction for any disposable resource (JDBC connections, transactions, thread locks, network sockets), whileCloseable(Java 5) was designed specifically for byte and character I/O streams.
Both interfaces are fully supported by Java 7+ try-with-resources (TWR).
Core Comparison Matrix
| Criterion | java.lang.AutoCloseable |
java.io.Closeable |
|---|---|---|
| Introduced In | Java 7 (Project Coin / JSR 334) | Java 5 (retrofitted in Java 7) |
| Package | java.lang (No import required) |
java.io (Explicit import required) |
close() Signature |
void close() throws Exception; |
void close() throws IOException; |
| Hierarchy | Root lifecycle interface | Sub-interface (extends AutoCloseable) |
| Idempotency | Strongly Recommended (optional) | Strictly Mandatory (contractual invariant) |
| Domain Scope | Universal (Databases, Pools, Locks, JNI) | Dedicated strictly to byte/char I/O streams & channels |
Hierarchy Tree
java.lang.AutoCloseable (Java 7)
│ void close() throws Exception;
│
├── java.sql.Connection / Statement / ResultSet
├── java.util.concurrent.ExecutorService (Java 19+)
│
└── java.io.Closeable (Java 5, extends AutoCloseable since Java 7)
void close() throws IOException;
│
├── java.io.InputStream / OutputStream
├── java.io.Reader / Writer
└── java.nio.channels.Channel
Try-With-Resources Compatibility Example
Both resource types can be seamlessly combined in a single TWR statement:
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class ResourceComparisonExample {
public static void runDemo() {
// FileInputStream implements Closeable (java.io)
// Connection implements AutoCloseable (java.sql)
try (FileInputStream fis = new FileInputStream("app.properties");
Connection conn = DriverManager.getConnection("jdbc:postgresql://localhost:5432/db")) {
int data = fis.read();
System.out.println("First byte: " + data);
System.out.println("Connection valid: " + conn.isValid(2));
} catch (IOException | SQLException e) {
System.err.println("Resource handling error: " + e.getMessage());
}
}
}
🟡 Middle Level
Backward Compatibility Architecture
When the Java architects designed try-with-resources in Java 7, they needed a mechanism to close not only files and sockets (java.io), but also database connections (java.sql), channels (java.nio), graphics contexts, and custom user components.
If they had coupled try-with-resources directly to the existing java.io.Closeable:
- It would have forced non-I/O modules (such as JDBC) to depend on
java.io. - JDBC methods would have been forced to throw
IOExceptionwhen closing connections, breaking the domain model.
To solve this cleanly, the architects introduced java.lang.AutoCloseable as a universal parent interface and updated Closeable:
package java.io;
public interface Closeable extends AutoCloseable {
@Override
void close() throws IOException;
}
Because Java supports covariant exception narrowing (an overriding method in a subtype can declare a narrower subset of checked exceptions), Closeable narrowed throws Exception down to throws IOException. Consequently, millions of lines of pre-existing Java 5 and 6 I/O code instantly became compatible with try-with-resources without recompilation.
The Idempotency Contract Nuance
The Javadoc specifications specify different guarantees regarding repeated calls to close():
Closeable(Strict Invariant):“If the stream is already closed then invoking this method has no effect.”
Callingclose()multiple times on anyCloseablestream MUST be a silent no-op and must never throw an exception.AutoCloseable(Strong Recommendation):“Note that unlike the close method of Closeable, this close method is not required to be idempotent. In other words, calling this close method more than once may have some visible side effect… However, implementers of this interface are strongly recommended to make their close methods idempotent.”
Why Isn’t AutoCloseable 100% Idempotent by Contract?
Certain non-I/O resources represent state machines or transactions where a second close() call indicates a serious application bug:
- A transactional resource where
close()commits or rolls back pending work: callingclose()on an already finalized transaction may violate ACID invariants, justifying anIllegalStateException. - Distributed consensus locks where repeated release indicates state corruption.
In 99% of general application programming, however, developers should make close() idempotent to prevent cascading failures when wrapped by decorators.
Interaction with Flushable
Most output stream implementations of Closeable in java.io also implement java.io.Flushable:
public class BufferedOutputStream extends FilterOutputStream implements Flushable, Closeable {
@Override
public void close() throws IOException {
try {
flush(); // 1. Flush buffered bytes to the underlying sink
} finally {
super.close(); // 2. Guarantee release of native OS handle even if flush() fails
}
}
}
If flush() throws an IOException (e.g., full disk or network disconnect), the finally block ensures that super.close() still executes. TWR’s LIFO closing sequence guarantees that flush() occurs before underlying low-level socket streams are severed.
🔴 Senior Level
Implementation Decision Tree
Do you have a resource that requires deterministic cleanup?
│
├── Is it a byte/char stream, socket stream, or I/O channel?
│ └── YES ──► Implement java.io.Closeable (throws IOException)
│
└── Is it a service, transaction, lock, or thread context?
└── YES ──► Implement java.lang.AutoCloseable
│
├── Can closing fail with a checked exception?
│ ├── YES ──► Narrow to specific CheckedException
│ └── NO ──► Omit throws entirely (void close())
The “No-Throws AutoCloseable” Scope Guard Pattern
For scoped contextual modifications (e.g., SLF4J MDC, SecurityContext, or custom thread-name tags), create an AutoCloseable that throws no checked exceptions. This enables clean Try-With-Resources usage without any catch or throws boilerplate:
import org.slf4j.MDC;
public final class MdcScope implements AutoCloseable {
private final String key;
private final String previousValue;
public MdcScope(String key, String value) {
this.key = key;
this.previousValue = MDC.get(key);
MDC.put(key, value);
}
// Omit throws clause entirely: clean try-with-resources without catch blocks!
@Override
public void close() {
if (previousValue != null) {
MDC.put(key, previousValue);
} else {
MDC.remove(key);
}
}
}
// Production Usage:
public void handleOrder(String orderId) {
try (var ignored = new MdcScope("orderId", orderId)) {
// Every log line within this block automatically includes [orderId=12345]
processOrder();
} // MdcScope.close() executes deterministically; no catch block required!
}
Decorators, Cascading Closes, and the Shared Resource Trap
In java.io, decorators (e.g., BufferedReader(InputStreamReader(stream))) assume ownership of the underlying wrapped stream. Calling close() on the outermost decorator cascades down to close the wrapped inner stream.
The Shared System Stream Trap:
public void readUserData() {
// ❌ DANGEROUS BUG: Scanner.close() closes System.in for the ENTIRE JVM!
try (Scanner scanner = new Scanner(System.in)) {
String line = scanner.nextLine();
}
// Any future attempts to read from System.in will throw IOException: Stream Closed!
}
Senior Solution: Non-Closing Stream Wrapper
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
public class NonClosingInputStream extends FilterInputStream {
public NonClosingInputStream(InputStream in) {
super(in);
}
@Override
public void close() throws IOException {
// No-op: Prevents decorators from closing shared process streams
}
}
Asynchronous Channel Closing (NIO)
In java.nio.channels, the Channel interface extends Closeable.
When a worker thread is blocked on a read or write call (channel.read(buffer)), and an external thread invokes channel.close():
- The blocked thread is immediately unblocked and receives a
java.nio.channels.AsynchronousCloseException. - If the blocked thread was interrupted via
thread.interrupt(), the channel is closed automatically and the thread receives ajava.nio.channels.ClosedByInterruptException.
4 Tricky Questions
1. Why does Closeable inherit from AutoCloseable rather than the reverse?
Answer:
AutoCloseable was introduced later (in Java 7) as a universal language-level abstraction (java.lang) with a wide contract (throws Exception). Making Closeable extends AutoCloseable allowed millions of existing Java 5 I/O classes to instantly become eligible for Try-With-Resources without modifying legacy code or breaking source/binary backward compatibility.
2. Why is idempotency strictly required in Closeable but only recommended in AutoCloseable?
Answer:
For I/O streams, closing an already closed stream has no meaningful state consequence and must safely be a no-op to allow nested decorator chains to close cleanly. For arbitrary AutoCloseable resources (such as database transactions or distributed locks), a duplicate close() call may signify a severe concurrency defect or attempt to double-commit, where throwing an exception (e.g., IllegalStateException) is architecturally necessary.
3. What happens if an AutoCloseable implementation’s close() method throws an unchecked RuntimeException?
Answer:
The exception aborts TWR cleanup. If the try block completed without an error, the RuntimeException from close() propagates directly to the caller. If the try block had already thrown an exception, the RuntimeException from close() is caught by the synthetic TWR bytecode and attached as a suppressed exception via primaryException.addSuppressed(e), preserving the original failure.
4. Which interface should you implement when creating a custom database transaction manager?
Answer:
Implement java.lang.AutoCloseable. You should narrow the method signature by declaring a specific exception (e.g., throws SQLException) or omit throws entirely if the close() implementation executes a safe, silent rollback for uncommitted transactions.
🎯 Interview Cheat Sheet
30-Second Summary
Closeable(Java 5,java.io) is dedicated strictly to byte and character I/O streams and channels. Itsclose()method declaresthrows IOExceptionand must be strictly idempotent.AutoCloseable(Java 7,java.lang) is the general parent interface (Closeable extends AutoCloseable) designed for any disposable resource (JDBC connections, thread pools, locks). Itsclose()method declaresthrows Exception, and idempotency is strongly recommended.- Both interfaces work with
try-with-resources.- Best Practice: When implementing
AutoCloseablefor custom components, always narrow or eliminatethrows Exceptionto avoid forcing callers into genericcatch (Exception e)blocks.
Quick Contrast
AutoCloseable: Java 7 •java.lang•throws Exception• Universal resources • Idempotency recommended.Closeable: Java 5 •java.io•throws IOException• Strictly I/O streams • Idempotency mandatory.
Red Flags (What to Avoid)
- ❌ “Closeable throws RuntimeException while AutoCloseable throws Exception.” (
Closeablethrows checkedIOException). - ❌ “AutoCloseable was designed only for files and streams.” (It is a core language primitive in
java.langfor all resources). - ❌ “Wrapping
System.inin aScannerinside TWR is good practice.” (Closing theScannerclosesSystem.infor the entire JVM process). - ❌ “Leaving
throws Exceptionon customAutoCloseableclasses is recommended.” (Forces callers to catch genericException; narrow or omit it).
Related Topics
- What is the AutoCloseable interface — AutoCloseable contract details
- What is try-with-resources — TWR language construct
- What are the requirements for resources in try-with-resources — Resource requirements
- What are suppressed exceptions — Exception suppression mechanics
- What is exception wrapping — Wrapping and translating exceptions