一、什么是空指针异常
空指针异常(NullPointerException)是一种在Java等编程语言中常见的异常类型,它发生在尝试调用一个空(null)对象的属性或方法时。
空指针异常通常发生在以下两种情况:
- 有一个对象没有分配空间,即它是一个空对象,这可能是因为一个对象类型的属性没有直接初始化,或者在定义了一个对象类型的变量后,赋值为null。
- 尝试直接调用该空对象的属性或方法,例如,尝试访问一个null值的字符串的charAt方法,或者调用一个null值的List的add方法。
在Java中,空指针异常通常会导致程序崩溃,因此通常需要避免这种情况的发生。
二、如何避免空指针异常
下面是举一个例子,假设userList是存在空对象的
public static List<User> getOldMan() {
List<Long> ids = Collections.singletonList(1000L);
List<User> userList = getUser(ids);
List<User> resultList = new ArrayList<>(10);
for (User user : userList) {
if (null != user){//直接加判空
Long id = user.getId();
String name = user.getName();
int age = user.getAge();
SexyEnum sexy = user.getSex();
//找出性别是男性的,40岁以上的人员
if (age > 40 && sexy.getValue().equals(SexyEnum.MAN.getSexy())) {
resultList.add(user);
}
}
}
return resultList;
}
1.最直接的方式,if判断
常规的做法是直接判空
for (User user : userList) {
if (null != user){//直接加判空
Long id = user.getId();
String name = user.getName();
int age = user.getAge();
SexyEnum sexy = user.getSex();
//找出性别是男性的,40岁以上的人员
if (age > 40 && sexy.getValue().equals(SexyEnum.MAN.getSexy())) {
resultList.add(user);
}
}
}
或者continue,减少一些代码层级结构
for (User user : userList) {
if (null == user) {//使用continue减少代码层级结构
continue;
}
Long id = user.getId();
String name = user.getName();
int age = user.getAge();
SexyEnum sexy = user.getSex();
//找出性别是男性的,40岁以上的人员
if (age > 40 && sexy.getValue().equals(SexyEnum.MAN.getSexy())) {
resultList.add(user);
}
}
2.其他更优雅的判空方式
假如userList也可能为空,那么代码将变得很臃肿
public static List<User> getOldMan() {
//假如userList也可能是空,那么我们的程序就显得很臃肿
List<Long> ids = Collections.singletonList(1000L);
List<User> userList = getUser(ids);
if(null == userList){
return Collections.emptyList();
}
List<User> resultList = new ArrayList<>(10);
for (User user : userList) {
if (null == user) {//使用continue减少代码层级结构
continue;
}
Long id = user.getId();
String name = user.getName();
int age = user.getAge();
SexyEnum sexy = user.getSex();
//找出性别是男性的,40岁以上的人员
if (age > 40 && sexy.getValue().equals(SexyEnum.MAN.getSexy())) {
resultList.add(user);
}
}
return resultList;
}
这里建议使用JDK8的Optional、stream流,代码会更加简洁优雅
public static List<User> getOldMan() {
List<Long> ids = Collections.singletonList(1000L);
List<User> userList = getUser(ids);
return Optional.ofNullable(userList).orElseGet(Collections::emptyList).stream().filter(Objects::nonNull)
.filter(user -> user.getAge() > 40 && user.getSex().getValue().equals(SexyEnum.MAN.getSexy())).collect(Collectors.toList());
}
关于JDK8的其他用法,我在另一篇博客有介绍,感兴趣的可以去看
3.接口的入参都做考虑加判空
对于提供给外部的接口,亦或者是本类的私有方法,只要是入参,养成一个习惯。
第一时间检查入参是否有可能是空的,并做容错处理,或直接返回,给出提示或记录日志。
4.利用IDEA的sonar扫描工具
IDEA plugins安装sonarLint扫描工具,可以扫描出当前类明显的空指针问题
5.初始化对象尽量不赋予空值
比如方法返回集合时,使用Collections.emptyList()返回空集合,那么在调用方使用的时候可以避免很多无意义的空判断和使用问题
public static List<User> getOldMan() {
List<Long> ids = Collections.singletonList(1000L);
List<User> userList = getUser(ids);
if(null == userList){
//返回空集合
return Collections.emptyList();
}
//....
6.写接口单元测试
有时候在一些业务场景中,参数值一般都是有的,只有在一些特定的业务场景才会暴露出问题。如果我们有意的传入空值或者一些边界值,测试方法,很多问题往往能在开发阶段提早发现。
=========================================================================创作不易,请勿直接盗用,使用请标明转载出处。
喜欢的话,一键三连,您的支持是我一直坚持高质量创作的原动力。