常见 Java 代码缺陷及规避方式

问题列表

空指针异常

NPE 或许是编程语言中最常见的问题,被 Null 的发明者托尼·霍尔(Tony Hoare)称之为十亿美元的错误。在 Java 中并没有内置的处理 Null 值的语法,但仍然存在一些相对优雅的方式能够帮助我们的规避 NPE。

  • 使用 JSR-305/jetbrain 等注解
  1. NotNull

  2. Nullable

图片

通过在方法参数、返回值、字段等位置显式标记值是否可能为 Null,配合代码检查工具,能够在编码阶段规避绝大部分的 NPE 问题,建议至少在常用方法或者对外 API 中使用该注解,能够对调用方提供显著的帮助。

  • 用 Optional 处理链式调用

Optional 源于 Guava 中的 Optional 类,后 Java 8 内置到 JDK 中。Optional 一般作为函数的返回值,强制提醒调用者返回值可能不存在,并且能够通过链式调用优雅的处理空值。

public class OptionalExample {
  
    public static void main(String[] args) {
          // 使用传统空值处理方式        User user = getUser();        String city = "DEFAULT";        if (user != null && user.isValid()) {
              Address address = user.getAddress();            if (adress != null) {
                  city = adress.getCity();            }        }        System.out.println(city);
        // 使用 Optional 的方式        Optional<User> optional = getUserOptional();        city = optional.filter(User::isValid)                .map(User::getAddress)            .map(Adress::getCity)              .orElse("DEFAULT")        System.out.println(city);    }
    @Nullable    public static User getUser() {
          return null;    }
    public static Optional<User> getUserOptional() {
          return Optional.empty();    }
    @Data    public static class User {
          private Adress address;        private boolean valid;    }
    @Data    public static class Address {
          private String city;    }}
  • 用 Objects.equals(a,b) 代替 a.equals(b)

equals方法是 NPE 的高发地点,用 Objects.euqals来比较两个对象,能够避免任意对象为 null 时的 NPE。

  • 使用空对象模式

空对像模式通过一个特殊对象代替不存在的情况,代表对象不存在时的默认行为模式。常见例子:

用 Empty List 代替 null,EmptyList 能够正常遍历:

public class EmptyListExample {
  
    public static void main(String[] args) {
          List<String> listNullable = getListNullable();        if (listNullable != null) {
              for (String s : listNullable) {
                  System.out.println(s);            }        }
        List<String> listNotNull = getListNotNull();        for (String s : listNotNull) {
              System.out.println(s);        }    }
    @Nullable    public static List<String> getListNullable() {
          return null;    }
    @NotNull    public static List<String> getListNotNull() {
          return Collections.emptyList();    }}

空策略

public class NullStrategyExample {
  
    private static final Map<String, Strategy> strategyMap = new HashMap<>();
    public static void handle(String strategy, String content) {
          findStrategy(strategy).handle(content);    }
    @NotNull    private static Strategy findStrategy(String strategyKey) {
          return strategyMap.getOrDefault(strategyKey, new DoNothing());    }
    public interface Strategy {
          void handle(String s);    }
    // 当找不到对应策略时, 什么也不做    public static class DoNothing implements Strategy {
          @Override        public void handle(String s) {
  
        }    }}

对象转化

在业务应用中,我们的代码结构往往是多层次的,不同层次之间经常涉及到对象的转化,虽然很简单,但实际上繁琐且容易出错。

反例 1:

public class UserConverter {
  
    public static UserDTO toDTO(UserDO userDO) {
          UserDTO userDTO = new UserDTO();        userDTO.setAge(userDO.getAge());        // 问题 1: 自己赋值给自己        userDTO.setName(userDTO.getName());        return userDTO;    }
    @Data    public static class UserDO {
          private String name;        private Integer age;        // 问题 2: 新增字段未赋值        private String address;    }
    @Data    public static class UserDTO {
          private String name;        private Integer age;    }}

反例2:

public class UserBeanCopyConvert {
  
    public UserDTO toDTO(UserDO userDO) {
          UserDTO userDTO = new UserDTO();        // 用反射复制不同类型对象.        // 1. 重构不友好, 当我要删除或修改 UserDO 的字段时, 无法得知该字段是否通过反射被其他字段依赖        BeanUtils.copyProperties(userDO, userDTO);        return userDTO;    }}
  • 使用 Mapstruct

Mapstruct 使用编译期代码生成技术,根据注解, 入参,出参自动生成转化,代码,并且支持各种高级特性,比如:

  1. 未映射字段的处理策略,在编译期发现映射问题;

  2. 复用工具,方便字段类型转化;

  3. 生成 spring Component 注解,通过 spring 管理;

  4. 等等其他特性;

@Mapper(    componentModel = "spring",    unmappedSourcePolicy = ReportingPolicy.ERROR,    unmappedTargetPolicy = ReportingPolicy.ERROR,</
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值