Chapter 13

Looking Good When Things Take Unexpected Turns

In This Chapter

arrow Recovering from bad input and other nasty situations

arrow Making your code (more or less) crash proof

arrow 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.

April 19, 1957: Herbert Bright, manager of the data processing center at Westinghouse in Pittsburgh, receives an unmarked deck of computer punch cards in the mail (which is like getting an unlabeled CD-ROM in the mail today). Mr. Bright guesses that this deck comes from the development team for FORTRAN — the first computer programming language. He’s been waiting a few years for this software. (No web downloads were available at the time.)

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 (https://groups.google.com/d/forum/alt.folklore.computers), the Free On-line Dictionary of Computing (http://foldoc.org), the “Looking Back” column in Computer Now (http://www.computer.org/portal/web/computingnow/computer), and the web pages of the IEEE (www.computer.org).

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 13-1 shows the software to handle this bit of inventory. The software has a flaw, which is revealed in Figure 13-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 13-1:  Counting Boxes

  import static java.lang.System.out;
import java.util.Scanner;
import java.text.NumberFormat;

public 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));
      keyboard.close();
  }
}

9781118407806-fg1301.tif

Figure 13-1: Three separate runs of the code in Listing 13-1.

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.

cross-reference.eps For a brief explanation of the Integer.parseInt method, see Chapter 11.

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 13-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 13-2, the method Integer.parseInt can throw a NumberFormat
Exception. When you call Integer.parseInt, this NumberFormat
Exception is passed on to you.

tip.eps The Java API (Application Programming Interface) documentation for the parseInt method says, “Throws: NumberFormatException — if the string does not contain a parsable integer.” Once in a while, reading the documentation actually pays.

9781118407806-fg1302.tif

Figure 13-2: Throwing, passing, and catching an exception.

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 13-2 shows the catching of an exception.

Listing 13-2: A Hero Counts Boxes

  import static java.lang.System.out;
import java.util.Scanner;
import java.text.NumberFormat;

public 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.");
       }

       keyboard.close();
  }
}

Figure 13-3 shows three runs of the code from Listing 13-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.parse
Int 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 13-2, the computer jumps directly to the catch (Number
FormatException e) clause. The computer executes the println statement inside the clause and then marches on with normal processing. (If there were statements in Listing 13-2 after the end of the catch clause, the computer would go on and execute them.)

9781118407806-fg1303.tif

Figure 13-3: Three runs of the code in Listing 13-2.

technicalstuff.eps An entire try-catch assembly — complete with a try clause, catch clause, and what have you — is called a try statement. Sometimes, for emphasis, I call it a try-catch statement.

The parameter in a catch clause

Take a look at the catch clause in Listing 13-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 13-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 13-4. When you call getMessage, you fetch some detail about the exception. (In Figure 13-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 13-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.

9781118407806-fg1304.tif

Figure 13-4: Calling an exception object’s methods.

technicalstuff.eps When you mix System.out.println calls with printStackTrace calls, the order in which Java displays the information is not predictable. For example, in Figure 13-4, the text Message: ***For input string: "three"*** may appear before or after the stack trace. If the ordering of this output matters to you, change out.println("Message: ***" to System.err.println("Message: ***".

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 2-13 and 3-4.

Listing 13-3: Making Your Own Kind of Exception

  @SuppressWarnings("serial")
class OutOfRangeException extends Exception {
}

Listing 13-4: Using Your Custom Made Exception

  import static java.lang.System.out;
import java.util.Scanner;
import java.text.NumberFormat;

public 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!");
      }

      keyboard.close();
  }
}

Listings 13-3 and 13-4 remedy a problem that cropped up in Figure 13-3. Look at the last of the three runs in Figure 13-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 13-4 does. To see the upset code, look at Figure 13-5.

9781118407806-fg1305.tif

Figure 13-5: Three runs of the code from Listings 2-13 and 3-4.

The code in Listing 13-3 declares a new kind of exception class — OutOf
RangeException. 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 13-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 13-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!

cross-reference.eps The text @SuppressWarnings("serial") in Listing 13-3 is a Java annotation. For an introduction to annotations, see Chapter 8. For a few words about the SuppressWarnings annotation, see Chapter 9.

