经典接口

本文详细介绍了Java中的Comparable和Comparator接口,讲解了它们在对象排序中的作用。Comparable接口用于对象的自然排序,而Comparator接口则提供了定制排序的可能。文章通过实例展示了如何在数组工具类中实现不同数据类型和对象类型的排序,并讨论了对象数组比较大小的解决方案。同时,还探讨了Cloneable接口的使用及其与对象克隆的关系。
摘要由CSDN通过智能技术生成

1、java.lang.Comparable
    int compareTo(Object obj):  自然排序接口

    返回值类型是int,它的结果的特点:
    如果调用compareTo方法的对象 大于()中的实参对象,那么返回正整数
    如果调用compareTo方法的对象 小于()中的实参对象,那么返回负整数
    如果调用compareTo方法的对象 等于()中的实参对象,那么返回0

什么时候会用到这个接口?
当需要比较两个对象的大小,就可以使用Comparable接口。

需求:想要定义一个数组工具类MyArrays,这个工具类中包含以下一些方法:
(1)可以给任意的int类型的数组排序,从小到大
(2)可以返回任意的int类型的数组的元素列表拼接的字符串结果

(3)可以给任意的double类型的数组排序,从小到大
(4)可以返回任意的double类型的数组的元素列表拼接的字符串结果

因为我们各种基本数据类型的数组之间是互不兼容的,所以如果要支持8种基本数据类型,需要重载8套这样的方法。

(5)可以给任意的对象类型(引用数据类型)的数组排序,从小到大,按照元素类型重写的Comparable接口的compareTo方法比较大小
(6)可以返回任意的对象类型(引用数据类型)的数组的元素列表拼接的字符串结果

(7)可以给任意的对象类型(引用数据类型)的数组排序,“从小到大”,按照Comparator接口的实现类的compare方法比较大小

public class TestInterface {
    public static void main(String[] args) {
        //String类型实现Comparable接口
        String[] array3 = {"helloabc","hello","java","world","chai","atguigu"};
        MyArrays.sort(array3);
        System.out.println(MyArrays.toString(array3));

        System.out.println("--------------------");
        Student[] array4 = new Student[3];
        array4[0] = new Student("张三",87);
        array4[1] = new Student("李四",99);
        array4[2] = new Student("王五",85);
        MyArrays.sort(array4);
        System.out.println(MyArrays.toString(array4));
    }
}

class Student implements Comparable{
    private String name;
    private int score;

    public Student() {
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }

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

    @Override
    public int compareTo(Object o) {
        //this对象,和o对象的两个学生对象比较大小
/*        if(this.score > ((Student)o).score){
            return 1;
        }else if(this.score < ((Student)o).score){
            return -1;
        }
        return 0;*/
        return this.score - ((Student)o).score;
        /*
        this.score > ((Student)o).score,差是正
        this.score < ((Student)o).score,差是负
        this.score = ((Student)o).score,差是0
         */
    }
}

对象数组的比较大小?
    (1)对象之间是不支持直接使用">,<,<=,>="运算符比较大小的,怎么办?
    (2)形参Object[]数组,它是实参的对象数组不一定是什么类型的,可能是String[],可能是Student[]。。。。
        Object[]的数组,可以接收任意的对象数组,但是就不能接收基本数据类型的数组。
    (3)因此不知道对方想要按照什么规则比较大小。
        例如:String[]想要按照Unicode编码值比较大小
             Student[]想要按照成绩score比较大小

      解决方案的讨论:
      (1)发现Object类中没有“比较大小”的方法  :否决
      (2)用instanceof判断,向下转型,比较大小,要考虑的类型太多了写不完:否决
      (3)我们可以设计一个接口(标准),让想要使用这个sort(Object[] arr)排序的对象数组的元素类型
        去遵循这个标准,实现这个标准。例如:java.lang.Comparable接口。
        这点就有点像,电脑想要提供给任意的硬件设备一个连接方式,如果不给出标准的话,
        就需要提供n种类型的插口,没头。
        所以电脑想要简化自己的插口,就需要制定一个标准,例如:USB标准,自己和其他硬件厂商都遵循这个标准。
        谁要用这个USB标准,谁就遵守并且实现(提供硬件和软件的支持),软件支持就是提供驱动程序。

       Java制定了一个对象比较大小的标准接口,java.lang.Comparable接口。
       然后所有要比较大小的对象类型,都可以实现这个Comparable接口,并且重写它的抽象方法。
       这样的话,所有对象比较大小,就有了一个统一的方式。

       java.lang.Comparable接口的抽象方法是int compareTo(Object o)。
       换句话说,凡是要比较大小的对象类型,都要实现Comparable接口,都重写了compareTo方法,
       它的对象就都可以调用compareTo方法。

import java.util.Comparator;

