Try-With-Resources vs Try-Finally
Fast recall
Quick revision
Core answer
finally cleanup because the compiler closes initialized resources on every exit and preserves a body failure while attaching close failures as suppressed exceptions.Interview-ready guide
Interview answer
Before Java 7, resource cleanup was commonly written in a finally block. The code opened a resource, stored it in a variable, checked for null in finally, and called close. That pattern can work, but it is verbose and easy to get wrong when acquisition, use, and cleanup can all fail.
Try-with-resources expresses the same ownership directly: resources appear in the try (...) header and the compiler supplies the closing logic. Every successfully initialized resource closes on normal completion, exception, or return. With several resources, Java also applies the correct reverse close order automatically.
Exception behavior is an important improvement. In a naive finally block, close() can throw and replace the exception that originally explained why the work failed. Try-with-resources keeps the body exception as the primary one and attaches close failures through Throwable.getSuppressed(). If the body succeeds and close alone fails, that close exception still propagates.
For example, a parser can open a file, throw because the content is invalid, and still close the reader. If closing also fails, logs can show the parsing exception plus the suppressed cleanup exception rather than losing either fact. The feature also handles failure while initializing a second resource by closing the first.
A finally block remains useful for cleanup that is not represented by an owned AutoCloseable, such as restoring a thread-local value, although a small scope object can sometimes improve that design too. For file, network, database, and similar resources, try-with-resources is the default because it is shorter, structurally safer, and has defined exception semantics.
Try-With-Resources vs Try-Finally
Direct Answer
Try-with-resources is safer and clearer than manual finally cleanup because the compiler closes initialized resources on every exit and preserves a body failure while attaching close failures as suppressed exceptions.
Deep Dive & Architecture
A traditional finally block always runs, but that alone does not make its cleanup correct. The variable may be null because opening failed. A second resource may have opened after the first. One close may throw before another is reached. Each case requires deliberate nesting and exception bookkeeping that quickly obscures the actual work.
Try-with-resources behaves like nested resource scopes. After each initializer succeeds, that resource is protected by cleanup. This is why an earlier resource closes when a later initializer throws and why resources close in reverse order: the newest dependent layer leaves first, followed by the layer it wraps.
Suppression preserves causal information. If the body is already failing, its Throwable is the reason the scope began to exit. A close failure is important but secondary, so Java attaches it to the primary Throwable. Diagnostic code should inspect suppressed exceptions when cleanup failures are operationally significant.
The language feature cannot decide business cleanup. A transaction may need explicit rollback, a temporary file may need deletion only on failure, and a borrowed connection may have framework-managed ownership. Use try-with-resources for the resource boundary, then keep domain-specific success and recovery rules visible around it.
Code Example
class TryComparedWithFinally {
static final class Tracked implements AutoCloseable {
boolean closed;
public void close() { closed = true; }
}
public static void main(String[] args) {
Tracked resource = new Tracked();
try {
try (resource) {
throw new IllegalStateException("invalid content");
}
} catch (IllegalStateException expected) {
if (!resource.closed) throw new AssertionError("not closed");
System.out.println(expected.getMessage() + ":closed=" + resource.closed);
}
}
}
Check your understanding
Answer these without looking back at the article.
- How can a naive finally block hide the original exception?
- What happens when the body succeeds but close throws?
- When might a finally block still be appropriate?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.