Exception handling with try, catch, and finally blocks

 

Overview

One of the major differences between commercial quality programs and those we have written and studied so far is in their ability to survive various error conditions. For example, it is relatively difficult to make programs like Microsoft Word or Adobe Acrobat Reader crash. If they did crash easily, they would not be on the market for long.

Operating systems in particular must survive nearly any error that can occur. If the operating system fails, the entire system is brought down and all currently executing programs come to an abrupt end. The result can be devastating with work being lost and files being corrupted. The ability of competing operating systems (such as Windows and Unix) to detect and recover from errors is a constant source of debate among their advocates.

In an effort to make software fail-safe, estimates are that as much as 90% of the code in a commercial product is there to detect and recover from errors that might never occur. But, it is best to be prepared.

As a robust language, Java provides built-in language features, interfaces, and classes that help a programmer detect and recover from a wide range of conditions that would otherwise result in program failure.

Some conditions are more severe than others but all are Throwable objects in Java. The Throwable class is the root class for all errors and exceptions and is part of the java.lang package. When an error or exception occurs, an object is constructed that encapsulates the condition and it is "thrown". If and how the object is "caught" will be covered shortly.

 

Errors

 

Exceptions

public class App {
  public static void main(String[] args) {

    // Loop control variable.

    char again = 'y';

    // Main loop. One array is entered and processed in each iteration.

    while (again == 'Y' || again =='y') {

      // Instantiate a three element array and load a single String
      // at a random element location.

      String[] strings = new String[3];
      int randomIndex = (int) ((Math.random()*100) % strings.length);
      strings[randomIndex] = "Lucky";

      // Ask the user to guess the element location.

      Utility.separator(50, '>');
      System.out.print("Pick a number from 1 to " + strings.length + ": ");

      // If they guess the element location, display a message. Otherwise,
      // an exception will occur to terminate processing.

      if (strings[Keyboard.readInt() - 1].equals("Lucky")) {
        Utility.skip();
        System.out.println("\tYOU WIN!!!");
      }

      // Ask the user if they want to do it again and repeat the loop as
      // requested.

      Utility.separator(40, '=');
      System.out.print("Again? (Y/N): ");
      again = Keyboard.readChar();
    }
  }
}

Note: Run this program several times and notice the exceptions that occur. Be sure to enter a number other than 1, 2, or 3 at least once. We will correct these errors shortly.

 

The exception handling technique

  1. try a block of code that may result in an exception being "thrown"

  2. Code one or more blocks designed to automatically catch and handle a specific type of exception if one occurs. At most, only one of these blocks can be called in a single pass through the code. If none of the specified exceptions occurs, none of the blocks will be executed.

  3. Optionally code a block that will finally be run in ALL situations, whether an exception occurs or not

