重写方法时应保持异常透明度,即通过契约设计、入口校验、上下文绑定和结构化异常链,确保故障变量与状态原样暴露。需遵守受检异常范围不扩大、变量故障早校验、异步异常带快照、自定义异常含faultyVariableName等字段,并映射为含field/value/reason的错误响应。

重写方法时保持异常透明度,核心是不让异常在子类中被“吃掉”或“变形”,让上层调用能原样感知到底层变量(如参数、状态、外部依赖)引发的故障。这不是靠多写 catch,而是靠设计契约、控制传播路径、暴露关键上下文。
明确继承链中的异常契约
父类方法声明了哪些受检异常(checked exception),子类重写时不能扩大异常范围——不能抛出父类没声明的新受检异常;但可以缩小(不抛)、或转为运行时异常(unchecked),前提是语义不变且不掩盖问题。
- 如果父类方法声明 throws IOException,子类重写后仍应只抛 IOException 或其子类(如 FileNotFoundException)
- 若子类因内部逻辑改用 HTTP 调用,不应直接 throw HttpException;而应包装为 IOException(语义一致:都是 I/O 故障),或统一转为自定义运行时异常(如 DataAccessException),并保留原始异常作为 cause
- 避免把 NullPointerException 包装成业务异常再抛出——这会丢失空值来源线索;应在早期校验并抛 IllegalArgumentException,带明变量名:“param 'userId' cannot be null”
变量故障必须在重写入口处显式暴露
变量类故障(如 null、非法格式、越界、未初始化)往往发生在方法刚进入时。重写方法的第一件事不是执行逻辑,而是校验输入和依赖状态,并立即反馈。
- 重写 processOrder(Order order) 时,先检查 order != null、order.getId() > 0、order.getItems() 非空,任一失败就 throw 新异常,消息包含变量名和当前值(如 “order.id = 0”)
- 若依赖一个外部配置对象 config,重写前先 assertConfigReady(),失败则抛 ConfigNotLoadedException,而不是等后续调用 config.getTimeout() 才 NPE
- 对集合类参数,不用等到 for 循环里才报错;提前用 Objects.requireNonNullElse() 或 CollectionUtils.isEmpty() 检查,失败即中断
异步/并发场景下异常必须绑定执行上下文
当重写的方法内部启用了虚拟线程、协程或 CompletableFuture,异常容易脱离原始调用栈。要确保上层能定位到是哪个变量、哪次调用出的问题。
- 虚拟线程中必须在 lambda 内部 try-catch,并将原始异常连同关键变量快照(如 requestId、inputHash)一起封装进新异常
- 使用 CompletableFuture 时,不要只用 .exceptionally(e -> null) 吞异常;而是用 .handle((r, e) -> { if (e != null) log.error("Failed on input: {}", input, e); return r; })
- Kotlin 协程中,避免用 launch + 全局 try-catch;改用 withContext(NonCancellable) { ... } + supervisorScope 确保异常不中断兄弟任务,同时在 catch 块中记录触发异常的具体变量值
提供可追溯的异常链与结构化错误码
仅靠异常类型和消息不够透明。上层需要知道:是哪个变量坏的?坏成什么样?是否可重试?这些信息必须通过异常本身传递,而不是靠日志猜。
- 自定义异常类应含字段:faultyVariableName(String)、actualValue(Object)、errorCode(String,如 “VALIDATION.NULL_FIELD”)、retryable(boolean)
- 重写方法抛异常时,主动构造带 cause 的链:throw new ValidationException("Invalid status", "status", status, false).initCause(originalEx)
- 对外 SDK 或 API 层,将异常映射为标准错误响应体,其中 error.detail 包含变量名与原始值(脱敏后),例如 {"field": "email", "value": "user@", "reason": "missing domain"}

















