JavaRush /Java Blog /Random EN /Coffee break #203. How to handle exceptions using the try...

Coffee break #203. How to handle exceptions using the try-with-resource statement

Published in the Random EN group
Source: Medium This guide describes the benefits of try-with-resource over try-catch-finally. You'll also learn under what conditions suppressed exceptions are thrown and how to use try-with-resources with multiple resources. The try with resourcesCoffee break #203.  How to handle exceptions using try-with-resource statement - 1 construct , also known as try-with-resources , is an exception-handling mechanism in Java that can automatically close resources such as a Java InputStream or JDBC Connection when it has finished working with them. In 2011, Oracle added try with resources to the Java language syntax to ensure that objects such as network sockets, database connections, and file and folder links are closed gracefully after they are used. Failure to close these resources after a developer opens a handle to them can result in memory leaks, triggering preventable garbage collection routines, and CPU overhead on the server.

Before Java 7

In Java, if you use resources such as input/output streams, you always need to close them after use. Since they can also throw exceptions, they must be contained in a try-catch block . Closing must occur in a finally block . At least this was the case until Java 7. But it has several disadvantages:
  • You need to check if your resource is null before closing it.
  • The closure itself can throw exceptions, so you have to have another try-catch in your finally block .
  • Programmers tend to forget to close their resources.

How to use try-with-resource statement?

This operator was originally introduced in Java 7 and the idea was that developers no longer need to worry about managing the resources they use in a try-catch-finally block . This is achieved by eliminating the need for finally blocks , which in practice were only used by developers to close resources. In Java, a try-with-resources statement is a try statement that declares one or more resources. A resource is an object that must be closed after the program terminates. When code execution leaves the try-with-resources block , then any resource opened in the try-with-resources block is automatically closed, regardless of whether any exceptions are thrown either within the try-with-resources block or while attempting to close resources. To use the Java try-with-resources language feature , the following rules apply:
  • All objects controlled by the try-with-resources statement must implement the AutoCloseable interface .
  • Multiple AutoCloseable objects can be created in a try-with-resources block .
  • Objects declared in a try-with-resources statement are executed in a try block , but not in catch or finally blocks .
  • The close() method of objects declared in a try-with-resources block is called regardless of whether an exception is thrown at runtime.
  • If an exception is thrown in the close() method , it may be classified as a suppressed exception.
Catch and finally blocks can still be used in a try-with-resource block , and they will work the same as in a regular try block . A resource referenced by an AutoCloseable object will always be closed if try-with-resource is used . This eliminates potential memory leaks, usually caused by poor resource allocation.

Syntax

try(declare resources here) {
    // использовать ресурсы
}
catch(FileNotFoundException e) {
    // обработка исключений
}

Practical use of try-with-resource

To close automatically, the resource must be declared and initialized inside try :
try (PrintWriter writer = new PrintWriter(new File("test.txt"))) {
    writer.println("Hello World");
}

Replacing try-catch-finally with try-with-resources

A simple and obvious way to use try-with-resources functionality is to replace the traditional and verbose try-catch-finally block . Let's compare the following code examples. The first example is a typical try-catch-finally block :
Scanner scanner = null;
try {
    scanner = new Scanner(new File("test.txt"));
    while (scanner.hasNext()) {
        System.out.println(scanner.nextLine());
    }
} catch (FileNotFoundException e) {
    e.printStackTrace();
} finally {
    if (scanner != null) {
        scanner.close();
    }
}
And here is a new super concise solution using try-with-resources :
try (Scanner scanner = new Scanner(new File("test.txt"))) {
    while (scanner.hasNext()) {
        System.out.println(scanner.nextLine());
    }
} catch (FileNotFoundException fnfe) {
    fnfe.printStackTrace();
}

Difference between try and try-with-resource

When it comes to exceptions, there is a difference between a try-catch-finally block and a try-with-resources block . The exception is thrown in both the try and finally block , however the method returns the exception thrown only in the finally block . For try-with-resources , if an exception occurs in the try block and in the try-with-resources statement, the method returns the exception thrown in the try block . Exceptions thrown by the try-with-resources block are suppressed, that is, we can say that the try-with-resources block throws suppressed exceptions.

Why should I use the try-with-resource statement?

The try-with-resources statement ensures that each resource is closed at the end of the statement. If we do not close resources, this may lead to resource leakage and the program may exhaust the resources available to it. This happens when you use a try-catch-finally block . Before Java SE 7, you could use a finally block to ensure that the resource would be closed regardless of whether the try statement exited normally or abruptly. The following example uses a finally block instead of a try-with-resources statement :
static String readFirstLineFromFileWithFinallyBlock(String path) throws IOException {

    FileReader fr = new FileReader(path);
    BufferedReader br = new BufferedReader(fr);
    try {
        return br.readLine();
    } finally {
        br.close();
        fr.close();
    }
}
There may be a resource leak in this example. The program must do more than rely on the garbage collector to free the resource's memory after it has finished using it. The program must also return the resource back to the operating system, usually by calling the resource's close method. However, if the program does not do this before the garbage collector returns the resource, then the information needed to free the resource will be lost. A resource is leaked that the operating system still considers in use. In the example shown above, if the readLine method throws an exception and the br.close() statement in the finally block throws an exception, then the FileReader has leaked . This is why you're better off using a try-with-resources statement instead of a finally block to close your program's resources.

