Chapter 13

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 process run-time errors that may happen regardless of your proficiency with the language itself.

Let’s say a Java program that reads customers’ data is 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.

Stack Trace

When a Java application is running, the JVM performs a number of internal and application-specific method calls. If a run-time 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 13-1, which deliberately divides by zero.

download.eps

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

1 class TestStackTrace{    
2    TestStackTrace()
3    {
4        divideByZero();
5    }
6
7    int divideByZero()
8    {
9        return 25/0;
10    }
11
12    static void main(String[]args)
13    {
14           new TestStackTrace();
15
16    }
17 }

Listing 13-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 ArithmeticException was 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.

download.eps

Listing 13-2: Sample stack trace

c:	emp>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 Lesson 20, and the stack trace output reflects what was happening in the main thread of our simple TestStackTrace program.

Java Exceptions

In many programming languages, error processing depends on the programmer’s goodwill and experience. Java forces a programmer to include the error-handling code for the majority of 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 13-3. Whenever you read or write files you have to process input/output (I/O) errors.

download.eps

Listing 13-3: Catching I/O errors

try{
    fileCustomer.read();     
}
catch (IOException e){
    System.out.println("There seems to be a problem with the file customers. ");  
}

Read the code from Listing 13-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 Lesson 16). The catch block will catch this error and process it. The program won’t terminate and this exception is considered handled. If the method read() finishes successfully, the code in the section catch won’t be 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() will result in NullPointerException:

Tax t;
t.calcTax(); 

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

If a run-time error can be fixed, the exception is called checked. The method reads() from Listing 13-3 will throw an exception and the JVM will try 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 Exception are called checked exceptions and have to be handled in your code. You can declare and throw your own application-specific exception, for example BadShipmentAddressException.

Subclasses of the class Error are fatal JVM errors and are called unchecked. You are not required 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.

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 will print 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

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 13-4 illustrates a multi-catch block.

download.eps

Listing 13-4: One try with multiple catch statements

public void getCustomers(){ 
   try{
         fileCustomers.read();
   }catch(FileNotFoundException e){
         System.out.println("Can not find file Customers");
   }catch(EOFException e1){
            System.out.println("Done with file read");
   }catch(IOException e2){
      System.out.println("Problem reading  file " + 
                                          e2.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 catch block 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. A lazy programmer would cut corners and handle the exceptions that may happen during the reading of the file in a coarse-grained way (the compiler won’t complain):

public void getCustomers(){ 
    try{
         fileCustomers.read();
    }catch(Exception e){
         System.out.println("Problem reading  file " + 
                                           e.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 will return this info. If the description of an error returned by getMessage()is not clear enough, try the method toString() instead.

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

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

download.eps

Listing 13-5: Using throw clause

class CustomerList{
 
    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 re-throw it 
       //(see the throw keyword explanation below)
 
         getAllCustomers();  
    }
    catch(IOException e){
      System.out.println("Customer List is not available");
    }
 }

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

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.
  • 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 will work; in some cases part of the code from the try block and all the code in catch will be 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 clause finally.

download.eps

Listing 13-6: Using clause finally

try{
    file.read();
    //file.close();   don't close files inside try block
}
catch(Exception e){
    e.printStackTrace();
}
finally{
   
   file.close();
 
}

The code in Listing 13-6 will definitely close the file regardless of the success of the read operation, because the close() function is called in the finally block. But if you place the close() function inside the try block without writing the finally portion, in case of error during read the program will jump from the failed read() into catch, skipping the close operation. The finally clause is typically used for the release of system resources.

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

void myMethod () throws IOException{
     try{
         file.read();
      }
      finally{
         file.close();
      }
}

The throw Operator

If an exception has occurred in a method, you may want to catch the exception, do partial error processing such as error logging, or throw it to the calling method again for further processing or just to make the user aware of the problem. In some cases you may want to catch an exception and handle it but throw 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 (you can throw a ball, but you can’t throw a grand piano). This means that you can throw only subclasses of the Throwable class and that all Java exceptions are its subclasses:

class CustomerList{
 
  void getAllCustomers() throws Exception{
          
    // some other code goes here
    
    try{
           file.read(); // this line may throw an exception
    } catch (IOException e) {
 
      // Log this error here, and rethrow another exception
       throw new  Exception ("Customer List is not available "+ 
                                           e.getMessage());
         }
   }
 
  public static void main(String[] args){
      System.out.println("Customer List");
        ...
     try{
        
        // Since the  getAllCustomers() declares an exception, 
        // you have  to either  handle  or re-throw it
         getAllCustomers();
    }
    catch(Exception e){
          System.out.println(e.getMessage());
    }
 }

Creating Your Own Exceptions

Programmers can also create exceptions specific to their business applications. Such a class has to be a subclass of one of the classes from the hierarchy of Throwable objects.

Let’s say you are in business selling bikes and need to validate a customer’s order. Create a new class, TooManyBikesException, and throw it if someone tries to order more bikes than can fit into one container, as in the class BikeOrder shown in Listing 13-7.

download.eps

Listing 13-7: Creating and throwing your own exceptions

class TooManyBikesException extends Exception{
 
   TooManyBikesException (String msgText){
        super(msgText);      
   }  
}
 
class BikeOrder{
   ...
  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("Can not ship" + 
         quantity+" bikes of the model " + bikeModel +);
 }
}
 
class OrderWindow extends JFrame{
     ...
 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 13-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 carry multiple pieces of data that describe the erroneous situation and may help in fixing it.

download.eps

Listing 13-8: Custom exception with extra property

class TooManyBikesException extends Exception{
 
   // Declare an application-specific property 
   ShippingErrorInfo shippingErrorInfo; 
 
   TooManyBikesException (String msgText,ShippingErrorInfo shippingErrorInfo){
        super(msgText);  
        this.shippingErrorInfo = shippingErrorInfo; 
   }  
}

Listing 13-8 illustrates the code that adds an application-specific object, ShippingErrorInfo, to the custom 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 will store 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 won’t be lost, but each tier can add more specifics to this custom property, helping 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 the lessons about the server-side technologies (Lesson 26 and above), but since this is the lesson dedicated to Exceptions, I mentioned it here.

Keep in mind that a Java program can communicate with non-Java systems, and that throwing RemoteException or its subclass is a proper way to handle cross-language errors. For example, this technique is used for communications based on Adobe’s AMF protocol between server-side Java and a rich Internet client application written with Adobe Flex.

For more details on Java exceptions refer to Oracle’s tutorial at http://java.sun.com/docs/books/tutorial/essential/exceptions.

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 will throw an exception. Use the code snippets from Listing 13-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.ai

You can download the code and resources for this Try It from the book’s web page at www.wrox.com. You can find them in the Lesson13 folder in the download.

Step-by-Step

1. Create a new Eclipse project, Lesson13.

2. Learn how to work with JComboBox at the following tutorial: http://java.sun.com/docs/books/tutorial/uiswing/components/combobox.html.

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.

cd.ai

Please select Lesson 13 on the DVD with the print book, or watch online at www.wrox.com/go/fainjava to view the video that accompanies this lesson.

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

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