Introduction
try-with-resources is one of the several try
statements in Java, aimed to relieve developers of the obligation to release resources used in a try
block.
It was initially introduced in Java 7 and the whole idea behind it was that the developer doesn't need to worry about resource management for resources they use only in one try-catch-finally block. This is achieved by removing the need for finally
blocks, which developers only used to close resources in practice.
Additionally, code using try-with-resources is often cleaner and more readable, therefore, making the code easier to manage, especially when we're dealing with many try
blocks.
Syntax
The syntax for try-with-resources is almost identical to the usual try-catch-finally syntax. The only difference are the parentheses after try
in which we declare which resources we'll be using:
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new FileWriter(fileName));
writer.write(str); // do something with the file we've opened
} catch (IOException e) {
// handle the exception
} finally {
try {
if (writer != null)
writer.close();
} catch (IOException e) {
// handle the exception
}
}
The same code written using try-with-resources would look like this:
try(BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))){
writer.write(str); // do something with the file we've opened
}
catch(IOException e){
// handle the exception
}
The way Java understands this code:
The resources opened in the parentheses after the try statement will only be needed here and now. I'll call their
.close()
methods as soon as I'm done with the work in the try block. If an exception is thrown while in the try block, I'll close those resources anyway.
Before this approach was introduced, closing resources was done manually, as seen in the code before. This was essentially boilerplate code, and codebases were littered with them, reducing readability and making them harder to maintain.
The catch
and finally
part of try-with-resources work as expected, with catch
blocks handling their respective exceptions and the finally
block executed regardless of whether there was an exception or not. The only difference are suppressed exceptions, which are explained at the end of this article.
Note: Since Java 9, it isn't necessary to declare the resources within the try-with-resources statement. We can do something like this instead:
BufferedWriter writer = new BufferedWriter(new FileWriter(fileName));
try (writer) {
writer.write(str); // do something with the file we've opened
}
catch(IOException e) {
// handle the exception
}
Multiple Resources
Another good aspect of try-with-resources is the ease of adding/removing resources we're using while having the assurance that they will be closed after we've finished.
If we wanted to work with several files, we'd open the files in the try()
statement and separate them with a semi-colon:
try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName));
Scanner scanner = new Scanner(System.in)) {
if (scanner.hasNextLine())
writer.write(scanner.nextLine());
}
catch(IOException e) {
// handle the exception
}
Java then takes care to call .close()
on all resources we've opened in try()
.
Note: They're closed in reverse declaration order, meaning that, in our example, scanner
will be closed before the writer
.
Supported Classes
All the resources declared in try()
must implement the AutoCloseable
interface. These are usually various types of writers, readers, sockets, output or input streams, etc. Anything you'd need to write resource.close()
after you've finished working with it.
This, of course, includes user-defined objects that implement the AutoClosable
interface. Though, you'll rarely come across a situation where you wish to write your own resources.
In case that happens, you need to implement the AutoCloseable
or Closeable
(only there to preserve backward compatibility, prefer AutoCloseable
) interface and override the .close()
method:
public class MyResource implements AutoCloseable {
@Override
public void close() throws Exception {
// close your resource in the appropriate way
}
}
Exception Handling
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.
As mentioned previously, try-with-resources works the same as try-catch-finally, except with one small addition. The addition is called suppressed exceptions. It is not necessary to understand suppressed exceptions in order to use try-with-resources, but reading about them might be useful for debugging when nothing else seems to work.
Imagine a situation:
- For some reason, an exception occurs in the try-with-resources block
- Java stops execution in the try-with-resources block, and calls
.close()
on all the resources declared intry()
- One of the
.close()
methods throws an exception - Which exception would the
catch
block "catch"?
Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!
This situation introduces us to the aforementioned suppressed exceptions. A suppressed exception is an exception which is in a way ignored when it's thrown within the implicit finally block of a try-with-resources block, in the case where an exception is thrown from the try
block as well.
Those exceptions are exceptions that occur in the .close()
methods and they are accessed differently than "regular" exceptions.
It's important to understand that the order of execution is:
- try-with-resources block
- implicit finally
- catch block (if an exception was thrown in [1] and/or [2])
- (explicit) finally
For example, here's a resource that does nothing but throw exceptions:
public static class MyResource implements AutoCloseable {
// method throws RuntimeException
public void doSomething() {
throw new RuntimeException("From the doSomething method");
}
// we'll override close so that it throws an exception in the implicit finally block of try-with-resources (when it attempts to close our resource)
@Override
public void close() throws Exception {
throw new ArithmeticException("I can throw whatever I want, you can't stop me.");
}
}
public static void main(String[] arguments) throws Exception {
// declare our resource in try
try (MyResource resource = new MyResource()) {
resource.doSomething();
}
catch (Exception e) {
System.out.println("Regular exception: " + e.toString());
// getting the array of suppressed exceptions, and its length
Throwable[] suppressedExceptions = e.getSuppressed();
int n = suppressedExceptions.length;
if (n > 0) {
System.out.println("We've found " + n + " suppressed exceptions:");
for (Throwable exception : suppressedExceptions) {
System.out.println(exception.toString());
}
}
}
}
This code is runnable. You can use it to experiment with using multiple MyResource
objects or seeing what happens when try-with-resources doesn't throw an exception, but .close()
does.
Hint: Suddenly, exceptions thrown while closing the resources start being important.
It's important to note that 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.
Another fact to note is that in a situation where the try
block doesn't throw an exception, and where there are multiple exceptions thrown while attempting to .close()
the resources used, the first exception will be propagated up the call stack, while the others will be suppressed.
As you can see in the code, you can get the list of all suppressed exceptions by accessing the Throwable
array returned by Throwable.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 Java tries to close the resources.
Conclusion
try-with-resources should be used instead of the regular try-catch-finally whenever possible. It's easy to forget to close one of your resources after coding for hours or forgetting to close a resource you've just added to your program after a random burst of inspiration.
The code is more readable, easier to change and maintain, and usually shorter.