Tuesday, December 2, 2008

Java's Built-in & User Defined Exceptions

Java's Built-in Exceptions

 

Inside the standard package java.lang, Java defines several exception classes. A few have been used by the preceding examples. The most general of these exceptions are subclasses of the standard type RuntimeException. Since java.lang is implicitly imported into all Java programs, most exceptions derived from RuntimeException are automatically available. Furthermore, they need not be included in any method's throws list. In the language of Java, these are called unchecked exceptions because the compiler does not check to see if a method handles or throws these exceptions. The unchecked exceptions defined in java.lang are listed in Table 10-1. Table 10-2 lists those exceptions defined by java.lang that must be included in a method's throws list if that method can generate one of these exceptions and does not handle it itself. These are called checked exceptions. Java defines several other types of exceptions that relate to its various class libraries.

 

 

Creating Your Own Exception Subclasses

Although Java's built-in exceptions handle most common errors, you will probably want to create your own exception types to handle situations specific to your applications. This is quite easy to do: just define a subclass of Exception (which is, of course, a subclass of Throwable). Your subclasses don't need to actually implement anything—it is their existence in the type system that allows you to use them as exceptions.

 

The Exception class does not define any methods of its own. It does, of course, inherit those methods provided by Throwable. Thus, all exceptions, including those that you create, have the methods defined by Throwable available to them. They are shown in Table 10-3. Notice that several methods were added by Java 2, version 1.4. You may also wish to override one or more of these methods in exception classes that you create.

LANGUAGE

Method

Description

Throwable fillInStackTrace( )

Returns a Throwable object that contains a completed stack trace. This object can be rethrown.

Throwable getCause( )

Returns the exception that underlies the current exception. If there is no underlying exception, null is returned. Added by Java 2, version 1.4.

String getLocalizedMessage( )

Returns a localized description of the exception.

String getMessage( ) Returns a description of the exception.

String getMessage( )

Returns a description of the exception.

StackTraceElement[ ] getStackTrace( )

Returns an array that contains the stack trace, one element at a time as an array of StackTraceElement. The method at the top of the stack is the last method called before the exception was thrown. This method is found in the first element of the array.

The StackTraceElement class gives your program access to information about each element in the trace, such as its method name. Added by Java 2, version 1.4

Throwable initCause(Throwable causeExc)

Associates causeExc with the invoking exception as a cause of the invoking exception. Returns a reference to the exception. Added by Java 2, version 1.4

void printStackTrace( )

Displays the stack trace.

void printStackTrace(PrintStream

stream)

Sends the stack trace to the specified stream.

void printStackTrace(PrintWriter

stream)

 

Sends the stack trace to the specified stream.

 

void setStackTrace(StackTraceElement

elements[ ])

 

Sets the stack trace to the elements passed in elements. This method is for specialized applications, not normal use. Added by Java 2, version 1.4

String toString( )

Returns a String object containing a description of the exception. This method is called by println( ) when outputting a Throwable object.

Table 10-3. The Methods Defined by Throwable

 

The following example declares a new subclass of Exception and then uses that subclass to signal an error condition in a method. It overrides the toString( ) method, allowing the description of the exception to be displayed using println( ).

 

// This program creates a custom exception type.

class MyException extends Exception {

private int detail;

MyException(int a) {

detail = a;

}

public String toString() {

return "MyException[" + detail + "]";

}

}

class ExceptionDemo {

static void compute(int a) throws MyException {

System.out.println("Called compute(" + a + ")");

 

 

if(a > 10)

throw new MyException(a);

System.out.println("Normal exit");

}

public static void main(String args[]) {

try {

compute(1);

compute(20);

} catch (MyException e) {

System.out.println("Caught " + e);

}

}

}

 

This example defines a subclass of Exception called MyException. This subclass is quite simple: it has only a constructor plus an overloaded toString( ) method that displays the value of the exception. The ExceptionDemo class defines a method named compute( ) that throws a MyException object. The exception is thrown when compute( )'s integer parameter is greater than 10. The main( ) method sets up an exception handler for MyException, then calls compute( ) with a legal value (less than 10) and an illegal one to show both paths through the code.

 

