Skip to content

15.5 自定义异常(入门)

自定义异常的概念

自定义异常是指根据业务需求自己定义的异常类。在 Java 中,所有异常都继承自 Throwable 类,自定义异常通常继承自 Exception 类或 RuntimeException 类。

为什么需要自定义异常

  1. 业务逻辑需要:针对特定业务场景定义异常,使异常信息更加具体和有意义

  2. 异常分类:将不同类型的错误分类,便于异常处理和管理

  3. 代码可读性:自定义异常可以使代码更加清晰,便于理解和维护

  4. 异常处理:可以为特定的异常类型提供专门的处理逻辑

自定义异常的实现

继承 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());
        }
    }
}

自定义异常的最佳实践

  1. 选择合适的父类

    • 如果异常需要强制处理,继承自 Exception(检查型异常)
    • 如果异常不需要强制处理,继承自 RuntimeException(非检查型异常)
  2. 提供有意义的异常信息

    • 异常信息应该清晰、具体,能够准确描述错误原因
  3. 添加必要的构造方法

    • 至少提供一个带消息参数的构造方法
    • 可以提供带原因参数的构造方法,便于异常链的处理
  4. 添加自定义属性

    • 根据需要添加自定义属性,提供获取这些属性的方法
  5. 合理命名

    • 异常类名应该以 Exception 结尾,清晰表达异常的类型
  6. 异常层次结构

    • 对于复杂的业务场景,可以创建异常层次结构,便于异常的分类和处理

示例:异常层次结构

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 异常处理机制的重要组成部分,它允许我们根据业务需求定义特定的异常类型。通过自定义异常,我们可以:

  1. 提高代码可读性:使用有意义的异常类名和异常信息,使代码更加清晰

  2. 便于异常处理:为特定类型的异常提供专门的处理逻辑

  3. 实现异常分类:将不同类型的错误分类,便于管理和维护

  4. 传递更多信息:通过自定义属性,传递更多的错误上下文信息

在实现自定义异常时,需要注意:

  1. 选择合适的父类:根据是否需要强制处理选择继承 ExceptionRuntimeException

  2. 提供必要的构造方法:至少提供一个带消息参数的构造方法

  3. 添加有意义的异常信息:异常信息应该清晰、具体

  4. 合理命名:异常类名应该以 Exception 结尾

  5. 避免过度使用:只在必要时创建自定义异常

通过合理使用自定义异常,可以使代码的异常处理更加灵活和有效,提高程序的健壮性和可维护性。

© 2026 编程马·菜鸟教程 版权所有