Using the Catch Clause

Before you learn to catch exceptions in your program, you need to see what happens when you don’t catch them. Here is an example which generates an exception.

public class GenerateException {
    
    public static void main(String ... arguments) {
        int a = 100;
        int b = 0;
        int c = a / b;
    }
}

When the Java Virtual Machine tries to evalute a / b, it throws an exception because you tried to divide an integer by zero.

The output of this example is shown here.

Exception in thread "main" java.lang.ArithmeticException: / by zero
        at GenerateException.main(GenerateException.java:6)

The output contains the type of the exception, an error message and the stacktrace which points to the location from where the exception occurred. The stack trace includes the class names, the method names, the file names, and the line numbers through which the exception propogated.

When an exception is thrown, Java stops whatever your program is doing and tries to find an exception handler. In this example, this causes the execution of the main method to stop. Since we have not provided an exception handler, the exception propogates outside your method and is caught by the default exception handler.

Every thread in Java has a default exception handler which is triggered when an exception is never caught. In other words, any exception your program fails to catch is processed by the default exception handler. The default exception handler prints the exception, the stacktrace from the point where the exception was thrown, and terminates the current thread. In this example, there is only one thread, hence the program is terminated.

A thread is a component which allows multiple parts of your program to run simultaneously.

Here is another example which demonstrates how the stacktrace can be useful to locate the cause of the error.

public class GenerateException {
    
    private static int divide(int a, int b) {
        return a / b;
    }
    
    public static void main(String ... arguments) {
        int a = 100;
        int b = 0;
        divide(a, b);
    }
}

Here is the output generated by this example.

Exception in thread "main" java.lang.ArithmeticException: / by zero
        at GenerateException.divide(GenerateException.java:4)
        at GenerateException.main(GenerateException.java:10)

As you can see, the stacktrace includes the class name, the method name, the file name, and the line numbers through which the exception propogated. The exception can occur in any source file, any class, and any method. This is why each element in the stacktrace contains these information. In other words, the stack trace will always show the method invocations which led up to the error.

Using the Try and Catch Clauses

You cannot always depend on the default exception handler to handle the exceptions. In some situations, you will want to handle the exception yourself. With a custom exception handler, you can resolve the error and prevent the program from terminating. Imagine, you are writing a server side program. How troublesome would it be if your program terminated whenever an exception occurred?

You need to monitor a block of code for exceptions using the try clause. You can then provide a custom exception handler using a catch clause. Whenever you write a try clause, you are essentially writing the try statement. It is a compound statement.

Here is the general form of the try clause.

try {
    statement1
    statement2
    ...
    statementN
}

Unlike most compound statements which allow you to skip the braces when the body contains a single statement, all the clauses in a try statement require you to enclose their respective bodies with braces. In other words, you always need to enclose the bodies of try clause, catch clause, and finally clause with braces even if they contain only one statement or no statement at all.

Further, a try clause should always be followed by a catch clause or a finally clause. When both the clauses appear, the finally clause should be the last clause.

Here is the general form of the catch clause.

catch (ExceptionType identifier) {
}

The ExceptionType indicates the type of exception the catch clause handles. When an exception is caught, the variable identified by identifier you specify holds a reference to the exception object. Basically, you declare a parameter in the parenthesis of the catch clause. The parameter references the exception object when the exception is caught.

Here is an example which demonstrates how to catch an exception.

public class GenerateException {
    
    private static int divide(int a, int b) {
        return a / b;
    }
    
    public static void main(String ... arguments) {
        int a = 100;
        int b = 0;
        try {
            int result = divide(a, b);
            System.out.println(result);
        }
        catch (ArithmeticException exception) {
            System.out.println("Error: you cannot divide an integer by zero.");
            System.out.println(exception);
        }
    }
}

This example generates the following output.

java.lang.ArithmeticException: / by zero
Error: you cannot divide an integer by zero.

Notice that the System.out.println() inside the try clause is never executed. When an exception is thrown, the program stops whatever it is doing. Java then tries to find the nearest catch clause in the stack trace. When a catch clause which anticipates the exception which occurred is found in the stack trace, the program control jumps out of the try clause and is transferred to the catch clause. A reference to the exception object is stored in the parameter of the catch clause. Once the catch clause is done executing, the statement following the catch clause is executed. The control never returns back to the location where the exception occurred. Because exceptions are not like methods to return back.

