You have a choice
Therefore, when writing exception handlers, you have a choice. You can
write a handler whose exception type corresponds to a node in the inheritance
hierarchy, and it will be appropriate to catch exceptions of that type,
or any subclass of that type.
Alternately, you can write a handler whose exception type corresponds to a
leaf, in which case, it will be appropriate to catch exceptions of
that type only.
And finally, you can mix and match, writing some exception handlers whose
type corresponds to a node, and other exception handlers whose type corresponds
to a leaf. In all cases, however, be sure to position your exception
handlers in reverse subclass order, with the furthermost subclass from the root
appearing first, and the root class appearing last.
The finally block
And finally (no pun intended), Campione and Walrath tell us:
"Java's finally block provides a mechanism that allows your method to
clean up after itself regardless of what happens within the try block. Use the
finally block to close files or release other system resources."
To elaborate, the finally block can be used to provide a mechanism for
cleaning up open files, etc., before allowing control to be passed to a
different part of the program. You accomplish this by writing the cleanup
code within a finally block.
Code in finally block is always executed
It is important to remember that the runtime system always executes the code
within the finally block regardless of what happens within the try
block.
If no exceptions are thrown, none of the code in catch blocks is
executed, but the code in the finally block is executed.
If an exception is thrown and the code in an exception handler is executed,
once the execution of that code is complete, control is passed to the finally
block and the code in the finally block is executed.
(There is one important exception to the above. If the code in the
catch block terminates the program by executing System.exit(0),
the code in the finally block will not be executed.)
The power of the finally block
The sample program shown in Listing 5 illustrates the power of the finally
block.
/*File Excep15.java
Copyright 2002, R. G. Baldwin
Tested with JDK 1.4.0 under Win2000
**************************************/
class Excep15{
public static void main(
String[] args){
new Excep15().aMethod();
}//end main
//---------------------------------//
void aMethod(){
try{
int x = 5/0;
}//end try block
catch(ArithmeticException e){
System.out.println(
"In catch, terminating aMethod");
return;
}//end catch block
finally{
System.out.println(
"Executing finally block");
}//end finally block
System.out.println(
"Out of catch block");
}//end aMethod
}//end class Excep15
Listing 5
|
Execute return statement in catch block
The code in Listing 5 forces an ArithmeticException by attempting to
do an integer divide by zero. Control is immediately transferred to the
matching catch block, which prints a message and then executes a
return statement.
Normally, execution of a return statement terminates the method
immediately. In this case, however, before the method terminates and
returns control to the calling method, the code in the finally block is
executed. Then control is transferred to the main method, which
called this method in the first place.
Figure 8 shows the output produced by this program.
In catch, terminating aMethod
Executing finally block
Figure 8
|
This program demonstrates that the finally block really does have the final
word.
Declaring exceptions thrown by a method
Sometimes it is better to handle exceptions in the method in which they are
detected, and sometimes it is better to pass them up the call stack and let
another method handle them.
In order to pass exceptions up the call stack, you must declare them
in your method signature.
To declare that a method throws one or more exceptions, you add a
throws clause to the method signature for the method. The throws
clause is composed of the throws keyword followed by a
comma-separated list of all the exceptions thrown by that method.
The throws clause goes after the method name and argument list and
before the curly bracket that defines the scope of the method.
Figure 9 shows the syntax for declaring that a method throws four
different types of exceptions.
void myMethod() throws
InterruptedException,
MyException,
HerException,
UrException
{
//method code
}//end myMethod()
Figure 9
|
Assuming that these are checked exceptions, any method calling this method
would be required to either handle these exception types, or continue passing
them up the call stack. Eventually, some method must handle them or the program
won't compile.
(Note however that while it might not represent good programming
practice, it is allowable to declare that the main method throws
exceptions. This is a way to avoid handling checked exceptions and still
get your program to compile.)
The throw keyword
Before your code can catch an exception, some Java code must throw
one. The exception can be thrown by code that you write, or by code that you are
using that was written by someone else.
Regardless of who wrote the code that throws the exception, it's always
thrown with the Java throw keyword. At least that is true
for exceptions that are thrown by code written in the Java language.
(Exceptions such as ArithmeticException are also thrown by the
runtime system, which is probably not written using Java source code.)
A single argument is required
When formed into a statement, the throw keyword requires a single
argument, which must be a reference to an object instantiated from the
Throwable class, or any subclass of the Throwable class. Figure
10 shows an example of such a statement.
throw new myThrowableClass("Message");
Figure 10
|
If you attempt to throw an object that is not instantiated from Throwable
or one of its subclasses, the compiler will refuse to compile your program.
Defining your own exception classes
Now you know how to write exception handlers for those exception objects that
are thrown by the runtime system, and thrown by methods in the standard class
library.
It is also possible for you to define your own exception classes, and to
cause objects of those classes to be thrown whenever an exception occurs.
In this case, you get to decide just what constitutes an exceptional condition.
For example, you could write a data-processing application that processes
integer data obtained via a TCP/IP link from another computer. If the
specification for the program indicates that the integer value 10 should never
be received, you could use an occurrence of the integer value 10 to cause an
exception object of your own design to be thrown.
Choosing the exception type to throw
Before throwing an exception, you must decide on its type. Basically,
you have two choices in this regard:
- Use an exception class written by someone else, such as the myriad of
exception classes defined in the Java standard library.
- Define an exception class of your own.
An important question
So, an important question is, when should you define your own exception
classes and when should you use classes that are already available.
Because this is only one of many design issues, I'm not going to try to give you
a ready answer to the question. However, I will refer you to
The Java
Tutorial by Campione and Walrath where you will find a checklist to help you
make this decision.
Choosing a superclass to extend
If you decide to define your own exception class, it must be a subclass of
Throwable. You must decide which class you will extend.
The two existing subclasses of Throwable are Exception and
Error. Given the earlier description of Error and its subclasses, it
is not likely that your exceptions would fit the Error category. (In
concept, errors are reserved for serious hard errors that occur deep within the
system.)
Checked or unchecked exception
Therefore, your new class should probably be a subclass of Exception.
If you make it a subclass of RuntimeException, it won't be a checked
exception. If you make it a subclass of Exception, but not a
subclass of RuntimeException, it will be a checked exception.
Only you can decide how far down the Exception hierarchy you want to
go before creating a new branch of exception classes that are unique to your
application.
Naming conventions
Many Java programmers append the word Exception to the end of all class names
that are subclasses of Exception, and append the word Error to the end of
all class names that are subclasses of Error.
One more sample program
Let's wrap up this lesson with one more sample program named Excep16.
We will define our own exception class in this program. Then we will throw,
catch and process an exception object instantiated from that class.
Discuss in fragments
This program is a little longer than the previous programs, so I will break
it down and discuss it in fragments. A complete listing of the program is
shown in Listing 10.
The class definition shown in Listing 6 is used to construct a custom
exception object that encapsulates a message. Note that this class extends
Exception. (Therefore, it is a checked exception.)
class MyException extends Exception{
MyException(String message){//constr
super(message);
}//end constructor
}//end MyException class
Listing 6
|
The constructor for this class receives an incoming String message
parameter and passes it to the constructor for the superclass. This makes
the message available for access by the getMessage method invoked in the
catch block.
The try block
Listing 7 shows the beginning of the main method, including the entire
try block
class Excep16{//controlling class
public static void main(
String[] args){
try{
for(int cnt = 0; cnt < 5; cnt++){
//Throw a custom exception, and
// pass message when cnt == 3
if(cnt == 3) throw
new MyException("3");
//Transfer control before
// processing for cnt == 3
System.out.println(
"Processing data for cnt = "
+ cnt);
}//end for-loop
}//end try block
Listing 7
|
The main method executes a for loop (inside the try
block) that guarantees that the variable named cnt will reach a value
of 3 after a couple of iterations.
Once during each iteration, (until the value of cnt reaches 3)
a print statement inside the for loop displays the value of cnt.
This results in the output shown in Figure 11.
Processing data for cnt = 0
Processing data for cnt = 1
Processing data for cnt = 2
Figure 11
|
What happens when cnt equals 3?
However, when the value of cnt equals 3, the throw statement
highlighted in boldface in Listing 7 is executed. This causes control to
transfer immediately to the matching catch block following the try
block (see Listing 8). During this iteration, the print statement
following the throw statement is not executed. Therefore, the
output never shows a value for cnt greater than 2, as shown in Figure 11.
The catch block
Listing 8 shows a catch block whose type matches the type of exception
thrown in Listing 7.
catch(MyException e){
System.out.println(
"In exception handler, "
+ "get the message\n"
+ e.getMessage());
}//end catch block
Listing 8
|
When the throw statement is executed in Listing 7, control is
transferred immediately to the catch block in Listing 8. No further
code is executed within the try block.
A reference to the object instantiated as the argument to the throw
keyword in Listing 7 is passed as a parameter to the catch block.
That reference is known locally by the name e inside the catch
block.
Using the incoming parameter
The code in the catch block invokes the method named getMessage
(inherited from the Throwable class) on the incoming parameter and
displays that message on the screen. This produces the output shown in
Figure 12.
In exception handler, get the message
3
Figure 12
|
When the catch block finishes execution ...
When the code in the catch block has completed execution, control is
transferred to the first executable statement following the catch block
as shown in Listing 9.
System.out.println(
"Out of catch block");
}//end main
}//end class Excep16
Listing 9
|
That executable statement is a print statement that produces the output shown
in Figure 13.
Out of catch block
Figure 13
|
A complete listing of the program is shown in Listing 10.
/*File Excep16.java
Copyright 2002, R. G. Baldwin
Illustrates defining, throwing,
catching, and processing a custom
exception object that contains a
message.
Tested using JDK 1.4.0 under Win 2000
The output is:
Processing data for cnt = 0
Processing data for cnt = 1
Processing data for cnt = 2
In exception handler, get the message
3
Out of catch block
**************************************/
//The following class is used to
// construct a customized exception
// object containing a message
class MyException extends Exception{
MyException(String message){//constr
super(message);
}//end constructor
}//end MyException class
//===================================//
class Excep16{//controlling class
public static void main(
String[] args){
try{
for(int cnt = 0; cnt < 5; cnt++){
//Throw a custom exception, and
// pass message when cnt == 3
if(cnt == 3) throw
new MyException("3");
//Transfer control before
// processing for cnt == 3
System.out.println(
"Processing data for cnt = "
+ cnt);
}//end for-loop
}//end try block
catch(MyException e){
System.out.println(
"In exception handler, "
+ "get the message\n"
+ e.getMessage());
}//end catch block
//-------------------------------//
System.out.println(
"Out of catch block");
}//end main
Listing 10
|