All topics
library
beginner

try-with-resources & AutoCloseable

Automatically close resources (streams, connections, locks) and avoid resource leaks using the try-with-resources statement.

Before Java 7, closing a resource correctly took a finally block that most people wrote slightly wrong. try-with-resources exists because the correct version is tedious and the incorrect version loses the original exception.

try-with-resources = a hotel checkout guarantee. You check into rooms (open resources) and the hotel guarantees checkout (close) even if you leave in a hurry (exception). If checkout itself fails, that problem is noted but doesn't prevent you from leaving.

Key Concepts

1
The mechanism. Anything implementing AutoCloseable can be declared in the parentheses of a try, and the compiler generates the finally that closes it. Resources close in reverse order of declaration, which matters when a later resource wraps an earlier one — a BufferedWriter around a FileWriter must close first so its buffer flushes into a stream that is still open.
AutoCloseabletryfinallyBufferedWriterFileWriter
2
Suppressed exceptions are the detail that makes it better than hand-written code. If the body throws and close() also throws, the body's exception propagates and the close exception is attached to it, retrievable through getSuppressed(). The hand-written finally version does the opposite: the exception from close() replaces the real failure, and the cause of the incident disappears. That inversion is why this feature exists, and it is the answer interviewers are listening for.
close()getSuppressed()finally
3
Closeable versus AutoCloseable. Closeable came first, extends AutoCloseable, and declares throws IOException; AutoCloseable declares throws Exception, so it fits non-IO resources such as locks, database sessions and spans. Implement AutoCloseable for new types, and narrow the thrown type in your override where you can.
CloseableAutoCloseablethrows IOExceptionthrows Exception
4
close() should be idempotent. try-with-resources calls it exactly once, but resources get closed by other paths too — a pool returning a connection, a framework tearing down a context — and a second call that throws turns cleanup into a new failure. Closeable explicitly requires that a repeat call has no effect; make your implementations honour it.
close()Closeable
5
Java 9 removed the last piece of ceremony. An effectively final variable declared outside the try can be used directly — try (conn) — instead of the Java 7 requirement to re-declare it as a new local. It is a small change that removes the last reason people gave for not using the construct.
trytry (conn)
6
What the interviewer is probing.1. "What does try-with-resources do?" Probing: beyond "closes things". Stalls: "It's automatic cleanup." Moves up: compiler-generated finally, reverse close order, and suppressed exceptions preserving the original failure.
finally
7
2. "Body throws and close throws. What does the caller see?" Probing: the key behaviour. Stalls: "The close exception." Moves up: the body's exception propagates; close's is attached as suppressed — the opposite of the hand-written finally.
finally
8
3. "In what order do multiple resources close?" Probing: wrapping. Stalls: "In order." Moves up: reverse of declaration, so a wrapper flushes into a stream that is still open.
9
4. "Closeable or AutoCloseable?" Probing: precision. Stalls: "They're the same." Moves up: Closeable is the IO-specific ancestor throwing IOException; AutoCloseable is the general one throwing Exception — use it for locks, sessions, spans.
CloseableIOExceptionAutoCloseableException
10
5. "Does close() run if the body returns early?" Probing: the guarantee. Stalls: "Only on exception." Moves up: it runs on every exit — normal completion, return, break or throw.
11
  Old finally versus try-with-resources
12
    // Java 6 -- close's exception REPLACES the real failure
    InputStream in = null;
    try {
        in = new FileInputStream(f);
        parse(in);                 // throws ParseException
    } finally {
        if (in != null) in.close(); // throws IOException
    }                               // caller sees IOException,
                                    // ParseException is gone
13
    // Java 7+ -- the real failure survives
    try (InputStream in = new FileInputStream(f)) {
        parse(in);                 // throws ParseException
    }                               // caller sees ParseException;
                                    // IOException is suppressed
14
    catch (ParseException e) {
        for (Throwable s : e.getSuppressed()) { log(s); }
    }
15
  Close order with wrapped resources
16
    try (FileWriter fw = new FileWriter(path);
         BufferedWriter bw = new BufferedWriter(fw)) {
        bw.write(data);
    }
    // bw.close() runs FIRST (flushes into an open fw),
    // then fw.close()
    // Declaring them the other way round loses buffered data.
17
  Interface choice
    Closeable      extends AutoCloseable, throws IOException,
                   close() must be idempotent
    AutoCloseable  throws Exception; use for locks, transactions,
                   tracing spans, anything non-IO