Skip to main content
June 9, 2015

Exception Handling with Try with Resources Statement in Java 7

In a previous article, we saw how to avoid nested try-catch-finally blocks in Java. It was pointed out to me that Java 7 (and beyond) has a new try-with-resources construct. It can take multiple resources and ensure that each resource is closed at the end of the statement. I think this new construct is a good way to avoid some of the issues with try-catch-finally statement. In this article, we will have a look at how try-with-resources can avoid nested blocks and help correctly close multiple resources.

Try-with-resources Example

Consider the following snippet of code. We are trying to copy the contents of the input file into the output file using I/O streams.

publicvoid copy() throws FileNotFoundException
{
  InputStream input = new FileInputStream("in.txt");
  OutputStream output = new FileOutputStream("out.txt");
  byte[] buffer = newbyte[1024];
  try {
    try {
      while (-1 != input.read(buffer)) {
        output.write(buffer);
      }
    } catch (IOException ex) {
      Logger.getLogger(Easy.class.getName()).log(Level.SEVERE, null, ex);
    } finally {
      input.close();
      output.close();
    }
  } catch (IOException ex) {
    Logger.getLogger(Easy.class.getName()).log(Level.SEVERE, null, ex);
  }
}

In this example, there are two kinds of exceptions that are possible - FileNotFoundException if the file with the given name does not exist and IOException while reading from the file (and writing). For now, to keep things simple, we throw the FileNotFoundException and handle the IOException by catching and logging it. In the finally block we close the I/O streams. The close() method itself can throw an exception, that is handled subsequently. Now, the same example can also be written using try-with-resources as follows:

publicvoid copy(String content) throws FileNotFoundException
{
  try(InputStream input = new FileInputStream("in.txt");
  OutputStream output = new FileOutputStream("out.txt");) {
    byte[] buffer = newbyte[1024];
    while (-1 != input.read(buffer)) {
        output.write(buffer);
    }
  } catch (IOException ex) {
    Logger.getLogger(Easy.class.getName()).log(Level.SEVERE, null, ex);
  }
}

The try-with-resources version is much shorter and concise. There is no need call the close() method separately in the finally block. When the execution leaves the try block the resources are closed automatically. The resource must implement the AutoClosable interface to work with the try-with-resources construct. The interface for AutoClosable has only one method as shown below:

publicinterfaceAutoClosable{
    publicvoidclose()throws Exception;
}

Any class that implements this interface can use the try-with-resources construct to automatically close the resources. You may be wondering what happened to the exception that was thrown while calling the close() method. To answer that lets take a look at the bytecode that is generated by these two versions of copy method.

Try-with-resources Under the Hood

In order to explore the bytecode of the class files generated by the Java compiler we can use the javap utility. It is a command line tool that is bundled with the JDK and it helps in printing user readable bytecode information from class files.

javap -c example.class

After running it on the class file compiled with our first version of copy() method we get the following bytecode (to focus on the exceptions I have removed some lines, the full output is available here).

public void copy() throws java.io.FileNotFoundException;
    Code:
      29: invokevirtual #33// Method java/io/InputStream.read:([B)I37: invokevirtual #34// Method java/io/OutputStream.write:([B)V40: goto2643: aload_1
      44: invokevirtual #35// Method java/io/InputStream.close:()V47: aload_2
      48: invokevirtual #36// Method java/io/OutputStream.close:()V51: goto9768: aload         473: aload_1
      74: invokevirtual #35// Method java/io/InputStream.close:()V77: aload_2
      78: invokevirtual #36// Method java/io/OutputStream.close:()V81: goto9784: astore        586: aload_1
      87: invokevirtual #35// Method java/io/InputStream.close:()V90: aload_2
      91: invokevirtual #36// Method java/io/OutputStream.close:()V94: aload         5119: return
    Exception table:
       from    to  target type264354Class java/io/IOException
          264384   any
          547384   any
          848684   any
          2697100Class java/io/IOException

