java

异常处理机制

By AI-Writer 8 min read

异常处理机制

**异常(Exception)**是程序运行过程中出现的非正常情况。Java 提供了完整的异常处理机制,让程序能够在出错时优雅地做出反应,而不是直接崩溃。本文将系统讲解 Java 异常体系、常用处理语法以及自定义异常的实践。

Java 异常体系结构

Java 的异常体系以 Throwable 为顶层父类,分为两大分支:

  • Error(错误):JVM 本身发生的严重问题,如内存溢出(OutOfMemoryError)、栈溢出(StackOverflowError)。程序无法从 Error 中恢复,不应捕获 Error
  • Exception(异常):程序运行中可预见的问题,如空指针访问、数组越界、网络中断等。其中 RuntimeException 及其子类称为非受检异常(Unchecked Exception),编译器不强制要求处理;其他 Exception 及其子类称为受检异常(Checked Exception),必须在代码中显式处理。
plaintext
Throwable
├── Error(错误)
│   ├── OutOfMemoryError
│   └── StackOverflowError
└── Exception(异常)
    ├── RuntimeException(非受检异常)
    │   ├── NullPointerException
    │   ├── ArrayIndexOutOfBoundsException
    │   └── IllegalArgumentException
    └── IOException / SQLException 等(受检异常)

try-catch-finally:基本异常捕获

基础语法

java
try {
    // 可能抛出异常的代码
    int result = 10 / 0;  // ArithmeticException:除数不能为零
} catch (ArithmeticException e) {
    // 捕获特定异常类型
    System.out.println("发生算术异常:" + e.getMessage());
} catch (Exception e) {
    // 捕获更通用的异常(兜底)
    System.out.println("发生未知异常:" + e.getMessage());
} finally {
    // 无论是否发生异常,finally 块一定会执行
    System.out.println("清理资源...");
}

执行顺序:try 中的代码从左到右执行——若发生异常,控制权跳转到匹配的 catch;若未发生异常,跳过所有 catch,直接执行 finally。

多异常捕获(Java 7+)

java
try {
    int index = Integer.parseInt(input);
    int value = arr[index];
} catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
    // Java 7+ 支持在一个 catch 中捕获多种异常类型
    System.out.println("输入无效:" + e.getMessage());
}

finally 的实际用途

finally 块最适合做资源释放——关闭文件流、数据库连接、网络连接等:

java
import java.io.*;

