- Understand the
AutoCloseableinterface and how it enables automatic resource management in Java - Implement try-with-resources statements to replace verbose try-catch-finally patterns
- Recognize when and why try-with-resources ensures reliable resource cleanup even during exceptions
- Handle exceptions properly in try-with-resources contexts, including suppressed exceptions
- Apply try-with-resources to multiple resources and create custom
AutoCloseableclasses
Resource management used to be painful in Java. Open a file, read some data, hit an exception—suddenly your application is leaking file handles. The traditional solution involved nested try-catch-finally blocks that turned simple operations into error-handling mazes.
Java 7 introduced try-with-resources to solve this problem. The try-with-resources statement is a try statement that
declares one or more resources and guarantees they'll close automatically. Resources declared in try-with-resources must
implement the AutoCloseable interface, and Java ensures they close when the block exits—whether your code succeeds or
throws an exception.
This feature fundamentally changed resource management in Java. Instead of writing defensive cleanup code everywhere, you declare your resources in the try statement and the compiler handles the rest. Try-with-resources makes sure resources are closed reliably, even during exception scenarios that used to cause leaks.
Before try-with-resources, proper resource cleanup meant verbose try-catch-finally blocks. Here's what reading a file looked like:
package blog.academy.javapro;
import java.io.*;
import java.util.Scanner;
public class TraditionalApproach {
// Helper to create test file
private static void createFile() {
PrintWriter writer = null;
try {
writer = new PrintWriter("data.txt");
writer.println("Line 1");
writer.println("Line 2");
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (writer != null) {
writer.close();
}
}
}
public static void main(String[] args) {
createFile();
Scanner scanner = null;
try {
scanner = new Scanner(new File("data.txt"));
while (scanner.hasNext()) {
System.out.println(scanner.nextLine());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (scanner != null) {
scanner.close();
}
}
}
}That finally block is necessary but tedious. We need the null check because scanner might not initialize if the
constructor fails. This pattern is error-prone—developers often skip the finally block entirely, causing resource leaks
in production.
The try-with-resources statement simplifies everything. Declare your resource in parentheses, and Java closes it automatically:
package blog.academy.javapro;
import java.io.*;
import java.util.Scanner;
public class TryWithResourcesBasic {
private static void createFile() {
try (PrintWriter writer = new PrintWriter("data.txt")) {
writer.println("Line 1");
writer.println("Line 2");
writer.println("Line 3");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
createFile();
try (Scanner scanner = new Scanner(new File("data.txt"))) {
while (scanner.hasNext()) {
System.out.println(scanner.nextLine());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}No finally block. No null checks. The Scanner implements AutoCloseable, so try-with-resources handles closing
automatically. This is how try-with-resources replaces the traditional and verbose try-catch-finally block—with clean,
reliable code.
Try-with-resources handles multiple resources by separating them with semicolons:
package blog.academy.javapro;
import java.io.*;
import java.util.Scanner;
public class MultipleResources {
private static void createFile() {
try (PrintWriter writer = new PrintWriter("input.txt")) {
writer.println("hello world");
writer.println("java programming");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
createFile();
try (Scanner scanner = new Scanner(new File("input.txt"));
PrintWriter writer = new PrintWriter(new File("output.txt"))) {
while (scanner.hasNext()) {
writer.println(scanner.nextLine().toUpperCase());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
System.out.println("File processing complete!");
}
}Resources close in reverse order—writer closes before scanner. This ordering matters when resources depend on each
other. Try-with-resources ensures that resources are closed automatically in the correct sequence.
Any class implementing AutoCloseable works with try-with-resources. Here's a custom resource:
package blog.academy.javapro;
public class MyResource implements AutoCloseable {
public MyResource() {
System.out.println("MyResource opened");
}
public void doWork() {
System.out.println("MyResource doing work");
}
@Override
public void close() {
System.out.println("MyResource closed");
}
public static void main(String[] args) {
try (MyResource resource = new MyResource()) {
resource.doWork();
}
}
}When the try block exits, close() gets called automatically. Try-with-resources guarantees this happens regardless of
how the block exits.
Resources close in reverse order of declaration. First declared, last closed:
package blog.academy.javapro;
public class ResourceFirst implements AutoCloseable {
public ResourceFirst() {
System.out.println("ResourceFirst created");
}
public void doSomething() {
System.out.println("ResourceFirst working");
}
@Override
public void close() {
System.out.println("ResourceFirst closed");
}
}package blog.academy.javapro;
public class ResourceSecond implements AutoCloseable {
public ResourceSecond() {
System.out.println("ResourceSecond created");
}
public void doSomething() {
System.out.println("ResourceSecond working");
}
@Override
public void close() {
System.out.println("ResourceSecond closed");
}
}package blog.academy.javapro;
public class ClosingOrder {
public static void main(String[] args) {
try (ResourceFirst first = new ResourceFirst();
ResourceSecond second = new ResourceSecond()) {
first.doSomething();
second.doSomething();
}
}
}Output:
ResourceFirst created
ResourceSecond created
ResourceFirst working
ResourceSecond working
ResourceSecond closed
ResourceFirst closed
The second resource closes first. This reverse order ensures proper cleanup when resources have dependencies.
Try-with-resources preserves exception information through suppressed exceptions:
package blog.academy.javapro;
public class ResourceWithException implements AutoCloseable {
private final String name;
public ResourceWithException(String name) {
this.name = name;
System.out.println(name + " opened");
}
public void doWork() throws Exception {
System.out.println(name + " working");
throw new Exception(name + " work failed");
}
@Override
public void close() throws Exception {
System.out.println(name + " closing");
throw new Exception(name + " close failed");
}
public static void main(String[] args) {
try (ResourceWithException resource = new ResourceWithException("MyResource")) {
resource.doWork();
} catch (Exception e) {
System.out.println("\nPrimary exception: " + e.getMessage());
Throwable[] suppressed = e.getSuppressed();
if (suppressed.length > 0) {
System.out.println("Suppressed exceptions:");
for (Throwable t : suppressed) {
System.out.println(" - " + t.getMessage());
}
}
}
}
}The work exception is primary. The close exception becomes suppressed. Try-with-resources ensures that resources are closed automatically while preserving all exception details.
Try-with-resources blocks can include catch and finally clauses:
package blog.academy.javapro;
import java.io.*;
import java.util.Scanner;
public class CatchAndFinally {
private static void createFile() {
try (PrintWriter writer = new PrintWriter("test.txt")) {
writer.println("Test data");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
createFile();
try (Scanner scanner = new Scanner(new File("test.txt"))) {
while (scanner.hasNext()) {
System.out.println(scanner.nextLine());
}
} catch (FileNotFoundException e) {
System.out.println("Caught: " + e.getMessage());
} finally {
System.out.println("Finally block executed");
}
}
}The resource closes before the catch or finally blocks execute. This ordering ensures cleanup happens first.
Before Java 9, you could only use fresh variables in try-with-resources. Java 9 allows effectively final variables:
package blog.academy.javapro;
import java.io.*;
import java.util.Scanner;
public class EffectivelyFinal {
private static void createFile() {
try (PrintWriter writer = new PrintWriter("final-test.txt")) {
writer.println("Java 9 feature");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws FileNotFoundException {
createFile();
// Pre-declared resources
final Scanner scanner = new Scanner(new File("final-test.txt"));
PrintWriter writer = new PrintWriter(new File("final-output.txt"));
// Java 9+: Use them directly
try (scanner; writer) {
while (scanner.hasNext()) {
writer.println(scanner.nextLine());
}
}
System.out.println("Resources closed automatically");
}
}The scanner is explicitly final. The writer is effectively final (not reassigned). Both work with try-with-resources
in Java 9+.
Try-with-resources shines with database code:
package blog.academy.javapro;
import java.sql.*;
public class DatabaseOperations {
private static void setupDatabase(Connection conn) throws SQLException {
try (Statement stmt = conn.createStatement()) {
stmt.execute("CREATE TABLE users (id INT, name VARCHAR(50))");
stmt.execute("INSERT INTO users VALUES (1, 'Alice')");
stmt.execute("INSERT INTO users VALUES (2, 'Bob')");
}
}
public static void main(String[] args) {
try (Connection conn = DriverManager.getConnection("jdbc:h2:mem:testdb")) {
setupDatabase(conn);
try (PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users");
ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
System.out.println("User: " + rs.getString("name"));
}
}
} catch (SQLException e) {
System.err.println("Database error: " + e.getMessage());
}
}
}Three resources—Connection, PreparedStatement, and ResultSet—all close automatically in the right order. No manual
cleanup needed. Try-with-resources makes sure resources are closed reliably even when exceptions occur.
The try-with-resources statement transformed resource management in Java. It replaces the traditional and verbose try-catch-finally block with concise, reliable code. Resources declared in try-with-resources get closed automatically when the block exits, preventing resource leaks.
Any class implementing AutoCloseable works with try-with-resources. Files, database connections, network
sockets—anything needing cleanup can use this pattern. Try-with-resources ensures that resources are closed
automatically, even during exceptions.
Multiple resources work seamlessly. Declare them together separated by semicolons, and they close in reverse order. Exception handling is sophisticated—primary exceptions propagate while close exceptions become suppressed, preserving all diagnostic information.
The try-with-resources statement is one of Java's most important features for resource management. It eliminates entire categories of bugs by moving resource cleanup from manual finally blocks to compiler-guaranteed automatic management. Every Java developer should use try-with-resources as their default approach to resource management. It's simpler, safer, and more maintainable than any manual alternative.
Download the full try-with-resources source code.
Ready to master Java? Join our Java Bootcamp to master enterprise-level development, or start with our free Core Java course to build your foundation.