Aqua Phoenix
     >>  Lectures >>  Java 3  
 

Navigator
   
 
       
   

3.3 Exception Handling

Exceptions in Java are the equivalent of segmentation faults and core dumps in C. Exceptions are generated when an illegal operation takes place, for example division by zero. However, Java implements a useful facility by which such illegal operations can be captured interactively, and handled appropriately. This makes undesired program stops virtually impossible.

Exceptions occur on a per-method and per-operation basis. When a method implements the functionality for an exception, then exception handling takes place for the specific call of the method. Similarly, when performing a single operation, like division, exception handling can be implemented for only that operation specifically.

In Java terminology, an exception is said to be thrown by a method or operation and the exception is caught by the piece of code calling the method or performing the operation.

Exceptions are named after the type of failure that occurs, and the word Exception tends to appear in the name.

For a simple example, the following operation would cause an exception:

...
int a = 9 / 0;
...
The exception is printed on the command line and the program terminates, because no exception handling has been included:

Exception in thread "main" java.lang.ArithmeticException: / by zero
        at YYYYYY
YYYYYY denotes the class name and method name in which the exception occurs.

Exception handling can be used to catch this exception and prevent such illegal operation from stopping the program:

...
int a;
try {
  a = 9 / 0;
} catch (ArithmeticException e) {
  a = 0;
}
...
In this piece of code, the previous detrimental ArithmeticException is now caught, and an alternative assignment (a = 0) is made. No error messages are printed to the screen, and the program continues execution without interruption.

Some methods require that exceptions be caught. When including calls to such methods without the proper exception handling, the compiler will output proper error messages, e.g.:

CLASSXYZ:LINE: unreported exception java.lang.Exception; must be caught or declared to be thrown
The compiler specifies exactly what line the exception must be caught in.

The following example involves arrays and array positions. Accessing an array index beyond the array's determined size is considered a violation. Consider this:

...
int[] a = new int[10];
for (int i = 0; i < 20; i++) {
  a[i] = 0;
}
...
The array has 10 positions, and yet the loop iterates over 20 indices. When executing this code, the following exception occurs:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 10
        at YYYYYY
While it is always good practice to use the array's instance variable length to determine over what range to iterate, the above code can also be enclosed in exception handling code:

...
int[] a = new int[10];
try {
  for (int i = 0; i < 20; i++) {
    a[i] = 0;
  }
} catch (ArrayIndexOutOfBoundsException e) {
  System.out.println("The array index has exceeded the number of elements");
}
...
In this example, as soon as the index into the array is no longer valid, the exception ArrayIndexOutOfBoundsException is thrown by the array class, and the exception handling code in the catch statement is evaluated. For this example, an error message is printed to the screen, but the code continues to be executed without interruption.

3.3.1 Multiple Exceptions

In some cases, a method can throw more than one exception. When a method performs a complicated operation, or a series of conceptually different operations, then it might throw several kinds of exceptions. When several method calls are combined in one line, then the exceptions that are thrown by all method calls in that line are thrown separately, and must be handled separately.

try {
  BufferedReader in = new BufferedReader(new FileReader("infilename"));
  String line;
  while ((str = in.readLine()) != null) {
    ...
  }
  in.close();
} catch (FileNotFoundException e) {
  System.out.println("File not found");
} catch (IOException e) {
  System.out.println("Other Input/Output Exception occurred");
} catch (Exception e) {
  System.out.println("Other exception occurred");
}
In this example, 3 different exceptions are thrown by different method calls and are caught in hierarchical form. The hierarchy of catch statements dictates which exception is caught first. If a thrown exception does not match the first caught one, then the next one is tested. If no exception matches, then the exception is finally printed on screen, and the program is halted.

Without going into details, the FileNotFoundException above is thrown by the constructor call to new FileReader(...). If the passed filename does not exist, this exception is thrown. The IOException is thrown when some error occurs while reading from the file stream. Finally, any other exception is is caught by the final catch statement.

3.3.2 Exception Inheritance

Exceptions are objects that follow an inheritance scheme much like one from a previously discussed example. In fact, all objects in Java are part of an inheritance tree, because all objects in Java inherit from the root class named Object.

At the root of the exception hierarchy is a class by the name of Throwable. From this class, other categories of exceptions are derived, for example, the generic exception named Exception. This exception is extended by the IOException (Input/Output exception). The FileNotFoundException is yet another exception, which extends the IOException, because it is a special kind of Input/Output exception.

This hierarchy of exceptions allows for a very useful and uncomplicated way of handling exceptions: For any exception of whatever kind, we can catch the most generic exception Exception, and automatically catch all possible exceptions that exist in Java. By means of inheritance, we are not required to catch every kind of exception that is thrown by a series of methods.

The root class of exception defines a number of methods that can, by definition of Inheritance, be called, and the most appropriate versions of those methods will then be used. The most important of those methods are:

getMessage()
printStackTrace()
When an exception is caught in the catch statement, the exception object is passed as a parameter, e.g. in catch (IOException e) . The exception object can be used to extract information about what exception occurred and where the exception comes from in code.

Given exception object e, e.getMessage() returns a string with a short message describing the exception, and e.printStackTrace() prints out the source of the error on the screen.

3.3.3 Implementing Exceptions

To thrown an exception for your own method, the following syntax is required:

public double divideNumbers(int a, int b) throws Exception {

  if (b == 0) {
    throw new Exception("Infinity");
  }

  return a / b;
}
The method prototype must throw the exception, and somewhere inside the method, zero or more references of throw new Exception("...") can be made. The string passed to the class Exception becomes the error message, which can later be retrieved using getMessage() . The above code is used as follows:

...
try {
  System.out.println(divideNumbers(9, 0));
} catch (Exception e) {
  System.out.println(e.getMessage());
}
...
Which produces the following output when denominators of value zero are passed:

Infinity
The method call e.printStackTrace() would instead have produced the following more elaborate output, which gives detailed information of the whereabouts of the exception:

java.lang.Exception: Infinity
        at CLASS.divideNumbers(...)
        ...