Chapter 7

Exceptions

WHAT YOU WILL LEARN IN THIS CHAPTER

  • What an exception is
  • How you handle exceptions in your programs
  • The standard exceptions in Java
  • How to guarantee that a particular block of code in a method will always be executed
  • How to define and use your own types of exceptions
  • How to throw exceptions in your programs

Java uses exceptions as a way of signaling problems when you execute a program. Exceptions act as a control mechanism through which a program may be able to recover from an exceptional event. They provide important debug information (through stack traces) that help you figure what went wrong. Problems signaled by exceptions can be, but aren’t always, serious (as I describe later in this chapter). The standard classes use them extensively. Because they arise in your Java programs when things go wrong — and if something can go wrong in your code, sooner or later it will — they are a very basic consideration when you are designing and writing your programs.

The reason I’ve been sidestepping the question of exceptions for the past six chapters is that you first needed to understand classes and inheritance before you could understand what an exception is and appreciate what happens when an exception occurs. Now that you have a good grasp of these topics I can delve into how to use and deal with exceptions in a program.

THE IDEA BEHIND EXCEPTIONS

An exception usually signals an error and is so called because errors in your Java programs are bound to be the exception rather than the rule — by definition! An exception doesn’t always indicate an error though — it can also signal some particularly unusual event in your program that deserves special attention.

If you try to deal with the myriad and often highly unusual and often unexpected error conditions that might arise in your application code, your program structure soon becomes very complicated and difficult to understand. One major benefit of having an error signaled by an exception is that it separates the code that deals with errors from the code that is executed when things are moving along smoothly. Another positive aspect of exceptions is that they provide a way of enforcing a response to particular errors. With many kinds of exceptions, you must include code in your program to deal with them; otherwise, your code does not compile. These exceptions are referred to as checked exceptions. Unchecked exceptions are exceptions where you have the option of dealing with them or not.

One important idea to grasp is that not all errors in your programs need to be signaled by exceptions. Exceptions should be reserved for the unusual or catastrophic situations that can arise. A user entering incorrect input to your program, for instance, is a normal event and should be handled without resorting to exceptions. The reason for this is that dealing with exceptions involves quite a lot of processing overhead, so if your program is handling exceptions a lot of the time it runs a lot slower than it needs to.

An exception in Java is an object that’s created when an abnormal situation arises in your program. Exceptions can be created by the JVM, by standard library class methods, or by your application code. This exception object has fields that store information about the nature of the problem. The exception is said to be thrown — that is, the object identifying the exceptional circumstance is tossed as an argument to a specific piece of program code that has been written specifically to deal with that kind of problem. The code receiving the exception object as a parameter is said to catch it.

The situations that cause exceptions are quite diverse, but they fall into four broad categories, as shown in Table 7-1.

TABLE 7-1: Exceptions in Java

EXCEPTION DESCRIPTION
Code or data errors For example, if you attempt an invalid cast of an object, you try to use an array index that’s outside the limits for the array, or an integer arithmetic expression has a zero divisor.
Standard method exceptions For example, if you use the substring() method in the String class, it can throw a StringIndexOutOfBoundsException exception.
Throwing your own exceptions You see later in this chapter how you can throw a few of your own when you need to.
Java errors These can be due to errors in executing the Java Virtual Machine, which runs your compiled program, but usually arise as a consequence of an error in your program.

Before you look at how you make provision in your programs for dealing with exceptions, you should understand what specific classes of exceptions could arise.

TYPES OF EXCEPTIONS

An exception is always an object of some subclass of the standard class Throwable. This is true for exceptions that you define and throw yourself, as well as the standard exceptions that are defined in Java’s standard packages. It’s also true for exceptions that are thrown by methods in one or another of the standard packages.

Two direct subclasses of ThrowableError and Exception — are the parent of all the standard exceptions. Both these classes have subclasses that identify specific exception conditions. Figure 7-1 shows the Throwable class hierarchy.

Exceptions of Type Error

The exceptions that are defined by the Error class and its subclasses all represent conditions that you aren’t expected to do anything about, so you aren’t expected to catch them. Error has several direct subclasses including ThreadDeath, LinkageError, and VirtualMachineError. These are unchecked exceptions. You can do little or nothing to recover from these types of errors during the execution of the program. All you can hope to do (if you’re lucky) is read the error message that is generated by the exception being thrown and then, particularly in the case of a LinkageError exception, try to figure out what might be wrong with your code to cause the problem.

Exceptions of Type RuntimeException

Almost all the exceptions that are represented by subclasses of Exception are checked exceptions so you must include code in your programs to deal with them if your code may cause them to be thrown. In methods whose code has the potential to throw a checked exception, you must either handle the checked exception within the method or register that your method may throw such an exception. If you don’t, your program does not compile. You see in a moment how to handle exceptions and how to specify that a method can throw an exception.

Exceptions of type RuntimeException are exempt from the requirement to catch them, even though RuntimeException is derived from Exception. The reason that the compiler allows you to ignore RuntimeException exceptions is that they generally arise because of serious errors in your code. In most cases you can do little to recover the situation. However, in a few instances, it may be useful to include code to recognize them.

You need to know when these various types of exception can be thrown and how you can handle them. You try out some of the RuntimeException exceptions later in the chapter, as some of them are very easy to generate, but let’s see what other sorts of exception classes have Exception as a base.

