Java Exception Anti Patterns

Java exception handling is an important activity to ensure our programs function as expected. In this tutorial we will look at some of the Java exception anti-patterns that occur when working with Java exceptions.

Swallowing Exceptions

When working with checked exceptions we have to handle the exception explicitly by wrapping it an try-catch block or by adding it to the throws clause of a method signature. We may also want to handle Java unchecked / runtime exceptions within our code. One lazy and not recommended way of handling exceptions is to swallow the Java exception.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
private static String readFileAsString(String file) throws IOException {
    FileInputStream is = null;
    try {
        is = new FileInputStream(ResourceUtils.getFileFromResources(file));
        StringBuilder fileContents = new StringBuilder();
        int data = is.read();
        while (data != -1) {
            //read the file
        }

        return fileContents.toString();
    } catch(IOException ioe) {
        return null;
    }
    finally {
        //close the file resource
    }
}

The above code shows a technique known as swallowing the exception. This technique is known as an exception anti-pattern because it hides valuable information that can aid us in analyzing and debugging the program code. It also hides the error from the client of our code, instead of returning the error to the client, making our code more difficult to reason about.
Another way to swallow an exception is to print out the exception to the standard error stream.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
private static String readFileAsString(String file) throws IOException {
    FileInputStream is = null;
    try {
        //
    } catch(IOException ioe) {
        ioe.printStackTrace();
    }
    finally {
        //close the file resource
    }
}

Again this approach is an exception anti-pattern because it can cause performance problems in our code, and also it hides important debugging information. Although, this approach is better than swallowing the exception we can improve the situation by logging the exception.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
private static String readFileAsString(String file) throws IOException {
    FileInputStream is = null;
    try {
        //
    } catch(IOException ioe) {
        if(LOGGER.isDebugEnabled()) {
            LOGGER.logDebug("Unable to process file: " + file, ioe);
        }

      return null;
    }
    finally {
        //close the file resource
    }
}

While this approach can be useful in handling exceptions we need to make sure that we are not swallowing information the client of our code may find useful. It is also important to note that we wrap the logging statement in an if condition as this can prevent performance problems if debugging is disabled – creation of arguments passed to the logging call. In this case some considerations when following this approach is:

  • can we safely ignore the exception if the caller does not need to be made aware of error.
  • is it a scenario that we can safely ignore, such as a failure when closing a resource .
  • can we take an alternative action in the case of failure.

Throwing an Exception From A Finally Block

Another java exception anti-pattern is when we throw an exception from a finally block it takes precedence over an exception that is raised from a catch block. This action will hide the important information that can be gained from the original exception which can make it harder to analyze / debug our programs.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
private static String readFileAsString(String file) throws IOException {
    FileInputStream is = null;
    try {
        //
    } catch(IOException ioe) {
        throw ioe;
    }
    finally {
        throw new RuntimeException("This is an anti-pattern");
    }
}

Returning From A Finally Block

Again a java exception anti-pattern is returning from a finally block, this is similar to throwing an exception from a finally block anti-pattern, in that we lose important debugging information about the exceptional case.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
private static String readFileAsString(String file) throws IOException {
    FileInputStream is = null;
    try {
        //
    } catch(IOException ioe) {
        throw ioe;
    }
    finally {
        return "This is an anti-pattern");
    }
}

Using Exceptions For Explicit Flow Control

This is also a java exception anti-pattern and not a very good idea is using exceptions as a form of goto statement within our code. This practice can make code difficult to read and understand as well as having possible performance implications.

1
2
3
4
5
6
7
8
9
public void throwAsFlowControl() {
    try {
        //go to exception handler
        throw new IOException();

    } catch (IOException e) {
        // execute logic after exception
    }
}

Conclusion

Java exception handling is a practice of ensuring our code works gracefully. However, there are a number of anti-patterns that should be avoided when implementing exception handling. In this tutorial we looked at some of those anti-patterns.