Java Try With Resources
Jakob Jenkov |
The Java try with resources construct, AKA Java try-with-resources, is an exception handling mechanism that can automatically close resources like a Java InputStream or a JDBC Connection when you are done with them. To do so, you must open and use the resource within a Java try-with-resources block. When the execution leaves the try-with-resources block, any resource opened within the try-with-resources block is automatically closed, regardless of whether any exceptions are thrown either from inside the try-with-resources block, or when attempting to close the resources.
This Java try-with-resources tutorial explains how the Java try-with-resources construct works, how to properly use it, and how exceptions are handled that are thrown both from inside the try-with-resources block, and during the closing of the resources.
Try-with-resources Video
I have created a video version of this Java try-with-resources tutorial, in case you prefer that:
Try-with-resources
To see how the Java try-with-resources construct work, let us look at a Java try-with-resources example:
private static void printFile() throws IOException { try(FileInputStream input = new FileInputStream("file.txt")) { int data = input.read(); while(data != -1){ System.out.print((char) data); data = input.read(); } } }
This try-with-resources example shows how to open a Java FileInputStream
inside a try-with-resources block, read some data from the FileInputStream
, and have the
FileInputStream
closed automatically once execution leaves the try-with-resources block
(not explicitly visible).
Notice the first line inside the method in the try-with-resources example above:
try(FileInputStream input = new FileInputStream("file.txt")) {
This is the try-with-resources construct. The FileInputStream
variable is declared inside the parentheses after the try
keyword. Additionally,
a FileInputStream
is instantiated and assigned to the variable.
When the try
block finishes the FileInputStream
will be closed
automatically. This is possible because FileInputStream
implements the
Java interface java.lang.AutoCloseable
. All classes implementing this
interface can be used inside the try-with-resources construct.
Try-with-resources Java 9 Enhancement
Before Java 9 a resource that is to be automatically closed must be created inside the parentheses of the try block of a try-with-resources construct. From Java 9, this is no longer necessary. If the variable referencing the resource is effectively final, you can simply enter a reference to the variable inside the try block parentheses. Here is an example of the Java 9 try-with-resources enhancement:
private static void printFile() throws IOException { FileInputStream input = new FileInputStream("file.txt"); try(input) { int data = input.read(); while(data != -1){ System.out.print((char) data); data = input.read(); } } }
Notice how the input
variable is now declared and has a FileInputStream
assigned
outside the try block. Notice also, how the input
variable is references inside the parentheses
of the try block. This way, Java will still close it properly once the try block is exited.
Using Multiple Resources
You can use multiple resources inside a Java try-with-resources block and have them all automatically closed. Here is an example of using multiple resources inside a try-with-resources block:
private static void printFile() throws IOException { try( FileInputStream input = new FileInputStream("file.txt"); BufferedInputStream bufferedInput = new BufferedInputStream(input) ) { int data = bufferedInput.read(); while(data != -1){ System.out.print((char) data); data = bufferedInput.read(); } } }
This example creates two resources inside the parentheses after the try keyword.
An FileInputStream
and a BufferedInputStream
. Both of these resources
will be closed automatically when execution leaves the try block.
Closing Order
The resources declared in a Java try-with-resources construct will be closed in reverse order of the order
in which they are created / listed inside the parentheses. In the example in the previous
section, first the FileInputStream
.
Custom AutoClosable Implementations
The Java try-with-resources construct does not just work with Java's built-in classes.
You can also implement the java.lang.AutoCloseable
interface in your own classes,
and use them with the try-with-resources construct.
The AutoClosable
interface only has a single method called close()
.
Here is how the interface looks:
public interface AutoClosable { public void close() throws Exception; }
Any class that implements this interface can be used with the Java try-with-resources construct. Here is a simple example implementation:
public class MyAutoClosable implements AutoCloseable { public void doIt() { System.out.println("MyAutoClosable doing it!"); } @Override public void close() throws Exception { System.out.println("MyAutoClosable closed!"); } }
The doIt()
method is not part of the AutoClosable
interface. It is there because
we want to be able to do something more than just closing the object.
Here is an example of how the MyAutoClosable
is used with the try-with-resources
construct:
private static void myAutoClosable() throws Exception { try(MyAutoClosable myAutoClosable = new MyAutoClosable()){ myAutoClosable.doIt(); } }
Here is the output printed to System.out
when the method myAutoClosable()
is called:
MyAutoClosable doing it! MyAutoClosable closed!
As you can see, try-with-resources is a quite powerful way of making sure that resources
used inside a try-catch
block are closed correctly, no matter if these resources are your
own creation, or Java's built-in components.
Try-with-resources Exception Handling
The exception handling semantics of a Java try-with-resources block vary a bit from the exception handling semantics of a standard Java try-catch-finally block. In most situations the changed semantics will work better for you than the semantics of the original try-catch-finally block, even without you understanding the difference precisely. Even so, it can be a good idea to actually understand what is going on exception handling wise, in the try-with-resources construct. Therefore I will explain the exception handling semantics of the try-with-resources construct here.
If an exception is thrown from within a Java try-with-resources block, any resource opened inside the parentheses of the try block will still get closed automatically. The throwing of the exception will force the execution to leave the try block, and this will force the automatic closing of the resource. The exception thrown from inside the try block will get propagated up the call stack, once the resources have been closed.
Some resources may also throw exceptions when you try to close them. In case a resource throws an exception when you try to close it, any other resources opened within the same try-with-resources block will still get closed. After closing all resources, the exception from the failed close-attempt will get propagated up the call stack. In case multiple exceptions are thrown from multiple resource close attempts, the first exception encountered will be the one propagated up the call stack. The rest of the exceptions will be suppressed.
If an exception is thrown both from inside the try-with-resources block,
and when a resource is closed (when close()
is called),
the exception thrown inside the try block will be propagated up the call stack.
The exception thrown when the resource was attempted closed will be suppressed.
This is opposite of what happens in a normal try-catch-finally block,
where the last exception encountered is the exception that is propagated up the call stack.
To better understand the exception handling semantics of the Java try-with-resources construct,
let us look at some examples. For these examples I have created the following AutoClosable
implementation
which I can force to throw exceptions both when used and when attempted closed:
public class AutoClosableResource implements AutoCloseable { private String name = null; private boolean throwExceptionOnClose = false; public AutoClosableResource(String name, boolean throwExceptionOnClose) { this.name = name; this.throwExceptionOnClose = throwExceptionOnClose; } public void doOp(boolean throwException) throws Exception { System.out.println("Resource " + this.name + " doing operation"); if(throwException) { throw new Exception("Error when calling doOp() on resource " + this.name); } } @Override public void close() throws Exception { System.out.println("Resource " + this.name + " close() called"); if(this.throwExceptionOnClose){ throw new Exception("Error when trying to close resource " + this.name); } } }
First, let us look at a basic example with a single resource in use:
public static void main(String[] args){ try { tryWithResourcesSingleResource(); } catch (Exception e) { e.printStackTrace(); Throwable[] suppressed = e.getSuppressed(); } } public static void tryWithResourcesSingleResource() throws Exception { try(AutoClosableResource resourceOne = new AutoClosableResource("One", false)) { resourceOne.doOp(false); } }
In case the second parameter to the AutoClosableResource
construct was changed to true
,
it would throw an exception when attempted closed. In that case, the exception thrown when attempted closed will
be propagated up the call stack to the main()
method where the try-catch block will catch
it. In that case, the Throwable
array returned from e.getSuppessed()
will be an empty
array (size of 0).
In case the parameter to resourceOne.doOp()
was changed to true
also, the doOp()
method would throw an exception. In that case, it is this exception that is propagated up the call stack to
the main()
method. The exception thrown when attempting to close the resource would be available inside
the Throwable
array returned by e.getSuppressed()
.
Let us look at an example with two AutoClosable
resources in use:
public static void main(String[] args){ try { tryWithResourcesTwoResources(); } catch (Exception e) { e.printStackTrace(); Throwable[] suppressed = e.getSuppressed(); System.out.println("suppressed = " + suppressed); } } public static void tryWithResourcesTwoResources() throws Exception { try(AutoClosableResource resourceOne = new AutoClosableResource("One", true); AutoClosableResource resourceTwo = new AutoClosableResource("Two", true) ){ resourceOne.doOp(true); resourceTwo.doOp(false); } }
In the case where only one of the resources throw an exception, either during use or when attempted closed,
the behaviour is the same as when only one resource is used. However, in the example above I have forced
both resources to throw an exception when attempted closed, and the first resource to throw an exception when
used (when doOp()
is called). In that case, the exception thrown from inside the
try block is propagated up the call stack. The two exceptions thrown when attempting to close the
resources are available in the Throwable
array returned by e.getSuppressed()
.
Remember, only a single exception can be thrown inside the try block. As soon as an exception is thrown, the try block code is exited, and the resources attempted closed.
Catch Block
You can add a catch block to a try-with-resources block just like you can to a standard try block. If an exception is thrown from within the try block of a try-with-resources block, the catch block will catch it, just like it would when used with a standard try construct.
Before the catch block is entered, the try-with-resources construct will attempt to close the
resources opened inside the try block. In case an exception is thrown when attempting to close one of
the resources, these exceptions will be available from the exception's getSuppressed()
method inside
the catch block. Here is an example of a Java try-with-resources block with a catch
block attached:
try(AutoClosableResource resourceOne = new AutoClosableResource("One", true)) { resourceOne.doOp(true); } catch(Exception e) { Throwable[] suppressed = e.getSuppressed(); throw e; }
In the example above, the AutoClosableResource
is configured to throw an exception both when
doOp()
is called, and when it is attempted closed (via close()
). The exception thrown
from doOp()
is caught in the catch block, its getSuppressed()
method returns
an array with the exception thrown when the resource was attempted closed.
In case that an exception is only thrown when the resource is attempted closed, the catch block will
also catch it. The getSuppressed()
method of that exception will return an empty array, since no
exceptions where suppressed.
Finally Block
It is also possible to add a finally block to a Java try-with-resources block. It will behave just like a standard finally block, meaning it will get executed as the last step before exiting the try-with-resources block - after any catch block has been executed.
In case you throw an exception from within the finally block of a try-with-resources construct, all previously thrown exceptions will be lost! Here is an example of throwing an exception from within the finally block of a Java try-with-resources construct:
public static void main(String[] args){ try { tryWithResourcesSingleResource(); } catch (Exception e) { e.printStackTrace(); Throwable[] suppressed = e.getSuppressed(); } } public static void tryWithResourcesSingleResource() throws Exception { try(AutoClosableResource resourceOne = new AutoClosableResource("One", true)) { resourceOne.doOp(false); } catch(Exception e) { Throwable[] suppressed = e.getSuppressed(); throw e; } finally { throw new Exception("Hey, an exception from the finally block"); } }
Notice, that the exception thrown from within the catch block will be ignored because a new exception is thrown from within the finally block. This would also be true if there was no catch block. Then any exception thrown from inside the try block would get lost because a new exception is thrown from inside the finally block. Any previous exceptions are not suppressed, so they are not available from within the exception thrown from the finally block.
Adding Suppressed Exceptions Manually
The Throwable
class has a method named addSuppressed()
which takes a Throwable
object as parameter. Using the addSuppressed()
method it is possible to add suppressed exceptions to
another exception, in case you need that. Here is an example that shows how to add suppressed exceptions to a
Java exception manually:
Exception finalException = null; try(AutoClosableResource resourceOne = new AutoClosableResource("One", true)) { resourceOne.doOp(false); } catch(Exception e) { finalException = new Exception("Error..."); finalException.addSuppressed(e); for(Throwable suppressed : e.getSuppressed()){ finalException.addSuppressed(suppressed); } } finally { if(finalException != null){ throw finalException; } }
Notice how the Throwable
reference has to be declared outside the try-with-resources construct.
Otherwise the catch and finally blocks cannot access it.
In most cases you will not need to add suppressed exceptions to an exception manually, but now you have at least seen how it can be done, in case you ever run into a situation where you need it.
Resource Management With Try-Catch-Finally, Old School Style
The Java try-with-resources construct was added in Java 7.
Managing resources that need to be explicitly closed was somewhat tedious
before Java 7. You had to handle the correct closure of the resources manually. This was not an easy
task to handle correctly. To understand why, look at the following method which reads a file and prints
it to the System.out
:
private static void printFile() throws IOException { InputStream input = null; try { input = new FileInputStream("file.txt"); int data = input.read(); while(data != -1){ System.out.print((char) data); data = input.read(); } } finally { if(input != null){ input.close(); } } }
The code marked in bold is where the code can throw an Exception
. As you can see,
that can happen in 3 places inside the try
-block, and 1 place inside the finally
-block.
The finally
block is always executed no matter if an exception is thrown from the try
block
or not. That means, that the InputStream
is closed no matter what happens in the try
block. Or, attempted closed that is. The InputStream
's close()
method may throw an
exception too, if closing it fails.
Imagine that an exception is thrown from inside the try
block. Then the finally
block
is executed. Imagine then, that an exception is also thrown from the finally
block. Which exception
do you think is propagated up the call stack?
The exception thrown from the finally
block would be propagated up the call stack, even if the
exception thrown from the try
block would probably be more relevant to propagate.
Tweet | |
Jakob Jenkov |