Main Page

Previous Next

Dealing with Exceptions

As we discussed in the previous sections, if your code can throw exceptions other than those of type Error or type RuntimeException, (you can take it that we generally include the subclasses when we talk about Error and RuntimeException exceptions) you must do something about it. Whenever you write code that can throw an exception, you have a choice. You can supply code within the method to deal with any exception that is thrown, or you can essentially ignore it by enabling the method containing the exception throwing code to pass it on to the code that called the method.

Let's first see how you can pass an exception on.

Specifying the Exceptions a Method Can Throw

Suppose you have a method which can throw an exception that is neither a subclass of RuntimeException nor of Error. This could be an IOException for example, which can be thrown if your method involves some file input or output operations. If the exception isn't caught and disposed of in the method, you must at least declare that the exception can be thrown. But how do you do that?

You do it simply by adding a throws clause in the definition of the method. Suppose we write a method that uses the methods from classes that support input/output that are defined in the package java.io. You'll see in the chapters devoted to I/O operations that some of these can throw exceptions represented by objects of classes IOException and FileNotFoundException. Neither of these are subclasses of RuntimeException or Error, and so the possibility of an exception being thrown needs to be declared. Since the method can't handle any exceptions it might throw, for the simple reason that we don't know how to do it yet, it must be defined as:

double myMethod() throws IOException, FileNotFoundException {
  // Detail of the method code...
}

As the fragment above illustrates, to declare that your method can throw exceptions you just put the throws keyword after the parameter list for the method. Then add the list of classes for the exceptions that might be thrown, separated by commas. This has a knock-on effect - if another method calls this method, it too must take account of the exceptions this method can throw. After all, calling a method that can throw an exception is clearly code where an exception may be thrown. The calling method definition must either deal with the exceptions, or declare that it can throw these exceptions as well. It's a simple choice. You either pass the buck, or decide that the buck stops here. The compiler checks for this and your code will not compile if you don't do one or the other. The reasons for this will become obvious when we look at the way a Java program behaves when it encounters an exception.

Handling Exceptions

If you want to deal with the exceptions where they occur, there are three kinds of code block that you can include in a method to handle them - try, catch, and finally:

  • A try block encloses code that may give rise to one or more exceptions. Code that can throw an exception that you want to catch must be in a try block.

  • A catch block encloses code that is intended to handle exceptions of a particular type that may be thrown in a try block.

  • The code in a finally block is always executed before the method ends, regardless of whether any exceptions are thrown in the try block.

Let's dig into the detail of try and catch blocks first, then come back to the application of a finally block a little later.

The try Block

When you want to catch an exception, the code in the method that might cause the exception to be thrown must be enclosed in a try block. Code that can cause exceptions need not be in a try block but, in this case, the method containing the code won't be able to catch any exceptions that are thrown and it must declare that it can throw the types of exceptions that are not caught.

A try block is simply the keyword try, followed by braces enclosing the code that can throw the exception:

try {
  // Code that can throw one or more exceptions
}

Although we are discussing primarily exceptions that you must deal with here, a try block is also necessary if you want to catch exceptions of type Error or RuntimeException. When we come to a working example in a moment, we will use an exception type that you don't have to catch, simply because exceptions of this type are easy to generate.

The catch Block

You enclose the code to handle an exception of a given type in a catch block. The catch block must immediately follow the try block that contains the code that may throw that particular exception. A catch block consists of the keyword catch followed by a parameter between parentheses that identifies the type of exception that the block is to deal with. This is followed by the code to handle the exception enclosed between braces:

try {
  // Code that can throw one or more exceptions

} catch(ArithmeticException e) {
  // Code to handle the exception
}

This catch block only handles ArithmeticException exceptions. This implies this is the only kind of exception that can be thrown in the try block. If others can be thrown, this won't compile. We will come back to handling multiple exception types in a moment.

In general, the parameter for a catch block must be of type Throwable or one of the subclasses of the class Throwable. If the class that you specify as the parameter type has subclasses, the catch block will be expected to process exceptions of that class, plus all subclasses of the class. If you specified the parameter to a catch block as type RuntimeException for example, the code in the catch block would be invoked for exceptions defined by the class RuntimeException, or any of its subclasses.

