溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Java 抽象類和接口,看這一篇就夠了

發(fā)布時(shí)間:2020-07-20 18:16:32 來(lái)源:網(wǎng)絡(luò) 閱讀:92 作者:沉默王二 欄目:編程語(yǔ)言

本篇來(lái)談?wù)?Java 的抽象類和接口。

曹操在《短歌行》中為杜康酒打過(guò)一個(gè)價(jià)值一億個(gè)億的廣告——“何以解憂,唯有杜康”,我替曹操感到惋惜的是他本人并不會(huì)收到這筆不菲的代言費(fèi)。想一想,要是三國(guó)時(shí)期的明星人物們有這個(gè)代言意識(shí)的話,保證各家的軍費(fèi)收入會(huì)多出來(lái)一個(gè)重量級(jí)的來(lái)源。

不過(guò),酒真的能解憂嗎?我不大敢相信。李白就曾質(zhì)疑過(guò):“舉杯消愁愁更愁,抽刀斷水水更流。”我和李白持相同的觀點(diǎn),酒啊,真的不容易解憂,但絕對(duì)可以增加作者莫名的寫作沖動(dòng)。

我在寫本篇之前就小酌了一杯,一不小心激發(fā)了我強(qiáng)烈的創(chuàng)作欲望。不過(guò)我要奉勸各位,寒冬之際,如果遇到煩心事,千萬(wàn)別肆意地追求一醉方休,萬(wàn)事要懂得適可而止

01、 抽象類

一種比較蒼白的說(shuō)法是:在 Java 中,通過(guò)關(guān)鍵字 abstract 定義的類叫做抽象類。Java 是一門面向?qū)ο蟮恼Z(yǔ)言,因此所有的對(duì)象都是通過(guò)類來(lái)描述的;但反過(guò)來(lái),并不是所有的類都是用來(lái)描述對(duì)象的,抽象類就是其中的一種。

以下示例展示了一個(gè)簡(jiǎn)單的抽象類:

//?個(gè)人認(rèn)為,一名教練必須攻守兼?zhèn)?/span>
abstract?class?Coach?{
????public?abstract?void?defend();

????public?abstract?void?attack();
}

在一個(gè)抽象類中,至少有一個(gè)抽象方法(通過(guò)關(guān)鍵字 abstract 定義的方法,并且沒(méi)有方法體,如上例中的 defend() 方法和 attack() 方法),否則就沒(méi)有必要稱之為抽象類。需要注意的是,抽象類是不能實(shí)例化的! 它需要被一個(gè)子類繼承,就像以下示例那樣。

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();
????}
}

我們都知道,一個(gè)好的教練,必須攻守兼?zhèn)?,但每個(gè)教練的進(jìn)攻理念和防守理念不盡相同。因此,我在教練這個(gè)抽象類(Coach)中定義兩個(gè)抽象方法,一個(gè)進(jìn)攻(attack)一個(gè)防守(defend),這兩個(gè)方法的具體實(shí)現(xiàn)都要由抽象類的子類確定,抽象類本身并不負(fù)責(zé)。

我們也都知道,何塞·穆里尼奧是足球界的頂級(jí)教練。他是我最愛(ài)的足球教練,沒(méi)有之一。盡管他在曼聯(lián)的失敗有他自身的原因,但我依然崇拜他,因?yàn)椋骸罢?qǐng)不要說(shuō)我傲慢,因?yàn)槲抑皇菍?shí)話實(shí)說(shuō),我是歐洲冠軍,因此我并非籍籍無(wú)名,而是特殊的一個(gè)!”他是固執(zhí)的反控球主義者,堅(jiān)信控球是把雙刃劍,防守贏得冠軍。

好了,對(duì)于抽象類我們簡(jiǎn)單總結(jié)一下:

1、抽象類不能被實(shí)例化。
2、抽象類應(yīng)該至少有一個(gè)抽象方法,否則它沒(méi)有任何意義。
3、抽象類中的抽象方法沒(méi)有方法體。
4、抽象類的子類必須給出父類中的抽象方法的具體實(shí)現(xiàn),除非該子類也是抽象類。

02、 接口

