Lesson 7.3 Exceptions
Java Exceptions
In the first two sections in this lesson, you worked with errors: compile-time errors which are violations of Java's strict grammar, and runtime errors which are often caused by meaning one thing, but saying quite another. Such logical or semantic errors must be carefully and systematically expunged from your code.
However, runtime "errors" are not always the result of logical errors. Sometimes, runtime errors are caused by unusual--you could almost say exceptional--situations.
For instance, most of the time, when you read and write to a file, everything goes along normally. Sometimes, however, "bad things" happen; the disk may be full, for instance, or the file you want to write to may be a read-only file.
These kinds of situations are not mistakes that you've made in your code; instead they are unusual occurrences that you must allow for. If you just assume that everything will always go normally, your program is not very robust. Exceptional situations are often confused with error conditions, but they are a tiny bit different.
The Traditional Method
The traditional way of dealing with this kind of situation is through the use of completion codes. Every time you perform an operation that may fail, you must first check to be sure that the operation succeeded before you continue. If the operation fails, you have to decide whether to handle the failure at that point or to pass it back to the method that invoked the operation.
Here's some psuedocode that shows the traditional method of dealing with exceptional situations. Consider the steps you need to follow to open a file, read some data, process the data, write the changed data back to the file, and finally, close the file:
|
Open, Read, Write, Close
|
GET A FILENAME
OPEN THE FILE
IF NO ERROR OPENING THE FILE
READ SOME DATA
IF NO ERROR READING THE DATA
PROCESS THE DATA
WRITE THE DATA
IF NO ERROR WRITING THE DATA
CLOSE THE FILE
IF NO ERROR CLOSING FILE
RETURN OK
ELSE
RETURN CLOSE_ERROR
ENDIF
ELSE
RETURN WRITE_ERROR
ENDIF
ELSE
RETURN READ_ERROR
ENDIF
ELSE
RETURN FILE_OPEN_ERROR
ENDIF |
There are three things to notice about this code:
- Most of these operations, except for "PROCESS THE DATA" can fail, and so the program has to be prepared.
- It is very difficult to determine the normal course of action. The program is so taken up with what can go wrong, that it's hard to be certain you are doing the right things in the right order.
- It is very difficult to write a library that depends upon code like this, because every function in the library has to return several different error codes.
The Exceptional Method
Using an exception mechanism, the same pseudocode looks like this:
|
Open, Read, Write, Close
|
TRY TO DO THESE THINGS:
GET A FILENAME
OPEN THE FILE
READ SOME DATA
PROCESS THE DATA
WRITE THE DATA
CLOSE THE FILE
RETURN
IF ERROR OPENING FILE THEN ...
IF ERROR READING FILE THEN ...
IF ERROR WRITING FILE THEN ...
IF ERROR CLOSING FILE THEN ... |
Compare this code to the previous code, and you'll immediately see that the code that uses an exception strategy:
- is shorter and easier to read.
- makes the normal logic--what you are actually trying to do with the method--its focus. In the traditional method, the error-handling often obscures the normal logic.
- allows you to handle an error or pass it back to the caller. Using exceptions, the caller is not forced to manually pass errors back up the "chain" of command. This is done automatically making it very easy to implement a centralized error-handling strategy.
Exception and Error Objects
In Java, when an exceptional situation occurs--if you try to write to a file that cannot be written to, if you try to write past the end of an array, or if you try to divide by zero, even though your mother warned you not to--Java will create one of two types of Throwable objects: an Error object or an Exception object.
It will then take that Throwable object and throw it right back up the call stack where the method was called from. Any method, anywhere along the way, can decide to deal with that Throwable object, by catching it. [click the image to enlarge it]
Types of Exceptions
This illustration shows a small portion of Java's exception hierarchy. [Click the image to enlarge it] As you can see, and as was previous mentioned, Throwable has two subclasses, Exception and Error.

Error Objects
An Error is an unusual situation, like the other exceptions, but it is the kind of situation that has no easy solution. If, for instance, the JVM throws an InternalError, there is nothing you can put in your code that will cure it.
Because of Java's reliance on automatic memory management and garbage collection, an OutOfMemoryError is likewise fatal. And, even if you could catch the wiley UnknownError, what would you do with it after you'd caught it? When your program throws an Error object, it's about to give up the ghost, and there's nothing you can do about it.
Checked and Unchecked Exceptions
If you can't do anything about the Error side of the Throwable family, how about the Exception side? The Exception class represents unusual situations that you may want to handle in your own code. Like Throwable, the Exception class is also subdivided into two branches--commonly called the checked and the unchecked exceptions.
The RuntimeException Class
Unchecked exceptions are those thrown by the Java runtime system, and are descended the RuntimeException class. These kinds of exceptional situations--accessing an element past the end of an array, for instance, or dividing by zero--represent unusual situations that you would normally prevent by careful programming.
These are called unchecked exceptions because you are not required to specifically handle these exceptions in your code if they occur. Most of the time, in fact, you will not handle them at all. Some times, however, handling one of these unchecked exceptions provides an easy way to accomplish a difficult task. Here's an example. Type a number in the TextField shown here, and press ENTER. Now try typing a word, like "one." What happens now?
The NumberFormatException class provides an easy way to recover when converting between Strings and ints using Integer.parseInt(). Writing the logic necessary to determine if a String contains a well-formed number is more work than manually converting the String to an int.
Instead of going to all that work, you can simply create a try block, [which you'll learn to do in the next lesson], and catch NumberFormatException when someone types in "one hundred" instead of "100".
Other Exceptions
To use the other exceptions you have to do two things. First, you have to put the code that may cause an exceptional situation into a “try” block. Then, you provide one or more “catch” blocks immediately following the “try” block.
If a method throws one of these checked exceptions--an exception not derived from the RuntimeException class--you must handle the exception, you can't simply ignore it like you can with the unchecked exceptions.
Something to Talk About
Look up the Throwable class in the online documentation, and browse through the list of methods shown. [Throwable is in the java.lang package]. Then see if you can answer these two questions.
- Which method would you use to display the history of method calls leading up to the current exception?
- Which method would you use to display a description of the current exception for the user?
Please continue to the next section of this lesson.
|