We can see how this works with a simple example. It doesn't matter what the code does - the important thing is that it throws an exception we can catch.

Try It Out - Using a try and a catch Block

The following code is really just an exhaustive log of the program's execution:

public class TestTryCatch {
  public static void main(String[] args) {
    int i = 1; 
    int j = 0;

    try {
      System.out.println("Try block entered " + "i = "+ i + " j = "+j);
      System.out.println(i/j);         // Divide by 0 - exception thrown
      System.out.println("Ending try block");

    } catch(ArithmeticException e) { // Catch the exception
      System.out.println("Arithmetic exception caught");
    }

    System.out.println("After try block");
    return;
  }
}

If you run the example, you should get the output:

Try block entered i = 1 j = 0
Arithmetic exception caught
After try block

How It Works

The variable j is initialized to 0, so that the divide operation in the try block will throw an ArithmeticException exception. We must use the variable j with the value 0 here because the Java compiler will not allow you to explicitly divide by zero - that is, the expression i/0 will not compile. The first line in the try block will enable us to track when the try block is entered, and the second line will throw an exception. The third line can only be executed if the exception isn't thrown - which can't occur in this example.

This shows that when the exception is thrown, control transfers immediately to the first statement in the catch block. It's the evaluation of the expression that is the argument to the println() method that throws the exception, so the println() method never gets called. After the catch block has been executed, execution then continues with the statement following the catch block. The statements in the try block following the point where the exception occurred aren't executed. You could try running the example again after changing the value of j to 1 so that no exception is thrown. The output in this case will be:

Try block entered i = 1 j = 1
1
Ending try block
After try block

From this you can see that the entire try block is executed. Execution then continues with the statement after the catch block. Because no arithmetic exception was thrown, the code in the catch block isn't executed.

Important 

You need to take care when adding try blocks to existing code. A try block is no different to any other block between braces when it comes to variable scope. Variables declared in a try block are only available until the closing brace for the block. It's easy to enclose the declaration of a variable in a try block, and, in doing so, inadvertently limit the scope of the variable and cause compiler errors.

The catch block itself is a separate scope from the try block. If you want the catch block to output information about objects or values that are set in the try block, make sure the variables are declared in an outer scope.

try catch Bonding

The try and catch blocks are bonded together. You must not separate them by putting statements between the two blocks, or even by putting braces around the try keyword and the try block itself. If you have a loop block that is also a try block, the catch block that follows is also part of the loop. We can see this with a variation of the previous example.

Try It Out - A Loop Block That is a try Block

We can make j a loop control variable and count down so that eventually we get a zero divisor in the loop:

public class TestLoopTryCatch {
  public static void main(String[] args) {
    int i = 12; 

    for(int j=3 ;j>=-1 ; j--) {
      try {
        System.out.println("Try block entered " + "i = "+ i + " j = "+j);
        System.out.println(i/j);         // Divide by 0 - exception thrown
       System.out.println("Ending try block");

      } catch(ArithmeticException e) {     // Catch the exception
        System.out.println("Arithmetic exception caught");
      }
    }

    System.out.println("After try block");
    return;
  }
}

This will produce the output:

Try block entered i = 12 j = 3
4
Ending try block
Try block entered i = 12 j = 2
6
Ending try block
Try block entered i = 12 j = 1
12
Ending try block
Try block entered i = 12 j = 0
Arithmetic exception caught
Try block entered i = 12 j = -1
-12
Ending try block
After try block

How It Works

The try and catch blocks are all part of the loop since the catch is inextricably bound to the try. You can see this from the output. On the fourth iteration, we get an exception thrown because j is 0. However, after the catch block is executed, we still get one more iteration with j having the value -1.

Even though both the try and catch blocks are within the for loop, they have separate scopes. Variables declared within the try block cease to exist when an exception is thrown. You can demonstrate that this is so by declaring an arbitrary variable, k say, in the try block, and then adding a statement to output k in the catch block. Your code will not compile in this case.

