【JAVA |Object类重写实例】Cloneable 接口、Comparable接口、比较器

✨✨谢谢大家捧场,祝屏幕前的小伙伴们每天都有好运相伴左右,一定要天天开心哦!✨✨ 
🎈🎈作者主页: 🎈丠丠64-CSDN博客🎈


✨✨ 帅哥美女们,我们共同加油!一起进步!✨✨ 

目录

一、前言

二、Obiect类

1.获取对象信息的打印

2.比较类中对象的异同

-equals方法

-hashcode方法

三、接口实现实例

-Comparable接口

-排序一个数组成员元素

1.直接使用接口Comparable

2.构造新的比较器

四、对象的拷贝

1.Cloneable拷贝

-浅拷贝

-深拷贝


一、前言

上一篇我们介绍了抽象类以及接口的相关知识,这篇我们来接着探讨一下关于接口的实例,并且认识一下所有类的父类Object


二、Obiect类

Object时JAVA中默认提供的一个类,所有的类都是继承Oject,换句话来说Oject是所有类的父类,这样就可以说在有需要实现一些功能的时候,子类方法就可以用重写来实现

1.获取对象信息的打印

我们首先来看这样一段代码

public class Person {
    public int age;
    public String name;

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }
}
public class Test {
    public static void main(String[] args){
        Person person = new Person(16,"张三");
        System.out.println(person);
    } 
}

我们目的想要实现传入变量名字以后,打印出对应的成员属性,传入println()中是否能实现呢?

事实却是输出了这样一个值,为什么呢?我们跳转println函数定义去看

我们发现实现的最终函数是这个toString()前半部分是全路径,后面部分是类似地址一样的东西(后面会说)


toString()是Object的子类,所以我们只需要重写toString(),就可以随意实现我们的功能,所以修改一下这个代码重写toString()

public class Person {
    public int age;
    public String name;

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" + "age=" + age + ", name='" + name + '\'' + '}';
    }
}

即可实现我们的功能


2.比较类中对象的异同

首先我们先来看一段代码

        Person person1 = new Person(16,"张三");
        System.out.println(person1);

        Person person2 = new Person(16,"张三");
        System.out.println(person2);

        System.out.println(person1 == person2);

对于两个不同的变量,但是里面的成员属性却相同,比较person1和person2,他两会相等吗?

运行结果来看,两者是不同的,我们屏蔽掉刚写的toString()来看一下结果

发现原来是他们类似于地址一样的东西不一样!两个对象比较的其实是类似于地址的地址!

两个对象以不同的位置进行分配


-equals方法

在JAVA在有一个方法也是用来比较两个对象是否相等,就是equals(),我们跳转到定义去看

System.out.println(person1.equals(person2));

我发现它的定义只是这样(其中的this指谁调用equals谁就是this),跟刚才的person1 == person2没有区别,我们要实现我们的功能,对成员中的属性进行比较,因为equals是Object的子类,就要对其进行重写

@Override
    public boolean equals(Object o) {
        if (this == o) {        
            return true;        //如果指向同一个对象为真
        }

        if (o == null || getClass() != o.getClass()) {
            return false;        //如果对象属性为空或者不是一个东西了为假
        }

        Person person = (Person) o;//向下转型比较属性值
                                //判断对象中各个属性,都为真才为真
        return age == person.age && Objects.equals(name, person.name);
    }

通过调用自己重写的方法,这样这段代码就可以实现我们所需要的功能了,比较两个对象中各属性是否相等

        System.out.println(person1.equals(person2));

比较对象中内容是否相同的时候,一定要重写 equals方法         

-hashcode方法

刚才在调用toString方法时我们看见了hashCode()这个方法,他帮我算了一个具体的对象位置,该方法是一个native方法,底层是由C/C++代码写的。我们看不到。

public native int hashCode();

我们来看一下这两个的值为多少 

System.out.println(person1.hashCode());
System.out.println(person2.hashCode());

因为两者所分配的空间不同,所以对象位置也不相同,返回的值也就不相同,倘若我们现在想要实现,为两个名字相同,年龄相同的对象,将存储在同一个位置,hashcode是Object的子类,我们就要重写hashcode()方法

 @Override
    public int hashCode() {

        return Objects.hash(age, name);
    }

再输出我们结果,发现经过一系列算法,两个对象出现了同一位置

两个对象的hash值不一样,hashcode方法用来确定对象在内存中存储的位置是否相同

三、接口实现实例

我们先看这样一个代码

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

    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }

}

当我们想实现两个变量之间的比较,是不是直接比较他们两个变量是否相等然后返回布尔值就行了呢?答案是错的,引用类型变量不可以这样比较,直接比较会报错

