理解 异常(Exception) 的概念及其在程序执行中的作用
掌握 try、catch、finally 三大核心关键字的使用方式
区分 检查型异常 与 非检查型异常
能够使用 throw 主动抛出异常,使用 throws 声明方法可能抛出的异常
了解 Java 异常类的继承体系及 JVM 的异常处理流程
能够设计自定义异常以满足特定业务需求
在 Java 中,异常是程序执行过程中发生的意外事件,它会中断正常的指令流。异常处理机制允许我们在不终止整个程序的前提下,优雅地应对错误。

异常 ≠ 错误(Error):异常通常可恢复,而错误(如内存溢出)往往不可恢复。
try-catch
示例:除零异常处理
public class MainApp {
public static void main(String[] args) {
int numerator = 10;
int denominator = 0;
try {
int result = numerator / denominator; // 可能抛出 ArithmeticException
System.out.println("结果: " + result);
} catch (ArithmeticException e) {
System.out.println("错误:不能除以零!");
}
}
}错误:不能除以零!执行流程:
JVM 执行
try块中的代码;若发生异常,立即跳转到匹配的
catch块;执行完
catch后,继续执行后续代码(程序不会崩溃)。
无论是否发生异常,finally 块总是执行,常用于关闭文件、数据库连接等资源清理操作。
示例:数组越界与 finally
public class FinallyExample {
public static void main(String[] args) {
int[] numbers = {1, 2, 3};
try {
System.out.println(numbers[5]); // 抛出 ArrayIndexOutOfBoundsException
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("捕获异常: " + e.getMessage());
} finally {
System.out.println("finally 块始终执行。");
}
System.out.println("程序继续运行...");
}
}捕获异常: Index 5 out of bounds for length 3
finally 块始终执行。
程序继续运行...注意:即使
try或catch中有return,finally仍会执行(除非调用System.exit())。
throw 与 throwsthrow 用于在代码中显式触发异常。
class AgeValidator {
static void checkAge(int age) {
if (age < 18) {
throw new IllegalArgumentException("年龄必须大于或等于 18 岁");
}
System.out.println("年龄验证通过");
}
public static void main(String[] args) {
try {
checkAge(15);
} catch (IllegalArgumentException e) {
System.out.println("异常信息: " + e.getMessage());
}
}
}异常信息: 年龄必须大于或等于 18 岁建议:使用语义更准确的异常类型(如
IllegalArgumentException),而非ArithmeticException。
throws 主要用于检查型异常,告知调用者需处理该异常。
import java.io.*;
class FileReaderUtil {
// 声明该方法可能抛出 IOException
static void readFile(String filename) throws IOException {
FileReader reader = new FileReader(filename); // 可能抛出 FileNotFoundException
// ...读取文件逻辑
reader.close();
}
public static void main(String[] args) {
try {
readFile("nonexistent.txt");
} catch (IOException e) {
System.out.println("文件操作失败: " + e.getMessage());
}
}
}
输出(若文件不存在):
文件操作失败: nonexistent.txt (No such file or directory)规则:若方法调用了一个声明
throws检查型异常的方法,且未在内部catch,则自身也必须声明throws。
所有异常和错误都继承自 Throwable 类:
Throwable
├── Error // 严重系统错误,通常不可恢复(如 OutOfMemoryError)
└── Exception
├── RuntimeException // 非检查型异常(如 NullPointerException, ArithmeticException)
└── 其他 Exception // 检查型异常(如 IOException, SQLException)
catch 块try {
// 可能抛出多种异常的代码
int[] arr = new int[3];
arr[5] = 10; // ArrayIndexOutOfBoundsException
String s = null;
s.length(); // NullPointerException
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组越界: " + e.getMessage());
} catch (NullPointerException e) {
System.out.println("空指针异常: " + e.getMessage());
}注意:子类异常应放在父类异常之前(否则编译报错)。
public class NestedTryExample {
public static void main(String[] args) {
try {
System.out.println("外层 try");
try {
int a = 10 / 0; // ArithmeticException
} catch (ArithmeticException e) {
System.out.println("内层捕获: " + e.getMessage());
}
String str = null;
str.length(); // NullPointerException
} catch (NullPointerException e) {
System.out.println("外层捕获: " + e.getMessage());
}
}
}外层 try
内层捕获: / by zero
外层捕获: Cannot invoke "String.length()" because "<local1>" is nulltry {
int x = 1 / 0;
} catch (ArithmeticException e) {
System.out.println("toString(): " + e.toString());
System.out.println("getMessage(): " + e.getMessage());
e.printStackTrace(); // 通常用于调试
}异常发生时,JVM 创建异常对象并记录当前调用栈(Call Stack)。
从当前方法开始,向上传递调用栈,寻找匹配的 catch 块。
若找到,执行对应 catch 和 finally。
若未找到,交由 默认异常处理器:打印堆栈并终止程序。
示例:未捕获的异常
public class UnhandledException {
public static void main(String[] args) {
String s = null;
System.out.println(s.length()); // 抛出 NullPointerException
System.out.println("这行不会执行");
}
}输出:
Exception in thread "main" java.lang.NullPointerException: ...
at UnhandledException.main(...)使用 try-catch-finally 结构安全处理运行时错误。
finally 用于资源清理,几乎总被执行。
throw 用于主动抛出异常,throws 用于声明方法可能抛出的检查型异常。
检查型异常必须处理,非检查型异常可选处理。
自定义异常可通过继承 Exception 或 RuntimeException 实现。
异常处理的目标是提升程序健壮性,而非掩盖错误。
为什么 FileNotFoundException 是检查型异常,而 NullPointerException 是非检查型异常?这种设计背后的哲学是什么?
在 finally 块中修改返回值是否会影响方法的最终返回结果?请编写代码验证。
如何设计一个自定义异常 InvalidEmployeeIdException,并在员工管理系统中使用它来验证 ID 格式?