Java 8 有多牛逼?打破一切你對接口的認知

2020-10-14 java網際網路架構

來源:https://mp.weixin.qq.com/s/Jy0zu5LcNM-EvZFJIY9Eeg

前段時間面試了一個 39 歲的程式設計師,結果不是很理想

最近也面試一些 Java 程式設計師,不乏工作 4、5 年經驗的,當我問他一些 Java 8 的新特性時,大多卻答不上來。

比如下面這道題:

棧長:接口裡面可以寫方法嗎?

小A:當然可以啊,默認就是抽象方法。

棧長:那接口裡面可以寫實現方法嗎?

小A:不可以,所有方法必須是抽象的。

棧長:你確定嗎?

小A:確定……

小A看起來對我的問題有點懷疑人生,心裡肯定估摸著,我不會在給他埋了什麼坑吧。然後他還是仔細再想了一下,最後還是斬釘截鐵的告訴我:接口裡面只能寫抽象方法,不能寫實現方法

棧長:接口裡面是可以寫實現方法的,Java 8 開始就可以了,你用過 Java 8 嗎?

小A:好吧,看來是我學藝不精,Java 8 有了解一點,比如那個 Lambda 表達式,但實際項目中也沒怎麼用。

通過和小A的交流,我也看到了許多開發者的問題,雖然開發版本用的是 Java 8,但實際用的還是 Java 8 之前的最基礎的語法,對 Java 8 新增的特性一無所知。

Java 8 至 2014 年發布至今,已經過了 6 個年頭了,最新的 Java 14 都發布了,OK,這個不在本篇討論範圍之內, Java 8+ 系列教程請關注公眾號Java技術棧回復 "java" 進行閱讀,本篇就是想順著問小A的這個問題展開。

什麼是默認方法和靜態方法?

上面也說了,Java 8 開始是可以有方法實現的,可以在接口中添加默認方法和靜態方法。

默認方法用 default 修飾,只能用在接口中,靜態方法用 static 修飾,這個我們不陌生了。並且接口中的默認方法、靜態方法可以同時有多個。

在接口中寫實現方法一點也不稀奇,像這樣的用法,從 Java 8 到 Java 14 已是遍地開花,到處都可以看到接口默認方法和靜態方法的身影。

比如我們來看下在 JDK API 中 java.util.Map 關於接口默認方法和靜態方法的應用。

/** 來源公眾號:Java技術棧 */public interface Map<K,V> {    ...        /**    * 接口默認方法    */    default boolean remove(Object key, Object value) {        Object curValue = get(key);        if (!Objects.equals(curValue, value) ||            (curValue == null && !containsKey(key))) {            return false;        }        remove(key);        return true;    }        ...        /**    * 接口靜態方法    */    public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() {        return (Comparator<Map.Entry<K, V>> & Serializable)            (c1, c2) -> c1.getKey().compareTo(c2.getKey());    }        ...    }    

為什麼要有接口默認方法?

舉一個很現實的例子:

我們的接口老早就寫好了,後面因為各種業務問題,避免不了要修改接口。

在 Java 8 之前,比如要在一個接口中添加一個抽象方法,那所有的接口實現類都要去實現這個方法,不然就會編譯錯誤,而某些實現類根本就不需要實現這個方法也被迫要寫一個空實現,改動會非常大。

所以,接口默認方法就是為了解決這個問題,只要在一個接口添加了一個默認方法,所有的實現類就自動繼承,不需要改動任何實現類,也不會影響業務,爽歪歪。

另外,接口默認方法可以被接口實現類重寫。

為什麼要有接口靜態方法?

接口靜態方法和默認方法類似,只是接口靜態方法不可以被接口實現類重寫。

接口靜態方法只可以直接通過靜態方法所在的 接口名.靜態方法名 來調用。

接口默認方法多繼承衝突問題

因為接口默認方法可以被繼承並重寫,如果繼承的多個接口都存在相同的默認方法,那就存在衝突問題。

下面我會列舉 3 個衝突示例場景。

衝突一

來看下面這段程序:

/** 來源公眾號:Java技術棧 */interface People { default void eat(){  System.out.println("人吃飯"); }}/** 來源公眾號:Java技術棧 */interface Man { default void eat(){  System.out.println("男人吃飯"); }}/** 來源公眾號:Java技術棧 */interface Boy extends Man, People {}

Boy 同時繼承了 People 和 Man,此時在 IDEA 編輯器中就會報錯:

這就是接口多繼承帶來的衝突問題,Boy 不知道該繼承誰的,這顯然也是個問題,IDEA 也會提示,需要重寫這個方法才能解決問題:

/** 來源公眾號:Java技術棧 */interface Boy extends Man, People { @Override default void eat() {  System.out.println("男孩吃飯"); }}

在方法裡面還能直接調用指定父接口的默認方法,比如:

/** 來源公眾號:Java技術棧 */interface Boy extends Man, People { @Override default void eat() {  People.super.eat();  Man.super.eat();  System.out.println("男孩吃飯"); }}