technicalstuff.eps If you use Eclipse, you might see a yellow warning marker next to the throw new OutOfRangeException() line in Listing 13-4. When you hover your pointer over the warning marker, Eclipse says, “Resource leak: ‘keyboard’ is not closed at this location.” Eclipse is being very fussy to make sure that your code eventually executes the keyboard.close() statement. (Yes, under certain circumstances, throwing your OutOfRangeException can cause the program to skip the keyboard.close() statement. But no, that can’t happen when you run the code in Listing 13-4.) In my opinion, you can safely ignore this warning.

Who’s going to catch the exception?

Take one more look at Listing 13-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 2-13 and 5-4.

Listing 13-5: Yet Another Exception

  @SuppressWarnings("serial")
class NumberTooLargeException
                  extends OutOfRangeException {
}

Listing 13-6: Where Does the Buck Stop?

  import static java.lang.System.out;
import java.util.Scanner;
import java.text.NumberFormat;

public 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.");

       keyboard.close();
  }
}

To run the code in Listings 2-13 and 5-4, you need one additional Java program file. You need the OutOfRangeException class in Listing 13-3.

Listing 13-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 13-6 does a quick reality check. Any number of boxes over 1,000 is tossed out as being unrealistic.

Listing 13-6 watches for a NumberTooLargeException, but to make life more interesting, Listing 13-6 doesn’t have a catch clause for the Number
TooLargeException. 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 13-6 has a catch clause for the OutOfRangeException.

You see, because NumberTooLargeException is a subclass of OutOfRange
Exception, any instance of NumberTooLargeException is just a special kind of OutOfRangeException. So in Listing 13-6, the computer may start looking for a clause to catch a NumberTooLargeException. When the computer stumbles upon the OutOfRangeExceptioncatch 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 13-6.

9781118407806-fg1306.tif

Figure 13-6: Four runs of the code from Listing 13-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 13-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 13-8.)
    9781118407806-fg1307.tif

    Figure 13-7: No exception is thrown.

    9781118407806-fg1308.tif

    Figure 13-8: A NumberFormatException is thrown.

  • 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 13-9.)
    9781118407806-fg1309.tif

    Figure 13-9: An OutOfRangeException is thrown.

  • 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 NumberFormat
Exception.)

    But, according to the code in Listing 13-5, NumberTooLargeException is a subclass of OutOfRangeException. When the computer reaches the second catch clause, the computer says, “Hmm! A NumberToo
LargeException 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 13-10.)

    9781118407806-fg1310.tif

    Figure 13-10: A NumberTooLargeException is thrown.

  • 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 13-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 13-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.
    9781118407806-fg1311.tif

    Figure 13-11: An IOException is thrown.

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 13-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.

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 13-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 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 13-7 has a try-catch statement inside a loop. The loop keeps running until the user types something sensible.

Listing 13-7: Keep Pluggin’ Along

  import static java.lang.System.out;
import java.util.Scanner;
import java.text.NumberFormat;

public 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.");

      keyboard.close();
  }
}

Figure 13-12 shows a run of the code from Listing 13-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.

9781118407806-fg1312.tif

Figure 13-12: A run of the code in Listing 13-7.

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 13? 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 13-8 has a program that’s supposed to pause its execution for five seconds. The problem is that the program in Listing 13-8 is incorrect. Take a look at Listing 13-8 for a minute, and then I’ll tell you what’s wrong with it.

Listing 13-8: An Incorrect Program

  /*
 * This code does not compile.
 */

import static java.lang.System.out;

public 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 13-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 13-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.

technicalstuff.eps For the purpose of understanding exceptions in general, you don’t need to know exactly what an InterruptedException is. All you really have to know is that a call to Thread.sleep can throw one of these InterruptedException objects. But if you’re really curious, an InterruptedException is thrown when some code interrupts some other code’s sleep. Imagine that you have two pieces of code running at the same time. One piece of code calls the Thread.sleep method. At the same time, another piece of code calls the interrupt method. By calling the interrupt method, the second piece of code brings the first code’s Thread.sleep method to a screeching halt. The Thread.sleep method responds by spitting out an InterruptedException.

Now, the Java programming language has two 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 13-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 13-9: Acknowledging with a try-catch Statement

  import static java.lang.System.out;

