当前位置:

首页 > 编程开发 > java代码实现对比分析对象是否有变化

java代码实现对比分析对象是否有变化

在项目中大家应该遇到过这种问题,需要分析对象是否发生变化,发生变化后,具体是哪个字段发生变化 针对这个常见的开发痛点,下面提供一个可以直接“开箱即用”的工具类,帮你快速定位对象的变化细节。 package com.eternal.base.utils; import androidx.databin

本文目录

    在项目中大家应该遇到过这种问题,需要分析对象是否发生变化,发生变化后,具体是哪个字段发生变化

    ja va代码实现对比分析对象是否有变化

    针对这个常见的开发痛点,下面提供一个可以直接“开箱即用”的工具类,帮你快速定位对象的变化细节。

    package com.eternal.base.utils;
    
    import androidx.databinding.ObservableBoolean;
    import androidx.databinding.ObservableField;
    import androidx.databinding.ObservableInt;
    
    import com.eternal.framework.utils.KLog;
    
    import ja va.lang.reflect.Field;
    import ja va.util.Objects;
    
    import io.reactivex.Observable;
    
    /**
     * 用于分析对象是否发生变化,发生变化后,具体是哪个字段发生变化
     */
    public class ObjectChangeDetector {
    
        private String tag = "ObjectChangeDetector";
        private int lastHash = 0;
        private Object lastObject = null;
    
        public ObjectChangeDetector(Object object) {
            this.lastObject = object;
            this.lastHash = Objects.hashCode(object);
        }
    
        public void checkAndReport(Object newObject) {
            int newHash = Objects.hashCode(newObject);
    
            // 1. 快速预检:通过 Hash 判断是否有变化
            if (newHash == this.lastHash) {
                KLog.d(tag, "Hash 值一致,对象无变化。");
                return;
            }
    
            // 2. Hash 变化了,进行详细对比
            KLog.d(tag, "Hash 值变化 (" + this.lastHash + " -> " + newHash + "),开始分析差异...");
            if (this.lastObject != null) {
                // 使用上面介绍的 JSON 或 Ja vers 方法进行 Diff
                compareObjects(this.lastObject, newObject);
            }
            // 3. 更新状态
            this.lastHash = newHash;
            this.lastObject = newObject;
        }
    
        private void compareObjects(Object a, Object b) {
            Field[] fieldsA = a.getClass().getDeclaredFields();
            Field[] fieldsB = b.getClass().getDeclaredFields();
            for (Field field : fieldsA) {
                field.setAccessible(true);
                String fieldName = field.getName();
                try {
                    Field fieldFromB = Observable.fromArray(fieldsB).filter(it->fieldName.contentEquals(it.getName())).blockingFirst();
                    if (fieldFromB == null) {
                        KLog.d(tag, b.getClass().getSimpleName() + " 的字段 " + fieldName + " 不存在");
                        continue;
                    }
                    fieldFromB.setAccessible(true);
                    // 获取静态字段的值
                    Object valueA = field.get(a);
                    Object valueB = fieldFromB.get(b);
                    KLog.d(tag, b.getClass().getSimpleName() + " 的字段 " + fieldName + ", 类型:" + valueA.getClass().getSimpleName());
                    if (valueA instanceof Integer && valueB instanceof Integer) {
                        if ((int)valueA != (int)valueB) {
                            String msg = a.getClass().getSimpleName() + "." + fieldName + ": " + valueA + " -> " + valueB;
                            KLog.d(tag, msg);
                        }
                    }
                    else if (valueA instanceof ObservableInt && valueB instanceof ObservableInt) {
                        if (((ObservableInt)valueA).get() != ((ObservableInt)valueB).get()) {
                            String msg = a.getClass().getSimpleName() + "." + fieldName + ": " + ((ObservableInt) valueA).get() + " -> " + ((ObservableInt) valueB).get();
                            KLog.d(tag, msg);
                        }
                    }
                    else if (valueA instanceof ObservableBoolean && valueB instanceof ObservableBoolean) {
                        if (((ObservableBoolean)valueA).get() != ((ObservableBoolean)valueB).get()) {
                            String msg = a.getClass().getSimpleName() + "." + fieldName + ": " + ((ObservableBoolean) valueA).get() + " -> " + ((ObservableBoolean) valueB).get();
                            KLog.d(tag, msg);
                        }
                    }
                    else if (valueA instanceof ObservableField && valueB instanceof ObservableField) {
                        valueA = ((ObservableField) valueA).get();
                        valueB = ((ObservableField) valueB).get();
                        if (valueA instanceof String && valueB instanceof String) {
                            if (!((String) valueA).contentEquals((String)valueB)) {
                                String msg = a.getClass().getSimpleName() + "." + fieldName + ": " + valueA + " -> " + valueB;
                                KLog.d(tag, msg);
                            }
                        } else if (valueA.getClass().getSimpleName().contentEquals("PortItem") && valueB.getClass().getSimpleName().contentEquals("PortItem")) {
                            compareObjects(valueA, valueB);
                        }
                    }
                } catch (Exception e) {
                    KLog.e(tag,e + ", field name:" + field.getName());
                }
            }
        }
    }
    

    知识扩展

    其实,在 Ja va 开发中,“对比分析对象是否有变化”是个高频需求,但具体含义可能略有不同。它通常指两种情况:一是判断对象的当前状态与之前状态是否一致;二是更精细地探查对象的哪些字段值发生了改变。根据不同的应用场景,业内其实有几种主流的实现思路。

    1.基础方法:重写 equals() 与 hashCode()

    这算是最经典、最通用的方案了。核心思路是通过重写 equals() 方法来定义对象“相等”的逻辑(通常是逐一比较所有关键字段),然后直接调用 oldObj.equals(newObj) 来判断是否有变化。

    public class User {
        private String name;
        private int age;
        
        // 构造器、getter/setter 省略
        
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            User user = (User) o;
            return age == user.age && Objects.equals(name, user.name);
        }
        
        @Override
        public int hashCode() {
            return Objects.hash(name, age);
        }
    }
    
    // 使用
    User oldUser = new User("Alice", 25);
    User newUser = new User("Alice", 26);
    boolean changed = !oldUser.equals(newUser); // true

    优点:完全符合 Ja va 对象比较规范,能够被各种集合类(如 HashSet, HashMap)正确使用。

    缺点:需要手动维护 equals 和 hashCode 方法,而且它只能给出一个“是或否”的结论,无法告诉你具体是哪个字段动了手脚。

    2.利用工具库进行字段级差异检测

    如果你需要精确知道哪些字段被修改了(比如用于生成审计日志或数据同步),那么专门的对比工具库会是更好的选择。

    方案一:Apache Commons Lang3 – EqualsBuilder & ReflectionToStringBuilder

    import org.apache.commons.lang3.builder.EqualsBuilder;
    import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
    
    public boolean isChanged(Object oldObj, Object newObj) {
        return !EqualsBuilder.reflectionEquals(oldObj, newObj);
    }
    
    // 查看具体差异字段(配合 toString)
    String oldStr = ReflectionToStringBuilder.toString(oldObj);
    String newStr = ReflectionToStringBuilder.toString(newObj);
    // 对比字符串差异

    优点:无需手动编写冗长的 equals 方法,通过反射就能自动比较所有字段,非常省事。

    缺点:反射操作会带来一定的性能开销,并且无法灵活地忽略某些不需要比较的字段。

    方案二:Ja vers – 专门的对象变化审计库

    Ja vers 是这个领域的专业选手,它能深入对比复杂的对象图(比如包含嵌套对象、集合),并返回一份详细的变更日志,明确告诉你哪个字段从什么值变成了什么值。

    
        org.ja vers
        ja vers-core
        7.4.0
    
    import org.ja vers.core.Ja vers;
    import org.ja vers.core.Ja versBuilder;
    import org.ja vers.core.diff.Diff;
    
    Ja vers ja vers = Ja versBuilder.ja vers().build();
    Diff diff = ja vers.compare(oldUser, newUser);
    
    if (diff.hasChanges()) {
        System.out.println(diff.getChanges()); 
        // 输出类似:Change{ 'age' changed from 25 to 26 }
    }

    优点:功能极其强大,支持集合、嵌套对象对比,还能自定义比较策略,适合企业级审计需求。

    缺点:需要引入额外的依赖,并且有一定的学习成本。

    3.反射对比工具(轻量自制)

    如果项目不想引入庞大的第三方库,自己动手写一个轻量的反射对比工具也是个不错的选择,它可以返回发生变化的字段名列表。

    public static List findChangedFields(Object oldObj, Object newObj) throws IllegalAccessException {
        List changedFields = new ArrayList<>();
        Field[] fields = oldObj.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            Object oldVal = field.get(oldObj);
            Object newVal = field.get(newObj);
            if (!Objects.equals(oldVal, newVal)) {
                changedFields.add(field.getName());
            }
        }
        return changedFields;
    }

    优点:代码完全自主可控,没有外部依赖。

    缺点:功能相对基础,无法处理继承的字段和复杂的嵌套对象;同样,反射操作对性能有一定影响。

    4.基于版本号的简单变化跟踪(适用于 ORM 场景)

    在数据库持久化场景中,一种非常经典的做法是利用版本号(version)字段来实现乐观锁,同时也能快速判断对象是否被修改过。

    @Entity
    public class Product {
        @Version
        private Long version;  // 每次更新自动递增
        // ...
    }

    每次更新前,只需要比较一下 version 字段的值是否发生变化,就能知道这个对象是否被其他事务修改过。这种方法简单高效,但仅限于有版本号字段的实体类。

    5.序列化对比(深拷贝对比)

    这是一种比较“重”但很彻底的方法:将对象序列化为字节数组或 JSON 字符串,然后直接比较序列化后的内容是否一致。

    byte[] oldBytes = serialize(oldObj);
    byte[] newBytes = serialize(newObj);
    boolean changed = !Arrays.equals(oldBytes, newBytes);

    优点:能够进行深层次的对比,对象图里任何嵌套部分的变化都无所遁形。

    缺点:序列化和反序列化的开销很大,不适合在需要高频调用的性能敏感场景中使用。

    6.单元测试场景中的对象对比

    在编写单元测试时,我们常常需要断言对象的状态是否符合预期。这时,像 AssertJ 或 Hamcrest 这样的断言库提供了非常优雅的对象对比方式。

    import static org.assertj.core.api.Assertions.assertThat;
    User original = new User("Bob", 30);
    User updated = new User("Bob", 31);
    assertThat(updated)
        .usingRecursiveComparison()
        .ignoringFields("updateTime")
        .isNotEqualTo(original);

    这些库的语法非常直观,并且支持忽略特定字段、递归比较等高级功能,是测试代码中的利器。

    总结:如何选择?

    方法这么多,到底该怎么选?其实关键在于匹配你的具体需求。下面这张表可以帮你快速决策:

    需求场景推荐方案
    简单判断两个对象是否相等(如单元测试)重写 equals() / Objects.equals()
    业务上需要记录详细修改日志Ja vers 或自写反射工具
    不想写 equals,只想知道是否改变EqualsBuilder.reflectionEquals()
    针对数据库实体乐观锁使用 @Version 注解
    深拷贝对比(不常用)序列化对比
    高性能、大对象频繁对比使用字段级差值缓存(如 MD5 摘要)
    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发
    相关文章 更多
    如何选择最适合的AI玩具?2026年专业选购指南与对比分析
    如何选择最适合的AI玩具?2026年专业选购指南与对比分析

    面对市面上琳琅满目的AI玩具,到底该怎么给孩子选,或者作为礼物送人,才不至于买回去吃灰?这篇文章就想把“如何挑一款真正合适的AI玩具”这件事,从头到尾说透。我们会从功能需求匹配度、预算与长期价值、适用场景与年龄段这几个关键维度入手,帮你在2026年的产品浪潮中,做出一个不那么容易后悔的决定。 真的需

    tftpd32使用 怎么选?常见方案对比分析
    tftpd32使用 怎么选?常见方案对比分析

    TFTPD32是一款集成TFTP、DHCP等服务的免费网络工具,常用于网络设备维护与嵌入式开发。选择时需明确场景需求:若需功能集成与简易设置,TFTPD32适合;若追求传输稳定性与专业日志,可考虑SolarWinds;跨平台或深度定制则宜选PumpKIN。配置时应注意设置根目录、防火墙及安全过滤,并遵循测试验证等最佳实践。

    百度seo优化 怎么选?常见方案对比分析
    百度seo优化 怎么选?常见方案对比分析

    百度SEO优化旨在提升网站在搜索结果中的自然排名与流量质量,核心在于遵循搜索规则、提供高质量内容并优化用户体验。常见方案包括技术优化、内容优化、站外推广及综合服务。选择时需评估网站现状、资源、目标与竞争环境,坚持以用户为中心,重视数据分析并持续关注搜索动态。

    orc软件与其他同类软件的对比分析
    orc软件与其他同类软件的对比分析

    OCR软件的核心差异体现在识别能力、功能集成、操作体验和成本上。专业软件识别精度高、功能全面,适合大批量复杂文档处理,但成本较高。免费或轻量工具便捷易用,能满足临时性基础需求。用户应根据自身场景、精度要求及预算综合选择。

    对比分析几种 propertieseditor 版本的优缺点
    对比分析几种 propertieseditor 版本的优缺点

    属性编辑器用于高效编辑.properties等配置文件。经典独立编辑器轻量专注但功能单一;IDE内置编辑器功能强大深度集成但显笨重;现代编辑器插件灵活高效但需配置;在线及配置中心工具便于协作但涉及安全与复杂度。选择需权衡使用频率、格式支持、集成需求及团队协作等因素。

    asp学习教程 怎么选?常见方案对比分析
    asp学习教程 怎么选?常见方案对比分析

    ASP作为早期服务器端脚本技术,其核心原理对理解Web开发历史和维护遗留系统仍有价值。学习路径包括阅读经典书籍与文档、观看视频课程以及项目实践,需掌握脚本语言、内置对象、数据库连接等内容,并注重动手编码。掌握ASP有助于理解后续框架设计思想,培养向现代技术迁移的能力。

    smplayer与其它媒体播放器对比分析
    smplayer与其它媒体播放器对比分析

    SMPlayer是一款基于MPlayer/MPV的免费开源播放器,无需外部解码包即可播放多种格式。其特色在于能记忆播放位置、集成字幕搜索,并提供了图形化高级设置选项。与VLC的简洁通用、PotPlayer的深度定制相比,SMPlayer在易用性、功能与资源占用间取得了平衡,尤其适合注重观看连续性和便捷体验的用户。

    filebox 与常见文件管理工具对比分析
    filebox 与常见文件管理工具对比分析

    文件管理工具能显著提升效率。系统自带工具稳定易用但功能有限。专业第三方工具如FileBox注重效率,支持多标签、双窗格、高级搜索等,且高度可定制。用户应根据自身需求选择,轻度用户可用系统工具,处理复杂任务则适合功能全面的第三方管理器。体验工作流是否契合习惯是关键。

    android 2.0 怎么选?常见方案对比分析
    android 2.0 怎么选?常见方案对比分析

    Android2.0设备面临软件适配挑战。若发挥余热,可寻找兼容应用或轻量替代;若追求体验,可尝试定制ROM或第三方启动器,但需承担风险。多数情况下,购置现代入门设备更高效安全;若用于怀旧或嵌入式场景,应严格限定使用范围并隔离网络。核心在于匹配需求,在设备极限内寻找可行方案。

    jetpack攻略:与其他平台对比分析
    jetpack攻略:与其他平台对比分析

    Jetpack是Google推出的Android开发组件集合,通过解耦设计简化生命周期管理,提升开发效率。它封装了数据库、后台任务等复杂操作,专注于原生体验和性能,具有官方支持和深度集成优势。技术选型需结合项目需求、团队技术栈及长期维护考量,常与第三方库互补使用。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。