深入分析Comparable与Comparator及Clonable三个Java接口

目录
  • 1.Comparable
  • 2.Comparator比较器
  • 3.Clonable接口和深拷贝

1.Comparable

这个接口是用来给对象数组来排序的

在我学接口之前我用的排序方法是Arrays.sort(),我发现单单靠之前所学知识并不能解决给对象数组排序的问题,后来学习过程中发现Comparable这一接口解决了我的疑惑,也感受到了这一接口的强大之处,但这也不是最好的,后续会说到,毕竟学知识是个循序渐进的过程嘛

首先,我们看一下我们之前学习时用的Arrays.sort

public class TestDemo {
    public static void main(String[] args) {
        int[] array = {1,3,6,2,4};
        Arrays.sort(array);
        System.out.println(Arrays.toString(array));
    }
}

它能将整形数组从小到大排序,,,下面我们再来看一下Arrays.sort给对象数组排序(错误示范)

class Student {
    public String name;
    public int age;
    public double score;

    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
}
public class TestDemo {
    public static void main(String[] args) {
        Student[] students = new Student[3];
        students[0] = new Student("zhangsan",98,78.9);
        students[1] = new Student("lisi",38,48.9);
        students[2] = new Student("wangwu",18,88.9);
        Arrays.sort(students);
        System.out.println(Arrays.toString(students));
    }
}

当我们写出这样一个代码的时候,我们会发现运行结果是个什么东西???

这时候不要慌,简单分析一下报错原因,我们可以看到它报了一个ClassCastException(类型转换异常),根据后面的稍微能看懂的几个英文(小编自己英文水平太差,所以只能看懂几个),可以大概的知道是说Student不能转换为java.lang包下的Comparable,这时候我们点进去看一下源码,不要害怕看源码,有时候我们往往只需要看懂一点就能明白错误的原因了

经过粗略的分析if条件语句这一行,发现数组取下标呢,元素与元素之间都用compareTo来比较,这时候,我们发现其中的猫腻了,,,我们打开帮助手册查一下Comparable,发现它是一个泛型接口,,并且它有一个抽象方法compareTo,这时候面纱就将要一层一层的揭开了

compareTo方法中这一大段画,看到第一行我们就明白了,这东西可以帮我们解决数组对象的比较问题,所以我们要拿Student这个类去实现Comparable,并且实现compareTo方法,就能做到对象数组的排序了,看代码:

class Student implements Comparable<Student>{
    public String name;
    public int age;
    public double score;

    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
    @Override
    public int compareTo(Student o) {
        //return this.name.compareTo(o.name);
        //return this.age - o.age;
        return (int)(this.score-o.score);
    }
}

这时候Arrays.sort()就可以帮我们做到对象数组的排序了,在 sort 方法中会自动调用 compareTo 方法. compareTo 的参数是 Object , 其实传入的就是 Student 类型的对象.

然后比较当前对象和参数对象的大小关系 (例如按score ).

  • 如果当前对象应排在参数对象之前 , 返回小于 0 的数字 ;
  • 如果当前对象应排在参数对象之后 , 返回大于 0 的数字;
  • 如果当前对象和参数对象不分先后 , 返回 0;

如果你对姓名的比较存在疑惑,比如:为什么name也可以调用compareTo方法这种类似的问题,因为name是String类型的,我建议你先看一下String的源码,里面也实现了Comparable接口,以及重写了compareTo方法,这里就不详细介绍了,感兴趣的小伙伴们可以尝试一下哦,当然,我相信你们都是大佬,一看就懂哈哈

执行程序,看运行结果,这下就能达到我们想要的效果了

前面说了,这样的代码也不是最好的,存在局限性,我按分数来排序,那代码就写死了 ,那我以后想按姓名来排序,我又得回头改???,以后进公司了,你的代码做改变,影响其他人的代码,那不得把你骂死,所以我们需要做进一步改进,引入Comparator接口