Suppose you wanted the loop to end when an exception was thrown. You can easily arrange for this. Just put the whole loop in a try block, thus:

  public static void main(String[] args) {
    int i = 12; 
    try {
      System.out.println("Try block entered.");
      for(int j=3 ;j>=-1 ; j--) {
        System.out.println("Loop entered " + "i = "+ i + " j = "+j);
        System.out.println(i/j);         // Divide by 0 - exception thrown
      }
      System.out.println("Ending try block");

    } catch(ArithmeticException e) {    // Catch the exception
      System.out.println("Arithmetic exception caught");
    }

    System.out.println("After try block");
    return;
  }

With this version of main(), the previous program will produce the output:

Try block entered.
Loop entered i = 12 j = 3
4
Loop entered i = 12 j = 2
6
Loop entered i = 12 j = 1
12
Loop entered i = 12 j = 0
Arithmetic exception caught
After try block

Now we no longer get the output for the last iteration because control passes to the catch block when the exception is thrown, and that is now outside the loop.

Multiple catch Blocks

If a try block can throw several different kinds of exception, you can put several catch blocks after the try block to handle them.

try {
  // Code that may throw exceptions

} catch(ArithmeticException e) {
  // Code for handling ArithmeticException exceptions
} catch(IndexOutOfBoundsException e) {
  // Code for handling IndexOutOfBoundsException exceptions
}
// Execution continues here...

Exceptions of type ArithmeticException will be caught by the first catch block, and exceptions of type IndexOutOfBounds Exception will be caught by the second. Of course, if an ArithmeticException exception is thrown, only the code in that catch block will be executed. When it is complete, execution continues with the statement following the last catch block.

When you need to catch exceptions of several different types for a try block, the order of the catch blocks can be important. When an exception is thrown, it will be caught by the first catch block that has a parameter type that is the same as that of the exception, or a type that is a superclass of the type of the exception. An extreme case would be if you specified the catch block parameter as type Exception. This will catch any exception that is of type Exception, or of a class type that is derived from Exception. This includes virtually all the exceptions you are likely to meet in the normal course of events.

This has implications for multiple catch blocks relating to exception class types in a hierarchy. The catch blocks must be in sequence with the most derived type first, and the most basic type last. Otherwise your code will not compile. The simple reason for this is that if a catch block for a given class type precedes a catch block for a type that is derived from the first, the second catch block can never be executed and the compiler will detect that this is the case.

Suppose you have a catch block for exceptions of type ArithmeticException, and another for exceptions of type Exception as a catch-all. If you write them in the following sequence, exceptions of type ArithmeticException could never reach the second catch block as they will always be caught by the first.

// Invalid catch block sequence - won't compile!
try {
  // try block code

} catch(Exception e) {
  // Generic handling of exceptions
} catch(ArithmeticException e) {
  // Specialized handling for these exceptions
}

Of course, this won't get past the compiler - it would be flagged as an error.

To summarize - if you have catch blocks for several exception types in the same class hierarchy, you must put the catch blocks in order, starting with the lowest subclass first, and then progressing to the highest superclass.

In principle, if you're only interested in generic exceptions, all the error handling code can be localized in one catch block for exceptions of the superclass type. However, in general it is more useful, and better practice, to have a catch block for each of the specific types of exceptions that a try block can throw.

The finally Block

The immediate nature of an exception being thrown means that execution of the try block code breaks off, regardless of the importance of the code that follows the point at which the exception was thrown. This introduces the possibility that the exception leaves things in an unsatisfactory state. You might have opened a file, for instance, and because an exception was thrown, the code to close the file is not executed.

The finally block provides the means for you to clean up at the end of executing a try block. You use a finally block when you need to be sure that some particular code is run before a method returns, no matter what exceptions are thrown within the previous try block. A finally block is always executed, regardless of what happens during the execution of the method. If a file needs to be closed, or a critical resource released, you can guarantee that it will be done if the code to do it is put in a finally block.

The finally block has a very simple structure:

