Try-With-Resources in Java
Introduction
Before Java 7, managing resources such as file streams, database connections, and network sockets was notoriously verbose and error-prone. Developers had to ensure that every resource opened was explicitly closed in a finally block. Failing to do so resulted in severe memory leaks and file lock issues.
To solve this, Java 7 introduced the Try-With-Resources statement. It is a specialized form of the try statement that declares one or more resources. A resource is an object that must be closed after the program is finished with it. The Try-With-Resources statement ensures that each resource is closed automatically at the end of the statement.
How It Works: The AutoCloseable Interface
For a class to be used inside a try-with-resources statement, it must implement the java.lang.AutoCloseable interface (or its sub-interface java.io.Closeable).
The AutoCloseable interface dictates a single method: void close() throws Exception. When the try block finishes execution (normally or abruptly due to an exception), the JVM automatically invokes the close() method on the resources declared within the parenthesis of the try statement.
The Old Way vs. The Modern Way
The Old Way (Pre-Java 7)
Look at how much boilerplate code was required just to read the first line of a file, ensuring the BufferedReader is closed safely:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class OldWay {
public static void main(String[] args) {
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader("test.txt"));
System.out.println(br.readLine());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
}
The Try-With-Resources Way
By declaring the BufferedReader inside the try parenthesis, Java automatically calls .close() on it when the block exits. The code is much cleaner, more readable, and fundamentally safer.
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class ModernWay {
public static void main(String[] args) {
try (BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
System.out.println(br.readLine());
} catch (IOException e) {
e.printStackTrace();
}
}
}
Managing Multiple Resources
You can declare multiple resources within a single try-with-resources block. They must be separated by a semicolon (;). The resources are closed in the reverse order in which they were declared.
try (
FileReader fr = new FileReader("input.txt");
BufferedReader br = new BufferedReader(fr);
FileWriter fw = new FileWriter("output.txt");
BufferedWriter bw = new BufferedWriter(fw)
) {
String line;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
}
} catch (IOException e) {
e.printStackTrace();
}
Java 9 Enhancements
In Java 7 and 8, you had to declare and initialize the resource inside the try parenthesis.
Java 9 introduced a quality-of-life improvement: if you have a resource that is already declared as effectively final outside the try block, you can simply reference it inside the try-with-resources statement without re-declaring it.
// Java 9+ feature
BufferedReader br = new BufferedReader(new FileReader("test.txt"));
try (br) { // The resource is automatically closed
System.out.println(br.readLine());
} catch (IOException e) {
e.printStackTrace();
}
Suppressed Exceptions
When using try-with-resources, an exception can be thrown from the try block, and another exception can be thrown while automatically closing the resource.
In the old try-finally approach, the exception thrown during .close() would swallow the original exception from the try block, masking the root cause of the bug.
Try-with-resources fixes this. The original exception thrown from the try block is preserved, and the exception thrown during the automatic closing of the resource is appended to it as a "suppressed exception". You can retrieve these using the Throwable.getSuppressed() method.