ObjectUtils.nullSafeEquals你真的用对了吗?

news2024/11/15 17:38:14

目录

  • 引言
  • 排查
  • 思考

引言

在写代码时,我们通常喜欢使用org.springframework.util.ObjectUtils#nullSafeEquals来比较两个对象是否相等,从而避免使用equals方法在对象为空时导致空指针异常。
最近在写代码时,我试图使用stream流的filter,配合使用ObjectUtils.nullSafeEquals过滤出租户id不为0的数据。于是写了类似如下的代码。在具体讲述问题之前,我们先看下代码,大家判断下代码会输出什么?

public static void main(String[] args) {
   List<Long> tenantIds = new ArrayList<>();
   tenantIds.add(0L);
   tenantIds.add(1L);
   tenantIds.add(2L);
   List<Long> list = tenantIds.stream()
           .filter(id -> !ObjectUtils.nullSafeEquals(id, 0))
           .collect(Collectors.toList());
   System.out.println(list);
}

OK,我们运行下代码揭晓下答案,竟然并没有过滤掉为0的id。
在这里插入图片描述

排查

那为啥没有过滤掉0呢?我们来看下ObjectUtils.nullSafeEquals的相关源码。
在这里插入图片描述
方法其实很直观,我们一行一行看下。

public static boolean nullSafeEquals(@Nullable Object o1, @Nullable Object o2) {
    // 比较的两个对象引用地址相同,说明对象为同一个。返回true
    if (o1 == o2) {
        return true;
    }
    // 如果两个对象存在为null的,则返回false
    if (o1 == null || o2 == null) {
        return false;
    }
    // 由于上面的if校验,说明o1不为null,这里调用equals方法比较两个对象是否相等
    if (o1.equals(o2)) {
        return true;
    }
    // 这里是比较两个数组对象,调用了arrayEquals方法比较
    if (o1.getClass().isArray() && o2.getClass().isArray()) {
        return arrayEquals(o1, o2);
    }
    return false;
}

看完上述方法,再根据我们传入的参数。我们可以定位到问题发生在o1.equals(o2),那这里为啥返回了false?
我们知道equals方法为Object类的方法,所有类的父类。我们在编写自定义类时,通常会重写equals方法来实现当前类的比较。

由于咱们上面定义的tenantId为Long类型,那我们找到Long类中的equals方法,看看它做了什么就能知道原因了?源码如下
在这里插入图片描述
我们可以看到这里判断了传入的obj如果是Long类型或其子类,会把传入的对象强转为Long类型,调用其中的longValue方法与当前的Long对象维护的value进行比较。
ObjectUtils.nullSafeEquals(id, 0) 这里的两个0为啥不相等呢?我们简化下代码 ,使用IDEA中jclasslib插件看下字节码文件(这个插件推荐下)。

在这里插入图片描述

简化后的代码:

public static void main(String[] args) {
    Long tenantId = 0L;
    System.out.println(ObjectUtils.nullSafeEquals(tenantId, 0));
}

字节码

 0 lconst_0
 1 invokestatic #2 <java/lang/Long.valueOf : (J)Ljava/lang/Long;>
 4 astore_1
 5 getstatic #3 <java/lang/System.out : Ljava/io/PrintStream;>
 8 aload_1
 9 iconst_0
10 invokestatic #4 <java/lang/Integer.valueOf : (I)Ljava/lang/Integer;>
13 invokestatic #5 <org/springframework/util/ObjectUtils.nullSafeEquals : (Ljava/lang/Object;Ljava/lang/Object;)Z>
16 invokevirtual #6 <java/io/PrintStream.println : (Z)V>
19 return

我们可以看到ObjectUtils.nullSafeEquals(id, 0) 中的0,在编译时,会被编译器转换为 Integer的包装类。从而导致了运行结果与预期不符合的场景。

那我们怎么解决呢?
我们可以把ObjectUtils.nullSafeEquals(id, 0)修改为ObjectUtils.nullSafeEquals(id, 0L),这样编译器就会把0L转换为Long类型的包装类,从而就能正确调用Long类的equals方法了。

