深入分析Comparable與Comparator及Clonable三個(gè)Java接口
1.Comparable
這個(gè)接口是用來給對(duì)象數(shù)組來排序的
在我學(xué)接口之前我用的排序方法是Arrays.sort(),我發(fā)現(xiàn)單單靠之前所學(xué)知識(shí)并不能解決給對(duì)象數(shù)組排序的問題,后來學(xué)習(xí)過程中發(fā)現(xiàn)Comparable這一接口解決了我的疑惑,也感受到了這一接口的強(qiáng)大之處,但這也不是最好的,后續(xù)會(huì)說到,畢竟學(xué)知識(shí)是個(gè)循序漸進(jìn)的過程嘛
首先,我們看一下我們之前學(xué)習(xí)時(shí)用的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ù)組從小到大排序,,,下面我們?cè)賮砜匆幌翧rrays.sort給對(duì)象數(shù)組排序(錯(cuò)誤示范)
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)); } }
當(dāng)我們寫出這樣一個(gè)代碼的時(shí)候,我們會(huì)發(fā)現(xiàn)運(yùn)行結(jié)果是個(gè)什么東西???
這時(shí)候不要慌,簡(jiǎn)單分析一下報(bào)錯(cuò)原因,我們可以看到它報(bào)了一個(gè)ClassCastException(類型轉(zhuǎn)換異常),根據(jù)后面的稍微能看懂的幾個(gè)英文(小編自己英文水平太差,所以只能看懂幾個(gè)),可以大概的知道是說Student不能轉(zhuǎn)換為java.lang包下的Comparable,這時(shí)候我們點(diǎn)進(jìn)去看一下源碼,不要害怕看源碼,有時(shí)候我們往往只需要看懂一點(diǎn)就能明白錯(cuò)誤的原因了
經(jīng)過粗略的分析if條件語句這一行,發(fā)現(xiàn)數(shù)組取下標(biāo)呢,元素與元素之間都用compareTo來比較,這時(shí)候,我們發(fā)現(xiàn)其中的貓膩了,,,我們打開幫助手冊(cè)查一下Comparable,發(fā)現(xiàn)它是一個(gè)泛型接口,,并且它有一個(gè)抽象方法compareTo,這時(shí)候面紗就將要一層一層的揭開了
compareTo方法中這一大段畫,看到第一行我們就明白了,這東西可以幫我們解決數(shù)組對(duì)象的比較問題,所以我們要拿Student這個(gè)類去實(shí)現(xiàn)Comparable,并且實(shí)現(xiàn)compareTo方法,就能做到對(duì)象數(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); } }
這時(shí)候Arrays.sort()就可以幫我們做到對(duì)象數(shù)組的排序了,在 sort 方法中會(huì)自動(dòng)調(diào)用 compareTo 方法. compareTo 的參數(shù)是 Object , 其實(shí)傳入的就是 Student 類型的對(duì)象.
然后比較當(dāng)前對(duì)象和參數(shù)對(duì)象的大小關(guān)系 (例如按score ).
- 如果當(dāng)前對(duì)象應(yīng)排在參數(shù)對(duì)象之前 , 返回小于 0 的數(shù)字 ;
- 如果當(dāng)前對(duì)象應(yīng)排在參數(shù)對(duì)象之后 , 返回大于 0 的數(shù)字;
- 如果當(dāng)前對(duì)象和參數(shù)對(duì)象不分先后 , 返回 0;
如果你對(duì)姓名的比較存在疑惑,比如:為什么name也可以調(diào)用compareTo方法這種類似的問題,因?yàn)閚ame是String類型的,我建議你先看一下String的源碼,里面也實(shí)現(xiàn)了Comparable接口,以及重寫了compareTo方法,這里就不詳細(xì)介紹了,感興趣的小伙伴們可以嘗試一下哦,當(dāng)然,我相信你們都是大佬,一看就懂哈哈
執(zhí)行程序,看運(yùn)行結(jié)果,這下就能達(dá)到我們想要的效果了
前面說了,這樣的代碼也不是最好的,存在局限性,我按分?jǐn)?shù)來排序,那代碼就寫死了 ,那我以后想按姓名來排序,我又得回頭改???,以后進(jìn)公司了,你的代碼做改變,影響其他人的代碼,那不得把你罵死,所以我們需要做進(jìn)一步改進(jìn),引入Comparator接口
2.Comparator比較器
這個(gè)接口又叫比較器,那比較器又是個(gè)什么東西呢???下面我也是老套路啦,一步一步揭開這東西的面紗
為了解決Comparable接口的局限性,我們這個(gè)比較器完美的展現(xiàn)了實(shí)現(xiàn)效果,它也是一個(gè)泛型接口,同樣只有一個(gè)抽象方法需要重寫,下面看代碼:
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)比較器是真真正正的做到了,想按什么排序就按什么排序,在對(duì)類的侵入性以及代碼耦合度方面也算是不用太過擔(dān)心了
3.Clonable接口和深拷貝
Java 中內(nèi)置了一些很有用的接口 , Clonable 就是其中之一 .
Object 類中存在一個(gè) clone 方法 , 調(diào)用這個(gè)方法可以創(chuàng)建一個(gè)對(duì)象的 " 拷貝 ". 但是要想合法調(diào)用 clone 方法 , 必須要先實(shí)現(xiàn) Clonable 接口 , 否則就會(huì)拋 CloneNotSupportedException 異常.
先來看一段代碼吧,我個(gè)人喜歡結(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(); } } }
實(shí)現(xiàn)克隆的兩個(gè)條件(結(jié)合上面代碼):
1.這個(gè)對(duì)象可以被克隆,也就是這個(gè)Student類要實(shí)現(xiàn)這個(gè)Clonable接口
2.要在這個(gè)類中重寫父類Object的clone()方法
我們現(xiàn)在的代碼只是達(dá)到了這樣一個(gè)效果,,重頭戲還在后邊,因?yàn)槲覀兊目截愑袝r(shí)候遠(yuǎn)遠(yuǎn)不止于此,這種只能算是一個(gè)淺拷貝,那什么才算是深拷貝呢??? 請(qǐng)看下面的代碼:
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(); //為了代碼的好看,我這里不處理這個(gè)異常 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ǔ)上添加了一個(gè)Money類,并且實(shí)例化Money的對(duì)象作為Student的成員,這時(shí)候克隆之后,改變我money的值,會(huì)是一個(gè)什么的效果呢???
先看運(yùn)行結(jié)果吧,我就不兜圈子了
我們發(fā)現(xiàn),這并不是我們想要的結(jié)果,我們想要的結(jié)果是,通過student2去改變money的值,它并不會(huì)影響student1中的money,而我們剛剛的代碼并沒有做到,它的效果圖如下:
那又如何做到深拷貝呢??這時(shí)我們就需要對(duì)我們剛才的代碼做一些改進(jìn)了
我們只需將原來的Student類中的Object的克隆方法改成下面這份代碼就能做到我們想要的效果:
@Override protected Object clone() throws CloneNotSupportedException { Student tmp = (Student) super.clone();//將id變量克隆一份,tmp指向它 tmp.m = (Money) this.m.clone();//將m對(duì)象中的money變量克隆一份 tmp中的m指向它 return tmp; //return super.clone(); }
看到這個(gè)代碼先不要慌,如果注釋沒看明白,咱還有板書可以參照
其實(shí)分析起來也就那么回事
當(dāng)我們tmp返回的時(shí)候,就把0x99給到了student2,克隆完成之后,tmp是局部變量,也就被回收了,,,就變成了下面這副摸樣:
這個(gè)時(shí)候,我們?cè)偃ネㄟ^student2去改變我們的money,就不會(huì)影響student1中的money的值了,廢話不多說,運(yùn)行結(jié)果為證
以上就是三個(gè)重要接口的全部分析了,下次再見!??!
到此這篇關(guān)于深入分析Comparable與Comparator及Clonable三個(gè)Java接口的文章就介紹到這了,更多相關(guān)Java Comparable Comparator Clonable內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
在Java中輕松將HTML格式文本轉(zhuǎn)換為純文本的方法示例(保留換行)
這篇文章主要介紹了在Java中輕松將HTML格式文本轉(zhuǎn)換為純文本的方法示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04Java中ShardingSphere 數(shù)據(jù)分片的實(shí)現(xiàn)
其實(shí)很多人對(duì)分庫分表多少都有點(diǎn)恐懼,我們今天用ShardingSphere 給大家演示數(shù)據(jù)分片,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09關(guān)于JwtToken使用-重點(diǎn)看一下過期時(shí)間
這篇文章主要介紹了關(guān)于JwtToken使用-重點(diǎn)看一下過期時(shí)間,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07java容器類知識(shí)點(diǎn)詳細(xì)總結(jié)
這篇文章主要介紹了java容器類知識(shí)點(diǎn)詳細(xì)總結(jié),2019-06-06詳談Java枚舉、靜態(tài)導(dǎo)入、自動(dòng)拆裝箱、增強(qiáng)for循環(huán)、可變參數(shù)
下面小編就為大家?guī)硪黄斦凧ava枚舉、靜態(tài)導(dǎo)入、自動(dòng)拆裝箱、增強(qiáng)for循環(huán)、可變參數(shù)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-08-08springboot模塊里面調(diào)用另外一個(gè)模塊的方法實(shí)現(xiàn)
在Spring-Boot項(xiàng)目開發(fā)中,存在著本模塊的代碼需要訪問外面模塊接口,本文就來介紹一下springboot模塊里面調(diào)用另外一個(gè)模塊的方法實(shí)現(xiàn),感興趣的可以了解一下2023-11-11Java策略模式實(shí)現(xiàn)簡(jiǎn)單購(gòu)物車功能
這篇文章主要介紹了Java策略模式實(shí)現(xiàn)簡(jiǎn)單地購(gòu)物車,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08java、spring、springboot中整合Redis的詳細(xì)講解
這篇文章主要介紹了java、spring、springboot中整合Redis的詳細(xì)講解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04SpringCloud組件OpenFeign之?dāng)r截器解讀
這篇文章主要介紹了SpringCloud組件OpenFeign之?dāng)r截器用法。具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04