Java 常见异常全面梳理(分类+含义+典型场景)
Java 异常体系核心分为 运行时异常(RuntimeException,非受检) 和 编译时异常(Checked Exception 受检) ,前者编译期无需强制捕获,由程序逻辑错误导致;后者编译期必须显式捕获/抛出,由外部环境(如IO、网络、输入)异常导致。此外,还有错误(Error) 属于JVM层面严重问题,通常无需程序处理。
区分「运行时异常(预防为主)、受检异常(强制处理)、JVM错误(提前规避)」

一、运行时异常(RuntimeException)------ 逻辑错误主导,预防优先,捕获兜底
继承自java.lang.RuntimeException,编译期无需强制处理,多由代码逻辑不严谨导致,核心处理原则是通过严谨的代码逻辑从根源避免,仅对无法预防的场景做捕获兜底。
1. NullPointerException(NPE)------ 开发最高频异常
- 核心含义:调用空对象的方法/属性、对空对象执行集合/数组操作,或自动拆箱空包装类
- 典型场景 :
Object obj = null; obj.toString();、Integer num = null; int n = num;、方法返回null未判空直接使用 - 标准处理方式 (按优雅度排序,从基础到高级)
- 基础方案:显式判空,对所有可能为null的对象执行非空校验;
- 优雅方案:Java 8+
Optional类,包装可能为null的对象,避免手动判空; - 校验方案:使用
Objects.requireNonNull()做参数/对象非空校验,快速定位空指针源头; - 开发规范:方法返回值尽量返回空集合/空对象 (如
Collections.emptyList()),而非null,减少调用方判空成本。
- 核心代码示例
java
// 1. 显式判空
Object obj = getObj();
if (obj != null) {
obj.toString();
}
// 2. Optional优雅处理(推荐,无嵌套判空)
Optional.ofNullable(getObj()).ifPresent(o -> o.toString()); // 仅当非空时执行
String res = Optional.ofNullable(getStr()).orElse("默认值"); // 空则返回默认值
// 3. 非空校验(参数/方法内对象,快速定位问题)
public void doWork(User user) {
Objects.requireNonNull(user, "用户对象不能为空");
Objects.requireNonNull(user.getName(), "用户名不能为空");
}
- 避坑点 :切勿仅用
if (obj != null)做多层嵌套判空(会导致代码臃肿),优先使用Optional。
2. ArrayIndexOutOfBoundsException ------ 数组操作专属
- 核心含义:访问数组的索引超出有效范围(索引<0 或 索引>=数组长度)
- 典型场景 :
int[] arr = new int[3]; arr[3] = 10;、循环遍历数组时条件写为i <= arr.length - 标准处理方式
- 核心原则:遍历数组始终以
arr.length为边界,循环条件用i < arr.length; - 根源规避:使用增强for循环遍历数组,无需手动操作索引,从源头避免越界;
- 前置校验:手动操作索引时,先校验索引合法性(
index >= 0 && index < arr.length)。
- 核心原则:遍历数组始终以
- 核心代码示例
java
int[] arr = {1,2,3};
// 1. 增强for循环(推荐,无索引操作)
for (int num : arr) {
System.out.println(num);
}
// 2. 手动索引操作(前置校验)
int index = 3;
if (index >= 0 && index < arr.length) {
System.out.println(arr[index]);
} else {
System.err.println("索引越界,合法索引0-" + (arr.length-1));
}
3. IndexOutOfBoundsException(含子类StringIndexOutOfBoundsException)
- 核心含义:访问集合/字符串的索引超出有效范围,是数组越界的「集合/字符串版」
- 典型场景 :
List<String> list = new ArrayList<>(); list.get(0);、"java".substring(5) - 标准处理方式
- 集合操作:遍历用增强for循环,手动获取元素前校验
index < list.size()+集合非空(!list.isEmpty()); - 字符串操作:截取/获取字符前校验
index < str.length()+字符串非空(!str.isBlank()); - 工具辅助:使用
org.apache.commons.lang3.StringUtils/CollectionUtils,避免原生方法的越界问题。
- 集合操作:遍历用增强for循环,手动获取元素前校验
- 核心代码示例
java
List<String> list = Arrays.asList("a", "b");
String str = "java";
int index = 4;
// 集合安全获取
if (!CollectionUtils.isEmpty(list) && index < list.size()) {
System.out.println(list.get(index));
}
// 字符串安全截取(Apache工具类,越界会返回空/原字符串,无异常)
String sub = StringUtils.substring(str, 0, index);
4. ClassCastException ------ 类型转换高频异常
- 核心含义 :将对象强制转换为其实际运行时类型并非所属的类/接口(无继承/实现关系)
- 典型场景 :
Object obj = new Integer(10); String str = (String) obj;、泛型擦除后集合强转元素 - 标准处理方式
- 前置判断:转换前用
instanceof校验对象实际运行时类型,再执行强转; - 简化方案:Java 14+ 模式匹配,一步完成「类型判断+强制转换」,简化代码;
- 根源规避:集合操作强制指定泛型 (如
List<String> list = new ArrayList<>()),编译期拦截非目标类型元素。
- 前置判断:转换前用
- 核心代码示例
java
Object obj = new Integer(10);
// 1. 传统方式:instanceof判断+强转
if (obj instanceof String) {
String str = (String) obj;
}
// 2. Java14+ 模式匹配(推荐,少一层代码)
if (obj instanceof String str) {
System.out.println(str.length());
}
// 3. 泛型规避(从根源避免)
List<String> list = new ArrayList<>();
list.add("java");
// list.add(10); // 编译期直接报错,无法添加非String类型
5. InputMismatchException ------ Scanner输入专属
- 核心含义 :
java.util.Scanner读取数据时,输入数据类型与预期读取类型不兼容 - 典型场景 :用
scanner.nextInt()读整数,用户输入字符串("abc")、小数(3.14) - 标准处理方式 (核心:清空输入缓冲区,避免死循环 )
- 推荐方案:先判断再读取 ,使用Scanner的
hasNextXxx()系列方法匹配读取方法(hasNextInt()对应nextInt()); - 兜底方案:捕获异常后,必须调用
scanner.next()清空缓冲区的无效数据; - 万能方案:先读取原始字符串,再通过包装类手动转换,配合
try-catch处理。
- 推荐方案:先判断再读取 ,使用Scanner的
- 核心代码示例(推荐方案,无异常)
java
Scanner scanner = new Scanner(System.in);
int num = 0;
while (true) {
System.out.print("请输入整数:");
if (scanner.hasNextInt()) {
num = scanner.nextInt();
break; // 输入合法,退出循环
} else {
System.err.println("输入错误,仅支持整数!");
scanner.next(); // 关键:清空缓冲区的无效数据,避免死循环
}
}
6. ArithmeticException ------ 数值计算错误
- 核心含义 :算术运算中出现非法操作,整数除零/取模模数为0是最典型场景
- 典型场景 :
int a = 10 / 0;、10 % 0(注:浮点数除零结果为Infinity,不会触发此异常) - 标准处理方式
- 前置校验:对除法/取模的除数/模数做非零校验,从根源避免;
- 异常兜底:捕获异常后,根据业务返回默认值或抛出自定义提示,避免程序崩溃。
- 核心代码示例
java
int a = 10, b = 0;
int res = 0;
// 前置校验(推荐)
if (b != 0) {
res = a / b;
} else {
System.err.println("除数不能为零");
res = 0; // 业务默认值
}
// 异常兜底(无法提前校验的场景)
try {
res = a / b;
} catch (ArithmeticException e) {
System.err.println("计算失败:除数不能为零");
res = 0;
}
7. IllegalArgumentException ------ 参数非法通用异常
- 核心含义 :方法接收到不符合业务要求的非法参数,是自定义方法参数校验的标准异常
- 典型场景 :方法要求参数>0却传入-1、要求非空字符串却传入空串、
Collections.sort(null) - 标准处理方式
- 主动抛出:方法入口处做参数校验 ,参数不合法时主动抛出该异常,并携带明确的错误提示;
- 工具辅助:使用
Objects/Apache Commons工具类简化参数校验,避免重复代码; - 框架适配:Spring项目可使用
@Valid/@NotBlank等注解做参数自动校验,无需手动判断。
- 核心代码示例
java
// 手动参数校验+主动抛出异常(推荐,语义清晰)
public void setAge(int age) {
if (age < 0 || age > 150) {
throw new IllegalArgumentException("年龄必须在0-150之间,当前值:" + age);
}
this.age = age;
}
// Spring项目注解校验(简化代码)
public void addUser(@Valid User user) {
// User类中:@NotBlank(message = "用户名不能为空") private String name;
}
8. NumberFormatException ------ 数值格式转换错误
- 核心含义:将非数值格式的字符串转换为int/double/long等数值类型时触发
- 典型场景 :
Integer.parseInt("123a");、Double.parseDouble("3.14.5");、前端传参非数值字符串后端直接转换 - 标准处理方式
- 异常捕获:转换时通过
try-catch捕获异常,返回业务默认值或提示格式错误; - 工具辅助:使用
org.apache.commons.lang3.math.NumberUtils,转换失败返回默认值,无需手动捕获; - 前置校验:转换前通过正则表达式 校验字符串是否为合法数值(如
^\\d+$匹配整数)。
- 异常捕获:转换时通过
- 核心代码示例
java
String str = "123a";
// 1. try-catch兜底
int num1 = 0;
try {
num1 = Integer.parseInt(str);
} catch (NumberFormatException e) {
System.err.println("数值格式错误,默认值为0");
}
// 2. NumberUtils工具类(推荐,简化代码)
int num2 = NumberUtils.toInt(str, 0); // 转换失败返回默认值0
double num3 = NumberUtils.toDouble(str, 0.0);
9. ConcurrentModificationException ------ 集合并发修改错误
- 核心含义 :单/多线程中,遍历集合的同时执行结构性修改(增/删/修改集合容量)
- 典型场景 :增强for循环遍历List时调用
list.remove()、多线程同时操作非线程安全集合(ArrayList/HashMap) - 标准处理方式 (分单/多线程场景)
- 单线程:使用迭代器(Iterator) 遍历并修改,调用
it.remove(),这是集合遍历修改的标准方式; - 多线程:替换为线程安全集合 (
CopyOnWriteArrayList替代ArrayList、ConcurrentHashMap替代HashMap); - 兜底方案:遍历前将集合转为数组,或遍历过程中收集需要修改的元素,遍历完成后统一处理。
- 单线程:使用迭代器(Iterator) 遍历并修改,调用
- 核心代码示例
java
List<String> list = new ArrayList<>();
list.add("a"); list.add("b"); list.add("c");
// 1. 单线程安全修改(迭代器)
Iterator<String> it = list.iterator();
while (it.hasNext()) {
if ("a".equals(it.next())) {
it.remove(); // 无异常,标准方式
}
}
// 2. 多线程安全集合(直接替换,无需修改业务代码)
List<String> safeList = new CopyOnWriteArrayList<>();
Map<String, String> safeMap = new ConcurrentHashMap<>();
10. UnsupportedOperationException ------ 不支持的操作
- 核心含义 :调用了对象未实现/不支持的方法,多为只读/固定长度对象的修改操作
- 典型场景 :
Arrays.asList(arr).add(1);(该List为固定长度)、对Collections.unmodifiableList()执行增删 - 标准处理方式
- 根源规避:若需修改数组转换的List,重新创建可修改集合 (
new ArrayList<>(Arrays.asList(arr))); - 开发规范:使用集合前确认其类型,避免对只读集合执行修改操作;
- 前置判断:通过集合的
class判断是否为可修改类型(兜底方案)。
- 根源规避:若需修改数组转换的List,重新创建可修改集合 (
- 核心代码示例
java
String[] arr = {"a", "b"};
// 错误:固定长度List,无法修改
// List<String> list = Arrays.asList(arr);
// 正确:重新创建可修改ArrayList
List<String> list = new ArrayList<>(Arrays.asList(arr));
list.add("c"); // 正常执行
二、受检异常(Checked Exception)------ 外部环境异常,强制处理,资源释放
继承自java.lang.Exception(非RuntimeException子类),编译期必须通过try-catch捕获或throws声明抛出,多由程序外部环境异常导致(如文件不存在、网络断开),核心处理原则是强制捕获处理,核心操作是「释放资源+业务兜底」。
1. IOException(及子类)------ IO操作核心异常(最常用)
- 核心含义 :所有IO操作的通用异常父类,子类覆盖具体场景:
FileNotFoundException(文件不存在)、SocketException(网络断开)、EOFException(文件提前结束) - 典型场景:文件读写、流的打开/关闭、网络数据传输、控制台输入流操作
- 标准处理方式 (核心:保证IO资源100%释放 )
- 优雅方案:Java 7+ try-with-resources 语法(自动关闭实现
AutoCloseable接口的资源,替代finally,推荐); - 传统方案:
try-catch-finally,在finally块中手动关闭资源(需判空,避免空指针); - 业务兜底:捕获异常后记录日志,返回友好的业务提示(如"文件读取失败"),避免暴露底层异常。
- 优雅方案:Java 7+ try-with-resources 语法(自动关闭实现
- 核心代码示例(try-with-resources 自动释放资源,推荐)
java
// 流对象实现AutoCloseable,try代码块结束后自动关闭,无需手动finally
try (FileReader fr = new FileReader("test.txt");
BufferedReader br = new BufferedReader(fr)) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (FileNotFoundException e) {
System.err.println("业务提示:文件不存在,请检查路径");
log.error("文件读取失败,路径:test.txt", e); // 记录详细日志(含堆栈)
} catch (IOException e) {
System.err.println("业务提示:文件读取失败");
log.error("文件读写异常", e);
}
- 关键子类 :
FileNotFoundException(优先捕获,精准定位文件问题)、SocketException(网络IO,需增加重连逻辑)。
2. InterruptedException ------ 线程中断协作异常
- 核心含义 :线程处于可中断阻塞状态 时,被其他线程调用
interrupt()中断,是线程间的「协作中断信号」(非错误异常) - 典型场景 :线程执行
Thread.sleep()/Object.wait()/CountDownLatch.await()/Thread.join()时被中断 - 关键特性 :异常触发后,JVM会自动清除线程的中断状态 (
isInterrupted()返回false),切勿空catch吞掉异常(并发编程大忌) - 标准处理方式 (核心三原则:恢复状态/释放资源/优雅终止,分3种场景)
- 工具方法场景 :捕获异常后恢复中断状态 (
Thread.currentThread().interrupt())+ 向上抛出异常,让上层调用方处理; - 线程入口场景 :捕获异常后立即释放资源 (关闭流/释放锁)+ 直接终止线程(
return); - 批量任务场景:恢复中断状态 + 跳过当前任务,让后续代码检测到中断并处理。
- 工具方法场景 :捕获异常后恢复中断状态 (
- 核心代码示例(工具方法+线程入口,最常用)
java
// 场景1:工具方法(不负责线程生命周期,向上抛出)
public void doBlockingWork() throws InterruptedException {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢复中断状态,让上层感知
throw e; // 向上抛出
}
}
// 场景2:线程入口(最终处理,释放资源+终止)
Thread worker = new Thread(() -> {
FileWriter writer = null;
try {
writer = new FileWriter("log.txt");
doBlockingWork(); // 调用工具方法
} catch (InterruptedException e) {
System.out.println("收到中断请求,开始优雅终止");
// 步骤1:释放资源
if (writer != null) {
try { writer.close(); } catch (IOException ex) { log.error("资源释放失败", ex); }
}
// 步骤2:终止线程
return;
} catch (IOException e) {
log.error("IO异常", e);
}
});
worker.start();
worker.interrupt(); // 主线程触发中断
3. SQLException ------ 数据库JDBC操作专属
- 核心含义:执行数据库操作(增删改查、连接、关闭)时触发,覆盖连接失败、SQL语法错误、表/字段不存在等场景
- 典型场景:数据库地址/账号密码错误、SQL语句拼写错误、操作不存在的表、连接池耗尽
- 标准处理方式 (核心:资源释放+事务回滚+隐藏敏感信息 )
- 资源管理:使用try-with-resources自动关闭
Connection/Statement/ResultSet(均实现AutoCloseable),避免数据库连接泄漏; - 事务处理:异常时执行事务回滚 (
conn.rollback()),避免数据不一致; - 日志与提示:记录详细日志 (含SQL语句、参数),返回通用业务提示(如"数据库操作失败"),避免暴露数据库地址/账号等敏感信息;
- 重试机制:对连接超时、网络抖动等临时异常,增加重试逻辑(配合Guava Retryer/Spring Retry)。
- 资源管理:使用try-with-resources自动关闭
- 核心代码示例
java
String sql = "INSERT INTO user (name) VALUES (?)";
// try-with-resources 自动关闭数据库资源
try (Connection conn = DBUtil.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
conn.setAutoCommit(false); // 关闭自动提交,开启事务
pstmt.setString(1, "test");
pstmt.executeUpdate();
conn.commit(); // 提交事务
} catch (SQLException e) {
// 事务回滚
if (conn != null) {
try { conn.rollback(); } catch (SQLException ex) { log.error("事务回滚失败", ex); }
}
log.error("SQL执行失败,SQL:{}", sql, e); // 记录详细日志
System.err.println("业务提示:数据保存失败,请稍后重试"); // 通用提示
}
4. ClassNotFoundException ------ 类加载异常
- 核心含义 :JVM在类加载过程中,无法找到指定全限定名的类文件(包名+类名)
- 典型场景 :反射加载类时类名写错(
Class.forName("com.test.User"))、项目依赖缺失、classpath未包含该类 - 标准处理方式
- 基础排查:检查类的全限定名是否拼写正确(包名、类名大小写);
- 环境检查:确认项目运行时的classpath包含该类的class文件/依赖包;
- 异常处理:捕获异常后记录详细日志(类名、classpath信息),返回明确的排查提示。
- 核心代码示例
java
try {
Class<?> cls = Class.forName("com.test.User");
Object obj = cls.newInstance();
} catch (ClassNotFoundException e) {
log.error("类加载失败,类名:com.test.User,请检查类名/依赖", e);
System.err.println("系统异常:类加载失败,请联系开发人员");
} catch (InstantiationException | IllegalAccessException e) {
log.error("类实例化失败", e);
}
5. ParseException ------ 日期/字符串解析异常
- 核心含义 :对日期、自定义格式字符串进行解析/格式化时,输入格式与指定格式不匹配
- 典型场景 :
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); sdf.parse("2026/02/05") - 标准处理方式
- 根源规避:统一数据格式,前端传参做格式限制(如日期仅允许yyyy-MM-dd);
- 优雅方案:使用Java 8+ 新日期时间API(
LocalDate/DateTimeFormatter),线程安全且抛出更友好的DateTimeParseException(运行时异常); - 异常捕获:解析前校验输入格式,捕获异常后返回默认值或友好提示。
- 核心代码示例(推荐新日期API,替代传统SimpleDateFormat)
java
String dateStr = "2026/02/05";
// 传统方式(SimpleDateFormat,非线程安全)
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
Date date = sdf.parse(dateStr);
} catch (ParseException e) {
System.err.println("日期格式错误,请按yyyy-MM-dd输入");
}
// Java8+ 新日期API(推荐,线程安全,异常更友好)
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
try {
LocalDate date = LocalDate.parse(dateStr, formatter);
} catch (DateTimeParseException e) { // 运行时异常,无需强制捕获
System.err.println("日期格式错误,请按yyyy-MM-dd输入");
}
三、JVM错误(Error)------ 系统层面严重问题,提前规避,无需处理
继承自java.lang.Error,属于JVM/系统层面的严重异常 ,程序无法捕获和恢复,一旦发生通常导致程序崩溃,核心处理原则是提前规避,通过代码优化/JVM调优/环境配置防止发生,无需在代码中手动捕获。
1. OutOfMemoryError(OOM)------ 内存溢出
- 核心含义:JVM堆内存、方法区、元空间等内存区域耗尽,无法为新对象分配内存
- 典型场景 :无限循环创建大对象、集合存储大量数据未清理(内存泄漏)、JVM堆内存设置过小(
-Xmx配置不合理)、静态集合持有大量对象引用 - 标准规避方式
- 代码优化:及时释放无用对象引用(如集合使用后清空、避免静态集合存储业务数据),排查内存泄漏(使用MAT/JProfiler工具);
- JVM调优:合理设置堆内存参数(
-Xms初始堆 =-Xmx最大堆,避免内存频繁扩容,如-Xms2G -Xmx2G),调整元空间大小(-XX:MetaspaceSize=256M); - 业务优化:采用分页查询/流式处理替代全量加载数据,避免一次性将大量数据加载到内存。
2. StackOverflowError ------ 栈溢出
- 核心含义 :JVM方法调用栈的深度超过最大值,无限递归是最典型、最常见的场景
- 典型场景 :无限递归调用方法(
public void test() { test(); })、方法调用链过长(多层级业务方法嵌套) - 标准规避方式
- 代码优化:杜绝无限递归 ,检查递归方法的终止条件 ,确保能正常退出;若递归层级过深,改为循环实现(根本解决方案);
- 调优辅助:调整JVM栈大小参数(
-Xss,如-Xss1M),增大方法调用栈深度(谨慎使用,治标不治本); - 代码重构:将过长的方法调用链拆分为多个独立方法,减少嵌套层级。
- 避坑点 :切勿依赖调整
-Xss解决问题,无限递归即使增大栈大小,最终仍会触发栈溢出。
3. NoClassDefFoundError ------ 类定义未找到
- 核心含义 :编译期能找到类,但运行期JVM无法找到该类的字节码文件 (与
ClassNotFoundException的核心区别:前者是运行期类加载失败,后者是编译/反射时类不存在) - 典型场景:编译后删除了类的class文件、依赖的第三方包编译期存在但运行期缺失、Maven/Gradle依赖包版本冲突
- 标准规避方式
- 环境检查:确认项目运行时的classpath包含所有依赖包,编译后的class文件完整无缺失;
- 依赖管理:使用Maven/Gradle统一管理依赖,执行
clean install清理并重新编译项目,避免依赖包缺失/冲突; - 类加载器排查:避免自定义类加载器的加载范围冲突,确保类被JVM正确加载。
四、Java 常见异常速查表(含核心处理方式,快速查阅)
| 分类 | 异常名称 | 核心关键词 | 核心处理/规避方式 |
|---|---|---|---|
| 运行时异常 | NullPointerException | 空对象、自动拆箱 | Optional包装/Objects非空校验/返回空对象而非null |
| ArrayIndexOutOfBoundsException | 数组索引越界 | 增强for循环/索引前置校验/以length为边界 | |
| IndexOutOfBoundsException | 集合/字符串索引越界 | 非空校验/以size/length为边界/Apache工具类辅助 | |
| ClassCastException | 强制类型转换错误 | instanceof判断/Java14+模式匹配/集合指定泛型 | |
| InputMismatchException | Scanner输入类型不匹配 | hasNextXxx()前置判断/清空输入缓冲区/先读字符串再转换 | |
| ArithmeticException | 整数除零、非法算术运算 | 除数非零校验/try-catch兜底返回默认值 | |
| IllegalArgumentException | 方法参数非法 | 方法入口校验/主动抛出异常/Spring注解校验 | |
| NumberFormatException | 字符串转数值格式错误 | NumberUtils工具类/正则校验/try-catch兜底 | |
| ConcurrentModificationException | 集合并发修改 | 迭代器修改/线程安全集合/遍历后统一修改 | |
| UnsupportedOperationException | 不支持的操作 | 重新创建可修改集合/避免修改只读集合 | |
| 受检异常 | IOException(及子类) | 文件/流/网络IO操作 | try-with-resources自动关资源/finally释放/日志+业务提示 |
| InterruptedException | 线程阻塞时被中断 | 恢复中断状态/释放资源/优雅终止/切勿吞异常 | |
| SQLException | 数据库JDBC操作 | 自动关连接/事务回滚/详细日志/通用业务提示 | |
| ClassNotFoundException | 反射/类加载找不到类 | 检查类名拼写/确认依赖完整/排查classpath | |
| ParseException | 日期/字符串解析格式不匹配 | 统一格式/Java8+新日期API/前置格式校验 | |
| JVM错误 | OutOfMemoryError | 内存溢出、OOM | 排查内存泄漏/JVM堆调优/分页/流式处理/释放无用引用 |
| StackOverflowError | 栈溢出、无限递归 | 杜绝无限递归/递归改循环/调整-Xss/拆分长调用链 | |
| NoClassDefFoundError | 运行期找不到类定义 | 检查classpath/重新编译项目/Maven/Gradle统一依赖管理 |
五、Java 异常处理通用最佳实践(所有场景均适用)
- 切勿空catch吞掉异常 :尤其是
InterruptedException、IO异常、SQL异常,空catch会导致问题无法定位、资源泄漏、程序逻辑混乱; - 异常处理粒度适中 :不要用一个try-catch捕获所有异常(如
catch (Exception e)),按异常类型细分处理,便于精准定位问题; - 资源释放是核心:涉及IO、数据库、网络连接等资源的操作,必须保证资源释放(优先使用try-with-resources);
- 日志记录要完整 :捕获异常时记录异常堆栈+业务上下文 (如参数、SQL语句),避免仅打印
e.printStackTrace(); - 前端/用户提示要友好:隐藏底层异常细节(如数据库地址、SQL语句),返回通用的业务提示(如"操作失败,请稍后重试");
- 运行时异常以预防为主:通过判空、参数校验、索引检查等逻辑从根源避免,而非依赖try-catch;
- 自定义异常语义清晰 :复杂业务中自定义异常(继承Exception/RuntimeException),让异常更贴合业务场景(如
UserNotExistException),便于统一处理。 - 运行时异常 :重点在「预防」,通过严谨的代码逻辑(判空、参数校验、索引检查、泛型使用)避免,是日常bug排查的主要对象;
- 受检异常 :重点在「处理」,编译期强制捕获/抛出,需根据业务场景做兜底(如IO异常关闭流、SQL异常回滚事务);
- Error :重点在「规避」,通过合理的JVM参数配置(如调整堆内存)、优化代码(避免无限递归)防止,程序无法处理;
- 异常处理原则 :切勿空catch吞掉异常(尤其是
InterruptedException),异常处理的核心是「恢复状态、释放资源、优雅提示/终止」。
掌握以上异常的处理方式和通用原则,能大幅提升Java代码的健壮性和可维护性,快速定位并解决开发中的各类异常问题,同时避免因异常处理不当导致的资源泄漏、程序崩溃、数据不一致等严重问题。