简介Object类+接口实例(深浅拷贝、对象数组排序)

news2025/4/10 17:36:09

本期目录

  • 前言
  • 一、初识Object类
    • 🍑1、toString()
    • 🍑2、hashCode()
    • 🍑3、equals()
    • 🍑4、clone()
  • 三、对象的深浅拷贝
    • 🍑1、浅拷贝
    • 🍑2、深拷贝
    • 🍑3、深浅拷贝的特点
  • 二、对象数组排序
    • 🍑1、通过Comparable接口排序
    • 🍑2、通过Comparator接口排序
  • 小结


前言

上期我们深入探讨了Java中的接口,其实Java中内置了很多非常有用的接口,为了能够进一步加深对接口的认识,也为能够灵活掌握这些常见接口的使用,我们就这期单独谈谈接口的实例。


一、初识Object类

在后面介绍接口的使用时,我们绕不开要使用Object类,为了后面能够更好的理解,我们这里就将Object类放到前面讲解。

Object是Java默认提供的一个类。Java里面除了Object类,所有的类都是存在继承关系的。默认会继承Object父类。即所有类的对象都可以使用Object的引用进行接收;所有的类都可以重写Object的方法。

Object类中方法都特别重要,由于这里我们还没学习多线程,所以就目前介绍一下如下四种:

🍑1、toString()

toString方法的作用是将对象转换为字符串形式。通常为了方便输出对象的内容,需要重写toString方法。

class Student {
    private String name;
    private int age;

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

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

public class Test {
    public static void main(String[] args) {
		Student student = new Student("张三",18);
		System.out.println(student);
    }

在这里插入图片描述

🍑2、hashCode()

如果不重写Object类的toString方法,默认会调用Object类的toString方法,源码如下:

public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

假如不重写toString方法,这次我们再来运行:

这次我们并没有输出对象的内容,而是输出了一串数字,观察源码(我们先不理解@前的部分)显然这是Integer.toHexString(hashCode())搞的鬼,那么hashCode究竟是什么呢?

加上这段代码System.out.println(student.hashCode());我们再来测试一下:

我们暂且将其理解为计算对象的位置,暂将这段地址看做对象的地址。其实hashcode这个方法对于我们目前来说是用不上的,这里先埋个伏笔,之后学了Hashmap再来详细介绍它的用法。

🍑3、equals()

equals方法在Object类中的源码:

public boolean equals(Object obj) {
    return (this == obj);
}

源码中的equals比较的是对象的引用是否相同,显然这样默认的equals方法在对象的比较中是几乎用不到的,所以一般我们会对这个equals方法进行重写以满足实际的比较需求。

比如如果我们将两个名字一样的学生看作相同的对象,我们就可以这样重写equals方法:

public boolean equals(Object obj) {
    if ( obj == null ) {//空对象和任何非空对象不同
        return false;
    }
    if ( this == obj ) {//引用相同对象必相同
        return true;
    }
    if(! (obj instanceof Student)) {//如果obj不是Student类的实例
        return false;				//那么必然不可能等于this
    }
    Student student = (Student)obj;
    if(this.name.equals(student.name)) {//这里利用了String类中重写的equals方法
        return true;
    }
    return false;
}

测试:

//……
public class Test {
    public static void main(String[] args) {
        Student student1 = new Student("张三",18);
        Student student2 = new Student("张三",25);
        if(student1.equals(student2)) {
            System.out.println(student1+"和"+student2+"是同一个人!");
        }
    }
}

🍑4、clone()

clone()方法是用来复制一个对象,关于如何复制,这里涉及到了接口的知识,下面来详细介绍:👇


三、对象的深浅拷贝

1、Clonable接口

Clonable接口源码:

public interface Cloneable {
}

:Clonable是一个空接口,也叫标记接口,只要一个类实现了这个接口就标志这个类是可以克隆的,否则不可以克隆。

2、重写clone方法
clone方法是Object类下protected修饰的一个native方法,如果希望提供从类(被克隆的类)外部复制其对象的功能,则可以覆盖Object.clone()作为公共对象,只需在内部调用super.clone()仍使用默认实现即可。(这里不太好理解,大家简单了解,主要是clone()的使用)

//浅拷贝实现方式
@Override
protected Object clone() throws CloneNotSupportedException {
    return super.clone();
}
//深拷贝实现方式不唯一,需要具体情况具体分析。

完成了上面两个步骤我们就可以开始进行对象的拷贝了,根据重写方法的实现方式,我们将拷贝分为浅拷贝和深拷贝,下面分别详细介绍:

🍑1、浅拷贝

浅拷贝: 按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址(引用) ,因此如果其中一个对象改变了这个引用下的数据,就会影响到另一个对象。(浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象。)

理论太过枯燥,下面我们直接测试代码:

//--------------浅拷贝----------------
class Money implements Cloneable{
    public double m = 15.3;

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

