Handling Errors in a Program - Moving into Advanced Topics - Sams Teach Yourself Java in 24 Hours, 7th Edition (2014)

Sams Teach Yourself Java in 24 Hours, 7th Edition (2014)

Part V: Moving into Advanced Topics

Hour 18. Handling Errors in a Program

THIS HOUR’S TO-DO LIST:

Image Learn why errors are called exceptions.

Image Respond to exceptions in your Java programs.

Image Create methods that ignore an exception.

Image Use methods that cause exceptions.

Image Create your own exceptions.

Errors, the bugs, blunders, and typos that prevent a program from running correctly, are a natural part of the software development process. “Natural” is probably the kindest word that’s ever been used to describe them. In my own programming, when I can’t find the cause of an elusive error, I use words that would make Vice President Joe Biden blush.

Some errors are flagged by the compiler and prevent you from creating a class. Others are noted by the interpreter in response to a problem that keeps it from running successfully. Java divides errors into two categories:

Image Exceptions—Events that signal an unusual circumstance has taken place as a program runs

Image Errors—Events that signal the interpreter is having problems that might be unrelated to your program

Errors normally aren’t something a Java program can recover from, so they’re not the focus of this hour. You might have encountered an OutOfMemoryError as you worked on Java programs. There’s nothing that can be done in a Java program to handle that kind of error after it occurs. The program exits with the error.

Exceptions often can be dealt with in a way that keeps a program running properly.

Exceptions

Although you are just learning about them now, you have probably become well acquainted with exceptions during the previous 17 hours. These errors turn up when you write a Java program that compiles successfully but encounters a problem as it runs.

For example, a common programming mistake is to refer to an element of an array that doesn’t exist, as in the following statements:

String[] greek = { "Alpha", "Beta", "Gamma" };
System.out.println(greek[3]);

The String array greek has three elements. Because the first element of an array is numbered 0 rather than 1, the first element is greek[0], the second greek[1], and the third greek[2]. So the statement attempting to display greek[3] is erroneous. The preceding statements compile successfully, but when you run the program, the Java Virtual Machine halts with a message such as the following:

Output

Exception in thread "main" java.lang.ArrayIndexOutBoundsException: 3
at SampleProgram.main(SampleProgram.java:4)

This message indicates that the application has generated an exception, which the JVM noted by displaying an error message and stopping the program.

The error message refers to a class called ArrayIndexOutOfBoundsException in the java.lang package. This class is an exception, an object that represents an exceptional circumstance that has taken place in a Java program.

When a Java class encounters an exception, it alerts users of the class to the error. In this example, the user of the class is the JVM.

All exceptions are subclasses of Exception in the java.lang package. The ArrayIndexOutOfBoundsException does what you would expect—it reports that an array element has been accessed outside the array’s boundaries.

There are hundreds of exceptions in Java. Many such as the array exception indicate a problem that can be fixed with a programming change. These are comparable to compiler errors—after you correct the situation, you don’t have to concern yourself with the exception any longer.


Note

Two terms are used to describe this process: throw and catch. Objects throw exceptions to alert other objects that they have occurred. These exceptions are caught by other objects or the Java Virtual Machine.


Other exceptions must be dealt with every time a program runs by using five new keywords: try, catch, finally, throw, and throws.

Catching Exceptions in a try-catch Block

Up to this point, you have dealt with exceptions by fixing the problem that caused them. There are times you can’t deal with an exception in that manner and must handle the issue within a Java class.

As an introduction to why this is useful, enter the short Java application in Listing 18.1 in a new Java file called Calculator and save the file.

LISTING 18.1 The Full Text of Calculator.java


1: package com.java24hours;
2:
3: public class Calculator {
4: public static void main(String[] arguments) {
5: float sum = 0;
6: for (String argument : arguments) {
7: sum = sum + Float.parseFloat(argument);
8: }
9: System.out.println("Those numbers add up to " + sum);
10: }
11: }


The Calculator application takes one or more numbers as command-line arguments, adds them up, and displays the total.

Because all command-line arguments are represented by strings in a Java application, the program must convert them into floating-point numbers before adding them together. The Float.parseFloat() class method in Line 7 takes care of this, adding the converted number to a variable named sum.

Before running the application, set the following command-line arguments in NetBeans with the command Run, Set Project Configuration, Customize: 7 4 8 1 4 1 4. Choose Run, Run Main Project to run the application and you should see the output in Figure 18.1.

Image

FIGURE 18.1 The output of the Calculator application.

Run the program several times with different numbers as arguments. It should handle them successfully, which might make you wonder what this project has to do with exceptions.

To see the relevance, change the Calculator application’s command-line arguments to 1 3 5x.

The third argument contains a typo—there shouldn’t be an x after the number 5. The Calculator application has no way to know this is a mistake, so it tries to add 5x to the other numbers, causing the following exception to be displayed:

Output

Exception in thread "main" java.lang.NumberFormatException: For input
string: "5x" at sun.misc.FloatingDecimal.readJavaFormatString
(FloatingDecimal.java:1241)
at java.lang.Float.parseFloat(Float.java:452)
at Calculator.main(Calculator.java:7)
Java Result: 1

This message can be informative to a programmer, but it’s not something you’d want a user to see. It would be better to hide the error message and deal with the problem in the program.

Java programs can take care of their own exceptions by using a try-catch block statement, which takes the following form:

try {
// statements that might cause the exception
} catch (Exception e) {
// what to do when the exception occurs
}

A try-catch block must be used on any exception that you want a method of a class to handle. The Exception object that appears in the catch statement should be one of three things:

Image The class of the exception that might occur

Image More than one class of exception, separated by pipe | characters

Image A superclass of several different exceptions that might occur

The try section of the try-catch block contains the statement (or statements) that might throw an exception. In the Calculator application, the call to the Float.parseFloat(String) method in Line 7 of Listing 18.1 throws a NumberFormatException whenever it is used with a string argument that can’t be converted to a floating-point value.

To improve the Calculator application so that it never stops running with this kind of error, you can use a try-catch block.

Create a new Java file called NewCalculator and enter the text of Listing 18.2.

LISTING 18.2 The Full Text of NewCalculator.java


1: package com.java24hours;
2:
3: public class NewCalculator {
4: public static void main(String[] arguments) {
5: float sum = 0;
6: for (String argument : arguments) {
7: try {
8: sum = sum + Float.parseFloat(argument);
9: } catch (NumberFormatException e) {
10: System.out.println(argument + " is not a number.");
11: }
12: }
13: System.out.println("Those numbers add up to " + sum);
14: }
15: }


After you save the application, customize the project configuration and run com.java24hours.NewCalculator with the command-line argument 1 3 5x. You should see the output shown in Figure 18.2.

Image

FIGURE 18.2 The output of the NewCalculator application.

The try-catch block in Lines 7–11 deals with NumberFormatException errors thrown by Float.parseFloat(). These exceptions are caught within the NewCalculator class, which displays an error message for any argument that is not a number. Because the exception is handled within the class, the Java interpreter does not display an error. You can often deal with problems related to user input and other unexpected data by using try-catch blocks.

Catching Several Different Exceptions

A try-catch block can be used to handle several different kinds of exceptions, even if they are thrown by different statements.

One way to handle multiple classes of exceptions is to devote a catch block to each one, as in this code:

String textValue = "35";
int value;
try {
value = Integer.parseInt(textValue);
} catch (NumberFormatException exc) {
// code to handle exception
} catch (ArithmeticException exc) {
// code to handle exception
}

You also can handle multiple exceptions in the same catch block by separating them with pipe | characters and ending the list with a name for the exception variable. Here’s an example:

try {
value = Integer.parseInt(textValue);
} catch (NumberFormatException | ArithmeticException exc) {
// code to handle exceptions
}

If a NumberFormatException or ArithmeticException is caught, it will be assigned to the exc variable.

Listing 18.3 contains an application called NumberDivider that takes two integer command-line arguments and uses them in a division expression.

This application must be able to deal with two potential problems in user input:

Image Nonnumeric arguments

Image Division by zero

Create a new Java file for NumberDivider and enter the text of Listing 18.3 into the source editor.

LISTING 18.3 The Full Text of NumberDivider.java


1: package com.java24hours;
2:
3: public class NumberDivider {
4: public static void main(String[] arguments) {
5: if (arguments.length == 2) {
6: int result = 0;
7: try {
8: result = Integer.parseInt(arguments[0]) /
9: Integer.parseInt(arguments[1]);
10: System.out.println(arguments[0] + " divided by " +
11: arguments[1] + " equals " + result);
12: } catch (NumberFormatException e) {
13: System.out.println("Both arguments must be numbers.");
14: } catch (ArithmeticException e) {
15: System.out.println("You cannot divide by zero.");
16: }
17: }
18: }
19: }


Using command-line arguments to specify two arguments, you can run it with integers, floating-point numbers, and nonnumeric arguments.

The if statement in Line 5 checks to make sure that two arguments are sent to the application. If not, the program exits without displaying anything.

