Leepoint Java Reference Notes on the Java language and its standard library.

Exceptions

A separate channel for reporting that something went wrong, so the normal path stays readable.

An exception unwinds the call stack until something catches it. The point is that the code doing the work does not have to check a return value at every step.

try {
    int n = Integer.parseInt(input);
    use(n);
} catch (NumberFormatException e) {
    System.err.println("Not a number: " + input);
}

Checked and unchecked

Checked exceptions (IOException, SQLException) must be caught or declared with throws. The compiler enforces it. They represent conditions a correct program should anticipate — a file might genuinely not be there.

Unchecked exceptions extend RuntimeException (NullPointerException, IllegalArgumentException, IndexOutOfBoundsException). No compiler enforcement. They usually mean a bug rather than an expected condition.

The rough guide: if the caller can plausibly do something about it, checked; if it means the code is wrong, unchecked.

finally, and why you rarely need it

finally runs whether or not an exception was thrown — the traditional place to close things.

For anything implementing AutoCloseable, try-with-resources is better:

try (var reader = Files.newBufferedReader(path)) {
    return reader.readLine();
}

The resource closes automatically, in reverse order of opening, even if the body throws. It also handles the case where closing itself throws, which hand-written finally blocks almost always get wrong.

Catching well

Catch what you can handle. catch (Exception e) catches everything including bugs you would rather see.

Never swallow silently. An empty catch block turns a failure into wrong behaviour later, at a point with no connection to the cause:

catch (IOException e) { }      // the worst line in Java

Keep the cause. When rethrowing, pass the original:

throw new IllegalStateException("Could not load config", e);

Without that second argument the stack trace stops at your line and the real cause is gone.

Catch narrowly, catch late. Wrapping every statement in its own try block produces unreadable code. One try around a coherent operation is usually right.

Multi-catch

catch (IOException | NumberFormatException e) {

when the handling is identical. The variable is effectively final.

Cost

Creating an exception captures a stack trace, which is not free. That does not matter for genuine errors, and it does matter if you use exceptions for ordinary control flow — which is the main argument for not doing that.