The exception hierarchy in Java: what are the different Exception classes and when to use them?
In our introduction to exceptions, we mentioned
that Java exceptions are also objects. Since they're objects, different types of exceptions
can have different Exception subclasses. As an example,
we mentioned that FileNotFoundException is an extension of IOException.
Why would we create exceptions as subclasses of other "master" Exception classes in this way?
Firstly, creating such a hierarchy can be extremely useful when it comes to
If we catch IOException, then
by implication, we also catch FileNotFoundException. The programmer can decide
what level of granularity they need when organising their catch blocks.
In addition to any hierarchy that may be defined within a particular program or library,
Java exception classes are organised into a fundamental hierarchy at the very top level. There
is a basic exception class called Exception as you might expect. But in
fact, the base of the hierarchy starts not with Exception but with a
class called Throwable, which is then subclassed into Exception
and Error. Part of this fundamental hierarchy is illustrated in Figure 1.
Figure 1: The base of the Java exception hierarchy. Types in
red, and their subclasses, represent unchecked exceptions
which do not need to be explicitly declared and caught (see below).
Why are there different base exception classes?
Why are there different types of base exception classes set up in this way,
and which type of "exception" should you use when? The answer comes down to how your program
expects to handle (or not) the condition in question. As a general guide,
this is how you should generally use the different exception class types:
- Exception subclasses represent errors that a program can reasonably
recover from. Except for RuntimeException and its subclasses
(see below), they generally represent errors that a program will expect
to occur in the normal course of duty: for example, network connection
errors and filing system errors.
- Error subclasses represent "serious" errors that a program generally
shouldn't expect to catch and recover from. These include conditions
such as an expected class file being missing, or an OutOfMemoryError.
- RuntimeException is a further subclass of Exception.
RuntimeException and its subclasses are slightly different: they
represent exceptions that a program shouldn't generally expect to occur,
but could potentially recover from.
They represent what are likely to be programming errors rather than errors due
to invalid user input or a badly configured environment.
In reality, there are exceptions to these uses. For example,
there are cases where the organisation of the JDK libraries means that we have to
catch an exception that we essentially know will never occur. And there are some cases
where we've "got nothing to loose" by trying to catch an Error, even though
we may not actually be able to in practice.
Checked vs unchecked exceptions
We mentioned above that certain exceptions are generally indicative of a programming
error rather than errors that we'd expect to occur in the normal course of
events. (Whereas, it is reasonable for the programmer to expect that a networking or
file system error will occur from time to time.) We also mentioned that subclasses of Error
are not necessarily programming errors, but still errors that we wouldn't really expect to occur
in the normal course of events.
A feature built into the Java language is that Errors and RuntimeExceptions
(and their subclasses– marked in red in Figure 1) are what are called unchecked exceptions:
- unchecked exceptions can be thrown "at any time";
- methods don't explicitly have to declare that they can throw an unchecked exception;
- callers don't have to handle them explicitly.
On the next page, we'll look in more detail at unchecked exceptions in Java.