The NumberDivider application performs integer division, so the result is an integer. In integer division, 5 divided by 2 equals 2, not 2.5.

If you use a floating-point or nonnumeric argument, a NumberFormatException is thrown by Lines 8–9 and caught by Lines 14–15.

If you use an integer as the first argument and a zero as the second argument, an ArithmeticExpression is thrown in Lines 8–9 and caught by Lines 14–15.

Some sample output from a successful run of the program is shown in Figure 18.3.

Image

FIGURE 18.3 The output of the NumberDivider application.

Handling Something After an Exception

When you are dealing with multiple exceptions by using try and catch, there are times when you want the program to do something at the end of the block whether an exception occurred or not.

You can handle this by using a try-catch-finally block, which takes the following form:

try {
// statements that might cause the exception
} catch (Exception e) {
// what to do when the exception occurs
} finally {
// statements to execute no matter what
}

The statement or statements within the finally section of the block are executed after everything else in the block, even if an exception occurs.

One place this is useful is in a program that reads data from a file on disk, which you do in Hour 21, “Reading and Writing Files.” There are several ways an exception can occur when you are accessing data: the file might not exist, a disk error could occur, and so on. If the statements to read the disk are in a try section and errors are handled in a catch section, you can close the file in the finally section. This makes sure that the file is closed whether or not an exception is thrown as it is read.

Throwing Exceptions

When you call a method of another class, that class can control how the method is used by throwing exceptions.

As you make use of the classes in the Java class library, the compiler often displays a message such as the following:

Output

NetReader.java:14: unreported exception java.net.MalformedURLException;
must be caught or declared to be thrown

Whenever you see an error stating that an exception “must be caught or declared to be thrown,” it indicates the method you are trying to use throws an exception.

Any class that calls these methods, such as an application that you write, must do one of the following things:

Image Handle the exception with a try-catch block

Image Throw the exception

Image Handle the exception with a try-catch block and then throw it

Up to this point in the hour, you have seen how to handle exceptions. If you would like to throw an exception after handling it, you can use a throw statement followed by the exception object to throw.

The following statements handle a NumberFormatException error in a catch block, and then throw the exception:

float principal;
try {
principal = Float.parseFloat(loanText) * 1.1F;
} catch (NumberFormatException e) {
System.out.println(arguments[0] + " is not a number.");
throw e;
}

This rewritten code handles all exceptions that could be generated in the try block and throws them:

float principal;
try {
principal = Float.parseFloat(loanText) * 1.1F;
} catch (Exception e) {
System.out.println("Error " + e.getMessage());
throw e;
}

Exception is the parent of all exception subclasses. A catch statement will catch the class and any subclass below it in the class hierarchy.

When you throw an exception with throw, it generally means you have not done everything that needs to be done to take care of the exception.

An example of where this might be useful: Consider a hypothetical program called CreditCardChecker, an application that verifies credit card purchases. This application uses a class called CheckDatabase, which has the following job:

1. Make a connection to the credit card lender’s computer.

2. Ask that computer if the customer’s credit card number is valid.

3. Ask the computer if the customer has enough credit to make the purchase.

As the CheckDatabase class is doing its job, what happens if the credit card lender’s computer doesn’t respond to any attempts to connect? This kind of error is exactly the kind of thing that the try-catch block was designed for, and it is used within CheckDatabase to handle connection errors.

If the CheckDatabase class handles this error by itself, the CreditCardChecker application doesn’t know that the exception took place at all. This isn’t a good idea—the application should know when a connection cannot be made so it can report this to the person using the application.

One way to notify the CreditCardChecker application is for CheckDatabase to catch the exception in a catch block, and then throw it again with a throw statement. The exception is thrown in CheckDatabase, which must then deal with it like any other exception.

Exception handling is a way that classes can communicate with each other in the event of an error or other unusual circumstance.

When you use throw in a catch block that catches a parent class, such as Exception, throwing the exception throws that class. This loses some detail of what kind of error occurred, because a subclass such as NumberFormatException tells you a lot more about the problem than simply the Exception class.

Java offers a way to keep this detail: the final keyword in a catch statement.

try {
principal = Float.parseFloat(loanText) * 1.1F;
} catch (final Exception e) {
System.out.println("Error " + e.getMessage());
throw e;
}

That final keyword in catch causes throw to behave differently. The specific class that was caught is thrown.

Ignoring Exceptions

The next technique that is covered this hour is how to ignore an exception completely. A method in a class can ignore exceptions by using a throws clause as part of the method definition.

