首页/新闻资讯/正文详情

Java对象比较与克隆:Comparable、Comparator接口与深拷贝实现

发布时间:2026/9/27 8:38:56 来源:云帆数科 栏目:资讯中心
Java对象比较与克隆:Comparable、Comparator接口与深拷贝实现
接口使用实例以及Object类中的equals,hashcode方法1.Comparable 接口实现2.Comparator 接口实现3.Clonable 接口和深拷贝4.对象比较equals方法5.hashcode方法1.Comparable 接口实现实现该接口时需要加上一个泛型类比 C的模板Class Student implements ComparableStudent,我们比较什么类型就填什么类型在我们了解了 Comparable 接口之后会知道里面存在一个方法compareTo()因此compareTo()需要我们去重写这其实就是一个比较规则需要怎么比较类型可以由我们来完成我们举出一个例子如下classStudentimplementsComparableStudent{publicStringname;publicintage;publicStudent(Stringname,intage){this.namename;this.ageage;}OverridepublicintcompareTo(Studento){//通过年龄比较returnthis.age-o.age;//通过名字比较//return this.name.compareTo(o.name);}}publicclassTest{publicstaticvoidmain(String[]args){Studentstd1newStudent(小李,20);Studentstd2newStudent(小刘,19);System.out.println(std1.compareTo(std2));}}//运行结果为 1有人会好奇为什么用名字比较会这么写return this.name.compareTo(o.name);可以发现String 类型中也有 compareTo()方法因此我们直接调用即可当我们将用到数组对对象进行排序时中间的对比过程正是运用了类似这个步骤classStudent/*implements ComparableStudent*/{publicStringname;publicintage;publicStudent(Stringname,intage){this.namename;this.ageage;}OverridepublicStringtoString(){returnStudent {namename, ageage};}/*Override public int compareTo(Student o) { return this.age - o.age; //return this.name.compareTo(o.name); }*/}publicclassTest{publicstaticvoidmain(String[]args){Studentstd[]newStudent[3];std[0]newStudent(小李,20);std[1]newStudent(小刘,19);std[2]newStudent(小新,18);Arrays.sort(std);System.out.println(Arrays.toString(std));}}我们继续使用上面那个例子将compareTo()方法以及接口屏蔽 会发现运行到Arrays.sort(std);这里的时候发生异常ClassCastException报错信息中 为类型转换异常class demo3.Student cannot be cast to class java.lang.Comparable也就是说 Student 不能被转换为 Comparable 型图片中的意思是这是报错信息中的一个方法Object[] a 是我们传 Student std[] 的形参这是原本正确运行需要进行的但是 我们现在连 Comparable 接口都还没有实现怎么能强转为 Comparable 类型呢更不用说调用 compareTo()方法了因此当我们放开 Comparable 实现和 compareTo()方法重写那么 Arrays.sort()就会按照我们是按名字还是按年龄写的 compareTo()方法去排序。实际上也就是把比较规则放出来别人才能拿来比较那么我们得到的结论就是只要是自定义的类型涉及到了大小比较那就得一定实现 Comparable 接口至于Arrays.sort()内部是怎样实现的我给出一个大概的模拟实现实际内部更复杂类似冒泡排序的排序方法publicstaticvoidmySort(Comparable[]comparables){for(inti0;icomparables.length-1;i){for(intj0;jcomparables.length-1-i;j){if(comparables[j].compareTo(comparables[j1])0){Comparabletmpcomparables[j];comparables[j]comparables[j1];comparables[j1]tmp;}}}}为什么要用Comparable[] comparables作为形参呢因为就是我们前面说的向上转型一样转为Comparable 类型我们就可以直接使用这个接口中的方法。再整体看一下这段代码classStudentimplementsComparableStudent{publicStringname;publicintage;publicStudent(Stringname,intage){this.namename;this.ageage;}OverridepublicStringtoString(){returnStudent {namename, ageage};}OverridepublicintcompareTo(Studento){//方法二将this和o调换位置也可以变为降序returnthis.age-o.age;//return this.name.compareTo(o.name);}}publicclassTest1{publicstaticvoidmySort(Comparable[]comparables){for(inti0;icomparables.length-1;i){for(intj0;jcomparables.length-1-i;j){//方法一这里大于零做的是一个升序的排序改为小于零就是降序了if(comparables[j].compareTo(comparables[j1])0){//swap(comparables, j, j1);Comparabletmpcomparables[j];comparables[j]comparables[j1];comparables[j1]tmp;}}}}// public static T void swap(T[] arr, int i, int j) {// T tmp arr[i];// arr[i] arr[j];// arr[j] tmp;// }publicstaticvoidmain(String[]args){Studentstd[]newStudent[3];std[0]newStudent(小李,20);std[1]newStudent(小刘,19);std[2]newStudent(小新,18);mySort(std);System.out.println(Arrays.toString(std));//System.out.println(std1.compareTo(std2));}}但是这个接口的方法存在一定的局限性就是我有时候想用年龄去排可以但我有时候又想用名字去排但这个方法不能共存只能一个compareTo()方法比较器/也就是比较规则补充建议其实像上面提到的用年龄比较OverridepublicintcompareTo(Studento){//通过年龄比较returnthis.age-o.age;}我们实际上还可以用它类型自带的compare()方法下面我们举一个例子让compareTo()按score 从高到低排序如果分数相同则按年龄从小到大排序。OverridepublicintcompareTo(Studento){intcmpDouble.compare(o.score,this.score);if(cmp0){cmpInteger.compare(this.age,o.age);}returncmp;}这里我们就用到了整型自带的Integer.compare()方法和 double 型自带的Double.compare()方法为什么建议用类型自带的方法呢而不用以下这种OverridepublicintcompareTo(Studento){if(this.scoreo.score){return1;// 分数低的排在后面}elseif(this.scoreo.score){return-1;// 分数高的排在前面}else{returno.age-this.age;// 分数相同时年龄大的排前}}问题compareTo 的返回值逻辑不够直观 那样写更安全避免浮点数误差问题 逻辑清晰可读性高 不会因为比较浮点数出问题2.Comparator 接口实现我们可以创建一个新的类NameComparatorimportjava.util.Comparator;publicclassNameComparatorimplementsComparatorStudent{Overridepublicintcompare(Studento1,Studento2){returno1.name.compareTo(o2.name);}}以及另外一个新的类AgeComparatorimportjava.util.Comparator;publicclassAgeComparatorimplementsComparatorStudent{Overridepublicintcompare(Studento1,Studento2){//按照降序排returno1.age-o2.age;}}这个方法不需要依赖 Student类 去实现任何接口AgeComparatorageComparatornewAgeComparator();System.out.println(ageComparator.compare(std[0],std[1]));直接实例化调用如果需要升序降序我们可以再去分类写几个升序降序的类即可即我们可以同时存在多个类似地比较方法只需要放在不同类去实现接口重写 compare()方法就行3.Clonable 接口和深拷贝当我们先了解到下面要讲的 Object 类之后可以再回头看看Object 类中有一个 clone 方法这个方法的作用就是字面意思克隆一个对象举一个例子publicclassPersonimplementsCloneable{publicintage;OverrideprotectedObjectclone()throwsCloneNotSupportedException{returnsuper.clone();}OverridepublicStringtoString(){returnPerson{ageage};}}使用步骤创建 Person 类然后实例化一个 person1接着再克隆给对象 person22.重写 Object 父类的 clone() 方法因为该方法在 Object 中是 protected 访问权 [AltIns 键 选择重写克隆 clone()方法即可]4.注意返回值是父类 Object所以我们需要向下转型为 Person5.在 Person类 中实现 Cloneable 接口6.抛去异常处理public static void main(String[] args) throws CloneNotSupportedException补充一下Cloneable 是空接口空接口也叫标记接口表示当前类可以被克隆publicstaticvoidmain(String[]args)throwsCloneNotSupportedException{Personperson1newPerson();person1.age19;Personperson2(Person)person1.clone();System.out.println(person2.toString());}我们再拓展一下新增一个 Money 类classMoney{publicdoublemoney19.9;}publicclassPersonimplementsCloneable{publicintage;publicStringname;publicPerson(intage,Stringname){this.ageage;this.namename;}MoneymnewMoney();OverrideprotectedObjectclone()throwsCloneNotSupportedException{returnsuper.clone();}OverridepublicStringtoString(){returnPerson{ageage, namename\, moneym.money};}}此时在栈和堆中是这样的引用同一个 m 对象我们因此称之为浅拷贝如果 m 对象也被拷贝了那就是深拷贝所以我们就进行如下操作变为深拷贝吧先让 Money 能够被克隆classMoneyimplementsCloneable{publicdoublemoney19.9;OverrideprotectedObjectclone()throwsCloneNotSupportedException{returnsuper.clone();}}然后再修改一下克隆方法protectedObjectclone()throwsCloneNotSupportedException{Persontmp(Person)super.clone();tmp.m(Money)m.clone();returntmp;}我们再进行测试一下即可publicstaticvoidmain(String[]args)throwsCloneNotSupportedException{Personperson1newPerson(19,小李);person1.age19;Personperson2(Person)person1.clone();System.out.println(修改前person1.toString());System.out.println(修改前person2.toString());person2.m.money100;System.out.println(修改后person1.toString());System.out.println(修改后person2.toString());}返回的结果修改前Person{age19, name‘小李’, money19.9}修改前Person{age19, name‘小李’, money19.9}修改后Person{age19, name‘小李’, money19.9}修改后Person{age19, name‘小李’, money100.0}此时的 Person2 的 m 对象是另外一个独立的对象与 Person 引用的 m 不是同一个4.对象比较equals方法在Java中进行比较时a.如果左右两侧是基本类型变量比较的是变量中值是否相同b.如果左右两侧是引用类型变量比较的是引用变量地址是否相同c.如果要比较对象中内容必须重写Object中的equals方法因为equals方法默认也是按照地址比较的// Object类中的equals方法publicbooleanequals(Objectobj){return(thisobj);// 使用引用中的地址直接来进行比较}我们自己写一个比较引用类型中的基本类型比较(eg:Person类中的name和age)publicbooleanequals(Objectobj){if(objnull){returnfalse;}if(thisobj){returntrue;}// 不是Person类对象if(!(objinstanceofPerson)){returnfalse;}Personperson(Person)obj;// 向下转型比较属性值returnthis.name.equals(person.name)this.ageperson.age;}结论比较对象中内容是否相等的时候一定要重写equals方法。5.hashcode方法我们回忆一下Object中的toString方法// Object类中的toString()方法实现publicStringtoString(){returngetClass().getName()Integer.toHexString(hashCode());}我们看到了hashCode()这个方法他帮我算了一个具体的对象位置这里面涉及数据结构但是我们还没学数据结构没法讲述所以我们只能说它是个内存地址。然后调用Integer.toHexString()方法将这个地址以16进制输出。hashcode方法源码publicnativeinthashCode();该方法是一个native方法底层是由C/C编写无法观察到我们认为两个名字相同年龄相同的对象将存储在同一个位置如果不重写hashcode()方法我们可以来看示例代码classPerson{publicStringname;publicintage;publicPerson(Stringname,intage){this.namename;this.ageage;}}publicclassTestDemo4{publicstaticvoidmain(String[]args){Personper1newPerson(Sirens,20);Personper2newPerson(Sirens,20);System.out.println(per1.hashCode());System.out.println(per2.hashCode());}}//执行结果4601419581163157884注意事项两个对象的hash值不一样。像重写equals方法一样我们也可以重写hashcode()方法。此时我们再来看看。classPerson{publicStringname;publicintage;publicPerson(Stringname,intage){this.namename;this.ageage;}OverridepublicinthashCode(){returnObjects.hash(name,age);}}publicclassTestDemo4{publicstaticvoidmain(String[]args){Personper1newPerson(Sirens,20);Personper2newPerson(Sirens,20);System.out.println(per1.hashCode());System.out.println(per2.hashCode());}}//执行结果460141958460141958注意事项哈希值一样。结论1、hashcode方法用来确定对象在内存中存储的位置是否相同2、事实上hashCode() 在散列表中才有用在其它情况下没用。在散列表中hashCode() 的作用是获取对象的散列码进而确定该对象在散列表中的位置。以上是我关于Java的笔记分享感谢你读到这里这也是我学习路上的一个小小记录。希望以后回头看时能看到自己的成长~

相关推荐

抖音批量下载器怎么用:Cookie、去重与直播存档一次讲清
抖音批量下载器怎么用:Cookie、去重与直播存档一次讲清

抖音批量下载器怎么用:Cookie、去重与直播存档一次讲清 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback supp… · 2026/9/27 8:38:50

免费网站建站2773避坑指南:从丑到爆的实操全解
免费网站建站2773避坑指南:从丑到爆的实操全解

免费网站建站2773避坑指南:从丑到爆的实操全解 别再看那些一眼假的模板网站了,真不够用! 想靠【免费网站建站2773】白嫖流量,结果做出来的页面像2005年的互联网遗迹?… · 2026/9/27 8:38:44

3个维度拆解哪些网站是做免费推广的完整流程
3个维度拆解哪些网站是做免费推广的完整流程

3个维度拆解哪些网站是做免费推广的完整流程 域名服务器搞不懂,是很多新手接手“免费推广”项目时的第一道坎。别被“免费”二字骗了,真正能落地的 完整流程 ,核心在于技术选型的成本与效果平衡。 1. 先厘清概念:哪些网站能做免费推广… · 2026/9/27 8:38:44

在 Puma 集群模式下使用 gRPC:生命周期钩子完整实战指南
在 Puma 集群模式下使用 gRPC:生命周期钩子完整实战指南

后端网络 【免费下载链接】puma A Ruby/Rack web server built for parallelism 项目地址: https://gitcode.com/gh_mirrors/pu/puma 点击查看 免费下载 导读 本指南围绕 Puma(Ruby/Rack 并行 Web 服务器)的集群模式(Clustered … · 2026/9/27 9:14:55

hunterplayer开发笔记
hunterplayer开发笔记

开发了一款猎头和HR的搜索人才工具。通过网盘分享的文件:hunterplayer.zip 链接: https://pan.baidu.com/s/1_-xLY3-JCcceDccYMPbo8A?pwdsgaf 提取码: sgaf 在conda prompt中使用conda activate image ,image使用python 3.13.12。直接在vs中activate不… · 2026/9/27 9:14:55

网站建设实践报告绪论图解步骤拆解与选型避坑
网站建设实践报告绪论图解步骤拆解与选型避坑

网站建设实践报告绪论图解步骤拆解与选型避坑 网站做好了没人访问,这比做不出网站更让人崩溃。我见过太多独立站长,前端代码写得行云流水,服务器配置得滴水不漏,结果上线三个月,后台数据除了蜘蛛爬虫就是… · 2026/9/27 9:14:55

Mosquitto 1.2.3 版本发布解析:Broker、客户端库与 CLI 工具的修复细节
Mosquitto 1.2.3 版本发布解析:Broker、客户端库与 CLI 工具的修复细节

物联网消息队列后端 【免费下载链接】mosquitto Eclipse Mosquitto - An open source MQTT broker 项目地址: https://gitcode.com/gh_mirrors/mosquit/mosquitto 点击查看 免费下载 导读 本文基于 Mosquitto 仓库的 v1.2.3 发布说明,逐条梳理这一 bug… · 2026/9/27 9:14:49

AgentOps 快速上手:两行代码接入 AI Agent 可观测
AgentOps 快速上手:两行代码接入 AI Agent 可观测

AgentOps 快速上手:两行代码接入 AI Agent 可观测 【免费下载链接】agentops Python SDK for AI agent monitoring, LLM cost tracking, benchmarking, and more. Integrates with most LLMs and agent frameworks including CrewAI, Agno, OpenAI Agents SDK, Lang… · 2026/9/27 9:14:49

Enquirer Form Prompt 入门指南:从单个输入到多字段表单的完整实战
Enquirer Form Prompt 入门指南:从单个输入到多字段表单的完整实战

开发工具 【免费下载链接】enquirer Stylish, intuitive and user-friendly prompts. Used by eslint, webpack, yarn, pm2, pnpm, RedwoodJS, FactorJS, salesforce, Cypress, Google Lighthouse, Generate, tencent cloudbase, lint-staged, gluegun, hygen, hardhat, AWS Am… · 2026/9/27 9:14:43

MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现

简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01

汕头网站建设制作厂家避坑指南:5大注意事项救急
汕头网站建设制作厂家避坑指南:5大注意事项救急

汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01

多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习

简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01

MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现

简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01

汕头网站建设制作厂家避坑指南:5大注意事项救急
汕头网站建设制作厂家避坑指南:5大注意事项救急

汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01

多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习

简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码