预防优于治疗:用预检查规避 Java 中的运行时异常

334 阅读3分钟

引言

扁鹊的大哥擅长在疾病尚未形成之前就通过望闻问切诊断隐患并铲除病因,这是“事前控制”,是最理想的医疗模式。这种模式体现了“防患于未然”的重要性,即通过早期干预,避免疾病的发生和发展。在现代医学中,这类似于健康管理和疾病预防的理念,如定期体检、健康教育和生活方式干预等,能够有效减少疾病的发生率。

在开发过程中,我们也是期望在问题发生之前就已经发现并防治OK,下面我们来看看如何防治一些运行时异常。

一、有理可依

image.png

上图是《Java开发手册(黄山版)》的截图。

实际在 Java 开发中,RuntimeException 是一种常见的运行时异常,例如 NullPointerExceptionIndexOutOfBoundsException 等。这些异常通常是因为代码中存在潜在的逻辑错误或未进行充分的检查。通过预检查的方式,可以在执行操作之前验证条件,从而避免一些异常的发生。下面我将介绍一些常见的预检查方法和最佳实践。

二、 常见预检类型

2.1 检查空值(避免 NullPointerException)

要点:在访问对象的属性或调用方法之前,检查对象是否为 null

这是非常普遍的一种预检行为,常见的地方是接口参数收录之前,数据库或者缓存查询结果使用之前都会做这些检查。

  • 常见的空值预检如下:
public User getUser(String id) {
    if (id == null) {
        return null;
    }
    return mapper.selectByPrimaryKey(id);
}
  • 下面这种也是空值预检:

image.png image.png

  • 使用Objects.requireNonNull

注意:上面两种都是不会抛出空指针异常的,但是Objects.requireNonNull它是会抛出空指针异常的,只是自定义了异常信息,方便我们快速定位问题。

import java.util.Objects;

public class Demo {
    public static void main(String[] args) throws Exception {
        String name = null;
        String username = Objects.requireNonNull(name, "name为空呢");
    }
}

image.png

  • 使用Optional

Optional 是 Java 8 引入的一个类,用于优雅地处理可能为 null 的值。

public String getUserName(User user) {
    return Optional.ofNullable(user)
            .map(User::getName)
            .orElseThrow(() -> new IllegalArgumentException("User or user name cannot be null"));
}

2.2 检查集合或数组的索引(避免 IndexOutOfBoundsException

要点:在访问集合或数组的元素之前,检查索引是否在有效范围内。

image.png

  • 使用 List 的 get 方法时,先检查大小。
public String getElement(List<String> list, int index) {
    if (list == null) {
        return null;
    }
    if (index < 0 || index >= list.size()) {
        return null;
    }
    return list.get(index);
}

2.3 检查输入参数的有效性

要点:在方法中对输入参数进行预检查,确保它们符合预期的条件。

下面是一些常见的参数有效性校验注解:

image.png

当然一些复杂的参数校验不是一个简单的注解就能完成的,这个时候我们就需要更多的代码来完成。

2.4 使用断言(Assertion)

开发阶段,可以使用断言来检查条件是否满足。如果条件不满足,程序将抛出 AssertionError

public void processUser(User user) {
    assert user != null : "User cannot be null";
    assert user.getName() != null : "User name cannot be null";
    // 进一步处理
}

特别注意,开发阶段,生产环境不建议这么弄。

三、更多的RuntimeException

实际Java里面,继承自RuntimeException的类非常非常多,这里就不多介绍了,感兴趣可以看看。

image.png

四、总结

预检查好比“防火墙”,它在阻止运行时异常传播、保护程序稳定运行方面起到重要作用,预检查能够有效发现并处理潜在的异常问题;增加程序的健壮性。

特别注意,别把东西全部catch起来,这种操作是很不负责任的态度。

最后:如果你从本篇文章中学到一点点东西,麻烦发财的小手点一下赞,如有疑问或者错误,欢迎提出和指正。