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