Unit 7: Exception Handling

Table of Contents

  1. Exception Handling
    1. Exception Messages
    2. Exception Classes
  2. Try-Catch
    1. Exception Methods
    2. Finally
  3. Throwing Exceptions
    1. Throw
    2. Throws
    3. @throws

Exception Handling

Throughout your programming journey, you have probably encountered something like this (probably in glaring red text) when you ran your program:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
        at com.omegarobotics.unit7.lessons.exceptions.Exception.main(Exception.java:6)

Messages like this are displayed whenever your program has some sort of error. To be more specific, this happens when your program throws an exception (notice that it said Exception in thread "main").

Exception Messages

If you were a diligent programmer, you would have noticed that exception messages are pretty detailed. They tell you what type of exception was thrown (in this case, java.lang.ArrayIndexOutOfBoundsException), in what file the exception occurred (com/omegarobotics/unit7/lessons/exceptions/TestException.java) and at what line the exception occurred (line 6 since it said TestException.java:7).

Exception Classes

What you might not have known is that exceptions are actually classes, and that you can customize how those exceptions are handled in the event that they are thrown.

Note: All exception classes extend the Exception class and share common methods, which we will learn later.

Note: You can actually write your own exception classes if you want!

Try-Catch

When an exception is thrown, by default the program will terminate and the message will be printed to the console. However, as a programmer, you can catch a thrown exception and decide what happens after that.

To do this, you need to use a try-catch block. They look generally like this:

try {
    // write some code that could throw an exception
} catch (ExceptionClassName ex) {
    // handle the exception somehow
}

A programmer would put code inside the try block if they think an exception could be thrown when executing that code. If indeed an exception is thrown during the try block, the rest of the try block is skipped. Instead, code inside the first catch block with the corresponding exception class will run.

Note that the variable ex can be called whatever you want it to be (of course, follow Java identifier naming rules). This variable is what you will use to refer to the exception object (for example, if you want to call a method from the exception class).

Note: You can have multiple catch blocks which are tested one after another when an exception is thrown. For example:

try {
    // write some code that could throw an exception
} catch (ExceptionClassName1 ex) {
    // handle the exception somehow
} catch (ExceptionClassName2 ex) {
    // handle the exception somehow
} catch (ExceptionClassName3 ex) {
    // handle the exception somehow
}

Similar to an if-else statement, once code in a catch block is executed, the rest of the catch blocks are skipped.

Let’s see a try-catch block in action. Analyze the code and output below.

import java.util.Scanner;

public class Quotient {

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);

        // prompt the user to enter two integers
        System.out.print("Enter two integers: ");
        int n1 = input.nextInt();
        int n2 = input.nextInt();

        input.close();

        try {
            System.out.println(n1 + " / " + n2 + " = " + (n1 / n2));
        } catch (ArithmeticException ex) {
            System.out.println("Error, division by zero");
        }
    }
}

Example Output 1

Enter two integers: 5 0
Error, division by zero

Example Output 2

Enter two integers: 24 6
24 / 6 = 4

The program above prompts the user for 2 integers and divides them. Notice that if the divisor is 0, there will be a division by 0, which as we know is undefined. Without a try-catch, a division by 0 would result in an ArithmeticException.

Using the try-catch block, we can catch the ArithmeticException that is thrown and avoid the messy error message that wouldn’t be readable to a non-programmer. Instead, we can print a user friendly message: "Error, division by zero".

Exception Methods

As mentioned previously, since all exception classes extend Exception, they have common methods. One of those methods is printStackTrace(), which allows you to print the regular error message that would be printed if you hadn’t caught the exception.

For example, let’s modify the code from the Quotient class so that it calls the printStackTrace() method instead of printing a short error message.

import java.util.Scanner;

public class Quotient {

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);

        // prompt the user to enter two integers
        System.out.print("Enter two integers: ");
        int n1 = input.nextInt();
        int n2 = input.nextInt();

        input.close();

        try {
            System.out.println(n1 + " / " + n2 + " = " + (n1 / n2));
        } catch (ArithmeticException ex) {
            ex.printStackTrace();
        }
    }
}

Example Output

