Proper exception handling is an important part of writing correct Java programs, and unfortunately also a rather complex one since the control flow is invisible compared to returning and checking error codes. You need to be aware of how exceptions work for two reasons:

  • Error reporting, and gracefully handling of errors generated by subsystems.
  • Guarding resources, preventing resource leaks

This article will describe simple rules and useful patterns to help you write exception-safe code.

This article makes a lot of references to you and your responsibility. When this personal pronoun is used, it always refers to the piece of code that is currently under design, be it a class or a method. You (the reader) are encouraged to place yourself in the position of the piece of code and consider your responsibilities and expectations toward pieces of code that you call into, and pieces of code that call you.

Exception Basics

Exceptions can occur at any line of your program. If an exception is thrown, the code that is currently running will be aborted and control will jump to the nearest enclosing exception handler. As such, Exceptions are very useful to signal error conditions without cluttering your code with a cascade of if statements to see whether calls to statements succeeded. Your code will describe the Success flow of your program, while error handling will be confined to specific areas of code (so-called Exception handlers).

You define exception handlers in the following way:

try {
    .
    .
    .
} catch (SomeExceptionType ex) {
    .
} catch (OtherExceptionType ex) {
    .
} finally {
    .
}

catch and finally clauses can be ommitted if you don’t need them. That is, you can just define a try…catch or try…finally handler if you don’t need the other one.

Rule 1 of exception handling

`try…catch` blocks are used for error handling. `try…finally` blocks are used for resource guarding. This article assumes you know how the `try…` blocks work, what is caught by `catch(...)` clauses (viz. the Exception class hierarchy) and when the `finally` clause is executed. If you don’t yet, JFGI ;).

Checked vs. Unchecked Exceptions

Java introduces the concept of Checked Exceptions. Checked Exceptions are special exception types of which the compiler checks whether you handle them, or pass the responsibility of handling them off to your caller. It is forbidden not to handle or pass off responsibility for checked exceptions. These are the exceptions that your editor warns you about if you fail to handle them (and it will usually offer to generate a default exception handler for you that will simply log the error and not do anything else).

Rule 2 of exception handling

NEVER simply let your editor generate a default exception handler for you! The lines of code where checked exceptions are thrown are RARELY the spot where you should be handling them by simply logging them and going on.

Checked exceptions sound like a good idea in theory (more help from the compiler preventing you from making stupid mistakes!). In practice it turns out that this concept needlessly litters your code with exceptions-related boilerplate code: can you really “handle” a FileNotFoundException gracefully deep in the call stack where your openFile() method is called? What, do you retry with a different filename? No, the proper response is to display this error to the user, have him enter a different filename and then try again. However, because FileNotFoundException is a checked exception, you’re forced either to deal with the exception then and there (you can’t) or add the clause throws FileNotFoundException to every method signature in the call chain. If one of those methods is in an external library that you can’t modify, you’re SOL.

A more practical way to deal with checked exceptions that you can’t handle on-the-spot is to turn them into unchecked exceptions. Unchecked exceptions are exceptions that inherit from RuntimeException, so you can simply wrap the checked exception in a RuntimeException and throw that instead:

try {
    .
    . 
    .
} catch (IOException ex) {
    throw new RuntimeException(ex);
}

Yes, you will lose exception type information in this way. In 90% of cases this doesn’t matter; when it does, define a custom subclass of RuntimeException and throw that instead, then catch it in some exception handler somewhere. Alternatively, you can inspect the exception stack with Exception.getCause().

Error Handling: Logging vs. Rethrowing vs. Doing something intelligent

Rule 3 of exception handling

Only catch exceptions if you can deal with them! (Rule 3a: or if you intend to rethrow exceptions to change their type)

“Dealing with them” can be as complex as retrying the operation or choosing a different strategy, or as simple as showing an error message to the user. Most errors you can’t do anything about, so your only recourse is to show them to the user, and let him figure out what is wrong and how to correct the situation. In fact, you should always put the infrastructure to do this in place anyway, even if you intend to catch and really handle some exceptions along the way. There is no way you can handle all exceptions, and keeping the user informed with error messages is always a good idea. Even if the user can’t correct the error immediately, he will come to you with a descriptive error message readily burned into his mind, instead of a generic “something went wrong” message after which you can go dig through logs.

