先贊後看,養成習慣java
文本已收錄至GitHub開源倉庫 Lu_JavaNodes 碼雲倉庫地址Lu_JavaNodes,包含教程涉及全部思惟導圖,案例代碼和後續講解視頻,歡迎Star增磚添瓦。git
在傳統的接口語法中,接口中只能夠有抽象方法。在是在實際的使用中,咱們每每會須要用到不少和接口相關的功能(方法),這些功能會單獨的拿出開放在工具類中。github
工具類:類中全部的方法都是靜態的web
例如:Collection 和 Collocations,Collection 是一個集合接口,而咱們須要不少集合相關的操做,像集合的排序,搜索等等, 這時候人們會把這些靜態方法放在 Collections 工具類中。 面試
在傳統Java中咱們常常會看到這樣的狀況,有一個接口叫 A,這時候就會有一個類叫 As,As中全是和A接口有關的靜態方法。
例如:Executor 和 Executorsapp
這樣的一種方式總歸來講是有點不方便。因而在JDK8中Java對於接口作了一些改動,容許將靜態方法直接寫入接口中。(接口中能夠定義靜態方法,靜態方法確定不是抽象的,是有實現的)。ide
根據上述內容,咱們來定義一個接口,在接口中寫入一個靜態方法。工具
public class TestStaticInterface {
public static void main(String[] args) {
// 靜態方法能夠經過類名直接調用 接口能夠說是特殊的類 因此經過接口名能夠調用接口中的靜態方法
HelloInterface.printHello();
}
}
interface HelloInterface{
int hhh();
// 定義靜態方法
static void printHello(){
System.out.println("Hello");
}
}
複製代碼
運行代碼能夠看到以下結果
學習
靜態方法其實是很實用的,最基本的用法:咱們能夠把產生接口對象的方法放在接口中。ui
什麼意思???好,接下來咱們經過代碼演示一下。
假設如今咱們有一個 Animal 接口,那麼這時候若是要得到一個Animal類型的對象,咱們要怎麼作呢?
傳統方法,建立一個Animals工具類,在其中有一個 static Animal createDog()
能夠獲取一個Animal類型的對象,代碼以下
public class TestStaticInterface {
public static void main(String[] args) {
// 經過工具類獲取對象
Animal animal = Animals.createDog();
}
}
class Animals{
// 靜態方法獲取對象
static Animal createDog(){
// 局部內部類
class Dog implements Animal{
}
// 返回對象
return new Dog();
}
}
複製代碼
可是當你擁抱JDK8的時候,一切都不同了,由於有接口靜態方法,能夠直接將接口對象的獲取放在接口的靜態方法中。代碼以下
public class TestStaticInterface {
public static void main(String[] args) {
// 經過接口的靜態方法獲取一個Animal類型的對象
Animal animal = Animal.createDog();
}
}
interface Animal{
// 靜態方法獲取對象
static Animal createDog(){
// 局部內部類
class Dog implements Animal{
}
// 返回對象
return new Dog();
}
}
複製代碼
接下來咱們經過Java中的API來驗證一下這種使用方法。經過API文檔,能夠找到 Comparator 接口(比較器),在這個接口中如今就有不少的靜態方法(JDK8)。如圖
經過這些靜態方法,就能夠經過接口直接獲取比較器對象。
public class TestStaticInterface {
public static void main(String[] args) {
// 經過Comparator接口獲取一個天然排序的比較器(天然排序就是String中默認實現的排序邏輯)
Comparator<String> comparator = Comparator.naturalOrder();
// 建立集合
List<String> list = Arrays.asList("b","a","c");
// 經過比較器對集合進行排序
list.sort(comparator);
for (String s : list) {
System.out.println(s);
}
}
}
複製代碼
如今接口已經有了靜態方法,可是傳統的接口還有另外一個問題。咱們舉例說明:
假設你正在公司中作項目,在你的代碼中,有一個UserService的接口,接口中有一個方法String getUsernameById()
。
interface UserService{
String getUsernameById();
}
複製代碼
該接口由於在項目中存在老長時間了,因此實現類衆多,有100個實現類。
one day,領導但願你給這個接口中添加一個新的接口方法String getIdByUsername()
。這樣的需求意味着要修改100個實現類,不要說寫代碼了,刪庫跑路的心都有了。
這是一個極端的案例,可是說明了一個事兒,傳統的接口向後兼容性很差,不易於維護和改造。
而這個問題,在JDK8中獲得瞭解決,解決方法就是:接口的默認方法。
Java 8 中容許接口中包含具備具體實現的方法,該方法稱爲 「默認方法」,默認方法使用 default 關鍵字修飾。
在接口中使用 default 表示這個方法有實現,接口中全部的方法都是 public
interface UserService{
String getUsernameById();
// 默認方法
default void m1(){
System.out.println("這是一個默認方法");
}
}
class UserServiceImpl implements UserService{
@Override
public String getUsernameById() {
return null;
}
}
複製代碼
看了這樣的一段代碼,你必定會有一些疑問,咱們一塊兒來解決一下。
接口中的默認方法,實現類能不能繼承到?
答:這個固然是能夠的,而且在實現類中依然能夠進行方法的覆蓋。
若是 UserServiceImpl 再有一個父類,父類中也有m1方法,那麼UserServiceImpl 繼承到的是父類仍是接口中的m1方法?
interface UserService{
String getUsernameById();
// 默認方法
default void m1(){
System.out.println("這是一個默認方法");
}
}
//父類
class UserSer{
public void m1(){
System.out.println("這是一個默認方法");
}
}
class UserServiceImpl extends UserSer implements UserService{
@Override
public String getUsernameById() {
return null;
}
}
複製代碼
答:在實現類中繼承到的是父類中的。由於接口默認方法有」類優先」的原則。
接口默認方法的」類優先」原則
若一個接口中定義了一個默認方法,而另一個父類或接口中 又定義了一個同名的方法時
- 選擇父類中的方法。若是一個父類提供了具體的實現,那麼
接口中具備相同名稱和參數的默認方法會被忽略。- 接口衝突。若是一個父接口提供一個默認方法,而另外一個接 口也提供了一個具備相同名稱和參數列表的方法(無論方法是不是默認方法),那麼必須覆蓋該方法來解決衝突
關於接口新語法的講解實際上已經結束了,可是想要和你們一塊兒延伸一下思考,看下面一個案例。
interface IA{
default void m2(){
System.out.println("IA");
}
}
interface IB{
default void m2(){
System.out.println("IB");
}
}
class ImplC implements IA,IB{
// 接口衝突 經過覆蓋解決
public void m2(){
System.out.println("Impl");
}
}
複製代碼
以上代碼實際上就是 「類優先」原則第二條接口衝突的演示代碼,而我要思考的問題不是這個,而是:1.在實現類中,如何使用super,2.若是IA 和 IB 接口中的m2方法返回值不一樣怎麼辦?
1.在實現類中,如何使用super?
第一個問題,比較好解決,由於有m2來自兩個接口,因此咱們若是要調用super的話,須要說明要調用那個接口的super,語法:接口名.super.m2()
實現類繼承的方法來自兩個接口,必須覆蓋,不然引用不明確。要調用super,也必須指明要調用那個接口。
其實這個問題來自多繼承,過去接口比較簡單,調用 super確定不會調用接口,接口中方法都是抽象的,如今不同了,父類和接口中都有方法實現,這時候再要調用就要指明要調用誰了。
雖然Java一直都是單繼承,可是這個語法實際上已是向多繼承靠近了。只不過並無把多繼承正式的引入Java,因此會有必定的不足,這就是咱們的第二個思考題。
2.若是IA 和 IB 接口中的m2方法返回值不一樣怎麼辦?
這其實也是一個標準的多繼承的問題,在現版本沒有解決。
在C++中其實就簡單了,能夠指定要覆蓋誰
學過了接口的靜態方法和默認方法,彷彿發現了一個事兒,接口和抽象類愈來愈像了,那麼這時候再問你那個問題:接口和抽象類有什麼區別?
這個問題留給你們,好像之前背答案開始很差使了。
最後咱們簡單總結一下JDK8接口語法的新變化:在JDK8之後的接口中,容許有靜態方法和默認方法(default)修飾
歡迎關注本人公衆號:鹿老師的Java筆記,將在長期更新Java技術圖文教程和視頻教程,Java學習經驗,Java面試經驗以及Java實戰開發經驗。