Z
ZHANK
异常处理

异常处理机制

掌握 try-catch-finally、throws、自定义异常

异常处理

程序不可能永远正常运行——文件不存在、网络断开、用户输入非法……异常处理就是给程序加上"安全带",让它在出问题时优雅处理而不是直接崩溃。

学完本章你将: 掌握异常体系、try-catch-finally、throws、try-with-resources、自定义异常。


异常体系 —— 两大类

Java 把异常分为两类,核心区别在编译时是否强制处理

Throwable(所有异常的祖宗)
├── Error — 严重系统错误(OOM、StackOverflow),程序无法处理,只能认栽
└── Exception
    ├── RuntimeException — 运行时异常(NPE、数组越界),不强制处理
    └── 受检异常(Checked)— 编译时必须 try-catch 或 throws,如 IOException
类型示例是否强制处理典型原因
Error`OutOfMemoryError`JVM 资源耗尽
RuntimeException`NullPointerException`代码逻辑 bug
受检异常`IOException`、`SQLException`外部因素(文件、网络)

💡 设计思想: 受检异常是"可预见的外部问题",编译器强迫你提前想好怎么应对。运行时异常是"代码写错了",应该修代码而不是捕获。


try-catch-finally

try 包裹可能出错的代码。catch 按异常类型匹配——子类异常必须写在父类前面,否则子类永远匹配不到。finally 无论是否异常都会执行,通常用来释放资源。

java
try {
    int[] arr = {1, 2, 3};
    System.out.println(arr[10]); // 数组越界→跳入 catch
} catch (ArrayIndexOutOfBoundsException e) {
    System.out.println("下标越界:" + e.getMessage());
} finally {
    System.out.println("无论如何我都执行——比如关闭文件");
}

// 多个 catch:先子类后父类
try {
    String s = null;
    s.length();
} catch (NullPointerException e) {   // 子类在前
    System.out.println("空指针!");
} catch (Exception e) {              // 父类在后(兜底)
    System.out.println("其他异常:" + e);
}

throws —— 把锅甩给调用者

方法内部不处理异常时,可以用 throws 声明"这个方法可能抛出这些异常,调用者你来处理"。

java
// 声明:我可能抛出 IOException,你来解决
public static String readFirstLine(String path) throws IOException {
    BufferedReader reader = new BufferedReader(new FileReader(path));
    String line = reader.readLine();
    reader.close();
    return line;
}

// 调用者:要么 try-catch,要么继续往上 throws
try {
    String line = readFirstLine("test.txt");
} catch (IOException e) {
    System.err.println("读文件失败:" + e.getMessage());
}

try-with-resources —— 自动关闭

finally 中手动关闭资源容易忘、容易写错。Java 7+ 的 try-with-resources 会自动调用 close(),管它是正常结束还是抛异常。

java
// try (资源声明) —— 出了这个块自动 close,无论是否异常
try (
    BufferedReader reader = new BufferedReader(new FileReader("in.txt"));
    BufferedWriter writer = new BufferedWriter(new FileWriter("out.txt"))
) {
    String line;
    while ((line = reader.readLine()) != null) {
        writer.write(line.toUpperCase());
        writer.newLine();
    }
} catch (IOException e) {
    System.err.println("处理文件出错:" + e.getMessage());
}
// 不用写 finally reader.close() writer.close() 了!

自定义异常

内置异常有时语义不够明确,自定义异常让错误更有可读性。

java
// 继承 Exception(受检)或 RuntimeException(非受检)
public class InsufficientFundsException extends Exception {
    private final double deficit; // 差多少钱

    public InsufficientFundsException(double deficit) {
        super("余额不足,还差 " + deficit + " 元");
        this.deficit = deficit;
    }

    public double getDeficit() { return deficit; }
}

// 使用
public void withdraw(double amount) throws InsufficientFundsException {
    if (balance < amount) {
        throw new InsufficientFundsException(amount - balance);
    }
    balance -= amount;
}

💡 经验: 大多数场景用已有的异常类就够了。需要自定义时,优先继承 RuntimeException(不强制 try-catch,更灵活)。

public double getDeficit() { return deficit; }

}

// 使用 public void withdraw(double amount) throws InsufficientFundsException { if (balance < amount) { throw new InsufficientFundsException(amount - balance); } balance -= amount; }