文章目录
- 前言
- 一 比较器
- 1.1 关于两个对象的比较
- 1.2 Comparable接口:
- 1.3 Arrays.sort方法的实现
- 1.4 比较器的实现
- Comparator接口
- 二 深拷贝与浅拷贝
- 2.1 浅拷贝:
- Cloneable接口:
- clone方法:
- 实现拷贝:
- 浅拷贝:
- 2.2 深拷贝:
前言
上一篇博客并没有将接口的内容阐述完毕,这篇博客继续阐述!
一 比较器
1.1 关于两个对象的比较
在比较单个基本数据类型时,我们可以通过关系运算符进行比较。
public class Test {public static void main(String[] args) {int age1 = 10;int age2 = 8;System.out.println(age1 > age2);}
但是当比较对象等引用数据类型时,便不能仅仅通过关系运算符进行比较了。
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是一个抽象方法,我们需要重写:
//创建一个Student类,实现Comparable接口
class Student implements Comparable<Student> {String name;int age;public Student(int age, String name) {this.age = age;this.name = name;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}
//重写compareTo方法@Overridepublic 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方法
class Student implements Comparable<Student> {String name;int age;public Student(int age, String name) {this.age = age;this.name = name;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic 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方法
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方法:
class Student implements Comparable<Student> {String name;int age;public Student(int age, String name) {this.age = age;this.name = name;}@Overridepublic 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文件中
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文件中
package demo1;import java.util.Comparator;
//实现Comparator接口
public class AgeComparator implements Comparator<Student> {@Overridepublic int compare(Student o1, Student o2) {return o1.age - o2.age;}
}
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;}@Overridepublic 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类:
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++等其他编程语言实现,不能查看其具体实现。
实现拷贝:
package demo1;public class Person implements Cloneable{ //实现空接口的类,代表可以拷贝String name;int age ;public Person(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}
}
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方法:
protected Object clone() throws CloneNotSupportedException {return super.clone();}
(2) 此时编译器又报警告:
这是异常的问题,以后会阐述到,解决这个问题,在main方法后,加上一条语句即可:
(3)编译器又报警告的原因是:
方法的返回值类型为Object类型,我们需要将其强制为Person类型。
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);}
}
此刻,内存中情况如下:
浅拷贝:
拷贝的情况讲完了,那什么是浅拷贝呢?
当对象中有对象的创建时,此时只拷贝外部的对象,而不拷贝内部的对象,称为浅拷贝!
举例:
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;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}
//重写克隆方法@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}
package demo1;
//创建一个Money类
public class Money {int moneycount ;public Money(int moneycount) {this.moneycount = moneycount;}
}
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类中的格式进行重写编写
代码:
//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;}@Overridepublic 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;}
}
//Money类
package demo1;public class Money implements Cloneable {int moneycount ;public Money(int moneycount) {this.moneycount = moneycount;}
//也重写clone方法@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}
//测试类
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中的值!
在内存中的展示: