Skip to content

异常处理(try-catch-finally、throws、try-with-resources)

概述

在了解 异常体系 之后,需要掌握如何正确处理异常。Java 提供了三种核心机制:try-catch-finally 在方法内部捕获并处理异常;throws 在方法签名上声明「可能抛出的异常」,由调用方处理;try-with-resources(JDK 7+)用于自动关闭实现了 AutoCloseable 的资源(如流、连接),避免遗漏关闭导致资源泄漏。本文逐一说明语法、适用场景与组合用法。


try-catch-finally:捕获并处理异常

基本语法

在可能抛出异常的代码外包裹 try,用 catch 捕获指定类型的异常并处理,可选 finally 在无论是否发生异常时都执行(常用于释放资源)。

java
try {
    // 可能抛出异常的代码
} catch (异常类型1 e) {
    // 处理异常类型1
} catch (异常类型2 e) {
    // 处理异常类型2
} finally {
    // 可选:总会执行的代码(如关闭流)
}
  • try:必须,包裹可能抛出异常的代码。
  • catch:可有一个或多个,按书写顺序匹配;捕获的异常类型可以是具体子类或父类(如先 catch (IOException e)catch (Exception e) 会编译报错,因为 Exception 已包含 IOException,应子类在前)。
  • finally:可选;无论是否发生异常、是否被 catch,都会执行(除非 JVM 退出或线程被 kill)。

提示

从 JDK 7 开始支持多异常合并捕获catch (IOException | SQLException e),同一 catch 块中 e 被视为这些类型的共同父类型,不能再用 e 调用各自特有的方法。

示例 1:基础 try-catch

java
public class TryCatchDemo {
    public static void main(String[] args) {
        try {
            int n = Integer.parseInt("abc");  // 抛出 NumberFormatException
            System.out.println(n);
        } catch (NumberFormatException e) {
            System.err.println("数字格式错误: " + e.getMessage());
        }
        System.out.println("程序继续执行");
    }
}

NumberFormatException 是非受检异常,不强制捕获;这里主动捕获后打印信息并继续运行。

示例 2:多 catch 与 finally

java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

public void readAndParse(String path) {
    try {
        String content = Files.readString(Path.of(path));  // 可能抛出 IOException
        int value = Integer.parseInt(content.trim());     // 可能抛出 NumberFormatException
        System.out.println("解析结果: " + value);
    } catch (IOException e) {
        System.err.println("文件读取失败: " + e.getMessage());
    } catch (NumberFormatException e) {
        System.err.println("内容不是有效数字: " + e.getMessage());
    } finally {
        // 例如:释放不实现 AutoCloseable 的某种资源,或打日志
        System.out.println("readAndParse 结束");
    }
}

IOException 是受检异常,必须被捕获或通过 throws 声明;finally 无论是否发生异常都会执行。

示例 3:多异常合并捕获(JDK 7+)

java
try {
    // 可能抛出 IOException 或 UnsupportedEncodingException 等
    byte[] bytes = "hello".getBytes("UTF-8");
    // ...
} catch (IOException e) {
    // e 为 IOException 类型,可统一处理
    System.err.println("IO 异常: " + e.getMessage());
}

throws:声明抛出异常

基本语法

若当前方法不打算处理某种异常,可在方法签名上用 throws 声明,将异常交给调用方处理。调用方同样要么 try-catch,要么继续 throws,直到有调用方捕获或传到 main(再往外则交给 JVM,程序可能终止)。

java
修饰符 返回类型 方法名(参数列表) throws 异常类型1, 异常类型2 {
    // 方法体
}

注意

只有受检异常必须在方法上声明或在本方法内捕获;非受检异常(RuntimeException 及其子类)可声明也可不声明,编译器不强制。

示例 1:受检异常声明抛出

java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

// 调用方必须处理 IOException(try-catch 或继续 throws)
public String readFirstLine(String path) throws IOException {
    return Files.readAllLines(Path.of(path)).get(0);
}

// 调用方选择在此处捕获
public void useReadFirstLine(String path) {
    try {
        String line = readFirstLine(path);
        System.out.println(line);
    } catch (IOException e) {
        System.err.println("读取失败: " + e.getMessage());
    }
}

示例 2:同时声明多个异常

java
import java.io.IOException;
import java.sql.SQLException;

