• Java开发中常见的坑


    1、用==号比较的坑

    (1)使用==号的情况

    Integer orderStatus1 = new Integer(1);
    Integer orderStatus2 = new Integer(1);
    System.out.println(orderStatus1 == orderStatus2);
    
    • 1
    • 2
    • 3

    答案:是false,因为比较的是两个变量的内存地址,很明显这两个变量在栈中的地址当然不一样,所有为false

    (2)使用==号,走缓存的情况

    String orderStatus1 = new String("1");
    String orderStatus2 = new String("1");
    System.out.println(Integer.valueOf(orderStatus1) == Integer.valueOf(orderStatus2));
    
    • 1
    • 2
    • 3

    答案:是true。这种情况是走了缓存

    public static Integer valueOf(int i) {
            if (i >= IntegerCache.low && i <= IntegerCache.high)
                return IntegerCache.cache[i + (-IntegerCache.low)];
            return new Integer(i);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    我们要养成良好编码习惯,尽量少用==判断两个Integer类型数据是否相等,只有在上述非常特殊的场景下才相等。

    (3)使用equals方法

    public static Integer valueOf(int i) {
            if (i >= IntegerCache.low && i <= IntegerCache.high)
                return IntegerCache.cache[i + (-IntegerCache.low)];
            return new Integer(i);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    运行结果为true.

    2、Objects.equals的坑

    假设现在有这样一个需求:判断当前登录的用户,如果是我们指定的系统管理员,则发送一封邮件。系统管理员没有特殊的字段标识,他的用户id=888,在开发、测试、生产环境中该值都是一样的。

    UserInfo userInfo = CurrentUser.getUserInfo();
    
    if(Objects.isNull(userInfo)) {
       log.info("请先登录");
       return;
    }
    
    if(Objects.equals(userInfo.getId(),888L)) {
       sendEmail(userInfo):
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    当我们用id=888的系统管理员账号登录之后,做了相关操作,满怀期待的准备收邮件的时候,却发现收了个寂寞。

    后来,发现UserInfo类是这样定义的:

    @Data
    public class UserInfo {
        private Integer id;
        private String name;
        private Integer age;
        private String address;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    id定义为Integer类型的,接下来分析为什么返回的是false
    首先查看Objects类的equals()方法

    public static boolean equals(Object a, Object b) {
            return (a == b) || (a != null && a.equals(b));
        }
    
    • 1
    • 2
    • 3

    equals方法的判断逻辑如下:
    1、该方法先判断对象a和b的引用是否相等,如果相等则直接返回true。
    2、如果引用不相等,则判断a是否为空,如果a为空则返回false。
    3、如果a不为空,调用对象的equals方法进一步判断值是否相等。

    接着需要分析Integer的equals方法

    public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    先判断参数obj是否是Integer类型,如果不是,则直接返回false。如果是Integer类型,再进一步判断int值是否相等。
    而上面这个例子中b是long类型,所以Integer的equals方法直接返回了false。

    也就是说,如果调用了Integer的equals方法,必须要求入参也是Integer类型,否则该方法会直接返回false。

    除此之外,还有Byte、Short、Double、Float、Boolean和Character也有类似的equals方法判断逻辑。

    常见的坑有:
    1、Long类型和Integer类型比较,比如:用户id的场景。
    2、Byte类型和Integer类型比较,比如:状态判断的场景。
    3、Double类型和Integer类型比较,比如:金额为0的判断场景。

    3、Java8中filter的坑

    通过对集合的Stream操作,可以实现:遍历集合、过滤数据、排序、判断。下面重点讲一下过滤:

    public List<User> filterUser(List<User> userList) {
        if(CollectionUtils.isEmpty(userList)) {
            return Collections.emptyList();
        }
        
        List<User> resultList = Lists.newArrayList();
        for(User user: userList) {
            if(user.getId() > 1000 && user.getAge() > 18)   {
               resultList.add(user);
            }
        }
        return resultList;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    通常需要另一个集合辅助完成这个功能。代码不够简洁
    但如果使用Java8的filter功能,代码会变得简洁很多:

    public List<User> filterUser(List<User> userList) {
        if(CollectionUtils.isEmpty(userList)) {
            return Collections.emptyList();
        }
        
        return userList.stream()
        .filter(user -> user.getId() > 1000 && user.getAge() > 18)
        .collect(Collectors.toList());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    但是如果你对过滤后的数据进行修改:

    List<User> userList = queryUser();
    List<User> filterList = filterUser(userList);
    for(User user: filterList) {
       user.setName(user.getName() + "测试");
    }
    
    for(User user: userList) {
       System.out.println(user.getName());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    你当时可能只是想修改过滤后的数据,但实际上,你会把元素数据一同修改了。
    根本原因:过滤后的集合中,保存的是对象的引用,该引用只有一份数据。
    也就是说,只要有一个地方,把该引用对象的成员变量的值,做修改了,其他地方也会同步修改。
    如下图所示:
    在这里插入图片描述

    4、自动拆箱的坑

    首先搞清楚什么是自动拆箱,自动装箱?
    1、自动装箱:JDK会把基本类型,自动转为包装类型。

    Integer i = 1;
    
    • 1

    等价于:

    Integer i = new Integer(1);
    
    • 1

    1、自动拆箱:将包装类型转为基本数据类型。

    Integer integer = new Integer(2);
    int sum = integer + 5;
    
    • 1
    • 2

    等价于:

    Integer integer = new Integer(2);
    int sum = integer.intValue() + 5;
    
    • 1
    • 2

    但实际工作中,我们在使用自动拆箱时,往往忘记了判空,导致出现NullPointerException异常。

    4.1 运算

    很多时候,我们需要对传入的数据进行计算,例如:

    public class Test2 {
        public static void main(String[] args) {
            System.out.println(add(new Integer(1), new Integer(2)));
        }
    
        private static Integer add(Integer a, Integer b) {
            return a + b;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    如果传入了null值:

    System.out.println(add(null, new Integer(2)));
    
    • 1

    则会直接报错。

    4.2 传参

    有时候,我们定义的某个方法是基本类型,但实际上传入了包装类,比如:

    public static void main(String[] args) {
        Integer a = new Integer(1);
        Integer b = null;
        System.out.println(add(a, b));
    }
    
    private static Integer add(int a, int b) {
        return a + b;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    如果出现add方法报NullPointerException异常,你可能会懵逼,int类型怎么会出现空指针异常呢?

    其实,这个问题出在:Integer类型的参数,其实际传入值为null,JDK字段拆箱,调用了它的intValue方法导致的问题。

  • 相关阅读:
    为什么推荐 Java 开发人员都学习并使用 Kotlin?
    靠做网络安全,工资是同龄人的5倍:赚钱真的不能靠拼命!
    小心钓鱼电子邮件攻击!
    SSM框架学习——SpringBoot之基础配置
    vscode使用远程服务器jupyter
    堆相关例子-排序最多移动k距离
    Docker部署深度学习模型
    在 .NET 8 Release Candidate 2 中宣布 .NET MAUI:更高质量
    ARM 笔记
    大学生简单静态HTML网页模板源码——家乡介绍美丽乡村11页
  • 原文地址:https://blog.csdn.net/java123456111/article/details/125876529