Java里你为什么不用这种优雅的方式封装处理和抛出异常?

Java异常处理与封装的实践教程 自定义异常类是Java异常处理机制中一个重要的组成部分,它让开发者能够根据实际业务需求创建更加具体和专业的异常类型。这不仅可以提高代码的可读性和可维护性,还可以为异常处理提供更精确的控制。在介绍自定义异常类的必要性之前,我们先来理解一下异常与错误的区别。自定义异常类的创建主要适用于以下几种情况:当需要表示特定的业务错误时。当标准异常不能提供足够的信息时。当需要区分不同类型的错误时。当需要通过异常来控制程序的流程时。 阅读详情

今天我们来分享点关于异常处理和封装的不同方式。

一、定义自定义枚举类

@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
public class ServiceException extends RuntimeException {
    // 错误代码
    private int code;
    
    // 错误信息
    private String message;

    // 构造函数 传入错误代码和错误信息
    public ServiceException(int code, String message) {
        setCode(code).setMessage(message);
    }
}

此时,我们可以使用 throw 关键词来进行传统异常的抛出:

throw new ServiceException(401, "你的身份凭证已过期");

太难受了,如果很多地方抛出这个异常的话,状态码成了魔法值,还要写一堆文案。

又不太想定义八百个异常类,那更麻烦了

继续改造:

二、异常枚举封装

我们把上面的异常代码和消息封装到枚举里面,再通过 ServiceException 的新构造来抛出:

@Getter
@AllArgsConstructor
public enum ServiceError {
    PARAM_MISSING(4001, "缺少必要的请求参数"),
    UNAUTHORIZED(401, "获取你的身份信息失败,请重新登录后再试"),
    FORBIDDEN(403, "无权操作"),
    FORBIDDEN_EXIST(4031, "添加失败,数据已存在"),
    DATA_NOT_FOUND(404, "没有查到相关的数据"),
    // 省略一堆其他状态码...

    private final int code;
    private final String message;
}

然后为自定义业务异常类 ServiceException 添加两个可以传入 ServiceError 枚举项的构造方法:

public class ServiceException extends RuntimeException {
    // 之前的代码省略...
    public ServiceException(@NotNull ServiceError serviceError) {
        setCode(serviceError.getCode()).setMessage(serviceError.getMessage());
    }

    public ServiceException(@NotNull ServiceError serviceError, String message) {
        setCode(serviceError.getCode()).setMessage(message);
    }
    // 之后的代码省略...
}

那么接下来,我们抛出异常就不会出现魔法值了,而且同一个错误代码,也可以在不同场景下写不同的错误信息文案了:

throw new ServiceException(ServiceError.UNAUTHORIZED);
throw new ServiceException(ServiceError.UNAUTHORIZED, "小子,你掉线了");

三、控制器接管异常抛出

接下来,我们就可以使用 @ControllerAdvice 和 @ExceptionHandler 注解来处理异常了:

@ControllerAdvice
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public class ExceptionInterceptor {
    /**
     * <h2>处理自定义异常</h2>
     */
    @ExceptionHandler(ServiceException.class)
    public Json systemExceptionHandle(@NotNull ServiceException exception) {
        return Json.error(exception).setData(exception.getData());
    }

    // 处理其他异常
}

这样,我们的异常抛出给前端,就会是我们定义的标准 JSON 格式了。

不过,抛出异常的地方还是 不够优雅。

比如:

if(Objects.isNull(user)){
    throw new ServiceException(ServiceError.USER_NOT_FOUND);
}

我们能不能再精简一下上面的代码呢?

能!

四、封装断言类型异常抛出

首先我们分析下,ServiceError枚举类 和 ServiceException类 都存在 code message 属性,我们先抽离一个 IException 接口,把 code message 属性抽象出来:

public interface IException {
    // 获取错误代码
    int getCode();

    // 错误信息
    String getMessage();
}

然后,我们将 ServiceError 枚举类和 ServiceException 类都实现这个接口:

public enum ServiceError implements IException {
    // 省略一堆状态码
}

public class ServiceException extends RuntimeException implements IException {
    // 省略一堆属性和方法
}

这样,我们就保持了异常和枚举都可以正常使用 code message 属性,并且 ServiceError 枚举类和 ServiceException 类也保持了统一。

接着,我们直接在这个异常类里做默认实现,来创建并抛出异常:

public interface IException {
    // 获取错误代码
    int getCode();

    // 错误信息
    String getMessage();

    // 添加一个私有化创建异常的方法
    private @NotNull ServiceException create() {
        return new ServiceException(getCode(), getMessage());
    }

    // 抛出异常
    default void stop() {
        stop(getMessage());
    }

    // 抛出异常 带自定义消息
    default void stop(String message) {
        throw create().setMessage(message);
    }
    
