可检查的异常
可检查的异常Checked Exception, 所有的可检查的异常都是需要在代码中处理的, 它们的发生是可预测, 可以合理处理的. 比如IOExeption, 或者一些自定义的异常. 除了RuntimeException及其子类以外, 都是checked exception.
可检查的异常处理
- 捕获对应的异常, 通过日志logger打印异常信息.
- 捕获对应的异常, 返回和业务相关的提示信息.
- 如果不想处理它, 请将该异常抛给它的调用者, 最外层的业务使用者, 必须处理异常, 将其转换为用户可以理解的内容.
不可检查的异常
Java类库中定义的一类RuntimeException可以通过预先检查进行规避, 而不应该通过catch来处理, 比如IndexOutOfBoundsException, NullPointerException等等.
无法通过预检查的异常除外, 如在解析一个外部传来的字符串形式数字时, 通过catch NumberFormatException来实现.
NPE的产生
- 返回类型为基本类型数据, return 包装数据类型的对象时, 自动拆箱有可能产生NPE.
public int f() { return Integer 对象 }, 如果为null, 自动解箱抛NPE.
- 数据库的查询结果可能为null.
- 集合里的元素即使isNotEmpty, 取出的数据元素也可能为null.
- 远程调用返回对象时, 一律要求进行空指针判断, 防止NPE.
- 对于Session 中获取的数据, 建议NPE检查, 避免空指针.
规避NPE
- 人们常常使用null值表示值不存在, Optional对象能更好地表达这个概念. 使用null代表值不存在的最大问题在于NullPointerException. 一旦引用一个存储null值的变量, 程序会立即崩溃.
- 规避NPE是调用者的责任. 即使被调用方法返回空集合或者空对象, 对调用者来说, 也并非高枕无忧, 必须考虑到远程调用失败, 序列化失败, 运行时异常等场景返回null 的情况.
JAVA8的Optional类
Optional主要用作返回类型. 在获取到这个实例后, 如果他有值, 你可以取得这个值, 否则可以进行一些替代行为.
Optional是Java语言的有益补充, 它旨在减少代码中的NPE, 虽然还不能完全消除这些异常.
// 利用Optional.isPresent()进行判空操作
@Test
public void whenCheckIfPresent_thenOk() {
HelloWorld helloWorld = new HelloWorld(1, "HelloWorld");
Optional<HelloWorld> opt = Optional.ofNullable(helloWorld);
assertTrue(opt.isPresent());
assertEquals(helloWorld.getHelloWorld(), opt.get().getHelloWorld());
}
// 在参数为null的情况下, 利用Optional.orElse方法提供默认值
@Test
public void whenEmptyValue_thenReturnDefault() {
HelloWorld helloWorld = null;
HelloWorld helloWorld1 = new HelloWorld(1, "HelloWorld");
HelloWorld result = Optional.ofNullable(helloWorld).orElse(helloWorld1);
assertEquals(helloWorld1.getHelloWorld(), result.getHelloWorld());
}
// 可以决定抛出什么异常, 而不总是抛出NPE
@Test(expected = IllegalArgumentException.class)
public void whenThrowException_thenOk() {
HelloWorld helloWorld = null;
HelloWorld helloWorld1 = Optional.ofNullable(helloWorld)
.orElseThrow(() -> new IllegalArgumentException());
}
预先检查
if (obj != null) {...}
Note
- 定义时区分unchecked/ checked 异常, 避面直接抛出new RuntimeException(), 更不允许抛出Exception 或者 Throwable, 应使用有业务含义的自定义异常.推荐业界已定义过的自定义异常, 如: DAOException / ServiceException等.
- 在代码中使用"抛异常"还是返回"错误码", 对于公司外的http/api开放接口必须使用错误码; 而应用内部推荐异常抛出; 跨应用间RPC调用优先考虑使用Result方式, 封装isSuccess()方法, “错误码”, “错误简短信息”.
- 采用日志log打印异常信息, 在生产环境中别用异常的printStackTrace()方法, printStackTrace默认会把调用的堆栈打印到控制台上, 在生产环境中访问控制台是不现实的.