1. 程式人生 > >菜雞的Java課筆記 第二十三 抽象類的概念

菜雞的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關鍵字來繼承抽象類,一個子類只能夠繼承一個父類
                抽象類的子類(如果不是抽象類)那麼必須要覆寫抽象類中的全部抽象方法
                抽象類可以利用物件的向上轉型機制,通過子類物件進行例項化操作


*/