One more example

The following example reads the first line from a file. FileReader and BufferedReader instances are used to read data . These are resources that need to be closed after the program exits.
import java.io.*;
class Main {
  public static void main(String[] args) {
    String line;
    try(BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
      while ((line = br.readLine()) != null) {
        System.out.println("Line =>"+line);
      }
    } catch (IOException e) {
      System.out.println("IOException in try block =>" + e.getMessage());
    }
  }
}
As you can see, the resource declared in the try-with-resources statement is a BufferedReader . The declaration statements for this resource appear in parentheses immediately after the try keyword . The BufferedReader classes in Java SE 7 and later implement the java.lang.AutoCloseable interface . Because BufferedReader instances are declared in a try-with-resource statement , they will be closed regardless of whether the try statement exits normally or abruptly (if the BufferedReader.readLine() method throws an IOException ).

Suppressed exceptions

If a try block throws an exception, and one or more exceptions appear in the try-with-resources block , then the exceptions thrown by the try-with-resources block are suppressed. In other words, we can say that the exceptions thrown by try-with-resources are suppressed exceptions. You can catch these exceptions using the getSuppress() method of the Throwable class . In the example shown earlier, exceptions were thrown by the try-with-resources statement , under the following conditions:
  • The file test.txt was not found.
  • Closing the BufferedReader object .
An exception can also be thrown from a try block , since reading a file can fail for many reasons at any time. If exceptions are thrown from both a try block and a try-with-resources statement , then in the first case an exception is thrown, and in the second case the exception is suppressed.

Getting suppressed exceptions

In Java 7 and later, suppressed exceptions can be obtained by calling the Throwable.getSuppressed() method from an exception thrown by a try block . getSuppress() returns an array containing all exceptions that were suppressed by the try-with-resources statement . If no exceptions are suppressed or suppression is disabled, an empty array is returned. Here is an example of receiving suppressed exceptions in a catch block :
catch(IOException e) {
  System.out.println("Thrown exception=>" + e.getMessage());
  Throwable[] suppressedExceptions = e.getSuppressed();
  for (int i=0; i" + suppressedExceptions[i]);
  }
}

Benefits of using try-with-resources

  • Readable and easy to write code.
  • Automatic resource management.
  • The number of lines of code has been reduced.
  • When multiple resources are opened in try-with-resources , they are closed in reverse order to avoid dependency issues.
And of course, a finally block is no longer required to close a resource . Previously, before Java 7, we had to use a finally block to ensure that a resource is closed to avoid resource leaks. Here is a program similar to the very first example. In this program, we used a finally block to close resources.
import java.io.*;
class Main {
  public static void main(String[] args) {
    BufferedReader br = null;
    String line;
    try {
      System.out.println("Entering try block");
      br = new BufferedReader(new FileReader("test.txt"));
      while ((line = br.readLine()) != null) {
        System.out.println("Line =>"+line);
      }
    } catch (IOException e) {
      System.out.println("IOException in try block =>" + e.getMessage());
    } finally {
      System.out.println("Entering finally block");
      try {
        if (br != null) {
          br.close();
        }
      } catch (IOException e) {
        System.out.println("IOException in finally block =>"+e.getMessage());
      }
    }
  }
}
Conclusion:
Entering try block Line =>line from test.txt file Entering finally block
As you can see from the example above, using a finally block to clean up resources adds complexity to the code. Notice the try...catch block in the finally block ? This is because an IOException can also occur when closing a BufferedReader instance inside a finally block , so it is also caught and handled. The try-with-resources statement performs automatic resource management. We don't need to explicitly close resources because the JVM closes them automatically. This makes the code more readable and easier to write.

Try-with-resources with multiple resources

We can declare multiple resources in a try-with-resources block by simply separating them with a semicolon:
try (Scanner scanner = new Scanner(new File("testRead.txt"));
    PrintWriter writer = new PrintWriter(new File("testWrite.txt"))) {
    while (scanner.hasNext()) {
	writer.print(scanner.nextLine());
    }
}

Java 9 - Effectively Final Variables

Before Java 9, we could only use fresh variables inside a try-with-resources block :
try (Scanner scanner = new Scanner(new File("testRead.txt"));
    PrintWriter writer = new PrintWriter(new File("testWrite.txt"))) {
    // omitted
}
Note that this is quite verbose when declaring multiple resources. Since Java 9 (JEP 213 update), we can use final or even effectively final variables inside a try-with-resources block :
final Scanner scanner = new Scanner(new File("testRead.txt"));
PrintWriter writer = new PrintWriter(new File("testWrite.txt"))
try (scanner;writer) {
    // omitted
}
Simply put, a variable is effectively final if it is not modified after the first assignment, even if it is not explicitly marked final . As shown above, the scanner variable is explicitly declared final so we can use it with a try-with-resources block . Although the writer variable is not explicitly final , it does not change after the first assignment. So we can also use the writer variable . I hope today you got a better understanding of how to handle exceptions using the try-with-resource statement . Happy learning!
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION