ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔

文章目录

  • 三个常用接口
  • Comparable 接口
    • 在了解之前,我们回顾一下数组是怎么排序
      • 代码如下
        • 效果图
    • 再来写一个复杂的数组排序
      • 效果图
      • 原因附图
        • 进入sort
    • 既然知道了,为什么不能排序的问题所在,那么我们现在应该思考的是如何让告诉sort,以什么去排序数组,来接着看。
      • 先让 student 类 实现了 Comparable 接口
    • 由上得知 Arrays.sort();默认排序规律是升序(从小到大),那么逆序怎么实现呢?(交换 this.age 与 o.age的位置就可以了)
      • 总结:
  • 因为Comparable 的缺陷,很dan疼,所以 Comparator 接口(别称 比较器) 出世了。
    • 来着跟着我的思路来一步步来看
  • Cloneable 接口
    • 在了解cloneable接口之前,先来了解创建对象的方式
      • 1. new 关键字
        • 附图
      • 2.克隆
    • 我们通过 clone方法,了解到了Cloneable接口,是与 clone搭配使用的。
      • 最终代码如下:
        • 附图
        • 效果图
    • 拓展一
    • 拓展二
      • 程序如下
        • 附图
        • 最终 程序
  • 本文至此结束

三个常用接口

1. Comparable

2. Comparator

3. cloneable


Comparable 接口

在了解之前,我们回顾一下数组是怎么排序

代码如下

import java.util.Arrays;

public class Test {
    public static void main(String[] args) {
        int[] array = {1,21,3,14,5,16};
        System.out.println(Arrays.toString(array));
        Arrays.sort(array);
        System.out.println(Arrays.toString(array));
    }
}

效果图

在这里插入图片描述


再来写一个复杂的数组排序

import java.util.Arrays;

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

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

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

public class Test {
    public static void main(String[] args) {
        Student[] students = new Student[3];
        students[0] = new Student(18,"小明",80.5);
        students[1] = new Student(16,"小红",96.8);
        students[2] = new Student(19,"小刚",81.5);
        System.out.println(Arrays.toString(students));
        Arrays.sort(students);
        System.out.println(Arrays.toString(students));
    }
}

效果图

ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第1张图片


原因附图

进入sort

ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第2张图片
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第3张图片
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第4张图片
说白了,就是让每个元素都去调用 Comparable。Comparable就是 数组按照某个规律去排序。


既然知道了,为什么不能排序的问题所在,那么我们现在应该思考的是如何让告诉sort,以什么去排序数组,来接着看。

先让 student 类 实现了 Comparable 接口

在这里插入图片描述
点进 Comparable
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第5张图片
再我们程序上 Comparable 接口后面加上 尖括号,里面写 Student,意为 比较学生
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第6张图片


再来观察一下,接口 Comparable 里还有什么东西、
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第7张图片
在这里插入图片描述


compareTo 方法是用来比较数组,那就需要看比较 结果是大于0,还是等于0,再者小于0.
在这里插入图片描述
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第8张图片
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第9张图片
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第10张图片
student调用 compareTo方法,所以 compareTo中的this,指的是 student。
即:this.age == student.age , 而 o.age == student2.age.
compareTo 按照 两者的年龄大小去比较。
最后通过 swap方法交换位置
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第11张图片


再一次执行那个复杂一点的数组排序
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第12张图片


其实 compareTo 方法还可以简化
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第13张图片


由上得知 Arrays.sort();默认排序规律是升序(从小到大),那么逆序怎么实现呢?(交换 this.age 与 o.age的位置就可以了)

ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第14张图片


总结:

如果 自定义的数据类型,要进行大小的比较’ 一定要实现可以比较的接口Comparable。


但是 Comparable 接口存在着缺陷: 如果我们要以学生的分数或者名字来排序,就需要重新 把 compareTo 方法的实现重写一遍,
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第15张图片


如果某一天有个朋友抽风了,觉得你用年龄排序不合适,把你的 compareTo 给改了,改成了分数排序。
有的人觉得没问题,但是你有没有想过,这会影响到你后面实现的代码实现逻辑。
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第16张图片