思考

  1. 我们平时开发时,通常会犯一些很低级的错误,从而导致系统的bug出现。只是一个租户id为0的一条数据未能正确过滤,也许就会导致生产事故的发生。在去年,我所在的项目就遇到了一个空串导致的p0级别的生产事故,导致了很多数据异常删除,最后处理了一两天才完全把数据恢复好。这个后面有时间会作为负面教材给大家分享出来。
  2. 我们在使用一些轮子(方法)时,不要想当然的去使用,一定要有阅读源码的习惯,并做一些单元测试,来确保方法结果符合预期。
  3. 在编写代码时,一定要尽量考虑周全,提高代码的健壮性,该判空的时候不要觉得数据不可能为空就不去写兜底判断,对自己代码负责一些。
  4. 这个代码其实排查起来很快的,但是考虑到也许有刚入行的同学不明白,就给大家分享的细致了些,希望大家都能有所收获。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2048793.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

1.Linux_常识

UNIX、Linux、GNU 1、UNIX UNIX是一个分时操作系统&#xff0c;特点是多用户、多任务 实时操作系统&#xff1a;来了请求就去解决请求 分时操作系统&#xff1a;来了请求先存着&#xff0c;通过调度轮到执行时执行 2、Linux Linux是一个操作系统内核 发行版本&#xff1…

Linux - 常用基础指令和命令

文章目录 1、ifconfig指令2、ssh指令3、ls指令4、pwd命令5、cd 指令6、stat命令7、 touch指令8、mkdir指令9、rmdir指令10、rm指令11、man指令12、cp指令13、mv指令14、cat指令15、more指令16、less指令17、head指令18、tail指令19、时间相关的指令20、cal指令21、find指令22、…

指向派生类的基类指针、强转为 void* 再转为基类指针、此时调用虚函数会发生什么?

指向派生类的基类指针、强转为 void* 再转为基类指针、此时调用虚函数会发生什么&#xff1f; 1、无论指针类型怎么转&#xff0c;类对象内存没有发生任何变化&#xff0c;还是vfptr指向虚函数表&#xff0c;下面是成员变量&#xff0c;这在编译阶段就已经确定好了&#xff1b…

校园失物招领系统pf

TOC springboot337校园失物招领系统pf 绪论 1.1研究背景与意义 信息化管理模式是将行业中的工作流程由人工服务&#xff0c;逐渐转换为使用计算机技术的信息化管理服务。这种管理模式发展迅速&#xff0c;使用起来非常简单容易&#xff0c;用户甚至不用掌握相关的专业知识&…

自动驾驶-机器人-slam-定位面经和面试知识系列09之C++STL面试题(04)

这个博客系列会分为C STL-面经、常考公式推导和SLAM面经面试题等三个系列进行更新&#xff0c;基本涵盖了自己秋招历程被问过的面试内容&#xff08;除了实习和学校项目相关的具体细节&#xff09;。在知乎和牛客&#xff08;某些文章上会附上内推码&#xff09;也会同步更新&a…

鸿蒙(API 12 Beta3版)【HDR Vivid视频录制】 音视频编码

开发者可以调用本模块的Native API接口&#xff0c;实现在视频录制中支持HDR Vivid标准。 视频录制的主要流程是“相机采集 > 编码 > 封装成mp4文件”。 HDR Vivid视频编码 应用创建H265编码器&#xff0c;配置profile(main 10)相机底层包含HDR Vivid的surfacebuffer内…

Airtest 的使用

Airtest 介绍 Airtest Project 是网易游戏推出的一款自动化测试框架&#xff0c;其项目由以下几个部分构成 Airtest : 一个跨平台的&#xff0c;基于图像识别的 UI 自动化测试框架&#xff0c;适用于游戏和 App &#xff0c; 支持 Windows, Android 和 iOS 平台&#xff0c…

蛋白质基础

氨基酸:必需氨基酸 条件必需氨基酸和非必需氨基酸 必需氨基酸:机体需要但自身不能合成&#xff0c;必须从食物中获取的氨基酸。共有八种&#xff0c;对婴儿&#xff0c;组氨酸也是必需氨基酸。 条件必需氨基酸:半胱氨酸和酪氨酸在体内分别由蛋氨酸和苯丙氨酸转变而来。若膳食中…

HTML 列表和容器元素——WEB开发系列10

