CMPS 144
Exceptions

Acknowledgement: This material is derived largely from Chapter 2 of Koffman and Wolfgang.

Errors in Computer Programs

In programming, there are two major categories of errors: syntax and logic. Each programming language has a syntax, i.e., rules that govern which strings of characters qualify as well-formed programs, as opposed to ill-formed programs. A program containing one or more syntax errors is ill-formed. Such an error is analogous to a grammatical error in a sentence written in a natural language such as English. In the most strict sense, the intended meaning of a program having one or more such errors is undiscernible, just as is a sentence that has a grammatical error.

Note: Programs written in some languages (e.g., BASIC, perl, php, LISP) are usually interpreted, in which case a syntax error is typically not found until the program is running: specifically, when the statement in which the error occurs is to be executed. Hence, if a syntax error occurs in a rarely-used section of code, a program might be executed hundreds or thousands of times before the error is detected. However, for at least some of these languages software tools exist for checking the syntax of a program.

For languages that are compiled (e.g., Java, Ada, COBOL, FORTAN, Pascal), a program cannot be executed until it is free of syntax errors. End of Note.

A logic error, on the other hand, is the underlying cause of a flaw in the behavior of an executing program.

Alarmingly, students often have the impression that, once they have removed all syntax errors from a program, they are done, or at least close to it. In fact, it is usually the case that detecting and repairing errors in logic takes far longer than repairing the syntax errors that appeared in the student's original version of the program.

Often, unfortunately, logic errors lead to a program producing flawed output that goes undetected by the humans who make use of that output.

A more fortunate scenario is one in which a logic error leads to a program attempting to perform an illegal operation of some kind, which it can detect itself. (This is often referred to as a run-time error.) Examples include dividing by zero, attempting to access a non-existent array element ("array index out of bounds"), attempting to invoke a method upon a non-existent object ("null pointer exception"), attempting to interpret a non-numeric character string as a number, and attempting to open (for input) a nonexistent file.

Exceptions in Java

Java responds to a run-time error by throwing an exception. The Java API includes a number of different exception classes. Part of the exception class hierarchy is as follows:

For more details, go here.

There are two categories of exceptions: checked and unchecked. Generally speaking, the former are ones for which the programmer is not responsible, in some sense. Examples include I/O errors. The latter are usually due to programmer error.

Instances of subclasses of RunTimeException and Error are unchecked exceptions; all others are checked. In a Java class, any method whose execution could result in a checked exception being thrown (but not caught) must announce this fact in its heading, using a throws clause, as in

public void doSomething(int k) throws FileNotFoundException, EOFException {
    ...
}

Catching, Handling, and Throwing Exceptions in Java

One of the benefits of exception handling (which exists in Java and in some other languages, including Ada) is that it provides a means for the programmer to make software more robust by allowing a program to recover from a run-time error rather than "crashing" (i.e., terminating abnormally).

When, during execution of a Java program, the JVM (Java Virtual Machine) detects a run-time error, it throws an exception. (Exactly which kind depends upon what kind of run-time error occurred.) In the spirit of object-oriented programming, an exception is an object that is an instance of one of Java's exception classes (i.e., one of the subclasses of Throwable) or an instance of some exception class created by an application programmer. (Yes, you can create your own exception classes!)

The default behavior resulting from the throwing of an exception is for an error message to be displayed (indicating which kind of exception was thrown and which line of code caused it) and for execution of the program to terminate.

This behavior can be overidden by catching and handling the exception by using a try-catch-finally structure, which is just one more kind of structured command in Java, analogous to a while loop or an if-else statement. The general syntactic form is as follows:

try {
   < statement-block-0 > //possibly causing an exception to be thrown
}
catch (<exception-type-1> <var-name>) {
   < statement-block-1 >    // to handle exceptions of type 1
}
catch (<exception-type-2> <var-name>) {
   < statement-block-2 >    // to handle exceptions of type 2
}
...
...
catch (<exception-type-n> <var-name>) {
   < statement-block-n >    // to handle exceptions of type n
}
finally {
   < statement-block-(n+1) >
}

When execution reaches a try block, the code embedded there (statement-block-0 above) is executed. If, during its execution, an exception is thrown, the catch blocks are examined, from top to bottom, until one is reached that indicates an exception type matching that of the thrown exception. At that point, the corresponding block of code (statement-block-k for some k in the range 1..n above) is executed. (This block of code is the exception handler.) Following that, the code in the finally block (which is optional) (statement-block-(n+1) above) is executed. Execution continues with whatever follows the finally block.

In the case that none of the catch blocks indicates an exception type that matches that of the exception that was thrown, the exception is not handled. This means that the current method aborts (but only after executing the code in the finally block) and the exception is "thrown upward" to its caller, possibly to be handled there.

For examples of handling exceptions, see the class SqRootDriver.

A method may also explicitly throw an exception using a throw command. Assuming (as would be the usual case) that the thrown exception is not handled within the method itself, the method's execution aborts and the exception is "thrown up" (or "propagated") to the method's caller, possibly to be handled there. For an example of throwing an exception, see the class SquareRootCalc (which is a companion to the SqRootDriver class mentioned above).

The syntax for throwing an exception is

throw < reference-to-exception-object >;

A typical example is

throw new IllegalArgumentException("Argument must be positive");

Here, as is typical, the reference to the exception object is generated by the keyword new followed by an invocation of an appropriate constructor.