As we can see above, the nested try-catch-finally block in the copy() method generates code for three pairs (InputStream and OutputStream) of calls to close() method. The first two calls are due to the fact that we are closing the stream in the finally block. The close() method is to be executed regardless of the fact whether the try block throws an exception or not. The third call is from the nested try-catch. If the finally block itself has an exception then the control needs to pass to the outer catch rather than the return statement. Thus, the first version generates 3 calls to properly close() the resource. Before we consider the bytecode generated by the try-with-resources version, note that the total number of bytecode instructions generated in this case is 119 and the exception table has 5 entries in it.

For the try-with-resources version of the copy() method we have the following bytecode (full output is available here):

public void copy(java.lang.String) throws java.io.FileNotFoundException;
    Code:
      37: invokevirtual #33                 // Method java/io/InputStream.read:([B)I
      47: invokevirtual #34                 // Method java/io/OutputStream.write:([B)V
      60: ifnull        83
      63: aload         4
      65: invokevirtual #36                 // Method java/io/OutputStream.close:()V
      77: invokevirtual #44                 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
      85: invokevirtual #36                 // Method java/io/OutputStream.close:()V
     109: ifnull        132
     112: aload         4
     114: invokevirtual #36                 // Method java/io/OutputStream.close:()V
     126: invokevirtual #44                 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
     134: invokevirtual #36                 // Method java/io/OutputStream.close:()V
     145: ifnull        166
     148: aload_2
     149: invokevirtual #35                 // Method java/io/InputStream.close:()V
     160: invokevirtual #44                 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
     167: invokevirtual #35                 // Method java/io/InputStream.close:()V
     188: ifnull        209
     191: aload_2
     192: invokevirtual #35                 // Method java/io/InputStream.close:()V
     203: invokevirtual #44                 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
     210: invokevirtual #35                 // Method java/io/InputStream.close:()V
     236: return
    Exception table:
       from    to  target type
          63  68  71   Class java/lang/Throwable
          26  53  91   Class java/lang/Throwable
          26  53  100   any
         112  117  120   Class java/lang/Throwable
          91  102  100   any
         148  152  155   Class java/lang/Throwable
          12  140  173   Class java/lang/Throwable
          12  140  181   any
         191  195  198   Class java/lang/Throwable
         173  183  181   any
           0  216  219   Class java/io/IOException

From the output it is clear that the version with the new try-with-resources generates a class file with almost twice as much bytecode (236 v/s 119) and the exception table is also over twice the size (11 v/s 5). Now, if we look at the number of calls to the close() method that are generated we see that there are 4 pairs of calls. The bytecode also shows what happens to the exception generated by the close() method. It is suppressed (as shown on line 77, 126, 160 and 203 above). For more details on suppressed exceptions you can check out the Java docs here.

So, in fact using try-with-resources generates a more nested control flow. The reason is that, in the first case, we combined the close() method call for both input and output in the same finally block. The compiler cannot make this choice and handles the most general case, i.e. once for each resource declared in the try statement. On the other hand, the benefit for the developer is obvious since she doesn't need to worry about closing the I/O stream. In conclusion, even though the generated bytecode is much larger, the source code of the program is a lot more readable and concise. Try-with-resources is a good abstraction for developers to adopt and avoids some of the problems that can lead to nested blocks of try-catch statements.

What do you think about the exception handling mechanisms in Java and how often do you use try-with-resources? Let us know in comments or reach out to me on twitter.

Dr. Asankhaya Sharma is the Director of Software Engineering at Veracode. Asankhaya is a cyber security expert and technology leader with over a decade of experience in creating security products for industry, academia and open-source community. He is passionate about building high performing teams and taking innovative products to market. He is also an Adjunct Professor at the Singapore Institute of Technology.

Love to learn about Application Security?

Get all the latest news, tips and articles delivered right to your inbox.