Saturday, May 7, 2011

ERROR HANDLING

Java uses Exceptions to represent error conditions.
Exception is actually a class in Java, and when a program creates an Exception, it creates a new
object, which is an instance of the Exception class. An Exception object can have information about what
went wrong, usually including an error message, and a “stack trace” showing which method created the error.
Having created an Exception object when something goes wrong, the program “throws” the Exception
using the key word throw. The JVM will print an error message and stop execution when a program throws
an Exception, unless the programmer has provided code to “catch” the exception and handle the error
condition in the program. This approach to handling program errors is called “exception handling” for obvious
reasons, and it’s a relatively modern idea.
One advantage of handling errors this way is that code to handle error conditions will be segregated into
code blocks separate from the main logic of the program. This makes it much easier to follow the intent of the
programmer, both in the main logic and in the error handling code.
Java provides many subclasses of Exception so that different problems result in different classes
of Exception objects being thrown. Some examples include FileNotFoundException,
NullPointerException, and NumberFormatException.
Java recognizes two types of Exceptions, checked and unchecked. The names come from what the Java
compiler does with them. The compiler “checks for” appropriate handling of checked Exceptions, but does
not check for handling of unchecked Exceptions.
An unchecked Exception represents an error that is probably too serious for an application to correct.
An example is the NullPointerException, which occurs when the program tries to access a variable
which should contain a reference to an object, but which contains null instead. The compiler assumes that such
an exception should cause the program to terminate with an error condition, so it does not check to see that the
program has code to handle that error condition itself.
A checked exception such as FileNotFoundException represents an error condition from which
the program potentially could recover. In the case of FileNotFoundException, for example, the
program could prompt the operator for a new file name and try again. If the compiler recognizes that a
method could encounter a checked exception, the compiler will require that the method either provide a
handler for that exception or declare with its own throws declaration that it can itself generate the checked
Exception.
The way to add exception handling to a Java program is to enclose program statements which might generate
an Exception within a try block. A try block begins with the key word try and an open curly brace.
At the end of the code being included in the try block is a close curly brace. Immediately following the try block
will be one or more catch blocks. A catch block contains the code to handle the error.
Let’s go back to our Automobile class and its accelerate() method. Instead of simply setting the
speed of the Automobile to its maximum value when the argument to the accelerate() method is too
large, we can have the Automobile class generate a special subclass of Exception appropriate to this
application. Here is the code for our new ExcessiveSpeedException class:


public class ExcessiveSpeedException extends Exception {
ExcessiveSpeedException( Automobile a ) {
super( "New speed exceeds maximum speed of "
+ a.toString() );
}
}
Our ExcessiveSpeedException inherits from Exception. The ExcessiveSpeedException
constructor expects an Automobile object as an argument. To take advantage of the message attribute inherited
from the superior class, it incorporates the toString() description of the Automobile into the message
that it passes to the constructor of the superior Exception class.
Now we can rewrite the accelerate() method of Automobile as follows:
public void accelerate ( double newSpeed )
throws ExcessiveSpeedException {
if( newSpeed > 70. )
throw new ExcessiveSpeedException( this );
speed = newSpeed;
}
The reference to this means that the particular instance of Automobile that generates the
ExcessiveSpeedException will be passed to the ExcessiveSpeedException constructor. Notice
that the method header for accelerate() now includes a declaration that the method can throw an
ExcessiveSpeedException. If you forget to include the declaration, the compiler will require such a
declaration when it sees that some statement within the method throws an ExcessiveSpeedException.
Finally, notice that we no longer require an else clause after the if statement. Once a method throws an
exception, execution stops, except for whatever code is ready to catch the Exception. Therefore, we no
longer need else to insure two non-overlapping paths of execution in response to the test in the if statement.
We can rewrite the AutomobileFactory class to handle the possible occurrence of an
ExcessiveSpeedException.
.
.
.
try {
family.accelerate( 120. );
sports.accelerate( 120. );
}
catch( ExcessiveSpeedException ex) {
System.out.println( ex.getMessage() );
}
System.out.println( family + " " + family.getSpeed() );
System.out.println( sports + " " + sports.getSpeed() );
}
In this case, the catch block simply reports the error, and the program continues on. With other errors, the
catch block might try to correct the problem, ask the user for a decision, or simply terminate the program by
calling System.exit().
The output from this version of AutomobileFactory looks like this:
3 Automobiles
2006 Kia Rio
2002 VW Passat
2005 Ford Mustang
New speed exceeds maximum speed of 2002 VW Passat
2002 VW Passat 0.0
2005 Ford Mustang 0.0

When AutomobileFactory tries to accelerate the VW to too great a speed, the Automobile class
throws an ExcessiveSpeedException which stops execution of accelerate() and transfers control
to the catch block. The catch block reports the problem by printing the message attribute of the Exception
object. When the catch block completes, the program continues, but the speeds of both Automobiles remain
0.0, because the path of execution never set the speed of either one.
There can be more than one catch block; in fact, you can have several. Each one can specify a particular
class of Exception to handle. That is important to segregating code for handling different kinds of problems.
If a method throws a FileNotFoundException, it may be easy to fix by asking the operator to enter the
file name again. On the other hand, if a read of the file fails and the method throws an IOException, it may
be difficult for the program to recover. In the first case the catch block may “soldier on,” and in the second case
the catch block may simply report the error and then call System.exit().
When more than one catch block follows a try block, the catch blocks should be ordered such that lowerlevel
Exception classes occur before higher-level, more general classes of Exceptions. When a method
throws an exception, the try block searches down the list of catch blocks until it finds a match between the
Exception class that was thrown and the Exception class declared in the catch block. The first acceptable
match will be invoked to handle the error. If the first catch block specifies objects of class Exception, the
most general class, the first catch block will handle all Exceptions, regardless of whatever other catch
blocks there may be. So, if a program wants to attempt to recover from a FileNotFoundException and
terminate on any other failure, the catch block for the FileNotFoundException should come before the
catch block for the class Exception.
There’s one more option. After the try block and all the catch blocks, a programmer can add a finally block.
A finally block contains code that will always be executed, whether an error occurs or not. A finally block is
a good place to put general “clean-up” code, like the statement to close a data base.
Here is the syntax for the try/catch/finally construction:
try {
.
. //main line logic goes here
.
}
catch( SpecificException ex ) {
.
. //handle SpecificException
.
}
catch( LessSpecificException ex ) {
.
. //handle LessSpecificException
.
}
catch( Exception ex ) {
.
. //handle everything else that might happen
.
}
finally {
.
. //tidy up — this code will always be executed
.
}
//If code is successful, or exception is caught and
// handled, execution will continue here.

Java’s error-handling mechanism is one of its great strengths. Exception handling with try/
catch/finally is very robust and leads to very supportable code. Since one can easily add application-specific
exception classes to support one’s own work, this approach is also very extendable.

No comments:

Post a Comment