Exception Handling

Checked vs Unchecked Exceptions

15 min Lesson 4 of 14

Checked vs Unchecked Exceptions

You have already seen how try, catch, and finally let you handle exceptions at runtime. In this lesson you will learn that Java divides all exceptions into two broad categories — checked and unchecked — and that the compiler itself enforces the rules around one of them. Understanding the difference is essential for writing Java code that compiles cleanly and handles errors at the right level.

The Two Categories at a Glance

  • Checked exceptions — the compiler knows they can happen and forces you to deal with them (either catch them or declare that your method passes them up to the caller with throws).
  • Unchecked exceptions — the compiler does not require you to handle them. They represent programming mistakes or truly unexpected conditions.
Where they live in the hierarchy: All checked exceptions extend Exception directly (but not RuntimeException). All unchecked exceptions extend RuntimeException (which itself extends Exception). Error subclasses are also unchecked, but those represent JVM-level failures outside your control.

Checked Exceptions — The Compiler Enforces Them

A checked exception signals a condition that is outside your program's control but is foreseeable — for example, a file that might not exist, a network connection that might be refused, or a database that might be unavailable. Because these situations are expected in the real world, Java requires you to acknowledge them explicitly.

If a method can throw a checked exception, you must do one of two things:

  1. Wrap the call in a try/catch block and handle it there.
  2. Declare the exception in the method signature with throws, passing responsibility to the caller.

If you do neither, the program will not compile. Here is a simple example using FileNotFoundException, which is a checked exception thrown by FileReader:

import java.io.FileReader; import java.io.FileNotFoundException; public class CheckedDemo { // Option 1: handle it here with try/catch public void readFileHandled() { try { FileReader reader = new FileReader("data.txt"); System.out.println("File opened successfully."); } catch (FileNotFoundException e) { System.out.println("File not found: " + e.getMessage()); } } // Option 2: declare it and let the caller handle it public void readFileDeclared() throws FileNotFoundException { FileReader reader = new FileReader("data.txt"); System.out.println("File opened successfully."); } }
Which option should you pick? Handle the exception in the method where you have enough context to do something meaningful (show a message, use a default value, retry). Declare it with throws when the current layer does not know what to do — perhaps a UI layer or a service layer higher up can react better.

Common Checked Exceptions in Java

  • IOException — reading or writing files, streams, or network sockets.
  • FileNotFoundException — a specific IOException; the file path does not exist.
  • SQLException — something went wrong talking to a database.
  • ParseException — text could not be parsed into a date or number.
  • ClassNotFoundException — the JVM cannot find a class by name at runtime (used in reflection).

Unchecked Exceptions — Runtime Surprises

Unchecked exceptions almost always indicate a bug in your code — you passed null where an object was required, you accessed an array index that does not exist, or you tried to divide by zero. The compiler does not force you to catch these because in a well-written program they should not occur at all.

public class UncheckedDemo { public static void main(String[] args) { // NullPointerException — accessing a method on null String name = null; System.out.println(name.length()); // throws NullPointerException // ArrayIndexOutOfBoundsException — bad index int[] numbers = {1, 2, 3}; System.out.println(numbers[5]); // throws ArrayIndexOutOfBoundsException // ArithmeticException — division by zero int result = 10 / 0; // throws ArithmeticException // NumberFormatException — bad string to integer conversion int value = Integer.parseInt("abc"); // throws NumberFormatException } }

You can catch unchecked exceptions if you have a good reason (for example, a top-level handler that logs unexpected crashes), but you are not required to.

Common Unchecked (Runtime) Exceptions

  • NullPointerException — calling a method or accessing a field on a null reference.
  • ArrayIndexOutOfBoundsException — index is negative or beyond the array length.
  • ArithmeticException — illegal arithmetic, typically integer division by zero.
  • NumberFormatException — cannot parse a string as a number.
  • IllegalArgumentException — a method received an argument that makes no sense.
  • IllegalStateException — the object is in an inappropriate state for the requested operation.
  • ClassCastException — an invalid cast between types.
  • StackOverflowError — infinite recursion consumed all stack space (this is an Error, not an Exception).

Why Does This Distinction Exist?

The design philosophy is simple: if a problem is predictable and recoverable (the network is down, the file is missing), the language should force you to plan for it. If a problem is a programming mistake (you forgot to check for null, you used the wrong index), the language trusts you to write correct code — requiring a catch block everywhere would just add noise without improving correctness.

Do not silence checked exceptions. A common beginner mistake is catching a checked exception and doing nothing with it — an empty catch block. This hides real problems and makes debugging extremely difficult. At a minimum, log the exception or rethrow it.

A Side-by-Side Comparison

import java.io.FileReader; import java.io.IOException; public class Comparison { // Checked: compiler demands a try/catch or throws declaration public void checkedExample() throws IOException { FileReader fr = new FileReader("config.txt"); // IOException is checked fr.close(); } // Unchecked: no compiler requirement — but a bug causes a crash at runtime public void uncheckedExample() { String s = null; s.toUpperCase(); // NullPointerException — unchecked, no throws needed } }

Summary

Checked exceptions model foreseeable external failures — file I/O, databases, networks. The compiler ensures you acknowledge them. Unchecked exceptions model programming errors — bad indexes, null dereferences, bad arguments. You fix the root cause rather than catching them everywhere. In the next lesson you will learn how to explicitly signal both kinds using throw and throws.