Here is the result:

 

Called compute(1)

Normal exit

Called compute(20)

Caught MyException[20]

 

Chained Exceptions

Java 2, version 1.4 added a new feature to the exception subsystem: chained  exceptions. The chained exception feature allows you to associate another exception with an exception. This second exception describes the cause of the first   exception. For example, imagine a situation in which a method throws an ArithmeticException because of an attempt to divide by zero. However, the actual cause of the problem was that an I/O error occurred, which caused the divisor to be set improperly. Although the method must certainly throw an ArithmeticException, since that is the error that occurred, you might also want to let the calling code know that the underlying cause was an I/O error. Chained exceptions let you handle this, and any other situation in which layers of exceptions exist.

 

To allow chained exceptions, Java 2, version 1.4 added two constructors and two methods to Throwable. The constructors are shown here.

 

Throwable(Throwable causeExc)

Throwable(String msg, Throwable causeExc)

 

In the first form, causeExc is the exception that causes the current exception. That is, causeExc is the underlying reason that an exception occurred. The second form allows you to specify a description at the same time that you specify a cause exception. These two constructors have also been added to the Error, Exception,  and RuntimeException classes.

 

The chained exception methods added to Throwable are getCause( ) and initCause( ). These methods are shown in Table 10-3, and are repeated here for the sake of discussion.

 

Throwable getCause( )

Throwable initCause(Throwable causeExc)

 

The getCause( ) method returns the exception that underlies the current exception. If there is no underlying exception, null is returned. The initCause( ) method associates causeExc with the invoking exception and returns a reference to the exception. Thus, you can associate a cause with an exception after the exception has been created.

 

However, the cause exception can be set only once. Thus, you can call initCause() only once for each exception object. Furthermore, if the cause exception was set by a constructor, then you can't set it again using initCause( ).

 

In general, initCause( ) is used to set a cause for legacy exception classes which don't support the two additional constructors described earlier. At the time of this writing, most of Java's built-in exceptions, such as ArithmeticException, do not define the additional constructors.

 

Thus, you will use initCause( ) if you need to add an exception chain to these exceptions. When creating your own exception classes you will want to add the two chained-exception constructors if you will be using your exceptions in situations in which layered exceptions are possible.

 

Here is an example that illustrates the mechanics of handling chained exceptions.

 

// Demonstrate exception chaining.

class ChainExcDemo {

static void demoproc() {

// create an exception

NullPointerException e =

new NullPointerException("top layer");

// add a cause

e.initCause(new ArithmeticException("cause"));

throw e;

}

public static void main(String args[]) {

try {

demoproc();

} catch(NullPointerException e) {

// display top level exception

System.out.println("Caught: " + e);

// display cause exception

System.out.println("Original cause: " +

e.getCause());

}

}

}

 

The output from the program is shown here.

 

Caught: java.lang.NullPointerException: top layer

Original cause: java.lang.ArithmeticException: cause

 

In this example, the top-level exception is NullPointerException. To it is added a cause exception, ArithmeticException. When the exception is thrown out of demoproc( ), it is caught by main( ). There, the top-level exception is displayed, followed by the underlying exception, which is obtained by calling getCause( ). Chained exceptions can be carried on to whatever depth is necessary. Thus, the cause exception can, itself, have a cause. Be aware that overly long chains of exceptions may indicate poor design.

 

Chained exceptions are not something that every program will need. However, in cases in which knowledge of an underlying cause is useful, they offer an elegant solution.

 

Using Exceptions

Exception handling provides a powerful mechanism for controlling complex programs that have many dynamic run-time characteristics. It is important to think of try, throw, and catch as clean ways to handle errors and unusual boundary conditions in your program's logic. If you are like most programmers, then you probably are used to returning an error code when a method fails. When you are programming in Java, you should break this habit. When a method can fail, have it throw an exception. This is a cleaner way to handle failure modes.

 

One last point: Java's exception-handling statements should not be considered a general mechanism for non-local branching. If you do so, it will only confuse your code and make it hard to maintain.

No comments: