Exceptions: structured panic with cleanup
Non-local control flow with deterministic resource release
When a method cannot complete its contract — a file is missing, a parse fails, a precondition is violated — it can throw an exception. The JVM unwinds the stack, popping frames and running any finally or try-with-resources cleanup along the way, until it finds a matching catch. This is powerful for real failure modes and expensive if you use it for ordinary control flow.
Checked vs. unchecked
- Checked (
IOException,SQLException): the compiler forces you to declare or handle them. The API author is signaling "recovery here might be real." - Unchecked (
IllegalArgumentException,NullPointerException): usually programmer bugs or contract violations. No compiler ceremony. - Errors (
OutOfMemoryError,StackOverflowError): don't catch these unless you are a last-resort supervisor.
try (InputStream in = Files.newInputStream(path)) {
return parse(in);
} catch (IOException ex) {
throw new ServiceException("Could not read " + path, ex); // preserve cause
} finally {
// ran on every exit path (return / exception / normal), already after resource close
metrics.recordLatency("parse", timer.stop());
}Wrap, but preserve the cause
When an exception crosses an architectural boundary — a repository throws SQLException and a service reports OrderNotFoundException to the controller — wrap it, but pass the original as the cause (second constructor argument). The resulting stack trace prints a Caused by: chain that preserves the full story for whoever reads your logs at 3 a.m.
Takeaways
- Exceptions are for exceptional situations — not for ordinary control flow.
- Checked exceptions signal recoverable failure; unchecked signal bugs.
- Always preserve the cause when wrapping across boundaries.
try-with-resourcesmakes cleanup deterministic.
Enjoying This Lesson?
Your support helps create more comprehensive courses and lessons like this one. Help me build better learning experiences for everyone.
Support Awashyak