Ch. 9. Exception Handling |
Session 9
|
A Baker's Dozen is 13, one extra, just to make sure that customers cannot claim they were shortchanged - http://en.wikipedia.org/wiki/Dozen#Baking
However, BakersDozen.java shows that if you go to most supermarkets expecting to find 13 eggs in the carton, you will experience an instance of a subclass of java.lang.Exception, namely http://docs.oracle.com/javase/7/docs/api/java/lang/ArrayIndexOutOfBoundsException.html
The JVM outputs a "stack trace" that provides the following:
Note that the JVM never executes line 11 because line 8 forces an immediate exit from execution. Any unhandled exception causes the application to crash.
An Exception is an abnormal event that is occurs during the execution of a program. If the exception is not handled, the JVM halts execution, prints to the console the name of the exception and its stack trace, then causes the program to abort immediately. In other words, the program crashes.
Let's throw and catch a exceptions using the exception handling mechanism that Java provides so that instead of a hot potato we are likely to drop on the floor, we have robust code that does not crash and sells like hotcakes. The character on the right uses the spatula mechanism to throw and catch. Our robust code will use a try block and a catch block.
This week, we learn how to "handle" exceptions, so that:
Note that lines 11 and 12 did not execute. Now let's add exception handling by:
Exception handling allows execution to continue, so lines 18-19 do execute.
SUMMARY: An object of type Exception inherits from java.lang.Exception. An exception object is an instance of the Exception class or one of its subtypes.
On line 14, "e" is similar to a formal parameter and represents the exception object.
The compiler is conservative when an assignment occurs within a try block.
In ThrowUpTheCallStackTwiceDemo.java, the exception that occurs in processArray() is not caught by that method, so it is thrown to the caller of the method, callProcessArray().
Note:
Just as you might want to reverse a film sequence to see how an accident took place, the stack trace unloads the stack, which approximates a reversal of the flow of calls during runtime:
Suppose you input into the JVM a data structure that is so large that the JVM
cannot function. You will get the OutOfMemoryError
http://docs.oracle.com/javase/7/docs/api/java/lang/OutOfMemoryError.html.
In such a situation, there is nothing your application can do. It's too late. It
is like blowing up a balloon until the balloon itself blows up, and it is too
late to retract the air.
SUMMARY: The Java Virtual Machine (JVM) provides a default exception handling mechanism, which is to crash with a "stack trace". At least the crash is accompanied with an a string representation that includes the name of the exception and the line of code associated with the exception. Often, Java provides default behavior without us having to write code for it. In this case, the result is the same as if we had written a try block and a catch block, with the catch block printing the stack trace. The printStackTrace() method is behavior that every Exception object inherits from its superclass, Throwable.
What happens if the class with main is in a file whose name does not match the class name? In this case the file name is TestClassNameMismatch.java and the class with main is named Test.
The current implementation is a brief message of type Error, which unlike Exception, is not something your code can recover from during runtime:
Earlier implementations exposed the details of this problem as an Exception, and provided the internal stack trace.
Note that the default stack trace information is printed to System.err, which by default is the console, the same as System.out - http://download.oracle.com/javase/6/docs/api/java/lang/System.html#err
What happens if we try to do things with an object that does not exist? -
http://download.oracle.com/javase/6/docs/api/java/lang/NullPointerException.html
In this case, the crunch method is defined with
a formal parameter that expects a runtime argument to be an object of a certain
type, that is, an array of type int, but the
runtime call fails to provide the expected object because the runtime argument
is null. The println method on line 11, which is
part of the mash method, cannot find an
int value in the first position of the
array named b, so the JVM looks at the caller of
mash method, which is line 7 in
crunch method. However, the
crunch method has no error handling, so the JVM traces back
its caller, which is line 16, where
crunch was called without a valid runtime argument. In this manner, tracing the
stack calls unloads or unearths the root cause: at least this is the hope for
debugging purposes.
Given the formal parameter of the method on line 5, the compiler will NOT allow us to pass in a runtime argument that is an int, a boolean, or a String. Nor can we compile without an argument, that is, by invoking a parameterless crunch() overload that is not defined. However, if we pass in the keyword null, the compiler respects that null is a possible placeholder or "token" for a valid argument and allows compilation to proceed. Only at runtime do we see a problem, namely, the NullPointerException. We find the same situation when our code divides by zero at runtime, which throws the ArithmeticException, or we point to an array element beyond the limit of the array, which throws the IndexOutOfBoundsException or the NegativeArraySizeException. The complete list is available at http://docs.oracle.com/javase/7/docs/api/java/lang/package-frame.html.
Let's insert the try and catch blocks so that the exception does not abort execution.
http://docs.oracle.com/javase/tutorial/essential/exceptions/runtime.html
Note: Some engineers at what was Sun Microsystems wanted the compiler to be so robust that it would catch most issues at compile time, and thus prevent runtime exceptions. This is what lead to the effort for the compiler to enforce "checked" exceptions, which we will discuss when we look at working with the java.io package. Java architects wanted coders to plan for carefully for the high likelihood of problems when performing tasks involving the dependencies related to input and output. At this point, we can note that exceptions of type RuntimeException and its subclasses are, by definition, NOT checked at compile time. On the other hand, exceptions of type IOException and its subclasses are associated with stricter "checking" by the compiler.
RuntimeException and its subclasses are unchecked:
The architects of Java considered some exceptions to be so foreseeable that Java automatically handles them. A better name might be: "JVM-handled exceptions". Because the JVM handles these exceptions, the compiler does not check to see whether you handle them in YOUR code:
We have encountered several runtime exceptions:
which are subclasses of RuntimeException. Such exceptions are not checked by the compiler.
A more complete list: http://docs.oracle.com/javase/1.4.2/docs/api/java/lang/RuntimeException.html
The following example is for an unchecked exception: divide by zero.
If your code is. say, trying to open a stream of data (which might from a remote computer, or depend on code from another programmer), it seems the architects wanted the compiler to force your code to be robust. Therefore, the compiler verifies that IOException types are considered before allowing compilation to produce runnable bytecode.
The compiler checks to see if you handle with try / catch, or use the keyword throws, for the following kinds of exceptions, none of which are a subclass of RuntimeException:
Many programmers find the distinction between checked and unchecked exceptions to be confusing. The architectures of a modern Java-derived language, Scala (which has become the central language for Twitter), considered checked exceptions to be so confusingly implemented that they eliminated checked exceptions altogether.
Every exception is an object of a specific type. Java is rigorous in using an object-oriented hierarchy for exceptions.
SUMMARY To review, the crash is the result of using what we might call the default exception handler, which is the JVM. The JVM handles an exception by
In the following case, at runtime we reference an index in an array of chars that is outside the length of the array. Java arrays are zero-based so to remain within the bounds of the array, the maximum index is the array length minus 1.
Remember that a String is internally implemented by the JVM as an array of type char. If we try to get the character at an index with negative value or the value of the length of the char array (not minus 1), a StringIndexOutOfBoundException is thrown:
Note that the stack trace mentions line 686. This might be a different
number, such as 695, in a different release of Java:
In any case, the number is internal to the JVM, and probably not something you want end-users to see. Instead, your application might save the stack trace to a log file, and print a more user-friendly message for the end-user.
If we handle errors using Java's built-in try/catch mechanism, exceptions can occur without necessarily blocking program execution. The program might even be able to recover from the exception.
SUMMARY: The try block contains whatever we expect might cause a problem. If an object of type Exception is thrown, the try block immediately ceases execution, and execution jumps to the matching catch block, if any. A catch block matches the exception object through its formal parameter. The catch block contains the behavior to execute if such a problem occurs. If there is no problem, the catch block does NOT execute. For example, the program might prompt the end-user to enter valid data.
You can create your own custom exception type as a subclass of Exception, or as a subclass of any existing subclass of Exception. Line 15 below uses the keyword throw to create and throw an exception object. This custom exception, like all exceptions (except Exception) is a subclass of Exception.
Why would a programmer deliberately throw an exception? Isn't that like shooting oneself in the foot? Maybe, but it is also like recording the origin of a condition: a way to know that a particular state occurred and the circumstances around that occurrence.
In the following program, I want to know when a field gets a negative value (Line 13).
The following example also has two catch blocks. Each catch block is like an overload signature of an overloaded constructor or method. Each catch block must be unique in the type of runtime parameter, that is the Exception subclass, it accepts.
Exception handling is built into the architecture of the Java language. Insofar as every Exception class is a subclass of java.lang.Exception, and every catch block is working with an object of a particular Exception class, exception handling provides a good example of how the object-oriented paradigm is fundamental to the language.
Sun's tutorial for exceptions: http://docs.oracle.com/javase/tutorial/essential/exceptions/index.html
http://docs.oracle.com/javase/7/docs/api/java/lang/Throwable.html java.lang.Throwable is the superclass for both Error and Exception |
|
In Java terminology, an Error
is something beyond the programmer's control and something the programmer
should not try to handle. http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Error.html |
An Exception is a
potential problem that your application should consider. http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Exception.html |
The default java.lang package defines both the Exception class and the Error class. When the exception condition occurs, the virtual machine can construct an object of type Exception. This object inherits from its superclass, Throwable, methods that are useful for diagnosis of the problem.
Both
Error and
Exception inherit from
Throwable (that is,
they can occur at runtime), and Throwable
inherits from
Object.
The class
Throwable provides methods that are common to
errors and exceptions, such as
printStackTrace(), getCause(),
and toString(). Therefore, the Throwable - Error
- Exception hierarchy is a good example of object-oriented design, which allows
maximum code reuse.
As a programmer, you prepare for the possibility of an exception by writing code to handle the exception.
The minimal mechanism to handle Exception objects involves two keywords: try and catch.
Note that e.toString() is possible because
every Exception object inherits from the class
Throwable.
We can print the exception by passing it as a runtime argument to the
println method signature that accepts an
object
http://docs.oracle.com/javase/7/docs/api/java/io/PrintStream.html#println%28java.lang.Object%29
In the following, each catch clause is defined to handle a different type of runtime Exception object.
Another version:
The output above occurred because no command-line arguments were given at runtime, which causes a divide-by-zero exception on line 7.
If instead at runtime there are three (3) command-line arguments, the output shows a different exception because Line 11 tries to access the 42nd element in an array that only has 3 elements:
The following example has two try blocks, each of which catches a different exception.
Note the addition of the finally block, which executes whether or not an exception is thrown.
The finally block is for an action that you want to occur when the try block is no longer executing, whether or not an exception is thrown in the try block. Typical use cases are:
You can group multiple exception handlers into a single catch block by using the logical OR operator, also called the "pipe" (|). The syntax that FinallyAlwaysExecutesWithASingleCatch.java at Line 13 illustrates is
catch(Exception1 | Exception2 | Exception3 exceptionIdentifier)
The keywords throw and throws are different!
The compiler checks that exception handling exists for certain types of exceptions, namely those unrelated to RuntimeException.The keyword throws in a method declaration tells the compiler that exception handing does not exist within method. Instead, the JVM will have to redirect the exception to the caller of the method to see if a catch block is available.
Note: This "feature" of Java is considered a flaw by the architects of Scala (see http://en.wikipedia.org/wiki/Scala_%28programming_language%29 ).
The following program does NOT compile because it has a method called throwCustomException that:
The problem at Line 18 occurs because Line 11 does not declare that this method can throw an Exception or a subclass of Exception. That means this program is not exempt from compiler checking the way that RuntimeException or its subclasses are except from compiler checking.
One way to avoid the compiler's refusal is to make the custom exception a subclass of RuntimeException (Line 2).
Another way to keep the compiler happy with a non-runtime exception thrown by a method that does not declare throws, is to have the method that throws the exception also catch the exception (see Line 21 and Line 26).
To conclude, if you write a method that throws an exception that is not a runtime exception, and your method does not catch the exception, then you must declare that your method throws this exception. If another programmer wants to use your method, that programmer can see that your method throws this non-runtime exception. This allows the user of your method to write a catch block external to your method.
The compiler checks to see if you handle or use the keyword throws for the following kinds of exceptions, none of which are a subclass of RuntimeException:
These exceptions are "checked" exceptions because the architects of Java considered that they could write a compiler that could check for at compile time. The unchecked exceptions include problems, such as bad user input at runtime causing a divide by zero, that the compiler cannot check because it has no access to runtime occurrences.
This example shows the syntax for declaring that a method "throws" a checked exception. In this case, the throwOne method throws the exception up to the caller, which in this case is the main method.
If your application works with system input and output, the java.io package, it must declare that it throws IOException - http://www.write-technical.com/126581/session10/io/session10.htm#Byte%20array
How would an IOException be caught in the following?:
If you use this method, which can throw the IOException , you can write your own exception handling routine according to your needs (or those of your users).
Some integrated development environments (IDEs) are able to visually represent the call stack, which can help for debugging.
In the following code, the unhandled exception is propagated up the call
stack to the caller, which in this case is the main method.
An unhandled exception propagates up the call stack until it is
handled or reaches its caller.
You might want to follow the call chain associated with an exception to help with debugging code, so the superclass of Exception, Throwable, provides a method for getting the stack trace - http://download.oracle.com/javase/6/docs/api/java/lang/Throwable.html#printStackTrace%28%29
Before exception is generated.
java.lang.ArrayIndexOutOfBoundsException: 7
at ExcTest.genException(StackTraceBubble.java:8)
at StackTraceBubble.myMethod(StackTraceBubble.java:18)
at StackTraceBubble.main(StackTraceBubble.java:29)
Before exception is generated.
Standard message is:
java.lang.ArrayIndexOutOfBoundsException: 7
Stack trace:
java.lang.ArrayIndexOutOfBoundsException: 7
at ExcTest.genException(StackTraceBubble.java:8)
at StackTraceBubble.main(StackTraceBubble.java:30)
After catch statement.
You can have a superclass handle a group of exceptions that get thrown up to the
superclass from a group of subclasses.
The location of exception handling is flexible in Java.
Here is another example that "throws" an exception (a custom exception), and it has a "finally" block (described in the following section): CallStackExampleWithCustomException3.java
The finally block executes whether or not an exception is thrown in the try block.
Use case for finally: you might want a method to do some cleanup, such as close a database connection or a file buffer, whether or not an exception occurs.
When does finally execute? Before or after the return statement of a method?
If your try block has a finally block, is it necessary or optional to have a catch block?
First, consider the classic form of a try, catch, finally sequence:
Now, the try block with the new statement syntax on line 16:
The 1.7 form of a try block know as a try-with-resources statement has two benefits:
This feature relies on an interface with a close() method - http://docs.oracle.com/javase/8/docs/api/java/lang/AutoCloseable.html. The JVM automatically invokes the close() method. You, as "implementer" are allowed to override the close()method such that it throws an exception more specific that its default exception, which is java.lang.Exception.
Why define your own exceptions? Because it allows your program to be aware of a certain situation that the standard Java APIs cannot know about. Your custom exception might provide a customize type of security or a customize tool to diagnose a bug.
String[] buzzwords = {"replace",
"excellent", "resonate", "create", "renounce"};
try
{
for(String x : buzzwords)
{
System.out.println(x);
if (x == "resonate")
{
throw new
MyBuzzwordException();
}
}
}
Your custom exception might notice that the user is trying to set the same string for both username and password, then take appropriate action, such as notifying the user that the password must be set to a different string.
Because Exception is an exposed class, you can subclass it (customize it) for your needs. Java provides specific Exceptions for the core APIs, but only domain programmers know which domain objects and Exceptions they need.
Use the keyword throw to force the JVM to throw the specified exception.
Another example, this time with multiple catch blocks.
SUMMARY: Exceptions are a subclass of
Throwable:
http://java.sun.com/j2se/1.4.1/docs/api/java/lang/Throwable.html,
which is a class and not an interface.This allows for code re-use and default behavior. If
Throwablewere an interface, Exceptions would not inherit default
behavior, such as GetStackTrace
http://java.sun.com/j2se/1.4.1/docs/api/java/lang/Throwable.html#getStackTrace()
and toString
http://java.sun.com/j2se/1.4.1/docs/api/java/lang/Throwable.html#toString()
In Java, objects of type Error are beyond the programmer's control, and Error objects also inherit the default behavior of Throwable.
Some Exceptions are built into Java's API specification. For example,
the
ArithmeticException occurs if a program tries to
divide a number by zero.
http://java.sun.com/j2se/1.4.1/docs/api/java/lang/ArithmeticException.html
You can have a catch statement for each type of exception.
To ensure that a specialized exception is handled, make its catch block precede the catch block of its superclass. Otherwise, the more general superclass will handle the subclass exceptions, and you lose the ability to ever get to the specialized exception.
The compiler complains:
ExcDemo5.java:20: exception
java.lang.ArrayIndexOutOfBoundsException has already been caught
catch (ArrayIndexOutOfBoundsException exc)
You can have inner try blocks for specialized errors, and reserve the outmost try block for the error handling to use if all the previous attempts fail.
You have the flexibility to handle various types of exceptions at various levels, if you want. MultipleCatchStatementsAtDifferentLayers.java catches one type of exception within main, and another type of exception within a method in a different class.
MultipleCatchStatementsAtDifferentLayersWithPipe.java shows on Line 36 that the pipe (|) operator allows you to handle multiple Exception types within a single code block. In other words, a catch block using a formal parameter list with pipe (|) can catch more than one type of exception. The benefit of the multi-catch feature is code reuse. No need to repeat identical code to handle similar exceptions.
Use the keyword throw to force the JVM to throw the specified exception. MultipleCatchStatementsAndRethrow.java shows that you can rethrowing an exception, which is then caught by the next catch statement.
The output is:
4 / 2 is 2 caught in doSomething java.lang.ArithmeticException: / by zero at ExceptionGenerator.generateException(MultipleCatchStatementsAndRethro w.java:10) at MethodCaller.doSomething(MultipleCatchStatementsAndRethrow.java:34) at MultipleCatchStatementsAndRethrow.main(MultipleCatchStatementsAndReth row.java:56) 128 / 8 is 16 caught in main java.lang.ArrayIndexOutOfBoundsException: 6 at ExceptionGenerator.generateException2(MultipleCatchStatementsAndRethr ow.java:22) at MethodCaller.doSomething(MultipleCatchStatementsAndRethrow.java:42) at MultipleCatchStatementsAndRethrow.main(MultipleCatchStatementsAndReth row.java:56)
To create your custom exception class, you extend Java's Exception class. As always, you need to create an exception object, and your catch block must provide a reference to the exception object.
Another example:
The following program reads from a file by using the read method of the Java io package. The first line of the program is the import statement that gives this application access to all the members of the io package. Which two exceptions does the following program handle?
If I give a file name as a command-line argument, the program prints to the screen the contents of the file:
An exception is a non-normal condition that your code handles in a catch block. A finally block is for clean-up code that you want to happen at the end of method execution whether or not an Exception is thrown. Generally speaking, an exception can be thrown during execution of a try block. The finally block executes after execution leaves the try block, whether the cause of executing leaving the try block is no Exception was thrown and all the try block code has completed it execution, or an Exception was thrown and execution immediately left the try block.
Checked exceptions are certain Exceptions that that the compiler expects you, as programmer, to catch. These include input/output exceptions (IO Exception), NoSuchMethodException, ClassNotFoundException, and others listed in Table 9-3 (p. 355) of the textbook. If the method you defined MIGHT throw one of the checked exceptions AND the method does not have a catch block for that exception, then the method must declare that is capable of throwing the exception:
void myMethod() throws NoSuchMethodException { ... }
This tells the Java Virtual Machine that if NoSuchMethodException is thrown during the execution of myMethod(), then the JVM should look for a catch block in the method that calls myMethod(), which might be main().
On the other hand, unchecked Exceptions are something that the compiler does not care about. For example, it is your choice whether to have a catch block for ArithmeticException [for divide by zero], ArrayIndexOutOfBoundsException [reference to an element beyond the length of the array], NegativeArraySizeException [setting the length to less than zero]. The compiler does not force you to handle these exceptions. If you do not write a catch block for the unchecked Exceptions, the JVM will automatically return the Exception message as the application fails (crashes). Other unchecked exceptions are listed in Table 9-2 (p.354).
Output is:
size = 3
length is: 3
size = 2
length is: 2
size = 1
length is: 1
size = 0
length is: 0
size = -1
ThomasException has been thrown.
ThomasException is ThomasException
Stack trace next
ThomasException
at DemoThomasExceptionSpecial.main(DemoThomasExceptionSpecial.java:25)
size = -2
Negative array java.lang.NegativeArraySizeException
Stack trace next
java.lang.NegativeArraySizeException
at DemoThomasExceptionSpecial.main(DemoThomasExceptionSpecial.java:27)
Let's look at how to catch an input/output exception when we want to read from a file.
What happens if the specified file cannot be found?
All exceptions inherit from Throwable, so let's review Throwable
http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Throwable.html
The Throwable
class is the superclass of all errors and
exceptions in the Java language. Only objects that are instances of this class
(or one of its subclasses) are thrown by the Java Virtual Machine or can be
thrown by the Java throw
statement. Similarly, only this class or
one of its subclasses can be the argument type in a catch
clause.
Instances of two subclasses,
Error
and
Exception
, are conventionally used to indicate that exceptional
situations have occurred. Typically, these instances are freshly created in the
context of the exceptional situation so as to include relevant information (such
as stack trace data).
===============
Throwable provides two methods I want to point out:
public String getMessage() // returns the detail message string of this Throwable instance (which may be null).
public String toString()
Throwable
object was created with a non-null detail message string, then the result is
the concatenation of three strings:
getMessage()
method for this object If this
Throwable
object was created with a
null
detail message string, then the name of the actual class of this object is
returned.
===============
Let's look at Error
http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Error.html
An Error
is a subclass of Throwable
that indicates
serious problems that a reasonable application should not try to catch. Most
such errors are abnormal conditions. The ThreadDeath
error, though
a "normal" condition, is also a subclass of Error
because most
applications should not try to catch it.
===============
Let's look at Exception
http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Exception.html
The class Exception
and its subclasses are a form of
Throwable
that indicates conditions that a reasonable application might
want to catch.
===============
System.out.println(" The exception message is " +
e.getMessage() + " and the exception string is " + e.toString());
===============
try
{
if (coverage2.equals("liability"))
{
liabilityFlag = true;
liabilityPremium = premium2;
}
else if (coverage2.equals("physicalDamage"))
{
physicalDamageFlag = true;
physicalDamagePremium = premium2;
}
else
throw new CoverageException (coverage2);
}
catch (CoverageException e)
{
System.out.println("EXCEPTION is " + e.toString());
}
Just for fun ... let's look at the APIs of an open source project http://docs.liferay.com/portal/6.2/javadocs-all/