菜雞的Java課筆記 第二十三 抽象類的概念
abstractClass 抽象類的概念
1.抽象類的基本定義
2.抽象類的使用原則
不會抽象類與介面,java = 沒學
如果說現在在一個類之中需要定義一個沒有方法體的方法,那麼可以利用adstract關鍵字來進行抽象方法的定義
而包含有抽象方法的類就可以使用abstract來定義成為抽象類
類的核心組成:屬性,方法。但是在學習完繼承操作之後,會發現子類存在有一種覆寫父類方法的機制,而且這一機制直接與物件的多型性有關
於是這樣就會出現一個問題:假設現在使用的是普通類,並且在這個類裡面有一個println()方法
class A{ public void print(){ System.ot.println("**************"); } } public class abstractClass{ public static void main(String args[]){ } }
但是這個A類在設計之初有一個要求,希望繼承它的子類一定要覆寫這個 print()方法。但是事實上這個時候的子類完全可以靈活的選擇是否需要覆寫方法
class A{ public void print(){ System.ot.println("**************"); } } class B extends A{} public class abstractClass{ public static void main(String args[]){ } }
但是由於只是一個普通方法,所以對於子類是否覆寫沒有任何的要求,於是這樣就會出現一個漏洞,父類無法強制要求子類覆寫方法
如果只依靠普通類的繼承,那麼根本就不能夠對子類產生限制,所以就可以利用抽象類和抽象方法來解決此類問題
範例:定義抽象類
abstract class A{ public void fun(){ System.ot.println("**************"); } public abstract void print();//沒有方法體,使用 abstract 宣告 } class B extends A{} public class abstractClass{ public static void main(String args[]){ } }
抽象方法的特點: 一個是使用了 abstract 關鍵字定義,另外一個是方法的後面沒有“{}”,表示沒有方法體
範例:錯誤的使用抽象類
abstract class A{ public void fun(){ System.ot.println("**************"); } public abstract void print();//沒有方法體,使用 abstract 宣告 } // class B extends A{} public class abstractClass{ public static void main(String args[]){ A a = new A(); } }
本處直接採用了關鍵字new例項化了抽象類物件,但是在程式編譯的時候就會出現的錯誤資訊“A是抽象的:無法例項化”
抽象類是不能夠直接進行物件例項化操作的,因為一旦類的物件例項化了,就意味著可以呼叫類中的所有方法了,但是抽象方法只是一個宣告,並沒有具體的方法體
所以在實際的開發之中,抽象類的使用原則如下:
抽象類必須有子類,子類利用 extends關鍵字來繼承抽象類,一個子類只能夠繼承一個父類
抽象類的子類(如果不是抽象類)那麼必須要覆寫抽象類中的全部抽象方法
抽象類可以利用物件的向上轉型機制,通過子類物件進行例項化操作
不能直接產生例項化物件的分析:
當一個類物件例項化之後就意味著,需要進行屬性的堆記憶體分配,同時該物件可以呼叫類中的全部方法
而抽象類中有抽象方法,抽象方法沒有方法體,你如何呼叫呢?
abstract class A{ public void fun(){ System.ot.println("**************"); } public abstract void print();//沒有方法體,使用 abstract 宣告 } class B extends A{ public void print(){ System.out.println("這個方法是強制子類要覆寫的方法"); } } public class abstractClass{ public static void main(String args[]){ A a = new B(); // 向上轉型 a.print(); // 被子類所覆寫過的方法 } } /* 結果: 這個方法是強制子類要覆寫的方法 */
抽象類與普通類相比最大的好處是強制定義了子類的實現要求
本質上講抽象類就是比普通類多了一些抽象方法的定義而已
在實際的設計之中,父類的設計是最重要的,普通類與抽象相比,明顯抽象類的約束更加的嚴格
所以在實際的開發之中,幾乎不會出現普通類定義子類的情況,大多數都是繼承抽象類
*/
/* 抽象類的相關說明
整個的設計結構裡面多了抽象類的定義,那麼多;了一個定義之後,就需要與原始的結構有一些對比
0.抽象類允許提供構造方法,如果抽象類父類提供的是有參構造,則子類必須使用 super() 明確的去呼叫父類的指定構造方法
1.抽象類不能使用final關鍵字來定義,因為抽象類必須有子類,而final不能有子類
2.抽象類就是比普通類多了抽象方法而已,但是普通類中的所有結構抽象類都可以定義,包括普通方法,構造方法,屬性,常量等內容:
而且子類物件也符合於物件例項化流程,預設先呼叫父類中的無參構造,而後再執行子類自己的構造操作
abstract class A{ public static final String INFO = "HELLO"; public A(){ this.fun(); } public void fun(){ System.out.println("**************"); } public abstract void print();//沒有方法體,使用 abstract 宣告 } class B extends A{ public void print(){ System.out.println("這個方法是強制子類要覆寫的方法"); } } public class abstractClass{ public static void main(String args[]){ A a = new B(); // 向上轉型 a.print(); // 被子類所覆寫過的方法 } } /* 結果: ******************* 這個方法是強制子類要覆寫的方法 */
思考題:
abstract class A{ public A(){ this.fun(); } public abstract void print();//沒有方法體,使用 abstract 宣告 } class B extends A{ private int num = 50; public B(int num){ this.num = num; } public void print(){ System.out.println("num ="+this.num); } } public class abstractClass{ public static void main(String args[]){ new B(100); } } /* 結果: num = 0 */
本程式的解決關鍵思路:子類物件例項化前一定要先例項化父類物件,也就是說此時,子類物件的屬性都沒有內容
分析:
abstract class A{ public A(){ // 2.預設呼叫父類構造 this.fun();// 3.呼叫println()方法 } public abstract void print();//沒有方法體,使用 abstract 宣告 } class B extends A{ private int num = 50; public B(int num){ // 1.傳遞內容過來,在子類物件例項化前先例項化父類物件 this.num = num; } public void print(){ // 4.呼叫此方法執行,此時子類物件還未例項化,內容沒有賦值 System.out.println("num ="+this.num);// 5.只能夠輸出對應資料的型別預設值 } } public class abstractClass{ public static void main(String args[]){ new B(100); } }
3. 抽象類中可以沒有抽象方法,但是依然不可能使用關鍵字new進行物件的例項化操作
abstract class A{ } public class abstractClass{ public static void main(String args[]){ A a = new A(); } } //結果:出錯
因為類A上存在有 abstract 關鍵字,所以此處無法進行物件的直接例項化
4.外部抽象類上不允許使用static宣告,但是內部抽象中可以使用 static宣告,這樣表示的是一個外部抽象類
範例:定義普通的內部抽象類
abstract class A{ public abstract void printA(); abstract class B{ public abstract void printB(); } } class X extends A{ public void printA(){} class Y extends B{ public void printB(){ } } } public class abstractClass{ public static void main(String args[]){ A a = new A(); } } //結果:
範例:在內部抽象類中使用 staic 關鍵字
abstract class A{ public abstract void printA(); static abstract class B{ public abstract void printB(); } } class X extends A.B{ public abstract void printB(){} } public class abstractClass{ public static void main(String args[]){ A a = new A(); } }
5.抽象類中可以存在有 static方法,而且 static 方法不受例項化物件的控制
範例:直接通過抽象類產生例項化物件
abstract class A{// 設計之初考慮到N年後的發展,需要有子類,但是N年前不需要子類 public abstract void printA(); private static class B extends A { // 在A類裡面直接定義實現的子類 public void printA(){ System.out.println("**************"); } } public static A getlnnstance(){ // 取得 return new B(); } } public class abstractClass{ public static void main(String args[]){ A a = A.getlnnstance(); a.printA(); } }
日後如果發現在系統類庫中有某個抽象類可以直接利用一個 static 方法取得例項化物件的時候不要覺得陌生
abstract class A{ public abstract void printA(); private static class B extends A { // 在A類裡面直接定義實現的子類 public void printA(){ System.out.println("**************"); } } public static A getlnnstance(){ // 取得 return new B(); } } class C extends A{ // N年以後 public void printA(){ System.out.println("***************"); } } public class abstractClass{ public static void main(String args[]){ A a = A.getlnnstance(); a.printA(); A a1 = new C(); // 留給使用者做的 a1.printA(); } }
以上出現的幾種形式有些是在後面講解系統類庫中會出現的問題,現階段看看就完了
*/
/* 抽象類實際應用--模版設計模式
清楚了抽象類產生動機以及使用之後,下面就必須搞清楚一個問題,抽象類與普通類到底有那些區別?
現在假設有三個物種;
機器人:補充能量+工作
人:吃飯+工作+睡覺
豬:吃+睡
現在要求實現一種命令的模式,不管是何物種,只要傳遞指定的指令就可以進行操作
範例:實現程式操作
abstract class Action{// 定義的是行為,行為一定不是具體的 public static final int EAT = 1; public static final int SLEEP = 2; public static final int WPRK = 5; public void command(int flag){// 執行命令 switch(flag){// 數值用 switch 判斷最好 case EAT:{ this.eat(); break; } case SLEEP:{ this.sleep(); break; } case WORK:{ this.work(); break; } } } public abstract void eat();// 因為這些具體的行為如何執行不知道 交由子類根據自己的實際情況完成 public abstract void sleep(); public abstract void work(); } class Robot extends Action{ public void eat(){ System.out.println("機器人補充能量"); } public void sleep(){} public void work(){ System.out.println("機器人正在工作"); } } class Person extends Action{ public void eat(){ System.out.println("人在吃飯"); } public void sleep(){ System.out.println("人在休息"); } public void work(){ System.out.println("人在工作"); } } class Pig extends Action{ public void eat(){ System.out.println("豬在吃"); } public void sleep(){ System.out.println("豬在睡"); } public void work(){} } // 不同的子類有著自己不同的操作支援 public class abstractClass{ public static void main(String args[]){ } }
範例:程式測試
abstract class Action{// 定義的是行為,行為一定不是具體的 public static final int EAT = 1; public static final int SLEEP = 2; public static final int WORK = 5; public void command(int flag){// 執行命令 switch(flag){// 數值用 switch 判斷最好 case EAT:{ this.eat(); break; } case SLEEP:{ this.sleep(); break; } case WORK:{ this.work(); break; } } } public abstract void eat();// 因為這些具體的行為如何執行不知道 交由子類根據自己的實際情況完成 public abstract void sleep(); public abstract void work(); } class Robot extends Action{ public void eat(){ System.out.println("機器人補充能量"); } public void sleep(){} public void work(){ System.out.println("機器人正在工作"); } } class Person extends Action{ public void eat(){ System.out.println("人在吃飯"); } public void sleep(){ System.out.println("人在休息"); } public void work(){ System.out.println("人在工作"); } } class Pig extends Action{ public void eat(){ System.out.println("豬在吃"); } public void sleep(){ System.out.println("豬在睡"); } public void work(){} } // 不同的子類有著自己不同的操作支援 public class abstractClass{ public static void main(String args[]){ fun(new Pig()); System.out.println("*********************"); fun(new Robot()); System.out.println("*********************"); fun(new Person()); } public static void fun(Action act){ // 接收的是行為 act.eat(); act.sleep(); act.work(); } }
這樣的設計就是將抽象類設計為了一個模版,如果要想實現這個模版的功能,子類就必須按照要求進行指定方法的覆寫
而關於這一設計的應用,在以後學習到的 Servlet 程式設計上就要採用到
現在在整個程式之中,如果某一類事物需要實現特定的功能,那麼就必須按照Action所定義的方法進行覆寫
這個時候類必須按照父類提供的模版編寫程式碼
總結
1.抽象類的設計是在普通類之上的抽象類
2.抽象類關鍵的問題就是約定了子類必須要覆寫的抽象方法
3.抽象類的使用原則:
抽象類必須有子類,子類利用 extends關鍵字來繼承抽象類,一個子類只能夠繼承一個父類
抽象類的子類(如果不是抽象類)那麼必須要覆寫抽象類中的全部抽象方法
抽象類可以利用物件的向上轉型機制,通過子類物件進行例項化操作
*/