Try-with-resources (Java 7+) is a try statement that declares one or more
resources in parentheses and closes them automatically when the block
finishes — normally or via an exception. It replaces the error-prone
manual-finally cleanup pattern, eliminating resource leaks.
// Automatic: br is closed no matter how the block exits
try (BufferedReader br = new BufferedReader(new FileReader("a.txt"))) {
return br.readLine();
}
A resource is anything that must be released — files, sockets, DB connections,
streams. Any object that implements AutoCloseable can be a resource. The
problem it solves: developers routinely forgot the finally close, nested it
wrong, or let a close() exception swallow the real one.
You had to declare the resource outside the try, close it in a finally,
and null-check because the constructor might have failed. It was verbose and
easy to get wrong.
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader("a.txt"));
return br.readLine();
} finally {
if (br != null) br.close(); // close itself can throw IOException
}
With two resources you'd nest two try/finally blocks (closing the outer one
even if the inner constructor threw). Try-with-resources collapses all of that
into one parenthesized declaration — less code, no leaks, and correct
exception handling.
AutoCloseable (Java 7, in java.lang) is the single-method interface that
makes a class usable as a try-with-resources resource. Its only method is
close(), declared to throw the broadest checked exception, Exception.
public interface AutoCloseable {
void close() throws Exception;
}
Only types implementing AutoCloseable (or its subtype Closeable) may appear
in the resource list — anything else is a compile error. The compiler guarantees
close() is invoked when the block exits, so implementing this interface is all
it takes to plug a custom type into automatic resource management.
Closeable (Java 5, in java.io) predates try-with-resources and was
retrofitted to extend AutoCloseable in Java 7, so every Closeable works
as a resource. The differences are in the close() contract:
AutoCloseable |
Closeable |
|
|---|---|---|
| Package | java.lang |
java.io |
close() throws |
Exception (broad) |
IOException (narrow) |
| Idempotent? | not required | required — repeat calls have no effect |
| Use for | any resource | I/O streams |
public interface Closeable extends AutoCloseable {
void close() throws IOException;
}
Prefer implementing Closeable when your resource is I/O-related, and
AutoCloseable for everything else (e.g. a lock or a transaction).
AutoCloseable is the general abstraction, so its close() throws
Exception to allow any resource — a DB connection, a JNI handle — to report
whatever failure it has. Closeable is I/O-specific, so it narrows to
IOException, which is more precise for stream code.
// AutoCloseable: may throw any checked exception
void close() throws Exception;
// Closeable: I/O resources, narrower contract
void close() throws IOException;
An implementation can always narrow the declared exception (override
throws Exception with throws IOException, or throws nothing at all).
Throwing nothing is encouraged: a close() that can't fail is far easier to use
safely.
Idempotent means calling close() a second (or third) time has no
additional effect and doesn't throw. Closeable mandates this; AutoCloseable
only recommends it.
Reader r = new FileReader("a.txt");
r.close();
r.close(); // safe — Closeable.close() is idempotent, does nothing
It matters because a resource can be closed both explicitly in your code and
automatically by try-with-resources, and you don't want the second close to
blow up. When you write your own close(), guarding it with a boolean closed
flag makes it idempotent.
The compiler desugars it into an ordinary try/finally with the close call
and suppressed-exception bookkeeping baked in. Roughly:
// try (Resource r = ...) { body }
// becomes:
Resource r = ...;
Throwable primary = null;
try {
body;
} catch (Throwable t) {
primary = t;
throw t;
} finally {
if (r != null) {
if (primary != null) {
try { r.close(); }
catch (Throwable sup) { primary.addSuppressed(sup); }
} else {
r.close(); // body succeeded; let close throw normally
}
}
}
So close() is in a generated finally, but unlike hand-written finally it
records a close() failure as a suppressed exception instead of discarding
the original. That bookkeeping is the whole point.
In the reverse order of declaration — last declared is closed first. This
mirrors how dependencies stack: a later resource often depends on an earlier
one, so it must be torn down first (like nested finally blocks).
try (Resource a = new Resource("A");
Resource b = new Resource("B");
Resource c = new Resource("C")) {
// ... use a, b, c
}
// close order: C, then B, then A
This LIFO order is guaranteed by the spec. For example, a BufferedWriter
wrapping a FileWriter is closed before the FileWriter it depends on.
Declare them in the parentheses separated by semicolons; each is closed
automatically. This avoids the deeply nested try/finally blocks the old style
required.
try (FileInputStream in = new FileInputStream("src.txt");
FileOutputStream out = new FileOutputStream("dst.txt")) {
in.transferTo(out);
} // out closed first, then in — both guaranteed
Each resource is independently closed: even if closing out throws, the
runtime still closes in. A trailing semicolon after the last resource is
allowed but optional.
When the try body throws and a resource's close() also throws,
try-with-resources propagates the body's exception (the more useful one) and
attaches the close() exception to it as a suppressed exception, retrievable
via Throwable.getSuppressed().
try (var r = new FlakyResource()) {
throw new RuntimeException("body failed"); // primary
} // r.close() throws too -> attached as suppressed
// caught primary:
catch (Exception e) {
for (Throwable s : e.getSuppressed())
System.out.println("suppressed: " + s); // close's exception
}
So no exception is lost: the primary surfaces, the secondary rides along. The
stack trace prints suppressed ones under a Suppressed: heading.
In a hand-written try/finally, if both the body and close() throw, the
finally's exception replaces the body's — the original failure is silently
lost, hiding the real bug.
try {
throw new RuntimeException("REAL cause");
} finally {
throw new IOException("close failed"); // this WINS; "REAL cause" gone
}
Try-with-resources reverses the priority: the body's exception wins and the
close() exception is recorded as suppressed rather than thrown over it. You
get the genuine cause as the primary, with the cleanup failure preserved
alongside instead of erased.
They're the two Throwable methods (Java 7) behind the suppressed-exception
mechanism. addSuppressed(Throwable) attaches a secondary exception to a primary
one; getSuppressed() returns the array of attached exceptions.
Throwable[] suppressed = primaryException.getSuppressed();
// what the compiler-generated finally effectively calls:
primaryException.addSuppressed(closeException);
Try-with-resources calls addSuppressed for you automatically, but the methods
are public so you can use them manually. You can't suppress a throwable into
itself, and passing null throws NullPointerException.
Before Java 9 you had to declare the resource inside the try parentheses.
Java 9 lets you reference an already-declared variable there, as long as it's
final or effectively final (never reassigned after init).
// Java 9+: reuse an existing effectively-final variable
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
try (br) { // no re-declaration needed
return br.readLine();
} // br still closed automatically
This avoids awkward redundant declarations like try (Reader r2 = r1). The
variable must not be reassigned, or the compiler rejects it — the resource must
be stable so the runtime closes the right object.
Yes. You can append catch and finally clauses just like a normal try. The
key ordering rule: resources are closed first, then the catch/finally
run. So a catch sees an already-closed resource.
try (var conn = open()) {
conn.run();
} catch (SQLException e) { // runs AFTER conn is closed
log.error("db error", e);
} finally {
System.out.println("done"); // runs LAST
}
This matters because the catch can handle exceptions thrown by close() too —
by the time it executes, cleanup is complete and you only deal with what
propagated out.
Implement AutoCloseable (or Closeable) and put release logic in close().
Make it idempotent and avoid throwing from close() where you can.
class Timer implements AutoCloseable {
private final long start = System.nanoTime();
private boolean closed = false;
@Override public void close() { // narrowed: throws nothing
if (closed) return; // idempotent guard
closed = true;
System.out.println("took " + (System.nanoTime() - start) + "ns");
}
}
try (var t = new Timer()) { doWork(); } // prints elapsed time on exit
This pattern is great for scoped concerns — timing, locks, transaction commit, MDC context — anything with a clear "enter then guaranteed exit."
If the body finishes without an exception, there's no primary exception to
suppress against, so the close() exception is thrown normally out of the
try statement. It becomes the exception the caller sees.
try (var r = new FlakyResource()) {
// body succeeds, no exception here
} // but close() throws IOException -> THIS propagates to the caller
So a successful body can still produce a failure from cleanup — which is why
close() on something like a BufferedWriter (which flushes) genuinely matters:
a failed final flush surfaces as a real, non-suppressed exception you must handle.
Yes — every exit path closes the resources: a normal fall-through, an early
return, a break/continue out of the block, or a thrown exception. That
guarantee is the entire value proposition; closing happens in the
compiler-generated finally.
String first(BufferedReader br) throws IOException {
try (br) {
return br.readLine(); // br is STILL closed before returning
}
}
Just like a finally, the close runs before the method actually returns or the
exception escapes. There's no code path that can leak the resource.
Resources are initialized left to right. If a later resource's constructor throws, the resources already opened are closed (in reverse order) before the exception propagates — no leak.
try (Resource a = new Resource("A"); // opens OK
Resource b = openThatThrows(); // throws here
Resource c = new Resource("C")) { // never created
...
}
// a IS closed; c was never opened; the constructor's exception propagates
This is exactly what the nested-try/finally style struggled to get right by
hand. The runtime closes only what was successfully created, in LIFO order.
Try-with-resources is the modern replacement; reach for manual try/finally
only for cleanup that isn't a closeable resource.
| Aspect | try-with-resources | try/finally |
|---|---|---|
| Closes resource | automatic | manual close() |
| Both throw | body wins, close suppressed | finally wins, body lost |
| Null safety | handled by compiler | manual null check |
| Multiple resources | one statement, LIFO | nested blocks |
| Requires | AutoCloseable type |
any cleanup code |
try (var r = open()) { use(r); } // preferred for resources
Use try/finally for non-resource teardown (resetting a flag, restoring thread
state); use try-with-resources for anything implementing AutoCloseable.
Nothing breaks. The generated code null-checks each resource before calling
close(), so a null resource is simply skipped — no NullPointerException at
close time.
try (Reader r = mightReturnNull()) { // returns null
if (r != null) r.read(); // your own guard still needed for use
} // close() is NOT called on null — safe
Note the guard only protects the close, not your use of the resource inside
the body — dereferencing a null r there still throws. This null-tolerance is
one of the boilerplate cases the old manual if (x != null) x.close() pattern
had to handle by hand.
A Lock is not AutoCloseable, and more importantly the lock must be
acquired before the try, not in it — otherwise a failure to acquire would
still trigger an unlock. The correct idiom keeps lock() outside and unlock()
in a finally.
lock.lock(); // acquire BEFORE try
try {
criticalSection();
} finally {
lock.unlock(); // try/finally, NOT try-with-resources
}
You can wrap a lock in a small AutoCloseable helper that unlocks in close(),
but you must still acquire before entering the try. This is a classic interview
"when is try-with-resources the wrong tool" question — its answer is manual
acquisition + try/finally.
More Exceptions interview questions
More ways to practice
The self-quiz is live. Get notified when mock interviews and new question packs drop.