元数据卡
- 前置知识:异常机制(第11章)
- 预计时间:15 分钟
- 完成标志:能写出带多 catch、try-with-resources 的健壮代码;能自定义异常类
多 catch 块
一个 try 可能抛出多种异常。按从具体到通用的顺序排列 catch 块:
import java.io.*;
public class MultiCatchDemo {
public static void main(String[] args) {
try {
String input = readFile("config.txt");
int number = Integer.parseInt(input);
System.out.println("数字是:" + number);
} catch (FileNotFoundException e) {
System.out.println("config.txt 没找到,用默认配置");
} catch (NumberFormatException e) {
System.out.println("内容不是数字:" + e.getMessage());
} catch (IOException e) {
System.out.println("IO 出错:" + e.getMessage());
} catch (Exception e) {
System.out.println("未知错误:" + e.getMessage());
}
}
static String readFile(String path) throws IOException {
return new BufferedReader(new FileReader(path)).readLine();
}
}运行步骤: javac MultiCatchDemo.java && java MultiCatchDemo
预期输出(假设 config.txt 不存在):
config.txt 没找到,用默认配置子类异常必须在父类前(FileNotFoundException -> IOException -> Exception);Exception 只作末尾兜底。
Java 7+ 支持一个 catch 捕获多种异常:
catch (NumberFormatException | IOException e) {
System.out.println("数据格式或 IO 问题:" + e.getMessage());
}try-with-resources(Java 7+)
手工 finally 关资源啰嗦且容易盖掉原始异常。try-with-resources 替你搞定:
import java.io.*;
public class TryWithResourcesDemo {
public static void main(String[] args) {
try (BufferedReader reader = new BufferedReader(
new FileReader("map.txt"))) {
String line = reader.readLine();
System.out.println("地图数据:" + line);
} catch (IOException e) {
System.out.println("加载地图失败:" + e.getMessage());
}
}
}运行步骤: 同目录创建 map.txt,内容写 森林,150,200,然后 javac TryWithResourcesDemo.java && java TryWithResourcesDemo
预期输出:
地图数据:森林,150,200机制: 实现了 AutoCloseable 的资源,try 结束后 JVM 自动调 close()。多资源也行:
try (BufferedReader r = new BufferedReader(new FileReader("in.txt"));
BufferedWriter w = new BufferedWriter(new FileWriter("out.txt"))) {
String line;
while ((line = r.readLine()) != null) { w.write(line); }
}自定义异常
Java 内置异常不够用时,自己定义:
// 受检异常——外部环境问题
public class InsufficientGoldException extends Exception {
public InsufficientGoldException(int required, int available) {
super("需要 " + required + " 金币,但背包里只有 " + available);
}
}
// 非受检异常——调用方数据问题
public class InvalidQuestException extends RuntimeException {
public InvalidQuestException(String questName) {
super("任务 \"" + questName + "\" 不存在或已过期");
}
}使用:
public class CustomExceptionDemo {
public static void main(String[] args) {
try { buySword(50, 30);
} catch (InsufficientGoldException e) {
System.out.println(e.getMessage());
}
}
static void buySword(int cost, int gold)
throws InsufficientGoldException {
if (gold < cost) {
throw new InsufficientGoldException(cost, gold);
}
System.out.println("购买成功!");
}
}运行步骤: javac CustomExceptionDemo.java && java CustomExceptionDemo
预期输出:
需要 50 金币,但背包里只有 30三条约定: 类名以 Exception 结尾;构造方法传递有意义的上下文;调用方数据问题 -> RuntimeException,外部环境问题 -> Exception。
常见反模式
空 catch: 只写 catch 但不做任何事——错误被静默吞掉,你永远不知道出过问题。至少打印日志。
catch Exception 一把抓: 用 catch (Exception e) 覆盖所有异常——到底哪个步骤出了哪种异常?拆开分别处理。
finally 里 return: finally 中的 return 会覆盖 try/catch 里的任何 return 和异常。永远别在 finally 里写 return。
public static int getNumber() {
try { int result = 10 / 0; return 1;
} catch (ArithmeticException e) { return 2;
} finally { return 3; }
}
// getNumber() -> 3(2 被吞了)旅人笔记
多 catch 从具体到通用。try-with-resources 自动关资源。自定义异常给错误起名字。别空 catch、别 Exception 一把抓、别在 finally 里 return。
-> 下一步:泛型
写了三遍几乎一样的代码——为 int、double、String 各写一个"最大值"函数。泛型让你写一次。