Java 101: Exceptions to the programming rules, Part 2

Incorporate Java's throw-object/catch-object exception-handling technique into your Java programs

1 2 3 4 5 Page 3
Page 3 of 5
Caution
The catch keyword must appear immediately after a try block's closing brace character. The compiler reports an error upon encountering anything else ahead of catch.

The previous code fragment demonstrated catching a checked exception object; a program can also catch an unchecked exception object. For example, to quickly decipher Listing 1's problem, place SortIntegerArray1's sort (numbers); method call inside a try block and follow that statement with an appropriate catch clause. When the JVM throws an ArrayIndexOutOfBoundsException unchecked exception object, the new catch clause displays an appropriate message. Execution then leaves that clause and the program continues with the for loop statement. That loop statement and the subsequent System.out.println (numbers [i]); method-call statement cause the partially sorted array's contents to output -- which might clue you in to the code's problem. Listing 2's SortIntegerArray2 source code demonstrates this:

Listing 2. SortIntegerArray2.java

// SortIntegerArray2.java
class SortIntegerArray2
{
   public static void main (String [] args)
   {
      int numbers [] = { 22, 13, 5, 76, 3 };
      try
      {
         sort (numbers);
      }
      catch (ArrayIndexOutOfBoundsException e)
      {
         System.out.println ("Unable to sort: " + e.getMessage ());
      }
      for (int i = 0; i < numbers.length; i++)
           System.out.println (numbers [i]);
   }
   static void sort (int [] x)
   {
      for (int i = 0; i < x.length-1; i++)
           for (int j = 0; j < x.length-i; j++)
                if (x [j] > x [j+1])
                {
                    int temp = x [j];
                    x [j] = x [j+1];
                    x [j+1] = temp;
                }
   }
}

When run, SortIntegerArray2 produces the following output:

Unable to sort: null
13
5
22
3
76

The output proves that a program can use the catch clause to catch unchecked exception objects. The null that appears beside Unable to sort: indicates that the JVM did not specify a message as part of the ArrayIndexOutOfBoundsException object.

Multiple catch clauses

Associating multiple catch clauses with one try block, where each catch clause typically catches a specific exception object type can prove convenient. For example, one catch clause catches FileNotFoundException objects to handle that file exception type, whereas another catch clause catches other kinds of file-related exception objects to deal with other file-related exception types. The following code fragment demonstrates that scenario:

try
{
    // Open file.
    // File-manipulation statements.
    // Close file. This executes when no exceptions are thrown.
}
catch (FileNotFoundException e)
{
    // Handle exception.
    // Close file. This executes when file-not-found exceptions are thrown.
}
catch (IOException e)
{
    // Handle exception.
    // Close file. This executes when other file-related exceptions are thrown.
}

Should the try block directly/indirectly throw a FileNotFoundException object, the JVM transfers execution to the first catch clause. If some other input/output exception results in a different thrown IOException object, the second catch clause executes.

When the JVM confronts a sequence of multiple catch clauses, the JVM searches for the appropriate clause from the sequence's top to its bottom. Using the previous code fragment as an example, the JVM first examines catch (FileNotFoundException e). If it finds a match, that clause executes. Otherwise, the JVM examines catch (IOException e) and executes that clause if it matches. That search order implies an important point: some catch clauses might never execute, as the following code fragment demonstrates:

try
{
    // Open file.
    // File-manipulation statements.
    // Close file. This executes when no exceptions are thrown.
}
catch (IOException e)
{
    // Handle exception.
    // Close file. This executes when other file-related exceptions are thrown.
}
catch (FileNotFoundException e)
{
    // Handle exception.
    // Close file. This executes when file-not-found exceptions are thrown.
}

Suppose the previous code fragment's try block throws a FileNotFoundException object. Because FileNotFoundException is an IOException subclass, the JVM passes control to the catch (IOException e) catch clause. Not only does catch (IOException e) catch IOException objects, but that clause catches all IOException subclass objects. As a result, the catch (FileNotFoundException e) clause never executes. Because that clause is dead code, the compiler reports an error.

Caution
In a multiple catch clause sequence following a try block, if a catch clause that catches exception objects of a superclass exception type (such as IOException) precedes a clause that catches exception objects of one of that superclass exception type's subclass exception types (such as FileNotFoundException), the compiler reports an error.

Throw exception objects from catch clauses

Why would you throw an exception object from a catch clause? Consider what happens when a development team creates a Java library: To begin, the team leader designs a specification that itemizes those classes, methods, and fields that a program can access, including throws clauses and their checked exception class names. Think of the specification as a contract between the library and programs using that library. That contract specifies the responsibilities of the library and user programs to each other.

Given the specification, the team begins to code the library. Some library methods might call internal helper methods to assist in accomplishing various tasks. Those helper methods might throw various exception object types that the specification's throws clauses portion does not describe. Therefore, a library method must catch internal exception objects and throw different kinds of external exception objects that describe the exceptions according to the specification. Typically, translating internal exception objects to external exception objects occurs in a catch clause. Listing 3 demonstrates that translation activity:

Listing 3. TranslatedExceptionDemo1.java

// TranslatedExceptionDemo1.java
class TranslatedExceptionDemo1
{
   public static void main (String [] args)
   {
      try
      {
         Library.methodA (); // Line 9.
      }
      catch (Library.ExternalException e)
      {
         System.out.println (e.getMessage ());
         // Send stack trace to standard output device, instead of
         // default standard error device.
         e.printStackTrace (System.out);
      }
   }
}
class Library
{
   static void methodA () throws ExternalException
   {
      try
      {
         methodB ();
      }
      catch (InternalException e)
      {
         System.out.println (e.getMessage ());
         throw new ExternalException (); // Line 35.
      }
   }
   static class ExternalException extends Exception
   {
      ExternalException ()
      {
         super ("External Exception");
      }
   }
   private static void methodB () throws InternalException
   {
      throw new InternalException ();
   }
   private static class InternalException extends Exception
   {
      InternalException ()
      {
         super ("Internal Exception");
      }
   }
}

