【Java基础篇 | 面向对象】—— 聊聊什么是接口(下篇)

news2025/1/23 3:10:05

个人主页:兜里有颗棉花糖
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创
收录于专栏【JavaSE_primary】
本专栏旨在分享学习JavaSE的一点学习心得,欢迎大家在评论区交流讨论💌
在这里插入图片描述

上篇(【Java基础篇 | 面向对象】—— 聊聊什么是接口(上篇))中我们已经对Java接口中有了一定的了解。本篇中我们将对Java接口进行更进一步的学习。加油吧!!!

目录

  • 一、接口使用实例
    • 比较器(Comparator)
  • 二、Clonable接口和深拷贝
    • 浅拷贝
    • 深拷贝
  • 三、Object类
    • 对象比较equals()方法
    • hashcode()方法

一、接口使用实例

首先我们要使用记住一句话,对象与对象之间进行比较的话一定要实现对应的接口。只有我们实现了对应的接口之后才能证明这两个对象是可比较的

现在有一个整数数组,我们当然可以使用sort()方法来对这个整数数组进行升序或者降序排序。但是如果我们现在有一个学生类对象呢我们是无法直接拿两个学生类对象进行直接排序的。此时我们应该参照学生类中的某个属性来对这个学生类对象进行排序以达到我们想要的排序效果。

现在我们就以学生类中的年龄属性来进行排序吧:

我们在进行自定义类型的对象比较的时候,一定要实现可以比较的接口。比如如果我们的Student类实现Comparable接口, 并实现其中的compareTo方法。否则的话自定义类型的对象是无法进行比较的。

如下图就是我们实现的Comparable接口中的compareTo方法。
在这里插入图片描述
如果我们要比较两个对象的引用的话(两个学生类对象按照年龄来进行排序),我们可以这样来写,请看:
在这里插入图片描述
如果我们要比较的是一个学生类对象数组的话(按照年龄来进行比较),我们可以这样,请看:
在这里插入图片描述
运行结果如下:
在这里插入图片描述

现在我们来试着使用自己写一个排序方法(冒泡排序)来对学生类对象进行排序。
请看下面我们自己实现的冒泡排序来对学生类对象按照年龄进行排序。代码如下:
在这里插入图片描述
运行结果如下:
在这里插入图片描述
现在我们来对上述冒泡排序中的代码进行解释:
排序的时候我们排序的是一个学生数组(按照年龄来进行排序),所以我们在进行排序的时候底层一定会去调用compareTo方法,所以冒泡排序中的参数一定为Comparable[] comparables,即接口数组。另外array数组(即学生类对象数组)中的每个元素都是一个学生类对象,而且每个学生类对象都实现了compareTo方法

比较器(Comparator)

好了,现在我们换一种排序的写法。上述学生类中有年龄也有分数,如果我们一会想依据年龄进行排序,一会又想用分数进行排序的话,如果按照compareTo()方法完成上述排序的话,那么根据我们比较依据的不同那么compareTo()方法中的内容也是不一样的(即我们需要修改compareTo方法中的内容)。
在这里插入图片描述
请看上图,上图中的compareTo()只能对学生类对象中的年龄进行排序而无法对学生类中的成绩进行排序,所以排序的内容就比较单一。此时我们就需要Comparator接口

好了,现在我们利用Comparator接口来实现学生类对象的排序工作,代码如下图,请看:
在这里插入图片描述

具体代码如下,请看:

import com.sun.javaws.IconUtil;
import java.util.Comparator;