finally {
  // Clean-up code to be executed last
}

Just like a catch block, a finally block is associated with a particular try block, and it must be located immediately following any catch blocks for the try block. If there are no catch blocks then you position the finally block immediately after the try block. If you don't do this, your program will not compile.

Important 

The primary purpose for the try block is to identify code that may result in an exception being thrown. However, you can use it to contain code that doesn't throw exceptions for the convenience of using a finally block. This can be useful when the code in the try block has several possible exit points - break or return statements for example, but you always want to have a specific set of statements executed after the try block has been executed to make sure things are tidied up - such as closing any open files. You can put these in a finally block. Note: if a value is returned within a finally block, this return overrides any return executed in the try block.

Structuring a Method

We've looked at the blocks you can include in the body of a method, but it may not always be obvious how they are combined. The first thing to get straight is that a try block, plus any corresponding catch blocks, and the finally block all bunch together in that order:

try {
  // Code that may throw exceptions...

} catch(ExceptionType1 e) {
  // Code to handle exceptions of type ExceptionType1 or subclass
} catch(ExceptionType2 e) {
  // Code to handle exceptions of type ExceptionType2 or subclass
... // more catch blocks if necessary
} finally {
  // Code always to be executed after try block code
}

You can't have just a try block by itself. Each try block must always be followed by at least one block that is either a catch block or a finally block.

You must not include other code between a try block and its catch blocks, or between the catch blocks and the finally block. You can have other code that doesn't throw exceptions after the finally block, and you can have multiple try blocks in a method. In this case, your method might be structured as shown in the following diagram.

Click To expand

In many cases, a method will only need a single try block followed by all the catch blocks for the exceptions that need to be processed in the method, perhaps followed by a finally block. Java, however, gives you the flexibility to have as many try blocks as you want. This makes it possible for you to separate various operations in a method by putting each of them in their own try block - an exception thrown as a result of a problem with one operation does not prevent subsequent operations from being executed.

The throws clause that follows the parameter list for the method identifies exceptions that can be thrown in this method, but which aren't caught by any of the catch blocks within the method. We saw this earlier in this chapter. Exceptions that aren't caught can be thrown by code anywhere in the body of the method - in code not enclosed by a try block.

Execution Sequence

We saw how the sequence of execution proceeded with the simple case of a try block and a single catch block. We also need to understand the sequence in which code is executed when we have the try-catch-finally combinations of blocks, when different exceptions are thrown. This is easiest to comprehend by considering an example. We can use the following code to create a range of exceptions and conditions.

Try It Out - Execution Sequence of a try Block

It will be convenient, in this example, to use an input statement to pause the program. The method we will use can throw an exception of a type defined in the java.io package. We will start by importing the java.io.IOException class into the source file. We will give the class containing main() the name TryBlockTest, and we will define another method, divide(), in this class that will be called in main(). The overall structure of the TryBlockTest class source file will be:

import java.io.IOException;

public class TryBlockTest {
  public static void main(String[] args) {
    // Code for main()..
  }

  // Divide method
  public static int divide(int[] array, int index) {
    // Code for divide()...
  }
}

The idea behind the divide() method is to pass it an array and an index as arguments. By choosing the values in the array and the index value judiciously, we can get ArithmeticException and ArrayIndexOutOfBoundsException exceptions thrown. We'll need a try block plus two catch blocks for the exceptions, and we will throw in a finally block for good measure. Here's the code for divide():

  public static int divide(int[] array, int index) {
    try {
      System.out.println("\nFirst try block in divide() entered");
      array[index + 2] = array[index]/array[index + 1];
      System.out.println("Code at end of first try block in divide()");
      return array[index + 2];

    } catch(ArithmeticException e) {
      System.out.println("Arithmetic exception caught in divide()");
    } catch(ArrayIndexOutOfBoundsException e) {
      System.out.println("Index-out-of-bounds exception caught in divide()");
    } finally {
      System.out.println("finally block in divide()");
    }

    System.out.println("Executing code after try block in divide()");
    return array[index + 2];
  }

