Chapter 12
Looking Good When Things Take Unexpected Turns
In This Chapter
Recovering from bad input and other nasty situations
Making your code (more or less) crash proof
Defining your own exception class
September 9, 1945: A moth flies into one of the relays of the Harvard Mark II computer and gums up the works. This becomes the first recorded case of a real computer bug.
Armed with nothing but this good guess, Bright writes a small FORTRAN program and tries to compile it on his IBM 704. (The IBM 704 lives in its own specially built, 2,000-square-foot room. With vacuum tubes instead of transistors, the machine has a whopping 32K of RAM. The operating system has to be loaded from tape before the running of each program, and a typical program takes between two and four hours to run.) After the usual waiting time, Bright’s attempt to compile a FORTRAN program comes back with a single error — a missing comma in one of the statements. Bright corrects the error, and the program runs like a charm.
July 22, 1962: Mariner I, the first U.S. spacecraft aimed at another planet, is destroyed when it behaves badly four minutes after launch. The bad behavior is attributed to a missing bar (like a hyphen) in the formula for the rocket’s velocity.
Around the same time, orbit computation software at NASA is found to contain the incorrect statement DO 10 I=1.10
(instead of the correct DO 10 I=1,10
). In modern notation, this is like writing do10i = 1.10
in place of for (int i=1; i<=10; i++)
. The change from a comma to a period turns a loop into an assignment statement.
January 1, 2000: The Year 2000 Problem wreaks havoc on the modern world.
Any historically accurate facts in these notes were borrowed from the following sources: the Computer Folklore newsgroup (alt.folklore.computers, which you can access through http://groups.google.com), the Free On-line Dictionary of Computing (http://foldoc.org), the “Looking Back” column in Computer magazine (www.computer.org/computer), and the web pages of the IEEE (www.computer.org/history).
Handling Exceptions
You’re taking inventory. This means counting item after item, box after box, and marking the numbers of such things on log sheets, in little handheld gizmos, and into forms on computer keyboards. A particular part of the project involves entering the number of boxes that you find on the Big Dusty Boxes That Haven’t Been Opened Since Year One shelf. Rather than break the company’s decades-old habit, you decide not to open any of these boxes. You arbitrarily assign the value $3.25 to each box.
Listing 12-1 shows the software to handle this bit of inventory. The software has a flaw, which is revealed in Figure 12-1. When the user enters a whole number value, things are okay. But when the user enters something else (like the number 3.5), the program comes crashing to the ground. Surely something can be done about this. Computers are stupid, but they’re not so stupid that they should fail royally when a user enters an improper value.
Listing 12-1: Counting Boxes
import static java.lang.System.out;
import java.util.Scanner;
import java.text.NumberFormat;
class InventoryA {
public static void main(String args[]) {
final double boxPrice = 3.25;
Scanner keyboard = new Scanner(System.in);
NumberFormat currency =
NumberFormat.getCurrencyInstance();
out.print(“How many boxes do we have? “);
String numBoxesIn = keyboard.next();
int numBoxes = Integer.parseInt(numBoxesIn);
out.print(“The value is “);
out.println(currency.format(numBoxes * boxPrice));
}
}
The key to fixing a program bug is examining the message that appears when the program crashes. The inventory program’s message says java.lang.NumberFormatException
. That means a class named NumberFormatException is in the java.lang
API package. Somehow, the call to Integer.parseInt
brought this NumberFormatException
class out of hiding.
Well, here’s what’s going on. The Java programming language has a mechanism called exception handling. With exception handling, a program can detect that things are about to go wrong and respond by creating a brand-new object. In the official terminology, the program is said to be throwing an exception. That new object, an instance of the Exception
class, is passed like a hot potato from one piece of code to another until some piece of code decides to catch the exception. When the exception is caught, the program executes some recovery code, buries the exception, and moves on to the next normal statement as if nothing had ever happened. The process is illustrated in Figure 12-2.
The whole thing is done with the aid of several Java keywords. These keywords are as follows:
throw
: Creates a new exception object.
throws
: Passes the buck from a method up to whatever code called the method.
try
: Encloses code that has the potential to create a new exception object. In the usual scenario, the code inside a try
clause contains calls to methods whose code can create one or more exceptions.
catch
: Deals with the exception, buries it, and then moves on.
So, the truth is out. Through some chain of events like the one shown in Figure 12-2, the method Integer.parseInt
can throw a NumberFormatException
. When you call Integer.parseInt
, this NumberFormatException
is passed on to you.
If you call yourself a hero, you’d better catch the exception so that all the other code can get on with its regular business. Listing 12-2 shows the catching of an exception.
Listing 12-2: A Hero Counts Boxes
import static java.lang.System.out;
import java.util.Scanner;
import java.text.NumberFormat;
class InventoryB {
public static void main(String args[]) {
final double boxPrice = 3.25;
Scanner keyboard = new Scanner(System.in);
NumberFormat currency =
NumberFormat.getCurrencyInstance();
out.print(“How many boxes do we have? “);
String numBoxesIn = keyboard.next();
try {
int numBoxes = Integer.parseInt(numBoxesIn);
out.print(“The value is “);
out.println(
currency.format(numBoxes * boxPrice));
} catch (NumberFormatException e) {
out.println(“That’s not a number.”);
}
}
}
Figure 12-3 shows three runs of the code from Listing 12-2. When a misguided user types three instead of 3, the program maintains its cool by displaying That’s not a number
. The trick is to enclose the call to Integer.parseInt
inside a try
clause. If you do this, the computer watches for exceptions when any statement inside the try
clause is executed. If an exception is thrown, the computer jumps from inside the try
clause to a catch
clause below it. In Listing 12-2, the computer jumps directly to the catch (NumberFormatException e)
clause. The computer executes the println
statement inside the clause and then marches on with normal processing. (If there were statements in Listing 12-2 after the end of the catch
clause, the computer would go on and execute them.)
The parameter in a catch clause
Take a look at the catch
clause in Listing 12-2 and pay particular attention to the words (NumberFormatException e)
. This looks a lot like a method’s parameter list, doesn’t it? In fact, every catch
clause is like a little mini-method with its own parameter list. The parameter list always has an exception type name and then a parameter.
In Listing 12-2, I don’t do anything with the catch
clause’s e
parameter, but I certainly could if I wanted to. Remember, the exception that’s thrown is an object — an instance of the NumberFormatException
class. When an exception is caught, the computer makes the catch
clause’s parameter refer to that exception object. In other words, the name e stores a bunch of information about the exception. To take advantage of this, you can call some of the exception object’s methods.
} catch (NumberFormatException e) {
out.println(“Message: ***” +
e.getMessage() + “***”
);
e.printStackTrace()
;
}
With this new catch
clause, a run of the inventory
program may look like the run shown in Figure 12-4. When you call getMessage
, you fetch some detail about the exception. (In Figure 12-4, the detail is Message: ***For input string: “three”***
.) When you call printStackTrace
, you get some additional information; namely, a display showing the methods that were running at the moment when the exception was thrown. (In Figure 12-4, the display includes Integer.parseInt
and the main
method.) Both getMessage
and printStackTrace
present information to help you find the source of the program’s difficulties.
Exception types
So what else can go wrong today? Are there other kinds of exceptions — things that don’t come from the NumberFormatException
class? Sure, plenty of different exception types are out there. You can even create one of your own. You wanna try? If so, look at Listings 12-3 and 12-4.
Listing 12-3: Making Your Own Kind of Exception
@SuppressWarnings(“serial”)
class OutOfRangeException extends Exception {
}
Listing 12-4: Using Your Custom Made Exception
import static java.lang.System.out;
import java.util.Scanner;
import java.text.NumberFormat;
class InventoryC {
public static void main(String args[]) {
final double boxPrice = 3.25;
Scanner keyboard = new Scanner(System.in);
NumberFormat currency =
NumberFormat.getCurrencyInstance();
out.print(“How many boxes do we have? “);
String numBoxesIn = keyboard.next();
try {
int numBoxes = Integer.parseInt(numBoxesIn);
if (numBoxes < 0) {
throw new OutOfRangeException();
}
out.print(“The value is “);
out.println(
currency.format(numBoxes * boxPrice));
} catch (NumberFormatException e) {
out.println(“That’s not a number.”);
} catch (OutOfRangeException e) {
out.print(numBoxesIn);
out.println(“? That’s impossible!”);
}
}
}
Listings 12-3 and 12-4 remedy a problem that cropped up in Figure 12-3. Look at the last of the three runs in Figure 12-3. The user reports that the shelves have –25 boxes, and the computer takes this value without blinking an eye. The truth is that you would need a black hole (or some other exotic space-time warping phenomenon) to have a negative number of boxes on any shelf in your warehouse. So the program should get upset if the user enters a negative number of boxes, which is what the code in Listing 12-4 does. To see the upset code, look at Figure 12-5.
The code in Listing 12-3 declares a new kind of exception class — OutOfRangeException
. In many situations, typing a negative number would be just fine, so OutOfRangeException
isn’t built in to the Java API. However, in the inventory program, a negative number should be flagged as an anomaly.
The OutOfRangeException
class in Listing 12-3 wins the award for the shortest self-contained piece of code in the book. The class’s code is just a declaration line and an empty pair of braces. The code’s operative phrase is extends Exception
. Being a subclass of the Java API Exception
class allows any instance of the OutOfRangeException
class to be thrown.
Back in Listing 12-4, a new OutOfRangeException
instance is thrown. When this happens, the catch
clause (OutOfRangeException e)
catches the instance. The clause echoes the user’s input and displays the message That’s impossible!
Who’s going to catch the exception?
Take one more look at Listing 12-4. Notice that more than one catch
clause can accompany a single try
clause. When an exception is thrown inside a try
clause, the computer starts going down the accompanying list of catch
clauses. The computer starts at whatever catch
clause comes immediately after the try
clause and works its way down the program’s text.
For each catch
clause, the computer asks itself, “Is the exception that was just thrown an instance of the class in this clause’s parameter list?”
If not, the computer skips this catch
clause and moves on to the next catch
clause in line.
If so, the computer executes this catch
clause and then skips past all the other catch
clauses that come with this try
clause. The computer goes on and executes whatever statements come after the whole try
-catch
statement.
For some concrete examples, see Listings 12-5 and 12-6.
Listing 12-5: Yet Another Exception
@SuppressWarnings(“serial”)
class NumberTooLargeException
extends OutOfRangeException {
}
Listing 12-6: Where Does the Buck Stop?
import static java.lang.System.out;
import java.util.Scanner;
import java.text.NumberFormat;
class InventoryD {
public static void main(String args[]) {
final double boxPrice = 3.25;
Scanner keyboard = new Scanner(System.in);
NumberFormat currency =
NumberFormat.getCurrencyInstance();
out.print(“How many boxes do we have? “);
String numBoxesIn = keyboard.next();
try {
int numBoxes = Integer.parseInt(numBoxesIn);
if (numBoxes < 0) {
throw new OutOfRangeException();
}
if (numBoxes > 1000) {
throw new NumberTooLargeException();
}
out.print(“The value is “);
out.println(
currency.format(numBoxes * boxPrice));
}
catch (NumberFormatException e) {
out.println(“That’s not a number.”);
}
catch (OutOfRangeException e) {
out.print(numBoxesIn);
out.println(“? That’s impossible!”);
}
catch (Exception e) {
out.print(“Something went wrong, “);
out.print(“but I’m clueless about what “);
out.println(“it actually was.”);
}
out.println(“That’s that.”);
}
}
To run the code in Listings 12-5 and 12-6, you need one additional Java program file. You need the OutOfRangeException
class in Listing 12-3.
Listing 12-6 addresses the scenario in which you have limited shelf space. You don’t have room for more than 1,000 boxes, but once in a while, the program asks how many boxes you have, and somebody enters the number 100000 by accident. In cases like this, Listing 12-6 does a quick reality check. Any number of boxes over 1,000 is tossed out as being unrealistic.
Listing 12-6 watches for a NumberTooLargeException
, but to make life more interesting, Listing 12-6 doesn’t have a catch
clause for the NumberTooLargeException
. In spite of this, everything still works out just fine. It’s fine because NumberTooLargeException
is declared to be a subclass of OutOfRangeException
, and Listing 12-6 has a catch
clause for the OutOfRangeException
.
You see, because NumberTooLargeException
is a subclass of OutOfRangeException
, any instance of NumberTooLargeException
is just a special kind of OutOfRangeException
. So in Listing 12-6, the computer may start looking for a clause to catch a NumberTooLargeException
. When the computer stumbles upon the OutOfRangeException
catch
clause, the computer says, “Okay, I’ve found a match. I’ll execute the statements in this catch
clause.”
To keep from having to write this whole story over and over again, I introduce some new terminology. I say that the catch
clause with parameter OutOfRangeException
matches the NumberTooLargeException
that’s been thrown. I call this catch
clause a matching catch clause.
The following bullets describe different things that the user may do and how the computer responds. As you read through the bullets, you can follow along by looking at the runs shown in Figure 12-6.
The user enters an ordinary whole number, like the number 3. All the statements in the try
clause are executed. Then the computer skips past all the catch
clauses and executes the code that comes immediately after all the catch
clauses. (See Figure 12-7.)
The user enters something that’s not a whole number, like the word fish. The code throws a NumberFormatException
. The computer skips past the remaining statements in the try
clause. The computer executes the statements inside the first catch
clause — the clause whose parameter is of type NumberFormatException
. Then the computer skips past the second and third catch
clauses and executes the code that comes immediately after all the catch
clauses. (See Figure 12-8.)
The user enters a negative number, like the number –25. The code throws an OutOfRangeException
. The computer skips past the remaining statements in the try
clause. The computer even skips past the statements in the first catch
clause. (After all, an OutOfRangeException
isn’t any kind of a NumberFormatException
. The catch
clause with parameter NumberFormatException
isn’t a match for this OutOfRangeException
.) The computer executes the statements inside the second catch
clause — the clause whose parameter is of type OutOfRangeException
. Then the computer skips past the third catch
clause and executes the code that comes immediately after all the catch
clauses. (See Figure 12-9.)
The user enters an unrealistically large number, like the number 1001. The code throws a NumberTooLargeException
. The computer skips past the remaining statements in the try
clause. The computer even skips past the statements in the first catch
clause. (After all, a NumberTooLargeException
isn’t any kind of NumberFormatException
.)
But, according to the code in Listing 12-5, NumberTooLargeException
is a subclass of OutOfRangeException
. When the computer reaches the second catch
clause, the computer says, “Hmm! A NumberTooLargeException
is a kind of OutOfRangeException
. I’ll execute the statements in this catch
clause — the clause with parameter of type OutOfRangeException
.” In other words, it’s a match.
So, the computer executes the statements inside the second catch
clause. Then the computer skips the third catch
clause and executes the code that comes immediately after all the catch
clauses. (See Figure 12-10.)
Something else, something very unpredictable, happens (I don’t know what). With my unending urge to experiment, I reached into the try
clause of Listing 12-6 and added a statement that throws an IOException
. No reason — I just wanted to see what would happen.
When the code threw an IOException
, the computer skipped past the remaining statements in the try
clause. Then the computer skipped past the statements in the first and second catch
clauses. When the computer reached the third catch
clause, I could hear the computer say, “Hmm! An IOException
is a kind of Exception
. I’ve found a matching catch
clause — a clause with a parameter of type Exception
. I’ll execute the statements in this catch
clause.”
So, the computer executed the statements inside the third catch
clause. Then the computer executed the code that comes immediately after all the catch
clauses. (See Figure 12-11.)
When the computer looks for a matching catch
clause, the computer latches on to the topmost clause that fits one of the following descriptions:
The clause’s parameter type is the same as the type of the exception that was thrown.
The clause’s parameter type is a superclass of the exception’s type.
If a better match appears farther down the list of catch
clauses, that’s just too bad. For instance, imagine that you added a catch
clause with a parameter of type NumberTooLargeException
to the code in Listing 12-6. Imagine, also, that you put this new catch
clause after the catch
clause with parameter of type OutOfRangeException
. Then, because NumberTooLargeException
is a subclass of the OutOfRangeException
class, the code in your new NumberTooLargeException
clause would never be executed. That’s just the way the cookie crumbles.
Java 7 and the multi-catch clause
Starting with Java 7, you can catch more than one kind of exception in a single catch
clause. For example, in a particular inventory program, you might not want to distinguish between the throwing of a NumberFormatException
and your own OutOfRangeException
. In that case, you can rewrite part of Listing 12-6 as follows:
try {
int numBoxes = Integer.parseInt(numBoxesIn);
if (numBoxes < 0) {
throw new OutOfRangeException();
}
if (numBoxes > 1000) {
throw new NumberTooLargeException();
}
out.print(“The value is “);
out.println(
currency.format(numBoxes * boxPrice));
}
catch (
NumberFormatException | OutOfRangeException e
) {
out.print(numBoxesIn);
out.println(“? That’s impossible!”);
}
catch (Exception e) {
out.print(“Something went wrong, “);
out.print(“but I’m clueless about what “);
out.println(“it actually was.”);
}
The pipe symbol, |
, tells Java 7 to catch either a NumberFormatException
or an OutOfRangeException
. If you throw an exception of either type, the program displays the value of numBoxesIn
followed by the text ? That’s impossible!
If you throw an exception that is neither a NumberFormatException
nor an OutOfRangeException
, the program jumps to the last catch clause and displays Something went wrong, but I’m clueless . . .
Throwing caution to the wind
Are you one of those obsessive-compulsive types? Do you like to catch every possible exception before the exception can possibly crash your program? Well, watch out. Java doesn’t let you become paranoid. You can’t catch an exception if the exception has no chance of being thrown.
Consider the following code. The code has a very innocent i++
statement inside a try
clause. That’s fair enough. But then the code’s catch
clause is pretending to catch an IOException
.
// Bad code!
try {
i++;
} catch (IOException e) {
e.printStackTrace();
}
Who is this catch
clause trying to impress? A statement like i++
doesn’t do any input or output. The code inside the try
clause can’t possibly throw an IOException
. So the compiler comes back and says, “Hey, catch
clause. Get real. Get off your high horse.” Well, to be a bit more precise, the compiler’s reprimand reads as follows:
exception java.io.IOException is never thrown in body of corresponding try statement
Doing useful things
So far, each example in this chapter catches an exception, prints a “bad input” message, and then closes up shop. Wouldn’t it be nice to see a program that actually carries on after an exception has been caught? Well, it’s time for something nice. Listing 12-7 has a try
-catch
statement inside a loop. The loop keeps running until the user types something sensible.
Listing 12-7: Keep Pluggin’ Along
import static java.lang.System.out;
import java.util.Scanner;
import java.text.NumberFormat;
class InventoryLoop {
public static void main(String args[]) {
final double boxPrice = 3.25;
boolean gotGoodInput = false;
Scanner keyboard = new Scanner(System.in);
NumberFormat currency =
NumberFormat.getCurrencyInstance();
do {
out.print(“How many boxes do we have? “);
String numBoxesIn = keyboard.next();
try {
int numBoxes = Integer.parseInt(numBoxesIn);
out.print(“The value is “);
out.println
(currency.format(numBoxes * boxPrice));
gotGoodInput = true;
} catch (NumberFormatException e) {
out.println();
out.println(“That’s not a number.”);
}
} while (!gotGoodInput);
out.println(“That’s that.”);
}
}
Figure 12-12 shows a run of the code from Listing 12-7. In the first three attempts, the user types just about everything except a valid whole number. At last, the fourth attempt is a success. The user types 3, and the computer leaves the loop.
Our friends, the good exceptions
A rumor is going around that Java exceptions always come from unwanted, erroneous situations. Although there’s some truth to this rumor, the rumor isn’t entirely accurate. Occasionally, an exception arises from a normal, expected occurrence. Take, for instance, the detection of the end of a file. The following code makes a copy of a file:
try {
while (true) {
dataOut.writeByte(dataIn.readByte());
}
} catch (EOFException e) {
numFilesCopied = 1;
}
To copy bytes from dataIn
to dataOut
, you just go into a while
loop. With its true
condition, the while
loop is seemingly endless. But eventually, you reach the end of the dataIn
file. When this happens, the readByte
method throws an EOFException
(an end-of-file exception). The throwing of this exception sends the computer out of the try
clause and out of the while
loop. From there, you do whatever you want to do in the catch
clause and then proceed with normal processing.
Handle an Exception or Pass the Buck
So you’re getting to know Java, hey? What? You say you’re all the way up to Chapter 12? I’m impressed. You must be a hard worker. But remember, all work and no play. . . .
So, how about taking a break? A little nap could do you a world of good. Is ten seconds okay? Or is that too long? Better make it five seconds.
Listing 12-8 has a program that’s supposed to pause its execution for five seconds. The problem is that the program in Listing 12-8 is incorrect. Take a look at Listing 12-8 for a minute, and then I’ll tell you what’s wrong with it.
Listing 12-8: An Incorrect Program
/*
* This code does not compile.
*/
import static java.lang.System.out;
class NoSleepForTheWeary {
public static void main(String args[]) {
out.print(“Excuse me while I nap “);
out.println(“for just five seconds...”);
takeANap();
out.println(“Ah, that was refreshing.”);
}
static void takeANap() {
Thread.sleep(5000);
}
}
The strategy in Listing 12-8 isn’t bad. The idea is to call the sleep
method, which is defined in the Java API. This sleep
method belongs to the API Thread
class. When you call the sleep
method, the number that you feed it is a number of milliseconds. So, Thread.sleep(5000)
means pause for five seconds.
The problem is that the code inside the sleep
method can throw an exception. This kind of exception is an instance of the InterruptedException
class. When you try to compile the code in Listing 12-8, you get a message such as
unreported exception java.lang.InterruptedException;
must be caught or declared to be thrown
Maybe the message reads
Unhandled exception type InterruptedException
One way or another, the message is unwelcome.
Now, the Java programming language has two different kinds of exceptions. They’re called checked and unchecked exceptions:
The potential throwing of a checked exception must be acknowledged in the code.
The potential throwing of an unchecked exception doesn’t need to be acknowledged in the code.
An InterruptedException
is one of Java’s checked exception types. When you call a method that has the potential to throw an InterruptedException
, you need to acknowledge that exception in the code.
Now, when I say that an exception is acknowledged in the code, what do I really mean?
// The author wishes to thank that InterruptedException,
// without which this code could not have been written.
No, that’s not what it means to be acknowledged in the code. Acknowledging an exception in the code means one of two things:
The statements (including method calls) that can throw the exception are inside a try
clause. That try
clause has a catch
clause with a matching exception type in its parameter list.
The statements (including method calls) that can throw the exception are inside a method that has a throws
clause in its header. The throws
clause contains a matching exception type.
If you’re confused by the wording of these two bullets, don’t worry. The next two listings illustrate the points made in the bullets.
In Listing 12-9, the method call that can throw an InterruptedException
is inside a try
clause. That try
clause has a catch
clause with exception type InterruptedException
.
Listing 12-9: Acknowledging with a try-catch Statement
import static java.lang.System.out;
class GoodNightsSleepA {
public static void main(String args[]) {
out.print(“Excuse me while I nap “);
out.println(“for just five seconds...”);
takeANap();
out.println(“Ah, that was refreshing.”);
}
static void takeANap() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
out.println(“Hey, who woke me up?”);
}
}
}
It’s my custom, at this point in a section, to remind you that a run of Listing Such-and-Such is shown in Figure So-and-So. But the problem here is that Figure 12-13 doesn’t do justice to the code in Listing 12-9. When you run the program in Listing 12-9, the computer displays Excuse me while I nap for just five seconds
, pauses for five seconds, and then displays Ah, that was refreshing
. The code works because the call to the sleep
method, which can throw an InterruptedException
, is inside a try
clause. That try
clause has a catch
clause whose exception is of type InterruptedException
.
So much for acknowledging an exception with a try
-catch
statement. You can acknowledge an exception another way, shown in Listing 12-10.
Listing 12-10: Acknowledging with throws
import static java.lang.System.out;
class GoodNightsSleepB {
public static void main(String args[]) {
out.print(“Excuse me while I nap “);
out.println(“for just five seconds...”);
try {
takeANap();
} catch (InterruptedException e) {
out.println(“Hey, who woke me up?”);
}
out.println(“Ah, that was refreshing.”);
}
static void takeANap()
throws InterruptedException
{
Thread.sleep(5000);
}
}
To see a run of the code in Listing 12-10, refer to Figure 12-13. Once again, Figure 12-13 fails to capture the true essence of the run, but that’s okay. Just remember that in Figure 12-13, the computer pauses for five seconds before it displays Ah, that was refreshing
.
The important part of Listing 12-10 is in the takeANap
method’s header. That header ends with throws InterruptedException
. By announcing that it throws an InterruptedException
, method takeANap
passes the buck. What this throws
clause really says is, “I realize that a statement inside this method has the potential to throw an InterruptedException
, but I’m not acknowledging the exception in a try
-catch
statement. Java compiler, please don’t bug me about this. Instead of having a try
-catch
statement, I’m passing the responsibility for acknowledging the exception to the main
method (the method that called the takeANap
method).”
Indeed, in the main
method, the call to takeANap
is inside a try
clause. That try
clause has a catch
clause with a parameter of type InterruptedException
. So everything is okay. Method takeANap
passes the responsibility to the main
method, and the main
method accepts the responsibility with an appropriate try
-catch
statement. Everybody’s happy. Even the Java compiler is happy.
To better understand the throws
clause, imagine a volleyball game in which the volleyball is an exception. When a player on the other team serves, that player is throwing the exception. The ball crosses the net and comes right to you. If you pound the ball back across the net, you’re catching the exception. But if you pass the ball to another player, you’re using the throws
clause. In essence, you’re saying, “Here, other player. You deal with this exception.”
throws InterruptedException, IOException,
ArithmeticException
The Java API has hundreds of exception types. Several of them are subclasses of the RuntimeException
class. Anything that’s a subclass of RuntimeException
(or a sub-subclass, sub-sub-subclass, and so on) is unchecked. Any exception that’s not a descendent of RuntimeException
is checked. The unchecked exceptions include things that would be hard for the computer to predict. Such things include the NumberFormatException
(of Listings 12-2, 12-4, and others), the ArithmeticException
, the IndexOutOfBoundsException
, the infamous NullPointerException
, and many others. When you write Java code, much of your code is susceptible to these exceptions, but enclosing the code in try
clauses (or passing the buck with throws
clauses) is completely optional.
The Java API also has its share of checked exceptions. The computer can readily detect exceptions of this kind. So Java insists that, for an exception of this kind, any potential exception-throwing statement is acknowledged with either a try
statement or a throws
clause. Java’s checked exceptions include the InterruptedException
(Listings 12-9 and 12-10), the IOException
, the SQLException
, and a gang of other interesting exceptions.
Finishing the Job with a finally Clause
Once upon a time, I was a young fellow, living with my parents in Philadelphia, just starting to drive a car. I was heading toward a friend’s house and thinking about who knows what when another car came from nowhere and bashed my car’s passenger door. This kind of thing is called a RunARedLightException.
Anyway, both cars were still drivable, and we were right in the middle of a busy intersection. To avoid causing a traffic jam, we both pulled over to the nearest curb. I fumbled for my driver’s license (which had a very young picture of me on it), and opened the door to get out of my car.
And that’s when the second accident happened. As I was getting out of my car, a city bus was coming by. The bus hit me and rolled me against my car a few times. This kind of thing is called a DealWithLawyersException.
The truth is that everything came out just fine. I was bruised but not battered. My parents paid for the damage to the car, so I never suffered any financial consequences. (I managed to pass on the financial burden by putting the RunARedLightException
into my throws
clause.)
This incident helps to explain why I think the way I do about exception handling. In particular, I wonder, “What happens if, while the computer is recovering from one exception, a second exception is thrown?” After all, the statements inside a catch
clause aren’t immune to calamities.
Well, the answer to this question is anything but simple. For starters, you can put a try
statement inside a catch
clause. This protects you against unexpected, potentially embarrassing incidents that can crop up during the execution of the catch
clause. But when you start worrying about cascading exceptions, you open up a very slimy can of worms. The number of scenarios is large, and things can become complicated very quickly.
One not-too-complicated thing that you can do is to create a finally
clause. Like a catch
clause, a finally
clause comes after a try
clause. The big difference is that the statements in a finally
clause are executed whether or not an exception is thrown. The idea is, “No matter what happens, good or bad, execute the statements inside this finally
clause.” Listing 12-11 has an example.
Listing 12-11: Jumping Around
import static java.lang.System.out;
class DemoFinally {
public static void main(String args[]) {
try {
doSomething();
} catch (Exception e) {
out.println(“Exception caught in main.”);
}
}
static void doSomething() {
try {
out.println(0 / 0);
} catch (Exception e) {
out.println(
“Exception caught in doSomething.”);
out.println(0 / 0);
} finally {
out.println(“I’ll get printed.”);
}
out.println(“I won’t get printed.”);
}
}
Normally, when I think about a try
statement, I think about the computer recovering from an unpleasant situation. The recovery takes place inside a catch
clause, and then the computer marches on to whatever statements come after the try
statement. Well, if something goes wrong during execution of a catch
clause, this picture can start looking different.
Listing 12-11 gets a workout in Figure 12-14. First, the main
method calls doSomething
. Then, the stupid doSomething
method goes out of its way to cause trouble. The doSomething
method divides 0 by 0, which is illegal and undoable in anyone’s programming language. This foolish action by the doSomething
method throws an ArithmeticException
, which is caught by the try
statement’s one and only catch
clause.
Inside the catch
clause, that lowlife doSomething
method divides 0 by 0 again. This time, the statement that does the division isn’t inside a protective try
clause. That’s okay, because an ArithmeticException
isn’t checked. (It’s one of those RuntimeException
subclasses. It’s an exception that doesn’t have to be acknowledged in a try
or a throws
clause. For details, see the previous section.)
Well, checked or not, the throwing of another ArithmeticException
causes control to jump out of the doSomething
method. But, before leaving the doSomething
method, the computer executes the try
statement’s last will and testament — namely, the statements inside the finally
clause. That’s why, in Figure 12-14, you see the words I’ll get printed
.
Interestingly enough, you don’t see the words I won’t get printed
in Figure 12-14. Because the catch
clause’s execution throws its own uncaught exception, the computer never makes it down past the try
-catch
-finally
statement.
So, the computer goes back to where it left off in the main
method. Back in the main
method, word of the doSomething
method’s ArithmeticException
mishaps causes execution to jump into a catch
clause. The computer prints Exception caught in main
, and then this terrible nightmare of a run is finished.
Close Those Files!
In the year 4839, the inhabitants of Earth will open a time capsule containing Java For Dummies. They’ll notice that several of the book’s examples possess a fatal flaw. “In Chapter 8, Barry reads from a disk file named EmployeeInfo.txt
. His DoPayroll
program grabs ahold of this resource (this file that normally lives outside of his DoPayroll
program). But his DoPayroll
program never releases the resource. In early third millennium terminology, Barry opens a file in the beginning of his program, but doesn’t close the file later in the program.”
Barry did this because he didn’t want to burden his readers with details about closing files. For many simple examples, Java closes a program’s files automatically when the program stops running. But in the year 2978, a reader follows Barry’s example and fails to explicitly close files. This oversight causes the Great Robot Blight of 2980, which leads to the failure of the post-post-post-industrial economy and the eventual cancellation of American Idol.
“So Barry Burd is responsible for all our problems,” say the Earth’s inhabitants in 4839. “Let’s not remove him from his cryogenic freezer.”
How to close a file
In hopes of seeing the year 4840, I add one line to the code in Listing 8-2. The listing already contains the following statement:
Scanner diskScanner =
new Scanner(new File(“EmployeeInfo.txt”));
So at the end of the program’s main
method, I add
diskScanner.close();
Of course, I can add this call to the close
method at any point in the program. My best strategy is to call close
immediately after my last use of the EmployeeInfo.txt
file.
At this point in the book, the people of 4839 will probably say “Barry added this file-closing business as an afterthought. Closing files has nothing to do with the rest of Chapter 12.” But the people of 4839 will be wrong.
A try statement with resources
The trouble with an ordinary call to the close
method is that things can go wrong before Java reaches the close
call. Listing 12-12 is almost identical to Listing 8-2. Listing 12-12 has only one additional statement.
Listing 12-12: Close the File?
import java.util.Scanner;
import java.io.File;
import java.io.IOException;
class DoPayroll {
public static void main(String args[])
throws IOException {
Scanner diskScanner =
new Scanner(new File(“EmployeeInfo.txt”));
for (int empNum = 1; empNum <= 3; empNum++) {
payOneEmployee(diskScanner);
}
diskScanner.close();
}
static void payOneEmployee(Scanner aScanner) {
Employee anEmployee = new Employee();
anEmployee.setName(aScanner.nextLine());
anEmployee.setJobTitle(aScanner.nextLine());
anEmployee.cutCheck(aScanner.nextDouble());
aScanner.nextLine();
}
}
Listing 12-12 looks nice, but looks are deceiving. If something goes wrong in the middle of executing payOneEmployee
, then the program ends abruptly with a stack trace and a big kaboom. The program never reaches the call to diskScanner.close()
, and Barry is still in the cryogenic freezer.
I can enclose some statements inside a try
statement, and I can even add a finally
clause, but when my program uses several resources (many files, a database and a file, or whatever) the buildup of try
statements becomes very complicated. I can make try
statements within catch
clauses and all kinds of crazy combinations. But Java 7 has a better way to solve the problem. In Java 7, I can create a try
-with-resources statement. Listing 12-13 shows you how.
Listing 12-13: Barry’s Redemption
import java.util.Scanner;
import java.io.File;
import java.io.IOException;
class DoPayroll {
public static void main(String args[])
throws IOException {
try (Scanner diskScanner =
new Scanner(new File(“EmployeeInfo.txt”))) {
for (int empNum = 1; empNum <= 3; empNum++) {
payOneEmployee(diskScanner);
}
}
}
static void payOneEmployee(Scanner aScanner) {
Employee anEmployee = new Employee();
anEmployee.setName(aScanner.nextLine());
anEmployee.setJobTitle(aScanner.nextLine());
anEmployee.cutCheck(aScanner.nextDouble());
aScanner.nextLine();
}
}
In Listing 12-13, the declaration of diskScanner
is in parentheses after the word try
. The parenthesized declaration tells Java 7 to close the diskScanner
automatically after execution of the statements in the try
clause. You can declare several resources inside one try
statement’s parentheses. When you do, Java 7 closes all the resources automatically after execution of the try
clause’s statements. You can add catch
clauses and a finally
clause if you want. You can access all kinds of resources (files, databases, connections to servers, and others) and have peace of mind knowing that Java will sever the connections automatically.
Life is good.