初识java——javaSE (6)接口的实现——比较器与深拷贝,浅拷贝

文章目录

  • 前言
  • [一 比较器](#一 比较器)
    • [1.1 关于两个对象的比较](#1.1 关于两个对象的比较)
    • [1.2 Comparable接口:](#1.2 Comparable接口:)
    • [1.3 Arrays.sort方法的实现](#1.3 Arrays.sort方法的实现)
    • [1.4 比较器的实现](#1.4 比较器的实现)
  • [二 深拷贝与浅拷贝](#二 深拷贝与浅拷贝)

前言


上一篇博客并没有将接口的内容阐述完毕,这篇博客继续阐述!

一 比较器

1.1 关于两个对象的比较

在比较单个基本数据类型时,我们可以通过关系运算符进行比较。
java 复制代码
 public class Test {
   
        public static void main(String[] args) {
            int age1 = 10;
            int age2 = 8;
            System.out.println(age1 > age2);
           }
但是当比较对象等引用数据类型时,便不能仅仅通过关系运算符进行比较了。
java 复制代码
class Student {
    String name;
    int age;

    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }
  }
     public class Test {
               public static void main(String[] args) {

            Student student1 = new Student(10,"张三");
            Student student2 = new Student(12,"李四");
            System.out.println(student1>student2);

     }
   
    }
要进行对象间的比较,需要确定进行比较的规则是什么,比较哪一个属性。

1.2 Comparable接口:

Comparable接口中的compareTo 方法用于进行对象间属性的比较。

compareTo是一个抽象方法,我们需要重写:

java 复制代码
//创建一个Student类,实现Comparable接口
class Student implements Comparable<Student> {
    String name;
    int age;

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
//重写compareTo方法
    @Override
    public int compareTo(Student o) {
        //比较年龄:

        if(this.age == o.age){
            return 0;
        }else if(this.age>o.age){
            return 1;
        }else {
            return -1;
        }
    }
        //比较姓名
      //  return this.name.compareTo(o.name);
    }

    public class Test {
  
        public static void main(String[] args) {

            Student student1 = new Student(10,"张三");
            Student student2 = new Student(12,"李四");
            System.out.println(student1.compareTo(student2));

结果为-1 ,表明student1的年龄比student2的年龄小。
代码分析:

Student类实现了Comparable接口,

<>是泛型的标记,以后会阐述到,比较那个类,就将类名填写在<>中!

对象间的比较本质上依然是对象属性之间的比较。

1.3 Arrays.sort方法的实现

如果创建一个对象数组,使得数组中的这些对象按照某种规则进行排序

则可以使用Array.sort方法

java 复制代码
class Student implements Comparable<Student> {
    String name;
    int age;

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

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


    @Override
    public int compareTo(Student o) {
        //比较年龄:

        if(this.age == o.age){
            return 0;
        }else if(this.age>o.age){
            return 1;
        }else {
            return -1;
        }
    }
        //比较姓名
      //  return this.name.compareTo(o.name);
    }

  public class Test {
  public static void main(String[] args) {
            Student[] arr1 = new Student[3];
            arr1[0] = new Student(10, "王五");
            arr1[1] = new Student(8, "李四");
            arr1[2] = new Student(9, "张三");
                Student[] arr1 = {student1,student2,student3};
            System.out.println("排序前:"+Arrays.toString(arr1));
            
            Arrays.sort(arr1);
            System.out.println("排序后:"+Arrays.toString(arr1));
          
     }
}

排序后,年龄从小到大,依次递增。

如果不实现Comparable接口,会怎么样呢?

结果表明Student类型,不能转换成Comparable类型,这是怎么回事,我们调用Arrays.sort方法进行排序,与转换类型有什么关系?

这里我们需要手动实现一下Arrays.sort方法

java 复制代码
 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++){
              
                  if(comparables[j].compareTo(comparables[j+1])>0){
                       //如果数组前面元素的值大于数组后面元素的值,则交换引用的值,这是升序
                      Comparable tmp  = comparables[j];
                      comparables[j]  = comparables[j+1];
                      comparables[j+1] = tmp;
                  }
              }

        }
    }

代码分析: 问题就在于 if(comparables[j].compareTo(comparables[j+1])>0) 这条语句

我们通过接口类型数组来接收实现了接口的数组,并且对compareTo方法进行调用!

在上个例子中,因为没有Student没有实现Comparable接口,所以会发生Student类型无法发生向上转型成Comparable接口类型的情况。

调用自己实现的mysort方法:

java 复制代码
class Student implements Comparable<Student> {
    String name;
    int age;
    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    public int compareTo(Student o) {
        //比较年龄:

        if(this.age == o.age){
            return 0;
        }else if(this.age>o.age){
            return 1;
        }else {
            return -1;
        }
    }
        //比较姓名
      //  return this.name.compareTo(o.name);
    }
    public class Test {


  public static void main(String[] args) {

       
            Student[] arr1 = new Student[3];


            arr1[0] = new Student(10, "王五");
            arr1[1] = new Student(8, "李四");
            arr1[2] = new Student(9, "张三");
         
            System.out.println("排序前:" + Arrays.toString(arr1));
           
              mysort(arr1);
            System.out.println("排序后:" + Arrays.toString(arr1));
           

        }
    }

1.4 比较器的实现

在上面实现compareTo方法时,我们只能比较某一固定的属性,比如年龄或者名字,
这比较有局限性,总不能当需要比较某一属性时,再去修改类的实现。

解决方案:当需要比较某一属性时,就调用相关的类!

Comparator接口

如图所示:Comparator接口中有一个compare抽象方法。

我们可以创建不同的类来实现此接口,当需要比较不同的属性值时,调用不同的类:

举例:

java 复制代码
//在单独一个java文件中
package demo1;

import java.util.Comparator;
//创建一个NameComparator类,实现Comparator接口
public class NameComparator implements Comparator<Student> {
    @Override
    //实现接口中的抽象方法,用于进行名字之间的比较!
    public int compare(Student o1, Student o2) {
        return o1.name.compareTo(o2.name);
    }
}

o1.name之所以可以引用compareTo方法是因为String类中重写了compareTo方法:

java 复制代码
//在另一个java文件中
package demo1;

import java.util.Comparator;
//实现Comparator接口
public class AgeComparator implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {

        return o1.age - o2.age;
    }
}
java 复制代码
package demo1;


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

//接口的应用!
// 比较两个对象的尝试!
// 实现关于comparable接口!
class Student implements Comparable<Student> {
    String name;
    int age;

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

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


    public int compareTo(Student o) {
        //比较年龄:

        if(this.age == o.age){
            return 0;
        }else if(this.age>o.age){
            return 1;
        }else {
            return -1;
        }
    }
        //比较姓名
      //  return this.name.compareTo(o.name);
    }
    public class Test {
  
      public static void main(String[] args) {
            Student[] arr1 = new Student[3];
            arr1[0] = new Student(10, "王五");
            arr1[1] = new Student(8, "李四");
            arr1[2] = new Student(9, "张三");
       
       System.out.println("排序前:" + Arrays.toString(arr1));
            //创建一个NameComparator对象。
    NameComparator nameComparator   =  new NameComparator();
            //进行名字间的比较!
            //Arrays.sort方法可以接收第二个参数!
            Arrays.sort(arr1,nameComparator);
       
           
     System.out.println("排序后:" + Arrays.toString(arr1));
           
        }
    }

这是按照姓名排序的结果:

按照年龄排序,则调用AgeComparator类:

java 复制代码
     System.out.println("排序前:" + Arrays.toString(arr1));
     AgeComparator ageComparator = new AgeComparator();
            //根据年龄进行比较!
            Arrays.sort(arr1,ageComparator);
         System.out.println("排序后:"+Arrays.toString(arr1));

排序后,年龄从小到大!

我们通过创建不同的实现Comparator的类,并实现抽象方法compare,
当需要比较某一属性时,即调用某一属性对应类进行比较,这就是比较器的思想与实现!

二 深拷贝与浅拷贝

2.1 浅拷贝:

 所谓拷贝即将一个对象复制一份,由另一个引用指向新复制出的对象。

Cloneable接口:

要进行拷贝的类,需要先实现Cloneable接口.

Cloneable接口是一个空接口,代表着实现此接口的类可以被拷贝!

clone方法:

clone方法是Object类中用来拷贝对象的方法。

此方法被Native修饰,说明它是由C/C++等其他编程语言实现,不能查看其具体实现。

实现拷贝:

java 复制代码
package demo1;

public class Person implements Cloneable{   //实现空接口的类,代表可以拷贝
              String name;
              int age ;

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
java 复制代码
package demo1;
// 浅拷贝

public class Test {
    public static void main(String[] args) {
      Person person1 = new Person("张三",10);
      Person person2 =  person1.clone();
    }
}

(1)此时编译器报警告:

尽管clone方法的访问权限是protected且Test也是Object的子类,但是当person1调用clone方法时,
是在Person类的外部,所以报错。

解决这个问题,我们需要在子类中重写clone方法:

java 复制代码
 protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

(2) 此时编译器又报警告:

这是异常的问题,以后会阐述到,解决这个问题,在main方法后,加上一条语句即可:

(3)编译器又报警告的原因是:

方法的返回值类型为Object类型,我们需要将其强制为Person类型。

java 复制代码
package demo1;
// 浅拷贝

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException{
      Person person1 = new Person("张三",10);
      Person person2 = (Person) person1.clone();
        System.out.println(person1);
        System.out.println(person2);

    }
}

此刻,内存中情况如下:

浅拷贝:

拷贝的情况讲完了,那什么是浅拷贝呢?

当对象中有对象的创建时,此时只拷贝外部的对象,而不拷贝内部的对象,称为浅拷贝!

举例:

java 复制代码
package demo1;
//创建一个Person类,实现接口
public class Person implements Cloneable{   //实现空接口的类,代表可以拷贝
              String name;
              int age ;

              Money money1 = new Money(10);

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
//重写克隆方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
java 复制代码
package demo1;
//创建一个Money类
public class Money {
    int moneycount ;

    public Money(int moneycount) {
        this.moneycount = moneycount;
    }
}
java 复制代码
package demo1;
// 浅拷贝

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException{
      Person person1 = new Person("张三",10);
      Person person2 = (Person) person1.clone();
        System.out.println("修改前:"+person1.money1.moneycount);
        System.out.println("修改前:"+person2.money1.moneycount);
//仅仅修改person2中的money对象的值,会不会改变person1中的值?
        person2.money1.moneycount = 20;
        System.out.println("修改后:"+person1.money1.moneycount);
        System.out.println("修改后:"+person2.money1.moneycount);

    }
}

在内存中情况:

这种未将对象 中的对象 拷贝的不彻底拷贝,我们称为浅拷贝!

2.2 深拷贝:

深拷贝也就是将对象中的对象也进行拷贝, 
这需要对Person类中的clone方法进行重写:
并且对Money类按照Person类中的格式进行重写编写

代码:

java 复制代码
//Person类
package demo1;

public class Person implements Cloneable{   //实现空接口的类,代表可以拷贝
              String name;
              int age ;

              Money money1 = new Money(10);

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

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

    @Override
    //重写后的方法
    protected Object clone() throws CloneNotSupportedException {
        Person tmp = (Person) super.clone();    //谁调用了super方法,不需要this指定对象。
        //对于对象中的对象也进行拷贝!
        tmp.money1 = (Money) this.money1.clone();
        return tmp;



    }
}
java 复制代码
//Money类
package demo1;

public class Money implements Cloneable {
    int moneycount ;

    public Money(int moneycount) {
        this.moneycount = moneycount;
    }
//也重写clone方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
java 复制代码
//测试类
package demo1;
// 浅拷贝

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException{
      Person person1 = new Person("张三",10);
      Person person2 = (Person) person1.clone();
        System.out.println("修改前:"+person1.money1.moneycount);
        System.out.println("修改前:"+person2.money1.moneycount);

        person2.money1.moneycount = 20;
        System.out.println("修改后:"+person1.money1.moneycount);
        System.out.println("修改后:"+person2.money1.moneycount);

    }

结果表明,此时深拷贝成功,修改person2中的money值,不会改变person1中的值!

在内存中的展示:

相关推荐
XiaoLeisj2 小时前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
paopaokaka_luck2 小时前
【360】基于springboot的志愿服务管理系统
java·spring boot·后端·spring·毕业设计
dayouziei2 小时前
java的类加载机制的学习
java·学习
励志成为嵌入式工程师3 小时前
c语言简单编程练习9
c语言·开发语言·算法·vim
捕鲸叉3 小时前
创建线程时传递参数给线程
开发语言·c++·算法
A charmer3 小时前
【C++】vector 类深度解析:探索动态数组的奥秘
开发语言·c++·算法
Peter_chq3 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
Yaml44 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
小小小妮子~4 小时前
Spring Boot详解:从入门到精通
java·spring boot·后端
hong1616884 小时前
Spring Boot中实现多数据源连接和切换的方案
java·spring boot·后端