一、什么是空指针异常?
空指针(Null Pointer Exception,NPE)是Java中最常见不过的异常了。其原因虽然显而易见,但是开发人员往往会忽略,或未能及时采取措施,常会导致程序崩溃或产生不可预测的行为。
二、空指针场景
防止NPE,是程序员的基本修养,注意NPE产生的场景:
返回类型为基本数据类型,return包装数据类型的对象时,自动拆箱有可能产生NPE;
反例 :public int method() { return Integer 对象; },如果为null,自动解箱抛NPE。
- 对象为空:
Object.getProperty(); 当Object为空时会抛NPE; - 集合里的元素为空,即便我们使用
CollectionUtils.isNotEmpty()判空,取出的数据元素也有可能为NULL; - 级联调用
user.getAddress().getCountry()级联调用为空时会抛空指针; - 常见的
RPC远程调用,返回的数据对象为空;
三、Optional概述
🎈Optional是Java 8中引入的一个类,用于描述一个值不存在的情况。它可以存储任意类型的值,或者表示一个空值。使用Optional类可以避免null值的传递和检查,提高代码的健壮性和可读性。
四、Optional常用属性
基本上的使用方式都是:
Optional.ofNullable(需要判断的对象).ifPresent(具体操作)
4.1 检查Optional是否有值
4.1.1 empty()
返回一个空的
Optional实例:Optional.empty。
4.1.2 of(T value)
创建一个包含非
null值的Optional对象。如果值为null,则会抛出NullPointerException异常。
4.1.3 ofNullable(T value)
创建一个包含指定值的
Optional对象。如果该值为null,则创建一个空的Optional对象。
可以使用静态方法of()或ofNullable()来创建一个非空的Optional对象,比如:
ini
tring value = "Hello World";
Optional<String> optionalStr = Optional.of(value);
System.out.println(optionalStr); // 输出Optional[Hello World]
String nullValue = null;
Optional<String> optionalNull = Optional.of(nullValue); // 抛出NullPointerException
4.1.4 get()
获取
Optional对象中的值。如果Optional对象为空,则会抛出NoSuchElementException异常。
4.1.5 isPresent()
判断
Optional对象是否有值,返回一个boolean类型值。
vbnet
Optional<String> optionalStr = Optional.of("Hello World");
if (optionalStr.isPresent()) {
System.out.println("optionalStr存在值:" + optionalStr.get());
} else {
System.out.println("optionalStr不存在值");
}
4.1.6 ifPresent(Consumer<? super T> consumer)
如果
Optional对象包含值,则对该值执行指定的操作。
4.2 Optional的链式调用
可以使用map()、flatMap()和filter()等方法进行链式调用,操作Optional的值。其中map()和flatMap()可以对Optional中的值进行映射或转换操作,filter()可以进行过滤操作。
4.2.1 map(Function<? super T,? extends U> mapper)
使用
map()方法可以对Optional对象中的值进行映射或转换操作,返回一个新的Optional对象。
4.2.2 flatMap(Function<? super T,Optional> mapper)
使用
flatMap()方法可以对Optional对象中的值进行映射或转换操作,返回一个新的Optional对象,与map()方法不同的是,flatMap()方法返回的是一个Optional对象,而map()方法返回的是一个包含Optional对象的Optional对象。
9. orElse(T other)
如果
Optional对象不包含值,则返回指定的默认值。
使用orElse()方法可以设置一个默认值,如果Optional对象中的值为空,则返回默认值。
ini
Optional<String> optionalStr = Optional.of("Hello World");
String value = optionalStr.orElse("Default Value");
System.out.println(value); // 输出Hello World
Optional<String> optionalNull = Optional.empty();
String nullValue = optionalNull.orElse("Default Value");
System.out.println(nullValue); // 输出默认值 Default Value
10. orElseGet(Supplier<? extends T> other)
如果
Optional对象不包含值,则返回由指定的Supplier生成的默认值。
使用orElseGet()方法可以设置一个Supplier接口实现,返回一个默认值,如果Optional对象中的值为空,则调用Supplier接口实现获取默认值,比如:
ini
Optional<String> optionalStr = Optional.of("Hello World");
String value = optionalStr.orElseGet(() -> "Default Value");
System.out.println(value); // 输出Hello World
Optional<String> optionalNull = Optional.empty();
String nullValue = optionalNull.orElseGet(() -> "Default Value");
System.out.println(nullValue); // 输出默认值 Default Value
11. orElseThrow(Supplier<? extends X> exceptionSupplier)
如果
Optional对象不包含值,则抛出由指定的Supplier产生的异常。
五、Optional的优点和应用场景
- 避免空指针异常 :使用
Optional类可以避免空指针异常,提高代码的健壮性。 - 优雅的代码处理 :使用
Optional类可以使代码更加简洁和优雅,减少代码的嵌套层级,提高代码可读性。 - 显式地指出变量可能为空 :使用
Optional类可以明确地指出一个变量可能为空,从而提醒程序员对此进行处理。 - 对集合的处理效率更高 :使用
Optional类对集合进行处理,可以避免遍历整个集合,从而提高处理效率。
下面我们看几个使用Optional类过滤集合中的null值的示例:
java
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class ListUtils {
public static List<String> filterNullValues(List<String> list) {
return list.stream()
.filter(str -> str != null)
.map(Optional::ofNullable)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
}
}
上述代码中,ListUtils.filterNullValues(List list)方法会过滤输入的List对象中的null值,并返回一个新的List对象。过程中使用了Java 8的Stream API和Optional类。
下面是对上述代码的解释:
- 通过
list.stream()方法将输入的List对象转换成一个Stream对象。 - 使用
filter()函数过滤掉值为null的元素。 - 使用
map()函数将不为null的元素包装成Optional对象。 - 使用
filter()函数过滤掉没有值的Optional对象。 - 使用
map()函数获取Optional对象中的值。 - 最后使用
collect()函数将过滤过后的元素收集起来,并返回一个新的List对象。
✔️使用Optional类可以避免null值的传递和检查,减少代码的嵌套层级,提高代码可读性。
再比如我们在获取rpc远程调用的结果时,可以基于Optional+Map函数做数据判空校验:
c
/**
* 远程调用响应处理工具类
*/
@Slf4j
@Component
public class RemoteResponseUtils {
/**
* 检查响应结果是否正常
*/
public static boolean checkResponse(BaseResponse respBody) {
return ofNullable(respBody)
.map(BaseResponse::getCode)
.map(code -> {
if (GlobalErrorCode.OK.getCode().equals(code)) {
return true;
} else {
log.warn("[rpc invoke] checkResponse respBody error:{}", respBody);
return false;
}
}).orElse(false);
}
/**
* 获取响应结果,如果为空,返回默认值
*/
public static <T> T getData(BaseResponse<T> baseResponse, T defaultValue) {
return ofNullable(baseResponse)
.filter(b -> {
if (log.isDebugEnabled()) {
log.debug("[rpc invoke]-RespBody-getData: {}", b);
}
if (ResponseCodeEnum.SUCCESS.getCode() == b.getCode()) {
return true;
} else {
log.warn("[rpc invoke] getData respBody error :{}", baseResponse);
return false;
}
}).map(BaseResponse::getData).orElse(defaultValue);
}
/**
* 获取响应结果,如果为空,返回null
*/
public static <T> T getData(BaseResponse<T> baseResponse) {
return ofNullable(baseResponse)
.filter(b -> {
if (log.isDebugEnabled()) {
og.debug("[rpc invoke] respBody-getData: {}", b);
}
if (ResponseCodeEnum.SUCCESS.getCode() == b.getCode()) {
return true;
} else {
log.warn("[rpc invoke] getData respBody error :{}", baseResponse);
return false;
}
}).map(BaseResponse::getData).orElse(null);
}
}
六、总结
6.1 Optional的总结
Optional是Java 8中引入的一个新特性,可以避免空指针异常,提高代码的可读性和健壮性,可以在集合处理、函数式编程、I/O操作等场景下发挥重要作用。使用Optional时需要注意值是否存在,避免出现NoSuchElementException异常。
6.2 Optional的发展趋势
随着函数式编程的流行和
Java 9、Java 10的发布,Optional类也会得到更多的应用和完善。在Java9中,Optional类新增了ifPresentOrElse()方法,可以在Optional对象为空时执行一个指定的操作。在Java10中,Optional类新增了or()和stream()方法、or()方法可以设置一个备选值,如果Optional对象中不存在值,则返回备选值;stream()方法可以将Optional对象转换为一个包含0或1个元素的Stream流。同时,在函数式编程中,对于空值的处理也越来越重要,Optional类的优势会得到更加充分的发挥。