【JavaSE】接口剩余内容

目录

1、接口使用实例

📕逐步分析学生数组排序的写法

✨思路:

✨代码实现

✨弊端

📕、改进 

改进思路:

代码实现:

2、Cloneable接口和深拷贝

2.1、cloneable接口的作用

 2.2、深拷贝和浅拷贝

2.2.1、浅拷贝

2.2.2、深拷贝

 3、Object类

3.1、toString方法的调用 

3.2、equals方法(比较是否一样)


1、接口使用实例

给对象(student)数组排序

import java.util.Arrays;
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;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
}
//测试类
public class Test {
    public static void main(String[] args) {
        Student[] student =new Student[3];
        student[0] = new Student("zhangsan",19,10);
        student[1] = new Student("lisi",29,20);
        student[2] = new Student("wangwu",39,5);

        Arrays.sort(student);//这样排序编译器会报错,因为编译器不知道按什么方式给数组成员排序
        System.out.println(Arrays.toString(student));
    }

运行结果为:

 结果中蓝色字体表示:自己编写的程序有错误

灰色字体表示:调用的方法在那一行出错。

📕逐步分析学生数组排序的写法

✨思路:

  1. 要排序先从数组的元素比较开始
  2. 元素比较完成之后,在进行排序 

1️⃣、元素比较

这里要比较学生对象,还是要看一下Java中给出ComparableTimSort.java第320行的源码。

 在我们自己写的代码中Student和Comparable这个类型没有关系,联系不到一起。所以在来看这个源码

 所以在Student类上实现接口Comparable就可以(在Student类上实现接口Comparable接口,实际上就是规定了一个比较规则)

class Student implements Comparable<Student>{//这里的<>表示泛型,<>中的Student表示比较Student这个类

在Comparable接口的源码中,有一个compareTo的方法 ,所以在Student类中重写这个方法。但是在ComparableTimSort.java方法的源码中调用了compareTo的方法,刚好对上

 在数组中存在的是student[0]、student[1]和student[2]的引用,是学生的地址,但是在比较的时候不可能是对地址的比较。所以因该还要提供比较的方法。所以重写的compareTo方法就是比较的方法。

在测试类中调用compareTo方法对student[0]和student[1]进行比较

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

🎃 那么现在我们按照学生的年龄来比较:因为重写的方法的返回值是int类型,所以比较的返回值设置为1,-1,0

 @Override
    public int compareTo(Student o) {
       if(this.age > o.age) {//this表示的是:谁调用这个compareTo这个方法,谁就是this
           //this表示的就是student[0],o表示的就是student[1]
           return 1;
       }else if(this.age < o.age){
           return -1;
       }else {
           return 0;
       }
    }
}

上述写法有些繁琐,可以这样写,更简洁

 public int compareTo(Student o) {
    return this.age - o.age; 
 }

🎃、按照名字来比较

这里很多人就想到了equals这个方法,但是放在这里用作比较肯定是不满足需求的,并且他只能比较相不相同,不能用来比较大小。来看一下他的源码

 再来了解一下String这个类型

 那么此时用名字比较的方法为:

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

这里将比较的代码解释一下

在Student类中

 在测试类中

2️⃣、数组元素排序

测试类中:

1、直接调用Java中的sort方法

  Arrays.sort(student);
  System.out.println(Arrays.toString(student));

2、通过自己写一个冒泡排序

public class Test {

    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] > array[j+1]) {
                    交换;
                }*/
                if(array[j].compareTo(array[j+1]) > 0) {
                    Comparable tmp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = tmp;
                }
            }
        }

✨代码实现

 1️⃣、元素之间的比较 

 🎉按照年龄比较,将完整的代码写出来

import java.util.Arrays;
class Student implements Comparable<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;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }

    @Override
    public int compareTo(Student o) {
       if(this.age > o.age) {//this表示的是:谁调用这个compareTo这个方法,谁就是this
           //this表示的就是student[0],o表示的就是student[1]
           return 1;
       }else if(this.age < o.age){
           return -1;
       }else {
           return 0;
       }
    }
}
public class Test {
    public static void main(String[] args) {
        Student[] student =new Student[3];
        student[0] = new Student("zhangsan",19,10);
        student[1] = new Student("lisi",29,20);
        student[2] = new Student("wangwu",39,5);
        System.out.println(student[0].compareTo(student[1]));
    }
}

🎉 按照名字比较,将完整代码写出来

import java.util.Arrays;
class Student implements Comparable<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;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
@Override
    public int compareTo(Student o) {
        if (this.name.compareTo(o.name) > 0) {
            return 1;
        } else if (this.name.compareTo(o.name) < 0) {
            return -1;
        } else {
            return 0;
        }
    }
}

public class Test {
    public static void main(String[] args) {
        Student[] student =new Student[3];
        student[0] = new Student("zhangsan",19,10);
        student[1] = new Student("lisi",29,20);
        student[2] = new Student("wangwu",39,5);
        System.out.println(student[0].compareTo(student[1]));
}

 2️⃣、数组元素排序(按名字排序)

🧨调用Java中的Array.sort();方法

import java.util.Arrays;
class Student implements Comparable<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;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
 @Override
    public int compareTo(Student o) {
        if (this.name.compareTo(o.name) > 0) {
            return 1;
        } else if (this.name.compareTo(o.name) < 0) {
            return -1;
        } else {
            return 0;
        }
    }
}

public class Test {
    public static void main(String[] args) {
        Student[] student =new Student[3];
        student[0] = new Student("zhangsan",19,10);
        student[1] = new Student("lisi",29,20);
        student[2] = new Student("wangwu",39,5);
        Arrays.sort(student);
        System.out.println(Arrays.toString(student));
    }

 

🧨调用自己写的冒泡方法(sort)

import java.util.Arrays;
class Student implements Comparable<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;
    }

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

    @Override
    public int compareTo(Student o) {
  if(this.name.compareTo(o.name) > 0) {
            return 1;
        }else if(this.name.compareTo(o.name) < 0) {
            return -1;
        }else {
            return 0;
        }
    }
}
public class Test3 {
    public static void sort(Comparable[] array) {//只要Student类实现了接口Comparable就可以
        for (int i = 0; i < array.length-1; i++) {
            for (int j = 0; j < array.length-1-i; j++) {
                /*if(array[j] > array[j+1]) {
                    交换;
                }*/
                if(array[j].compareTo(array[j+1]) > 0) {
                    Comparable tmp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = tmp;
                }
            }
        }
    }
    public static void main(String[] args) {
        Student[] students = new Student[3];
        students[0] = new Student("zhangsan",19,10);
        students[1] = new Student("lisi",59,20);
        students[2] = new Student("abc",39,5);
        sort(students);
        System.out.println(Arrays.toString(students));
    }
}

✨弊端

 上述代码中的比较方法存在不灵活的弊端。

在compareTo方法中实现的比较方法在Student类中写死之后,在测试类中没有办法改变。

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

当我们在实际的项目中,三种比较方法要灵活切换,该怎样实现?

我们不可能直接在类中修改比较的方法,直接在类中修改,那使用过这个类的测试结果,就会全部出错。

所以下面来看一下改进的方案。


📕、改进 

改进思路:

🎊1、这里来了解一下Comparator接口。

 compare方法根据其返回值确定比较对象的大小,如果返回值为正,认为o1>o2;返回值为负,认为o1<o2;返回值为0,认为两者相等。

🎊2、再来了解一下Arrays类中的sort(T[],Comparator<? super T>):void方法。

 public static <T> void sort(T[] a, Comparator<? super T> c) {

上述方法可以根据比较器的compare方法对数组进行排序,compare方法的不同实现对应着不同的排序准则。

🎊3、设置比较器

创建一个AgeComparator类,重写compare方法(以年龄比较)


class AgeComparator implements Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2) {
        return o1.age - o2.age;
    }
}

创建一个ScoreComparator类,重写compare方法(以成绩比较)


class ScoreComparator implements Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2) {
        return o1.score- o2.score;
    }
}

创建一个NameComparator类,重写compare方法(以姓名比较)

class NameComparator implements Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2) {
        return o1.name.compareTo(o2.name);//这里compareTo方法是String类中实现Comparable接口后重写的方法
    }
}

代码实现:

import java.util.Arrays;
import java.util.Comparator;

class Student implements Comparable<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;
    }

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

     @Override
     public int compareTo(Student o) {
         if(this.age > o.age) {
             return 1;
         }else if(this.age < o.age){
             return -1;
         }else {
             return 0;
         }
     }
class AgeComparator implements Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2) {
        return o1.age - o2.age;
    }
}
class ScoreComparator implements Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2) {
        return o1.score- o2.score;
    }
}
class NameComparator implements Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2) {
        return o1.name.compareTo(o2.name);
    }
}
public class Test{
    public static void main(String[] args) {
        Student[] students = new Student[3];
        students[0] = new Student("zhangsan",10,19);
        students[1] = new Student("lisi",8,78);
        students[2] = new Student("wangwu",15,57);
        //比较器
        //这样就是实现了灵活使用不同规则排序
        AgeComparator ageComparator = new AgeComparator();
        ScoreComparator scoreComparator  = new ScoreComparator();
        NameComparator nameComparator = new NameComparator();

        //Arrays.sort(students);//当然不传,是以Student类实现的接口Comparable中重写的compareTo方法的比较规则排序
        Arrays.sort(students,ageComparator);
       // Arrays.sort(students,scoreComparator);这里传的是那个比较器,就用那种比较规则排序
        //Arrays.sort(students,nameComparator);
        System.out.println(Arrays.toString(students));

    }
}

运行结果:


2、Cloneable接口和深拷贝

2.1、cloneable接口的作用

  • Java中内置了一些很有用的接口,Cloneable就是其中之一。
  • Object 类中存在一个clone()方法,调用这个方法可以创建一个对象的"拷贝"。但是要想合法调用clone()方法,必须要先实现Cloneable接口,但是Cloneable是一个空接口,里面没有任何内容,但是如果没有实现Cloneable接口,就会抛出CloneNotSupportedException异常。
  • 通常实现了Cloneable接口的子类,应当以public访问权限重写clone()方法(尽管Java.Object类中的clone方法是protected类型的)
  • 因为每个类的基类都是Object,每个类都会默认继承Object类,所以每个类都有clone方法,但是它是protected,所以不能在不同包当中的类中访问,要在不同包当中的子类(也就是说继承了Object类的类)中访问,克隆一个对象,需要重写clone

所以可以把Cloneable 接口看成是实现clone()方法必须要的一个因素。

 2.2、深拷贝和浅拷贝

2.2.1、浅拷贝

我们通过这个代码来了解一下浅拷贝:

package demo;
class Money{
    public double money = 12.25;
}
//要拷贝student,当然Student要实现Cloneable接口
class Student implements Cloneable{
    public String name;
    public Money m = new Money();//实例化对象m,将对象的地址存入引用m中
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                '}';
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Student student = new Student();
        Student student2 =(Student)student.clone();//student2只拷贝了student对象,并没有拷贝m对象
        System.out.println(student.m.money);//对象student调用属性m,m是类Money的引用
        System.out.println(student2.m.money);
        System.out.println("=============");
        student2.m.money = 99;//修改拷贝后的m所引用的对象
        System.out.println(student.m.money);
        System.out.println(student2.m.money);
    }
}

 如上代码,我们可以看到,通过clone,我们只拷贝了student对象。但是studentd对象中的m对象,并没有拷贝,通过student2这个引用修改了money的值后,student这个引用访问money的时候,值也发生了改变。这里就是发生了浅拷贝。

画图理解:

 


2.2.2、深拷贝

同样通过代码来了解深拷贝:要实现深拷贝,那么当前对象中的所有对象都得拷贝

package demo;
//Money对象要实现拷贝,就得在Money类中实现接口Cloneable,并重写clone方法
class Money implements Cloneable{
    public double money = 12.25;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
//Student对象要实现拷贝,就得在Student类中实现接口Cloneable,并重写clone方法
class Student implements Cloneable{
    public String name;
    public Money m = new Money();
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                '}';
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {//克隆方法的返回值为Object类型
        //两次克隆都发生了向下转型,都是将Object类转换为各自引用相应的类
        //只是克隆了Student对象,发生向下转型,所以将Object类型强转为Student类型
        //这里在Student类中创建一个student3引用,用来接收克隆后的对象的地址
        Student student3 = (Student) super.clone();
        
        //克隆了Student对象 里面的Money对象,克隆的是Money对象,所以将Object类型强转为Money类型
        //将克隆后的对象地址传给student3引用所指向的对象中的m引用
        student3.m = (Money) this.m.clone();
        //这里调用的clone方法是Money类当中的clone方法。
        //通过this.m调用clone方法,现在需要克隆的是student对象中m引用所指向的对象,所以this代表的就是student引用

        return student3;
        //这里是将克隆后用来接收克隆结果的student3作为返回结果返回

    }
}
public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Student student = new Student();
         //student调用clone方法,将student对象进行拷贝,student2引用将student克隆结果进行接收
        Student student2 =(Student)student.clone();//student2接收了返回的克隆结果
        System.out.println(student.m.money);
        System.out.println(student2.m.money);
        System.out.println("=============");
        student2.m.money = 99;
        System.out.println(student.m.money);
        System.out.println(student2.m.money);
    }
}

画图解释:

🧨🧨🧨 总结:深浅拷贝和你实现的方法(例如:clone方法)没关系,和你实现的方式(代码的实现)有关系


 3、Object类

