异常体系结构
异常不同于编译错误,语法错误在代码编写阶段就会被编译器拦截。而运行时错误(如算术异常、数组越界、空指针等)则是在程序执行过程中动态发生的。
异常本质上是一系列类的集合。所有异常都继承自顶层类 Throwable,它派生出两个重要的子类:Error 和 Exception。
- Error:指 JVM 无法解决的严重问题,例如栈溢出(StackOverflowError)或内存溢出(OutOfMemoryError)。一旦发生,通常意味着系统层面的崩溃,程序难以恢复。
- Exception:指程序运行中可能出现的可处理问题。我们日常所说的'异常'通常指这一类。它又分为两大分支:
- 运行时异常(RuntimeException):非受查异常。这类异常通常在代码运行时发生,编译器不强制要求处理。比如空指针访问、数组下标越界。它们往往代表编程逻辑上的疏漏。
- 编译时异常(Checked Exception):受查异常。编译器强制要求必须处理,否则无法通过编译。比如文件读写失败、网络中断。这通常代表外部环境的不可控因素。
异常处理策略
在防御式编程中,主要有两种思路:
- LBYL (Look Before You Leap):先检查再行动。比如在操作前判断对象是否为 null。缺点是正常流程和错误处理代码混杂,逻辑显得臃肿。
- EAFP (It's Easier to Ask Forgiveness than Permission):先尝试后原谅。直接执行操作,捕获异常后再处理。这是 Java 推荐的主流风格,因为正常流程与错误处理分离,代码更清晰,开发者能更专注于业务逻辑。
核心关键字实战
Java 异常处理主要依赖五个关键字:throw、throws、try、catch、finally。
throw 与 throws 的区别
- throw:用于方法内部,主动抛出一个具体的异常对象。比如参数校验失败时,手动抛出
IllegalArgumentException。 - throws:用于方法声明处,表示该方法可能会抛出某些异常,将处理责任交给调用者。如果调用者不想处理,可以继续使用
throws向上抛出。
public void readFile(String path) throws IOException {
// 这里声明了可能抛出 IO 异常,具体实现由调用方决定如何处理
}
try-catch-finally 机制
try 块包裹可能出错的代码。一旦抛出异常,后续代码不再执行,直接跳转到匹配的 catch 块。
try {
int result = 10 / 0; // 触发 ArithmeticException
} catch (ArithmeticException e) {
System.out.println();
e.printStackTrace();
}


