Error Handling - Wrox Press Java Programming 24-Hour Trainer 2nd (2015)

Wrox Press Java Programming 24-Hour Trainer 2nd (2015)

Lesson 10. Error Handling

Fixing the compiler’s errors becomes trivial as you become more comfortable with the Java syntax. But you also should ensure that your programs handle runtime errors that may happen regardless of your proficiency with the language itself.

Let’s say you have a Java program that reads customers’ data from a file deployed in production. What’s going to happen if this file gets corrupted? Will the program crash with a scary geeky error message, or will it stay alive, displaying a user-friendly message such as, “There seems to be a problem with the file Customers. Please make sure that the file is not corrupted”? Error processing in the Java world is called exception handling, which is the subject of this lesson. An exception is a runtime error that may stop the execution of your program.

Stack Trace

When a Java application is running, the JVM performs a number of internal and application-specific method calls. If a runtime error occurs that’s not handled by the program, the program prints a stack trace, which reflects in the call stack the sequence of unfortunate events that caused this error. A stack trace helps software developers follow the workflow of the program that led to the error.

To illustrate what a stack trace may look like, consider the program shown in Listing 10-1, which deliberately divides by zero.

Listing 10-1: Generating stack trace by dividing by zero

1 public class TestStackTrace{

2 TestStackTrace()

3 {

4 divideByZero();

5 }

6

7 int divideByZero()

8 {

9 return 25/0;

10 }

11

12 public static void main(String[] args)

13 {

14 new TestStackTrace();

15 }

16 }

Listing 10-2 depicts the output of this program, which has traced what happened in the program stack before the error occurred. Read the output from the last line upward. It shows that the program was executing the methods main(), init() for the constructor, and divideByZero(). The line numbers 14, 4, and 9, respectively, indicate where in the program these methods were called. After that the ArithmeticExceptionwas thrown—the code in line 9 tried to divide by zero. Turning the line numbers on in the Eclipse IDE helps you locate problematic code.

Listing 10-2: Sample stack trace

c:\temp>java TestStackTrace

Exception in thread "main"

java.lang.ArithmeticException: / by zero

at TestStackTrace.divideByZero(TestStackTrace.java:9)

at TestStackTrace.<init>(TestStackTrace.java:4)

at TestStackTrace.main(TestStackTrace.java:14)

Executing any Java program means running multiple threads, as explained in introduction_to_multi-threading, and the stack trace output reflects what was happening in the main thread of the simple TestStackTrace program.

Java Exceptions

In many programming languages, error processing depends on the programmer’s good will and experience. Java forces a programmer to include the error-handling code for certain errors; otherwise the programs won’t even compile.

Say you need to write a piece of code that reads a file containing data about customers. It’s easy to foresee that unless the code includes error handling there is a chance that one day you’ll see a stack trace instead of a customer list.

The creators of Java didn’t want to allow this code to fail just because some programmers are too lazy to include error-handling code. Java forces you to place such code inside a try/catch block, as in Listing 10-3. Whenever you read or write files you have to process input/output (I/O) errors.

Listing 10-3: Catching I/O errors

try {

fileCustomer.read(); // the file may be corrupted or missing

}

catch (IOException ioe) {

System.out.println(

"There seems to be a problem with the file customers.");

}

Read the code from Listing 10-3 as follows: “Try to execute fileCustomer.read(), and if an error occurs, jump into the catch section and execute the code from there.” IOException is a class that contains information about input/output errors.

In the case of an I/O error, the method read() throws an exception (for more details on reading files refer to Chapter 14). The catch block catches this error and processes it. The program doesn’t terminate, and this exception is considered handled. If the method read() finishes successfully, the code in the section catch isn’t executed.

Exception Hierarchy

Errors in Java are represented as classes that can be divided into two major types: those that were caused by bad programming and those that were thrown because of some other external condition. For example, if a program declares a variable of type Tax, but this object was never instantiated, any attempts to call the (non-static) method calcTax() result in NullPointerException:

Tax tax;

tax.calcTax();

