Java异常处理全解析:从基础到自定义

目录

  • 🚀前言
  • 🤔异常的定义与分类
    • 💯运行时异常
    • 💯编译时异常
    • 💯异常的基本处理
  • 🌟异常的作用
  • 🐧自定义异常
    • 💯自定义运行时异常
    • 💯自定义编译时异常
  • ✍️异常的处理方案
    • 💯方案一:异常捕获与用户友好提示
    • 💯方案二:异常捕获与自动修复

🚀前言

大家好!我是 EnigmaCoder

本文介绍java的异常部分,从基础到自定义,最后到处理方案进行全解析。

🤔异常的定义与分类

定义:Java 中的异常是程序运行时发生的错误或意外情况,是Throwable类的子类,用于封装程序执行过程中出现的不正常事件,可通过异常处理机制进行捕获和处理,分为可处理的Exception(包括受检查异常和运行时异常)和不可处理的Error(系统级错误)。

异常的分类

Java 异常体系基于Throwable类,主要分为两大类:

  • Error:系统级错误(如内存溢出OutOfMemoryError),程序无法处理,也就是说系统一旦出现问题,sun公司会把这些问题封装成Error对象给出来。
  • Exception:指可被程序捕获和处理的异常,我们程序员通常会用Exception以及它的孩子来封装程序出现的问题,其进一步分为:
    • 运行时异常(RuntimeException):RuntimeException及其子类,编译阶段不会出现错误提示,运行时出现的异常。(如:数组索引越界异常)
    • 编译时异常(Checked Exception):编译阶段会出现错误提示。(如:日期解析异常)

💯运行时异常

特点:编译阶段不会报错,运行时出现的异常,继承自RuntimeException类。

示例:

java 复制代码
public class Text {
    public static void main(String[] args) {
        int []arr={1,2,3,4,5};
        System.out.println(arr[5]);
    }
}

这是一段数组越界的的代码,编译时没有报错,运行时就会出现以下报错:

💯编译时异常

java 复制代码
public class Text {
    public static void main(String[] args) {
        show();
    }
    public static void show(){
        System.out.println("==程序开始==");
        String str="2025-01-01 00:00:00";

        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date=sdf.parse(str);
        System.out.println(date);
        System.out.println("==程序结束==");
    }
}

报错信息如下:

