This course has already ended.

Exception handling in Java, part 2

This section will briefly introduce Java’s exception class hierarchy and discuss how to implement a custom exception class and cause (throw, or raise) an exception.

Earlier examples have involved e.g. the exception types IOException and NumberFormatException. To be more precise, these are classes that the Java class library uses for exception handling. All Java’s error handling classes have a superclass named Throwable, and in fact the try-catch statement can only catch exceptions of type Throwable. That class has two direct subclasses, Exception and Error, that also act as general superclasses. Exception is designated as the superclass of all classes that are used in typical exception handling. Error is related to very abnormal problems where usual exception handling would not be useful.

All usual exception classes of the Java class library are subclasses of Exception. The class has e.g. the following public constructors and member functions:

  • Constructors Exception() and Exception(String msg). The latter stores the message msg into the exception object, and the former works in same manner as Exception(null), that is, it stores null as the message.

    • The message can be used to convey some additional information about what caused the exception.

  • Member function getMessage() that returns the message stored in the exception object.

  • Member function printStackTrace() that prints to the standard output information about the active function call chain and the source file code line where the exception originated from.

It would be possible to use just the superclass Exception for exception handling. Its subclasses, such as IOException, usually do not add any new functionality to it. The motive for using different exception classes is not about functionality of the exception classes themselves. The main benefits are (1) the ability to define separate exception handlers for different types of problems and (2) the fact that the exception class names, mentioned e.g. in exception specifications or catch blocks, on their own convey some information about what type of a problem they correspond to.

It is quite simple to define a custom exception class: extend the class Exception and, if required, implement a constructor that passes its parameters to the superclass.

One can raise an exception explicitly in Java by using a throw statement of form throw exceptionObject. The mechanism is very similar with C++. The main difference is that Java allows to throw only exceptions of type Throwable whereas C++ permist pretty much all types. The thrown exception object is often created with the new operator in the throw statement, e.g. as throw new IOException("Errod reading line 3").

Below is an example of defining and using a custom exception class DivideByZeroException. The classes should be placed into separate files.

// A very simple exception class DivideByZeroException.
public class DivideByZeroException extends Exception {
  public DivideByZeroException(String msg) {
    super(msg);
  }
}

public class DivideByZeroTest {
  public static double divide(int numerator, int denominator)
        throws DivideByZeroException { // Declare what type of exceptions may be thrown.
    if(denominator == 0) { // Are we trying to divide by zero?
      // The message can describe more details about the problem.
      throw new DivideByZeroException(String.format("Trying to compute %d/%d!",
              numerator, denominator));
    }
    return (double) numerator / denominator;
  }

  public static void main(String[] args) {
    int num = 7;
    for(int i = -2; i <= 2; i++) {
      try {
        System.out.format("%d/%d = %.3f%n", num, i, divide(num, i));
      }
      catch(DivideByZeroException e) {
        System.out.println(e);  // Prints (at least usually) the exception type and message.
      }
    }
  }
}

The example program outputs:

7/-2 = -3.500
7/-1 = -7.000
DivideByZeroException: Trying to compute 7/0!
7/1 = 7.000
7/2 = 3.500

Because Exception is a superclass of all (typical) exception classes, any type of exception can be caught by a catch block using the header catch(Exception e). Such so-called “catch all” blocks should be used cautiously due to how they hide information about the more detailed exception type. Catch all blocks do have their uses, even quite often, whenever we wish to catch all feasible types of exceptions. Recall that Java forces us to acknowledge only “checked” exceptions, and hence e.g. the Java compiler or IDE’s usually do not direct the programmer to handle unchecked exceptions. Since it is not very easy to deduce all possible unchecked exception types from the code, it might be convenient to use a catch all block; it should ensure that we do not omit any possibly occurring exception type. It is also good to note that being categorized as “unchecked” does not mean that an exception is harmless or rare. E.g. NullPointerException, which is caused by trying to refer to a class member via a null reference, is a very common unchecked exception.

When an exception occurs, the program execution jumps to the nearest catch block that is compatible with the occurred exception type (or if no such block is found, the program will be terminated due to an unhandled exception). Here it is good to note that if a try block is followed by several compatible catch blocks, the first of them will be selected. Therefore if you use a catch all block, it always needs to be that last catch block. Otherwise the following catch blocks would be useless as the preceding catch all block would catch all exceptions before them. Below is an example of a try-catch statement whose last catch block would never be reached.

void doSomething(String s) {
  try {
    Integer.parseInt(s);
  }
  catch(Exception e) {
    System.out.println("An exception was caught: " + e);
  }
  catch(NumberFormatException e) {
    System.out.println("I would never get to print this exception: " + e);
  }
}

The Java compiler actually enforces the preceding rule. If you try to compile this example, the Java compiler will give more or less the following kind of an error message:

SomeJavaFile.java:xy: error: exception NumberFormatException has already been caught
  catch(NumberFormatException e) {
  ^

When an exception occurs during runtime, the execution of the program

Different exception classes are used e.g.

catch(Exception e)

Posting submission...