对于所有对象都通用的方法⭐良好习惯总结

news2024/11/26 19:20:19

对于所有对象都通用的方法⭐良好习惯总结

Object是每个类的父类,它提供一些非final方法:equals、hashCode、clone、toString、finalize...

这些方法在设计上是可以被子类重写的,但是重写前需要遵守相关的规定,否则在使用时就可能踩坑

为了避免业务开发踩坑,本文基于Effective Java中第三章节汇总出对于所有对象都通用方法的好习惯(文末附案例地址)

finalize方法上篇文章已经描述就不再讨论

1.重写equals的通用规定

equals是Object中提供比较对象逻辑相等的方法

在Object中equals方法比较对象引用地址是否相同,相同则返回true

public boolean equals(Object obj) {
    return (this == obj);
}

如果想让对象逻辑相等,则可以重写equals方法

但在重写equals方法前需要遵守一些规定:

  1. 自反性:x.equals(x)需要返回true
  2. 对称性:x.equals(y)返回true,那么y.equals(x)也要返回true
  3. 传递性:x.equals(y)返回true,y.equals(z)返回true,那么x.equals(z)也要true
  4. 一致性:x.equals(y)返回true,只要x\y都没被修改多次执行都返回true
  5. 非null的x: x.equals(null) 要返回 false
  6. 重写equals必须重写hashCode

如果要实现equals,通用情况可以使用以下总结:

  1. 先判断对象的引用地址是否相等,相等则返回true
  2. 判断两个对象是否为相同类型,不同类型则返回false
  3. 转换成相同类型后根据规定逻辑相等的关键字段进行比较,相等返回true

比如String中的equals就是这样重写的:

public boolean equals(Object anObject) {
    //1.判断对象的引用地址是否相等
    if (this == anObject) {
        return true;
    }

    //2.判断两个对象是否为相同类型
    if (anObject instanceof String) {
        //3.转换成相同类型后根据规定逻辑相等的关键字段进行比较
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

也可以使用工具去进行生成,但要记得重写equals时还需要重写hashCode

重写hashCode也要根据逻辑相等的关键字段进行,能够根据关键字段充分打乱哈希码

如果不遵循约定,那么在使用哈希表的数据结构时可能出现严重的问题

并且使用哈希表时,Key最好是不可变对象如String,或者保证哈希码不变

2.始终要重写toString

在Object的toString中返回:全限定类名 + @ + 哈希码的十六进制

public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

使用起来十分不方便,不好调试,查看对象信息

因此最好对其进行重写,返回容易阅读、有用的对象信息

3.谨慎重写clone

clone方法提供克隆一个新的对象,重写时使用super.clone()进行克隆

clone方法坑多,重写时需要谨慎

  1. 如果重写clone需要实现Cloneable接口(该接口是一个空接口)否则就会抛出不支持克隆的运行时异常(这是Cloneable设计上的缺陷)

    protected native Object clone() throws CloneNotSupportedException;
  2. 在clone的重写时,super.clone() 使用的是浅拷贝,如果字段存在对象,想要深拷贝对象,则对象也要重写clone方法

    class CloneObject implements Cloneable {
            private int num;
            private CloneA cloneA = new CloneA(99);
    
            @Override
            protected CloneObject clone() throws CloneNotSupportedException {
                CloneObject res = (CloneObject) super.clone();
                //深拷贝:CloneA也要重写clone实现Cloneable
                res.cloneA = cloneA.clone();
                return res;
            }   
    }
  3. 如果字段是final的,则无法使用深拷贝

    因为深拷贝时还需要去调用clone进行赋值:res.cloneA = cloneA.clone();

  4. 一个实体类携带克隆的方法,耦合性较高,违反单一职责

4.考虑实现Comparable接口

有的对象如果你需要对它进行排序,那么可以实现Comparable接口来进行排序,然后使用一些排序工具如:Arrays.sort

它是一个泛型接口,可以指定需要排序的类型,实现compareTo 负数为小于、正数为大于、零为等于

与其相似功能的另一个接口Comparator是外部比较器,常用于外部排序

  1. Comparator 外部比较器优先Comparable 内部比较器

有时候在一些容器中会需要排序,如果没提供外部比较器也没有实现内部比较器,会导致转换失败抛出异常

如红黑树实现的TreeMap中:

Comparator<? super K> cpr = comparator;
//优先外部排序器
if (cpr != null) {
    //小于去左子树寻找、大于去右子树寻找、相等替换
    do {
        parent = t;
        cmp = cpr.compare(key, t.key);
        if (cmp < 0)
            t = t.left;
        else if (cmp > 0)
            t = t.right;
        else
            return t.setValue(value);
    } while (t != null);
}
else {
    if (key == null)
        throw new NullPointerException();
    @SuppressWarnings("unchecked")
    //如果未实现内部排序器 则抛出异常
    Comparable<? super K> k = (Comparable<? super K>) key;
    do {
        parent = t;
        cmp = k.compareTo(t.key);
        if (cmp < 0)
            t = t.left;
        else if (cmp > 0)
            t = t.right;
        else
            return t.setValue(value);
    } while (t != null);
}
  1. 使用某些需要排序的容器(TreeMap 红黑树)时,如果不实现比较器在转换时会发生异常
  1. 实现排序时,根据多个关键字段从重要程度依次排序,基本类型可以使用包装类的compare方法

比如需要按照学生年龄排序,那么可以先比较age,age相等再比较day

public int compareTo(Student o) {
    // int res = this.age - o.age;
    // 使用包装类的compare
    int res = Integer.compare(this.age, o.age);

    if (0 == res) {
        return Integer.compare(this.day, o.day);
    }

    return res;
}
  1. 外部比较器还提供lambda表达式构造Comparator
TreeSet<Student> students = new TreeSet<>(
        //先比较age再比较day
        Comparator
                .comparingInt(Student::getAge)
                .thenComparingInt(Student::getDay)
);
总结

equals表示逻辑相等,当需要判断对象逻辑相等时重写equals方法

重写equals通用方案一般为先判断对象引用是否相等,再判断对象是否为同类型,为同类型再根据关键字段进行比较

重写equals需要根据根据逻辑相等的字段重写hashCode,否则在使用哈希表实现的数据结构时会出现严重问题

使用哈希表时Key最好为不可变对象,或让对象的hashCode不会随着字段值改变,否则会出现严重问题

始终要重写toString,输出关键字段信息,方便阅读、调试

谨慎重写clone,clone用于对象的克隆,在设计上并不太好还存在一些缺点:

  1. 重写clone需要实现Cloneable空接口,否则会抛出 CloneNotSupportedException 异常
  2. 调用 super.clone 实现的是浅拷贝,如果要实现深拷贝,字段中的类也需要重写clone方法
  3. 如果字段是final的则无法实现深拷贝
  4. 实体类携带克隆方法,耦合性较高,违法单一职责

对于需要排序的对象,考虑实现Comparable或Comparator接口:

  1. Comparator 外部比较器一般优先 Comparable 内部比较器
  2. 使用某些需要排序的容器时(红黑树 TreeMap),如果不实现比较器在转换时会发生异常
  3. 实现排序时,根据多个关键字段重要程度进行排序,基本类型可以使用包装类的compare方法
  4. 外部排序器提供lambda表达式构造Comparator外部比较器
最后(不要白嫖,一键三连求求拉~)

本篇文章被收入专栏 Effective Java,感兴趣的同学可以持续关注喔

本篇文章笔记以及案例被收入 Gitee-CaiCaiJava、 Github-CaiCaiJava 感兴趣的同学可以stat下持续关注喔~

有什么问题可以在评论区交流,如果觉得菜菜写的不错,可以点赞、关注、收藏支持一下~

关注菜菜,分享更多干货,公众号:菜菜的后端私房菜

本文由博客一文多发平台 OpenWrite 发布!

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

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

相关文章

MYBATIS获取参数值

MYBATIS最核心的莫过于动态的获取各种的参数值, 为了将来更好的使用MYBATIS进行开发, 我们必须先打好 "获取参数值" 这一基础 一. MYBATIS获取参数值的两种情况: 1.${} 实质:字符串的拼接 注解:${}使用的字符串拼接的方式拼接SQL语句, 所以, 如果其中出现了字符串…

23. 【Android教程】轮播滚动视图:ViewFlipper

轮播视图 ViewFlipper 是 Android 从第一个版本就开始提供的 UI 控件&#xff0c;它能够承载多个 View&#xff0c;但一个时机只会有一个 View 展示在屏幕上。通过 ViewFlipper 我们可以实现很多常见的带有展示类型的功能&#xff0c;类似 Gallery、轮播图、导航栏、广告banner…

基于Springboot+Vue的Java项目-旅游网站系统(附演示视频+源码+LW)

大家好&#xff01;我是程序员一帆&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &am…

C# WinForm —— 项目目录结构

1. WinForm 应用程序项目 Properties&#xff1a;属性文件夹存放了一个自动生成的类文件AssemblyInfo.cs&#xff0c;保存了一些应用程序集的一些信息引用存放了一些为应用程序提供所需的&#xff0c;某些功能的一些程序集&#xff08;dll文件&#xff09;等添加引用&#xff…

一些知识点小细节

当遇到的问题有关逆序输出&#xff0c;可以转换一下思想&#xff0c;就是使用for循环的时候&#xff0c;i的初始化是从数组或者是字符串的最后一个&#xff0c;然后注意设置循环结束的条件&#xff0c;最重要的是不要忘记i--;而不是I&#xff1b; 注意&#xff1a;当要逆序输出…

蚓链赋能企业拥有“数据能力”会带来哪些变革?

数字时代&#xff0c;对任何一家无论大小的企业&#xff0c;数据都是不可或缺的资源和工具。蚓链赋能企业拥有“数据能力”将会给企业带来哪些变革呢&#xff1f; 首先&#xff0c;将会提高企业的业务效率&#xff0c;通过数据分析和挖掘&#xff0c;企业可以更好地了解客户需…

Composer安装与配置

Composer&#xff0c;作为PHP的依赖管理工具&#xff0c;极大地简化了PHP项目中第三方库的安装、更新与管理过程。本文将详细介绍Composer的安装步骤、基本配置方法&#xff0c;以及一些实用的操作示例&#xff0c;帮助读者快速上手并熟练运用Composer。 一、Composer安装 环…

redis 数据迁移到rds2214(TongRDS-2.2.1.3.Load版 by lqw)

​ 文章目录 一.备份redis文件 vi redis.conf &#xff0c;看看有没有这两行设置&#xff0c;有的话改成跟下面的一致&#xff1a; appendonly yes appendfilename “appendonly.aof” 之后连接redis客户端&#xff0c;输入INFO persistence&#xff0c;如图所示即为开启成功…

中国历年GDP统计-探数API统计

数据介绍 时间维度&#xff1a;1978年-2021年 单位&#xff1a;亿元 该数据来源于国家统计局发布的中国统计年鉴2021&#xff0c;为按当年价格计算的中国历年GDP以及人均GDP。 数据说明&#xff1a; 数据来源于国家统计局。

SpringBoot-自定义Starter精华版

SpringBoot自定义Starter精华版 一、自定义 Starter 分析 项目首先加载 starter,starter加载自动配置类&#xff0c;然后再通过配置绑定对象读取配置属性&#xff0c;注册组件。 二、实现步骤 ​ 开发的自定义 Starter 需求是&#xff0c;项目依赖starterTest-spring-boot-s…

重生奇迹MU圣导师与弓箭手职业对比

职业定位对比 在职业定位上&#xff0c;弓箭手是一个远程物理输出职业&#xff0c;不过弓箭手也有一定的辅助能力&#xff0c;可以为队友提供控场效果&#xff0c;还能为队友提供一个攻击力加成BUFF。同时弓箭手也是一个非常需要操作的职业&#xff0c;想要玩好这个职业&#…

智能体Agent是AI时代普通人可参与的第一个红利机会

自从生成式 AI 兴起&#xff0c;掌握 prompt 能让 AI 工具更好为我们服务。而智能体的出现&#xff0c;让 AI 的使用门槛更是不断降低——都不用写 Prompt 了&#xff0c;直接用智能体就行了。 智能体Agent是AI时代普通人可参与的第一个红利机会© 由 ZAKER科技 提供 前几…

基于java+springboot+vue实现的药品管理系统(文末源码+Lw)23-297

摘 要 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;药品信息因为其管理内容繁杂&#xff0c;管理数量繁多导致手工进行处理不能满足广…

Go 自定义14位时间类型 yyyyMMddHHmmss

目录 功能 代码 功能 数据库或者接口时间类型&#xff0c;经常会使用14位的时间格式。每次都转换有点麻烦。可以自定义一个时间类型。 自定义类型需要实现json接口中的MarshalJSON与UnmarshalJSON两个函数&#xff0c;这样在做json编码解码时就会自动转为14位的时间格式了。…

【力扣】142. 环形链表 II

142. 环形链表 II 题目描述 给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&am…

第6章:6.3.3 正则表达式的应用 (MATLAB入门课程)

讲解视频&#xff1a;可以在bilibili搜索《MATLAB教程新手入门篇——数学建模清风主讲》。​ MATLAB教程新手入门篇&#xff08;数学建模清风主讲&#xff0c;适合零基础同学观看&#xff09;_哔哩哔哩_bilibili 到目前为止&#xff0c;我们已经系统地介绍了正则表达式的基本语…

Bytebase 2.15.0 - GitOps 整体升级

&#x1f514; GitOps 整体升级 新版 GitOps 和之前版本不兼容&#xff0c;如果需要升级协助&#xff0c;请联系我们。 使用访问令牌进行身份验证。支持项目中配置多个 VCS 连接器。支持在 VCS 连接器中指定数据库分组为目标&#xff08;默认情况下应用于项目中的所有数据库&…

uni-app实现下拉刷新

业务逻辑如下&#xff1a; 1.在滚动容器中加入refresher-enabled属性&#xff0c;表示为开启下拉刷新 2.监听事件&#xff0c;添加refresherrefresh事件 3.在事件监听函数中加载数据 4.关闭动画&#xff0c;添加refresher-triggered属性&#xff0c;在数据请求前开启刷新动画…

自营商城上货采集,上货软件采集(淘宝1688)接口

自营商城上货采集及上货软件采集涉及到淘宝和1688等平台的接口时&#xff0c;主要流程包括确定数据源、申请API权限、编写自动化采集脚本以及后续的数据处理和商城上货。 taobao.item_get-获取淘宝商品数据接口返回值说明 1.请求方式&#xff1a;HTTP POST GET &#xff08;复…

信息学奥赛一本通T1441-生日蛋糕【dfs】

信息学奥赛一本通T1441-生日蛋糕 - C语言网 (dotcpp.com) #include <iostream> #include <algorithm> #include <cmath> using namespace std; const int N1e5100; int n,m; int res1e9; void dfs(int spv,int cnt,int r,int h,int sarea) //spv:剩余的体积&…