With regards to logging: log as much as you want, but be careful: you may be logging the exact same exception many times in the log, which will confuse you later on as you start digging through the log. There’s no real need to do this, as the final stack trace of the exception will show all the root exception causes and their stack traces as well.

Error pattern: log and show error to user

Idea: you install a generic error handler at the very highest level in your application. Normally, it is bad form to catch exceptions of all types but in this case it is acceptable since you really want to catch any and all errors to prevent a system abort of your application.

In a web application, you typically install this error handler before you start handling a HTTP request. In a GUI application, you install the error handler in the event loop. In case you use a framework, that framework should provide a way to install an exception handler at the dispatcher level.

try {

    switch (whatToDo) {
        case 1: doAction1(); break;
        case 2: doAction2(); break;
        case 3: ...; //

        // The actual handler methods called here are expected to throw exceptions
        // in case anything goes wrong, and not to handle those themselves
        .
        .
    }    

} catch (Exception ex) {
    log.error(ex.getMessage(), ex);
    displayErrorToUser(ex.getMessage());
}

Use Exception.getMessage() to obtain the error message for the exception. Conversely, if you throw exceptions, include a descriptive message to make the contents of this field useful to the user.

In a web application, displayErrorToUser() will simply output a nicely formatted error page, in a GUI application it may popup an alert box, etc. In a console application, it would typically print the error message to System.stderr and exit with an error code. In all cases, the state of the application returns to where the user can make another call: for the web application and the console application, the request/application is finished and the user can try another invocation with different parameters. In the GUI application, the event loop continues and the user can click another button to try again.

Error pattern: retry N times

In some cases of errors, you may want to retry an operation a number of times. In these instances, you typically only do this under very specific circumstances, in which you know it makes sense to retry. In this case, you don’t catch Exception but the specific exception type that you’re trying to recover from.

In this example, we try retrieving a webpage N times in the face of a NoSuchHostException (indicating that we are currently not connect to a network).

int tries = 10;
while (true) {
    try {
    
        readWebPage();
    
    } catch (NoSuchHostException ex) {
        tries—;
        
        if (tries <= 0) 
        throw ex; // All tries used up, give up and throw exception to caller
        
        try {
            Thread.sleep(10000); // Sleep to avoid hammering the server and our CPU
        } catch (InterruptedException ex) {
            // Java threading exception that we can ignore
        }
    }
}

Resource Cleanup

It is important to always guarantee freeing up external resources that the JVM does not have control over. Typical examples of these that you will use in your application include database connections, file handles, network sockets etc. Anything that deals with the outside world (that is to say, anything that does I/O) typically needs to be cleaned up.

In the face of exceptions, it is tricky to guarantee cleanup of allocated resources. See the following piece of code:

Resource r = allocateResource();
doSomethingWith(r);
r.free();

If an exception is thrown in doSomethingWith(), the execution of this piece of code will abort, and hence r.free() will never be called: your program has a resource leak! If this happens often enough, the system may run out of resources and your application will cease to work, necessitating an application/server restart.

Fortunately, this is what the finally block was invented for.

Rule 4 of exception handling

Once you acquire a resource, you become responsible for releasing it. **Always** start a `try…finally` block after acquiring a resource.

The proper way to write this code is:

Resource r = allocateResource();
try {
    doSomethingWith(r);
} finally {
    r.free();
}

This guarantees that:

  • The resource will be released if it was allocated, whether or not doSomethingWith() succeeds or not.
  • The resource will not be released if it was never succesfully acquired: that means an exception would have occurred within allocateResource(), before the try block was even started. It is the responsibility of allocateResource() to either allocate and return or don’t allocate at all; we will describe how to guarantee this in a following section.

Guarding multiple resources

A typical example of where you need resource guarding is when doing database operations. These typically involve a Connection, PreparedStatement and ResultSet, each of which must be closed separately or you will have a database connection leak!

The simple (and proper) way to deal with this is to guard every resource individually:

Connection c = openConnection();
try {
    PreparedStatement s = c.prepareStatement(“...”);
    try {
        ResultSet r = s.execute();
        try {
            doSomethingWith(r);
        } finally {
            r.close();
        }
    } finally {
        s.close();
    }
} finally {
    c.close();
}

