5.9 'throws' Clause

A throws clause can be specified in the method protoype.


... someMethod(...)
    throws <ExceptionType1>, <ExceptionType2>,..., <ExceptionTypen> { ... }

Each <ExceptionTypei> declares a checked exception. The compiler enforces that the checked exceptions thrown by a method are limited to those specified in its throws clause. Of course, the method can throw exceptions that are subclasses of the checked exceptions in the throws clause. This is permissable since exceptions are objects, and a subclass object can polymorphically act as an object of its superclass (see Section 6.1, p. 226). The throws clause can have unchecked exceptions specified, but this is seldom used and the compiler does not check them.

Handling of checked exceptions in initializers is covered in Section 8.2 on page 331.

Any method that can cause a checked exception to be thrown, either directly by using the throw statement or indirectly by invoking other methods that can throw such an exception, must deal with the exception in one of three ways. It can

  • use a try block and catch the exception in a handler and deal with it

  • use a try block and catch the exception in a handler, but throw another exception that is either unchecked or declared in its throws clause

  • explicitly allow propagation of the exception to its caller by declaring it in the throws clause of its method protoype

This mechanism ensures that, regardless of the path of execution, a checked exception will be dealt with in some way. It aids development of robust programs, as allowance can be made for many contingencies. Native methods can also declare checked exceptions in their throws clause, but the compiler is not able to check them for consistency.

A new checked exception is defined in Example 5.16. The checked exception class IntegerDivisionByZero is defined at (11) by extending the Exception class.

In Example 5.16, the method main() calls the method printAverage() in a try block at (1). In the if statement at (9), the method computeAverage() throws the checked exception IntegerDivisionByZero defined at (11). Neither the computeAverage() method nor the printAverage() method catch the exception, but instead throw it to their caller, as declared in the throws clause in their headers at (6) and (8). The exception propagates to the main() method. Since the printAverage() method was called from the context of the try block at (1) in the main() method, the exception is successfully matched with its catch block at (3). The exception is handled and the finally block at (4) executed, with normal execution proceeding at (5). If the method main() did not catch the exception, it would have to declare this exception in a throws clause. In that case, the exception would end up being taken care of by the default exception handler.

Example 5.16 throws Clause
public class Average8 {
    public static void main(String[] args) {
        try {                                                    // (1)
            printAverage(100, 0);                                // (2)
        } catch (IntegerDivisionByZero idbze) {                  // (3)
            idbze.printStackTrace();
            System.out.println("Exception handled in " +
                               "main().");
        } finally {                                              // (4)
            System.out.println("Finally done in main().");
        }

        System.out.println("Exit main().");                      // (5)
     }

    public static void printAverage(int totalSum, int totalNumber)
            throws IntegerDivisionByZero {                       // (6)

        int average = computeAverage(totalSum, totalNumber);
        System.out.println("Average = " +
            totalSum + " / " + totalNumber + " = " + average);
        System.out.println("Exit printAverage().");              // (7)
    }

    public static int computeAverage(int sum, int number)
            throws IntegerDivisionByZero {                       // (8)

        System.out.println("Computing average.");
        if (number == 0)                                         // (9)
            throw new IntegerDivisionByZero("Integer Division By Zero");
        return sum/number;                                       // (10)
    }
}

class IntegerDivisionByZero extends Exception {                  // (11)
    IntegerDivisionByZero(String str) { super(str); }            // (12)
}

Output from the program:

Computing average.
IntegerDivisionByZero: Integer Division By Zero
        at Average8.computeAverage(Average8.java:33)
        at Average8.printAverage(Average8.java:22)
        at Average8.main(Average8.java:7)
Exception handled in main().
Finally done in main().
Exit main().

The exception type specified in the throws clause in the method protoype can be a superclass type of the actual exceptions thrown, that is, the exceptions thrown must be assignable to the type of the exceptions specified in the throws clause. If a method can throw exceptions of the type A, B, and C where these are subclasses of type D, then the throws clause can either specify A, B, and C or just specify D. In the printAverage() method, the method protoype could specify the superclass Exception of the subclass IntegerDivisionByZero in a throws clause.

public static void printAverage(int totalSum, int totalNumber)
        throws Exception { /* ... */ }

It is generally a bad programming style to specify exception superclasses in the throws clause of the method protoype, when the actual exceptions thrown in the method are instances of their subclasses. Programmers will be deprived of information about which specific subclass exceptions can be thrown, unless they have access to the source code.

A subclass can override a method defined in its superclass by providing a new implementation (see Section 6.2, p. 233). What happens when an inherited method with a list of exceptions in its throws clause is overridden in a subclass? The method definition in the subclass can only specify a subset of the checked exception classes (including their subclasses) from the throws clause of the inherited method in the superclass. This means that an overriding method cannot allow more checked exceptions in its throws clause than the inherited method does. Allowing more checked exceptions in the overriding method would create problems for clients who already deal with the exceptions specified in the inherited method. Such clients would be ill prepared if an object of the subclass (under the guise of polymorphism) threw a checked exception they were not prepared for.

class A {
    // ...
    protected void superclassMethodX()
        throws FirstException, SecondException, ThirdException {/* ... */} // (1)
    // ...
}

class B extends A {
    // ...
    protected void superclassMethodX()
        throws FirstException, ThirdException { /* ... */ }                // (2)
    // ...

}

In the previous code, the method superclassMethodX in superclass A is overridden in subclass B. The throws clause of the method in subclass B at (2) is a subset of the exceptions specified for the method in the superclass at (1).