Java Programming, Lecture Notes # 56, Revised 10/06/99.
Preface
Introduction
Exception Hierarchy, An Overview
General Discussion
Advantages of Using Exception Handling
Exception Handling Specifics
Some Sample Programs using Standard Exceptions
A Nuisance Problem Explained
Catching and Handling Exceptions
The throw Statement
Creating Your Own Exception Classes
Review
Students in Prof. Baldwin's Introductory Java Programming classes at ACC are responsible for knowing and understanding all of the material in this lesson (except that they are not responsible for detailed information that is specific to C++).
The detailed material on C++ is provided as supplementary material for the benefit of persons already familiar with C++ who are making the transition into Java.
Stated in simple terms, the exception-handling capability of Java and C++ makes it possible for you to monitor for exceptional conditions within your program, and to transfer control to special exception-handling code which you design if an exceptional condition occurs. This is accomplished using three keywords: try, throw, and catch in C++. Java adds the finally keyword to the list.
You try to execute the statements contained within a block surrounded by braces.
If you detect an exceptional condition within that block, you throw an exception object of a specific type.
You then catch and process the exception object using code that you have designed.
In Java, you can then optionally execute a block of code designated by finally which is normally used to perform some type of cleanup which is needed whether or not an exception occurs.
There are also situations where an exceptional condition automatically transfers control to special exception-handling code which you write (cases where you don't provide the code to throw the exception object). In this case, you are responsible only for the code in the catch block and optionally, in Java, for the code in the finally block.
Although Java and C++ provide similar exception handling capabilities, the manner in which exception handling is implemented is different between the two languages. Therefore, the remainder of this lesson will concentrate on exception handling in Java only.
Our discussion in this lesson will include the following topics, not necessarily in this order:
According to The Java Tutorial by Campione and Walrath:
"An exception is an event that occurs during the execution of a program that disrupts the normal flow of instructions." |
When an exceptional condition causes an exception to be thrown, that exception is an object derived, either directly, or indirectly from the class Throwable.
The interpreter and many different methods in many different classes throw exceptions and errors.
A good summary of the entire list of exceptions and errors which can be thrown by the interpreter and the methods in the Java API is provided on page 349 of Java in a Nutshell, First Edition, by David Flanagan (unfortunately, that table seems to have been purged from the second edition).
In addition, you can define and throw exception objects of your own design (but you must inherit your exception class, either directly, or indirectly from Throwable).
The Throwable class has two subclasses:
Paraphrasing David Flanagan and Java in a Nutshell, an Error indicates that a non-recoverable error has occurred that should not be caught. Errors usually cause the Java interpreter to display a message and exit... Still paraphrasing Flanagan, an Exception indicates an abnormal condition that must be properly handled to prevent program termination. |
As of JDK 1.1.3, there are nine subclasses of the Exception class several of which have numerous subclasses.
One subclass of Exception is the class named RuntimeException This class has eleven subclasses, some of which are further subclassed. (Flanagan also provides an excellent exception class hierarchy chart in his book. By and large, it is an outstanding reference book.)
All exceptions other than those in the RuntimeException class must be either
According to Flanagan, the exception classes in this category represent routine abnormal conditions that should be anticipated and caught to prevent program termination. |
Campione and Walrath refer to these as "checked" exceptions.
As mentioned above, all errors and exceptions are subclasses of the Throwable class. The following chart shows the declarations in the Throwable class definition as extracted from the JavaSoft API Specification.
public class java.lang.Throwable extends java.lang.Object { // Constructors public Throwable(); public Throwable(String message); // Methods public Throwable fillInStackTrace(); public String getMessage(); public void printStackTrace(); public void printStackTrace(PrintStream s); public String toString(); } |
All exception objects inherit the methods of the Throwable class which you see listed above.
All errors and exceptions may have a message associated with them 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 of where the exception or error occurred and to convert the exception object to a String.
Continuing with information from 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 create an exception object and hand it off to the runtime system to deal with it.
The exception object should contain information about the exception, including its type and the state of the program when the error occurred.
Per Campione and Walrath,
"The runtime system is then responsible for finding some code to handle the error. In Java terminology, creating an exception object and handing it to the runtime system is called throwing an exception." |
The runtime system then attempts to find a block of code designed to handle the type of the exception object.
The runtime system begins with the method in which the error occurred and searches backwards through the call stack until it finds a method that contains an appropriate exception handler.
Per Campione and Walrath,
"An exception handler is considered appropriate if the type of the exception thrown is the same as the type of exception handled by the handler. Thus the exception bubbles up through the call stack until an appropriate handler is found and one of the calling methods handles the exception. The exception handler chosen is said to catch the exception." |
If an appropriate exception handler cannot be located by this method, the runtime system and the Java program terminate.
Separating Error Handling Code from Regular Code
Propagating Errors Up the Call Stack
Grouping Error Types and Error Differentiation
According to Campione and Walrath, exception handling provides the following advantages over "traditional" error management techniques:
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 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 this version 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.
The use of exception handling does not spare you the effort of doing the work of detecting, reporting, and handling errors.
What it does do 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.
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 which implements 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.
Again, Campione and Walrath provide some interesting examples which illustrate the advantage of propagating errors up the call stack.
Since
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 which applies to all exceptional conditions associated with an object of your stack class. From this, you might inherit other classes which pertain to specific exceptional conditions such a push exceptions, pop exceptions, initialization exceptions, etc.
If you code throws an exception object of one of the specific types, that object can be caught
This capability to handle exceptions on an organized hierarchical basis is very powerful, and is described very well by Campione and Walrath. You are strongly encouraged to study what they have to say on this topic.
Now let's set aside the general discussion of exception handling and take a look at the specifics.
Java's Catch or Specify Requirement
Catch
Specify
Checked Exceptions
Exceptions That Can Be Thrown Within the Scope of the Method
As indicated earlier, except for Throwable objects of type Error and Throwable/Exception objects of type RuntimeException, Java programs must either
all Exception objects which are thrown.
Campione and Walrath refer to these as "checked" exceptions, and they refer to declaring an exception as specifying an exception. More specifically, according to Campione and Walrath,
"The Java language requires that methods either catch or specify all checked exceptions that can be thrown within the scope of that method. ... If the compiler detects a method...that doesn't meet this requirement, it issues an error message...and refuses to compile the program. |
We need to further examine
A method catches an exception by providing an exception handler for that type of exception object.
If a method does not provide an exception handler for the type of exception object thrown, the method must specify (declare) that it can throw that exception.
This is because any exception that can be thrown by a method is part of the method's public programming interface and users of a method must know about the exceptions that a method can throw. Thus, you must specify the exceptions that the method can throw in the method signature.
As mentioned earlier, "checked" exceptions are all thrown objects of the Exception class other than those of the RuntimeException class which is a subclass of Exception.
Non-checked exceptions or exceptions of type RuntimeException are those exceptions that occur within the Java runtime system. This includes arithmetic exceptions (such as dividing by zero), pointer exceptions (such as trying to access an object through a null reference), and indexing exceptions (such as attempting to access an array element through an index that is too large or too small).
Checked exceptions are exceptions that are not runtime exceptions.
Exceptions of all Exception classes and subclasses other than RuntimeException which is a subclass of Exception (approximately seventeen different classes plus any that you may add) are checked by the compiler and will result in compiler errors if they are not either caught or specified.
Later, we will learn how you can create your own exception classes. Whether your exception objects become non-checked or checked depends on which class you choose as your superclass in defining your exception class.
The statement "exceptions that can be thrown within the scope of the method" includes not only exceptions which are thrown by code written into the method, but also includes exceptions thrown by methods called by that method, or methods called by those methods, etc.
According to Campione and Walrath,
"This phrase includes any exception that can be thrown while the flow of control remains within the method. Thus, this statement includes both exceptions that are thrown directly by the method with Java's throw statement, and exceptions that are thrown indirectly by the method through calls to other methods." |
Sample Program with No Exception Handling Code
Sample Program which Fixes One Compiler Error
Sample Program which Fixes the Remaining Compiler Error
This section contains sample programs which illustrate exception handling for "standard" exceptions.
While the term "standard" may not be entirely appropriate, it is intended here to indicate those exceptions which are part of the Java JDK as delivered, and does not indicate exception classes that you define for custom purposes.
Later we will discuss the design and use of custom exception classes.
We will look at three applications which illustrate the successive stages of dealing with checked exceptions by either catching or specifying those exceptions.
The first sample program is shown below which neither catches nor specifies the InterruptedException which can be thrown by the sleep() method of the Thread class. This is a checked exception.
/*File Excep01.java Copyright 1997, R.G.Baldwin Illustrates failure to catch or specify InterruptedException in called method. This program won't compile. Compiler errors are similar to the following: Excep01.java:27: Exception java.lang.InterruptedException must be caught, or it must be declared in the throws clause of this method. Thread.currentThread().sleep(1000); **********************************************************/ import java.lang.Thread; class Excep01{ public static void main(String[] args){ //instantiate obj to dispatch method Excep01 obj = new Excep01(); //call method which throws an exception obj.myMethod(); }//end main //-----------------------------------------------------// void myMethod(){ //sleep() throws exception Thread.currentThread().sleep(1000); }//end myMethod }//end class Excep01 |
Despite its length which is largely made up of comments, this is a fairly simple program. The method named main() calls the method named myMethod() which in turn calls the method named sleep().
The method named sleep() throws a checked exception named InterruptedException.
No provisions were made to deal with this exception. Therefore, the program refused to compile with the compiler errors shown.
Note that the compiler error identified the problem at the sleep() method.
The next version of the program, shown below, fixes the problem identified with the call to sleep() by declaring the exception in the signature for the method named myMethod(). An explanation of the fix follows the program listing.
/*File Excep02.java Copyright 1997, R.G.Baldwin Illustrates failure to catch or specify InterruptedException in called method. This program won't compile. Compiler errors are similar to the following: Excep02.java:20: Exception java.lang.InterruptedException must be caught, or it must be declared in the throws clause of this method. obj.myMethod(); **********************************************************/ import java.lang.Thread; class Excep02{ public static void main(String[] args){ //instantiate obj to dispatch method Excep02 obj = new Excep02(); obj.myMethod(); }//end main //-----------------------------------------------------// void myMethod() throws InterruptedException{ //sleep() throws exception Thread.currentThread().sleep(1000); }//end myMethod }//end class Excep02 |
As you can see from the comments, this version of the program eliminated the compiler error identified with the call to the method named sleep().
This was accomplished by specifying that the method named myMethod() throws InterruptedException. This is one way to deal with an exception. In particular, this simply passes the exception up the call stack to the next higher-level method in a group of nested methods. This doesn't solve the problem, it simply hands it off to another method to solve.
The problem still exists, and is now identified solely with the call to myMethod() where it will have to be handled in order to make it go away.
The next version of the program fixes the remaining compiler error. A discussion of the fix follows the program listing.
/*File Excep03.java Copyright 1997, R.G.Baldwin Illustrates specifying and catching Interrupted Exception **********************************************************/ import java.lang.Thread; class Excep03{ public static void main(String[] args){ //instantiate obj to dispatch method Excep03 obj = new Excep03(); try{//create a proper try/catch sequence //call method which throws an exception obj.myMethod(); } catch(InterruptedException e){ //process exception inside braces }//end catch }//end main //-----------------------------------------------------// void myMethod() throws InterruptedException{ //sleep() throws exception Thread.currentThread().sleep(1000); }//end myMethod }//end class Excep03 |
As you can see, this version of the program is fairly clean.
As in the previous version, the method named myMethod() gets rid of the problem by passing it upstream to the method from which it was called.
In this version, the method named main() provides an appropriate structure for dealing with the problem (although it doesn't actually deal with it in any significant way).
That structure consists of a try block and a catch block. This can be interpreted as follows:
Try to execute the code within the try block.
If an exception occurs, search for a catch block which matches the type of object thrown by the exception.
If such a catch block can be found, immediately transfer control to the catch block without executing any of the remaining code in the try block (for simplicity, our program didn't have any remaining code). Some later sample programs will illustrate code being skipped due to the occurrence of an exception.
This transfer of control is not a method call. It is an unconditional transfer of control. There is no return from a catch block.
In our case, there was a matching catch block to receive control. In the event of an exception, the program would execute the statements within the body of the catch block (which in our case was empty for simplicity) and then transfer control to the code following the final catch block in the group of catch blocks (in our case, there was only one).
Now let's look an a sample program that actually experiences an exceptional condition and deals with it.
You may have noticed that I try to keep my sample programs as simple as possible, introducing the minimum amount of complexity necessary to illustrate the main point of the program.
As it turns out, it is not easy to write a really simple program that is guaranteed to experience one of the checked exceptions. Therefore, the following program was written to experience an exception of the runtime variety which is very easy to create: divide by zero.
/*File Excep04.java Copyright 1997, R. G. Baldwin Illustrates implementation of exception handling using try/catch blocks. Before implementing exception handling code, the output was: Program is running. The quotient is: 3 Program is running. The quotient is: 6 java.lang.ArithmeticException: / by zero at Excep04.main(Excep04.java:8) After implementing exception handling code, the output was: Program is running. The quotient is: 3 Program is running. The quotient is: 6 Oops, caught an exception with the message: / by zero and with the stacktrace showing: java.lang.ArithmeticException: / by zero at Excep04.main(Excep04.java:17) Converting the exception object to a String we get: java.lang.ArithmeticException: / by zero In a real program, we might take corrective action here, but in this sample program, we will simply terminate. Because of the way this program was developed and documented, line numbers in the messages are not correct. **********************************************************/ class Excep04{ public static void main(String[] args){ try{ for(int cnt = 2; cnt >-1; cnt--){ System.out.println( "Program is running. The quotient is: " + 6/cnt); }//end for-loop }//end try block catch(ArithmeticException e){ //put corrective code in here System.out.println( "\nOops, caught an exception with the message: " + e.getMessage() + "\nand with the stacktrace showing:"); e.printStackTrace(); System.out.println( "Converting the exception object to a String " + "we get:\n" + e.toString()); System.out.println( "In a real program, we might take corrective " + "action here,\nbut in this sample program, we " + "will simply terminate.\nBecause of the way " + "this program was developed and \ndocumented, " + "line numbers in the messages are not correct."); }//end catch block }//end main }//end class Excep04 |
As you can see, a for loop that was guaranteed to experience a divide-by-zero condition was placed in the program. The program was executed, and the output, including the runtime error message which appeared on the screen was captured and added to the comments at the beginning of the program.
At this stage, the program experienced abnormal termination, and the exceptional condition was not properly handled.
Then the for loop was enclosed in a try block which was followed by a catch block. (We will discuss the specifics of try and catch blocks later.) The catch block was designed to catch and process exceptions of the ArithmeticException class.
The bulk of the program consists of processing the exception object in various ways to obtain and display information about the exception.
The methods invoked using the exception object are all methods of the Throwable class, of which ArithmeticException is a subclass, two levels down.
This program does not attempt to illustrate how an actual program might recover from an exception of this sort. However, it is clear that (rather than experiencing automatic and unconditional termination as was the case before exception-handling was implemented) the program remains in control, and in some cases, recovery might be possible.
This example illustrates try and catch. The third, and optional part of exception handling, finally, will be discussed later.
While we are at it, we would be be remiss in failing to mention a nuisance problem associated with exception handling.
As you may recall, the scope of a variable in Java is limited to the block of code in which it is declared. A block is determined by enclosing code within a pair of matching braces: {...}.
Since a pair of braces is required to define a try block, the scope of any variables or objects declared inside the try block is limited to the try block.
While this is not an insurmountable problem, it may require you to modify your programming style in ways that you find distasteful.
This is illustrated in the following simple program which (for whatever good reason) declares and initializes a variable named aVariable inside the try block and then attempts to access the variable outside the try block.
The program won't compile. The compiler error message is shown in the comments at the beginning of the program.
True, you can avoid the problem by declaring the variable ahead of the try block and then assigning data to it within the try block, but that may not be consistent with your programming style. Regardless, this is a situation that you will have to learn to live with.
/*File Excep05.java Copyright 1997, R. G. Baldwin Illustrates practical scoping problems which accompany try blocks. This program won't compile. Compiler error is: Excep05.java:23: Undefined variable: aVariable int myVariable = aVariable; **********************************************************/ class Excep05{ public static void main(String[] args){ try{ for(int cnt = 2; cnt >0; cnt--){ System.out.println( "Program is running. The quotient is: " + 6/cnt); }//end for-loop int aVariable = 6; }catch(ArithmeticException e){ //put corrective code in here }//end catch block //Compiler error occurs here. aVariable is not // accessible int myVariable = aVariable; }//end main }//end class Excep05 |
The try Block
The catch Block(s)
Catching Multiple Exception Types with One Handler
The finally Block
Specifying (Declaring) the Exceptions Thrown by a Method
Now that you have seen some sample programs to help you visualize the process, lets discuss the process in more detail.
According to Campione and Walrath,
"The first step in writing any exception handler is putting the Java statements within which an exception can occur into a try block. The try block is said to govern the statements enclosed within it and defines the scope of any exception handlers (established by subsequent catch blocks) associated with it." |
Note that the terminology being used by Campione and Walrath treats the catch block as the "exception handler" and treats the try block as something that precedes one or more exception handlers. I don't necessarily disagree with their terminology, I mention it only for the purpose of avoiding confusion over terminology.
The general syntax of a try block, as you saw in the previous program has the word try followed by one or more statements enclosed in a pair of matching braces, as shown below:
try{ //java statements } |
You could have more than one statement that could throw one or more exceptions and you will need to deal with all of them.
You could put each such statement that might throw exceptions within its own try block and provide separate exception handlers for each try block. Note that some statements, particularly those that invoke other methods, could potentially throw many different types of exceptions. Thus a try block consisting of a single statement might require many different exception handlers.
You could put all or several of the statements that might throw exceptions within a single try block and associate multiple exception handlers with it. There are a number of practical issues here, and only you can decide in any particular instance which approach would be best.
Exception handlers must be placed immediately following their associated try block. If an exception occurs within the try block, that exception is handled by the appropriate exception handler associated with the try block.
According to Campione and Walrath,
"A try block must be accompanied by at least one catch block or one finally block." |
Continuing with Campione and Walrath,
"Next, you associate exception handlers with a try block by providing one or more catch blocks directly after the try block." |
There can be no intervening code between the end of the try block and the beginning of the first catch block, and no intervening code between catch blocks. The general form of Java's catch statement is:
catch (AThrowableObjectType variableName) { //Java statements } |
The header line in the catch block requires a single argument as shown.
The syntax is the same as an argument declaration for a method.
The argument type declares the type of exception object that a particular exception handler can handle and must be the name of a class that inherits from the Throwable class discussed earlier.
Also as in a method declaration, there is a parameter which is the name by which the handler can refer to the exception object.
For example, in an earlier example program, we used statements such as e.getMessage() to access an instance method of an exception object caught by our exception handler.
You access the instance variables and methods of exception objects the same way that you access the instance variables and methods of other objects.
According to Campione and Walrath,
"The catch block contains a series of legal Java statements. These statements are executed if and when the exception handler is invoked. The runtime system invokes the exception handler when the handler is the first one in the call stack whose type matches that of the exception thrown. |
Therefore, the order of your exception handlers is important, particularly if you have some handlers which are further up the exception hierarchy tree than others.
Those handlers designed to handle exceptions furthermost from the root of the hierarchy tree should be placed first in the list of exception handlers.
Otherwise, an exception hander designed to handle a specialized "leaf" object may be preempted by another handler whose exception object type is closer to the root if the second exception handler appears earlier in the list of exception handlers.
Exception handlers that you write may be more or less specialized. In addition to writing handlers for very specialized exception objects, the Java language allows you to write general exception handlers that handle multiple types of exceptions.
Java exceptions are Throwable objects (instances of the Throwable class or a subclass of the Throwable class).
The Java packages contain numerous classes that derive from Throwable and build a hierarchy of Throwable classes.
According to Campione and Walrath,
"Your exception handler can be written to handle any class that inherits from Throwable. If you write a handler for a "leaf" class (a class with no subclasses), you've written a specialized handler: it will only handle exceptions of that specific type. If you write a handler for a "node" class (a class with subclasses), you've written a general handler: it will handle any exception whose type is the node class or any of its subclasses." |
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." |
And again, from Campione and Walrath
"The final step in setting up an exception handler is providing a mechanism for cleaning up the state of the method before (possibly) allowing control to be passed to a different part of the program. You do this by enclosing the cleanup code within a finally block. ...The runtime system always executes the statements within the finally block regardless of what happens within the try block. ...After the exception handler has run, the runtime system passes control to the finally block." |
Cleanup might include closing files, closing communication links, flushing buffers, etc.
There is something less than total agreement within the industry as to the value of the finally block. Campione and Walrath argue to justify its existence. You will simply need to review all the available material on the subject and make up your own mind.
A sample program is presented at the end of this lesson that demonstrates the power of the finally block.
In that program, an exception handler attempts to terminate the program by executing a return statement. However, before that return statement is executed to terminate the program, control is passed to the finally block and all of the statements in the finally block are executed. Then the program terminates.
This program demonstrates that the finally block does have the final word.
Sometimes it is best to handle exceptions in the method where they are detected, and sometimes it is better to pass them up the call stack and let another method handle them. This was illustrated in one of the sample programs early in this lesson.
I won't try to justify passing exceptions up the call stack. I will simply state that sometimes that is the best thing to do. In order to pass exceptions up the call stack, you must specify or declare them (depending on whether you prefer the terminology of Campione and Walrath, or the terminology of Flanagan).
To specify 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.
One of the methods in an earlier sample program included a throws clause. The header line for the method is repeated below, but modified to indicate that it throws four different exceptions.
void myMethod() throws InterruptedException, MyException, HerException, UrException { //method code }//end myMethod() |
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.
According to Campione and Walrath,
"Before you can catch an exception, some Java code somewhere must throw one. Any Java code can throw an exception: your code, code from a package written by someone else (such as the packages that come with the Java development environment), or the Java runtime system. Regardless of who (or what) throws the exception, it's always thrown with the Java throw statement." |
The throw statement requires a single argument, which must be an object derived either directly or indirectly from the class Throwable.
The object may be an instance of any subclass of the Throwable class defined in java.lang . Here is an example:
throw myThrowableObject; |
This is not too difficult, but if you attempt to throw an object that is not subclassed from the Throwable class, the compiler will refuse to compile your program and will display an error message.
Choosing the Exception Type to Throw
Choosing a Superclass
Naming Conventions
Another Sample Program
Now you know how to write exception handlers for those exception objects which are automatically thrown by the system.
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 (according to your definition of an exception).
For example, you could write a data-processing application that processes integer data obtained via a TCP/IP link with another computer.
If for some reason the specification for the program is such that the integer value 10 should never be received, then you could use an occurrence of that situation to cause an exception object of your design to be thrown.
Before creating your own exception class, you must decide on its type.
According to Campione and Walrath, when trying to decide on the type, you have two choices:
|
Not too earthshaking, but true nonetheless. The big question is, when should you write your own exception classes and when should you use classes that are already available.
According to Campione and Walrath, if you answer "yes" to any of the following questions, you probably should write your own exception classes. Otherwise, you should strive to use existing classes.
|
If you decide to write your own exception classes, it will be necessary for them to be a subclass of Throwable. Then you must decide which class you will subclass.
The two subclasses of Throwable are Exception and Error and it probably doesn't make logical sense to create a third branch of Throwable. You can probably live within the two existing branches.
Given the earlier description of Error and its subclasses, it is not likely that your exceptions would fit the Error category. (Errors are reserved for serious hard errors that occur deep in the system.)
Therefore, you should probably make your new classes direct or indirect descendants of Exception.
Only you can decide how far down the Exception tree you want to go before creating a new branch of exception classes that are unique to your application.
According to Campione and Walrath,
"It's good practice to append the word "Exception" to the end of all classes that inherit (directly or indirectly) from the Exception class. Similarly, classes that inherit from the Error class should end with the string "Error"." |
Let's examine one more sample program in which we create our own exception class. Then we will throw, catch and process it.
We will also provide a finally block to prove that the code in the finally block gets executed despite the fact that the exception handler attempts to terminate the program by executing a return statement.
If a finally block exists, it will always be executed before control can be transferred to another part of the program, or terminated altogether. That is why it is a good place to do cleanup work.
/*File Excep06.java Copyright 1997, R. G. Baldwin Illustrates creating, throwing, catching, and processing a custom exception object that contains diagnostic information. Also illustrates that the code in the finally block executes despite the fact that the exception handler tries to terminate the program by executing a return statement. The output from the program is: Processing data for cnt = :0 Processing data for cnt = :1 Processing data for cnt = :2 In exception handler, get the message The message is: buy low, sell high The diagnosticData value is: 3 In exception handler, trying to terminate program. by executing a return statement In finally block just to prove that we get here despite the return statement in the exception handler. **********************************************************/ //The following class is used to construct a customized // exception object. The instance variable in the object // is used to simulate passing diagnostic information // from the point where the exception is thrown to the // exception handler. class MyPrivateException extends Exception{ int diagnosticData; //constructor MyPrivateException(int diagnosticInfo){ //save diagnosticInfo in the object diagnosticData = diagnosticInfo; }//end constructor //-----------------------------------------------------// //Overrides Throwable's getMessage() method public String getMessage(){ return ("The message is: buy low, sell high\n" + "The diagnosticData value is: " + diagnosticData); }//end getMessage method }//end MyPrivateException class //=======================================================// class Excep06{//controlling class public static void main(String[] args){ try{ for(int cnt = 0; cnt < 5; cnt++){ //Throw a custom exception, and pass // diagnosticInfo if cnt == 3 if(cnt == 3) throw new MyPrivateException(3); //Transfer control before // "processing" for cnt == 3 System.out.println( "Processing data for cnt = :" + cnt); }//end for-loop System.out.println( "This line of code will never execute."); }catch(MyPrivateException e){ System.out.println( "In exception handler, get the message\n" + e.getMessage()); System.out.println( "In exception handler, trying to terminate " + "program.\n" + "by executing a return statement"); return; //try to terminate the program }//end catch block //---------------------------------------------------// finally{ System.out.println( "In finally block just to prove that we get " + "here despite\nthe return statement in the " + "exception handler."); }//end finally block //---------------------------------------------------// System.out.println( "This statement will never execute due to " + "return statement in the exception handler."); }//end main }//end class Excep06 |
The program and its associated comments are generally self-explanatory based on what you have learned earlier.
Note that even though the exception handler attempts to terminate the program by executing a return statement, before that return statement is executed, control is passed to the finally block and the code in the finally block is executed. Then the program terminates.
Note also that the program overrides the getMessage() method from the Throwable class. The output from a similar program (which is not reproduced here) shows that the getMessage() method returns null if it isn't specifically overridden by the new exception class.
Q - The exception-handling capability of Java makes it possible for you to monitor for exceptional conditions within your program, and to transfer control to special exception-handling code which you design. List five keywords that are used for this purpose.
A - try, throw, catch, finally, and throws
Q - All exceptions in Java are thrown by code that you write: True or False? If false, explain why.
A - False. There are situations where an exceptional condition automatically transfers control to special exception-handling code which you write (cases where you don't provide the code to throw the exception object).
Q - When an exceptional condition causes an exception to be thrown, that exception is an object derived, either directly, or indirectly from the class Exception: True or False? If false, explain why.
A - False. When an exceptional condition causes an exception to be thrown, that exception is an object derived, either directly, or indirectly from the class Throwable.
Q - All exceptions other than those in the RuntimeException class must be either caught, or declared in a throws clause of any method that can throw them: True or False? If false, explain why.
A - True.
Q - What method of which class would you use to extract the message from an exception object?
A - The getMessage() method of the Throwable class.
Q - Normally, those exception handlers designed to handle exceptions closest to the root of the exception class hierarchy should be placed first in the list of exception handlers: True or False? If false, explain why.
A - False. The above statement has it backwards. Those handlers designed to handle exceptions furthermost from the root of the hierarchy tree should be placed first in the list of exception handlers.
Q - Explain why you should place exception handlers furthermost from the root of the exception hierarchy tree first in the list of exception handlers.
A - An exception hander designed to handle a specialized "leaf" object may be preempted by another handler whose exception object type is closer to the root of the exception hierarchy tree if the second exception handler appears earlier in the list of exception handlers.
Q - In addition to writing handlers for very specialized exception objects, the Java language allows you to write general exception handlers that handle multiple types of exceptions: True or False? If false, explain why.
A - True.
Q - Your exception handler can be written to handle any class that inherits from Throwable. If you write a handler for a node class (a class with no subclasses), you've written a specialized handler: it will only handle exceptions of that specific type. If you write a handler for a leaf class (a class with subclasses), you've written a general handler: it will handle any exception whose type is the node class or any of its subclasses. True or False? If false, explain why.
A - False. "Leaf" and "node" are reversed in the above statement. If you write a handler for a "leaf" class (a class with no subclasses), you've written a specialized handler: it will only handle exceptions of that specific type. If you write a handler for a "node" class (a class with subclasses), you've written a general handler: it will handle any exception whose type is the node class or any of its subclasses."
Q - Java's finally block provides a mechanism that allows your method to clean up after itself regardless of what happens within the try block. True or False? If false, explain why.
A - True.
Q - Explain how you would specify that a method throws one or more exceptions.
A - To specify 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.
Q - Provide a code fragment that illustrates how you would specify that a method throws more than one exception.
A - See code fragment below.
void myMethod() throws InterruptedException, MyException, HerException, UrException { //method body } |
Q - What type of argument is required by the throw statement?
A - The throw statement requires a single argument, which must be an object derived either directly or indirectly from the class Throwable.
Q - Some exception objects are automatically thrown by the system. 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. True or False? If false, explain why.
A - True.
Q - Write a program that meets the following specifications.
/*File SampProg101.java, from lesson 56 Copyright R.G.Baldwin, 1997 The following program won't compile. Without viewing the following solution, Fix the program by declaring that the method named myMethod throws an exception of the proper type, and by catching and processing that exception in main. ======================================================= import java.lang.Thread; class SampProg101{ public static void main(String[] args){ SampProg101 obj = new SampProg101(); obj.myMethod(); }//end main void myMethod(){ Thread.currentThread().sleep(1000); }//end myMethod }//end class Excep01 */ import java.lang.Thread; class SampProg101{ public static void main(String[] args){ SampProg101 obj = new SampProg101(); try{ obj.myMethod(); }catch(InterruptedException e){ //do something with the exception }//end catch block }//end main void myMethod() throws InterruptedException{ Thread.currentThread().sleep(1000); }//end myMethod }//end class SampProg101 |
Q - Write a program that meets the following specifications.
/*File SampProg102.java from lesson 56 Copyright 1997, R. G. Baldwin Without viewing the following solution, write a Java application that purposely creates and then deals with an ArithmeticException caused by attempting to divide by zero. The output from the program should be similar to the following: Before implementing exception handling code, the output was: Program is running. The quotient is: 3 Program is running. The quotient is: 6 java.lang.ArithmeticException: / by zero at SampProg102.main(SampProg102.java:8) After implementing exception handling code, the output was: Program is running. The quotient is: 3 Program is running. The quotient is: 6 Oops, caught an exception with the message: / by zero and with the stacktrace showing: java.lang.ArithmeticException: / by zero at SampProg102.main(SampProg102.java:17) Converting the exception object to a String we get: java.lang.ArithmeticException: / by zero In a real program, we might take corrective action here **********************************************************/ class SampProg102{ public static void main(String[] args){ try{ for(int cnt = 2; cnt >-1; cnt--){ System.out.println( "Program is running. The quotient is: "+6/cnt); }//end for-loop }catch(ArithmeticException e){ //put corrective code in here System.out.println( "\nOops, caught an exception with the message: " + e.getMessage() + "\nand with the stacktrace showing:"); e.printStackTrace(); System.out.println( "Converting the exception object to a " + "String we get:\n" + e.toString()); System.out.println( "In a real program, we might take corrective " + "action here"); }//end catch block }//end main }//end class SampProg102 |
Q - Write a program that meets the following specifications.
/*File SampProg103.java from lesson 56 Copyright 1997, R. G. Baldwin Without viewing the following solution, write a Java application that illustrates creating, throwing, catching, and processing a custom exception object that contains diagnostic information. Also illustrate that the code in the finally block executes despite the fact that the exception handler tries to terminate the program by executing a return statement. The output from the program should be similar to the following: Processing data for cnt =0 Processing data for cnt =1 Processing data for cnt =2 In exception handler, get the message The message is: buy low, sell high The diagnosticData value is: 3 In exception handler, trying to terminate program. by executing a return statement In finally block just to prove that we get here despite the return statement in the exception handler. **********************************************************/ //The following class is used to construct a customized // exception object. The instance variable in the object // is used to simulate passing diagnostic information from // the point where the exception is thrown to the // exception handler. class MyPrivateException extends Exception{ int diagnosticData; MyPrivateException(int diagnosticInfo){//constructor //save diagnosticInfo in the object diagnosticData = diagnosticInfo; }//end constructor public String getMessage(){ //overrides Throwable's getMessage() method return ("The message is: buy low, sell high\n" + "The diagnosticData value is: " + diagnosticData); }//end getMessage method }//end MyPrivateException class class SampProg103{//controlling class public static void main(String[] args){ try{ for(int cnt = 0; cnt < 5; cnt++){ //Throw a custom exception, and // pass diagnosticInfo if cnt == 3 if(cnt == 3) throw new MyPrivateException(3); //Transfer control before // "processing" for cnt == 3 System.out.println( "Processing data for cnt =" + cnt); }//end for-loop System.out.println( "This line of code will never execute."); }catch(MyPrivateException e){ System.out.println( "In exception handler, get the message\n" + e.getMessage()); System.out.println( "In exception handler, trying to terminate " + "program.\n" + "by executing a return statement"); return; //try to terminate the program }//end catch block finally{ System.out.println( "In finally block just to prove that we get " + "here despite\n" + "the return statement in the exception " + "handler."); }//end finally block System.out.println( "This statement will never execute due to return " + " statement in the exception handler."); }//end main }//end class SampProg103 |
-end-