Enter two integers: 5 0
java.lang.ArithmeticException: / by zero
        at com.omegarobotics.unit7.lessons.exceptions.Quotient.main(Quotient.java:25)

Note: Remember that calling an exception method isn’t the only thing you can do in the catch block. If you wanted to, you could add statements before or after method calls. That’s why try-catch blocks are cool - they give you the flexibility to handle exceptions rather than being stuck with an error message every time.

Finally

There’s an additional clause you can add to a try-catch block called finally. This code runs at the end of a try-catch block no matter what happens in between. It generally looks like this:

try {
    // write some code that could throw an exception
} catch (ExceptionClassName ex) {
    // handle the exception somehow
} finally {
    // code in this block is executed at the end
    // no matter what happens
}

We can rewrite the Quotient class to include a thank you message to the user at the end of the program using the finally block.

import java.util.Scanner;

public class Quotient {

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);

        // prompt the user to enter two integers
        System.out.print("Enter two integers: ");
        int n1 = input.nextInt();
        int n2 = input.nextInt();

        input.close();

        try {
            System.out.println(n1 + " / " + n2 + " = " + (n1 / n2));
        } catch (ArithmeticException ex) {
            System.out.println("Error, division by zero");
        } finally {
            System.out.println("Thanks for using the program");
        }
    }
}

Example Output 1

Enter two integers: 5 0
Error, division by zero
Thanks for using the program

Example Output 2

Enter two integers: 24 6
24 / 6 = 4
Thanks for using the program

Notice that "Thanks for using the program" is always printed, even if an exception is not caught.

Throwing Exceptions

Now that you know how to catch exceptions, what about throwing them? It turns out you can do that in Java too!

Throw

You can throw an exception in Java by using the throw keyword.

For example, let’s write a setRadius method for a class called Circle. We want our setRadius method to only accept radii which are greater than or equal to 0. Otherwise, we want to tell the user that the argument provided is illegal - perfect for the IllegalArgumentException.

Some methods are omitted for brevity.

public class Circle {
    private double radius;

    ...

    public void setRadius(double radius) {
        if (radius >= 0) {
            this.radius = radius;
        } else {
            throw new IllegalArgumentException("Radius cannot be negative");
        }
    }

    ...
}
public class TestCircle {
    public static void main(String[] args) {
        Circle myCircle = new Circle(5);
        myCircle.setRadius(-5);
    }
}

Example Output

Exception in thread "main" java.lang.IllegalArgumentException: Radius cannot be negative
        at com.omegarobotics.unit7.lessons.exceptions.Circle.setRadius(ThrowingExceptions.java:68)
        at com.omegarobotics.unit7.lessons.exceptions.TestCircle.main(TestCircle.java:4)

Note: Line numbers may be different for you.

Notice that when we threw the exception, we had to use the new operator and the exception class’s constructor. This is because you technically need to make an instance of the exception class and then throw it.

Thus, these are equivalent:

throw new IllegalArgumentException("Radius cannot be negative");
IllegalArgumentException ex = new IllegalArgumentException("Radius cannot be negative");
throw ex;

Also notice that the exception class’s constructor took 1 argument, the error message. All exception classes have this constructor. They also have a default constructor which doesn’t take any arguments. For example:

throw new IllegalArgumentException();

Throws

If you want to declare that your method throws an exception, you can use the throws keyword in the method signature. Note that you only need to do this for checked exceptions. To learn more about checked and unchecked exceptions, you can read this article.

A method signature with the throws keyword generally looks like this:

modifier(s) returnType methodName(ParameterType parameterName, ...) throws ExceptionName

For example:

public void setRadius(double radius) throws IllegalArgumentException

Note: In the case of the setRadius method, you actually do not need to declare that it throws the IllegalArgumentException because that is an unchecked exception.

@throws

Recall that there is an @throws annotation for JavaDoc comments that allow you to document the fact that your method throws an exception.

For example:

/**
 * Sets a new radius
 * @param radius  new radius
 * @throws IllegalArgumentException if the new radius is negative
 */
public void setRadius(double radius) throws IllegalArgumentException {
    if (radius >= 0) {
        this.radius = radius;
    } else {
        throw new IllegalArgumentException("Radius cannot be negative");
    }
}