    // 满足指定条件时抛出异常
    default void stopWhen(boolean condition) {
        stopWhen(condition, getMessage());
    }
    
    // 满足指定条件时抛出异常 带自定义消息
    default void stopWhen(boolean condition, String message) {
        if (condition) {
            stop(message);
        }
    }
}

这样就美滋滋了,我们再来看看 三、控制器接管异常抛出 中的判断空抛出代码,我们就可以这么改造了:

  • 改造前,三行代码
if(Objects.isNull(user)){
    throw new ServiceException(ServiceError.USER_NOT_FOUND);
}
  • 改造后,一行代码
ServiceError.USER_NOT_FOUND.stopWhen(Objects.isNull(user));

五、是不是美滋滋?

我们还能再过分一点,添加很多的默认实现,比如:

public interface IException {
    // 为空时抛出异常
    default void stopWhenNull(Object object) {
        stopWhenNull(object, getMessage());
    }

    default void stopWhenNull(Object object, String message) {
        if(Objects.isNull(object)){
            stop(message);
        }
    }

    // 相同时抛出异常
    default void stopWhenEquals(Object object1, Object object2) {
        if(Objects.equals(object1, object2)){
            stop();
        }
    }

    // 你可以继续扩展了~
}

每个调用的人就会很舒坦:

ServiceError.USER_NOT_FOUND.stopWhenNull(user, "用户信息不能为空");
ServiceError.USER_TARGET_ERROR.stopWhenEquals(fromUserId, toUserId, "发送人和收件人不能是同一个人");

如果是判了 null 异常后,后续不再需要 IDEA 飘黄提示可能为空

2017年电赛题目-自适应滤波器 电赛题目解析之自适应滤波器 首先来看看题目: 2017全国大学生电子设计竞赛题目 这道题有很多发挥空间,本文将介绍三种电路: 数字电位器移相模拟电路 压控移相模拟电路 数字方法 一、数字电位器移相模拟电路 对于前级的加法器比较简单,这里贴出一种使用OPA209实现的常规做法: 输入端分别输入A信号和B信号就可以实现A+B,因为信号频率很低不涉及到阻抗匹配问题,所以这种接法没............ 阅读详情

相关推荐

从零到一:手把手教你用YonBuilder for NCC搭建NC Cloud 2021.11开发环境(含避坑指南)

本文详细指导如何使用YonBuilder for NCC搭建NC Cloud 2021.11开发环境,涵盖硬件要求、软件安装、YonBuilder配置及常见故障排除。特别提供避坑指南,帮助开发者快速上手NCC开发,确保环境配置的原子化分解和开发思维的体系化建立。

weixin_42523326的博客 347

Java类和对象(下篇)

封装概念,访问限定符,包,static成员,代码块,内部类,对象打印

wx_3432161877的博客 1218

【MCU】一种基于RTOS的无应答重发机制

文章目录一、背景描述二、结构框图三、基于RTOS的信号量的实现(3-1)思路描述(3-2)步骤(3-2-1)定义超时重发信号量(3-2-2)定义发送线程入口(3-2-3)定义接收线程入口四、基于定时器的实现(4-1)思路描述(4-2)步骤(4-2-1)创建定时器(4-2-2)定义定时器超时处理函数(4-2-3)定义发送线程入口(4-2-4)定义接收线程入口 一、背景描述 ​ 在微控制器之间进行数据通信时,大多需要添加无应答重发机制,以增强数据交互和通信的稳定性。 在使用UART通信的双方设备,当一次数据传输

iriczhao的博客 1523

Java封装最佳实践:打造高内聚、低耦合的优雅代码~

基本规则在文件的最上方加上一个 package 语句指定该代码在哪个包中.包名需要尽量指定成唯一的名字, 通常会用公司的域名的颠倒形式(例如 com.bit.demo1 ).包名要和代码路径相匹配. 例如创建 com.bit.demo1 的包, 那么会存在一个对应的路径 com/bit/demo1 来存储 代码.如果一个类没有 package 语句, 则该类被放到一个默认包中.操作步骤1. 在 IDEA 中先新建一个包: 右键 src -> 新建 -> 包。

2301_80221228的博客 3495

基于Codex平台与AI Agent技能开发,实现抖音爆款视频分析与脚本自动生成

大家好,我是专注于AI应用开发的技术博主。最近在探索如何利用AI工具自动化内容创作时,我发现了一个非常有趣的组合:通过Codex平台创建自定义的Skill(技能),来批量分析抖音平台的爆款视频,并基于分析结果自动生成新的带货视频脚本。这听起来像是内容团队的“黑科技”,但实现起来其实有清晰的路径可循。本文将为你完整拆解这个实战项目,从环境搭建、Skill编写、数据抓取到视频脚本生成,手把手教你打造自己的“AI内容助理”。无论你是想了解AI Agent的开发,还是对短视频自动化创作感兴趣,都能从本文中获得可直接