Other Subclasses of Exception

All the other classes derived from the class Exception are checked exceptions so the compiler verifies that you either have provided code to handle the exception in a method where the exception may be thrown or that you’ve indicated that the method can throw such an exception. If you do neither, your code doesn’t compile. You look more at how you ensure that the code does compile in the next two sections.

Apart from those that are subclasses of RuntimeException, all exceptions thrown by methods in the Java class library are checked. In Chapter 8 you look at input and output where the code is liberally sprinkled with provisions for exceptions being thrown.

image

NOTE You see later in this chapter that when you want to define your own exceptions, you do this by subclassing the Exception class. Wherever your exception can be thrown by a method, the compiler verifies either that it is caught in the method or that the method definition indicates that it can be thrown by the method, just as it does for the built-in exceptions.

DEALING WITH EXCEPTIONS

As I discussed in the previous sections, if your code can throw exceptions other than those of type Error or type RuntimeException (you can assume that I generally include the subclasses when I talk about Error and RuntimeException exceptions), you must do something about it. Whenever you write code that can throw a checked 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 invoked 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 that can throw an exception that is neither a subclass of RuntimeException nor of Error. This could be an exception of type 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 method signature. Suppose you write a method that uses the methods from classes that support input/output that are defined in the package java.io. You 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 is a subclass of RuntimeException or Error, so the possibility of an exception being thrown needs to be declared. Because the method can’t handle any exceptions it might throw, if only for the simple reason that you 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 preceding fragment 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 propagates — if another method calls this method, it too must take account of the exceptions this method can throw. 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 does not compile if you don’t do one or the other. The reasons for this become obvious when you look at the way a Java program behaves when it encounters an exception.

Handling Exceptions

If you want to deal with checked exceptions where they occur, you can include three kinds of code blocks in a method to handle them — try, catch, and finally blocks:

  • 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 the associated try block. I get to how a catch block is associated with a try block later in this chapter.
  • 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 and then come back to the application of a finally block a little later.

The try Block

You can do something about many of the exceptions that are thrown. In the last two chapters you’ll be working with XML documents where exceptions are thrown to indicate errors in the XML. You will usually want to catch these and report the location of an error in the document. 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 the method 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 I am 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 you come to a working example in a moment, you 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 single parameter between parentheses. The parameter 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(IOException e) {
  // Code to handle the exception
}
 

This catch block handles exceptions of type IOException or of any subclass of IOException. If other checked exceptions can be thrown that are not declared in the method signature, this won’t compile. I’ll come back to handling multiple exception types in a moment.

TRY IT OUT: Using a try and a catch Block

This example throws an unchecked exception. I chose to do this rather that an example throwing checked exceptions because the condition for an ArithmeticException to be thrown is very easy to generate. The following code is really just an exhaustive log of the program’s execution:

image
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");
  }
}
 

TestTryCatch.java

If you run the example, you should get the following 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 causes an ArithmeticException exception to be thrown by the Java Virtual Machine. The first line in the try block enables you to track when the try block is entered, and the second line throws an exception. The third line can be executed only if the exception isn’t thrown — which can’t occur in this example.

The output 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 is:

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.

image

WARNING 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 available only 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. You can see this with a variation of the previous example.

TRY IT OUT: A Loop Block That Is a try Block

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

image
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: " + e);
      }
    }
 
    System.out.println("After try block");
  }
}
 

TestLoopTryCatch.java

This produces the following 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: java.lang.ArithmeticException: / by zero
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 because the catch is inextricably bound to the try. You can see this from the output. On the fourth iteration, you get an exception thrown because j is 0. However, after the catch block is executed, you still get one more iteration with j having the value −1.

Even though the try and catch blocks are both 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 does 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:

image
  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("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: " + e);
    }
 
    System.out.println("After try block");
  }
 

TestLoopTryCatch2.java

With this version of main(), the program produces the following output:

Try block entered.
Try block entered  i = 12 j = 3
4
Try block entered  i = 12 j = 2
6
Try block entered  i = 12 j = 1
12
Try block entered  i = 12 j = 0
Arithmetic exception caught: java.lang.ArithmeticException: / by zero
After try block
 

Now, you 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 are caught by the first catch block, and exceptions of type IndexOutOfBoundsException are caught by the second. Of course, if an ArithmeticException is thrown, only the code in that catch block is 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 that may be thrown in a try block, the order of the catch blocks can be important. When an exception is thrown, it is 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 would 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 does 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 detects 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 because they are always 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 wouldn’t get past the compiler — it would be flagged as an error.

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. That enables you to indentify and deal with each type of exception individually.

Catching Multiple Exception Types in a Block

You can catch an exception that may be any of two or more different types in a single catch block. You specify the possible types for the catch block parameter separated by |. Here’s a fragment showing how you do this:

try {
  // Code that can throw exceptions 
  // of type ArithmeticException and ArrayStoreException...
} catch(ArithmeticException|ArrayStoreException e) {
  // Code to handle exception of either type...
}
 

The catch block is executed if an exception of either type ArithmeticException or type ArrayStoreException is thrown in the try block. This is particularly useful when you want to handle exceptions of two or more different types in the same way because it avoids having to write multiple catch blocks containing the same code. This can arise quite easily when you call several methods in a single block, each of which may throw an exception of a different type. When you want to handle more than one type of exception in the same way, you can use the multiple types form for the catch block parameter.

