JavaException Handlingintermediate
Updated:

Checked vs Unchecked Exceptions in Java

6 min read

The compiler forces you to handle checked exceptions but ignores unchecked ones. Here is why the split exists and how to choose for your own exceptions.

TL;DR – Quick Answer

Checked exceptions extend Exception (but not RuntimeException) and the compiler forces you to catch or declare them — examples are IOException and SQLException. Unchecked exceptions extend RuntimeException, compile without any handling, and usually signal programming bugs like NullPointerException. Use checked types for recoverable external failures, unchecked for code errors.

On This Page

Java is one of the few mainstream languages whose compiler refuses to build your code until you acknowledge certain failures. Write one line that reads a file and javac demands a plan for IOException. Yet the same compiler happily lets a NullPointerException crash production without a word of warning. That asymmetry is the checked vs unchecked split, and understanding why it exists — not just the definitions — is what interviewers are really probing.

The quick verdict

Dimension Checked Unchecked
Parent class Exception (excluding RuntimeException) RuntimeException
Compiler enforcement Must catch or declare with throws None
Typical cause External failure: file, network, DB Programming bug: null, bad index
Examples IOException, SQLException, ParseException NullPointerException, IllegalArgumentException, ArithmeticException
Caller can recover? Often yes — retry, fallback, prompt user Rarely — the code itself is wrong
Modern usage trend Shrinking (frameworks wrap them) Default for custom exceptions

If you remember one sentence: checked exceptions are for things that can go wrong even in correct code; unchecked exceptions are for code that is simply incorrect.

Where the split sits in the hierarchy

The distinction is purely a matter of inheritance. Everything throwable descends from Throwable, which has two children:

  • Error — JVM-level failures (OutOfMemoryError, StackOverflowError). Unchecked, and not meant to be caught.
  • Exception — application-level problems. Checked by default, with one carve-out: RuntimeException and everything under it is unchecked.

So the rule the compiler applies is mechanical: does the thrown type extend RuntimeException (or Error)? Then no enforcement. Otherwise, the method must catch it or declare it. There is no annotation, no keyword — position in the class tree decides everything.

Checked exceptions: contracts the compiler enforces

A checked exception is part of a method's contract, as visible as its return type. This program will not compile as written — try it:

import java.io.FileReader;

public class WontCompile {
    public static void main(String[] args) {
        FileReader reader = new FileReader("config.txt"); // compile error
        System.out.println("Opened file");
    }
}

The compiler stops you with unreported exception FileNotFoundException; must be caught or declared to be thrown. You have exactly two ways to satisfy it:

import java.io.FileReader;
import java.io.FileNotFoundException;

public class Compiles {
    // Option 1: declare it and push the decision to the caller
    static FileReader open(String path) throws FileNotFoundException {
        return new FileReader(path);
    }

    // Option 2: handle it here
    public static void main(String[] args) {
        try {
            FileReader reader = open("config.txt");
            System.out.println("Opened file");
            reader.close();
        } catch (Exception e) {
            System.out.println("Could not open config: " + e.getMessage());
        }
    }
}

Note that close() also throws a checked IOException, which is why the catch above uses the broader type — in real code you would use try-with-resources instead, covered in exception handling in Java.

Interview note: "Checked" refers to compile-time checking only. At runtime the JVM treats both kinds identically — same throw mechanics, same stack unwinding. Candidates who say checked exceptions are "checked at runtime" fail this question immediately.

Unchecked exceptions: bugs wearing an exception costume

Unchecked exceptions compile silently and detonate at runtime. This program compiles without complaint and crashes on line six:

public class CrashesAtRuntime {
    public static void main(String[] args) {
        String[] names = {"Asha", "Ravi"};
        String third = names[2]; // ArrayIndexOutOfBoundsException
        System.out.println(third.toUpperCase());
    }
}

The compiler's silence is deliberate. An ArrayIndexOutOfBoundsException here means the code is wrong — the fix is checking names.length, not wrapping the access in try-catch. The same logic applies to NullPointerException from a missing String null check, or IndexOutOfBoundsException when you call get(5) on an ArrayList of three elements. If the language forced declarations for these, every method in every program would declare all of them, and the declarations would carry zero information.

Common mistake: A common mistake beginners make is "fixing" a NullPointerException by catching it. The exception disappears from the console, but the underlying null is still flowing through the program and will surface somewhere harder to debug. Treat every unchecked exception as an arrow pointing at a bug.

Why modern Java leans unchecked

Checked exceptions looked like a great idea in 1995: the compiler guarantees nobody forgets a failure case. Two decades of large codebases exposed the cost — the declarations spread virally. If a method five levels deep adds throws SQLException, every caller up the chain must either handle it or re-declare it, and most end up doing neither meaningfully:

try {
    repository.save(order);
} catch (SQLException e) {
    throw new RuntimeException(e); // the classic surrender
}