//数组工具类
public class MyArrays {
    //(1)可以给任意的int类型的数组排序,从小到大
    public static void sort(int[] arr){
        for(int i=1; i<arr.length; i++){
            for(int j=0; j<arr.length-i; j++){
                if(arr[j]>arr[j+1]){
                    int temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
    }

   // (2)可以返回任意的int类型的数组的元素列表拼接的字符串结果
    public static String toString(int[] arr){
        String result = "[";
        for(int i=0; i<arr.length; i++){
            if(i==0){
                result += arr[i];
            }else{
                result += "," + arr[i];
            }
        }
        result += "]";
        return result;
    }

    //    (3)可以给任意的double类型的数组排序,从小到大
    public static void sort(double[] arr){
        for(int i=1; i<arr.length; i++){
            for(int j=0; j<arr.length-i; j++){
                if(arr[j]>arr[j+1]){
                    double temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
    }
    //(4)可以返回任意的double类型的数组的元素列表拼接的字符串结果
    public static String toString(double[] arr){
        String result = "[";
        for(int i=0; i<arr.length; i++){
            if(i==0){
                result += arr[i];
            }else{
                result += "," + arr[i];
            }
        }
        result += "]";
        return result;
    }

    // (5)可以给任意的对象类型(引用数据类型)的数组排序,“从小到大”,按照元素类型重写的Comparable接口的compareTo方法比较大小
   
  
    public static void sort(Object[] arr){
        for(int i=1; i<arr.length; i++){
            for(int j=0; j<arr.length-i; j++){
               /* if(arr[j] > arr[j+1]){ //arr[j]和arr[j+1]是引用数据类型,引用数据类型是不能直接比较大小的
                if(arr[j] instanceof Student){
                     Student before = (Student) arr[j];
                     Student after = (Student) arr[j+1];
                     if(before.getScore() > after.getScore()) {
                         Object temp = arr[j];
                         arr[j] = arr[j + 1];
                         arr[j + 1] = temp;
                     }
                 }*/
                /*arr[j]和arr[j+1]要比较大小,
                arr[j]和arr[j+1]的编译时类型是Object,
                需要arr[j]和arr[j+1]的对象的运行时类型实现Comparable接口,
                既然它们实现了这个接口,我们就可以把arr[j]向下转型为Comparable接口类型,
                向下转型的目的是为了调用compareTo方法,否则就调不了,因为Object类型中没有compareTo方法。
                这里没有把arr[j+1]向下转型的原因是compareTo方法的形参是Object类型,可以直接接收arr[j+1]*/
                Comparable before = (Comparable) arr[j];
                /*如果arr[j],就是这里的before,大于arr[j+1],那么compareTo的返回值结果就是正整数.
                compareTo是虚方法,before编译时类型现在是Comparable类型
                运行时类型,要看调用sort方法的实参数组的类型,
                当是String[]排序时,before的运行时类型是String,compareTo执行的就是String类中compareTo方法。
                当是Student[]排序时,before的运行时类型是Student,compareTo执行的就是Student类中compareTo方法。
                 */
                if(before.compareTo(arr[j+1])>0){
                    Object temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
    }

    //(6)可以返回任意的对象类型(引用数据类型)的数组的元素列表拼接的字符串结果
    public static String toString(Object[] arr){
        String result = "[";
        for(int i=0; i<arr.length; i++){
            if(i==0){
                result += arr[i];
            }else{
                result += "," + arr[i];
            }
        }
        result += "]";
        return result;
    }

    //(7)可以给任意的对象类型(引用数据类型)的数组排序,“从小到大”,按照Comparator接口的实现类的compare方法比较大小
//    形参Object[] arr的作用是接收一个对象类型(引用数据类型)的数组,多态数组
//    形参Comparator comparator的作用是接收一个照Comparator接口的实现类对象,多态参数
    public static void sort(Object[] arr, Comparator comparator){
        for(int i=1; i<arr.length; i++){
            for(int j=0; j<arr.length-i; j++){
                /* if(arr[j] > arr[j+1]){ //arr[j]和arr[j+1]是引用数据类型,引用数据类型是不能直接比较大小的
                //这里comparator是一个Comparator接口类型的一个变量,它调用的compare方法是一个抽象方法,
                //最终执行的方法体代码,要看 形参comparator接收的实参的类型是什么,找对应类型的compare方法实现*/
                if(comparator.compare(arr[j],arr[j+1])>0){
                    Object temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
    }
}

2、java.util.Comparator接口

int compare(Object o1, Object o2)    定制排序接口
Comparator接口相当于是对Comparable接口的一个补充。即当我们存在多种排序要求时,默认的排序一般选择Comparable接口的实现,我们称为自然排序。
       其他的排序一般就选择Comparator接口的实现,我们称为定制排序。

   Comparator接口的抽象方法:int compare(Object o1, Object o2)
            当o1对象大于o2对象时,返回正整数
            当o1对象小于o2对象时,返回负整数
            当o1对象等于o2对象时,返回0

需求:
    有一个员工类Employee,它包含id,name,salary,age等。
    (1)默认情况下,Employee的对象数组是按照id排序。从小到大。
    (2)财务部门,想要把Employee按照薪资高低排一下顺序,(从高到低),薪资相同的,按照name排序。
    (3)人事部分,想要把Employee按照年龄大小排序,从小到大

 都想要排序,怎么办?

问题:Employee类中实现了Comparable接口,重写了compareTo方法,但是它是按照id排序的,
    无法满足我们更多的其他排序要求。如何使用Comparator接口?
   单独声明一个类型,实现 Comparator接口。
   有几种排序情况,就写几个Comparator接口的实现类即可。这叫定制。

import java.util.Comparator;

public class TestInterface2 {
    public static void main(String[] args) {
        Employee[] arr = new Employee[5];
        arr[0] = new Employee(2,"lisi",14000,24);
        arr[1] = new Employee(1,"zhangsan",15000,23);
        arr[2] = new Employee(3,"wangwu",11000,22);
        arr[3] = new Employee(5,"zhaoliu",15000,25);
        arr[4] = new Employee(4,"laowang",18000,22);
        /*        System.out.println(MyArrays.toString(arr));//显示一行太长,可以自己遍历*/
        /*按照id排序从小到大
        //想起,我们刚刚写的MyArrays数组工具类,
        // 有一个public static void sort(Object[] arr)方法,可以实现对象的排序
        //但是要求调用这个sort方法的对象数组的元素类型,必须实现Comparable接口,重写compareTo方法*/
        MyArrays.sort(arr);

        System.out.println("按照id升序排列的结果:");
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }

        System.out.println("-----------------------");
        //财务部门,想要把Employee按照薪资高低排一下顺序,(从高到低),薪资相同的,按照name排序。
        MyArrays.sort(arr, new EmployeeSalaryComparator());

        System.out.println("按照薪资降序排列的结果:");
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }

        System.out.println("-------------------------");
        //(3)人事部分,想要把Employee按照年龄大小排序,从小到大
        MyArrays.sort(arr, new EmployeeAgeComparator());

        System.out.println("按照年龄升序排列的结果:");
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }

    }
}
class EmployeeAgeComparator implements Comparator{
    @Override
    public int compare(Object o1, Object o2) {
        Employee e1 = (Employee) o1;
        Employee e2 = (Employee) o2;
        int result = e1.getAge() - e2.getAge();
        return result == 0 ? e1.getName().compareTo(e2.getName()): result;
    }
}

class EmployeeSalaryComparator implements Comparator{
    @Override
    public int compare(Object o1, Object o2) {
        Employee e1 = (Employee) o1;
        Employee e2 = (Employee) o2;
        int result = Double.compare(e1.getSalary(), e2.getSalary());//Double是包装类,它有一个compare方法,比较两个小数
        /*e1.getSalary() > e2.getSalary(),result结果是正整数
        //e1.getSalary() < e2.getSalary(),result结果是负整数
        //e1.getSalary() = e2.getSalary(),result结果是0
        if(result==0){
            return e1.getName().compareTo(e2.getName());//因为e1.getName()是String类型,String类型是实现了Comparable接口的,有compareTo方法
        }else{
            return result;
        }*/
        return result==0 ? e1.getName().compareTo(e2.getName()) : -result;
        //这里:后面写 -result,是在调用sort方法时实现降序的效果
    }
}

class Employee implements Comparable{
    private int id;
    private String name;
    private double salary;
    private int age;

    public Employee() {
    }

    public Employee(int id, String name, double salary, int age) {
        this.id = id;
        this.name = name;
        this.salary = salary;
        this.age = age;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

    @Override
    public int compareTo(Object o) {
        return this.id - ((Employee)o).id;
    }
}

3、java.lang.Cloneable接口:使用的不是很多,但是因为Object类所有类型的根类型,它里面有一个clone()方法和这个接口有关。
java.lang.Object类
     protected Object clone()throws CloneNotSupportedException
当子类没有实现Cloneable接口,就算重写clone方法也会报CloneNotSupportedException异常,
换句话说,子类需要调用这个方法,就要实现Cloneable接口,并重写clone()方法。

Cloneable接口中,没有看到抽象方法,因为这个接口的抽象方法是clone()方法,它已经在根父类Object中声明了,
表示所有类型都有这个方法了,它就不需要在这个接口中声明了,如果子类想要重写的话,
直接重写Object类中的即可,但是子类还得实现Cloneable接口。

public class TestCloneable {
    public static void main(String[] args) throws CloneNotSupportedException {
        //创建Person的对象
        Person p1 = new Person("张三",23);
        //clone()是Object类的,那么任意对象都应该有这个方法
        Object cloneObj = p1.clone();//如果Person类没有重写clone,那么这样访问报错,因为clone方法的权限修饰符是protected的
        System.out.println("p1="+p1);
        System.out.println("cloneObj="+cloneObj);

        System.out.println(p1 == cloneObj);//false,原对象与副本对象的地址是不同的,因为是两个对象,但是它们的属性内容是一样的
        System.out.println(p1.equals(cloneObj));//false,如果Person类没有重写equals方法,效果和==比较是一样,除非重写

    }
}

class Person implements Cloneable{
    private String name;
    private int age;

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

    @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);
    }

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

    //这里重写的目的是为了扩大clone方法的可见性范围,把protected变为public
    //如果对象的类不支持 Cloneable 接口,则重写 clone 方法的子类也会抛出CloneNotSupportedException异常,以指示无法复制某个实例。
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();//调用父类Object被重写的clone()
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值