2.Comparator比较器

这个接口又叫比较器,那比较器又是个什么东西呢???下面我也是老套路啦,一步一步揭开这东西的面纱

为了解决Comparable接口的局限性,我们这个比较器完美的展现了实现效果,它也是一个泛型接口,同样只有一个抽象方法需要重写,下面看代码:

class Student {
    public String name;
    public int age;
    public double score;
    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
}
//比较器
class AgeComparator implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.age - o2.age;
    }
}
class StringComparator implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.name.compareTo(o2.name);
    }
}
class ScoreComparator implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        return (int)(o1.score-o2.score);
    }
}
public class TestDemo {
    public static void main(String[] args) {
        Student[] students = new Student[3];
        students[0] = new Student("zhangsan",98,78.9);
        students[1] = new Student("lisi",38,48.9);
        students[2] = new Student("wangwu",18,88.9);
        /*AgeComparator ageComparator = new AgeComparator();
        Arrays.sort(students,ageComparator);
        StringComparator stringComparator = new StringComparator();
        Arrays.sort(students,stringComparator);*/
        ScoreComparator scoreComparator = new ScoreComparator();
        Arrays.sort(students,scoreComparator);
        System.out.println(Arrays.toString(students));
    }
}

通过这段代码,我们发现比较器是真真正正的做到了,想按什么排序就按什么排序,在对类的侵入性以及代码耦合度方面也算是不用太过担心了

3.Clonable接口和深拷贝

Java 中内置了一些很有用的接口 , Clonable 就是其中之一 .

Object 类中存在一个 clone 方法 , 调用这个方法可以创建一个对象的 " 拷贝 ". 但是要想合法调用 clone 方法 , 必须要先实现 Clonable 接口 , 否则就会抛 CloneNotSupportedException 异常.

先来看一段代码吧,我个人喜欢结合代码看分析,这样子就降低了云里雾里的可能性了,

class Student implements Cloneable{
    public int id = 1234;
    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                '}';
    }
    //重写Object父类的clone()方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
