Previous Table of Contents Next


13.12. Exceptions

What do you do when an error occurs in a program? In many languages, error conditions are signaled by unusual return values like 01. Programmers often don’t check for exceptional values because they may assume errors “can’t happen.” On the other hand, adding error-detection and recovery to what should be a straightforward flow of logic can obscure that logic to the point where the normal flow is incomprehensible. An ostensibly simple task such as reading a file into memory might require about seven lines of code. Error checking and reporting extend this to 40 or more lines. Making normal operation the needle in your code haystack is undesirable.

Java used checked exceptions to manage error handling. Exceptions force you to deal with errors. If a checked exception is not handled, this is noticed when the error happens, not later when problems have compounded because of the unchecked error.

A method that detects an unusual error condition throws an exception. Exceptions can be caught by code farther back on the calling stack—this prior code can handle the exception as needed and then continue executing. Uncaught exceptions are handled by a default handler in the Java implementation, which may report the exception and terminate the thread of execution.

An exception in Java is an object, with type, methods, and data. Representing exceptions as objects is useful, because an exception object can include data or methods or both to report on or recover from specific kinds of exceptions. Exception objects are generally derived from the Exception class, which provides a string field to describe the error. Java requires all exceptions to be extensions of a class named Throwable.

The general paradigm of exceptions in Java is the try-catch-finally sequence: You try something; if that something throws an exception, you catch the exception; and, finally, clean up from either the normal code path or the exception code path, whichever actually happened.

Below you see an averageOf method that returns the average of two elements in an array. If either index is outside the bounds of the array, it wants to throw an exception describing the error. First, we define a new exception type IllegalAverageException to describe such an error. Then we declare that the averageOf method throws that exception using a throws clause:

   class IllegalAverageException extends Exception {
   }

   class MyUtilities {
        public double averageOf(double[] vals, int I, int j)
             throws IllegalAverageException
        {
             try {
                  return (vals[i] + vals[j]) / 2;
             } catch(IndexOutOfBoundsException e) {
                  throw new IllegalAverageException();
             }
        }
   }

During the averaging calculation, if both i and j are within the array bounds, the calculation succeeds and the average value is returned. But if either index is out of bounds, an IndexOutOfBoundsException is thrown, and the matching catch clause is executed. The catch clause creates a new IllegalAverageException object and throws it, in effect translating the general array index exception into a specific exception that more precisely describes the real failure. Methods farther back on the execution stack have a chance to catch the new exception and react to it appropriately.

If execution of a method can result in checked exceptions, it must declare the types of these exceptions in a throws clause, as shown for averageOf. Other than exceptions of type RuntimeException and Error, or subclasses of these exception types, which can be thrown anywhere, a method can throw only those exceptions it declares, whether it throws those exceptions directly with throw, or indirectly by invoking a method that throws exceptions.

Declaring exceptions that a method throws means the compiler can ensure that the method throws only those exceptions it declared, and no others. This check prevents errors in cases where your method should handle another method’s exceptions, but did not. In addition, the method that invokes your method is assured that your method will not result in unexpected exceptions. This is why the exceptions you must declare in a throws clause are called checked exceptions. Exceptions that are extensions of RuntimeException and Error need not be declared and are not checked; they are called unchecked exceptions.

Exercise 13.14

Add fields to IllegalAverageException to hold the array and indices so that whoever catches the exception will know details about the error.


Previous Table of Contents Next