public void saveToFileAndDb(String data) throws IOException, SQLException {
    Files.writeString(Path.of("out.txt"), data);  // 可能抛出 IOException
    // 假设某方法可能抛出 SQLException
    // jdbcTemplate.update(...);
}

调用 saveToFileAndDb 的代码必须处理这两种受检异常(或其一,或继续 throws)。


try-with-resources:自动关闭资源

基本语法与适用场景

实现了 AutoCloseable(或 JDK 7 之前的 Closeable)的资源,如 InputStreamOutputStreamReaderWriterConnection 等,应在使用完毕后调用 close()try-with-resources 会在 try 块正常结束或发生异常时自动调用 close(),避免遗忘关闭导致资源泄漏。

java
try (资源声明1; 资源声明2; ...) {
    // 使用资源
}
// 离开 try 块时自动按声明顺序的逆序关闭资源
  • 资源声明形式:ResourceType var = new ...,且 ResourceType 必须实现 AutoCloseable
  • 可声明多个资源,用分号分隔;关闭顺序与声明顺序相反
  • JDK 9+ 可在 try 外先声明变量,再在 try 中引用(变量需为 effectively final)。

提示

优先使用 try-with-resources 管理所有「打开后需关闭」的资源,代码更简洁,且异常时也能保证关闭,避免 finally 中再写 close 的繁琐与遗漏。

示例 1:单资源

java
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public void readLines(String path) throws IOException {
    try (BufferedReader br = new BufferedReader(new FileReader(path))) {
        String line;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
    }
    // br.close() 已自动调用,即使发生异常也会先关闭再抛出
}

BufferedReader 实现了 Closeable(继承自 AutoCloseable),因此可放在 try-with-resources 中。

示例 2:多资源与异常抑制

java
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public void copyFile(String src, String dest) throws IOException {
    try (FileInputStream in = new FileInputStream(src);
         FileOutputStream out = new FileOutputStream(dest)) {
        byte[] buf = new byte[8192];
        int n;
        while ((n = in.read(buf)) != -1) {
            out.write(buf, 0, n);
        }
    }
    // 先关闭 out,再关闭 in;若关闭时抛出异常,会作为「抑制异常」附加到主异常
}

若 try 块内抛出异常,关闭时再抛异常,则主异常会被保留,关闭产生的异常通过 Throwable.getSuppressed() 获取,不会丢失。

示例 3:在 try-with-resources 中捕获异常

java
public void readFirstLineSafe(String path) {
    try (BufferedReader br = new BufferedReader(new FileReader(path))) {
        System.out.println(br.readLine());
    } catch (IOException e) {
        System.err.println("读取失败: " + e.getMessage());
    }
}

既自动关闭资源,又在当前方法内处理了 IOException,调用方无需再 throws。


组合使用:何时用 catch,何时用 throws

场景建议
当前方法能妥善处理异常(如重试、降级、记录日志后返回默认值)使用 try-catch 在本方法内处理
当前方法只做「透传」,由上层或调用方统一处理使用 throws 声明
使用了流、连接等需关闭的资源使用 try-with-resources,可再配合 catch 或 throws

同一方法内可以:用 try-with-resources 打开资源,在 try 块内写业务逻辑,用 catch 处理受检异常,无需在签名上写 throws。


注意事项

注意

catch 顺序:若多个 catch 捕获的类型有继承关系,必须先写子类、再写父类,否则子类 catch 永远不可达,编译器报错。

java
// 错误示例:Exception 包含 IOException,IOException 的 catch 不可达
try {
    Files.readAllLines(Path.of("x"));
} catch (Exception e) {
    // ...
} catch (IOException e) {  // 编译错误:不可达
    // ...
}

注意

finally 与 return:若 try 或 catch 中有 returnfinally 仍会执行;若 finally 里也有 return,会覆盖 try/catch 的返回值,容易造成困惑,应避免在 finally 中 return。

提示

对实现了 AutoCloseable 的资源,优先使用 try-with-resources,避免在 finally 中手动 close;若 close 本身可能抛异常,需再 try-catch,代码会变得冗长且易错。

注意

不要用空的 catch 块「吞掉」异常(如 catch (Exception e) {}),至少应记录日志或做有意义的处理,否则问题难以排查。


相关链接

基于 VitePress 构建