Java

Chapter 52: Checked vs Unchecked Exception in Java — What the Compiler Nagging Really Marks

Contrast Java checked and unchecked exceptions: compile-time handling rules, RuntimeException subtree, design tradeoffs, and small examples you can tweak in the IDE.

Author: Sushil Kumar

Java checked exceptionJava unchecked exceptionJava RuntimeExceptionJava Exception vs RuntimeExceptionJava compiler exception rules

Chapter 52: Checked vs Unchecked Exception in Java

Java splits throwable problems into two families for compile-time rules. This chapter names them calmly: checked means the compiler watches whether you declared or handled; unchecked means you may throw or catch, but the compiler does not force a plan.

throw was Chapter 50; throws was Chapter 51. Custom types land in Chapter 53.


1. Topic title

Checked: compiler-enforced plan. Unchecked: optional handling, still serious


2. What it means

Unchecked types inherit RuntimeException (or Error, which you should almost never catch). Examples: IllegalArgumentException, NullPointerException, ArithmeticException. You can throws them for documentation, but callers are not required to catch or throws by the compiler.

Checked types inherit Exception but not RuntimeException. Example: java.io.IOException. If your method can let one escape, you either catch it or add throws IOException (Chapter 51). Callers repeat the choice up the stack.


3. Why the split exists

The language designers wanted recoverable external failures (disk, network) to stay visible in signatures. Programming mistakes were treated as bugs you fix rather than annotate on every method.

Modern teams disagree on how helpful that is—still, you must recognize the split to read Java libraries.


4. Quick contrast table (conceptual)

Idea Checked Unchecked
Typical examples IO-style failures you might retry Bad arguments, broken invariants
Compiler expects catch or throws chain does not
API feel noisy signatures lighter signatures

5. Java code example

class Gate {
    void admit(String ticket) throws java.io.IOException {
        if (ticket == null) {
            throw new IllegalArgumentException("ticket required");
        }
        if (ticket.isBlank()) {
            throw new java.io.IOException("reader could not scan barcode");
        }
    }
}
 
public class CheckedUncheckedDemo {
    public static void main(String[] args) {
        Gate g = new Gate();
        try {
            g.admit("   ");
        } catch (java.io.IOException ex) {
            System.out.println("checked path: " + ex.getMessage());
        }
 
        try {
            g.admit(null);
        } catch (IllegalArgumentException ex) {
            System.out.println("unchecked path: " + ex.getMessage());
        }
    }
}

admit declares throws IOException because IOException is checked and may throw from the blank-ticket branch. IllegalArgumentException needs no throws clause.


6. Explanation of code

main must catch IOException (or declare throws IOException) because it calls admit. The null call never reaches IOException; it throws IllegalArgumentException, which main catches by choice, not by compiler force.


7. Common mistakes

Catching Exception to silence both families—you lose signal about what you are willing to recover from.

Declaring throws Exception on public APIs—callers cannot specialize their handling.

Assuming unchecked means “ignore it”—uncaught RuntimeException still stops the thread just like checked ones.


8. Best practices

When designing your own types (Chapter 53), default toward unchecked for caller bugs and checked only when recovery is common and well-defined.


9. Small practice task

Temporarily remove the try/catch around g.admit(" ") and watch the compiler error. Then add throws IOException to main instead of catch and confirm the project compiles—notice how the responsibility moved upward.


Beginner tip

If your IDE highlights a method call in red and mentions “unreported exception”, you met a checked type. Hover, read the type name, then choose catch (local recovery) or throws (delegate).