JAVA学习之异常

1 阅读3分钟

JAVA异常

Java 异常(Exception)是 Java 语言中用于处理程序运行时错误和非正常流程的核心机制。它允许程序在遇到问题时优雅地处理,而不是直接崩溃

1.异常体系结构

Java 的所有异常都继承自 java.lang.Throwable 类,主要分为两大分支

Throwable
├── Error (严重错误)
│   ├── OutOfMemoryError      (内存溢出)
│   ├── StackOverflowError    (栈溢出)
│   └── NoClassDefFoundError  (类定义缺失)
│
└── Exception (可处理的异常)
    ├── Checked Exception (受检异常/编译时异常)
    │   ├── IOException
    │   ├── SQLException
    │   └── ClassNotFoundException
    │
    └── RuntimeException (非受检异常/运行时异常)
        ├── NullPointerException      (空指针)
        ├── ArrayIndexOutOfBoundsException (数组越界)
        ├── ClassCastException        (类型转换错误)
        ├── IllegalArgumentException  (非法参数)
        └── ArithmeticException       (算术异常,如除零)

2.三大核心分类

分类说明编译器检查典型示例处理建议
ErrorJVM 层面的严重问题,程序无法恢复❌ 不检查OOM、StackOverflow不应捕获,应修复代码或调整JVM参数
Checked Exception外部因素导致的可预见异常✅ 必须处理IO、网络、数据库用 try-catch 或 throws 声明
RuntimeException编程逻辑错误导致的异常❌ 不检查空指针、越界通过代码规范避免,而非依赖捕获

3.异常处理关键字

  1. try: 包裹可能抛出异常的代码块
  2. catch: 捕获并处理特定类型的异常(可多个,子类在前父类在后)
  3. finally: 无论是否异常都会执行,常用于资源释放
  4. throw: 手动抛出一个异常对象
  5. throws: 在方法签名上声明该方法可能抛出的异常
// 标准用法示例
public String readFile(String path) throws IOException {
    BufferedReader reader = null;
    try {
        reader = new BufferedReader(new FileReader(path));
        return reader.readLine();
    } catch (FileNotFoundException e) {
        System.err.println("文件不存在: " + path);
        throw e; // 重新抛出
    } catch (IOException e) {
        System.err.println("读取失败: " + e.getMessage());
        return null;
    } finally {
        // 传统方式关闭资源
        if (reader != null) {
            try { reader.close(); } catch (IOException ignored) {}
        }
    }
}

4.现代最佳实践

使用 try-with-resources(Java 7+)自动管理资源

try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
    return reader.readLine();
} // 自动关闭,无需 finally
  1. 精确捕获:只捕获你能处理的异常,不要笼统地 catch (Exception e)
  2. 保留异常链:重新包装异常时传入原始异常 new ServiceException("msg", cause)
  3. 使用日志框架记录异常:log.error("描述", e) 而非 e.printStackTrace()
  4. 自定义业务异常:继承 RuntimeException,携带错误码和业务信息

避免

  1. 吞掉异常:catch 块为空或仅打印消息而不处理/传播
  2. 用异常控制业务流程:异常有性能开销,不应用于正常逻辑分支
  3. 在 finally 中 return:会覆盖 try/catch 中的返回值和异常
  4. 过度使用 Checked Exception:现代框架(Spring等)普遍倾向 RuntimeException

5.自定义异常示例

public class BusinessException extends RuntimeException {
    private final int code;

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

    public BusinessException(int code, String message, Throwable cause) {
        super(message, cause); // 保留异常链
        this.code = code;
    }

    public int getCode() { return code; }
}

// 使用
if (user == null) {
    throw new BusinessException(404, "用户不存在");
}

6.面试高频点速记

  1. NoClassDefFoundError vs ClassNotFoundException:前者是运行时找不到类(Error),后者是动态加载时找不到(Checked Exception)
  2. final / finally / finalize 的区别
  3. try-with-resources 的原理:AutoCloseable 接口 + 编译器生成关闭代码
  4. 异常对性能的影响:创建异常对象时需要填充栈快照(fillInStackTrace),这是主要开销