本篇來談談 Java 的抽象類和接口。html
曹操在《短歌行》中爲杜康酒打過一個價值一億個億的廣告——「何以解憂,惟有杜康」,我替曹操感到可惜的是他本人並不會收到這筆不菲的代言費。想想,要是三國時期的明星人物們有這個代言意識的話,保證各家的軍費收入會多出來一個重量級的來源。java
不過,酒真的能解憂嗎?我不大敢相信。李白就曾質疑過:「舉杯消愁愁更愁,抽刀斷水水更流。」我和李白持相同的觀點,酒啊,真的不容易解憂,但絕對能夠增長做者莫名的寫做衝動。算法
我在寫本篇以前就小酌了一杯,一不當心激發了我強烈的創做慾望。不過我要奉勸各位,寒冬之際,若是遇到煩心事,千萬別肆意地追求一醉方休,萬事要懂得適可而止。編程
一種比較蒼白的說法是:在 Java 中,經過關鍵字 abstract
定義的類叫作抽象類。Java 是一門面向對象的語言,所以全部的對象都是經過類來描述的;但反過來,並非全部的類都是用來描述對象的,抽象類就是其中的一種。設計模式
如下示例展現了一個簡單的抽象類:微信
// 我的認爲,一名教練必須攻守兼備
abstract class Coach {
public abstract void defend();
public abstract void attack();
}
在一個抽象類中,至少有一個抽象方法(經過關鍵字 abstract
定義的方法,而且沒有方法體,如上例中的 defend()
方法和 attack()
方法),不然就沒有必要稱之爲抽象類。須要注意的是,抽象類是不能實例化的! 它須要被一個子類繼承,就像如下示例那樣。ide
abstract class Coach {
public abstract void defend();
public abstract void attack();
}
class Hesai extends Coach {
@Override
public void defend() {
System.out.println("防守贏得冠軍");
}
@Override
public void attack() {
System.out.println("控球是把雙刃劍");
}
}
public class Demo {
public static void main(String[] args) {
Coach moliniao = new Hesai();
moliniao.defend();
moliniao.attack();
}
}
咱們都知道,一個好的教練,必須攻守兼備,但每一個教練的進攻理念和防守理念不盡相同。所以,我在教練這個抽象類(Coach)中定義兩個抽象方法,一個進攻(attack)一個防守(defend),這兩個方法的具體實現都要由抽象類的子類肯定,抽象類自己並不負責。spa
咱們也都知道,何塞·穆里尼奧是足球界的頂級教練。他是我最愛的足球教練,沒有之一。儘管他在曼聯的失敗有他自身的緣由,但我依然崇拜他,由於:「請不要說我傲慢,由於我只是實話實說,我是歐洲冠軍,所以我並不是籍籍無名,而是特殊的一個!」他是執拗的反控球主義者,堅信控球是把雙刃劍,防守贏得冠軍。翻譯
好了,對於抽象類咱們簡單總結一下:設計
一、抽象類不能被實例化。
二、抽象類應該至少有一個抽象方法,不然它沒有任何意義。
三、抽象類中的抽象方法沒有方法體。
四、抽象類的子類必須給出父類中的抽象方法的具體實現,除非該子類也是抽象類。
咱們知道,有抽象方法的類被稱爲抽象類,也就意味着抽象類中還能有不是抽象方法的方法。這樣的類就不能算做純粹的接口,儘管它也能夠提供接口的功能——只能說抽象類是普通類與接口之間的一種中庸之道。
接口(英文:Interface),在 Java 中是一個抽象類型,是抽象方法的集合;接口經過關鍵字 interface
來定義。接口與抽象類的不一樣之處在於:
一、抽象類能夠有方法體的方法,但接口沒有。
二、接口中的成員變量隱式爲 static final
,但抽象類不是的。
三、一個類能夠實現多個接口,但只能繼承一個抽象類。
如下示例展現了一個簡單的接口:
// 隱式的abstract
interface Coach {
// 隱式的public
void defend();
void attack();
}
接口是隱式抽象的,因此聲明時沒有必要使用 abstract
關鍵字;接口的每一個方法都是隱式抽象的,因此一樣不須要使用 abstract
關鍵字;接口中的方法都是隱式 public
的。
和抽象類同樣,接口也不能直接被實例化,它須要一個類來實現它,就像如下示例展現那樣。
class Hesai implements Coach {
@Override
public void defend() {
System.out.println("防守贏得冠軍");
}
@Override
public void attack() {
System.out.println("控球是把雙刃劍");
}
}
public class Demo2 {
public static void main(String[] args) {
Coach moliniao = new Hesai();
moliniao.defend();
moliniao.attack();
}
}
實現一個接口須要用到關鍵字 implements
,它表示:「我這個類聽從了接口的協議,若是你想使用我,看接口就好了,具體實現不用關心。」
在現實生活中,何塞·穆里尼奧不止是一名足球教練,他仍是一個值得被尊重的英雄——憑藉自身的努力,他從一名籍籍無名的跟班翻譯,逐漸蛻變爲一名家喻戶曉的頂級教練。
若是要在程序的世界裏體現何塞·穆里尼奧的多重角色,就可使用接口,就像如下示例展現那樣。
package com.cmower.java_demo.nine.inf;
interface Coach {
// 隱式的public
void defend();
void attack();
}
interface Hero {
void fight();
}
class Hesai implements Coach, Hero {
@Override
public void defend() {
System.out.println("防守贏得冠軍");
}
@Override
public void attack() {
System.out.println("控球是把雙刃劍");
}
@Override
public void fight() {
System.out.println("只要一息尚存,就應該戰鬥到最後");
}
}
public class Demo2 {
public static void defend(Coach coach) {
coach.defend();
}
public static void fight(Hero hero) {
hero.fight();
}
public static void main(String[] args) {
Hesai moliniao = new Hesai();
defend(moliniao);
fight(moliniao);
}
}
能夠看到,建立的 Hesai 對象能夠向上轉型爲 Coach 和 Hero,而後調用各自接口中實現的具體方法,由於 Hesai 這個類同時實現了兩個接口,分別是 Coach 和 Hero(class Hesai implements Coach, Hero
,接口之間經過英文逗號隔開)。
在編程領域,好的設計模式可以讓咱們的代碼事半功倍。在使用接口的時候,常常會用到三種模式,分別是策略模式、適配器模式和工廠模式。
1)策略模式
策略模式的思想是,針對一組算法,將每一種算法封裝到具備共同接口的實現類中,接口的設計者能夠在不影響調用者的狀況下對算法作出改變。示例以下:
// 接口:教練
interface Coach {
// 方法:防守
void defend();
}
// 何塞·穆里尼奧
class Hesai implements Coach {
@Override
public void defend() {
System.out.println("防守贏得冠軍");
}
}
// 德普·瓜迪奧拉
class Guatu implements Coach {
@Override
public void defend() {
System.out.println("進攻就是最好的防守");
}
}
public class Demo {
// 參數爲接口
public static void defend(Coach coach) {
coach.defend();
}
public static void main(String[] args) {
// 爲同一個方法傳遞不一樣的對象
defend(new Hesai());
defend(new Guatu());
}
}
Demo.defend()
方法能夠接受不一樣風格的 Coach,並根據所傳遞的參數對象的不一樣而產生不一樣的行爲,這被稱爲「策略模式」。
2)適配器模式
適配器模式的思想是,針對調用者的需求對原有的接口進行轉接。生活當中最多見的適配器就是HDMI(英語:High Definition Multimedia Interface
,中文:高清多媒體接口)線,能夠同時發送音頻和視頻信號。適配器模式的示例以下:
interface Coach {
void defend();
void attack();
}
// 抽象類實現接口,並置空方法
abstract class AdapterCoach implements Coach {
public void defend() {};
public void attack() {};
}
// 新類繼承適配器
class Hesai extends AdapterCoach {
public void defend() {
System.out.println("防守贏得冠軍");
}
}
public class Demo {
public static void main(String[] args) {
Coach coach = new Hesai();
coach.defend();
}
}
Coach 接口中定義了兩個方法(defend()
和 attack()
),若是類直接實現該接口的話,就須要對兩個方法進行實現。
若是咱們只須要對其中一個方法進行實現的話,就可使用一個抽象類做爲中間件,即適配器(AdapterCoach),用這個抽象類實現接口,並對抽象類中的方法置空(方法體只有一對花括號),這時候,新類就能夠繞過接口,繼承抽象類,咱們就能夠只對須要的方法進行覆蓋,而不是接口中的全部方法。
3)工廠模式
所謂的工廠模式理解起來也不難,就是什麼工廠生產什麼,好比說寶馬工廠生產寶馬,奔馳工廠生產奔馳,A 級學院畢業 A 級教練,C 級學院畢業 C 級教練。示例以下:
// 教練
interface Coach {
void command();
}
// 教練學院
interface CoachFactory {
Coach createCoach();
}
// A級教練
class ACoach implements Coach {
@Override
public void command() {
System.out.println("我是A級證書教練");
}
}
// A級教練學院
class ACoachFactory implements CoachFactory {
@Override
public Coach createCoach() {
return new ACoach();
}
}
// C級教練
class CCoach implements Coach {
@Override
public void command() {
System.out.println("我是C級證書教練");
}
}
// C級教練學院
class CCoachFactory implements CoachFactory {
@Override
public Coach createCoach() {
return new CCoach();
}
}
public class Demo {
public static void create(CoachFactory factory) {
factory.createCoach().command();
}
public static void main(String[] args) {
// 對於一支球隊來講,須要什麼樣的教練就去找什麼樣的學院
// 學院會介紹球隊對應水平的教練。
create(new ACoachFactory());
create(new CCoachFactory());
}
}
有兩個接口,一個是 Coach(教練),能夠 command()
(指揮球隊);另一個是 CoachFactory(教練學院),能 createCoach()
(教出一名優秀的教練)。而後 ACoach 類實現 Coach 接口,ACoachFactory 類實現 CoachFactory 接口;CCoach 類實現 Coach 接口,CCoachFactory 類實現 CoachFactory 接口。當須要 A 級教練時,就去找 A 級教練學院;當須要 C 級教練時,就去找 C 級教練學院。
依次類推,咱們還能夠用 BCoach 類實現 Coach 接口,BCoachFactory 類實現 CoachFactory 接口,從而不斷地豐富教練的梯隊。
儘管接口使得抽象更進一步,但任何抽象性都應該根據真正的需求而產生,所以恰當的原則是優先選擇類而不是接口,只有在真正須要接口的時候再重構代碼。
上一篇:Java:多態乃幸福本源
下一篇:Java內部類真的那麼難以理解?
微信搜索「沉默王二」公衆號,關注後回覆「免費視頻」獲取 500G Java 高質量教學視頻(已分門別類)。