前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【如何通过JAVA实现接口的应用和图书的管理】

【如何通过JAVA实现接口的应用和图书的管理】

作者头像
ImAileen
发布2024-12-17 09:47:45
发布2024-12-17 09:47:45
11900
代码可运行
举报
运行总次数:0
代码可运行
在这里插入图片描述
在这里插入图片描述

我们可以通过查看String源码看到它里面有一个compareTo的方法,它能够帮助我们去比较引用类型的大小。

在这里插入图片描述
在这里插入图片描述

根据compareTo的源码我们可以写出比较这两个字符串大小的代码,首先我们要先理解compareTo这个方法的逻辑,我们将我们的str2作为参数传入这个方法内,然后通过str1去调用compareTo这个方法,如果str1 > str2 则返回一个大于0的int 类型的整数打印我们的第一条语句,否则返回一个小于等于0的数,打印else后面的语句。 由于我们给的str1和str2中字母A的Unicode值是65,字母M的是77,很明显A的值小于M的,因此str1.compareTo(str2)返回的结果是一个负数,所以输出else后面的语句。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

根据String的源码我们可以知道java里面string执行了Comparable这个接口,并且在这个接口里面重写了compareTo这个方法,所以我们能够去比较字符串的大小,所以我们如果需要比较年龄的大小也要·跟他的·源码一样去执行这个comparAble接口,重写这个compareTo方法。我们从源码里面可知执行comparA这个接口需要将这个对象的类型写到<>里面,所以修改后的代码如下所示:

在这里插入图片描述
在这里插入图片描述

这个重写方法是系统提示给的。

引用类型的数据比较需要通过重写compareTo方法将比较方式写明白。比如说我们想通过年龄去比较,其代码可以修改如下:

以年龄为比较标准去比较
代码语言:javascript
代码运行次数:0
运行
复制
package demointerface;

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

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

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

    @Override
    public int compareTo(Student o) {
        //以年龄为标准比较大小
        return this.age - o.age;
    }
}

public class Test {
    public static void main(String[] args) {
        Student student1 = new Student("张山",20);
        Student student2 = new Student("李四",15);

        if (student1.compareTo(student2) > 0) {
            System.out.println("student1 > student2");
        }else {
            System.out.println("student1 <= student2");
        }

}

运行结果:

在这里插入图片描述
在这里插入图片描述

根据运行结果我们可以知道我们重写的compareTo方法是正确的,

  • 🐬step1: 我们首先定义了一个Student类,实现了Comparable<Student>接口,以便根据年龄比较学生对象。该类包含两个属性:name(学生姓名)age(学生年龄),并通过构造函数进行初始化。
  • 🐋step2: 然后我们重写了toString方法,以便在输出学生对象时显示其姓名和年龄。在compareTo方法中,我们基于年龄来比较两个Student对象的大小。当调用compareTo方法时,this.age代表当前对象的年龄(即student1的年龄),而o.age代表传入的对象的年龄(即student2的年龄)。通过返回年龄差值来确定学生的大小关系。
  • 🐳step3: 最后在Test类的main方法中,我们创建了两个学生对象,student1student2,并通过比较它们的年龄输出结果。如果student1的年龄大于student2,则输出“student1 > student2”,否则输出“student1 <= student2”
以字符串长度为比较标准去比较
代码语言:javascript
代码运行次数:0
运行
复制
package demointerface;

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

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    @Override
    public int compareTo(Student o) {
//        //以姓名为标准来比较大小
        return this.name.compareTo(o.name);
    }
}

public class Test {
    public static void main(String[] args) {
        Student student1 = new Student("Aileen",20);
        Student student2 = new Student("Mike",15);

        if (student1.compareTo(student2) > 0) {
            System.out.println("student1 > student2");
        }else {
            System.out.println("student1 <= student2");
        }
    }
}

运行结果:

在这里插入图片描述
在这里插入图片描述

这里的比较原理和上面字母的比较原理是一样的,只不过是重写了compareTo方法。


  • 如果没有执行Comparable接口,我们将姓名和年龄属性装到Student类型的数组里面然后通过sort排序会发生什么情况呢?
代码语言:javascript
代码运行次数:0
运行
复制
package demointerface;
import java.util.Arrays;

class Student /*implements Comparable<Student>*/{
    public String name;
    public  int age;

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

public class Test {
    public static void main(String[] args) {
        Student[] students = new Student[3];
        students[0] = new Student("Aileen",20);
        students[1] = new Student("Orange",22);
        students[2] = new Student("Sandy",26);
        Arrays.sort(students);
        System.out.println(Arrays.toString(students));
    }
 }
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

根据上面的执行结果,我们可以知道如果我们直接使用sort去进行排序,会发生报错(类型转换异常),我们点击源码报错信息可以看到,sort关键字调用了Comparable接口中的compareTo方法,但是我们的自定义类Student里面并没有实现这个接口,所以才会发生报错,我们可以得出以下结论:在使用排序的时候,要排序的数据一定是可以比较的,为了实现可比较,我们需要让Student这个类去执行Comparable这个接口,所以我们的代码可以修改成如下所示:

代码语言:javascript
代码运行次数:0
运行
复制
  Arrays.sort(students);
代码语言:javascript
代码运行次数:0
运行
复制
import java.util.Arrays;

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

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

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

    @Override
    public int compareTo(Student o) {
//        //以姓名为标准来比较大小
        return this.name.compareTo(o.name);
    }
}

public class Test {
    public static void main(String[] args) {
        Student[] students = new Student[3];
        students[0] = new Student("Aileen",20);
        students[1] = new Student("Orange",22);
        students[2] = new Student("Sandy",26);
        Arrays.sort(students);
        System.out.println(Arrays.toString(students));

    }

运行结果:

在这里插入图片描述
在这里插入图片描述
代码语言:javascript
代码运行次数:0
运行
复制
package demointerface;
import java.util.Arrays;
class Student implements Comparable<Student>{
    public String name;
    public  int age;

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    @Override
    public int compareTo(Student o) {
//        //以年龄为标准比较大小
        return this.age - o.age;
    }
}

public class Test {
    //用冒泡排序写自己的排序
    //将Student的数组的接口Comparable作为参数传入
    public static void mySort(Comparable[] comparables){
        //比较趟数
        for (int i = 0; i < comparables.length - 1; i++) {
            //比较次数
            //循环控制每趟比较次数 ,  因为每一次比上一次少比较一趟
            for (int j = 0; j < comparables.length - 1 - i ; j++) {
                //因为comparables引用指向的是students数组,数组是引用类型的数据,所以我们不能用不等号去比较他们之间的大小
                //而是需要利用compareTo方法进行大小的比较。
                if (comparables[j].compareTo(comparables[j+1]) > 0) {
                    //交换
                    Comparable temp = comparables[j];
                    comparables[j] = comparables[j + 1];
                    comparables[j + 1] = temp;
                }

            }

        }

    }
    public static void main(String[] args) {
        Student[] students = new Student[3];
        students[0] = new Student("Aileen",20);
        students[1] = new Student("Orange",22);
        students[2] = new Student("Sandy",10);
        System.out.println(Arrays.toString(students));
        System.out.println("--------------------------");
        Arrays.sort(students);
        System.out.println(Arrays.toString(students));
        System.out.println("--------------------------");
        mySort(students);
        System.out.println(Arrays.toString(students));

    }
}

此外,我们还可以根据sort的源代码知道它是调用了comparable的接口的方法,我们可以根据这个去实现自己的排序方法,上面的mysort是我们通过冒泡排序实现的自己的排序方法。

  • 在Test类中,威猛实现了自定义的排序方法mySort,使用冒泡排序算法对传入的Comparable数组进行排序。此方法通过两成嵌套循环,逐次比较相邻的元素,并在必要时进行转换。然后我们在main方法中调用自定义的mySort方法对同一数组进行排序,并打印最终排序结果。
在这里插入图片描述
在这里插入图片描述

上面的代码会根据比较的条件不同其对应的代码块也会受到相应的影响,所以为了解决这问题,我们引入了:Comparator接口。

Comparator接口的应用

在这里插入图片描述
在这里插入图片描述

在构造器Comparator中我们需要重写compare方法才能实现我们的需求,其它方法要么是static要么是default的,我们无法进行重写。以下是通过comparator实现的代码:我们想根据什么去比较就可以通过构造器直接引用哪个。

代码语言:javascript
代码运行次数:0
运行
复制
package demointerface;
import java.util.Arrays;
import java.util.Comparator;
import java.util.NavigableMap;

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

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

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

    @Override
    public int compareTo(Student o) {
//        //以姓名为标准来比较大小
//        return this.name.compareTo(o.name);
//        //以年龄为标准比较大小
        return this.age - o.age;
    }
}

//1.定义年龄比较器进行年龄比较
class AgeComparator implements Comparator<Student>{

    @Override
    public int compare(Student o1, Student o2) {
        return o1.age - o2.age;
    }

}

 //1.定义字符串比较器进行姓名比较
 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 student1 = new Student("Aileen",20);
        Student student2 = new Student("Mike",15);

        AgeComparator ageComparator = new AgeComparator();
        System.out.println(ageComparator.compare(student1, student2));

        NameComparator nameComparator = new NameComparator();
        System.out.println(nameComparator.compare(student1,student2));

        System.out.println("============================== ");

    }

通过sort方法进行排序

