Java Language The try-with-resources statement


Example

Java SE 7

As the try-catch-final statement example illustrates, resource cleanup using a finally clause requires a significant amount of "boiler-plate" code to implement the edge-cases correctly. Java 7 provides a much simpler way to deal with this problem in the form of the try-with-resources statement.

What is a resource?

Java 7 introduced the java.lang.AutoCloseable interface to allow classes to be managed using the try-with-resources statement. Instances of classes that implement AutoCloseable are referred to as resources. These typically need to be disposed of in a timely fashion rather than relying on the garbage collector to dispose of them.

The AutoCloseable interface defines a single method:

public void close() throws Exception

A close() method should dispose of the resource in an appropriate fashion. The specification states that it should be safe to call the method on a resource that has already been disposed of. In addition, classes that implement Autocloseable are strongly encouraged to declare the close() method to throw a more specific exception than Exception, or no exception at all.

A wide range of standard Java classes and interfaces implement AutoCloseable. These include:

  • InputStream, OutputStream and their subclasses
  • Reader, Writer and their subclasses
  • Socket and ServerSocket and their subclasses
  • Channel and its subclasses, and
  • the JDBC interfaces Connection, Statement and ResultSet and their subclasses.

Application and third party classes may do this as well.

The basic try-with-resource statement

The syntax of a try-with-resources is based on classical try-catch, try-finally and try-catch-finally forms. Here is an example of a "basic" form; i.e. the form without a catch or finally.

try (PrintStream stream = new PrintStream("hello.txt")) {
    stream.println("Hello world!");
}

The resources to be manage are declared as variables in the (...) section after the try clause. In the example above, we declare a resource variable stream and initialize it to a newly created PrintStream.

Once the resource variables have been initialized, the try block is executed. When that completes, stream.close() will be called automatically to ensure that the resource does not leak. Note that the close() call happens no matter how the block completes.

The enhanced try-with-resource statements

The try-with-resources statement can be enhanced with catch and finally blocks, as with the pre-Java 7 try-catch-finally syntax. The following code snippet adds a catch block to our previous one to deal with the FileNotFoundException that the PrintStream constructor can throw:

try (PrintStream stream = new PrintStream("hello.txt")) {
    stream.println("Hello world!");
} catch (FileNotFoundException ex) {
    System.err.println("Cannot open the file");
} finally {
    System.err.println("All done");
}

If either the resource initialization or the try block throws the exception, then the catch block will be executed. The finally block will always be executed, as with a conventional try-catch-finally statement.

There are a couple of things to note though:

  • The resource variable is out of scope in the catch and finally blocks.
  • The resource cleanup will happen before the statement tries to match the catch block.
  • If the automatic resource cleanup threw an exception, then that could be caught in one of the catch blocks.

Managing multiple resources

The code snippets above show a single resource being managed. In fact, try-with-resources can manage multiple resources in one statement. For example:

try (InputStream is = new FileInputStream(file1);
     OutputStream os = new FileOutputStream(file2)) {
    // Copy 'is' to 'os'
}

This behaves as you would expect. Both is and os are closed automatically at the end of the try block. There are a couple of points to note:

  • The initializations occur in the code order, and later resource variable initializers can use of the values of the earlier ones.
  • All resource variables that were successfully initialized will be cleaned up.
  • Resource variables are cleaned up in reverse order of their declarations.

Thus, in the above example, is is initialized before os and cleaned up after it, and is will be cleaned up if there is an exception while initializing os.

Equivalence of try-with-resource and classical try-catch-finally

The Java Language Specification specifies the behavior of try-with-resource forms in terms of the classical try-catch-finally statement. (Please refer to the JLS for the full details.)

For example, this basic try-with-resource :

try (PrintStream stream = new PrintStream("hello.txt")) {
    stream.println("Hello world!");
}

is defined to be equivalent to this try-catch-finally:

// Note that the constructor is not part of the try-catch statement
PrintStream stream = new PrintStream("hello.txt");

// This variable is used to keep track of the primary exception thrown
// in the try statement. If an exception is thrown in the try block,
// any exception thrown by AutoCloseable.close() will be suppressed.
Throwable primaryException = null;

// The actual try block
try {
    stream.println("Hello world!");
} catch (Throwable t) {
    // If an exception is thrown, remember it for the finally block
    primaryException = t;
    throw t;
} finally {
    if (primaryException == null) {
        // If no exception was thrown so far, exceptions thrown in close() will
        // not be caught and therefore be passed on to the enclosing code.
        stream.close();
    } else {
        // If an exception has already been thrown, any exception thrown in
        // close() will be suppressed as it is likely to be related to the
        // previous exception. The suppressed exception can be retrieved
        // using primaryException.getSuppressed().
        try {
            stream.close();
        } catch (Throwable suppressedException) {
            primaryException.addSuppressed(suppressedException);
        }
    }
}

(The JLS specifies that the actual t and primaryException variables will be invisible to normal Java code.)

The enhanced form of try-with-resources is specified as an equivalence with the basic form. For example:

try (PrintStream stream = new PrintStream(fileName)) {
    stream.println("Hello world!");
} catch (NullPointerException ex) {
    System.err.println("Null filename");
} finally {
    System.err.println("All done");    
}

is equivalent to:

try {
    try (PrintStream stream = new PrintStream(fileName)) {
        stream.println("Hello world!");
    }
} catch (NullPointerException ex) {
    System.err.println("Null filename");
} finally {
    System.err.println("All done");    
}