HTML 提供了多种方式来组织和展示内容&#xff0c;其中包括无序列表、有序列表、分区元素 ​​<div>​​ 和内联元素 ​​<span>​​、以及如何使用 ​​<div>​​​ 进行布局和表格布局。 一、HTML 列表 1. 无序列表 (​​<ul>​​) 无序列表用于展…

Java流程控制06:for循环详解

教学视频链接&#xff1a;https://www.bilibili.com/video/BV12J41137hu?p41&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5https://www.bilibili.com/video/BV12J41137hu?p41&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5 Java中的‌for循环结构‌是一种基础…

Python与DIAdem联合开发

Python可以通过COM接口与NI的DIAdem软件集成&#xff0c;允许用户以编程方式自动生成和定制报告。这种方式使得报告生成更加灵活且可定制&#xff0c;尤其适用于需要定期生成大量报告或对报告内容有特定要求的场景。 1. 工作原理 Python与DIAdem的集成主要依赖于COM&#xff0…

二叉树的判断

二叉树的判断 判断一颗二叉树是不是搜索二叉树 &#xff08;左边的比根小&#xff0c;右边的比根大&#xff09; 中序遍历一下&#xff0c;如果是的话就一定是升序的 如何判断一颗二叉树是否是完全二叉树 1.遍历任意的节点时候&#xff0c;如果返回右孩子没有左孩子&#x…

【Linux学习】权限

&#x1f525;个人主页&#xff1a; Forcible Bug Maker &#x1f525;专栏&#xff1a;Linux学习 目录 &#x1f308;前言&#x1f525;Linux权限的概念&#x1f525;Linux权限管理01.文件访问者的分类02.文件类型和访问权限&#xff08;文件属性&#xff09;03.文件权限值得…

线程同步-条件变量

文章目录 引言条件变量初始化条件变量&#xff1a;pthread_cond_init销毁条件变量&#xff1a;pthread_cond_destroy条件等待&#xff1a;pthread_cond_wait唤醒等待&#xff1a;pthread_cond_signal、pthread_cond_broadcast认识条件变量接口使用 引言 有一个非常好的VIP自习…

树莓派3B运行rasa init和rasa shell遇到的tensorflow报错总结

终于在我的树莓派上安装rasa-1.4.0版本成功&#xff08;见《树莓派智能语音助手之聊天机器人-RASA》&#xff09;。不过&#xff0c;在初始化rasa的时候还是遇到了很多报错&#xff0c;在此总结&#xff0c;供朋友们参考。 1. ModuleNotFoundError: No module named ‘tensorf…

合肥网站制作服务

合肥网站制作服务&#xff1a;为您的企业搭建数字化桥梁 随着互联网的迅猛发展&#xff0c;越来越多的企业意识到拥有一个专业的网站对于提升品牌形象和扩大市场影响力的重要性。合肥作为安徽省的省会&#xff0c;经济发展迅速&#xff0c;许多企业渴望通过优质的网站制作服务&…

Windows采用VS2019实现Open3D的C++应用

1、参考链接 https://blog.csdn.net/qq_31254435/article/details/137799739 但是&#xff0c;我的方法和上述链接不大一样&#xff0c;我是采用VS2019进行编译的&#xff0c;方便在Windows平台上验证各种算法。 2、创建一个VS2019的C Console工程 #include <iostream>…

Unity 6 预览版正式发布

Unity 6 预览版发布啦&#xff0c;正式版本将于今年晚些时候正式发布&#xff01; 下载链接&#xff1a; https://unity.com/releases/editor/whats-new/6000.0.0 Unity 6 预览版是 Unity 6 开发周期的最后一个版本&#xff0c;在去年 11 月 Unite 大会上&#xff0c;我们宣…

应急响应-DDOS-典型案例

某单位遭受DDoS攻击事件如下 事件背景 2019年2月17日&#xff0c;某机构门户网站无法访问&#xff0c;网络运维人员称疑似遭受DDoS攻击&#xff0c;请求应急响应工程师协助。 事件处置 应急响应工程师在达到现场后&#xff0c;通过查看流量设备&#xff0c;发现攻击者使用僵…

日拱一卒(4)——leetcode学习记录:路经总和

一、任务&#xff1a; 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum 。如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 叶…