代码语言:javascript
代码运行次数:0
运行
复制
Student[] students = new Student[3];
students[0] = new Student("Aileen",20);
students[1] = new Student("Orange",22);
students[2] = new Student("Sandy",10);
Arrays.sort(students);
System.out.println(Arrays.toString(students));
在这里插入图片描述
在这里插入图片描述

我们在数组里面通过sort方法实现数组排序,但是这样的排序需要通过调用sort方法里面的comparable接口将数组强转成comparable类型,这会导致它会根据我们重写的comparTo方法中的比较条件(年龄)来进行排序,输出结果就会根据年龄进行从小到大排序。

我们通过查看sort的源代码(见下图)可以看到sort方法进行排序除了可以传入数组类型数据以外,还可以传入构造器类型数据,这样我们就可以通过sort方法,直接传入我们需要比较对应的构造器就可以进行相应的排序。其修改代码如下所示:

在这里插入图片描述
在这里插入图片描述
代码语言:javascript
代码运行次数:0
运行
复制
package demointerface;
import java.util.Arrays;
import java.util.Comparator;
import java.util.NavigableMap;

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

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

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

    @Override
    public int compareTo(Student o) {
//        //以年龄为标准比较大小
        return this.age - o.age;
    }
}

//1.定义年龄比较器进行年龄比较
class AgeComparator implements Comparator<Student>{

    @Override
    public int compare(Student o1, Student o2) {
        return o1.age - o2.age;
    }

}

 //2.定义字符串比较器进行姓名比较
 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 student1 = new Student("Aileen",20);
        Student student2 = new Student("Mike",15);

        AgeComparator ageComparator = new AgeComparator();
       // System.out.println(ageComparator.compare(student1, student2));

        NameComparator nameComparator = new NameComparator();
       // System.out.println(nameComparator.compare(student1,student2));

        System.out.println("============================== ");

        Student[] students = new Student[3];
        students[0] = new Student("Aileen",20);
        students[1] = new Student("Orange",22);
        students[2] = new Student("Sandy",10);
        Arrays.sort(students,nameComparator);
        System.out.println(Arrays.toString(students));
        System.out.println("**************************************** ");
        Arrays.sort(students,ageComparator);
        System.out.println(Arrays.toString(students));
        System.out.println("**************************************** ");
    }
在这里插入图片描述
在这里插入图片描述
  • 根据上面的运行结果我们可以知道使用Comparator接口可以适应多标准的排序情况,为排序带来更大的灵活性和可扩展性,但是代码量会增多。

总结:

在这里插入图片描述
在这里插入图片描述

Clonable接口

...able:adj.可…的

在这里插入图片描述
在这里插入图片描述

我们想克隆person1这个对象,但是无法直接通过person1这个对象直接调用clone这个方法,那是因为这个Person类是我们自己创建的,但是它默认继承了Object这个类,在Object这个类里面我们能够找到clone这个方法。

在这里插入图片描述
在这里插入图片描述

所以我们可以通过调用父类的clone方法去进行访问,由于Object这个父类和我们的Person是在不同包中的,并且有protect关键字修饰,所以只能在person这个子类中通过super去调用父类的clone方法。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

通过重写父类的clone方法,我们可以知道当我们调用clone方法时它的返回值是一个Object类型的,所以在我们用person1调用这个方法的时候需要将其强转成person类型,并且在main方法中也要加上抛出的异常信息,其修改后的代码如下所示:

代码语言:javascript
代码运行次数:0
运行
复制
package demointerface;

class Person{
    public String name;

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

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

public class Test2 {
    public static void main(String[] args) throws CloneNotSupportedException{
        Person person1 = new Person("Aileen");
        //通过person这个引用来克隆一个对象
        Person person2 = (Person) person1.clone();
    }
}

运行结果:

在这里插入图片描述
在这里插入图片描述

编译器报错信息提示:不支持克隆。但如果我们给Persson加上一个执行的接口Cloneable,他就能够完成克隆,其结果如下:

代码语言:javascript
代码运行次数:0
运行
复制
package demointerface;

class Person implements  Cloneable{
    public String name;

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

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

public class Test2 {
    public static void main(String[] args) throws CloneNotSupportedException{
        Person person1 = new Person("Aileen");
        //通过person这个引用来克隆一个对象
        Person person2 = (Person) person1.clone();
        System.out.println(person2.name);
    }
}
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
  • 但是我们查看Cloneable接口时,可以看到它这个接口是空的,什么都没有实现,那为什么我们要让person执行这个接口呢?
    • 如果一个类实现了这个空接口/标记接口,那么证明当前类是可以被克隆的。
在这里插入图片描述
在这里插入图片描述
浅拷贝

浅拷贝:只克隆对象的其中一部分。

代码语言:javascript
代码运行次数:0
运行
复制
class Money{
    public double money = 12.5;
}

class Person implements  Cloneable{
    public String name;
    public Money q;

