Java supports throw/catch style exceptions. This can result in much cleaner, more robust, and easier to debug code if used correctly. This note provides some background information on Java exceptions and then a few simple rules for using exceptions effectively.
In Java, exceptions are raised by throwing an exception object that
must be an instance of a class derived from
java.lang.Throwable. Generally a new exception class adds no new
methods or fields, but does provide two constructors, one that takes
no arguments and one thats a single string argument (a message to
associate with the exceptions). Figure 1, below,
shows a typical exception definition. However, it is prefectly ok to
add additional methods or fields to an exception class, such as an
error code, or other context information. It would also be ok the add
code to log exceptions in the exceptions constructors.
Java provides a number of
predefined exception classes, such as the class java.lang.RuntimeException used
in Figure 1. When catching an exception the catch statement specifies
the class of exception it will catch. Java will then direct any
exception of that class or any sub-class of that class to the
statements following the catch statement. (See Figure
2 below.)
Rationale: Exceptions are expensive to process. Also, It
usually takes more code to catch an exception than it does to test for
a result.
Rationale: Catching an exception that you don't handle masks
the real exception and makes debugging harder. So if you must catch
one at least print the stack trace so that the original exception
point and type will be revealed.
Rationale:Exceptions derived from RuntimeException or Error
do not have to be declared in interfaces which means that they can
pass through the call stack until they hit a point where they can be
really handled. This means that intermedite methods are not forced to
catch the exception and then re-throw it as another exception. This
reduces code bulk and give the best messages when debugging. Declaring
them is still a good idea because it helps others to understand your
interfaces.
Rationale: Gives a single point of change to introduce
logging or other such support.
Figure 1: A typical exception class definition
--------------------------------------------------------
package mypackage;
public class MyException extends java.lang.RuntimeException {
public MyException() {
super();
}
public MyException(String message) {
super(message);
}
}
--------------------------------------------------------
Figure 2: Sketch of a try block
--------------------------------------------------------
// ...
try {
// Some code that might raise MyException
} catch (MyException e) {
// This code will be executed only if a MyException
// instance is thrown.
// ... Code to handle the exception
}
// ... This will be executed after the try block even if a
// MyException exception is raise unless an uncaught exception is
// raised in either the "try" or "catch" parts of the block.
--------------------------------------------------------
Java requires that each method handle any exceptions raised in it
unless the exceptions are listed as ones that can be thrown by the
method, or unless the exceptions are derived from either
java.lang.Error or java.lang.RuntimeException. This is intended to
force a robust coding style where exceptions are always handled. (We
will come back to this issue later.)
A final comment about exceptions is that their typical implementation
design point tries to reduce to zero the cost of potential exceptions
at great increase to the cost of actually processing an exception. It
is too early to have hard data on how high-speed Java implemenations
will do this exactly, but experience with other languages indicates
that the difference between an expection return and a normal return
can be several orders of magnaitude. Simple measurements done with the
Java JDK 1.02 interpreter shows only a factor of 2, but the same test
run with the Cafe JIT shows a factor of about 100.
Guidelines
Figure 3: Processing an exceptions that is required
by an interface.
--------------------------------------------------------
void foo() {
try {
char c = System.in.read();
// ...
} catch (java.io.IOException e) {
e.printStackTrace();
throw new Error("Unexpected IOException");
}
}
--------------------------------------------------------