Exception Handling
Hierarchy
Throwable splits into Error (do not catch) and Exception, whose subclass RuntimeException is unchecked. Checked exceptions must be caught or declared; unchecked ones need not be. Catch blocks match top-down, so a subtype must precede its supertype, and multi-catch (catch (IOException | SQLException e)) is illegal when the types are related by inheritance; the multi-catch variable is implicitly final.
try/catch/finally
finally runs whether or not an exception is thrown - except after System.exit or a non-terminating loop. A return inside finally silently discards the pending return value or exception, which is a favourite exam trap. Catching a checked exception the try block cannot throw is a compile error (except for Exception/Throwable). An overriding method may narrow throws but never widen it.
try-with-resources
Resources must be AutoCloseable; they are closed in reverse order of declaration. If the body also throws, the close exception becomes a suppressed exception attached to the primary one (getSuppressed()). A previously declared variable can be a resource if it is effectively final.
Traps
Lambdas can only throw checked exceptions when the functional interface declares them. Rethrowing a caught exception is allowed if the compiler can prove the try block only throws that type (precise rethrow). NullPointerException messages now name the null expression, so read them rather than guessing.
Worked rule: compute the return value before finally runs - Java evaluates the return expression first, so later reassignment of a local inside finally does not change what is returned.