    public Person(String name) {
        this.name = name;
        q = new Money();
    }

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

public class Test2 {
    public static void main(String[] args) throws CloneNotSupportedException{
        Person person1 = new Person("Aileen");
        //通过person这个引用来克隆一个对象
        Person person2 = (Person) person1.clone();
        System.out.println("修改之前: " + person1.q.money);
        System.out.println("修改之前: " + person2.q.money);
        person2.q.money = 1000;
        System.out.println("修改之后: " + person1.q.money);
        System.out.println("修改之后: " + person2.q.money);
    }
}
在这里插入图片描述
在这里插入图片描述

peson2是克隆person1的对象,由于person2只克隆了person1所指向的对象,这个对象原本指向money,但person2并未克隆money,所以person2的对象也指向money;所以我们通过person2去修改money的值,打印出来person1和person2的结果是一样的,因为这两个对象指向同一个值。

深拷贝

深拷贝:每一个对象都能有一个独立于原来对象的内容(每一个对象的对象都得克隆),是否是深拷贝需要由程序员实现,与我们的使用的方法无关。

在这里插入图片描述
在这里插入图片描述

由于我们首先要克隆的是person1的对象(它包括name和q),然而person1是Person类型的,我们在调用默认父类Object的clone方法时,由于他是Object类型所以我们要将其进行向下转型。


在这里插入图片描述
在这里插入图片描述

为了实现深拷贝,然后我们克隆了person1的对象q的对象Money,从而能够确保person1和person2拥有各自独立的Money对象。


在这里插入图片描述
在这里插入图片描述

然后我们将我们创建的temp对象指向person2,从而实现person1所有对象的独立克隆。


在这里插入图片描述
在这里插入图片描述

由于temp是局部变量,当它执行完clone函数以后,它的内存就会被回收。


在这里插入图片描述
在这里插入图片描述

现在person1和person2都能够有独立的对象,每个对象都独立于原对象以外,这就是深拷贝,他们之间互不干扰。


实现代码:

代码语言:javascript
代码运行次数:0
运行
复制
package demointerface;

class Money implements Cloneable{
    public double money = 12.5;

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

class Person implements  Cloneable{
    public String name;
    public Money q;

    public Person(String name) {
        this.name = name;
        q = new Money();
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
//        return super.clone();//通过super.clone()克隆出来的就是person
        Person temp = (Person) super.clone();

        temp.q =(Money) this.q.clone();//克隆person1的对象q并将其指向temp,因为这个函数返回值是Object类型所以需要向下转型成Money。

        return temp;
    }
}

public class Test2 {
    public static void main(String[] args) throws CloneNotSupportedException{

        Person person1 = new Person("Aileen");
        Person person2 = (Person) person1.clone();
        System.out.println("修改之前: " + person1.q.money);//12.5
        System.out.println("修改之前: " + person2.q.money);//12.5
        person2.q.money = 1000;
        System.out.println("修改之后: " + person1.q.money);//12.5
        System.out.println("修改之后: " + person2.q.money);//1000
    }
}

运行结果:

在这里插入图片描述
在这里插入图片描述

图书

  • Step1
在这里插入图片描述
在这里插入图片描述

通过向上转型将两个不同的子类对象指向父类User这个类型的引用,然后通过判断语句返回选中的对象

  • Step2
在这里插入图片描述
在这里插入图片描述
  • Step3
在这里插入图片描述
在这里插入图片描述
  • Step4
  • user一定会指向一个对象,一定会对IOPeration进行初始化。
在这里插入图片描述
在这里插入图片描述
  • 假设我们的user调用的是NormalUser这个对象,我们选择的choice值为1,当我们调用这个对象的时候会先执行它的构造方法,然后打印出相应对象的菜单,在我们输入完choice的值后,它会调用对应的NormalUser对象的doOperation方法,由于他没有重写这个方法,所以它会访问父类的doOperation方法然后将通过父类的doOperation方法去访问数组的1下标的对象FindOperation,再通过这个对象去调用它的work方法并将bookList作为参数传入。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

虽然Java已经覆盖掉了红楼梦这本书但是原来位置的Java还是跟现在的一样指向相同的内存地址,但是这块空间已经没有任何作用,却还指向对象,这会导致内存泄漏。所以我们可以通过垃圾回收器GC进行回收,可以通过Java中的set方法将其置为空。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-12-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 以年龄为比较标准去比较
  • 以字符串长度为比较标准去比较
  • Comparator接口的应用
  • 通过sort方法进行排序
  • 总结:
  • Clonable接口
    • 浅拷贝
    • 深拷贝
  • 图书
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档