Java中的抽象类和接口
目錄
什么是抽象類
抽象類再實現多態中的意義?
接口是什么
?各位鐵汁們大家好呀😊!
😎今天讓我們繼續學習java,看看java中的抽象類和接口到底是什么🤔??
什么是抽象類
🍑我們之前學過什么是類,那么抽象類是不是也是類的一種呢?
聽名字就感覺好抽象呀!說對了,他就是抽象的,不是具體的。在類中沒有包含足夠的信息來描繪一個具體的對象,這樣的類稱為抽象類。
🌰來看一個抽象類的例子
// 抽象類和抽象方法需要被 abstract 關鍵字修飾 abstract class Shape {// 抽象類中的方法一般要求都是抽象方法,抽象方法沒有方法體abstract void draw(); }大家覺得這個抽象類是不是什么也沒干,他唯一的方法draw()還是空的。
像這樣的類是不是就沒有包含足夠的信息來描繪一個具體的對象,自然也就不能實例化對象了。不信你看:
🍑那既然一個類不能實例化,那這種抽象類存在的意義是什么呀🤔?別急,存在即合理,聽我慢慢道來。
抽象類在實現多態中的意義?
🍑抽象類存在的一個最大意義就是被繼承,當被繼承后就可以利用抽象類實現多態。
來看一段代碼
// 抽象類和抽象方法需要被 abstract 關鍵字修飾 abstract class Shape {// 抽象類中的方法一般要求都是抽象方法,抽象方法沒有方法體abstract void draw(); } // 當一個普通類繼承一個抽象類后,這個普通類必須重寫抽象類中的方法 class Cycle extends Shape {@Overridevoid draw() { // 重寫抽象類中的draw方法System.out.println("畫一個圓圈");} }public class Test4 {public static void main(String[] args) {//Shape shape = new Shape(); 抽象類雖然不能直接實例化// 但可以把一個普通類對象傳給一個抽象類的引用呀,即父類引用指向子類對象Shape shape = new Cycle(); // 這稱作:向上轉型/*Cycle cycle = new Cycle();Shape shape = cycle // 這是向上轉型的另一種寫法*/shape.draw(); // 通過父類引用調用被子類重寫的方法} }運行之后你就會發現神奇的一幕:
?
大家在看完了代碼可能會有很多疑問,別急咱們一個一個的說,
📝什么是向上轉型:一句話總結就是“父類引用指向子類對象”
向上轉型后的變化
向上轉型的作用
🌰這樣的話就我們上面的代碼就可以理解了
??
📝看來,我們可以通過子類對抽象類的繼承和重寫,抽象類還真有點用呀!
🍑但這和多態有什么關系呢,抽象類用起來這么麻煩,我還不如直接用普通類,也能達到這樣的效果,還不用再寫一個子類呢😫?
🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟
🌰那行,你再看看下面的代碼,你就知道抽象類在實現多態時的好處了。?
abstract class Shape {public abstract void draw(); // 抽象方法不能里有具體的語句 } // 當一個普通類繼承一個抽象類的時候,再這個子類中必須重寫抽象類中的抽象方法 class Cycle extends Shape { @Override // 如果不重寫會報錯,但如果繼承的是普通類則不會報錯,用抽象類更安全public void draw() {System.out.println("畫一個圓圈");} } class Flower extends Shape { // 不同的子類對父類的draw方法進行了不同的重寫@Overridepublic void draw() {System.out.println("畫一朵花");} } class Square extends Shape {@Overridepublic void draw() {System.out.println("畫一個正方形");} }public class Test4 {public static void main(String[] args) {Cycle cycle = new Cycle(); // 子類引用cycleFlower flower = new Flower(); // 子類引用flowerSquare square = new Square();// 數組的類型是Shape,即數組中每一個元素都是一個父類引用// 在這個過程其實也發生了向上轉型,對抽象類中的方法進行了重寫Shape[] shapes = {cycle, flower, square}; // 父類引用引用不同的子類對象for (int i = 0; i < shapes.length; i++) {Shape shape = shapes[i]; // 父類引用shape指向—>當前所對應的子類對象shape.draw(); // 通過父類引用調用子類重寫的draw方法}} }🍑調用同一個方法竟然打印出了不同的結果😮,這難道就是所謂的多態🤔?
?是不是有點懵😂,下面我們來解釋一下😉
// 對上面的代碼補充一下 // 可能你對 Shape[] shapes = {cycle, flower, square};不太理解 // 但上面的代碼就相當于?Shape[] shapes1 = new Shape[3]; // 有三個不同的子類對象呀!數組大小為3// (將指向->子類對象)的子類引用賦值給父類對象,不就相當于該夫類引用指向->所對應的子類對象嗎 //這是向上轉型的另一種寫法,應為前面已經實例化了子類對象 ?Cycle cycle = new Cycle(); ??shapes1[0] = cycle; ?// 如果前面沒實例化子類對象,就要寫成shape1[0] = new Cycleshapes1[1] = flower;shapes1[2] = square;對于多態來說,他有這三個要素
🍑回頭再看一下我們的代碼,是不是就剛好符合了多態的三要素😉。
📝當我們的父類引用指向不同的子類對象時,當我們調用同一個draw方法時卻輸出了不同的結果。(其實就是該方法再子類中被重寫成了不同形式)這就叫做多態 。
嘻嘻😂,其實只要只要結合著例子來看,多態也沒那么難理解呀😎
??
🍑那為啥一定要用抽象類呢😂?我一個普通類繼承普通類來實現多態不可以嗎🤔
🌰當然可以,但不太安全有風險;
?????
但如果是抽象類的話,就不一樣了😉?
??
🍑從這我們也可以看出,當用抽象類的時候,編譯器自動就對我們是否重寫進行了校驗,而充分利用編譯器的校驗, 在實際開發中是非常有意義的?。所以說抽象類還是有用的,嘻嘻😉
📝好了,相信到這里你對抽象類也有了一個大概的認識😊,下面我們來簡單做一下總結
🌰哈哈,上面的8條總結是不是臭長臭長的,大家也不用專門去記(用的多了,自然就記住了😉)
🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟🐟
接口是什么
🍑抽象類是從多個類中抽象出來的模板,如果將這種抽象進行的更徹底,則可以提煉出一種更加特殊的“抽象類”——接口(Interface)🤔。
📝接口是Java中最重要的概念之一,它可以被理解為一種特殊的類,不同的是接口的成員沒有執行體,是由全局常量和公共的抽象方法所組成😎。
如何定義一個接口呢?下面我們來看一個栗子🌰
//接口的定義格式與定義類的格式基本相同,將class關鍵字換成 interface 關鍵字,就定義了一個接口public interface 接口名稱{ // 定義變量 int a = 10; // 接口當中的成員變量默認都是public static final// 抽象方法 public abstract void method1(); // public abstract 是固定搭配,可以不寫 void method2(); // 接口當中的成員方法默認都是public abstract, 更推薦用第二種來定義方法 }可以看到接口和類其實還是有很多相似點:
📝接口中也包含抽象方法,所以也不能直接實例化接口🤔,那么我們怎么用接口呢?
🍑哈哈,很簡單😉,我們再用一個普通類實現這個接口不就行了嗎😂,不同的是抽象類是被子類來繼承而實現的,而接口與類之間則是用關鍵字implements來實現。
📝就像普通類實現實現抽象類一樣,一個類實現某個接口則必須實現該接口中的抽象方法,否則該類必須被定義為抽象類🤔。
?通過接口實現多態
🍑鐵汁們!剛才我們是用抽象類來實現多態,那么現在我們可以嘗試用接口來實現多態,嘻嘻😎
接口可以看成是一種特殊的類,只能用 interface 關鍵字修飾 interface IShape {int a = 10; 接口當中的成員變量默認都是public static finalint b = 23;void draw(); 接口當中的成員方法一般只能是抽象方法,默認是public abstract(JDK1.8以前)default void show() {System.out.println("接口中的其他方法");//接口中的其他方法也可以實現,但要用default修飾}public static void test() {System.out.println("這是接口當中的一個靜態的方法");} }// 一個普通的類要想實現接口,可以用implement, //因為接口也是抽象方法的,所以實現接口的這個類也要重寫抽象方法 class Cycle implements IShape {@Overridepublic void draw() {System.out.println("畫一個圓圈");} } class Square implements IShape {@Overridepublic void draw() {System.out.println("畫一個正方形");} } class Flower implements IShape {@Overridepublic void draw() {System.out.println("畫一朵花");} } public class Test4 {public static void main(String[] args) {// IShape iShape = new IShape(); 接口也不能直接實例化Cycle cycle = new Cycle();Square square = new Square();Flower flower = new Flower();// 這里的IShape接口就相當與抽象類中父類,接口類型也是一種引用類型IShape[] iShapes = {cycle, square, flower}; // 這個過程其實就發生了向上轉型for (IShape iShape : iShapes) { // 增強型的for—each循環,也可以寫成普通的for循環形式iShape.draw(); // 通過重寫實現了多態}} } 引用變量cycle和square都賦值給了Shape類型的引用變量shape, 但當執行shape.draw()時,java虛擬機到底要調用誰重寫的的draw方法, 就看此時接口引用的是那個對象的,是shape的、還是cycle的?😎看一下運行結果😎
?看完代碼你可能有點暈😫,但沒關系。一般接口咱也不這么用,直接使用抽象類不就好了😉(我只是演示一下用接口也能實現多態😂)
🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑🍑
📝下面我們來總結一下Java中接口的幾個主要特點
🍑那么接口一般用在什么地方呢?
- 一般情況下,實現類和它的抽象類之前具有 "is-a" 的關系,但是如果我們想達到同樣的目的,但是又不存在這種關系時,使用接口。
- 由于 Java 中單繼承的特性,導致一個類只能繼承一個類,但是可以實現一個或多個接口,此時可以使用接口。
🌰下面就讓我們來看看接口的正確用法:幫助java實現“ 多繼承 ”😎
由于 Java 中單繼承的特性,導致一個類只能繼承一個類,但是可以實現一個或多個接口,此時可以使用接口。 class Animal {String name; // 不能使用private,后面的子類也要用public Animal(String name) { // 父類的自定義的構造方法this.name = name;} } interface IFlying { // 自定義多種接口void fly(); } interface IRunning {void run(); } interface ISwimming {void swimming(); } // 小鴨子,不僅會跑,還會游泳、飛行 一個類繼承父類,并實現多個接口,間接的解決java中不能多繼承的問題 class Duck extends Animal implements IRunning, ISwimming, IFlying {public Duck(String name) { // 子類構造方法super(name); // 必須在子類構造方法的第一行// 在給實現子類的構造方法前,先要用super()調用實現父類的構造方法,比較先有父后有子呀!// 因為父類自己定義了構造方法,編譯器不會自動給給子類構造方法中添加super();來實現父類的構造方法,需要我們自己實現}// 對接口中的抽象方法進行重寫@Overridepublic void fly() {System.out.println(this.name + "正在用翅膀飛");}@Overridepublic void run() {System.out.println(this.name + "正在用兩條腿跑");}@Overridepublic void swimming() {System.out.println(this.name + "正在漂在水上");}}public class 接口的使用 { // 不用學我用中文名作為類名,我只是為演示方便public static void main(String[] args) {Duck duck = new Duck("第一個小鴨子"); // 實例化鴨子對象duck.fly(); // 通過引用 變量名.方法名 輸出重寫后的方法duck.run();duck.swimming();} } 有人可能會說干嘛用接口,我直接在父類Animal中實現fly、run、swimming這些屬性, 然后不同的動物子類再繼承父類這些方法不行嗎?但問題是,鴨子會fly、swimming,那貓會飛和游泳嗎?你再寫個其他動物的子類是不是就不行了 而用接口呢?我們只是把這種飛、游泳的行為給抽象出來了,只要一個子類有這種行為,他就可以實現相對應的接口,接口是更加靈活的?📝上面的代碼展示了 Java 面向對象編程中最常見的用法: 一個類繼承一個父類, 同時實現多個接口。
?📝繼承表達的含義是 is - a 語義, 而接口表達的含義是 具有 xxx 特性 ,能實現接口的類和該接口并不一定有is_a的關系,只要該類有這個接口的特性就行
貓是一種動物, 具有會跑的特性.
青蛙也是一種動物, 既能跑, 也能游泳
鴨子也是一種動物, 既能跑, 也能游, 還能飛
?🍑這樣設計有什么好處呢? 時刻牢記多態的好處, 讓程序猿忘記類型. 有了接口之后, 類的使用者就不必關注具體類型,只要這個類有有這個特性就好。
舉個栗子🌰
class Robot implements IRunning {private String name;public Robot(String name) {this.name = name;}// 對run方法進行重寫@Overridepublic void run() {System.out.println("機器人" + this.name + "正在跑");} } public class Test4 {public static void main(String[] args) {Robot robot1 = new Robot("圖圖");robot1.run();} } // 執行結果 機器人圖圖正在跑😎只要能跑就行,管他是機器人還是動物呢😂,接口是不是很靈活呀!
同時在實際的開發過程中,一般來說,一個類是一個Java文件,一個接口也是一個Java文件。大家也要在平時就養成這個好習慣呀!
哈哈,不知道現在你對抽象類和接口的認識是不是又上升了一個高度呢?嘻嘻,我們下篇博客再見了,下篇讓我們瞅瞅Java中常用的接口有哪些😉
?每天進步一點點,鐵汁們,一起加油😎😎😎
總結
以上是生活随笔為你收集整理的Java中的抽象类和接口的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: BTG遭遇51%攻击,幕后黑手竟是他!
- 下一篇: VB.net:使用Visual Stud