public class Test {
    public static void main(String[] args){
        Student student1 = new Student(12,"小明");
        Student student2 = new Student(15,"小美");

        System.out.println(student1 > student1);
    }

}


-Comparable接口

自定义想要比较大小,就要实现这个接口

我们应该在Student这个类给它加上一个Comparable接口,再把Student传进去就可以进行比较了

public class Student implements Comparable<Student>{
    public int age;
    public String name;

    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }

}

但是我们发现还是会报错,我们跳转过去定义查看

发现需要按照自己的需求去重写这个compareTo方法

假如说我们需要按照年龄去比较两个对象,于是就可以这样重写

@Override
    public int compareTo(Student o) {
        return this.age - o.age;  //大于输出正数,小于输出负数,相等输出0
    }

这样就不会报错了

System.out.println(student1.compareTo(student2));
System.out.println(student2.compareTo(student1));

输出结果

-排序一个数组成员元素

1.直接使用接口Comparable

先实现一个学生的类,并且使用接口Comparable

public class Student implements Comparable<Student>{
    public int age;
    public String name;

    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }

    @Override
    public int compareTo(Student o) {
        return this.age - o.age;  //大于输出正数,小于输出负数,相等输出0
    }
}

定义一个学生的数组

Student[] students = new Student[3];

students[0] = new Student(18,"小明");
students[1] = new Student(15,"小礼");
students[2] = new Student(21,"小花");

然后根据冒泡排序对学生的年龄对其排序

public static void my_sort(Comparable[] comparable){
        for (int i = 0; i < comparable.length - 1; i++) {
            for (int j = 0; j < comparable.length - 1 - i; j++) {
                if (comparable[j].compareTo(comparable[j+1]) > 0){
                    Comparable tmp = comparable[j];
                    comparable[j] = comparable[j+1];
                    comparable[j+1] = tmp;
                }
            }
        }
    }

打印出结果,对其学生年龄进行排序

my_sort(students);
System.out.println(Arrays.toString(students));

结果成立

但是这种方法也有很大的危害,对类的侵入性比较强,也不够灵活


2.构造新的比较器

所以基于上述的的危害我们可以进行优化

对于年龄比较,我们新建一个类AgeComparator,并对其compare进行重写即可

class AgeComparator implements Comparator<Student> {

    @Override
    public int compare(Student o1,Student o2) {
        return o1.age - o2.age;  
    }
}

对于名字比较,我们新建一个类NameComparator,并对其compare进行重写即可

class NameComparator implements Comparator<Student> {

    @Override
    public int compare(Student o1, Student o2) {
        return o1.name.compareTo(o2.name);
    }
}

我们来实现一下,分别以年龄跟名字比较

        Student student1 = new Student(12,"zahngsan");
        Student student2 = new Student(15,"lisi");

        NameComparator nameComparator = new NameComparator();
        System.out.println(nameComparator.compare(student1, student2));

        AgeComparator ageComparator = new AgeComparator();
        System.out.println(ageComparator.compare(student1, student2));

结果成立,且互不干扰,这就是比较器的好处,比较灵活,对类的侵入性不强


四、对象的拷贝

我们先构造一个类,并且实例化一个对象

public class Person {
    public int age;

    public Person(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                '}';
    }
}
public class Test {
    public static void main(String[] args){
     Person person1 = new Person(19);
    }
}

实例化的对象person1他在内存中是这样分配的,对象的属性在堆区开辟了空间地址由persin1保存

倘若我们要实现对变量person1进行一份拷贝,如何实现呢?


1.Cloneable拷贝

我们先介绍Cloneable接口,我们先跳转过定义,可以看到是一个空接口

又称为标记接口:证明当前类是可以被克隆的

我们实例化第二个对象,用JAVA中提供的clone 方法,创建一个对象的 "拷贝"

Person person2 = person1.clone();

但是我们发现报错了,我们还要经过以下三步

clone属于Obiect中的方法,我们转到clone的定义,发现他的访问权限是protected,直接访问不到只能够重写这个方法

但是同时呢我们发现还多了一个 throws CloneNotSupportedException这样的东西,必须是编译时处理,所以我们也要在main主函数上加上 throws CloneNotSupportedException

同时呢用我们发现它的返回值为Object,父类访问子类中的方法就是发生向下转型强转为Person类型

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person1 = new Person(19);
        Person person2 = (Person) person1.clone();
    }
}

同时也要加接口,来证明这个类可以被克隆

public class Person implements Cloneable{
    public int age;

    public Person(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                '}';
    }
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

不加接口则会报错

至此编译正常通过

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person1 = new Person(19);
        Person person2 = (Person) person1.clone();

        System.out.println(person1);
        System.out.println(person2);
    }
}

成功克隆


-浅拷贝

我们来看以下的这段代码

构造了个Money、Perso两个类,重写了clone

