Certification Objective Handling Exceptions (Exam Objectives 2.4 and 2.5)


Certification Objective —Handling Exceptions (Exam Objectives 2.4 and 2.5)

2.4 Develop code that makes use of exceptions and exception handling clauses (try, catch, finally), and declares methods and overriding methods that throw exceptions.

2.5 Recognize the effect of an exception arising at a specific point in a code fragment, Note that the exception may he a runtime exception, a checked exception, or an error.

An old maxim in software development says that 80 percent of the work is used 20 percent of the time. The 80 percent refers to the effort required to check and handle errors. In many languages, writing program code that checks for and deals with errors is tedious and bloats the application source into confusing spaghetti. Still, error detection and handling may be the most important ingredient of any robust application. Java arms developers with an elegant mechanism for handling errors that produces efficient and organized error-handling code: exception handling.

Exception handling allows developers to detect errors easily without writing special code to test return values. Even better, it lets us keep exception-handling code cleanly separated from the exception-generating code. It also lets us use the same exception-handling code to deal with a range of possible exceptions.

The exam has three objectives covering exception handling. We'll cover the first two in this section, and in the next section we'll cover those aspects of exception handling that are new to the exam as of Java 5.

Catching an Exception Using Try and Catch

Before we begin, let's introduce some terminology. The term "exception" means "exceptional condition" and is an occurrence that alters the normal program flow. A bunch of things can lead to exceptions, including hardware failures, resource exhaustion, and good old bugs. When an exceptional event occurs in Java, an exception is said to be "thrown." The code that's responsible for doing something about the exception is called an "exception handler," and it "catches" the thrown exception.

Exception handling works by transferring the execution of a program to an appropriate exception handler when an exception occurs. For example, if you call a method that opens a file but the file cannot be opened, execution of that method will stop, and code that you wrote to deal with this situation will be run. Therefore, we need a way to tell the JVM what code to execute when a certain exception happens. To do this, we use the try and catch keywords. The try is used to define a block of code in which exceptions may occur. This block of code is called a guarded region (which really means "risky code goes here"). One or more catch clauses match a specific exception (or group of exceptions—more on that later) to a block of code that handles it. Here's how it looks in pseudocode:

  1. try {  2.   // This is the first line of the "guarded region"  3.   // that is governed by the try keyword.  4.   // Put code here that might cause some kind of exception.  5.   // We may have many code lines here or just one.  6. }  7. catch(MyFirstException) {  8.   // Put code here that handles this exception.  9.   // This is the next line of the exception handler. 10.   // This is the last line of the exception handler. 11. } 12. catch(MySecondException) { 13.   // Put code here that handles this exception 14. } 15. 16. // Some other unguarded (normal, non-risky) code begins here 

In this pseudocode example, lines 2 through 5 constitute the guarded region that is governed by the try clause. Line 7 is an exception handler for an exception of type MyFirstException. Line 12 is an exception handler for an exception of type MySecondException. Notice that the catch blocks immediately follow the try block. This is a requirement; if you have one or more catch blocks, they must immediately follow the try block. Additionally, the catch blocks must all follow each other, without any other statements or blocks in between. Also, the order in which the catch blocks appear matters, as we'll see a little later.

Execution of the guarded region starts at line 2. If the program executes all the way past line 5 with no exceptions being thrown, execution will transfer to line 15 and continue downward. However, if at any time in lines 2 through 5 (the try block) an exception is thrown of type MyFirstException, execution will immediately transfer to line 7. Lines 8 through 10 will then be executed so that the entire catch block runs, and then execution will transfer to line 15 and continue.

Note that if an exception occurred on, say, line 3 of the try block, the rest of the lines in the try block (4 and 5) would never be executed. Once control jumps to the catch block, it never returns to complete the balance of the try block. This is exactly what you want, though. Imagine your code looks something like this pseudocode:

 try {   getTheFileFromOverNetwork   readFromTheFileAndPopulateTable } catch(CantGetFileFromNetwork) {   displayNetworkErrorMessage } 

The preceding pseudocode demonstrates how you typically work with exceptions. Code that's dependent on a risky operation (as populating a table with file data is dependent on getting the file from the network) is grouped into a try block in such a way that if, say, the first operation fails, you won't continue trying to run other code that's also guaranteed to fail. In the pseudocode example, you won't be able to read from the file if you can't get the file off the network in the first place.

One of the benefits of using exception handling is that code to handle any particular exception that may occur in the governed region needs to be written only once. Returning to our earlier code example, there may be three different places in our try block that can generate a MyFirstException, but wherever it occurs it will be handled by the same catch block (on line 7). We'll discuss more benefits of exception handling near the end of this chapter.

Using Finally

Although try and catch provide a terrific mechanism for trapping and handling exceptions, we are left with the problem of how to clean up after ourselves if an exception occurs. Because execution transfers out of the try block as soon as an exception is thrown, we can't put our cleanup code at the bottom of the try block and expect it to be executed if an exception occurs. Almost as bad an idea would be placing our cleanup code in each of the catch blocks—let's see why.

Exception handlers are a poor place to clean up after the code in the try block because each handler then requires its own copy of the cleanup code. If, for example, you allocated a network socket or opened a file somewhere in the guarded region, each exception handler would have to close the file or release the socket. That would make it too easy to forget to do cleanup, and also lead to a lot of redundant code. To address this problem, Java offers the finally block.

A finally block encloses code that is always executed at some point after the try block, whether an exception was thrown or not. Even if there is a return statement in the try block, the finally block executes right after the return statement is encountered, and before the return executes!

This is the right place to close your files, release your network sockets, and perform any other cleanup your code requires. If the try block executes with no exceptions, the finally block is executed immediately after the try block completes. If there was an exception thrown, the finally block executes immediately after the proper catch block completes. Let's look at another pseudocode example:

  1: try {  2:   // This is the first line of the "guarded region".  3: }  4: catch(MyFirstException) {  5:   // Put code here that handles this exception  6: }  7: catch(MySecondException) {  8:   // Put code here that handles this exception  9: } 10: finally { 11:   // Put code here to release any resource we 12:   // allocated in the try clause. 13: } 14: 15: // More code here 

As before, execution starts at the first line of the try block, line 2. If there are no exceptions thrown in the try block, execution transfers to line 11, the first line of the finally block. On the other hand, if a MySecondException is thrown while the code in the try block is executing, execution transfers to the first line of that exception handler, line 8 in the catch clause. After all the code in the catch clause is executed, the program moves to line 11, the first line of the finally clause. Repeat after me: finally always runs! OK, we'll have to refine that a little, but for now, start burning in the idea that finally always runs. If an exception is thrown, finally runs. If an exception is not thrown, finally runs. If the exception is caught, finally runs. If the exception is not caught, finally runs. Later we'll look at the few scenarios in which finally might not run or complete.

Remember, finally clauses are not required. If you don't write one, your code will compile and run just fine. In fact, if you have no resources to clean up after your try block completes, you probably don't need a finally clause. Also, because the compiler doesn't even require catch clauses, sometimes you'll run across code that has a try block immediately followed by a finally block. Such code is useful when the exception is going to be passed back to the calling method, as explained in the next section. Using a finally block allows the cleanup code to execute even when there isn't a catch clause.

The following legal code demonstrates a try with a finally but no catch:

 try {   // do stuff } finally {   //clean up } 

The following legal code demonstrates a try, catch, and finally:

 try {   // do stuff } catch (SomeException ex) {   // do exception handling } finally {   // clean up } 

The following ILLEGAL code demonstrates a try without a catch or finally:

 try {   // do stuff } // need a catch or finally here System.out.println("out of try block"); 

The following ILLEGAL code demonstrates a misplaced catch block:

 try {   // do stuff } // can't have code between try/catch System.out.println("out of try block"); catch(Exception ex) { } 

image from book
Exam Watch

It is illegal to use a try clause without either a catch clause or a finally clause.A try clause by itself will result in a compiler error.Any catch clauses must immediately follow the try block.Any finally clause must immediately follow the last catch clause (or it must immediately follow the try block if there is no catch) It is legal to omit either the catch clause or the finally clause, but not both.

image from book

image from book
Exam Watch

You can't sneak any code in between the try, catch, or finally blocks. The following won't compile:

 try {   // do stuff } System.out.print("below the try"); //Illegal! catch(Exception ex) { } 

image from book

Propagating Uncaught Exceptions

Why aren't catch clauses required? What happens to an exception that's thrown in a try block when there is no catch clause waiting for it? Actually, there's no requirement that you code a catch clause for every possible exception that could be thrown from the corresponding try block. In fact, it's doubtful that you could accomplish such a feat! If a method doesn't provide a catch clause for a particular exception, that method is said to be "ducking" the exception (or "passing the buck").

So what happens to a ducked exception? Before we discuss that, we need to briefly review the concept of the call stack. Most languages have the concept of a method stack or a call stack. Simply put, the call stack is the chain of methods that your program executes to get to the current method. If your program starts in method main() and main() calls method a(), which calls method b(), which in turn calls method c(), the call stack consists of the following:

 c b a main 

We will represent the stack as growing upward (although it can also be visualized as growing downward). As you can see, the last method called is at the top of the stack, while the first calling method is at the bottom. The method at the very top of the stack trace would be the method you were currently executing. If we move back down the call stack, we're moving from the current method to the previously called method. Figure 5-1 illustrates a way to think about how the call stack in Java works.

image from book
Figure 5-1: The Java method call stack

Now let's examine what happens to ducked exceptions. Imagine a building, say, five stories high, and at each floor there is a deck or balcony. Now imagine that on each deck, one person is standing holding a baseball mitt. Exceptions are like balls dropped from person to person, starting from the roof. An exception is first thrown from the top of the stack (in other words, the person on the roof), and if it isn't caught by the same person who threw it (the person on the roof), it drops down the call stack to the previous method, which is the person standing on the deck one floor down. If not caught there, by the person one floor down, the exception/ball again drops down to the previous method (person on the next floor down), and so on until it is caught or until it reaches the very bottom of the call stack. This is called exception propagation.

If an exception reaches the bottom of the call stack, it's like reaching the bottom of a very long drop; the ball explodes, and so does your program. An exception that's never caught will cause your application to stop running. A description (if one is available) of the exception will be displayed, and the call stack will be "dumped." This helps you debug your application by telling you what exception was thrown, from what method it was thrown, and what the stack looked like at the time.

image from book
Exam Watch

You can keep throwing an exception down through the methods on the stack. But what about when you get to the main() method at the bottom? You can throw the exception out of main() as well. This results in the Java Virtual Machine (JVM) halting, and the stack trace will be printed to the output.

The following code throws an exception,

 class TestEx {   public static void main (String [] args) {     doStuff();   }   static void doStuff() {     doMoreStuff ();   }   static void doMoreStuff () {     int x = 5/0;  // Can't divide by zero!                   // ArithmeticException is thrown here   } } 

which prints out a stack trace something like,

  %java TestEx Exception in thread "main" java.lang. ArithmeticException: / by zero at TestEx.doMoreStuff(TestEx.java:10) at TestEx.doStuff(TestEx.java:7) at TestEx.main(TestEx.java:3) 

image from book

Exercise 5-3: Propagating and Catching an Exception

image from book

In this exercise you're going to create two methods that deal with exceptions. One of the methods is the main() method, which will call another method. If an exception is thrown in the other method, main() must deal with it. A finally statement will be included to indicate that the program has completed. The method that main() will call will be named reverse, and it will reverse the order of the characters in a String. If the String contains no characters, reverse will propagate an exception up to the main() method.

  • Create a class called Propagate and a main() method, which will remain empty for now.

  • Create a method called reverse. It takes an argument of a string and returns a String.

  • In reverse, check if the String has a length of 0 by using the String.length() method. If the length is 0, the reverse method will throw an exception.

  • Now include the code to reverse the order of the String. Because this isn't the main topic of this chapter, the reversal code has been provided, but feel free to try it on your own.

     String reverseStr = ""; for(int i=s. length()-l;i>=0;--i) {   reverseStr += s.charAt(i); } return reverseStr; 

  • Now in the main() method you will attempt to call this method and deal with any potential exceptions. Additionally, you will include a finally statement that displays when main() has finished.

image from book

Defining Exceptions

We have been discussing exceptions as a concept. We know that they are thrown when a problem of some type happens, and we know what effect they have on the flow of our program. In this section we will develop the concepts further and use exceptions in functional Java code. Earlier we said that an exception is an occurrence that alters the normal program flow. But because this is Java, anything that's not a primitive must bean object. Exceptions are no, well, exception to this rule. Every exception is an instance of a class that has class Exception in its inheritance hierarchy. In other words, exceptions are always some subclass of java.lang.Exception.

When an exception is thrown, an object of a particular Exception subtype is instantiated and handed to the exception handler as an argument to the catch clause. An actual catch clause looks like this:

 try {    // some code here } catch (ArraylndexOutOfBoundsException e) {    e.printStackTrace(); } 

In this example, e is an instance of the ArraylndexOutOfBoundsException class. As with any other object, you can call its methods.

Exception Hierarchy

All exception classes are subtypes of class Exception This class derives from the class Throwable (which derives from the class Object). Figure 5-2 shows the hierarchy for the exception classes.

image from book
Figure 5-2: Exception class hierarchy

As you can see, there are two subclasses that derive from Throwable:Exception and Error. Classes that derive from Error represent unusual situations that are not caused by program errors, and indicate things that would not normally happen during program execution, such as the JVM running out of memory. Generally, your application won't be able to recover from an Error, so you're not required to handle them. If your code does not handle them (and it usually won't), it will still compile with no trouble. Although often thought of as exceptional conditions, Errors are technically not exceptions because they do not derive from class Exception.

In general, an exception represents something that happens not as a result of a programming error, but rather because some resource is not available or some other condition required for correct execution is not present. For example, if your application is supposed to communicate with another application or computer that is not answering, this is an exception that is not caused by a bug. Figure 5-2 also shows a subtype of Exception called RuntimeException. These exceptions are a special case because they sometimes do indicate program errors. They can also represent rare, difficult-to-handle exceptional conditions. Runtime exceptions are discussed in greater detail later in this chapter.

Java provides many exception classes, most of which have quite descriptive names. There are two ways to get information about an exception. The first is from the type of the exception itself. The next is from information that you can get from the exception object. Class Throwable (at the top of the inheritance tree for exceptions) provides its descendants with some methods that are useful in exception handlers. One of these is printStackTrace(). As expected, if you call an exception object's printStackTrace() method, as in the earlier example, a stack trace from where the exception occurred will be printed.

We discussed that a call stack builds upward with the most recently called method at the top. You will notice that the printStackTrace() method prints the most recently entered method first and continues down, printing the name of each method as it works its way down the call stack (this is called unwinding the stack) from the top.

image from book
Exam Watch

For the exam, it is not necessary to know any of the methods contained in the Throwable classes, including Exception and Error. You are expected to know that Exception, Error, RuntimeException, and Throwable types can all be thrown using the throw keyword, and can all be caught (although you rarely will catch anything other than Exception subtypes).

image from book

Handling an Entire Class Hierarchy of Exceptions

We've discussed that the catch keyword allows you to specify a particular type of exception to catch. You can actually catch more than one type of exception in a single catch clause. If the exception class that you specify in the catch clause has no subclasses, then only the specified class of exception will be caught. However, if the class specified in the catch clause does have subclasses, any exception object that subclasses the specified class will be caught as well.

For example, class IndexOutOfBoundsException has two subclasses, ArrayIndexOutOfBoundsException and StringIndexOutOfBoundsException. You may want to write one exception handler that deals with exceptions produced by either type of boundary error, but you might not be concerned with which exception you actually have. In this case, you could write a catch clause like the following:

 try {   // Some code here that can throw a boundary exception } catch (IndexOutofBoundsException e) {   e.printStackTrace (); } 

If any code in the try block throws Array IndexOutOf BoundsException or StringIndexOutOfBoundsException, the exception will be caught and handled. This can be convenient, but it should be used sparingly. By specifying an exception class's superclass in your catch clause, you're discarding valuable information about the exception. You can, of course, find out exactly what exception class you have, but if you're going to do that, you're better off writing a separate catch clause for each exception type of interest.

On the Job 

Resist the temptation to write a single catchall exception handler such as the on the following:

 try {   // some code } catch (Exception e) {   e.printStackTrace(); } 

This code will catch every exception generated. Of course, no single exception handler can properly handle every exception, and programming in this way defeats the design objective. Exception handlers that trap many errors at once will probably reduce the reliability of your program because it's likely that an exception will be caught that the handler does not know how to handle.

Exception Matching

If you have an exception hierarchy composed of a superclass exception and a number of subtypes, and you're interested in handling one of the subtypes in a special way but want to handle all the rest together, you need write only two catch clauses.

When an exception is thrown, Java will try to find (by looking at the available catch clauses from the top down) a catch clause for the exception type. If it doesn't find one, it will search for a handler for a supertype of the exception. If it does not find a catch clause that matches a supertype for the exception, then the exception is propagated down the call stack. This process is called exception matching. Let's look at an example:

  1: import java.io.*;  2: public class ReadData {  3:   public static void main(String args[]) {  4:     try {  5:       RandomAccessFile raf =  6:         new RandomAccessFile("myfile.txt", "r");  7:       byte b[] = new byte[1000];  8:       raf.readFully(b, 0, 1000);  9:     } 10:     catch(FileNotFoundException e) { 11:       System.err.println("File not found"); 12:       System.err.println(e.getMessage()); 13:       e.printStackTrace(); 14:     } 15:     catch(IOException e) { 16:       System.err.println("IO Error"); 17:       System. err. println(e. toString()); 18:       e.printStackTrace(); 19:     } 20:   } 21: } 

This short program attempts to open a file and to read some data from it. Opening and reading files can generate many exceptions, most of which are some type of IOException. Imagine that in this program we're interested in knowing only whether the exact exception is a FileNotFoundException. Otherwise, we don't care exactly what the problem is.

FileNotFoundException is a subclass of IOException. Therefore, we could handle it in the catch clause that catches all subtypes of IOException, but then we would have to test the exception to determine whether it was a FileNotFoundException. Instead, we coded a special exception handler for the FileNotFoundException and a separate exception handler for all other IOException subtypes.

If this code generates a FileNotFoundException, it will be handled by the catch clause that begins at line 10. If it generates another IOException—perhaps EOFException, which is a subclass of IOException—it will be handled by the catch clause that begins at line 15. If some other exception is generated, such as a runtime exception of some type, neither catch clause will be executed and the exception will be propagated down the call stack.

Notice that the catch clause for the FileNotFoundException was placed above the handler for the IOException. This is really important! If we do it the opposite way, the program will not compile. The handlers for the most specific exceptions must always be placed above those for more general exceptions. The following will not compile:

 try {   // do risky IO things } catch (IOException e) {   // handle general IOExceptions } catch (FileNotFoundException ex) {   // handle just FileNotFoundException } 

You'll get a compiler error something like this:

 TestEx.java:15: exception java.io.FileNotFoundException has  already been caught } catch (FileNotFoundException ex) {   ^ 

If you think back to the people with baseball mitts (in the section "Propagating Uncaught Exceptions"), imagine that the most general mitts are the largest, and can thus catch many different kinds of balls. An IOException mitt is large enough and flexible enough to catch any type of IOException, So if the person on the fifth floor (say, Fred) has a big 'ol IOException mitt, he can't help but catch a FileNotFoundException ball with it. And if the guy (say, Jimmy) on the second floor is holding a FileNotFoundException mitt, that FileNotFoundException ball will never get to him, since it will always be stopped by Fred on the fifth floor, standing there with his big-enough-for-any-IOException mitt.

So what do you do with exceptions that are siblings in the class hierarchy? If one Exception class is not a subtype or supertype of the other, then the order in which the catch clauses are placed doesn't matter.

Exception Declaration and the Public Interface

So, how do we know that some method throws an exception that we have to catch? Just as a method must specify what type and how many arguments it accepts and what is returned, the exceptions that a method can throw must be declared (unless the exceptions are subclasses of RuntimeException). The list of thrown exceptions is part of a method's public interface. The throws keyword is used as follows to list the exceptions that a method can throw:

 void myFunction() throws  MyException1, MyException2 {   // code for the method here } 

This method has a void return type, accepts no arguments, and declares that it can throw one of two types of exceptions: either type MyException1 or type MyException2. (Just because the method declares that it throws an exception doesn't mean it always will. It just tells the world that it might.)

Suppose your method doesn't directly throw an exception, but calls a method that does. You can choose not to handle the exception yourself and instead just declare it, as though it were your method that actually throws the exception. If you do declare the exception that your method might get from another method, and you don't provide a try/catch for it, then the method will propagate back to the method that called your method, and either be caught there or continue on to be handled by a method further down the stack.

Any method that might throw an exception (unless it's a subclass of RuntimeException) must declare the exception. That includes methods that aren't actually throwing it directly, but are "ducking" and letting the exception pass down to the next method in the stack. If you "duck" an exception, it is just as if you were the one actually throwing the exception. RuntimeException subclasses are exempt, so the compiler won't check to see if you've declared them. But all non-RuntimeExceptions are considered "checked" exceptions, because the compiler checks to be certain you've acknowledged that "bad things could happen here."

Remember this:

Each method must either handle all checked exceptions by supplying a catch clause or list each unhandled checked exception as a thrown exception.

This rule is referred to as Java's "handle or declare" requirement. (Sometimes called "catch or declare.")

image from book
Exam Watch

Look for code that invokes a method declaring an exception, where the calling method doesn't handle or declare the checked exception. The following code (which uses the throw keyword to throw an exception manually—more on this next) has two big problems that the compiler will prevent:

 void doStuff ( )   doMore(); } void doMore() {   throw new IOException(); } 

First, the doMore() method throws a checked exception, but does not declare it! But suppose we fix the doMore() method as follows:

 void doMore() throws IOException {  } 

The dostuff() method is still in trouble because it, too, must declare the IOException, unless it handles it by providing a try/catch, with a catch clause that can take an IOException.

image from book

Again, some exceptions are exempt from this rule. An object of type RuntimeException may be thrown from any method without being specified as part of the method's public interface (and a handler need not be present). And even if a method does declare a RuntimeException, the calling method is under no obligation to handle or declare it. RuntimeException, Error, and all of their subtypes are unchecked exceptions and unchecked exceptions do not have to be specified or handled. Here is an example:

 import java.io.*; class Test {   public int myMethod1() throws EOFException {     return myMethod2();   }   public int myMethod2() throws EOFException {     // code that actually could throw the exception goes here     return 1;   } } 

Let's look at myMethod1(). Because EOFException subclasses IOException and IOException subclasses Exception, it is a checked exception and must be declared as an exception that may be thrown by this method. But where will the exception actually come from? The public interface for method myMethod2() called here declares that an exception of this type can be thrown. Whether that method actually throws the exception itself or calls another method that throws it is unimportant to us; we simply know that we have to either catch the exception or declare that we throw it. The method myMethod1() does not catch the exception, so it declares that it throws it. Now let's look at another legal example, myMethod3().

 public void myMethod3() {   // code that could throw a NullPointerException goes here } 

According to the comment, this method can throw a NullPointerExceptiont Because RuntimeException is the superclass of NullPointerException, it is an unchecked exception and need not be declared. We can see that myMethod3() does not declare any exceptions.

Runtime exceptions are referred to as unchecked exceptions. All other exceptions are checked exceptions, and they don't derive from java.lang.RuntimeException. A checked exception must be caught somewhere in your code. If you invoke a method that throws a checked exception but you don't catch the checked exception somewhere, your code will not compile. That's why they're called checked exceptions; the compiler checks to make sure that they're handled or declared. A number of the methods in the Java 2 Standard Edition libraries throw checked exceptions, so you will often write exception handlers to cope with exceptions generated by methods you didn't write.

You can also throw an exception yourself, and that exception can be either an existing exception from the Java API or one of your own. To create your own exception, you simply subclass Exception (or one of its subclasses) as follows:

 class MyException extends Exception { } 

And if you throw the exception, the compiler will guarantee that you declare it as follows:

 class TestEx {   void doStuff() {     throw new MyException();  // Throw a checked exception   } } 

The preceding code upsets the compiler:

 TestEx.java:6: unreported exception MyException; must be caught or declared to be thrown   throw new MyException();   ^ 

image from book
Exam Watch

When an object of a subtype of Exception is thrown, it must be handled or declared. These objects are called checked exceptions, and include all exceptions except those that are subtypes of RuntimeException, which are unchecked exceptions. Be ready to spot methods that don't follow the "handle or declare" rule, such as

 class MyException extends Exception {   void someMethod () {     doStuff();   }  void doStuff() throws MyException {    try {      throw new MyException();    }    catch(MyException me) {      throw me;    }   } } 

You need to recognize that this code won't compile. If you try, you'll get

 MyException.java:3: unreported exception MyException; must be caught or declared to be thrown doStuff();        ^ 

Notice that somemethod() falls to either handle or declare the exception that can be thrown by doStuff().

image from book

You need to know how an Error compares with checked and unchecked exceptions. Objects of type Error are not Exception objects, although they do represent exceptional conditions. Both Exception and Error share a common superclass, Throwable, thus both can be thrown using the throw keyword. When an Error or a subclass of Error is thrown, it's unchecked. You are not required to catch Error objects or Error subtypes. You can also throw an Error yourself (although other than AssertionError you probably won't ever want to), and you can catch one, but again, you probably won't. What, for example, would you actually do if you got an outOfMemoryError? It's not like you can tell the garbage collector to run; you can bet the JVM fought desperately to save itself (and reclaimed all the memory it could) by the time you got the error. In other words, don't expect the JVM at that point to say, "Run the garbage collector? Oh, thanks so much for telling me. That just never occurred to me. Sure, I'll get right on it." Even better, what would you do if a VirtualMachineError arose? Your program is toast by the time you'd catch the Error, so there's really no point in trying to catch one of these babies. Just remember, though, that you can! The following compiles just fine:

 class TestEx {   public static void main (String [] args) {     badMethod();   }   static void badMethod() {  // No need to declare an Error     doStuff()   }   static void doStuff() { //No need to declare an Error     try {       throw new Error();     }     catch(Error me) {       throw me; // we catch it, but then rethrow it     }   } } 

If we were throwing a checked exception rather than Error, then the doStuff() method would need to declare the exception. But remember, since Error is not a subtype of Exception, it doesn't need to be declared. You're free to declare it if you like, but the compiler just doesn't care one way or another when or how the Error is thrown, or by whom.

On the Job 

Because Java has checked exceptions, it's commonly said that Java forces developers to handle exceptions. Yes, Java forces us to write exception handlers for each exception that can occur during normal operation, but it's up to us to make the exception handlers actually do something useful. We know software managers who melt down when they see a programmer write:

 try {   callBadmethod(); } catch (Exception ex) { } 

Notice anything missing? Don't "eat" the exception by catching it without actually handling it. You won't even be able to tell that the exception occurred, because you'll never see the stack trace.

Rethrowing the Same Exception

Just as you can throw a new exception from a catch clause, you can also throw the same exception you just caught. Here's a catch clause that does this:

 catch(IOException e) {   // Do things, then if you decide you can't handle it...   throw e; } 

All other catch clauses associated with the same try are ignored, if a finally block exists, it runs, and the exception is thrown back to the calling method (the next method down the call stack). If you throw a checked exception from a catch clause, you must also declare that exception! In other words, you must handle and declare, as opposed to handle or declare. The following example is illegal:

 public void doStuff() {   try {     // risky IO things   } catch(IOException ex) {     // can't handle it      throw ex;  // Can't throw it unless you declare it   } } 

In the preceding code, the dostuff() method is clearly able to throw a checked exception—in this case an IOException—so the compiler says, "Well, that's just peachy that you have a try/catch in there, but it's not good enough. If you might rethrow the IOException you catch, then you must declare it!"

Exercise 5-4: Creating an Exception

image from book

In this exercise we attempt to create a custom exception. We won't put in any new methods (it will have only those inherited from Exception), and because it extends Exception, the compiler considers it a checked exception. The goal of the program is to determine whether a command-line argument, representing a particular food (as a string), is considered bad or OK.

  • Let's first create our exception. We will call it BadFoodException. This exception will be thrown when a bad food is encountered.

  • Create an enclosing class called MyException and a main() method, which will remain empty for now.

  • Create a method called checkFood(). It takes a String argument and throws our exception if it doesn't like the food it was given. Otherwise, it tells us it likes the food. You can add any foods you aren't particularly fond of to the list.

  • Now in the main() method, you'll get the command-line argument out of the String array, and then pass that String on to the checkFood() method. Because it's a checked exception, the checkFood() method must declare it, and the main() method must handle it (using a try/catch). Do not have main() declare the exception, because if main() ducks the exception, who else is back there to catch it?

  • As nifty as exception handling is, it's still up to the developer to make proper use of it. Exception handling makes organizing our code and signaling problems easy, but the exception handlers still have to be written. You'll find that even the most complex situations can be handled, and your code will be reusable, readable, and maintainable.

image from book




SCJP Sun Certified Programmer for Java 5 Study Guide Exam 310-055
SCJP Sun Certified Programmer for Java 5 Study Guide (Exam 310-055) (Certification Press)
ISBN: 0072253606
EAN: 2147483647
Year: 2006
Pages: 131

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net