大家好,我是小郭。
在平时的业务开发中,空指针是我们经常遇到的问题,
他可能会导致我们的流程无法正常进行或者一些意外情况的发生。
这就是我们需要避免空指针的原因,那我们有哪些方式去解决这个问题呢?
日常的开发过程中,一般情况下我们都是通过查看日志来排查空指针的问题,如果日志没有做到位的情况下,我们只能通过NullPointerException抛出的位置去跟踪代码。
这就要求我们,在写代码的时候做好日志的打印
如果没有提前做好日志打印,那我们可以考虑利用阿里的Java诊断工具Arthas来处理
对于这个问题,我总结了一些我在工作中使用到的方法,
最直接的操作都是从根源上消灭出现空指针的可能性,进行先判空再操作。
下面拿商品信息作为一个例子,我们要得到他店铺的名称,你会怎么写
- @Data
- public class ProductVO implements Serializable {
- @ApiModelProperty("skuId")
- private Long skuId;
- @ApiModelProperty("商品名称")
- private String name;
- @ApiModelProperty("品牌名")
- private String brandName;
- @ApiModelProperty("库存")
- private Integer quantity;
- @ApiModelProperty("小图列表")
- private List
smallImgUrls; - @ApiModelProperty("店铺信息")
- private ShopVO shop;
- 复制代码
- // 获取店铺名称
- private String getShopName1(ProductVO productVO){
- if (productVO != null){
- ShopVO shop = productVO.getShopVO();
- if (shop != null){
- return shop.getName();
- }
- }
- return "";
- }
- 复制代码
- // 获取店铺名称
- private String getShopName2(ProductVO productVO){
- String result = "";
- if (Objects.isNull(productVO)){
- return result;
- }
- ShopVO shop = productVO.getShopVO();
- if (Objects.isNull(shop)){
- return result;
- }
- return shop.getName();
- }
- 复制代码
人为控制,对数据进行严格的控制,不能存在非空字段,但是不能很难保证所有数据都是正常的数据
利用Java8中的optional来做控制,对缺失的值建模,变量存在时对类进行简单的封装,不存在时,缺失的值会被建模成一个空的Optional对象
- private static String getName4(ProductVO productVO){
- return Optional.ofNullable(productVO).flatMap(data -> Optional.ofNullable(data.getShopVO()))
- .map(ShopVO::getName).orElse("");
- }
- 复制代码
创建一个Optional封装的ProductVO对象
将Optional转换为Optional
利用map,将Optianal转换为Optional
调用链上的任何一个方法,返回一个空,那么结果就是我们设置的默认值

上面的几种方法中,我们看到了第四种方法,只用了一行代码就帮我们实现了消灭空指针的动作,但是Java 8中的 Optional 需要结合多个的方法来使用他,现在网上已经有很多详细方法文章,就不再做过多的介绍。
对于Stream流的运算中,flatMap 与 Map是我们常使用到的方法,很多人都没有搞清楚他们的差别是什么。
map(): map对列表中的每个元素应用一个函数,返回应用后的元素所组成的列表。
flatMap(): flatMap是一种常用的组合子,结合映射[mapping]和扁平化[flattening]。 flatMap需要一个处理嵌套列表的函数,然后将结果串连起来。
举个例子,一眼看出他们的区别
- List<String> list = Arrays.asList("北京 天安门", "上海 东方明珠", "厦门 鼓浪屿");
- //flatMap方法
- list.stream().flatMap(item -> Arrays.stream(item.split(" "))).collect(Collectors.toList()).forEach(System.out::println);
- //结果: 北京 天安门 上海 东方明珠 厦门 鼓浪屿
- // Map方法
- list.stream().map(item -> Stream.of(item.split(" "))).forEach(System.out::println);
- // java.util.stream.ReferencePipeline$Head@6576fe71
- // java.util.stream.ReferencePipeline$Head@76fb509a
- // java.util.stream.ReferencePipeline$Head@300ffa5d
- 复制代码
我们可以看到他们结果差异是非常大的
Map方法将list转换为了三个小的List对象的结果集
FlatMap方法的操作就是比Map方法的基础上,多做了一个扁平化[flattening]操作,将结果转化成一级结构,将里面的结果都取出来。
- List<ProductRespDTO> list = productVOList.getList().stream().map(d -> {
- return ProductRespDTO.builder().drugEncode(d.getSkuId()).drugName(d.getName())
- .price(BigDecimal.valueOf(d.getPrice()).divide(new BigDecimal("100"))).usage(d.getUseMethod())
- .imgUrl(d.getSmallImgUrls().stream().findFirst().orElse(null)).build();
- 复制代码
这个例子中是否会存在空指针问题呢,如果是你你会怎么去修改?
对于空指针问题,看起来问题不大,但是影响到了线上的业务正常运转,那肯定是不行的。
我们一定要有很清晰的思路去解决这个问题
事前,
事中,事情既然已经发生了,那需要我们快速的通过日志和Arthas工具来定位问题,快速修复上线减少故障发生的时间:
事后,我们可以加强code review来审查自己的代码,避免这类情况的再次发生。
大家可以分享一下,自己平时遇到空指针是如何处理的~
