Handling Exceptions

What are best practices for handling exceptions?

Do not ignore Exceptions

Never use an empty catch block to handle exceptions. At least add a comment why it is save to swallow the exception at this point.

try {
  ...
}
catch(final FileNotFoundException e) {
  // OK, we run with the default
}

Better in almost all scenarios is to make a log entry so that the occurrence of the exception can be seen at the selected log level.

try {
  ...
}
catch(final FileNotFoundException e) {
  LOG.debug("Could not find file: {}", e.getMessage());
}

Release Resources

Make sure to clean up your resources prior to forward to the exception handling. The standard pattern for acquiring and releasing a resource (a InputStream in the given example) is:

InputStream in = null;
try {
  in = openStream();
  doSomething(in);
}
catch(final IOException e) {
  // handle the exception here: log or throw,
  // probably throw a business exception
  // ...
}
finally {
  IOUtils.closeQuietly(in)
}

Please note that sometimes it would be of interest to log that closing a stream did not work. In this case logging a warn message would be helpful. Using closeQuietly from commons IO does not log anything. The reason why this is most often ok is that failure on closing the stream is tragic for the system resources, but not for the program. There is nothing it could do and no reason why it should not go on. The information is already read.

If you are using Java 7, releasing resources is a lot easier:

try (final BufferedReader reader =
               new BufferedReader(new FileReader(path))) {
    return reader.readLine();
}

Do not log and throw

'Log and Throw' is defined as an anti-pattern by Tim McCune in Exception-Handling Antipatterns. The point is that the log file should mention each exception (with it's stack trace) only once. Otherwise this poses a burden on the service team wading through MBs of log messages always encountering the same exception at different levels.

On the other side it is always a problem to know who is the last in the row to be responsible to do the logging. It is a burden to have the same exception logged multiple times, but it is a disaster if an exception is not logged at all.

Logging should be done at tier borders, if they are deployed on different nodes. Unique Exception Identifiers help to match exceptions found in log files.

Do not catch java.lang.Exception

Catch Exception as in

try {
  ...
}
catch(final Exception e) {
}

also catches any RuntimeException which may obscure a problem in the code. More often than not the programmer does not have the caught runtime exception in mind, when the catch for the java.lang.Exception is written. Design Exception Class Hierarchy prevents the need to catch java.lang.Exceptions. Otherwise there is an individual catch clause required for every checked or unchecked exception that has to be handled.

Do not analyze caught Exception

The caught exception should not be analyzed to decide how to react.

There is a PMD rule to find locations using instanceof AvoidInstanceofChecksInCatchClause to check the type of an exception. But neither this, nor any other information of an exception should be used to determine how to handle the situation.

The type of the exception should be the only information to do this decision with the use of catch clauses.

This best practice is related to Never analyze the Code Parameter in the Catch Block

Multi-Catch and Rethrow

Catching multiple exceptions with one catch clause is a new feature in Java 7.

public void method() throws AException, BException
try {
  ...
}
catch (final AException | BException e) {
   ...
   throw e;
}

As is shown in the snippet, rethrowing a caught exception in this catch clause is also possible. This was a problem prior to Java 7, where the thrown exception had to be declared as Exception as you can see in the following example:

public void method() throws Exception
try {
  ...
}
catch (final Exception e) {
   ...
   throw e;
}

Since clients usually should not be forced to catch unspecific Exceptions. There are Checkstyle rules to check this: IllegalCatch and IllegalThrows.

Catch late

Exceptions need not to be caught at the first time their occur. The latter in the call hierarchy the exception is handled, the more information is gathered and the more expressive the message can be made. So let an exception bubble up the call hierarchy and catch it as late as possible. But catch as early as all information to describe the exception situation is collected and do not throw an exception that is of a foreign API or a different semantic layer.

Extract try-catch Blocks

The try-catch-finally structure is complicated enough. To write clean code Robert C. Martin advises to only call methods in those blocks. This will make code easier to understand and modify.

The example below is taken from his book Clean Code, p. 47, slightly modified:

public void delete(final Page page) {
  try {
    deletePageAndAllReferences(page);
  }
  catch(final DeletionException e) {
    logError(e);
  }

This is used instead of

public void delete(final Page page) {
  try {
    deletePage(page);
    registry.deleteReference(page.name);
    configKeys.deleteKey(page.name.makeKey());
  }
  catch(final DeletionException e) {
    logger.log(e.getMessage());
  }