Of course, you can still have multiple catch blocks, each of which may respond to one or more exception types.

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 example, 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 associated try block. A finally block is always executed, regardless of whether or not exceptions are thrown during the execution of the associated try block. If a file needs to be closed, or a critical resource released, you can guarantee that it is 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 does not compile.

image

NOTE 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 that if a value is returned within a finally block, this return overrides any return statement executed in the try block.

Structuring a Method

You’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 Figure 7-2.

The doSomething() method in Figure 7-2 deals with exceptions of either type MyException1 or MyException2 in the first catch block. The second catch block is executed for exceptions of type MyException3. The method can also throw two other types of exceptions that are not caught within the method, and these are identified in the throws clause that follows the parameter list for the method. The code in the finally block always gets executed, regardless of whether an exception is thrown or not.

In many cases, a method needs only 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.

Execution Sequence

You saw how the sequence of execution proceeds with the simple case of a try block and a single catch block. Let’s explore the sequence in which code executes when you have the try-catch-finally combinations of blocks, when different exceptions are thrown. This is easiest to comprehend by considering an example. You can use the following code to create a range of exceptions and conditions.

TRY IT OUT: Execution Sequence of a try Block

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

image
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()...
  }
}

TryBlockTest.java

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, you are able to cause exceptions of type ArithmeticException and ArrayIndexOutOfBoundsException to be thrown. You use a try block plus two catch blocks for the exceptions, and you throw in a finally block for good measure. Here’s the code for divide():

