Exception handling in Java

We briefly review Java’s try-catch statement for exception handling.

It was mentioned earlier that e.g. many input operations require us to somehow acknowledge possible exceptions. Consider the following simple program:

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class NameLength {
  public static void main(String[] args) {
    var input = new BufferedReader(new InputStreamReader(System.in));
    System.out.print("Enter your name: ");
    String name = input.readLine();
    System.out.format("Your name \"%s\" has %d characters%n",
                                          name, name.length());
  }
}

Attempt to compile the code would result in a compiler error similar to:

NameLength.java:8: error: unreported exception IOException; must be caught or declared to be thrown
  String name = input.readLine();

Earlier we simply ignored exceptions: instead of handling them, we added to the function header an exception specification that lists the exception types that may propagate out from the function (because we do not handle them). A second option would be to handle (or “catch”) the exceptions ourselves. This can be done in Java by using a very similar so-called try-catch statement as e.g. in C++ or Python. Java’s try-catch statement has the following structure:

// Enclose the code that may cause an exception into a try block.
try {
  // A code block that may cause exceptions either directly in the code block itself or
  // indirectly by propagating exceptions out from some function called in this block.
}
// A try block is followed by catch blocks that can handle ("catch") exceptions that occur
// in the preceding try block. Here we give as an example two catch blocks that handle two
// types of exceptions: ExceptionTypeA and ExceptionTypeB.
catch(ExceptionTypeA parameter) {
  // A code block into which the program execution will directly jump if an exception of
  // type ExceptionTypeA occurs in the preceding try block.
}
catch(ExceptionTypeB parameter) {
  // A code block into which the program execution will directly jump if an exception of
  // type ExceptionTypeB occurs in the preceding try block.
}
// Catch blocks can be followed by a single finally block that will be executed always,
// ie. regardless of whether the try block caused an exception and whether any corresponding
// catch block was executed. If no exception occurs, the finally lock will be executed
// immediately after the try block. If an exception does occur, the finally block will be
// executed after a possible matching catch block.
finally {
  // A code block that will always be executed regardless of exceptions.
}

The catch blocks receive an object of the handled exception type as a parameter. These exception parameters may convey some information about what kind of an exception occurred.

If the same catch block is suitable for handling several exception types, Java allows a catch block parameter to list several exception types separated by vertical bar characters ‘|’. Note that a catch block will have only a single parameter also in this case. An example:

try {
  // A code block that might cause an exception.
}
catch(ExceptionTypeA | ExceptionTypeB parameter) {
  // A code block into which the program execution jumps if the preceding
  // try block causes an exception of type ExceptionTypeA or ExceptionTypeB.
}

The code shown in the beginning of this section will compile if we add to it a try-catch statement that handles exceptions of type IOException. The example below simply prints out information about the caught exception:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

public class NameLength {
  public static void main(String[] args) {
    var input = new BufferedReader(new InputStreamReader(System.in));
    System.out.print("Enter your name: ");
    try { // An IOException exception may occur within this code block.
      String name = input.readLine();
      System.out.format("Your name \"%s\" has %d characters%n",
                                          name, name.length());
    }
    catch(IOException e) {  // A handler for IOException exception.
      System.out.println("An exception occurred: " + e);
    }
  }
}

We will dicuss some further technical aspects of try-catch statements later.

One thing to note is that exceptions are not used only for indicating serious errors. Exceptions provide a general program flow control mechanism that allows code execution to jump from one point (where an exception occurs, or is “thrown”) directly to another point (into a catch block that handles the exception). Although many exceptions do arise from serious errors (e.g. the program executes some illegal operation, runs out of memory, etcs.), exceptions are also used in fairly “routine” manner to e.g. convey information about the result of a function call. For example the string parsing functions of the number wrapper classes (Integer, Double etc.) throw an exception of type NumberFormatException if the parsed string does not represent a legal number. Hence e.g. the fairly typical task of checking whether a string represents a number can be solved with exception handling. The example program given below prints out for each command line parameter whether the parameter represents a decimal number (that could be converted into a double). The working principle is simple: place the call of the string parsing function Double.parseDouble into a try block and define after it a catch block for NumberFormatException exceptions.

public class NumberParameters {
  public static void main(String[] args) {
    for(String arg : args) {
      boolean isNumber = false;
      try { // This code block can cause a NumberFormatException exception.
        Double.parseDouble(arg);  // Try to convert "arg" into a double.
        isNumber = true;  // This line will not be executed if an exception occurs!
      }
      catch(NumberFormatException e) {
      } // Just catch the exception without doing anything: isNumber is still false.
      System.out.format("The parameter \"%s\" %s a number.%n",
                                            arg, (isNumber ? "is" : "is NOT"));
    }
  }
}

Executing this program as java NumberParameters one two 3.0 4 five 6 will output:

The parameter "one" is NOT a number.
The parameter "two" is NOT a number.
The parameter "3.0" is a number.
The parameter "4" is a number.
The parameter "five" is NOT a number.
The parameter "6" is a number.

Class library documentation usually describes what kind of exceptions, and under what circumstances, facilities provided by that library throw.

In Java code that can potentionally throw an exception is surrounded by a try{} block followed by

finally block:

catch block gets as a parameter