95.105/145 - Introduction to Programming
Fall 2001

 10  Exception Handling


What's in This Set of Notes ?


 10.1 Exceptions


In the "real" world, things go wrong.  There are many chances for errors to occur in a program when the programer has no control over information that is entered into the program from the keyboard, files, or from other methods/classes/packages etc...    Even worse...when such errors occur, it is not always clear how to handle the error.

Exceptions are:

When an Exception occurs, Java forces us to do one of the following: Exception Handling: When should we handle exceptions ? Why use Java Exception handling techniques ? Exceptions are thrown (i.e., generated) by either: Exceptions are always caught (i.e., handled) by one of these:

Java has many predefined Exceptions, and we can also create our own.  An Exception is an objects, so each one has its own class definition in Java.  The Exception classes are arranged in a hierarchy, and their position in the hierarchy can affect the way that they are handled.

There are three main kinds of errors:


The Error class and its subclasses represent a serious problem (i.e, unrecoverable error).   Generally, applications should not try to catch them.
There are many subclasses, here are just a few:
  • VirtualMachineError
    • StackOverflowError  (e.g., recursion too deep)
    • OutOfMemoryError  (e.g., cannot create any more objects)
  • LinkageError
    • NoClassDefFoundError  (e.g., no class with a given name)
    • ClassFormatError  (e.g., class is incompatible)

The Exception class and its subclasses indicate a less serious problem.   The Exceptions are either "checked" or "unchecked".   Generally, applications should try to catch the "checked" Exceptions.  The Java Virtual Machine detects these kinds of Exceptions and forces you to deal with them before it will compile your code.

This checking is made possible since

Here are just a few of the "checked" Exceptions: Here are a few of the "unchecked" Exceptions.   Normally you don't explicitly check for these errors in your code: Recall that to deal with Exceptions you must either handle the exception yourself, or declare that someone else will handle it (we call this delegating the responsibility to someone else).

Delegating Responsibility

When we do not wish to handle an error in our code, we can delegate the responsibility to the "calling method".  We do this by adding a throws clause to our method declaration:

