可能是把 Java 接口講得最通俗的一篇文章

對于面向?qū)ο缶幊虂碚f,抽象是一個極具魅力的特征肥惭。如果一個程序員的抽象思維很差,那他在編程中就會遇到很多困難紊搪,無法把業(yè)務(wù)變成具體的代碼蜜葱。在 Java 中,可以通過兩種形式來達到抽象的目的耀石,一種是抽象類牵囤,另外一種就是接口。

如果你現(xiàn)在就想知道抽象類與接口之間的區(qū)別,我可以提前給你說一個:

一個類只能繼承一個抽象類揭鳞,但卻可以實現(xiàn)多個接口炕贵。

當然了,在沒有搞清楚接口到底是什么野崇,它可以做什么之前称开,這個區(qū)別理解起來會有點難度。

01乓梨、接口是什么

接口是通過 interface 關(guān)鍵字定義的鳖轰,它可以包含一些常量和方法,來看下面這個示例扶镀。

public?interfaceElectronic{

????//?常量

????String?LED?=?"LED";

????//?抽象方法

????intgetElectricityUse();

????//?靜態(tài)方法

????staticbooleanisEnergyEfficient(String?electtronicType){

????????return?electtronicType.equals(LED);

????}

????//?默認方法

????defaultvoidprintDescription(){

????????System.out.println("電子");

????}

}

1)接口中定義的變量會在編譯的時候自動加上public static final修飾符蕴侣,也就是說 LED 變量其實是一個常量。

Java 官方文檔上有這樣的聲明:

Every field declaration in the body of an interface is implicitly public, static, and final.

換句話說臭觉,接口可以用來作為常量類使用昆雀,還能省略掉public static final,看似不錯的一種選擇胧谈,對吧忆肾?

不過,這種選擇并不可取菱肖。因為接口的本意是對方法進行抽象客冈,而常量接口會對子類中的變量造成命名空間上的“污染”。

2)沒有使用private稳强、default或者static關(guān)鍵字修飾的方法是隱式抽象的场仲,在編譯的時候會自動加上public abstract修飾符。也就是說getElectricityUse()其實是一個抽象方法退疫,沒有方法體——這是定義接口的本意渠缕。

3)從 Java 8 開始,接口中允許有靜態(tài)方法褒繁,比如說isEnergyEfficient()方法亦鳞。

靜態(tài)方法無法由(實現(xiàn)了該接口的)類的對象調(diào)用,它只能通過接口的名字來調(diào)用棒坏,比如說Electronic.isEnergyEfficient("LED")燕差。

接口中定義靜態(tài)方法的目的是為了提供一種簡單的機制,使我們不必創(chuàng)建對象就能調(diào)用方法坝冕,從而提高接口的競爭力徒探。

4)接口中允許定義default方法也是從 Java 8 開始的,比如說printDescription()喂窟,它始終由一個代碼塊組成测暗,為實現(xiàn)該接口而不覆蓋該方法的類提供默認實現(xiàn)央串,也就是說,無法直接使用一個“;”號來結(jié)束默認方法——編譯器會報錯的碗啄。


允許在接口中定義默認方法的理由是很充分的质和,因為一個接口可能有多個實現(xiàn)類,這些類就必須實現(xiàn)接口中定義的抽象類挫掏,否則編譯器就會報錯侦另。假如我們需要在所有的實現(xiàn)類中追加某個具體的方法秩命,在沒有default方法的幫助下尉共,我們就必須挨個對實現(xiàn)類進行修改。

來看一下 Electronic 接口反編譯后的字節(jié)碼吧弃锐,你會發(fā)現(xiàn)袄友,接口中定義的所有變量或者方法,都會自動添加上public關(guān)鍵字——假如你想知道編譯器在背后都默默做了哪些輔助霹菊,記住反編譯字節(jié)碼就對了剧蚣。

public?interfaceElectronic

{

????publicabstractintgetElectricityUse();

????publicstaticbooleanisEnergyEfficient(String?electtronicType)

{

????????return?electtronicType.equals("LED");

????}

????publicvoidprintDescription()

{

????????System.out.println("\u7535\u5B50");

????}

????public?static?final?String?LED?=?"LED";

}

有些讀者可能會問,“二哥旋廷,為什么我反編譯后的字節(jié)碼和你的不一樣鸠按,你用了什么反編譯工具?”其實沒有什么秘密饶碘,微信搜「沉默王二」回復(fù)關(guān)鍵字「JAD」就可以免費獲取了目尖,超級好用。

02扎运、定義接口的注意事項

由之前的例子我們就可以得出下面這些結(jié)論:

接口中允許定義變量

接口中允許定義抽象方法

接口中允許定義靜態(tài)方法(Java 8 之后)

接口中允許定義默認方法(Java 8 之后)

除此之外瑟曲,我們還應(yīng)該知道:

1)接口不允許直接實例化。


