Appearance
15.5 自定义异常(入门)
自定义异常的概念
自定义异常是指根据业务需求自己定义的异常类。在 Java 中,所有异常都继承自 Throwable 类,自定义异常通常继承自 Exception 类或 RuntimeException 类。
为什么需要自定义异常
业务逻辑需要:针对特定业务场景定义异常,使异常信息更加具体和有意义
异常分类:将不同类型的错误分类,便于异常处理和管理
代码可读性:自定义异常可以使代码更加清晰,便于理解和维护
异常处理:可以为特定的异常类型提供专门的处理逻辑
自定义异常的实现
继承 Exception 类(检查型异常)
java
public class CustomException extends Exception {
// 默认构造方法
public CustomException() {
super();
}
// 带消息的构造方法
public CustomException(String message) {
super(message);
}
// 带消息和原因的构造方法
public CustomException(String message, Throwable cause) {
super(message, cause);
}
// 带原因的构造方法
public CustomException(Throwable cause) {
super(cause);
}
}继承 RuntimeException 类(非检查型异常)
java
public class CustomRuntimeException extends RuntimeException {
// 默认构造方法
public CustomRuntimeException() {
super();
}
// 带消息的构造方法
public CustomRuntimeException(String message) {
super(message);
}
// 带消息和原因的构造方法
public CustomRuntimeException(String message, Throwable cause) {
super(message, cause);
}
// 带原因的构造方法
public CustomRuntimeException(Throwable cause) {
super(cause);
}
}自定义异常的使用
示例 1:检查型自定义异常
java
// 自定义检查型异常
class InvalidUserException extends Exception {
public InvalidUserException(String message) {
super(message);
}
}
public class CustomExceptionExample {
// 声明可能抛出自定义异常
public static void validateUser(String username) throws InvalidUserException {
if (username == null || username.isEmpty()) {
throw new InvalidUserException("用户名不能为空");
}
if (username.length() < 3) {
throw new InvalidUserException("用户名长度不能少于 3 个字符");
}
System.out.println("用户名验证通过: " + username);
}
public static void main(String[] args) {
try {
validateUser(""); // 传递空用户名
} catch (InvalidUserException e) {
System.out.println("用户验证失败: " + e.getMessage());
}
try {
validateUser("ab"); // 传递长度不足的用户名
} catch (InvalidUserException e) {
System.out.println("用户验证失败: " + e.getMessage());
}
try {
validateUser("admin"); // 传递有效的用户名
} catch (InvalidUserException e) {
System.out.println("用户验证失败: " + e.getMessage());
}
}
}示例 2:非检查型自定义异常
java
// 自定义非检查型异常
class InsufficientFundsException extends RuntimeException {
public InsufficientFundsException(String message) {
super(message);
}
}
public class CustomRuntimeExceptionExample {
private double balance;
public CustomRuntimeExceptionExample(double initialBalance) {
this.balance = initialBalance;
}
public void withdraw(double amount) {
if (amount > balance) {
throw new InsufficientFundsException("余额不足,当前余额: " + balance + ", 取款金额: " + amount);
}
balance -= amount;
System.out.println("取款成功,当前余额: " + balance);
}
public static void main(String[] args) {
CustomRuntimeExceptionExample account = new CustomRuntimeExceptionExample(1000);
try {
account.withdraw(500); // 正常取款
} catch (InsufficientFundsException e) {
System.out.println("取款失败: " + e.getMessage());
}
try {
account.withdraw(600); // 余额不足
} catch (InsufficientFundsException e) {
System.out.println("取款失败: " + e.getMessage());
}
}
}示例 3:带自定义属性的异常
java
// 带自定义属性的异常
class OrderException extends Exception {
private int orderId;
public OrderException(String message, int orderId) {
super(message);
this.orderId = orderId;
}
public int getOrderId() {
return orderId;
}
}
public class CustomExceptionWithPropertiesExample {
public static void processOrder(int orderId) throws OrderException {
if (orderId <= 0) {
throw new OrderException("无效的订单ID", orderId);
}
// 处理订单逻辑
System.out.println("订单处理成功: " + orderId);
}
public static void main(String[] args) {
try {
processOrder(-1); // 传递无效的订单ID
} catch (OrderException e) {
System.out.println("订单处理失败: " + e.getMessage() + ", 订单ID: " + e.getOrderId());
}
try {
processOrder(1001); // 传递有效的订单ID
} catch (OrderException e) {
System.out.println("订单处理失败: " + e.getMessage() + ", 订单ID: " + e.getOrderId());
}
}
}自定义异常的最佳实践
选择合适的父类:
- 如果异常需要强制处理,继承自
Exception(检查型异常) - 如果异常不需要强制处理,继承自
RuntimeException(非检查型异常)
- 如果异常需要强制处理,继承自
提供有意义的异常信息:
- 异常信息应该清晰、具体,能够准确描述错误原因
添加必要的构造方法:
- 至少提供一个带消息参数的构造方法
- 可以提供带原因参数的构造方法,便于异常链的处理
添加自定义属性:
- 根据需要添加自定义属性,提供获取这些属性的方法
合理命名:
- 异常类名应该以
Exception结尾,清晰表达异常的类型
- 异常类名应该以
异常层次结构:
- 对于复杂的业务场景,可以创建异常层次结构,便于异常的分类和处理
示例:异常层次结构
java
// 基础异常类
class BusinessException extends Exception {
public BusinessException(String message) {
super(message);
}
}
// 子类异常:用户相关异常
class UserException extends BusinessException {
public UserException(String message) {
super(message);
}
}
// 子类异常:订单相关异常
class OrderException extends BusinessException {
public OrderException(String message) {
super(message);
}
}
// 具体异常:用户名无效异常
class InvalidUsernameException extends UserException {
public InvalidUsernameException(String message) {
super(message);
}
}
// 具体异常:密码错误异常
class InvalidPasswordException extends UserException {
public InvalidPasswordException(String message) {
super(message);
}
}
public class ExceptionHierarchyExample {
public static void validateUser(String username, String password) throws UserException {
if (username == null || username.isEmpty()) {
throw new InvalidUsernameException("用户名不能为空");
}
if (password == null || password.length() < 6) {
throw new InvalidPasswordException("密码长度不能少于 6 个字符");
}
System.out.println("用户验证通过");
}
public static void main(String[] args) {
try {
validateUser("", "123456"); // 无效的用户名
} catch (UserException e) {
System.out.println("用户验证失败: " + e.getMessage());
}
try {
validateUser("admin", "123"); // 无效的密码
} catch (UserException e) {
System.out.println("用户验证失败: " + e.getMessage());
}
try {
validateUser("admin", "123456"); // 有效的用户
} catch (UserException e) {
System.out.println("用户验证失败: " + e.getMessage());
}
}
}常见问题
1. 自定义异常没有提供合适的构造方法
症状:自定义异常缺少必要的构造方法,导致使用不便
解决方案:提供至少一个带消息参数的构造方法,以及其他必要的构造方法
示例:
java
// 错误:没有提供带消息的构造方法
// class CustomException extends Exception {
// }
// 正确:提供带消息的构造方法
class CustomException extends Exception {
public CustomException(String message) {
super(message);
}
}2. 自定义异常继承了错误的父类
症状:自定义异常继承了错误的父类,导致异常处理方式不符合预期
解决方案:根据需要选择合适的父类
- 需要强制处理的异常继承自
Exception - 不需要强制处理的异常继承自
RuntimeException
示例:
java
// 对于需要强制处理的业务异常,继承自 Exception
class BusinessException extends Exception {
// 构造方法
}
// 对于不需要强制处理的运行时异常,继承自 RuntimeException
class SystemException extends RuntimeException {
// 构造方法
}3. 异常信息不够具体
症状:异常信息过于简单,无法准确描述错误原因
解决方案:提供详细、具体的异常信息,包含必要的上下文信息
示例:
java
// 错误:异常信息不够具体
// throw new CustomException("错误");
// 正确:提供详细的异常信息
throw new CustomException("用户名长度不能少于 3 个字符,当前长度: " + username.length());4. 过度使用自定义异常
症状:创建了过多的自定义异常,导致代码复杂
解决方案:合理使用自定义异常,只在必要时创建
示例:
- 对于常见的错误,可以使用系统提供的异常
- 对于特定业务逻辑的错误,使用自定义异常
总结
自定义异常是 Java 异常处理机制的重要组成部分,它允许我们根据业务需求定义特定的异常类型。通过自定义异常,我们可以:
提高代码可读性:使用有意义的异常类名和异常信息,使代码更加清晰
便于异常处理:为特定类型的异常提供专门的处理逻辑
实现异常分类:将不同类型的错误分类,便于管理和维护
传递更多信息:通过自定义属性,传递更多的错误上下文信息
在实现自定义异常时,需要注意:
选择合适的父类:根据是否需要强制处理选择继承
Exception或RuntimeException提供必要的构造方法:至少提供一个带消息参数的构造方法
添加有意义的异常信息:异常信息应该清晰、具体
合理命名:异常类名应该以
Exception结尾避免过度使用:只在必要时创建自定义异常
通过合理使用自定义异常,可以使代码的异常处理更加灵活和有效,提高程序的健壮性和可维护性。
