12 异常处理¶
本章目标¶
- 理解什么是异常(Exception)
- 掌握 try-catch-finally
- 掌握 throw 与 throws
- 了解常见异常类型
12.1 什么是异常?¶
程序运行时出了错,Java 会抛出一个异常对象。不处理的话,程序直接崩溃退出。
int[] arr = {1, 2, 3};
System.out.println(arr[5]); // 运行时抛 ArrayIndexOutOfBoundsException
不处理时的输出:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 3
at Main.main(Main.java:3)
学会读异常信息是程序员的基本功:
异常类型: ArrayIndexOutOfBoundsException
错误描述: Index 5 out of bounds for length 3
出错位置: Main.main(Main.java:3) ← 第 3 行
12.2 try-catch 捕获异常¶
try {
// 可能出错的代码
int result = 10 / 0; // 除零
System.out.println(result);
} catch (ArithmeticException e) {
// 捕获后处理
System.out.println("数学错误: " + e.getMessage());
}
System.out.println("程序继续运行…"); // ✅ 不会崩溃
多 catch¶
try {
int[] arr = null;
arr[0] = 1;
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("数组越界");
} catch (NullPointerException e) {
System.out.println("空指针");
}
多个异常类型相同处理,可以用 |:
} catch (IOException | SQLException e) {
System.out.println("IO 或 SQL 错误");
}
12.3 finally:无论如何都会执行¶
try {
// 读文件
} catch (IOException e) {
System.out.println("读取失败");
} finally {
System.out.println("清理资源"); // 无论成功失败都执行
}
经典用途:关闭文件、释放连接。不过现在资源类推荐 try-with-resources(第 18 章 IO 讲):
try (BufferedReader br = new BufferedReader(new FileReader("a.txt"))) {
// 用 br 读文件,用完自动关闭
} catch (IOException e) {
System.out.println("读取失败: " + e.getMessage());
}
12.4 异常体系¶
Throwable(所有错误/异常的根)
├── Error 严重错误(内存溢出等,不处理)
│ └── OutOfMemoryError、StackOverflowError
└── Exception 异常(要处理)
├── RuntimeException 运行时异常(不强制处理)
│ ├── NullPointerException
│ ├── ArrayIndexOutOfBoundsException
│ ├── ArithmeticException
│ └── ClassCastException
└── 受检异常(编译器强制处理)
├── IOException
├── SQLException
└── ...
运行时异常 vs 受检异常¶
| 运行时异常 | 受检异常 | |
|---|---|---|
| 编译器要求处理 | 不要求 | 必须 try-catch 或 throws |
| 例子 | 空指针、越界 | 文件读写、网络 |
| 说明 | 代码逻辑问题 | 外部条件问题(文件不存在等) |
// 受检异常:编译器直接报错"未处理"
BufferedReader br = new BufferedReader(new FileReader("a.txt")); // ❌ 必须处理 IOException
12.5 throw 与 throws¶
throw:手动抛出异常¶
public void setLevel(int level) {
if (level < 1) {
throw new IllegalArgumentException("等级不能小于 1");
}
this.level = level;
}
throws:声明方法可能抛出异常¶
public void readConfig() throws IOException {
// 方法内部不处理,抛给调用者
FileReader fr = new FileReader("config.json");
}
// 调用者必须处理
try {
readConfig();
} catch (IOException e) {
System.out.println("配置文件读取失败");
}
12.6 自定义异常¶
public class NotEnoughMoneyException extends RuntimeException {
public NotEnoughMoneyException(String message) {
super(message);
}
}
// 使用
public void buy(int price, int money) {
if (money < price) {
throw new NotEnoughMoneyException("余额不足,还差 " + (price - money));
}
}
12.7 常见坑¶
1. 空指针 NPE¶
String s = null;
s.length(); // NPE!
// 防御写法
if (s != null && s.length() > 0) { ... }
2. 异常被吞掉¶
try {
doSomething();
} catch (Exception e) {
// 什么都不写 ← 极其不推荐!出错完全没痕迹
}
// 至少要打日志
catch (Exception e) {
System.out.println("出错了: " + e.getMessage());
e.printStackTrace(); // 打印完整堆栈
}
3. catch 范围过大¶
catch (Exception e) { ... } // 尽量 catch 具体类型
12.8 实战:模组场景¶
// 加载模组配置文件
public Map<String, Object> loadConfig(Path path) {
try {
String json = Files.readString(path);
return gson.fromJson(json, Map.class);
} catch (IOException e) {
System.out.println("[MyMod] 配置读取失败,使用默认配置: " + e.getMessage());
return new HashMap<>();
} catch (JsonSyntaxException e) {
System.out.println("[MyMod] 配置格式错误: " + e.getMessage());
return new HashMap<>();
}
}
12.9 小练习¶
- 故意制造 3 种运行时异常(空指针、越界、除零),观察报错信息
- 用 try-catch 捕获除零异常,程序不崩溃继续运行
- 自定义一个
TooHotException,当温度 > 100 时抛出,main 里捕获 - 写一个方法用 throws 声明受检异常,并在调用处 try-catch
本章小结¶
- 异常 = 程序出错的信号;不处理会崩溃
- try-catch-finally 捕获处理;try-with-resources 自动关资源
- 运行时异常不强制处理,受检异常必须处理
- throw 抛异常,throws 声明异常
- 别吞异常,至少打日志
下一章:13 泛型