概述
作者爱说话
Hello,大家好,我是 「行云」
这是原创的第 「11」 篇文章,希望今天这篇文章能带给你一点思考和启发
上一周接受了一个任务,排期比较赶,由于开发数据库的数据问题,在调试的过程中,在我认为业务规则下数据不应该为空的地方,抛出的 NPE ,简直折磨死个人,并且使用的是公司自研的框架,不支持热部署,模块还巨多,每改动一次都得重新打包再启动,所以这一期就聊一聊这个大家耳熟能详的 NPE 到底是怎么回事,看下你是不是真的了解 NPE 并且都能完美规避它了。
你遇到过 NPE 吗?
![75b0ca5317fe3254dedfc671a979b3e6.png](https://file2.kaopuke.com:8081/files_image/2023061020/75b0ca5317fe3254dedfc671a979b3e6.png)
空指针异常应该是开发者或多或少都接触过的 bug 之一
一不小心,就掉了这个坑里面,然后说声,对啊,忘记了,回到代码里,连忙加个判空操作
那么今天就来聊聊这个常见 NullPointerException 到底是个何方神圣
- 透过现象看本质,到底什么是 NPE?
- 就是不小心,哪些操作会导致 NPE ?
- 我该怎么做,如何避免该死的 NPE ?
何为 NPE
![8afca6a5e7f1eb7c1c80b9c10a34181f.png](https://file2.kaopuke.com:8081/files_image/2023061020/8afca6a5e7f1eb7c1c80b9c10a34181f.png)
空指针发生的原因是应用需要一个对象时却传入了 null,包含以下几种情况:
- 调用 null 对象的实例方法
- 访问或者修改 null 对象的属性
- 获取值为 null 的数组的长度
- 访问或者修改值为 null 的二维数组的列时
- 把 null 当做 Throwable 对象抛出时
道理我都懂,那么在实际 Coding 中,哪些操作会引发 NPE 呢?
该死的 NPE,你又出现了
翻开我们熟悉的《手册》
手册中说:防止 NPE,是程序员的基本修养,注意 NPE 产生的场景:
- 返回类型为基本数据类型,return 包装数据类型的对象时,自动拆箱有可能产生 NPE
- 数据库的查询结果可能为 null。
- 集合里的元素即使 isNotEmpty,取出的数据元素也可能为 null
- 远程调用返回对象时,一律要求进行空指针判断,防止 NPE
- 对于 Session 中获取的数据,建议进行 NPE 检查,避免空指针
- 级联调用 obj.getA().getB().getC();一连串调用,易产生 NPE
- Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用 equals
「接下来我们就看一些实际场景」
林步动同学接受了一个「包装类型的对象参数」a ,然后经过一系列花里胡哨的操作,然后进行了 「return 基本数据类型」,但是如果传入的 参数 a 是 null 的话,sorry,空指针将会出现在你的控制台
public int test(Integer a) {
// something code
return a;
}
林步动同学需要从数据库里查出一堆借据,然后看看是谁到底没还钱,「但是数据库的查询结果可能为 null」,一旦 loanReceiptDtoList 为 null,那么第一处就会出现 NPE
当然假设林步动同学查出了一些借据,可是,借据数据并不完整,一些借据数据没有客户名字,那么第二处就会报出 NPE
public void lookReceipt(Condition condition) {
List<LoanReceiptDto> loanReceiptDtoList = loanReceiptMapper.sqlect(condition);
for(LoanReceiptDto loanReceiptDto : loanReceiptDtoList) {
// 第一处
whoNoMoney(loanReceiptDto);
}
}
private void whoNoMoney(LoanReceiptDto loanReceiptDto) {
// 第二处
loanReceiptDto.getCustomerName();
}
林步动同接受小美同学传过来的 hashMap,但是他只简单判断了元素是否为 size 为0,但是假设小美传进来的是 ("k", null) ,取出来的值,依旧是 null
public void test(HashMap<String, String> hashMap) {
// 这个结果是 true
isNotEmpty(hashMap);
}
林步动同学想判断这个借据的客户名字是不是小美,这样写是对的吗? 当然不合适,正例应该是翻转equals的调用,小美应该作为方法 "equals()"的调用方,而不是参数。 你的调用方「应使用常量或确定有值的对象来调用equals」 当然 「JDK7 引入的工具类 java.util.Objects#equals(Object a, Object b)」 也是很香的
public void test() {
LoanReceiptDto loanReceiptDto = new LoanReceiptDto();
// 小美是魔法值,不建议哈,此处举例使用
if (loanReceiptDto.getCustomerName().equals("小美")) {
// something code
}
}
接下来分享一个花式踩坑例子
// 我们作为使用方调用如下的二方服务接口
public Boolean someRemoteCall();
// 然后自以为对方肯定会返回 TRUE 或 FALSE,然后直接拿来作为判断条件或者转为基本类型,如果返回的是 null,则会报空指针异常:
if (someRemoteCall()) {
// something code
}
大家看例子的时候,可能觉得很简单,但是在实际开发的时候,往往会忽略一些场景去思考,此处是否会 NPE,希望大家且 Code 且小心
如何避免 NPE
(为了说明真实可用性,都尽量从项目代码中截图展示)
「1,首推 使用 Optional」
Optional 是 Java 8 引入的特性,返回一个 Optional 则明确告诉使用者结果可能为空:
![cc534d9ba72494868c6aafbf91a9c4ce.png](https://file2.kaopuke.com:8081/files_image/2023061020/cc534d9ba72494868c6aafbf91a9c4ce.png)
「2,其次 学习《代码简洁之道》第 7.8 节 中“别传 null 值”」
也是最常见的方式之一,通过防御性参数检测,可以极大降低出错的概率,提高程序的健壮性
![db018e9f2d35cdd2015ded71a44be1b7.png](https://file2.kaopuke.com:8081/files_image/2023061020/db018e9f2d35cdd2015ded71a44be1b7.png)
「3,使用 lombok 的 @Nonnull 注解」
![6057dae338542b57be31d231f73e2f4a.png](https://file2.kaopuke.com:8081/files_image/2023061020/6057dae338542b57be31d231f73e2f4a.png)
「4, 返回空集合」
如果参数不合适直接返回空集合
![eebdcc8784ea2586babde298be698417.png](https://file2.kaopuke.com:8081/files_image/2023061020/eebdcc8784ea2586babde298be698417.png)
「5,使用 Objects」
可以使用 Java 7 引入的 Objects 类,来简化判空抛出空指针的代码。
![51c712739d3d32437c40270de343dbd9.png](https://file2.kaopuke.com:8081/files_image/2023061020/51c712739d3d32437c40270de343dbd9.png)
这边还可以使用一些工具包进行判断处理,不一一推荐了,希望大家可以关注 NPE 对程序健壮性带来的危害
我是 「行云」,点个赞再走吧,我们下期再见
最后
以上就是迷人小蚂蚁为你收集整理的判断是否是空对象_你被空指针折磨过吗?的全部内容,希望文章能够帮你解决判断是否是空对象_你被空指针折磨过吗?所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复