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

个人主页:兜里有颗棉花糖
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创
收录于专栏【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.mfbz.cn/a/232010.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

学习Linux(1)-开始前的准备

一、Linux介绍 如图,“Linux的发行版说简单点就是将Linux内核与应用软件做一个打包”&#xff0c;所以&#xff0c;我们要学习Linux&#xff0c;就要选择一个趁手的应用软件&#xff0c;通常使用较多的有centerOs、Ubuntu。本文将基于centerOs6进行学习。 二、安装环境 使用Li…

认识线程和创建线程

目录 1.认识多线程 1.1线程的概念 1.2进程和线程 1.2.1进程和线程用图描述关系 1.2.2进程和线程的区别 1.3Java 的线程和操作系统线程的关系 2.创建线程 2.1继承 Thread 类 2.2实现 Runnable 接口 2.3匿名内部类创建 Thread 子类对象 2.4匿名内部类创建 Runnable 子类对…

SAP UI5 walkthrough step7 JSON Model

这个章节&#xff0c;帮助我们理解MVC架构中的M 我们将会在APP中新增一个输入框&#xff0c;并将输入的值绑定到model&#xff0c;然后将其作为描述&#xff0c;直接显示在输入框的右边 首先修改App.controllers.js webapp/controller/App.controller.js sap.ui.define([&…

教师需要什么技能?

作为一名老师&#xff0c;需要掌握许多技能&#xff0c;以便能够成功地教育和指导学生。以下是一些关键技能&#xff1a; 1.教学技能&#xff1a;老师需要有深入的学科知识和教学经验&#xff0c;以便能够有效地传授知识。教师应该了解如何设计和执行教学计划&#xff0c;制定课…

Java、JDK、JRE、JVM

Java、JDK、JRE、JVM 一、 Java 广义上看&#xff0c;Kotlin、JRuby等运行于Java虚拟机上的编程语言以及相关的程序都属于Java体系的一员。从传统意义上看&#xff0c;Java社区规定的Java技术体系包括以下几个部分&#xff1a; Java程序设计语言各种硬件平台上的Java虚拟机实…

JFrog----基于Docker方式部署JFrog

文章目录 1 下载镜像2 创建数据挂载目录3 启动 JFrog服务4 浏览器登录5 重置密码6 设置 license7 设置 Base URL8 设置代理9 选择仓库类型10 预览11 查看结果 1 下载镜像 免费版 docker pull docker.bintray.io/jfrog/artifactory-oss体验版&#xff1a; docker pull releas…

论文导读|10月MSOM文章精选:智慧医疗

编者按 在“10月MSOM文章精选&#xff1a;智慧医疗”中&#xff0c;我们有主题、有针对性地选择了MSOM期刊杂志中一些有关智慧医疗领域的有趣文章&#xff0c;不但对文章的内容进行了概括与点评&#xff0c;而且也对文章的结构进行了梳理&#xff0c;旨在激发广大读者的阅读兴趣…

vue预览pdf,放大缩小拖动,dialog拖动,父页面滚动

公共组件部分代码 main.js import draggable from /directive/drag/index Vue.use(draggable) pdf组件部分代码

1-3、Java反编译

语雀原文链接 文章目录 1、JD-GUI反编译下载1-1、打开class文件无反应 1、JD-GUI反编译下载 http://java-decompiler.github.io jd-gui-windows-1.6.6.zip 1-1、打开class文件无反应 目前是可以正常打jar包文件&#xff0c;但是在直接打开.class文件时软件会卡住。首先将要…

【ArcGIS Pro微课1000例】0051:创建数据最小几何边界范围(点、线、面数据均可)

本实例为专栏系统文章:创建点数据最小几何边界(范围),配套案例数据,持续同步更新! 文章目录 一、工具介绍二、实战演练三、注意事项一、工具介绍 创建包含若干面的要素类,用以表示封闭单个输入要素或成组的输入要素指定的最小边界几何。 工具界面及参数如下所示: 核心…

EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks(2020)

文章目录 -Abstract1. Introductiondiss former methodour method 2. Related Work3. Compound Model Scaling3.1. 问题公式化3.2. Scaling Dimensions3.3. Compound Scaling 4. EfficientNet Architecture5. Experiments6. Discussion7. Conclusion 原文链接 源代码 - 本文中…

互联网数据传输原理 |OSI七层网络参考模型

网络模型 OSI 网络参考模型&#xff0c;仅作为参考&#xff0c;也就是说OSI网络实际中并不使用。我们只是把OSI网络模型作为参考&#xff0c;在网络出现问题的时候&#xff0c;可以从一个宏观的整体去分析和解决问题。而且搭建网络的时候也并不一定需要划分为7层 但是当今互联…

springcloud分布式事务

文章目录 一.为什么引入分布式事务?二.理论基础1.CAP定理2.BASE理论 三.Seata1.微服务集成Seata2.XA模式(掌握)3.AT模式(重点)4.TCC模式(重点)5.Saga模式(了解) 四.四种模式对比五.Seata高可用 一.为什么引入分布式事务? 事务的ACID原则 在大型的微服务项目中,每一个微服务都…

CPU的三大调度

计算机系统中的调度可以分为不同层次&#xff0c;包括作业调度、内存调度和进程调度。这三种调度分别负责管理和优化计算机系统中不同层次的资源分配和执行顺序。 高级调度&#xff1a;作业调度&#xff08;Job Scheduling&#xff09;&#xff1a; 作业调度是指对提交到计算…

ubuntu上搭建bazel编译环境,构建Android APP

背景是github上下载的工程&#xff0c;说明仅支持bazel编译&#xff0c;折腾了一天Android studio&#xff0c;失败。 不得不尝试单价bazel编译环境&#xff0c;并不复杂&#xff0c;过程记录如下 说明&#xff1a;ubuntu环境是20.04&#xff0c;pve虚拟机安装 1.安装jdk sudo…

坚鹏:广发银行梅州分行银行数字化转型战略、方法与案例培训

广发银行成立于1988年&#xff0c;前身为广东发展银行&#xff0c;经国务院和中国人民银行批准&#xff0c;是国内首批组建的全国性股份制商业银行之一&#xff0c;2016年8月&#xff0c;广发银行成为中国人寿团成员单位&#xff0c;2021年成为首批国内系统重要性银行。2022年&…

Java解决矩阵对角线元素的和问题

Java解决矩阵对角线元素的和问题 01 题目 给你一个正方形矩阵 mat&#xff0c;请你返回矩阵对角线元素的和。 请你返回在矩阵主对角线上的元素和副对角线上且不在主对角线上元素的和。 示例 1&#xff1a; 输入&#xff1a;mat [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a…

Ubuntu-rsyslog和systemd-journald日志服务

rsyslog日志服务 rsyslog作为传统的系统日志服务&#xff0c;把所有收集到的日志都记录到/var/log/目录下的各个日志文件中。 常见的日志文件如下&#xff1a; /var/log/messages 绝大多数的系统日志都记录到该文件 /var/log/secure 所有跟安全和认证授权等日志…

k8s初始化报错 [ERROR CRI]: container runtime is not running: ......

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

构建Servlet项目流程

第一步&#xff1a;创建maven项目 部分基础 依赖的模板基础部分如下 maven-archetype-quickstart: 这是最基本的Archetype&#xff0c;它创建一个包含简单Java类和单元测试的项目。 maven-archetype-webapp: 这个Archetype创建一个简单的Java web应用&#xff0c;包括一个serv…
最新文章