【JAVA入門】Day15 - 接口
文章目錄
- 【JAVA入門】Day15 - 接口
- 一、接口是對“行為”的抽象
- 二、接口的定義和使用
- 三、接口中成員的特點
- 四、接口和類之間的關系
- 五、接口中新增的方法
- 5.1 JDK8開始接口中新增的方法
- 5.1.1 接口中的默認方法
- 5.1.2 接口中的靜態方法
- 5.2 JDK9 開始接口中新增的方法
- 5.2.1 在接口中定義私有方法
- 六、接口的應用
- 七、適配器設計模式
????????為什么有接口?
????????眾所周知,繼承是將子類中的共性抽象提煉到父類中,獲取整個體系的共性以節省代碼,但是,也存在這樣一種情況:某一共性,絕大部分子類都有,但唯獨極少部分子類未擁有此特性,那么如果把這個共性寫進父類,繼承下來,對這些少數子類而言就是不合理的了。
????????比如:貓和狗都會游泳,但兔子不會,這個時候如果在父類中定義“游泳”的方法,兔子繼承是不合理的,但如果貓和狗分別寫自己的游泳方法,又有可能發生兩個方法書寫的格式、命名不一致的情況。因此我們需要定義一個新概念,來約束這兩個子類中“游泳”方法的書寫規范。此時,我們就可以定義一個“游泳”的接口,在接口里定義抽象方法 swim(),然后讓貓和狗與這個接口連接,保證了代碼的統一。
???????? 綜上所述,接口就是一種規則,當我們需要給多個類同時定義規則時,就需要用到接口。
一、接口是對“行為”的抽象
????????接口不代表一類事物,接口代表的是一種規則,因此接口可以作為參數傳遞給方法。
????????“不論來搬家的是貨拉,三輪,哪怕是人力,只要他能實現搬家,他就有用”。
搬家(車的對象);
搬家(搬家公司);
public interface 運輸 {...
}
public void 搬家(運輸的接口 c) {...
}
二、接口的定義和使用
- 接口用關鍵字 interface 來定義。
public interface 接口名 {}
- 接口不能實例化,也就是說接口不能用來創建對象。
- 接口和類之間是實現關系,通過 implements 關鍵字表示。
public class 類名 implements 接口名 {}
- 接口的子類(實現類)要么重寫接口中的所有抽象方法,要么本身也是一個抽象類。
- 接口和類的實現關系,可以單實現,也可以多實現:
public class 類名 implements 接口名1 , 接口名2 {}
- 實現類可以在繼承一個類的同時實現多個接口。
public class 類名 extends 父類 implements 接口名1 , 接口名2 {}
練習:編寫帶有接口和抽象類的標準 Javabean 類。
青蛙 屬性:名字,年齡 行為:吃蟲子,蛙泳狗 屬性:名字,年齡 行為:吃骨頭,狗刨兔子 屬性:名字,年齡 行為:吃胡蘿卜
先寫父類,因為三個字類吃的東西不同,可以把 eat() 定義為抽象方法:
package oopInterface;public abstract class Animal {private String name;private int age;public Animal() {}public Animal(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public abstract void eat();
}
寫接口Swim:
package oopInterface;public interface Swim {public abstract void swim();
}
寫青蛙類,注意繼承父類重寫父類的抽象方法,注意實現游泳接口:
package oopInterface;public class Frog extends Animal implements Swim {public Frog() {super();}public Frog(String name, int age) {super(name, age);}@Overridepublic void swim() {System.out.println("青蛙在蛙泳。");}@Overridepublic void eat() {System.out.println("青蛙在吃蟲子。");}
}
寫狗類,注意繼承父類重寫父類的抽象方法,實現游泳接口:
package oopInterface;public class Dog extends Animal implements Swim {public Dog(){super();}public Dog(String name, int age) {super(name,age);}@Overridepublic void swim() {System.out.println("狗在狗刨。");}@Overridepublic void eat() {System.out.println("狗在吃骨頭。");}
}
寫兔子類,注意只需要繼承父類重寫抽象方法,不需要實現游泳接口(不會游泳):
package oopInterface;public class Rabbit extends Animal {public Rabbit() {}public Rabbit(String name, int age) {super(name, age);}@Overridepublic void eat() {System.out.println("兔子在吃胡蘿卜。");}
}
寫測試類。
package oopInterface;public class Test {public static void main(String[] args) {Frog f = new Frog("小綠",23);f.eat();f.swim();System.out.println(f.getName() + ", " + f.getAge());Dog d = new Dog("大D", 24);d.eat();d.swim();System.out.println(d.getName() + ", " + d.getAge());Rabbit r = new Rabbit("兔子", 30);r.eat();System.out.println(r.getName() + ", " + r.getAge());}
}
三、接口中成員的特點
- 成員變量:接口中的成員變量只能是常量,默認使用 public static final 修飾(就是不寫也自動認為是這樣)。
- 構造方法:接口沒有構造方法。
- 成員方法:JDK7以前只能是抽象方法,默認修飾符為 public abstract;JDK8以后,接口中可以定義有方法體的方法;JDK9以后,接口中可以定義私有方法。
四、接口和類之間的關系
- 類和類的關系:繼承關系——只能單繼承,不能多繼承,但可以多層繼承。
- 類和接口的關系:實現關系——可以單實現,也可以多實現,還可以在繼承一個類的同時實現多個接口。
- 接口和接口的關系:繼承關系——可以單繼承,也可以多繼承。
注意:
1.類在實現接口時,要么實現接口中所有的抽象方法,要么這個類本身也是一個抽象類。
2.類可以多實現接口,如果多實現,需要把所有要實現接口的抽象方法都實現。
3.接口可以多繼承,如果一個子接口繼承了多個接口,然后被一個實現類實現,那么這個實現類就要把這個子接口和他的所有父接口中的所有抽象方法全部實現。
練習:編寫帶有接口和抽象類的標準Javabean類。
乒乓球運動員:姓名,年齡,學打乒乓球,說英語
籃球運動員:姓名,年齡,學打籃球
乒乓球教練:姓名,年齡,教打乒乓球,說英語
籃球教練:姓名,年齡,教打籃球
//Person類
package oopInterExp;//因為直接創建頂層父類人的對象是沒有意義的
//所以將其寫為抽象類
public abstract class Person {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}public Person() {}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}
//Sporter類繼承Person
package oopInterExp;public abstract class Sporter extends Person {public Sporter(String name, int age) {super(name, age);}public Sporter() {}public abstract void learn();
}
//Coach類繼承Person
package oopInterExp;public abstract class Coach extends Person {public Coach() {}public Coach(String name, int age) {super(name, age);}public abstract void teach();
}
//SpeakEnglish接口
package oopInterExp;public interface SpeakEnglishInter {public abstract void speakEnglish();
}
//PingPongSporter繼承Sporter,實現SpeakEnglish
package oopInterExp;public class PingPongSporter extends Sporter implements SpeakEnglishInter {public PingPongSporter(String name, int age) {super(name, age);}public PingPongSporter() {}@Overridepublic void learn() {System.out.println("學習乒乓球。");}@Overridepublic void speakEnglish() {System.out.println("乒乓球運動員在說英語。");}
}
//PingPongCoach繼承Coach,實現SpeakEnglish
package oopInterExp;public class PingPongCoach extends Coach implements SpeakEnglishInter {public PingPongCoach() {}public PingPongCoach(String name, int age) {super(name, age);}@Overridepublic void teach() {System.out.println("教乒乓球。");}@Overridepublic void speakEnglish() {System.out.println("乒乓球教練在說英語。");}
}
//BasketballSporter繼承Sporter
package oopInterExp;public class BasketballSporter extends Sporter {public BasketballSporter(String name, int age) {super(name, age);}public BasketballSporter() {}public void learn() {System.out.println("學籃球。");}
}
//BasketballCoach繼承Coach
package oopInterExp;public class BasketballCoach extends Coach {public BasketballCoach() {}public BasketballCoach(String name, int age) {super(name, age);}public void teach() {System.out.println("教籃球。");}
}
五、接口中新增的方法
5.1 JDK8開始接口中新增的方法
????????JDK7 以前,接口中只能定義抽象方法。
????????JDK8新增的特性是:接口中可以定義有方法體的方法(可以定義默認方法或靜態方法)。
????????JDK9新增的特性是:接口中可以定義私有方法。
5.1.1 接口中的默認方法
????????接口中定義有方法體的方法,主要是為了接口升級考慮的,接口不可能是一成不變的,需要往里面添加新的方法來升級,如果這些方法都是抽象方法,此時,實現類中就需要同時也實現這些方法,非常麻煩且不容易同步;倘若升級使用的是有方法體的方法,那么實現類就不需要額外進行修改了,如果需要修改,也可以利用重寫進行修改。
- 在接口中定義默認方法,需要使用關鍵字 default 修飾。
- 格式:public default 返回值類型 方法名(參數列表) { }
- 范例:public default void show() { }
- 默認方法不是抽象方法,不強制需要被重寫。但是如果被重寫,重寫的時候要去掉 default 關鍵字。
- public 可以省略,但是 default 不可以省略。
- 如果實現了多個接口,多個接口中存在相同名字的默認方法,那么子類就必須對該方法進行重寫了(不重寫會引起沖突)。
接口Inter1:
package oopInterface5;public interface Inter1 {public abstract void method();public default void default_method() {System.out.println("Inter1接口中的默認方法");}
}
接口Inter2:
package oopInterface5;public interface Inter2 {public default void default_method() {System.out.println("Inter2接口中的默認方法");}
}
兩個默認方法同名,如果實現類同時實現這倆接口,一定需要重寫這個默認方法!
package oopInterface5;public class InterImpl implements Inter1, Inter2 {@Overridepublic void method() {System.out.println("抽象方法的實現");}@Overridepublic void default_method() {System.out.println("重寫接口中的默認方法");}
}
測試類:
package oopInterface5;public class Test {public static void main(String[] args) {InterImpl ii = new InterImpl();ii.method(); //抽象方法的實現ii.default_method(); //重寫接口中的默認方法}
}
5.1.2 接口中的靜態方法
????????JDK8 以后允許在接口中定義靜態方法,需要用 static 修飾。
????????接口中靜態方法的定義格式為:
- public static 返回值類型 方法名(參數列表) { }
- 范例:public static void show() { }
????????接口中靜態方法的注意事項:
- 靜態方法只能通過接口名調用,不能通過實現類名或對象名調用。
- public 可以省略,static 不能省略。
寫一個接口:
package oopInterface6;public interface Inter {public abstract void method();public static void static_method() {System.out.println("接口中的靜態方法");}
}
寫一個實現類,類里面還有一個靜態方法,與接口里面的方法同名,但這不是重寫,因為靜態方法是不能被重寫的:
package oopInterface6;public class InteImpl implements Inter {@Overridepublic void method() {System.out.println("重寫接口中的抽象方法");}//這不叫重寫public static void static_method() {System.out.println("我不是重寫的Inter接口中的靜態方法");}
}
但其實,二者是不同的方法。
package oopInterface6;public class Test {public static void main(String[] args) {InteImpl ii = new InteImpl();ii.method(); //重寫接口中的抽象方法Inter.static_method(); //調用接口中的靜態方法InteImpl.static_method(); //調用實現類中的一個同名的靜態方法}
}
5.2 JDK9 開始接口中新增的方法
5.2.1 在接口中定義私有方法
- 格式1:private 返回值類型 方法名(參數列表) { }
- 范例1:private void show() { }
- 用法:給默認方法服務。
package oopInterface7;public interface InterA {public default void show1() {System.out.println("show1開始執行");show3();}public default void show2() {System.out.println("show2開始執行");show3();}//普通的私有方法,給默認方法服務的private void show3() {System.out.println("記錄程序在運行過程中的各種細節,這里有100行代碼。");}
}
- 格式2:private static 返回值類型 方法名(參數列表) { }
- 范例2:private static void method() { }
- 用法:給靜態方法服務。
package oopInterface7;public interface InterB {public static void show1() {System.out.println("show1開始執行");show3();}public static void show2() {System.out.println("show2開始執行");show3();}//普通的私有方法,給靜態方法服務的private static void show3() {System.out.println("記錄程序在運行過程中的各種細節,這里有100行代碼。");}
}
六、接口的應用
1.接口代表規則,是行為的抽象。想要讓哪個類擁有一個行為,就讓這個類實現對應的接口就可以了。
2.當一個方法的參數是接口時,可以傳遞接口所有實現類的對象,這種方式稱之為接口多態。
七、適配器設計模式
- 設計模式(Design Pattern) 是一套被反復使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。使用設計模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性、程序的重用性。
????????適配器可以用來簡化代碼,避免因為接口中抽象方法過多,而我們只需要使用其中一部分的時候造成的不便。
????????書寫步驟一般為:
1.編寫中間類XXXAdapter,實現對應的接口。
2.對接口中的抽象方法進行空實現。
3.讓真正的實現類繼承中間類,并重寫需要用的方法。
4.為了避免其他類去創建適配器類的對象,中間的適配器類用 abstract 修飾。
接口:
package AdapterDesignPattern;public interface Inter {public abstract void method1();public abstract void method2();public abstract void method3();public abstract void method4();public abstract void method5();
}
適配器類:
package AdapterDesignPattern;public abstract class InterAdapter implements Inter {@Overridepublic void method1() {}@Overridepublic void method2() {}@Overridepublic void method3() {}@Overridepublic void method4() {}@Overridepublic void method5() {}
}
實現類:
package AdapterDesignPattern;public class InterImpl extends InterAdapter {//我需要用到哪個方法,就重寫哪個方法就可以了@Overridepublic void method5() {System.out.println("只要用第五個方法");}
}