Object是Java默认提供的一个类。Java里面除了Object类,所有的类都是存在继承关系的 。所有的类默认会继承Object这个父类即所有类的对象都可以使用Object的引用进行接收

范例:使用Object接收所类的对象

class Person{};
class Student{};
public class Test {
        public static void function(Object obj) {
          
        }
        public static void main(String[] args) {
            function(new Person());
            function(new Student());
        }
}

 上述代码中的 没有引用接收的对象叫做匿名对象。

function(new Person());
function(new Student());

它的缺点就是每次使用都得new。他的使用场景是只需要使用一次的时候。

Object类中存在有定义好的一些方法。如下:

我们来了解一些上述的方法 

3.1、toString方法的调用 

❓❓❓先来想一个问题,为什么Object类型中实现了toString方法,当我们在类中重写了toString方法后,运行代码时,回调用我们自己的toString方法?

带着问题,我们通过代码来了解toString方法。

class Student{
    public String name;
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                '}';
    }
}
public class Test {
    public static void main(String[] args) {
        Student student = new Student();
        System.out.println(student);
    }
}

画图解释: 

  • 上述代码中将student引用传给println方法,println方法的实现中用Object类型的 x引用接收;
  • 在prinln方法中有调用了String类中的valueOf方法,将参数x传给了valueOf方法;
  • valueOf方法用Object类型的obj引用接收,就相当于valueOf方法中obj这个引用,引用的就是student对象,就相当于父类引用,引用子类对象,所以发生了向上转型和动态绑定。站在valueOf这个方法的角度看,当引用的子类对象不一样,发生的调用toString方法的行为是不一样的(若在一个类中没有重写toString方法,他就会调用Object类当中的toString方法 )。这也就体现了多态的思想
  • 在valueOf方法的实现中,判断使用哪种toString方法,调用Object类当中的toString方法,还是调用自己在Student类当中重写的toString方法。 

3.2、equals方法(比较是否一样)

第一种:用 == 判断相等

class Student{
    public String name;
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                '}';
    }
}
public class Test2 {
    public static void main(String[] args) {
        Student student1 = new Student1();
        student1.name = "zhangsan";

        Student student2 = new Student1();
        student2.name = "zhangsan";

        System.out.println(student1 == student2);
    }
}

假设在一个班中,只有一个zhangsan,现在代码中有两个同学都叫zhansan。那么在我的逻辑上,我认为这两个同学是同一个同学。但是不能通过像上述代码中的 用==去判断,那样结果会是

 这是将两个引用进行比较,引用中存的是地址,比较结果肯定是false。

第二种:调用equals方法,不重写。 

那么我么使用equals方法来比较

先看代码

class Student1{
    public String name;
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                '}';
    }
}
public class Test2 {
    public static void main(String[] args) {
        Student1 student = new Student1();
        student.name = "zhangsan";

        Student1 student2 = new Student1();
        student2.name = "zhangsan";
        boolean flg = student.equals(student2);
        System.out.println("flg:"+flg);
    }
}

 结果还是false,为什么呢?

这里实现的效果和用==判断是一样的。因为我们没有在Student1类中重写equals方法,现在使用的是Object类中的equals方法 

 ✨第三种:调用equals方法,并且在类中重写equals方法。

class Student1{
    public String name;
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                '}';
    }
    public boolean equals(Object obj) {
        if(obj == null) {
            return false;
        }
        //判断是否指向的是同一个对象
        if(this == obj){
            return true;
        }
        //判断两个对象是否为同一个类型(判断是不是Student1类)
        if(!(obj instanceof Student1)){
            return false;
        }
        Student1 student =(Student1) obj;
        if(this.name.equals(student.name)){
            return true;
        }
            return false;
    }
}
public class Test2 {
    public static void main(String[] args) {
        Student1 student = new Student1();
        student.name = "zhangsan";

        Student1 student2 = new Student1();
        student2.name = "zhangsan";
        boolean flg = student.equals(student2);
        System.out.println("flg:"+flg);
    }
}

 

equals方法的调用,和toString 方法的调用是同理的,所以我们在写代码时,在使用给定的方法时,一定要符合自己的代码场景,当然不符合使用场景的时候我们可以在继承了该方法所在类之后,对该方法进行重写。满足自己的使用场景

第四种:通过编译器自己生成重写equals方法。

第一步:

第二步: 

 

第三步: 

 

 往后直接戴拿Next就行,知道生成代码就行

   @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student1 student1 = (Student1) o;
        return Objects.equals(name, student1.name);
    }

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

以后写代码的时候更多的是通过编译器自己生成需要重写的方法


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值