Skip to content
Java

Checked vs Unchecked Exceptions in Java, Explained

By The EbookWale Team · Updated June 16, 2026 · 4 min read

Checked exceptions must be handled or declared at compile time; unchecked exceptions happen at runtime and don't have to be declared. Here's the difference, the exception hierarchy, and when to use each.

In Java, checked exceptions are verified at compile time — the compiler forces you to handle or declare them — while unchecked exceptions happen at runtime and carry no such requirement. Checked exceptions model recoverable problems (a missing file); unchecked ones usually signal programming bugs (a null reference). Knowing the difference is a Phase 4 skill from the Java roadmap and a common interview question.

The exception hierarchy

Everything throwable descends from Throwable, which branches:

Throwable
├─ Error            (serious JVM problems — don't catch, e.g. OutOfMemoryError)
└─ Exception
   ├─ RuntimeException  → UNCHECKED (e.g. NullPointerException)
   └─ (everything else) → CHECKED   (e.g. IOException)

So the rule is precise: RuntimeException and its subclasses (plus Error) are unchecked; all other Exceptions are checked.

Throwable Error Exception RuntimeException IOException unchecked unchecked checked
RuntimeException (and Error) are unchecked; the rest are checked.

Checked exceptions

The compiler forces you to deal with these — either catch them or declare them with throws:

void readFile(String path) throws IOException {   // declared
    Files.readAllLines(Path.of(path));            // can throw IOException
}

// or handle it:
try {
    Files.readAllLines(Path.of(path));
} catch (IOException e) {
    // recover
}

They represent recoverable, anticipated conditions outside your control — file not found, network failure, a database hiccup. The compiler makes you acknowledge them.

Unchecked exceptions

RuntimeException and its subclasses (NullPointerException, IllegalArgumentException, IndexOutOfBoundsException) need no declaration and aren’t enforced by the compiler:

int[] a = new int[3];
int x = a[5];                 // ArrayIndexOutOfBoundsException at runtime
String s = null;
s.length();                   // NullPointerException at runtime

These usually mean a programming bug — something that shouldn’t happen if the code is correct. You fix the code rather than routinely catch them.

try / catch / finally

The handling mechanism for both:

try {
    risky();
} catch (SpecificException e) {
    // handle this specific case
} finally {
    // always runs — clean up resources
}

For resources (files, connections), prefer try-with-resources, which closes them automatically:

try (var reader = Files.newBufferedReader(path)) {
    // reader auto-closed, even on exception
}
🔑 REMEMBER — Checked = the compiler forces you to handle it (recoverable, external — like I/O). Unchecked = runtime, your responsibility to prevent (usually a bug — like null or bad index). The dividing line is RuntimeException: under it is unchecked, everything else under Exception is checked.

When to use which

  • Throw a checked exception for a recoverable condition a caller should anticipate and handle (a failed external operation).
  • Throw an unchecked exception for programming errors — invalid arguments, illegal states — that signal a bug to fix, not a condition to routinely catch (throw new IllegalArgumentException("age must be positive")).

Modern Java codebases often lean toward unchecked exceptions to avoid throws clutter, but checked exceptions remain valuable for genuinely recoverable errors.

throw vs throws

Two similar words, different jobs:

  • throw raises an exception: throw new IllegalStateException();
  • throws declares, in a method signature, which checked exceptions it may raise: void read() throws IOException.

Common mistakes

  • Swallowing exceptions — an empty catch {} hides bugs. At least log it.
  • Catching Exception or Throwable broadly — you’ll catch programming bugs you meant to surface.
  • Catching ErrorOutOfMemoryError and friends shouldn’t be caught; the JVM is in trouble.
  • Not using try-with-resources — manual finally cleanup is error-prone; let the language close resources.

Where this fits

Exceptions are Phase 4 of the Java roadmap, part of writing robust object-oriented code, and they pair with the equals/hashCode contract as the “correctness” fundamentals.

Exception design, custom exceptions, and try-with-resources are covered in Java in One Month and the job-ready Java in Three Months; exception strategy at scale is in Java for Staff Engineers.

Checked for recoverable and external, unchecked for bugs — that distinction guides every exception decision.

Frequently asked questions

What is the difference between checked and unchecked exceptions in Java?

Checked exceptions are checked at compile time — the compiler forces you to handle them with try/catch or declare them with throws. They represent recoverable conditions like a missing file (IOException). Unchecked exceptions (RuntimeExceptions) occur at runtime, usually from programming bugs like null access, and do not have to be declared or caught.

What is the Java exception hierarchy?

All exceptions descend from Throwable, which splits into Error (serious problems like OutOfMemoryError you shouldn't catch) and Exception. Exception splits into RuntimeException and its subclasses (unchecked) and all other exceptions (checked). So checked = Exception but not RuntimeException; unchecked = RuntimeException and Error.

When should I use a checked vs unchecked exception?

Use a checked exception for recoverable conditions a caller should anticipate and handle, like network or file errors. Use an unchecked (runtime) exception for programming errors that shouldn't happen if the code is correct, like invalid arguments or null. Many modern Java codebases lean toward unchecked exceptions to avoid clutter.

What is the difference between throw and throws in Java?

throw actually raises an exception: throw new IllegalArgumentException(). throws is part of a method signature declaring which checked exceptions the method might raise, so callers know to handle them: void read() throws IOException. One raises; the other declares.