直接 throw new Exception("xxx") 不够用,因其将所有错误混为同一类型,调用方无法区分错误类型,只能依赖脆弱的 message 字符串匹配;应按业务上下文命名自定义异常,如 UserAlreadyExistsException,并携带错误码、时间戳等关键上下文信息,严格遵循继承契约以确保正确处理。

为什么直接 throw new Exception("xxx") 不够用
因为这种写法把所有错误都压进同一个类型里,调用方无法区分是参数错、网络断、还是库存不足。捕获时只能靠 message 字符串匹配,既脆弱又难维护。比如 "user not found" 和 "order not found" 都塞进 Exception,日志里看不出是哪个模块出的问题。
怎么命名自定义异常类才不会让人困惑
命名必须带业务上下文,且一眼能分清层级。不推荐 MyException 或 BusinessException 这种泛化名。
- Java/C# 推荐格式:
UserAlreadyExistsException、InsufficientStockException、PaymentTimeoutException - Python 推荐格式:
UserNotFoundError、InventoryShortageError、ThirdPartyApiTimeoutError - 避免在类名里加 “Error” 和 “Exception” 混用(如
UserNotFoundExcption),统一选一个后缀并全项目遵守
构造函数里该塞哪些信息才真正有用
只传 message 是最低限度;生产环境需要能快速定位问题的上下文。关键不是“说了什么”,而是“能查到什么”。
- 必带字段:错误码(如
"ERR_USER_001")、触发时刻(Instant.now()或datetime.utcnow()) - 按场景选带:用户 ID(
userId)、订单号(orderId)、请求 ID(traceId)、原始输入值(inputValue) - Java/C# 建议提供带
innerException的构造函数,方便包装底层异常而不丢失堆栈 - Python 可重写
__str__或__repr__,让print(e)直接输出结构化内容,而不是默认的类名+内存地址
继承 Exception 还是 RuntimeException(Java)/ Exception(C#)/ Exception(Python)
这不是风格问题,是契约问题。选错会导致调用方漏处理或过度处理。
- Java 中:如果错误可预期且必须由调用方恢复(如文件不存在、网络超时),继承
Exception;如果是编程错误或不可恢复状态(如空指针、非法参数),继承RuntimeException - C# 中:统一继承
Exception即可,.NET 不强制检查,但可通过静态分析工具约束;若想强调“非致命”,可用ArgumentException等已有子类扩展 - Python 中:全部继承
Exception;不要继承BaseException(会拦截KeyboardInterrupt等系统信号) - 切忌为了“省事”全用运行时异常——比如数据库连接失败,本应让上层决定重试还是降级,却抛了
RuntimeException导致静默失败
真实项目里最容易被忽略的,是异常类的版本兼容性:一旦上线,就不能删字段、不能改构造函数签名、不能删 getter 方法。哪怕只是加个 traceId,也要考虑旧日志解析逻辑是否还能读取新异常实例。

















