异常体系

异常是什么

就是程序运行的时候出了意外状况,比如你去ATM取钱,余额不足——这就是一个”异常”

Java不会让程序直接崩掉,而是把这个意外包装成一个对象,你可以接住它、处理它

所有异常相关的类都在 java.lang 包下,不用额外import

Throwable 层次结构

Java异常体系就像一棵家族树,顶端是 Throwable,下面分两大分支

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Throwable(所有异常的祖宗)
├── Error(JVM级别的严重错误,你管不了)
│ ├── OutOfMemoryError(内存不够了)
│ ├── StackOverflowError(方法递归太深,栈溢出了)
│ └── VirtualMachineError(虚拟机要挂了)

└── Exception(程序可以处理的异常)
├── 受检异常 Checked Exception(编译器逼你处理)
│ ├── IOException(文件读写出错)
│ ├── SQLException(数据库操作出错)
│ ├── FileNotFoundException(文件找不到)
│ └── ClassNotFoundException(类找不到)

└── RuntimeException(运行时异常,编译器不管你)
├── NullPointerException(空指针,最常见!)
├── ArrayIndexOutOfBoundsException(数组越界)
├── ClassCastException(类型转换错误)
├── ArithmeticException(算术错误,比如除以0
├── NumberFormatException(数字格式错误)
└── IllegalArgumentException(非法参数)

Error 和 Exception 的区别

Error 是 JVM 层面的灾难,程序员处理不了,出现了基本就是要崩

Exception 是程序层面的问题,程序员可以写代码来应对

受检异常 vs 非受检异常

这是异常体系里最核心的区分,面试必问

受检异常(Checked Exception)

编译器强制你处理,你不处理就编译不过

就像出门前老妈逼你带伞——“天气预报说要下雨,你不带伞别想出门”

典型代表:IOExceptionSQLExceptionFileNotFoundException

处理方式:要么 try-catch 捕获,要么 throws 甩给调用者

非受检异常(Unchecked Exception / RuntimeException)

编译器不管,但运行时可能炸

就像你走路可能摔跤——没人能提前阻止你,但你可以注意脚下

典型代表:NullPointerExceptionArrayIndexOutOfBoundsException

通常是程序员写了bug,应该从代码层面避免,而不是 try-catch

对比项 受检异常 Checked 非受检异常 Unchecked
继承关系 继承 Exception(不含RuntimeException) 继承 RuntimeException
编译器是否强制处理 强制,不处理编译报错 不强制,编译能过
出现时机 通常是外部环境问题(文件、网络、数据库) 通常是代码逻辑问题(空指针、越界)
处理方式 try-catchthrows 修复代码逻辑,必要时才 catch
典型代表 IOExceptionSQLException NullPointerExceptionClassCastException
设计理念 “这个问题可能会发生,你必须提前准备” “这个问题是你代码写错了,去改代码”

常见异常对照表

异常名 什么情况下出现 怎么避免
NullPointerException 对null对象调用方法或访问属性 使用前先判null,或用Optional
ArrayIndexOutOfBoundsException 数组下标超出范围(< 0 或 >= length) 循环条件写对,i < arr.length
ClassCastException 强制类型转换失败,比如把Dog转成Cat 转换前用 instanceof 判断
ArithmeticException 整数除以0 除法前检查除数是否为0
NumberFormatException 字符串转数字失败,比如 Integer.parseInt("abc") 转换前校验字符串格式
StringIndexOutOfBoundsException 字符串下标越界 检查下标范围
StackOverflowError 递归没有正确退出条件,无限递归 确保递归有终止条件
OutOfMemoryError 创建了太多对象,内存不够 检查内存泄漏,调整JVM参数
IOException 文件读写、网络通信出错 try-catch处理,检查文件是否存在
FileNotFoundException 要读的文件不存在 先用 File.exists() 判断
ConcurrentModificationException 遍历集合时修改了集合 用Iterator的remove,或用CopyOnWriteArrayList

代码示例:异常的基本演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@Test
public void testCommonExceptions() {
System.out.println("=== 常见异常演示 ===");

// 1. NullPointerException —— 最常见的异常,没有之一
String str = null;
try {
int len = str.length(); // 对null调用方法,炸了
} catch (NullPointerException e) {
System.out.println("空指针异常:" + e.getMessage());
}

// 2. ArrayIndexOutOfBoundsException
int[] arr = {1, 2, 3};
try {
int val = arr[5]; // 数组只有3个元素,你要第6个?
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组越界:" + e.getMessage());
}

// 3. ArithmeticException
try {
int result = 10 / 0; // 除以0,数学不允许
} catch (ArithmeticException e) {
System.out.println("算术异常:" + e.getMessage());
}

// 4. ClassCastException
try {
Object obj = "hello";
Integer num = (Integer) obj; // 字符串不能强转成Integer
} catch (ClassCastException e) {
System.out.println("类型转换异常:" + e.getMessage());
}

// 5. NumberFormatException
try {
int num = Integer.parseInt("abc"); // "abc"不是数字
} catch (NumberFormatException e) {
System.out.println("数字格式异常:" + e.getMessage());
}
}

常见坑

坑1:catch了Exception就觉得万事大吉

不要无脑 catch (Exception e) {},空catch块会吞掉异常,出了问题你都不知道

至少打个日志:e.printStackTrace() 或者用日志框架记录

坑2:把Error也catch了

catch (Throwable t) 会连Error一起接住,但Error你根本处理不了

正确做法:只catch你能处理的异常

坑3:用异常来控制流程

别写 try { ... } catch (Exception e) { ... } 来代替 if-else 判断

异常是用来处理意外情况的,不是用来做正常逻辑控制的,性能也差

练习题

题1:看代码说输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void main(String[] args) {
try {
int[] arr = new int[3];
arr[5] = 10;
System.out.println("A");
} catch (NullPointerException e) {
System.out.println("B");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("C");
} catch (Exception e) {
System.out.println("D");
}
System.out.println("E");
}
// 输出是什么?答案:C E
// 数组越界 → 被第二个catch接住 → 打印C → try-catch结束 → 打印E

题2:以下哪些是受检异常?

A. NullPointerException → 非受检(RuntimeException子类)

B. IOException受检

C. ArrayIndexOutOfBoundsException → 非受检(RuntimeException子类)

D. SQLException受检

E. ClassCastException → 非受检(RuntimeException子类)

答案:B和D

题3:Error和Exception的区别是什么?分别举例

Error:JVM级别的严重错误,程序无法处理。如 OutOfMemoryErrorStackOverflowError

Exception:程序级别的异常,可以通过代码处理。如 IOExceptionNullPointerException

相关笔记

try-catch-finally | throw与throws | 自定义异常


上一章 目录 下一章
泛型 java基础 try-catch-finally