目录
- 接口一 :?Comparable
- 接口二 :?Comparator
- 接口三 : Clonable?
1.Comparable
这个接口是用来给对象数组来排序的
在我学接口之前我用的排序方法是Arrays.sort(),我发现单单靠之前所学知识并不能解决给对象数组排序的问题,后来学习过程中发现Comparable这一接口解决了我的疑惑,也感受到了这一接口的强大之处,但这也不是最好的,后续会说到,毕竟学知识是个循序渐进的过程嘛,,,
?首先,我们看一下我们之前学习时用的Arrays.sort,,,
public class TestDemo {
public static void main(String[] args) {
int[] array = {1,3,6,2,4};
Arrays.sort(array);
System.out.println(Arrays.toString(array));
}
}
它能将整形数组从小到大排序,,,下面我们再来看一下Arrays.sort给对象数组排序,,,(错误示范)
class Student {
public String name;
public int age;
public double score;
public Student(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
}
public class TestDemo {
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student("zhangsan",98,78.9);
students[1] = new Student("lisi",38,48.9);
students[2] = new Student("wangwu",18,88.9);
Arrays.sort(students);
System.out.println(Arrays.toString(students));
}
}
?当我们写出这样一个代码的时候,我们会发现运行结果是个什么东西???
这时候不要慌,简单分析一下报错原因,我们可以看到它报了一个ClassCastException(类型转换异常),根据后面的稍微能看懂的几个英文(小编自己英文水平太差,所以只能看懂几个),可以大概的知道是说Student不能转换为java.lang包下的Comparable,这时候我们点进去看一下源码,不要害怕看源码,有时候我们往往只需要看懂一点就能明白错误的原因了,,,
?经过粗略的分析if条件语句这一行,发现数组取下标呢,元素与元素之间都用compareTo来比较,这时候,我们发现其中的猫腻了,,,我们打开帮助手册查一下Comparable,发现它是一个泛型接口,,并且它有一个抽象方法compareTo,这时候面纱就将要一层一层的揭开了,,,
?compareTo方法中这一大段画,看到第一行我们就明白了,这东西可以帮我们解决数组对象的比较问题,所以我们要拿Student这个类去实现Comparable,并且实现compareTo方法,就能做到对象数组的排序了,,,看代码:
class Student implements Comparable<Student>{
public String name;
public int age;
public double score;
public Student(String name, int age, double 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) {
//return this.name.compareTo(o.name);
//return this.age - o.age;
return (int)(this.score-o.score);
}
}
这时候Arrays.sort()就可以帮我们做到对象数组的排序了,在 sort 方法中会自动调用 compareTo 方法. compareTo 的参数是 Object , 其实传入的就是 Student 类型的对象.
然后比较当前对象和参数对象的大小关系
(例如按score
).
-
如果当前对象应排在参数对象之前
,
返回小于
0
的数字
;
-
如果当前对象应排在参数对象之后
,
返回大于
0
的数字;
-
如果当前对象和参数对象不分先后
,
返回
0;
? ? ? ?如果你对姓名的比较存在疑惑,比如:为什么name也可以调用compareTo方法这种类似的问题,因为name是String类型的,我建议你先看一下String的源码,里面也实现了Comparable接口,以及重写了compareTo方法,这里就不详细介绍了,感兴趣的小伙伴们可以尝试一下哦,当然,我相信你们都是大佬,一看就懂哈哈,,
执行程序,看运行结果,这下就能达到我们想要的效果了,,,
?前面说了,这样的代码也不是最好的,存在局限性,我按分数来排序,那代码就写死了 ,那我以后想按姓名来排序,我又得回头改???,以后进公司了,你的代码做改变,影响其他人的代码,那不得把你骂死,所以我们需要做进一步改进,引入Comparator接口,,,
2.Comparator比较器
这个接口又叫比较器,那比较器又是个什么东西呢???下面我也是老套路啦,一步一步揭开这东西的面纱,,,
为了解决Comparable接口的局限性,我们这个比较器完美的展现了实现效果,它也是一个泛型接口,同样只有一个抽象方法需要重写,下面看代码:
class Student {
public String name;
public int age;
public double score;
public Student(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
}
//比较器
class AgeComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.age - o2.age;
}
}
class StringComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.name.compareTo(o2.name);
}
}
class ScoreComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return (int)(o1.score-o2.score);
}
}
public class TestDemo {
public static void main(String[] args) {
Student[] students = new Student[3];
students[0] = new Student("zhangsan",98,78.9);
students[1] = new Student("lisi",38,48.9);
students[2] = new Student("wangwu",18,88.9);
/*AgeComparator ageComparator = new AgeComparator();
Arrays.sort(students,ageComparator);
StringComparator stringComparator = new StringComparator();
Arrays.sort(students,stringComparator);*/
ScoreComparator scoreComparator = new ScoreComparator();
Arrays.sort(students,scoreComparator);
System.out.println(Arrays.toString(students));
}
}
?通过这段代码,我们发现比较器是真真正正的做到了,想按什么排序就按什么排序,在对类的侵入性以及代码耦合度方面也算是不用太过担心了,,,
3.Clonable接口和深拷贝
Java
中内置了一些很有用的接口
, Clonable
就是其中之一
.
Object 类中存在一个
clone
方法
,
调用这个方法可以创建一个对象的
"
拷贝
".
但是要想合法调用
clone
方法
,
必须要先实现 Clonable
接口
,
否则就会抛
CloneNotSupportedException
异常.
先来看一段代码吧,我个人喜欢结合代码看分析,这样子就降低了云里雾里的可能性了,
class Student implements Cloneable{
public int id = 1234;
@Override
public String toString() {
return "Student{" +
"id=" + id +
'}';
}
//重写Object父类的clone()方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class TestDemo {
public static void main(String[] args) {
Student student1 = new Student();
try {
Student student2 = (Student) student1.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
实现克隆的两个条件(结合上面代码):?
1.这个对象可以被克隆,也就是这个Student类要实现这个Clonable接口,,,
2.要在这个类中重写父类Object的clone()方法,,,
?我们现在的代码只是达到了这样一个效果,,重头戏还在后边,因为我们的拷贝有时候远远不止于此,这种只能算是一个浅拷贝,那什么才算是深拷贝呢??? 请看下面的代码:
class Money implements Cloneable{
public double money = 19.9;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Student implements Cloneable{
public int id = 1234;
public Money m = new Money();
@Override
public String toString() {
return "Student{" +
"id=" + id +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class TestDemo {
public static void main(String[] args) throws CloneNotSupportedException {
Student student1 = new Student();
//为了代码的好看,我这里不处理这个异常
Student student2 = (Student) student1.clone();
System.out.println(student1.m.money);
System.out.println(student2.m.money);
System.out.println("=========================");
student1.m.money = 99.99;
System.out.println(student1.m.money);
System.out.println(student2.m.money);
}
}
?我现在在之前的代码的基础上添加了一个Money类,并且实例化Money的对象作为Student的成员,这时候克隆之后,改变我money的值,会是一个什么的效果呢???
先看运行结果吧,我就不兜圈子了,,,
?我们发现,这并不是我们想要的结果,我们想要的结果是,通过student2去改变money的值,它并不会影响student1中的money,而我们刚刚的代码并没有做到,它的效果图如下:
?那又如何做到深拷贝呢??这时我们就需要对我们刚才的代码做一些改进了,,,
我们只需将原来的Student类中的Object的克隆方法改成下面这份代码就能做到我们想要的效果:
@Override
protected Object clone() throws CloneNotSupportedException {
Student tmp = (Student) super.clone();//将id变量克隆一份,tmp指向它
tmp.m = (Money) this.m.clone();//将m对象中的money变量克隆一份 tmp中的m指向它
return tmp;
//return super.clone();
}
?看到这个代码先不要慌,如果注释没看明白,咱还有板书可以参照,,,
其实分析起来也就那么回事,,
?当我们tmp返回的时候,就把0x99给到了student2,克隆完成之后,tmp是局部变量,也就被回收了,,,就变成了下面这副摸样:
?这个时候,我们再去通过student2去改变我们的money,就不会影响student1中的money的值了,废话不多说,运行结果为证,,,
/**
* 以上就是三个重要接口的全部分析了,下次再见!!!
*/
|