Java try-with-resources and AutoCloseable Explained
Introduction
Files, network connections, database connections and many other objects hold resources outside the JVM, which must be released by calling
(Note - This tutorial builds on try, catch, finally and multi-catchClick to read tutorial: Java try, catch, finally and Multi-catch with Examples.) The problem - closing resources by hand Before Java 7, the correct way to use a resource was a
That is a lot of code for one line of real work, and it is easy to get wrong - forgetting the
Where,
OUTPUT of the above code
The same job as the old code, in a fraction of the lines - and the reader is closed in every case. For a missing file,
Writing your own AutoCloseable resource
Any class can be used with try-with-resources by implementing
OUTPUT of the above code
Explanation of the code
OUTPUT of the above code
The real problem - "work failed" - is the exception that was caught, and the secondary failure from
Java 9 - using an existing variable
In Java 7 and 8, every resource had to be declared inside the parentheses. Since Java 9, you can also list an existing variable which is
OUTPUT of the above code
close() when you are done - even if an exception occurs. Doing this correctly by hand takes surprisingly careful code. try-with-resources, added in Java 7, does it for you. This tutorial explains try-with-resources and AutoCloseable in Java. We will start with the problem of closing resources by hand with finally. We will then use try-with-resources, and write our own resource class to see exactly when close() is called - including with several resources. Next we will understand suppressed exceptions, which try-with-resources keeps instead of losing. Lastly, we will look at the Java 9 improvement and at best practices.(Note - This tutorial builds on try, catch, finally and multi-catchClick to read tutorial: Java try, catch, finally and Multi-catch with Examples.) The problem - closing resources by hand Before Java 7, the correct way to use a resource was a
try block for the work and a finally block for closing it - with a null check, because the resource may never have been opened, and another try, because close() itself can throw.
Closing a resource the old way
BufferedReader reader = null;
try {
reader = Files.newBufferedReader(Path.of("data.txt"));
System.out.println(reader.readLine());
} catch (IOException e) {
System.out.println("Read failed: " + e.getMessage());
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
System.out.println("Close failed: " + e.getMessage());
}
}
}
null check, or the finally altogether. With two or three resources it gets much worse. And it has a hidden flaw: if both the read and the close fail, one of the two exceptions is lost.
try-with-resources
Syntax of try-with-resourcesSyntax of try-with-resources
try (ResourceType resource = new ResourceType(...)) {
// use the resource
} catch (SomeException e) {
// optional catch blocks
} finally {
// optional finally block
}
- resources are declared in parentheses after
try. Each must implementjava.lang.AutoCloseable(or its subinterfacejava.io.Closeable) - which almost every resource class in the JDK does. - when the
tryblock ends - normally, throughreturn, or because of an exception - Java calls each resource'sclose()method automatically, before anycatchorfinallyblock runs. - a resource which was never successfully created (its expression threw) is not closed, so no
nullcheck is needed. - the resource variable is implicitly
final, and its scope is thetryblock.
Reading a file with try-with-resources
//ReadWithResources.java
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public class ReadWithResources {
static void printFile(String name) {
try (BufferedReader reader = Files.newBufferedReader(Path.of(name))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(" " + line);
}
} catch (IOException e) {
System.out.println(" Could not read " + name + ": " + e);
}
}
public static void main(String[] args) throws IOException {
Files.writeString(Path.of("greetings.txt"), "Hello\nfrom\ntry-with-resources\n");
System.out.println("greetings.txt:");
printFile("greetings.txt");
System.out.println("missing.txt:");
printFile("missing.txt");
Files.delete(Path.of("greetings.txt"));
}
}
greetings.txt: Hello from try-with-resources missing.txt: Could not read missing.txt: java.nio.file.NoSuchFileException: missing.txt
newBufferedReader() threw, no reader was created, and nothing needed closing.
AutoCloseable, whose only method is void close() throws Exception. Overriding it, you can narrow or drop the throws clause. Let us write a resource which prints when it opens and closes, and use two of them together.
A custom AutoCloseable resource
//Connection.java
public class Connection implements AutoCloseable {
private final String name;
Connection(String name) {
this.name = name;
System.out.println(" open " + name);
}
void send(String message) {
if (message.isEmpty()) {
throw new IllegalArgumentException("Empty message on " + name);
}
System.out.println(" " + name + " sent: " + message);
}
@Override
public void close() { // no checked exception declared
System.out.println(" close " + name);
}
}
Closing order with several resources
//ResourceOrder.java
public class ResourceOrder {
static void transfer(String message) {
try (Connection db = new Connection("database");
Connection queue = new Connection("queue")) {
db.send(message);
queue.send(message);
System.out.println(" try block finished");
} catch (IllegalArgumentException e) {
System.out.println(" catch: " + e.getMessage());
} finally {
System.out.println(" finally");
}
}
public static void main(String[] args) {
System.out.println("Normal run:");
transfer("order #317");
System.out.println("Run with an exception:");
transfer("");
}
}
Normal run: open database open queue database sent: order #317 queue sent: order #317 try block finished close queue close database finally Run with an exception: open database open queue close queue close database catch: Empty message on database finally
- Several resources are separated by semicolons. They are opened in the order written, and closed in the reverse order -
queuefirst, thendatabase- so a resource is never closed while one that depends on it is still open. - In the second run,
db.send("")threw. Both connections were closed before thecatchblock ran, and thenfinallyran last. close()was overridden withoutthrows Exception, so code usingConnectiondoes not have to handle a checked exception from closing.
try block throws, and then close() throws as well? With the old finally pattern, the exception from close() replaced the original one, which was simply lost - usually the more important of the two. try-with-resources keeps the original exception, and attaches the exception from close() to it as a suppressed exception, available from getSuppressed().
An exception from close() is suppressed, not lost
//SuppressedDemo.java
public class SuppressedDemo {
static class FaultyResource implements AutoCloseable {
@Override
public void close() {
throw new IllegalStateException("close() failed");
}
}
public static void main(String[] args) {
try (FaultyResource resource = new FaultyResource()) {
throw new RuntimeException("work failed");
} catch (RuntimeException e) {
System.out.println("Caught : " + e.getMessage());
for (Throwable suppressed : e.getSuppressed()) {
System.out.println("Suppressed : " + suppressed.getMessage());
}
}
}
}
Caught : work failed Suppressed : close() failed
close() is still available instead of being thrown away. Stack traces printed by Java show suppressed exceptions too, under "Suppressed:".
final or effectively final (never reassigned) - handy when a resource is created earlier, or passed in as a parameter.
try-with-resources on an existing variable (Java 9+)
//Java9Resources.java
public class Java9Resources {
static void sendAll(Connection connection, String... messages) {
try (connection) { // an existing, effectively final variable
for (String m : messages) {
connection.send(m);
}
}
}
public static void main(String[] args) {
Connection mail = new Connection("mail-server");
sendAll(mail, "Welcome, Tom", "Your order has shipped");
}
}
open mail-server mail-server sent: Welcome, Tom mail-server sent: Your order has shipped close mail-server
Java9Resources reuses the Connection class from earlier. Note that it closes a connection it was given - make sure that is what callers expect, as the method now owns the resource.
Best practices
- Use try-with-resources for every resource - files, streams, readers and writers, sockets, JDBC connections, statements and result sets,
Scanners on files, and executor services (which areAutoCloseablesince Java 19). - Declare one resource per object. Writing
try (var r = new BufferedReader(new FileReader(f)))registers only the outer reader; if its constructor failed, the innerFileReaderwould not be closed. Declaring each object as its own resource avoids that. - Make your own resource classes AutoCloseable, make
close()safe to call more than once, and declare the narrowest exception it can throw (or none). - Do not close System.in with try-with-resources around a
Scanner- closing the scanner closes standard input for the rest of the program, as explained in the Scanner tutorialClick to read tutorial: Reading User Input in Java with Scanner.
finally, and that it can lose exceptions. We then used try-with-resources, which calls close() automatically on every AutoCloseable resource, whether or not an exception occurs. Next we wrote our own resource class, and saw that several resources are closed in reverse order, before any catch or finally block runs. We then saw that an exception from close() is kept as a suppressed exception instead of hiding the original. Lastly, we used an existing variable as a resource (Java 9+), and went through best practices. In the next tutorial, we will bring it all together with exception handling best practicesClick to read tutorial: Java Exception Handling Best Practices.Core Java Tutorials
Creating Custom Exceptions in Java with ExamplesClick to read tutorial: Creating Custom Exceptions in Java with Examples Java Exception Handling Best PracticesClick to read tutorial: Java Exception Handling Best Practices Checked vs Unchecked Exceptions in JavaClick to read tutorial: Checked vs Unchecked Exceptions in Java Java try, catch, finally and Multi-catch with ExamplesClick to read tutorial: Java try, catch, finally and Multi-catch with Examples throw vs throws in Java with ExamplesClick to read tutorial: throw vs throws in Java with Examples Java Math Class: Essential Methods with ExamplesClick to read tutorial: Java Math Class: Essential Methods with Examples
Creating Custom Exceptions in Java with ExamplesClick to read tutorial: Creating Custom Exceptions in Java with Examples Java Exception Handling Best PracticesClick to read tutorial: Java Exception Handling Best Practices Checked vs Unchecked Exceptions in JavaClick to read tutorial: Checked vs Unchecked Exceptions in Java Java try, catch, finally and Multi-catch with ExamplesClick to read tutorial: Java try, catch, finally and Multi-catch with Examples throw vs throws in Java with ExamplesClick to read tutorial: throw vs throws in Java with Examples Java Math Class: Essential Methods with ExamplesClick to read tutorial: Java Math Class: Essential Methods with Examples