03.01 Java 抽象類與模板設計模式詳解

抽象類

抽象類是為了方法覆寫而提供的類結構,因為其無法自身直接進行對象實例化操作,所以在實際應用中,抽象類主要目的是進行過程操作使用,當你要使用抽象類進行開發的時候,往往都是在你設計中需要解決類繼承問題時所帶來的的代碼重複處理。

普通類是一個完善的功能類,可以直接產生實例化對象,並且在普通類中可以包含有構造方法、普通方法、static方法、常量和變量等內容。而抽象類是指在普通類的結構裡面增加抽象方法的組成部分。

那麼什麼叫抽象方法呢?所有的普通方法上面都會有一個“{}”,這個表示方法體,有方法體的方法一定可以被對象直接使用。而抽象方法,是指沒有方法體的方法,同時抽象方法還必須使用關鍵字abstract做修飾。而擁有抽象方法的類就是抽象類,抽象類要使用abstract關鍵字聲明。

抽象類已經實現的方法是為了代碼複用,待實現的方法是為了限制子類的功能。

定義一個抽象類

<code> /*定義一個抽象類*/   
 abstract class AbstractMessage{
      /*提供setter getter*/  
  private String infoString ;
     /*構造方法*/
  public AbstractMessage(String info) {
  this.infoString = info;
  }
     /*普通方法*/
  public String getInfo() {
  return this.infoString;
  }
  /*抽象方法,沒有方法體,有abstract關鍵字做修飾*/

  public abstract void send() ;
 }/<code>

使用抽象類

首先,我們直接實例化抽象類的對象,看下是否可以直接進行實例化抽象類,代碼示例如下。

<code> abstract class AbstractMessage{
     /*提供setter getter*/
     private String infoString ;
     /*構造方法*/
     public AbstractMessage(String info) {
         this.infoString = info;
    }
     /*普通方法*/
     public String getInfo() {
         return this.infoString;
    }
     /*抽象方法,沒有方法體,有abstract關鍵字做修飾*/
     public abstract void send() ;
 }
 public class Students {
     public static void main(String[] args) {
         System.out.println();
         AbstractMessage messageInfo = new AbstractMessage("芝蘭生於深谷,不以無人而不芳\\r\\n" + "君子修身養德,不以窮困而改志") ;
         messageInfo.send();
    }
 }/<code>

運行上述代碼,通過如下結果可知,AbstractMessage抽象類是無法直接進行實例化操作。

<code> Error:(21, 39) java: abstractMessage是抽象的; 無法實例化/<code>

那麼為什麼無法被直接實例化呢,試想當一個類實例化之後,那麼可以通過對象調用類的屬性或方法,但由於抽象方法並沒有方法體,因此無法進行調用。既然無法進行方法調用的話,又怎麼去產生實例化對象呢。

抽象類的使用約束:

  1. 抽象類不能直接實例化,抽象類必須有子類,使用extends繼承,一個子類只能繼承一個抽象類,需要依靠子類採用向上轉型的方式處理;
  2. 抽象方法必須為public或者protected,因為如果為private,則不能被子類繼承,子類便無法實現該方法,當缺省情況下默認為public;
  3. 子類(不是抽象類)必須覆寫抽象類之中的全部抽象方法;

代碼示例如下:

<code> public class Lian {
  public static void main(String[] args) {
  System.out.println();
          /*向上轉型*/
  AbstractMessage messageInfo = new message("芝蘭生於深谷,不以無人而不芳\\r\\n" + "君子修身養德,不以窮困而改志") ;
  messageInfo.send();
  }
 }
 
 abstract class AbstractMessage{
     /*提供setter getter*/
     private String infoString ;
     /*構造方法*/
     public AbstractMessage(String info) {
         this.infoString = info;
    }
     /*普通方法*/
     public String getInfo() {
         return this.infoString;

    }
     /*抽象方法,沒有方法體,有abstract關鍵字做修飾*/
     public abstract void send() ;
 }
 
 /*message類是抽象類的子類,是普通類*/
 class message extends AbstractMessage {
  public message(String info) {
  super(info); //抽象類存在有參構造方法,子類必須明確調用有參構造。
  }
     /*強制要求覆寫*/
  @Override
  public void send() {
  System.out.println("發送信息\\n"+super.getInfo());
  }
 }/<code>

