⚡IntermediateAdvanced Exception Handling

Try-With-Resources

Automatically close resources like streams and connections using the try-with-resources statement.

Overview

Resources such as file streams, database connections, and network sockets must be closed after use. Forgetting to close them causes resource leaks. Java 7 introduced try-with-resources to solve this. Any object implementing AutoCloseable can be declared in the try header. Java automatically calls close() when the block exits, even if an exception occurs. This replaces verbose try-finally patterns and is the standard way to work with I/O and other closeable resources.

Key Points

  • •Syntax: try (ResourceType r = new ResourceType()) { ... }
  • •The resource must implement AutoCloseable (or its subinterface Closeable)
  • •close() is called automatically when the try block exits, whether normally or via exception
  • •Multiple resources can be declared, separated by semicolons. They are closed in reverse order
  • •Exceptions from close() are suppressed if an exception was already thrown in the try body; access them with getSuppressed()
  • •You can create your own AutoCloseable classes

Examples

Basic try-with-resources with a custom resource

public class Main {
    static class DatabaseConnection implements AutoCloseable {
        private final String url;

        DatabaseConnection(String url) {
            this.url = url;
            System.out.println("Opened connection to " + url);
        }

        String query(String sql) {
            System.out.println("Running query: " + sql);
            return "result";
        }

        @Override
        public void close() {
            System.out.println("Closed connection to " + url);
        }
    }

    public static void main(String[] args) {
        // Connection is closed automatically after the block
        try (DatabaseConnection conn = new DatabaseConnection("localhost/mydb")) {
            String result = conn.query("SELECT * FROM users");
            System.out.println("Got: " + result);
        }
        // close() was called here
        System.out.println("After try block");
    }
}

close() is called after the try block regardless of whether an exception occurred. The output will always show "Closed connection" before "After try block".

Multiple resources and exception handling

public class Main {
    static class Resource implements AutoCloseable {
        final String name;
        Resource(String name) {
            System.out.println("Opening " + name);
            this.name = name;
        }

        void use() {
            System.out.println("Using " + name);
            if (name.equals("Unstable")) {
                throw new RuntimeException(name + " failed!");
            }
        }

        @Override
        public void close() {
            System.out.println("Closing " + name);
        }
    }

    public static void main(String[] args) {
        // Multiple resources: opened in order, closed in REVERSE order
        try (Resource r1 = new Resource("Database");
             Resource r2 = new Resource("Unstable")) {
            r1.use();
            r2.use(); // throws
        } catch (RuntimeException e) {
            System.out.println("Caught: " + e.getMessage());
        }
    }
}

Resources are closed in reverse order of declaration. Even when r2.use() throws, both r2 and r1 are closed before the catch block runs.

Suppressed exceptions

public class Main {
    static class Leaky implements AutoCloseable {
        Leaky() { System.out.println("Opened"); }

        void doWork() { throw new RuntimeException("Work failed"); }

        @Override
        public void close() { throw new RuntimeException("Close failed too"); }
    }

    public static void main(String[] args) {
        try (Leaky r = new Leaky()) {
            r.doWork();
        } catch (RuntimeException e) {
            System.out.println("Primary: " + e.getMessage());
            for (Throwable suppressed : e.getSuppressed()) {
                System.out.println("Suppressed: " + suppressed.getMessage());
            }
        }
    }
}

When both the body and close() throw, the body exception is primary and the close() exception is attached as a suppressed exception. getSuppressed() returns them.

Common Mistakes

  • Using a manual try-finally to close resources instead of try-with-resources. The finally approach is error-prone: if both the body and finally throw, the body exception is lost
  • Storing the resource in a variable before the try: InputStream in = new FileInputStream(f); try (InputStream x = in) { ... } is fine, but try { ... } finally { in.close(); } is the old pattern to replace
  • Forgetting that the resource variable is effectively final inside the try block. You cannot reassign it

Note

Use try-with-resources for everything that is closeable: file streams, HTTP connections, database connections, ZIP files, and any class you write that holds an external resource. It is always safer than manual close().

Exercises

Write Java code and click Run to execute it instantly. When your output looks correct, mark as done.

Loading editor…

Completed: 0 / 4 exercises

Knowledge Check

Question 1 of 4

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