Monday, January 25, 2016

Topic 29: Exception Handling in Java

  • An exception is a run-time error.  

  • 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 our 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

Program statements that we want to monitor for exceptions are contained within a try block. If an exception occurs within the try block, it is thrown. Our 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 after a try block completes 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 after try block ends
}


Here, ExceptionType is the type of exception that has occurred.

  • 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:


  • One branch is headed by Exception. This class is used for exceptional conditions that user programs should catch. This is also the class that we will subclass to create your own custom exception types.

  • The other branch is topped by Error, which defines exceptions that are not expected to be caught under normal circumstances by our 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.

It is useful to see what happens when we don’t handle Exceptions. When the Java run-time system detects an exception, it constructs a new exception object and then throws this exception. This causes the execution of program to stop, because once an exception has been thrown, it must be caught by an exception handler and dealt with immediately. Any exception that is not caught by our program will ultimately be processed by the default handler. The default handler displays a string describing the exception, prints a stack trace(The call stack is quite useful for debugging, because it pinpoints the precise sequence of steps that led to the error) from the point at which the exception occurred, and terminates the program.

  • Although the default exception handler provided by the Java run-time system is useful for debugging, we will usually want to handle an exception our self. Doing so provides two benefits. First, it allows us to fix the error. Second, it prevents the program from automatically terminating.

To guard against and handle a run-time error, simply enclose the code that we want to monitor inside a try block. Immediately following the try block, include a catch clause that specifies the exception type that we wish to catch. Consider the code fragment:

int d = 0, a;
try
{
a = 42 / d;
System.out.println("This will not be printed.");
}
catch (ArithmeticException e)
{
// catch divide-by-zero error
System.out.println("Division by zero.");
}

The println( ) inside the try block is never executed. Reason is once an exception is thrown, program control transfers out of the try block into the catch block. Once the catch statement has executed, program control continues with the next line in the program following the entire try/catch mechanism.

A try and its catch statement form a unit.


We cannot use try on a single statement.

  • Throwable overrides the toString( ) method (defined by Object) so that it returns a string containing a description of the exception. We can display this description in a println( ) statement by simply passing the exception as an argument. For example:

catch (ArithmeticException e)
{
System.out.println("Exception: " + e);
}

This is useful while debugging.

  • In some cases, more than one exception could be raised by a single piece of code. To handle this type of situation, we can specify two or more catch clauses, each catching a different type of exception. When an exception is thrown, each catch statement is inspected in order, and the first one whose type matches that of the exception is executed. After one catch statement executes, the others are bypassed, and execution continues after the try/catch block.

When using multiple catch statements, it is important to remember that exception subclasses must come before any of their superclasses. This is because a catch statement that uses a superclass will catch exceptions of that type plus any of its subclasses. Thus, a subclass would never be reached if it came after its superclass and result in unreachable code which is an error.

  • The try statement can be nested. 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.

We 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.

  • It is possible for our 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.

There are two ways we can obtain a Throwable object: using a parameter in 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 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.

Sample code fragment that creates and throws an exception. The handler that catches the exception rethrows it to the outer handler.

try
{
try
{
throw new NullPointerException("Inner try");
}
catch(NullPointerException e)
{
System.out.println("Inner catch");
throw e; // rethrow the exception
}

}
catch(NullPointerException e)
{
System.out.println("Recaught: " + e);
}

  • 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. We 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. 

Sample code:

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);
}
}

  • finally creates a block of code that will be executed after a try/catch block has completed and before the code following the try/catch block.  

    The finally block will execute whether or not an exception is thrown. If an exception is thrown, the finally block will execute even if no catch statement matches the exception. 

    Any time a method is about to return to the caller from inside a try/catch block, via an uncaught exception or an explicit return statement, the finally clause is also executed just before the method returns. This can be useful for closing file handles and freeing up any other resources that might have been allocated at the beginning of a method with the intent of disposing of them before returning. 

    The finally clause is optional. However, each try statement requires at least one catch or a finally clause. 

    Sample code shows three methods that exit in various ways after executing their finally clauses:

// Through an exception out of the method.
static void procA()
{
try
{
System.out.println("inside procA");
throw new RuntimeException("demo");
}
finally
{
System.out.println("procA's finally");
}
}


// Return from within a try block.
static void procB()
{
try
{
System.out.println("inside procB");
return;
}
finally
{
System.out.println("procB's finally");
}
}


// Execute a try block normally.
static void procC()
{
try
{
System.out.println("inside procC");
}
finally
{
System.out.println("procC's finally");
}
}
public static void main(String args[])
{
try
{
procA();
}
catch (Exception e)
{
System.out.println("Exception caught");
}
procB();
procC();
}

  • Those exceptions defined by java.lang and subclasses of RuntimeException need not be included in any method’s throws list. These are called unchecked exceptions because the compiler does not check to see if a method handles or throws these exceptions. 

    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.

  • We can create a subclass of Exception. The Exception class does not define any methods of its own but inherit methods provided by its superclass Throwable. We may also wish to override one or more of these methods in exception classes that we create. Exception constructors:

Exception( )

Exception(String msg)

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 a carefully tailored description of the exception to be displayed.

// This program creates a custom exception type.
class MyException extends Exception
{
private int detail;
MyException(int a)
{
detail = a;
}
public String toString() //Override method in Exception class
{
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);
}
}
}

  • The chained exception feature allows us 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, we might also want to let the calling code know that the underlying cause was an I/O error. Chained exceptions let us handle this, and any other in which layers of exceptions exist.  
To allow chained exceptions, two constructors and two methods were added 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. The second form allows us to specify a description at the same time that we 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:  

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, we can associate a cause with an exception after the exception has been created. However, the cause exception can be set only once. Thus, we can call initCause( ) only once for each exception object. Furthermore, if the cause exception was set by a constructor, then we can’t set it again using initCause( ). In general, initCause( ) is used to set a cause for legacy exception classes that don’t support the two additional constructors described earlier. 
Here is an example that illustrates the mechanics of handling chained exceptions:

static void meth()
{
// create an exception
NullPointerException e = new NullPointerException("top layer");
// add a cause
e.initCause(new ArithmeticException("cause"));
throw e;
}
//Inside main()
try
{
meth();
}
catch(NullPointerException e)
{
// display top level exception
System.out.println("Caught: " + e);
// display cause exception
System.out.println("Original cause: " + e.getCause());
}

In this example, the top-level exception is NullPointerException. To it is added a cause exception, ArithmeticException. When the exception is thrown out of meth( ), 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.

 

Click for NEXT article.

 

Please feel free to correct me by commenting your suggestions and feedback.

No comments:

Post a Comment