    @Override
    public String toString() {
        return "Money{" +
                "m=" + m +
                '}';
    }
}

class Student implements Cloneable{
    public String name;
    public int age;
    public Money money = new Money();

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

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

    //浅拷贝
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
//测试浅拷贝
public class Test {
	//throws CloneNotSupportedException先记忆固定格式,后期讲解
    public static void main(String[] args) throws CloneNotSupportedException {
        Student student1 = new Student("张三",18);
        Student student2 = (Student) student1.clone();
        System.out.println("修改前:"+student1);
        System.out.println("修改前:"+student2);
        System.out.print("\n");
        student1.money.m=10.0;
        System.out.println("修改后:"+student1);
        System.out.println("修改后:"+student2);

    }
}

🍑2、深拷贝

深拷贝: 在拷贝引用类型成员变量时,为引用类型的数据成员另辟了一个独立的内存空间,实现真正内容上的拷贝。(深拷贝把要复制的对象所引用的对象都复制了一遍。)

实现深拷贝的核心就是对clone方法重写的实现,下面我们还以上面的Student-Money为例,重写一下深拷贝的方法:

@Override
protected Object clone() throws CloneNotSupportedException {
	//1.先克隆student对象
    Student stutclone = (Student) super.clone();
    //2.克隆student对象中的money引用对象,并将克隆的新引用赋值给stuclone
    stutclone.money = (Money) this.money.clone();
    //3.将得到的深拷贝对象引用返回
    return stutclone;
}

同样的测试用例,测试结果:

🍑3、深浅拷贝的特点

通过上面的例子,相信大家对深浅拷贝已经渐入佳境,下面就针对上面观察到的现象对深浅拷贝做一个简单的小结,加深一下印象。

浅拷贝特点 :

(1) 对于基本数据类型的成员对象,因为基础数据类型是值传递的,所以是直接将属性值赋值给新的对象。基础类型的拷贝,其中一个对象修改该值,不会影响另外一个。

(2) 对于引用类型,比如数组或者类对象,因为引用类型是引用传递,所以浅拷贝只是把内存地址赋值给了成员变量,它们指向了同一内存空间。改变其中一个,会对另外一个也产生影响。

深拷贝特点 :

(1) 对于基本数据类型的成员对象,因为基础数据类型是值传递的,所以是直接将属性值赋值给新的对象。基础类型的拷贝,其中一个对象修改该值,不会影响另外一个(和浅拷贝一样)。

(2) 对于引用类型,比如数组或者类对象,深拷贝会新建一个对象空间,然后拷贝里面的内容,所以它们指向了不同的内存空间。改变其中一个,不会对另外一个也产生影响。

(3) 对于有多层对象的,每个对象都需要实现 Cloneable 并重写 clone() 方法,进而实现了对象的串行层层拷贝。

(4) 深拷贝相比于浅拷贝速度较慢并且花销较大。


二、对象数组排序

在设计程序时我们会经常遇到给对象数组排序的问题,此时我们就可以用到Java为我们提供的两个比较接口对对象数组进行排序。

🍑1、通过Comparable接口排序

当我们使用Arrays.sort(数组名)排序对象数组时,内部用到Comparable接口,所以我们进行排序时也要对排序类实现Comparable接口。

Comparable接口源码:

public interface Comparable<T> {
    public int compareTo(T o);
}

注意事项:

  1. 源码中的代表泛型,这里我们写成比较对象的类名,先不理解会用即可。
  2. 对于一个对象可能会有多个成员属性,所以排序时我们要重写Comparable中的compareTo方法用来指定排序规则。

compareTo比较时返回值对于不同的情况可能会五花八门,这就给我们重写方法带来了障碍,所以这里有一种简单的技巧:

  1. 当我们比较的是整形家族的成员时,升序写成this.成员名-o.成员名;。 降序写成o.成员名-this.成员名;
  2. 当我们比较的是字符串类型的成员时,升序写成this.成员名.compareTo(o.成员名);。降序写成o.成员名.compareTo(this.成员名);

比如下面对一个学生对象的数组进行排序:

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

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
	//按年龄排序
    @Override
    public int compareTo(Student o) {
        return this.age-o.age;
    }

public class Test {
    //按年龄排序
    public static void main(String[] args) {
        Student[] students = new Student[3];
        students[0] = new Student("zhangsan",18);
        students[1] = new Student("lisi",39);
        students[2] = new Student("wangwu",26);
        Arrays.sort(students);
        System.out.println(Arrays.toString(students));
    }
}

按姓名排序:(重写compareTO方法即可)

@Override
public int compareTo(Student o) {
   return this.name.compareTo(o.name);
}

🍑2、通过Comparator接口排序

我们通常将实现Comparator接口的类称为一个比较器,通过Arrays.sort(数组名,比较器类名)实现对对象数组的排序。

下面仍以学生对象为例,这次通过实现Comparator接口实现对其排序:

import java.util.Arrays;
import java.util.Comparator;

class Student {
    private String name;
    private int age;

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

