Chapter 11 - Java for Beginners Course

Throw and throws

By: Andres Perez

So far, we have shown how to handle exceptions when they occur, and how the try/catch/finally block allows us to control what the code does in the event of an exception.

But, it is also possible to manually throw exceptions in your code and use them inside try/catch/finally blocks to control possible errors.

The throw statement

We can throw exceptions in Java using the throw statement. In order to be able to throw an exception, the Object thrown must be a Throwable or one of its subclasses. It is also possible to throw custom exceptions.

This is usually done by creating a class that extends the Exception class. Once an exception is thrown, it can be handled by a try block. If no suitable try block is found for the exception to be handled, the execution will stop.

As an example, let’s create a method that divides only positive integers. For the arguments to be valid, the dividend must be greater or equal than 0 and the divisor must be greater than 0, as we can’t divide by 0:

public class PositiveIntegerDivider {

    int divide(int dividend, int divisor) {
        try {
            System.out.println("Try block executed");
            if (dividend < 0 && divisor <= 0) {
                throw new IllegalArgumentException("dividend is not greater or equal than 0 and divisor is not greater than 0");
            } else if (dividend < 0) {
                throw new IllegalArgumentException("dividend is not greater or equal than 0");
            } else if (divisor <= 0) {
                throw new IllegalArgumentException("divisor is not greater than 0");
            }
            return dividend/divisor;
        } catch(IllegalArgumentException e) {
            System.out.println("Catch block executed");
            System.out.println("An IllegalArgumentException occurred: " + e.getMessage());
            return -1;
        }
    }

}

We will call our method in the following way:

PositiveIntegerDivider divider = new PositiveIntegerDivider();
// the divisor and the dividend are both negative, so an IllegalArgumentException is thrown
System.out.println("The result is: "  + divider.divide(-50,-10));

In this case, if we call our method with a dividend of -50 and a divisor of -10, the output will be:

Try block executed
Catch block executed
An IllegalArgumentException occurred: dividend is not greater or equal than 0 and divisor is not greater than 0
The result is: -1

Furthermore, if we call our method with a dividend of -50 and a divisor of 10:

PositiveIntegerDivider divider = new PositiveIntegerDivider();
// the dividend is negative, so an IllegalArgumentException is thrown
System.out.println("The result is: "  + divider.divide(-50,10));

Output:

Try block executed
Catch block executed
An IllegalArgumentException occurred: dividend is not greater or equal than 0
The result is: -1

Moreover, if we call our method with a dividend of 50 and a divisor of -10:

PositiveIntegerDivider divider = new PositiveIntegerDivider();
// the divisor is negative, so an IllegalArgumentException is thrown
System.out.println("The result is: "  + divider.divide(50,-10));

Output:

Try block executed
Catch block executed
An IllegalArgumentException occurred: divisor is not greater than 0
The result is: -1

And finally, we call our method with a dividend of 50 and a divisor of 10, so that no exception is thrown:

PositiveIntegerDivider divider = new PositiveIntegerDivider();
// the divisor and the dividend are both positive, so no is thrown
System.out.println("The result is: "  + divider.divide(50,10) );

Output:

Try block executed
The result is: 5

Throwing exceptions in this way is useful, as it allows you to throw exceptions with custom messages to give a better indication of what went wrong.

Also, as mentioned above, it lets you throw objects of classes you’ve extended from the Exception class, to be even more specific about what caused the exception in your application.

Checked and unchecked exceptions

So far, we have always used a try block when we throw exceptions. But for some exceptions, it is possible to just throw them without handling them. If an exception is not handled, it will stop the execution of the current method and report the exception that caused it.

This is only possible with what are known as Unchecked Exceptions. Many common exceptions belong to this category, such as ArithmeticException, IllegalArgumentException, NullPointerException, among others.

On the other hand, exceptions categorized as Checked Exceptions must be handled with a try block. This is verified when you compile your program, and will result in an error if the exception is not being handled.

One of the most common exceptions in this category is IOException, which is thrown when an input or output operation runs into an error (for example when reading data from a file that doesn’t exist).

When creating custom exceptions, it is important to decide what kind of exception you want your type to be. Unchecked exceptions, like ArithmeticException, IllegalArgumentException, and NullPointerException, are derived from RuntimeException, so your custom exception class should extend from it.

To create a custom checked exception, you should extend the Exception class. SInce they are checked exceptions, when your custom exception is thrown it will be mandatory for it to be handled.

Throwing checked exceptions: the throws statement

As mentioned before, if your method throws a checked exception, it must be handled inside a try block. But, it is not mandatory to do so in the method that the exception is first thrown. This is useful as it might be better to react to the exception where the method was originally called from.

To indicate that a method throws a checked exception that must be handled, the throws statement is used in the declaration of the method. It also requires us to specify the class of the exception that is being thrown.

For example, if we create a method for reading a single String from a file, we have to indicate that the method throws an IOException:

String read(String fileAddress) throws IOException {
    File inputFile = new File(fileAddress);
    BufferedReader reader = new BufferedReader(new FileReader(inputFile));
    return reader.readLine();
}

When we call this method somewhere else, we have to either handle the exception with a try block:

String methodForString(){
    StringFromFile reader = new StringFromFile();
    String s;
    try {
        s = reader.read("D:\\input.txt");
    } catch (IOException ex) {
        System.out.println("An IOException occurred: " + ex.getMessage());
        s = null;
    }
    return s;
}

or we can again include a throws statement in the declaration of this method and keep propagating the exception up to where the method was called:

String methodForString() throws IOException {
    StringFromFile reader = new StringFromFile();
    String s = reader.read("D:\\input.txt");
    return s;
}
You can also indicate a superclass of the exception thrown to cover more than one type of exception. Additionally, you can specify more than one class after the throws statement (for example throws IOException, NullPointerException).