The special case of RuntimeException
The first example in this chapter was
if(t == null)
throw new NullPointerException();
It can be a bit horrifying to think that you must check for null on every reference that is passed into a method (since you cant know if the caller has passed you a valid reference). Fortunately, you dontthis is part of the standard run-time checking that Java performs for you, and if any call is made to a null reference, Java will automatically throw a NullPointerException. So the above bit of code is always superfluous.
Theres a whole group of exception types that are in this category. Theyre always thrown automatically by Java and you dont need to include them in your exception specifications. Conveniently enough, theyre all grouped together by putting them under a single base class called RuntimeException, which is a perfect example of inheritance; It establishes a family of types that have some characteristics and behaviors in common. Also, you never need to write an exception specification saying that a method might throw a RuntimeException (or any type inherited from RuntimeException), because they are unchecked exceptions. Because they indicate bugs, you dont usually catch a RuntimeExceptionits dealt with automatically. If you were forced to check for RuntimeExceptions, your code could get too messy. Even though you dont typically catch RuntimeExceptions, in your own packages you might choose to throw some of the RuntimeExceptions.
What happens when you dont catch such exceptions? Since the compiler doesnt enforce exception specifications for these, its quite plausible that a RuntimeException could percolate all the way out to your main( ) method without being caught. To see what happens in this case, try the following example:
//: c09:NeverCaught.java
// Ignoring RuntimeExceptions.
// {ThrowsException}
import com.bruceeckel.simpletest.*;
public class NeverCaught {
private static Test monitor = new Test();
static void f() {
throw new RuntimeException("From f()");
}
static void g() {
f();
}
public static void main(String[] args) {
g();
monitor.expect(new String[] {
"Exception in thread \"main\" " +
"java.lang.RuntimeException: From f()",
" at NeverCaught.f(NeverCaught.java:7)",
" at NeverCaught.g(NeverCaught.java:10)",
" at NeverCaught.main(NeverCaught.java:13)"
});
}
} ///:~
You can already see that a RuntimeException (or anything inherited from it) is a special case, since the compiler doesnt require an exception specification for these types.
So the answer is: If a RuntimeException gets all the way out to main( ) without being caught, printStackTrace( ) is called for that exception as the program exits.
Keep in mind that you can only ignore exceptions of type RuntimeException (and subclasses) in your coding, since all other handling is carefully enforced by the compiler. The reasoning is that a RuntimeException represents a programming error:
- An error you cannot anticipate. For example, a null reference that is
outside of your control.
- An error that you, as a programmer, should have checked for in your code
(such as ArrayIndexOutOfBoundsException where you should have paid
attention to the size of the array). An exception that happens from point #1
often becomes an issue for point #2.
Its interesting to notice that you cannot classify Java exception handling as a single-purpose tool. Yes, it is designed to handle those pesky run-time errors that will occur because of forces outside your codes control, but its also essential for certain types of programming bugs that the compiler cannot detect.