那么通过名字去排序,要怎么写。
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第17张图片
由此不难想出,引用之间的比较,肯定是要通过合适的比较方法的, 那么我们就需要去看 String了。
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第18张图片
你会发现 String,也实现了 Comparable 接口
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第19张图片
我们 art + 7
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第20张图片
那么,如果我们想通过名字排序,就需要compareTo 方法去比较。
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第21张图片


Comparable这个接口,有一个非常大的缺点:对类的侵入性非常强,一旦写好,不敢轻易动。
一动,又不会报错,全班人马像是无头苍蝇一样,想想就觉得不能动,不敢动。


因为Comparable 的缺陷,很dan疼,所以 Comparator 接口(别称 比较器) 出世了。

来着跟着我的思路来一步步来看

回到最初的情况, 数组无法排序
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第22张图片
先进入sort(Ctrl+左键)
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第23张图片
你会发现sort 有很多的排序规则
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第24张图片


现在 我们来创建一个类,该类实现了Comparator接口
Ctrl+左键 进入 Comparator 接口
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第25张图片
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第26张图片


创建一个类来实现 Comparator 接口,并重写当中唯一的抽象方法
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第27张图片
输出原理,跟前面讲的,是一样的, 大于就返回一个非零数字,等于返回0,小于返回负数
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第28张图片
由此可以判断出 我们 Comparator接口(比较器)就完成了。
完成了,就要使用。
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第29张图片
这时,我们再点进sort。
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第30张图片
此时的 Aarrays,sort 就会根据 Comparator 接口(比较器)的规则,进行排序。
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第31张图片
如果,你们说:我们想用分数来排序。(创建分数比较器就可以了)
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第32张图片
名字排序也差不多。
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第33张图片
有的朋友会说,这根Comparable 有什么区别,实现方式都是一样的。
有区别!!!
Comparable 通过一个类去实现的,是定死了的。不能乱改。
而Comparator,是创建一个类 实现它接口的功能。意味着Comparator 不局限与一个类。

它可以根据 不同比较规则 来创建不同的类来实现。
想用哪种规则去排序,就去调用相对应的类。
即使 有人改了其中规则,也只影响调用此规则的数据,
调用其他规则的数据,则不受影响。

优点:
1.排查效率高
2.代码侵入性低(牵连范围小多了)
3.灵活


Cloneable 接口

在了解cloneable接口之前,先来了解创建对象的方式

1. new 关键字

class Person{
    public int age;
    public void eat(){
        System.out.println("睡");
    }

    @Override
    public String toString() {
        return super.toString();
    }
}


public class Test {
    public static void main(String[] args) {
        // 通常创建对象,是通过new
       Person person = new Person();

    }
}

附图

ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第34张图片

2.克隆

比如说:上面的程序中,我想 克隆 一个 person 对象。
这里就要使用 调用 clone() 方法
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第35张图片
那我们就抛出一个异常。快捷键 ALT + ENTER,我们发现
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第36张图片
为了搞清楚原因: Ctrl + 左键点击clone方法,进入clone方法。
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第37张图片
由图得知了 clone方法,是 一个Object的克隆方法,意味着该方法的返回值 是一个Object的类型,那么我们将其类型强转成Person类型
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第38张图片
继续 点击 clone ,ALT + ENTER
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第39张图片
点击 警告
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第40张图片
一看无数眼睛中透露着,这是要干什么?感觉就是不让我们去使用 clone方法。
其实最主要的原因是:一个对象要克隆,产生一个副本,那么这个对象,一定是可克隆的。所以我们必须给 Person 类 实现一个 Cloneable的接口。
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第41张图片
为了,搞明白 Person类,在没有重写 Cloneable 接口的抽象方法情况下,为什么不报错。我们点击进入Cloneable接口中
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第42张图片
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第43张图片
因为 Cloneable 是空的,由此引出一个面试问题: 你知道 Cloneable 接口吗?
你说: 知道。
面试官: 你知道为什么这个接口是一个空接口?
你: 没有为什么,这是一个默认的规则
面试官:有什么作用?
你:如果一个接口是空接口,这时有一个类实现了这个空接口,那么这个空接口,又被称为标志接口,代表当前这个类可以被克隆的。
但是有问题! 我们的clone 还是使用不了
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第44张图片
这是 因为 Cloneable 的特殊:实现接口后,虽然不用重写抽象方法,但是我们需要重写 clone 方法。
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第45张图片
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第46张图片
选择该选项
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第47张图片
那么在这里,可能有些朋友可能存在疑问:Person 类,默认继承时Object类(父类就是Object的类),那么为什么通过 person 这样的引用,不能调用clone方法?
 注意clone方法比较特殊的,在没有重写clone方法之前,其实是可以被调用的,前面我们 进入clone 方法时,它是可以跳转到 clone方法的。说明程序知道你要调用克隆方法,
