java异常处理的核心是建立防御性思维,需设三道防线:第一道校验外部输入并统一转业务异常;第二道在方法内做空值、范围、状态等“三防”判断;第三道对外部依赖设超时、捕获特定异常、判空降级。

Java异常处理不是写几个try-catch就能过关的事,核心在于建立防御性思维——不信任输入、不依赖外部、不假设调用方靠谱。真正健壮的代码,是在问题发生前就设好防线,而不是等炸了再补救。
所有外部输入必须校验(第一道硬闸)
前端传的JSON、RPC接口返回的数据、数据库查出来的记录,一律视为不可信。空值、非法格式、越界数值不能靠业务逻辑兜底,得在最外层拦截。
- DTO加JSR-380注解(@NotBlank、@Email、@Min/@Max),配合Controller上@Valid,非法请求直接400返回,不进service
- 非Spring环境用Apache Commons Validator或手动判空+范围检查,避免NPE和业务逻辑被脏数据带偏
- 校验失败不抛RuntimeException,统一转成明确语义的业务异常(如InvalidParamException),便于前端解析提示
方法内部做“三防”判断(第二道内控)
哪怕参数来自自己写的上层调用,也要当它可能出错。防御不是怀疑同事,而是承认系统复杂度下任何环节都可能漏掉边界条件。
- 防空:入参对象、关键字段(如id、amount)判null,立刻拒绝
- 防错:金额不能负数、状态码必须在枚举范围内、时间不能是未来日期,用if+throw明确拦截
- 防崩:对可能为null的返回值(如Optional.empty()、map.get()结果)不直接链式调用,先用isPresent()或orElse()兜底
外部依赖必须设限+兜底(第三道隔离墙)
HTTP调用、Redis读写、DB查询,本质都是网络操作,超时、熔断、空响应是常态,不是异常。把它们当“可能失败的正常流程”来设计。
- 超时必设:RestTemplate配client timeout,Feign用connect/read timeout配置,避免线程卡死
- 异常必捕:不能让IOException、RedisConnectionFailureException穿透到上层,统一转成ServiceUnavailableException
- 结果必判:远程返回null或空字符串?走默认值、降级逻辑或缓存旧数据,绝不让空指针蔓延
异常抛出要精准,捕获要克制
抛异常不是为了“出问题”,而是为了“说清楚问题在哪、怎么解决”。捕获也不是为了“吞掉错误”,而是为了“接管控制流”。
- 用标准异常优先:IllegalArgumentException代替BusinessException,NullPointerException已有语义,别重造轮子
- 不捕获Exception或Throwable:会掩盖真实问题,比如把SQLException和NPE混在一起处理,排查成本翻倍
- 日志要带上下文:log.error("订单支付回调失败,orderNo={}", orderNo, e),而不是e.printStackTrace()
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