We can define the main() method with the following code:

  public static void main(String[] args) {
    int[] x = {10, 5, 0};               // Array of three integers

    // This block only throws an exception if method divide() does
    try {
      System.out.println("First try block in main() entered");
      System.out.println("result = " + divide(x,0));  // No error
      x[1] = 0;                         // Will cause a divide by zero
      System.out.println("result = " + divide(x,0));  // Arithmetic error
      x[1] = 1;                         // Reset to prevent divide by zero
      System.out.println("result = " + divide(x,1));  // Index error

    } catch(ArithmeticException e) {
      System.out.println("Arithmetic exception caught in main()");
    } catch(ArrayIndexOutOfBoundsException e) {
      System.out.println("Index-out-of-bounds exception caught in main()");
    }

    System.out.println("Outside first try block in main()");
    System.out.println("\nPress Enter to exit");

    // This try block is just to pause the program before returning
    try {
      System.out.println("In second try block in main()");
      System.in.read();                 // Pauses waiting for input...
      return;

    } catch(IOException e) {        // The read() method can throw exceptions
      System.out.println("I/O exception caught in main()");
    } finally {                          // This will always be executed
      System.out.println("finally block for second try block in main()");
    }

    System.out.println("Code after second try block in main()");
  }

Because the read() method for the object in (this object represents the standard input stream, analogous to out) can throw an I/O exception, it must itself be called in a try block and have an associated catch block, unless we chose to add a throws clause to the header line of main().

If you run the example it will produce the output:

First try block in main()entered

First try block in divide() entered
Code at end of first try block in divide()
finally block in divide()
result = 2

First try block in divide() entered
Arithmetic exception caught in divide()
finally block in divide()
Executing code after try block in divide()
result = 2

First try block in divide() entered
Index-out-of-bounds exception caught in divide
finally block in divide()
Executing code after try block in divide()
Index-out-of-bounds exception caught in main()
Outside first try block in main()

Press Enter to exit
In second try block in main()

finally block for second try block in main()

How It Works

All the try, catch, and finally blocks in the example have output statements so we can trace the sequence of execution.

Within the divide()method the code in the try block can throw an arithmetic exception if the element array[index + 1] of the array passed to it is 0. It can also throw an ArrayIndexOutOfBounds exception in the try block if the index value passed to it is negative, or it results in index + 2 being beyond the array limits. Both these exceptions are caught by one or other of the catch blocks so they will not be apparent in the calling method main().

Note, however, that the last statement in divide() can also throw an index-out-of-bounds exception:

return array[index+2];

This statement is outside the try block so the exception will not be caught. The exception will therefore be thrown by the method when it is called in main(). However, we aren't obliged to declare that the divide() method throws this exception because the ArrayIndexOutOfBoundsException class is a subclass of RuntimeException, and is therefore exempted from the obligation to deal with it.

The main()method has two try blocks. The first try block encloses three calls to the divide() method. The first call will execute without error; the second call will cause an arithmetic exception in the method; and the third call will cause an index-out-of-bounds exception. There are two catch blocks for the first try block in main() to deal with these two potential exceptions.

The read() method in the second try block in main() can cause an I/O exception to be thrown. Since this is one of the exceptions that the compiler will check for, we must either put the statement that calls the read() method in a try block, and have a catch block to deal with the exception, or declare that main()throws the IOException exception. If we don't do one or the other, the program will not compile.

Using the read() method in this way has the effect of pausing the program until the Enter key is pressed. We'll be looking at read(), and other methods for I/O operations, in the next four chapters. The class IOException is in the package java.io, so we need the import statement for this class because we refer to it in the catch block. Remember that only classes defined in java.lang are included in your program automatically.

Normal Execution of a Method

The first line of output from the example, TryBlockTest, indicates that execution of the try block in main() has begun. The next block of four lines of output from the example is the result of a straightforward execution of the method divide(). No exceptions occur in divide(), so no catch blocks are executed.

