Exception Handling
in Java

 

D R A F T

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

By Alexandre POLOZOFF

polozoff@us.ibm.com

IBM Software Group

AIM Services

Copyright @ 2001, IBM Corporation


 

Introduction

 

This document compiles a list of different appropaches to Exception handling specifically when and when not to handle/throw Exceptions.  Best practises are presented for different scenarios.  Pitfalls and common mistakes made by programmers are outlined.

 

 

 

Case One – Classic programming: returning null

 

Most programmers are familiar with returning null from a function/method and then checking their code to see if the result is null.  This method was the mainstay of structured language programming.  In the object oriented world we want to handle errors as objects and not as null values (which tells us nothing about what happened).  Exceptions provide the object oriented representation of an error and specifically identifies the error that occurred. 

 

The classic programmer would have written code as:

 

public String processFoobar(String s) {

 

      if (s.indexOf(“hello”) == -1) return null;

 

      return s + “ Alex”;

} // processFoobar(String s)

 

public void foobar() {

     

      String answer = processFoobar(“hello”);

      if (answer != null) {

            System.out.println(“answer was “ + answer);

      } else

            System.out.println(“answer was not returned”);

      }

 

      String answer = processFoobar(“goodbye”);

      if (answer != null) {

            System.out.println(“answer was “ + answer);

      } else

            System.out.println(“answer was not returned”);

      }

     

} // foobar()

 

 

Source Code Listing 1.1
Which was good for its day but now with Exception handling in Java people can properly use exceptions to handle error conditions.  An example of the correct way to handle exceptions is as:

 

public String processFoobar(String s) throws NoAnswerException {

 

      if (s.indexOf(“hello”) == -1)

            throw new NoAnswerException();

 

      return s + “ Alex”;

}

 

public void foobar() {

 

      try {

            String answer = processFoobar(“hello”);

            System.out.println(“answer was “ + answer);

} catch (NoAnswerException nae) {

            System.out.println(“no answer returned”);

}

}

 

Source Code Listing 1.2

 

This example shows an incorrect way to process the exception:

 

public void foobar() {

 

      String answer = null;

 

      try {

            answer = processFoobar(“hello”);

      } catch (NoAnswerException nae) {

            System.out.println(“no answer returned”);

}

 

if (answer != null)

      System.out.println(“answer was “ + answer);

}

 

Source Code Listing 1.3

 

The extra step of checking the variable answer and whether it was null is completely unnecessary.  In listing 1.2 the code correctly processes the Exception and the println statement following the call to processFoobar is in the correct spot.  Many programmers erroneously resort to the latter listing 1.3 and unnecessarily check for a null value.

 

Programmers should look to encapsulate as much code as possible within their try-catch blocks and eliminate needless null value checks.


 

Case Two – Unhandled Exceptions

 

Most times middle layer methods do not want to directly process the exception but to percolate it on back up the calling chain.  The way to do this correctly is as:

 

public ResultSet getResultSet(Connection c, PreparedStatement sql)

throws SQLException, IOException

{

ResultSet rs = c.execute(sql);

      return rs;

}

Source Code Listing 2.1

 

An incorrect approach is to catch the exception and throw it again.  This is a waste of code logic because no error handling occurs in this method.  An example of this incorrect approach is as:

 

public ResultSet getResultSet(Connection c, PreparedStatement sql)

throws SQLException, IOException

{

ResultSet rs = null;

try {

rs = c.execute(sql);

      } catch (SQLException se) {

            throw se;

      } catch (IOException ie) {

            throw ie;

      }

      return rs;

}

Source Code Listing 2.2

 

Another incorrect approach is to throw a new exception and not to include the original exception.  This is not good for two reasons, as in the previous case this is unnecessary logic.  But the biggest reason this is not good practise is because the original exception information is lost.  An example of this incorrect approach is as:

 

public ResultSet getResultSet(Connection c, PreparedStatement sql)

throws SQLException, IOException

{

ResultSet rs = null;

try {

rs = c.execute(sql);

      } catch (SQLException se) {

            throw new SQLException();

      } catch (IOException ie) {

            throw new IOException();

      }

      return rs;

}

Source Code Listing 2.3

 

Many programmers make the mistakes shown in listings 2.2 and 2.3.  Programmers must avoid handling exceptions if no business logic or logical processing of the exception is performed.

 

Case Three – Empty Catch Blocks

 

Often times one sees code such as:

 

try {

      String s = null;

      s.equals(“abc”);

} catch (Exception e) { }

 

This is an empty catch block and this causes an untold number of hard to debug problems.  If you have a set of business logic where an exception is being caught then the catch block should take some kind of action. 

 

The only time an empty catch block is acceptable is in a try-catch-finally or try-finally where the finally block is closing resources and each resource close attempt is within its own try-catch block.

 

Appendix A - WebSphere Distributed Exceptions

 

As web applications become more distributed the ability to handle exceptions becomes more difficult.  Distributed Exceptions defined at C:\WebSphere\AppServer\web\InfoCenter\was\atswpgpm.htm allow for chaining exceptions within ones own exceptions.  The text provided with WebSphere v3.5.3 describes the philosophy of handling exceptions in a distributed environment.