public class TestDemo {
    public static void main(String[] args) {
        Student student1 = new Student();
        try {
            Student student2 = (Student) student1.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

实现克隆的两个条件(结合上面代码):

1.这个对象可以被克隆,也就是这个Student类要实现这个Clonable接口

2.要在这个类中重写父类Object的clone()方法

我们现在的代码只是达到了这样一个效果,,重头戏还在后边,因为我们的拷贝有时候远远不止于此,这种只能算是一个浅拷贝,那什么才算是深拷贝呢??? 请看下面的代码:

class Money implements Cloneable{
    public double money = 19.9;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
class Student implements Cloneable{
    public int id = 1234;
    public Money m = new Money();
    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                '}';
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
public class TestDemo {
    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);
        System.out.println("=========================");
        student1.m.money = 99.99;
        System.out.println(student1.m.money);
        System.out.println(student2.m.money);
    }
}

我现在在之前的代码的基础上添加了一个Money类,并且实例化Money的对象作为Student的成员,这时候克隆之后,改变我money的值,会是一个什么的效果呢???

先看运行结果吧,我就不兜圈子了

我们发现,这并不是我们想要的结果,我们想要的结果是,通过student2去改变money的值,它并不会影响student1中的money,而我们刚刚的代码并没有做到,它的效果图如下:

那又如何做到深拷贝呢??这时我们就需要对我们刚才的代码做一些改进了

我们只需将原来的Student类中的Object的克隆方法改成下面这份代码就能做到我们想要的效果:

@Override
    protected Object clone() throws CloneNotSupportedException {
        Student tmp = (Student) super.clone();//将id变量克隆一份,tmp指向它
        tmp.m = (Money) this.m.clone();//将m对象中的money变量克隆一份  tmp中的m指向它
        return tmp;
        //return super.clone();
    }

看到这个代码先不要慌,如果注释没看明白,咱还有板书可以参照

其实分析起来也就那么回事

当我们tmp返回的时候,就把0x99给到了student2,克隆完成之后,tmp是局部变量,也就被回收了,,,就变成了下面这副摸样:

这个时候,我们再去通过student2去改变我们的money,就不会影响student1中的money的值了,废话不多说,运行结果为证

以上就是三个重要接口的全部分析了,下次再见!!!

到此这篇关于深入分析Comparable与Comparator及Clonable三个Java接口的文章就介绍到这了,更多相关Java Comparable Comparator Clonable内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java 比较接口comparable与comparator区别解析

    这篇文章主要介绍了Java 比较接口comparable与comparator区别解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 package test0; import java.util.Comparator; //限定修饰符为friend不能为public,一个java文件中只能有一个public类 /*** * java程序是从一个public类的main函数开始执行的, *(其实是main线程),就像c程序是从main()函数开

  • 浅析Java中comparator接口与Comparable接口的区别

    Comparable 简介 Comparable 是排序接口. 若一个类实现了Comparable接口,就意味着"该类支持排序".  即然实现Comparable接口的类支持排序,假设现在存在"实现Comparable接口的类的对象的List列表(或数组)",则该List列表(或数组)可以通过 Collections.sort(或 Arrays.sort)进行排序. 此外,"实现Comparable接口的类的对象"可以用作"有序映射(如

  • Java的Comparable,Comparator和Cloneable三大接口详解

    目录 1.比较器 1.1Comparable接口 1.2Comparator接口 2.Cloneable接口 2.1深拷贝和浅拷贝 总结 1.比较器 ①比较器的引入 a.首先,当我们单一地比较某一种数据类型的数组时,可以直接用Arrays.sort()进行实现 b.而当我们同时含有多个参数时,并没有告诉我们按照什么来进行排序,此时,若是用Arrays.sort()就会出现报错的情况  基于这种情况,我们了解到,若是要将自定义类型进行大小比较 ,就要引入能够实现比较的接口,下面我们介绍Compar

  • 详解Java中Comparable和Comparator接口的区别

    详解Java中Comparable和Comparator接口的区别 本文要来详细分析一下Java中Comparable和Comparator接口的区别,两者都有比较的功能,那么究竟有什么区别呢,感兴趣的Java开发者继续看下去吧. Comparable 简介 Comparable 是排序接口. 若一个类实现了Comparable接口,就意味着"该类支持排序".  即然实现Comparable接口的类支持排序,假设现在存在"实现Comparable接口的类的对象的List列表(

  • 对比Java中的Comparable排序接口和Comparator比较器接口

    Comparable Comparable 是排序接口. 若一个类实现了Comparable接口,就意味着"该类支持排序". 即然实现Comparable接口的类支持排序,假设现在存在"实现Comparable接口的类的对象的List列表(或数组)",则该List列表(或数组)可以通过 Collections.sort(或 Arrays.sort)进行排序. 此外,"实现Comparable接口的类的对象"可以用作"有序映射(如Tree

  • JavaSE的三大接口:Comparator,Comparable和Cloneable详解

    进阶JavaSE-三大接口:Comparator.Comparable和Cloneable. Comparable和Comparator这两个接口很相似,都是用于比较大小的接口.在我们写一些数据结构的算法题时,用的比较多,具体是怎么用的,我们接着往下看. Comparator接口: public interface Comparator<T> { public int compare(T o1, T o2); //比较方法 } Comparable接口: public interface Co

  • Java中的Comparable和Comparator接口

    目录 一. Comparable接口 1. Comparable简介 2. 为什么要实现Comparable接口 3. Comparable的实际应用 二. Comparator接口 1. Comparator简介 2. Comparator接口的实际运用 三. Comparable和Comparator的比较 一. Comparable接口 1. Comparable简介 Comparable是排序接口. 若一个类实现了Comparable接口,就意味着该类支持排序. 实现了Comparabl

  • Java Comparable及Comparator接口区别详解

    在实际应用中,我们往往有需要比较两个自定义对象大小的地方.而这些自定义对象的比较,就不像简单的整型数据那么简单,它们往往包含有许多的属性,我们一般都是根据这些属性对自定义对象进行比较的.所以Java中要比较对象的大小或者要对对象的集合进行排序,需要通过比较这些对象的某些属性的大小来确定它们之间的大小关系. 一般,Java中通过接口实现两个对象的比较,比较常用就是Comparable接口和Comparator接口.首先类要实现接口,并且使用泛型规定要进行比较的对象所属的类,然后类实现了接口后,还需

  • 深入分析Comparable与Comparator及Clonable三个Java接口

    目录 1.Comparable 2.Comparator比较器 3.Clonable接口和深拷贝 1.Comparable 这个接口是用来给对象数组来排序的 在我学接口之前我用的排序方法是Arrays.sort(),我发现单单靠之前所学知识并不能解决给对象数组排序的问题,后来学习过程中发现Comparable这一接口解决了我的疑惑,也感受到了这一接口的强大之处,但这也不是最好的,后续会说到,毕竟学知识是个循序渐进的过程嘛 首先,我们看一下我们之前学习时用的Arrays.sort public c

  • Java Comparable 和 Comparator 的详解及区别

    Java Comparable 和 Comparator 的详解及区别 Java 中为我们提供了两种比较机制:Comparable 和 Comparator,他们之间有什么区别呢?今天来了解一下. Comparable 自然排序 Comparable 在 java.lang 包下,是一个接口,内部只有一个方法 compareTo(): public interface Comparable<T> { public int compareTo(T o); } Comparable 可以让实现它的

  • java 集合工具类Collections及Comparable和Comparator排序详解

    目录 一.常用功能 二.Comparator比较器 三.Comparable和Comparator两个接口的区别 四.练习 五.扩展 一.常用功能 java.utils.Collections是集合工具类,用来对集合进行操作. 部分方法如下: public static <T> boolean addAll(Collection<T> c, T... elements):往集合中添加一些元素. public static void shuffle(List<?> lis

  • java 中Comparable与Comparator详解与比较

    java 中Comparable与Comparator详解 今天查看TreeMap的源码,发现其键必须是实现Comparable或者Comparator的接口时产生了一些兴趣,比如在TreeMap中的put方法分别对Comparable和Comparator接口分别进行处理.那么疑问就来了,Comparable和Comparator接口的区别是什么,Java中为什么会存在两个类似的接口?   Comparable和Comparator接口都是用来比较大小的,首先来看一下Comparable的定义

  • Java 中Comparable和Comparator区别比较

    Comparable 简介Comparable 是排序接口.若一个类实现了Comparable接口,就意味着"该类支持排序".  即然实现Comparable接口的类支持排序,假设现在存在"实现Comparable接口的类的对象的List列表(或数组)",则该List列表(或数组)可以通过 Collections.sort(或 Arrays.sort)进行排序.此外,"实现Comparable接口的类的对象"可以用作"有序映射(如Tre

  • Java基础之Comparable与Comparator概述

    自然排序Comparable 对于自定义类进行排序要实现Comparable接口,重写compareTo() 方法,如果不重写,像使用Arrays.sort()排序就会报错 package com.che.lambda; import java.util.Objects; /** * @author cheyuhang on 2021/4/23 */ public class Good implements Comparable{ private String name; private Dou

  • java对象对比之comparable和comparator的区别

    一.元素的比较 1.1 基本类型的比较 java中的基本类型的对象是可以进行比较的 如 public static void main(String[] args){ int a = 10; int b = 20; System.out.println(a>b); System.out.println(a==b); System.out.println(a<b); char c1 = 'A'; char c2 = 'B'; System.out.println(c1>c2); Syste

随机推荐