自定义异常是实现fail-fast与early exit的载体,核心在于何时抛、为何抛、是否足够早且信息明确;需前置校验入口、分层定义异常类型、构造即校验、拒绝静默吞异常。

自定义异常本身不是目的,而是让 Fail-Fast 和 Early Exit 落地的载体。关键不在“抛不抛”,而在“什么时候抛、为什么抛、抛得是否足够早且信息明确”。
校验入口前置,拒绝无效输入进入业务层
Fail-Fast 的第一道防线是参数校验。把自定义异常用在 Controller 或 Service 入口,而不是等数据流进 DAO 才发现 ID 不存在。
- 接收 JSON 请求后,立即检查必填字段、枚举合法性、时间格式、手机号正则等;不满足就 throw InvalidRequestException(继承 RuntimeException),附带具体字段名和错误原因
- 避免在 service 方法里做“if (user == null) return null”,而是直接 throw UserNotFoundException,让调用方无法忽略缺失状态
- 批量操作前加轻量预检:比如传入 100 个订单 ID,先批量查库确认存在性;任一无效即中断,不执行后续扣库存、发消息等动作
异常类型分层,让失败语义可读、可决策
Early Exit 不等于随便 throw RuntimeException。不同失败场景应有不同异常类型,便于上层区分处理逻辑。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 业务规则类失败(如余额不足、库存不够)→ 自定义 BusinessValidationException,含 currentBalance、requiredAmount 等上下文字段
- 外部依赖不稳定(支付超时、短信限流)→ ExternalServiceUnstableException,带 canRetry、retryAfter 等恢复提示
- 数据一致性破坏(遍历时结构被修改)→ DataStructureCorruptedException,替代模糊的 ConcurrentModificationException,明确指向数据容器自身问题
构造即校验,让异常实例化过程自带防御
很多自定义异常只是包装消息,但 Fail-Fast 要求校验行为嵌入到异常创建环节。
- 在异常构造器中主动验证关键参数:比如 OrderCreationException(String orderId) 内部检查 orderId 是否为空或非法格式,不合法直接 throw IllegalArgumentException
- 避免“先 new 再 set”再 throw 的松散模式,强制构造时完成状态可信度断言
- 对敏感字段(如银行卡号、身份证号)在异常构造时做脱敏处理,防止堆栈日志泄露——这也是一种 Fail-Fast 式的安全拦截
拒绝静默吞异常,确保失败路径不被绕过
Early Exit 的反面不是重试,而是掩盖。自定义异常必须配合明确的处理契约,否则就是形同虚设。
- Controller 层统一捕获业务异常,转成标准 Result 响应(如 code=400, msg="用户不存在"),绝不让原始异常堆栈透出
- 异步任务链(CompletableFuture)中,每个 thenApply 都应检查前序结果是否为 null 或已含 error 状态,是则立即 throw 封装后的自定义异常,阻断下游无效执行
- 禁止在 for-each 循环中 catch 自定义异常后 continue —— 这等于把 Early Exit 变成 Silent Skip,违背 Fail-Fast 本意
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










