面向对象编程(十五)——抽象类和接口
一、抽象(abstract)類
抽象類,說白了就是包含抽象方法的類。那什么是抽象方法?抽象方法是一種特殊的方法:抽象方法只有聲明,而沒有具體的實現。抽象方法說白了就是只有方法的聲明,沒有方法體。
抽象方法必須用abstract關鍵字進行修飾。如果一個類含有抽象方法,則稱這個類為抽象類,抽象類必須在類前用abstract關鍵字修飾。因為抽象類中含有無具體實現的方法,所以不能用抽象類創建對象。
上圖Cat必須實現抽象方法run(),如果不實現,編譯通不過。可以選擇實現run()方法或者定義Cat為抽象類。
完整截圖:
1. 為什么需要抽象類?如何定義抽象類?
- 是一種模版模式。抽象類為所有子類提供了一個通用模板。子類可以在這個模版基礎上進行擴展。
- 通過抽象類,可以避免子類設計的隨意性。通過抽象類,我們可以做到嚴格限制子類的設計,使子類之間更加通用。
2. 抽象類要點
【示例】
下面通過一下的小程序深入理解抽象類
因此在類Animal里面只需要定義這個enjoy()方法就可以了,使用abstract關鍵字把enjoy()方法定義成一個抽象方法,定義如下:public?abstract?void?enjoy();?
從某種意義上來說,抽象方法就是被用來重寫的,所以在父類聲明的抽象方法一定要在子類里面重寫。如果真的不想在子類里面重寫這個方法,那么可以再在子類里面把這個方法再定義為抽象方法,因為子類覺得我去實現也不合適,應該讓繼承我的子類去實現比較合適,因此也可以在繼承這個子類的下一個子類里面重寫在父類里面聲明的抽象方法,這是可以的。
這里有一個規則:既然父類里面的方法是抽象的,那么對于整個類來說,它就有一個沒有實現的方法,這個方法不知道怎么去實現,那么這個類是就是殘缺不全的,因此這個類應該被定義為一個抽象類。所以前面這樣聲明的聲明的class?Animal應該要在class的前面加上abstract,即聲明成這樣:abstract?class?Animal,這樣Animal類就成了一個抽象類了。Animal類的最終定義代碼如下:
/*** 父類Animal* 在class的前面加上abstract,即聲明成這樣:abstract class Animal* 這樣Animal類就成了一個抽象類了*/ abstract class Animal {public String name;public Animal(String name) {this.name = name;}/*** 抽象方法* 這里只有方法的定義,沒有方法的實現。*/public abstract void enjoy(); }Java語言規定,當一個類里面有抽象方法的時候,這個類必須被聲明為抽象類。
子類繼承父類時,如果這個父類里面有抽象方法,并且子類覺得可以去實現父類的所有抽象方法,那么子類必須去實現父類的所有抽象方法,如:
1 /** 2 * 子類Dog繼承抽象類Animal,并且實現了抽象方法enjoy 3 * @author gacl 4 * 5 */ 6 class Dog extends Animal { 7 /** 8 * Dog類添加自己特有的屬性 9 */ 10 public String furColor; 11 12 public Dog(String n, String c) { 13 super(n);//調用父類Animal的構造方法 14 this.furColor = c; 15 } 16 17 @Override 18 public void enjoy() { 19 System.out.println("狗叫...."); 20 } 21 22 }這個父類里面的抽象方法,子類如果覺得實現不了,那么把就子類也聲明成一個抽象類,如:
1 /** 2 * 這里的子類Cat從抽象類Animal繼承下來,自然也繼承了Animal類里面聲明的抽象方法enjoy(), 3 * 但子類Cat覺得自己去實現這個enjoy()方法也不合適,因此它把它自己也聲明成一個抽象的類, 4 * 那么,誰去實現這個抽象的enjoy方法,誰繼承了子類,那誰就去實現這個抽象方法enjoy()。 5 * @author gacl 6 * 7 */ 8 abstract class Cat extends Animal { 9 10 /** 11 * Cat添加自己獨有的屬性 12 */ 13 public String eyeColor; 14 15 public Cat(String n, String c) { 16 super(n);//調用父類Animal的構造方法 17 this.eyeColor = c; 18 } 19 }這里的子類Cat從抽象類Animal繼承下來,自然也繼承了Animal類里面聲明的抽象方法enjoy(),但子類Cat覺得自己去實現這個enjoy()方法也不合適,因此它把它自己也聲明成一個抽象的類,那么,誰去實現這個抽象的enjoy方法,誰繼承了子類,那誰就去實現這個抽象方法enjoy()。如:
1 /** 2 * 子類BlueCat繼承抽象類Cat,并且實現了從父類Cat繼承下來的抽象方法enjoy 3 * @author gacl 4 * 5 */ 6 class BlueCat extends Cat { 7 8 public BlueCat(String n, String c) { 9 super(n, c); 10 } 11 12 /** 13 * 實現了抽象方法enjoy 14 */ 15 @Override 16 public void enjoy() { 17 System.out.println("藍貓叫..."); 18 } 19 20 }完整的測試代碼如下:
1 package javastudy.summary; 2 3 /** 4 * 父類Animal 5 * 在class的前面加上abstract,即聲明成這樣:abstract class Animal 6 * 這樣Animal類就成了一個抽象類了 7 */ 8 abstract class Animal { 9 10 public String name; 11 12 public Animal(String name) { 13 this.name = name; 14 } 15 16 /** 17 * 抽象方法 18 * 這里只有方法的定義,沒有方法的實現。 19 */ 20 public abstract void enjoy(); 21 22 } 23 24 /** 25 * 這里的子類Cat從抽象類Animal繼承下來,自然也繼承了Animal類里面聲明的抽象方法enjoy(), 26 * 但子類Cat覺得自己去實現這個enjoy()方法也不合適,因此它把它自己也聲明成一個抽象的類, 27 * 那么,誰去實現這個抽象的enjoy方法,誰繼承了子類,那誰就去實現這個抽象方法enjoy()。 28 * @author gacl 29 * 30 */ 31 abstract class Cat extends Animal { 32 33 /** 34 * Cat添加自己獨有的屬性 35 */ 36 public String eyeColor; 37 38 public Cat(String n, String c) { 39 super(n);//調用父類Animal的構造方法 40 this.eyeColor = c; 41 } 42 } 43 44 /** 45 * 子類BlueCat繼承抽象類Cat,并且實現了從父類Cat繼承下來的抽象方法enjoy 46 * @author gacl 47 * 48 */ 49 class BlueCat extends Cat { 50 51 public BlueCat(String n, String c) { 52 super(n, c); 53 } 54 55 /** 56 * 實現了抽象方法enjoy 57 */ 58 @Override 59 public void enjoy() { 60 System.out.println("藍貓叫..."); 61 } 62 63 } 64 65 /** 66 * 子類Dog繼承抽象類Animal,并且實現了抽象方法enjoy 67 * @author gacl 68 * 69 */ 70 class Dog extends Animal { 71 /** 72 * Dog類添加自己特有的屬性 73 */ 74 public String furColor; 75 76 public Dog(String n, String c) { 77 super(n);//調用父類Animal的構造方法 78 this.furColor = c; 79 } 80 81 @Override 82 public void enjoy() { 83 System.out.println("狗叫...."); 84 } 85 86 } 87 88 public class TestAbstract { 89 90 /** 91 * @param args 92 */ 93 public static void main(String[] args) { 94 95 /** 96 * 把Cat類聲明成一個抽象類以后,就不能再對Cat類進行實例化了, 97 * 因為抽象類是殘缺不全的,缺胳膊少腿的,因此抽象類不能被實例化。 98 */ 99 //Cat c = new Cat("Catname","blue"); 100 Dog d = new Dog("dogname","black"); 101 d.enjoy();//調用自己實現了的enjoy方法 102 103 BlueCat c = new BlueCat("BlueCatname","blue"); 104 c.enjoy();//調用自己實現了的enjoy方法 105 } 106 } View Code二、接口(interface)
接口,比抽象類還要抽象的類。
如:接口MyInterface
實現類:MyClass
1. 為什么需要接口?接口和抽象類的區別?
- 接口就是比“抽象類”還“抽象”的“抽象類”,可以更加規范的對子類進行約束。全面地專業地實現了“規范和具體實現的分離。
- 接口就是規范,定義的是一組規則,體現了現實世界中“如果你是……則必須能……”的思想。(如果你是天使,則必須能飛,如果你是汽車,則必須能跑……)
- 接口的本質是契約,就像我們人間的法律一樣,制定好后大家都遵守。
- 項目的具體需求多變,我們必須以不變應萬變才能從容開發,此處的不變就是規范。因此,我們開發項目往往都是面向接口編程!
2. 如何定義接口?
格式:
3. 接口要點
?JAVA是只支持單繼承的,但現實之中存在多重繼承這種現象,如“金絲猴是一種動物”,金絲猴從動物這個類繼承,同時“金絲猴是一種值錢的東西”,金絲猴從“值錢的東西”這個類繼承,同時“金絲猴是一種應該受到保護的東西”,金絲猴從“應該受到保護的東西”這個類繼承。這樣金絲猴可以同時從?“動物類”、“值錢的東西類”、“應該受到保護的東西”?這三個類繼承,但由于JAVA只支持單繼承,因此金絲猴只能從這三個類中的一個來繼承,不能同時繼承這三個類。因此為了封裝現實生活中存在的多重繼承現象,為了實現多繼承,可以把其中的兩個類封裝成接口。使用接口可以幫助我們實現多重繼承。
接口(interface)是一種特殊的抽象類,在這種抽象類里面,所有的方法都是抽象方法,并且這個抽象類的屬性(即成員變量)都是聲明成“public?static?final?類型?屬性名”這樣的,默認也是聲明成“public?static?final”即里面的成員變量都是公共的、靜態的,不能改變的。因此在接口里面聲明常量的時候,可以寫成“public?static?final?類型?常量名=value(值)”這樣的形式,也可以直接寫成“類型?常量名=value(值)”如:“public?static?final?int?id=10”可以直接寫成“int?id=10”這樣的形式,因為在接口里面默認的屬性聲明都是“public?static?final”的,因此“public?static?final”可以省略不寫。在接口里面聲明的抽象方法可以不寫abstract關鍵字來標識,因為接口里面所有的方法都是抽象的,因此這個“abstract”關鍵字默認都是省略掉的,如在一個接口里面聲明這樣的三個方法:“public?void?start()”、“public?void?run()”、“public?void?stop()”這三個方法前面都沒有使用abstract關鍵字來標識,可它們就是抽象方法,因為在接口里面的聲明的方法都是抽象方法,因此在接口里面的抽象方法都會把abstract關鍵字省略掉,因為默認聲明的方法都是抽象的,所以就沒有必要再寫“abstract”字了,這一點與在抽象類里面聲明抽象方法時有所區別,在抽象類里面聲明抽象方法是一定要使用“abstract”關鍵字的,而在接口里面聲明抽象方法可以省略掉“abstract”。注意:在接口里面聲明的抽象方法默認是“public(公共的)”的,也只能是“public(公共的)”之所以要這樣聲明是為了修正C++里面多重繼承的時候容易出現問題的地方,C++的多繼承容易出現問題,問題在于多繼承的多個父類之間如果他們有相同的成員變量的時候,這個引用起來會相當地麻煩,并且運行的時候會產生各種各樣的問題。JAVA為了修正這個問題,把接口里面所有的成員變量全都改成static?final,成員變量是static類型,那么這個成員變量就是屬于整個類里面的,而不是專屬于某個對象。對于多重繼承來說,在一個子類對象里面實際上包含有多個父類對象,而對于單繼承來說,子類對象里面就只有一個父類對象。多繼承子類對象就有多個父類對象,而這些父類對象之間可能又會存在有重復的成員變量,這就非常容易出現問題,因此在JAVA里面避免了這種問題的出現,采用了接口這種方式來實現多繼承。作為接口來說,一個類可以從接口繼承(或者叫實現接口),這也是多繼承,接口里面的成員變量不專屬于某個對象,都是靜態的成員變量,是屬于整個類的,因此一個類去實現多個接口也是無所謂的,不會存在對象之間互相沖突的問題。實現多個接口,也就實現了多重繼承,而且又避免了多重繼承容易出現問題的地方,這就是用接口實現多重繼承的好處。
?
?
| 參數 | 抽象類 | 接口 |
| 默認的方法實現 | 它可以有默認的方法實現 | 接口完全是抽象的。它根本不存在方法的實現 |
| 實現 | 子類使用extends關鍵字來繼承抽象類。如果子類不是抽象類的話,它需要提供抽象類中所有聲明的方法的實現。 | 子類使用關鍵字implements來實現接口。它需要提供接口中所有聲明的方法的實現 |
| 構造器 | 抽象類可以有構造器 | 接口不能有構造器 |
| 與正常Java類的區別 | 除了你不能實例化抽象類之外,它和普通Java類沒有任何區別 | 接口是完全不同的類型 |
| 訪問修飾符 | 抽象方法可以有public、protected和default這些修飾符 | 接口方法默認修飾符是public。你不可以使用其它修飾符。 |
| main方法 | 抽象方法可以有main方法并且我們可以運行它 | 接口沒有main方法,因此我們不能運行它。 |
| 多繼承 | 抽象方法可以繼承一個類和實現多個接口 | 接口只可以繼承一個或多個其它接口 |
| 速度 | 它比接口速度要快 | 接口是稍微有點慢的,因為它需要時間去尋找在類中實現的方法。 |
| 添加新方法 | 如果你往抽象類中添加新的方法,你可以給它提供默認的實現。因此你不需要改變你現在的代碼。 | 如果你往接口中添加方法,那么你必須改變實現該接口的類。 |
相關鏈接:
java基礎學習總結——抽象類
java基礎學習總結——接口
轉載于:https://www.cnblogs.com/Qian123/p/5190539.html
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的面向对象编程(十五)——抽象类和接口的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python | 四种运行其他程序的黑科
- 下一篇: EOS全球行南京站:降低用户及开发者门槛