Try-with-Resources

Automatically and reliably close resources like files and connections by declaring them in the try statement itself, instead of hand-writing a finally block.

Manually closing resources in a finally block is easy to get subtly wrong — forgetting a null check, closing in the wrong order, or losing an exception from close(). Try-with-resources (Java 7+) lets you declare resources directly in the try statement so the JVM closes them automatically, in the correct order, even when an exception occurs.

AutoCloseable and Closeable

Try-with-resources works with any object whose class implements java.lang.AutoCloseable, which declares one method: close() throws Exception. java.io.Closeable is a more specific, older interface (used by I/O classes) that narrows close()'s throws clause to IOException; Closeable extends AutoCloseable, so both families work identically in try-with-resources.

public interface AutoCloseable {
    void close() throws Exception;
}
// Closeable (java.io) extends AutoCloseable and narrows close() to IOException

Basic Syntax

The resource is declared inside parentheses right after try. It's in scope only within the try block, and its close() method is guaranteed to run when control leaves that block — normally, via a break/return, or because of an exception.

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class ReadFile {
    public static void main(String[] args) throws IOException {
        try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        } // reader.close() is called automatically here, no matter how the block exits
    }
}

Why This Matters: Resource Management

Unclosed resources leak operating-system handles (open file descriptors, sockets, database connections) that garbage collection does not reliably reclaim in a timely way — GC only manages memory, and an unreachable object becomes eligible for collection at an unspecified time, which is far too late for a resource limit like 'max open file handles'. Explicit, prompt closing is required, and try-with-resources makes that closing automatic and exception-safe.

Multiple Resources

Multiple resources can be declared in one try statement, separated by semicolons. They are closed automatically in the reverse of their declaration order — the last-opened resource is closed first.

import java.io.*;

try (FileInputStream in = new FileInputStream("in.txt");
     FileOutputStream out = new FileOutputStream("out.txt")) {
    in.transferTo(out);
} // out.close() runs first, then in.close() — reverse declaration order

Resource Closing Order (LIFO)

Closing in reverse declaration order mirrors normal stack-based scoping and matters when resources depend on each other — for example, a buffered wrapper around a raw stream should be closed before (i.e., listed after) the stream it wraps, so its own close() call (which typically flushes and then delegates to the wrapped resource's close()) has a valid underlying resource to work with.

Suppressed Exceptions

If the try block throws an exception and then a resource's close() also throws while cleaning up, Java doesn't discard either one. The exception from the try block is the one that propagates; the exception from close() is attached to it as a 'suppressed exception,' retrievable via getSuppressed(). This is a deliberate improvement over manual finally-based closing, where the close() exception would normally overwrite (and hide) the original.

class NoisyResource implements AutoCloseable {
    public void use() { throw new RuntimeException("failure during use"); }
    public void close() { throw new IllegalStateException("failure during close"); }
}

public class SuppressedDemo {
    public static void main(String[] args) {
        try {
            try (NoisyResource r = new NoisyResource()) {
                r.use();
            }
        } catch (RuntimeException e) {
            System.out.println("Primary: " + e.getMessage());
            for (Throwable suppressed : e.getSuppressed()) {
                System.out.println("Suppressed: " + suppressed.getMessage());
            }
        }
    }
}
// Output:
// Primary: failure during use
// Suppressed: failure during close

Comparison with finally

The manual version needs a null check (the resource might fail to initialize), a nested try/catch around close(), and still risks silently losing the original exception if close() throws. Try-with-resources handles all of this correctly by construction, and is shorter besides.

// Manual approach — verbose and easy to get wrong
BufferedReader reader = null;
try {
    reader = new BufferedReader(new FileReader("data.txt"));
    System.out.println(reader.readLine());
} finally {
    if (reader != null) {
        try {
            reader.close();
        } catch (IOException e) {
            // easy to forget, or to let this silently mask the original exception
        }
    }
}

// try-with-resources — concise, correct by construction
try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
    System.out.println(reader.readLine());
}

Effectively Final Resources (Java 9+)

Since Java 9, you can use an existing effectively-final variable directly in the try-with-resources parentheses, instead of declaring a brand-new variable there — useful when the resource is created earlier.

BufferedReader reader = new BufferedReader(new FileReader("data.txt"));
try (reader) { // Java 9+: reuse an effectively-final existing variable
    System.out.println(reader.readLine());
}

Practical Usage

Try-with-resources is the standard way to work with files (Reader/Writer/InputStream/OutputStream families), JDBC Connection/Statement/ResultSet objects, sockets, and any custom class that manages an external resource (implement AutoCloseable on it to get the same guarantees).

import java.sql.*;

void printUsers(Connection conn) throws SQLException {
    String sql = "SELECT name FROM users";
    try (Statement stmt = conn.createStatement();
         ResultSet rs = stmt.executeQuery(sql)) {
        while (rs.next()) {
            System.out.println(rs.getString("name"));
        }
    } // rs, then stmt, closed automatically in that order
}

Edge Cases

A try-with-resources statement can still have catch and finally clauses after the resource-closing part; ordering is: try block body runs, then resources close (in reverse order), then any catch blocks handle exceptions from either the body or the closing, then finally runs last.

If a resource expression itself throws while being constructed (e.g. new FileReader on a missing file), any resources already successfully opened earlier in the same try statement are still closed correctly before the exception propagates.

A resource variable declared in the try-with-resources parentheses is implicitly final; it cannot be reassigned inside the try block.

Complete Example

import java.io.*;
import java.nio.file.*;

public class CopyFile {
    static void copy(String source, String destination) throws IOException {
        try (InputStream in = Files.newInputStream(Path.of(source));
             OutputStream out = Files.newOutputStream(Path.of(destination))) {
            in.transferTo(out);
        } catch (IOException e) {
            System.err.println("Copy failed: " + e.getMessage());
            throw e;
        }
    }