class Money {
    public double m = 99.99;
}

class Person implements Cloneable{
    public Money money = new Money();

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

person1拷贝一份persona2,并且修改persinal2的值,理想的结果应该是persinal2的m值被修改,person1的不变

public class Test{
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person1 = new Person();
        Person person2 = (Person) person1.clone();
        System.out.println("通过person2修改前的结果");
        System.out.println(person1.money.m);
        System.out.println(person2.money.m);

        person2.money.m = 13.6;
        System.out.println("通过person2修改后的结果");
        System.out.println(person1.money.m);
        System.out.println(person2.money.m);
    }

但是输出结果并不是这样,我们发现persona1中的m也被修改了,为什么会这样呢?

我们发现拷贝只拷贝了一份新的对象,并没有拷贝对象中的元素,对象中的元素位置没有被改变,两个对象中的m指向了同一块内存,同一个吗,对象中的元素没有被克隆,所以两者都可以修改,这种没有完全拷贝就称为浅拷贝


-深拷贝

  我们对以上的代码进行修改,将clone进行重写,将对象中的对象也进行拷贝,这个问题就解决了

@Override
    protected Object clone() throws CloneNotSupportedException {
        Person tmp = (Person) super.clone();
        tmp.money = this.money.clone();
    }

深浅拷贝说白了就是重写clone方法实现的,方法内部实现的不一样


希望对你有帮助

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

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

相关文章

阿贝云免费虚拟主机及免费云服务器评测

阿贝云是一家提供免费虚拟主机和免费云服务器的公司&#xff0c;其服务质量备受用户好评。用户可以通过阿贝云的网站 https://www.abeiyun.com 进行申请并获得免费服务。首先&#xff0c;我们来看看阿贝云的免费虚拟主机服务。免费虚拟主机提供了足够的存储空间和带宽&#xff…

HackTheBox-Machines--Cronos

文章目录 0x01 信息收集0x02 命令注入漏洞0x03 权限提升 Cronos 测试过程 0x01 信息收集 1.端口扫描 发现 SSH&#xff08;22&#xff09;、DNS&#xff08;53&#xff09;、HTTP&#xff08;80&#xff09;端口 nmap -sC -sV 10.129.227.2112.53端口开启&#xff0c;进行DNS…

靶机Moonraker_1练习报告

Moonraker: 1靶机练习实践报告 一、安装靶机 靶机是.ova文件&#xff0c;需要用VirtualBox打开&#xff0c;但我习惯于使用VMWare,因此修改靶机文件&#xff0c;使其适用于VMWare打开。 解压ova文件&#xff0c;得到.ovf文件和.vmdk文件。 直接用VMWare打开.ovf文件即可。 …

【VTKExamples::Utilities】第四期 CameraModifiedEvent

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ:870202403 公众号:VTK忠粉 前言 本文分享VTK样例CameraModifiedEvent,希望对各位小伙伴有所帮助! 感谢各位小伙伴的点赞+关注,小易会继续努力分享,一起进步! 你的点赞就是我的动力(^U^)ノ~YO 1. CameraModifi…

OpenMV的VisionBoard视觉识别开发板学习记录

此篇博客仅用于对VisionBoard的开发板的学习研究记录&#xff0c;没有教学内容。 一、资料来源 开发板资料链接 开发板环境搭建手册 开发板视频教程 板子的资料网站 openmv官方的网站 目录 一、资料来源二、针对 VisionBoard的目标识别和定位总结1. 目标识别功能1.1 物体检测…

react ant 表格实现 拖拽排序和多选

项目背景 : react ant 要实现 : 有多选功能(实现批量删除 , 也可以全选) 可以拖拽(可以复制 , 方便顶部的搜索功能) 要实现效果如下 1 这是最初的拖拽功能实现 , 不能复制表格里的内容 , 不符合要求 2 更改了ROW的内容 , 实现了可以复制表格内容 代码 //控制是否可以选中表格…

Oracle数据库操作问题汇总

一、简介 Oracle Database&#xff0c;又名Oracle RDBMS&#xff0c;或简称Oracle。是甲骨文公司的一款关系数据库管理系统。它是在数据库领域一直处于领先地位的产品。可以说Oracle数据库系统是世界上流行的关系数据库管理系统&#xff0c;系统可移植性好、使用方便、功能强&…

数据结构--二叉树--顺序存储判断是否二叉搜索树(2022统考真题)

数据结构–二叉树–顺序存储判断是否二叉搜索树(2022统考真题) 题目描述&#xff1a; 思路 二叉搜索树&#xff08;Binary Search Tree&#xff0c;简称BST&#xff09;是一种具有以下性质的二叉树&#xff1a; 对于树中的每个节点 N&#xff0c;它的左子树&#xff08;如果…

重学java 49 List接口

但逢良辰&#xff0c;顺颂时宜 —— 24.5.28 一、List接口 1.概述: 是collection接口的子接口 2.常见的实现类: ArrayList LinkedList Vector 二、List集合下的实现类 1.ArrayList集合的使用及源码分析 1.概述 ArrayList是List接口的实现类 2.特点 a.元素有序 —> 按照什么顺…

Oracle中rman的增量备份使用分享

继上次使用RMAN的全量备份和异机还原以后&#xff0c;开始研究一下增量备份和还原的方法。相比于全量RMAN的备份还原&#xff0c;增量的备份还原就相对简单。本实践教程直接上操作&#xff0c;还是回归到一个问题&#xff0c;就是关于两个数据库创建时候&#xff0c;必须保持or…

【职业教育培训机构小程序】教培机构“招生+教学”有效解决方案

教培机构“招生教学”有效解决方案在数字化转型的浪潮中&#xff0c;职业教育培训机构面临着提升教学效率、拓宽招生渠道、增强学员互动等多重挑战。小程序作为一种新兴的移动应用平台&#xff0c;为解决这些痛点提供了有效途径。 一、职业教育培训机构小程序的核心功能 &…

SpringBoot自动装配源码

自动装配&#xff1a; 实际上就是如何将Bean自动化装载到IOC容器中管理&#xff0c;Springboot 的自动装配时通过SPI 的方式来实现的 SPI&#xff1a;SpringBoot 定义的一套接口规范&#xff0c;这套规范规定&#xff1a;Springboot 在启动时会扫描外部引用 jar 包中的META-IN…

栈(从数据结构的三要素出发)

文章目录 逻辑结构物理结构顺序栈链栈共享栈 数据的操作顺序栈的基本操作链栈的基本操作共享栈的基本操作 数据结构的应用栈在括号匹配中的应用栈在表达式求值中的应用栈在递归调用中的应用 逻辑结构 栈是只允许在一端进行插入或删除操作的线性表。首先栈是一种线性表&#xf…

保留两位小数不四舍五入,10000.55变成10000.54的坑

正解 function moneyFormat(num){ let money num "";//隐式转换为字符串和toString()效果一样//没有小数补齐这个0if(money.indexOf(".")"-1"){moneymoney".00";}else{//有小数截取前二位小数moneymoney.substring(0,money.inde…

工业AI的崛起,中国自主创新的新机遇

我们都知道&#xff0c;互联网已经深刻地改变了我们的生活方式&#xff0c;催生了无数的新型商业模式和创新产业&#xff0c;推动了社会的经济变革。中国在互联网领域的发展取得了举世瞩目的成就&#xff0c;建成了全球规模最大、技术领先的5G网络&#xff0c;互联网应用的普及…

vue3 vite title 页面标题设置

效果图&#xff1a; 1. 安装 vite-plugin-html 插件 npm install vite-plugin-html -D2. 修改 vite.config.js import {defineConfig, loadEnv} from vite import { createHtmlPlugin } from "vite-plugin-html" import {resolve} from path import vue from vitej…

我说同事咋找工作命中率这么高,原来是学习了这些招式

最近有两个同事离职了&#xff0c;其中一个还是专科&#xff0c;他俩一个是前端开发&#xff0c;一个是python开发&#xff0c;两个人都接近35岁了。我们还劝告他们&#xff0c;不要离职&#xff0c;要骑驴找马。但了解后&#xff0c;他俩非常有信心的说&#xff1a;不怕&#…

振弦采集仪在岩土工程地质灾害监测中的可行性研究

振弦采集仪在岩土工程地质灾害监测中的可行性研究 引言&#xff1a; 岩土工程地质灾害是指在岩土体中由于自然力和人类活动等因素引起的&#xff0c;对人类生活、财产以及环境造成威胁的灾害。为了及时发现并准确监测地质灾害的发生和演化过程&#xff0c;振弦采集仪作为一种新…

web自动化-下拉框操作/键鼠操作/文件上传

在我们做UI自动化测试的时候&#xff0c;会有一些元素需要特殊操作&#xff0c;比如下拉框操作/键鼠操作/文件上传。 下拉框操作 在我们很多页面里有下拉框的选择&#xff0c;这种元素怎么定位呢&#xff1f;下拉框分为两种类型&#xff1a;我们分别针对这两种元素进行定位和…

6-4 先序输出度为2的结点

作者 DS课程组 单位 临沂大学 本题要求实现一个函数&#xff0c;按照先序遍历的顺序输出给定二叉树中度为2的结点。 函数接口定义&#xff1a; void PreorderPrintNodes( BiTree T);T是二叉树树根指针&#xff0c;PreorderPrintNodes按照先序遍历的顺序输出给定二叉树T中度为…