class Student{
    public String name;
    public int age;
    public double score;

    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
}
// 这里我们利用了解耦的思想
class AgeComparator implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.age - o2.age;
    }
}
class ScoreComparator implements Comparator<Student>{
    @Override
    public int compare(Student o1,Student o2) {
        return (int)(o1.score - o2.score);
    }
}
public class Test {
    public static void main(String[] args) {
        Student student1 = new Student("jkl",1,87.3);
        Student student2 = new Student("ajk",2,87.3);

        // 依据年龄进行比较
        AgeComparator ageComparator = new AgeComparator();
        int ret = ageComparator.compare(student1,student2);
        System.out.println("ret = " + ret);

        // 依据成绩进行比较
        ScoreComparator scoreComparator = new ScoreComparator();
        int ret2 = scoreComparator.compare(student1,student2);
        System.out.println("ret = " + ret2);
    }
}

运行结果如下:
在这里插入图片描述
对比一下这两种接口(Comparator接口Comparable接口):经过上述的演示,我们不难发现Comparator接口更加的灵活。

二、Clonable接口和深拷贝

浅拷贝

浅拷贝概念:浅拷贝是指在对一个对象进行拷贝时,只拷贝对象本身和其中的基本数据类型,而不拷贝对象内部的引用类型。因此,在浅拷贝的对象中,引用类型的变量指向的依旧是原始对象中的引用。

下面来进行举例。

现在我们有一个Student学生类,如下图:
在这里插入图片描述
同时新创建了一个学生类对象student1,该对象对应的内存结构图如下:
在这里插入图片描述
现在我们想要把student引用所指向的对象克隆出来一份,如下图的克隆方式是错误的:
在这里插入图片描述
要解决上述错误的话,我们需要修改三个地方。如下图在这里插入图片描述
好了,现在重新运行一下程序,发现还是会报错,请看:
在这里插入图片描述
我们需要实现一个接口以证明当前的类是可以进行克隆的。
在这里插入图片描述
运行结果如下:
在这里插入图片描述

浅拷贝的对象中,引用类型的变量指向的依旧是原始对象中的引用。请看举例:
在这里插入图片描述
运行结果如下:
在这里插入图片描述
解释:通过调用clone()方法,创建了student1的一个克隆对象student2。克隆的实现是通过调用Object类的clone()方法来完成的。
输出结果显示了两次student1.m.money和student2.m.money的值,分别为52.0。这是因为浅拷贝只是简单地复制字段的值,而对于引用类型的字段,只复制了引用地址,并没有复制该引用指向的实际对象。因此,student1和student2的m字段引用同一个Money对象。

在这里插入图片描述

深拷贝

深拷贝是指在对一个对象进行拷贝时,不仅拷贝对象本身和其中的基本数据类型,同时也拷贝对象内部的引用类型。因此,在深拷贝的对象中,引用类型的变量指向的是全新的对象。

好了,现在来总结一下:clone方法是Object类中的一个方法,调用这个方法可以创建一个对象的 “拷贝”. 但是要想合法调用 clone 方法,必须要先实现Clonable接口, 否则就会抛出CloneNotSupportedException异常.

下面是深拷贝的代码举例:

class Money implements Cloneable {
    public double money;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
class Student implements Cloneable {
    public int age;
    public Money m = new Money();

    public Student(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                '}';
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        //return super.clone();
        Student tmp = (Student)super.clone();
        tmp.m = (Money)this.m.clone();
        return tmp;
    }
}
public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Student student1 = new Student(21);
        student1.m.money = 52.0;
        Student student2 = (Student)student1.clone();
        System.out.println(student1.m.money);
        System.out.println(student2.m.money);
        System.out.println("分割线--------------");
        student1.m.money = 18.8;
        System.out.println(student1.m.money);
        System.out.println(student2.m.money);
    }
}

运行结果如下:
在这里插入图片描述
在这里插入图片描述
好了,现在我们再来回顾一下什么是深拷贝:深拷贝就是在拷贝对象的同时创建一个新的对象,并将原对象中的所有数据逐个拷贝到新对象中去,包括成员变量引用的其他对象。这样可以确保原对象和拷贝对象之间的数据相互独立,互不影响。

三、Object类

