??前面的話??
本篇文章帶大家認識Java常用的介面——Comparable,Comparator,Cloneable介面,其中Comparable,Comparator介面是比較器,可以用來對物件進行排序,Cloneable介面是克隆介面,用來生成一個物件的副本,
📒博客主頁:未見花聞的博客主頁
🎉歡迎關注🔎點贊👍收藏??留言📝
📌本文由未見花聞原創,CSDN首發!
📆首發時間:🌴2022年1月2日🌴
??堅持和努力一定能換來詩與遠方!
💭參考書籍:📚《Java核心技術》,📚《Java編程思想》,📚《Effective Java》
💬參考在線編程網站:🌐牛客網🌐力扣
博主的碼云gitee,平常博主寫的程式代碼都在里面,
博主的github,平常博主寫的程式代碼都在里面,
🙏作者水平很有限,如果發現錯誤,一定要及時告知作者哦!感謝感謝!
📌導航小助手📌
- 1.Comparable介面
- 1.1引子:陣列排序
- 1.2Camparable介面的使用
- 2.Comparator比較器
- 3.Cloneable克隆介面
- 3.1Cloneable的使用
- 3.2深拷貝與淺拷貝

1.Comparable介面
1.1引子:陣列排序
我們知道Arrays類中的·sort方法可以對陣列進行排序,比如對整型陣列進行排序:
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
int[] arr = {21,3,6,1,42,13,18,85,68};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
}
}
顯然我們會得到一個從小到大排好序的陣列,但是實際中往往需要對自定義型別的陣列進行排序,比如我定義一個Person類:
class Person {
public int age;
public String name;
public int score;
public Person(int age, String name,int score) {
this.age = age;
this.name = name;
this.score = score;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
", score=" + score +
'}';
}
}
我們用同樣的方法來排序試一試:

報錯了,因為編譯器不知道你需要根據物件中哪個元素來進行排序,這時候就需要用到Comparable這個介面來實作對自定義型別的排序,其實有一點像C語言中的qsort函式的用法,
1.2Camparable介面的使用
首先我們來看看呼叫sort方法時發生例外處的原始碼:

我們發現Person進行排序時需要強轉Comparable并且呼叫compareTo方法,這就意味著我們需要對Person類實作Comparable介面,并重寫該介面中的compareTo方法,
public interface Comparable<T> {
public int compareTo(T o);
}
我們發現Comparable介面中只有一個方法compareTo方法,還有在介面后面多了一個<T>,這個是泛型的意思,由于泛型概念及其地抽象,晦澀難懂,這里知道他是泛型即可,它相當于方法的引數一樣,可以理解為對一個型別進行傳參,在實作Comparable介面時,把型別當做方法引數一樣使用<>傳入就可以了,像這樣:
class Person implements Comparable<Person>{
public int age;
public String name;
public int score;
public Person(int age, String name, int score) {
this.age = age;
this.name = name;
this.score = score;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
", score=" + score +
'}';
}
@Override
public int compareTo(Person o) {
return this.age - o.age;
}
}
泛型會在后續博文中由淺到深多次介紹,這里知道簡單地使用即可,
對于其中的compareTo方法,誰呼叫它誰就是this,如果回傳的值大于0,表示this大于o,等于0,兩者相等,小于0表示this小于o,如果需要降序排列,將this與o的位置換一換即可,
@Override //升序
public int compareTo(Person o) {
return this.age - o.age;
}
@Override //降序
public int compareTo(Person o) {
return this.age - o.age;
}
如果要進行字串或者其他類的比較,需要呼叫類中所提供的compareTo方法進行比較,比如字串:
@Override
public int compareTo(Person o) {
return this.name.compareTo(o.name);
}
同理,降序將this與o反過來,
實作了Comparable介面,我們再來嘗試一下能不能排序成功,
根據年齡age進行排序:
import java.util.Arrays;
class Person implements Comparable<Person>{
public int age;
public String name;
public int score;
public Person(int age, String name, int score) {
this.age = age;
this.name = name;
this.score = score;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
", score=" + score +
'}';
}
@Override
public int compareTo(Person o) {
return this.age - o.age;
}
}
public class Test1 {
public static void main(String[] args) {
Person[] people = new Person[3];
people[0] = new Person(32, "001", 92);
people[1] = new Person(18, "002", 78);
people[2] = new Person(12, "003", 90);
Arrays.sort(people);
System.out.println(Arrays.toString(people));
}
}

再來試一試姓名name排序:
import java.util.Arrays;
class Person implements Comparable<Person>{
public int age;
public String name;
public int score;
public Person(int age, String name, int score) {
this.age = age;
this.name = name;
this.score = score;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
", score=" + score +
'}';
}
@Override
public int compareTo(Person o) {
return this.name.compareTo(o.name);
}
}
public class Test1 {
public static void main(String[] args) {
Person[] people = new Person[3];
people[0] = new Person(32, "zhang", 92);
people[1] = new Person(18, "wang", 78);
people[2] = new Person(12, "chen", 90);
Arrays.sort(people);
System.out.println(Arrays.toString(people));
}
}

