Exceptions


Contents


Error Handling

Runtime errors can be divided into low-level errors that involve violating constraints, such as:

and higher-level, logical errors, such as violations of a method's precondition:

Logical errors can lead to low-level errors if they are not detected. Often, it is better to detect them (to provide better feedback).

Errors can arise due to:

Note that recovery is often not possible at the point of the error (because the error may occur inside some utility method that doesn't know anything about the overall program or what error recovery should involve). Therefore, it is desirable to "pass the error up" to a level that can deal with it.

There are several possible ways to handle errors:

Exceptions

Idea:

Exceptions can be built-in (actually, defined in one of Java's standard libraries) or user-defined. Here are some examples of built-in exceptions with links to their documentation:

How to Catch Exceptions

Catch exceptions using try blocks:

    try {
       // statements that might cause exceptions
       // possibly including method calls
    } catch ( exception-1 id-1 ) {
       // statements to handle this exception 
    } catch ( exception-2 id-2 ) {
       // statements to handle this exception 
    .
    .
    .
    } finally {
       // statements to execute every time this try block executes
    }

Notes:

  1. Each catch clause specifies the type of one exception, and provides a name for it (similar to the way a method header specifies the type and name of a parameter). Java exceptions are objects, so the statements in a catch clause can refer to the thrown exception object using the specified name.
  2. The finally clause is optional; a finally clause is usually included if it is necessary to do some clean-up (e.g., closing opened files).
  3. In general, there can be one or more catch clauses. If there is a finally clause, there can be zero catch clauses.

Example (a program that tries to open a file named by the first command-line argument for reading)

public static void main(String[] args) {
    InputStream istream;
    File        inputFile;
    
    try {
        inputFile = new File(args[0]);
        istream =   new InputStream(inputFile);  // may throw FileNotFoundException
    } catch (FileNotFoundException ex) {
        System.out.println("file " + args[0] + " not found");
    }
}

Notes:

  1. The program really should make sure there is a command-line argument before attempting to use args[0].
  2. Also, it probably makes more sense to put the try block in a loop, so that if the file is not found the user can be asked to enter a new file name, and a new attempt to open the file can be made.
  3. As is, if the user runs the program with a bad file name foo, the message "file foo not found" will be printed, and the program will halt.
  4. If there were no try block and the program were run with a bad file name foo, a more complicated message, something like this:
             java.io.FilenotFoundException: foo
                at java.io.FileInputStream ...
                at ...
                at Test.main ...
             
    

    would be printed. (Actually, if there were no try/catch for the FileNotFoundException, the program wouldn't compile because it fails to list that exception as one that might be thrown. We'll come back to that issue later...)

Checked and Unchecked Exceptions

Every exception is either a checked exception or an unchecked exception. If a method includes code that could cause a checked exception to be thrown, then:

So in general, you must always include some code that acknowledges the possibility of a checked exception being thrown. If you don't, you will get an error when you try to compile your code.

Exception Hierarchy

                    +--------+
                    | Object |
                    +--------+
                        |
                        |
                   +-----------+
                   | Throwable |
                   +-----------+
                    /         \
                   /           \
          +-------+          +-----------+
          | Error |          | Exception |
          +-------+          +-----------+
           /  |  \           / |        \
         \________/       \______/       \
                                        +------------------+
        unchecked        checked        | RuntimeException |
                                        +------------------+
                                          /   |    |      \
                                         \_________________/
                                           
                                           unchecked

Choices when calling a method that may throw an exception

  1. Catch and handle the exception.
  2. Catch the exception, then re-throw it or throw another exception.
  3. Ignore the exception (let it "pass up" the call chain).

As mentioned above, if your code might cause a checked exception to be thrown; i.e.,:

then your method must include a throws clause listing all such exceptions. For example:

    public static void main(String[] args) throws FileNotFoundException, EOFException
    { // an uncaught FileNotFoundException or EOFException may be thrown here }

Only uncaught checked exceptions need to be listed in a method's throws clause. Unchecked exceptions can be caught in a try block, but if not, they need not be listed in the method's throws clause.


Test Yourself #1

Consider the following program (assume that comments are replaced with actual code that works as specified):

public class TestExceptions {

    static void e() {
      // might cause any of the following unchecked exceptions to be thrown:
      // Ex1, Ex2, Ex3, Ex4
    }
    
    static void d() {
      try {
          e();
      } catch (Ex1 ex) {
          System.out.println("d caught Ex1");
      }
    }
    
    static void c() {
      try {
          d();
      } catch (Ex2 ex) {
          System.out.println("c caught Ex2");
          // now cause exception Ex1 to be thrown
      }
    }
    
    static void b() {
      try {
          c();
      } catch (Ex1 ex) {
          System.out.println("b caught Ex1");
      } catch (Ex3 ex) {
          System.out.println("b caught Ex3");
      }
    }
    
    static void a() {
      try {
          b();
      } catch (Ex1 ex) {
          System.out.println("a caught Ex1");
      } catch (Ex4 ex) {
          System.out.println("a caught Ex4");
          // now cause exception Ex1 to be thrown
      }
    }
    
    public static void main(String[] args) {
        a();
    }
}

Assume that this program is run four times. The first time, method e throws exception Ex1, the second time, it throws exception Ex2, etc. For each of the four runs, say what is printed; if an uncaught exception is thrown, say what happens.

solution


How to Define and Throw Exceptions


Test Yourself #2

Question 1: Assume that method f might throw exceptions Ex1, Ex2, or Ex3. Complete method g, outlined below, so that:

    static void g() throws ... {
        try {
            f();
        } catch ( ... ) {
            ...
        } ...
    }

Question 2: Consider the following method.

static void f(int k, int[] A, String S) {
    int j = 1 / k;
    int len = A.length + 1;
    char c;
    
    try {
        c = S.charAt(0);
        if (k == 10) j = A[3];
    } catch (ArrayIndexOutOfBoundsException ex) {
        System.out.println("array error");
        throw new InternalError();
    } catch (ArithmeticException ex) {
        System.out.println("arithmetic error");
    } catch (NullPointerException ex) {
        System.out.println("null ptr");
    } finally {
        System.out.println("in finally clause");
    }
    System.out.println("after try block");
}

Part A.

Assume that variable X is an array of int that has been initialized to be of length 3. For each of the following calls to method f, say what (if anything) is printed by f, and what, if any, uncaught exceptions are thrown by f.

A. f(0, X, "hi");
B. f(10, X, "");
C. f(10, X, "bye");
D. f(10, X, null);

Part B.

Why doesn't f need to have a throws clause that lists the uncaught exceptions that it might throw?

solution


Summary