Spring took this to its conclusion: the entire DataAccessException hierarchy is unchecked, and JDBC's checked SQLException is translated automatically. Lambdas made the problem worse — a Function<T, R> cannot throw checked exceptions, so stream pipelines force wrapping. That is why the practical advice in 2026 differs from what older textbooks say: default to unchecked, reserve checked for the rare case where you want to force the caller's hand.

Choosing for your own exceptions

When you design a custom exception, ask one question: can the immediate caller realistically recover?

  • Yes, and I want the compiler to remind them → extend Exception. Example: a PaymentDeclinedException where the calling code should offer another payment method.
  • No, or recovery belongs far away (a global handler) → extend RuntimeException. Example: an InvalidOrderStateException that indicates a business-rule violation upstream code should have prevented.
// PaymentDeclinedException.java
// Checked: caller must decide what to do about a declined payment
public class PaymentDeclinedException extends Exception {
    public PaymentDeclinedException(String message) { super(message); }
}
// InvalidOrderStateException.java
// Unchecked: signals a bug or violated precondition
public class InvalidOrderStateException extends RuntimeException {
    public InvalidOrderStateException(String message) { super(message); }
}

Whichever you pick, keep it consistent across the codebase. A service layer that mixes checked and unchecked exceptions for similar failures forces callers to remember which is which — the worst of both worlds.

Pro tip: When wrapping a checked exception in an unchecked one, always pass the original as the cause: throw new StorageException("save failed", e). Losing the original stack trace to save one constructor argument is a trade you will regret at 2 a.m. during an outage.

Examples worth memorizing from the JDK

Interviewers expect concrete class names, not just categories. For checked exceptions, the reliable set is IOException and its child FileNotFoundException (file system), SQLException (JDBC), ParseException (date and number parsing with the older APIs), and InterruptedException — thrown when a sleeping or waiting thread is interrupted, which you will meet constantly in multithreaded code. For unchecked, know NullPointerException, ArrayIndexOutOfBoundsException, ClassCastException, NumberFormatException (thrown by Integer.parseInt on bad input), IllegalArgumentException and IllegalStateException.

The last two deserve attention because you should be throwing them yourself: IllegalArgumentException when a caller passes a bad value, IllegalStateException when a method is called at the wrong time (reading from a closed connection, starting a started thread). Using the standard unchecked types for precondition failures — instead of inventing custom ones — is a habit that makes your code read like the JDK's own.

How this question is asked in interviews

This topic appears in three escalating forms. Freshers get the definition: which classes are checked, which are unchecked, name two examples of each. At the 2-years-experience level it becomes a design question: would you make this custom exception checked or unchecked, and why? Senior rounds turn it into a debate: "Checked exceptions were a mistake — agree or disagree?" — where the strong answer acknowledges both the compiler-safety argument and the boilerplate cost, then lands on a concrete convention you have actually used.

Whatever level you are at, anchor your answer in the hierarchy (RuntimeException is the dividing line), give one example of each from the JDK, and state the recovery rule: checked for recoverable external failures, unchecked for bugs. That structure covers ninety percent of the follow-ups.

Frequently Asked Questions

Why does the compiler not check RuntimeException?
RuntimeException and its subclasses represent programming errors such as null dereferences or bad array indexes. These can occur on almost any line, so forcing declarations everywhere would bury real logic in boilerplate. The language designers decided such bugs should be fixed in code, not caught.
Is NullPointerException checked or unchecked?
Unchecked. NullPointerException extends RuntimeException, so the compiler never forces you to catch it or declare it. If you see one, the correct response is to fix the null handling in your code rather than wrap the call in a try-catch.
Are errors like OutOfMemoryError checked exceptions?
No. Error and its subclasses sit outside the checked category even though they do not extend RuntimeException. They indicate serious JVM-level failures such as running out of memory or stack space, and application code is not expected to catch or recover from them.
Should custom exceptions be checked or unchecked?
Most modern Java codebases, including Spring, default to unchecked custom exceptions because checked ones clutter every method signature up the call chain. Choose checked only when the caller can realistically recover and you want the compiler to force that decision.
Can I catch an unchecked exception in Java?
Yes, the catch syntax works identically for both kinds. The difference is only that the compiler never requires it. Catching unchecked exceptions makes sense at framework boundaries, for example a global handler that converts them into HTTP 500 responses and logs the stack trace.
Does throws work with unchecked exceptions?
You can write throws NullPointerException on a method, but it has no effect on callers — the compiler ignores it. It is occasionally used as documentation, though a Javadoc @throws tag is the cleaner way to communicate the same information.

Want to Build Your Career in Java Full Stack with AI?

Join CodeBegun and train with working industry engineers — Explore the Program

Apply for Demo Class →
Siva Prasad Galaba
Founder, CodeBegun · Staff Engineer

Founder of CodeBegun. 15+ years building Java systems at companies like Crunchyroll. Teaches Java, Spring Boot and system design the way the industry actually works, and mentors students through projects, mock interviews and placement preparation.

Technically reviewed by CodeBegun Technical TeamLast reviewed 14 July 2026 LinkedIn
Chat with us