Chapter 8
IN THIS CHAPTER
Seeing what to do when bad things happen to good programs
Finding out all about exceptions
Using try, catch, and finally
Preventing exceptions from happening in the first place
This chapter is about what happens when Java encounters an error situation that it can’t deal with. Over the years, computer programming languages have devised many ways to deal with these types of errors. The earliest programming languages dealt with them rudely, by abruptly terminating the program and printing out the entire contents of the computer’s memory in hexadecimal. This output was called a dump.
Later programming languages tried various ways to keep the program running when serious errors occurred. In some languages, the statements that could potentially cause an error had elements added to them that would provide feedback about errors. A statement that read data from a disk file, for example, might return an error code if an I/O error occurred. Still other languages let you create a special error processing section of the program to which control would be transferred if an error occurred.
Being an object-oriented programming language, Java handles errors by using special exception objects
that are created when an error occurs. In addition, Java has a special statement called the
try
statement that you must use to deal with exception objects. In this chapter, you find all the gory details of working with exception objects and
try
statements.
An exception is an object that’s created when an error occurs in a Java program and Java can’t automatically fix the error. The exception object contains information about the type of error that occurred. The most important information — the cause of the error — is indicated by the name of the exception class used to create the exception. You usually don’t have to do anything with an exception object other than figure out which one you have.
Each type of exception that can occur is represented by a different exception class. Here are some typical exceptions:
Scanner
class.There are many other types of exceptions, and you find out about many of them in later chapters of this book.
You need to know a few other things about exceptions:
nextInt
method of the
Scanner
class, for example, throws an unchecked exception if the user enters something other than a valid integer value. For more information, read on.Submitted for your approval is a tale of a hastily written Java program, quickly put together to illustrate certain Java programming details while ignoring others. Out of sight, out of mind, as they say. Said program played a guessing game with the user, accepting numeric input via a class called
Scanner
. Yet this same program ignored the very real possibility that the user may enter strange and unexpected data — data that could hardly be considered numeric, at least not in the conventional sense. The time: Now. The place: Here. This program is about to cross over into … the Exception Zone.
The program I’m talking about here is, of course, the guessing-game program that’s appeared in several forms in recent chapters. (You can find the most recent version at the end of Book 2, Chapter 7
.) This program includes a validation routine that prevents the user from making a guess that’s not between 1 and 10. That validation routine, however, assumes that the user entered a valid integer number. If the user enters something other than an integer value, the
nextInt
method of the
Scanner
class fails badly.
Figure 8-1
shows an example of what the console looks like if the user enters text (such as
five
) instead of a number. The first line after the user enters the incorrect data says the program has encountered an exception named
InputMismatchException
. In short, this exception means that the data entered by the user couldn’t be matched with the type of data that the
Scanner
class expected. The
nextInt
method expected to find an integer, and instead, it found the word
five
.
FIGURE 8-1: This program has slipped into the Exception Zone.
You can find the exact statement in your program that caused the exception to occur by examining the lines that are displayed right after the line that indicates which exception was encountered. These lines, called the stack trace,
list the methods that the exception passed through before your program was aborted. Usually, the first method listed is deep in the bowels of the Java API, and the last method listed is your application’s
main
method. Somewhere in the middle, you find the switch from methods in the Java API to a method in your program. That’s usually where you find the statement in your program that caused the error.
In Figure 8-1 , the stack trace lines look something like this:
at java.util.Scanner.throwFor(Scanner.java:909)
at java.util.Scanner.next(Scanner.java:1530)
at java.util.Scanner.nextInt(Scanner.java:2160)
at java.util.Scanner.nextInt(Scanner.java:2119)
at GuessingGameMethod3.getGuess(GuessingGameMethod3.java:51)
at GuessingGameMethod3.playARound(GuessingGameMethod3.java:31)
at GuessingGameMethod3.main(GuessingGameMethod3.java:13)
Each line lists not only a class and method name, but also the name of the source file that contains the class and the line number where the exception occurred. Thus, the first line in this stack trace indicates that the exception is handled in the
throwFor
method of the
Scanner
class at line 909 of the
Scanner.java
file. The next three lines also indicate methods in the
Scanner
class. The first line to mention the
GuessingGame
class (
GuessingGameMethod3
) is the fifth line. It shows that the exception happened at line 51 in the
GuessingGameMethod3.java
file. Sure enough, that’s the line that calls the
nextInt
method of the
Scanner
class to get input from the user.
Whenever you use a statement that might throw an exception, you should write special code to anticipate and catch the exception. That way, your program won’t crash as shown in Figure 8-1 if the exception occurs.
You catch an exception by using a
try
statement, which has this general form:
try
{
statements that can throw exceptions
}
catch (exception-type identifier
)
{
statements executed when exception is thrown
}
Here, you place the statements that might throw an exception within a try block . Then you catch the exception with a catch block .
Here are a few things to note about
try
statements:
catch
block. That way, if the statements in the
try
block might throw more than one type of exception, you can catch each type of exception in a separate
catch
block.try
block is its own self-contained block, separate from the
catch
block. As a result, any variables you declare in the
try
block are not visible to the
catch
block. If you want them to be, declare them immediately before the
try
statement.catch
blocks. For more information about coding
finally
blocks, see the section “Using a
finally
Block
,” later in this chapter.java.lang
package that’s always available, you need to provide an
import
statement for the package that defines the exception class.To illustrate how to provide for an exception, here’s a program that divides two numbers and uses a
try
/
catch
statement to catch an exception if the second number turns out to be zero:
public class DivideByZero
{
public static void main(String[] args)
{
int a = 5;
int b = 0; // you know this won't work
try
{
int c = a / b; // but you try it anyway
}
catch (ArithmeticException e)
{
System.out.println("Oops, you can't "
+ "divide by zero.");
}
}
}
Here, the division occurs within a
try
block, and a
catch
block handles
ArithmeticException
.
ArithmethicException
is defined by
java.lang
, so an
import
statement for it isn’t necessary.
When you run this program, the following is displayed on the console:
Oops, you can't divide by zero.
There’s nothing else to see here. The next section shows a more complicated example, though.
Listing 8-1
shows a simple example of a program that uses a method to get a valid integer from the user. If the user enters a value that isn’t a valid integer, the
catch
block catches the error and forces the loop to repeat.
LISTING 8-1 Getting a Valid Integer
import java.util.*;
public class GetInteger
{
static Scanner sc = new Scanner(System.in);
public static void main(String[] args)
{
System.out.print("Enter an integer: ");
int i = GetAnInteger();
System.out.println("You entered " + i);
}
public static int GetAnInteger()
{
while (true)
{
try
{
return sc.nextInt();
}
catch (InputMismatchException e)
{
sc.next();
System.out.print("That's not "
+ "an integer. Try again: ");
}
}
}
}
Here the statement that gets the input from the user and returns it to the methods called is coded within the
try
block. If the user enters a valid integer, this statement is the only one in this method that gets executed.
If the user enters data that can’t be converted to an integer, however, the
nextInt
method throws an
InputMismatchException
. Then this exception is intercepted by the
catch
block — which disposes of the user’s incorrect input by calling the
next
method, as well as by displaying an error message. Then the
while
loop repeats.
Here’s what the console might look like for a typical execution of this program:
Enter an integer: three
That's not an integer. Try again: 3.001
That's not an integer. Try again: 3
You entered 3
Here are a couple other things to note about this program:
import
statement specifies
java.util.*
to import all the classes from the
java.util
package. That way, the
InputMismatchException
class is imported.
The
next
method must be called in the
catch
block to dispose of the user’s invalid input because the
nextInt
method leaves the input value in the
Scanner
’s input stream if an
InputMismatchException
is thrown. If you omit the statement that calls
next
, the
while
loop keeps reading it, throws an exception, and displays an error message in an infinite loop. If you don’t believe me, look at Figure 8-2
. I found this error out the hard way. (The only way to make it stop is to close the console window.)
FIGURE 8-2: Why you have to call next to discard the invalid input.
The
try
statement is a useful and necessary tool in any Java programmer’s arsenal. The best way to handle exceptions, however, is to prevent them from happening in the first place. That’s not possible all the time, but in many cases it is. The key is to test your data before performing the operation that can lead to an exception and then skipping or bypassing the operation of the data that is problematic. (One thing I really hate is problematic data.)
You can usually avoid the
ArithmethicException
that results from dividing integer data by zero by checking the data before performing the division:
if (b != 0)
c = a / b;
This eliminates the need to enclose the division in a
try
block, because you know that the division by zero won’t happen.
You can apply this same technique to input validation by using the
hasNextInt
method of the
Scanner
class. This method checks the next input value to make sure it’s a valid integer. (The
Scanner
class calls the next input value a token,
but that won’t be on the test.) You can do this technique in several ways, and I’ve been encouraging you to ponder the problem since Book 2, Chapter 2
. Now behold the long-awaited answer: Listing 8-2
shows a version of the
GetInteger
method that uses a
while
loop to avoid the exception.
LISTING 8-2 Another Version of the GetInteger Method
import java.util.*;
public class GetInteger2
{
static Scanner sc = new Scanner(System.in);
public static void main(String[] args)
{
System.out.print("Enter an integer: ");
int i = GetAnInteger();
System.out.println("You entered " + i);
}
public static int GetAnInteger()
{
while (!sc.hasNextInt())
{
sc.nextLine();
System.out.print("That's not "
+ "an integer. Try again: ");
}
return sc.nextInt();
}
}
This is a clever little bit of programming, don’t you think? The conditional expression in the
while
statement calls the
hasNextInt
method of the
Scanner
to see whether the next value is an integer. The
while
loop repeats as long as this call returns
false
, indicating that the next value is not a valid integer. The body of the loop calls
nextLine
to discard the bad data and then displays an error message. The loop ends only when you know that you have good data in the input stream, so the
return
statement calls
nextInt
to parse the data to an integer and return the resulting value.
Java provides a catch-all exception class called
Exception
that all other types of exceptions are based on. (Don’t worry about the details of what I mean by that. When you read Book 3, Chapter 4
, it makes more sense.)
If you don’t want to be too specific in a
catch
block, you can specify
Exception
instead of a more specific exception class. For example:
try
{
int c = a / b;
}
catch (Exception e)
{
System.out.println("Oops, you can't "
+ "divide by zero.");
}
In this example, the
catch
block specifies
Exception
rather than
ArithmeticException
.
If you have some code that might throw several types of exceptions, and you want to provide specific processing for some types but general processing for all the others, code the
try
statement this way:
try
{
// statements that might throw several types of // exceptions
}
catch (InputMismatchException e)
{
// statements that process InputMismatchException
}
catch (IOException e)
{
// statements that process IOException
}
catch (Exception e)
{
// statements that process all other exception types
}
In this example, imagine that the code in the
try
block could throw an
InputMismatchException
, an
IOException
, and perhaps some other type of unanticipated exception. Here the three
catch
blocks provide for each of these possibilities.
In most cases, the
catch block
of a
try
statement won’t do anything at all with the exception object passed to it. You may want to display an error message occasionally, however; exception objects have a few interesting methods that can come in handy from time to time. These methods are listed in Table 8-1
.
TABLE 8-1 Methods of the Exception Class
Method |
Description |
|
Describes the error in a text message. |
|
Prints the stack trace to the standard error stream. |
|
Returns a description of the exception. This description includes the name of the exception class followed by a colon and the
|
The following example shows how you could print the message for an exception in a
catch
block:
try
{
int c = a / b;
}
catch (Exception e)
{
System.out.println(e.getMessage());
}
This code displays the text
/ by zero
on the console if
b
has a value of
0
. You can get even more interesting output by using this line in the
catch
clause:
e.printStackTrace(System.out);
A
finally
block is a block that appears after all the
catch
blocks for a statement. It’s executed whether or not any exceptions are thrown by the
try
block or caught by any
catch
blocks. Its purpose is to let you clean up any mess that might be left behind by the exception, such as open files or database connections.
The basic framework for a
try
statement with a
finally
block is this:
try
{
statements that can throw exceptions
}
catch (exception-type identifier
)
{
statements executed when exception is thrown
}
finally
{
statements that are executed whether or not
exceptions occur
}
Listing 8-3
shows a contrived but helpful example that demonstrates how to use the
finally
clause. In this example, a method called
divideTheseNumbers
tries to divide the numbers twice. If the division fails the first time (due to a divide-by-zero exception), it tries the division again. Completely irrational, I know. But persistent, like a teenager.
LISTING 8-3 A Program That Uses a finally Clause
public class CrazyWithZeros
{
public static void main(String[] args)
{
try
{
int answer = divideTheseNumbers(5, 0); →7
}
catch (Exception e) →9
{
System.out.println("Tried twice, "
+ "still didn't work!");
}
}
public static int divideTheseNumbers(int a, int b) →16
throws Exception
{
int c;
try
{
c = a / b; →22
System.out.println("It worked!"); →23
}
catch (Exception e)
{
System.out.println("Didn't work the first time."); →27
c = a / b; →28
System.out.println("It worked the second time!"); →29
}
finally
{
System.out.println("Better clean up my mess."); →33
}
System.out.println("It worked after all."); →35
return c; →36
}
}
Here’s the console output for the program:
Didn't work the first time.
Better clean up my mess.
Tried twice, still didn't work!
The following paragraphs explain what’s going on, step by step:
divideTheseNumbers
method, passing
5
and
0
as the parameters. You already know that this method isn’t going to work.catch
clause catches any exceptions thrown by line 7.divideTheseNumbers
method declares that it throws
Exception
."It worked!"
is printed. Alas, the division throws an exception, so this line never gets executed.catch
clause catches the exception, and the message
"Didn't work the first time."
is displayed. That’s the first line in the console output.divideTheseNumbers
method stubbornly tries to divide the same two numbers again. This time, no
try
statement is there to catch the error."It worked the second time!"
on the console. (If you do, you’re in an episode of The Twilight Zone.
)
finally
clause is always executed, no matter what happens. That’s where the second line in the console output came from.After the
finally
clause executes, the
ArithmeticException
is thrown back up to the calling method, where it is caught by line 9. That’s where the last line of the console output came from.
try
block ends, and you’d see the message
"It worked after all."
on the console.return
statement would return the result of the division.Checked exceptions are exceptions that the designers of Java feel that your programs absolutely must provide for, one way or another. Whenever you code a statement that could throw a checked exception, your program must do one of two things:
try
statement that has a
catch
block for the exception.throws
clause on the method that contains the statement to indicate that your method doesn’t want to handle the exception, so it’s passing the exception up the line.This is known as the catch-or-throw rule. In short, any method that includes a statement that might throw a checked exception must acknowledge that it knows the exception might be thrown. The method does this by handling it directly or by passing the exception up to its caller.
Here’s a program that uses a class called
FileInputStream
. To create an object from this class, you must pass the constructor a string that contains the path
and name of a file that exists on your computer. If the file can’t be found, the
FileInputStream
throws a
FileNotFoundException
that you must either catch or throw. This class is in the
java.io
package, so any program that uses it must include an
import java.io
statement.
Consider the following program:
import java.io.*;
public class FileException1
{
public static void main(String[] args)
{
openFile("C:\test.txt");
}
public static void openFile(String name)
{
FileInputStream f = new FileInputStream(name);
}
}
This program won’t compile. The compiler issues the following error message:
unreported exception java.io.FileNotFoundException; must be caught or declared to be thrown
This message simply means that you have to deal with the
FileNotFoundException
.
One way to deal with the
FileNotFoundException
is to catch it by using an ordinary
try
statement:
import java.io.*;
public class FileException2
{
public static void main(String[] args)
{
openFile("C:\test.txt");
}
public static void openFile(String name)
{
try
{
FileInputStream f =
new FileInputStream(name);
}
catch (FileNotFoundException e)
{
System.out.println("File not found.");
}
}
}
In this example, the message
"File not found."
is displayed if the
C:\test.txt
file doesn’t exist.
Suppose that you don’t want to deal with this error condition in the
openFile
method, but would rather just pass the exception up to the method that calls the
openFile
method.
To do that, you omit the
try
statement. Instead, you add a
throws
clause to the
openFile
method’s declaration. That clause indicates that the
openFile
method knows it contains a statement that might throw a
FileNotFoundException
but doesn’t want to deal with that exception here. Instead, the exception is passed up to the caller.
Here’s the
openFile
method with the
throws
clause added:
public static void openFile(String name)
throws FileNotFoundException
{
FileInputStream f = new FileInputStream(name);
}
As you can see, the
throws
clause simply lists the exception or exceptions that the method might throw. If more than one exception is in the list, separate the exceptions with commas:
public static void readFile(String name)
throws FileNotFoundException, IOException
Adding a
throws
clause to the
openFile
method means that when the
FileNotFoundException
occurs, it is simply passed up to the method that called the
openFile
method. That means the calling method (in this illustration,
main
) must catch or throw the exception. To catch the exception, the
main
method would have to be coded like this:
public static void main(String[] args)
{
try
{
openFile("C:\test.txt");
}
catch (FileNotFoundException e)
{
System.out.println("File not found.");
}
}
Then, if the file doesn’t exist, the
catch
block catches the exception, and the error message is displayed.
If you don’t want the program to handle the
FileNotFound
exception at all, you can add a
throws
clause to the
main
method, like this:
public static void main(String[] args)
throws FileNotFoundException
{
openFile("C:\test.txt");
}
Then the program abruptly terminates with an exception message and stack trace if the exception occurs.
What if you don’t want to do anything if a checked exception occurs? In other words, you want to simply ignore the exception. You can do that by catching the exception in the
catch
block of a
try
statement but leaving the body of the
catch
block empty. Here’s an example:
public static void openFile(String name)
{
try
{
FileInputStream f = new FileInputStream(name);
}
catch (FileNotFoundException e)
{
}
}
Here the
FileNotFoundException
is caught and ignored. This technique is called swallowing the exception.
If you must swallow exceptions, at least write a message to the console indicating that the exception occurred. That way, you have a constant reminder that the program has some unfinished details you must attend to.
Note that not all exception swallowing is bad. Suppose you want the
openFile
method to return a
boolean
value to indicate whether the file exists, rather than throw an exception. Then you could code the method something like this:
public static boolean openFile(String name)
{
boolean fileOpened = false;
try
{
FileInputStream f = new FileInputStream(name);
fileOpened = true;
}
catch (FileNotFoundException e)
{
}
return fileOpened;
}
Here the exception isn’t really swallowed. Instead, its meaning is converted to a
boolean
result that’s returned from the method. As a result, the error condition indicated by the
FileNotFoundException
isn’t lost.
Although such methods are uncommon, you may want to write methods that throw exceptions all on their own. To do that, you use a
throw
statement. The
throw
statement has the following basic format:
throw new exception-class
();
The exception-class
can be
Exception
or a class that’s derived from
Exception
. You find out how to create your own classes — including exception classes — in Book 3
. For now, I just focus on writing a method that throws a general
Exception
.
Here’s a program that demonstrates the basic structure of a method that throws an exception:
public class MyException
{
public static void main(String[] args)
{
try
{
doSomething(true);
}
catch (Exception e)
{
System.out.println("Exception!");
}
}
public static void doSomething(boolean t)
throws Exception
{
if (t)
throw new Exception();
}
}
Here the
doSomething
method accepts a
boolean
value as a parameter. If this value is
true
, it throws an exception; otherwise it doesn’t do anything.
Here are the essential points to glean from this admittedly trivial example:
throw
statement. The
throw
statement specifies the exception object to be thrown.throw
statement, it must include a
throws
clause in its declaration.
Yup, this example is pretty trivial. But it illustrates the essential points.
A feature that was new in Java 7 lets you catch two or more exception types with a single
catch
statement. Suppose you have a bit of code that might throw two different exceptions — say,
FileNotFoundException
and
IOException
— and you want to handle both identically. One way to do that is to write two
catch
statements (one to catch each section), and then write the same code for both
catch
blocks, like this:
try
{
// statements that might throw FileNotFoundException // or IOException
}
catch (FileNotFoundException e)
{
System.out.println(e.getMessage());
}
catch (IOException e)
{
System.out.println(e.getMessage());
}
This method works, but it’s clumsy. Java lets you specify two or more exception types in the
catch
statement. You separate the exception types with a vertical bar, like this:
{
// statements that might throw FileNotFoundException // or IOException
}
catch (FileNotFoundException | IOException e)
{
System.out.println(e.getMessage());
}
This way, you can handle both exception types with just one block of code.