A catch clause can catch an exception only if one of the statements within the try clause associated with it throws an exception.

The Throwable class overrides the toString() method, which means you print an exceptions details with println() method.

Multiple Catch Clauses

In some cases, more than one type of exception can be thrown by the statements in your try clause. To handle such situations, you can specify more than one catch clause, each catching a different type of exception. When an exception is thrown, each catch clause is checked in order to see if it handles the exception. The first catch clause whose type matches the thrown exception object is executed. When one catch statement is finished executing, the other catch clauses are skipped. The program control transfers to the statement following the try statement.

Here is an example of a try statement which handles multiple types of exceptions.

public class MultipleCatchClauses {
    
    private static int divide(int a, int b) {
        return a / b;
    }
    
    public static void main(String ... arguments) {
        try {
            int a = 10;
            int b = 20;
            int c = divide(a, b);
            System.out.println(c);
            
            Object object = null;
            String s = object.toString();
            System.out.println(s);
        }
        catch(ArithmeticException exception) {
            System.out.println("Error: You cannot divide an integer by zero.");
        }
        catch (NullPointerException exception) {
            System.out.println("Error: You cannot dereference null.");
        }
    }
}

Here is the output produced by this example.

0
Error: You cannot dereference null.

The statements within the try clause in this program can generate two exceptions. You cannot divide an integer by zero. In this example, invoking the divide() method may produce an ArithmeticException. Since the arguments are positive integers, this exception does not occur. We nevertheless provide a catch clause to handle it. The program successfully divides a and b and prints the result.

You cannot dereference a null reference. Doing so will cause Java to throw a NullPointerException. Therefore, we provide a catch clause to handle it.

This program was intentionally designed to produce a NullPointerException exception. When the exception is thrown, the catch clause which handles the NullPointerException exception is triggered. Thus, the second println() statement in the try clause is not executed.

Multiple Catch Clauses and Inheritance

When you use multiple catch clauses, you need arrange them properly. Imagine that you have two catch clauses. The first catch clause handles exceptions of type A and the second catch clause handles exceptions of type B. The order in which your catch clauses appear does not matter as long as A and B are not related. In other words, the order does not matter if A is neither the superclass or subclass of B.

However, if A and B are related, that is, if A is either the superclass or subclass of B then the order matters. You always need to write the catch clause which handles the subclass first because Java always selects the first suitable clause without considering other clauses. Java searches through the catch clauses in the order in which they appear in your source code. If you first write a catch clause which handles your superclass, the exception object even if it is an instance of the subclass is implicitly cast by Java to match the superclass. This results in the selection of the catch clause which handles the superclass, not the subclass.

Consider the following example.

class MultipleCatchClauses {
    
    private static int divide(int a, int b) {
        return a / b;
    }
    
    public static void main(String ... arguments) {
        int a = 100;
        int b = 0;
        try {
            divide(a, b);
        }
        catch (Exception exception) {
            System.out.println("Error: This is a generic message.");
        }
        catch (ArithmeticException exception) {
            System.out.println("Error: You cannot divide an integer by zero.");
        }
    }
}

If you try to compile this example, the compiler will generate the following error.

MultipleCatchClauses.java:16: error: exception ArithmeticException has already been caught
            catch (ArithmeticException exception) {
            ^
1 error

The error basically means that your second catch clause will never be executed because the first catch clause handles it. You need to change the order of the catch clauses to fix this problem.

Handling all Exceptions in One Place

You can create a general catch clause such that all the exceptions are handled by a single catch clause. You have learnt how implicit casting works with exceptions. Using this concept, we can create a general catch clause.

You know that the Throwable class is the superclass of all exceptions. Which means, if you write a catch clause which handles Throwable, then all types of exceptions are handled by it.

Here is the general form of such a catch clause.

try {
    statement1
    statement2
    ...
    statementN
}
catch (Throwable exception) {
    ...
}

Since, exceptions which inherit Error should not be caught, it is better to write a catch clause that handles the Exception class. This way, you can catch only the exceptions that are meant to be caught.

Here is the general form of such a catch clause.

try {
    statement1
    statement2
    ...
    statementN
}
catch (Exception exception) {
    ...
}