好了,上面這些就是Comparable介面的用法,可見該介面有個很大的缺點,那就是對類的侵入性極強,不小心改了compareTo方法后,程式出現bug還不報錯,很難排查,因此實際當中常常使用比較器Comparator,下面就來聊一聊Comparator,
2.Comparator比較器
在Arrays類中實作了許多的sort方法能夠對不同的資料型別進行比較,使用Comparator介面就能實作這種形式,你需要哪一個就呼叫哪一個比較器,
public interface Comparator<T> {
int compare(T o1, T o2);
...
}
只要重寫該compare方法就可以實作資料型別大小的比較,
class AgeComparator implements Comparator<Person> {
@Override
public int compare(Person o1, Person o2) {
return o1.age - o2.age;
}
}
class NameComparator implements Comparator<Person> {
@Override
public int compare(Person o1, Person o2) {
return o1.name.compareTo(o2.name);
}
}
class ScoreComparator implements Comparator<Person> {
@Override
public int compare(Person o1, Person o2) {
return o1.score - o2.score;
}
}
如果需要降序排列,同樣的道理,將o1與o2交換一下位置就好了,那怎么用呢?很簡單,實體化比較器物件后,在sort方法中多加一個比較器物件就可以了,因為在原始碼中有這樣一個sort的多載方法:
public static <T> void sort(T[] a, Comparator<? super T> c) {
if (c == null) {
sort(a);
} else {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, c);
else
TimSort.sort(a, 0, a.length, c, null, 0, 0);
}
}
不用看懂該方法的實作方式,只需要知道該方法的第二個引數是比較器就可以了,下面就來實踐看看,
public class Test {
public static void main(String[] args) {
Person[] people = new Person[3];
people[0] = new Person(32, "wang", 92);
people[1] = new Person(18, "zhang", 78);
people[2] = new Person(12, "chen", 90);
AgeComparator ageCmp = new AgeComparator();
NameComparator nameCmp = new NameComparator();
ScoreComparator scoreCmp = new ScoreComparator();
Arrays.sort(people, ageCmp);
System.out.println("按年齡排序:");
System.out.println(Arrays.toString(people));
Arrays.sort(people, nameCmp);
System.out.println("按姓名排序:");
System.out.println(Arrays.toString(people));
Arrays.sort(people, scoreCmp);
System.out.println("按分數排序:");
System.out.println(Arrays.toString(people));
}
}

沒有問題,成功排序了,Comparator比起Comparable,對類的侵入性就非常的小了,而且更加的直觀,這兩個介面是Java當中很常見的比較器工具,在后續資料結構的實作肯定會再次見到他們,最后再來說一說物件的拷貝吧,Cloneable介面是實作拷貝最常用的工具,
3.Cloneable克隆介面
3.1Cloneable的使用
首先,該介面的作用能夠幫助生成一個物件的副本,也就是拷貝一個物件,其實其拷貝作用的是clone方法,該介面的作用其實是標志一個物件能夠被克隆(拷貝),為什么這么說呢?我們去看看這個介面的原始碼你就知道了,嘻嘻!
public interface Cloneable {
}
我的天啊,它竟然是一個空介面,面試的時候常常會問空介面的作用是什么?空介面的作用是起到一個標志的作用,比如這個Cloneable介面,它是一個空介面,物件實作該介面,這個物件(類)就被標記了,表示該物件(類)能夠被克隆,
我們再來看一看真正起作用的clone方法:
protected native Object clone() throws CloneNotSupportedException;
該方法被關鍵字native修飾,說明它是使用C++代碼實作的,并且使用它的時候需要接收例外,
按理說,既然Cloneable介面是一個空介面,實作該介面時不需要重寫clone方法,在這里,有點特殊,雖然該介面是空的,也需要實作clone方法,實作方式很簡單,呼叫Object(該類是所有類的父類)類中的clone方法即可,
class Person implements Cloneable {
int age;
public Person(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Test {
public static void main(String[] args) {
Person p1 = new Person(18);
try {
Person p2 = (Person) p1.clone();
System.out.println(p1);
System.out.println(p2);
}catch (CloneNotSupportedException e) {
e.printStackTrace();
System.out.println("克隆例外!");
}
}
}

拷貝成功了,上面這些就是Cloneable介面的使用方法,
3.2深拷貝與淺拷貝
深拷貝:當拷貝一個物件時,會新生成一個一模一樣的物件,并且如果物件里面還有其他物件,也會新生成一個一模一樣的物件,這就是深拷貝,
淺拷貝:當拷貝一個物件時,會新生成一個一模一樣的物件,但是如果該物件里面還有其他物件,只會拷貝這個物件的地址,并不會新生成一個物件,
首先深淺拷貝是對代碼實作方面來說的,方法的拷貝并沒有深淺拷貝這一個說法,如果硬是要有個說法,Java中給出的拷貝方法都是淺拷貝,雖然對于簡單資料型別拷貝可以說是深拷貝,是因為拷貝簡單資料型別并不能體現出來它到底是淺拷貝還是深拷貝,當去拷貝參考型別(比如陣列,字串)的時候,只是拷貝了物件的地址,并沒有重新生成一個物件,所以說單論方法的拷貝,可以理解為淺拷貝,
所以說,對于一個拷貝到底是深拷貝還是淺拷貝,在于程式員對代碼的實作,到底是一個深拷貝還是淺拷貝,
這個深淺拷貝就簡單說一下吧,就不舉例子了,如果有不懂的歡迎與博主交流~~,
??后面的話??
博主正在參加2021【博客之星】,小伙伴們能否給博主一個五星呢?投票地址:
https://bbs.csdn.net/topics/603955252
投我以桃,報之以李,對于每一個評價,小博主都定當相報!
最后,祝各位小伙伴新年快樂,心想事成,沒有煩惱!
下一篇文章是有關集合框架的哦!我們要開始資料結構之旅了!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/402727.html
標籤:java
上一篇:Java小題精煉訓練營(篇三)
