在java开发中中NPE异常是常见异常,在阿里java开发手册中就明确强调防止NPE是调用者的责任是程序员基本修养,本篇博客先讲解NUll的知识点,随后讲解利用一些小技巧避免NPE
Null是什么
null是java中的关键字用来表示一些缺失的东西,null是所有引用类型的默认值,在这一部分强调几个关于null的知识点
1
null既不是对象也不是一种类型,它是一种特殊的值,你可以将null赋值任何引用类型,也可以将null强制转化为任何引用类型
如下
String str = (String) null;
强制转化过程编译器并不会报错
2
null的instanceof操作返回false
String str = null;
System.out.println(str instanceof String);
3
在java中null==null返回true
避免NPE
1.1equals方法
public void equals() {
//代表未知的对象,可能会是NULL也可能不是NULL
Object unKnowObject = null;
//这样某些情况下可能会抛出NULLPointException
System.out.println(unKnowObject.equals("str"));
//改成如下
//str一定不会空,所以不会抛出异常
System.out.println("str".equals(unKnowObject));
}
1.2valueOf与toString
利用String.valueOf避免NPE
public void toMyString() {
BigDecimal bigDecimal = null;
//避免如下写法,当对象为空时候会抛出异常
System.out.println(bigDecimal.toString());
//使用String静态方法
System.out.println(String.valueOf(bigDecimal));
}
1.3元素的list避免null
从数据库中检索数据如果结果集为空,利用0元素的list或者set来避免返回null
public void retrieveDataFromDB() {
//如果没有检索到数据,返回0元素的list,map,set而不是null
List result = Collections.EMPTY_LIST;
}
1.4拆箱与解包
下面这个例子是我们统计一个数字数组每个数字出现的次数
如下的写法是常见错误
public void unBox() {
int[] array = {20, 5, 7, 5, 9, 2};
final Map<Integer, Integer> numCount = new HashMap<>();
for (int v : array) {
int result = numCount.get(v);
numCount.put(v, ++result);
}
numCount.entrySet().stream().forEach(entry -> System.out.println(entry.getKey() + " -> " + entry.getValue()));
}
int result=numCount.get(v)取出的数据可能是null,而result是基本类型,对null进行拆箱自然就抛NPE异常了,所以get时需要进一步判断值是否是null
除了上面几种方法之外,我们还可以使用guava或者java8的Optional来避免NPE,下面主要讲解jdk中Optional使用
2.Optional
2.1初始化
初始化Optional有两种方法
1. of: of函数传入的参数不允许为null,否则抛出NullPointerException
2. ofNullable: ofNullable允许参数为空,创建一个不包含任何值得Optional值实例
Optional<String> name = Optional.of("wenruo");
Optional<String> nullableName = Optional.ofNullable(null);
2.2取数据
取数据时可以先isPresient判断是否有具体的数据,然后再get,不过get时候如果Optional没有数据就会抛异常
//isPresent函数有具体值得情况下返回true
if (name.isPresent()) {
System.out.println(name.get());
}
//如果对空类型调用get会抛出异常
System.out.println(nullableName.get());
或者利用lambda表达式在实例有值的时候进行操作
//实例有值的情况下
name.ifPresent(value -> System.out.println(value));
2.3在无值情况下指定值
orElse和orElseGet可以在无值的情况下指定值,orElse可以无值的情况下指定字符串默认值
而orElseGet可以利用Supplier接口生成默认值
System.out.println(nullableName.orElse("* empty *"));
System.out.println(nullableName.orElseGet(() -> {
int i = 5;
return "default value";
}));
2.4值不存在的情况下抛自定义异常
自定义异常
private class ValueAbsentException extends Throwable {
public ValueAbsentException() {
super();
}
public ValueAbsentException(String message) {
super(message);
}
}
自定义在值不存在的情况下抛出指定异常
try {
nullableName.orElseThrow(ValueAbsentException::new);
} catch (ValueAbsentException e) {
e.printStackTrace();
System.out.println("exception caught");
}
3.Optional扩展
3.1map
Optional还有其他的常用操作
//map可以返回任意类型
Optional<String> upperName = name.map(value -> value.toUpperCase());
System.out.println(upperName.orElse("no value found"));
3.2flatMap
//flatMap返回值必须是Optional类型
name = name.flatMap((value) -> Optional.of(value.toUpperCase()));
System.out.println(name.orElse("No Value Found"));
3.3filter
Optional<String> longName = name.filter(value -> value.length() > 10);
System.out.println(longName.orElse("the string length less than 10"));