Learn · OOP & Java
Exceptions & Error Handling
Things go wrong — a file’s missing, the network drops, a number won’t parse. Exceptions let your program deal with it instead of just dying.
Before we start
- What exceptions are and why they beat crashing
- try / catch / finally / throw
- Checked vs unchecked exceptions
- How to handle errors well (not swallow them)
- Wrap risky code and recover gracefully
- Explain checked vs unchecked
- Avoid the classic error-handling mistakes
Why you’re learning it: real systems fail constantly; handling failure well is what separates hobby code from production code — and it’s asked in interviews. ⏱️ ~20 min.
The idea — a safety net
An exception is the program saying “something went wrong here” and jumping to your plan B instead of crashing. You wrap risky code in try, handle the problem in catch, and clean up in finally.
try {
int x = Integer.parseInt(input); // might fail
} catch (NumberFormatException e) {
// plan B: tell the user it wasn't a number
} finally {
// always runs — close files, release locks
}What actually happens when it’s thrown
The moment the JVM hits something it can’t continue past, it does three things — this is the whole mental model:
- 1. Builds an object describing the failure — its type, a message, and a snapshot of the call stack.
- 2. Abandons the current line immediately — nothing below the failing line runs.
- 3. Climbs up the call stack, method by method, looking for a
catchthat handles it. If nobody catches it, the thread dies and the stack trace prints.
An object climbing the stack looking for a catcher — that picture is everything. The rest is just syntax.
The family tree
Everything you can throw descends from one class, Throwable, which splits into two branches that mean very different things:
Throwable
├── Error ← the JVM itself is dying — you do NOT catch these
│ ├── OutOfMemoryError
│ └── StackOverflowError
└── Exception ← your program hit a problem — YOUR territory
├── IOException (checked)
├── SQLException (checked)
└── RuntimeException (unchecked — the special subtree)
├── NullPointerException
├── ClassCastException
└── IllegalArgumentExceptionError means the JVM is out of memory or blown its stack — there’s nothing sensible to do, so you let it die. Exception is where you live. And one subtree inside it — RuntimeException — gets special treatment from the compiler.
Checked vs unchecked
Problems you’re expected to plan for — a missing file, a network error. Java forces you to handle or declare them.
Bugs — null pointer, index out of bounds, divide by zero. They mean “fix the code,” not “handle at runtime.”
The dividing line is RuntimeException: anything under it is unchecked; any other Exception is checked. Why does the compiler force you to handle checked ones? Because the outside world will fail no matter how perfect your code is — a file can be deleted between checking it exists and opening it. The compiler is saying: “this failure isn’t optional, decide now what happens.” Unchecked exceptions are programmer mistakes — the fix isn’t a catch, it’s correcting the code so the bug can’t happen.
Handle errors well
- Catch specific, not a blanket
catch (Exception e)that hides real bugs. - Never swallow — an empty catch block hides failures until they explode later.
- Clean up in finally (or try-with-resources) so files/connections always close.
- Fail with a clear message — “order 123 not found”, not a raw stack trace to the user.
try-with-resources — the modern cleanup
The old way of closing a file used finally — and almost everyone got it subtly wrong (null checks, and an exception inside close() masking the real one):
// OLD WAY — don't write this anymore
BufferedReader reader = null;
try {
reader = Files.newBufferedReader(Path.of("notes.txt"));
System.out.println(reader.readLine());
} catch (IOException e) {
System.out.println("Could not read file: " + e.getMessage());
} finally {
if (reader != null) {
try { reader.close(); } catch (IOException ignored) {}
}
}The modern way — declare the resource in the try parentheses and Java closes it for you automatically, even when an exception flies out:
// The reader is closed automatically — success OR exception
try (BufferedReader reader = Files.newBufferedReader(Path.of("notes.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.out.println("Could not read notes.txt: " + e.getMessage());
}Anything that implements AutoCloseable works here — files, database connections, HTTP clients. Multiple resources close in reverse order. finally still exists for non-resource cleanup, but for closing things, try-with-resources won permanently.
Write your own exception
Standard exceptions describe generic failures. Your business logic has specific failures that deserve specific exceptions — with messages that carry context someone can read at 2 AM without opening a debugger:
public class InsufficientBalanceException extends Exception {
public InsufficientBalanceException(int requestedRupees, int availableRupees) {
super("Withdrawal of Rs " + requestedRupees + " failed. Balance is Rs "
+ availableRupees + ". Short by Rs " + (requestedRupees - availableRupees));
}
}Why extend Exception (checked) and not RuntimeException? Because the caller can realistically recover — show the user “insufficient balance, you need Rs 3000 more.” A short balance is a normal business event, not a bug. Rule of thumb: caller can recover → checked; programmer error → unchecked.
Reading a stack trace — the daily skill
In a real project an exception travels through layers and gets wrapped on the way, so the trace ends up looking like this:
Exception in thread "main" java.lang.RuntimeException: Could not process withdrawal
at com.bank.WithdrawalService.process(WithdrawalService.java:31)
at com.bank.Main.main(Main.java:12)
Caused by: InsufficientBalanceException: Withdrawal of Rs 5000 failed. Balance is Rs 2000. Short by Rs 3000
at com.bank.Account.withdraw(Account.java:18)
at com.bank.WithdrawalService.process(WithdrawalService.java:28)
... 1 moreHow to read it — same order, every time:
- 1. Jump to the last “Caused by” — that’s the root cause. Everything above it is wrapping.
- 2. Read its class and message first — here, “short by Rs 3000.” Half your diagnosis is done.
- 3. Find the first line in YOUR package (
com.bank.Account.withdraw:18) — that’s where you open the editor. - 4. Skip framework lines —
java.base,org.springframework,org.hibernateare travel, not destination. In a Spring app, 40 of 45 lines are noise.
Where you’ll use it — real life
The bank times out — you catch it and retry or mark it, instead of crashing (your EMV world).
A failed transaction throws — you catch and roll back.
Bad input shouldn’t take the app down — catch and return a 400.
finally guarantees connections close even when things fail.
Interview check — say these out loud
- What happens the instant an exception is thrown and nothing catches it? The JVM builds an exception object with a stack snapshot, abandons the current line, and climbs the call stack looking for a matching
catch. - Error vs Exception — why not catch Error?
Errormeans the JVM itself is failing (out of memory, stack overflow) — nothing sensible to do, so you let it die.Exceptionis a handleable problem in your program. - Which class marks the checked/unchecked line?
RuntimeException. Under it = unchecked, usually a bug in your code. OtherExceptionsubclasses = checked, usually the outside world failing. - Order of try / catch / finally? try runs until the throw → matching catch runs → finally runs → method exits.
finallyruns even when try or catch has areturn. - Why is try-with-resources better than finally? The resource closes automatically on success AND exception, in reverse order for multiple resources, with no null checks and no nested try — all the old bugs are gone.
- What’s wrong with
catch (Exception e) {}? The broad type also grabs bugs likeNullPointerExceptionyou wanted to crash loudly; the empty body destroys the evidence while the failure still happens.
Out loud: “What do try/catch/finally do, and what’s the difference between checked and unchecked exceptions?” Log it in your Journal.
Next: Generics →