作为一名 Java 开发工程师,你一定遇到过运行时错误、空指针异常、文件找不到等问题。Java 提供了强大的异常处理机制,帮助我们优雅地捕获和处理这些错误。
本文将带你全面掌握:
并通过丰富的代码示例和真实业务场景讲解,帮助你写出更健壮、更可维护的 Java 代码。
Java 中的异常(Exception)本质上是程序在运行过程中出现的非正常情况,导致程序无法继续执行。Java 使用面向对象的方式对异常进行封装和管理。
Throwable
├── Error // 严重问题(JVM 错误),通常不被捕获
└── Exception // 可控异常
├── RuntimeException // 运行时异常(unchecked)
└── 其他所有异常 // 检查型异常(checked)
类名 | 特点 |
---|---|
Throwable |
所有异常的父类,包含堆栈信息、消息等 |
Error |
表示 JVM 无法处理的严重问题,如 OutOfMemoryError |
Exception |
所有可控异常的基类,必须被处理或声明抛出 |
RuntimeException |
运行时异常,编译器不强制要求处理 |
try {
// 尝试执行的代码
} catch (ExceptionType1 e1) {
// 处理异常
} catch (ExceptionType2 e2) {
// 处理其他异常
} finally {
// 无论是否发生异常都会执行(用于资源释放)
}
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("除数不能为0");
} finally {
System.out.println("finally 总会执行");
}
关键字 | 用途 | 示例 |
---|---|---|
throw |
主动抛出一个异常对象 | throw new IllegalArgumentException("参数错误") |
throws |
在方法签名中声明可能抛出的异常 | public void readFile() throws IOException |
public static void checkAge(int age) throws IllegalArgumentException {
if (age < 0) {
throw new IllegalArgumentException("年龄不能为负数");
}
}
异常类型 | 描述 | 示例 |
---|---|---|
NullPointerException |
空引用调用方法或属性 | String s = null; s.length() |
ArrayIndexOutOfBoundsException |
数组越界访问 | int[] arr = new int[3]; arr[5] = 10; |
ArithmeticException |
数学运算错误 | int a = 10 / 0; |
ClassCastException |
类型转换错误 | Object obj = "abc"; Integer i = (Integer)obj; |
NumberFormatException |
字符串转数字失败 | Integer.parseInt("abc") |
FileNotFoundException |
文件未找到 | new FileReader("不存在的文件.txt") |
IOException |
输入输出异常 | 读写文件、网络请求等 |
SQLException |
数据库操作异常 | JDBC 操作失败 |
Java 7 引入了自动资源管理机制,确保实现了 AutoCloseable
接口的对象在 try 块结束后自动关闭。
try (资源声明) {
// 使用资源
} catch (异常类型 e) {
// 异常处理
}
try (FileInputStream fis = new FileInputStream("data.txt")) {
int data;
while ((data = fis.read()) != -1) {
System.out.print((char) data);
}
} catch (IOException e) {
e.printStackTrace();
}
✅ 不再需要手动在
finally
中关闭资源,避免资源泄漏。
当 Java 内置的异常类型不足以表达你的业务逻辑时,可以自定义异常类。
public class InvalidUserInputException extends Exception {
public InvalidUserInputException(String message) {
super(message);
}
}
public void validateEmail(String email) throws InvalidUserInputException {
if (!email.contains("@")) {
throw new InvalidUserInputException("邮箱地址不合法");
}
}
实践 | 说明 |
---|---|
避免空 catch 块 | 不要只写 catch (Exception e) {} ,应记录日志或处理 |
异常应具体化 | 捕获具体的异常类型,而非直接捕获 Exception |
合理使用 finally | 用于关闭流、连接等资源 |
异常信息清晰 | 抛出异常时提供有意义的信息,便于排查 |
日志记录优先 | 使用日志框架(如 Log4j、SLF4J)记录异常堆栈 |
不滥用异常控制流程 | 异常不应作为正常的程序流程控制手段 |
分层异常处理 | 在 service 层统一捕获并包装异常,controller 返回友好提示 |
包装原始异常 | 使用 Throwable.initCause() 或构造函数链式传递异常 |
误区 | 正确做法 |
---|---|
catch(Exception e) {} | 至少打印日志或抛出 |
捕获 Throwable | 除非特殊需求,否则不要捕获 Error |
忽略关闭资源 | 使用 try-with-resources 或 finally 显式关闭 |
在 finally 中 return | 避免在 finally 中返回值,容易覆盖 try/catch 中的返回值 |
把异常吞掉不处理 | 应该记录日志或重新抛出 |
把异常作为流程控制 | 应使用条件判断代替异常跳转 |
在 catch 块中抛出新异常但丢失原异常 | 使用 initCause() 或带 cause 构造器 |
不加限制地抛出异常 | 控制异常传播层级,合理封装 |
try (BufferedReader reader = new BufferedReader(new FileReader("input.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
processLine(line);
}
} catch (FileNotFoundException e) {
System.err.println("文件未找到,请检查路径");
} catch (IOException e) {
System.err.println("读取文件时发生IO异常");
}
public List getAllUsers() throws DatabaseException {
try (Connection conn = dataSource.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users")) {
// 处理结果集
} catch (SQLException e) {
throw new DatabaseException("查询用户失败", e);
}
}
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(InvalidUserInputException.class)
public ResponseEntity handleInvalidInput(InvalidUserInputException ex) {
return ResponseEntity.badRequest().body(ex.getMessage());
}
@ExceptionHandler(Exception.class)
public ResponseEntity handleUnexpectedError(Exception ex) {
return ResponseEntity.status(500).body("服务器内部错误");
}
}
内容 | 说明 |
---|---|
异常体系 | Throwable → Error / Exception |
try-catch | 捕获并处理异常 |
finally | 总会执行,用于资源释放 |
throw | 主动抛出异常 |
throws | 方法声明可能抛出的异常 |
try-with-resources | 自动关闭资源(Java 7+) |
自定义异常 | 继承 Exception 或 RuntimeException |
最佳实践 | 记录日志、避免空 catch、分层处理、异常封装 |
注意事项 | 不要用异常控制流程、避免吞异常、不要捕获 Throwable |
功能 | 示例 |
---|---|
获取异常信息 | e.getMessage() |
打印堆栈信息 | e.printStackTrace() |
获取异常类型 | e.getClass().getName() |
获取异常原因 | e.getCause() |
设置异常原因 | e.initCause(otherEx) |
抛出自定义异常 | throw new MyCustomException("msg") |
日志记录异常 | logger.error("发生错误", e) |
包装异常并抛出 | throw new BusinessException("业务错误", e) |
多个异常捕获(Java 7+) | `catch (IOException |
判断是否为空指针 | if (obj == null) throw new NullPointerException() |
如果你正在准备一篇面向初学者的技术博客,或者希望系统回顾 Java 基础知识,这篇文章将为你提供完整的知识体系和实用的编程技巧。
欢迎点赞、收藏、转发,也欢迎留言交流你在实际项目中遇到的异常相关问题。我们下期再见
关注我,获取更多Java核心技术深度解析!