简介Object类+接口实例(深浅拷贝、对象数组排序)
创始人
2024-03-29 09:44:43

本期目录

  • 前言
  • 一、初识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@Overridepublic 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;@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}@Overridepublic 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;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}//浅拷贝@Overrideprotected 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引用对象,并将克隆的新引用赋值给stuclonestutclone.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 {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{private String name;private int age;public Student(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}//按年龄排序@Overridepublic 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;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}
}
//姓名比较器
class NameCompare implements Comparator {@Overridepublic int compare(Student o1, Student o2) {return o1.getName().compareTo(o2.getName());}
}
//年龄比较器
class AgeCompare implements Comparator {@Overridepublic 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给对象数组进行排序。
重点三: 通过这些例子再次加深对接口的理解。

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...