This situation could have been predicted and properly handled by the programmer.

If a runtime error can be handled programmatically, the exception is called checked. The method reads() from Listing 10-3 throws an exception and the JVM tries to find the code that handles this error. Such an exception can be anticipated and recovered from without the need to change the code. While the program remains operational, the user can find the missing file containing the list of customers and try again to populate the GUI with this list.

All exceptions are subclasses of Throwable, which has two immediate descendants: Error and Exception.

Subclasses of the class Error are fatal errors and are called unchecked exceptions, and are not required to be caught. You don’t have to put them in try/catch blocks as there is not much you can do if, say, the JVM runs out of memory and crashes.

Subclasses of Exception (excluding RuntimeException) are called checked exceptions and have to be handled in your code.

You can declare and throw your own application-specific exception; for example, LoveFailedException or ShipmentCreationException.

image

Figure 10-1: Figure 10.1. Sample Exceptions hierarchy with custom exception

How is a programmer supposed to know in advance if some Java method may throw a particular exception and that the try/catch block should therefore be used? No need to memorize anything. If a method throws an exception, the Java compiler prints an error message similar to this one:

"Tax.java": unreported exception: java.io.IOException; must be

caught or declared to be thrown at line 57

If you see a message like this, find the description of the class method being invoked or search for the documentation for the exception itself. For example, here’s the description of the java.io.IOException. Add the appropriate try/catch block to handle this exception, as explained in the following section.

Try/Catch Blocks

There are five Java keywords that can be used for exception handling: try, catch, finally, throw, and throws. One try block can have multiple catch blocks, to provide handling for more than one type of error. For example, when a program tries to read a file, the file may not be there—you must catch the FileNotFoundException. If the file is there, but the code tries to read past the end of file, the catch clause for EOFException is necessary. Listing 10-4 illustrates a multi-catch block.

Listing 10-4: One try with multiple catch statements

public void getCustomers() {

try {

fileCustomers.read();

} catch(FileNotFoundException fileEx) {

System.out.println("Cannot find file Customers");

} catch(EOFException eof) {

System.out.println("Done with file read");

} catch(IOException ioe) {

System.out.println("Problem reading file: " +

ioe.getMessage());

}

}

The order of the catch statements may be important if the exceptions being caught belong to the same inheritance branch. For example, the class EOFException is a subclass of the more generic IOException, and you have to put the catch block for the subclass first. If you place the catchblock for IOException before the one for EOFException, the latter block will never be reached—the end-of-file errors will be intercepted by the IOException catch block.

Starting from Java 7 you can catch multiple exceptions in one catch block. For example, the preceding code may be rewritten as follows:

public void getCustomers() {

try {

fileCustomers.read(); // may throw an error

} catch(FileNotFoundException | EOFException | IOException ioe) {

System.out.println("Problem reading file" + ioe.getMessage());

} catch (Exception ex) {

System.out.println("Exception in getCustomers:" +

ex.getMessage());

}

}

A catch block receives an instance of the Exception object that contains a short explanation of a problem, and the method getMessage() of the Exception object returns this info. If the description of an error returned by getMessage() is not clear enough, try the method Exception.toString()instead.

If you need more detailed information about the exception, use the method printStackTrace() on the received Exception object (see Listing 10-6). It prints all internal method calls that led to this exception, as described in the section “Stack Trace” earlier in this lesson.

Using the throws Clause

In some cases it makes more sense to handle an exception not in the method where it happened, but in the calling one. Let’s use the same example of code that reads a file. Because the method read() may throw an IOException, you should either handle it or declare that the calling method may throw it. The latter is done in Listing 10-5.

Listing 10-5: Using the throws clause

public class CustomerList {

public void getAllCustomers() throws IOException {

// Some other code goes here

// Don't use try/catch if you are not handling

// exceptions here

file.read();

}

public static void main(String[] args) {

System.out.println("Customer List");

// Some other code goes here

try {

// Since getAllCustomers() declared an exception,

// either handle it over here, or rethrow it

// (see the throw keyword explanation below)

getAllCustomers();

} catch(IOException ioe) {

System.out.println("Customer List is not available");

}

}

}

