Back to Blog
Java

Using try-with-resources in Java for Reliable Resource Management

java try with resources: Learn how the try-with-resources statement in Java automatically closes resources, handles exceptions cleanly, and prevents resource leaks in...

try-with-resourcesAutoCloseableresource managementexception handlingJava 7
A Java try-with-resources statement automatically closing a resource, represented by a shield and a closed lock.

In Java, managing resources like file streams, database connections, and network sockets has always required careful attention to closing them properly. The java try with resources statement, introduced in Java 7, provides a concise and reliable way to ensure that each resource is closed automatically at the end of the statement. This eliminates the classic pattern of manually closing resources in a finally block and reduces the risk of resource leaks.

The Problem with Manual Resource Management

Before try-with-resources, developers typically wrote code like this:

BufferedReader reader = null; try { reader = new BufferedReader(new FileReader("data.txt")); String line = reader.readLine(); // process line } catch (IOException e) { // handle exception } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { // handle close failure } } }

The finally block ensures the resource is closed even when an exception occurs, but it adds boilerplate and makes the code harder to read. If multiple resources are involved, the nesting grows and the chance of forgetting to close one increases. The java try with resources statement addresses this by handling closure automatically and consistently.

How try-with-resources Works

The syntax is straightforward:

try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) { String line = reader.readLine(); // process line } catch (IOException e) { // handle exception }

Any object that implements java.lang.AutoCloseable (which includes all java.io.Closeable resources) can be declared in the parentheses after try. The resource is closed automatically when the try block exits, whether normally or due to an exception. The compiler generates the necessary close() calls, and you no longer need an explicit finally block for resource cleanup.

This behavior is not limited to file I/O. It applies to any class implementing AutoCloseable, including JDBC connections, sockets, and custom classes you define.

Declaring Multiple Resources

You can declare multiple resources in the same try statement, separated by semicolons. They are closed in the reverse order of their declaration, which is important when resources depend on each other.

try (BufferedReader reader = new BufferedReader(new FileReader("data.txt")); BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))) { String line; while ((line = reader.readLine()) != null) { writer.write(line); writer.newLine(); } } catch (IOException e) { // handle exception }

Here, reader is closed before writer. This ordering is intentional: if writer depends on reader, closing the reader first could break the writer's ability to flush or close properly. The reverse-order closure minimizes such issues.

Exception Handling and Suppressed Exceptions

One subtle but critical behavior of try-with-resources is how exceptions from the close() method interact with exceptions thrown in the try block. If both the try block and the close() method throw exceptions, the exception from the try block is propagated, and the exception from close() is added to it as a suppressed exception. You can retrieve these suppressed exceptions using Throwable.getSuppressed().

Consider this example:

class Resource implements AutoCloseable { public void use() { throw new RuntimeException("Error during use"); } @Override public void close() { throw new RuntimeException("Error during close"); } } public class Main { public static void main(String[] args) { try (Resource r = new Resource()) { r.use(); } catch (RuntimeException e) { System.out.println("Caught: " + e.getMessage()); for (Throwable t : e.getSuppressed()) { System.out.println("Suppressed: " + t.getMessage()); } } } }

Output:

Caught: Error during use
Suppressed: Error during close

This ensures that the primary failure is not masked by a secondary failure during cleanup. In the old manual pattern, an exception in close() would override the original exception, making debugging harder.

Writing Custom AutoCloseable Classes

You can create your own resources that integrate with try-with-resources by implementing AutoCloseable. The interface has a single method, close(), which you must implement. It should release any underlying resources and can throw an exception if cleanup fails.

public class DatabaseConnection implements AutoCloseable { private boolean open; public void connect() { open = true; System.out.println("Connected"); } public void query(String sql) { if (!open) throw new IllegalStateException("Connection is closed"); System.out.println("Executing: " + sql); } @Override public void close() { if (open) { open = false; System.out.println("Closed"); } } }

Usage:

try (DatabaseConnection conn = new DatabaseConnection()) { conn.connect(); conn.query("SELECT * FROM users"); }

When the try block exits, close() is invoked automatically. If the try block throws an exception, close() is still called, and any exception from close() is suppressed as described earlier.

Performance and Resource Leak Considerations

The primary benefit of try-with-resources is not speed but correctness. It prevents resource leaks, which can lead to file descriptor exhaustion, database connection pool starvation, or network socket exhaustion in long-running applications. By ensuring close() is always called, try-with-resources reduces the risk of these production issues.

There is a negligible performance overhead from the generated bytecode, but it is far outweighed by the reliability gain. In most applications, the cost of the automatic close mechanism is insignificant compared to the actual I/O operations. The real performance win comes from avoiding leaks that degrade the application over time.

When Not to Use try-with-resources

Try-with-resources is not a universal replacement for all resource management patterns. If you need to close a resource conditionally—for example, only when a certain state is reached—or if the resource's lifetime extends beyond a single method call, you may need manual control. In such cases, you can still use a finally block or a dedicated manager class. However, for the common pattern of acquiring a resource, using it within a scoped block, and then releasing it, try-with-resources is the clearest and safest choice.

Another limitation is that resources must be declared in the try statement itself. If you need to pass a resource to another method that closes it later, try-with-resources is not appropriate. But for local, short-lived resources, it is the idiomatic solution.

java try with resources: Practical Usage and Code Examples | RYUSLOG DEV