Java checked exceptions VS runtime exceptions

One of my responsibilities at work is to interview candidates for open technical positions. Given that we are a Java shop, most of our questions naturally revolve around Java, both the language itself and the common open source tools and frameworks that are used by Java developers. An area that we often focus on during the interview is the Java exception hierarchy.

Every Java developer has to deal with exceptions in one way or another. They write code to handle exceptions, throw exceptions, and design APIs that use exceptions in a way that clearly communicates exceptional conditions to their users. Java is one of the few, if not the only programming language (I personally don’t know of any other) to have the concept of checked exceptions. Checked exceptions consist of the java.lang.Exception class and all of its subclasses; except for java.lang.RuntimeException and its subclasses (see below). Checked exceptions are exceptions that the Java compiler forces you to handle by either catching the exception, or re-throwing it. The failure to handle a checked exception results in a compiler error. Every Java developer knows what a checked exception is by the mere fact that they have to deal with them in order to get their code to compile.

Here is a simple example that shows some basic handling of checked exceptions, minus the nitty-gritty details.

// All checked exceptions that a method can throw must be
// declared in the method's throws clause
public void executeSqlStatements(String sqlFileName) 
        throws IOException {

   // File operations can throw an IOException.  Listing
   // IOException in the throws clause allows this method
   // to simply re-throw the exception if it is encountered.
   // No other handling is necessary.
   BufferedReader input = new BufferedReader(
      new FileReader(sqlFileName));
   String sqlLine = null;
   try {
      while ((sqlLine = input.readLine()) != null) {
         // Executes a DB query, could throw an
         // SQLException (checked)

         // Could throw an InterruptedException (checked)
   // SQLException and InterruptedException are not listed in
   // the throws clause, so they must be caught and handled here.
   } catch (SQLException e) {
      // IllegalArgumentException is a runtime exception, not
      // required to be listed in the throws clause (although you
      // should for documentation purposes)
      throw new IllegalArgumentException("Error executing SQL: " + 
            sqlLine, e);

   } catch (InterruptedException e) {
      // Log and swallow
      logger.error("Unable to sleep!", e);

   } finally {

What continuously surprises me is the number of Java developers I interview that have no idea what a runtime exception is or how it can be used. Runtime exceptions serve the same purpose as checked exceptions; to communicate exceptional conditions (unexpected failures, etc) to the user. They can be thrown and caught just like checked exceptions. However, handling runtime exceptions is not enforced by the compiler. This is how exceptions work in other programming languages. I was first introduced to the usefulness of runtime exceptions in Java while reading Professional J2EE Design and Development, by Rod Jonson, the creator of the Spring Framework. In that book, Rod questioned the over use of checked exceptions in Java. Rod pointed out that using a checked exception forces the caller of a method to handle that exception, even if they do not know how to handle it. Often times, developers will end up catching the checked exception, only to re-throw it (or another exception). If the code throws a new exception that does not wrap the original exception, the stack trace of the original exception, priceless for debugging, is lost. And, I’m sure we’ve all seen the dreaded “Log and Swallow” anti-pattern when the code doesn’t know what to do with an exception.

What’s the point in catching an exception if nothing can be done about it, or it has to be handled by another layer? You simply have to write a bunch of boiler plate code to catch and re-throw the exception until it gets to somebody that can handle it. Or, you have to pollute the throws clause of your method signature with a bunch of exceptions that your method itself does not throw, or with exceptions that may not be relevant to what that particular method is supposed to do. This is where runtime exceptions save the day. Since runtime exceptions can simply “bubble up” the stack, to either somebody who can handle the exception or to a catch-all, you only have to deal with the exception in one place. This reduces the amount of code you have to write, and reduces of number of places a bug can creep in. It also saves methods from having to catch or re-throw exceptions that they can’t recover from.

I think checked exceptions do still serve a purpose. I believe checked exceptions should be thrown when the caller is expected to handle the exception. For example, imagine you have an object that only does one thing: write data to a database.

public class DataWriter {

   public void updateRow(MyDataObject theData) 
         throws DatabaseConnectionException, ConcurrentModificationException {
      // Make the DB call to update the row

Let’s say it throws two exceptions, DatabaseConnectionException and ConcurrentModificationException. Of these two, I’d say that the ConcurrentModificationException could be handled by the caller. Maybe the caller will read the row that was updated, and present the user with their update and the concurrent update, asking her to either merge or overwrite the data. Maybe the calling code can handle this automatically. Either way, it’s recoverable. So, I might make this a checked exception, forcing the caller to handle it. However, the DatabaseConnectionException is a bit trickier. The caller of updateRow() may not know how to initialize a new database connection, or how to clean up an abandoned connection. I would probably make this exception a runtime exception, and let it propagate up to the top of the stack. This way, we could either show the user a general error message saying that the operation could not be completed (asking them to retry) or try to clean up the first attempt and re-try the operation for a second time automatically. The beauty about this is that this exception can be caught and handled, just like a checked exception, at any layer in the stack. So the layer that knows how to recover from the exception can catch it, and take the necessary action. All of this without having to catch and re-throw the exception once and without having to pollute our method signatures with throws DatabaseConnectionException.

One of the common complaints about using runtime exceptions is that since they do not have to be declared in the throws clause of the method that throws them, like checked exceptions do, it is often unclear what exceptions may be thrown by a method. But, just because they don’t have to be listed in the throws clause doesn’t mean that they can’t be. I believe that every method should list, in its throws clause, what exceptions it throws (checked and runtime). This way it is clear to the user what to expect out of the method. Adequate JavaDoc explaining why each of the exceptions may be thrown is also very useful. If your exception hierarchy is designed well, and you have a good separation of layers in your architecture, no individual method’s throws clause should become too verbose.

It is also important to use some form of catch-all at the top of your stack to prevent any runtime exceptions from slipping out and crashing the VM. I’ve found it helpful to properly log any exceptions caught in the catch-all, so that code can be added to handle or wrap them in a more appropriate place.

The Spring Framework uses runtime exceptions extensively. And at Orbitz, we’ve been exclusively using runtime exceptions for some time now. I think they make the code easier to read, maintain, extend, and comprehend. I can’t even imagine going back to using checked exceptions exclusively.