在Java中,所有的类都直接或间接地继承自java.lang.Object类。Object类是Java类层次结构中的根类,它提供了一些通用的方法和功能,可以在所有类中使用。可以这么认为,Object类是所有类的父类。所以在Java中,即使我们不显式地在类声明中使用extends关键字继承Object类,所有的类仍然会隐式地继承Object类。这是因为Object类是Java类层次结构中的根类,所有的类都直接或间接继承自它。

对象比较equals()方法

在这里插入图片描述
如上图:使用equals()方法来比较两个对象是否相等。
如果在Student类中没有重写equals()方法,则默认会使用Object类中的equals()方法,它执行的是比较对象引用的相等性(即比较两个对象在内存中的地址是否相同)。
因此,上图中的代码的最终结果就是False但是,如果我想要按照我们自己的方式来比较这两个对象是否相等的话我们就需要自己去重写equals()方法

现在,如果以两个对象的年龄是否相等为依据来判断两个对象是否相等的话,重写的equals()方法如下:
在这里插入图片描述
如果我们相比较对象中的内容是否相等的话,我们需要根据自己的判断依据来重写Object类中的equals()方法

hashcode()方法

hashcode()方法用于返回对象的hash码,相当于对象的标识符,它可以将对象转换为整数以便更好的比较、存储对象。

好了,现在来举个栗子,假设当两个对象的年龄是一样的话,那么我们认为这两个对象存储的位置是相同的,请看代码:
在这里插入图片描述
运行结果如下:
在这里插入图片描述
如上图中的运行结果,虽然两个对象的年龄是不同的,但是这两个对象存储的位置确实相同的,这与我们判断两个对象是否相同的判断依据发生了冲突。
此时我们就需要自己重写hashcode()方法。

最终代码如下,请看:

import java.util.Objects;

class Money implements Cloneable {
    public double money;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Money money1 = (Money) o;
        return Double.compare(money1.money, money) == 0;
    }

    @Override
    public int hashCode() {
        return Objects.hash(money);
    }
}
class Student implements Cloneable {
    public int age;
    public Money m = new Money();

    public Student(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                '}';
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        //return super.clone();
        Student tmp = (Student)super.clone();
        tmp.m = (Money)this.m.clone();
        return tmp;
    }

    @Override
    public boolean equals(Object obj) {
        Student student = (Student)obj;
        return age == student.age;
    }

    @Override
    public int hashCode() {
        return Objects.hash(age, m);
    }
}
public class Test {
    public static void main(String[] args) {
        Student student1 = new Student(19);
        Student student2 = new Student(19);
        System.out.println(student1.hashCode());
        System.out.println(student2.hashCode());
    }
}

运行结果如下:
在这里插入图片描述
此时就说明这两个对象的位置是相同的。

小总结:

  • hashcode()方法用来确定对象在内存中存储的位置是否相同。
  • 实际上,hashcode()在散列表中才会用到(在散列表中hashcode()的作用就是获取对象的散列码,进而确定该对象在散列表中的位置);然而hashcode()在其它情况下没多大用。
  • 如果一个类没有重写hashCode()equals() 方法,那么它将使用从Object类继承而来的默认实现。如果默认实现的hashCode()equals()方法不符合我们的需求,此时我们就需要自己重写hashCode()equals()方法。
  • 定义一个自定义类型时,应该养成重写hashCode()equals()方法的习惯。

好了,本文到这里就结束了,再见啦友友们!!!
在这里插入图片描述

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

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

相关文章

船舶机电设备智能故障诊断系统

1 船舶电气设备故障诊断专家系统 体系结构 整个系统的体系结构采用浏览器&#xff0f; 服务器 &#xff08;B&#xff0f; S&#xff09; 三层体系结构 &#xff08; 如图 1 所示 &#xff09;。B&#xff0f;S 模式是一种 以 Web 技术为基础的系统平台模式 。 把传统…

LeetCode刷题--- 求根节点到叶节点数字之和

