developer.com - Reference
Click here to support our advertisers
SHOPPING
JOB BANK
CLASSIFIEDS
DIRECTORIES
REFERENCE
Online Library
LEARNING CENTER
JOURNAL
NEWS CENTRAL
DOWNLOADS
COMMUNITY
CALENDAR
ABOUT US
Journal:
Get the weekly email highlights from the most popular journal for developers!
Current issue
developer.com
developerdirect.com
htmlgoodies.com
javagoodies.com
jars.com
intranetjournal.com
javascripts.com
All Categories :
Java
Chapter 7
Exceptions
CONTENTS
Eliminating Software Errors
Error Processing and Exceptions
Throwing Exceptions
Declaring Exceptions
Declare or Catch?
Using the try Statement
Catching Exceptions
Nested Exception Handling
Rethrowing Exceptions
Analysis of NestedExceptionTest
Summary
In this chapter you'll learn how to use exceptions to implement
error-handling capabilities in your Java programs. You'll learn
how to declare exceptions and identify methods that use them.
You'll also learn how to throw exceptions in response to error
conditions and how to catch exceptions in support of error processing.
When you finish this chapter, you'll be able to use exceptions
to handle all sorts of errors in your programs.
Eliminating
Software Errors
Programs are reliable, in part, because they are able to cope
with errors and exceptions that occur during their execution.
The development of reliable, error-tolerant software is a multiphase
effort that spans program design, coding, compilation, loading,
and execution.
The most serious errors are those that are designed into a program.
Many design errors can be eliminated by following a sound development
approach, using modern software engineering methods, and making
a firm commitment to software quality. The use of an object-oriented
approach to software development helps to simplify software design,
reduce errors, and promote software reliability.
Programming errors initially occur when a software design is translated
into source code. Program verification, validation, analysis,
and test activities help to eliminate design and programming errors.
The implementation of coding standards and code walkthroughs also
reduces the likelihood of undetected programming errors.
The Java language eliminates whole classes of errors that result
from the use of dangerous programming constructs such as pointers
and automatic type conversions. The simplicity and familiarity
of the language also reduce the occurrence of programming errors.
The Java compiler and runtime environment help to keep errors
from being introduced into executable Java code. The compiler
performs extensive type checking to ensure that objects are correctly
referenced and updated and that methods are properly invoked.
The runtime system duplicates compile-time checks and implements
additional checks to verify that executable code follows established
rules for program correctness.
With all the error checking that takes place before a program
is executed, you might think that it would be unlikely that errors
could still creep into a program. They can and always do, in accordance
with Murphy's Law. Runtime error and exception handling is used
to identify error conditions and perform processing that minimizes
their impact.
Error
Processing and Exceptions
Java provides superior support for runtime error and exception
handling, allowing programs to check for anomalous conditions
and respond to them with minimal impact on the normal flow of
program execution. This allows error- and exception-handling code
to be added easily to existing methods.
Exceptions are generated by the Java runtime system in response
to errors that are detected when classes are loaded and their
methods are executed. The runtime system is said to throw
these runtime exceptions. Runtime exceptions are objects
of the class java.lang.RuntimeException or of its subclasses.
Exceptions may also be thrown directly by Java code using the
throw statement. These exceptions are thrown when code
detects a condition that could potentially lead to a program malfunction.
The exceptions thrown by user programs are generally not objects
of a subclass of RuntimeException. These non-runtime
exceptions are referred to as program exceptions.
Note
It is possible for user programs to throw runtime exceptions, but it is almost always a bad programming practice.
Both program and runtime exceptions must be caught in order for
them to be processed by exception-handling code. If a thrown exception
is not caught, its thread of execution is terminated and an error
message is displayed on the Java console window.
The approach used by Java to catch and handle exceptions is to
surround blocks of statements for which exception processing is
to be performed with a try statement. The try
statement contains a catch clause that identifies what
processing is to be performed for different types of exceptions.
When an exception occurs, the Java runtime system matches the
exception to the appropriate catch clause. The catch
clause then handles the exception in an appropriatemanner.
Throwing
Exceptions
Exceptions are thrown using the throw statement. Its
syntax is as follows:
throw Expression;
Expression must
evaluate to an object that is an instance of a subclass of the
java.lang.Exception class. The Exception class
is defined in the Java API. When an exception is thrown, execution
does not continue after the throw statement. Instead,
it continues with any code that catches the exception. If an exception
is not caught, the current thread of execution is terminated and
an error is displayed on the console window.
For example, the following statement will throw an exception,
using an object of class ExampleException:
throw new ExampleException();
The new operator is invoked with the ExampleException()
constructor to allocate and initialize an object of class ExampleException.
This object is then thrown by the throw statement.
Note
A throw statement can throw an object of any class that is a subclass of java.lang.Throwable; however, it is wise to stick with the standard convention of only throwing objects that are a subclass of class Exception.
Declaring
Exceptions
A method's throws clause lists the types of exceptions
that can be thrown during a method's execution. The throws
clause appears immediately before a method's body in the method
declaration. For example, the following method throws the ExampleException:
public void exampleMethod() throws ExampleException {
throw new ExampleException();
}
When more than one exception may be thrown during the execution
of a method, the exceptions are separated by commas in the throws
clause. For example, the following method can throw either the
Test1Exception or the Test2Exception:
public void testMethod(int i) throws Test1Exception, Test2Exception
{
if(i==1) throw new Test1Exception();
if(i==2) throw new Test2Exception();
}
The types identified in the throws clause must be capable
of being legally assigned to the exceptions that may be thrown.
Declare
or Catch?
If a program exception can be thrown during the execution of a
method, the method must either catch the expression or declare
it in the throws clause of its method declaration. If
an exception is not caught, it must be declared, even if it is
thrown in other methods that are invoked during the method's execution.
For example, suppose that method A of object X invokes method
B of object Y, which invokes method C of object Z. If method C
throws an exception, it must be caught by method C or declared
in method C's throws clause. If it is not caught by method
C, it must be caught by method B or declared in method B's throws
clause. Similarly, if the exception is not caught by method B,
it must be caught by method A or declared in method A's throws
clause. The handling of exceptions is a hierarchical process that
mirrors the method invocation hierarchy (or call tree).
Either an exception is caught by a method and removed from the
hierarchy or it must be declared and propagated back through the
method invocation hierarchy.
Note
Because runtime exceptions can occur almost anywhere in a program's execution, the catch-or-declare requirement applies only to program exceptions.
The CDrawApp programs of Chapters 5,
"Classes and Objects," and 6,
"Interfaces," provide an extended example of the declaration
of uncaught exceptions. The jdg.ch05.KeyboardInput class
contains three access methods that use the readLine()
method: getChar(), getText(), and getInt().
The readLine() method is inherited from the DataInputStream
class. It throws an exception of class IOException. Because
the getChar(), getText(), and getInt()
methods invoke the readLine() method, they must either
catch the exception or declare it in their throws clauses.
None of these methods catches IOException; therefore,
all declare it in their throws clauses. The getCommand()
method of class CDraw invokes the getChar()
method of an object of class KeyboardMethod. It does
not catch IOExeption, so it also must declare it. Because
the run() method of class CDraw invokes the
getCommand() method, it too is faced with catching or
declaring IOException. Because run() declares
IOException and the main() method of CDrawApp
invokes the run() method for a CDraw object,
it also must declare IOException in its throws
clause.
At this point you are probably coming to the conclusion that it
is a lot easier to catch and handle an exception than to declare
it throughout the class hierarchy. If so, you have discovered
a key benefit of Java's exception-handling approach. Java makes
it easier to develop more-reliable software and harder to develop
less-reliable software. If you are a lazy programmer like me,
Java will exploit your tendency to do things the easy way to encourage
you to do things the right way.
Using
the try
Statement
Statements for which exception processing is to be performed are
surrounded by a try statement with a valid catch
or finally clause. The syntax of the try statement
is as follows:
try TryBlock CatchClauses FinallyClause;
At least one catch clause or finally clause
must be defined. More than one catch clause may be used,
but no more than one finally clause may be identified.
The try block
is a sequence of Java statements that are preceded by an opening
brace ({) and followed by a closing brace (}).
The catch clauses are a sequence of clauses of the form:
catch (Parameter) {
/*
* Exception handling statements
*/
}
Parameter is a
variable that is declared to be a class or interface. The statements
within the catch clause are used to process the exceptions
that they "catch," as I'll explain shortly.
The finally clause identifies a block of code that is
to be executed at the conclusion of the try statement
and after any catch clauses. Its syntax is as follows:
finally {
/*
* Statements in finally clause
*/
}
The finally clause is always executed, no matter whether
an exception is thrown.
Catching
Exceptions
The try statement executes a statement block. If an exception
is thrown during the block's execution, it terminates execution
of the statement block and checks the catch clauses to
determine which, if any, of the catch clauses can catch
the thrown exception. If none of the catch clauses can
catch the exception, the exception is propagated to the next higher
level try statement. This process is repeated until the
exception is caught or no more try statements remain.
A catch clause can catch an exception if its argument
may be legally assigned the object thrown in the throw
statement. If the argument of a catch clause is a class,
the catch clause can catch any object whose class is
a subclass of this class. If the argument to a catch
clause is an interface, the catch clause can catch any
object that implements that interface.
The try statement tries each catch clause, in
order, and selects the first one that can catch the exception
that was thrown. It then executes the statements in the catch
clause. If a finally clause occurs in the try
statement, the statements in the finally clause are executed
after execution of the catch clause has been completed.
Execution then continues with the statement following the try
statement.
The following example shows how catch clauses are used
to process exceptions that are thrown within the try
statement. Create a directory ch07 under c:\java\jdg
and enter the source code in the file ExceptionTest.java.
Compile it using the command javac ExceptionTest.java.
The source code for the ExceptionTest program is shown
in Listing 7.1.
Listing 7.1. The source code of the ExceptionTest program.
import jdg.ch05.KeyboardInput;
import java.lang.System;
import java.lang.Exception;
import java.io.IOException;
class VowelException extends Exception {}
class BlankException extends Exception {}
class ExitException extends Exception {}
class ExceptionTest {
static KeyboardInput kbd = new KeyboardInput(System.in);
public static void main(String args[]) {
boolean finished = false;
do {
try {
processUserInput();
}catch (VowelException x) {
System.out.println("A vowel exception
occurred.");
}catch (BlankException x) {
System.out.println("A blank exception
occurred.");
}catch (ExitException x) {
System.out.println("An exit exception
occurred.");
finished = true;
}finally {
System.out.println("This is the finally
clause.\n");
}
} while(!finished);
}
static void processUserInput() throws VowelException, BlankException,
ExitException {
System.out.print("Enter a character: ");
System.out.flush();
char ch;
try {
ch=Character.toUpperCase(kbd.getChar());
} catch (IOException x) {
System.out.println("An IOException occurred.");
return;
}
switch(ch) {
case 'A':
case 'E':
case 'I':
case 'O':
case 'U':
throw new VowelException();
case ' ':
throw new BlankException();
case 'X':
throw new ExitException();
}
}
}
The ExceptionTest program uses the jdg.ch05.KeyboardInput
class to retrieve a character entered by the user. It then throws
and catches a VowelException, BlankException,
or ExitException based on the user's input.
The ExceptionTest class consists of a single class variable,
kbd, that is statically initialized to an object of class
KeyboardInput, with System.in as an argument
to its constructor.
ExceptionTest provides two static methods, main()
and processUserInput(). The main() method consists
of a simple do statement that repeatedly tries to invoke
processUserInput(). The try statement has three
catch clauses and a finally clause. The three
catch clauses notify the user of the type of exception
they catch. The catch clause with an ExitException
parameter causes the do statement and the program to
terminate by setting finished to true. The finally
clause just displays the fact that it has been executed.
The processUserInput() method prompts the user to enter
a character. The actual reading of the character occurs within
a try statement. IOException is caught by the
try statement, eliminating the need to declare the exception
in the processUserInput() throws clause. The
IOException is handled by notifying the user that the
exception occurred and continuing with program execution.
The processUserInput() method throws one of three exceptions
based upon the character entered by the user. If the user enters
a vowel, VowelException is thrown. If the user enters
a line beginning with a non-printable character, BlankException
is thrown. If the user enters x or X, ExitException
is thrown.
To run ExceptionTest, type javac ExceptionTest:
C:\java\jdg\ch07>java ExceptionTest
Enter a character:
The program prompts you to enter a character. Enter a blank line,
and the following output is displayed:
A blank exception occurred.
This is the finally clause.
Enter a character:
The program notifies you that a blank exception has occurred and
displays the fact that the finally clause of the main()
try statement was executed. The processUserInput()
method, upon encountering a space character returned by getChar(),
throws the BlankException, which is caught by the main()
method. The finally clause always executes no matter
whether processUserInput() throws an exception or not.
Enter a at the program prompt, and the following output
appears:
Enter a character: a
A vowel exception occurred.
This is the finally clause.
Enter a character:
Here the program notifies you that a vowel exception has occurred.
The processing of the vowel exception is similar to the blank
exception. See if you can trace the program flow of control involved
in this processing.
Enter j, and the following is displayed:
Enter a character: j
This is the finally clause.
Enter a character:
No exceptions are thrown for the j character, but the
finally clause is executed. The finally clause
is always executed, no matter what happens during the execution
of a try statement. Go ahead and type x to exit
the ExceptionTest program. The program displays the following
output:
Enter a character: x
An exit exception occurred.
This is the finally clause.
The exception then returns you to the DOS prompt.
The output acknowledges the fact that the exit exception was thrown
by processUserInput() and caught by main().
The ExceptionTest program provides a simple example of
exception throwing and catching. The example in the following
section illustrates more complex exception handling.
Nested
Exception Handling
try statements can be nested to provide multiple levels
of exception-handling capabilities. This is accomplished by enclosing
a method or block of statements containing a lower-level try
statement within the try block of a higher-level try
statement. When an exception is thrown in the try block
of the lower-level try statement that cannot be caught,
it continues to be thrown until it reaches the higher-level try
statement. The higher-level try statement can then determine
whether the exception can be caught and processed by any of its
catch clauses. Any number of try statements
can be nested. Figure 7.1 illustrates
this concept.
Figure 7.1 : Nested exception handling:
An exception generated within the lower-level try statement
is first passed to its catch clause(s). If it is not
handled, it is propagated to the higher-level catch clause(s).
If it is not handled by the higher-level catch clause(s),
it is propagated further up the exception-handling hierarchy.
Rethrowing
Exceptions
When an exception is caught in the catch clause of a
try statement, that exception may be rethrown. When an
exception is rethrown, it can then be caught and processed by
the catch clause of a higher-level try statement.
A higher-level catch clause can then perform any secondary
clean-up processing.
The following example illustrates nested exception handling and
the rethrowing of exceptions. Enter the source code shown in Listing
7.2 into NestedExceptionTest.java and compile it.
Listing 7.2. The source code of the NestedExceptionTest
program.
import jdg.ch05.KeyboardInput;
import java.lang.System;
import java.lang.Exception;
import java.io.IOException;
class VowelException extends Exception {}
class BlankException extends Exception {}
class ExitException extends Exception {}
class NestedExceptionTest {
static KeyboardInput kbd = new KeyboardInput(System.in);
public static void main(String args[]) {
do{} while(!exitExceptionTest());
}
static boolean exitExceptionTest() {
try {
vowelExceptionTest();
System.out.println("Acceptable.\n");
}catch (ExitException x) {
try {
System.out.print("Exit (y/n): ");
System.out.flush();
char ch = Character.toUpperCase(kbd.getChar());
System.out.println();
if(ch=='Y') return true;
else return false;
}catch (IOException iox) {
return false;
}
}catch (Exception x) {
System.out.println("Not acceptable. Try
again.\n");
}
return false;
}
static void vowelExceptionTest() throws VowelException,
ExitException {
try {
blankExceptionTest();
}catch (BlankException x) {
System.out.println("Next time type a printable
character.\n");
vowelExceptionTest();
}catch (VowelException x) {
System.out.println("You typed a vowel.");
throw x;
}
}
static void blankExceptionTest() throws VowelException,
BlankException,
ExitException {
try {
processUserInput();
}catch (BlankException x) {
System.out.println("You entered a blank
line. Try again.");
throw x;
}
}
static void processUserInput() throws VowelException, BlankException,
ExitException {
System.out.print("Enter a character: ");
System.out.flush();
char ch;
try {
ch=Character.toUpperCase(kbd.getChar());
} catch (IOException x) {
System.out.println("An IOException occurred.");
return;
}
switch(ch) {
case 'A':
case 'E':
case 'I':
case 'O':
case 'U':
throw new VowelException();
case ' ':
throw new BlankException();
case 'X':
throw new ExitException();
}
}
}
This example is based on the previous example, but it is significantly
more complex. The exception handling has been removed from the
main() method and distributed among three nested exception
handlers: exitExceptionTest(), vowelExceptionTest(),
and blankExceptionTest().
The main() method invokes exitExceptionTest()
at each iteration of the do statement. The exitExceptionTest()
method returns a boolean value indicating whether the
do statement should terminate. The normal (non-exception)
processing performed by exitExceptionTest() consists
of the following three statements:
vowelExceptionTest();
System.out.println("Acceptable.\n");
return false;
All other exitExceptionTest() processing is error handling.
The first two statements are executed within the try
statement and are subject to error processing. The last statement
executes upon completion of the try statement, assuming
that no transfer of control occurs as the result of exception
handling involving the catch clauses.
The try statement has two catch clauses. The
first catch clause handles ExitException processing.
It consists of a try statement with a catch
clause that catches the pesky IOException. The try
statement contains a block of statements that asks the user for
a confirmation before exiting the program.
The second catch clause catches all other objects that
are instances of a subclass of Exception. It displays
a short warning to the user.
The vowelExceptionTest() method consists of a try
statement that invokes blankExceptionTest(). The rest
of the processing performed by vowelExceptionTest() is
exception handling. It catches two exceptions: BlankException
and VowelException. It handles BlankException
by warning the user to type a printable character and reinvoking
itself. It handles the vowel exception by notifying the user that
he typed a vowel and rethrowing the VowelException. By
rethrowing the exception, it allows exitExceptionTest()
to perform additional exception handling. Because vowelExceptionTest()
rethrows the VowelException, it must declare it in its
throws clause. It also must declare the ExitException
because the ExitException is declared in the throws
clause of blankExceptionTest() and is not caught by vowelExceptionTest().
The blankExceptionTest() simply invokes processUserInput()
as part of its normal processing. It handles one exception thrown
by processUserInput(): the BlankException. It
handles the BlankException by informing the user that
he typed a blank line and that he should try again. It then rethrows
the BlankException so that it can be rehandled by vowelExceptionTest().
The processUserInput() method performs in the same manner
as described in the previous example.
Analysis of NestedExceptionTest
If NestedExceptionTest seems overly complex, don't worry-it
was meant to be. Its purpose is to give you a good understanding
of the complex ways that exception handlers can be nested and
how exceptions are rethrown. Go ahead and run NestedExceptionTest
using the command java NestedExceptionTest:
C:\java\jdg\ch07>java NestedExceptionTest
Enter a character:
When you run NestedExceptionTest, the main()
method invokes exitExceptionTest(), which invokes vowelExceptionTest(),
which invokes blankExceptionTest(), which invokes processUserInput().
The processUserInput() method prompts you to enter a
character and then does one of four things, depending on the character
you enter. If you enter a vowel, it throws a VowelException.
If you enter a nonprintable character or blank line, it throws
a BlankException. If you enter x or X,
it throws an ExitException. Finally, as a default, if
you enter any other printable character, it will simply return
control to the blankExceptionTest() method.
Let's work through all four scenarios. First, enter j
to cause normal program processing to occur:
Enter a character: j
Acceptable.
Enter a character:
The processUserInput() method returns control to blankExceptionTest(),
which returns control to vowelExceptionTest(), which
returns control to exitExceptionTest(). The exitExceptionTest()
method informs the user that he has entered an acceptable character
and returns control to the main() method, which starts
another character-input cycle.
Now let's go through the case when the user enters a blank line.
Just enter a blank line at the prompt:
Enter a character:
You entered a blank line. Try again.
Next time type a printable character.
Enter a character:
A blank line causes processUserInput() to throw the BlankException.
This exception is caught by blankExceptionTest(). The
blankExceptionTest() method handles the exception by
informing the user that he has entered a blank line and that he
should try again. It then rethrows the exception, and the rethrown
exception is caught by vowelExceptionTest(). The vowelExceptionTest()
method handles the rethrown BlankException by telling
the user that he should enter a printable character the next time
he is prompted. It then invokes itself, starting the character-input
cycle all over.
Let's go through the case when the user enters a vowel. Enter
a at the prompt:
Enter a character: a
You typed a vowel.
Not acceptable. Try again.
Enter a character:
When a vowel is entered, processUserInput() throws the
VowelException. The VowelException is not caught
by blankExceptionTest() and continues to be thrown until
it is caught by vowelExceptionTest(). The vowelExceptionTest()
method handles the exception by informing the user that he has
typed a vowel and then rethrows the exception. The rethrown exception
is caught by exitExceptionTest(), and exitExceptionTest()
handles it by informing the user that his input is not acceptable.
Execution control returns to the main() method, which
starts another character-input cycle.
This last case examines what happens when the user types x
at the character prompt. Enter x to see what happens:
Enter a character: x
Exit (y/n):
The processUserInput() method throws an ExitException,
which is not caught by blankExceptionTest() nor vowelExceptionTest().
The exception continues to be thrown until it is caught by exitExceptionTest().
The exitExceptionTest() method prompts the user to enter
a y or Y to confirm the fact that he wants to
exit the program. If the user enters any other character, control
returns to the main() method and another character-input
cycle is initiated. If the user enters a y or Y,
control is returned to the main() method, but the true
return value is passed, causing the do statement and
the program to terminate.
Go ahead and type y to terminate the NestedExceptionTest
program.
Summary
In this chapter you have learned how to use Java exceptions to
implement error-handling capabilities in your Java programs. You
have learned how to throw exceptions in response to error conditions
and how to catch exceptions to perform error processing. You have
also learned how to implement nested exception handling and how
to rethrow exceptions. In Chapter 8, "Multithreading,"
you will learn how to use Java's multithreading capabilities to
write programs that use multiple threads of execution.
Contact
reference@developer.com with questions or comments.
Copyright 1998
EarthWeb Inc., All rights reserved.
PLEASE READ THE ACCEPTABLE USAGE STATEMENT.
Copyright 1998 Macmillan Computer Publishing. All rights reserved.
Wyszukiwarka
Podobne podstrony:
Cisco2 ch7 VocabCH7 (3)ch7ch7 pl p1ch7 (11)ch7 (14)M3 Ch7CH7DK2192 CH7ch7ch7 (10)CH7ch7 (13)Cisco2 ch7 Focusch7 (5)ch7ch7 (12)ch7więcej podobnych podstron