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.)适用场景 在某些类的算法中,用了相同的方法,造成代码的重复。控制子类扩展,子类必须遵守算法规则。


分享到:


相關文章: