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

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

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);@Overrideprotected CloneObject clone() throws CloneNotSupportedException {CloneObject res = (CloneObject) super.clone();//深拷贝:CloneA也要重写clone实现Cloneableres.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;elsereturn 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;elsereturn t.setValue(value);} while (t != null);
}
  1. 使用某些需要排序的容器(TreeMap 红黑树)时,如果不实现比较器在转换时会发生异常
  1. 实现排序时,根据多个关键字段从重要程度依次排序,基本类型可以使用包装类的compare方法

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

public int compareTo(Student o) {// int res = this.age - o.age;// 使用包装类的compareint 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再比较dayComparator.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.mzph.cn/news/812557.shtml

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

相关文章

应用实战|从头开始开发记账本2:基于模板快速开始

上期视频我们创建好了BaaS服务的后端应用。从这期视频开始&#xff0c;我们将从头开发一个互联网记账本应用。本期视频我们介绍一下如何使用模板快速开启我们的应用开发之旅。 应用实战&#xff5c;从头开始开发记账本2&#xff1a;基于模板快速开始 相关代码 本期视频我们介绍…

RestTemplate—微服务远程调用—案例解析

简介&#xff1a;总结来说&#xff0c;微服务之间的调用方式有多种&#xff0c;选择哪种方式取决于具体的业务需求、技术栈和架构设计。RESTful API和HTTP客户端是常见的选择&#xff0c;而Feign和Ribbon等辅助库可以简化调用过程。RPC和消息队列适用于特定的场景&#xff0c;如…

《由浅入深学习SAP财务》:第2章 总账模块 - 2.6 定期处理 - 2.6.4 月末操作:货币折算

2.6.4 月末操作&#xff1a;货币折算 如果一个公司代码启用了多个本位币&#xff0c;如下表所示&#xff0c;则在平时记账时&#xff0c;系统会在凭证行项目中同时体现出多个本位币的金额。 图2.6.4-1 两个本位币的金额都会实时更新到科目余额中。因此&#xff0c;在月末可以直…

pycharm2024关闭项目后一直显示正在关闭项目

网上的很多教程都试了不行&#xff0c;直接用下面的方法有效解决。 点击 帮助--查找操作--输入Registry--点注册表&#xff0c;取消ide.await.scope.completion后的勾选即可。

武汉星起航:跨境电商势头强劲,开启外贸增长新纪元

在全球化浪潮的推动下&#xff0c;跨境电商作为新兴贸易方式&#xff0c;正以前所未有的速度崛起。2024年前两个月&#xff0c;跨境电商进出口增长近10%&#xff0c;这一令人瞩目的数据&#xff0c;不仅彰显出跨境电商的强劲发展势头&#xff0c;更预示着其作为外贸新增长极的潜…

直接扩展到无限长,谷歌Infini-Transformer终结上下文长度之争

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 新建了免费的人工智能中文站https://ai.weoknow.com 新建了收费的人工智能中文站https://ai.hzytsoft.cn/ 更多资源欢迎关注 不知 Gemini 1.5 Pro 是否用到了这项技术。 谷歌又放大招了&#xff0c;发布下一代 Transfor…

突破像素限制,尽显照片细腻之美——Topaz Gigapixel AI for Mac/Win

在这个数字化的时代&#xff0c;我们都热爱用照片记录生活中的美好瞬间。然而&#xff0c;有时候我们会发现&#xff0c;由于各种原因&#xff0c;照片的像素可能无法满足我们的需求。这时候&#xff0c;Topaz Gigapixel AI for Mac/Win 这款强大的照片放大工具应运而生。 Top…

联储降息预期落空打了谁的脸

美国 3 月消费者价格指数&#xff08;CPI&#xff09;于本周发布&#xff0c;最新数据全线高于预期。具体而言&#xff0c;美国劳工部周三公布的数据显示&#xff0c;美国 3 月消费者物价指数&#xff08;CPI&#xff09;同比上涨 3.5%&#xff0c;为 2023 年 9 月以来最高水平…

python基础——类【类的定义和使用、魔术方法】

&#x1f4dd;前言&#xff1a; python中的类&#xff0c;自我感觉在某种程度上和C语言的结构体是有共同之处的&#xff0c;如果有兴趣&#xff0c;可以先看看这篇文章&#xff1a;C语言——结构体类型&#xff08;一&#xff09;&#xff0c;先了解一下C语言中的结构体&#x…

在Ubuntu上安装Docker Compose

Docker Compose 是一个用于定义和管理Docker容器的工具&#xff0c;它使用yml来配置应用的服务、网络和卷等。特别是在定义多个容器时&#xff0c;它非常擅长定义多个容器之间的关系和依赖。 第一步&#xff1a;更新软件包 sudo apt update第二步&#xff1a;安装网络工具cur…

基于FMC接口的Kintex-7 XC7K325T PCIeX4 3U PXIe接口卡

基于FMC接口的Kintex-7 XC7K325T PCIeX4 3U PXIe接口卡 一、板卡概述 本板卡基于Xilinx公司的FPGAXC7K325T-2FFG900 芯片&#xff0c;pin_to_pin兼容FPGAXC7K410T-2FFG900 &#xff0c;支持PCIeX8、64bit DDR3容量2GByte&#xff0c;HPC的FMC连接器&#xff0c;板卡支持PXI…

【INNODB引擎篇】深奥探究Innodb存储引擎

&#x1f525;作者主页&#xff1a;小林同学的学习笔录 &#x1f525;mysql专栏&#xff1a;小林同学的专栏 目录 1.InnoDB引擎 1.1 逻辑存储结构 1.2 架构 1.2.1 概述 1.2.2 内存结构 1.2.3 磁盘结构 1.2.4 后台线程 1.3 事务原理 1.3.1 事务基础 1.3.2 redo log 1.…

时间复杂度详解1——定义和简单计算

算法效率的度量方法 事后统计方法 事后统计方法&#xff1a;这种方法主要是通过设计好的测试程序和数据&#xff0c;利用计算机计时器对不同算法编制的程序的运行时间进行比较&#xff0c;从而确定算法效率的高低。 但这种方法显然是有很大缺陷的&#xff1a; 必须依据算法事先…

分布式系统:缓存与数据库一致性问题

前言 缓存设计是应用系统设计中重要的一环&#xff0c;是通过空间换取时间的一种策略&#xff0c;达到高性能访问数据的目的&#xff1b;但是缓存的数据并不是时刻存在内存中&#xff0c;当数据发生变化时&#xff0c;如何与数据库中的数据保持一致&#xff0c;以满足业务系统…

02-攻防世界PHP2

题目 authenticate:证明什么是真的 解题 观察题目可知&#xff0c;访问index.phps可能会有不一样的发现 http://61.147.171.105:51671/index.phps访问该链接&#xff0c;可以得到下面的界面 这里只显示出了部分代码&#xff0c;右键该界面&#xff0c;点击查看源代码&#xf…

使用ArrayList.removeAll(List list)导致的机器重启

背景 先说一下背景&#xff0c;博主所在的业务组有一个核心系统&#xff0c;需要同步两个不同数据源给过来的数据到redis中&#xff0c;但是每次同步之前需要过滤掉一部分数据&#xff0c;只存储剩下的数据。每次同步的数据与需要过滤掉的数据量级大概在0-100w的数据不等。 由…

【学习】移动端兼容性测试有什么方法及重要性

随着移动互联网的快速发展&#xff0c;移动应用程序已经成为人们日常生活中不可或缺的一部分。然而&#xff0c;由于各种移动设备的硬件和软件差异&#xff0c;移动应用程序的兼容性问题也越来越突出。因此&#xff0c;移动端兼容性测试成为了一个重要的环节&#xff0c;它可以…

如何在 Android 设备上恢复已删除/丢失的文档

随着Android设备内存容量的不断增加&#xff0c;许多人将手机作为移动硬盘来存储大量文档或其他文件。由于某些原因&#xff0c;文件丢失绝对是一场彻头彻尾的噩梦&#xff0c;因为里面的数据可能是要汇报的学习档案、领导会议的安排、或者付费电子书等。通常&#xff0c;你首先…

02_JavaWeb中的Tomcat(详解)

文章目录 Tomcat1, 概述1.1 安装1.2 目录结构1.3 启动/停止 2, 资源部署2.1 直接部署: 主要和重要的方式2.2 虚拟映射: 重要2.2.1 方式一:2.2.1 方式二: 2.3 原理解析 3, Tomcat组件3.1 Connector3.2 Engine3.2.1 Host3.2.1.1 Context 4, 其它: 重要4.1 设置 Tomcat 1, 概述 w…

sql server2008触发器

sql server在Navicat工具不能插入数据 可以去写代码插入&#xff0c;代码连接sql server可以插入 或者使用sql server专门的工具 BEGINdeclare a int;declare s t_amount;select a baddebt_age_id,srate from aa_baddebt_age;INSERT INTO dade(id,name) VALUES(a,s) END