1 自定义异常
1.1 是什么
Java为我们提供了创建自己的异常的工具,这些异常基本上是Exception的派生类。 例如,以下代码中的MyException扩展了Exception类。
1.2 为什么需要自定义异常
尽管Java异常几乎涵盖了所有异常情况和条件,但是有时候,我们需要创建自己的代表业务逻辑异常的内容,即业务逻辑或工作流程特有的异常。 例如EmailNotUniqueException,InvalidUserStateException等。
简单来说 使用自定义异常的好处
能够帮助应用程序客户端更好地了解出了什么问题。 它们对于执行REST API的异常处理特别有用,因为不同的业务逻辑约束要求将不同的响应代码发送回客户端。
如果java 中已经有了我们需要的异常则没必要重新创建自定义异常。
1.3 自定义检测类异常
让我们考虑一个场景,在该场景中,我们要验证作为参数传递给方法的电子邮件。
我们要检查它是否有效。 现在我们可以使用Java的内置IllegalArgumentException,如果我们仅检查单个事物(例如它是否与预定义的REGEX相匹配),这很好。
但是,假设我们还有一个业务条件来检查系统中的所有电子邮件都必须是唯一的。 现在,我们必须执行第二项检查(数据库/网络调用)。 当然,我们可以使用相同的IllegalArgumentException,但尚不清楚确切的原因是-电子邮件REGEX验证失败还是数据库中已经存在该电子邮件。
让我们创建一个自定义异常来处理这种情况。 要创建一个异常,就像其他任何异常一样,我们必须扩展java.lang.Exception类:
public class EmailNotUniqueException extends Exception {
public EmailNotUniqueException(String message) {
super(message);
}
}
我们提供了一个构造函数,该构造函数接收String错误消息并调用父类构造函数。
现在,让我们在代码中使用此自定义异常。 由于我们定义了一种可以在服务层中引发异常的方法,因此我们将使用throws关键字对其进行标记。
如果输入的电子邮件已经存在于我们的数据库中(在本例中为列表),我们将抛出自定义异常:
public class RegistrationService {
List<String> registeredEmails = Arrays.asList("abc@gmail.com", "xyz@gmail.com");
public void validateEmail(String email) throws EmailNotUniqueException {
if (registeredEmails.contains(email)) {
throw new EmailNotUniqueException("Email Already Registered");
}
}
}
测试类
public class RegistrationServiceClient {
public static void main(String[] args) {
RegistrationService service = new RegistrationService();
try {
service.validateEmail("abc@gmail.com");
} catch (EmailNotUniqueException e) {
// logging and handling the situation
}
}
}
运行结果
mynotes.custom.checked.exception.EmailNotUniqueException: Email Already Registered
at mynotes.custom.checked.exception.RegistrationService.validateEmail(RegistrationService.java:12)
at mynotes.custom.checked.exception.RegistrationServiceClient.main(RegistrationServiceClient.java:9)
1.4 自定义运行时异常
要创建一个自定义的未经检查的异常,我们必须扩展java.lang.RuntimeException类。
public class DomainNotValidException extends RuntimeException {
public DomainNotValidException(String message) {
super(message);
}
}
public class RegistrationService {
public void validateEmail(String email) {
if (!isDomainValid(email)) {
throw new DomainNotValidException("Invalid domain");
}
}
private boolean isDomainValid(String email) {
List<String> validDomains = Arrays.asList("gmail.com", "yahoo.com", "outlook.com");
if (validDomains.contains(email.substring(email.indexOf("@") + 1))) {
return true;
}
return false;
}
}
public class RegistrationServiceClient {
public static void main(String[] args) {
RegistrationService service = new RegistrationService();
service.validateEmail("abc@gmail1.com");
}
}
结果
Exception in thread "main" mynotes.custom.unchecked.exception.DomainNotValidException: Invalid domain
at mynotes.custom.unchecked.exception.RegistrationService.validateEmail(RegistrationService.java:10)
at mynotes.custom.unchecked.exception.RegistrationServiceClient.main(RegistrationServiceClient.java:7)
1.5 最佳实践
- 遵循整个Java生态系统的通用命名约定-所有自定义异常类名称都应以“ Exception”结尾
- 如果来自JDK本身的标准异常可以达到目的,则避免进行自定义异常。 在大多数情况下,无需定义自定义异常。
- 优先于运行时异常,而不是检查异常。 像Spring这样的框架已经将所有检查到的异常包装为运行时异常,因此不会强迫客户端编写他们不需要或不需要的样板代码。
- 根据如何引发自定义异常,提供许多重载的构造函数。 如果将其用于抛出现有异常,则一定要提供一个设置原因的构造函数。