In Listing 10-5 IOException has been propagated from the method getAllCustomers() to the main() method, and it has been handled there.

Using the finally Clause

The code can exit the try/catch block in several ways:

· The code inside the try block successfully ends and the program continues.

· The code inside the try block runs into a return statement and the program control returns to the calling method.

· The code inside the try block throws an exception and control goes to the catch block.

As you can see, in some cases only the code from the try block works; in some cases part of the code from the try block and all the code in catch is invoked. If there is a piece of code that must be executed regardless of the success or failure of the code in the try block, put it under the finallyclause.

Listing 10-6: Using the finally clause

try {

file.read();

// file.close(); don't close files inside try block

}

catch(Exception e) {

e.printStackTrace();

}

finally {

try {

file.close();

} catch(IOException ioe) {

ioe.printStackTrace();

}

}

The code in Listing 10-6 will try to close the file regardless of the success of the read operation because the close() function is called in the finally block. If you had placed the close() function inside the try block, then when an exception was thrown, the next code to execute would be in the catch block, skipping the close() operation, which would result in resource leak, the object referred by the file variable would get stuck in memory for some time. As a summary, you should always use the finally clause for the release of system resources. To minimize the programmer’s errors with unclosed resources Java introduced try-with-resources.

Using printStackTrace

In some of the code snippets I invoke the method printStackTrace() just to make the code samples short. But the printStackTrace() is a slow operation and it’s better to extract the error message from the exception object rather than printing the entire stack trace that led to the error.

Try-With-Resources

Starting from Java 7 you can simplify the code in try/catch blocks by using try-with-resources syntax, which directs Java run time to automatically close resources without using the finally clause. You just need to open the resources in the parentheses right after the try keyword, and they’re automatically closed. The next code fragment illustrates try-with-resources. The object InputStream (explained in Lesson 17) is closed automatically without the need to use finally.

InputStream myFileInputStream = null;

try (myFileInputStream = new FileInputStream(“customers.txt”);) {

// the code that reads data from customers.txt goes here

} catch (Exception e) {

e.printStackTrace();

}

The AutoClosable Interface

The automatic closing works only if the resource implements java.lang.AutoCloseable or java.io.Closeable interface, which is the case with FileInputStream. If you want to create your classes that automatically close some resources, have them implement one of these interfaces.

If you are not planning to handle exceptions in the current method, they will be propagated to the calling method. In this case you can use the finally clause without the catch clause where it would be mandatory otherwise:

public void myMethod() throws IOException {

try {

// If an exception occurs the method calling this one

// will deal with it

file.read();

} finally {

file.close();

}

}

The throw Keyword

If an exception has occurred in a method, you may want to do one of the following:

1. Catch the exception.

2. Do some partial error processing (such as error logging).

3. Throw the exception to the calling method for further processing.

4. Just make the user aware of the problem.

In some cases you may want to catch an exception and handle it by throwing another exception (with modified error information) to the calling method.

The throw statement is used to throw Java exception objects. The object that a program throws must be Throwable. This means that you can throw only subclasses of the Throwable class and that all Java exceptions are its subclasses:

public class CustomerList {

public void getAllCustomers() throws Exception {

// some other code can go here

try {

file.read(); // this line may throw an exception

} catch (IOException ioe) {

// Log this error here, and rethrow another exception

// with a custom error description

throw new Exception ("Customer List is not available"+

ioe.getMessage());

}

}

public static void main(String[] args){

System.out.println("Customer List");

// some other code can go here

try {

// Since the getAllCustomers() declares an exception,

// you should either handle. Rethrowing is also an

// option unless you are in the main() method already.

getAllCustomers();

}

catch(Exception e) {

System.out.println(e.getMessage());

}

}

}

Creating Your Own Exceptions

You can also create exceptions customized to fit their business applications. Just create a class that’s a subclass of one of the classes from the Throwable hierarchy.