TranslatedExceptionDemo1's source code consists of top-level classes TranslatedExceptionDemo1 (to simulate a program accessing a library) and Library (to simulate a library). Library's architecture allows TranslatedExceptionDemo1 to access methodA() and the ExternalException nested top-level class. Library's architecture also reveals methodA()'s call to methodB(), which throws an InternalException object. methodA()'s catch clause catches that object, extracts the object's message, prints that message, creates an ExternalException object, and throws that object. From TranslatedExceptionDemo1's perspective, methodA() throws only ExternalException objects; TranslatedExceptionDemo1 is not aware of InternalException.

When run, TranslatedExceptionDemo1 produces the following output:

Internal Exception
External Exception
Library$ExternalException: External Exception
    at Library.methodA(TranslatedExceptionDemo1.java:35)
    at TranslatedExceptionDemo1.main(TranslatedExceptionDemo1.java:9)

The output's stack-trace portion results from the call to printStackTrace(PrintStream ps). The stack trace reveals the location of the code, in the Library class, that threw the ExternalException object. Furthermore, the stack trace reveals the location of the code, in TranslatedExceptionDemo1's main() method's try block, that initiated a call to Library's methodA() method -- which ultimately led to the exception. Finally, the stack trace displays method-call stack details, with the innermost method-call details at the top and the outermost method-call details at the bottom.

Instead of using a catch clause to translate an internal exception object to an external exception object and throw the external exception object, you can use a catch clause to rethrow a caught exception object. That approach allows some exception handling near an exception and additional exception handling in the catch clause of a method farther up the method-call stack; both catch clauses can access the same exception details. Because rethrowing the same exception object can violate a library specification, you should wrap the caught exception object inside a new exception object that conforms to the specification. To obtain the original caught exception object's details, a catch clause farther up the method-call stack can access the wrapped exception object from the new exception object. Wrapping an exception object inside another exception object is known as exception chaining. Examine Listing 4 for an exception chaining example:

Listing 4. TranslatedExceptionDemo2.java

// TranslatedExceptionDemo2.java
class TranslatedExceptionDemo2
{
   public static void main (String [] args)
   {
      try
      {
         Library.methodA (); // Line 9.
      }
      catch (Library.ExternalException e)
      {
         System.out.println (e.getMessage ());
         // Send stack trace to standard output device, instead of
         // default standard error device.
         e.printStackTrace (System.out);
      }
   }
}
class Library
{
   static void methodA () throws ExternalException
   {
      try
      {
         methodB (); // Line 29.
      }
      catch (InternalException e)
      {
         System.out.println (e.getMessage ());
         ExternalException ee = new ExternalException (); // Line 35.
         ee.initCause (e);
         throw ee;
      }
   }
   static class ExternalException extends Exception
   {
      ExternalException ()
      {
         super ("External Exception");
      }
   }
   private static void methodB () throws InternalException
   {
      throw new InternalException (); // Line 51.
   }
   private static class InternalException extends Exception
   {
      InternalException ()
      {
         super ("Internal Exception");
      }
   }
}

When run, TranslatedExceptionDemo2 produces the following output:

Internal Exception
External Exception
Library$ExternalException: External Exception
    at Library.methodA(TranslatedExceptionDemo2.java:35)
    at TranslatedExceptionDemo2.main(TranslatedExceptionDemo2.java:9)
Caused by: Library$InternalException: Internal Exception
    at Library.methodB(TranslatedExceptionDemo2.java:51)
    at Library.methodA(TranslatedExceptionDemo2.java:29)
    ... 1 more

The output begins with lines Internal Exception and External Exception. Those lines simply display the messages stored in the thrown InternalException and ExternalException objects. Following those lines, you see two stack traces. The stack trace beginning with Library$ExternalException refers to the method-call stack at the time the ExternalException object is thrown. Also, the stack trace beginning with Caused by refers to the method-call stack at the time the InternalException object is thrown. Notice the line that consists of ... 1 more. That line is shorthand notation. It refers to the previous stack trace's final line. If you were to remove ... 1 more and expand the output, you would observe the following:

Internal Exception
External Exception
Library$ExternalException: External Exception
    at Library.methodA(TranslatedExceptionDemo2.java:35)
    at TranslatedExceptionDemo2.main(TranslatedExceptionDemo2.java:9)
Caused by: Library$InternalException: Internal Exception
    at Library.methodB(TranslatedExceptionDemo2.java:51)
    at Library.methodA(TranslatedExceptionDemo2.java:29)
    at TranslatedExceptionDemo2.main(TranslatedExceptionDemo2.java:9)

The bottommost stack trace reveals that methodB() created and threw an InternalException object (line 51), that methodA()'s try block called methodB() (line 29), and that main()'s try block called methodA() (line 9). That stack trace also refers to itself as the cause of another thrown exception object, which the uppermost stack trace describes. The uppermost stack trace reveals that methodA() created an ExternalException object (line 35) and that main()'s try block called methodA() (line 9).

The chained exception output results from the ee.initCause (e); method call in methodA()'s catch clause. That method call is necessary because ExternalException does not provide access to an appropriate Throwable constructor. As an exercise, consider how to modify TranslatedExceptionDemo2 to produce the same output while eliminating the ee.initCause (e); method call.

1 2 3 4 5 Page 3
Page 3 of 5