Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 8 of 8
'try'
'{'
statement ...
'}'
The try block begins with keyword try and concludes with a statement block. Statements within that block can throw exception objects. The following code fragment
throws a FileNotFoundException object from a try block and catches that object in the associated catch clause:
try
{
throw new FileNotFoundException ();
}
catch (FileNotFoundException exc)
{
System.out.println (exc.toString ()); // Display a string representation of the exception object.
}
Within the try block, a throw statement creates and throws a FileNotFoundException object to the JVM. In response, the JVM searches backwards through the method-call stack for a catch clause with a parameter of type FileNotFoundException. That search begins with the method that contains the throw statement's surrounding try block. Because an appropriate catch clause is part of the try block, the JVM transfers execution to that catch clause. Once the catch clause executes its last statement, execution continues with the first statement that follows that clause.
| 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.
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.
|
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.
Earlier in this article, I promised you an example of the StackTraceElement class and its methods. I now fulfill that promise by introducing a program that uses StackTraceElement methods to access stack-trace-element data from a catch clause, and produce output similar to the previous TranslatedExceptionDemo2 output:
Listing 5. TranslatedExceptionDemo3.java
// TranslatedExceptionDemo3.java
class TranslatedExceptionDemo3
{
public static void main (String [] args)
{
try
{
Library.methodA (); // Line 9.
}
catch (Library.ExternalException e)
{
System.out.println (e.getMessage ());
System.out.println ("\nUppermost stack trace");
StackTraceElement [] stearray = e.getStackTrace ();
for (int i = 0; i < stearray.length; i++)
{
System.out.println ("\nStack Trace element: " + i);
System.out.println ("File name = " +
stearray [i].getFileName ());
System.out.println ("Class name = " +
stearray [i].getClassName ());
System.out.println ("Method name = " +
stearray [i].getMethodName ());
System.out.println ("Line number = " +
stearray [i].getLineNumber ());
}
System.out.println ("\nBottommost stack trace");
stearray = e.getCause ().getStackTrace ();
for (int i = 0; i < stearray.length; i++)
{
System.out.println ("\nStack Trace element: " + i);
System.out.println ("File name = " +
stearray [i].getFileName ());
System.out.println ("Class name = " +
stearray [i].getClassName ());
System.out.println ("Method name = " +
stearray [i].getMethodName ());
System.out.println ("Line number = " +
stearray [i].getLineNumber ());
}
}
}
}
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 Uppermost stack trace Stack Trace element: 0 File name = TranslatedExceptionDemo3.java Class name = Library Method name = methodA Line number = 72 Stack Trace element: 1 File name = TranslatedExceptionDemo3.java Class name = TranslatedExceptionDemo3 Method name = main Line number = 9 Bottommost stack trace Stack Trace element: 0 File name = TranslatedExceptionDemo3.java Class name = Library Method name = methodB Line number = 88 Stack Trace element: 1 File name = TranslatedExceptionDemo3.java Class name = Library Method name = methodA Line number = 66 Stack Trace element: 2 File name = TranslatedExceptionDemo3.java Class name = TranslatedExceptionDemo3 Method name = main Line number = 9
The try-block/catch-clause exception-handling arrangement features a problem. Suppose a statement within the try block opens a file. At some point, that file must close -- especially if the try-block/catch-clause executes in a loop. (If the file doesn't close, most likely, the program will eventually stop opening files because
of limitations to the number of simultaneously open files.) To ensure each open file closes, a file-closing statement must
appear within the try block and each catch clause associated with that block, 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 file-related exceptions are thrown.
}
That duplicate file-closing code hearkens back to one of the problems with C's error code-testing approach to dealing with
exceptions: redundancy. Ideally, the file-closing code should appear in only one place. Java supports that idea by providing
a finally clause. That clause's syntax is:
'finally'
'{'
// cleanup statements
'}'
A finally clause begins with the finally keyword, and a block of cleanup statements follows. If there are no catch clauses, the finally clause immediately follows a try block; otherwise it immediately follows the final catch clause. After the last statement executes, in either the try block (if that block did not throw any exception objects) or a catch clause (if an exception object was thrown), the finally clause executes. The following code fragment shows how finally eliminates redundancy:
try
{
// Open file.
// File-manipulation statements.
}
catch (IOException e)
{
// Handle exception.
}
finally
{
// Close file, whether or not an exception was thrown.
}
In the preceding code fragment, finally's file-closing code executes after the last statement executes in either the try block or the catch clause. That behavior implies the following: if there is no catch clause and an exception object is thrown from the try block, the finally clause executes, and then the JVM searches for an appropriate catch clause to catch that object.
| Caution |
|---|
The finally keyword must appear immediately after the closing brace character of a preceding try block or catch clause. The compiler reports an error upon encountering anything else ahead of finally.
|
The finally clause also features a problem -- lost exception objects. Suppose a method contains a try block with a finally clause, and the method signature includes a throws clause identifying the class name of the checked exception object that try throws. Now suppose code within the try block throws an exception object. Before the JVM can look for an appropriate exception handler, the finally clause must execute. Suppose code within the finally clause creates an exception object and throws that object. The first exception object disappears, and the JVM searches the
method-call stack for a method with a catch clause that handles the new exception object. Listing 6 demonstrates that scenario:
Listing 6. MissingException.java
// MissingException.java
class ExceptionA extends Exception
{
ExceptionA ()
{
super ("Exception A");
}
}
class ExceptionB extends Exception
{
ExceptionB ()
{
super ("Exception B");
}
}
class MissingException
{
public static void main (String [] args)
{
try
{
methodA ();
}
catch (ExceptionA e)
{
System.out.println (e.getMessage ());
}
catch (ExceptionB e)
{
System.out.println (e.getMessage ());
}
}
static void methodA () throws ExceptionA, ExceptionB
{
try
{
methodB ();
}
finally
{
throw new ExceptionA ();
}
}
static void methodB () throws ExceptionB
{
throw new ExceptionB ();
}
}
When run, MissingException produces the following output:
Exception A
MissingException works as follows: main() invokes methodA(), which invokes methodB(). methodB() throws an exception object of type ExceptionB(). Before the JVM can locate a catch clause (in main()) that catches ExceptionB objects, the JVM must execute methodA's finally clause. However, that clause throws an ExceptionA object, and the JVM searches for an ExceptionA catch clause -- which the JVM finds in main(). The previous output proves that the ExceptionB object is missing.
| Caution |
|---|
Do not throw exception objects from finally clauses; you risk losing other exception objects.
|
Java's throw-object/catch-object exception-handling technique consists of an exceptions class hierarchy, statements/clauses,
and various rules. At the top of the exceptions class hierarchy, class Throwable describes an exception object's internal architecture: a message and a cause. Below Throwable, classes Exception and Error introduce subhierarchies that identify different kinds of program- and JVM-related exceptions, respectively. Apart from Exception's RuntimeException subclass hierarchy, all Exception classes identify checked exceptions. In contrast, RuntimeException and its subclasses (and Error and its subclasses) identify unchecked exceptions.
A method uses a throw statement to throw an exception object to the JVM. The calling method completes one of two tasks: It declares its interest
in catching that object by wrapping the method call in a try block and specifying an associated catch clause. Or, it incorporates a throws clause as part of its signature to indicate that the calling method wants the throw statement's calling method to catch the exception object. If no method catches the exception object, the JVM passes that
object to a default exception handler, which calls various Throwable methods to display exception object details and a stack trace. Whether or not a method chooses to catch an exception object,
that method can incorporate a finally clause as part of a try block or as part of a try block and one or more catch clauses. Following the execution of a try block or a catch clause, the JVM passes execution to the finally clause. Code within finally performs various cleanup tasks.
To be successful with Java's exception-handling architecture, you must understand and follow the various rules I've outlined in this article.
Now turn to the study guide and do some homework. That homework will help solidify what you have just read.
I encourage you to email me with any questions you might have involving either this or any previous article's material. (Please keep such questions relevant to material discussed in this column's articles.) Your questions and my answers will appear in the relevant study guides.
In next month's article, you'll discover Java's thread support.