【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;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", score=" + score +'}';}
}
// 这里我们利用了解耦的思想
class AgeComparator implements Comparator<Student> {@Overridepublic int compare(Student o1, Student o2) {return o1.age - o2.age;}
}
class ScoreComparator implements Comparator<Student>{@Overridepublic 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;@Overrideprotected 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;}@Overridepublic String toString() {return "Student{" +"age=" + age +'}';}@Overrideprotected 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;@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}@Overridepublic 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;}@Overridepublic 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;}@Overridepublic String toString() {return "Student{" +"age=" + age +'}';}@Overrideprotected Object clone() throws CloneNotSupportedException {//return super.clone();Student tmp = (Student)super.clone();tmp.m = (Money)this.m.clone();return tmp;}@Overridepublic boolean equals(Object obj) {Student student = (Student)obj;return age == student.age;}@Overridepublic 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.mzph.cn/news/210192.shtml

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

相关文章

SpringBoot 自动装配原理详解

什么是 SpringBoot 自动装配&#xff1f; 我们现在提到自动装配的时候&#xff0c;一般会和 Spring Boot 联系在一起。但是&#xff0c;实际上 Spring Framework 早就实现了这个功能。Spring Boot 只是在其基础上&#xff0c;通过 SPI 的方式&#xff0c;做了进一步优化。 Spr…

解决npm install时报:gyp ERR! configure error

报错内容&#xff1a; npm ERR! gyp ERR! cwd C:\Users\zccbbg\code\my\examvue\node_modules\node-sass npm ERR! gyp ERR! node -v v16.13.1 npm ERR! gyp ERR! node-gyp -v v3.8.0 npm ERR! gyp ERR! not ok npm ERR! Build failed with error code: 1 解决办法&#xff1a;…

自行编写一个简单的shell!

本文旨在编写一个简单的shell外壳程序&#xff01;功能类似于shell的一些基本操作&#xff01;虽然不能全部实现shell的一些功能&#xff01;但是通过此文章&#xff0c;自己写一个简单的shell程序也是不成问题&#xff01;并且通过此文章&#xff0c;可以让读者对linux中一些环…

C#基础面试题集

C#基础 1. 简述值类型和引用类型有什么区别2. C# String类型比 stringBuilder 类型的优势是什么?3.面向对象的三大特点4.请简述private&#xff0c;public&#xff0c;protected&#xff0c;internal的区别5.结构体和类6.请描述Interface与抽象类之间的不同7.在类的构造函数前…

go自带rpc框架生产环境使用demo

基础使用 序列化使用自带gob协议 server package mainimport ("net""net/rpc" )// 定义一个handler结构体 type HelloService struct { }// 定义handler方法,大小写&#xff0c;参数&#xff0c;返回值都是固定的&#xff0c;否则无法注册 func (receiv…

数据库事务:保障数据一致性的基石

目录 1. 什么是数据库事务&#xff1f; 1.1 ACID特性解析 2. 事务的实现与控制 2.1 事务的开始和结束 2.2 事务的隔离级别 3. 并发控制与事务管理 3.1 并发控制的挑战 3.2 锁和并发控制算法 4. 最佳实践与性能优化 4.1 事务的划分 4.2 批处理操作 5. 事务的未来发展…

SpringSecurity6 | 自定义登录页面

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; Java从入门到精通 ✨特色专栏&#xf…

高工氢电年会 | 未势能源解超朋博士受邀出席并做主题演讲

12月4日&#xff0c;以“战略重构 商业觉醒”为主题的2023高工氢电年会在深圳举办&#xff0c;未势能源副总裁解超朋博士受邀出席开幕式论坛&#xff0c;以《把握机遇、直面挑战&#xff0c;迎接氢车规模化推广时代》为主题发表演讲&#xff0c;并参与圆桌论坛研讨。 氢势已来&…

【Linux】resolv.conf 文件

resolv.conf resolv.conf 文件 是 DNS 的 client 端使用的文件&#xff0c;用于设置 DNS 服务器的 ip 地址以及 DNS 域名&#xff0c;还可以配置域名搜索顺序等等。主要包含如下关键字&#xff1a;nameserver、domain、search、sortlist、options。设置的格式都是 关键字空格 …

管理类联考——数学——真题篇——按知识分类——数据

文章目录 排列组合2023真题&#xff08;2023-05&#xff09;-数据分析-排列组合-组合-C运算-至少-需反面思考真题&#xff08;2023-08&#xff09;-数据分析-排列组合-相邻不相邻-捆绑法插空法-插空法注意空位比座位多1个&#xff0c;是用A&#xff1b;捆绑法内部排序用A&#…

2023中国(海南)国际高尔夫旅游文化博览会 暨国际商界峰层·全球华人高尔夫精英巡回赛 全国颍商自贸港行盛大启幕

2023中国&#xff08;海南&#xff09;国际高尔夫旅游文化博览会&#xff08;以下简称“海高博”&#xff09;暨全国颍商走进海南自贸港于12月7-9日在海口观澜湖盛大开幕。该活动由中国国际贸易促进委员会海南省委员会、海南省旅游和文化广电体育厅主办&#xff0c;中国国际商会…

最新版本11.17的YOLOv8加入注意力方法

本文基于11.17版本,以往版本略有不同,可查看改进YOLOv8,教你YOLOv8如何添加20多种注意力机制进行参考 放入注意力代码,以biformer注意力为例 import torch import torch.nn as nn import torch.nn.functional as Fdef position(H, W, is_cuda=

探索 Python 中链表的实现:从基础到高级

# 更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 链表是一种基础的数据结构&#xff0c;它由一系列节点组成&#xff0c;每个节点都包含数据和指向下一个节点的引用。在Python中&#xff0c;可以使用类来实现链表&#xff0c;本文将介绍如何实现链表&#xff…

【密码学引论】认证

认证是许多应用系统中安全保护的第一道设防认证和加密的区别&#xff1a;加密用来确保数据的保密性&#xff0c;而认证用来确保报文发送者和接受者的真实性和报文的完整性。认证和数字签名的区别&#xff1a; 认证总是基于某种收发双方共享的保密数据来认证被鉴别对象的真实性&…

关于linux开机自启动

1、系统启动流程 2、 init、 inittab、 init.d、 rcx.d /etc/inittab是Linux系统中的一个配置文件&#xff0c;用于定义系统的运行级别和相应的操作。其语法格式如下&#xff1a; 标签&#xff1a;运行级别&#xff1a;操作&#xff1a;进程 label:runlevel:action:process下面…

PHP基础 - 注释变量

一. 语言开始标识 在PHP中,文件的开头需要使用语言开始标识来指定该文件是PHP代码。标识通常为"<?php",也可以是"<?",但建议使用"<?php"以确保代码的兼容性和可读性。 <?php // PHP代码从这里开始写 二. PHP注释 注释是用…

【Python】Faker库详解:创建测试数据轻而易举

Python Faker库详解&#xff1a;创建测试数据轻而易举 在软件开发和测试过程中&#xff0c;通常需要大量的测试数据来模拟真实环境。Python的Faker库为开发者提供了一个方便、灵活且强大的工具&#xff0c;用于生成各种虚构数据。本文将深入介绍Faker库&#xff0c;演示其基本…

十一、了解分布式计算

1、什么是&#xff08;数据&#xff09;计算&#xff1f; 2、分布式(数据)计算 &#xff08;1&#xff09;概念 顾名思义&#xff0c;分布式计算&#xff0c;即以分布式的形式完成数据的统计&#xff0c;得到需要的结果。 分布式数据计算&#xff0c;顾名思义&#xff0c;就是…

c语言选择排序总结(详解)

选择排序cpp文件项目结构截图 项目cpp文件截图 项目具体代码截图 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <math.h> #include <iostream> #include <string.h> #include <time.h> #include &…

Java发展史

Java发展史 Java历史的几个重要节点&#xff1a; 1990&#xff0c;Sun公司启动了一个名为“绿色计划”的项目&#xff0c;由James Gosling等领导&#xff0c;目标是开发一种可以在机顶盒等消费性电子产品上运行的程序架构。这个计划产生了Java的前身&#xff1a;Oak&#xff…