Java try-with-resources

In Java, managing resources such as files, database connections, and sockets is crucial for ensuring your applications run efficiently and without memory leaks. Traditionally, you would need to manually close these resources using finally blocks, which can lead to verbose and error-prone code.

With the introduction of try-with-resources in Java 7, resource management has become simpler and safer. The try-with-resources statement automatically closes resources when they are no longer needed, removing the need for explicit finally block management.


What is Java try-with-resources?

The try-with-resources statement is a special form of the try block introduced in Java 7 that ensures that resources are automatically closed after they are no longer needed. Resources such as InputStream, OutputStream, Reader, Writer, and database connections (that implement the AutoCloseable interface) are automatically closed at the end of the try block.

Syntax of try-with-resources:

try (ResourceType resource = new ResourceType()) {
    // Code that uses the resource
} catch (ExceptionType e) {
    // Exception handling
} finally {
    // Optional: Code that will run after the try block
}
  • The resource declared in the parentheses after try must implement the AutoCloseable interface (which includes the close() method).
  • Once the try block finishes execution, whether normally or due to an exception, the close() method of the resource is automatically called.

Example: Using try-with-resources with Files

Let’s start by demonstrating how try-with-resources works when reading from a file using BufferedReader.

Example Code: Reading from a File

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class TryWithResourcesExample {
    public static void main(String[] args) {
        String fileName = "example.txt";
        
        try (BufferedReader br = new BufferedReader(new FileReader(fileName))) {
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            System.out.println("An error occurred: " + e.getMessage());
        }
    }
}

Explanation:

  • The BufferedReader resource is declared inside the parentheses of the try block.
  • Once the try block completes (either successfully or due to an exception), the BufferedReader is automatically closed, releasing the file resource.
  • No need for a finally block to close the resource manually.

How try-with-resources Works

The main advantage of the try-with-resources statement is automatic resource management. Here’s what happens when a resource is used in a try-with-resources block:

  1. Resource Initialization: The resource (e.g., a file, database connection, or socket) is created and opened within the parentheses of the try block.
  2. Automatic Closing: Once the try block exits, whether by completing normally or due to an exception, the close() method of each resource is called automatically.
  3. Exception Handling: If an exception occurs while opening the resource or within the try block, it is caught by the catch block (if provided).
  4. Chained Exceptions: If an exception occurs while closing the resource, it is suppressed and stored as a "suppressed exception" in the Throwable returned by the catch block.

Multiple Resources in a Single try-with-resources Block

You can manage multiple resources in a single try-with-resources block. The resources are separated by a semicolon (;).

Example Code: Managing Multiple Resources

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class MultipleResourcesExample {
    public static void main(String[] args) {
        String inputFile = "input.txt";
        String outputFile = "output.txt";
        
        try (
            BufferedReader reader = new BufferedReader(new FileReader(inputFile));
            FileWriter writer = new FileWriter(outputFile)
        ) {
            String line;
            while ((line = reader.readLine()) != null) {
                writer.write(line + "\n");
            }
        } catch (IOException e) {
            System.out.println("An error occurred: " + e.getMessage());
        }
    }
}

Explanation:

  • Both BufferedReader (for reading) and FileWriter (for writing) are declared and initialized inside the try-with-resources block.
  • The close() method is automatically called for both resources once the try block completes.

Best Practices for try-with-resources

  1. Prefer try-with-resources Over finally: If a resource implements the AutoCloseable interface, use try-with-resources to automatically close it. This approach is cleaner and less error-prone compared to manually managing resources in a finally block.

  2. Use Multiple Resources Wisely: If your application requires multiple resources (e.g., reading and writing to files), group them together in a single try-with-resources block. This ensures that all resources are properly closed.

  3. Handle Exceptions Properly: Always include a catch block to handle exceptions. When multiple resources are involved, it's important to catch and handle any potential IOException or other relevant exceptions.

  4. Be Aware of Suppressed Exceptions: If an exception occurs during the closing of a resource, it can be suppressed. Always check for suppressed exceptions in your error handling code, especially if closing multiple resources.

  5. Ensure AutoCloseable Interface: Only objects that implement the AutoCloseable interface can be used in try-with-resources. This includes FileReader, BufferedReader, database connections, and others. If you're using custom resources, ensure they implement AutoCloseable or Closeable.


Example: Custom Resource with try-with-resources

You can create your own custom class that implements the AutoCloseable interface, and use it in a try-with-resources block.

Example Code: Custom Resource Class

public class CustomResource implements AutoCloseable {
    public CustomResource() {
        System.out.println("Resource acquired!");
    }

    public void useResource() {
        System.out.println("Using resource...");
    }

    @Override
    public void close() {
        System.out.println("Resource closed!");
    }

    public static void main(String[] args) {
        try (CustomResource resource = new CustomResource()) {
            resource.useResource();
        }
    }
}

Output:

Resource acquired!
Using resource...
Resource closed!

In this example:

  • CustomResource implements the AutoCloseable interface.
  • The close() method is automatically invoked when the try block finishes, ensuring that the resource is properly released.