应根据错误是否可恢复选择:可恢复用错误码(如Result),不可恢复必须throw;RuntimeException用于编程错误,CheckedException用于外部依赖失败;避免原始数字错误码,优先用枚举或Result封装。

Java里该用throw还是return错误码?看异常是否可恢复
Java中抛异常和返回错误码不是风格偏好问题,而是语义分层问题。核心判断标准是:调用方能否在当前上下文合理处理并继续执行。如果能,用错误码;如果不能(比如数据库连接中断、空指针、非法参数),必须throw——否则会掩盖故障边界,让错误在调用栈深处突然爆发。
RuntimeException和CheckedException怎么选?看是否需要强制捕获
Java强制检查异常(IOException、SQLException)要求调用方显式处理,适合外部依赖失败(如文件读写、网络请求)。但滥用会导致大量try-catch或throws污染业务逻辑。相反,RuntimeException子类(如IllegalArgumentException、NullPointerException)应代表编程错误或不可恢复状态,不强制捕获,靠测试和日志暴露。
- 对外API(如Spring MVC接口)建议统一用
RuntimeException封装业务错误(如BusinessException),由全局异常处理器转为HTTP状态码+JSON错误体 - 内部模块间调用,若错误属于正常流程分支(如“用户不存在”),优先返回
Optional或自定义结果类Result,而非抛异常 - 不要把
NullPointerException当控制流用——这是bug,不是设计
返回错误码的常见陷阱:类型擦除和错误传播失真
用int或String表示错误码看似简单,但很快会遇到问题:不同模块错误码冲突、含义模糊、无法携带上下文。比如return -1可能代表“未找到”“权限不足”或“超时”,调用方只能靠文档猜。
public class Result{ private final boolean success; private final T data; private final String errorCode; // 比int更易读,支持国际化 private final String message; public static Result success(T data) { ... } public static Result fail(String code, String msg) { ... } }
- 避免用原始数字错误码,改用枚举(
ErrorCode.USER_NOT_FOUND)或字符串常量 - 错误码必须附带可读消息,且消息不应暴露敏感信息(如数据库表名)
- 若需链路追踪,错误对象里要预留
traceId字段,单纯返回码做不到
性能差异其实可以忽略,但堆栈开销真实存在
抛Exception确实比return慢,因为要生成堆栈快照。但在99%的业务场景中,这个开销远小于一次DB查询或HTTP调用。真正要注意的是:不要在高频循环里抛异常(比如用NumberFormatException做字符串数字校验),这会让JVM无法优化,且堆内存碎片化加剧。
立即学习“Java免费学习笔记(深入)”;
- 字符串转数字请用
Integer.parseInt()前先matches("\\d+"),别依赖catch - 集合取值用
list.get(i)前先判i ,而不是靠IndexOutOfBoundsException兜底 - Spring Boot默认开启
server.error.include-stacktrace=never,就是防止异常堆栈被误发给前端
异常设计最难的不是语法,是界定“什么是正常流程的失败”和“什么是系统级崩溃”。一个Result对象可能比五个自定义异常更清晰,也可能让错误处理散落在二十个if里——关键在团队对错误边界的共识,不在技术选型本身。










