Tuesday, December 2, 2008

Nested try Statements

Nested try Statements

 

The try statement can be nested. That is, a try statement can be inside the block of another try. Each time a try statement is entered, the context of that exception is pushed on the stack. If an inner try statement does not have a catch handler for a particular exception, the stack is unwound and the next try statement's catch handlers are inspected for a match. This continues until one of the catch statements succeeds, or until all of the nested try statements are exhausted. If no catch statement matches, then the Java run-time system will handle the exception.

 

Here is an example that uses nested try statements:

 

// An example of nested try statements.

class NestTry {

public static void main(String args[]) {

try {

int a = args.length;

/* If no command-line args are present,

the following statement will generate

a divide-by-zero exception. */

int b = 42 / a;

System.out.println("a = " + a);

try { // nested try block

/* If one command-line arg is used,

then a divide-by-zero exception

will be generated by the following code. */

if(a==1) a = a/(a-a); // division by zero

/* If two command-line args are used,

then generate an out-of-bounds exception. */

if(a==2) {

int c[] = { 1 };

c[42] = 99; // generate an out-of-bounds exception

}

} catch(ArrayIndexOutOfBoundsException e) {

System.out.println("Array index out-of-bounds: " + e);

}

} catch(ArithmeticException e) {

System.out.println("Divide by 0: " + e);

}

}

}

 

As you can see, this program nests one try block within another. The program works as follows. When you execute the program with no command-line arguments, a divide-by-zero exception is generated by the outer try block. Execution of the program by one command-line argument generates a divide-by-zero exception from within the nested try block. Since the inner block does not catch this exception, it is passed on to the outer try block, where it is handled. If you execute the program with two command-line arguments, an array boundary exception is generated from within the inner try block.

 

Here are sample runs that illustrate each case:

 

C:\>java NestTry

Divide by 0: java.lang.ArithmeticException: / by zero

C:\>java NestTry One

a = 1

Divide by 0: java.lang.ArithmeticException: / by zero

C:\>java NestTry One Two

a = 2

Array index out-of-bounds:

java.lang.ArrayIndexOutOfBoundsException

 

Nesting of try statements can occur in less obvious ways when method calls are involved.

 

For example, you can enclose a call to a method within a try block. Inside that method is another try statement. In this case, the try within the method is still nested inside the outer try block, which calls the method.

 

Here is the previous program recoded so that the nested try block is moved inside the method nesttry( ):

GE

/* Try statements can be implicitly nested via

calls to methods. */

class MethNestTry {

static void nesttry(int a) {

try { // nested try block

/* If one command-line arg is used,

then a divide-by-zero exception

will be generated by the following code. */

if(a==1) a = a/(a-a); // division by zero

/* If two command-line args are used,

then generate an out-of-bounds exception. */

if(a==2) {

int c[] = { 1 };

c[42] = 99; // generate an out-of-bounds exception

}

} catch(ArrayIndexOutOfBoundsException e) {

System.out.println("Array index out-of-bounds: " + e);

}

}

public static void main(String args[ ]) {

try {

int a = args.length;

/* If no command-line args are present,

the following statement will generate

a divide-by-zero exception. */

int b = 42 / a;

System.out.println("a = " + a);

nesttry(a);

} catch(ArithmeticException e) {

System.out.println("Divide by 0: " + e);

}

}

}

 

The output of this program is identical to that of the preceding example.

 

throw

 

So far, you have only been catching exceptions that are thrown by the Java run-time system. However, it is possible for your program to throw an exception explicitly, using the throw statement.

 

The general form of throw is shown here:

 

throw ThrowableInstance;

 

Here, ThrowableInstance must be an object of type Throwable or a subclass of Throwable. Simple types, such as int or char, as well as non-Throwable classes, such as String and Object, cannot be used as exceptions. There are two ways you can obtain a Throwable object: using a parameter into a catch clause, or creating one with the new operator.

 

The flow of execution stops immediately after the throw statement; any subsequent statements are not executed. The nearest enclosing try block is inspected to see if it has a catch statement that matches the type of the exception. If it does find a match, control is transferred to that statement. If not, then the next enclosing try statement is inspected, and so on. If no matching catch is found, then the default exception handler halts the program and prints the stack trace.

 

Here is a sample program that creates and throws an exception. The handler that catches the exception rethrows it to the outer handler.

 

// Demonstrate throw.

class ThrowDemo {

static void demoproc() {

try {

throw new NullPointerException("demo");

} catch(NullPointerException e) {

System.out.println("Caught inside demoproc.");

throw e; // rethrow the exception

}

}

public static void main(String args[ ]) {

try {

demoproc();

} catch(NullPointerException e) {

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

}

}

}

 

  

This program gets two chances to deal with the same error. First, main( ) sets up an exception context and then calls demoproc( ). The demoproc( ) method then sets up another exception-handling context and immediately throws a new instance of NullPointerException, which is caught on the next line. The exception is then rethrown.

 

Here is the resulting output:

 

Caught inside demoproc.

Recaught: java.lang.NullPointerException: demo

 

The program also illustrates how to create one of Java's standard exception objects.

 

Pay close attention to this line:

 

throw new NullPointerException("demo");

 

Here, new is used to construct an instance of NullPointerException. All of Java's built-in run-time exceptions have at least two constructors: one with no parameter and one that takes a string parameter. When the second form is used, the argument specifies a string that describes the exception. This string is displayed when the object is used as an argument to print( ) or println( ). It can also be obtained by a call to getMessage( ), which is defined by Throwable.

 

throws

 

If a method is capable of causing an exception that it does not handle, it must specify this behavior so that callers of the method can guard themselves against that exception. You do this by including a throws clause in the method's declaration. A throws clause lists the types of exceptions that a method might throw. This is necessary for all exceptions, except those of type Error or RuntimeException, or any of their subclasses. All other exceptions that a method can throw must be declared in the throws clause. If they are not, a compile-time error will result.

 

This is the general form of a method declaration that includes a throws clause:

 

type method-name(parameter-list) throws exception-list

{

// body of method

}

 

Here, exception-list is a comma-separated list of the exceptions that a method can throw.

 

Following is an example of an incorrect program that tries to throw an exception that it does not catch.

 

Because the program does not specify a throws clause to declare this fact, the program will not compile.

 

// This program contains an error and will not compile.

class ThrowsDemo {

static void throwOne() {

System.out.println("Inside throwOne.");

throw new IllegalAccessException("demo");

}

public static void main(String args[]) {

throwOne();

}

}

 

To make this example compile, you need to make two changes. First, you need to declare that throwOne( ) throws IllegalAccessException. Second, main( ) must define a try/catch statement that catches this exception.

 

The corrected example is shown here:

 

// This is now correct.

class ThrowsDemo {

static void throwOne() throws IllegalAccessException {

System.out.println("Inside throwOne.");

throw new IllegalAccessException("demo");

}

public static void main(String args[]) {

try {

throwOne();

} catch (IllegalAccessException e) {

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

}

}

}

 

Here is the output generated by running this example program:

 

inside throwOne caught java.lang.IllegalAccessException: demo

No comments: