January 5, 2022

Java try-with-resources With Examples

Java 7 onward a new feature try-with-resources is available for automatic resource management. With this feature, try-with-resources in Java, one or more resources are declared with the try statement itself. The try-with-resources statement ensures that the declared resources are closed automatically at the end.

Here resource is an object that must be closed after the program is finished with it. For example, an opened file stream, DB connection etc.

Prior to try-with-resources

Prior to Java 7 try-with-resources to close a resource you had to do two things-

  1. Call close() method explicitly to close the opened resource.
  2. Call close() method in finally block to ensure that a resource is closed regardless of whether the try statement completes normally or abruptly.
Example code with finally
public class FinallyDemo {
  public static void main(String[] args) {
    BufferedReader br = null;
    try {
      br = new BufferedReader(new FileReader("D:\\test.txt"));
      System.out.println(br.readLine());
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      try {
        if (br != null){
         System.out.println("Closing the file");
         br.close();
        }
          
      } catch (IOException ex) {
        ex.printStackTrace();
      }
    }
  }
}
Notice pain points here-
  • You are writing a whole block of code to close a resource, failing to do that will mean resource kept opened resulting in slow performance and memory leaks.
  • You are forced to use a try-catch block again while closing the resource.

Using try-with-resources in Java

The try-with-resources gives you an option to declare the resource with try statement. It is ensured by try-with-resources that a resource is closed regardless of whether the try statement completes normally or abruptly.

Try-with-resources Java example code
public class FinallyDemo {
  public static void main(String[] args) {	
    try (BufferedReader br = new BufferedReader(new FileReader("D:\\test.txt"))){
      System.out.println(br.readLine());
    } catch (IOException e) {
      e.printStackTrace();
    } 
  }
}

Now the resource is declared within parentheses immediately after the try keyword, finally block is not required. BufferedReader object will be closed automatically now. You can see how much boiler plate code is reduced because of try-with-resources.

AutoCloseable interface

Now the question is how resources are closed automatically using try-with-resources in Java. It is because of the interface java.lang.AutoCloseable introduced in Java 7. Any object that implements java.lang.AutoCloseable interface can be used as a resource with try-with-resource. If resource used with try-with-resource doesn't implement AutoCloseable interface that will result in compile time error.

Java example
public class TryResource {
  public static void main(String[] args) {
    try (MyAutoCloseResource myResource = new MyAutoCloseResource()) {
      System.out.println("MyAutoCloseResource created in try-with-resources");
    }catch(Exception ex){
      ex.printStackTrace();
    }
  }
	
  // Class implementing AutoCoseable
  static class MyAutoCloseResource implements AutoCloseable { 
    @Override
    public void close() throws Exception {
      System.out.println("Closing MyAutoCloseResource");
    }
  }
}
Output
MyAutoCloseResource created in try-with-resources
Closing MyAutoCloseResource

You can see that the close() method of the resource is called automatically.

try-with-resources Java 9 enhancement

If you see the example given above for using try-with-resources one thing to notice is that the resource that has to be closed automatically is created with in the try statement of a try-with-resources that was one of the restriction while using try-with-resources prior to Java 9.

try (BufferedReader br = new BufferedReader(new FileReader("D:\\test.txt"))){
    System.out.println(br.readLine());
}

Even if resource is already declared it has to be rereferenced with in the try-with-resources so that it is automatically closed.

public class TryResources {
  public static void main(String[] args) throws IOException{    
    BufferedReader br = new BufferedReader(new FileReader("D:\\test.txt"));
    try (BufferedReader refbr = br){
      System.out.println(refbr.readLine());
    } catch (IOException e) {
      e.printStackTrace();
    } 
  }
}

Java 9 onward this restriction is not there anymore, now you can declare the resource outside and use the same reference with in the try statement. Only restriction is that the referenced variable with in the try-with-resource construct must be final or effectively final.

public class TryResources {
  public static void main(String[] args) throws IOException{    
    BufferedReader br = new BufferedReader(new FileReader("D:\\test.txt"));
    // same reference used 
    try (br){
      System.out.println(br.readLine());
    } catch (IOException e) {
      e.printStackTrace();
    } 
  }
}

Declaring multiple resources in a try-with-resources statement

You can declare multiple resources with try-with-resource statement, resources are separated by semicolon.

As example-
try (ZipFile zf = new ZipFile(zipFileName); BufferedWriter writer = Files.newBufferedWriter(outputFilePath, charset))

Resources are closed in the opposite order of their creation so close() method of BufferedWriter will be called first then the close() method of ZipFile will be called.

Suppressed exception with try-with-resource

If an exception is thrown from a try block and an exception is thrown from a try-with-resource statement too then the exception thrown by try-with-resource statement is suppressed and the exception thrown by the try block is returned.

This is in direct contrast to what happens in a case of finally block. Where the exception thrown by finally block is returned and the exception thrown by try block is suppressed in case both of these blocks throw an exception.

Let’s try to clear it with an example.

First we’ll see a code with finally block where both try and finally blocks are throwing exception. In the code there is also a custom implementation MyAutoCloseResource of AutoCloseable interface. You can see exception is thrown explicitly from the close() method. From the try block too an exception is thrown.

public class TryResource {
  public static void main(String[] args) {
    TryResource tr = new TryResource();
    try {
      tr.Test();
    } catch (Exception e) {
      System.out.println("Exception caught -- " + e.getMessage());
    }
  }
	
  private void Test() throws Exception{
    MyAutoCloseResource myResource = null;
    try {
      myResource  = new MyAutoCloseResource();
      throw new Exception("Exception in class Test");
       
    }finally{
      if(myResource != null){
      myResource.close();			
      }
    }
  }
  // Class implementing AutoCoseable
  class MyAutoCloseResource implements AutoCloseable {	  
    @Override
    public void close() throws Exception {
      System.out.println("Closing MyAutoCloseResource");
      throw new Exception("Exception while closing resource");
    }
  }
}
Output
Closing MyAutoCloseResource
Exception caught -- Exception while closing resource

From the output you can see that the exception thrown in the finally block is the one that is returned.

Now let’s see the same example with try-with-resource statement.

public class TryResource {
  public static void main(String[] args) {
    TryResource tr = new TryResource();
    try {
      tr.Test();
    } catch (Exception e) {
      System.out.println("Exception caught -- " + e.getMessage());
      System.out.println("Suppressed Exception  -- " + e.getSuppressed()[0]);
    }
  }
	
  private void Test() throws Exception{
    try(MyAutoCloseResource myResource = new MyAutoCloseResource()) {		
      throw new Exception("Exception in class Test");         
    }
  }
  // Class implementing AutoCoseable
  class MyAutoCloseResource implements AutoCloseable {
    @Override
    public void close() throws Exception {
      System.out.println("Closing MyAutoCloseResource");
      throw new Exception("Exception while closing resource");
    }
  }
}
Output
Closing MyAutoCloseResource
Exception caught -- Exception in class Test
Suppressed Exception -- java.lang.Exception: Exception while closing resource

From the output you can see that the exception thrown in the try block is the one that is returned.

To retrieve these suppressed exceptions you can call the Throwable.getSuppressed method from the exception thrown by the try block as shown in the Java code.

That's all for the topic Java try-with-resources With Examples. If something is missing or you have something to share about the topic please write a comment.


You may also like

No comments:

Post a Comment