We now consider the language primitives for realizing the exception handling framework in Java. As seen,
• exception objects are defined via class constructs that inherit from the Exception class;
Exception handling is dynamically enabled for statement blocks within a
Exception Handling 121
• exception handling is enabled within a try-block, with handlers indicated by catch clauses; and
• an exception condition is identified by a throw statement. (Some prede- fined exception conditions are thrown implicitly by the Java Virtual Ma- chine.)
9.3.1 Defining Exception Objects
The smallest exception object in Java merely extends from the Exception superclass, as outlined in the class definition for TransmissionError below:
class TransmissionError extends Exception { }
Logically, its objects have the same structure as objects of the Exception parent class which implements the basic functionality of exception objects. However, sub- class objects are appropriately tagged (as part of Java semantics), so that objects may be subsequently distinguished. It is often more productive to define a richer structure so that such exception objects may be accurately identified as well as easily manipu- lated.
Encapsulating exception conditions in objects allow for rich representations and functionality (via instance variables and methods, respectively). An appropriate design for such objects would reduce any overheads of coupling between conditions and handlers.
9.3.2 Defining Exception Handlers
Exception handlers are introduced by the catch-clause within a try-block prefix, of which the following code fragment is representative.
class TransmissionError extends Exception { int errorKind;
TransmissionError() { errorKind = 0; } TransmissionError(int x) { errorKind = x; } errorKind); } }
class X { ...
T m() { ...
try {
Y b = new Y();
b.performOperation();
...
} catch (TransmissionError t) { errorRecovery();
...
String toString() { return("Transmission Error: " +
122 Object-Oriented Programming and Java
Code within the try-block, as well as code dynamically invoked from there, are regions where exception handling is enabled. In the representative class defini- tion for X above, this region includes the statements within the try-block and other blocks within methods invoked from there such as performOperation().
Exception objects thrown from a try-block may be potentially caught by a catch-block exception handler as long as the type of the former matches that ex- pected for the latter as indicated by its formal parameter. In our previous example, the first handler catches TransmissionError exception objects, while the second handler catches IOException exception objects. Since objects of a subclass share the characteristics and are also considered objects of the base class, the said handlers will also cater to subclasses of TransmissionError and IOException objects, respectively.
The placement order of catch-blocks is significant. Due to the inheritance mechanism, catch-blocks work in a sieve-like manner, and handlers for subclasses should appear before handlers for superclasses. When exception objects are caught, control-flow is transferred to the exception handler concerned. Control-flow then resumes at the statement following the try-block; in our example, this is method n(). 9.3.3 Raising Exceptions
An exception condition is ultimately represented by an exception object derived from the predefined Exception class.1 A condition is made known by throwing an appropri- ate object via a throw statement, to be subsequently caught by an associated handler.
1 Actually, objects that can be thrown by the throw statement must be derived from the Throwable superclass. However, in addition to the Exception class, Throwable also includes the Error class which indicates serious problems that a reasonable application should not try to catch. As such, we will continue to work from the Exception class.
} catch (IOException e) { errorReport();
...
} n();
} }
Exception Handling 123
In the event that the thrown exception object does not match what is expected by event handlers, it is further propagated to the caller of the method which contains the try-block. This caller chain (which forms a back-trace) proceeds until the staticvoidmain() method is encountered. The predefined environment supplies a default exception handler that aborts program execution with a execution back-trace from the run-time stack, and an appropriate error message.
TransmissionError is the typical case of a programmer-defined exception condition. The Java API also contains various exception classes which are used within the API, for example, MalformedURL and thrown from methods such as per- formOperation(). These are accessible via the normal mechanisms. There is also a unique set of exceptions that is thrown directly from the Java Virtual Machine. For example, integer division is translated into an operator for the Java Virtual Machine.
Thus, if a 0 divisor is encountered in an expression, a DivideByZero exception is raised from the Java Virtual Machine without any corresponding throw-statement from the application code.