public 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 13-13 doesn’t do justice to the code in Listing 13-9. When you run the program in Listing 13-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.

9781118407806-fg1313.tif

Figure 13-13: There’s a five-second pause before the “Ah” line.

So much for acknowledging an exception with a try-catch statement. You can acknowledge an exception another way, shown in Listing 13-10.

Listing 13-10: Acknowledging with throws

  import static java.lang.System.out;

public 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 13-10, refer to Figure 13-13. Once again, Figure 13-13 fails to capture the true essence of the run, but that’s okay. Just remember that in Figure 13-13, the computer pauses for five seconds before it displays Ah, that was refreshing.

The important part of Listing 13-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 Interrupted
Exception. 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.”

remember.eps A statement in a method can throw an exception that’s not matched by a catch clause. This includes situations in which the statement throwing the exception isn’t even inside a try block. When this happens, execution of the program jumps out of the method that contains the offending statement. Execution jumps back to whatever code called the method in the first place.

tip.eps A method can name more than one exception type in its throws clause. Just use commas to separate the names of the exception types, as in the following example:

  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 13-2, 13-4, and others), the ArithmeticException, the Index
OutOfBoundsException, 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 2-13 and 9-4), 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 Run
ARedLightException 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 13-11 has an example.

Listing 13-11: Jumping Around

  import static java.lang.System.out;

public 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 13-11 gets a workout in Figure 13-14. First, the main method calls do
Something. 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.

9781118407806-fg1314.tif

Figure 13-14: Running the code from Listing 13-11.

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 13-14 you see the words I'll get printed.

Interestingly enough, you don’t see the words I won't get printed in Figure 13-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.

A try Statement with Resources

Imagine a program that gets input from two different files or from a Scanner and a disk file. To make sure that you clean up properly, you put close method calls in a finally clause. (See Listing 13-12.)

Listing 13-12: Using Two Files

  import java.io.File;
import java.io.IOException;
import java.util.Scanner;

public class Main {

   public static void main(String args[]) {
      Scanner scan1 = null;
      Scanner scan2 = null;
      try {
         scan1 = new Scanner(new File("File1.txt"));
         scan2 = new Scanner(new File("File2.txt"));
         // Do useful stuff
      } catch (IOException e) {
         // Oops!
      } finally {
         scan1.close();
         scan2.close();
         System.out.println("Done!");
      }
   }
}

In theory, the computer always executes scan1.close() and scan2.close() no matter what goes wrong during execution of the try clause. But that’s theory. In reality, another programmer (not you, of course) might modify the code by closing scan1 in the middle of the try clause:

  try {
   scan1 = new Scanner(new File("File1.txt"));
   scan2 = new Scanner(new File("File2.txt"));
   // Do useful stuff but also ...
   scan1.close();
   scan1 = null;
} catch (IOException e) {
   // Oops!
} finally {
   scan1.close();
   scan2.close();
   System.out.println("Done!");
}

Now you have a real predicament. Inside the finally clause, the value of scan1 is null. The call to scan1.close() fails, so the program throws a NullPointerException and stops running before reaching the call to scan2.close(). In the worst of circumstances, scan2 isn't closed, and your program has File2.txt locked up so that no other program can use the file.

When a program uses several resources (many files, a database and a file, or whatever) the buildup of try statements becomes very complicated. You can make try statements within catch clauses and all kinds of crazy combinations. But Java has a better way to solve the problem. In Java 7 (and later versions of Java), you can create a try-with-resources statement. Listing 13-13 shows you how.

Listing 13-13: Making Sure to Close Resources

  import java.io.File;
import java.io.IOException;
import java.util.Scanner;

public class NewMain {

   public static void main(String args[]) {
      try (Scanner scan1 =
             new Scanner(new File("File1.txt"));
          Scanner scan2 =
             new Scanner(new File("File2.txt"))) {
         // Do useful stuff
      } catch (IOException e) {
         // Oops!
      }
      System.out.println("Done!");
   }
}

In Listing 13-13, the declarations of scan1 and scan2 are in parentheses after the word try. The parenthesized declarations tell Java to close scan1 and scan2 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 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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.129.218.45