skip to content

Why must a more specific exception type be caught before its supertype, and what happens if you reverse the order?

level: middleimportance: must knowfreq 78%

answer

  1. Most specific first, most general last
  2. Supertype-before-subtype = unreachable catch = compile error
  3. 'exception X has already been caught'
  4. Rule only applies on the same inheritance line
  5. Unrelated types: any order is fine

basics

~20 s

Catch the more specific type first. If you put the supertype (like Exception) before a subtype (like IOException), the subtype's catch can never be reached, so Java refuses to compile it as an unreachable catch.

solid answer

~50 s

Catch blocks are tried top to bottom and the first assignable type wins. A supertype catch (e.g. Exception) is assignable from every subtype, so if it appears before a more specific clause (e.g. IOException), the specific clause could never be selected — every IOException would already be grabbed by the Exception clause above it. Java treats this as a definite error: the compiler reports 'exception X has already been caught' and refuses to compile, because the later clause is provably unreachable. The fix is to order clauses from most specific to most general: list each subtype before any of its supertypes. This isn't just style — it is a compile-time guarantee that prevents dead handlers. Note the rule only applies between types on the same inheritance line; two unrelated exception types can be listed in either order since neither shadows the other.

code

java · 19 lines
java
// DOES NOT COMPILE: error "exception IOException has already been caught"
try {
    readFile();
} catch (Exception e) {        // supertype first
    handleGeneric(e);
} catch (IOException e) {       // unreachable -> compile error
    handleIo(e);
}

// CORRECT: most specific to most general
try {
    readFile();
} catch (FileNotFoundException e) {
    handleMissing(e);
} catch (IOException e) {
    handleIo(e);
} catch (Exception e) {
    handleGeneric(e);
}

go deeper

for a junior

Knows the convention 'specific before general' and that catching Exception before IOException won't compile.

for a middle

Explains why it's unreachable (first-assignable-match) and that it's a compile error with a specific message; orders multi-catch correctly.

for a senior

Distinguishes same-inheritance-line shadowing from unrelated types, and explains the rule is provable statically hence enforced at compile time.

for a principal

Uses ordering deliberately for layered error handling (narrow recovery first, broad fallback last) and weighs when a catch-all is appropriate vs masking failures.

## The setup Exception classes form an inheritance tree. For example: `Exception` is the parent of `IOException`, which is the parent of `FileNotFoundException`. Saying a subtype **IS-A** supertype means a `FileNotFoundException` object is simultaneously an `IOException` and an `Exception`. When the JVM matches a thrown exception against catch clauses, it goes **top to bottom** and picks the **first clause whose declared type is assignable from the thrown object's type** (i.e. the thrown type is the same as, or a subclass of, the clause's type). Crucially: the first match wins and the rest are skipped. ## Why order matters Consider two clauses, a supertype and a subtype: ```java try { ... } catch (Exception e) { // supertype FIRST } catch (IOException e) { // subtype SECOND } ``` Think about *any* `IOException` thrown in the try block. Because an `IOException` **IS-A** `Exception`, the very first clause `catch (Exception e)` is assignable from it and matches. The second clause `catch (IOException e)` is therefore **never reachable** — there is no exception value that could ever reach it, because anything it could catch was already caught above. The Java Language Specification calls this out: a catch clause is a compile error if its exception type is a subtype of (or the same as) the type of an earlier catch clause in the same try statement. The compiler emits **"exception IOException has already been caught"** and the code does not compile. This is a hard guarantee, not a warning — Java refuses to let you write a provably dead handler. ## The correct ordering List clauses from **most specific to most general**: ```java try { ... } catch (FileNotFoundException e) { // most specific } catch (IOException e) { // broader } catch (Exception e) { // most general (catch-all) } ``` Now each thrown exception finds its narrowest matching handler first, and every clause is reachable: a `FileNotFoundException` hits clause 1, a `SocketException` (another `IOException`) falls through to clause 2, and, say, a `RuntimeException` falls through to clause 3. ## The boundary of the rule The unreachable-catch rule applies **only between types on the same inheritance line**. Two *unrelated* exception types — say `IOException` and `SQLException`, where neither is a subtype of the other — can appear in **either order**, because neither shadows the other. The compiler only complains when an earlier clause already covers everything a later clause could. ## Why a compile error rather than a runtime surprise Reachability of catch clauses is fully determined by the static type hierarchy, which the compiler knows completely. So it can prove dead handlers at compile time and stop them, the same way it flags unreachable statements after a `return`. This protects you from a silent bug where you *thought* you were handling `IOException` specially but the broad clause above quietly intercepted it. ## Practical takeaway Always arrange catches narrowest-first. If you only need one broad handler, that's fine — just don't follow it with a more specific one on the same line.

  • Does the unreachable-catch rule apply to two unrelated exception types like IOException and SQLException?
    No. Neither is a subtype of the other, so neither clause shadows the other; they can appear in any order and both compile.
  • Is reversing specific/general order a compile error or a runtime error?
    A compile error. Reachability is decided from the static type hierarchy, so the compiler proves the later clause is dead and rejects the program.

saying these in an interview costs you the question

  • Claiming the wrong order just makes the specific catch 'never run' at runtime (it actually fails to compile)
  • Thinking the rule applies to any two exception types, even unrelated ones
  • Putting catch(Exception e) first 'to be safe' and then adding more specific clauses below it

context