需要定義一個類去實現(xiàn)接口豪治,然后再實例化洞拨。

public?classComputerimplementsElectronic{

????publicstaticvoidmain(String[]?args){

????????new?Computer();

????}

????@Override

????publicintgetElectricityUse(){

????????return?0;

????}

}

2)接口可以是空的,既不定義變量负拟,也不定義方法烦衣。

public?interfaceSerializable{

}

Serializable 是最典型的一個空的接口,我之前分享過一篇文章《Java Serializable:明明就一個空的接口嘛》掩浙,感興趣的讀者可以去我的個人博客看一看花吟,你就明白了空接口的意義。


http://www.itwanger.com/java/2019/11/14/java-serializable.html

3)不要在定義接口的時候使用 final 關(guān)鍵字涣脚,否則會報編譯錯誤示辈,因為接口就是為了讓子類實現(xiàn)的,而 final 阻止了這種行為遣蚀。


4)接口的抽象方法不能是 private矾麻、protected 或者 final纱耻。


5)接口的變量是隱式?public static final,所以其值無法改變险耀。

03弄喘、接口可以做什么

1)使某些實現(xiàn)類具有我們想要的功能,比如說甩牺,實現(xiàn)了 Cloneable 接口的類具有拷貝的功能蘑志,實現(xiàn)了 Comparable 或者 Comparator 的類具有比較功能。

Cloneable 和 Serializable 一樣贬派,都屬于標記型接口急但,它們內(nèi)部都是空的。實現(xiàn)了 Cloneable 接口的類可以使用Object.clone()方法搞乏,否則會拋出 CloneNotSupportedException波桩。

public?classCloneableTestimplementsCloneable{

????@Override

????protectedObjectclone()throwsCloneNotSupportedException{

????????return?super.clone();

????}

????publicstaticvoidmain(String[]?args)throwsCloneNotSupportedException{

????????CloneableTest?c1?=?new?CloneableTest();

????????CloneableTest?c2?=?(CloneableTest)?c1.clone();

????}

}

運行后沒有報錯。現(xiàn)在把?implements Cloneable?去掉请敦。

public?classCloneableTest{

????@Override

????protectedObjectclone()throwsCloneNotSupportedException{

????????return?super.clone();

????}

????publicstaticvoidmain(String[]?args)throwsCloneNotSupportedException{

????????CloneableTest?c1?=?new?CloneableTest();

????????CloneableTest?c2?=?(CloneableTest)?c1.clone();

????}

}

運行后拋出 CloneNotSupportedException:

Exception?in?thread?"main"?java.lang.CloneNotSupportedException:?com.cmower.baeldung.interface1.CloneableTest

????at?java.base/java.lang.Object.clone(Native?Method)

????at?com.cmower.baeldung.interface1.CloneableTest.clone(CloneableTest.java:6)

????at?com.cmower.baeldung.interface1.CloneableTest.main(CloneableTest.java:11)

至于 Comparable 和 Comparator 的用法镐躲,感興趣的讀者可以參照我之前寫的另外一篇文章《來吧,一文徹底搞懂Java中的Comparable和Comparator》侍筛。


2)Java 原則上只支持單一繼承萤皂,但通過接口可以實現(xiàn)多重繼承的目的。

可能有些讀者會問匣椰,“二哥裆熙,為什么 Java 只支持單一繼承?”簡單來解釋一下窝爪。

如果有兩個類共同繼承(extends)一個有特定方法的父類弛车,那么該方法會被兩個子類重寫。然后蒲每,如果你決定同時繼承這兩個子類纷跛,那么在你調(diào)用該重寫方法時,編譯器不能識別你要調(diào)用哪個子類的方法邀杏。這也正是著名的菱形問題贫奠,見下圖。


ClassC 同時繼承了 ClassA 和 ClassB望蜡,ClassC 的對象在調(diào)用 ClassA 和 ClassB 中重載的方法時唤崭,就不知道該調(diào)用 ClassA 的方法,還是 ClassB 的方法脖律。

接口沒有這方面的困擾谢肾。來定義兩個接口,F(xiàn)ly 會飛小泉,Run 會跑芦疏。

public?interfaceFly{

????voidfly();

}

public?interfaceRun{

????voidrun();

}

然后讓一個類同時實現(xiàn)這兩個接口冕杠。

public?classPigimplementsFly,Run{

????@Override

????publicvoidfly(){

????????System.out.println("會飛的豬");

????}

????@Override

????publicvoidrun(){

????????System.out.println("會跑的豬");

????}

}

這就在某種形式上達到了多重繼承的目的:現(xiàn)實世界里,豬的確只會跑酸茴,但在雷軍的眼里分预,站在風(fēng)口的豬就會飛,這就需要賦予這只豬更多的能力薪捍,通過抽象類是無法實現(xiàn)的笼痹,只能通過接口。

3)實現(xiàn)多態(tài)酪穿。