    public static void main(String[] args) throws IOException {
        copy("source.txt", "backup.txt");
        System.out.println("Copy complete.");
    }
}

Mental Model

Think of try-with-resources as automatically inserting a correct finally block for you, one that checks each resource for null, closes it in a nested try/catch, closes resources in reverse order, and attaches any close-time exception as suppressed rather than letting it overwrite your real error. You write the intent (which resources, what to do with them); the compiler writes the bookkeeping.

Quick Reference

try (Resource1 r1 = ...; Resource2 r2 = ...) {
    // use r1, r2
} catch (SomeException e) {
    // handle
} finally {
    // always runs, after resources are closed
}

Close order: reverse of declaration (r2 then r1)
Requires: AutoCloseable (close() throws Exception) or
          Closeable (close() throws IOException)
Java 9+: try (existingEffectivelyFinalVar) { ... }
Exceptions from close() during an active exception -> suppressed,
  retrieved via getSuppressed()

Key Takeaways

Try-with-resources automatically closes any AutoCloseable (or Closeable) resource declared in its parentheses, even when an exception is thrown.

Multiple resources close in the reverse of their declaration order.

An exception thrown while closing a resource, during an already-active exception from the try body, is attached as a suppressed exception rather than replacing the original.

It is strictly safer and shorter than a hand-written finally block, which needs a null check and nested try/catch to be equally correct.

Resource variables in the try-with-resources header are implicitly final; Java 9+ also allows reusing an existing effectively-final variable there.

BufferedReader r = new BufferedReader(new FileReader("f.txt")); try { process(r); } finally { r.close(); // NPE risk if construction had failed; no exception-safety for close() itself } try (BufferedReader r = new BufferedReader(new FileReader("f.txt"))) { process(r); }

Try-with-resources handles null-safety, correct ordering, and suppressed-exception bookkeeping automatically; a manual finally block has to replicate all of that correctly by hand.

try (InputStream in = open("a"); OutputStream out = open("b")) { // assumes in.close() runs first }

// Remember: resources close in REVERSE declaration order // out.close() runs before in.close() here — design dependent resources accordingly

Resources close in reverse of declaration order, not declaration order — a resource that depends on another (like a wrapper needing its underlying stream open) must be declared after the resource it depends on.

try (var r = new NoisyResource()) { r.use(); } catch (Exception e) { // ignores e.getSuppressed(), losing the close()-time failure entirely log(e.getMessage()); } try (var r = new NoisyResource()) { r.use(); } catch (Exception e) { log(e.getMessage()); for (Throwable s: e.getSuppressed()) { log("also: " + s.getMessage()); } }

When diagnosing a failure from a try-with-resources block, check getSuppressed() too — a close()-time exception is preserved there, not thrown separately.

What interface must a class implement to be used as a resource in try-with-resources?

AutoCloseable (whose close() method throws Exception), or its more specific subtype Closeable (whose close() throws only IOException).

In what order are multiple resources declared in one try statement closed?

In reverse of their declaration order — the last resource declared is closed first.

If the try block throws an exception and a resource's close() also throws, what happens to the close()-time exception?

It is attached to the primary exception as a suppressed exception (retrievable via getSuppressed()) rather than replacing or hiding the primary one.

What two problems with manual finally-based resource closing does try-with-resources solve?

It avoids needing a null check before calling close() (the resource variable is guaranteed non-null if construction succeeded) and it avoids a close()-time exception silently overwriting the exception from the try body.

Can you reassign a resource variable declared in a try-with-resources header inside the try block?

No — it is implicitly final.

Interview: Why doesn't relying on garbage collection to eventually call close() (e.g. via finalizers) work as a substitute for try-with-resources?

GC only reclaims memory and runs at an unspecified time once an object becomes unreachable; it does not manage other resources like file handles or sockets, and the delay before an unreachable object is actually collected can far exceed how long a limited resource (like a file descriptor) can safely stay open.

Write a method that copies all lines from one text file to another using try-with-resources with two resources (a BufferedReader and a BufferedWriter).

import java.io.*; static void copyLines(String src, String dst) throws IOException { try (BufferedReader reader = new BufferedReader(new FileReader(src)); BufferedWriter writer = new BufferedWriter(new FileWriter(dst))) { String line; while ((line = reader.readLine()) != null) { writer.write(line); writer.newLine(); } } } writer is declared after reader, so it is closed first (flushing pending output), then reader is closed — correct order for this use case.

What is the exact output order?

open a, open b, using, close b, close a — construction happens in declared order, and closing happens in reverse declared order.

Write code demonstrating a suppressed exception: a resource whose use() throws RuntimeException('use failed') and whose close() throws IllegalStateException('close failed'), used in try-with-resources, then print the primary message and all suppressed messages.

class Flaky implements AutoCloseable { void use() { throw new RuntimeException("use failed"); } public void close() { throw new IllegalStateException("close failed"); } } public static void main(String[] args) { try { try (Flaky f = new Flaky()) { f.use(); } } catch (RuntimeException e) { System.out.println("Primary: " + e.getMessage()); for (Throwable s : e.getSuppressed()) { System.out.println("Suppressed: " + s.getMessage()); } } }

Prints 'Primary: use failed' followed by 'Suppressed: close failed' — the exception from use() is the one that propagates, and the exception from close() is attached to it as suppressed rather than replacing it.

class R implements AutoCloseable {
    String name;
    R(String name) { this.name = name; System.out.println("open " + name); }
    public void close() { System.out.println("close " + name); }
}

public static void main(String[] args) {
    try (R a = new R("a"); R b = new R("b")) {
        System.out.println("using");
    }
}