💯异常的基本处理

  • 抛出异常(throws

    在方法上使用throws关键字,可以将方法内部出现的异常抛出去给调用者处理。

格式如下:

java 复制代码
方法  throws 异常1,异常2,异常3 ...{
    ...
}
  • 捕获异常(try...catch

    直接捕获程序出现的异常。

格式如下:

java 复制代码
try{
   //监视可能出现异常的代码
}catch (异常类型1 变量){
   //处理异常
}catch (异常类型2 变量){
   //处理异常
}...

🌟异常的作用

  • 作用1:异常是用来定位程序bug的关键信息

异常是程序运行过程中发生错误或异常情况时抛出的信号。它包含了丰富的调试信息,可以帮助开发者快速定位和修复问题。具体来说:

  1. 异常类型:指明错误的性质,如NullPointerExceptionArrayIndexOutOfBoundsException等。
  2. 异常信息:描述错误的具体原因,如"Index 5 out of bounds for length 3"。
  3. 堆栈跟踪:显示异常发生时的调用链,帮助定位问题发生的具体位置。
java 复制代码
try {
    int[] arr = new int[3];
    System.out.println(arr[5]);
} catch (ArrayIndexOutOfBoundsException e) {
    e.printStackTrace();
    // 输出:
    // java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 3
    //     at Main.main(Main.java:5)
}
 

通过分析异常信息,开发者可以快速定位到数组越界的问题发生在Main.java文件的第5行。

  • 作用2:可以作为方法内部的一种特殊返回值以便通知上层调用者,方法的执行问题

在方法设计中,异常机制提供了一种优雅的错误处理方式,相比传统的错误码返回具有以下优势:

  1. 强制处理:调用者必须处理或继续抛出检查型异常(Checked Exception)。
  2. 信息丰富:可以携带详细的错误描述和上下文信息。
  3. 代码解耦:将正常业务逻辑与错误处理逻辑分离,提高代码可读性。

应用场景示例:

java 复制代码
public void transfer(Account from, Account to, double amount) throws InsufficientBalanceException {
    if (from.getBalance() < amount) {
        throw new InsufficientBalanceException("账户余额不足,当前余额:" + from.getBalance());
    }
    // 执行转账逻辑
}

// 调用方法
try {
    transfer(accountA, accountB, 1000);
} catch (InsufficientBalanceException e) {
    System.out.println("转账失败:" + e.getMessage());
    // 输出:转账失败:账户余额不足,当前余额:500.0
}
 

通过抛出异常,transfer方法清晰地表达了业务约束,调用方可以针对性地处理特定错误情况,而不是依赖模糊的错误码或布尔返回值。

🐧自定义异常

java无法为这个世界上全部的问题都提供异常类来代表,如果企业自己的某种问题,想通过异常来表示,以便用异常来管理该问题,那就需要自己来定义异常类了。

💯自定义运行时异常

  • 定义一个异常类继承RuntimeException
java 复制代码
public class CustomRuntimeException extends RuntimeException {
    // 类体
}
 
  • 重写构造器
java 复制代码
public class CustomRuntimeException extends RuntimeException {

    // 无参构造器
    public CustomRuntimeException() {
        super();
    }

    // 带消息的构造器
    public CustomRuntimeException(String message) {
        super(message);
    }

    // 带消息和原因的构造器
    public CustomRuntimeException(String message, Throwable cause) {
        super(message, cause);
    }

    // 带原因的构造器
    public CustomRuntimeException(Throwable cause) {
        super(cause);
    }
}
 
  • 通过throw new 异常类(xxx)来创建异常对象并抛出。
java 复制代码
public void someMethod(int value) {
    if (value < 0) {
        throw new CustomRuntimeException("Value cannot be negative");
    }
    // 其他逻辑
}
 

特点:编译阶段不报错,运行时才可能出现。

💯自定义编译时异常

  • 定义一个异常类继承Exception
java 复制代码
public class CustomCompileException extends Exception {
    // 异常类的具体实现
}
 
  • 重写构造器。
java 复制代码
public class CustomCompileException extends Exception {
    // 无参构造器
    public CustomCompileException() {
        super();
    }

    // 带有错误信息的构造器
    public CustomCompileException(String message) {
        super(message);
    }

    // 带有错误信息和原因的构造器
    public CustomCompileException(String message, Throwable cause) {
        super(message, cause);
    }

    // 仅带有原因的构造器
    public CustomCompileException(Throwable cause) {
        super(cause);
    }
}
 
  • 通过throw new 异常类(xxx)创建异常对象并抛出。
java 复制代码
public void validateInput(String input) throws CustomCompileException {
    if (input == null || input.isEmpty()) {
        throw new CustomCompileException("输入不能为空");
    }
    // 其他逻辑
}
 

特点:编译阶段就报错,提醒十分激进。

✍️异常的处理方案

💯方案一:异常捕获与用户友好提示

底层异常层层往上抛出,最外层捕获异常,记录下异常信息,并响应适合用户观看的信息进行提示。

处理流程

  • 异常抛出:在底层代码中,当检测到异常情况时,使用 throw 关键字抛出异常对象。例如,在数据库操作中,如果查询失败,可以抛出 SQLException

  • 异常传递:异常会沿着调用栈向上传递,直到被捕获。中间层代码可以选择不处理异常,继续向上抛出。例如,在服务层捕获SQLException 后,可以将其包装为自定义的业务异常 BusinessException 并继续抛出。

  • 最外层捕获:在应用的最外层(如控制器层或主函数)使用 try-catch块捕获所有异常。例如,在 Spring MVC的控制器中,可以使用 @ExceptionHandler 注解来统一处理异常。

  • 记录日志:捕获异常后,使用日志框架(如 Log4jSLF4J)记录异常的详细信息,包括异常类型、堆栈轨迹、发生时间等。这有助于后续的问题排查和分析。

  • 用户提示:根据异常类型,生成适合用户观看的提示信息。例如,对于网络超时异常,可以提示"网络连接不稳定,请稍后重试";对于权限不足异常,可以提示"您没有权限执行此操作"。

示例代码

java 复制代码
try {
    // 业务逻辑代码
} catch (BusinessException e) {
    logger.error("业务异常: ", e);
    return new Response("操作失败,请检查输入是否正确");
} catch (Exception e) {
    logger.error("系统异常: ", e);
    return new Response("系统繁忙,请稍后重试");
}

应用场景

  • Web 应用中的全局异常处理
  • 微服务架构中的服务间调用异常处理
  • 桌面应用程序中的用户操作异常处理

💯方案二:异常捕获与自动修复

最外层捕获异常后,尝试重新修复。

处理流程

  • 异常捕获:在最外层代码中捕获异常,与方案一类似。

  • 异常分析:根据异常类型和上下文信息,判断是否可以进行自动修复。例如,对于网络连接异常,可以尝试重新连接;对于文件读取异常,可以尝试从备份文件读取。

  • 修复尝试:

    • 重试机制:对于暂时性异常(如网络抖动),可以设置重试次数和间隔时间,多次尝试执行操作。
    • 备用方案:对于无法直接修复的异常,可以切换到备用方案。例如,主数据库不可用时,切换到备用数据库。
    • 资源清理:在修复过程中,可能需要释放已占用的资源,如关闭文件句柄、释放数据库连接等。
  • 结果处理:

    • 如果修复成功,继续正常执行后续逻辑。
    • 如果修复失败,记录日志并按照方案一的方式向用户提示错误信息。

示例代码

java 复制代码
int retryCount = 3;
while (retryCount > 0) {
    try {
        // 可能失败的操作
        performOperation();
        break; // 成功则退出循环
    } catch (NetworkException e) {
        retryCount--;
        if (retryCount == 0) {
            logger.error("网络连接失败,重试次数用尽");
            return new Response("网络连接失败,请检查网络设置");
        }
        Thread.sleep(1000); // 等待1秒后重试
    }
}

应用场景

  • 分布式系统中的容错处理
  • 实时数据处理系统的故障恢复
  • 自动化运维系统中的错误自愈

注意事项

  • 自动修复可能会掩盖潜在的系统问题,应谨慎使用
  • 需要设置合理的重试次数和间隔时间,避免无限重试
  • 对于关键业务操作,建议在自动修复后仍进行人工确认

通过以上两种方案,可以有效地处理系统中的异常情况,提高系统的稳定性和用户体验。具体选择哪种方案,需要根据业务场景和异常类型来决定。

相关推荐
jian1105814 分钟前
JAVA Spring MVC+Mybatis Spring MVC的工作流程*,多表连查
java·spring·mvc
向哆哆20 分钟前
Java 项目管理工具:Maven 与 Gradle 的深度对比与选择
java·开发语言·maven
怪侠沈剑心24 分钟前
海康NVR录像回放SDK原始流转FLV视频流:基于Java的流媒体转码(无需安装第三方插件ffmpeg)
java·开发语言·前端
钟烁卓28 分钟前
C++日志
开发语言·c++
ss27333 分钟前
基于Springboot + vue3实现的流动摊位管理系统
java·spring boot·后端
Pafey38 分钟前
在 Qt 中实现动态切换主题(明亮和暗黑)
开发语言·qt
啊吧怪不啊吧40 分钟前
C++之初识模版
开发语言·数据结构·c++
nvvas1 小时前
Java Collection(集合) 接口
java·maven
*星星之火*1 小时前
【GPT入门】第39课 OPENAI官方API调用方法
java·服务器·gpt