再加個實現類測試一下:

/** 來源公眾號:Java技術棧 */static class Student implements Boy { public static void main(String[] args) {  Student student = new Student();  student.eat(); }}

輸出:

人吃飯男人吃飯男孩吃飯

嗯,很強大!

衝突二

我們再換一種寫法,把 Man 繼承 People,然後 Man 重寫 People 中的默認方法。

此時,編輯器不報錯了,而 People 的默認方法置灰了,提示沒有被用到。

再運行一下上面的示例,輸出:

男人吃飯

因為 Man 繼承 People,Man 又重定了默認方法。很顯然,這個時候,Boy 知道該繼承誰的默認方法了。

衝突三

在 Man 接口中新增一個方法:say,然後在 Boy 接口中新增一個默認方法:say。

這時候,Man 中的抽象方法居然被忽略了,IDEA 都提示說沒用到,這顯然是默認方法優先於抽象方法。

總結

本文介紹了 Java 8 的默認方法和靜態方法,以及默認方法的衝突問題解決方案。所以,大家出去面試時,再也不要說接口不能寫實現方法了,那就太 OUT 了。。

文中只舉了 3 個默認方法的衝突場景,不確定還沒有更多衝突問題。默認方法雖然解決了接口變動帶來的問題,但如果設計不當,或者過度設計,其帶來的方法衝突問題也是需要引起注意的。

本文到此就結束了,之前我也陸續分享了一系列 Java 8+ 新特性文章,感興趣的可以關注公眾號Java技術棧在菜單中獲取,後續還會繼續分享,公眾號第一時間推送,持續關注哦。

老鐵們,覺得有用,在看、轉發分享一下哦~

