您好,登錄后才能下訂單哦!
本文小編為大家詳細介紹“Java抽象類和接口如何使用”,內容詳細,步驟清晰,細節處理妥當,希望這篇“Java抽象類和接口如何使用”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。
什么是抽象類呢?抽象類顧名思義就是很抽象,就是當我們沒有足夠的信息去描述這個類的時候我們就可以先不用描述,這樣的類就是抽象類。
用代碼舉個例子:
class Shape { public void draw() { System.out.println("我要畫圖形!!"); } } class circle extends Shape { @Override public void draw() { System.out.println("我要畫圓形!!!"); } } class rect extends Shape { @Override public void draw() { System.out.println("我要畫矩形!!!"); } } public class TestDemo3 { public static void drawShape(Shape shape) { shape.draw(); } public static void main(String[] args) { drawShape(new circle()); drawShape(new rect()); } }
正好也通過這個小小的案例來復習一下多態,我們創建一個Shape父類,然后又創建兩個子類分別為 rect類和circle類 然后利用這兩個類使用同一個方法,產生的形態是不一樣的這就是多態,但是
我們可以發現我只用了這兩個子類來畫圖形分別是圓形和矩形,我并沒有調用父類來實現這個方法,為啥呢???就是因為這個父類并不能畫出圖形,那這個父類的draw方法的實現豈不是沒有用,我只需要它父類要有這方法,通過子類重寫就可以了,不需要父類的方法有具體的實現。
沒錯,這就引入到我們今天的主題抽象類
上面父類的draw方法由于它的具體實現根本沒有用處,所以我們就可以不寫,這也正符合抽象類的定義,當不能有足夠的信息去描述它,那我們就把它叫做抽象類。
上面父類的draw方法沒有具體的實現我們就可以把它叫做抽象方法。利用abstract來修飾
public abstract void draw();
但是這樣為啥會報錯呢??
原因是被abstract修飾的方法叫做抽象方法,如果一個類里面有抽象方法,那這個類一定是抽象類,所以類也要被abstract來修飾。
但是相反,抽象類里面一定有抽象方法么???
編譯器并不會報錯,所以答案是不一定,抽象類里面可以有抽象方法也可以沒有抽象方法。
抽象類能否被實例化呢???
所以,抽象類是不能實例化的。
抽象類能否像普通類一樣定義成員變量和方法,構造方法呢???
抽象類與普通類的區別就是在與抽象類不能實例化,其他定義成員變量,成員方法,構造方法等等都是一樣的。
既然抽象類不能實例化,那他存在的意義是什么呢???
老鐵思考的有道理,其實抽象類最大的意義就是被繼承,因為抽象類不能實例化對象,只能依靠子類來重寫父類(就是抽象類)的方法來完成業務需求。
怎么繼承抽象類呢???有哪些注意的點呢???
這樣繼承為啥會出錯呢??原因是在子類繼承抽象類的時候,子類需要重寫父類的所有方法或者子類需要用abstract修飾(抽象類被抽象類繼承或者子類重寫抽象類(父類)的所有方法)。
這樣就正確了。
這里還要注意當我們沒有重寫父類的方法時而是用abstract來修飾,這時當你在次繼承這個子類的時候也就是這個子類變成了父類,下一個子類還是要繼續重寫這個父類的方法和這個父類的父類的方法。
抽象方法能否別static和final修飾呢???
抽象方法不能被static 和final 修飾,因為子類要重寫父類的方法,同時可以省略訪問修飾限定符,默認是public。
我們把不能足夠描述清楚一個對象的類叫做抽象類。
被abstract修飾的方法稱為抽象方法,被abstract修飾的類稱為抽象類
抽象方法必須在抽象類里面,也就是說,只要有抽象方法,類名也必須利用abstract來修飾,相反,抽象類里面可以沒有抽象方法,也可以有抽象方法。
抽象類不能實例化,除了不能實例化之外其他與普通類一樣可以定義成員變量,成員方法,構造方法等。同時構造方法和類方法(被static修飾的方法)不能被abstract來修飾
抽象方法的訪問修飾限定符不能是private,如果省略默認是public,同時抽象方法不能被final修飾。
子類繼承抽象類的時候,子類必須重寫抽象類的所有方法并且要有方法的具體實現,如果重寫那子類還是抽象類,必須用abstract來修飾。
抽象類中的方法沒有具體實現,要通過子類重寫在子類中實現。
一個類只能繼承一個抽象類
說到接口我們會想到什么呢???我一開始想到的就是充電接口插排等等,比如充電接口,只要符合那個插口的標準,我們都可以使用那個接口來進行充電。這就是把標準進行統一起來,然后大家就可以根據不同的標準來使用不同的接口,比如蘋果手機與安卓手機的手機接口就是不一樣的,他們就是兩種不同的標準,安卓手機用安卓的接口,蘋果用蘋果的接口這就將標準統一起來。
而在Java中也是一樣的,我們把這個標準或者可以說是一種公共的規范叫做接口,只要符合這一接口的標準我們就可以使用它。
聽著這抽象的概念你可能現在還是不太明白這到底是什么,我在來拿代碼舉個例子:
class Animal { public String name; public int age; public void eat() { System.out.println(this.name+"吃飯***!"); } }
這里我定義了一個動物類,這個動物有名字,年齡,還有吃飯的行為。
class Animal { public String name; public int age; public void eat() { System.out.println(this.name+"吃飯***!"); } public void swim() { System.out.println("我要游泳~~~~"); } } class Fish extends Animal { }
接著我又定義了一個魚類(子類)繼承這個動物類(父類),我想讓這個魚有這個游泳的行為,但是這就會有一個問題,在父類定義了一個swim方法,接著我還要定義很多類,難道所有的類都要有這個游泳的行為么???答案是不可以的。所以我們就不能把這個swim方法定義在父類里面,那我們就可以把這個方法定義在子類(魚類)里面,這樣就符合了,但是如果我還要定義1000個動物都會游泳難道都要在自己類中寫這個游泳的方法么??這樣做顯然是不可行的。那我們該怎么做呢??
我們可以就提供一個公共的接口,這也是一種標準,只要符合這一標準都可以使用這個接口或者可以說可以實現這一功能。
看了上面的引例我們應該接口是干什么的了,接下來我們來學一下接口的語法。
定義接口要使用interface關鍵字:
//創建一個接口 //創建接口要是用interface關鍵字 接口的命名最好是形容詞其他的也可以 interface IFlying{ //這就是一個flying接口 }
接口中的成員變量:
這樣會報錯,接口中的成員變量都是常量,所以必須初始化,接口中的成員變量會被隱式指定為public static final 修飾的。
interface IFlying{ //接口中的成員變量默認都是被public static final修飾的常量 //這里的成員變量不可改變 public static final int a =10; }
接口中的成員方法:
interface IFlying{ //接口中的成員方法都是抽象方法,默認是public abstract //其中public abstract 可以被省略 //接口中的成員方法不能有具體的實現 public abstract void eat(); //一般就寫成: //void eat(); default void sleep(){ //接口中的方法想要具體實現,要加上default修飾 } //接口中可以有靜態方法的具體實現 public static void method() { System.out.println("我是靜態的方法!!"); } }
接口能否實例化???
接口是不能被實例化的。
怎么使用接口???
實現的接口如下:
interface IFlying{ void eat(); } interface IRunning{ void run(); } interface ISwimming{ void swim(); } interface IClimbing{ void climb(); }
//創建一個Ant類 //利用implements關鍵字來實現接口 //一個類可以實現多個接口,接口之間利用逗號連接 //實現了接口必須要在接口中重寫接口中的方法 //重寫方法快捷鍵:鼠標移動到implements關鍵字上然后 alt+enter class Ant implements IClimbing,IRunning{ public String name; @Override public void run() { System.out.println(this.name+"要跑步"); } @Override public void climb() { System.out.println(this.name+"爬山"); } }
接口的使用時利用implements關鍵字與類連接,類與接口之間使用implements連接的。
一個類可以實現多個接口,多個接口之間利用逗號連接。
class Ant implements IClimbing,IRunning.的意思是類Ant可以實現兩個功能,既可以爬又可以跑。
類實現接口時,必須要重寫接口中的方法。如果不重寫該類還是抽象類,要用abstract來修飾。
接口能否有靜態方法和代碼塊呢???
接口中是不能有靜態代碼塊和構造方法的。
我們這里總結一下類與接口之間的聯系
類與類之間是繼承關系利用extends來連接 代表子類繼承了父類
類與接口之間是利用implements來連接, 代表類能實現某個功能
接口與接口之間也可以進行聯系,利用extends 接口A和接口B interface A enxtends B 代表接口A拓展了接口B的功能。
這里來講一下接口與接口之間的繼承
我們利用extends關鍵字將兩個接口連接起來,這樣就實現了接口之間的繼承。
例如:
interface IRunning extends IFlying{ //類IRunning拓展了IFlying的功能 void run(); //接口與接口之間繼承后IRunning拓展了IFlying功能 //有了IRunning的功能的類也要重寫IFlying這個方法 }
類IRunning拓展了IFlying的功能
接口與接口之間繼承后IRunning拓展了IFlying功能
有了IRunning的功能的類也要重寫IFlying這個方法
我們這里舉一個例子:
比如我們要進行給一個學生進行排序,我們之前學過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[] student = new Student[4]; student[0] = new Student("張三",18,88); student[1] = new Student("李四",98,98); student[2] = new Student("王二麻子",8,18); student[3] = new Student("趙老八",58,38); System.out.println(Arrays.toString(student)); Arrays.sort(student); System.out.println(Arrays.toString(student)); } }
從這里發現Arrays.sort方法是比較具體的數字大小的,而我們這里比較學生這個對象并沒有指定,我們究竟依靠什么來比較這個學生對象,目前這個學生有名字,分數,年齡,我們到底依靠什么比較是根據我們的需求來定,那我們要具體的比較學生對象的某一個學生怎么比較呢·???看報錯信息也就是這個異常,我們需要提供這個comparable這個接口然后重寫這個comparable方法。
怎樣提供接口????
我們根據類要實現一個接口是利用關鍵字implements來連接的。
然后使用comparable這個接口,尖括號里面寫上你要排序的類。
好這樣我們就實現了這個接口,當然看前面那個紅線就知道會有報錯,這也就是當我們實現一個接口我們必須重寫這個接口中的方法,然后Alt+enter重寫這個接口中的方法。
好,我們就重寫了這樣的一個方法,比如我們要比較年齡按照升序排列:
然后調用Arrays.sort方法就可以進行排序了。
同樣我們還可以根據名字排序:
由于,名字是String類型也就是引用類型所以我們要調用compareTo方法來進行比較。
同樣的我們還可以根據分數來排序這個學生對象。
我們剛才使用comparable這個接口會有一個缺陷,就比如當我們已經實現按照年齡排序好了,但是有個人突然把他改成了名字比較,如果是未來做項目開發的時候,那就會給程序猿造成很大的困擾,就怕有一天別人修改了那段代碼,所以我們就有了這個comparator這個比較器,我們還是把他封裝起來,不用在去學生這個類中去修改。
對學生年齡排序:
class AgeComparator implements Comparator<Student>{ @Override public int compare(Student o1, Student o2) { return o1.age - o2.age; } }
對學生分數排序:
class ScoreComparator implements Comparator<Student>{ @Override public int compare(Student o1, Student o2) { return (int)(o1.score - o2.score); } }
對學生名字排序:
class NameComparator implements Comparator<Student>{ @Override public int compare(Student o1, Student o2) { return o1.name.compareTo(o2.name); } }
我們還是利用Arrays.sort方法里面再加一個比較器的這個參數就可以比較了。
public static void main(String[] args) { Student[] student = new Student[4]; student[0] = new Student("張三",18,88); student[1] = new Student("李四",98,98); student[2] = new Student("王二麻子",8,18); student[3] = new Student("趙老八",58,38); System.out.println(Arrays.toString(student)); AgeComparator ageComparator = new AgeComparator(); Arrays.sort(student,ageComparator); System.out.println(Arrays.toString(student)); }
這樣我們將根據什么排序,實例化對應的對象,通過對象調用重寫comparator的方法就可以進行比較,不需要擔心類中被修改。
我們之前學過數組中的克隆方法,就是把一個數組中的內容全部拷貝到另外一個數組中去。
今天我們學的cloneable接口可以將一個對象的屬性拷貝到另外一個對象里面去。
我們創建一個人這個類,人這個類中有兩個屬性,一個是分數,一個是smartphone這個對象(引用類型)。
class SmartPhone{ public int money = 9999; } class Person{ public int score = 96; SmartPhone smartPhone = new SmartPhone(); } public class TestDemo { public static void main(String[] args){ Person person1 = new Person(); } }
我們現在要將這個person這個類實現cloneable接口,利用implements連接,同樣的我們要重寫這個cloneable這個接口中的方法。
這里我們要注意·重寫cloneable這個方法,它的返回類型是object,object是Java中所有類的父類。
好,完成了接口的操作,我們接下來完成克隆的工作,我們怎么使用clone這個方法來進行克隆呢???
//淺拷貝 class SmartPhone{ public int money = 9999; } class Person implements Cloneable{ public int score = 96; SmartPhone smartPhone = new SmartPhone(); @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "Person{" + "score=" + score + ", smartPhone=" + smartPhone + '}'; } } public class TestDemo { public static void main(String[] args) throws CloneNotSupportedException { Person person1 = new Person(); Person person2 = (Person)person1.clone(); System.out.println(person1.smartPhone.money); System.out.println(person2.smartPhone.money); person1.smartPhone.money=9; System.out.println("===============拷貝之后================="); System.out.println(person1.smartPhone.money); System.out.println(person2.smartPhone.money); } }
原因是什么呢??畫一下圖理解一下
這就是淺拷貝,對基本數據類型進行值傳遞,對引用數據類型進行引用傳遞般的拷貝,此
為淺拷貝。只要將money值進行修改兩個對象的money的值都改變,沒有徹底的拷貝。
//深拷貝 class SmartPhone implements Cloneable{ public int money = 9999; @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "SmartPhone{" + "money=" + money + '}'; } } class Person implements Cloneable{ public int score = 96; SmartPhone smartPhone = new SmartPhone(); @Override protected Object clone() throws CloneNotSupportedException { Person tmp = (Person)super.clone(); tmp.smartPhone= (SmartPhone) this.smartPhone.clone(); return tmp; } @Override public String toString() { return "Person{" + "score=" + score + ", smartPhone=" + smartPhone + '}'; } } public class TestDemo { public static void main(String[] args) throws CloneNotSupportedException { Person person1 = new Person(); Person person2 = (Person)person1.clone(); System.out.println(person1.smartPhone.money); System.out.println(person2.smartPhone.money); person1.smartPhone.money=9; System.out.println("===============拷貝之后================="); System.out.println(person1.smartPhone.money); System.out.println(person2.smartPhone.money); } }
此時為深拷貝,進行了徹底的拷貝。對基本數據類型進行值傳遞,對引用數據類型,創建一個新的對象,并復制其內容,此為深拷貝。
讀到這里,這篇“Java抽象類和接口如何使用”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。