? ? ? ? 哈嘍大家好呀qvq,這里是乎里陳,接口這一知識點博主分為三篇博客為大家進行講解,今天為大家講解第二篇java中實現多個接口,接口間的繼承,抽象類和接口的區別知識點,更適合新手寶寶們閱讀~更多內容持續更新中~
目錄
1. 實現多個接口
完整代碼講解
?2. 接口間的繼承
完整代碼講解
3. 抽象類和接口的區別
定義方式上不同
繼承/實現方式不同
區別表格
1. 實現多個接口
? ? ? ? 在Java中,接口(Interface)定義了一組抽象方法,用于規范類的行為。類和類之間是單繼承的,一個類只能由一個父類,即java不支持類的多重繼承,但是一個類可以實現多個接口,從而擴展類的功能;這種機制使得Java可以實現類似多重繼承的效果。
? ? ? ? 比如說:
-
一個?Bird 類可以同時實現 Flyable 和 Run 這兩個接口。
-
一個 Smartphone 類可以同時實現 Callable(可打電話),Playable(可播放音樂)和 Photographable(可拍照)三個接口。
? ? ? ? 像這樣實現多個接口的話,我們可以更靈活地設計程序,避免單繼承的限制,可以讓類具備更多的功能~
首先,我們初步定義了一個狗狗類🐶繼承動物類:
//建立一個動物抽象類:
//1.用abstract修飾,表示這是一個抽象類,不能直接創建實例
//2.包含了所有動物共有的屬性和方法
public abstract class Animal {//公共屬性名字和年齡(所有動物和植物都有的)public String name;public int age;//抽象方法:吃東西//1.用abstract修飾,沒有具體的實現//2.所有的子類必須實現這個方法public abstract void eat();//動物類的構造方法public Animal(String name,int age) {this.name = name;this.age = age;}
}
//定義了一個狗狗類
//1.繼承自動物類(extends Animal)
//2.必須實現父類的抽象方法eat()
public class Dog extends Animal{//狗狗的構造方法public Dog(String name, int age) {//通過super調用父類的構造方法初始化name和agesuper(name, age);}//實現父類的eat()方法@Override//表示重寫public void eat() {System.out.println(name+"吃狗糧啦~");}
}
? ? ? ? 寫完上面代碼后,我們知道狗狗不僅會游泳,也會跑步,則可以讓這個狗狗類實現游泳接口和跑步接口(前提是定義了游泳和跑步的接口):
?定義的三個接口,狗狗只能實現里面的兩個:
public interface Flyable {void fly();
}
public interface Running {void run();
}
public interface Swimming {void swim();
}
-
extends Animal
?表示繼承Animal類 -
implements Running, Swimming
表示實現兩個接口,后面可通過逗號再添加幾個狗狗能實現的接口
// Dog通過extends繼承Animal并且通過implemens實現了Running和Swimming接口
public class Dog extends Animal implements Running,Swimming{...
}
在實現接口后,我們必須還要實現Running和Swimming接口里面的方法:
// Dog通過extends繼承Animal并且通過implemens實現了Running和Swimming接口
public class Dog extends Animal implements Running,Swimming{//狗狗的構造方法public Dog(String name, int age) {//通過super調用父類的構造方法初始化name和agesuper(name, age);}//實現父類的eat()方法@Override//表示重寫public void eat() {System.out.println(name+"吃狗糧啦~");}//實現Running接口的run()方法@Overridepublic void run() {System.out.println(name+"正在飛跑~");}//實現Swimming接口的swim()方法@Overridepublic void swim() {System.out.println(name+"正在狗刨~");}
}
? ? ? ? 所以通過上面的代碼我們實現了Dog類繼承Animal類,然后通過implements實現了Running和Swimming兩個接口,我們要注意一定是先繼承再實現接口,如果把extends放后面是會產生錯誤的啦:
同樣的,我們知道java中一個類只能繼承一個類,若是再繼承一個也會產生錯誤啦:
? ? ? ? 那么我們說過解決多繼承的問題,到底是怎么體現的呢?我們往下看,不妨假設一下,沒有Running和Swimming這兩個接口,而是Running和Swimming兩個類,然后Dog再繼承它們:
? ? ? ? 很顯然是不行的啦~怎么可以繼承多個類呢,所以我們就需要接口,可以將我們這些規則的行為放到我們接口當中,這樣就能解決多繼承。很明顯不是所有的動物都會飛,如果我們將Flyable放到我們Animal類里面,很明顯是不合適的,狗狗是不會飛的。
? ? ? ? 我們再定義一個鳥類繼承父類Animal并通過implements實現Running和Flyable接口來觀察:
// Bird通過extends繼承Animal并且通過implemens實現了Running和Flyable接口
public class Bird extends Animal implements Running,Flyable{//小鳥兒的構造方法public Bird(String name, int age) {//通過super調用父類的構造方法初始化name和agesuper(name, age);}//實現父類的eat()方法@Overridepublic void eat() {System.out.println(name+"吃鳥糧");}//實現Flyable接口的fly()方法@Overridepublic void fly() {System.out.println(name+"正在酷酷飛翔");}//實現Running接口的run()方法@Overridepublic void run() {System.out.println(name+"奮力疾走中");}
}
? ? ? ? 很明顯小鳥只能飛和跑,并不能游泳,所以鳥類只能實現Running和Flyable接口,不能實現Swimming接口~
🟢那么我們寫下測試類:
public class Test2 {//測試方法1 - 接收Animal類型參數;animal可以是Animal類或其任何子類的對象public static void test1(Animal animal) {// 調用動物的eat方法,具體執行哪個看實際傳入對象類型// 例如:test1(new Dog()) 會調用Dog類的eat()// test1(new Cat()) 會調用Cat類的eat()animal.eat();}//測試方法2 - 接收Running接口類型參數//是任何實現了Running接口的對象public static void test2(Running running) {//調用傳入對象的run()方法//只要對象實現了Running接口就能傳入,不管它是什么具體類running.run();}//測試方法3 - 接收Swimming接口類型參數//是任何實現了Swimming接口的對象public static void test3(Swimming swimming) {//調用傳入對象的swim()方法//可以接收任何"會游泳"的對象swimming.swim();}//測試方法4 - 接收Flyable接口類型參數//是任何實現了Flyable接口的對象public static void test4(Flyable flyable) {//調用傳入對象的fly()方法//可以接收任何"會飛"的對象flyable.fly();}//第一個測試主方法 - 演示接口多態public static void main1(String[] args) {// 創建一只鳥對象,名字叫"憤怒的小鳥兒",年齡2歲Bird bird = new Bird("憤怒的小鳥兒",2);// 創建一只狗對象,名字叫"開心的小狗狗",年齡3歲Dog dog = new Dog("開心的小狗狗",3);// 測試test2方法 - 傳入會跑步的對象test2(bird);// 讓鳥兒跑步(Bird實現了Running接口)test2(dog);// 讓狗狗跑步(Dog實現了Running接口)System.out.println("----------------");// 分隔線// 測試test3方法 - 傳入會游泳的對象test3(dog);// 讓狗狗游泳(Dog實現了Swimming接口)//??不可以test3(bird);Bird沒有實現Swimming接口System.out.println("----------------");// 分隔線// 測試test4方法 - 傳入會飛的對象test4(bird);// 讓鳥兒飛翔(Bird實現了Flyable接口)//??不可以test4(dog);Dog沒有實現Flyable接口}//第二個測試主方法 - 演示繼承多態public static void main2(String[] args) {// 創建一只鳥對象,名字叫"憤怒的小鳥兒",年齡2歲Bird bird = new Bird("憤怒的小鳥兒",2);// 創建一只狗對象,名字叫"開心的小狗狗",年齡3歲Dog dog = new Dog("開心的小狗狗",3);// 測試test1方法 - 傳入動物對象test1(bird);// 讓鳥兒吃東西(調用Bird的eat方法)test1(dog);// 讓狗狗吃東西(調用Dog的eat方法)// 這里因為Bird和Dog都是Animal的子類,所以都可以傳入啦}
}
🌟 我們可以從上述代碼得出以下的一些個人理解:
1. 多態是什么?
- 就像同一個"動作"在不同對象上有不同表現
- 例如:同樣是"吃",鳥吃鳥糧,狗吃狗糧
2. 接口多態(main1演示)
-
test2( )中可以接受任何會跑步的對象(實現了Running接口就可以)
-
不管傳入的是鳥還是狗,只要會跑步就行
3.?繼承多態(main2演示)
-
test1( )可以接受任何動物對象(是Animal的子類)
-
不管傳入的是鳥還是狗,因為它們都是動物
4. 那么為什么這樣設計呢?
-
能提高代碼靈活性:不需要為每種動物寫單獨的方法
-
便于擴展:新增一些動物類型的時候不需要修改測試方法
??還是再提一下:每個測試方法(test1
-test4
)的行為取決于:實際傳入的對象類型,該對象對應方法的實現方式~
完整代碼講解
下面是完整版本的代碼:附帶超詳細注釋,更適合新手寶寶們閱讀~
// 1. 首先定義接口
public interface Flyable {void fly();
}
public interface Running {void run();
}
public interface Swimming {void swim();
}// 2. 定義抽象父類
//建立一個動物抽象類:
//1.用abstract修飾,表示這是一個抽象類,不能直接創建實例
//2.包含了所有動物共有的屬性和方法
public abstract class Animal {//公共屬性名字和年齡(所有動物和植物都有的)public String name;public int age;//抽象方法:吃東西//1.用abstract修飾,沒有具體的實現//2.所有的子類必須實現這個方法public abstract void eat();//動物類的構造方法public Animal(String name,int age) {this.name = name;this.age = age;}
}// 3. 定義具體實現類
// Bird通過extends繼承Animal并且通過implemens實現了Running和Flyable接口
public class Bird extends Animal implements Running,Flyable{//小鳥兒的構造方法public Bird(String name, int age) {//通過super調用父類的構造方法初始化name和agesuper(name, age);}//實現父類的eat()方法@Overridepublic void eat() {System.out.println(name+"吃鳥糧");}//實現Flyable接口的fly()方法@Overridepublic void fly() {System.out.println(name+"正在酷酷飛翔");}//實現Running接口的run()方法@Overridepublic void run() {System.out.println(name+"奮力疾走中");}
}// Dog通過extends繼承Animal并且通過implemens實現了Running和Swimming接口
public class Dog extends Animal implements Running,Swimming{//狗狗的構造方法public Dog(String name, int age) {//通過super調用父類的構造方法初始化name和agesuper(name, age);}//實現父類的eat()方法@Override//表示重寫public void eat() {System.out.println(name+"吃狗糧啦~");}//實現Running接口的run()方法@Overridepublic void run() {System.out.println(name+"正在飛跑~");}//實現Swimming接口的swim()方法@Overridepublic void swim() {System.out.println(name+"正在狗刨~");}
}// 4. 定義測試類
public class Test2 {//測試方法1 - 接收Animal類型參數;animal可以是Animal類或其任何子類的對象public static void test1(Animal animal) {// 調用動物的eat方法,具體執行哪個看實際傳入對象類型// 例如:test1(new Dog()) 會調用Dog類的eat()// test1(new Cat()) 會調用Cat類的eat()animal.eat();}//測試方法2 - 接收Running接口類型參數//是任何實現了Running接口的對象public static void test2(Running running) {//調用傳入對象的run()方法//只要對象實現了Running接口就能傳入,不管它是什么具體類running.run();}//測試方法3 - 接收Swimming接口類型參數//是任何實現了Swimming接口的對象public static void test3(Swimming swimming) {//調用傳入對象的swim()方法//可以接收任何"會游泳"的對象swimming.swim();}//測試方法4 - 接收Flyable接口類型參數//是任何實現了Flyable接口的對象public static void test4(Flyable flyable) {//調用傳入對象的fly()方法//可以接收任何"會飛"的對象flyable.fly();}//第一個測試主方法 - 演示接口多態public static void main1(String[] args) {// 創建一只鳥對象,名字叫"憤怒的小鳥兒",年齡2歲Bird bird = new Bird("憤怒的小鳥兒",2);// 創建一只狗對象,名字叫"開心的小狗狗",年齡3歲Dog dog = new Dog("開心的小狗狗",3);// 測試test2方法 - 傳入會跑步的對象test2(bird);// 讓鳥兒跑步(Bird實現了Running接口)test2(dog);// 讓狗狗跑步(Dog實現了Running接口)System.out.println("----------------");// 分隔線// 測試test3方法 - 傳入會游泳的對象test3(dog);// 讓狗狗游泳(Dog實現了Swimming接口)//不可以test3(bird);Bird沒有實現Swimming接口System.out.println("----------------");// 分隔線// 測試test4方法 - 傳入會飛的對象test4(bird);// 讓鳥兒飛翔(Bird實現了Flyable接口)//不可以test4(dog);Dog沒有實現Flyable接口}//第二個測試主方法 - 演示繼承多態public static void main2(String[] args) {// 創建一只鳥對象,名字叫"憤怒的小鳥兒",年齡2歲Bird bird = new Bird("憤怒的小鳥兒",2);// 創建一只狗對象,名字叫"開心的小狗狗",年齡3歲Dog dog = new Dog("開心的小狗狗",3);// 測試test1方法 - 傳入動物對象test1(bird);// 讓鳥兒吃東西(調用Bird的eat方法)test1(dog);// 讓狗狗吃東西(調用Dog的eat方法)// 這里因為Bird和Dog都是Animal的子類,所以都可以傳入啦}
}
最后的輸出結果為:
憤怒的小鳥兒奮力疾走中??
開心的小狗狗正在飛跑~
----------------
開心的小狗狗正在狗刨~
----------------
憤怒的小鳥兒正在酷酷飛翔
?最后總結一下:
? ? ? ? 上述代碼展示了一個類繼承一個父類,同時實現多種接口。繼承表達的含義是 is-a,而接口表達的含義是具有某某特性。
?2. 接口間的繼承
? ? ? ? 想象你正在設計一個動物園管理系統。最初,你定義了Swimming(可游泳)和Flyable(可飛行)兩個基礎接口。但隨著系統發展,你發現有些鳥類既會飛又會潛水(比如企鵝)。這時候,就需要用到接口間的繼承啦~重新定義一個接口,這個接口具備Swimming和Flyable這兩個接口的功能,讓這個新的接口繼承Swimming接口和Flyable接口~
例如我們先定義三個接口:
//分別定義了兩個個接口
//并分別定義了各自的抽象方法
interface A {void testA();
}
interface B {void testB();
}
這時候我們想要有一個接口,擁有A和B這兩個接口的功能,則這樣實現:
//有一個接口,具備A和C接口的功能,extends為擴展
interface C extends A,B {//C這個接口具備了B和C的功能,同時也有自己的抽象方法void testC();
}
那么當一個類實現我們C接口,需要重寫A,B,C里的三個方法:
public class Test4 implements C{@Overridepublic void testA() {}@Overridepublic void testB() {}@Overridepublic void testC() {}
}
完整代碼講解
實現接口間繼承示例完整的代碼如下:
//分別定義了三個接口
//并分別定義了各自的抽象方法
interface A {void testA();
}
interface B {void testB();
}
//有一個接口,具備A和C接口的功能,extends為擴展
interface C extends A,B {//C這個接口具備了B和C的功能,同時也有自己的抽象方法void testC();
}
//實現子接口的類必須實現所有繼承鏈上的抽象方法
public class Test4 implements C{@Overridepublic void testA() {}@Overridepublic void testB() {}@Overridepublic void testC() {}
}
? ? ? ? 接口C通過extends關鍵字繼承了接口A和B,通過上述代碼我們可以總結出接口繼承的特點:
- 一個接口(C)可以繼承多個父接口(A,B)
- 子接口(C)可以繼承所有父接口(A,B)的抽象方法
- 子接口可以定義自己的新方法
- 接口間的繼承相當于把多個接口合并在一起
3. 抽象類和接口的區別
🔴java中抽象類和接口的區別,這是常見的面試題
抽象類和接口都是 Java 中多態的常見使用方式,我們需要知道:
"抽象類管是什么,接口管能做什么"
抽象類關注類的本質,接口關注行為擴展。
定義方式上不同
// 抽象類 - 用abstract修飾
abstract class Animal {// 可以有具體屬性String name;int age;// 可以有具體方法public void sleep() {System.out.println("動物在睡覺");}// 可以有抽象方法public abstract void eat();
}// 接口 - 用interface修飾
interface Swimmable {// 只能有常量(默認是public static final)int a = 5;// 只能有抽象方法(Java8前)void swim();
}
繼承/實現方式不同
// 類繼承抽象類(extends)
class Dog extends Animal {@Overridepublic void eat() {System.out.println("狗吃狗糧");}
}// 類實現接口(implements)
class Fish implements Swimmable {@Overridepublic void swim() {System.out.println("魚在游泳");}
}
區別表格
特性 | 抽象類 | 接口 |
---|---|---|
關鍵字 | abstract class | interface |
子類繼承/實現 | 使用extends關鍵字繼承抽象類(單繼承) | 使用implements關鍵字實現接口(多實現) |
構造方法 | 有 | 無 |
結構組成 | 普通類+抽象方法 | 抽象方法+全局變量 |
設計理念 | "是什么"(is-a關系) | "能做什么"(has-a能力) |
適用場景 | 有共同特征的類 | 定義類的行為規范 |
關系 | 一個抽象類可以實現多個接口 | 接口不能繼承抽象類, 但是可以使用extends繼承多個父接口 |
那么我們該選擇哪個使用,可以根據以下內容選擇:
1. 用抽象類當:
- 多個類有共同屬性和方法時
- 需要定義一些默認實現時
- 例子:不同種類的鳥都有羽毛,都會飛,但飛行方式不同
2. 用接口當:
- 只想定義行為規范時
- 需要類具備多種能力時
- 例子:鴨子既能游泳(Swimming)又跑(Running)
💡總結一下:
? ? ? ? 抽象類中可以包含普通方法和普通字段,這樣的普通方法和字段可以被子類直接使用(不必重寫)。
? ? ? ?而接口中,不能包含普通方法,子類必須重寫所有的抽象方法。比如此處的 Animal 中包含一個 name 這樣的屬性,這個屬性在任何子類中都是存在的。因此此處的 Animal 只能作為一個抽象類,而不應該成為一個接口。
class Animal {protected String name;public Animal(String name) {this.name = name;}}
在這其中,也經常會有這些面試題:?
- 一個類能同時繼承類和實現接口嗎?
? 可以的,先extends類后implements接口
-
接口能繼承類嗎?
? 不能的,接口只能繼承接口
制作不易,更多內容加載中~感謝友友們的點贊收藏關注~~
如有問題歡迎批評指正,祝友友們生活愉快,學習工作順順利利!