Exception Handling

try-with-resources & AutoCloseable

15 min Lesson 8 of 14

try-with-resources & AutoCloseable

Every time your code opens a file, a network connection, a database connection, or any other external resource, that resource must be closed when you are done with it. Forgetting to close resources is one of the most common bugs in Java: it causes memory leaks, file-handle exhaustion, and locked files that nothing else can open.

Java 7 introduced the try-with-resources statement to solve this problem automatically. Instead of writing close logic yourself, you declare the resource inside the try parentheses and Java guarantees it will be closed — even if an exception is thrown.

The Problem: Manual Resource Cleanup Is Error-Prone

Before try-with-resources, you had to close resources in a finally block. Even careful code had subtle bugs:

// Old-style: risky and verbose FileReader reader = null; try { reader = new FileReader("data.txt"); // read data... } catch (IOException e) { e.printStackTrace(); } finally { if (reader != null) { try { reader.close(); // close itself can throw! } catch (IOException e) { e.printStackTrace(); } } }

This is ten lines of boilerplate just to close one resource safely. If close() also throws, the original exception is swallowed — a very hard bug to track down.

The Solution: try-with-resources

Declare the resource in parentheses after try. Java automatically calls close() at the end of the block, whether the block exits normally or via an exception:

// Modern Java: clean and safe try (FileReader reader = new FileReader("data.txt")) { int character; while ((character = reader.read()) != -1) { System.out.print((char) character); } } catch (IOException e) { System.err.println("Could not read file: " + e.getMessage()); } // reader is guaranteed to be closed here

The compiler rewrites this into a version that correctly handles every edge case, including suppressed exceptions.

Key guarantee: close() is always called — even when an exception is thrown inside the try block, and even when there is no catch clause at all.

Multiple Resources in One Statement

You can declare multiple resources separated by semicolons. They are closed in reverse order of declaration — the last one opened is the first one closed:

try ( FileReader reader = new FileReader("input.txt"); FileWriter writer = new FileWriter("output.txt") ) { int ch; while ((ch = reader.read()) != -1) { writer.write(Character.toUpperCase(ch)); } } catch (IOException e) { System.err.println("IO error: " + e.getMessage()); } // writer closed first, then reader

The AutoCloseable Interface

Try-with-resources works with any class that implements AutoCloseable (or its subinterface Closeable). The interface has exactly one method:

public interface AutoCloseable { void close() throws Exception; }

All standard Java I/O classes (InputStream, OutputStream, Reader, Writer, Scanner, Connection, PreparedStatement, etc.) already implement this interface. That is why they all work in try-with-resources out of the box.

Writing Your Own AutoCloseable Class

You can make any class work with try-with-resources by implementing AutoCloseable. A common example is a class that wraps a network connection or a temporary file:

public class DatabaseConnection implements AutoCloseable { private final String url; private boolean open; public DatabaseConnection(String url) { this.url = url; this.open = true; System.out.println("Opened connection to " + url); } public void query(String sql) { if (!open) throw new IllegalStateException("Connection is closed"); System.out.println("Running query: " + sql); } @Override public void close() { if (open) { open = false; System.out.println("Closed connection to " + url); } } }

Now you can use it exactly like any built-in resource:

try (DatabaseConnection conn = new DatabaseConnection("jdbc:mysql://localhost/mydb")) { conn.query("SELECT * FROM users"); conn.query("SELECT * FROM orders"); } // Output: // Opened connection to jdbc:mysql://localhost/mydb // Running query: SELECT * FROM users // Running query: SELECT * FROM orders // Closed connection to jdbc:mysql://localhost/mydb
Make close() idempotent. It is good practice to allow close() to be called more than once without throwing an error. Use a boolean flag (like open above) to guard against double-close.

Suppressed Exceptions

What happens when the try body throws an exception and close() also throws? In old finally-block code, the second exception would hide the first. Try-with-resources handles this correctly: the exception from the try body is the primary exception, and the one from close() is attached to it as a suppressed exception.

public class FaultyResource implements AutoCloseable { @Override public void close() throws Exception { throw new Exception("Error while closing"); } } try (FaultyResource res = new FaultyResource()) { throw new RuntimeException("Error in body"); } catch (RuntimeException e) { System.out.println("Primary: " + e.getMessage()); for (Throwable suppressed : e.getSuppressed()) { System.out.println("Suppressed: " + suppressed.getMessage()); } } // Primary: Error in body // Suppressed: Error while closing

You can retrieve suppressed exceptions with getSuppressed(), which returns a Throwable[]. No information is lost.

Reading a File Line by Line — A Realistic Example

In practice, the most common use of try-with-resources is reading text files with BufferedReader:

import java.io.*; import java.nio.file.*; // BufferedReader wraps FileReader for efficient line-by-line reading try (BufferedReader br = new BufferedReader(new FileReader("employees.csv"))) { String line; while ((line = br.readLine()) != null) { String[] parts = line.split(","); System.out.println("Name: " + parts[0] + ", Role: " + parts[1]); } } catch (IOException e) { System.err.println("Failed to read file: " + e.getMessage()); }
Only the outermost resource needs to be declared in try-with-resources. In the example above, closing BufferedReader automatically closes the wrapped FileReader too, because the close call propagates through the chain. Declaring both separately could actually close the inner reader twice.

Summary

Try-with-resources is the modern, correct way to manage any resource that must be closed. Declare resources in the try() parentheses, implement AutoCloseable for your own classes, and let the JVM handle cleanup. You get shorter code, fewer bugs, and correct suppressed-exception handling — all for free.