我們知道,有抽象方法的類被稱為抽象類,也就意味著抽象類中還能有不是抽象方法的方法。這樣的類就不能算作純粹的接口,盡管它也可以提供接口的功能——只能說(shuō)抽象類是普通類與接口之間的一種中庸之道。

接口(英文:Interface),在 Java 中是一個(gè)抽象類型,是抽象方法的集合;接口通過(guò)關(guān)鍵字 interface 來(lái)定義。接口與抽象類的不同之處在于:

1、抽象類可以有方法體的方法,但接口沒(méi)有。
2、接口中的成員變量隱式為 static final,但抽象類不是的。
3、一個(gè)類可以實(shí)現(xiàn)多個(gè)接口,但只能繼承一個(gè)抽象類。

以下示例展示了一個(gè)簡(jiǎn)單的接口:

//?隱式的abstract
interface?Coach?{
????//?隱式的public
????void?defend();
????void?attack();
}

接口是隱式抽象的,所以聲明時(shí)沒(méi)有必要使用 abstract 關(guān)鍵字;接口的每個(gè)方法都是隱式抽象的,所以同樣不需要使用 abstract 關(guān)鍵字;接口中的方法都是隱式 public 的。

和抽象類一樣,接口也不能直接被實(shí)例化,它需要一個(gè)類來(lái)實(shí)現(xiàn)它,就像以下示例展示那樣。

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();
????}
}

實(shí)現(xiàn)一個(gè)接口需要用到關(guān)鍵字 implements,它表示:“我這個(gè)類遵從了接口的協(xié)議,如果你想使用我,看接口就行了,具體實(shí)現(xiàn)不用關(guān)心?!?/p>

03、 實(shí)現(xiàn)多個(gè)接口

在現(xiàn)實(shí)生活中,何塞·穆里尼奧不止是一名足球教練,他還是一個(gè)值得被尊重的英雄——憑借自身的努力,他從一名籍籍無(wú)名的跟班翻譯,逐漸蛻變?yōu)橐幻矣鲬魰缘捻敿?jí)教練。

如果要在程序的世界里體現(xiàn)何塞·穆里尼奧的多重角色,就可以使用接口,就像以下示例展示那樣。

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("只要一息尚存,就應(yīng)該戰(zhàn)斗到最后");
????}
}

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);
????}
}

可以看到,創(chuàng)建的 Hesai 對(duì)象可以向上轉(zhuǎn)型為 Coach 和 Hero,然后調(diào)用各自接口中實(shí)現(xiàn)的具體方法,因?yàn)?Hesai 這個(gè)類同時(shí)實(shí)現(xiàn)了兩個(gè)接口,分別是 Coach 和 Hero(class Hesai implements Coach, Hero,接口之間通過(guò)英文逗號(hào)隔開(kāi))。

04、 接口在應(yīng)用中常見(jiàn)的三種模式

在編程領(lǐng)域,好的設(shè)計(jì)模式能夠讓我們的代碼事半功倍。在使用接口的時(shí)候,經(jīng)常會(huì)用到三種模式,分別是策略模式、適配器模式和工廠模式。

1)策略模式

策略模式的思想是,針對(duì)一組算法,將每一種算法封裝到具有共同接口的實(shí)現(xiàn)類中,接口的設(shè)計(jì)者可以在不影響調(diào)用者的情況下對(duì)算法做出改變。示例如下:

//?接口:教練
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("進(jìn)攻就是最好的防守");
????}
}

public?class?Demo?{
????//?參數(shù)為接口
????public?static?void?defend(Coach?coach)?{
????????coach.defend();
????}

????public?static?void?main(String[]?args)?{
????????//?為同一個(gè)方法傳遞不同的對(duì)象
????????defend(new?Hesai());
????????defend(new?Guatu());
????}
}

Demo.defend() 方法可以接受不同風(fēng)格的 Coach,并根據(jù)所傳遞的參數(shù)對(duì)象的不同而產(chǎn)生不同的行為,這被稱為“策略模式”。

2)適配器模式

適配器模式的思想是,針對(duì)調(diào)用者的需求對(duì)原有的接口進(jìn)行轉(zhuǎn)接。生活當(dāng)中最常見(jiàn)的適配器就是HDMI(英語(yǔ):High Definition Multimedia Interface,中文:高清多媒體接口)線,可以同時(shí)發(fā)送音頻和視頻信號(hào)。適配器模式的示例如下:

