前面介紹了抽象方法及抽象類的用法,看似解決了不肯定行爲的方法定義,既然叫喚動做容許聲明爲抽象方法,那麼飛翔、游泳也能聲明爲抽象方法,而且雞類涵蓋的物種不夠多,最好把這些行爲動做擴展到鳥類這個羣體,因而整個鳥類的成員方法均可以如法炮製了。但是這種作法也帶來了一些弊端,包括但不限於:
一、能飛的動物不只僅是鳥類,還有昆蟲、蝙蝠等其它動物也能飛,難不成昆蟲類、哺乳動物類也要自行聲明飛翔方法?這麼作顯然產生了重複的方法定義。否則的話,要是把飛翔方法挪到更底層的動物類,一大羣動物爲了避免淪爲抽象類都得重寫飛翔方法,好比鱷魚、大象等根本不會飛的動物也要裝模做樣撲騰幾下,實在是滑天下之大稽。
二、除了幾種常見的鳥類爲大衆所熟知以外,大部分鳥類其實人們一時半刻叫不出它們的名字,假若在路上偶遇一隻鳥兒,難道由於不認識它就無法描述它的模樣了嗎?(若是鳥類是個抽象類,外部是不能建立鳥類實例的)
三、就算給整個動物類都添加了叫喚、飛翔、游泳這些抽象方法,而且費盡九牛二虎之力把全部派生而來的子類都實現了這三個抽象方法,也不意味着萬事大吉。譬如青蛙擅長跳躍這個動做,哪天程序員突發奇想要給抽象的動物類補充跳躍方法,從而支持青蛙的跳躍行爲,隨之而來的代價即是讓動物類的全部子類都重寫跳躍方法,這樣也太傷筋動骨了。
綜上所述,抽象類解決不了層出不窮的問題,遠非什麼靈丹妙藥,只能用於處理符合條件的特定要求。若想真正有效應對這些刁鑽古怪的挑戰,還得期望新的抽象技術,在Java編程中這就是接口。接口不從屬於類,而是與類平級,類經過關鍵字class標識,而接口經過關鍵字interface來標識。因爲接口是做爲類的輔助角色出現,所以它在結構上與類比較類似,不過也有很多不一樣之處,舉例以下:
一、凡是類都有構造方法,即使是抽象類也支持定義構造方法,但接口不容許定義構造方法,由於接口只用於聲明某些行爲動做,自己並不是一個實體。
二、在Java8之前,接口內部的全部方法都必須是抽象方法,具體的方法內部代碼有賴於該接口的實現類來補充。由於有這個強制規定,因此接口內部方法的abstract前綴可加可不加,即便不加abstract,編譯器也會默認把該方法看成抽象方法。
三、至於接口內部的屬性,則默認爲終態屬性,即添加了final前綴的成員屬性。固然這個final前綴也是可加可不加的,即便不加final,編譯器仍會默認把該屬性看成終態屬性。
按照上述的接口規定,再來編寫一個定義了動物行爲的接口代碼,其中主要包括飛翔方法、游泳方法、奔跑方法等,詳細的接口定義代碼示例以下:html
//定義一個接口。接口主要聲明一些特定的行爲方法 public interface Behavior { // 聲明瞭一個抽象的飛翔方法。注意,接口內部的方法默認都是抽象方法,因此能夠不用添加abstract前綴 public void fly(); //abstract public void fly(); // 這裏的abstract可加可不加 // 聲明瞭一個抽象的游泳方法 public void swim(); // 聲明瞭一個抽象的奔跑方法 public void run(); // 接口內部的屬性默認都是終態屬性,因此能夠不用添加final前綴 public String TAG = "動物世界"; //public final String TAG = "動物世界"; // 這裏的final可加可不加 // 接口不容許定義構造方法。在Java8之前,接口內部的全部方法都必須是抽象方法 }
接着定義一個鵝類,它不但繼承自Bird鳥類,並且實現了新的行爲接口Behavior。注意子類繼承父類的格式爲「extends 父類名」,實現某個接口的格式則爲「implements 接口名」,同時該類還要重寫接口裏的全部抽象方法。因而實現了行爲接口的鵝類代碼以下所示:java
//定義一個實現了接口Behavior的鵝類。注意鵝類須要實現Behavior接口的全部抽象方法 public class Goose extends Bird implements Behavior { public Goose(String name, int sexType) { super(name, sexType); } // 實現了接口的fly方法 @Override public void fly() { System.out.println("鵝飛不高,也飛不遠。"); } // 實現了接口的swim方法 @Override public void swim() { System.out.println("鵝,鵝,鵝,曲項向天歌。白毛浮綠水,紅掌撥清波。"); } // 實現了接口的run方法 @Override public void run() { System.out.println("檻外蕭聲輕盪漾,沙間鵝步滿蹣跚。"); } }
對於外部來講,這個鵝類跟通常的類沒啥區別,鵝類所實現的接口方法,在外部看來都是鵝類的成員方法,原來怎麼調用如今依然怎麼調用。下面是外部使用鵝類的代碼例子:程序員
// 演示簡單接口的實現類用法 private static void testSimple() { Goose goose = new Goose("家鵝", 0); goose.fly(); // 實現了接口的fly方法 goose.swim(); // 實現了接口的swim方法 goose.run(); // 實現了接口的run方法 }
接口與類相比還有一個重大區別,在Java體系之中,每一個類最多隻能繼承一個父類,不能同時繼承多個類,也就是不容許多重繼承。而接口不存在這方面的限制,某個類能夠只實現一個接口,也能夠同時實現兩個接口、三個接口等等,待實現的接口名稱之間以逗號分隔。例如除了飛翔、游泳、奔跑這三種動做以外,有些動物還擅長跳躍,好比青蛙、袋鼠等等,假若在現有的Behavior接口中增長跳躍方法jump,那麼包括Goose在內全部實現了Behavior的類都要重寫jump方法,顯然改造量巨大。如今藉助接口的多重實現特性,徹底能夠另外定義新的行爲接口Behavior2,在新接口中聲明跳躍方法,那麼只有實現Behavior2接口的類才須要重寫jump方法。按此思路單獨定義的新接口Behavior2代碼見下:編程
//定義另外一個行爲接口 public interface Behavior2 { // 聲明瞭一個抽象的跳躍方法 public void jump(); }
而後編寫Frog蛙類的定義代碼,這個蛙類同時實現了接口Behavior和Behavior2,這樣它要重寫Behavior的三個方法,以及Behavior2的跳躍方法。下面是一個蛙類代碼的簡單例子:ide
//定義一個實現了接口Behavior和Behavior2的蛙類。類只能繼承一個,但接口能夠實現多個 public class Frog implements Behavior, Behavior2 { // 實現了Behavior2接口的jump方法 @Override public void jump() { System.out.println("青蛙跳躍的技能叫作「蛙跳」"); } // 實現了Behavior接口的fly方法 @Override public void fly() { } // 實現了Behavior接口的swim方法 @Override public void swim() { System.out.println("青蛙游泳的技能叫作「蛙泳」"); } // 實現了Behavior接口的run方法 @Override public void run() { } }
因爲新增的jump方法屬於新接口Behavior2,不屬於原接口Behavior,所以實現了Behavior接口的鵝類代碼無需任何修改,只有實現Behavior2的蛙類代碼才需額外處理。固然這個特殊處理也僅限於蛙類的定義,對於外部而言,蛙類Frog還是一個普通的類,外部調用它並無什麼兩樣,具體的調用代碼示例以下:htm
// 演示某個類同時實現了多個接口 private static void testMultiple() { Frog frog = new Frog(); frog.swim(); // 實現了Behavior接口的swim方法 frog.jump(); // 實現了Behavior2接口的run方法 }
更多Java技術文章參見《Java開發筆記(序)章節目錄》blog