但是克隆方法的特殊性就是:如果你想使用clone方法,就必须要在一个类中实现Cloneable接口,并重写clone方法,最后还需要处理异常,才能使用,这是规定,


&ensp:

我们通过 clone方法,了解到了Cloneable接口,是与 clone搭配使用的。

clone 方法 作用: 就和前面 讲数组拷贝时,是一样的,拷贝一份副本。不了解数组克隆的朋友可以去看这篇文章The Definition And Use OF Arrays - 数组的定义与使用.

最终代码如下:

class Person implements Cloneable{
    public int age;
    public void eat(){
        System.out.println("睡");
    }

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}


public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        // 通常创建对象,是通过new
       Person person = new Person();
       Person person1 = (Person) person.clone();
       System.out.println(person1);
    }
}

附图

ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第48张图片

效果图

ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第49张图片


拓展一

在上程序中,如果我们 在 创建好对象之后,在进行拷贝,那么 拷贝的内容,是否会随之变化?
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第50张图片
如果 此时,我们再对 副本的age进行赋值,会影响 原本 的值吗?
ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第51张图片


拓展二

深拷贝和浅拷贝
无论是深拷贝,还是浅拷贝,都是是认为实现的一个方式。
深拷贝:拷贝简单数据,面对引用类型数据,要把引用指向的对象拷贝下来。
浅拷贝:拷贝引用,不拷贝引用指向的对象。
决定是深拷贝,还是浅拷贝 不是方法的用途,是代码的实现。也就是人为的实现。
上面的程序,是一个简单的数据类型,不涉及引用,所以是深拷贝
现在我们来讲这个程序升华一波。


程序如下

// 每个人都有钱,我们就定义一个money
class Money{
    public double m = 0.5;
}

class Person implements Cloneable{
    public int age;
    public Money money = new Money();
    public void eat(){
        System.out.println("睡");
    }

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person = new Person();
        Person person1 = (Person) person.clone();
    }
}

附图

ObjectOrientedProgramming - 面向对象的编程(三个常用接口)- Java - 细节狂魔_第52张图片


最终 程序

// 每个人都有钱,我们就定义一个money
class Money implements Cloneable{
    public double m = 0.5;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

class Person implements Cloneable{
    public int age;
    public Money money = new Money();
    public void eat(){
        System.out.println("睡");
    }

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
            // super.clone() 拷贝 是 Person 类
        // 我们只需将其强转,创建一个Person 类型 的 变量 来接收它。
        Person tmp = (Person) super.clone();
//        return super.clone(); 其实以前返回 就是 tmp
        tmp.money = (Money) this.money.clone();
        return tmp;
    }
}

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person = new Person();
        Person person1 = (Person) person.clone();
        person1.money.m = 2.5;
        System.out.println(person.money.m);
        System.out.println(person1.money.m);
    }



//    public static void main1(String[] args) throws CloneNotSupportedException {
//        // 通常创建对象,是通过new
//       Person person = new Person();
//       person.age = 9;
//        System.out.println(person);
//        System.out.println("==========");
//       Person person1 = (Person) person.clone();
//       person1.age = 99;
//       System.out.println(person1);
//        System.out.println(person);
//    }
}

本文至此结束

你可能感兴趣的:(java,接口)