程序运行时难免出错:文件不存在、数组越界、网络中断……Java 用异常(Exception)机制统一处理这些意外情况,让程序出错时不至于直接崩溃,而是按我们写好的逻辑兜底。

认识异常

看一个最常见的空指针:

int[] arr = null;
System.out.println(arr.length); // 抛出 NullPointerException,程序终止

没有处理异常时,程序会从出错的那一行直接中断。用 try/catch 可以把风险代码包起来:

int[] arr = null;
try {
    System.out.println(arr.length);
} catch (NullPointerException e) {
    System.out.println("数组还没初始化"); // 输出:数组还没初始化
}

try / catch / finally 完整流程

finally 里的代码无论是否出错都会执行,适合放关闭文件、释放连接这类收尾工作:

try {
    int result = 10 / 0; // 抛出 ArithmeticException
    System.out.println("这行不会执行");
} catch (ArithmeticException e) {
    System.out.println("捕获到异常:" + e.getMessage()); // 输出:/ by zero
} finally {
    System.out.println("无论如何都执行"); // 一定会输出
}

一个 try 也可以配多个 catch,按顺序匹配:

try {
    int[] arr = new int[2];
    System.out.println(arr[5]);
} catch (ArrayIndexOutOfBoundsException e) {
    System.out.println("下标越界了");
} catch (Exception e) {
    System.out.println("其他异常:" + e);
}

注意要把更具体的异常写在前面,Exception 这种"大筐"放在最后。

异常的继承体系

Java 异常体系的顶层是 Throwable,分两大支:

  • Error:严重系统错误(如内存不足),程序一般不处理
  • Exception:程序可以处理的异常,又分两种
    • 受检异常:编译器强制处理,如 IOException
    • 非受检异常:RuntimeException 及其子类,如空指针,可以不强制处理
// 受检异常必须处理,否则编译不通过
try {
    java.io.FileReader reader = new java.io.FileReader("data.txt");
} catch (java.io.FileNotFoundException e) {
    System.out.println("文件不存在,请检查路径");
}

throw 主动抛出异常

方法内部用 throw 抛出异常,必要时在方法签名上用 throws 声明:

static void setAge(int age) {
    if (age < 0) {
        throw new IllegalArgumentException("年龄不能为负数:" + age);
    }
    System.out.println("年龄设置为 " + age);
}

public static void main(String[] args) {
    try {
        setAge(-5);
    } catch (IllegalArgumentException e) {
        System.out.println(e.getMessage()); // 输出:年龄不能为负数:-5
    }
}

还可以自定义异常类,让业务错误更清晰:

class ScoreOutOfRangeException extends RuntimeException {
    public ScoreOutOfRangeException(String message) {
        super(message);
    }
}

static void checkScore(int score) {
    if (score < 0 || score > 100) {
        throw new ScoreOutOfRangeException("分数必须在 0~100 之间:" + score);
    }
}

小结

  • try/catch/finally 是处理异常的标准结构,finally 必定执行
  • 一个 try 可配多个 catch,具体异常放前面
  • Error 不用管,受检异常必须处理,RuntimeException 可选
  • throw 主动抛异常,throws 声明异常,必要时自定义异常类