More on Exception Handling

More on Exception Handling

A handler for an exception is found by propagating the exception backward through a chain of method calls, starting from the current method. Java’s exception-handling model is based on three operations: declaring an exception, throwing an exception, and catching an exception, as shown in figure below.

Declaring Exceptions

In Java, the statement currently being executed belongs to a method. The Java interpreter invokes the main method to start executing a program. Every method must state the types of checked exceptions it might throw. This is known as declaring exceptions. Because system errors and runtime errors can happen to any code, Java does not require that you declare Error and RuntimeException (unchecked exceptions) explicitly in the method. However, all other exceptions thrown by the method must be explicitly declared in the method header so that the caller of the method is informed of the exception.

To declare an exception in a method, use the throws keyword in the method header, as in this example:

public void myMethod() throws IOException

The throws keyword indicates that myMethod might throw an IOException. If the method might throw multiple exceptions, add a list of the exceptions, separated by commas, after throws:

public void myMethod()
throws Exception1, Exception2, …, ExceptionN

If a method does not declare exceptions in the superclass, you cannot override it to declare exceptions in the subclass.

Throwing Exceptions

A program that detects an error can create an instance of an appropriate exception type and throw it. This is known as throwing an exception. Here is an example: Suppose the program detects that an argument passed to the method violates the method contract (e.g., the argument must be nonnegative, but a negative argument is passed); the program can create an instance of IllegalArgumentException and throw it, as follows:

IllegalArgumentException ex =
new IllegalArgumentException(“Wrong Argument”);
throw ex;

Or, if you prefer, you can use the following:

throw new IllegalArgumentException(“Wrong Argument”);

IllegalArgumentException is an exception class in the Java API. In general, each exception class in the Java API has at least two constructors: a no-arg constructor, and a constructor with a String argument that describes the exception. This argument is called the exception message, which can be obtained using getMessage(). The keyword to declare an exception is throws, and the keyword to throw an exception is throw.

Catching Exceptions

You now know how to declare an exception and how to throw an exception. When an exception is thrown, it can be caught and handled in a try-catch block, as follows:

try {
statements; // Statements that may throw exceptions
}
catch (Exception1 exVar1) {
handler for exception1;
}
catch (Exception2 exVar2) {
handler for exception2;
}

catch (ExceptionN exVarN) {
handler for exceptionN;
}

If no exceptions arise during the execution of the try block, the catch blocks are skipped.

If one of the statements inside the try block throws an exception, Java skips the remaining statements in the try block and starts the process of finding the code to handle the exception. The code that handles the exception is called the exception handler; it is found by propagating the exception backward through a chain of method calls, starting from the current method. Each catch block is examined in turn, from first to last, to see whether the type of the exception object is an instance of the exception class in the catch block. If so, the exception object is assigned to the variable declared, and the code in the catch block is executed. If no handler is found, Java exits this method, passes the exception to the method that invoked the method, and continues the same process to find a handler. If no handler is found in the chain of methods being invoked, the program terminates and prints an error message on the console. The process of finding a handler is called catching an exception.

Suppose the main method invokes method1, method1 invokes method2, method2 invokes method3, and method3 throws an exception, as shown in Figure below. Consider the following scenario:

If the exception type is Exception3, it is caught by the catch block for handling exception ex3 in method2. statement5 is skipped, and statement6 is executed.
If the exception type is Exception2,** method2** is aborted, the control is returned to method1, and the exception is caught by the catch block for handling exception ex2 in method1. statement3 is skipped, and statement4 is executed.
If the exception type is Exception1, method1 is aborted, the control is returned to the main method, and the exception is caught by the catch block for handling exception ex1 in the main method. statement1 is skipped, and statement2 is executed.
If the exception type is not caught in method2, method1, or main, the program terminates, and statement1 and statement2 are not executed.

Various exception classes can be derived from a common superclass. If a catch block catches exception objects of a superclass, it can catch all the exception objects of the subclasses of that superclass.

The order in which exceptions are specified in catch blocks is important. A compile error will result if a catch block for a superclass type appears before a catch block for a subclass type. For example, the ordering in (a) on the next page is erroneous, because RuntimeException is a subclass of Exception. The correct ordering should be as shown in (b).

