Section 8.1
Exceptions, try, and catch

GETTING A PROGRAM TO WORK UNDER IDEAL circumstances is usually a lot easier than making the program robust. A robust program is one that can survive unusual or "exceptional" circumstances without crashing. For example, a program that does a calculation that involves taking a square root will crash if it tries to take the square root of a negative number. A robust program must anticipate the possibility of a negative number and guard against it. This could be done with an if statement:

      if (disc >= 0) {
         r = Math.sqrt(disc);  // Since disc >= 0, this must be OK
      else {
         ...  // Do something to handle the case where disc < 0

We would say that the statement "r = Math.sqrt(disc);" has the precondition that disc >= 0. A precondition is a condition that must be true at a certain point in the execution of a program, if that program is to continue without error and give a correct result. One approach to writing robust programs is to rigorously apply the rule, "At each point in the program identify any preconditions and make sure that they are true" -- either by using if statements to check whether they are true, or by verifying that the required preconditions are consequences of what the program has already done. An example of the latter case would be the sequence of statements

        x = Math.abs(x);
           // At this point, we know that x >= 0, since
           // the absolute value of any number is defined to be >= 0
        y = Math.sqrt(x);  // Since x >= 0, this must be OK

There are some problems with this approach. It is difficult and sometimes impossible to anticipate all the possible things that might go wrong. Furthermore, trying to anticipate all the possible problems can turn what would otherwise be a straightforward program into a messy tangle of if statements.

Java (like its cousin, C++) provides a neater, more structured alternative method for dealing with possible errors that can occur while a program is running. The method is referred to as exception-handling. The word "exception" is meant to be more general than "error." It includes any circumstance that arises as the program is executed which is meant to be treated as an exception to the normal flow of control of the program. An exception might be an error, or it might just be a special case that you would rather not have clutter up your elegant algorithm.

When an exception occurs during the execution of a program, we say that the exception is thrown. When this happens, the normal flow of the program is thrown off-track, and the program is in danger of crashing. However, the crash can be avoided if the exception is caught and handled in some way. An exception can be thrown in one part of a program and caught in a completely different part. An exception that is not caught will generally cause the program to crash.

By the way, since Java programs are executed by a Java interpreter, having a program crash simply means that it terminates abnormally and prematurely. It doesn't mean that the Java interpreter will crash. In effect, the interpreter catches any exceptions that are not caught by the program. The interpreter responds by terminating the program. In many other languages, a crashed program will often crash the entire system and freeze the computer until it is restarted. With Java, such system crashes should be impossible -- which means that when they happen, you have the satisfaction of blaming the system rather than your own program.

When an exception occurs, it is actually an object that is thrown. This object can carry information (in its instance variables) from the point where the exception occurred to the point where it is caught and handled. This information typically includes an error message describing what happened to cause the exception, but it could also include other data. The object thrown by an exception must be an instance of the class Throwable or of one of its subclasses. In general, each different type of exception is represented by its own subclass of Throwable. Throwable has two direct subclasses, Error and Exception. These two subclasses in turn have many other predefined subclasses. In addition, a programmer can create new exception classes to represent new types of exceptions.

Most of the subclasses of the class Error represent serious errors within the Java virtual machine that should ordinarily cause program termination because there is no reasonable way to handle them. You should not try to catch and handle such errors. An example is the ClassFormatError, which occurs when the Java virtual machine finds some kind of illegal data in a file that is supposed to contain a compiled Java class. If that class was supposed to be part of the program, then there is really no way for the program to proceed.

Subclasses of Exception represent exceptions that are meant to be caught. In many cases, these are exceptions that might naturally be called "errors," but they are errors in the program, or in input data, that a programmer can anticipate and possibly respond to in some reasonable way. (You have to avoid the temptation of saying, "Well, I'll just put a thing here to catch all the errors that might occur, so my program won't crash." If you don't have a reasonable way to respond to the error, it's usually best just to terminate the program, because trying to go on will probably only lead to worse things down the road -- in the worst case, a program that gives an incorrect answer without giving you any indication that the answer might be wrong!)

The class Exception has its own subclass, RuntimeException. This class groups together many common exceptions such as ArithmeticException, which occurs for example when there is an attempt to take the square root of a negative number, and NullPointerException, which occurs when there is an attempt to use a null reference in a context when an actual object reference is required. RuntimeExceptions and Errors share the property that a program can simply ignore the possibility that they might occur. ("Ignoring" here means that you are content to let your program crash if the exception occurs.) For example, a program does this every time it uses Math.sqrt() without making arrangements to catch a possible ArithmeticException. For all other exception classes besides Error, RuntimeException, and their subclasses, exception-handling is "mandatory" in a sense that I'll discuss below.

The following diagram is a class hierarchy showing the class Throwable and just a few of its subclasses. Classes that require mandatory exception-handling are shown in red.

(Partial class hierarchy for Throwable objects)

To handle exceptions in a Java program, you need a try statement. The idea is that you tell the computer to "try" to execute some commands. If it succeeds, all well and good. But if an exception is thrown during the execution of those commands, you can catch the exception and handle it. For example,

         try {
            d = Math.sqrt(b*b - 4*a*c);
            r1 = (-b + d) / (2*a);
            r2 = (-b - d) / (2*a);
            console.putln("The roots are " + r1 + " and " + r2);
         catch ( ArithmeticException e ) {
            console.putln("There are no real roots.");

The computer tries to execute the block of statements following the word "try". If no exception occurs during the execution of this block, then the "catch" part of the statement is simply ignored. However, if an ArithmeticException occurs, then the computer jumps immediately to the block of statements labeled "catch (ArithmeticException e)". This block of statements is said to be a exception handler for ArithmeticExceptions. By handling the exception in this way, you prevent it from crashing the program.

You might notice that there are some other possible sources of error in this try statement. For example, if the value of the variable a is zero, you would probably expect the division by zero to produce an error. The reality is a bit surprising: If the numbers that are being divided are of type int, then division by zero will indeed throw an ArithmeticException. However, no arithmetic operations with floating-point numbers will ever produce an exception. Instead, the double type includes a special value called not-a-number to represent the result of an illegal operation. When this value is printed out, it is written as "NaN" -- which is hardly what you would like to see in the output!

Another possible error in this example is even more subtle: If the value of the variable console is null, then a NullPointerException will be thrown when the console is referenced in the last line of the try block. You could catch such an exception by adding another catch clause to the try statement:

         try {
            d = Math.sqrt(b*b - 4*a*c);
            r1 = (-b + d) / (2*a);
            r2 = (-b - d) / (2*a);
            console.putln("The roots are " + r1 + " and " + r2);
         catch ( ArithmeticException e ) {
            console.putln("There are no real roots.");
         catch ( NullPointerException e ) {
            System.out.println("Programming error! " + e.getMessage());

I haven't tried to use the console in the handler for NullPointerExceptions, because it's likely that the value of console is itself the problem. In fact, it would almost surely be better in this case just to let the program crash! This is a case where careful programming is better than exception handling: Just be sure that your program assigns a non-null value to console before it is used. However, this excample does show how multiple catch clauses can be used with one try block. This example also shows what that little "e" is doing in the catch clauses. The e is actually a variable name. (You can use any name you like.) Recall that when an exception occurs, it is actually an object that is thrown. Before executing a catch clause, the computer sets this variable to refer to the exception object that is being caught. This object contains information about the exception. In particular, every exception object includes an error message, with can be retrieved using the object's getMessage() method, as is done in the above example.

The example I've given here is not particularly realistic. You are more likely to use an if statement than to use exception-handling to guard against taking the square root of a negative number. You would certainly resent it if the designers of Java forced you to set up a try...catch statement every time you wanted to take a square root. This is why handling of potential RuntimeExceptions is not mandatory. There are just too many things that might go wrong! (This also shows that exception-handling does not solve the problem of program robustness. It just gives you a tool that will in many cases let you approach the problem in a more organized way.)

The syntax of a try statement is a little more complicated than I've indicated so far. The syntax can be described as


where, as usual, the statement can be a block of statements enclosed between { and }. The try statement can include zero or more catch clauses and, optionally, a finally clause. (The statement must include either a finally clause or at least one catch clause.) The syntax for a catch clause is

       catch ( exception-class-name variable-name )

and the syntax for a finally clause is


The semantics of the finally clause is that the statement or block of statements in the finally clause is guaranteed to be executed as the last step in the execution of the try statement, whether or not any exception occurs and whether or not any exception that does occur is caught and handled. The finally clause is meant for doing essential cleanup that under no circumstances should be omitted. You'll see an example of this later in the chapter.

There are times when it makes sense for a program to deliberately throw an exception. This is the case when the program discovers some sort of exceptional or error condition, but there is no reasonable way to handle the error at the point where the problem is discovered. The program can throw an exception in the hope that some other part of the program will catch and handle the exception.

To throw an exception, use a throw statement. The syntax of the throw statement is

       throw exception-object ;

The exception-object must be an object belonging to one of the subclasses of Throwable. Usually, it will in fact belong to one of the subclasses of Exception. In most cases, it will be a newly constructed object created with the new operator. For example:

        throw new ArithmeticException("Division by zero");

The parameter in the constructor becomes the error message in the exception object.

When an exception is thrown during the execution of a subroutine and the exception is not handled in the same subroutine, then the subroutine is terminated (after the execution of any pending finally clauses). Then the routine that called that subroutine gets a chance to handle that exception. If it doesn't do so, then it also is terminated and the routine that called it gets the next shot at the exception. The exception will crash the program only if it passes up through the entire chain of subroutine calls without being handled.

A subroutine that can throw an exception can announce this fact by adding the phrase "throws exception-class-name" to the specification of the routine. For example:

       static double root(double A, double B, double C) 
                                   throws ArithmeticException {
              // returns the larger of the two roots of
              // the quadratic equation A*x*x + B*x + C = 0
           double d = Math.sqrt(b*b - 4*a*c);  // might throw an exception!
           if (a == 0)
              throw new ArithmeticException("Division by zero.");
           return  (-b + d) / (2*a);

In this case, declaring that root() can throw an ArithmeticException is just a courtesy to potential users of this routine. This is because handling of ArithmeticExceptions is not mandatory. A routine can throw an ArithmeticException without announcing the possibility. And the user of such a routine is free either to catch or ignore such an exception.

For those exception classes that require mandatory handling, the situation is different. If a routine can throw such an exception, that fact must be announced in a throws clause in the routine definition. Failing to do so is a syntax error that will be reported by the compiler.

Suppose you call a routine that can possibly throw an exception of a type requires mandatory handling. In that case, you must handle the exception in one of two ways. You can call the routine inside a try statement, and use a catch clause to catch the exception if it occurs. If you don't do this, then, since calling the offending routine might implicitly throw the same exception in your own routine, you have to add an appropriate throws clause to your own routine. If you don't handle the exception one way or another, it will be considered a syntax error, and the compiler will not accept your program. Exception-handling is mandatory in this sense for any exception class that is not a subclass of either Error or RuntimeException.

Among the exceptions that require mandatory handling are several that can occur when using Java's input/output routines. This means that you can't even use these routines unless you understand something about exception-handling. The rest of this chapter deals with input/output and uses exception-handling extensively.

[ Next Section | Previous Chapter | Chapter Index | Main Index ]