Exception Handling
How Java programs deal with things that go wrong — checked vs unchecked exceptions, try/catch/finally, and try-with-resources
Things go wrong. Files are missing. Network calls time out. Users type the wrong thing. Code passes the wrong argument. A robust program does not pretend otherwise — it has a clear, designed answer to every category of failure. In Java, that answer is exceptions.
What is an exception?
An exception is an object that represents "something went wrong right here." When code encounters a problem it cannot handle locally, it throws an exception. The JVM walks back up the call stack looking for a method that catches that kind of exception. If no one does, the program crashes with a stack trace.
A first example
Three keywords:
try— wraps code that might fail.catch— handles a specific type of exception.finally— always runs whether or not an exception was thrown. Used for cleanup (closing files, releasing locks).
The exception family tree
Three branches matter:
Error— extremely serious, you generally do not catch these (e.g.,OutOfMemoryError,StackOverflowError).Exception(other than RuntimeException) — checked. The compiler forces you to either catch them or declare them withthrows.RuntimeException— unchecked. The compiler does not force you to handle them.
Checked vs unchecked, intuitively
Checked exceptions represent "expected problems with the outside world" — files missing, networks failing, parsing user input. Java forces you to acknowledge them.
Unchecked exceptions represent "your program has a bug" — null pointers, out-of-bounds indices, divisions by zero, illegal arguments. The compiler can't help you with these (every line could have a bug), so it leaves the choice up to you.
A practical rule:
- Throw an unchecked exception (often
IllegalArgumentExceptionorIllegalStateException) when the caller broke a contract. - Throw a checked exception when the failure is normal-but-rare and the caller realistically has to handle it.
Declaring with throws
If a method can throw a checked exception that it does not catch, the method must declare it:
Notice the wrapping technique: we catch the JDK's
NumberFormatException and rethrow our own, more domain-specific
BadInputException. Wrapping helps you avoid leaking internal
details up to callers.
try-with-resources
Many resources (files, sockets, database connections) must be closed when you're done with them. The pattern used to look like this:
Resource r = open();
try {
use(r);
} finally {
r.close();
}That works, but the try-with-resources form is shorter and
safer:
try (Resource r = open()) {
use(r);
} // r.close() is called automaticallyAny class that implements AutoCloseable (which all standard
file/network/DB classes do) is eligible. We are not using real
files in this online runner, but the pattern is identical wherever
you encounter resources.
Anti-patterns to avoid
Swallowing exceptions
try {
risky();
} catch (Exception e) {
// ignore
}You have just made a bug invisible. When something goes wrong, nothing tells you. The program limps along producing wrong answers. Never write an empty catch block. At minimum log the exception.
Catching Exception (or Throwable)
A broad catch hides bugs you didn't even know about, like a
NullPointerException from a typo. Catch the narrowest type
that meaningfully handles the problem.
Using exceptions for control flow
try {
while (true) processNext();
} catch (NoSuchElementException end) {
// loop exit
}Exceptions are expensive and obscure the intent. If something is
a normal end of input, use a boolean hasNext() instead.
A worked example
Expected output:
parsed 42
bad input: not a number: oops
bad input: input was null
doneThe program survives all three cases — including the null — because each problem is recognized, named, and handled.
What is the difference between a checked and an unchecked exception?
Checked exceptions are slower
Unchecked exceptions cannot be caught
Checked exceptions must be either caught or declared in a throws clause; unchecked exceptions (RuntimeException and its subclasses) have no such requirement
Unchecked exceptions are always fatal
Why is an empty catch (Exception e) { } block a problem?
It is slow
The compiler refuses to compile it
It silently swallows every failure, hiding bugs and making the program produce wrong answers without telling anyone
It always throws NullPointerException
When is the finally block executed?
Only when no exception was thrown
Only when an exception was thrown
Always — after the try (and any catch) finishes, whether or not an exception was thrown
Only when the JVM is shutting down
A challenge
Implement SafeMath.divide(int a, int b) that returns a / b, but throws IllegalArgumentException with message "cannot divide by zero" when b is zero.
Main.java is provided. Expected output (exact):
10/2 = 5
10/0 -> cannot divide by zero
done
Exceptions are how Java programs draw a clean line between "the happy path" and "every way things can go wrong." With them in hand, we can finally talk about the broader engineering practice of debugging.