Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

AhmadLang / Java, How To Program, 2004

.pdf
Скачиваний:
630
Добавлен:
31.05.2015
Размер:
51.82 Mб
Скачать

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

{

 

 

10return numerator / denominator; // possible division by zero

11} // end method quotient

12

13public static void main( String args[] )

14{

15Scanner scanner = new Scanner( System.in ); // scanner for input

17System.out.print( "Please enter an integer numerator: " );

18int numerator = scanner.nextInt();

19System.out.print( "Please enter an integer denominator: " );

20int denominator = scanner.nextInt();

22int result = quotient( numerator, denominator );

23System.out.printf(

24"\nResult: %d / %d = %d\n", numerator, denominator, result );

25} // end main

26} // end class DivideByZeroNoExceptionHandling

Please

enter

an

integer

numerator: 100

Please

enter

an

integer

denominator: 7

Result:

100

/ 7

= 14

 

 

 

 

 

 

Please enter an integer numerator: 100 Please enter an integer denominator: 0

Exception in thread "main" java.lang.ArithmeticException: / by zero

at DivideByZeroNoExceptionHandling.quotient(DivideByZeroNoExceptionHandling.java:10)

at DivideByZeroNoExceptionHandling.main(DivideByZeroNoExceptionHandling. java:22)

Please enter an integer numerator: 100 Please enter an integer denominator: hello

Exception in thread "main" java.util.InputMismatchException at java.util.Scanner.throwFor(Unknown Source)

at java.util.Scanner.next(Unknown Source)

at java.util.Scanner.nextInt(Unknown Source) at java.util.Scanner.nextInt(Unknown Source)

at DivideByZeroNoExceptionHandling.main(DivideByZeroNoExceptionHandling. java:20)

In Fig. 13.1 both types of exceptions were detected in method main. In the next example, we will see how to handle these exceptions to enable the program to run to normal completion.

[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

49} // end main

50} // end class DivideByZeroWithExceptionHandling

Please

enter

an

integer

numerator: 100

Please

enter

an

integer

denominator: 7

Result:

100 /

7

= 14

 

 

 

 

 

 

Please enter an integer numerator: 100

Please enter an integer denominator: 0

Exception: java.lang.ArithmeticException: / by zero

Zero is an invalid denominator. Please try again.

Please

enter

an

integer

numerator: 100

Please

enter

an

integer

denominator: 7

Result:

100 /

7

= 14

 

Please enter an integer numerator: 100

Please enter an integer denominator: hello

Exception: java.util.InputMismatchException

You must enter integers. Please try again.

Please

enter

an

integer

numerator: 100

Please

enter

an

integer

denominator: 7

Result:

100 /

7

= 14

 

The first sample execution in Fig. 13.2 shows a successful execution that does not encounter any problems. In the second execution, the user enters a zero denominator and an ArithmeticException exception occurs. In the third execution, the user enters the string "hello" as the denominator, and an InputMismatchException occurs. For both exceptions, the user is informed of their mistake and asked to try again, then is prompted for two new integers. In each sample execution, the program runs successfully to completion.

[Page 645]

Class InputMismatchException is imported in line 3. Class ArithmeticException does not need to be imported because it is located in package java.lang. Method main (lines 1549) creates a Scanner object at line 17. Line 18 creates the boolean variable continueLoop, which is true if the user has not yet entered valid input. Lines 2048 contain a do...while statement that repeatedly asks users for input until a valid input is received.

Enclosing Code in a try Block

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.