一、开篇引入:为什么要重视 Java 异常?

  1. 实际开发场景痛点:程序崩溃、调试困难、用户体验差等问题与异常处理的关联
  1. 异常处理的核心价值:保证程序稳定性、提升代码可维护性、便于问题定位
  1. 本文阅读收益:明确异常核心概念、掌握规范处理流程、规避常见坑点

二、Java 异常基础:搞懂最核心的 3 个问题

2.1 什么是 Java 异常?

  • 定义:程序运行时偏离预期的非正常情况(如空指针、数组越界)
  • 与错误(Error)的区别:异常可处理,错误(如 OOM)通常无法恢复
  • 生活类比:用 “开车遇到爆胎(异常,可修复)” 和 “发动机报废(错误,无法修复)” 辅助理解

2.2 Java 异常体系结构

  • 核心父类:Throwable(包含 Error 和 Exception)
  • 两大分支详解:
  • Error:虚拟机相关错误(如 StackOverflowError、OutOfMemoryError),示例代码演示触发场景
  • Exception:可处理的异常,细分两类:
  • 受检异常(Checked Exception):编译期强制处理(如 IOException、SQLException)
  • 非受检异常(Unchecked Exception):运行时异常(如 NullPointerException、ArrayIndexOutOfBoundsException)
  • 体系结构图:用 Mermaid 语法绘制清晰的类继承关系图

2.3 常见异常案例与场景

异常类型

触发场景

代码示例

NullPointerException

调用 null 对象的方法或属性

String str = null; str.length();

ArrayIndexOutOfBoundsException

数组下标超出范围

int[] arr = {1}; arr[1] = 2;

ClassCastException

非法类型转换

Object obj = "abc"; Integer i = (Integer)obj;

三、异常处理核心语法:try-catch-finally-throw-throws

3.1 基础组合:try-catch

  • 语法结构:try 块包裹可能抛异常的代码,catch 块捕获并处理
  • 代码示例:捕获空指针异常并打印日志

try {

String str = null;

System.out.println(str.length());

} catch (NullPointerException e) {

// 异常处理逻辑:日志记录、友好提示等

System.err.println("发生空指针异常:" + e.getMessage());

e.printStackTrace();

}

  • 关键注意点:catch 块顺序(子类异常在前,父类异常在后)

3.2 资源释放:finally

  • 作用:无论是否发生异常,都会执行(常用于关闭流、连接等资源)
  • 代码示例:结合 try-catch 使用 finally 关闭文件流

FileReader reader = null;

try {

reader = new FileReader("test.txt");

// 读取文件操作

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

} finally {

// 确保资源释放

if (reader != null) {

try {

reader.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

  • 特殊场景:finally 与 return 的执行顺序(finally 先于 return 执行)

3.3 主动抛异常:throw

  • 语法:在方法内部用throw new 异常类()主动触发异常
  • 代码示例:参数校验不通过时抛非法参数异常

public void checkAge(int age) {

if (age < 0 || age > 150) {

// 主动抛出异常,明确错误原因

throw new IllegalArgumentException("年龄必须在0-150之间,当前值:" + age);

}

System.out.println("年龄校验通过");

}

3.4 声明异常:throws

  • 作用:在方法签名上声明可能抛出的异常,告知调用者处理
  • 代码示例:声明方法可能抛出 IO 异常

// 方法声明抛出IOException,由调用者处理

public void readFile(String filePath) throws FileNotFoundException, IOException {

FileReader reader = new FileReader(filePath);

reader.read();

reader.close();

}

  • 与 throw 的区别:throw 是 “执行动作”,throws 是 “声明告知”

四、进阶技巧:提升异常处理的专业性

4.1 自定义异常:打造业务专属异常

  • 场景:业务错误需要区分(如 “用户不存在异常”“余额不足异常”)
  • 实现步骤:
  1. 继承 Exception(受检)或 RuntimeException(非受检)
  2. 实现无参、带消息、带 cause 的构造方法
  • 代码示例:自定义业务异常

// 自定义非受检异常(业务异常通常用非受检)

public class UserNotFoundException extends RuntimeException {

// 无参构造

public UserNotFoundException() {

super();

}

// 带错误消息的构造

public UserNotFoundException(String message) {

super(message);

}

// 带错误消息和根因的构造(便于异常链追踪)

public UserNotFoundException(String message, Throwable cause) {

super(message, cause);

}

}

  • 使用场景:用户查询接口中,未找到用户时抛出

五、避坑指南:90% 开发者会犯的异常处理错误

捕获异常后不处理也不记录日志:问题无法定位,示例代码反例

用 Exception 捕获所有异常:掩盖具体错误,无法精准处理

finally 块中修改返回值:逻辑混乱,返回结果不符合预期

抛出异常后不提供明确消息:调试时无法快速定位原因

业务逻辑用异常控制流程:性能差,代码可读性低(如用 NullPointerException 判断空值)

更多推荐