Java避坑记录

2023年 8月 1日 27.5k 0

1.常见问题避坑记录

1.1 为什么写这个常见问题避坑记录?

看到公司同事的代码经常写的不规范,出现比较多低级错误,所以写这个文章说明常见的各种问题,后续将持续完善该文章,不断积累和记录,同时提高自身代码规范。

1.2 NPE问题

1.2.1 非正确使用对象以及属性

Person person = personMapper.selectById(123L);
if (!person.getUserName().isEmpty()) {
    // doSomething...
}

上述代码中想实现的效果是,查询id为123的person信息,然后判断如果userName不为空,就进行逻辑的处理,但是通过观察代码,可以直接发现2处错误

  • 没有处理person为null的情况
  • 没有处理person.getUserName()为null的情况
  • 为什么会这么说呢,来依次验证一下,首先是数据库里是没有准备id为123的person信息,所以这里肯定是查不到信息的,开始测试:

  • 验证情况1,不处理person为null的情况,直接进行使用
  • Person person = personMapper.selectById(123L);
    String userName = person.getUserName();
    

    运行结果
    image.png

    image.png

    原因分析

    可以看到上述代码在查询不到person信息的时候,直接调用person.getUserName()确实出现了NPE,因为person查不到就代表person == null,null.getUserName()肯定就NPE了

  • 验证情况2,查询到person信息之后,不正确的使用person上的属性,例如person.getUserName()
    我们把上面的代码稍微改改,对person进行处理之后再用它的属性,这里我提前准备好了一条id为1685291564680609793的数据,这条数据的userName为null,开始测试:
  • Person person = personMapper.selectById(1685291564680609793L);
    if (Objects.nonNull(person) && !person.getUserName().isEmpty()) {
        // doSomething...
    }
    

    运行结果

    image.png

    image.png

    原因分析

    上述代码的本意是在userName不为空的时候进行逻辑处理,但是判断的方式却出现了错误。如果person.getUserName()为null,那么person.getUserName().isEmpty()就等价于null.isEmpty(),因此就会出现NPE

    image.png

    image.png

    那么我们应该如何正确判断字符串为空呢,我们可以使用别人造好的轮子,直接引入这个依赖

    
        org.apache.commons
        commons-lang3
        3.9
    
    

    然后我们就可以使用StringUtils.isNotEmpty()方法来帮我们正确的判断字符串的为空
    稍微修改一下上面的代码:

    Person person = personMapper.selectById(1685291564680609793L);
    if (Objects.nonNull(person) && StringUtils.isNotEmpty(person.getUserName())) {
        // doSomething...
    }
    

    这样我们就可以正确的判断字符串为空了

    补充说明

    StringUtils.isEmpty()和StringUtils.isBlank()的作用不一样,前者不允许字符串中有空格,只判断null和length,后者允许存在空格

    1.2.2 非正确使用集合进行查询

    有的时候我们需要通过服务调用或者方法传递过来的参数进行查询,假设下面的nameList的来源是外部,不能确定传进来的是什么,可能是null、可能是[]、也可能是正常的list,那么我们直接调用下面的方法就有可能出现问题,如果nameList为null的时候就会出现NPE

    List nameList = null;  
    List people = personMapper.selectList(Wrappers.lambdaQuery().in(Person::getUserName, nameList));
    

    运行结果

    org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.builder.BuilderException: Error evaluating expression 'ew.sqlSegment != null and ew.sqlSegment != '' and ew.nonEmptyOfNormal'. Cause: org.apache.ibatis.ognl.OgnlException: sqlSegment [java.lang.NullPointerException]
    

    原因分析

    nameList为null,条件为in的时候传给mybatis一个null会导致NPE。那么应该如何解决呢?需要结合具体的场景,可以在service层就处理nameList,保证调用到这里的时候nameList不为null,或者如果一定要在这里调用的话,可以在in前面加上条件,修改之后的代码如下,这样就不会NPE

    List nameList = null;
    List people = personMapper.selectList(Wrappers.lambdaQuery()
            .in(!CollectionUtils.isEmpty(nameList), Person::getUserName, nameList));
    

    1.2.3 equals的不正确使用

    在方法中,直接用入参调用equals,这样如果key为null的时候就会出现NPE

    public void testMethod(Integer key) {
        if (key.equals(1)) {
            // doSomething...
        }
    }
    

    改进如下:

  • 把已知常量作为equals的调用方
  • if (1.equals(key)) {
        // doSomething...
    }
    
  • 使用Objects.equals方法
  • if (Objects.equals(1, key)) {
        // doSomething...
    }
    

    1.3 "多此一举"问题

    1.3.1 mybatis查询集合之后做多余的判断

    List list = personMapper.selectList(Wrappers.lambdaQuery().eq(Person::getId, 123L));  
    System.out.println("list = " + list);  
    if (list != null && list.isEmpty()) {  
    // doSomething ...  
    }
    

    查看控制台输出的list

    JDBC Connection [HikariProxyConnection@1703458581 wrapping com.mysql.cj.jdbc.ConnectionImpl@15a3b42] will not be managed by Spring
    ==> Preparing: SELECT id,user_name,sex,age,deleted,version,create_time,update_time FROM person WHERE deleted=0 AND (id = ?)
    ==> Parameters: 123(Long)

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论