个人主页&#xff1a;元清加油_【C】,【C语言】,【数据结构与算法】-CSDN博客 个人专栏&#xff1a;http://t.csdnimg.cn/ZxuNL http://t.csdnimg.cn/c9twt 前言&#xff1a;这个专栏主要讲述递归递归、搜索与回溯算法&#xff0c;所以下面题目主要也是这些算法做的 我讲述…

协同过滤算法springboot+java+ssm高校图书馆图书借阅管理系统k32vr

课题主要分为四大模块&#xff1a;即管理员模块&#xff0c;员工模块&#xff0c;教师模块和学生模块&#xff0c;主要功能包括&#xff1a;首页&#xff0c;个人中心&#xff0c;员工管理&#xff0c;学生管理&#xff0c;教师管理&#xff0c;图书分类管理&#xff0c;图书信…

交通|车辆路径启发式中的分解策略

推文作者&#xff1a;Amiee&#xff0c;研究方向&#xff1a;交通物流 编者按&#xff1a; VRP问题由于其理论难度和实践价值&#xff0c;学者们设计了大量的精确或启发式求解算法。本文讨论了车辆路径启发式中分解技术的主要特征&#xff0c;强调了它们的优缺点&#xff0c;并…

唱响主旋律——建行江门市分行推动服务实体经济高质量发展

建行江门市分行主动对接当地战略部署&#xff0c;在侨乡热土踏歌而行&#xff0c;全力当好服务实体经济的主力军和维护金融稳定的压舱石&#xff0c;在助力再造一个现代化新江门上贡献建行力量。 输血实体 为实体经济服务是金融的天职。建行江门市分行积极发挥在重大基建领域…

爱智EdgerOS之深入解析如何应用爱智的视频流模块完成拉流

一、ONVIF 规范和常见视频流传输协议 ① ONVIF 规范 随着视频监控产业链的成熟&#xff0c;市面上陆陆续续出现了各式各样的网络摄像设备&#xff0c;这些设备都需要通讯协议才能进行数据传输。早期厂商都采用私有协议&#xff0c;但是现在厂商分工明确&#xff0c;有的负责生…

用Rust刷LeetCode之26 删除有序数组中的重复项

26. 删除排序数组中的重复项[1] 难度: 简单 老的描述: 新的描述: 注意是 排序数组,非严格递增排列,即已经是排好序的,只不过有重复元素 func removeDuplicates(nums []int) int { if len(nums) 0 { return 0 } i : 0 for j : 1; j < len(nums); j { …

电源小白入门学习3——电源系统常见元件选型MOS管、二极管、电感篇

电源小白入门学习3——电源系统常见元件选型MOS管、二极管、电感篇 MOS管二极管电感 书接上文&#xff0c;上一期我们讲了电阻、电容选型中需要注意的事项&#xff0c;下面我们接着来介绍MOS管和二极管。 MOS管 关于MOS管的基本原理和内部的一些结构&#xff0c;PN结、半导体的…

uniapp切换页面时报错问题

我们来看如下错误&#xff1a; 该错误的意思是不能切换到 tabbar 页面。tabbar页面通常是公共页面或者底部导航栏&#xff0c;如果我们用 navigateTo 或者 redirectTo 都不能实现页面切换。 我们有两种方式&#xff1a; 第一种是用 switchTab 来进行切换&#xff0c;但注意切…

Kettle 安装配置

文章目录 Kettle 安装配置Kettle 安装Kettle 配置连接 Hive Kettle 安装配置 Kettle 安装 在安装Kettle之前&#xff0c;需要确定已经安装Java运行环境。Kettle需要Java的支持才能运行&#xff0c;JDK的版本最好是8.x的太新的也会出现bug。Kettle的7.1版本的太旧了&#xff0…

【PWN】学习笔记(一)【二进制基础】

目录 课程教学一次简单的Hack程序的编译与链接Linux下的可执行文件格式ELF进程虚拟地址空间程序的编译与链接程序的装载与进程的执行x86&amd64汇编简述 课程教学 课程链接&#xff1a;https://www.bilibili.com/video/BV1854y1y7Ro/?vd_source7b06bd7a9dd90c45c5c9c44d12…

【Gradle】mac环境安装Gradle及配置

官网安装说明&#xff1a;Gradle | Installation 由于Gradle运行依赖jvm&#xff0c;所以事先需要安装jdk&#xff0c;并确认你的jdk版本和gradle版本要求的对应关系&#xff0c;这个官网上有说明&#xff0c;但是我试了一下不太准确&#xff0c;供参考&#xff0c;链接如下&a…

自动化测试Allure报告

这一节主要是记录allure的内容以及用法&#xff0c;怎么让他生成一个完整的想要的报告。 allure生成的报告和其他五花八门的报告对比了一下&#xff0c;它的可读性是最好、最直观的。这不仅仅是我想要的效果&#xff0c;也是很多小伙伴想要的结果&#xff0c;毕竟这是给领导看…

kettle完成mysql表与表之间的更新和插入

版本&#xff1a;20231209 kettle完成数据库表与表之间的转换非常的简单&#xff0c;只需要在输入模块选择&#xff1a;输入表&#xff1b;在输出模块选择&#xff1a;插入和更新表模块 实例展示&#xff1a;将表stu1的数据同步到stu2&#xff0c;并覆盖掉stu2原本的数据。 cr…

情深不必纠缠

那一年&#xff0c;男孩女孩在万千人中相遇了。多年后女人的一封邮件&#xff0c;让男人与女人的灵魂相遇了。他们无缘夫妻&#xff0c;却发现彼此是灵魂的陪伴。不能携手相守&#xff0c;却懂得彼此的心灵。 有一天&#xff0c;女人告诉男人要回家了&#xff0c;问男人心里会不…

JOSEF约瑟 接触式中间继电器 JZC1-53 AC220V 导轨安装

系列型号 JZC1-22中间继电器&#xff1b;JZC1-44中间继电器&#xff1b; JZC1-62中间继电器&#xff1b;JZC1-80中间继电器&#xff1b; JZC1-71中间继电器&#xff1b;JZC1-53中间继电器&#xff1b; JZC1-32中间继电器&#xff1b;JZC1-40中间继电器&#xff1b; JZC1-31中间…

Laya2.13.3接入第三方库Socket.io

服务端&#xff1a; 1.新建一个文件夹&#xff0c;使用npm.init -y创建node工程 2.在控制台使用以下代码下载Socket.io npm install socket.io 3.创建一个app.js的文件&#xff0c;将以下代码填入 import { Server } from "socket.io"; import { createServer }…

nginx配置正向代理支持https

操作系统版本&#xff1a; Alibaba Cloud Linux 3.2104 LTS 64位 nginx版本&#xff1a; nginx-1.25.3 1. 下载软件 切换目录 cd /server wget http://nginx.org/download/nginx-1.25.3.tar.gz 1.1解压 tar -zxvf nginx-1.25.3.tar.gz 1.2切换到源码所在目录…

作为CSS开发人员你不能错过的15个节省时间的网站

本文翻译自 15 Time-Saving Websites You Should Know As A CSS Developer&#xff0c;作者&#xff1a;Shefali&#xff0c; 略有删改。 在这篇文章中&#xff0c;我将向您介绍15个网站&#xff0c;它们可以为您节省大量时间&#xff0c;并增强您的CSS开发体验。 CSS Buttons…

安装dhg

python版本&#xff1a;3.9 离线安装pytorch&#xff1a;download.pytorch.org/whl/torch_stable.html 问题1&#xff1a; 离线安装时没办法安装依赖包导致的&#xff0c;但是在线安装很蛋疼&#xff0c;最后按下面这个老哥的搞了解决pytorch指令安装时Could not find a versi…