想象你正在开车。有两类危险情况可能发生:
- 突发状况:比如一只猫突然窜到马路上(运行时异常)
- 可预见的危险:比如前方道路施工(受检异常)
对于突发状况,我们没法提前准备,只能在发生时立即应对。但对于道路施工这种可预见的情况,如果没有提前警示牌告诉你,那就是很危险的疏忽。
// 读取文件是一个可预见的风险操作
public void readFile(String path) throws IOException { // 编译器强制你声明这个风险
FileReader reader = new FileReader(path);
// 文件操作
}
public void processFile() {
// 编译器强制你处理这个风险,两种方式:
// 方式1:主动处理风险
try {
readFile("data.txt");
} catch (IOException e) {
// 处理文件读取失败的情况
logger.error("文件读取失败", e);
}
// 方式2:向上传递风险,让调用者知道并处理
throws IOException {
readFile("data.txt");
}
}
为什么这么设计?主要有三个原因:
- 提供编译时的安全保证
// 如果不处理 IOException,代码将无法通过编译
public void processFile() {
readFile("data.txt"); // 编译错误!
}
- 形成契约精神,即保证调用者一定要考虑这个情况
// 方法签名就是一个契约,明确告诉调用者可能的风险
public void transferMoney(Account from, Account to, double amount) throws InsufficientFundsException {
// 转账逻辑
}
// 调用者必须考虑这个风险
try {
bank.transferMoney(accountA, accountB, 1000);
} catch (InsufficientFundsException e) {
// 处理余额不足的情况
}
- 提高代码质量和可维护性
// 不好的设计:风险被隐藏
public boolean deletefile(String path) {
File file = new File(path);
return file.delete(); // 可能失败,但调用者并不知道原因
}
// 好的设计:风险被显式声明
public void deleteFile(String path) throws IOException {
File file = new File(path);
if (!file.exists()) {
throw new FileNotFoundException("文件不存在");
}
if (!file.canWrite()) {
throw new AccessDeniedException("没有删除权限");
}
if (!file.delete()) {
throw new IOException("删除失败");
}
}