Java接口的应用
创始人
2024-02-02 19:33:10
  1. 目标:总结Comparable接口以及compareTo方法、comparator接口中compare方法比较器、toString方法、equals方法、hashCode方法、Cloneable接口以及深浅拷贝

  2. 比较对象中内容的大小【Comparable接口以及compareTo方法】
    例如:学生类:成员有姓名、年龄、分数
    要求:分别按照姓名、年龄、分数排序
    初步编写:
    学生类:

package demo2;public class Student {public String name;public int age;public int score;public Student(String name, int age, int score) {this.name = name;this.age = age;this.score = score;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", score=" + score +'}';}
}

测试类:
package demo2;

import java.util.Arrays;

public class TestStudent {
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student(“王王王”,21,98);
students[1] = new Student(“帅帅帅”,22,66);
students[2] = new Student(“哈哈哈”,23,99);

    Arrays.sort(students);System.out.println(Arrays.toString(students));
}

}

运行结果:抛出异常
在这里插入图片描述
分析:Arrays.sort(students);不知道怎么比较大小
根据报错提示:
在这里插入图片描述
在这里插入图片描述
作用:取到数组的某一个值,把这个值转换为一个Comparable接口类型,然后调用compareTo方法
在这里插入图片描述

因此在Student类中实现Comparable接口,并重写compareTo方法。

package demo2;public class Student implements Comparable{public String name;public int age;public int score;@Overridepublic int compareTo(Object o) {//向下转型,因为Object是所有类的父类,将Object父类转成Studnet子类Student s = (Student) o;if (this.age > s.age) {return 1;}else if (this.age < s.age) {return  -1;}else {return 0;}}public Student(String name, int age, int score) {this.name = name;this.age = age;this.score = score;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", score=" + score +'}';}
}

运行结果:
在这里插入图片描述
分析:此时按照年龄从小到大排序。

        System.out.println(students[0].compareTo(students[1]));

让0下标的学生去调用compareTo方法去和1下标的学生进行比较,所以在compareTo方法中,this代表0下标的学生,传入的Object o,以及强制类型转换成Student s代表的是1下标的学生。
在这里插入图片描述
假如根据姓名比较,重写的compareTo方法相当于写了一个比较规则,从而sort也有用了

    public int compareTo(Object o) {Student s = (Student) o;if (this.name.compareTo(s.name) > 0) {return 1;}else if (this.name.compareTo(s.name) < 0) {return -1;}else {return 0;}}

运行结果:
在这里插入图片描述
分析:此处的name是String类型,String类型比较大小不能通过数学符号(>和<等)比较,要通过String类中的compareTo方法(String类也实现了Comparable接口,则必定也实现了compareTo方法),equals只能比较字符串相不相同(并且equals返回值的类型是boolean类型)

自己实现sort【采用冒泡排序】

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

注意:形参是Comparable接口类型,在if语句中的比较规则用到实现接口中的compareTo方法。

但是,目前这样写,用Comparable接口不好,因为代码不够灵活,一旦写死了拿年龄比较,以后默认都是拿年龄比较,那我要想拿名字或者分数比较就不行了。

  1. 比较对象中内容的大小【比较器Comparator接口中compare方法】
    刚刚是Student类实现Comparable接口重写compareTo方法,现在是新创建一个毫不相干的类实现Comparator接口。
    Comparator接口里面的抽象方法只有一个,所以新建一个AgeComparator类实现Comparator接口重写compare方法。
    在这里插入图片描述
    新建一个类实现接口并重写方法:
package demo4;import java.util.Comparator;public class AgeComparator implements Comparator {@Overridepublic int compare(Student o1, Student o2) {return o1.age - o2.age;}
}

实例化新建类的对象,并作为sort的第二个参数

    public static void main(String[] args) {Student[] students = new Student[3];students[0] = new Student("王王王",23,98);students[1] = new Student("帅帅帅",22,66);students[2] = new Student("哈哈哈",24,99);//既然是一个类,就在这里面实例化这个对象AgeComparator ageComparator = new AgeComparator();//把他传给sort的第二个参数Arrays.sort(students,ageComparator);System.out.println(Arrays.toString(students));}

分析: AgeComparator ageComparator = new AgeComparator(); 叫做比较器,这样实现比较就比较灵活。
比如想通过分数比较,可以新建一个分数的比较器:

package demo4;import java.util.Comparator;public class ScoreComparator implements Comparator  {@Overridepublic int compare(Student o1, Student o2) {return o1.score - o2.score;}
}
        ScoreComparator scoreComparator = new ScoreComparator();Arrays.sort(students,scoreComparator);

通过姓名比较:

package demo4;import java.util.Comparator;public class NameCompartor implements Comparator {@Overridepublic int compare(Student o1, Student o2) {return (o1.name.compareTo(o2.name));}
}
        NameCompartor nameCompartor = new NameCompartor();Arrays.sort(students,nameCompartor);

以上就是非常灵活的比较器,想怎么写就怎么写。

  1. Object是Java默认提供的一个类,Java中Object类是所有类的父类,默认会继承Object父类,即所有类的对象都可以使用Object的引用进行接受
    Object类中有的方法
    在这里插入图片描述
  2. Object类中的toString方法
    在这里插入图片描述
    如果我们要打印对象中的内容,则需要在对象中重写toString方法。
    因为可以认为要重写的那个对象的父类一定是Object,因此重写toString
    @Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", score=" + score +'}';}

①如果没有重写toString方法,运行结果:
在这里插入图片描述
②如果重写了toString方法,运行结果:
在这里插入图片描述

  1. Object类中的equals方法
    public boolean equals(Object obj) {return (this == obj);}

如果要比较对象中内容是否相同,必须重写Object中的equals方法,因为equals方法默认是按照地址比较的。
在创建2个对象,分别为(姓名和年龄都一样):
姓名:小王,年龄22;姓名:小王,年龄22

    public static void main(String[] args) {Student student1 = new Student("小王",22);Student student2 = new Student("小王",22);System.out.println(student1.equals(student2));}

①没有重写equals,运行结果:
在这里插入图片描述
分析:因为2个对象的姓名和年龄都相同,在主观现实世界中,我们会认为这2个对象是同一个对象,但是程序的运行结果却是flase,因此我们要重写Objec类中的equals方法。

②重写equals,运行结果:

   @Overridepublic boolean equals(Object o) {if (this == o) {return true;}if (o == null) {return false;}if (! (o instanceof Student)) {return false;}Student s = (Student) o;if (this.name == s.name && this.age == s.age) {return true;}return false;}

运行结果:
在这里插入图片描述
编译器自动重写的equals方法:

    @Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Student student = (Student) o;return age == student.age && Objects.equals(name, student.name);}
  1. Object类中的hashCode方法
    在上例中创建的2个学生类对象,我们认为2个对象是指同一个人(对象),所有在打印2个对象的地址的时候,理论上的运行结果应该是在内存中存同一个地址。
        System.out.println(student1);System.out.println(student2);

