Java 自定义异常:如何优雅地处理程序中的“业务病”?

 

🔥「炎码工坊」技术弹药已装填!
点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】

 

一、从一个真实场景开始:银行转账系统的困境

假设你正在开发一个银行转账系统,当用户尝试转账时可能出现以下问题:

  1. 转账金额为负数(非法输入)
  2.  账户余额不足(业务规则冲突)
  3. 目标账户不存在(数据异常)

如果直接使用if-else处理这些问题,代码会变成这样:

// 伪代码示例(不推荐)
if(amount < 0) {System.out.println("错误代码:1001,金额非法");
} else if(balance < amount) {System.out.println("错误代码:1002,余额不足");
} else if(accountNotExist) {System.out.println("错误代码:1003,账户不存在");
}

这种写法存在三个致命问题:

  • 错误信息分散,维护困难
  • 业务逻辑与错误处理混杂
  • 无法区分系统异常与业务异常

二、终极解决方案:自定义异常模式

1. 标准治疗方案:创建自定义异常类

// JDK 1.8+ 可运行示例
// 业务异常基类(检查型异常)
class BusinessException extends Exception {public BusinessException(String message) {super(message);}
}// 具体异常子类
class InvalidAmountException extends BusinessException {public InvalidAmountException(String message) {super(message);}
}class InsufficientBalanceException extends BusinessException {public InsufficientBalanceException(String message) {super(message);}
}class AccountNotFoundException extends BusinessException {public AccountNotFoundException(String message) {super(message);}
}

2. 异常抛出规范:像医生诊断一样精准

public class BankService {// 假设账户余额private double balance = 1000;public void transfer(double amount, String targetAccount) throws BusinessException {if(amount <= 0) {throw new InvalidAmountException("转账金额必须大于0");}if(balance < amount) {throw new InsufficientBalanceException("账户余额不足");}if(!"valid_account".equals(targetAccount)) {throw new AccountNotFoundException("目标账户不存在");}// 实际转账逻辑...System.out.println("转账成功");}
}

3. 异常处理:统一的异常捕获

public class Main {public static void main(String[] args) {BankService bank = new BankService();try {bank.transfer(-100, "invalid_account");} catch(InvalidAmountException e) {System.err.println("金额异常:" + e.getMessage());} catch(InsufficientBalanceException e) {System.err.println("余额不足:" + e.getMessage());} catch(AccountNotFoundException e) {System.err.println("账户异常:" + e.getMessage());} catch(Exception e) {System.err.println("未知异常:" + e.getMessage());}}
}

三、不同方案对比:内置异常 vs 自定义异常

方案类型优点缺点适用场景
使用内置异常开发速度快信息模糊,难以区分业务类型简单脚本或原型开发
自定义异常精准定位问题,业务清晰需要设计类结构正式项目、企业级应用
异常码+枚举统一错误码管理需要维护映射关系微服务间通信、API接口
异常链模式保留原始异常上下文增加调试复杂度多层架构、框架开发

四、异常处理流程图解

┌───────────────┐
│   try代码块   │
│ 尝试执行业务逻辑 │
└───────┬───────┘│▼
┌───────────────┐
│ 是否发生异常?├─否─→ 执行正常流程
└───────┬───────┘│是▼
┌───────────────┐
│  匹配异常类型  │
│(按catch顺序) │
└───────┬───────┘│▼
┌───────────────┐
│ 执行对应的    │
│ 异常处理逻辑  │
└───────┬───────┘│▼
┌───────────────┐
│ 执行finally代码│
│ 块(可选)    │
└───────────────┘

五、最佳实践指南

  1. 异常分类原则
    •  RuntimeException 子类:表示编程错误(如空指针)
    • Exception 子类:表示业务规则异常(需要强制处理)
  2. 异常信息规范
    // 好的写法:包含具体上下文
    throw new AccountNotFoundException("用户ID:123456 的账户不存在");// 不推荐:模糊描述
    throw new AccountNotFoundException("账户错误");
  3. 资源管理新姿势(JDK7+)
    // 自动资源管理(ARM)
    try (FileReader reader = new FileReader("data.txt")) {// 使用资源
    } catch (IOException e) {// 处理异常
    } // 自动关闭资源,无需finally

六、专有名词说明表

术语全称/解释
Checked异常检查型异常(Exception子类):必须捕获或声明抛出
Unchecked异常非检查型异常(RuntimeException子类):可不处理
异常链异常包装技术,保留原始异常信息(throw new NewException("msg", cause))
try-with-resourcesJDK7新特性,自动管理资源关闭
业务异常符合业务规则的异常(如余额不足),区别于系统异常(如IO异常)
异常传播异常从调用栈向上传递的过程
finally块无论是否异常都执行的代码块,用于资源清理

