Tuesday, November 11, 2008

Exception Handling

Exception Handling:

 

This Post examines Java's exception-handling mechanism. An exception is an abnormal condition that arises in a code sequence at run time. In other words, an exception is a run-time error. In computer languages that do not support exception handling, errors must be checked and handled manually—typically through the use of error codes, and so on. This approach is as cumbersome as it is troublesome.

 

Java's exception handling avoids these problems and, in the process, brings run-time error management into the object-oriented world. For the most part, exception handling has not changed since the original version of Java. However, Java 2, version 1.4 has added a new subsystem called the chained exception facility. This feature is described near the end of this Post.

 

Exception-Handling Fundamentals

A Java exception is an object that describes an exceptional (that is, error) condition that has occurred in a piece of code. When an exceptional condition arises, an object representing that exception is created and thrown in the method that caused the error.

 

That method may choose to handle the exception itself, or pass it on. Either way, at some point, the exception is caught and processed. Exceptions can be generated by the Java run-time system, or they can be manually generated by your code. Exceptions thrown by Java relate to fundamental errors that violate the rules of the Java language or the constraints of the Java execution environment. Manually generated exceptions are typically used to report some error condition to the caller of a method.

 

Java exception handling is managed via five keywords: try, catch, throw, throws, and finally. Briefly, here is how they work. Program statements that you want to monitor for exceptions are contained within a try block. If an exception occurs within the try block, it is thrown. Your code can catch this exception (using catch) and handle it in some rational manner. System-generated exceptions are automatically thrown by the Java run-time system. To manually throw an exception, use the keyword throw.

 

Any exception that is thrown out of a method must be specified as such by a throws clause. Any code that absolutely must be executed before a method returns is put in a finally block.

 

This is the general form of an exception-handling block:

 

try {

// block of code to monitor for errors

}

catch (ExceptionType1 exOb) {

// exception handler for ExceptionType1

}

catch (ExceptionType2 exOb) {

// exception handler for ExceptionType2

}

// ...

finally {

// block of code to be executed before try block ends

}

 

Here, ExceptionType is the type of exception that has occurred. The remainder of this following post describes how to apply this framework.

 

Exception Types

All exception types are subclasses of the built-in class Throwable. Thus, Throwable is at the top of the exception class hierarchy. Immediately below Throwable are two subclasses that partition exceptions into two distinct branches. One branch is headed by Exception. This class is used for exceptional conditions that user programs should catch. This is also the class that you will subclass to create your own custom exception types. There is an important subclass of Exception, called RuntimeException.

 

Exceptions of this type are automatically defined for the programs that you write and include things such as division by zero and invalid array indexing.

The other branch is topped by Error, which defines exceptions that are not expected to be caught under normal circumstances by your program.

 

Exceptions of type Error are used by the Java run-time system to indicate errors having to do with the run-time environment, itself. Stack overflow is an example of such an error.

 

This post will not be dealing with exceptions of type Error, because these are typically created in response to catastrophic failures that cannot usually be handled by your program.

 

Uncaught Exceptions

Before you learn how to handle exceptions in your program, it is useful to see what happens when you don't handle them. This small program includes an expression that intentionally causes a divide-by-zero error.

 

class Exc0 {

public static void main(String args[]) {

int d = 0;

int a = 42 / d;

}

}

 

When the Java run-time system detects the attempt to divide by zero, it constructs a new exception object and then throws this exception. This causes the execution of Exc0 to stop, because once an exception has been thrown, it must be caught by an exception handler and dealt with immediately.

 

In this example, we haven't supplied any exception handlers of our own, so the exception is caught by the default handler provided by the Java run-time system. Any exception that is not caught by your program will ultimately be processed by the default handler. The default handler displays a string describing the exception, prints a stack trace from the point at which the exception occurred, and terminates the program.

 

Here is the output generated when this example is executed.

 

java.lang.ArithmeticException: / by zero

at Exc0.main(Exc0.java:4)

 

Notice how the class name, Exc0; the method name, main; the filename, Exc0.java; and the line number, 4, are all included in the simple stack trace. Also, notice that the type of the exception thrown is a subclass of Exception called ArithmeticException, which more specifically describes what type of error happened. As discussed later in this Post, Java supplies several built-in exception types that match the various sorts of run-time errors that can be generated.

 

The stack trace will always show the sequence of method invocations that led up to the error.

 

For example, here is another version of the preceding program that introduces the same error but in a method separate from main( ):

 

class Exc1 {

static void subroutine() {

int d = 0;

int a = 10 / d;

}

public static void main(String args[]) {

Exc1.subroutine();

}

}

 

The resulting stack trace from the default exception handler shows how the entire call stack is displayed:

 

java.lang.ArithmeticException: / by zero

at Exc1.subroutine(Exc1.java:4)

at Exc1.main(Exc1.java:7)

 

As you can see, the bottom of the stack is main's line 7, which is the call to subroutine( ), which caused the exception at line 4. The call stack is quite useful for debugging, because it pinpoints the precise sequence of steps that led to the error.

No comments: