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

AhmadLang / Java, How To Program, 2004

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

Before we discuss the flow of control for this example, we would like to point out the use of the

System.err to output data (lines 15, 3132, 40, 56 and 6061). By default, System.err.println, like System.out.println, displays data to the command prompt.

Both System.out and System.err are streamsa sequence of bytes. While System.out (known as the standard output stream) is used to display a program's output, System.err (known as the standard error stream) is used to display a program's errors. Output from these streams can be redirected (i.e., sent somewhere other than the command prompt such as a file). Using two different streams enables the programmer to easily separate error messages from other output. For instance, data output from System.err could be sent to a log file, while data output from System.out can be displayed on the screen. For simplicity, this chapter will not redirect output from System.err, but will display such messages to the command prompt. You will learn more about streams in Chapter 14, Files and Streams.

Throwing Exceptions Using the tHRow Statement

Method main (Fig. 13.5) begins executing, enters its try block and immediately calls method tHRowException (line 11). Method throwException tHRows an Exception (line 27), catches it (line 29) and rethrows it (line 33). The statement at line 27 is known as a throw statement. The tHRow statement is executed to indicate that an exception has occurred. So far, you have only caught exceptions thrown by called methods. Programmers can throw exceptions by using the tHRow statement. Just as with exceptions thrown by the Java API's methods, this indicates to client applications that an error has occurred. A tHRow statement specifies an object to be thrown. The operand of a throw can be of any class derived from class Throwable.

Software Engineering Observation 13.9

When toString is invoked on any THRowable object, its resulting string includes the descriptive string that was supplied to the constructor, or simply the class name if no string was supplied.

Software Engineering Observation 13.10

An object can be thrown without containing information about the problem that occurred. In this case, simple knowledge that an exception of a particular type occurred may provide sufficient information for the handler to process the problem correctly.

Software Engineering Observation 13.11

Exceptions can be thrown from constructors. When an error is detected in a constructor, an exception should be thrown rather than creating an improperly formed object.

[Page 656]

Rethrowing Exceptions

Line 33 of Fig. 13.5 rethrows the exception. Exceptions are rethrown when a catch block, upon receiving an exception, decides either that it cannot process that exception or that it can only partially process it. Rethrowing an exception defers the exception handling (or perhaps a portion of it) to another catch block associated with an outer TRy statement. An exception is rethrown by using the throw keyword, followed by a reference to the exception object that was just caught. Note that exceptions cannot be rethrown from a finally block, as the exception parameter from the catch block has expired.

When a rethrow occurs, the next enclosing TRy block detects the rethrown exception, and that try block's catch blocks attempt to handle the exception. In this case, the next enclosing TRy block is found at lines 912 in method main. Before the rethrown exception is handled, however, the finally block (lines 3841) executes. Then method main detects the rethrown exception in the try block and handles it in the catch block (lines 1316).

Next, main calls method doesNotThrowException (line 18). No exception is thrown in doesNotThrowException's TRy block (lines 5053), so the program skips the catch block (lines 5457), but the finally block (lines 5862) nevertheless executes. Control proceeds to the statement after the finally block (line 64). Then control returns to main and the program terminates.

Common Programming Error 13.8

If an exception has not been caught when control enters a finally block and the finally block throws an exception that is not caught in the finally block, the first exception will be lost and the exception from the finally block will be returned to the calling method.

Error-Prevention Tip 13.9

Avoid placing code that can throw an exception in a finally block. If such code is required, enclose the code in a try...catch within the finally block.

Common Programming Error 13.9

Assuming that an exception thrown from a catch block will be processed by that catch block or any other catch block associated with the same TRy statement can lead to logic errors.

Good Programming Practice 13.2

Java's exception-handling mechanism is intended to remove errorprocessing code from the main line of a program's code to improve program clarity. Do not place TRy...catch...finally around every statement that may throw an exception. This makes programs difficult to read. Rather, place one try block around a significant portion of

your code, follow that TRy block with catch blocks that handle each possible exception and follow the catch blocks with a single finally block (if one is required).

[Page 656 (continued)]

13.8. Stack Unwinding

When an exception is thrown but not caught in a particular scope, the method-call stack is "unwound," and an attempt is made to catch the exception in the next outer try block. This process is called stack unwinding. Unwinding the method-call stack means that the method in which the exception was not caught terminates, all local variables in that method go out of scope and control returns to the statement that originally invoked that method. If a TRy block encloses that statement, an attempt is made to catch the exception. If a try block does not enclose that statement, stack unwinding occurs again. If no catch block ever catches this exception and the exception is checked (as in the following example), compiling the program will result in an error. The program of Fig. 13.6 demonstrates stack unwinding.

[Page 657]

Figure 13.6. Stack unwinding.

1// Fig. 13.6: UsingExceptions.java

2// Demonstration of stack unwinding.

4public class UsingExceptions

5

{

6

public static void main( String args[] )

7{

8try // call throwException to demonstrate stack unwinding

9{

10throwException();

11} // end try

12catch ( Exception exception ) // exception thrown in throwException

13{

14System.err.println( "Exception handled in main" );

15} // end catch

16} // end main

17

18// throwException throws exception that is not caught in this method

19public static void throwException() throws Exception

20{

21try // throw an exception and catch it in main

22{

23System.out.println( "Method throwException" );

24throw new Exception(); // generate exception

25} // end try

26catch ( RuntimeException runtimeException ) // catch incorrect type

27{

28System.err.println(

29

"Exception handled in method throwException" );

30} // end catch

31finally // finally block always executes

32{

33System.err.println( "Finally is always executed" );

34} // end finally

35} // end method throwException

36} // end class UsingExceptions

Method throwException Finally is always executed Exception handled in main

When method main executes, line 10 in the TRy block calls method throwException (lines 1935). In the try block of method throwException (lines 2125), line 24 throws an Exception. This terminates the try block immediately, and control skips the catch block at line 26, because the type being caught (RuntimeException) is not an exact match with the thrown type (Exception) and is not a superclass of it. Method throwException terminates (but not until its finally block executes) and returns control to line 10the point from which it was called in the program. Line 10 is in an enclosing try block. The exception has not yet been handled, so the try block terminates and an attempt is made to catch the exception at line 12. The type being caught (Exception) does match the thrown type. Consequently, the catch block processes the exception, and the program terminates at the end of main. If there were no matching catch blocks, a compilation error would occur. Remember that this is not always the casefor unchecked exceptions, the application will compile, but run with unexpected results.

[Page 658]

[Page 658 (continued)]

13.9. printStackTrace, getStackTrace and getMessage

Recall from Section 13.6 that exceptions derive from class Throwable. Class Throwable offers a

printStackTrace method that outputs to the standard error stream the stack trace (discussed in Section 13.3). Often, this is helpful in testing and debugging. Class Throwable also provides a

getStackTrace method that retrieves stack-trace information that might be printed by printStackTrace. Class Throwable's getMessage method returns the descriptive string stored in an exception. The example in this section demonstrates these three methods.

Error-Prevention Tip 13.10

An exception that is not caught in an application causes Java's default exception handler to run. This displays the name of the exception, a descriptive message that indicates the problem that occurred and a complete execution stack trace.

Error-Prevention Tip 13.11

Throwable method toString (inherited by all THRowable subclasses) returns a string containing the name of the exception's class and a descriptive message.

Figure 13.7 demonstrates getMessage, printStackTrace and getStackTrace. If we wanted to output the stack-trace information to streams other than the standard error stream, we could use the information returned from getStackTrace, and output this data to another stream. You will learn about sending data to other streams in Chapter 14, Files and Streams.

Figure 13.7. Throwable methods getMessage, getStackTrace and

printStackTrace.

(This item is displayed on pages 659 - 660 in the print version)

1

//

Fig. 13.7: UsingExceptions.java

2

//

Demonstrating getMessage and printStackTrace from class Exception.

3

 

 

4public class UsingExceptions

