前段時間面試了一個 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" 進行閱讀,本篇就是想順著問小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 = https://www.cnblogs.com/javastack/p/get(key);
if (!Objects.equals(curValue, value) ||
(curValue == null && !containsKey(key))) {
return false;
}
remove(key);
return true;
}
...
/**
* 介面靜態方法
*/
public static , V> Comparator> comparingByKey() {
return (Comparator> & 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技術堆疊在選單中獲取,后續還會繼續分享,公眾號第一時間推送,持續關注哦,
老鐵們,覺得有用,在看、轉發分享一下哦~
近期熱文推薦:
1.Java 15 正式發布, 14 個新特性,重繪你的認知!!
2.終于靠開源專案弄到 IntelliJ IDEA 激活碼了,真香!
3.我用 Java 8 寫了一段邏輯,同事直呼看不懂,你試試看,,
4.吊打 Tomcat ,Undertow 性能很炸!!
5.《Java開發手冊(嵩山版)》最新發布,速速下載!
覺得不錯,別忘了隨手點贊+轉發哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/172113.html
標籤:Java
