Hook: Interviewers love this question because it reveals whether you know what Java enforces at compile time versus what it only reports at runtime.
Question: What is the difference between checked and unchecked exceptions in Java?
Answer: Checked exceptions are validated by the compiler, so you must either catch them or declare them with throws. Unchecked exceptions are subclasses of RuntimeException and do not need to be handled at compile time. In practice, checked exceptions usually mean expected external failures, while unchecked exceptions usually mean a bug or invalid input.
Interview-Ready Answer: In Java, checked exceptions are enforced by the compiler: if a method can throw one, I must catch it or declare it. Unchecked exceptions are subclasses of RuntimeException, so the compiler does not force handling, although they can still crash the program at runtime. I use checked exceptions for situations a caller can reasonably recover from, like file or network failures, and unchecked exceptions for programming mistakes like bad arguments or null values.
In Java, every exception is a Throwable, which is the base type for things that can be thrown. The important split is this: a checked exception is any subclass of Exception that is not a RuntimeException, and an unchecked exception is either a RuntimeException or an Error. The compiler enforces checked exceptions; the JVM does not treat them differently at runtime.
throws IOException.try/catch block or passed upward with throws.| Aspect | Checked | Unchecked |
|---|---|---|
| Compiler rule | Must catch or declare | No requirement |
| Hierarchy | Exception minus RuntimeException | RuntimeException and Error |
| Typical meaning | Recoverable external issue | Bug or invalid state |
| Examples | IOException, SQLException | NullPointerException, IllegalArgumentException |
| API style | Explicit but noisy | Cleaner calls, less forced handling |
catch(Exception) everywhere.One important edge case: overriding methods cannot broaden checked exceptions. A subclass method can throw fewer checked exceptions, or more specific ones, but not a wider checked exception than the parent method declares. Since Java 7, try-with-resources also reduced the pain of checked exceptions by closing resources automatically and allowing suppressed exceptions to be attached to the main failure.
Throwing an exception is much more expensive than a normal if check because stack-trace creation and stack unwinding are involved. The cost grows with call depth, so think of it as roughly proportional to the number of frames the exception must pass through. That is why exceptions are for exceptional situations, not routine branching.
Memory model: the rule is compile-time, but the mechanism is runtime stack unwinding. That is the key interview point: the JVM throws everything the same way, but the compiler only forces you to deal with checked exceptions.
Real-World Story: In a checkout service for an e-commerce site, the code calls a payment gateway and then writes an order record. The HTTP client can fail with a checked exception such as an I/O problem, because the caller may retry or route to a backup gateway. But validation problems like a negative amount or a missing cart item should be unchecked, because those are bugs or bad input that should be fixed before payment starts.
What goes wrong when teams misunderstand this? A developer catches a broad Exception, logs it, and still marks the order as paid. Under load, the payment call starts timing out, logs fill with java.io.IOException: connection reset, and customers see duplicate charges or “paid” orders that never shipped. The bug shows up as rising retries, stuck orders in PENDING, and support tickets saying “I was charged but my order disappeared.”
class CheckedVsUncheckedDemo {
// Checked exception: callers must either catch it or declare it.
// This is ideal when the caller can actually recover, such as showing a friendly error.
static class InsufficientFundsException extends Exception {
InsufficientFundsException(String message) {
super(message);
}
}
// Unchecked exception: the compiler does not force handling.
// This usually signals bad input or a programming mistake.
static class InvalidAmountException extends RuntimeException {
InvalidAmountException(String message) {
super(message);
}
}
static class BankAccount {
private int balance;
BankAccount(int initialBalance) {
this.balance = initialBalance;
}
int getBalance() {
return balance;
}
void withdraw(int amount) throws InsufficientFundsException {
if (amount <= 0) {
throw new InvalidAmountException("Withdrawal amount must be positive: " + amount);
}
if (amount > balance) {
// Checked exception: the caller can decide whether to retry, ask for a smaller amount, etc.
throw new InsufficientFundsException(
"Need " + amount + ", but only have " + balance);
}
balance -= amount;
}
}
public static void main(String[] args) {
BankAccount account = new BankAccount(100);
System.out.println("Initial balance: " + account.getBalance());
demoWithdraw(account, 30); // success path
demoWithdraw(account, 80); // checked failure path
// Edge case: unchecked exception for invalid input.
// The compiler does not require us to catch it, but we still should handle it at the boundary.
try {
account.withdraw(-5);
} catch (InvalidAmountException e) {
System.out.println("Unchecked exception caught: " + e.getMessage());
} catch (InsufficientFundsException e) {
// Not expected for negative input, but legal to catch because it is declared by withdraw().
System.out.println("Checked exception caught: " + e.getMessage());
}
System.out.println("Final balance: " + account.getBalance());
}
private static void demoWithdraw(BankAccount account, int amount) {
try {
account.withdraw(amount);
System.out.println("Withdrew " + amount + ", new balance: " + account.getBalance());
} catch (InvalidAmountException e) {
System.out.println("Invalid amount: " + e.getMessage());
} catch (InsufficientFundsException e) {
System.out.println("Could not withdraw: " + e.getMessage());
}
}
}Follow-up & Tricky Questions:
Exception? Usually only at the top boundary of an app, where you log and convert to a user-facing error. Inside business logic, catching Exception often hides bugs and makes debugging harder.NullPointerException unchecked? Because it usually signals a programmer mistake, not a recoverable external failure. Java wants you to fix the code, not add boilerplate every time a null slips through.Exception and RuntimeException? RuntimeException is a subclass of Exception, but exceptions under it are treated as unchecked. That is why the hierarchy alone is not enough; the exact subclass matters.Error checked or unchecked? Error is unchecked, but it usually represents serious JVM problems such as OutOfMemoryError. In interviews, it is good to say that application code normally should not try to recover from it.Tricky / gotcha questions:
Exception checked? No. RuntimeException and its subclasses are the big exception to that rule.Common Mistakes:
Memory Hook: Think of checked exceptions as a compiler checkpoint: the compiler checks your ticket before you pass. Unchecked exceptions are runtime potholes: Java lets you drive on, but the road can still break under you.
Cheat Sheet:
RuntimeException and Error.Practice Tasks:
IllegalArgumentException for bad input and explain why it should stay unchecked.deposit, and decide which failures should be checked versus unchecked.