Java中应优先使用异常而非错误码,因异常由JVM保障传播、不被忽略、可携带上下文;checked异常用于可恢复场景,unchecked用于编程错误;自定义异常需含cause和业务字段,避免空异常和吞异常;仅跨语言时才转为错误码。

Java中不该用错误码替代异常,而该用异常替代错误码——这不是风格偏好,是语言机制和工程实践的必然选择。
为什么if (code == -1)比throw new IOException()更难维护
错误码需要在每一层手动检查、透传、解释,调用链越长,漏判或误判概率越高。异常则由JVM保障传播路径,除非显式catch,否则自动向上抛出。
- 错误码容易被忽略:
int result = doSomething(); // 忘记检查result - 错误码语义模糊:
return -2;到底代表“文件不存在”还是“权限不足”?得翻文档或源码 - 错误码无法携带上下文:没法附带
stack trace、cause、自定义字段(如错误ID、用户ID)
RuntimeException和Exception该选哪个
关键看是否需要强制调用方处理。Java用“检查型异常(checked)”和“非检查型异常(unchecked)”区分恢复可能性,不是按严重程度。
- 用
Exception子类(如IOException):问题可预期、调用方有合理手段恢复(比如重试、切换备用资源) - 用
RuntimeException子类(如IllegalArgumentException):属于编程错误或非法输入,修复应靠改代码,而非运行时兜底 - 避免继承
Error:那是JVM内部故障(如OutOfMemoryError),程序不应捕获或重抛
自定义异常怎么写才不踩坑
核心原则:异常类名要体现“发生了什么”,构造函数要支持传递原始原因和业务上下文。
立即学习“Java免费学习笔记(深入)”;
- 必须提供
Throwable cause构造器:public UserNotFoundException(String msg, Throwable cause) - 可加业务字段(如订单ID):
private final String orderId;,但别放太多,否则影响序列化和日志体积 - 不要重写
printStackTrace()或吞掉cause——这会让排查链断裂 - 避免空异常:
throw new BusinessException();不如throw new BusinessException("user not found: " + userId);
什么时候真该用错误码(极少数例外)
只有在跨语言、跨进程边界且对方无法解析Java异常结构时,才考虑把异常信息转成错误码+消息体。例如HTTP API返回{"code": 400, "message": "invalid email format"}。
- Java内部模块间通信,一律用异常
- RPC框架(如gRPC、Dubbo)已内置异常映射机制,无需手动转码
- 日志里记录
e.getMessage()不够,务必用log.error("failed to process order", e)保留完整堆栈
真正麻烦的从来不是“要不要用异常”,而是没想清楚哪些该声明为checked、哪些该封装成runtime、哪些该在日志里留痕——这些决定一旦固化进接口,改起来比修bug还疼。