Java forces you to deal with checked exceptions. If a method declares a checked exception (i.e., an exception other than Error or RuntimeException), you must invoke it in a try-catch block or declare to throw the exception in the calling method. For example, suppose that method p1 invokes method p2, and p2 may throw a checked exception (e.g., IOException); you have to write the code as shown in (a) or (b) below.

You can use the multi-catch feature to simplify coding for the exceptions
with the same handling code. The syntax is:

catch (Exception1 | Exception2 | … | Exceptionk ex) {
// Same code for handling these exceptions
}

Each exception type is separated from the next with a vertical bar (|). If one of the exceptions is caught, the handling code is executed.

Getting Information from Exceptions

An exception object contains valuable information about the exception. You may use the following instance methods in the java.lang.Throwable class to get information regarding the exception, as shown in Figure below. The printStackTrace() method prints stack trace information on the console. The getStackTrace() method provides programmatic access to the stack trace information printed by printStackTrace().

The program below gives an example that uses the methods in Throwable to display exception information. Line 7 invokes the sum method to return the sum of all the elements in the array. There is an error in line 26 that causes the ArrayIndexOutOfBoundsException, a subclass of IndexOutOfBoundsException. This exception is caught in the try-catch block. Lines 10, 11, and 12 display the stack trace, exception message, and exception object and message using the printStackTrace(), getMessage(), and toString() methods. Line 15 brings stack trace elements into an array. Each element represents a method call. You can obtain the method (line 17), class name (line 18), and exception line number (line 19) for each element.

`java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
at demo.TestException.sum(TestException.java:27)
at demo.TestException.main(TestException.java:7)

Index 5 out of bounds for length 5

java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5

Trace Info Obtained from getStackTrace
method sum(demo.TestException:27)
method main(demo.TestException:7)`

Example: Declaring, Throwing, and Catching Exceptions

This example demonstrates declaring, throwing, and catching exceptions by modifying the setRadius method in the Circle class in, CircleWithPrivateDataFields.java (here). The new setRadius method throws an exception if the radius is negative.

The program below defines a new circle class named CircleWithException, which is the same as CircleWithPrivateDataFields except that the setRadius(double newRadius) method throws an IllegalArgumentException if the argument newRadius is negative.

package demo;

public class CircleWithException {
/** The radius of the circle */
private double radius;

/** The number of the objects created */
private static int numberOfObjects = 0;

/** Construct a circle with radius 1 */
public CircleWithException() {
this(1.0);
}

/** Construct a circle with a specified radius */
public CircleWithException(double newRadius) {
setRadius(newRadius);
numberOfObjects++;
}

/** Return radius */
public double getRadius() {
return radius;
}

/** Set a new radius */
public void setRadius(double newRadius) throws IllegalArgumentException {
if(newRadius > 0)
radius = newRadius;
else
throw new IllegalArgumentException(“Radius cannot be negative”);
}

/** Return numberOfObjects */
public static int getNumberOfObjects() {
return numberOfObjects;
}

/** Return the area of this circle */
public double findArea() {
return radius * radius * 3.14159;
}
}

A test program that uses the new Circle class is given in the program below.

The original Circle class remains intact except that the class name is changed to CircleWithException, a new constructor CircleWithException(newRadius) is added, and the setRadius method now declares an exception and throws it if the radius is negative.

The setRadius method declares to throw IllegalArgumentException in the method header (lines 27–32 in CircleWithException.java). The CircleWithException class would still compile if the throws IllegalArgumentException clause (line 27) were removed from the method declaration, since it is a subclass of RuntimeException and every method can throw RuntimeException (an unchecked exception) regardless of whether it is declared in the method header.

The test program creates three CircleWithException objects—c1, c2, and c3—to test how to handle exceptions. Invoking new CircleWithException(-5) (line 8 in TestCircleWithException.java) causes the setRadius method to be invoked, which throws an IllegalArgumentException, because the radius is negative. In the catch block, the type of the object ex is IllegalArgumentException, which matches the exception object thrown by the setRadius method, so this exception is caught by the catch block.

The exception handler prints a short message, ex.toString() (line 12 in TestCircleWithException.java), about the exception, using System.out.println(ex).

Note that the execution continues in the event of the exception. If the handlers had not caught the exception, the program would have abruptly terminated.

The test program would still compile if the try statement were not used, because the method throws an instance of IllegalArgumentException, a subclass of RuntimeException (an unchecked exception). If a method throws an exception other than RuntimeException or Error, the method must be invoked within a try-catch block.