FileInputStream fis = null;
try {
    fis = new FileInputStream("data.txt");
    byte[] buffer = new byte[1024];
    int len = fis.read(buffer);
    System.out.println("读取到 " + len + " 字节");
} catch (IOException e) {
    System.out.println("读取失败:" + e.getMessage());
} finally {
    // 即使发生异常,也要确保资源被关闭
    if (fis != null) {
        try {
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

不过在 Java 7+ 中,推荐使用 try-with-resources 自动管理资源。

try-with-resources(自动资源管理)

try-with-resources 是 Java 7 引入的语法,声明了实现 AutoCloseable 接口的资源后,代码块结束时自动调用 close() 方法,无需手动编写 finally:

java
// 传统写法:需要手动关闭,代码繁琐
try {
    // ...
} finally {
    if (resource != null) {
        resource.close();
    }
}

// try-with-resources:自动关闭,简洁安全
try (FileInputStream fis = new FileInputStream("data.txt")) {
    byte[] buffer = new byte[1024];
    int len = fis.read(buffer);
    System.out.println("读取到 " + len + " 字节");
} catch (IOException e) {
    System.out.println("读取失败:" + e.getMessage());
}
// fis 在此处自动关闭,即使发生异常也会执行
java
// 同时管理多个资源
try (
    FileInputStream fis = new FileInputStream("data.txt");
    FileOutputStream fos = new FileOutputStream("copy.txt")
) {
    byte[] buffer = new byte[8192];
    int len;
    while ((len = fis.read(buffer)) != -1) {
        fos.write(buffer, 0, len);
    }
    System.out.println("文件复制完成");
} catch (IOException e) {
    System.out.println("IO 错误:" + e.getMessage());
}

throws 与 throw:异常声明与抛出

throws:声明可能抛出的异常

方法签名中使用 throws 关键字声明该方法可能抛出的异常类型,将异常处理的责任交给调用方

java
// 声明多个受检异常
public void readFile(String path) throws IOException, FileNotFoundException {
    FileReader reader = new FileReader(path);  // 可能抛出 FileNotFoundException
    BufferedReader br = new BufferedReader(reader);
    String line = br.readLine();
    br.close();
}

throw:手动抛出异常

使用 throw 关键字主动抛出一个异常实例,通常配合条件判断使用:

java
public double divide(double a, double b) {
    if (b == 0) {
        // 手动抛出 ArithmeticException
        throw new ArithmeticException("除数不能为零");
    }
    return a / b;
}
java
// 调用方捕获异常
try {
    double result = divide(10, 0);
} catch (ArithmeticException e) {
    System.out.println(e.getMessage());  // 除数不能为零
}

throw vs throwsthrow 是动词”抛出”,用于生成并抛出异常对象;throws 是副词”声明”,用于方法签名中声明可能抛出的异常类型。

自定义异常

当 Java 内置异常无法精确描述业务错误时,可以定义自己的异常类:

java
// 继承 RuntimeException → 非受检异常(业务异常常用)
public class BusinessException extends RuntimeException {
    private int errorCode;  // 业务错误码

    public BusinessException(String message) {
        super(message);
    }

    public BusinessException(String message, Throwable cause) {
        super(message, cause);
    }

    public BusinessException(int errorCode, String message) {
        super(message);
        this.errorCode = errorCode;
    }

    public int getErrorCode() {
        return errorCode;
    }
}
java
public class UserService {
    public User login(String username, String password) {
        if (username == null || username.trim().isEmpty()) {
            throw new BusinessException(1001, "用户名不能为空");
        }
        if (password == null || password.length() < 6) {
            throw new BusinessException(1002, "密码长度不能少于6位");
        }
        // 登录逻辑...
        return user;
    }
}

异常处理的最佳实践

1. 避免空的 catch 块

java
// ❌ 错误:异常被静默忽略,问题难以排查
try {
    doSomething();
} catch (Exception e) {
    // 什么都不做
}

// ✅ 正确:至少记录异常信息
try {
    doSomething();
} catch (Exception e) {
    LOGGER.warning("操作失败: " + e.getMessage());
    // 或重新抛出,提供更多上下文
    throw new RuntimeException("业务操作失败", e);
}

2. 异常链(异常传递)

捕获低层异常后重新抛出高层异常,保留原始异常信息:

java
try {
    dao.findUserById(id);
} catch (SQLException e) {
    // 保留原始异常原因,避免信息丢失
    throw new BusinessException("查询用户失败", e);
}

3. 合理选择受检 vs 非受检异常

  • 受检异常(checked):外部资源问题(IO、数据库、网络),调用方必须处理——适用于可恢复的业务场景
  • 非受检异常(unchecked):编程错误(空指针、参数非法)、不可恢复的系统错误——这类异常通常不需要声明 throws

小结

  • Java 异常分为 Error(无法恢复)和 Exception(可处理),其中 RuntimeException 为非受检异常
  • try-catch-finally 是基本异常捕获语法;try-with-resources(Java 7+)是更优雅的资源管理方式
  • throws 在方法签名中声明异常,throw 在代码中主动抛出异常
  • 自定义异常应继承 RuntimeException(业务异常)或 Exception(需要调用方处理)
  • 避免空的 catch 块,合理使用异常链传递上下文信息

掌握异常处理后,程序的健壮性将大幅提升。下一节我们将学习 Java 17+ 的新特性,这些新语法能显著提升代码的表达力和安全性。

#java #异常处理 #Exception #Error #防御式编程

评论

A

Written by

AI-Writer

Related Articles

java
#12

JVM 原理与调优基础

深入讲解 JVM 内存结构(堆/栈/方法区)、类加载机制、垃圾回收算法(GC),以及 Java 17+ G1GC、ZGC、常用调优参数和诊断工具

Read More