【JAVA |再谈接口、Object、内部类】Object类中子类重写,Cloneable 接口、比较器、内部类

news2024/10/6 22:29:27

✨✨谢谢大家捧场,祝屏幕前的小伙伴们每天都有好运相伴左右,一定要天天开心哦!✨✨ 
🎈🎈作者主页: 🎈丠丠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方法实现的,方法内部实现的不一样


希望对你有帮助

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

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

相关文章

element-plus:踩坑日记

el-table Q&#xff1a;有fixed属性时&#xff0c;无数据时&#xff0c;可能出现底部边框消失的bug 现象&#xff1a; 解决方法&#xff1a; .el-table__empty-block {border-bottom: 1px solid var(--el-table-border-color); } el-collapse 折叠面板 Q&#xff1a;标题上…

Java操作Word文档,根据模板生成文件

Java操作Word文档 poi-tl介绍 官方文档&#xff1a;https://deepoove.com/poi-tl/ poi-tl&#xff08;poi template language&#xff09;是Word模板引擎&#xff0c;使用模板和数据创建很棒的Word文档。 在文档的任何地方做任何事情&#xff08;Do Anything Anywhere&#…

在全志H616核桃派开发板上配置I2C引脚并读取温度数据

配置引脚 找到板子上的i2c引脚 为了方便查找&#xff0c;我们加入了一个显示功能引脚位置的功能&#xff0c;运行以下命令&#xff0c;查看板子的40pin引脚上有几个可用i2c gpio pin i2c启用i2c 我们使用set-device指令来使能/关闭指定设备的底层驱动&#xff0c;使能后&am…

视频汇聚/云存储/安防监控EasyCVR接入GB28181设备未回复ack信息的原因排查

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。 用户反馈&#xff0c;设备通过国标GB28181注…

Docker 容器间通讯

1、虚拟ip/访问 同一网络 安装docker时&#xff0c;docker会默认创建一个内部的桥接网络docker0&#xff0c;每创建一个容器分配一个虚拟网卡&#xff0c;容器之间(包括宿主机)可以根据分配的ip互相访问(ps:其他主机(包括其他主机的容器)无法ping通docker容器ip无法访问&#…

RepOptimizer原理与代码解析(ICLR 2023)

paper&#xff1a;Re-parameterizing Your Optimizers rather than Architectures offcial implementation&#xff1a;https://github.com/dingxiaoh/repoptimizers 背景 神经网络的结构设计是将先验知识融入模型中。例如将特征转换建模成残差相加的形式&#xff08;\(yf(x…

Zoho CRM怎么样?云衔科技为企业提供采购优惠!

企业对于客户关系管理&#xff08;CRM&#xff09;系统的需求日益增加&#xff0c;Zoho CRM作为一款备受赞誉的国际CRM服务提供商&#xff0c;凭借其全面的功能、出色的用户体验和卓越的性价比&#xff0c;成为了众多企业数字化转型的得力助手。 Zoho CRM是一款覆盖客户全生命…

Vue状态管理深度剖析:Vuex vs Pinia —— 从原理到实践的全面对比

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 &#x1f44b; 引言&#x1f4cc; Vuex 基础知识核心构成要素示例代码 &#x1f4cc; Pinia 基础知识核心构成要素示例代码 &#x1f4cc; Vuex与Pinia的区别&#x1f4cc; 使用示例与对比&#x1f4cc; 总结 &#x1f44b;…

Transormer(2)-位置编码

位置编码公式 偶数位置用sin,奇数位置用cos. d_model 表示token的维度&#xff1b;pos表示token在序列中的位置&#xff1b;i表示每个token编码的第i个位置&#xff0c;属于[0,d_model)。 torch实现 import math import torch from torch import nn from torch.autograd im…

Vue 3 的 setup语法糖工作原理

前言 我们每天写vue3项目的时候都会使用setup语法糖&#xff0c;但是你有没有思考过下面几个问题。setup语法糖经过编译后是什么样子的&#xff1f;为什么在setup顶层定义的变量可以在template中可以直接使用&#xff1f;为什么import一个组件后就可以直接使用&#xff0c;无需…

【如何让论文中摘要后面的内容不出现在目录中】

首先选择摘要二字&#xff0c;设置为一级标题&#xff0c;然后选择摘要后面的内容设置为正文样式&#xff0c;再选择这一部分看一下是不是都是正文大纲级别&#xff0c;如果是那就可以了。 具体流程如下 1、选择摘要二字&#xff0c;设置为一级标题样式 2、选择摘要后面的文…

FreeRTOS学习——FreeRTOS队列(下)之队列创建

本篇文章记录我学习FreeRTOS队列创建的知识。主要分享队列创建需要使用的初始化函数、队列复位函数。 需要进一步了解FreeRTOS队列的相关知识&#xff0c;读者可以参考以下文章&#xff1a; FreeRTOS学习——FreeRTOS队列&#xff08;上&#xff09;_freertos 单元素队列-CSDN博…

scikit-learn机器学习要点总结

目录 一、机器学习总体流程二、引入数据集三、将数据集转换为DataFrame四、可视化数据五、数据预处理&#xff08;一&#xff09;数据标准化&#xff08;二&#xff09;独热编码 六、数据集划分为训练集和测试集七、创建模型估计器(estimator)&#xff08;一&#xff09;用于回…

人力资源(HR)OKR 案例

HR人员 #OKR# 是一个很好的方法来建立一致性&#xff0c;吸引团队成员&#xff0c;并实现高绩效。 在本文中&#xff0c;我们将回答以下问题&#xff1a; 如何写好HR OKR &#xff1f; 什么是好的HR OKR 的例子 &#xff1f; 我应该在我的HR OKR 中填写什么 &#xff1f; 如何…

stream( ).collect ( Collectors.groupingBy ( ) ) 的用法

文章目录 第一种解释1、基本用法2、指定值收集器3、多级分组4、常见应用场景和用处 第二种解释1、基本语法2、示例3、更复杂的用法 第一种解释 Collectors.groupingBy 是 Java 8 引入的 Stream API 中的一个收集器&#xff08;Collector&#xff09;&#xff0c;它用于将流&am…

【NumPy】关于numpy.transpose()函数,看这一篇文章就够了

&#x1f9d1; 博主简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

16.js数学方法和进制转换

数学方法 &#xff08;1&#xff09;Math.random() 默认生成0-1的随机数 var resMath.random() console.log(res) &#xff08;2&#xff09;Math.round(数字) 取整&#xff1a;正数-四舍五入 负数-5舍6入 var resMath.round(11)console.log(res) //11var res1Math.round(1…

2024-5-6-从0到1手写配置中心Config之实现配置中心客户端

配置加载原理 在Spring中PropertySource类实现了所有属性的实例化。 启动赋值&#xff1a; 定义自定义属性配置源&#xff0c;从config-server获取全局属性&#xff1b;Spring启动时&#xff0c;插入自定义属性配置源&#xff1b;绑定属性会优先使用&#xff0c;给自定义属性…

下拉框操作/键鼠操作/文件上传

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

移动硬盘容量消失无法读取的解决方案

在数字化时代&#xff0c;数据的存储和备份变得尤为重要。移动硬盘作为一种便捷、大容量的存储设备&#xff0c;受到许多人的青睐。然而&#xff0c;有时我们可能会遭遇这样的问题&#xff1a;移动硬盘不显示容量且无法访问。这种情况无疑给我们的数据存储和管理带来了巨大的困…