①没有重写hashCode方法的时候,运行结果:
在这里插入图片描述
②重写hashCode方法的运行结果:

    @Overridepublic int hashCode() {return Objects.hash(name, age);}

在这里插入图片描述
分析:通过name和age去返回哈希地址,因为student1和student2的name和age都相同,因此打印出来的地址也是相同的。
总结:hashcode方法用来确定对象在内存中存储的位置是否相同

  1. 要去克隆,则一定要实现Clonable接口
    在这里插入图片描述
    分析:克隆接口Cloneable时一个空接口,也叫标记接口,作用是:表示当前对象是可以被克隆的。
    @Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
    public static void main(String[] args) throws CloneNotSupportedException  {Student student1 = new Student("小帅");Student student2 = (Student) student1.clone();System.out.println(student1);System.out.println(student2);}

整体代码:

package demo5;class Student implements Cloneable{public String name;public Student(String name) {this.name = name;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +'}';}
}public class Test {//学习自定义类型克隆拷贝public static void main(String[] args) throws CloneNotSupportedException  {Student student1 = new Student("小帅");Student student2 = (Student) student1.clone();System.out.println(student1);System.out.println(student2);}
}

运行结果:
在这里插入图片描述
分析:目前是可以对自定义类型进行拷贝了,但是拷贝分为浅拷贝和深拷贝。

  1. 关于浅拷贝和深拷贝
    浅拷贝:修改拷贝的对象(副本),之前的本身也会改变。
    深拷贝: 修改拷贝的对象(副本),之前的本身不受影响。
    如图:本身是蓝色,拷贝后也是蓝色,如果用红色修改拷贝后的,原本身的也被会红色修改则是浅拷贝,原本身的不会被红色修改就是深拷贝。
    在这里插入图片描述
  2. 浅拷贝例子
    新建一个Money类
class Money {public double money = 12.5;
}

组合:每个学生都有钱,在Student类中实例化Money对象

class Student implements Cloneable{public String name;public Money m = new Money();public Student(String name) {this.name = name;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", m=" + m +'}';}
}

测试:

public class Test {//学习自定义类型克隆拷贝public static void main(String[] args) throws CloneNotSupportedException  {Student student1 = new Student("小帅");Student student2 = (Student) student1.clone();System.out.println(student1.m.money);System.out.println(student2.m.money);student2.m.money = 99;System.out.println("修改拷贝对象的money之后的打印结果");System.out.println(student1.m.money);System.out.println(student2.m.money);}
}

运行结果:
在这里插入图片描述
分析:修改拷贝对象student2的值money,原来的student1的值也发生了改变,则是浅拷贝,因为只对student1拷贝,并没有把money也拷贝一份。
在这里插入图片描述

  1. 将上述例子改成深拷贝:即m所指的money对象也要克隆
    ①Money类实现Cloneable接口
class Money implements  Cloneable{public double money = 12.5;@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}

②Student类重写clone方法

    @Overrideprotected Object clone() throws CloneNotSupportedException {Student student = (Student) super.clone(); //得到一个浅拷贝对象student.m = (Money) this.m.clone(); //浅拷贝的对象的m要再次拷贝,从而实现深拷贝//this 谁调用这个方法就指谁,这里指student1return student;}

在这里插入图片描述
分析:此时实现了深拷贝,Student student = (Student) super.clone();只是克隆了student对象, student.m = (Money) this.m.clone();克隆了student对象里面的money对象。
在这里插入图片描述
所以如要要实现深拷贝,当前对象的每一个对象都要克隆。

  1. 使用Objetct接受所有类的对象,以及匿名对象的使用
package demo6;class  Dog {}public class Test {public static void func(Object o) {System.out.println("wow");}public static void main(String[] args) {func(new Dog());Dog dog = new Dog();func(dog);}
}

所有类的对象都可以使用Object的引用进行接受public static void func(Object o)
当没有给其他引用的时候,这些都是匿名对象,形如: func(new Dog());即为匿名对象,缺点是每次使用都需要new,使用场景是只用一次的时候。

相关内容

热门资讯

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