You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

开发StudentRepository类应使用自定义异常还是Java预定义异常?

异常选择建议及代码优化方案

核心问题解答:用自定义异常还是JDK预定义异常

  • 优先用JDK预定义异常的场景:如果错误属于通用编程错误,语义和JDK自带异常完全匹配,直接用即可,降低其他开发者的理解成本:
    • 传入的参数对象为null:用NullPointerException
    • 传入的参数内容不符合格式/范围要求(比如空姓名、性别值非法、出生日期超出范围):用IllegalArgumentException
    • 不支持的操作:用UnsupportedOperationException
  • 需要自定义异常的场景:如果错误属于特定业务规则不满足,上层代码需要针对这类错误做单独的捕获处理,就自定义异常,避免和通用异常混淆,不用通过判断异常信息来区分错误类型:
    • 比如删除学生时找不到对应学生、新增学生时学号重复这类和学生业务强相关的错误,就可以自定义StudentNotFoundException、StudentDuplicateCnpException等异常类

现有代码问题修正

1. 异常类型使用错误

你现在对空姓名抛NullPointerException是不合理的,NPE仅适用于参数对象本身为null的场景,trim后为空属于参数内容非法,应该改用IllegalArgumentException。

2. 删除学生逻辑错误

你现在的遍历逻辑只要第一个学生CNP不匹配就直接抛异常,不会遍历后续的学生,逻辑完全错误,可优化为使用removeIf批量判断。

3. 集合数据同步遗漏

原代码只维护了studentList的增删,没有同步更新studentsByAge和studentsByLastName两个TreeSet,会导致这两个集合数据始终为空。

4. 学生不存在的异常类型不合理

删除时找不到学生属于业务错误,要么用IllegalArgumentException,要么自定义对应的业务异常,不要用NullPointerException。

优化后代码示例

自定义业务异常示例

// 非受检业务异常,不需要上层强制捕获就继承RuntimeException,需要强制捕获就继承Exception
public class StudentNotFoundException extends RuntimeException {
    public StudentNotFoundException(String cnp) {
        super("CNP为" + cnp + "的学生不存在");
    }
}

修正后的StudentRepository代码

public class StudentRepository {

    private static List<Student> studentList = new ArrayList<>();
    private static Set<Student> studentsByAge = new TreeSet<>(new AgeComparator());
    private static Set<Student> studentsByLastName = new TreeSet<>(new LastNameComparator());

    public static Student addStudent(String firstName, String lastName, String gender, LocalDate dateOfBirth) {
        // 先判断参数本身是否为null,这种情况抛NPE
        if (firstName == null || lastName == null || gender == null || dateOfBirth == null) {
            throw new NullPointerException("入参不能为null");
        }
        // 再判断参数内容是否合法,这种情况抛IllegalArgumentException
        if (firstName.trim().isEmpty() || lastName.trim().isEmpty()) {
            throw new IllegalArgumentException("姓名不能为空");
        } else if (!(gender.equalsIgnoreCase("m") || gender.equalsIgnoreCase("f"))) {
            throw new IllegalArgumentException("性别只能为'M'或'F'");
        } else if (dateOfBirth.isBefore(LocalDate.of(1900, 1, 1)) ||
                dateOfBirth.isAfter(LocalDate.now())) {
            throw new IllegalArgumentException("出生日期需要在1900年至今之间");
        }

        Student newStudent = new Student(firstName, lastName, dateOfBirth, gender);
        studentList.add(newStudent);
        // 同步维护两个排序集合
        studentsByAge.add(newStudent);
        studentsByLastName.add(newStudent);
        return newStudent;
    }

    public static void deleteStudentByCnp(String cnp) {
        if (cnp == null) {
            throw new NullPointerException("CNP不能为null");
        }
        boolean removed = studentList.removeIf(student -> cnp.equals(student.getCnp()));
        if (!removed) {
            // 自定义异常的用法
            throw new StudentNotFoundException(cnp);
            // 不想自定义的话替换为下面的代码即可
            // throw new IllegalArgumentException("CNP为" + cnp + "的学生不存在");
        }
        // 同步删除两个排序集合里的学生数据
        studentsByAge.removeIf(student -> cnp.equals(student.getCnp()));
        studentsByLastName.removeIf(student -> cnp.equals(student.getCnp()));
    }
}

内容的提问来源于stack exchange,提问作者Alex Darius

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 20:36:03