深入分析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ù)對象的大小關(guān)系 (例如按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)在在之前的代碼的基礎(chǔ)上添加了一個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é)果為證
以上就是三個重要接口的全部分析了,下次再見?。?!
到此這篇關(guān)于深入分析Comparable與Comparator及Clonable三個Java接口的文章就介紹到這了,更多相關(guān)Java Comparable Comparator Clonable內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
在Java中輕松將HTML格式文本轉(zhuǎn)換為純文本的方法示例(保留換行)
這篇文章主要介紹了在Java中輕松將HTML格式文本轉(zhuǎn)換為純文本的方法示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-04-04Java中ShardingSphere 數(shù)據(jù)分片的實現(xiàn)
其實很多人對分庫分表多少都有點恐懼,我們今天用ShardingSphere 給大家演示數(shù)據(jù)分片,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09詳談Java枚舉、靜態(tài)導入、自動拆裝箱、增強for循環(huán)、可變參數(shù)
下面小編就為大家?guī)硪黄斦凧ava枚舉、靜態(tài)導入、自動拆裝箱、增強for循環(huán)、可變參數(shù)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08springboot模塊里面調(diào)用另外一個模塊的方法實現(xiàn)
在Spring-Boot項目開發(fā)中,存在著本模塊的代碼需要訪問外面模塊接口,本文就來介紹一下springboot模塊里面調(diào)用另外一個模塊的方法實現(xiàn),感興趣的可以了解一下2023-11-11java、spring、springboot中整合Redis的詳細講解
這篇文章主要介紹了java、spring、springboot中整合Redis的詳細講解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-04-04