weixin_34110749的博客 645

深入解析ACS SPiiPlus运动控制器的托管API架构与实战应用

本文深入解析了ACS SPiiPlus运动控制器的托管API架构,详细介绍了其核心模块、设计哲学与实战应用。通过C#/.NET封装的托管接口,开发者能够高效实现多轴协同运动控制、EtherCAT总线集成以及事件驱动的异步编程,显著提升高精度工业自动化设备的开发效率与系统稳定性。

jwt8token的博客 862

Java版UI自动化测试的断言方法/selenium Testng方法封装断言

记录下我在工作中自己封装的一些断言! 目录场景一:判断真与假场景二:判断元素的文本内容存在/包含预期内容 前置条件是已经导入TestNg的包了 场景一:判断真与假 /** * 断言预期的结果的真假 * * @param bool */ protected void assertTrue(boolean bool) { try { Assert.assertTrue(bool); logger.i

Franciz777的博客 2697

手把手教你用Simulink给C2000配置SCI串口发送数据(附模型与避坑点)

本文详细介绍了如何使用Simulink为C2000系列微控制器配置SCI串口发送数据,包括硬件资源分配、模型搭建、参数设置及调试技巧。通过代码生成和串口通信实践,帮助开发者高效实现嵌入式系统开发,避免常见通信问题。

weixin_30831529的博客 297

JAVA项目封装返回数据及统一异常处理

对返回数据进行封装,统一格式。对各种异常进行统一处理,返回异常信息或错误信息(操作相应信息),便于后续纠错和提示用户。新手向,包含简单的实例演示。

qq_45880237的博客 5795

YOLOv8改进 | 注意力篇 | 一文带你改进GAM、CBAM、CA、ECA等通道注意力机制和多头注意力机制

官方论文地址:官方论文地址点击此处即可跳转官方代码地址:官方代码地址点击此处即可跳转GAM旨在通过设计一种机制,减少信息损失并放大全局维度互动特征,从而解决传统注意力机制在通道和空间两个维度上保留信息不足的问题。GAM采用了顺序的通道-空间注意力机制,并对子模块进行了重新设计。具体来说,通道注意力子模块使用3D排列来跨三个维度保留信息,并通过一个两层的MLP增强跨维度的通道-空间依赖性。

Snu77的博客 4万+

【数据集】1980-2020年(5年)土地利用分类数据-中国科学院

简介1980-2020年每五年土地利用数据-中国科学院

WW、forever的博客 3936

Java优雅封装的6个段位,看看你现在处于哪一层?

封装是一种将对象的内部状态和行为隐藏起来,对外只提供一些公共接口的技术。在实际开发中,封装可以提高代码的可维护性、可复用性和安全性,减少代码的耦合度和错误率。

爱媳妇儿爱编程 1322

使用everything出现mem_virtual_alloc(): Fatal Error: out of memory解决方案

本文主要介绍了使用everything出现mem_virtual_alloc(): Fatal Error: out of memory解决方案,希望能对使用everything的同学们有所帮助。 文章目录 1. 问题描述 2. 解决方案

weixin_43178406的博客 1万+

如何优雅的处理Lambda中异常?

1.如何优雅的处理Lambda中异常? java8新增的特性Lambda表达式为我们使用匿名内部类提供了非常简洁的方式。然而,平时在使用lambda的过程发,却发现lambda表达式中如果存在异常,使用try-catch去处理异常,那么这个lambda表达式就会看起来非常臃肿。 在lambda中使用try-catch处理异常:      List&...

Java晋升 4220

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

java 异常分类和处理机制

一、背景介绍 程序在运行过程中发生错误或异常情况是不可避免的,如果每一个运行时错误都由程序员手动控制和处理,其工作量是不可想象的。 Java语言中的异常处理机制就解决的上述问题,把错误与异常的管理带到了面向对象的世界 Java语言定义了很多异常类,将运行错误和异常的信息和处理方法封装在了异常类中,帮助程序员检查和控制异常。即J...

Wonder ZH 2万+

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

替代try catch处理异常的优雅方式

处理异常的新方法前言一、什么是统一异常处理?二、统一异常处理实战总结 前言 软件开发过程中,不可避免的是需要处理各种异常,就我自己来说,至少有一半以上的时间都是在处理各种异常情况,所以代码中就会出现大量的try {…} catch {…} finally {…} 代码块,不仅有大量的冗余代码,而且还影响代码的可读性。 一、什么是统一异常处理? Spring在3.2版本增加了一个注解@ControllerAdvice,可以与@ExceptionHandler、@InitBinder、@ModelAttri

qq_37892751的博客 2612

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

php构造函数里抛出异常_Java中优雅处理异常

一句话异常一句话简单概括异常:程序运行时,发生的不被期望的事件,阻止了程序按预期正常执行,这就是异常。Java 的异常层级结构Throwable 类Throwable 类是 Java 异常类型的顶级父类,一个对象只有是 Throwable 类的实例才能被异常处理机制识别。它有两个重要的方法,分别是 getCause 方法和 getStackTrace 方法,通常情况下我们不用打印 Cau...

weixin_42551028的博客 101

springboot优雅的做全局异常处理(完整代码+已运行使用)

首先我们需要知道的是,java异常的基础知识和springboot对异常处理是如何支持的。Java异常的基础知识大家可以参考菜鸟教程的java教程。 springboot对异常处理的支持 在springboot中,我们只需要定义一个全局异常处理类(用@ControllerAdvice),不需要在每个controller类重复定义。在全局异常处理类中,我们使用@ExceptionHandler指定...

Hitmi_的博客 3753

Java全局异常处理器:优雅处理系统异常

全局异常处理器是干嘛的?怎么用?

H1658554092的博客 1111

Java中如何优雅的处理异常

对于后端程序员来说,写Java程序的时候,是必须要做的事。错误处理固然重要,但是若是被错误处理占据了大部分逻辑,那么就大错特错了。最近对这一点略有研究,稍微提一些优雅的处理异常的技巧和思路。...

java_beautiful的博客 1573

如何用SpringBoot优雅地对异常进行统一的捕获和处理呢?

​ 一、 前言 在日常项目开发中,异常是常见的,但是如何更高效的处理好异常信息,让我们能快速定位到BUG,是很重要的,不仅能够提高我们的开发效率,还能让你代码看上去更舒服,SpringBoot的项目已经对有一定的异常处理了,但是对于我们开发者而言可能就不太合适了,因此我们需要对这些异常进行统一的捕获并处理。 如果不进行异常处理,当出现错误的时候,返回的信息可能是如下图这样的: ​ 二、异常分类 1. 从定义角度的异常分类 ​ (1). Error(错误...

wdjnb的博客 5212

全局异常处理器——优雅的处理异常

封装自定义异常类,用于自定义异常抛出,根据实际情况对业务异常进行更细致和明确的拓展;

xianren95的博客 4552

如何优雅地处理异常

目录问题Java异常层次结构异常处理1、使用异常替代错误返回码2、先写Try-Catch-Finally语句3、使用非受检异常4、给出异常发生的环境说明5、依调用者定义异常类6、定义常规流程7、别返回NULL值8、别传递NULL值 问题 1、什么时候才需要抛异常? 如果你觉得某些“问题”解决不了了,那么你就可以抛出异常了。 问题:对于异常情形,已经无法继续下去了,因为在当前环境下无法获得必要的信息来解决问题,你所能做的就是从当前环境中跳出,并把问题提交给上一级环境。 2、抛出什么类型的异常? 非受检异常和受

kl_Dreaming的博客 748

《Java异常处理的艺术从CheckedException到优雅的异常封装策略》

建立业务相关的自定义运行时异常体系是关键一步。通过定义BusinessException、SystemException等顶级异常类,并为其添加适当的构造方法和属性,可以实现统一的异常处理。这样既能避免Checked Exception的侵入性,又能提供足够的上下文信息。Java异常处理的艺术在于找到强制性与灵活性的平衡点。从Checked Exception的强制约束到优雅的异常封装策略,反映了Java社区对代码质量和开发效率的不懈追求。

haiyinlong的专栏 370

异常、响应体的封装与自定义

*what;生意异常how:状态码 错误信息传给给父亲运行异常careOut:如果 final 变量在声明时没有赋值,那么必须在该类的每个构造函数中对其进行赋值。这是因为构造函数是对象初始化的地方,在对象创建过程中,每个构造函数都会被调用一次,保证 final 变量在对象创建时就被赋予一个确定的值,之后就不能再修改了。

qq_36545519的博客 1012

Java统一异常设计:优雅处理异常,提升代码质量

定义一个ErrorCode// 获取错误码// 获取错误信息通过ErrorCode接口构造// 错误码接口// Getter方法@Override通过统一异常设计,可以显著提升Java代码的质量和可维护性。本文从ErrorCode接口和模块化枚举实现开始,结合UML类图和代码示例,详细介绍了如何实现统一异常设计。希望这些方法能帮助你在实际开发中更好地处理异常,构建健壮的应用程序。如果你对统一异常设计有任何疑问或建议,欢迎在评论区留言讨论!关于作者。

双非二本自学到互联网公司的成长经历,有故事,有干货。 1449
上一篇: HashMap 的实现原理和扩容原理是怎样的?
下一篇: Lombok 的@EqualsAndHashCode 注解:你真的了解它的重要性吗?
加班不秃顶
博客等级 码龄7年 1822粉丝 599原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值