運行結果如下:

<code> 發送信息
 芝蘭生於深谷,不以無人而不芳
 君子修身養德,不以窮困而改志/<code>

通過如上運行結果,可以發現:

  1. 繼承抽象類的子類必須覆寫抽象方法,而繼承普通類的子類可以選擇性的覆寫其中方法;
  2. 抽象類較普通類而言,多了抽象方法,其他組成部分和普通類一樣,普通類對象可以直接進行實例化,但抽象類的對象必須通過子類向上轉型進行實例化。

抽象類中允許有構造方法?

由於抽象類裡會存在一些屬性,那麼抽象類中一定存在構造方法,其存在目的是為了屬性的初始化。 並且子類對象實例化的時候,依然滿足先執行父類構造,再執行子類構造的順序。

範例如下:

<code> public class Lian {
     public static void main(String[] args) {
         System.out.println();
         /*向上轉型*/
         AbstractMessage messageInfo = new message("芝蘭生於深谷,不以無人而不芳\\r\\n" + "君子修身養德,不以窮困而改志") ;
         messageInfo.send();
    }
 }
 
 abstract class AbstractMessage{
     /*提供setter getter*/
     private String infoString ;
     /*構造方法*/
     public AbstractMessage(String info) {
         this.infoString = info;
         System.out.println("abstractMessage 構造方法");
    }
     /*普通方法*/
     public String getInfo() {
         return this.infoString;
    }

     /*抽象方法,沒有方法體,有abstract關鍵字做修飾*/
     public abstract void send() ;
 }
 

 /*message類是抽象類的子類,是普通類*/
 class message extends AbstractMessage {
     /*抽象類存在有參構造方法,子類必須明確調用有參構造。*/
     public message(String info) {
         super(info);
         System.out.println("message 構造方法");
    }

     /*強制要求覆寫*/
     @Override
     public void send() {
         System.out.println("發送信息\\n"+super.getInfo());
    }
 }/<code>

執行結果:

<code> abstractMessage 構造方法
 message 構造方法
 發送信息
 芝蘭生於深谷,不以無人而不芳
 君子修身養德,不以窮困而改志/<code>

因為抽象類必須有子類,而final定義的類不能有子類,因此抽象類運行不能final聲明。

如下一個外部抽象類的示例:

<code> public class Lian {
     public static void main(String[] args) {
         System.out.println();
         /*向上轉型*/
         AbstractMessage messageInfo = new message("芝蘭生於深谷,不以無人而不芳\\r\\n" + "君子修身養德,不以窮困而改志") ;
         messageInfo.send();

    }
 }
 
 static  abstract class AbstractMessage{
     /*提供setter getter*/
     private String infoString ;
 
     /*構造方法*/
     public AbstractMessage(String info) {
         this.infoString = info;
         System.out.println("abstractMessage 構造方法");
    }
     /*普通方法*/
     public String getInfo() {
         return this.infoString;
    }
     /*抽象方法,沒有方法體,有abstract關鍵字做修飾*/
     public abstract void send() ;
 }
 
 /*message類是抽象類的子類,是普通類*/
 class message extends abstractMessage {
     /*抽象類存在有參構造方法,子類必須明確調用有參構造。*/
     public message(String info) {
         super(info);
         System.out.println("message 構造方法");
    }
     /*強制要求覆寫*/
     @Override
     public void send() {
         System.out.println("發送信息\\n"+super.getInfo());
    }
 }/<code>


<code> Error:(12, 18) java: 此處不允許使用修飾符static/<code>

