跳到内容

元数据卡

  • 前置知识:异常机制(第11章)
  • 预计时间:15 分钟
  • 完成标志:能写出带多 catch、try-with-resources 的健壮代码;能自定义异常类

多 catch 块

一个 try 可能抛出多种异常。按从具体到通用的顺序排列 catch 块:

java
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 捕获多种异常:

java
catch (NumberFormatException | IOException e) {
    System.out.println("数据格式或 IO 问题:" + e.getMessage());
}

try-with-resources(Java 7+)

手工 finally 关资源啰嗦且容易盖掉原始异常。try-with-resources 替你搞定:

java
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()。多资源也行:

java
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 内置异常不够用时,自己定义:

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 + "\" 不存在或已过期");
    }
}

使用:

java
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。

java
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 各写一个"最大值"函数。泛型让你写一次。

看泛型 ->

Built with VitePress | Software Systems Atlas