什么是多態(tài)呢凳干?通俗的理解,就是同一個事件發(fā)生在不同的對象上會產(chǎn)生不同的結(jié)果昆稿,鼠標左鍵點擊窗口上的 X 號可以關(guān)閉窗口纺座,點擊超鏈接卻可以打開新的網(wǎng)頁息拜。

多態(tài)可以通過繼承(extends)的關(guān)系實現(xiàn)溉潭,也可以通過接口的形式實現(xiàn)。來看這樣一個例子少欺。

Shape 是表示一個形狀喳瓣。

public?interfaceShape{

????Stringname();

}

圓是一個形狀。

public?classCircleimplementsShape{

????@Override

????publicStringname(){

????????return?"圓";

????}

}

正方形也是一個形狀赞别。

public?classSquareimplementsShape{

????@Override

????publicStringname(){

????????return?"正方形";

????}

}

然后來看測試類畏陕。

List<Shape>?shapes?=?new?ArrayList<>();

Shape?circleShape?=?new?Circle();

Shape?squareShape?=?new?Square();

shapes.add(circleShape);

shapes.add(squareShape);

for?(Shape?shape?:?shapes)?{

????System.out.println(shape.name());

}

多態(tài)的存在 3 個前提:

1、要有繼承關(guān)系仿滔,Circle 和 Square 都實現(xiàn)了 Shape 接口

2惠毁、子類要重寫父類的方法,Circle 和 Square 都重寫了name()方法

3崎页、父類引用指向子類對象鞠绰,circleShape 和 squareShape 的類型都為 Shape,但前者指向的是 Circle 對象飒焦,后者指向的是 Square 對象蜈膨。

然后,我們來看一下測試結(jié)果:


正方形

也就意味著牺荠,盡管在 for 循環(huán)中翁巍,shape 的類型都為 Shape,但在調(diào)用?name()?方法的時候休雌,它知道 Circle 對象應(yīng)該調(diào)用 Circle 類的?name()?方法灶壶,Square 對象應(yīng)該調(diào)用 Square 類的?name()?方法。


04杈曲、接口與抽象類的區(qū)別

好了驰凛,關(guān)于接口的一切孝情,你應(yīng)該都搞清楚了。現(xiàn)在回到讀者春夏秋冬的那條留言洒嗤,“兄弟箫荡,說說抽象類和接口之間的區(qū)別?”

1)語法層面上

接口中不能有 public 和 protected 修飾的方法渔隶,抽象類中可以有羔挡。

接口中的變量只能是隱式的常量,抽象類中可以有任意類型的變量间唉。

一個類只能繼承一個抽象類绞灼,但卻可以實現(xiàn)多個接口。

2)設(shè)計層面上

抽象類是對類的一種抽象呈野,繼承抽象類的類和抽象類本身是一種is-a的關(guān)系低矮。

接口是對類的某種行為的一種抽象,接口和類之間并沒有很強的關(guān)聯(lián)關(guān)系被冒,所有的類都可以實現(xiàn)Serializable接口军掂,從而具有序列化的功能。

就這么多吧昨悼,能說道這份上蝗锥,我相信面試官就不會為難你了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末率触,一起剝皮案震驚了整個濱河市终议,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌葱蝗,老刑警劉巖穴张,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異两曼,居然都是意外死亡皂甘,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進店門合愈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來叮贩,“玉大人,你說我怎么就攤上這事佛析∫胬希” “怎么了?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵寸莫,是天一觀的道長捺萌。 經(jīng)常有香客問我,道長膘茎,這世上最難降的妖魔是什么桃纯? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任酷誓,我火速辦了婚禮,結(jié)果婚禮上态坦,老公的妹妹穿的比我還像新娘盐数。我一直安慰自己,他們只是感情好伞梯,可當我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布玫氢。 她就那樣靜靜地躺著,像睡著了一般谜诫。 火紅的嫁衣襯著肌膚如雪漾峡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天喻旷,我揣著相機與錄音生逸,去河邊找鬼。 笑死且预,一個胖子當著我的面吹牛槽袄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播辣之,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼掰伸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了怀估?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤合搅,失蹤者是張志新(化名)和其女友劉穎多搀,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體灾部,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡康铭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了赌髓。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片从藤。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖锁蠕,靈堂內(nèi)的尸體忽然破棺而出夷野,到底是詐尸還是另有隱情,我是刑警寧澤荣倾,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布悯搔,位于F島的核電站,受9級特大地震影響舌仍,放射性物質(zhì)發(fā)生泄漏妒貌。R本人自食惡果不足惜通危,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望灌曙。 院中可真熱鬧菊碟,春花似錦、人聲如沸在刺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽增炭。三九已至忍燥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間隙姿,已是汗流浹背梅垄。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留输玷,地道東北人队丝。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像欲鹏,于是被迫代替她去往敵國和親机久。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內(nèi)容