Checked exceptions, unchecked exceptions, and try-with-resources
The one real difference between the two families, and the resource leak try-with-resources exists to prevent.
After this lesson you can
- Tell a checked exception from an unchecked one
- Say why the compiler forces you to handle one but not the other
- Use try-with-resources instead of a hand-written finally block
Every Throwable is either checked or unchecked, and the
difference is entirely about the compiler, not about how serious the
problem is.
public String readFile(String path) throws IOException { // checked: must declare
return Files.readString(Path.of(path));
}
public int divide(int a, int b) { // unchecked: no declaration needed
return a / b; // may throw ArithmeticException, uncaught
}
IOException extends Exception — checked. The compiler requires every
method that can throw one to either catch it or declare it with
throws, and every caller is forced to deal with that, one way or
another. ArithmeticException, NullPointerException, and
IllegalArgumentException all extend RuntimeException — unchecked.
Nothing forces a caller to acknowledge them; they propagate silently
until something up the stack catches them, or nothing does and the
program terminates.
The convention: checked exceptions for conditions a well-written caller can reasonably recover from — a missing file, a failed network call. Unchecked exceptions for programmer errors — a null that should never have been null, an index that was never in range — where the fix is a code change, not a recovery path.
Try it
public class Solution { public static String describe() { try { int[] nums = {1, 2, 3}; return "value=" + nums[5]; } catch (ArrayIndexOutOfBoundsException e) { return "array: " + e.getMessage(); } catch (RuntimeException e) { return "runtime: " + e.getMessage(); } }}Catch the most specific type first
try {
process(data);
} catch (FileNotFoundException e) { // a subclass of IOException
// ...
} catch (IOException e) { // the broader type
// ...
}
A catch block matches the first clause whose type the thrown exception
is an instance of, top to bottom. Putting the broader IOException
first would catch everything there, and the compiler refuses to compile
a narrower catch clause that can now never be reached — the ordering
is enforced, not just a style preference.
try-with-resources
try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
return reader.readLine();
}
// reader.close() has already run here, even if readLine() threw
Anything implementing AutoCloseable — a file, a database connection, a
lock — declared inside the parentheses of a try is closed automatically
when the block exits, success or exception, in the reverse of the order
it was opened. The equivalent by hand is a finally block that has to
null-check the resource (it might have failed to open) and can itself
throw while closing, silently hiding the original exception — a genuine
and common source of resource leaks that try-with-resources removes by
construction.
Try it yourself
2 visible tests · 2 hidden testsImplement parseAll(values). For each string in values, try
Integer.parseInt. Return a Map<String, Object> with two keys:
"parsed", a List<Integer> of every value that parsed successfully
in order, and "failed", a List<String> of every value that raised
NumberFormatException, in order. Catch that specific exception —
nothing broader.
parseAll(["1","abc","42","3.5"])parseAll(["1","2","3"])
Sign up to check the hidden tests and save your progress. Sign up