美文网首页
Java JDK 常见基础类存在的一些坑

Java JDK 常见基础类存在的一些坑

作者: wuchao226 | 来源:发表于2021-03-17 15:36 被阅读0次

    1、String.valueOf() 方法的陷阱

    String.valueOf() 是 String 提供的一个类型转换的方法。

    // 调用用户服务根据用户id获取用户信息
    Map<String, Object> userInfo = userService.getUserInfoById(userId);
    Object userNameObject = userInfo.get("name");
    String userName = String.valueOf(userNameObject);
    // 判空
    if(userName!=null&&userName.length()>0) {
        String message = getMessage(userName);
        smsService.send(message);
    }
    

    这段代码会出现发给用户的短信有部分尊敬的"null"你好,xx等的问题。

    String.java 类源码:

    注意这里返回了一个"null"的字符串,而不是null。这两个是有很大区别的,当进行非空判断的时候,返回的是ture。也就是这个"null"的字符串它是符合判空条件的!

    正确的姿势是在String.valueOf方法前必须判空:

    if(userNameObject!=null){
       String userName = String.valueOf(userNameObject);
    }
    

    2、Integer.parseInt()方法

    Integer.parseInt() 方法用于将字符串转化为 Integer 类型的方法,此方法的适用方向就显得比较窄,因为是String类型的参数,没有任何限定,当在传入一些比如50.0、20L、30d、40f这类数据的情况下,

    System.out.println(Integer.parseInt("50.0"));
    

    会抛出异常NumberFormatException:

    对于这样的数据,比如小数、浮点数据、long型数据它都可以自动转换,而不是给我们抛出烦人的报错信息,如果预先知道是整数或者小数,可以用Bigdecimal转换(注意此方法不适用于double和float、Long类型的数据,比如10d,20L)

        String input="50.0";
        int value = new BigDecimal(input).intValue();
        System.out.println(value);
    

    对于浮点类型、long类型的数据可以用以下方法来处理:
    推荐使用hutool的NumberUtil.parseInt()方法,充分考虑到了浮点、long、小数等类型数据可能带来的解析异常的问题,hutool是一个国人开源的工具类库。

    3、BigDecimal 的除法坑

    BigDecimal 是处理金额最有效的数据类型,一般进行财务报表计算的时候为了防止金额出现错误,一般情况下都会采用 Bigdecimal,而 double、float 都会存在些许的误差。

        BigDecimal ten = new BigDecimal(10);
        BigDecimal two = new BigDecimal(2);
        BigDecimal divide = ten.divide(two);
        System.out.println(divide.toString());
    

    常见的除法用起来没有任何丝毫的问题,但是一旦程序中的数据出现以下情况,如果用Bigdecimal来接受前端的参数,而前端的参数是用户输入不确定的,一旦出现如下的数据,我们来看看结果:

        BigDecimal ten = new BigDecimal(10);
        BigDecimal three = new BigDecimal(3);
        BigDecimal divide = ten.divide(three);
        System.out.println(divide.toString());
    

    执行结果一看,居然报错了:

    这就是BidDecimal的坑,一旦返回的结果是无限循环小数,就会抛出ArithmeticException。因此在进行Bigdecimal除法的时候,需要进行保留小数的处理,正确的处理姿势:

        BigDecimal ten = new BigDecimal(10);
        BigDecimal two = new BigDecimal(3);
        BigDecimal divide = ten.divide(two, 2, BigDecimal.ROUND_HALF_UP);
        System.out.println(divide.toString());
    

    4、Collections.emptyList() 此 list 非彼 list

    先看下面的例子:

    public List<String> getUserNameList(String userId) {
        List<String> resultList = Collections.emptyList();
        try {
            resultList = userDao.getUserName(userId);
        } catch (Exception ex) {
            logger.info(ex);
        }
        return resultList;
    }
    

    这样会抛出错误,主要问题在于 Collections.emptyList() 并非我们平时看到的List,此List不支持add、remove方法,否则会抛出operationNotSupportException:

    List<String> emptyList = Collections.emptyList();
    emptyList.add("111");
    

    结果抛出异常:

    原因是:Collections.emptyList 返回的并不是我们平时认识的那个list,它是一个内部常量类:
    Collections 源码:

    1615964132179.jpg

    这个list并不具有 add、remove 元素的能力,,并不提供数据的写入能力,因此它仅可作为一种 空值返回,无法进行删除、添加操作。

    5、List 可以一边删除一边遍历吗?

    先看 list 遍历删除的例子:

     List<Integer> resultList = new ArrayList<>();
        resultList.add(1);
        resultList.add(12);
        resultList.add(3);
        resultList.add(4);
        for (Integer num : resultList) {
          if (num == 1) {
            resultList.remove(num);
          }
        }
    

    报错了:

    仔细翻阅源码会发现,每次 remove 之前会检查元素的条数,如果发现预期的 modCount 和当前的 modCount 不一致就会抛出这个异常。modCount 是 list 中用来记录修改次数的一个属性,当对元素进行统计的时候就会对该元素加1,而当对 list 边遍历边删除的话,就会造成 excepted 与 modCount 不一致,从而抛出异常。

    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
    

    正确的删除姿势就是使用Iterator.remove进行遍历删除,可以规避这个问题。

    List<Integer> resultList = new ArrayList<>();
        resultList.add(1);
        resultList.add(2);
        resultList.add(3);
        resultList.add(4);
       
        Iterator<Integer> iterator = resultList.iterator();
        while (iterator.hasNext()) {
          Integer next = iterator.next();
          if (next == 2) {
            iterator.remove();
          }
        }
    

    6、Bigdecimal在比较的时候,最好使用compareTo方法,不要使用equals方法

    如下案例,虽然Bigdecimal重写了equals方法,但是使用会存在问题:

    1和1.0在比较的时候返回了false,这是因为在equals的源码中进行了数据的scale(也就是精度)的比较,如果不一致就会返回false,如果使用compareTo方法就不存在这个问题

    String的split方法在进行||分割的时候需要进行转义,否则结果会有问题

    相关文章

      网友评论

          本文标题:Java JDK 常见基础类存在的一些坑

          本文链接:https://www.haomeiwen.com/subject/zrxjcltx.html