发布于2026-07-10 阅读(0)
扫一扫,手机访问
在 Ja va 里自定义异常,说白了就是定义一个新的异常类,让它继承 Exception 或 RuntimeException。为啥必须二选一?因为 Ja va 编译器强制要求所有异常都必须是 Throwable 的子类,而实际项目中你几乎不会直接继承 Throwable 本身——它太底层,连编译器都懒得跟你玩。选 Exception 还是 RuntimeException,核心区别在于“要不要逼调用方处理”。举个例子,业务校验失败(比如用户名为空)你大概率不希望到处写 try-catch,那就用 RuntimeException 子类;而文件读取失败这种外部依赖问题,往往强制调用方感知,那就用 Exception 子类。

一个最简的自定义异常类,两行构造函数就够了:一个空参,一个带 String message 的。Ja va 异常机制靠这两个构造器把消息传给父类,堆栈信息也由父类自动维护,你完全不用操心 toString() 或手动拼接。下面的例子,就是那种“能用、且够用”的模样:
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException() {
super();
}
public UserNotFoundException(String message) {
super(message);
}
}
注意几个细节:不要加额外字段,比如错误码——除非真有多个地方需要读取它,而且逻辑统一。否则只会让代码变臃肿,维护成本直线上升。另外,throw 的时候别犯低级错误:有人会写成 throw UserNotFoundException;,忘了 new,编译直接报错。还有种常见情况:消息写死又冗余。比如在 service 层抛出 new UserNotFoundException("用户不存在"),调用方根本不知道到底是哪个用户出了问题。更好的做法是把具体 ID 带进去:new UserNotFoundException("user id: " + userId + " not found")。这才是真正有用的信息。
至于什么时候需要加带 Throwable cause 的构造器?只有当你想保留原始异常上下文时才有必要。比如 DAO 层捕获了 SQLException,又想包装成业务异常向上抛,那就必须把原始异常通过 cause 传进去:
catch (SQLException e) {
throw new DataAccessException("查询用户失败", e);
}
如果没有这个构造器,原始异常的堆栈就丢了,排查时你只能看到“查询用户失败”,根本不知道是连接超时还是 SQL 语法错。这个构造器不是必须的,但一旦涉及异常转译,漏掉它会让日志丧失关键线索,严重拖慢定位速度。
说到底,写一个自定义异常类本身很简单,真正难的是判断:该不该让异常逃出当前方法?该不该包装?该不该记录日志?这些决策没法靠继承搞定,更多依赖你对业务场景和团队规范的把握。记住一点:异常不只是语法规则,更是沟通方式——告诉调用方、告诉同事、告诉未来的自己,到底发生了什么。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8