skip to content

What is try-with-resources in Java, and why is it preferred over closing resources in a finally block?

level: juniorimportance: must knowfreq 78%

answer

  1. AutoCloseable + try(...) header
  2. auto-close on normal AND exceptional exit
  3. reverse order of declaration
  4. close() exception becomes suppressed, not swallowed
  5. no null checks, less nesting

basics

~20 s

try-with-resources declares a resource in the try header and Java automatically closes it when the block ends, even on an exception. It is preferred because manual finally blocks are easy to get wrong and can leak resources.

solid answer

~40 s

try-with-resources lets you declare one or more resources (anything implementing AutoCloseable) in parentheses after try. Java guarantees each is closed at the end of the block, in reverse order of opening, whether the block completes normally or throws. Compared to a finally block you do not write the close call by hand, so you cannot forget it, you avoid null checks, and you avoid the classic bug where an exception in close() hides the real exception from the try body. With try-with-resources, a close() exception is instead attached as a suppressed exception on the primary one, so nothing is lost. It also reduces nesting when you open several resources. In short, it makes correct resource cleanup the default and removes a whole category of leak and stack-trace bugs.

code

java · 7 lines
java
// Multiple resources: closed in reverse order automatically.
try (var in = new FileInputStream(src);
     var out = new FileOutputStream(dst)) {
    in.transferTo(out);
} // out.close() then in.close() run here, even if transferTo throws.
// If transferTo throws A and out.close() throws B,
// A propagates and B is attached via A.getSuppressed().

go deeper

for a junior

Can state that try-with-resources auto-closes the resource and that you put an AutoCloseable in the try parentheses, and recognizes it avoids forgetting to close.

for a middle

Explains close-on-exception, reverse close order, the null-check elimination, and prefers it over finally by default.

for a senior

Articulates the suppressed-exception mechanism vs the old finally swallow bug, multi-resource ordering, effectively-final resources (Java 9), and when fallback to manual cleanup is unavoidable.

for a principal

Frames it as making correct cleanup the default to eliminate a class of leaks at scale; sets conventions/lint rules requiring it, and reasons about resources whose lifetime spans method boundaries (pools, try-with-resources vs ownership transfer).

## What a "resource" is A **resource** is any object that holds something the operating system or runtime must hand back when you are done: an open file, a network socket, a database `Connection`, an input/output `Stream`, a `Lock`. If you stop using such an object without closing it, the underlying handle stays open. That is a **resource leak**. Leaked file handles or DB connections eventually exhaust a fixed pool or OS limit, and the program starts failing with errors like "too many open files" or "connection pool exhausted" — often long after the buggy code ran, which makes leaks hard to diagnose. ## How cleanup used to be done: finally Before Java 7 you closed resources by hand in a `finally` block. `finally` is a block that always runs after `try`, whether the `try` finished normally or threw an exception. The classic pattern: ```java InputStream in = null; try { in = new FileInputStream(path); // use in } finally { if (in != null) in.close(); } ``` This works but has several traps: 1. **You can forget the finally entirely**, and then the resource leaks on every call. 2. **You need null checks** because the variable might not have been assigned if `new` threw. 3. **The "swallowed exception" bug**: if the `try` body throws exception A, then `in.close()` in `finally` also throws exception B, the `finally`'s exception B *replaces* A as the exception that propagates. The original failure A — usually the one you actually care about — is silently lost. Diagnosing becomes very hard. 4. **Multiple resources** force nested try/finally blocks that are verbose and easy to get wrong. ## try-with-resources (Java 7+) You declare the resource inside parentheses right after `try`: ```java try (InputStream in = new FileInputStream(path)) { // use in } ``` The compiler generates the closing logic for you. The rules: - Any object you declare there must implement **`AutoCloseable`** (or its subinterface `Closeable`), which means it has a `close()` method. Most JDK and library resources already do. - Each declared resource is **automatically closed** at the end of the `try` block, **whether it exits normally or by exception**. - If you declare several, they are **closed in reverse order** of declaration (last opened, first closed), which respects dependencies between them. - **Suppressed exceptions**: if the `try` body throws A and a `close()` throws B, A is the exception that propagates, and B is *attached* to A as a **suppressed exception** (retrievable via `Throwable.getSuppressed()`). Nothing is lost — the reverse of the old finally bug. Since Java 9 you can also list an already-declared effectively-final variable: ```java final var in = openStream(); try (in) { ... } ``` ## Why it is preferred It makes the correct behavior the **default**: you cannot forget the close, you avoid null checks, you avoid the swallowed-exception bug, and you get clear multi-resource handling. It removes a whole class of leaks and lost-stack-trace bugs with less code than the equivalent finally. ## When you still cannot use it If the object you must release does not implement `AutoCloseable` (rare in modern code) or its lifetime does not match a single lexical block (e.g. a connection held across method calls), you fall back to explicit cleanup — but you should still ensure the close runs on every path.

  • What interface must a resource implement to be used in a try-with-resources header?
    AutoCloseable (or its subinterface Closeable, whose close() throws IOException). Anything with a close() method declared via that interface qualifies.
  • If both the try body and close() throw, which exception propagates?
    The one from the try body propagates; the close() exception is attached to it as a suppressed exception, accessible via getSuppressed(). This is the reverse of the old finally behavior.

Like a self-closing door: you do not have to remember to shut it behind you. With a manual finally you are the one who must remember to close the door, and if you trip on the way out you might slam it on the thing you were carrying (the real exception).

saying these in an interview costs you the question

  • Claiming finally is always equivalent and just as safe — it does not give you suppressed exceptions and is easy to forget.
  • Thinking the resource is closed only on normal completion (it is closed on exceptions too).
  • Saying resources are closed in declaration order (they are closed in reverse).
  • Believing any object can go in the try header — it must implement AutoCloseable.

context