try {
  statements that may result in an exception being thrown; 

catch (exception-type1 reference) { 
 
statements to handle the exception
; 
} 
catch (exception-type2 reference) { 
 
statements to handle the exception
; 
} 
other catch blocks... 
finally {
 
statements to be ALWAYS executed
; 
}

where exception-typeN is the class name of the exception to be caught (such as NullPointerException) and reference is its local object reference within the catch block.

Example: The following is a modified version of the "shell game" program presented earlier. It contains a try block and two catch blocks. One handles a NullPointerException and the other handles an ArrayIndexOutOfBoundsException. No finally block is specified, so logic comes together after the end of the last catch block.

public class App {
  public static void main(String[] args) {

    // Loop control variable.

    char again = 'y';

    // Main loop. One array is entered and processed in each iteration.

    while (again == 'Y' || again =='y') {

      // Instantiate a three element array and load a single String
      // at a random element location.

      String[] strings = new String[3];
      int randomIndex = (int) ((Math.random()*100) % strings.length);
      strings[randomIndex] = "Lucky";

      // Ask the user to guess the element location.

      Utility.separator(50, '>');
      System.out.print("Pick a number from 1 to " + strings.length + ": ");

      // "Try" to read their response, use it to index into the array,
      // and see if they win.

      try {

        // If they guess the element location, display a message. Otherwise,
        // an exception will occur.

        if (strings[Keyboard.readInt() - 1].equals("Lucky")) {
          Utility.skip();
          System.out.println("\tYOU WIN!!!");
        }
      }

      // This block is automatically executed if a NullPointerException
      // occurs to indicate a null array location.

      catch (NullPointerException err) {
        Utility.skip();
        System.out.println("\tEmpty shell - YOU LOSE!!!");
      }

      // If an ArrayIndexOutOfBoundsException occurs, this block is
      // automatically executed. It indicates an invalid number was
      // used as an index.

      catch (ArrayIndexOutOfBoundsException err) {
        Utility.skip();
        System.out.println("\tInvalid number - YOU LOSE");
      }

      // Ask the user if they want to do it again and repeat the loop as
      // requested.

      Utility.separator(40, '=');
      System.out.print("Again? (Y/N): ");
      again = Keyboard.readChar();
    }
  }
}

Note: The catch blocks are mutually exclusive. In a single iteration you may not process within more than one catch block.

 

Special considerations

public class App {
  public static void main(String[] args) {
    try {
      int x = 3 / 0;
    }
    catch (ArithmeticException err) {
      System.out.println(err.getMessage());
    }
    catch (Exception err) {
      System.out.println("An error has occurred");
    }
  }

will result in the message "/ by zero" being displayed because the getMessage() method of an Exception objects and its descendents returns a message associated with the exception. while,

public class App {
  public static void main(String[] args) {
    try {
      int x = 3 / 0;
    }
    catch (Exception err) {
      System.out.println("An error has occurred");
    }
    catch (ArithmeticException err) {
      System.out.println(err.getMessage());
    }
  }
}

results in a compile error that says the second catch block is unreachable.

Because the Exception class is the ancestor of all other exception classes, a general-purpose catch block may be coded as

catch (Exception err) {
  System.out.println(err.getMessage());
}

and may be the only catch block. Alternatively, this general-purpose catch block may be placed at the end of all other catch blocks to become the default catch block. It would automatically be called if an exception is thrown that doesn't match the more specific exceptions in the preceding catch blocks.

catch (Exception err) {}

As far as the JVM is concerned, the exception has been handled.

Exceptions that descend from RuntimeException are NOT checked by the compiler. Programs that might fail due to such exceptions do not require try and catch blocks.

public class App {
  public static void main(String[] args) {
    try {
      int x = 3 / 0;
    }
    finally {
      System.out.println("I'm about to unwind");
    }
  }
}

compiles fine. When executed, however, an uncaught ArithmeticException occurs. The finally block is executed prior to unwinding the stack.

 

Lab exercise for Ferris students

E-mail your answers to this assignment no later than the due date listed in the class schedule.

 

Review questions

  1. Assuming all unseen code is correct, which of the following will be part of the output displayed by executing the following statements? The line numbers are for reference purposes only.  (choose four)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
try {
  int[] numbers = new int[3];
  System.out.println("Message 1");
  numbers[3] = 5;
  System.out.println("Message 2");
}
catch (ArrayIndexOutOfBoundsException err) {
  System.out.println("Message 3");
}
catch (Exception err) {
  System.out.println("Message 4");
}
finally {
  System.out.println("Message 5");
}
System.out.println("Message 6");
  1. Message 1

  2. Message 2

  3. Message 3

  4. Message 4

  5. Message 5

  6. Message 6

  1. Assuming all unseen code is correct, what will be displayed when the following statements are executed? The line numbers are for reference purposes only.

1
2
3
4
5
6
7
8
9
10
11
try {
  StringBuffer[] s = new StringBuffer[10];
  s[0].reverse();
}
catch (ArrayIndexOutOfBoundsException err) {
  System.out.println("Message 1");
}
catch (Exception err) {
  System.out.println("Message 2");
}
System.out.println("Message 3");
  1. the statements will not compile

  2. the program will terminate with nothing being displayed

  3. Message 1
    Message 3

  4. Message 2
    Message 3

  5. Message 3

  1. Assuming all unseen code is correct, what will be displayed when the following statements are executed? The line numbers are for reference purposes only.

1
2
3
4
5
6
7
8
9
10
try {
  double x = 17.5 / 0.0;
}
catch (Exception err) {
  System.out.println("Message 1");
}
finally {
  System.out.println("Message 2");
}
System.out.println("Message 3");
  1. the statements will not compile

  2. the program will terminate with nothing being displayed

  3. Message 1
    Message 2
    Message 3

  4. Message 2
    Message 3

  5. Message 3

  1. Assuming all unseen code is correct, what will be displayed when the following statements are executed? The line numbers are for reference purposes only.

1
2
3
4
5
6
try {
  int x = 3 / 0;
}
catch (Exception err) {
}
System.out.println("Message 1");
  1. the statements will not compile

  2. the program will terminate with nothing being displayed

  3. Message 1