再看一個關於內部抽象類:

<code> public class Lain {
     public static void main(String[] args) {
         System.out.println();
         /*向上轉型*/
         AbstractMessage.AbstractMessageChild messageInfo = new message("芝蘭生於深谷,不以無人而不芳\\r\\n" + "君子修身養德,不以窮困而改志") ;
         messageInfo.send();
    }
 }
 
 abstract class AbstractMessage{
     static abstract class AbstractMessageChild{//static定義的內部類屬於外部類
         /*提供setter getter*/
         private String infoString ;
         /*構造方法*/
         public AbstractMessageChild(String info) {
             this.infoString = info;
             System.out.println("abstractMessageChild 構造方法");
        }
         /*普通方法*/
         public String getInfo() {
             return this.infoString;
        }
         /*抽象方法,沒有方法體,有abstract關鍵字做修飾*/
         public abstract void send() ;
    }
 }
 
 /*message類是抽象類的子類,是普通類*/
 class message extends AbstractMessage.AbstractMessageChild {
     /*抽象類存在有參構造方法,子類必須明確調用有參構造。*/
     public message(String info) {
         super(info);
         System.out.println("message 構造方法");
    }

     /*強制要求覆寫*/
     @Override

     public void send() {
         System.out.println("發送信息\\n"+super.getInfo());
    }
 }/<code>

執行結果如下:

<code> abstractMessageChild 構造方法
 message 構造方法
 發送信息
 芝蘭生於深谷,不以無人而不芳
 君子修身養德,不以窮困而改志/<code>

由此可見,外部抽象類不允許使用static聲明,而內部的抽象類運行使用static聲明。使用static聲明的內部抽象類相當於一個外部抽象類,繼承的時候使用“外部類.內部類”的形式表示類名稱。

可以直接調用抽象類中用static聲明的方法麼? 任何時候,如果要執行類中的static方法的時候,都可以在沒有對象的情況下直接調用,對於抽象類也一樣。 範例如下:

<code> public class Lain {
     public static void main(String[] args) {      
         abstractMessage.getInfo();
    }
 }

 abstract class AbstractMessage{
     /*靜態方法*/
     public static void getInfo() {
         System.out.println("芝蘭生於深谷,不以無人而不芳\\r\\n君子修身養德,不以窮困而改志");
    }

     /*抽象方法,沒有方法體,有abstract關鍵字做修飾*/
     public abstract void send() ;
 }
 
 /*message類是抽象類的子類,是普通類*/
 class message extends AbstractMessage {
     /*強制要求覆寫*/
     @Override
     public void send() {
         System.out.println("發送信息\\n");
    }
 }/<code>

運行結果:

<code> 芝蘭生於深谷,不以無人而不芳
 君子修身養德,不以窮困而改志/<code>

(5)有時候由於抽象類中只需要一個特定的系統子類操作,所以可以忽略掉外部子類。這樣的設計在系統類庫中會比較常見,目的是對用戶隱藏不需要知道的子類。 範例如下:

<code> public class Lain {
     public static void main(String[] args) {
         AbstractMessage abstractMessage = AbstractMessage.getInstance();
         abstractMessage.send();
    }
 }
 
 abstract class AbstractMessage{
     /*抽象方法,沒有方法體,有abstract關鍵字做修飾*/
     public abstract void send() ;
     /*靜態方法*/
     public static void getInfo() {
         System.out.println("芝蘭生於深谷,不以無人而不芳\\r\\n君子修身養德,不以窮困而改志");

    }
     /*內部抽象類子類*/
     private static class AbstractMessageChild extends AbstractMessage{//內部抽象類子類
         public void send(){//覆寫抽象類的方法
             System.out.println("發送信息 !");
        }
    }
 
     /*靜態方法*/
     public static AbstractMessage getInstance(){
         return new AbstractMessageChild();
    }
 }
 
 /<code>

運行結果:

<code> 發送信息 !/<code>

抽象類之模板設計模式