七、延伸思考:异常处理的艺术

在大型系统中,建议采用异常分层设计

┌───────────────────────┐
│  API 层               │
│ 捕获全局异常,返回    │
│ 标准化错误响应        │
└─────────▲─────────────┘│
┌─────────┴─────────────┐
│  Service 层           │
│ 抛出业务异常          │
└─────────▲─────────────┘│
┌─────────┴─────────────┐
│  DAO 层               │
│ 抛出数据访问异常      │
└───────────────────────┘

通过合理使用自定义异常,你可以:

  • 提升代码可读性:一眼看出业务规则限制
  •  降低维护成本:异常处理集中管理
  •  增强系统健壮性:防止异常误处理
  • 提供调试线索:精确的错误定位

记住:优秀的异常设计就像人体的免疫系统——既能及时发现异常,又能精准应对,让程序在风雨中保持稳定运行。

 

🚧 您已阅读完全文99%!缺少1%的关键操作:
加入「炎码燃料仓」🚀 获得:
√ 开源工具红黑榜
√ 项目落地避坑指南
√ 每周BUG修复进度+1%彩蛋
(温馨提示:本工坊不打灰工,只烧脑洞🔥) 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/84209.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/84209.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【JAVA】【Stream流】

1. filter操作 filter()方法用于根据给定的条件过滤列表中的元素&#xff0c;仅保留满足条件的项。 List<Integer> list Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);List<Integer> res list.stream().filter(a -> a % 2 0).collect(Collectors.toList());for(I…

四、Redis实现限流

简介&#xff1a; 限流算法在分布式领域是一个经常被提起的话题&#xff0c;当系统的处理能力有限时&#xff0c;如何阻止计划外的请求继续对系统施压。 系统要限定用户的某个行为在指定的时间里只能允许发生 N 次&#xff0c;如何使用 Redis 的数据结构来实现这个限流的功能&a…

基于Geotools的两条道路相交并根据交点形成新路线实战-以OSM数据为例

目录 前言 一、需求场景及分解 1、需求场景 2、需求应用 二、需求实现 1、加载路网数据 2、获取道路信息 3、相交点求解 4、生成新道路 5、结果可视化 三、总结 前言 在当今数字化迅速发展的时代&#xff0c;地理空间数据的处理与分析已成为众多领域不可或缺的关键技…

goland有基础速通(需要其它编程语言基础)

tip: 无论是变量、方法还是struct的访问权限控制都是通过命名控制的&#xff0c;命名的首字母是大写就相当于java中的public&#xff0c;小写的话就是private&#xff0c;&#xff08;private只有本包可以访问&#xff09; 1 go的变量声明 普通变量 特点&#xff1a; 变量类…

量化面试绿皮书:19. 相关系数

文中内容仅限技术学习与代码实践参考&#xff0c;市场存在不确定性&#xff0c;技术分析需谨慎验证&#xff0c;不构成任何投资建议。 19. 相关系数 假设有三个随机变量x、y和z。 x与y之间的相关系数为0.8&#xff0c;x与z之间的相关系数也是0.8。 Q: 那么y与z之间的最大相关…

新生活的开启:从 Trae AI 离开后的三个月

很久没有写文章了&#xff0c;想借着入职新公司一个月的机会&#xff0c;和大家唠唠嗑。 离职 今年2月份我从字节离职了&#xff0c;结束了四年的经历&#xff0c;当时离开的核心原因是觉得加班时间太长了&#xff0c;平均每天都要工作15&#xff0c;16个小时&#xff0c;周末…

LLM部署之vllm vs deepspeed

部署大语言模型(如 Qwen/LLaMA 等)时,vLLM 与 DeepSpeed 是当前主流的两种高性能推理引擎。它们各自专注于不同方向,部署流程也有明显区别。 vLLM 提供极致吞吐、低延迟的推理服务,适用于在线部署;DeepSpeed 更侧重训练与推理混合优化,支持模型并行,适用于推理 + 微调/…

Git(二):基本操作

文章目录 Git(二)&#xff1a;基本操作添加文件修改文件版本回退撤销修改情况一&#xff1a;工作区的代码还没有 add情况⼆&#xff1a;已经 add 但没有 commit情况三&#xff1a;已经 add 并且也 commit 删除文件 Git(二)&#xff1a;基本操作 添加文件 首先我们先来学习一个…

nginx + ffmpeg 实现 rtsp视频实时播放和历史播放

nginx和ffmpeg 的安装请参考我的另一篇文章 Nginxrtmpffmpeg搭建视频转码服务_nginx-rtmp-module-master-CSDN博客 目录 1、整体方案设计如图 2、nginx下目录创建和配置文件创建 3、创建视频流生成脚本 4、修改nginx配置 5、管理界面 (video.html) 6、ffmpeg后台启动 …

