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