The following method throws a MalformedURLException, an error that can occur when you are working with web addresses in a Java program:

public void loadURL(String address) throws MalformedURLException {
URL page = new URL(address);
// code to load web page
}

The second statement in this example creates a URL object, which represents an address on the Web. The constructor method of the URL class throws a MalformedURLException to indicate that an invalid address is used, so no object can be constructed. The following statement causes one of these exceptions to be thrown when you attempt to open a connection to that URL:

URL source = new URL("http:www.java24hours.com");

The string http:www.java24hours.com is not a valid URL. It’s missing some punctuation: two slash characters (//) after the colon.

Because the loadURL() method has been declared to throw MalformedURLException errors, it does not have to deal with them inside the method. The responsibility for catching this exception falls to any method that calls the loadURL() method.

Exceptions That Don’t Need catch

Although this hour has shown that exceptions need to be caught with try-catch or declared to be thrown with a throws clause, there’s an exception.

Some exceptions that might occur in a Java program don’t have to be handled in any way. The compiler won’t come to a screeching halt when it detects that the exception is being ignored. These exceptions are called unchecked exceptions, while the others are checked exceptions.

Unchecked exceptions are all subclasses of RuntimeException in the java.lang package. A common example of an unchecked exception is IndexOutOfBoundsException, which indicates that the index used to access an array, string, or array list is not within its boundaries. If an array has five elements and you attempt to read element number 10, this exception occurs.

Another is NullPointerException, which occurs when an object that has no value is used. Object variables have the value null before they are assigned an object. Some methods also return null when an object can’t be returned. If a statement incorrectly assumes an object has a value, a NullPointerException occurs.

Both of these errors are things a programmer could (and should) prevent in the code, not things that require exception handling. If you write a program that accesses an out-of-bounds array element, fix the code that does this and recompile it. If you expect an object and it equals null, check for that with an if conditional before using the object.

The rationale for unchecked exceptions in Java is that they either can be prevented by well-written code, or they could occur so often that catching them all the time would make programs unnecessarily complex. A NullPointerException could occur in every statement in a program where an object’s methods are called.

Of course, just because an exception can be ignored doesn’t mean it should be. You still have the option of using try, catch, and throws with unchecked exceptions.

Throwing and Catching Exceptions

For the hour’s final project, you create a class that uses exceptions to tell another class about an error that has taken place.

The classes in this project are HomePage, a class that represents a personal home page on the Web, and PageCatalog, an application that catalogs these pages.

Enter the text of Listing 18.4 in a new Java file called HomePage.

LISTING 18.4 The Full Text of HomePage.java


1: package com.java24hours;
2:
3: import java.net.*;
4:
5: public class HomePage {
6: String owner;
7: URL address;
8: String category = "none";
9:
10: public HomePage(String inOwner, String inAddress)
11: throws MalformedURLException {
12:
13: owner = inOwner;
14: address = new URL(inAddress);
15: }
16:
17: public HomePage(String inOwner, String inAddress, String inCategory)
18: throws MalformedURLException {
19:
20: this(inOwner, inAddress);
21: category = inCategory;
22: }
23: }


You can use the HomePage class in other programs. This class represents personal web pages. It has three instance variables: address, a URL object representing the address of the page; owner, the person who owns the page; and category, a short comment describing the page’s primary subject matter.

Like any class that creates URL objects, HomePage must either deal with MalformedURLException errors in a try-catch block or declare that it is ignoring these errors.

The class takes the latter course, as shown in Lines 10–11 and Lines 17–18. By using throws in the two constructor methods, HomePage removes the need to deal with MalformedURLException errors in any way.

To create an application that uses the HomePage class, return to NetBeans and create a Java file called PageCatalog that contains the text of Listing 18.5.

LISTING 18.5 The Full Text of PageCatalog.java


1: package com.java24hours;
2:
3: import java.net.*;
4:
5: public class PageCatalog {
6: public static void main(String[] arguments) {
7: HomePage[] catalog = new HomePage[5];
8: try {
9: catalog[0] = new HomePage("Mark Evanier",
10: "http://www.newsfromme.com", "comic books");
11: catalog[1] = new HomePage("Jeff Rients",
12: "http://jrients.blogspot.com", "gaming");
13: catalog[2] = new HomePage("Rogers Cadenhead",
14: "http://workbench.cadenhead.org", "programming");
15: catalog[3] = new HomePage("Juan Cole",
16: "http://www.juancole.com", "politics");
17: catalog[4] = new HomePage("Rafe Colburn",
18: "www.rc3.org");
19: for (int i = 0; i < catalog.length; i++) {
20: System.out.println(catalog[i].owner + ": " +
21: catalog[i].address + " -- " +
22: catalog[i].category);
23: }
24: } catch (MalformedURLException e) {
25: System.out.println("Error: " + e.getMessage());
26: }
27: }
28: }


When you run the compiled application, the output shown in Figure 18.4 is displayed.

Image

FIGURE 18.4 The erroneous output of the PageCatalog application.

The PageCatalog application creates an array of HomePage objects and then displays the contents of the array. Each HomePage object is created using up to three arguments:

Image The name of the page’s owner

Image The address of the page (as a String, not a URL)

Image The category of the page

The third argument is optional, and it is not used in Lines 17–18.

The constructor methods of the HomePage class throw MalformedURLException errors when they receive a string that cannot be converted into a valid URL object. These exceptions are handled in the PageCatalog application by using a try-catch block.

To correct the problem causing the “no protocol” error, edit Line 18 so the string begins with the text http:// like the other web addresses in Lines 9–16. When you run the program again, you see the output shown in Figure 18.5.

Image

FIGURE 18.5 The corrected output of the PageCatalog application.

Summary

Now that you have put Java’s exception handling techniques to use, the subject of errors ought to be a bit more popular than it was at the beginning of the hour.

You can do a lot with these techniques:

Image Catch an exception and deal with it.

Image Ignore an exception, leaving it for another class or the Java interpreter to take care of.

Image Catch several different exceptions in the same try-catch block.

Image Throw your own exception

Managing exceptions in your Java programs makes them more reliable, more versatile, and easier to use because you don’t display any cryptic error messages to people who are running your software.

Workshop

Q&A

Q. Is it possible to create your own exceptions?

A. You can create your own exceptions easily by making them a subclass of an existing exception, such as Exception, the superclass of all exceptions. In a subclass of Exception, there are only two methods you might want to override: Exception() with no arguments andException() with a String as an argument. In the latter, the string should be a message describing the error that has occurred.

Q. Why doesn’t this hour cover how to throw and catch errors in addition to exceptions?

A. Java divides problems into Errors and Exceptions because they differ in severity. Exceptions are less severe, so they are something that should be dealt with in your programs using try-catch or throws in the method declaration. Errors, on the other hand, are more serious and can’t be dealt with adequately in a program.

Two examples of these errors are stack overflows and out-of-memory errors. These can cause the Java interpreter to crash, and there’s no way you can fix them in your own program as the interpreter runs it.

Q. What is the oldest comic strip that’s still running in newspapers?

A. Katzenjammer Kids, which was created by Rudolph Dirks in 1897 and is still offered today by King Features Syndicate. The strip was started only two years after the first comic strip, The Yellow Kid, and is the first to use speech balloons.

Dirks, a German immigrant to the United States, was inspired to create the rebellious kids Hans and Fritz by a children’s story from his native country. He quit the strip in 1912 in a contractual dispute and was succeeded by Harold Knerr, who wrote and drew it until 1949. There have been five subsequent cartoonists working on it. Hy Eisman has been doing it since 1986.

The word katzenjammer literally means “the wailing of cats” in German, but it’s more often used to describe a hangover.

Quiz

Although this hour is literally filled with errors, see if you can answer the following questions about them without making any errors of your own.

1. How many exceptions can a single catch statement handle?

A. Only one.

B. Several different exceptions.

C. This answer intentionally left blank.

2. When are the statements inside a finally section run?

A. After a try-catch block has ended with an exception

B. After a try-catch block has ended without an exception

C. Both

3. With all this talk about throwing and catching, what do the Texas Rangers need to do in the off-season?

A. Get more starting pitching

B. Sign a left-handed power-hitting outfielder who can reach the short porch in right

C. Bring in new middle relievers

Answers

1. B. An Exception object in the catch statement can handle all exceptions of its own class and its superclasses.

2. C. The statement or statements in a finally section always are executed after the rest of a try-catch block, whether an exception has occurred.

3. A. Every answer is correct, but A is more correct than the others and will probably be correct for the next 30 years.

Activities

To see whether you are an exceptional Java programmer, try to make as few errors as possible in the following activities:

Image Modify the NumberDivider application so it throws any exceptions that it catches and run the program to see what happens.

Image There’s a try-catch block in the LottoEvent class you created in Hour 15, “Responding to User Input.” Use this block as a guide to create your own Sleep class, which handles InterruptedException so other classes such as LottoEvent don’t need to deal with them.

To see Java programs that implement these activities, visit the book’s website at www.java24hours.com.