Software Engineer's Blog

Checked vs Unchecked Exceptions in Java

Checked vs Unchecked Exceptions in Java

In Java, exceptions are categorized into Checked Exceptions and Unchecked Exceptions, depending on how the compiler and exception handling mechanisms operate.

Throwable
 ├─ Error (Unchecked)
 └─ Exception
     ├─ CheckedException (e.g., IOException)
     └─ RuntimeException (Unchecked)

1. Classification Criteria

TypeInheritance HierarchyCompiler EnforcementCommon Examples
Checked ExceptionException (excluding RuntimeException)RequiredIOException, SQLException
Unchecked ExceptionRuntimeException and its subclassesNot RequiredNullPointerException, IllegalArgumentException

2. What is a Checked Exception?

  • Subclasses of Exception, excluding RuntimeException
  • Compiler forces exception handling
  • You must handle them using try-catch or declare them with throws

Common Examples:

  • IOException
  • SQLException
  • FileNotFoundException
  • ClassNotFoundException

3. What is an Unchecked Exception?

  • Subclasses of RuntimeException
  • Compiler does not enforce handling
  • These exceptions can be left unhandled without compilation errors

Common Examples:

  • NullPointerException
  • ArrayIndexOutOfBoundsException
  • IllegalArgumentException

4. Code Examples

Checked Exception (Must be handled)

try {
    FileInputStream fis = new FileInputStream("file.txt");
} catch (FileNotFoundException e) {
    e.printStackTrace();
}

Unchecked Exception (Optional handling)

int[] arr = new int[3];
System.out.println(arr[5]); // May throw ArrayIndexOutOfBoundsException

5. Why the Difference in Enforcement?

Why Checked Exceptions Must Be Handled

  • External, Predictable Factors
    Checked exceptions arise from conditions outside the program’s control, such as file I/O, network issues, or database access. These are predictable events that could happen even during normal program usage.
  • Compiler Enforcement Improves Stability
    By requiring developers to handle or declare these exceptions, the compiler ensures greater reliability and fault tolerance.

Why Unchecked Exceptions Are Optional

  • Logical or Programming Errors
    Unchecked exceptions typically occur due to developer mistakes—such as accessing a null reference or going out of array bounds. These are better prevented by fixing the code, rather than catching them.
  • Avoiding Code Bloat and Hidden Bugs
    Forcing exception handling in every case could clutter the code with unnecessary try-catch blocks and potentially hide bugs. Java allows developers to decide when handling is appropriate for such cases.

6. Error: the Third Category You Don’t Catch

Look at the hierarchy again — Error sits beside Exception, and it’s unchecked too, but for the opposite reason from RuntimeException. Error represents serious JVM-level failures like OutOfMemoryError and StackOverflowError. The compiler doesn’t force you to handle them because a normal application usually shouldn’t try — recovering from a fatal JVM condition is rarely meaningful, and a blanket catch (Throwable) tends to swallow a problem the JVM is trying to surface. (Catching Error at a single top-level boundary to log and shut down cleanly is the exception, not the rule.)

So “unchecked” spans two different things. Error is the let-it-crash category. RuntimeException, on the other hand, covers both genuine programming bugs (NullPointerException, IllegalArgumentException) and deliberate application exceptions — the custom business exceptions the next post builds on all extend RuntimeException.

7. So Which Should You Throw?

For your own exceptions the mechanical rule is short: extends Exception makes it checked, extends RuntimeException makes it unchecked. There’s also a language wrinkle worth knowing — the standard functional interfaces behind Stream/Function pipelines don’t declare checked exceptions, so checked I/O inside them has to be caught or wrapped anyway.

In real projects, the choice leans heavily toward unchecked, and why that is — plus the pattern of wrapping checked exceptions at the boundary — is a topic of its own: how real-world Java developers handle exceptions. And once you’re defining your own, designing them well — naming, hierarchy, HTTP mapping — is the next step.