✨✨谢谢大家捧场,祝屏幕前的小伙伴们每天都有好运相伴左右,一定要天天开心哦!✨✨
🎈🎈作者主页: 🎈丠丠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));
-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);
}
再输出我们结果,发现经过一系列算法,两个对象出现了同一位置
三、接口实现实例
我们先看这样一个代码
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方法实现的,方法内部实现的不一样
希望对你有帮助