    public String getName() {
        return name;
    }


    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
//姓名比较器
class NameCompare implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.getName().compareTo(o2.getName());
    }
}
//年龄比较器
class AgeCompare implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.getAge()- o2.getAge();
    }
}
//测试用例
public class Test {
    public static void main(String[] args) {
        Student[] students = new Student[3];
        NameCompare nameCompare = new NameCompare();
        AgeCompare ageCompare = new AgeCompare();
        students[0] = new Student("zhangsan",18);
        students[1] = new Student("lisi",39);
        students[2] = new Student("wangwu",26);
        Arrays.sort(students,nameCompare);//按姓名排序
        System.out.println(Arrays.toString(students));
        Arrays.sort(students,ageCompare);//按年龄排序
        System.out.println(Arrays.toString(students));
    }
}

实现Comparable接口就可以实现对象的比较,那么为什么还要引出Comparator接口呢?

其实,实现Comparable接口的方式比实现Comparator接口的耦合性更强,也就是说,如果要修改比较算法,要修改Comparable接口的实现类,而实现Comparator的类是在外部进行比较的,不需要对实现类有任何修改。从这个角度说,通过实现Comparator接口的比较,使用起来会更加灵活。


小结

本章浅浅介绍了Object类中的四种方法,另外本期重点:

重点一: 是能够理解并且灵活的使用Clonable接口搭配clone方法进行深浅拷贝。
重点二: 是能够灵活应用ComparatorComparable给对象数组进行排序。
重点三: 通过这些例子再次加深对接口的理解。

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

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

相关文章

什么是BadUSB攻击以及如何预防

BadUSB 攻击是指 USB 设备存在内置固件漏洞&#xff0c;该漏洞允许自身伪装成人机接口设备。一旦连接到其目标计算机&#xff0c;BadUSB 就可以谨慎地执行有害命令或注入恶意负载。 一种常见的BadUSB攻击类型是橡皮鸭。它可以通过使用使用隐藏漏洞创建的闪存驱动器来执行&…

第二证券|卡塔尔给体育烧的钱,不止世界杯

11月&#xff0c;世界杯史上首次在北半球冬季打响。 全世界的目光也聚焦到了卡塔尔——这个面积仅11576平方千米、人口不足300万的中东小国。 虽然面积小&#xff0c;卡塔尔人花钱却很大方。 本届世界杯总计2200亿美元的投入&#xff0c;为历届世界杯花费之最。巴西世界杯和俄…

java基于springboot的新生宿舍管理系统-计算机毕业设计

项目介绍 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;新生宿舍管理系统当然也不能排除在外。新生宿舍管理系统是以实际运用为开发背景&#xff0c;运用软件工程原理和开发…

海外社媒运营,推特内容营销

Twitter 成立于 2006 年&#xff0c;已成为全球第三大用户社交平台&#xff0c;月活跃用户达 3.89 亿。推特最大的特点就是字数限制和信息短小&#xff0c;正好符合现代人的阅读习惯。 对于跨境卖家来说&#xff0c;推特不仅可以获取有价值的客户信息&#xff0c;收集粉丝反馈…

大学电子系C++模拟考试之一

随手附上一些代码&#xff0c;未必是最优解&#xff0c;仅供参考。 加密四位数 【问题描述】 输入一个四位数&#xff0c;将其加密后输出。方法是将该数每一位的数字加9&#xff0c;然后除以10取余作为该位上的新数字&#xff0c;最后将千位上的数字和十位上的数字互换&#…

7个成功的DTC品牌出海营销策略,提高海外客户的忠诚度!

关键词&#xff1a;DTC品牌出海、DTC营销、客户忠诚度 近年来&#xff0c;普通消费者关心的事情发生了巨大变化。 60% 的消费者会特意从品牌而不是第三方零售商处购买。 从大型零售商处购买再成为主流。人们希望与他们关心并感到关心的品牌建立关系。他们希望支持独立企业并找到…

Spring中IOC容器

IOC入门案例思路分析 1.管理什么&#xff08;Service和Dao&#xff09; 2.如何将管理的对象存放到IOC容器&#xff08;配置applicationContext.xml&#xff09;第二步 3.将管理的对象存放到IOC容器&#xff0c;如何获取IOC容器 第三步 4.获取到IOC容器后&#xff0c;如何从…

纷繁复杂见真章,华为云产品需求管理利器CodeArts Req解读

摘要&#xff1a;到底什么是需求&#xff1f;又该如何做好需求管理&#xff1f;本文分享自华为云社区《纷繁复杂见真章&#xff0c;华为云产品需求管理利器 CodeArts Req 解读》&#xff0c;作者&#xff1a;华为云头条 。 2022 年 8 月&#xff0c;某国国税局获得数十亿美元新…

