程序运行时难免出错:文件不存在、数组越界、网络中断……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声明异常,必要时自定义异常类