You may find it useful to open another copy of this lesson in a separate
browser window. That will make it easier for you to scroll back and forth
among the different listings while you are reading about them.
For further reading, see my extensive collection of online Java tutorials at
Gamelan.com. A consolidated
index is available at
www.DickBaldwin.com.
Preview
This lesson explains Exception Handling in Java. The discussion
includes the following topics:
What is an exception?
How do you throw and catch exceptions?
What do you do with an exception once you have caught it?
How do you make use of the exception class hierarchy provided by the Java
development environment?
This lesson will cover many of the details having to do with exception
handling in Java. By the end of the lesson, you should know that the use
of exception handling is not optional in Java, and you should have a pretty good
idea how to use exception handling in a beneficial way.
Discussion and Sample Code
Introduction
Stated simply, the exception-handling capability of Java makes it possible
for you to:
Monitor for exceptional conditions within your program
Transfer control to special exception-handling code (which you design)
if an exceptional condition occurs
The basic concept
This is accomplished using the keywords: try, catch, throw,
throws, and finally. The basic concept is as follows:
You try to execute the statements contained within a block of code.
(A block of code is a group of one or more statements surrounded by
braces.)
If you detect an exceptional condition within that block, you throw
an exception object of a specific type.
You catch and process the exception object using code that you have
designed.
You optionally execute a block of code, designated by finally,
which needs to be executed whether or not an exception occurs. (Code
in the finally block is normally used to perform some type of cleanup.)
Exceptions in code written by others
There are also situations where you don't write the code to throw the
exception object, but an exceptional condition that occurs in code written by
someone else transfers control to exception-handling code that you write.
For example, the read method of the InputStream class throws an
exception of type IOException if an exception occurs while the read
method is executing. In this case, you are responsible only for the code
in the catch block and optionally for the code in the finally
block.
(This is the reason that you must surround the invocation of
System.in.read() with a try block followed by a catch block,
or optionally declare that your method throws an exception of type
IOException.)
Exception hierarchy, an overview
When an exceptional condition causes an exception to be thrown, that
exception is represented by an object instantiated from the class named
Throwable or one of its subclasses.
Here is part of what Sun has to say about the Throwable class:
"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."
Sun goes on to say:
"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)."
The Error and Exception classes
The virtual machine and many different methods in many different classes
throw exceptions and errors. I will have quite a lot more to
say about the classes named Error and Exception later in this
lesson.
Defining your own exception types
You may have concluded from the Sun quotation given above that you can define
and throw exception objects of your own design, and if you did, that is a
correct conclusion. (Your new class must extend Throwable or one of
its subclasses.)
The difference between Error and Exception
As mentioned above, the Throwable class has two subclasses:
Error
Exception
What is an error?
What is the difference between an Error and an Exception?
Paraphrasing David Flanagan and his excellent series of books entitled Java
in a Nutshell, an Error indicates that a non-recoverable error has
occurred that should not be caught. Errors usually cause the Java virtual
machine to display a message and exit.
Sun says the same thing in a slightly different way:
"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."
For example, one of the subclasses of Error is named
VirtualMachineError. This error is "Thrown to indicate that the
Java Virtual Machine is broken or has run out of resources necessary for it to
continue operating. "
What is an exception?
Paraphrasing Flanagan again, an Exception indicates an abnormal
condition that must be properly handled to prevent program termination.
Sun explains it this way:
"The class Exception and its subclasses are a form of
Throwable that indicates conditions that a reasonable application might
want to catch."
As of JDK 1.4.0, there are more than fifty known subclasses of the
Exception class. Many of these subclasses themselves have numerous
subclasses, so there is quite a lot of material that you need to become familiar
with.
The RuntimeException class
One subclass of Exception is the class named RuntimeException
As of JDK 1.4.0, this class has about 30 subclasses, many which are further
subclassed. The class named RuntimeException is a very important
class.
Unchecked exceptions
The RuntimeException class, and its subclasses, are important not so
much for what they do, but for what they don't do. I will refer to
exceptions instantiated from RuntimeException and its subclasses as
unchecked exceptions.
Basically, an unchecked exception is a type of exception that you can
optionally handle, or ignore. If you elect to ignore the possibility of an
unchecked exception, and one occurs, your program will terminate as a result.
If you elect to handle an unchecked exception and one occurs, the result will
depend on the code that you have written to handle the exception.
Checked exceptions
All exceptions instantiated from the Exception class, or from
subclasses of Exception other than RuntimeException and its
subclasses must either be:
Handled with a try block followed by a catch block, or
Declared in a throws clause of any method that can throw them
In other words, checked exceptions cannot be ignored when you write
the code in your methods. According to Flanagan, the exception classes in
this category represent routine abnormal conditions that should be anticipated
and caught to prevent program termination.
Checked by the compiler
Your code must anticipate and either handle or declare checked exceptions. Otherwise,
your program won't compile. (These are exception types that are checked
by the compiler.)
Throwable constructors and methods
As mentioned above, all errors and exceptions are subclasses of the
Throwable class. As of JDK 1.4.0, the Throwable class provides
four constructors and about a dozen methods. The four constructors are
shown in Figure 1.
Throwable()
Throwable(String message)
Throwable(String message,
Throwable cause)
Throwable(Throwable cause)
Figure 1
The first two constructors have been in Java for a very long time.
Basically, these two constructors allow you to construct an exception object
with, or without a String message encapsulated in the object.
New to JDK 1.4
The last two constructors are new in JDK 1.4.0. These two constructors
are provided to support the cause facility. The cause facility is
new in release 1.4. It is also known as the chained exception facility.
(I won't cover this facility in this lesson. Rather, I plan to cover it
in a series of lessons that I am developing having to do with the myriad of new
features in JDK 1.4.)
Methods of the Throwable class
Figure 2 shows some of the methods of the Throwable class. (I
omitted some of the methods introduced in JDK 1.4 for the reasons given above.)
The first four methods in Figure 2 deal with the StackTrace. In
case you are unfamiliar with the term StackTrace, this is a list of the
methods executed in sequence that led to the exception. (This is what
you typically see on the screen when your program aborts with a runtime error
that hasn't been handled.)
Messages
The two methods dealing with messages provide access to a String
message that may be encapsulated in the exception object. The
getMessage class simply returns the message that was encapsulated when the
object was instantiated. (If no message was encapsulated, this method
returns null.)
The getLocalizedMessage method is a little more complicated to use.
According to Sun, "Subclasses may override this method in order to produce a
locale-specific message."
The toString method
The toString method is inherited from the Object class and
overridden in the exception subclass to "return a short description of the
Throwable".
Inherited methods
All exception objects inherit the methods of the Throwable class,
which are listed in Figure 2. Thus, any of these methods may be invoked by
the code in the catch block in its attempt to successfully handle the
exception.
For example, exceptions may have a message encapsulated in the exception
object, which can be accessed using the getMessage method. You can use
this to display a message describing the error or exception.
You can also use other methods of the Throwable class to:
Display a stack trace showing where the exception or error occurred
Produce a String representation of the exception object
So, what is an exception?
According to the online book entitled
The Java Tutorial by
Campione and Walrath:
"The term exception is shorthand for the phrase "exceptional event". It
can be defined as follows: Definition: An exception is an event that occurs
during the execution of a program that disrupts the normal flow of
instructions."
When an exceptional condition occurs within a method, the method may
instantiate an exception object and hand it off to the runtime system to deal
with it. This is accomplished using the throw keyword.
(This is called throwing an exception.)
To be useful, the exception object should probably contain information about
the exception, including its type and the state of the program when the
exception occurred.
Handling the exception
At that point, the runtime system becomes responsible for finding a block of
code designed to handle the exception.
The runtime system begins its search with the method in which the exception
occurred and searches backwards through the call stack until it finds a method
that contains an appropriate exception handler (catch block).
An exception handler is appropriate if the type of the exception
thrown is the same as the type of exception handled by the handler, or is a
subclass of the type of exception handled by the handler.
Thus, the requirement to handle an exception that has been thrown progresses
up through the call stack until an appropriate handler is found to handle the
exception. If no appropriate handler is found, the runtime system and the
program terminate.
(If you have ever had a program terminate with a NullPointerException,
then you know how program termination works).
According to the jargon, the exception handler that is chosen is said to
catch the exception.
Advantages of using exception handling
According to Campione and Walrath, exception handling provides the following
advantages over "traditional" error management techniques:
Separating Error Handling Code from "Regular" Code
Propagating Errors Up the Call Stack
Grouping Error Types and Error Differentiation
Separating error handling code from regular code
I don't plan to discuss these advantages in detail. Rather, I will simply
refer you to The Java Tutorial and other good books where you can read
their discussions. However, I will comment briefly.
Campione and Walrath provide a good illustration where they show how a simple
program having about six lines of code get "bloated" into about 29 lines of very
confusing code through the use of traditional error management techniques. Not
only does the program suffer bloat, the logical flow of the original program
gets lost in the clutter of the modified program.
They then show how to accomplish the same error management using exception
handling. Although the version with exception handling contains about seventeen
lines of code, it is orderly and easy to understand. The additional lines of
code do not cause the original logic of the program to get lost.
You must still do the hard work
However, the use of exception handling does not spare you from the hard work
of detecting, reporting, and handling errors. What it does is provide a
means to separate the details of what to do when something out-of-the-ordinary
happens from the normal logical flow of the program code.
Propagating exceptions up the call stack
Sometimes it is desirable to propagate exception handling up the call stack
and let the corrective action be taken at a higher level.
For example, you might provide a class with methods that implement a stack.
One of the methods of your class might be to pop an element off the
stack.
What should your program do if a using program attempts to pop an element off
an empty stack? That decision might best be left to the user of your stack
class, and you might simply propagate the notification up to the calling method
and let that method take the corrective action.
Grouping exception types
When an exception is thrown, an object of one of the exception classes is
passed as a parameter. Objects are instances of classes, and classes fall
into an inheritance hierarchy in Java. Therefore, a natural hierarchy can
be created, which causes exceptions to be grouped in logical ways.
For example, going back to the stack example, you might create an exception
class that applies to all exceptional conditions associated with an object of
your stack class. Then you might extend that class into other classes that
pertain to specific exceptional conditions, such as push exceptions,
pop exceptions, and initialization exceptions.
When your code throws an exception object of a specific type, that object can
be caught by an exception handler designed either to:
Catch on the basis of a group of exceptions, or
Catch on the basis of a subgroup of that group, or
Catch on the basis of one of the specialized exceptions.
In other words, an exception handler can catch exceptions of the class
specified by the type of its parameter, or can catch exceptions of any subclass
of the class specified by the type of its parameter.
More detailed information on exception handling
As explained earlier, except for Throwable objects of type Error
and for Throwable.Exception objects of type RuntimeException, Java
programs must either handle or declare all Exception
objects that are thrown. Otherwise, the compiler will refuse to compile
the program.
In other words, all exceptions other than those specified above are
checked by the compiler, and the compiler will refuse to compile the program
if the exceptions aren't handled or declared. As a result, exceptions other than
those specified above are often referred to as checked exceptions.
Catching an exception
Just to make certain that we are using the same terminology, a method
catches an exception by providing an exception handler whose parameter type
is appropriate for that type of exception object. (I will more or less
use the terms catch block and exception handler interchangeably.)
The type of the parameter in the catch block must be the class from
which the exception was instantiated, or a superclass of that class that resides
somewhere between that class and the Throwable class in the inheritance
hierarchy.
Declaring an exception
If the code in a method can throw a checked exception, and the method does
not provide an exception handler for the type of exception object thrown, the
method must declare that it can throw that exception. The throws
keyword is used in the method declaration to declare that it throws an
exception of a particular type.
Any checked exception that can be thrown by a method is part of the method's
programming interface (see the read method of the InputStream class,
which throws IOException, for example). Users of a method must
know about the exceptions that a method can throw in order to be able to handle
them. Thus, you must declare the exceptions that the method can throw in the
method signature.
Checked exceptions
Checked exceptions are all exception objects instantiated from subclasses of
the Exception class other than those of the RuntimeException
class.
Exceptions of all Exception subclasses other than RuntimeException
are checked by the compiler and will result in compiler errors if they are
neither caught nor declared.
You will learn how you can create your own exception classes later. Whether
your exception objects become checked or not depends on the class that you
extend when you define your exception class.
(If you extend a checked exception class, your new exception type will
be a checked exception. Otherwise, it will not be a checked exception.)
Add www.developer.com to your favorites Add www.developer.com to your browser search box IE 7 | Firefox 2.0 | Firefox 1.5.xReceive news via our XML/RSS feed