What Is an Exception?

Before comparing checked and unchecked exceptions, define exception vs error. An exception interrupts normal program flow, such as invalid input or invalid references. In Java, exceptions are recoverable and should be anticipated and handled.

An error represents abnormal system conditions, usually thrown by the JVM. You should not try to handle them in application code. Examples: OutOfMemoryError, ThreadDeath, StackOverflowError.

/**
 * StackOverflowError example.
 * @author kimtaeng
 */
public class SomeTest {

    public static void test() {
        test();
    }

    public static void main(String[] args) {
        try {
            test();
        } catch (StackOverflowError e) {
            // ...!?
        }
    }
}


Exception Classification

In Java, exceptions are classified as checked or unchecked. A simple rule:

  • Classes not extending RuntimeException are checked.
  • Classes extending RuntimeException are unchecked.

exceptions


RuntimeException is a subclass of Exception, but Java treats it specially. You are not required to handle it explicitly.

(2022.02.24 update) I removed a previous paragraph about transactions. The post did not explain Spring’s default behavior and the content did not fit this category. Apologies for the confusion.


Exception Handling Strategies

There are three common strategies: recovery, propagation, and translation.

Recovery

  • Identify the failure, recover, and continue.
  • Retry after waiting for a condition.
  • Throw an exception after exceeding retry limits.
final int MAX_RETRY = 100;
public Object someMethod() {
    int maxRetry = MAX_RETRY;
    while(maxRetry > 0) {
        try {
            ...
        } catch(SomeException e) {
            // log and wait
        } finally {
            // cleanup
        }
    }
    // throw after exceeding retries
    throw new RetryFailedException();
}

Propagation

  • Do not handle the exception; pass it to the caller.
  • If necessary, log and rethrow.
  • Blindly throwing exceptions without responsibility is careless unless roles are tightly coupled.
// example 1
public void add() throws SQLException {
    // ...
}

// example 2 
public void add() throws SQLException {
    try {
        // ...
    } catch(SQLException e) {
        // log and rethrow
        throw e;
    }
}

Translation

  • Throw a more meaningful exception instead of the original.
  • You can also wrap exceptions to simplify handling.
// translate to a more meaningful exception
public void add(User user) throws DuplicateUserIdException, SQLException {
    try {
        // ...
    } catch(SQLException e) {
        if(e.getErrorCode() == MysqlErrorNumbers.ER_DUP_ENTRY) {
            throw DuplicateUserIdException();
        }
        else throw e;
    }
}

// wrap to simplify handling
public void someMethod() {
    try {
        // ...
    }
    catch(NamingException ne) {
        throw new EJBException(ne);
        }
    catch(SQLException se) {
        throw new EJBException(se);
        }
    catch(RemoteException re) {
        throw new EJBException(re);
        }
}


Summary

In Java, exceptions are classified as checked (must be handled) and unchecked (extend RuntimeException and are not required to be handled explicitly).