Exception Handling

No matter how well designed a program is, there is always a chance that an error will occur during its execution. You should anticipate errors and include in your program, code that will handle errors or exceptions when they arise. This code should be in place during the first stages of development. Our aim is to produce rock solid code.

One way to handle exceptions is to abort the program. For example, a common error is division by zero. You could write:

if ( divisor == 0 )
{
   System.out.println ( "ERROR: Division by Zero" );
   System.out.println ( "Aborting program" );
   System.exit (0);
}

What if your program is running the controls of a nuclear reactor? You do not have the option of aborting the program. In Java errors are handled by throwing and catching exceptions. Throwing an exception is a way of signaling that something has gone wrong. Catching an exception is a way of dealing with the exception. When you get an error condition you create an Exception object and pass it a message describing the error condition.

if ( divisor == 0 )
{  
  throw new Exception ("ERROR: Division by Zero");
}

In Java we use the try / catch / finally combination to handle exceptions.

try
{
  // Block of statements one of which may throw an exception

  if ( /* some condition is true */ )
    throw new ExceptionName();

}
catch ( Exception1 Parameter1 )
{
  // Statements to be executed if Exception1 is thrown
}
catch ( Exception2 Parameter2 )
{
  // Statements to be executed if Exception2 is thrown
}
finally
{
  // Optional block of statements that are executed
  // whether an exception is thrown or not
}
If an exception is thrown, the try block is exited and control does not return to it. Now every non void method must return a value. There is a conflict. We can place the return statement in the optional finally block. The other option is to place the return statement outside the try / catch statement altogether.

Syntax for throwing and catching

A try block must be followed by one or more catch blocks to handle particular types of exceptions. There cannot be any intervening code between the blocks. One could check for an error and throw an exception and handle the exception elsewhere in the code. The key element to realize in exception handling is that the exception handler - the catch block - is distinct from the code that throws the exception - the try block. The try block contains the normal algorithm. The catch block contains code for handling exceptional conditions.
if ( divisor == 0 )
  throw new ArithmeticException ( "ERROR: Division by Zero" );

... ... ...

try
{

}
catch ( ArithmeticException e )
{
  System.out.prinln ( e.getMessage() );
  e.printStackTrace();
  System.exit (0);
}

e.printStackTrace() method will print a trace of all the methods that were called leading up to the method that threw the exception.

An exception can only be thrown within a dynamically enclosing try block. This means the throw statement must fall within the dynamic scope of an enclosing try block. Dynamic scope refers to how the program executes. Look at what it actually does. Static scoping refers to how the program is written. Look at its definitions. Dynamic scoping can only be determined by running the program. You cannot necessarily determine it by reading the program.

When an exception is thrown, Java uses both static and dynamic scoping to find a catch clause to handle it. Java knows how the program is defined. This defines the static scope of its methods. Java also places a record of every method call the program makes on a method call stack. An important feature of the method call stack is that the current executing method is always represented by the top block in the method call stack.

When an exception occurs Java traces backward through the program until it finds an appropriate catch clause. The trace begins within the block that threw the exception. If the exception is not caught within the method in which it was thrown, Java uses the method call stack to search backward through the method calls that were made leading up to the exception.

Exception Hierarchy

There is an exception hierarchy. The root java.lang.Exception is located in the java.lang package. But not all subclasses are contained there. The IOException classes are contained in the java.io package. While others are in the java.net package.

There are two types of exceptions - checked and unchecked. A checked exception must either be caught within the method where it is thrown or at least declared within that method. These are exceptions that can be analyzed by the compiler. IOException is a checked exception and must be caught or declared.

Any method that contains an expression that may throw a checked exception must declare the exception. If a method calls another method that contains an expression that might throw a checked exception, then both methods must have a throws clause.

An unchecked exception belongs to the subclass Runtime Exception and does not have to be caught within your program. If uncaught they will be handled by the Java's default exception handler. The sub classes for RuntimeException are:

ArithmeticException Division by zero or some other kind of arithmetic problem
ArrayIndexOutOfBoundsException An array index is less than zero or greater than or equal to the array's length
FileNotFoundException Reference to a file that cannot be found
IllegalArgumentException Calling a method with an improper argument
IndexOutOfBoundsException An array or string index is out of bounds
NullPointerException Reference to an object that has not been instantiated
NumberFormatException Use of an illegal number format, such as when calling a method
StringIndexOutOfBoundsException A String index is less than zero or greater than or equal to the String's length

Unchecked exceptions are not checked by the compiler. The possibility that some statement or expression will lead to an ArithmeticException is extremely difficult to detect at compile time. The designers of Java decided that forcing programmers to declare such exceptions would not significantly improve the correctness of Java programs. Hence, unchecked exceptions do not have to be handled within a program. They do not have to be declared in a throws clause.

The java.lang.Exception class consists of just two constructors.

public class Exception extends Throwable
{
  public Exception ();
  public Exception ( String message );
}

The Throwable class is the superclass or parent of the Exception class. This is where the getMessage() and printStackTrace() methods are defined. Catch clauses should be arranged from the most specific to the most general. The Exception clause should always be the last in the sequence.

Creating and Throwing Your Own Exceptions

The Exception class can be extended to handle cases that are not already covered by Java's built-in exceptions. Exceptions that you define will be handled the same way by the Java interpreter but you will have to throw them yourself. The following example shows the design of an exception that can be used to validate that an integer is less than or equal to a certain maximum value.
public class IntOutOfRangeException extends Exception
{
  public IntOutOfRangeException ( int Bound )
  {
    super ( "The input value exceeds the bound " + Bound );
  }
}
The class extends Exception and consists of a constructor method. The argument passed to the superclass constructor is the message that will be returned by getMessage() when an instance of this exception is created.

The following piece of code shows how this new Exception is thrown.

if ( num > bound )
  throw new IntOutOfRangeException ( bound );

Handling Exceptions

There are three general ways to handle exceptions:

If the exceptional condition is serious enough to affect the correctness or integrity of the program, then unless the error can be fixed or unless the program cannot reasonably be terminated, it is better to report the error and terminate the program rather than allowing the program to continue running with an erroneous value.

If an unfixable exception arises in a program that cannot be terminated, the exception should be reported and the program should continue executing.