相關焦點

  • Java之父都要《Effective Java中文版》有多牛
    這是一本分享經驗並指引你少走彎路的經典著作,針對如何編寫高效、設計優良的程序提出了最實用、最權威的指導方針,通過90條經驗法則,探索新的設計模式和語言習慣用法,幫你更加有效地使用Java程式語言及其基本類庫。
  • Java之父都需要的《Effective Java中文版(第3版)》到底有多牛
    如果你連equals()、toString(). hashCode()都還不了解的話,建議先去看些優秀的Java入門書籍,之後再來閱讀本書。如果你在Java開發方面已經有一定的經驗,想更加深入地了解Java編程語想更加深入地了解Java程式語言,成為一名更優秀、更高效的Java開發人員,那麼我建議你用心研讀本書。
  • 為什麼Java類不支持多繼承而接口可以?
    每個用Java的人都知道java不支持多繼承,但為什麼呢?無論從抽象還是多態等層面思考,感覺都是行得通的,那麼為什麼不支持呢?很多人都是分析一旦一個類繼承了多個父類,那麼父類中如果有相同的成員變量和方法,就不好處理,如下圖,這就是Diamond
  • 你應該這樣去開發接口:Java多線程並行計算
    直接相當於一個方法的調用耗時,實際開發中如果你的一個接口經過壓測耗時在100ms左右(大多數正規公司對接口性能都會要求不超過100ms),那麼再通過線程池+Future並行計算的方式,並可以瞬間將你的接口性能提高上去,再也不用擔心壓測不過了。有時候測試同學告訴你接口壓測不過是不是覺得很沒面子?
  • Java中的函數式接口
    什麼是函數式接口java中函數式接口中的定義如下:函數式接口(Functional Interface)就是一個有且僅有一個抽象方法,但是可以有多個非抽象方法的接口。需要注意以下幾點:JDK1.8中接口支持默認方法,默認方法和靜態方法都不屬於抽象方法,因此,函數式接口可以包含默認方法和抽象方法;接口默認繼承java.lang.Object,如果接口顯示聲明覆蓋了Object中方法,也不算抽象方法,例如,toString(),equals()等。
  • Java教程:Java基礎教程之set接口
    Set接口java.util.Set接口和java.util.List接口一樣,同樣繼承自Collection接口,它Collection接口中的方法基本一致,並沒有對Collection接口進行功能上的擴充,只是比Collection接口更加嚴格了。
  • ava 15 正式發布,14 個新特性,刷新你的認知
    這個時間牛逼啊,和蘋果發布會同天?現在的 JDK 真變成了「版本帝」,無力吐槽啊,版本發到 15,大部分人卻都還在用著 JDK 7/8,甚至 6。不過沒關係,多了解一下,多掌握一點新東西,對你來說沒有壞處。
  • C#8.0之後接口已經不再單純了,我懵逼了
    講故事大家在經過面向對象洗禮的時候,都了解過接口,而且知道它是一種自上而下的設計思路,舉個例子,我們電腦上都有 USB 2.0 接口,藍牙耳機實現了它可以進行充電,移動硬碟實現了它可以在電腦端顯示硬碟內容,藍牙滑鼠實現了它可以進行滑鼠操控,可以看出USB插口做出來後,誰來實現誰也搞不清楚,實現者能做出什麼東西,誰也不知道,這就是接口的魅力,落實在 C# 上就是接口中那一個一個的 stub
  • java調用印表機接口
    我們平時使用某些軟體或者在超市購物的時候都會發現可以使用印表機進行列印,本文教大家如何使用java完成這個功能。並在最後附上代碼。
  • Java為什麼要使用接口_java接口怎麼使用
    另外,在Java中,接口類型可用來宣告一個變量,他們可以成為一個空指針,或是被綁定在一個以此接口實現的對象。   其中一個使用接口的優勢是,可以利用他們模擬多重繼承,類在JAVA中不允許多重繼承,所有在JAVA中的類必須而且僅能有一個父類,而java.lang.Object(JAVA類型系統中最頂層的類型)是唯一一個例外。
  • 鄭爽曬的黑卡到底有多牛逼?
    鄭爽曬的黑卡到底有多牛逼?美國運通百夫長黑金卡,是美國運通於1999年在英國[1] 推出的「百夫長系列籤帳卡」的黑金(最高)級別版本,由於其卡面主體色調為黑色所以又被稱為「黑卡」。百夫長黑金卡是世界公認的「卡片之王」,該卡定位於頂級群體,無額度上限(中國地區的百夫長黑金卡為合作發行的信用卡,所以存在額度限制,授信額度在200-1000萬),持卡人多為各國政要、億萬富豪及社會名流並由美國運通邀請辦理,並接受辦卡申請。持卡人可以享受全球最頂級的會員專屬禮遇、權益和服務。
  • 傳輸速度吊錘USB3.0的雷電接口到底有多牛逼?高端電腦必配!
    在一些高端的筆記本電腦上,像Macbook Pro,Surface Book都會帶有一個或幾個帶雷電標誌的接口。仔細看,這個接口的外觀和手機上的 USB Type-C接口一模一樣,但事實上這個接口並不是Type-C,而是雷電3,不要小看這個接口,遠比你想像的還要強大。
  • 打破你的認知,數字除以 0 一定會崩潰嗎?
    不要看不起這些零零碎碎的基礎知識,這些基礎日積月累,慢慢的會讓你跟同事拉開差距。接下來,我們直奔主題。開始我們的基本功。我們先說結論:因為java的float和double使用了IEEE 754標準。這個標準規定:浮點數除以0等於正無窮或負無窮。
  • Java 15 正式發布,14 個新特性,刷新你的認知
    現在的 JDK 真變成了「版本帝」,無力吐槽啊,版本發到 15,大部分人卻都還在用著 JDK 7/8,甚至 6。不過沒關係,多了解一下,多掌握一點新東西,對你來說沒有壞處。這個牛逼啊,有了這個特性,意味著以後不是你想繼承就繼承,想實現就實現了,你得經過允許才行。
  • Java與Node.js的較量--一場史詩之戰
    同樣的情況,許多java程序開發人員在工作中也需要寫一些sql語句,此時就要使用java編寫的資料庫(比如Derby),到這一步你以為就萬事大吉,那麼只能說:你想多了!開發人員寫好的sql語句,需要進一步經過Derby解析,方可加載在java程序中編譯執行。
  • 聊聊Java 8 Lambda 表達式
    一:Lambda表達式的組成 java 8 中Lambda 表達式由三個部分組成:第一部分為一個括號內用逗號分隔的形式參數,參數是函數式接口裡面方法的參數;第二部分為一個箭頭符號:->;第三部分為方法體,可以是表達式和代碼塊。
  • XDoc-Java 1.1.0 發布,Java 純注釋生成接口文檔工具
    增加對JFinal的支持3.應大眾要求,增加@paramObj注釋標籤支持4.優化markdown格式輸出XDoc 基於Java注釋的接口文檔工具 基於java注釋生成接口文檔-對代碼無侵入,無需註解,純代碼注釋 支持SpringWeb, SpringBoot, JFinal 文檔輸出格式支持
  • Java接口的作用與意義
    ,所以類的特徵也同樣適用於接口:一個java文件中可以定義多個接口,但是最多只能有一個公開接口公開接口的接口名必須和java文件的文件名完全相同一個接口編譯後會生成一個class文件2.類與接口的關係定義一個實現類實現AI接口,因為接口中都是抽象方法,所以在implements時需要實現抽象方法
  • Java對象為啥要實現Serializable接口?
    關於這些問題,不知道各位讀者朋友有沒有過類似的問題,如果有那麼我們就在這篇文章中一起尋找答案吧!當然,如果你對這些問題都很清楚,也歡迎表達看法!Serializable接口概述Serializable是java.io包中定義的、用於實現Java類的序列化操作而提供的一個語義級別的接口。
  • 「三角鐵十級」有多牛?快來刷新認知!
    「三角鐵十級」有多牛?快來刷新認知! 據說 樂器界也是有鄙視鏈的 憑考級火爆程度來說 鋼琴、小提琴、吉他