image
  public static int divide(int[] array, int index) {
    try {
      System.out.println("
First 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()");
      System.out.println("index = " + index +
       "  Expression: " + "array[" + index + "]/array["+ (index+1) +"] is " +
                                        array[index] + "//" + array[index+1]); 
    } catch(ArrayIndexOutOfBoundsException e) {
      System.out.println("Index-out-of-bounds exception caught in divide()");
      System.out.println("array length = " + array.length + 
                                                       "  index = " + index);
    } finally {
      System.out.println("finally block in divide()");
    }
 
    System.out.println("Executing code after try block in divide()");
    return array[index + 2];
  }

TryBlockTest.java

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

image
  public static void main(String[] args) {
    int[] x = {10, 5, 0};               // Array of three integers
 
    // This block only throws an exception if the divide() method 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("
Press 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()");
  }
 

TryBlockTest.java

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

If you run the example, it produces the following 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()
index = 0  Expression: array[0]/array[1] is 10/0
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()
array length = 3  index = 1
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 you can trace the sequence of execution. Note that the finally block is executed, whatever happens in the divide() method, even when the normal return is executed in the try block in divide().

Within the divide() method, the code in the try block can throw an ArithmeticException if the element array[index+1] of the array passed to it is 0. It can also throw an ArrayIndexOutOfBoundsException 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 are not apparent in the calling method main().

Note, however, that the last statement in divide() can also throw an ArrayIndexOutOfBoundsException:

return array[index+2];
 

This statement is outside the try block, so the exception is not caught. The exception is therefore thrown by the method when it is called in main(). However, you 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 executes without error; the second call causes an arithmetic exception in the method; and the third call causes 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. Because this is one of the exceptions that the compiler checks for, you 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 you don’t do one or the other, the program does not compile.

Using the read() method in this way has the effect of pausing the program until the Enter key is pressed. You look at read(), and other methods for I/O operations in the next four chapters. The IOException class is in the package java.io, so you need the import statement for this class because you refer to it in the catch block using its unqualified name. Of course, if you referred to it as java.io.IOException, you would not need to import the class name. 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 TryBlockTest example indicates that execution of the try block in main() has begun. The next block of output from the example is the result of a straightforward execution of the divide() method:

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

No exceptions occur in divide(), so no catch blocks are executed.

The code at the end of the divide() method, 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 is executed.

The sequence of execution when no exceptions occur is shown in Figure 7-3.

Figure 7-3 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 is 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 causes an immediate return to the calling point, and the code following the finally block isn’t executed in this case.

Execution When an Exception Is Thrown

The next block of output corresponds to an ArithmeticException being thrown and caught in the divide() method:

First try block in divide() entered
Arithmetic exception caught in divide()
index = 0  Expression: array[0]/array[1] is 10/0
finally block in divide()
Executing code after try block in divide()
result = 2
 

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 a try-catch-finally set of blocks when an exception occurs is shown in Figure 7-4.

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 I discussed earlier, if a return statement that returns a value is executed within a finally block, that value is returned, not the value from any previous return statement.

If an exception is thrown in a finally block, this will terminate the execution of the code in the finally block and the method. You can see the effect if you add the following statement to the finally block in divide():

      int result = array[index]/array[index+1];
 

When ArithmeticException is thrown in the try block, the same exception is thrown in the finally block as a result of this statement. You will see from the output that the divide() method terminates and the exception is caught in main(). Try moving the statement to the catch block for ArithmeticException. You’ll see that the finally block still executes even though an exception is thrown in the catch block.

Execution When an Exception Is Not Caught

The next block of output is a consequence of the third call to the divide() method:

First try block in divide() entered
Index-out-of-bounds exception caught in divide()
array length = 3  index = 1
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()
 

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 divide() method because the statement throwing it isn’t in a try block. Because this exception isn’t caught in the divide() method, the method terminates immediately at the point where the divide() method was called. This causes the code in the relevant catch block in main() to be executed as a consequence of the uncaught exception.

An exception that isn’t caught in a method is always propagated upward to the calling method. It continues 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 terminates and a suitable message is displayed. This situation is illustrated in Figure 7-5.

The sequence of events in Figure 7-5 is shown by the numbers on the arrows. It 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 to method3(). It isn’t caught and continues to be thrown until it reaches method1() where there’s a catch block to handle it.

In our TryBlockTest example, 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

I don’t go into these in detail, but you should note that you can have nested try blocks, as Figure 7-6 illustrates.

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 in Figure 7-6, the catch block for the outer try block catches 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 affects 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 you look at how to define your own exceptions later in this chapter, you use exactly the same mechanism to throw them.

EXCEPTION OBJECTS

Well, you now understand how to put try blocks together with catch blocks and finally blocks in your methods. You may be thinking at this point that it seems a lot of trouble to go to just to display a message when an exception is thrown. You may be right, but whether you can do so very much more depends on the nature and context of the problem. In many situations a message may be the best you can do, although you can produce messages that are a bit more informative than those you’ve used so far in our examples. For one thing, I have totally ignored the exception object that is passed to the catch block.

The exception object that is passed to a catch block can provide additional information about the nature of the problem that originated it. To understand more about this, let’s first look at the members of the base class for exceptions Throwable because these are inherited by all exception classes and are therefore contained in every exception object that is thrown.

The Throwable Class

The Throwable class is the class from which all Java exception classes are derived — that is, every exception object contains the methods defined in this class. The Throwable class has five constructors:

  • Throwable() creates an object with no detail message.
  • Throwable(String message) create an object containing message as the message.
  • Throwable(String message, Throwable cause) creates an object containing message as the message and a second Throwable object, cause, specifying the cause of the exception.
  • Throwable(String message, Throwable cause, boolean suppress, boolean stackTrace) creates an object containing message as the message and a second Throwable object, cause, specifying the cause of the exception. If suppress is true, it allows exception to be suppressed in order to deliver this exception. If stackTrace is true, recording of the stack trace is enabled.
  • Throwable(Throwable cause) creates an object with the message cause.toString() if cause is not null, and cause as the cause of the exception.

The constructors with Throwable parameters provide the basis for storing a reference to one exception inside another. The cause reference can be obtained by calling getCause() for a Throwable object. This allows exceptions to be chained, so when one exception has been thrown, you can create another exception that provides more information about the problem and record within it a reference to the original exception that caused the new exception to be thrown. This ensures that information available from the original exception is not lost. You’ll see that you can define you own exception classes to allow exception chaining.

Objects of type Throwable can contain the following information:

  • A message, which I have just referred to as being initialized by a constructor.
  • A Throwable object identifying the cause of the exception.
  • A record of the execution stack at the time the object was created.
  • A record of exceptions suppressed in order to deliver this exception.

The execution stack keeps track of all the methods that are in execution at any given instant. It provides the means whereby executing a return gets back to the calling point for a method. The record of the execution stack that is stored in the exception object consists of the line number in the source code where the exception originates followed by a trace of the method calls that immediately precede the point at which the exception occurs. This is made up of the fully qualified name for each of the methods called, plus the line number in the source file where each method call occurs. The method calls are in sequence with the most recent method call appearing first. This helps you to understand how this point in the program was reached.

The Throwable class has the following public methods that enable you to access the message, the cause, and the stack trace as shown in Table 7-2.

TABLE 7-2: Throwable Class Public Methods

METHOD DESCRIPTION
getMessage() This returns the contents of the message, describing the current exception. This is typically the fully qualified name of the exception class (it is a subclass of Throwable) and a brief description of the exception.
getCause() Returns the Throwable that records the cause for this exception. This will typically be the exception that caused this exception to be thrown.
printStackTrace() This outputs the message and the stack trace to the standard error output stream — which is the screen in the case of a console program.
printStackTrace
(PrintStream s)
This is the same as the previous method except that you specify the output stream as an argument. Calling the previous method for an exception object e is equivalent to e.printStackTrace(System.err);

You can get the stack trace as an array of StackTraceElement references by calling getStackTrace() for a Throwable object. Each StackTraceElement object records information about the execution point in a method when the exception was thrown. There will be a StackTraceElement array element for each method in the call stack when the exception was thrown. The following StackTraceElement methods provide you with details of a stack trace entry:

  • getClassName() returns the fully qualified name of the class containing the execution point for this stack trace entry.
  • getFileName() returns the name of the source file containing the execution point.
  • getLineNumber() returns the line number for the execution point in the source file.
  • getMethodName() returns the name of the method containing the execution point.

Another Throwable method, fillInStackTrace(), updates the stack trace to the point at which this method is called. For example, if you put a call to this method in the catch block

e.fillInStackTrace();
 

the line number recorded in the stack record for the method in which the exception occurs is the line where fillInStackTrace() is called. The main use of this is when you want to rethrow an exception (so it is caught by the calling method) and record the point at which it is rethrown. For example:

e.fillInStackTrace();                            // Record the throw point
throw e;                                         // Rethrow the exception
 

In practice, it’s often more useful to throw an exception of your own that encapsulates the exception that caused your exception to be thrown. This is referred to as exception chaining. You see how to define your own exceptions in the next section and use chained exceptions, but first, let’s exercise some of the methods defined in the Throwable class and see the results.

TRY IT OUT: Dishing the Dirt on Exceptions

The easiest way to try out some of the methods I’ve just discussed is to make some changes to the catch blocks in the divide() method you have in the TryBlockTest class example. I make the class for this example TryBlockTest2. The main() method is the same as for TryBlockTest. Here’s the modified version of divide():

image
public static int divide(int[] array, int index) {
  try {
    System.out.println("
First 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.err.println("Arithmetic exception caught in divide()
" +
                       "
Message in exception object:
	" +  
                        e.getMessage());
    System.err.println("
Stack trace output:
");
    e.printStackTrace();
    System.err.println("
End of stack trace output
");
  } catch(ArrayIndexOutOfBoundsException e) {
    System.err.println("Index-out-of-bounds exception caught in divide()
" +
                       "
Message in exception object:
	" + e.getMessage());
    System.err.println("
Stack trace output:
");
    e.printStackTrace();
    System.out.println("
End of stack trace output
");
  } finally {
    System.err.println("finally clause in divide()");
  }
  System.out.println("Executing code after try block in divide()");
  return array[index + 2];
}
 

TryBlockTest2.java

If you recompile the program and run it again, it produces all the output as before but with extra information when exceptions are thrown in the divide() method. The output generated for the ArithmeticException is:

Message in exception object:
Arithmetic exception caught in divide()
 
Message in exception object:
       / by zero
 
Stack trace output:
 
java.lang.ArithmeticException: / by zero
      at TryBlockTest2.divide(TryBlockTest2.java:44)
      at TryBlockTest2.main(TryBlockTest2.java:12)
 
End of stack trace output
 

The output generated for the ArrayIndexOutOfBoundsException is:

Index-out-of-bounds exception caught in divide()
 
Message in exception object:
       3
 
Stack trace output:
 
java.lang.ArrayIndexOutOfBoundsException: 3
    at TryBlockTest2.divide(TryBlockTest2.java:44)
    at TryBlockTest2.main(TryBlockTest2.java:14)
 
End of stack trace output
 

How It Works

The code in each of the catch blocks in the divide() method output the message associated with the exception object e by calling its getMessage() method. You could have just put e here, which would invoke the toString() method for e, and in this case, the class name for e would precede the message. The message for the arithmetic exception identifies it as being caused by a divide by zero. The message for the index out of bounds exception is just the invalid index value.

There are a couple of extra println() calls around the call to printStackTrace() to make it easier to find the stack trace in the output.

The first stack trace, for the arithmetic exception, indicates that the error originated at line 44 in the source file TryBlockText.java and the last method call was at line 14 in the same source file. The second stack trace provides similar information about the index-out-of-bounds exception, including the offending index value. As you can see, with the stack trace output, it’s very easy to see where the error occurs and how this point in the program is reached.

Standard Exceptions

The majority of predefined exception classes in Java don’t provide detailed information about the conditions that created the exception. The type alone serves to differentiate one exception from another in most cases. This general lack of detailed information is because it can often be gleaned only by prior knowledge of the computation that is being carried out.

This should spark the glimmer of an idea. If you need more information about the circumstances surrounding an exception, you are going to have to obtain it and, equally important, communicate it to the appropriate point in your program. This leads to the notion of defining your own exceptions.

DEFINING YOUR OWN EXCEPTIONS

There are three basic reasons for defining your own exception classes:

  • You want to add information when a standard exception occurs, and you can do this by rethrowing an object of your own exception class.
  • You may have error conditions that arise in your code that warrant the distinction of a special exception class.
  • To consolidate exceptions into a smaller, more manageable set (making it easier on calling methods).

However, you should bear in mind that there’s a lot of overhead in throwing exceptions, so it is not a valid substitute for “normal” recovery code that you would expect to be executed frequently. If you have recovery code that is executed often, then it doesn’t belong in a catch block, but rather in something like an if-else statement.

Let’s see how you create your own exceptions.

Defining an Exception Class

Your exception classes must always have Throwable as a superclass; otherwise, they do not define an exception. Although you can derive them from any of the standard exception classes, your best policy is to derive them from the Exception class or from a subclass of Exception. This allows the compiler to keep track of where such exceptions are thrown in your program and checks that they are either caught or declared as thrown in a method. If you use RuntimeException or one of its subclasses, the compiler checking for catch blocks of your exception class are suppressed.

Let’s go through an example of how you define an exception class:

public class DreadfulProblemException extends Exception {
  // Constructors
  public DreadfulProblemException(){ }       // Default constructor
 
  public DreadfulProblemException(String s) {
    super(s);                                // Call the base class constructor
  }
}
 

This is the minimum you should supply in your exception class definition. By convention, your exception class should include a default constructor and a constructor that accepts a String object as an argument. The message stored in the superclass Exception (in fact, in Throwable, which is the superclass of Exception) is automatically initialized with the name of your class, whichever constructor for your class objects is used. The String passed to the second constructor is appended to the name of the class to form the message stored in the exception object. Of course, you can subclass any of the classes derived from Exception.

You can add other constructors in your exception class. In general, you want to do so, particularly when you’re rethrowing your own exception after a standard exception has been thrown because you are likely to want additional parameters for passing information relating to your exceptions. In addition, you typically want to add instance variables to the exception class that store additional information about the problem, plus methods that enable the code in a catch block to get at the data. Because your exception class is ultimately derived from Throwable, the stack trace information is automatically available for your exceptions along with the capability for creating chained exceptions. If you plan to use exception chaining, you will want to define at least one constructor with a Throwable parameter so that a reference to a previous exception in a chain can be passed. In this case the class constructors could be:

public class DreadfulProblemException extends Exception {
  // Constructors
  public DreadfulProblemException(){ }       // Default constructor
 
  public DreadfulProblemException(String s) {
    super(s);                                // Call the base class constructor
  }
 
  // Constructor providing for chained exceptions
  public DreadfulProblemException(String s, Throwable cause) {
    super(s, cause);                         // Call the base class constructor
  }
}
 

The exception chaining capability is built into the base class because it is defined in Throwable. Therefore you can just pass the Throwable argument that is passed to your constructor to the base class constructor.

Even when you have not provided a constructor in your exception class that accepts a Throwable reference, you can still chain your exception object to the exception that caused your exception to be thrown. Your exception class inherits the initCause() method from Throwable that accepts an argument of type Throwable. Calling this method chains your exception to the previous exception. Note that you can only call this method for an exception once, and you must not call it at all if you created your exception object with a Throwable cause argument. The initCause() method will throw IllegalStateException if either is the case. It will also throw IllegalArgumentException if you pass a reference to your exception object as the argument; an exception cannot be its own cause.

Throwing Your Own Exception

As you saw earlier, you throw an exception with a statement that consists of the throw keyword, followed by an exception object. This means you can throw your own exception in a method with the following statements:

DreadfulProblemException e = new DreadfulProblemException();
throw e;
 

The method ceases execution at this point — unless the preceding code snippet is in a try block with a catch block that catches this exception. The exception will be thrown to the calling program if it is not caught. The message in the exception object consists only of the qualified name of the exception class.

If you want to add a specific message to the exception, you could define it as:

DreadfulProblemException e = new DreadfulProblemException("Uh-Oh, trouble.");
 

You’re using a different constructor here. In this case the message stored in the superclass is a string that consists of the class name with the string passed to the constructor appended to it. The getMessage() method inherited from Throwable, therefore, returns a String object containing the following string:

"DreadfulProblemException: Uh-Oh, trouble."

You can also create an exception object and throw it in a single statement. For example:

throw new DreadfulProblemException("Terrible difficulties");
 

In all the examples of throwing your own exception, the stack trace record inherited from the superclass Throwable is set up automatically.

If you plan to use your exception type in a catch block to provide further information about an exception that has been thrown, you can make provision for recording the original exception:

try {
  // Code that may throw SomeException...
} catch SomeException e) {
  // Analyze the cause...
  throw new DreadfulProblemException("Disaster strikes!", e);
}
 

This fragment throws an object of your exception class that you create with a constructor that accepts a second argument of type Throwable to record the previous exception object. This will enable the code that catches your exception to access the SomeException object that caused your exception to be thrown and extract information from that.

An Exception Handling Strategy

You should think through what you want to achieve with the exception handling code in your program. There are no hard-and-fast rules. In some situations you may be able to correct a problem and enable your program to continue as though nothing happened. In other situations, outputting the stack trace and a fast exit is the best approach — a fast exit being achieved by calling the exit() method in the System class. Here you take a look at some of the things you need to weigh when deciding how to handle exceptions.

Consider the last example where you handled arithmetic and index-out-of-bounds exceptions in the divide() method. Although this was a reasonable demonstration of the way the various blocks worked, it wasn’t a satisfactory way of dealing with the exceptions in the program for at least two reasons.

  • First, it does not make sense to catch the arithmetic exceptions in the divide() method without passing them on to the calling method. After all, it is the calling method that set the data up, and only the calling program has the potential to recover the situation.
  • Second, by handling the exceptions completely in the divide() method, you allow the calling program to continue execution without any knowledge of the problem that arose. In a real situation this would undoubtedly create chaos, as further calculations would proceed with erroneous data.

You could have simply ignored the exceptions in the divide() method by having no try-catch combination for the exception. This might not be a bad approach in this particular situation, but the first problem the calling program would have is determining the source of the exception. After all, such exceptions might also arise in the calling program itself. A second consideration could arise if the divide() method were more complicated. There could be several places where such exceptions might be thrown, and the calling method would have a hard time distinguishing them.

An Example of an Exception Class

Another possibility is to catch the exceptions in the method where they originate and then pass them on to the calling program. You can pass them on by throwing new exceptions that provide more granularity in identifying the problem (by having more than one exception type or by providing additional data within the new exception type). For example, you could define more than one exception class of your own that represents an ArithmeticException, where each reflected the specifics of a particular situation. This situation is illustrated in Figure 7-7. The numbers on the arrows indicate the sequence.

Figure 7-7 shows how two different circumstances causing an ArithmeticException in method2() are differentiated in the calling method, method1(). The method2() method can throw an exception either of type Exception1 or of type Exception2, depending on the analysis that is made in the catch block for the ArithmeticException type. The calling method has a separate catch block for each of the exceptions that may be thrown.

You could also define a new exception class that had instance variables to identify the problem more precisely. Let’s suppose that in the last example you wanted to provide more information to the calling program about the error that caused each exception in the divide() method. The primary exception can be either an ArithmeticException or an ArrayIndexOutOfBoundsException, but because you’re dealing with a specific context for these errors, you could give the calling program more information by throwing your own exceptions.

It would also make sense to make your exceptions chained exceptions that also recorded the originating exception. Let’s take the ArithmeticException case as a model and define an exception class to use in the program to help identify the reason for the error more precisely. This is just for illustration purposes and is not intended to imply that you need to catch exceptions of type ArithmeticException.

TRY IT OUT: Chaining Exceptions

This is the first step in creating a new variation on the TryBlockTest example called TryChainedExceptions. You can define a new exception class that you will use when an ArithmeticException is thrown in the divide() method:

image
public class ZeroDivideException extends Exception {
  private int index = -1;              // Index of array element causing error
 
  // Default Constructor
  public ZeroDivideException(){ }
 
  // Constructor that can be chained
  public ZeroDivideException(String s, Throwable cause) {
    super(s, cause);                              // Call the base constructor
  }
 
  // Contructor recording an index value & can be chained
  public ZeroDivideException(int index, Throwable cause) {
    super(cause);                                // Call the base constructor
    this.index = index;                          // Set the index value
  }
 
  // Get the array index value for the error
  public int getIndex() {
    return index;                                // Return the index value
  }
}
 

Directory "TryChainedExceptions"

How It Works

You might think that because this exception type is a form of arithmetic exception, it would be advantageous to derive the class from ArithmeticException. Of course, if you did this, the new exception type would have RuntimeException as an indirect base class, and therefore such exceptions would not need to be caught. Because you have derived the ZeroDivideException class from the Exception class, the compiler checks that the exceptions thrown are either caught or identified as thrown in a method.

Your class inherits all the members of the class Throwable via the Exception class, so you get the stack trace record, the message for the exception, and the ability to record the originating exception for free. It also inherits the toString() method, which is satisfactory in this context, but you could override this if desired. The Exception class has five constructors with the same parameters as the Throwable class constructors. You can therefore to pass a Throwable reference to record the originating exception to the base class constructor, so the ArithmeticException that was originally thrown is still available from your exception object.

You’ve added a data member, index, to store the index value of the zero divisor in the array passed to divide(). This gives the calling program a chance to fix this value if appropriate in the catch block for the exception. This might arise in an application with data that might be logged automatically in a manufacturing plant for example, where because of the conditions, some erroneous data is to be expected. In this case, the catch block needs to include code that enables the divide() method to be called again with the corrected array to reprocess the data. There may be circumstances where the index cannot be supplied. In this case the default value that is an invalid index value will allow code that catches the exception to determine when this is the case.

Let’s now put it to work in the TryChainedExceptions example.

TRY IT OUT: Using Chained Exceptions

The computations in this example are completely arbitrary and have been contrived so that exceptions will be thrown. The TryChainedExceptions class defines two static methods, main() and divide(). You’ll use the new exception class in two contexts — in the divide() method when you catch a standard ArithmeticException and in the calling method main() to catch the new exception. Let’s define the divide() method first:

image
public static int divide(int[] array, int index) throws ZeroDivideException {
    try {
      System.out.println("
First try block in divide() entered");
      array[index] = array[index+2]/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()");
      ZeroDivideException zde = new ZeroDivideException(index + 1, e);
      System.out.println("Throwing ZeroDivideException");
      throw zde;                                    // Throw the new exception
    } catch(ArrayIndexOutOfBoundsException e) {
      System.out.println(
                  "Index-out-of-bounds index exception caught in divide()");
    }
    System.out.println("Executing code after try block in divide()");
    return array[index];
  }
}
 

Directory "TryChainedExceptions"

Note the throws clause in the method definition. Without this you will get an error message from the compiler. The statements in the catch block for ArithmeticException creates and throws a new exception.

This new exception needs to be caught in the calling method main():

image
public static void main(String[] args) {
    int[] x = {10, 5, 0, 3, 12, 0, 6};        // Array of  integers
    System.out.print("Values: ");
    for(int xValue : x) {
      System.out.print("  " + xValue);
    }
    System.out.println();
 
    for(int i = 0 ; i < x.length ; ++i) {
      // 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,i));
      } catch(ZeroDivideException e) {
        System.out.println("
ZeroDivideException caught in main()");
        int index = e.getIndex();   // Get the index for the error
        if(index > 0) {             // Verify it is valid and now fix the array
          x[index] = 1;                 // ...set the divisor to 1...
          x[index + 1] = x[index - 1];  // ...and set the result
          e.printStackTrace();
          System.err.println("Zero divisor at x[" + index + "] corrected to " +
                                                                     x[index]);
        }
      } 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.print("Values: ");
    for(int xValue : x) {
      System.out.print("  " + xValue);
    }
    System.out.println();
}
 

Directory "TryChainedExceptions"

You should put the TryChainedExceptions class file and the file for the ZeroDivideException class together in the same directory.

The output from this example includes:

Values:   10  5  0  3  12  0  6
First try block in main()entered
... more output
First try block in divide() entered
Arithmetic exception caught in divide()
Throwing ZeroDivideException
 
ZeroDivideException caught in main()
ZeroDivideException: java.lang.ArithmeticException: / by zero
       at TryChainedExceptions.divide(TryChainedExceptions.java:49)
       at TryChainedExceptions.main(TryChainedExceptions.java:16)
Caused by: java.lang.ArithmeticException: / by zero
       at TryChainedExceptions.divide(TryChainedExceptions.java:43)
       ... 1 more
Zero divisor at x[2] corrected to 1
Outside first try block in main()
... more output
 
First try block in divide() entered
Index-out-of-bounds index exception caught in divide()
Executing code after try block in divide()
result = 1
Outside first try block in main()
First try block in main()entered
... more output
 
Outside first try block in main()
Values:   0  5  2  0  12  1  12
 

How It Works

The main() method creates an array of integers and iterates over the elements in the for loop. The divide() method is called in a try block, so any exceptions throw by divide() can be caught in main(). The divide() methods sets the value of the array element corresponding to the index value that is passed. The value is the ratio of elements at index+2 and index+1. When the divisor is zero, an ArithmeticException is thrown and caught by the catch block in divide().

The catch block for ArithmeticException creates an instance of the new exception class ZeroDivideException and passes the index for the element with the zero value and the reference to the ArithmeticException to the constructor. The new exception is thrown to notify the calling program, main().

The main() method catches the ZeroDivideException and corrects the element in error by setting its value to 1. This allows the process to continue. You can see from the output that the ZeroDivideException object has the complete stack trace available, including that relating to the ArithmeticException and the ArithmeticException message is also there. This is because the ZeroDivideException chains to the ArithmeticException.

The loop in main() iterates over all the index values for the array. Because divide() references elements at index+1 and index+2, ArrayIndexOutOfBoundsException is inevitably thrown in the try block in divide(). This exception is not rethrown in the catch block and so can never be caught in main().

image

WARNING The example has a try-catch block combination inside the loop to enable you to see several exceptions being thrown and caught, but this is not a good idea in practice. Setting up try-catches are resource-intensive, so repeatedly doing that inside a loop could result in a lot of overhead.

Chains with Multiple Links

The previous example shows two exceptions in a chain, but there are no limitations on this. Where the method call stack goes through several levels, you can chain as many exceptions together as necessary. You can access the previous exception to the last exception thrown by calling its getCause() method. You can then call getCause() for the Throwable reference you have obtained to get the next exception in the chain. You can repeat this process until getCause() returns null, which signals that you have reach the end of a chain of exceptions.

SUMMARY

In this chapter you learned what exceptions are and how to deal with them in your programs. You should make sure that you consider exception handling as an integral part of developing your Java programs. The robustness of your program code depends on how effectively you deal with the exceptions that can be thrown within it. Having said that, you should keep in mind that exceptions involve considerable overhead and should be reserved for very unusual situations. Problems that arise in the normal course of events should be handled without recourse to throwing exceptions.

EXERCISES

You can download the source code for the examples in the book and the solutions to the following exercises from www.wrox.com.

1. Write a program that generates exceptions of type NullPointerException, NegativeArraySizeException, and IndexOutOfBoundsException. Record the catching of each exception by displaying the message stored in the exception object and the stack trace record.

2. Add an exception class to the last example that differentiates between the index-out-of-bounds error possibilities, rethrows an appropriate object of this exception class in divide(), and handles the exception in main().

3. Write a program that calls a method that throws an exception of type ArithmeticException at a random iteration in a for loop. Catch the exception in the method and pass the iteration count when the exception occurred to the calling method by using an object of an exception class you define.

4. Add a finally block to the method in the previous example to output the iteration count when the method exits.

image

• WHAT YOU LEARNED IN THIS CHAPTER

TOPIC CONCEPT
Exceptions Exceptions identify abnormal errors that arise in your program. Exceptions are objects of subclasses of the Throwable class.
Standard Exceptions Java includes a set of standard exceptions that may be thrown automatically, as a result of errors in your code, or may be thrown by methods in the standard classes in Java.
Uncaught Exceptions If a method throws exceptions that aren’t caught, and aren’t represented by subclasses of the Error class or the RuntimeException class, then you must identify the exception classes in a throws clause in the method definition.
Catching Exceptions If you want to handle an exception in a method, you must place the code that may generate the exception in a try block. A method may have several try blocks.
catch Blocks Exception handling code is placed in a catch block that immediately follows the try block that contains the code that can throw the exception. A try block can have multiple catch blocks that each deals with a different type of exception.
finally Blocks A finally block is used to contain code that must be executed after the execution of a try block, regardless of how the try block execution ends. A finally block is always executed before execution of the method ends.
Throwing Exceptions You can throw an exception by using a throw statement. You can throw an exception anywhere in a method. You can also rethrow an existing exception in a catch block to pass it to the calling method.
Defining Exceptions You can define your own exception classes. In general, your exceptions classes should be derived from the Exception class to ensure that they are checked exceptions. The compiler ensures that all checked exceptions are dealt with in the code where such exceptions may be thrown.
Chained Exceptions When you throw a new exception in a catch block, you can record the previous exception by creating your exception object to store a reference to the previous exception or by calling initCause() for your new exception object.
image
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.216.21.138