深入分析Comparable與Comparator及Clonable三個Java接口
1.Comparable
這個接口是用來給對象數(shù)組來排序的
在我學接口之前我用的排序方法是Arrays.sort(),我發(fā)現(xiàn)單單靠之前所學知識并不能解決給對象數(shù)組排序的問題,后來學習過程中發(fā)現(xiàn)Comparable這一接口解決了我的疑惑,也感受到了這一接口的強大之處,但這也不是最好的,后續(xù)會說到,畢竟學知識是個循序漸進的過程嘛
首先,我們看一下我們之前學習時用的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));
}
}它能將整形數(shù)組從小到大排序,,,下面我們再來看一下Arrays.sort給對象數(shù)組排序(錯誤示范)
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));
}
}當我們寫出這樣一個代碼的時候,我們會發(fā)現(xiàn)運行結(jié)果是個什么東西???

這時候不要慌,簡單分析一下報錯原因,我們可以看到它報了一個ClassCastException(類型轉(zhuǎn)換異常),根據(jù)后面的稍微能看懂的幾個英文(小編自己英文水平太差,所以只能看懂幾個),可以大概的知道是說Student不能轉(zhuǎn)換為java.lang包下的Comparable,這時候我們點進去看一下源碼,不要害怕看源碼,有時候我們往往只需要看懂一點就能明白錯誤的原因了

經(jīng)過粗略的分析if條件語句這一行,發(fā)現(xiàn)數(shù)組取下標呢,元素與元素之間都用compareTo來比較,這時候,我們發(fā)現(xiàn)其中的貓膩了,,,我們打開幫助手冊查一下Comparable,發(fā)現(xiàn)它是一個泛型接口,,并且它有一個抽象方法compareTo,這時候面紗就將要一層一層的揭開了


compareTo方法中這一大段畫,看到第一行我們就明白了,這東西可以幫我們解決數(shù)組對象的比較問題,所以我們要拿Student這個類去實現(xiàn)Comparable,并且實現(xiàn)compareTo方法,就能做到對象數(shù)組的排序了,看代碼:
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()就可以幫我們做到對象數(shù)組的排序了,在 sort 方法中會自動調(diào)用 compareTo 方法. compareTo 的參數(shù)是 Object , 其實傳入的就是 Student 類型的對象.
然后比較當前對象和參數(shù)對象的大小關系 (例如按score ).
- 如果當前對象應排在參數(shù)對象之前 , 返回小于 0 的數(shù)字 ;
- 如果當前對象應排在參數(shù)對象之后 , 返回大于 0 的數(shù)字;
- 如果當前對象和參數(shù)對象不分先后 , 返回 0;
如果你對姓名的比較存在疑惑,比如:為什么name也可以調(diào)用compareTo方法這種類似的問題,因為name是String類型的,我建議你先看一下String的源碼,里面也實現(xiàn)了Comparable接口,以及重寫了compareTo方法,這里就不詳細介紹了,感興趣的小伙伴們可以嘗試一下哦,當然,我相信你們都是大佬,一看就懂哈哈
執(zhí)行程序,看運行結(jié)果,這下就能達到我們想要的效果了

前面說了,這樣的代碼也不是最好的,存在局限性,我按分數(shù)來排序,那代碼就寫死了 ,那我以后想按姓名來排序,我又得回頭改???,以后進公司了,你的代碼做改變,影響其他人的代碼,那不得把你罵死,所以我們需要做進一步改進,引入Comparator接口
2.Comparator比較器
這個接口又叫比較器,那比較器又是個什么東西呢???下面我也是老套路啦,一步一步揭開這東西的面紗
為了解決Comparable接口的局限性,我們這個比較器完美的展現(xiàn)了實現(xiàn)效果,它也是一個泛型接口,同樣只有一個抽象方法需要重寫,下面看代碼:
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));
}
}通過這段代碼,我們發(fā)現(xiàn)比較器是真真正正的做到了,想按什么排序就按什么排序,在對類的侵入性以及代碼耦合度方面也算是不用太過擔心了
3.Clonable接口和深拷貝
Java 中內(nèi)置了一些很有用的接口 , Clonable 就是其中之一 .
Object 類中存在一個 clone 方法 , 調(diào)用這個方法可以創(chuàng)建一個對象的 " 拷貝 ". 但是要想合法調(diào)用 clone 方法 , 必須要先實現(xiàn) Clonable 接口 , 否則就會拋 CloneNotSupportedException 異常.
先來看一段代碼吧,我個人喜歡結(jié)合代碼看分析,這樣子就降低了云里霧里的可能性了,
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();
}
}
}實現(xiàn)克隆的兩個條件(結(jié)合上面代碼):
1.這個對象可以被克隆,也就是這個Student類要實現(xiàn)這個Clonable接口
2.要在這個類中重寫父類Object的clone()方法

我們現(xiàn)在的代碼只是達到了這樣一個效果,,重頭戲還在后邊,因為我們的拷貝有時候遠遠不止于此,這種只能算是一個淺拷貝,那什么才算是深拷貝呢??? 請看下面的代碼:
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);
}
}我現(xiàn)在在之前的代碼的基礎上添加了一個Money類,并且實例化Money的對象作為Student的成員,這時候克隆之后,改變我money的值,會是一個什么的效果呢???
先看運行結(jié)果吧,我就不兜圈子了

我們發(fā)現(xiàn),這并不是我們想要的結(jié)果,我們想要的結(jié)果是,通過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的值了,廢話不多說,運行結(jié)果為證

以上就是三個重要接口的全部分析了,下次再見?。?!
到此這篇關于深入分析Comparable與Comparator及Clonable三個Java接口的文章就介紹到這了,更多相關Java Comparable Comparator Clonable內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
springboot yml中profiles的巧妙用法(小白必看多環(huán)境配置)
這篇文章主要介紹了springboot yml中profiles的巧妙用法,非常適合多環(huán)境配置場景,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-04-04
解決mysql字符串類型的數(shù)字排序出錯:cast(year as signed)
這篇文章主要介紹了解決mysql字符串類型的數(shù)字排序出錯問題 :cast(year as signed),如有錯誤或未考慮完全的地方,望不吝賜教2023-08-08
mybatis如何設置useGeneratedKeys=true
這篇文章主要介紹了mybatis如何設置useGeneratedKeys=true,具有很好的參考價值,希望對大家有所幫助。2022-01-01
Java中Runnable和Callable分別什么時候使用
提到 Java 就不得不說多線程了,就算你不想說,面試官也得讓你說呀,那說到線程,就不得不說Runnable和Callable這兩個家伙了,二者在什么時候使用呢,下面就來和簡單講講2023-08-08
springboot中rabbitmq實現(xiàn)消息可靠性機制詳解
這篇文章主要介紹了springboot中rabbitmq實現(xiàn)消息可靠性機制詳解,本文通過實例代碼給大家介紹的非常詳細,需要的朋友可以參考下2021-09-09
詳解XML,Object,Json轉(zhuǎn)換與Xstream的使用
這篇文章主要介紹了詳解XML,Object,Json轉(zhuǎn)換與Xstream的使用的相關資料,需要的朋友可以參考下2017-02-02
Springboot升級至2.4.0中出現(xiàn)的跨域問題分析及修改方案
這篇文章主要介紹了Springboot升級至2.4.0中出現(xiàn)的跨域問題分析及修改方案,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-12-12

