AhmadLang / Java, How To Program, 2004
.pdf
where variable width is the width specified by the user in the line-width text field. If the user chooses a dashed line, then create the Stroke with the expression
new BasicStroke( width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 10, dashes, 0 )
where width again is the width in the line-width field, and dashes is an array with one element whose value is the length specified in the dash-length field. The Panel and Stroke objects should be passed to the shape object's constructor when the shape is created in DrawPanel.
[Page 638]
Chapter 13. Exception Handling
It is common sense to take a method and try it. If it fails, admit it frankly and try another. But above all, try something.
Franklin Delano Roosevelt
O! throw away the worser part of it,
And live the purer with the other half.
William Shakespeare
If they're running and they don't look where they're going I have to come out from somewhere and catch them.
Jerome David Salinger
O infinite virtue! com'st thou smiling from the world's great snare uncaught?
William Shakespeare
OBJECTIVES
In this chapter you will learn:
How exception and error handling works.
To use try, tHRow and catch to detect, indicate and handle exceptions, respectively.
To use the finally block to release resources.
How stack unwinding enables exceptions not caught in one scope to be caught in another scope.
How stack traces help in debugging.
How exceptions are arranged in an exception class hierarchy.
To declare new exception classes.
To create chained exceptions that maintain complete stack trace information.
[Page 639]
Outline
13.1 Introduction
13.2 Exception-Handling Overview
13.3 Example: Divide By Zero Without Exception Handling
13.4 Example: Handling ArithmeticExceptions and InputMismatchExceptions
13.5 When to Use Exception Handling
13.6 Java Exception Hierarchy
13.7 finally block
13.8 Stack Unwinding
13.9 printStackTrace, getStackTrace and getMessage
13.10 Chained Exceptions
13.11 Declaring New Exception Types
13.12 Preconditions and Postconditions
13.13 Assertions
13.14 Wrap-Up
Summary
Terminology
Self-Review Exercises
Answers to Self-Review Exercises
Exercises
[Page 639 (continued)]
13.1. Introduction
In this chapter, we introduce exception handling. An exception is an indication of a problem that occurs during a program's execution. The name "exception" implies that the problem occurs infrequentlyif the "rule" is that a statement normally executes correctly, then the "exception to the rule" is that a problem occurs. Exception handling enables programmers to create applications that can resolve (or handle) exceptions. In many cases, handling an exception allows a program to continue executing as if no problem had been encountered. A more severe problem could prevent a program from continuing normal execution, instead requiring it to notify the user of the problem before terminating in a controlled manner. The features presented in this chapter enable programmers to write robust and fault-tolerant programs (i.e., programs that are able to deal with problems that may arise and continue executing). The style and details of Java exception handling are based in part on the Andrew
Koenig's and Bjarne Stroustrup's paper, "Exception Handling for C++ (revised)."[1]
[1] Koenig, A., and B. Stroustrup. "Exception Handling for C++ (revised)," Proceedings of the Usenix C++ Conference, pp. 149176, San Francisco, April 1990.
Error-Prevention Tip 13.1
Exception handling helps improve a program's fault tolerance.
You have already been briefly introduced to exceptions in earlier chapters. In Chapter 7 you learned that an ArrayIndexOutOfBoundsException occurs when an attempt is made to access an element past the end of an array. Such a problem may occur if there is an "off by one" error in a for statement that manipulates an array. In Chapter 10, we introduced the ClassCastException, which occurs when an attempt is made to cast an object that does not have an is-a relationship with the type specified in the cast operator. Chapter 11 briefly mentioned the NullPointerException, which occurs whenever a null reference is used where an object is expected (for example, when an attempt is made to attach a GUI component to a Container, but the GUI component has not yet been created). You have also used class Scanner throughout this text, which as you will see in this chapter also may cause exceptions.
[Page 640]
The chapter begins with an overview of exception-handling concepts, then demonstrates basic exceptionhandling techniques. We show these techniques in action by handling an exception that occurs when a method attempts to divide an integer by zero. Next, we introduce several classes at the top of Java's class hierarchy for exception handling. As you will see, only classes that extend Throwable (package java.lang) directly or indirectly can be used with exception handling. We then discuss the chained exception feature that was introduced in J2SE 1.4. This feature allows programmers to wrap information about an exception that occurred in another exception object to provide more detailed information about a problem in a program. Next, we discuss additional exception-handling issues, such as how to handle exceptions that occur in a constructor. We introduce preconditions and postconditions, which users of the classes you create understand conditions that must be true when your methods are called and when those methods return. Finally, we present assertions, which programmers use at development time to help debug their code.
[Page 640 (continued)]
13.2. Exception-Handling Overview
Programs frequently test conditions to determine how program execution should proceed. Consider the following pseudocode:
Perform a task
If the preceding task did not execute correctly
Perform error processing
Perform next task
If the preceding task did not execute correctly
Perform error processing
...
In this pseudocode, we begin by performing a task; then we test whether that task executed correctly. If not, we perform error processing. Otherwise, we continue with the next task. Although this form of error handling works, intermixing program logic with error-handling logic can make programs difficult to read, modify, maintain and debugespecially in large applications.
Performance Tip 13.1
If the potential problems occur infrequently, intermixing program and error-handling logic can degrade a program's performance, because the program must perform (potentially frequent) tests to determine whether the task executed correctly and the next task can be performed.
Exception handling enables programmers to remove error-handling code from the "main line" of the program's execution, improving program clarity and enhancing modifiability. Programmers can decide to handle any exceptions they chooseall exceptions, all exceptions of a certain type or all exceptions of a group of related types (i.e., exception types that are related through an inheritance hierarchy). Such flexibility reduces the likelihood that errors will be overlooked, thus making programs more robust.
With programming languages that do not support exception handling, programmers often delay writing error-processing code or sometimes forget to include it. This results in less robust software products. Java enables programmers to deal with exception handling easily from the inception of a project.
[Page 641]
[Page 641 (continued)]
13.3. Example: Divide By Zero Without Exception Handling
First we demonstrate what happens when errors arise in an application that does not use exception handling. Figure 13.1 prompts the user for two integers and passes them to method quotient, which calculates the quotient and returns an int result. In this example, we will see that exceptions are thrown (i.e., the exception occurs) when a method detects a problem and is unable to handle it.
The first of the three sample executions in Fig. 13.1 shows a successful division. In the second sample execution, the user enters the value 0 as the denominator. Notice that several lines of information are displayed in response to this invalid input. This information is known as the stack trace, which includes the name of the exception (java.lang.ArithmeticException) in a descriptive message that indicates the problem that occurred and the complete method-call stack (i.e., the call chain) at the time the exception occurred. The stack trace includes the path of execution that led to the exception method by method. This information helps in debugging a program. The first line specifies that an ArithmeticException has occurred. The text after the name of the exception, "/ by zero", indicates that this exception occurred as a result of an attempt to divide by zero. Java does not allow division by zero in integer arithmetic. [Note: Java does allow division by zero with floating-point values. Such a calculation results in the value infinity, which is represented in Java as a floating-point value (but actually displays as the string Infinity).] When division by zero in integer arithmetic occurs, Java throws an ArithmeticException. ArithmeticExceptions can arise from a number of different problems in arithmetic, so the extra data ("/ by zero") gives us more information about this specific exception.
Starting from the last line of the stack trace, we see that the exception was detected in line 22 of method main. Each line of the stack trace contains the class name and method (DivideByZeroNoExceptionHandling.main) followed by the file name and line number
(DivideByZeroNoExceptionHandling.java:22). Moving up the stack trace, we see that the exception occurs in line 10, in method quotient. The top row of the call chain indicates the throw pointthe initial point at which the exception occurs. The throw point of this exception is in line 10 of method quotient.
In the third execution, the user enters the string "hello" as the denominator. Notice again that a stack trace is displayed. This informs us that an InputMismatchException has occurred (package java.util). Our prior examples that read numeric values from the user assumed that the user would input a proper integer value. However, users sometimes make mistakes and input noninteger values. An InputMismatchException occurs when Scanner method nextInt receives a string that does not represent a valid integer. Starting from the end of the stack trace, we see that the exception was detected in line 20 of method main. Moving up the stack trace, we see that the exception occurs in method nextInt. Notice that in place of the file name and line number, we are provided with the text Unknown Source. This means that the JVM does not have access to the source code for where the exception occurred.
Notice that in the sample executions of Fig. 13.1 when exceptions occur and stack traces are displayed, the program also exits. This does not always occur in Javasometimes a program may continue even though an exception has occurred and a stack trace has been printed. In such cases, the application may produce unexpected results. The next section demonstrates how to handle these exceptions and keep the program running successfully.
[Page 643]
Figure 13.1. Integer division without exception handling.
(This item is displayed on page 642 in the print version)
1 // Fig. 13.1: DivideByZeroNoExceptionHandling.java 2 // An application that attempts to divide by zero. 3 import java.util.Scanner;
4
5public class DivideByZeroNoExceptionHandling
6{
7 |
// demonstrates throwing an |
exception when |
a divide-by-zero occurs |
8 |
public static int quotient( |
int numerator, |
int denominator ) |
9 |
{ |
|
|
[Page 643 (continued)]
13.4. Example: Handling ArithmeticExceptions and
InputMismatchExceptions
The application in Fig. 13.2, which is based on Fig. 13.1, uses exception handling to process any ArithmeticExceptions and InputMistmatchExceptions that might arise. The application still prompts the user for two integers and passes them to method quotient, which calculates the quotient and returns an int result. This version of the application uses exception handling so that if the user makes a mistake, the program catches and handles (i.e., deals with) the exceptionin this case, allowing the user to try to enter the input again.
Figure 13.2. Handling ArithmeticExceptions and
InputMismatchExceptions.
(This item is displayed on pages 643 - 644 in the print version)
1 // Fig. 13.2: DivideByZeroWithExceptionHandling.java
2 // An exception-handling example that checks for divide-by-zero.
3import java.util.InputMismatchException;
4import java.util.Scanner;
5
6public class DivideByZeroWithExceptionHandling
7{
8// demonstrates throwing an exception when a divide-by-zero occurs
9public static int quotient( int numerator, int denominator )
10throws ArithmeticException
11{
12return numerator / denominator; // possible division by zero
13} // end method quotient
14
15public static void main( String args[] )
16{
17Scanner scanner = new Scanner( System.in ); // scanner for input
18boolean continueLoop = true; // determines if more input is needed
20do
21{
22try // read two numbers and calculate quotient
23{
24 |
System.out.print( "Please enter an |
integer |
numerator: " ); |
|||
25 |
int numerator = scanner.nextInt(); |
|
|
|
||
26 |
System.out.print( "Please enter an |
integer |
denominator: " ); |
|||
27 |
int denominator |
= scanner.nextInt(); |
|
|
||
28 |
|
|
|
|
|
|
29 |
int result = quotient( numerator, denominator ); |
|||||
30 |
System.out.printf( "\nResult: %d / |
%d = %d\n", |
numerator, |
|||
31 |
denominator, |
result |
); |
|
|
|
32 |
continueLoop = |
false; |
// input successful; |
end |
looping |
|
33} // end try
34catch ( InputMismatchException inputMismatchException )
35{
36 |
System.err.printf( |
"\nException: %s\n", |
|
|
|
37 |
inputMismatchException ); |
|
|
|
|
38 |
scanner.nextLine(); // discard input so |
user can |
try |
again |
|
39 |
System.out.println( |
|
|
|
|
40 |
"You must enter |
integers. Please try |
again.\n" |
); |
|
41} // end catch
42catch ( ArithmeticException arithmeticException )
43{
44 |
System.err.printf( "\nException: %s\n", arithmeticException ); |
45 |
System.out.println( |
46 |
"Zero is an invalid denominator. Please try again.\n" ); |
47} // end catch
48} while ( continueLoop ); // end do...while
Lines 2233 contain a try block, which encloses the code that might tHRow an exception and the code that should not execute if an exception occurs (i.e., if an exception occurs, the remaining code in the TRy block will be skipped). A TRy block consists of the keyword try followed by a block of code enclosed in curly braces ({}). [Note: The term "try block" sometimes refers only to the block of code that follows the TRy keyword (not including the try keyword itself). For simplicity, we use the term "try block" to refer to the block of code that follows the TRy keyword, as well as the try keyword.] The statements that read the integers from the keyboard (lines 25 and 27) each use method nextInt to read an int value. Method nextInt tHRows an InputMismatchException if the value read in is not a valid integer.
The division that can cause an ArithmeticException is not performed in the TRy block. Rather, the call to method quotient (line 29) invokes the code that attempts the division (line 12); the JVM throws an ArithmeticException object when the denominator is zero.
Software Engineering Observation 13.1
Exceptions may surface through explicitly mentioned code in a TRy block, through calls to other methods, through deeply nested method calls initiated by code in a try block or from the Java Virtual Machine as it executes Java bytecodes.
Catching Exceptions
The try block in this example is followed by two catch blocksone that handles an InputMismatchException (lines 3441) and one that handles an ArithmeticException (lines 4247). A catch block (also called a catch clause or exception handler) catches (i.e., receives) and handles an exception. A catch block begins with the keyword catch and is followed by a parameter in parentheses (called the exception parameter, discussed shortly) and a block of code enclosed in curly braces. [Note: The term "catch clause" is sometimes used to refer to the keyword catch followed by a block of code, where the term "catch block" refers to only the block of code following the catch keyword, but not including it. For simplicity, we use the term "catch block" to refer to the block of code following the catch keyword, as well as the keyword itself.]
At least one catch block or a finally block (discussed in Section 13.7) must immediately follow the try block. Each catch block specifies in parentheses an exception parameter that identifies the exception type the handler can process. When an exception occurs in a try block, the catch block that executes is the one whose type matches the type of the exception that occurred (i.e., the type in the catch block matches the thrown exception type exactly or is a superclass of it). The exception parameter's name enables the catch block to interact with a caught exception objecte.g., to implicitly invoke the caught exception's toString method (as in lines 37 and 44), which displays basic information about the exception. Line 38 of the first catch block calls Scanner method nextLine. Because an InputMismatchException occurred, the call to method nextInt never successfully read in the user's dataso we read that input with a call to method nextLine. We do not do anything with the input at this point, because we know that it is invalid. Each catch block displays an error message and asks the user to try again. After either catch block terminates, the user is prompted for input. We will soon take a deeper look at how this flow of control works in exception handling.
[Page 646]
Common Programming Error 13.1
It is a syntax error to place code between a TRy block and its corresponding catch blocks.
