Chapter 3 - Java for Beginners Course

Exception Handling

Although they are not control flow statements as such, exceptions are a type of event that can alter the flow of the application and are important to bear in mind from an early stage.

In this section, we’ll introduce the basics of exceptions, identify some cases where they occur and the basics of the try/catch statement. There is plenty more to cover in future courses.

Exceptions in Java

As mentioned above, exceptions are events that are triggered when an error occurs inside a method. These exceptions are actual Java objects of type Throwable or any of its subclasses. These objects provide us information about where the exception occurred (class, method and line of code), and in general a human readable error message describing the issue.

The 3 main types of Throwable subclasses are Exception, RuntimeException and Error, and both Java and Java-based libraries provide subclasses of these 3 to specify more concise exceptions, for example, ArithmeticException is a subclass of RuntimeException that is thrown when an error occurs performing an arithmetic operation, like dividing by 0.

Raising exceptions - the throw statement

The throw statement allows us to trigger an exception event, this is, ask the JVM to stop the execution of the method and alter the flow of the application by raising an exception. The throw statement is of the form:

throw throwableObject;

For example:

throw new RuntimeException("An exception occurred due to: ...");
It is common to create the exception object in the same line it is thrown as shown above (new RuntimeException(…​)), although it isn’t mandatory.

For the remainder of this section, let’s assume we have a class PositiveNumberPrinter that looks like this:

public class PositiveNumberPrinter {

    public void printNumber(int number) {
        if (number < 0) {
            throw new IllegalArgumentException("The number must be positive, got: " + number);
        }

        System.out.println("The number is: " + number);
    }

}
IllegalArgumentException is a subclass of RuntimeException and is provided by Java.

In this class, we have a single method that will print a number only if it is positive. Otherwise, an IllegalArgumentException is thrown.

Now, let’s invoke this method and see what happens:

Run the app in class BasicExceptionsApp
PositiveNumberPrinter printer = new PositiveNumberPrinter();

// the number is positive, so the method doesn't throw an exception
printer.printNumber(20);

// the number is negative, an exception is thrown
printer.printNumber(-1);

// due to the exception, the main method stops executing and this line
// isn't reached by the program
printer.printNumber(5);

Output:

The number is: 20
Exception in thread "main" java.lang.IllegalArgumentException: The number must be positive, got: -1
	at io.jcoder.tutorials.ch03.exceptions.PositiveNumberPrinter.printNumber(PositiveNumberPrinter.java:25)
	at io.jcoder.tutorials.ch03.exceptions.BasicExceptionsApp.main(BasicExceptionsApp.java:24)

Analysing the throw statement example

Here, our PositiveNumberPrinter.printNumber method appears three times in our code, with arguments 20, -1 and 5 respectively.

On our first invocation, as the number is positive (20), the throw statement isn’t executed, hence the method outputs: The number is: 20.

However, on our second invocation, as the number is negative (-1), the throw statement is executed. This causes the method to stop its execution and Java propagates the exception until it finds something that can handle it. In our case, we don’t have anything to handle the exception (as we haven’t introduced that topic yet!), so our application prints out the Exception details and the process is terminated.

As the application is terminated, the third invocation of the method (with 5 as the argument) is never executed, hence why we don’t see the message The number is: 5.

If you check the details that are printed out of our exception, you’ll see that it tells us that the exception was of type IllegalArgumentException, it prints the exception message and it then provides details of where the exception occurred, in our case, it tells us that it occurred in the method PositiveNumberPrinter.printNumber and that the method that invoked this was BasicExceptionsApp.main.

This list of classes and methods is called the Call Stack as it shows us what methods were invoked and in what order when the exception occurred. In our case, we only have 2 methods, however call stacks can be longer than this.

Handling exceptions - the try statement

Without a way of handling exceptions, the whole mechanism would not be useful for error handling purposes. Java provides a try block that allows us to handle/catch exceptions when they occur and have the application respond gracefully when possible.

The try block has multiple forms and in this section we’ll only cover the basic try/catch as an introduction.

The basic try/catch has the following form:

try {
	// block of code we want to execute with exception handling
	// a.k.a the `try block`
} catch (ExceptionType ex) {
	// block of code that will be executed if an exception of
	// type `ExceptionType` is thrown from inside the try block
	// a.k.a the `catch block`
}

Here we have 2 main blocks of code:

  1. The try block that is the application code we want to execute and that might throw an exception. If an exception occurs inside a line in the try block, the remaining statements aren’t executed.

  2. And the catch block that is executed when an exception occurs. With catch blocks however, you need to specify what type of exception you’re expecting/catching, for example, you could say you’re expecting an exception of type IllegalArgumentException.

In the catch statement, note that we have ExceptionType ex, the ex is the name of the variable we’re giving to the exception object and it can be anything you want. Names e and ex are common.

Let’s take a look at an example:

Run the app in class BasicTryCatchApp
PositiveNumberPrinter printer = new PositiveNumberPrinter();

try {
    // no exception thrown
    printer.printNumber(1);

    // this throws an IllegalArgumentException
    printer.printNumber(-1);

    // this line isn't reached due to the exception
    printer.printNumber(2);
} catch (IllegalArgumentException ex) {
    System.out.println("An exception occurred: " + ex.getMessage());
}

// as the exception above was handled, this line is executed normally
printer.printNumber(3);

Output:

The number is: 1
An exception occurred: The number must be positive, got: -1
The number is: 3

Analysing the try/catch example

Similar to our previous example, we are invoking the printNumber multiple times, but in this case, the first 3 invocations are inside a try block.

When the code reaches the second invocation, with argument -1, as expected the throw statement inside the printNumber method is executed and an exception is raised. In this case however, we have a catch block that handles exceptions of type IllegalArgumentException. In this example, we just print out the details of the exception and the application resumes in the next line after our try/catch block.

Note however, that the last invocation inside the try block, printer.printNumber(2), isn’t executed as the exception causes the try block to stop executing and the flow of the application continues with the catch block.

The examples above are very basic and only meant to introduce the concepts of exception throwing and handling in Java. There are more concepts to cover in later chapters.