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:RuntimeExceptionand 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
NullPointerExceptionby 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: aPaymentDeclinedExceptionwhere the calling code should offer another payment method. - No, or recovery belongs far away (a global handler) → extend
RuntimeException. Example: anInvalidOrderStateExceptionthat 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?
Is NullPointerException checked or unchecked?
Are errors like OutOfMemoryError checked exceptions?
Should custom exceptions be checked or unchecked?
Can I catch an unchecked exception in Java?
Does throws work with unchecked exceptions?
Want to Build Your Career in Java Full Stack with AI?
Join CodeBegun and train with working industry engineers — Explore the Program

