This post looks at the syntax variations of the try/catch/finally blocks. For further details, see the excellent Java tutorial on the topic.

To start off, below is the basic syntax, with code surrounded by a try-block, and a NullPointerException caught by the catch-block. As can bee seen, the code will fail, since the variable “str” is null, leading to a NullPointerException. The Exception variable in the catch-block is by most common conventions simply “e”. It has a few convenience methods, including printStackTrace() which shows the call trace since the Exception was thrown. Although the print-out might look scary, it does provide useful information to the developer. Thus, keeping the full stack trace is helpful, typically in a detailed log-file. That is beyond the scope of this post.

    try {
      String str = null;
      str.toString();
    } catch (NullPointerException e) {
      e.printStackTrace();
    }

Exceptions are typed classes, and in the following example the catch-block will not be reached since the expected Exception is not the same or a sub-type of the one which is thrown: NullPointerException vs. ArithmeticException. Instead, the ArithmeticException will be thrown out of the method.

    try {
      int a = 1 / 0;
    } catch (NullPointerException e) {
      System.out.println("This will not trigger.");
    }
    System.out.println("Will not reach this point.");

To handle multiple exception types, there are three options: Declare multiple catch-blocks with different types, as seen in the first part below; or declare multiple Exceptions within the same catch statement, as in the second part. The latter syntax has been available since Java 7. Finally, it’s possible to catch multiple Exceptions by specifying a type higher up the class hierarchy, e.g. using Exception or Throwable.

    try {
      Integer a = null;
      int b = 1 / a;
    } catch (NullPointerException e) {
      System.out.println("NullPointerException");
    } catch (ArithmeticException e) {
      System.out.println("ArithmeticException");
    }

    try {
      Integer a = null;
      int b = 1 / a;
    } catch (NullPointerException | ArithmeticException e) {
      e.printStackTrace();
    }

In addition to the try and catch blocks, there is also a finally-block. It is executed at the end, regardless of whether there was an Exceptions thrown or not. This is useful for setting state or cleaning up, and a common example is closing an IO stream. However, as seen below, this can get crufty since we have to consider that the IO object might not have been opened in the first place, and that the close() method throws its own checked Exception.

    OutputStream out = null;
    try {
      out = new FileOutputStream("/dev/null");
      out.write(0);
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      if (out != null) {
        try {
          out.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }

To clean up the code above, the try-with-resources syntax was introduced in Java 7. It allows the try statement to take an extra block which is executed before its content. In addition, variables declared within this block will be closed at the end through the Closeable interface. This significantly reduces the foot-print of the code above.

    try (OutputStream out = new FileOutputStream("/dev/null")) {
      out.write(0);
    } catch (IOException e) {
      e.printStackTrace();
    }

A good example for multiple resources is the Socket example discussed previously. Here the Socket and both IO streams are closable resources handled by the try-block.

    try (Socket s = new Socket("google.com", 80);
         OutputStream out = s.getOutputStream();
         InputStream in = s.getInputStream()) {
      out.write(0);
      in.read();
    } catch (IOException e) {
      e.printStackTrace();
    }

Finally, a word on messaging and wrapping of Exceptions. As mentioned in the tutorial, it’s poor practice to throw RuntimeExceptions or simply wrap checked Exceptions, as seen below. However, regardless of where you stand in that debate, Exceptions can always be made more helpful and useful by clear messaging and relevant context. The wrapped RuntimeException below adds a more specific message and also includes the filename the IO stream operates on, since it might not be included in all types of IOExceptions. Furthermore, in the case of the File object, is is useful to use the getAbsolutePath() method. It forces the full path to the resolved and included. It really helps when debugging issues where the full path can be copy/pasted and confirmed.

    File file = new File("/dev/null");
    try (OutputStream out = new FileOutputStream(file)) {
      out.write(0);
    } catch (IOException e) {
      throw new RuntimeException("Could not open or read file " + file.getAbsolutePath(), e);
    }

Here is the full listing with all examples as tests.

TryCatchTest.java
GitHub Raw
/* Copyright rememberjava.com. Licensed under GPL 3. See http://rememberjava.com/license */
package com.rememberjava.basics;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

import org.junit.Test;

public class TryCatchTest {

  // The second try-block will throw an uncaught ArithmeticException because of
  // the divide by 0.
  @Test(expected = ArithmeticException.class)
  public void basic() {
    try {
      String str = null;
      str.toString();
    } catch (NullPointerException e) {
      e.printStackTrace();
    }

    try {
      int a = 1 / 0;
    } catch (NullPointerException e) {
      System.out.println("This will not trigger.");
    }
    System.out.println("Will not reach this point.");
  }

  @Test
  public void multi() {
    try {
      Integer a = null;
      int b = 1 / a;
    } catch (NullPointerException e) {
      System.out.println("NullPointerException");
    } catch (ArithmeticException e) {
      System.out.println("ArithmeticException");
    }

    try {
      Integer a = null;
      int b = 1 / a;
    } catch (NullPointerException | ArithmeticException e) {
      e.printStackTrace();
    }
  }

  @Test
  public void testFinally() {
    OutputStream out = null;
    try {
      out = new FileOutputStream("/dev/null");
      out.write(0);
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      if (out != null) {
        try {
          out.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
  }

  @Test
  public void tryWith() {
    try (OutputStream out = new FileOutputStream("/dev/null")) {
      out.write(0);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  @Test
  public void tryWithMulti() {
    try (Socket s = new Socket("google.com", 80);
         OutputStream out = s.getOutputStream();
         InputStream in = s.getInputStream()) {
      out.write(0);
      in.read();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  @Test
  public void message() {
    File file = new File("/dev/null");
    try (OutputStream out = new FileOutputStream(file)) {
      out.write(0);
    } catch (IOException e) {
      throw new RuntimeException("Could not open or read file " + file.getAbsolutePath(), e);
    }
  }
}