If you don’t like the christmas tree-like indenting of this piece of code, you can also achieve (almost) the same effect with a single try…finally block:

Connection        c = null;
PreparedStatement s = null;
ResultSet         r = null;
try {
    c = openConnection();
    s = c.prepareStatement(“...”);
    r = s.execute();
    doSomethingWith(r);
} finally {
    if (r != null) r.close();
    if (s != null) s.close();
    if (c != null) c.close();
}

This code has almost the same behaviour, except for one tricky detail: the close() methods can also throw exceptions! For example, if an exception happens while executing r.close(), the finally handler will be aborted and the ResultSet and Connection will not be closed, again leaking resources.

It is actually rather annoying that closing these resources can throw exceptions, because there is nothing that we can (or want to) do about this anyway. Either the resource doesn’t need to be closed because it already was (good enough for cleanup purposes!) or it can’t be closed for some other reason, and we can’t do much about that anyway except logging it. An easy fix is to create a helper close() method that will eat (and log) exceptions related to closing resources, guaranteeing that our finally block will run to completion:

try {
    .
    .
    .
} finally {
    if (r != null) close(r);
    if (s != null) close(s);
    if (c != null) close(c);
}

// ...

void close(ResultSet r) {
    try {
        r.close();
    } catch (Exception ex) {
        log.error(ex);
    }
}

void close(PreparedStatement s) { /* ... */ }

void close(Connection c) { /* ... */ }

Tip: take a look at the D Programming Language, which has a much more elegant method of writing resource guards.

Resource ownership, and writing a resource allocator function

In the previous sections, we took for granted that the allocateResource() function makes the following guarantee:

  • The resource is allocated if and only if the method returns without throwing an exception.

At that point, the caller of the allocateResource() method becomes the owner of the resource, and becomes responsible for deallocating it. Thinking about the ownership of a resource is very useful if you’re trying to decide who is responsible for deallocating an allocated resource. For example, the following is typically an antipattern:

void foo() {
    Resource r = allocateResource();
    bar(r);
}

void bar(Resource r) {
    try {
        // Do something with r
        // ...
    } finally {
        r.close();
    }
}

foo() triggers allocation of the resource, and then becomes the owner of that resource. However, the resource is ultimately cleaned up by bar(). Apparently, ownership of the resource is passed off along the way, but this is not clearly defined and behaviour is non-local (you cannot tell from looking at foo() that the resource is eventually cleaned up). Also, now you cannot call bar() on the resource anymore without the resource being released (which may be something that you want to do). All in all, it is better to keep the ownership of the resource confined to foo().

Rule 5 of exception handling

The class or method that allocates a resource assumes ownership of this resource and is responsible for cleaning it up.

In some exceptional cases, it may be necessary to deviate from this pattern (due to constraints imposed by external APIs). If a method or class assumes ownership of a resource that is passed into it, document this fact clearly in its docstring!

Now let’s look at how to write an allocator function that deals properly with resource ownership and makes the proper guarantees about the resource (that is, allocated and ownership is passed off, or not allocated at all). Note: you might not need to do this! Often, allocator functions provided by various framework APIs may very well suffice for your purposes, and you could regard the resource setup phase discussed below could be rolled into your “do something with r” phase. Nevertheless, if you do design convenience functions or otherwise, the patterns described here still apply.

An allocator function typically looks like this:

Resource allocateResource() {
    // Create
    Resource r = createResource();
    // Setup
    r.setFoo(foo);
    r.setBar(bar);
    // Pass to caller
    return r;
}

The point is that there are two steps:

  • Creating the resource, typically done in a library function
  • Initializing the resource, setting up various members and other stuff

The catch is that during setup, exceptions can happen, causing the allocator functions to abort. At this point, the resource to the reference will be lost and it will remain unreleased forever and, hence, leak. The trick is to catch any exceptions inside the setup block and in the exception handler, release the resource and re-throw the exception. This guarantees that the exception is signaled to the caller and the resource will be released properly. In Java, we have to deal with checked vs. unchecked exceptions. We could choose to declare checked exceptions, or simply convert all exceptions to unchecked exceptions. In the following example, we’ll do the latter:

Resource allocateResource() {
    Resource r = createResource();
    try {
        r.setFoo(foo);
        r.setBar(bar);
        return r;
    } catch (RuntimeException ex) {
        r.free();
        throw ex; // Unchecked, simply throw
    } catch (Exception ex) {
        r.free();
        throw new RuntimeException(ex); // Checked, wrap
    }
}