interface?Coach?{
????void?defend();
????void?attack();
}

//?抽象類實(shí)現(xiàn)接口,并置空方法
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 接口中定義了兩個(gè)方法(defend()attack()),如果類直接實(shí)現(xiàn)該接口的話,就需要對(duì)兩個(gè)方法進(jìn)行實(shí)現(xiàn)。

如果我們只需要對(duì)其中一個(gè)方法進(jìn)行實(shí)現(xiàn)的話,就可以使用一個(gè)抽象類作為中間件,即適配器(AdapterCoach),用這個(gè)抽象類實(shí)現(xiàn)接口,并對(duì)抽象類中的方法置空(方法體只有一對(duì)花括號(hào)),這時(shí)候,新類就可以繞過(guò)接口,繼承抽象類,我們就可以只對(duì)需要的方法進(jìn)行覆蓋,而不是接口中的所有方法。

3)工廠模式

所謂的工廠模式理解起來(lái)也不難,就是什么工廠生產(chǎn)什么,比如說(shuō)寶馬工廠生產(chǎn)寶馬,奔馳工廠生產(chǎn)奔馳,A 級(jí)學(xué)院畢業(yè) A 級(jí)教練,C 級(jí)學(xué)院畢業(yè) C 級(jí)教練。示例如下:

//?教練
interface?Coach?{
????void?command();
}

//?教練學(xué)院
interface?CoachFactory?{
????Coach?createCoach();
}

//?A級(jí)教練
class?ACoach?implements?Coach?{

????@Override
????public?void?command()?{
????????System.out.println("我是A級(jí)證書教練");
????}

}

//?A級(jí)教練學(xué)院
class?ACoachFactory?implements?CoachFactory?{

????@Override
????public?Coach?createCoach()?{
????????return?new?ACoach();
????}

}

//?C級(jí)教練
class?CCoach?implements?Coach?{

????@Override
????public?void?command()?{
????????System.out.println("我是C級(jí)證書教練");
????}

}

//?C級(jí)教練學(xué)院
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)?{
????????//?對(duì)于一支球隊(duì)來(lái)說(shuō),需要什么樣的教練就去找什么樣的學(xué)院
????????//?學(xué)院會(huì)介紹球隊(duì)對(duì)應(yīng)水平的教練。
????????create(new?ACoachFactory());
????????create(new?CCoachFactory());
????}
}

有兩個(gè)接口,一個(gè)是 Coach(教練),可以 command()(指揮球隊(duì));另外一個(gè)是 CoachFactory(教練學(xué)院),能 createCoach()(教出一名優(yōu)秀的教練)。然后 ACoach 類實(shí)現(xiàn) Coach 接口,ACoachFactory 類實(shí)現(xiàn) CoachFactory 接口;CCoach 類實(shí)現(xiàn) Coach 接口,CCoachFactory 類實(shí)現(xiàn) CoachFactory 接口。當(dāng)需要 A 級(jí)教練時(shí),就去找 A 級(jí)教練學(xué)院;當(dāng)需要 C 級(jí)教練時(shí),就去找 C 級(jí)教練學(xué)院。

依次類推,我們還可以用 BCoach 類實(shí)現(xiàn) Coach 接口,BCoachFactory 類實(shí)現(xiàn) CoachFactory 接口,從而不斷地豐富教練的梯隊(duì)。

05、 總結(jié)

盡管接口使得抽象更進(jìn)一步,但任何抽象性都應(yīng)該根據(jù)真正的需求而產(chǎn)生,因此恰當(dāng)?shù)脑瓌t是優(yōu)先選擇類而不是接口,只有在真正需要接口的時(shí)候再重構(gòu)代碼。


上一篇:Java:多態(tài)乃幸福本源

下一篇:Java內(nèi)部類真的那么難以理解?

微信搜索「沉默王二」公眾號(hào),關(guān)注后回復(fù)「免費(fèi)視頻」獲取 500G Java 高質(zhì)量教學(xué)視頻(已分門別類)。


向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI