Java 异常处理

异常层次、try-catch-finally、多重捕获、throw/throws 与自定义异常。

Intermediate
Key PointsThrowable 分 Error 与 Exception,Exception 又分受检与非受检;受检异常必须捕获或声明 throws。

Exception 类的层次

Throwable splits into Error (JVM-level, should not be caught) and Exception. Exception further splits into checked exceptions (compiler forces handling) and RuntimeException (unchecked).

Java
public class Hierarchy {
    public static void main(String[] args) {
        Object e = new ArithmeticException("x");

        System.out.println(e instanceof Throwable);
        System.out.println(e instanceof Exception);
        System.out.println(e instanceof RuntimeException);
        System.out.println(e instanceof Error);
    }
}
Output
true
true
true
false

Java 内置异常类

Common unchecked: NullPointerException, ArrayIndexOutOfBoundsException, ArithmeticException, ClassCastException, NumberFormatException. Checked: IOException, SQLException.

ExceptionTypical cause
NullPointerExceptionCalling a method on null
ArrayIndexOutOfBoundsIndex < 0 or >= length
ArithmeticExceptionInteger division by zero
NumberFormatExceptionParsing non-numeric text
ClassCastExceptionInvalid downcast
IOExceptionFile or network failure (checked)

捕获异常

try wraps risky code and catch handles by type. finally always runs and is meant for cleanup, though try-with-resources is now preferred.

Java
public class CatchDemo {
    public static void main(String[] args) {
        try {
            int[] a = new int[2];
            System.out.println(a[5]);
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("caught: " + e.getClass().getSimpleName());
        } finally {
            System.out.println("finally runs");
        }

        try {
            System.out.println(1 / 0);
        } catch (ArithmeticException e) {
            System.out.println(e.getMessage());
        }
    }
}
Output
caught: ArrayIndexOutOfBoundsException
finally runs
/ by zero

多重捕获块

A try may have several catch blocks matched top-down, so subclasses must precede their parents; otherwise the parent swallows everything and the compiler rejects the unreachable branch.

Java
public class MultiCatch {
    public static void main(String[] args) {
        String[] inputs = {"42", "abc", null};

        for (String s : inputs) {
            try {
                System.out.println(Integer.parseInt(s.trim()));
            } catch (NumberFormatException e) {
                System.out.println("not a number: " + s);
            } catch (NullPointerException e) {
                System.out.println("null input");
            }
        }
    }
}
Output
42
not a number: abc
null input
Note that Integer.parseInt(null) throws NumberFormatException (Cannot parse null string), not an NPE. The example calls s.trim() first, so a null element blows up on dereference and both branches are reachable.

多异常合并捕获

Since Java 7, catch (A | B e) merges exceptions sharing the same handling, cutting duplication. The variable is typed as their common parent, so only parent methods are available.

Java
public class UnionCatch {
    public static void main(String[] args) {
        try {
            String s = args.length > 0 ? args[0] : null;
            System.out.println(s.length());
        } catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
            System.out.println("handled: " + e.getClass().getSimpleName());
        }
    }
}
Output
handled: NullPointerException

抛出异常

throw raises an exception instance from code; throws on the signature declares what a method may propagate so callers handle it. Unhandled checked exceptions do not compile.

Java
public class ThrowDemo {
    static int div(int a, int b) {
        if (b == 0) {
            throw new IllegalArgumentException("b must not be zero");
        }
        return a / b;
    }

    static void risky() throws Exception {
        throw new Exception("checked");
    }

    public static void main(String[] args) throws Exception {
        try {
            div(1, 0);
        } catch (IllegalArgumentException e) {
            System.out.println(e.getMessage());
        }

        try {
            risky();
        } catch (Exception e) {
            System.out.println("caught: " + e.getMessage());
        }
    }
}
Output
b must not be zero
caught: checked

用户自定义异常

Extend Exception for a checked custom exception, RuntimeException for an unchecked one. Name it ...Exception and provide constructors taking a message and a cause.

Java
class InsufficientBalanceException extends Exception {
    private final double amount;

    public InsufficientBalanceException(double amount) {
        super("insufficient balance, need " + amount);
        this.amount = amount;
    }

    public double getAmount() {
        return amount;
    }
}

public class CustomException {
    static void withdraw(double balance, double amount) throws InsufficientBalanceException {
        if (amount > balance) {
            throw new InsufficientBalanceException(amount - balance);
        }
        System.out.println("ok");
    }

    public static void main(String[] args) {
        try {
            withdraw(100, 150);
        } catch (InsufficientBalanceException e) {
            System.out.println(e.getMessage());
            System.out.println(e.getAmount());
        }
    }
}
Output
insufficient balance, need 50.0
50.0

定义清理行为

finally runs on normal exit, on exception and even across return — the classic place to release resources. Beware that a return inside finally overrides the one from try.

Java
public class FinallyDemo {
    static String f() {
        try {
            return "from try";
        } finally {
            System.out.println("finally runs");
        }
    }

    public static void main(String[] args) {
        System.out.println(f());

        try {
            throw new RuntimeException("boom");
        } catch (RuntimeException e) {
            System.out.println("caught " + e.getMessage());
        } finally {
            System.out.println("cleanup");
        }
    }
}
Output
finally runs
from try
caught boom
cleanup

预定义的清理行为

Objects implementing AutoCloseable can be declared in try (...) and are closed automatically at block end, in reverse declaration order — the recommended way to manage resources.

Java
class Res implements AutoCloseable {
    private final String name;

    Res(String name) {
        this.name = name;
        System.out.println("open " + name);
    }

    @Override
    public void close() {
        System.out.println("close " + name);
    }
}

public class TryResource {
    public static void main(String[] args) {
        try (Res a = new Res("A"); Res b = new Res("B")) {
            System.out.println("working");
        }
    }
}
Output
open A
open B
working
close B
close A

Friends & Partners