前言
空指针场景
包装类型字段,因为自动拆箱出现空指针; A对象包含B对象,通过A对象获取B对象字段时,没有判断就直接去调用B对象中的方法出现空指针; 字符串比较,null.equal("字符串")出现空指针 远程返回的List不是空数组而是null,对其进行操作出现空指针。
线上空指针问题如何排查
调用方法的入口进行入参的打印,方法返回的结果进行出参打印
Arthas启动后,获取来了JVM进程 通过watch指令来监测方法的入参情况
思考
@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<String> smallImgUrls;
@ApiModelProperty("店铺信息")
private ShopVO shop;
防御性检查,每一个变量都做一次null检查,每一次不确定一个变量是否为null时,都需要添加一个嵌套的if块,这增加了代码的层数。
// 获取店铺名称
private String getShopName1(ProductVO productVO){
if (productVO != null){
ShopVO shop = productVO.getShopVO();
if (shop != null){
return shop.getName();
}
}
return "";
}
快速失败检查,每一个null检查都是一个退出点,都返回一个固定的字符串,但是不能避免的是,忘记对某一个变量的检查。
// 获取店铺名称
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 调用链上的任何一个方法,返回一个空,那么结果就是我们设置的默认值
注意Stream中flatMap 与 Map 的区别
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
实践
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();
总结
事前,
一定要做好日志的打印工作,为了更方便的排查问题; 在实现业务逻辑的时候,如果你对你操作的对象不是很确定,那一定要先判空后操作; 针对于字符串类型的空指针我们可以采用Objects来做对比; 必填字段的入参校验
转自:小郭的技术笔记
来源:juejin.cn/post/7168020414138941471