public void openThisFile(String fileName) throws java.io.FileNotFoundException {
    // code for method
}
A method may throw more than one Exception.   We can declare as many as we want with a single throws clause:
public Object convertFileToObject(String fileName)
             throws java.io.FileNotFoundException, java.lang.ClassNotFoundException {
    // code for method
}
The throws clause is part of a method declaration used to (tell the compiler) which Exceptions the method may throw back to its caller.  (We've seen this before when getting keyboard input by saying throws IOException.  The throws clause is required if the code in the method may generate, but not handle, an Exception.
You should think of the throws clause as a sign that the method holds up so that it tells the whole world that the code in that method may generate the specified error:

Notice now that the calling method may also delegate the responsibility to "its" calling method as well.

public void getCustomerInfo() throws java.io.FileNotFoundException {
    // do something
    this.openThisFile(“customer.txt”);
    // do something
}
Here, if the Exception is thrown while in the openThisFile() method, the getCustomerInfo() method will stop and it will then pass on the Exception to its caller.   The responsibility may be repeatedly delegated in this manner.  Everyone essentially ignores the error (like a hot potato).  Nobody explicitly handles the error.   The JVM will eventually catch it and halt the program:

At any time during this process however, any method may catch the exception and handle it.   Once caught, propagation of the Exception stops.

A method may catch an exception by specifying try and catch blocks.

The try block represents a sequence of java statements (defined between brackets { }) in which the result of any method calls or other operations might cause an exception.  We precede this block with the try keyword.

The catch block represents a sequence of java statements (defined between brackets { }) which follow the try block prefaced by the word catch.  This block of code defines the code that is to be evaluated when a specific type of exception occurs.

Here is the typical format for doing a try with many catches:
 
try {
    //some code that may do something causing an exception

catch (<ExceptionType1> e) {
    //some code that handles the exception
}
catch (<ExceptionType2> e) {
    //some code that handles the exception
}
catch (<ExceptionType3> e) {
    //some code that handles the exception
}
...
finally {
    //some code that is always executed
}

The method does not need to throw the exception any further (i.e., no throws clause):

public void getCustomerInfo() {
    try {
        openThisFile(“customer.txt”);
    }
    catch (java.io.FileNotFoundException  ex){
        // Handle the error here
    }
}
The catch block requires a parameter which indicates the type of error to be caught.  We also specify a name for the parameter and thus we can access and use the Exception object within the catch block.

More than one catch block may be used to catch one-of-many possible Exceptions.  We simply list all catch blocks one after another:

public void getCustomerInfo() {
    try {
        // do something that may cause an Exception
    }
    catch (java.io.FileNotFoundException  ex){
        // Handle the error here }
    catch (java.lang.NullPointerException  ex){
        // Handle the error here }
    catch (java.lang.ArithmeticException  ex){
        // Handle the error here }
}
Here is what happens when Java encounters try/catch blocks:
  1. The code in the try block is evaluated sequentially.
  2. If an Exception occurs:
    1. Java looks for the first catch block whose Exception type matches that of the error.
    2. If a match is found, that catch block is evaluated (see top most green arrow in image below).
    3. If not found, then the Exception is considered uncaught.
    4. Once an Exception is handled by a catch block, Java does NOT return to the try block (see red arrow in image below).
  3. If no Exception occurs, the catch blocks are all ignored and code proceeds as normal after the catch blocks.

Note that only one catch block (the one that first matches the Exception) will ever be evaluated.

Be careful !!   The order of the catch blocks is important when you start to use the hierarchy.   A more general Exception will be caught before one of its subclasses.   So we may end up with catch blocks that are unreachable!!  The compiler will tell you if this happens.

public void getCustomerInfo() {
    try {
        // do something that may cause an Exception
    }
    catch (java.lang.Exception  ex){
        // Catches all Exceptions
    }
    catch (java.io.IOException  ex){
        // Never reached since above catches all
    }
    catch (java.io.FileNotFoundException  ex){
        // Never reached since above two are caught first
    }
}
A finally block may be used after the catch blocks:
try {
    ...
}
catch (java.io.IOException  ex){}
catch (java.lang.Exception  ex){}
finally {
    // Code to release resources
}
The finally block is used to release resources, such as closing files.  It is evaluated: While inside the catch block, the following messages can be sent to the incoming Exception (i.e., to the parameter of a catch block): So we can do many different things inside catch blocks.   Here are some examples:
catch (<ExceptionType1> e) {
    System.out.println("Hey!  Something bad just hapenned!");
}
catch (<ExceptionType4> e) {
    System.out.println(e.getMessage());
}
catch (<ExceptionType3> e) {
    e.printStackTrace();
}
Consider the stack trace for this code:
public class MyClass {
    public static void doSomething(int[] anArray){
        doAnotherThing(anArray);
    }
    public static void doAnotherThing(int[] theArray){
        System.out.println(theArray[0]);
    }
    public static void main(String[] args){
        doSomething(null);
    }
}
When we run this code, we get the following stack trace printed to the console window:
java.lang.NullPointerException
    at MyClass.doAnotherThing(MyClass.java:7)
    at MyClass.doSomething(MyClass.java:5)
    at MyClass.main(MyClass.java:3)
Notice that the stack trace indicates:
 10.2 Examples of Handling Exceptions


We'll now do an example to see if we can catch an exception.   Consider a program that reads in two integers and divides the first one by the second and then shows the answer.  We'll assume that we want the number of times that the second number divides evenly into the first (i.e. ignore the remainder).  What problems can occur ?  Well we may get invalid data or we may get a divide by zero.  Lets look at how we would have done this previously: Notice the throws clause in the main method.   We are declaring that we will not handle any IOExceptions.

Here is the output if 143 and 24 are entered:
 
Enter the first number:
143
Enter the second number:
24
24 goes into 143 this many times: 5

What if we now enter 143 and ABC ?
 
Enter the first number:
143
Enter the second number:
ABC
java.lang.NumberFormatException: ABC
        at java.lang.Integer.parseInt(Integer.java:229)
        at java.lang.Integer.valueOf(Integer.java:310)
        at Except1.main(Except1.java:12)

Yep, this is not a nice way to handle the exception.  By default, when exceptions occur, they actually print out what is known as a stack trace.   This is the sequence of method calls that led to the exception.   It is ugly, but good for debugging purposes.

What if we enter 143 and 0 ?
 
Enter the first number:
143
Enter the second number:
0
0 goes into 143 this many times: java.lang.ArithmeticException: / by zero
        at Except1.main(Except1.java:14)

Once again, this is not nice at all.

Lets try to handle the errors.  First, we'll handle the I/O errors by printing out an appropriate message and then quitting the program.  Note that we can quit the program at any time by using the System.exit() call.

If we test it with 143 and 24 as before, it still works the same.  What about testing it with 143 and ABC ?
 
Enter the first number:
143
Enter the second number:
ABC
Those were not proper integers!  I quit!

What if we enter ABC as the first number ?
 
Enter the first number:
ABC
Those were not proper integers!  I quit!

Woops!  It appears that our error message is not grammatically correct anymore.  Perhaps we should change it to  "Invalid integer entered!"  and this should be clear enough.

Lets now try to handle the divide by zero exception (actually an ArithmeticException):

Here is the output now when 143 and 0 are entered:
 
Enter the first number:
143
Enter the second number:
0
Second number is 0, cannot do division!

Can we merge the two try blocks into one ?

What if we wanted to repeatedly prompt for integers until valid ones were entered ?  How do we do this ?  We will need a while loop since we do not know how many times to keep asking.  Also, we should probably put the numbers into an array so that we can save on code duplication: Here are the test results:
 
Enter number 1
what
Invalid integer entered. Please try again.
Enter number 1
help me
Invalid integer entered. Please try again.
Enter number 1
ok, ok, here goes
Invalid integer entered. Please try again.
Enter number 1
143
Enter number 2
did you say number 2 ?
Invalid integer entered. Please try again.
Enter number 2
40
40 goes into 143 this many times: 3


 10.3 Creating and Throwing Your Own Exceptions


You may throw an Exception in your code at any time. Exceptions are thrown with the throw statement.  Here are some examples:
 
 throw new java.io.FileNotFoundException();
 throw new NullPointerException();
 throw new Exception();

Methods that throw these Exceptions, must declare that they do so in their method declarations, using the throws clause (as we have seen before).
You may even catch an Exception, partially handle it and then throw it again !

public void getCustomerInfo() throws Exception {
    try { ... }
    catch (Exception  e){
        // Handle (partially) the exception here
        throw e; // throw it again
    }
}

It is possible to create your own Exceptions.  Simply create a subclass of an existing Exception.  If you are unsure where to put it in the hierarchy, use Exception as the superclass.   When making an exception, you should:

public class MyExceptionName extends  SuperclassOfMyException {
    public MyExceptionName() {
        super("Some string explaining the exception");
    }
}

You save this Exception in its own file and compile it as any other Java file.   Then, you can start using it in your program.

Example

Lets look at our previous example and our own DivideByZeroException.  Where does it go in the Exception hierarchy ?  Well it should go as a subclass of ArithmeticException since this is where things went wrong initially.  First, we must make the particular Exception subclass:

Now we should be able to handle the exception when it is thrown.  Lets modify our Except4 class from above to make a method that does the division.  This is where the error should occur, so we'll throw the exception if the error occurs: Now we can use this in our code:
import java.io.*;
public class Except6 {
    private static int quotient(int numerator, int denominator) throws DivideByZeroException {
       if (denominator == 0)
           throw new DivideByZeroException();
       return(numerator / denominator);
    }

    public static void main(String args[]) throws IOException {
        BufferedReader inputStream = new BufferedReader
                                     (new InputStreamReader(System.in));
        int   result = 0, number1 = 0, number2 = 0;

        try {
            System.out.println("Enter the first number:");
            number1 = Integer.valueOf(inputStream.readLine()).intValue();
            System.out.println("Enter the second number:");
            number2 = Integer.valueOf(inputStream.readLine()).intValue();
            result = quotient(number1, number2);
            System.out.print(number2 + " goes into "+ number1);
            System.out.println(" this many times: " + result);
        }
        catch (NumberFormatException e) {
            System.out.println("Invalid integer entered!");
            System.exit(-1);
        }
      catch (DivideByZeroException e) {
            System.out.println(e.toString());
            System.exit(-1);
        }
    }
}

Here is the result when 143 and 0 are entered:
 
Enter the first number:
143
Enter the second number:
0
DivideByZeroException: Attempted to divide by zero

Notice that we made use of the Exception description by merely sending the toString() message.

Lets take another look at the BankAccount object again ... more specifically ...the withdraw method:

public  boolean withdraw(float anAmount) {
    if (anAmount <= balance) {
        balance -= anAmount;
       return true;
    }
    return false;
}
When the user tries to withdraw more money than is actually in the account...nothing happens.   Since the method returns a boolean, we can always check for this error where we call the method:
public  static void main(String args[]) {
    BankAccount b = new BankAccount("Bob");
    b.deposit(100);
    b.deposit(500.00f);
    if (!b.withdraw(25.00f))
        System.out.println("Error withdrawing money from account");
    if (!b.withdraw(189.45f))
        System.out.println("Error withdrawing money from account");
    b.deposit(100.00f);
    if (!b.withdraw(1000000))
        System.out.println("Error withdrawing money from account");
}
Note that this form of error checking works fine.   However, it clearly clutters up the code!   Lets see how we can use Exceptions.

We'll create an WithdrawalException.   Where would it go in the Exception hierarchy ?   Probably right under the Exception class.

Here is the Exception:

Notice that we did not supply a constructor...it is not necessary.
Now how to we throw the exception in the withdraw method ?
public boolean withdraw(float anAmount) throws WithdrawalException {
    if (anAmount <= balance) {
        balance -= anAmount;
       return true;
    }
    throw new  WithdrawalException();
    return false;
}
Note that we must also instruct the compiler that this method may throw an  WithdrawalException  by writing this as part of the method declaration.   The addition of this simple statement means that whichever methods call the withdraw method, MUST handle the exception.

Also notice that we no longer need the return type for the withdraw method since its purpose was solely for error checking.   Now that we have the Exception being generated, this becomes our form of error checking.   Let us alter the return type:

public void withdraw(float anAmount) throws WithdrawalException {
    if (anAmount <= balance)
        balance -= anAmount;
    throw new WithdrawalException();
}
There.   That's better.   Now how do we change our "calling" code ?
public  static void main(String args[]) {
    BankAccount b = new BankAccount("Bob");
    try {
        b.deposit(100);
        b.deposit(500.00f);
        b.withdraw(25.00f);
        b.withdraw(189.45f);
        b.deposit(100.00f);
        b.withdraw(1000000);
    } catch (WithdrawalException e) {
        System.out.println("Error withdrawing money from account");
    }
}
Notice how much simpler and cleaner the calling code becomes.   We can make our code even more simpler by simply ignoring the error...but then the program will stop (in our example):
public  static void main(String args[])  throws  WithdrawalException {
    BankAccount b = new BankAccount("Bob");
    b.deposit(100);
    b.deposit(500.00f);
    b.withdraw(25.00f);
    b.withdraw(189.45f);
    b.deposit(100.00f);
    b.withdraw(1000000);
}
We could have also created a constructor in our Exception class that takes a String parameter to describe the error:
public class  WithdrawalException  extends  Exception {
    public WithdrawalException(String desc) {
        super(desc);
    }
}
We can then use this new constructor instead by supplying different explanations as to why the error occurred.   For example, there may be many reasons why we cannot withdraw from a BankAccount: Note that if this String-parameter constructor is used, we can access the particular String by sending the getMessage() message to the Exception:
publicstatic void main(String args[]) {
    PowerSavingsAccount p = new PowerSavingsAccount("Bob");
    SuperSavingsAccount s = new SuperSavingsAccount("Betty");
    try {
        p.deposit(100);
        s.deposit(500.00f);
        p.withdraw(25.00f);
        p.withdraw(189.45f);
        s.deposit(100.00f);
        s.withdraw(1000000);
    } catch (WithdrawalException e) {
        System.out.println(e.getMessage());
    }
}
Note that the catch block catches any errors for both accounts.   Note also that the reason for the error may be different.  For example, the SuperSavingsAccount may have the following withdraw() method:
public void withdraw(float anAmount) throws WithdrawalException {
    throw new  WithdrawalException(“Withdrawals are not allowed from this account”);
}
whereas the PowerSavingsAccount may have this method:
public void withdraw(float anAmount) throws WithdrawalException {
    if (anAmount > balance)
       throw new  WithdrawalException(“Insufficient funds in account to withdraw specified amount”);
    if (anAmount + WITHDRAW_FEE > balance) {
       throw new  WithdrawalException(“Not enough money to cover transaction fee”);
    balance -= anAmount + WITHDRAW_FEE;
}
It is neat how we can kinda "encapsulate" different explanations for the error within a single Exception type !!!