THE Java™ Programming Language, Fourth Edition

(Jeff_L) #1

Note that an exception can only have it's cause set onceeither via a constructor, or by a single call to
initCauseany attempt to set it again will cause an IllegalStateException to be thrown.


12.6. Stack Traces


When an exception is created, a stack trace of the call is saved in the exception object. This is done by the
THRowable constructor invoking its own fillInStacktrace method. You can print this stack trace by
using printStackTrace, and you can replace it with the current stack information by invoking
fillInStackTrace again. If an exception has a cause, then typically printStackTrace will print the
current exceptions stack trace, followed by the stack trace of its causehowever, the details of stack trace
printing depend on the virtual-machine implementation.


A stack trace is represented by an array of StackTraceElement objects that you can get from
getStackTrace. You can use this array to examine the stack or to create your own display of the
information. Each StackTraceElement object represents one method invocation on the call stack. You
can query these with the methods getFileName, getClassName, getMethodName,
getLineNumber, and isNativeMethod.


You can also set the stack trace with setStackTrace, but you would have to be writing a very unusual
program for this to be a good idea. The real information contained in a stack trace is quite valuable. Do not
discard it without compelling need. Proper reasons for changing a stack trace are vital, but very rare.


12.7. When to Use Exceptions


We used the phrase "unexpected error condition" at the beginning of this chapter when describing when to
throw exceptions. Exceptions are not meant for simple, expected situations. For example, reaching the end of
a stream of input is expected, so the method that returns the next input from the stream has "hitting the end" as
part of its expected behavior. A return flag that signals the end of input is reasonable because it is easy for
callers to check the return value, and such a convention is also easier to understand. Consider the following
typical loop that uses a return flag:


while ((token = stream.next()) != Stream.END)
process(token);
stream.close();


Compare that to this loop, which relies on an exception to signal the end of input:


try {
for (;;) {
process(stream.next());
}
} catch (StreamEndException e) {
stream.close();
}


In the first case, the flow of control is direct and clear. The code loops until it reaches the end of the stream,
and then it closes the stream. In the second case, the code seems to loop forever. Unless you know that end of
input is signaled with a StreamEndException, you don't know the loop's natural range. Even when you

Free download pdf