全国产!瑞芯微 RK3576 ARM 八核 2.2GHz 工业核心板—硬件说明书

前 言 本文为创龙科技 SOM-TL3576 工业核心板硬件说明书,主要提供 SOM-TL3576 工业 核心板的产品功能特点、技术参数、引脚定义等内容,以及为用户提供相关电路设计指导。 为便于阅读,下表对文档出现的部分术语进行解释;对于广泛认同释义的术语,在此不做注释。 硬件参考…

web3 浏览器注入 (如 MetaMask)

以下是关于 浏览器注入方式(如 MetaMask) 的完整详解,包括原理、使用方法、安全注意事项及常见问题解决方案: 1. 核心原理 当用户安装 MetaMask 等以太坊钱包扩展时,钱包会向浏览器的 window 对象注入一个全局变量 window.ethereum,这个对象遵循 EIP-1193 标准,提供与区…

解密提示词工程师:AI 时代的新兴职业

大家好!在人工智能飞速发展的当下&#xff0c;有一个新兴职业正悄然崛起——提示词工程师。他们虽不如数据科学家般广为人知&#xff0c;却在 AI 应用领域发挥着独特且关键的作用。 何为提示词工程师&#xff1f; 提示词工程师专注于设计和优化与 AI 模型进行交互的提示词&…

linux 下 jenkins 构建 uniapp node-sass 报错

背景: jenkins 中构建 uniapp 应用 配置: 1. 将windows HbuilderX 插件目录下的 uniapp-cli 文件夹复制到 服务器 /var/jenkins_home/uniapp-cli 2. jenkins 构建步骤增加 执行 shell ,内容如下 echo ">> 构建中..."# 打包前端 export LANGen_US.UTF-8…

QT常见问题(1)

QT常见问题&#xff08;1&#xff09; 1.问题描述 Qt在编译器中直接运行没有任何问题&#xff0c;但是进入exe生成目录直接双击运行就报错&#xff1a;文件无法定位程序输入点_zn10qarraydata10deallocateepsyy于动态链接库。 2.问题原因 这个错误通常是由于程序运行时找不…

『大模型笔记』第2篇:并发请求中的 Prefill 与 Decode:优化大语言模型性能

『大模型笔记』并发请求中的 Prefill 与 Decode:优化大语言模型性能 文章目录 一. Token 生成的两个阶段:Prefill 和 Decode1.1. 指标分析1.2. 资源利用率分析二. 并发处理机制2.1. 静态批处理 vs 持续批处理(Static Batching vs. Continuous Batching)2.2. Prefill 优先策略…

JVM(7)——详解标记-整理算法

核心思想 标记-整理算法同样分为两个主要阶段&#xff0c;但第二个阶段有所不同&#xff1a; 标记阶段&#xff1a; 与标记-清除算法完全一致。遍历所有可达对象&#xff08;从 GC Roots 开始&#xff09;&#xff0c;标记它们为“存活”。 整理阶段&#xff1a; 不再简单地清…

进程虚拟地址空间

1. 程序地址空间回顾 我们在学习语言层面时&#xff0c;会了解到这样的空间布局图&#xff0c;我们先对他进行分区了解&#xff1a; 如果以静态static修饰的变量就会当成已初始化全局变量来看待&#xff0c;存放在已初始化数据区和未初始化数据区之前。 如果不用static修饰test…

C语言学习day17-----位运算

目录 1.位运算 1.1基础知识 1.1.1定义 1.1.2用途 1.1.3软件控制硬件 1.2运算符 1.2.1与 & 1.2.2或 | 1.2.3非 ~ 1.2.4异或 ^ 1.2.5左移 << 1.2.6右移 >> 1.2.7代码实现 1.2.8置0 1.2.9置1 1.2.10不借助第三方变量&#xff0c;实现两个数的交换…

【linux】简单的shell脚本练习

简单易学 解释性语言&#xff0c;不需要编译即可执行 对于一个合格的系统管理员来说&#xff0c;学习和掌握Shell编程是非常重要的&#xff0c;通过shell程序&#xff0c;可以在很大程度上简化日常的维护工作&#xff0c;使得管理员从简单的重复劳动中解脱出来 用户输入任意两…

机构运动分析系统开发(Python实现)

机构运动分析系统开发(Python实现) 一、引言 机构运动分析是机械工程的核心内容,涉及位置、速度和加速度分析。本系统基于Python开发,实现了平面连杆机构的完整运动学分析,包含数学建模、数值计算和可视化功能。 二、系统架构设计 #mermaid-svg-bT8TPKQ98UU9ERet {font…