AutoCloseable: Resource Ownership and Close-Exception Semantics
ENGINEERING NOTES
Published / DavidHLPL
AutoCloseable is a resource-release protocol: code that owns a resource gives its lifetime to try-with-resources, which calls close() when the scope ends. This page focuses on ownership, ordering, and exception propagation rather than a catalog of stream, connection, or JDBC tutorials.
Core mechanism
1. Ownership comes before syntax
Implementing AutoCloseable means an object supports closing; it does not decide who owns it. The code that creates and is responsible for releasing a resource should put it in TWR. A borrower must not close a shared resource while another owner still uses it.
try (Connection c = dataSource.getConnection();
PreparedStatement p = c.prepareStatement(sql)) {
// c and p are used only inside this scope
}
Once initialization succeeds, the compiler-generated logic closes resources on both normal and exceptional exit. Multiple resources close in reverse declaration order: p before c, because a wrapper or dependent resource should be released before its underlying resource.
2. Primary and suppressed exceptions
- An exception from the
trybody is the primary exception. - A
close()failure is attached throughprimary.getSuppressed()instead of replacing the primary exception. - If the body succeeds but
close()fails, the close failure is thrown. - If initialization fails, later resources are not treated as acquired; already-acquired resources are still cleaned up according to the protocol.
This is safer than handwritten try-finally: a rethrown exception in finally can hide the business exception, while TWR preserves both pieces of evidence.
3. The smallest close protocol
final class Handle implements AutoCloseable {
private boolean closed;
@Override public void close() throws Exception {
if (!closed) {
closed = true;
release();
}
}
}
close() should be as idempotent as practical and should release resources rather than start new business work. If closing flushes data or determines whether output is complete, make failure observable. If it is compensating cleanup such as deleting a temporary file, logging and suppressing an accepted failure can be valid—but the policy must be explicit.
Applicability
- The object holds a file descriptor, connection, cursor, lock, temporary directory, or another external resource that needs explicit release.
- Ownership can be expressed in one scope, or transferred through an explicit wrapper.
- Several resources have dependencies and need deterministic reverse-order cleanup.
- Close failures must remain visible to callers through
Exception,IOException, or a domain-specific contract.
Closeable is the I/O-specialized child of AutoCloseable; its close() commonly declares IOException. A general business resource can implement AutoCloseable directly.
Not applicable and risks
- Do not put a borrowed shared connection, container-managed executor, or global singleton in TWR that does not own it; closing it breaks another caller’s lifetime.
- The exception type from
close()still follows the implementation and JDK API contract; not every resource closes silently. - Idempotence is a recommended production property, not a guarantee that every library implementation makes repeated close calls harmless; check the specific API.
- Reverse ordering solves resource dependency, not transaction commit, message acknowledgement, or business compensation. Those semantics need explicit design.
- Versions, connection pools, and serialization clients may change implementation details; compiler expansion is not a public API contract.
Minimum verification
- Use an observable resource that records
open,use, andclose; test both normal return and a body exception, asserting one close. - Declare two dependent resources and record events; assert that the later declaration closes first.
- Make both
use()andclose()throw; inspect the primary exception andgetSuppressed()contents. - Test a borrowed/shared resource separately: the owner should still be able to use it after the borrower scope ends. If not, remove TWR or define an explicit lifetime wrapper.
Evidence and uncertainty
- Source facts:
legacy-java-auto-closeabledescribes Java 7AutoCloseable, TWR reverse order, primary/suppressed exceptions, theCloseablerelationship, custom resources, and idempotentclose(). - Synthesis in this page: Ownership is made a precondition for the syntax, and close failures are divided into failures that must propagate and cleanup failures that may be logged by policy.
- Unconfirmed: Exact idempotence, exception types, and transaction behavior for JDBC, filesystems, and third-party clients require the relevant versioned API and a runtime test; examples are not universal guarantees.
Related pages
- AtomicBoolean: Atomic Boolean State and CAS Boundaries
- NullValue: The Cache-Null Placeholder and Serialization Boundary
- Java Backend Interview Retrospective: Project Truth, Engineering Mechanisms, and Production Evidence
Sources and related documents
Research notes
- Original document: src/content/note/en/java-auto-closeable.md
- Source revision: f49953fb0d4bd52a39bf81167b0c4b7dd7d78a64
- Evidence status: active