java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java常见异常

Java常见异常全面梳理(含分类+含义+典型场景)

作者:zhougl996

异常是指程序在执行过程中,出现的非正常情况,可能由程序员错误、系统错误或用户输入错误引起,下面这篇文章主要介绍了Java常见异常全面梳理的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

Java 异常体系核心分为 运行时异常(RuntimeException,非受检)编译时异常(Checked Exception 受检),前者编译期无需强制捕获,由程序逻辑错误导致;后者编译期必须显式捕获/抛出,由外部环境(如IO、网络、输入)异常导致。此外,还有错误(Error) 属于JVM层面严重问题,通常无需程序处理。

区分「运行时异常(预防为主)、受检异常(强制处理)、JVM错误(提前规避)」

一、运行时异常(RuntimeException)—— 逻辑错误主导,预防优先,捕获兜底

继承自java.lang.RuntimeException,编译期无需强制处理,多由代码逻辑不严谨导致,核心处理原则是通过严谨的代码逻辑从根源避免,仅对无法预防的场景做捕获兜底

1. NullPointerException(NPE)—— 开发最高频异常

// 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(), "用户名不能为空");
}

2. ArrayIndexOutOfBoundsException —— 数组操作专属

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 = 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);
// 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输入专属

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 —— 数值计算错误

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 —— 参数非法通用异常

// 手动参数校验+主动抛出异常(推荐,语义清晰)
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 —— 数值格式转换错误

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 —— 集合并发修改错误

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 —— 不支持的操作

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操作核心异常(最常用)

// 流对象实现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);
}

2. InterruptedException —— 线程中断协作异常

// 场景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操作专属

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 —— 类加载异常

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 —— 日期/字符串解析异常

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)—— 内存溢出

2. StackOverflowError —— 栈溢出

3. NoClassDefFoundError —— 类定义未找到

四、Java 常见异常速查表(含核心处理方式,快速查阅)

分类异常名称核心关键词核心处理/规避方式
运行时异常NullPointerException空对象、自动拆箱Optional包装/Objects非空校验/返回空对象而非null
ArrayIndexOutOfBoundsException数组索引越界增强for循环/索引前置校验/以length为边界
IndexOutOfBoundsException集合/字符串索引越界非空校验/以size/length为边界/Apache工具类辅助
ClassCastException强制类型转换错误instanceof判断/Java14+模式匹配/集合指定泛型
InputMismatchExceptionScanner输入类型不匹配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 异常处理通用最佳实践(所有场景均适用)

  1. 切勿空catch吞掉异常:尤其是InterruptedException、IO异常、SQL异常,空catch会导致问题无法定位、资源泄漏、程序逻辑混乱;
  2. 异常处理粒度适中:不要用一个try-catch捕获所有异常(如catch (Exception e)),按异常类型细分处理,便于精准定位问题;
  3. 资源释放是核心:涉及IO、数据库、网络连接等资源的操作,必须保证资源释放(优先使用try-with-resources);
  4. 日志记录要完整:捕获异常时记录异常堆栈+业务上下文(如参数、SQL语句),避免仅打印e.printStackTrace()
  5. 前端/用户提示要友好:隐藏底层异常细节(如数据库地址、SQL语句),返回通用的业务提示(如“操作失败,请稍后重试”);
  6. 运行时异常以预防为主:通过判空、参数校验、索引检查等逻辑从根源避免,而非依赖try-catch;
  7. 自定义异常语义清晰:复杂业务中自定义异常(继承Exception/RuntimeException),让异常更贴合业务场景(如UserNotExistException),便于统一处理。
  8. 运行时异常:重点在「预防」,通过严谨的代码逻辑(判空、参数校验、索引检查、泛型使用)避免,是日常bug排查的主要对象;
  9. 受检异常:重点在「处理」,编译期强制捕获/抛出,需根据业务场景做兜底(如IO异常关闭流、SQL异常回滚事务);
  10. Error:重点在「规避」,通过合理的JVM参数配置(如调整堆内存)、优化代码(避免无限递归)防止,程序无法处理;
  11. 异常处理原则:切勿空catch吞掉异常(尤其是InterruptedException),异常处理的核心是「恢复状态、释放资源、优雅提示/终止」。

掌握以上异常的处理方式和通用原则,能大幅提升Java代码的健壮性和可维护性,快速定位并解决开发中的各类异常问题,同时避免因异常处理不当导致的资源泄漏、程序崩溃、数据不一致等严重问题。

总结

到此这篇关于Java常见异常全面梳理的文章就介绍到这了,更多相关Java常见异常内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文