文章目录
- NPE
- 如何防止 NPE
- 如何优化判空 ——Optional
- 实例化
- isPresent & get
- ifPresent
- filter
- orElse & orElseThrow
- map & flatMap
- 重构
NPE
NullPointerException(NPE):空指针异常。
先抛出我理解的为何会发生异常:
当空对象尝试调用方法的时候,就会发生 NPE
public class ForCheckAnyObject {
public static void main(String args[]) {
//初始化一个空的 Map 对象
Map<String, Object> map = new HashMap<>();
String a = (String) map.get("a");
System.out.println(a);
//这里为了演示空对象调用方法,随便用了 String 的一个方法
String A = a.toLowerCase();
System.out.println(A);
}
}
null
Exception in thread "main" java.lang.NullPointerException
at testAll.ForCheckAnyObject.main(ForCheckAnyObject.java:35)
//其中的第 35 行,就是 String A = a.toLowerCase();
Java 是面对对象语言,我们要调用一个类中的方法,必须要对其进行实例化,用这个类的对象去调用这个方法。当这个对象为空的时候,而我们又尝试去调用这个对象所能调用的方法,那么就会报 NPE
如何防止 NPE
我以前(JDK8以前)写代码的时候,拿到一个对象,先去做判空操作(这当然是在几十次的教训下,形成的习惯):
if(a!=null){
//操作
}
要知道,我们写业务代码,获得数据库返回的数据,我们需要某一个字段,很可能有几个层级才能取到(而像上面这样的,当然很好,判断一下就可以了)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-npuxRmVQ-1578572765914)(./imageAll/多层对象取值.png)]
按照我们刚开始写代码的性格,一定是下面这样:只会考虑到都有值的情况。
//当然这里的 Class 是班级的意思
University.getColleage().getClass().getName();
当然这样遇到有空值的时候,就会报 NPE 了。
所以我们会进行改进,在报错之前进行判断,不会出错后直接中断程序:
University.getColleage().getClass().getName();
if(University!=null){
Colleage colleage=University.getColleage();
if(colleage!=null){
Class class=colleage.getClass();
if(class!=null){
String name=class.getName();
}
}
}
上面的代码,安全性来讲是没啥问题了,但是代码很臃肿,不易阅读。
如何优化判空 ——Optional
JDK8 引入了 Optional 类, 依靠 Optional 类提供 API,我们可以写出既安全又具有阅读性的代码。
实例化
在 Java 中我们像用一个类当中的方法,那么我们首先需要对其进行实例化:
- new,因为 Optional 类中的两个构造函数都是 private 权限,所以不能使用 new 关键字初始化对象
如何实例化对象:
- Optional.of(obj);
- Optional.ofNullable(obj)
- Optional.empty()
//JDK8 中 Optional#ofNullable 中的源码
public static <T> Optional<T> ofNullable(T value)
{
return value == null ? empty() : of(value);
}
对,就是看到的这样,ofNullable 会分别调用 of 和 empty 方法。
当不确定对象是否为空,就使用 ofNullable ,因为使用 of 会报空指针。
其实看到这里,在日常的开发中,想要实例化 Optional 就可以直接 ofNullable。忘掉其他两个方法都可以
isPresent & get
我们会用 isPresent 是否为 null,用 get 方法取获取里面的值
List<Map<String,Object>> list=new ArrayList<>();
Map<String,Object> map=new HashMap<>();
map.put("a","12");
map.put("b","34");
list.add(map);
Optional<List<Map<String,Object>>> optionalMapList=Optional.ofNullable(list);
if(optionalMapList.isPresent()){
System.out.println(optionalMapList.get());
}
//[{a=12, b=34}]
//是判断 null 的,而不是判断空对象的|注意 null 和空对象的区别
public boolean isPresent() {
return value != null;
}
这里的判空好像和原来直接判空并没有什么太大的区别,还要去加入新的类,反而有点麻烦了。
下面接着介绍(会涉及Lambda和Stream API):
ifPresent
注意上面是 isPresent(判空),这从命名上也很好理解,要不然怎么说我们在开发的时候,方法、类等命名也很重要呢。
List<Map<String,Object>> list=new ArrayList<>();
Map<String,Object> map=new HashMap<>();
map.put("a","12");
map.put("b","34");
list.add(map);
Optional<List<Map<String,Object>>> optionalMapList=Optional.ofNullable(list);
optionalMapList.ifPresent(System.out::println);
这里涉及到了 Java8 新的特性 Lambda 的方法引用。
使用 ifPresent 我们不用显式的进行检查,如果 list 为空,那么就不会输出。
filter
当我们需要满足一定条件的才执行操作的时候:
List<Map<String, Object>> list = new ArrayList<>();
Map<String, Object> map = new HashMap<>();
map.put("a", "12");
map.put("b", "34");
list.add(map);
System.out.println(list);
Optional<List<Map<String, Object>>> optionalMapList = Optional.ofNullable(list);
optionalMapList.filter(a->"12".equals(a.get(1).get("a"))).
ifPresent(a-> System.out.println("AAAA"));
filter
方法将会判断对象是否符合条件。如果不符合条件,将会返回一个空的Optional
。
orElse & orElseThrow
当一个对象为 null 时,业务上通常可以设置一个默认值或者抛出一个内部异常,记录失败原因,快速失败.从而使流程继续下去。
//设置一个默认值
String a = list.get(0) != null ? (String) list.get(0).get("a") : "unknown";
//抛出一个内部异常
if( list.get(0).get("a")==null){
throw new RuntimeException();
}
list=null;
Optional<List<Map<String, Object>>> optionalMapList = Optional.ofNullable(list);
//设置默认值
List a1=optionalMapList.orElse(new ArrayList(Collections.singleton("unknown")));
System.out.println(a1.toString());
//抛出一个内部异常
String a2= (String)optionalMapList.orElseThrow(RuntimeException::new).get(0).get("a");
System.out.println(a2);
map & flatMap
熟悉 Java8 Stream 同学的应该了解,Stream#map
方法可以将当前对象转化为另外一个对象, Optional#map
方法也与之类似。
map 方法可以将原先 Optional<List<Map<String, Object>>> 转变成 Optional,此时 Optional 内部对象变成 Object类型。如果转化之前
Optional
对象为空,则什么也不会发生。
List<Map<String, Object>> list = new ArrayList<>();
Map<String, Object> map = new HashMap<>();
map.put("a", "12");
map.put("b", "34");
list.add(map);
Optional<List<Map<String, Object>>> optionalMapList = Optional.ofNullable(list);
Optional<Object> s= optionalMapList.map(m->m.get(0).get("a"));
System.out.println(s.toString());
//Optional[12]
Optional<Object> s = optionalMapList.flatMap(e->Optional.ofNullable(e.get(0).get("a")));
System.out.println(s.toString());
//Optional[12]
上面两种:map、flatMap 的输出结果一样。
flatMap 的参数是 Optional类型,需要自己再次封装一下,map 方法,则直接使用即可。
重构
以后判空条件过多,那么就尝试着使用 Optional 类去重写它,还能顺带学习一波 Lambda 就不亏