5{

6public static void main( String args[] )

7{

8try

9{

10method1(); // call method1

11} // end try

12catch ( Exception exception ) // catch exception thrown in method1

13{

14System.err.printf( "%s\n\n", exception.getMessage() );

15exception.printStackTrace(); // print exception stack trace

16

17// obtain the stack-trace information

18StackTraceElement[] traceElements = exception.getStackTrace();

20System.out.println( "\nStack trace from getStackTrace:" );

21System.out.println( "Class\t\tFile\t\t\tLine\tMethod" );

23// loop through traceElements to get exception description

24for ( StackTraceElement element : traceElements )

25{

26

System.out.printf( "%s\t", element.getClassName() );

27

System.out.printf( "%s\t", element.getFileName() );

 

28

System.out.printf(

"%s\t",

element.getLineNumber()

);

29

System.out.printf(

"%s\n",

element.getMethodName()

);

30} // end for

31} // end catch

32} // end main

33

34// call method2; throw exceptions back to main

35public static void method1() throws Exception

36{

37method2();

38} // end method method1

39

40// call method3; throw exceptions back to method1

41public static void method2() throws Exception

42{

43method3();

44} // end method method2

45

46// throw Exception back to method2

47public static void method3() throws Exception

48{

49throw new Exception( "Exception thrown in method3" );

50} // end method method3

51} // end class UsingExceptions

Exception thrown in method3

java.lang.Exception: Exception thrown in method3

at UsingExceptions.method3(UsingExceptions.java:49) at UsingExceptions.method2(UsingExceptions.java:43) at UsingExceptions.method1(UsingExceptions.java:37) at UsingExceptions.main(UsingExceptions.java:10)

Stack trace from

getStackTrace:

 

 

Class

File

Line

Method

UsingExceptions

UsingExceptions.java

49

method3

UsingExceptions

UsingExceptions.java

43

method2

UsingExceptions

UsingExceptions.java

37

method1

UsingExceptions

UsingExceptions.java

10

main

In main, the TRy block (lines 811) calls method1 (declared at lines 3538). Next, method1 calls method2 (declared at lines 4144), which in turn calls method3 (declared at lines 4750). Line 49 of method3 throws an Exception objectthis is the throw point. Because throw statement at line 49 is not enclosed in a TRy block, stack unwinding occursmethod3 terminates at line 49, then returns control to the statement in method2 that invoked method3 (i.e., line 43). Because no try block encloses line 43, stack unwinding occurs againmethod2 terminates at line 43 and returns control to the statement in method1 that invoked method2 (i.e., line 37). Because no TRy block encloses line 37, stack unwinding occurs one more

timemethod1 terminates at line 37 and returns control to the statement in main that invoked method1 (i.e., line 10). The try block at lines 811 encloses this statement. The exception has not been handled, so the TRy block terminates and the first matching catch block (lines 1231) catches and processes the exception.

[Page 660]

Line 14 invokes the exception's getMessage method to get the exception description. Line 15 invokes the exception's printStackTrace method to output the stack trace that indicates where the exception occurred. Line 18 invokes the exception's getStackTrace method to obtain the stack-trace information as an array of StackTraceElement objects. Lines 2430 get each StackTraceElement in the array and invoke its methods getClassName, getFileName, getLineNumber and

getMethodName to get the class name, file name, line number and method name, respectively, for that StackTraceElement. Each Stack-TraceElement represents one method call on the method-call stack.

The output in Fig. 13.7 shows that the stack-trace information printed by printStackTrace follows the pattern: className.methodName(fileName:lineNumber), where className, methodName and fileName indicate the names of the class, method and file in which the exception occurred, respectively, and the lineNumber indicates where in the file the exception occurred. You saw this in the output for Fig. 13.1. Method getStackTrace enables custom processing of the exception information. Compare the output of printStackTrace with the output created from the StackTraceElements to see that both contain the same stack-trace information.

Software Engineering Observation 13.12

Never ignore an exception you catch. At least use printStackTrace to output an error message. This will inform users that a problem exists, so that they can take appropriate actions.

[Page 660 (continued)]

13.10. Chained Exceptions

Sometimes a catch block catches one exception type, then throws a new exception of a different type to indicate that a program-specific exception occurred. In earlier Java versions, there was no mechanism to wrap the original exception information with the new exception's information to provide a complete stack trace showing where the original problem occurred in the program. This made debugging such problems particularly difficult. J2SE 1.4 added chained exceptions to enable an exception object to maintain the complete stack-trace information. Figure 13.8 presents a mechanical example that demonstrates how chained exceptions work.

Figure 13.8. Chained exceptions.

(This item is displayed on pages 661 - 662 in the print version)

1// Fig. 13.8: UsingChainedExceptions.java

2// Demonstrating chained exceptions.

3

4public class UsingChainedExceptions

5{

6public static void main( String args[] )

7{

8try

9{

10method1(); // call method1

11} // end try

12catch ( Exception exception ) // exceptions thrown from method1

13{

14exception.printStackTrace();

15} // end catch

16} // end main

17

18// call method2; throw exceptions back to main

19public static void method1() throws Exception

20{

21try

22{

23method2(); // call method2

24} // end try

25catch ( Exception exception ) // exception thrown from method2

26{

27throw new Exception( "Exception thrown in method1", exception );

28} // end try

29} // end method method1

30

31// call method3; throw exceptions back to method1

32public static void method2() throws Exception

33{

34try

35{

36method3(); // call method3

37} // end try

38catch ( Exception exception ) // exception thrown from method3

39{

40throw new Exception( "Exception thrown in method2", exception );

41} // end catch

42} // end method method2

43

44// throw Exception back to method2

45public static void method3() throws Exception

46{

47throw new Exception( "Exception thrown in method3" );

48} // end method method3

49} // end class UsingChainedExceptions

java.lang.Exception: Exception thrown in method1

at

UsingChainedExceptions.method1(UsingChainedExceptions.java:27)

at

UsingChainedExceptions.main(UsingChainedExceptions.java:10)

Caused by:

java.lang.Exception: Exception thrown in method2

at

UsingChainedExceptions.method2(UsingChainedExceptions.java:40)

at

UsingChainedExceptions.method1(UsingChainedExceptions.java:23)

...

1 more

Caused by:

java.lang.Exception: Exception thrown in method3

at

UsingChainedExceptions.method3(UsingChainedExceptions.java:47)

at

UsingChainedExceptions.method2(UsingChainedExceptions.java:36)

...

2 more

[Page 662]

The program consists of four methodsmain (lines 616), method1 (lines 1929), method2 (lines 3242) and method3 (lines 4548). Line 10 in method main's TRy block calls method1. Line 23 in method1's try block calls method2. Line 36 in method2's TRy block calls method3. In method3, line 47 throws a new Exception. Because this statement is not in a TRy block, method3 terminates, and the exception is returned to the calling method (method2) at line 36. This statement is in a TRy block; therefore, the try block terminates and the exception is caught at lines 3841. Line 40 in the catch block throws a new exception. In this case, the Exception constructor with two arguments is called. The second argument represents the exception that was the original cause of the problem. In this program, that exception occurred at line 47. Because an exception is thrown from the catch block, method2 terminates and returns the new exception to the calling method (method1) at line 23. Once again, this statement is in a try block, so the try block terminates and the exception is caught at lines 2528. Line 27 in the catch block throws a new exception and uses the exception that was caught as the second argument to the Exception constructor. Because an exception is thrown from the catch block, method1 terminates and returns the new exception to the calling method (main) at line 10. The try block in main terminates, and the exception is caught at lines 1215. Line 14 prints a stack trace.

Notice in the program output that the first three lines show the most recent exception that was thrown (i.e., the one from method1 at line 23). The next four lines indicate the exception that was thrown from method2 at line 40. Finally, the last four lines represent the exception that was thrown from method3 at line 47. Also notice that, as you read the output in reverse, it shows how many more chained exceptions remain.