Let’s say you are in business of selling bikes and need to validate a customer’s order. You can create a new class, TooManyBikesException, and throw it if someone tries to order more bikes than can fit into the store’s truck. The class BikeOrder shown in Listing 10-7 highlights this idea.

Listing 10-7: Creating and throwing your own exceptions

public class TooManyBikesException extends Exception {

TooManyBikesException (String msgText){

super(msgText);

}

}

public class BikeOrder {

...

public static void validateOrder(String bikeModel,

int quantity) throws TooManyBikesException {

// perform some data validation, and if the entered

// the quantity or model is invalid, do the following:

throw new TooManyBikesException("Cannot ship" +

quantity + "bikes of the model" + bikeModel +);

}

}

public class OrderWindow extends JFrame {

...

public void actionPerformed(ActionEvent e) {

// the user clicked on the "Validate Order" button

try {

bikeOrder.validateOrder("Model-123", 50);

// the next line will be skipped in case of exception

txtResult.setText("Order is valid");

} catch(TooManyBikesException e) {

txtResult.setText(e.getMessage());

}

}

}

TooManyBikesException shown in Listing 10-8 has a unique name, and the text includes some information specific to the shipping business. But another way to provide application-specific information is to declare one or more additional variables in the custom exception. These variables can store multiple pieces of data that describe the erroneous situation.

Listing 10-8: A custom exception with an extra property

public class TooManyBikesException extends Exception{

// Declare an application-specific property

ShippingErrorInfo shippingErrorInfo;

TooManyBikesException(String msgText,

ShippingErrorInfo shippingErrorInfo) {

super(msgText);

this.shippingErrorInfo = shippingErrorInfo;

}

}

Listing 10-8 illustrates the code that adds an application-specific object, ShippingErrorInfo, to the custom exception class TooManyBikesException. An application can prepare the object describing a shipping error and pass it as an argument to the constructor of the exception. The latter stores it in the class variable shippingInfo, and whatever method catches this exception can extract the ShippingErrorInfo object and act accordingly.

In distributed Java EE applications, an exception can travel through several tiers (such as JMS, EJB, Servlet, Swing client), and not only does having a custom property inside the exception object ensure that the valuable information isn’t lost, but each tier can add more specifics to this custom property, which helps in tracing the error.

There is also a class called RemoteException, with a field called detail, that’s used for reporting communication errors. You can extend this class to make remote exceptions more descriptive. This subject may be more appropriate for lessons 25 through 35 about the server-side technologies, but because this is the lesson dedicated to exceptions, I mentioned it here.

Handling programming errors is a must. Unfortunately I’ve seen how some of the Java developers were quietly ignoring errors. Literally, they would write an empty catch clause. This is the worst thing that could be done. It’s like a time bomb that will definitely blow the program up one day, and finding such bombs is usually a time-consuming process. Don’t cut corners; exception handling should be taken very seriously. For more details on Java exceptions refer to Oracle’s tutorial at http://bit.ly/1nO3wIO.

Try It

Create a Swing application for placing bike orders. It has to have a drop-down list (JComboBox) containing several bike models, JTextField for entering quantity, and JButton for validating the order.

Make up several combinations of bike models and quantities that throw an exception. Use the code snippets from Listing 10-7 as examples. The validation should start when the user clicks the button to validate the order.

Lesson Requirements

You should have Java installed.

NOTE You can download the code and resources for this “Try It” from the book’s web page at www.wrox.com/go/javaprog24hr2e. You can find them in Lesson10.zip.

Step-by-Step

1. Create a new Eclipse project called Lesson10.

2. Learn how to work with JComboBox at the following tutorial: http://bit.ly/1qfPjbs .

3. Process events and revalidate the order whenever the user selects a new bike model or changes the quantity of the order.

4. Throw and handle TooManyBikesException if the order can’t be shipped.

TIP Please select the videos for Lesson 10 online at www.wrox.com/go/javaprog24hr2e. You will also be able to download the code and resources for this lesson from the website.