【Keras计算机视觉OCR文字识别】文字检测算法中CTPN、CRAFT的讲解(图文解释 超详细)

觉得有帮助麻烦点赞关注收藏~~~ 一、OCR文字识别的概念 OCR&#xff08;Optical Character Recognition&#xff09;图像文字识别是人工智能的重要分支&#xff0c;赋予计算机人眼的功能&#xff0c;可以看图识字。如图6-1所示&#xff0c;图像文字识别系统流程一般分为图像采…

干掉满屏的 try-catch,这样写太香了!

背景 软件开发过程中&#xff0c;不可避免的是需要处理各种异常&#xff0c;就我自己来说&#xff0c;至少有一半以上的时间都是在处理各种异常情况&#xff0c;所以代码中就会出现大量的try {...} catch {...} finally {...} 代码块&#xff0c;不仅有大量的冗余代码&#xf…

Windows系统如何部署Rabbit和启动Rabbit服务

如何部署Rabbit和启动Rabbit服务第一步&#xff1a;安装otp下载OPT应用&#xff1a;安装OPT第二部&#xff1a;安装Rabbit下载Rabbit安装Rabbit执行命令&#xff0c;添加可视化插件第三步&#xff1a;启动Rabbit服务第四步&#xff1a;在网页验证rabbit服务器启动第一步&#x…

题目给出一个字符串s1,我们可以用递归的方法将字符串分成两个非空的子串来将s1表示成一个二叉树

题目给出一个字符串s1&#xff0c;我们可以用递归的方法将字符串分成两个非空的子串来将s1表示成一个二叉树 下面是s1“coder”的一种二叉树的表现形式&#xff1a; 将字符串乱序的方法是&#xff1a;选择任意的非叶子节点&#xff0c;交换它的两个孩子节点。 例如&#xff1…

图扑虚拟现实解决方案,实现 VR 数智机房

如今&#xff0c;虚拟现实技术作为连接虚拟世界和现实世界的桥梁&#xff0c;正加速各领域应用形成新场景、新模式、新业态。 效果展示 图扑软件基于自研可视化引擎 HT for Web 搭建的 VR 数据中心机房&#xff0c;是将数据中心的运营搬到 VR 虚拟场景。以数据中心实际场景为…

new String 到底创建几个对象的问题困扰

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是「奇点」&#xff0c;江湖人称 singularity。刚工作几年&#xff0c;想和大家一同进步&#x1f91d;&#x1f91d; 一位上进心十足的【Java ToB端大厂…

Sharding-Proxy查询分库分表数据,很方便!!

##背景 目前公司不大&#xff0c;没有方便的数据库平台支持分库分表数据的查询&#xff0c;每次只能拿到分表的数据字段&#xff0c;手动hash获得标的索引&#xff0c;再去物理表查询&#xff0c;费时费力 ##改造 目前市面上有很多mysql分库分表的代理&#xff0c;自己选择了S…

24.前端笔记-CSS-vertical-align属性

1、vertical-align属性使用场景 经常用于设置图片或表单&#xff08;行内块元素&#xff09;和文字垂直对齐。 只对行内元素或行内块元素有效 vertical-align:baseline|top|middle|bottom值描述baseline默认&#xff0c;元素放在父元素的基线上top把元素顶端与行种最高元素顶…

这款 Java 性能调优的可视化工具,你真的会用吗?

VisualVM 是Netbeans的profile子项目&#xff0c;已在JDK6.0 update 7 中自带&#xff0c;能够监控线程&#xff0c;内存情况&#xff0c;查看方法的CPU时间和内存中的对 象&#xff0c;已被GC的对象&#xff0c;反向查看分配的堆栈(如100个String对象分别由哪几个对象分配出来…

以太网 VLAN(VLAN划分方式)

2.8.3 以太网 VLAN&#xff08;VLAN划分方式&#xff09; VLAN的划分方式有2.8.3 以太网 VLAN&#xff08;VLAN划分方式&#xff09;一、基于端口划分二、基于MAC地址划分三、基于IP子网划分四、基于协议划分五、基于策略划分一、基于端口划分 简述&#xff1a;端口上进行手动…

python安装 yaml、pymysql、python-docx

python安装 yaml E:\python3.10.7>pip install pyyaml python安装 pymysql E:\python3.10.7>pip install pymysql python安装 python-docx E:\python3.10.7>pip install python-docx python安装 sqlalchemy E:\python3.10.7>pip install sqlalchemy

客快物流大数据项目(九十四):ClickHouse的SummingMergeTree入了解

文章目录 ClickHouse的SummingMergeTree深入了解 一、创建SummingMergeTree引擎表的的语法 二、创建Su