Exceptions

Exceptions #

Programming languages may significantly differ in the way they support errors messages.

Java relies on a mechanism called exceptions. Several other languages use exceptions, for instance C++, C#, PHP, Python or Ruby. However, the purpose of exceptions may vary from one language to another.

An exception is an object or data structure that stores information about an exceptional condition encountered during the execution of a program.

in Java #

In Java, an exception is an object, instance of the native class Exception.

All exception types (e.g. IOException) are subclasses of Exception.

By convention, a class name for an exception:

  • ends with the word Exception, and
  • provides information about the cause of the exception.

Stack trace #

By default, when an exception is thrown, the program interrupts its execution, and the current call stack is sent to an output stream called System.error. This output is called the stack trace (because it allows tracing back the method calls that led to the exception being thrown).

For instance, consider the following class:

1
2
3
4
5
6
public class MyClass{

    void myMethod(){
        Integer.parseInt("clearlyNotANumber");
    }
}

When myMethod is executed, the following stack trace is printed to System.error:

java.lang.NumberFormatException: For input string: "clearlyNotANumber"

at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
at java.base/java.lang.Integer.parseInt(Integer.java:668)
at java.base/java.lang.Integer.parseInt(Integer.java:786)
at Myclass.myMethod(Myclass.java:4)
<rest of the stack trace>

The explanation is the following:

  • myMethod (Line 4) calls Integer.parseInt(String string),
  • Integer.parseInt(String string) calls Integer.parseInt(String string, int radix) with the same string as argument, and a value of 10 for the radix (meaning that the string should represent an integer in base 10).
  • Integer.parseInt(String string, int radix) throws an exception, because the string “clearlyNotANumber” does not represent a (base 10) number.

Hint. Your IDE allows you to navigate through Java’s source code to trace the cause of an exception.

Hint. To debug your code, by default:

  1. find the last method (from bottom to top) in the stack trace that was written by you (in this example Myclass.myMethod),
  2. using our IDE, create a breakpoint at the indicated line for this class (4 in this example),
  3. run your program in debug mode (e.g. clicking on the “bug” icon of your IDE).

Throwing an exception #

An exception can be thrown with the keyword throw, as follows:

boolean isSolvable(int[][] sudokuGrid){
    if(sudokuGrid.length != 9){
        throw new IllegalArgumentException("A sudoku grid should have 9 rows");
    }
    if(sudokuGrid[0].length != 9){
        throw new IllegalArgumentException("A sudoku grid should have 9 columns");
    }
    ...
}

Classes for native Java exceptions usually have several constructors, one of which takes a string as input. For instance, in the example above, we used the constructor IllegalArgumentException(String errorMessage).

Catching an exception #

When a runtime exception is thrown by a method, the method immediately below it in the call stack can either rethrow the exception, or catch it.

If some method in the call stack catches the exception, then the program is not interrupted. Instead, control is passed to the catching method.

In other words, when an exception is thrown, the JVM “pops” method calls from the call stack, until either:

  • a catching method is found, or
  • the call stack is empty (in which case the program is interrupted).

For instance, let us modify myMethod above so that:

  • it takes as input a string (maybe supplied at run time),
  • if this string can be converted to an integer, then it returns this integer,
  • otherwise it returns 0.

This can be achieved by catching the exception thrown by Integer.parseInt. Syntactically, this is done with a try/catch block, as follows:

int myMethod(String inputString){
    try {
        return Integer.parseInt(inputString);
    } catch (NumberFormatException e) {
        return 0;
    }
}

In this example:

  • the code inside the try block is always executed,
  • the code inside the catch block is only executed if a NumberFormatException is thrown during the execution of the try block,
  • the program is not interrupted.

Chaining #

The catch clause may itself throw an exception.

This is a very common pattern. In particular, it can be used to add information about the context in which an error occurred. In such a case, a common practice consists in chaining these two exceptions.

Most native Java classes for exceptions have a dedicated constructor for this purpose: it takes as input a string (for the error message), and the exception that was caught. For instance, we can modify our example in such a way that if the input string cannot be converted to an integer, then the program gets interrupted, but with a less generic error message:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public class MyClass{

    int myMethod(String inputString){
        try{
            return Integer.parseInt(inputString);
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException("Please provide a valid number", e);
        }
    }
}

Note in this example the second argument e of the constructor for IllegalArgumentException.

If the method is called as follows,

myMethod("notANumberEither");

then the program will be interrupted, with the following stack trace:

Java.lang.IllegalArgumentException: Please provide a valid number

  at MyClass.myMethod(MyClass.java:7)
  <rest of the stack trace>
Caused by: java.lang.NumberFormatException: For input string: "notANumberEither"
  at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
  at java.base/java.lang.Integer.parseInt(Integer.java:668)
  at java.base/java.lang.Integer.parseInt(Integer.java:786)
  at MyClass.myMethod(MyClass.java:5)