As you can see, the exception handlers are rather ugly because we have to deal with the checked/unchecked distinction. Another way that is slightly less repetitive code but also more obscure would be to do run-time type inspection on the exception object:

Resource allocateResource() {
    Resource r = createResource();
    try {
        r.setFoo(foo);
        r.setBar(bar);
        return r;
    } catch (Exception ex) {
        r.free();
        if (ex instanceof RuntimeException)
            throw (RuntimeException)ex;
        else
            throw new RuntimeException(ex);
    }
}

You can spot the similarities between the try…finally pattern used for guarding resources where you assume ownership, and the try…catch pattern used for guarding resources where you prepare to pass off ownership.

Rule 6 of exception handling

Guard resources with try…finally if you assume ownership, and with try…catch if you temporarily assume ownership before passing off ownership.

Combining error handling with resource cleanup

A lot of I/O related code throws IOException exceptions, and it is declared to throw these both on resource allocation, resource use and resource cleanup. Because IOException is a checked exception, you are required to catch these exceptions (or declare that you pass responsibility for catching these exceptions off to your caller). If you want to transform the checked exception into an unchecked exception, do so at the highest level.

Wrap your entire method body in a try…catch block that transforms the checked exception into an unchecked exception, and don’t try to deal with each invocation that can trigger an unchecked exception separately. Then, put nested try…finally blocks inside that exception handler to guard resources allocated inside the block.

Bad:

void writeOut(File file) {
    Reader r;
    try {
        Reader r = new InputStreamReader(new BufferedInputStream(new FileInputStream(file));
    } catch (IOException ex) {
        throw new RuntimeException(ex);
    }
    try {
        String s = null;
        do {
            try {
                s = r.readLine();
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
            if (s != null) System.out.println(s);
        } while (s != null);
        
        } finally {
            try {
                r.close();
            } catch (IOException ex) {
                throw new RuntimeException(ex);
        }
    }
}

Good:

void writeOut(File file) {
    try {
        Reader r = new InputStreamReader(new BufferedInputStream(new FileInputStream(file));
        try {
        
            for (String s = r.readLine(); ; s = r.readLine())
                System.out.println(s);
        
        } finally {
            r.close();
        }
        
    } catch (IOException ex) {
        throw new RuntimeException(ex); // Checked to unchecked
    }
}

Of course, the “bad” example here is quite pathological, but the difference illustrates the difference between integral and ad-hoc exception handling quite well.

Studious readers may notice that even the so-called “good” example is not free from a resource leak! If an exception is thrown inside either the InputStreamReader or BufferedInputStream constructors (unlikely as it is), the open file handle resource held by the FileInputStream will be leaked. That is why I usually overload functions that do I/O to accept both an InputStream as well as a File: the version that handles the File input will take ownership of the file and guarantees that the file handle will be released. As an added bonus, our API just became more flexible because it now can deal with data that arrives from other sources than just a file, such as over the network.

void writeOut(File file) {
    try {
        InputStream is = new FileInputStream(file);
        try {
            writeOut(is);
        } finally {
            is.close();
        }
    } catch (IOException ex) {
        throw new RuntimeException(ex); // Checked to unchecked
    }
}
        
void writeOut(InputStream is) {
    try {
        Reader r = new InputStreamReader(new BufferedInputStream(is));
        
        for (String s = r.readLine(); ; s = r.readLine())
            System.out.println(s);
    
    } catch (IOException ex) {
        throw new RuntimeException(ex); // Checked to unchecked
    }
}

We do not close the Reader anymore because closing the Reader would also trigger a close() on the underlying InputStream, and the writeOut(InputStream) method does not have ownership of that stream. Instead, we rely on the fact that there are no external resources associated with the Reader, and that it will eventually be garbage collected so we can avoid cleaning up that resource. However, in the writeOut(File) call is used, note that the FileInputStream will be cleaned up regardless (because that method owns that stream).

Conclusion

This article has shown a number of principles and patterns to apply when writing code that deals with exceptions, and should help you writing write code that behaves properly and safely in the face of exceptions. If you disagree with any of this, don’t hesitate to leave a comment!