模板方法模式:在一個方法中定義一個算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以在不改變算法結構的情況下,重新定義算法中的某些步驟。模板方法模式是基於繼承的代碼複用基本技術,模板方法模式的結構和用法也是面向對象設計的核心之一。在模板方法模式中,可以將相同的代碼放在父類中,而將不同的方法實現放在不同的子類中。

在模板方法模式中,我們需要準備一個抽象類,將部分邏輯以具體方法以及具體構造函數的形式實現,然後聲明一些抽象方法來讓子類實現剩餘的邏輯。不同的子類可以以不同的方式實現這些抽象方法,從而對剩餘的邏輯有不同的實現,這就是模板方法模式的用意。模板方法模式體現了面向對象的諸多重要思想,是一種使用頻率較高的模式。

例如,現在有如下三類事務:

  • 機器人:充電、工作;
  • 人:吃飯、工作、睡覺;
  • 豬:吃飯、睡覺;

現要求實現一個程序, 通過抽象類定義並實現一個模板方法。這個模板方法定義了算法的骨架,而邏輯的組成步驟在相應的抽象操作中,推遲到子類去實現可以實現三種不同事物的行為。

<code> abstract class AbstractAction{
 
     public static final int EAT = 1 ;
     public static final int SLEEP = 3 ;
     public static final int WORK = 5 ;
 
     public abstract void eat();
     public abstract void sleep();
     public abstract void work();
 
     public void commond(int flags){
         switch(flags){
             case EAT:
                 this.eat();
                 break;
             case SLEEP:
                 this.sleep();
                 break;
             case WORK:
                 this.work();
                 break;
             default:
                 break;
        }
    }

 }/<code>

定義一個機器人的類,如下:

<code> class Robot extends AbstractAction{
 
     @Override
     public void eat() {
         System.out.println("機器人充電");
    }
 
     @Override
     public void sleep() {
    }
 
     @Override
     public void work() {
         System.out.println("機器人工作");
    }
 
 }
 /<code>

定義一個人的類,如下:

<code> class Person extends AbstractAction{
 
     @Override
     public void eat() {
         System.out.println("人吃飯");
    }
 
     @Override
     public void sleep() {
         System.out.println("人睡覺");
    }
 
     @Override
     public void work() {
         System.out.println("人工作");
    }
 
 }/<code>

定義一個豬的類,如下:

<code> class Pig extends AbstractAction{
 
     @Override
     public void eat() {
         System.out.println("豬吃飯");
    }
 
     @Override
     public void sleep() {
         System.out.println("豬睡覺");
    }
 
     @Override
     public void work() {
    }
 
 }
 /<code>

主類如下:

<code> public class Students {
     public static void main(String[] args) {
         run(new Robot());
         run(new Person());
         run(new Pig());
    }
     public static void run(AbstractAction abstractAction){
         abstractAction.commond(AbstractAction.EAT);
         abstractAction.commond(AbstractAction.SLEEP);
         abstractAction.commond(AbstractAction.WORK);
    }
 
 }
 /<code>

運行結果:

<code> 機器人充電
 機器人工作
 人吃飯
 人睡覺
 人工作
 豬吃飯
 豬睡覺/<code>

所有的子類如果要想正常的完成操作,必須按照指定的方法進行覆寫才可以,而這個時候抽象類所起的功能就是一個類定義模板的功能。

模板設計模式的優缺點

模板方法模式通過把不變的行為搬移到超類,去除了子類中的重複代碼。子類實現算法的某些細節,有助於算法的擴展。通過一個父類調用子類實現的操作,通過子類擴展增加新的行為,符合“開放-封閉原則”。 2.)缺點 每個不同的實現都需要定義一個子類,這會導致類的個數的增加,設計更加抽象。 3.)適用場景 在某些類的算法中,用了相同的方法,造成代碼的重複。控制子類擴展,子類必須遵守算法規則。


分享到:


相關文章: