Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Ganesh_JavaSE7_Programming_1z0-804_study_guide.pdf
Скачиваний:
94
Добавлен:
02.02.2015
Размер:
5.88 Mб
Скачать

Chapter 11 Exceptions and Assertions

Note how you provided more than one catch handler by stacking them up: you provided specific (i.e., derived type) exception handlers followed by more general (i.e., base type) exception handlers. If you provide a derived exception type after a base exception type, you get a compiler error. You might not already know, but

NoSuchElementException is the base class of InputMismatchException! See what happens when you try to reverse the order of catch handlers for InputMismatchException and NoSuchElementException.

try {

System.out.println("The integer value scanned from string is: "         + consoleScanner.nextInt());

} catch(NoSuchElementException nsee) {

System.out.println("Error: Cannot scan an integer from the given string"); } catch(InputMismatchException ime) {

System.out.println("Error: Cannot scan an integer from the given string");

}

This code segment will result in this compiler error:

ScanInt4.java:14: error: exception InputMismatchException has already been caught

}catch(InputMismatchException ime) {

^

1 error 

When providing multiple catch handlers, handle specific exceptions before handling general exceptions. If you provide a derived class exception catch handler after a base class exception handler, your code will not compile.

Multi-Catch Blocks

You just saw that you cannot reverse the order of the catch handlers for InputMismatchException and NoSuchElementException. However, is it possible to combine these two catch handlers together? Java 7 provides a feature named multi-catch blocks in which you can combine multiple catch handlers (see Listing 11-6).

Listing 11-6.  ScanInt5.java

// A program that illustrates multi-catch blocks

import java.util.*;

class ScanInt5 {

        public static void main(String [] args) {

                String integerStr = "";

                System.out.println("The string to scan integer from it is: " + integerStr);

                Scanner consoleScanner = new Scanner(integerStr);

                try {

System.out.println("The integer value scanned from string is: " +        consoleScanner.nextInt());

324

Chapter 11 Exceptions and Assertions

                } catch(NoSuchElementException | IllegalStateException multie) {

                         System.out.println("Error: An error occured while attempting to scan the integer");

                }

        }

}

Note how you combine the catch handlers together using the | (OR) operator here (the same operator you use for performing bit-wise OR operation on integral values) for combining the catch clauses of NoSuchElementException and IllegalStateException.

Unlike the combined catch clauses for NoSuchElementException and IllegalStateException, you cannot combine the catch clauses of NoSuchElementException and InputMismatchException. As we've already discussed, NoSuchElementException is the base class of InputMismatchException, and you cannot catch both of them in the multi-catch block. If you try compiling such a multi-catch clause, you’ll get this compiler error:

ScanInt5.java:11: error: Alternatives in a multi-catch statement cannot be related by subclassing } catch(InputMismatchException | NoSuchElementException exception) {

         ^

So what is the alternative? When you need such a catch handler for the exceptions where one exception is the base class of another exception class, providing the catch handler for the base class alone is sufficient (since that base class catch handler will handle the derived class exception if it occurs). 

In a multi-catch block, you cannot combine catch handlers for two exceptions that share a baseand derived-class relationship. You can only combine catch handlers for exceptions that do not share the parent-child relationship between them.

How do you know if it is better to combine exception handling blocks or stack them? It is a design choice where you must consider the following aspects: a) Do the exceptions get thrown for similar reason or for different reasons?

(b) Is the handling code similar or different? If you answer “similar” for both the questions, it is better to combine them; if you say “different” for either one of these two questions, then it is better to separate them.

How about the specific situation in Listing 11-6? Is it better to combine or separate the handlers for the InputMismatchException and IllegalStateException exceptions? You can see that the exception handling is the same for both of the catch blocks. But the reasons for these two exceptions are considerably

different. The InputMismatchException gets thrown if you (or the user) type invalid input in the console. The IllegalStateException gets thrown because of a programming mistake when you call the nextInt() method after calling the close() method on Scanner. So, in this case, it is a better design choice to separate the handlers for these two exceptions.

General Catch Handlers

Did you notice that many exceptions can get thrown when you use APIs related to I/O operations? We just discussed that in order to call just one method, nextInt() of the Scanner class, you need to handle three exceptions: the

InputMismatchException, the NoSuchElementException, and the IllegalStateException. If you keep handling specific exceptions such as this that may not actually result in an exceptional condition when you run the program, most of your code will consist of try-catch code blocks! Is there a better way to say “handle all other exceptions”? Yes, you can provide a general exception handler.

325

Chapter 11 Exceptions and Assertions

Here is the code snippet that shows only the try-catch blocks for the class ScanInt3 from Listing 11-4, enhanced with a general exception handler:

try {

System.out.println("You typed the integer value: " + consoleScanner.nextInt());

}catch(InputMismatchException ime) {

//if something other than integer is typed, we'll get this exception, so handle it

        System.out.println("Error: You typed some text that is not an integer value...");

}catch(Exception e) {

//catch IllegalStateException here which is unlikely to occur...

         System.out.println("Error: Encountered an exception and could not read an integer from the console... ");

This code provides a catch handler for the base exception of the type Exception. So, if the try block throws any other exception than the InputMismatchException, and if that exception is a derived class of the Exception class, this general catch handler will handle it. It is recommended practice to catch specific exceptions, and then provide a general exception handler to ensure that all other exceptions are handled as well.

Chained Exceptions

When you want to catch an exception and throw another exception, you can “chain” the first exception to the thrown exception. In other words, when throwing an exception, you can associate another exception that caused it.

When creating an exception object you can use a constructor that takes another exception as an argument; this passed argument is the exception chained to the exception object being created. There is also an overloaded constructor that takes a description message as an additional argument. For example, the following are two overloaded constructors of the Exception class:

 Exception(Throwable cause)

Exception(string detailMsg, throwable cause)

Similar constructors are available for other classes such as Throwable, Error, and RuntimeException. The following program illustrates chained exceptions:

class ChainedException {

        public static void foo() {

                try {

                        String [] str = { "foo" };

                        System.out.println("About to throw ArrayIndexOutOfBoundsException");

                        // following statement has out-of-bounds access

                        String functionName = str[10];

                } catch(ArrayIndexOutOfBoundsException oob) {

                         System.out.println("Wrapping ArrayIndexOutOfBoundsException into a RuntimeException");

                        throw new RuntimeException(oob);

}

}

326

Chapter 11 Exceptions and Assertions

        public static void main(String []args) {

                try {

                        foo();

                } catch(Exception re) {

                        System.out.println("The caught exception in main is: " + re.getClass());

                        System.out.println("The cause of the exception is: " + re.getCause());

}

}

}

When executed, this program prints the following:

About to throw ArrayIndexOutOfBoundsException

Wrapping ArrayIndexOutOfBoundsException into a RuntimeException The caught exception in main is: class java.lang.RuntimeException

The cause of the exception is: java.lang.ArrayIndexOutOfBoundsException: 10

Methods related to chained exceptions are the getCause() and initCause() methods defined in the

Throwable class.

The getCause() method returns a Throwable object. It returns an exception chained to the exception object on which this method is invoked. This chained exception is the original exception that caused this exception. If no exception is chained to this exception, this method returns null.

The initCause(Throwable causeException) method sets the chained exception for the exception object on which this method is called. If the chained exception has already been set when creating the exception object, calling this method will result in throwing an IllegalStateException. This method can be called only once; any attempt to call it more than once will result in throwing an IllegalStateException.

Note that exceptions can be chained to any level of depth.

Finally Blocks

There is a close() method provided in the Scanner class, and you need to close it. In the classes ScanInt1, ScanInt2, and ScanInt3 (Listings 11-2, 11-3, and 11-4, respectively), note that you opened a Scanner object but did not close it. So, these programs have a resource leak! The word “resource” refers to any of the classes that acquire some

system sources from the underlying operating system, such as network, file, database, and other handles. But how do you know which classes need to be closed? Well, nice question. The answer is that if a class implements java.io.Closeable, then you must call the close() method of that class; otherwise, it will result in a resource leak.

The garbage collector (GC) is responsible for releasing only memory resources. If you are using any class that acquires system resources, it is your responsibility to release them by calling the close() method on that object.

ScanInt6 (Listing 11-7) calls the close() method of the Scanner object in its main() method; you want to shorten the code, so you’ll use a general exception handler for handling all exceptions that can be thrown within the try block.

327

Chapter 11 Exceptions and Assertions

Listing 11-7.  ScanInt6.java

import java.util.*;

class ScanInt6 {

        public static void main(String [] args) {

                System.out.println("Type an integer in the console: ");

                Scanner consoleScanner = new Scanner(System.in);

                try {

                        System.out.println("You typed the integer value: " +

                                        consoleScanner.nextInt());

                        System.out.println("Done reading the text... closing the Scanner");

                        consoleScanner.close();

                } catch(Exception e) {

                        // call all other exceptions here ...

System.out.println("Error: Encountered an exception and could not read an integer from the console... ");

System.out.println("Exiting the program - restart and try the program again!");

                }

        }

}

Let’s see if this program works.

D:\> java ScanInt6

Type an integer in the console: 10

You typed the integer value: 10

Done reading the text... closing the Scanner

Because the program printed "Done reading the text... closing the Scanner", and completed the execution normally, you can assume that the statement consoleScanner.close(); has executed successfully. What happens if an exception gets thrown?

D:\> java ScanInt6

Type an integer in the console: ten

Error: Encountered an exception and could not read an integer from the console...

Exiting the program - restart and try the program again!

As you can see from the output, the program did not print "Done reading the text... closing the Scanner", so the statement consoleScanner.close(); has not executed. How can you fix it? One way is to call consoleScanner. close() in the catch block as well, like this:

try {

System.out.println("You typed the integer value: " + consoleScanner.nextInt()); System.out.println("Done reading the text... closing the Scanner");

        consoleScanner.close();

}catch(Exception e) {

//call all other exceptions here ...

        consoleScanner.close();

328

Chapter 11 exCeptions and assertions

System.out.println("Error: Encountered an exception and could not read an integer from the console... ");

System.out.println("Exiting the program - restart and try the program again!");

}

This solution will work but is not elegant. You know you can have multiple catch blocks and you have to provide calls to consoleScanner.close(); in all the catch blocks! Is there a better way to release the resources? Yes, you can use release resources in a finally block (see Listing 11-8).

Listing 11-8. ScanInt7.java

import java.util.*;

class ScanInt7 {

public static void main(String [] args) { System.out.println("Type an integer in the console: "); Scanner consoleScanner = new Scanner(System.in);

try {

System.out.println("You typed the integer value: " + consoleScanner.nextInt());

} catch(Exception e) {

// call all other exceptions here ...

System.out.println("Error: Encountered an exception and could not read an integer from the console... ");

System.out.println("Exiting the program - restart and try the program again!"); } finally {

System.out.println("Done reading the integer... closing the Scanner"); consoleScanner.close();

}

}

}

In this case, a finally block is provided after the catch block. This finally block will be executed whether an exception has occurred or not. So, the finally block is a good place to call the close() method on the Scanner object to ensure that this resource is always released.

if you call System.exit() inside a method, it will abnormally terminate the program. so, if the calling method has a finally block, it will not be called and resources may leak. For this reason, it is a bad programming practice to call System.exit() to terminate a program.

Now, let’s see if the scanner is closed both in the case when the program completes normally (i.e., without throwing an exception) and when the program terminates after throwing an exception.

D:\> java ScanInt7

Type an integer in the console: 10

You typed the integer value: 10

Done reading the integer... closing the Scanner

329

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]