throws 是服务契约的关键部分,须精准声明受检异常、禁用运行时异常冗余声明、严守语法位置、遵循里氏替换原则,并在入口层兜底处理。

在企业级公共组件开发中,throws 不是“甩锅语法”,而是服务契约的关键部分——它明确定义方法的风险边界与调用方的责任义务。用得好,能提升接口可读性、降低集成成本;用得随意,会掩盖真实错误、破坏异常处理链。
只对受检异常(Checked Exception)强制声明
公共组件面向多业务方,必须让调用者清楚哪些异常不得不处理:
- 必须声明:IOException、SQLException、ClassNotFoundException、ParseException 等继承自 Exception 但非 RuntimeException 的类型
- 禁止冗余声明:NullPointerException、IllegalArgumentException、IllegalStateException 等运行时异常,加 throws 不仅编译器不拦,IDE 通常标灰提示,还误导调用方以为“必须处理”
- 例外情况:若为统一错误治理,需将业务异常(如 ServiceException)设计为受检异常,并在组件顶层接口中声明,便于下游做全局兜底
方法签名中 throws 的位置与格式必须严格合规
这是编译器硬性要求,错一个字符就报红:
- 写在参数列表右括号后、方法体左大括号前,例如:public String loadConfig(String path) throws IOException, ConfigParseException { ... }
- 多个异常用英文逗号分隔,无空格或换行干扰
- 不可写在返回类型前、不可塞进方法体内、不可和 throw 混淆(throw 是语句,throws 是声明)
- 常见报错:Syntax error on token "throws" 或 invalid method declaration; return type required,基本都是位置写错
子类/实现类重写时,不能扩大异常范围
组件常通过接口+抽象类+SPI 提供扩展能力,异常声明必须遵循里氏替换原则:
- 父接口声明 throws IOException,子类实现只能声明 throws IOException 或其子类(如 FileNotFoundException),不能新增 SQLException 或 Exception
- 接口方法若未声明 throws,实现类也不得擅自添加——否则违反契约,导致上游编译失败
- 建议在组件基类中定义标准异常体系(如 BaseCheckedException),所有模块复用同一根异常类,便于统一拦截与日志归因
入口方法(如 main / Controller / Filter)避免盲目 throws Exception
公共组件常被嵌入 Web 容器、调度框架或脚手架中,入口层要担起兜底责任:
- main 方法写 throws Exception → JVM 直接退出,堆栈裸露给终端用户,不符合企业可观测性规范
- 正确做法:在最外层捕获并转为结构化响应,例如 Spring Boot 中用 @ControllerAdvice 统一处理受检异常,返回 HTTP 500 + 可读错误码 + traceId
- 若组件本身是 SDK(如支付对接包),则对外 API 方法应明确声明具体异常(如 PayTimeoutException、SignVerifyException),而非笼统抛 Exception,方便业务方做差异化重试或降级