The code at the end of the method divide(), following the catch blocks, isn't executed because the return statement in the try block ends the execution of the method. However, the finally block in divide() is executed before the return to the calling method occurs. If you comment out the return statement at the end of the divide() method's try block and run the example again, the code that follows the finally block will be executed.

The sequence of execution when no exceptions occur is shown in the diagram.

Click To expand

The previous diagram illustrates the normal sequence of execution in an arbitrary try-catch-finally set of blocks. If there's a return statement in the try block, this will be executed immediately after the finally block completes execution - so this prevents the execution of any code following the finally block. A return statement in a finally block will cause an immediate return to the calling point and the code following the finally block wouldn't be executed in this case.

Execution when an Exception is Thrown

The next block of five lines in the output correspond to an ArithmeticException being thrown and caught in the method divide(). The exception is thrown because the value of the second element in the array x is zero. When the exception occurs, execution of the code in the try block is stopped, and you can see that the code that follows the catch block for the exception in the divide()method is then executed. The finally block executes next, followed by the code after the finally block. The value in the last element of the array isn't changed from its previous value, because the exception occurs during the computation of the new value, before the result is stored.

The general sequence of execution in an arbitrary try-catch-finally set of blocks when an exception occurs is shown here.

Click To expand

Execution of the try block stops at the point where the exception occurs, and the code in the catch block for the exception is executed immediately. If there is a return statement in the catch block, this isn't executed until after the finally block has been executed. As discussed earlier, if a return statement that returns a value is executed within a finally block, that value will be returned, not the value from any previous return statement.

Execution when an Exception is not Caught

The next block of six lines in the output is a consequence of the third call to the method divide(). This causes an ArrayIndexOutOfBoundsException to be thrown in the try block, which is then caught. However, the code at the end of the method which is executed after the finally block, throws another exception of this type. This can't be caught in the method divide() because the statement causing it isn't in a try block. Since this exception isn't caught in the method divide(), the method terminates immediately and the same exception is thrown in main() at the point where the divide() method was called. This causes the code in the relevant catch block in main() to be executed in consequence.

An exception that isn't caught in a method is always propagated upwards to the calling method. It will continue to propagate up through each level of calling method until either it is caught, or the main() method is reached. If it isn't caught in main(), the program will terminate and a suitable message will be displayed. This situation is illustrated here.

Click To expand

This shows method1() calling method2() which calls method3(), which calls method4() in which an exception of type Exception2 is thrown. This exception isn't caught in method4() so execution of method4() ceases and the exception is thrown in method3(). It isn't caught, and continues to be rethrown until it reaches method1() where there's a catch block to handle it.

In our Try It Out, execution continues in main() with the output statements outside the first try block. The read() method pauses the program until you press the Enter key. No exception is thrown, and execution ends after the code in the finally block is executed. The finally block is tied to the try block that immediately precedes it, and is executed even though there's a return statement in the try block.

Nested try Blocks

We will not be going into these in detail, but you should note that you can have nested try blocks, as illustrated in this diagram.

Click To expand

The catch blocks for the outer try block can catch any exceptions that are thrown, but not caught, by any code within the block, including code within inner try-catch blocks. In the example shown, the catch block for the outer try block will catch any exception of type Exception2. Such exceptions could originate anywhere within the outer try block. The illustration shows two levels of nesting, but you can specify more if you know what you're doing.

Rethrowing Exceptions

Even though you may need to recognize that an exception has occurred in a method by implementing a catch clause for it, this is not necessarily the end of the matter. In many situations, the calling program may need to know about it - perhaps because it will affect the continued operation of the program, or because the calling program may be able to compensate for the problem.

If you need to pass an exception that you have caught on to the calling program, you can rethrow it from within the catch block using a throw statement. For example:

try {
  // Code that originates an arithmetic exception

} catch(ArithmeticException e) {
  // Deal with the exception here
  throw e;             // Rethrow the exception to the calling program
}

The throw statement is the keyword throw followed by the exception object to be thrown. When we look at how to define our own exceptions later in this chapter, we'll be using exactly the same mechanism to throw them.

Previous Next
JavaScript Editor Java Tutorials Free JavaScript Editor