java抽象类详解

目录

 

一、抽象类基本概念

二、抽象类的使用

三、抽象类的相关规定(使用限制)

四、抽象类的应用——模板设计模式


一、抽象类基本概念

含有抽象方法的类就是抽象类。普通类中可以含有构造方法,普通方法,static方法,常量和变量等内容,而抽象类就是在普通类的结构里增加了抽象方法。

抽象方法:我们知道所有的普通方法都会有一对“{}”,这个表示方法体,有方法体的方法一定可以被对象直接使用,而抽象方法就是没有方法体的方法。

且抽象方法必须用abstract修饰,而抽象方法所在的类必须用abstract声明,表示抽象类。

范例(定义一个抽象类):

abstract class Person{ //定义一个抽象类
    public void fun1() {   //普通方法
        System.out.println("有方法体的普通方法");
    }
    public abstract void fun2();//抽象方法(1)没有方法体 (2)有abstract修饰
}

二、抽象类的使用

首先,如果直接实例化抽象类的对象

范例:

abstract class Person{ //定义一个抽象类
    public void fun1() {   //普通方法
        System.out.println("有方法体的普通方法");
    }
    public abstract void fun2();//抽象方法(1)没有方法体 (2)有abstract修饰
}
public class TestDemo5 {
    public static void main(String[]args){
            Person student=new Person();
    }
}

运行结果:

分析栗子可知:Person是抽象类,无法实例化,抽象类含有抽象方法,而抽象方法没有方法体,也就是没有具体实现,也就无法调用,因此抽象类不能直接产生实例化对象。

所以,小结一下:

抽象类的使用原则:

1、所有的抽象类必须有子类,使用关键字extends继承,一个子类只能继承一个抽象类;

2、抽象类不能直接实例化(不能直接new),需要依靠子类向上转型的方式来处理;

3、抽象方法必须为public或protected(如果是private则不能被子类继承,子类无法实现该方法),缺省情况下,默认为public;

4、子类(非抽象类)必须复写父类(抽象类)中全部的抽象方法(如果子类没有实现父类的抽象方法,则必须将子类也定义为abstract);

5、子类(抽象类),那么作为派生类的抽象类可以不实现父类的抽象方法。

范例(子类继承抽象类):

abstract class Person{ //定义一个抽象类
    public void fun1() {   //普通方法
        System.out.println("有方法体的普通方法");
    }
    public abstract void fun2();//抽象方法(1)没有方法体 (2)有abstract修饰
}
class Human extends Person{   //非抽象类单继承父类
    @Override
    public void fun2() {  //强制覆写父类的抽象方法
        System.out.println("实现父类的抽象方法");
    }
}
public class TestDemo5 {
    public static void main(String[]args){
        Person student=new Human();//向上转型
        //student.fun1();
        student.fun2();//调用被子类覆写过的抽象方法
    }
}

运行结果:

通过以上的例子可以知道,子类继承抽象类和非抽象类的区别:

1、继承抽象类,子类有明确的方法覆写要求,继承普通类,可以有选择性的来决定是否需要重写;

2、普通类对象可以直接实例化,而抽象类对象必须通过向上转型才能得到。

三、抽象类的相关规定(使用限制)

1、抽象类中的构造方法

抽象类只是比普通类多了一些抽象方法而已,因此,抽象类也有构造方法,并且,子类对象实例化的时候,依然满足先执行父类构造,再执行子类构造的顺序。

范例:

abstract class Person{ //定义一个抽象类
     public Person() {
         System.out.println("父类Person的构造方法");
     }
   /* public void fun1() {   //普通方法
        System.out.println("有方法体的普通方法");
    }*/
   public abstract void fun2();//抽象方法(1),没有方法体(2),有abstract修饰
}
//单继承
class Human extends Person{ //Human是Person的一个子类,是个普通类
     public Human() {
         System.out.println("子类Human的构造方法");
     }
    @Override
    public void fun2() {   //强制要求覆写
       System.out.println("实现父类的抽象方法");
    }
}
public class TestDemo5 {
    public static void main(String[]args){
        Person student=new Human();  //向上转型
        student.fun2();
    }
}

运行结果:

2、fianal声明

抽象类不能用final声明,因为抽象类必须有子类,而final定义的类不能有子类。

3、抽象类能否用static声明

范例(外部抽象类用static):

static abstract class Person{ //定义一个抽象类
    public abstract void fun3();
}
class Human extends Person{
    @Override
    public void fun3() {
        System.out.println("===华丽的分割线===");
    }
}
public class TestDemo5{
    public static void main(String[]args){
            Person student=new Human();
            student.fun3();
    }
}

运行结果:

范例(内部抽象类用static修饰):

abstract class Person{ //定义一个抽象类
    static abstract class A{
        public abstract void fun3();


    }
}
class Human extends Person.A{
    @Override
    public void fun3() {
        System.out.println("===华丽的分割线===");
    }
}
public class TestDemo5{
    public static void main(String[]args){
            Person.A student=new Human();
            student.fun3();
    }
}

运行结果:

由此可知,外部的抽象类不允许用static修饰,而内部的抽象类可以用static修饰,使用static修饰的内部抽象类相当于一个外部抽象类,继承的时候使用“外部抽象类.内部抽象类”来表示类名称

4、思考:可以直接调用static修饰的方法吗?

任何时候,要执行类中static方法的时候,都可以在没有对象的情况下直接调用,抽象类也不例外。调用语法:类名.static修饰的方法

范例:

abstract class Person{ //定义一个抽象类
    public static void fun4() {
        System.out.println("===华丽的分割线===");
    }
}

public class TestDemo5{
    public static void main(String[]args){
           Person.fun4();
    }
}

5、有时候,抽象类中只需要一个特点的系统子类操作,所以可以忽略掉外部子类,此模式非正常模式,但是对一些封装性有一定好处,目的是隐藏用户不需要知道的子类

范例:

abstract class Person{ //定义一个抽象类
    public abstract void fun5();
    private static class B extends Person{ //内部抽象类子类
        @Override
        public void fun5() {  //覆写抽象方法
            System.out.println("======");

        }
    }
    public static Person getInstance() {
        return new B();
    }
}
public class TestDemo5{
    public static void main(String[]args){
        //此时取得抽象类对象的时候完全不需要知道B这个子类
        Person student=Person.getInstance();
        student.fun5();

    }
}

四、抽象类的应用——模板设计模式

开闭原则:一个软件实体如类,模板,函数,应该对扩展开放,对修改关闭

4.1、模板设计模式定义

定义一个操作的算法框架,而将一些步骤延迟到子类中。使得一个子类可以不改变一个算法的框架,即可重定义某些算法的特定步骤。

栗子:现有三类事务:

①学生:吃饭,学习,睡觉

②工人:吃饭,工作,睡觉

③婴儿:喝奶,睡觉

设计程序,实现三种不同事务的行为

类图:

(1)先定义一个抽象行为类,然后三类不同的人去实现它:

package com.WFG;

/**
 * @Author WFG
 * @Date 2019/4/19 16:34
 */
public abstract class Behavior {
       protected abstract void eat();//吃饭
        protected abstract void work();//工作
        protected abstract void sleep();//休息

        final void day(){
            this.eat();
            this.work();
            this.sleep();
    }
}

(2)定义学生类

package com.WFG;

/**
 * @Author WFG
 * @Date 2019/4/19 16:36
 */
public class Student extends Behavior {

    @Override
    protected void eat() {
        System.out.println("学生吃饭");
        }

    @Override
     protected void work() {
        System.out.println("学生学习");
    }

    @Override
    protected void sleep() {
        System.out.println("学生休息");
    }
}

(3)定义工人类

package com.WFG;

/**
 * @Author WFG
 * @Date 2019/4/19 16:40
 */
public class Woker extends Behavior {
    @Override
    protected void eat() {
        System.out.println("工人吃饭");
    }

    @Override
    protected void work() {
        System.out.println("工人工作");
    }

    @Override
    protected void sleep() {
        System.out.println("工人休息");
    }
}

(4)定义婴儿类

package com.WFG;

/**
 * @Author WFG
 * @Date 2019/4/19 16:43
 */
public class Baby extends Behavior{
    @Override
    protected void eat() {
        System.out.println("婴儿喝奶");
    }

    @Override
    protected void work() {

    }

    @Override
    protected void sleep() {
        System.out.println("婴儿休息");
    }
}

(5)测试类:

package com.WFG;

/**
 * @Author WFG
 * @Date 2019/4/19 17:21
 */
public class TestDemo {
   public static void main(String[]args){
       Behavior student=new Student();
       student.day();
       Behavior woker=new Woker();
       woker.day();
       Behavior baby=new Baby();
       baby.day();
   }

}

(6)运行结果:

4.2、在上述例子中模板设计模式体现在哪呢?

注意:所谓模板设计模式就是定义一个操作的算法框架,很明显,抽象类中的day方法就是这个算法的框架也就是模板。

范例:看一个通用的模板设计模式

public abstract class AbstractClass{ //定义抽象类(包含子类的行为抽象以及模板)
    abstract void Behavior1();  //基本行为(抽象方法)
    abstract void Behavior2();   //基本行为(抽象方法)

    final void tempeteMethod() {  //模板方法
        this.Behavior1();
        this.Behavior2();
    }
}
public class ConcreteClass1 extends AbstractClass{
    @Override
    void Behavior1() {
       //具体实现
    }

    @Override
    void Behavior2() {
        //具体实现
    }
}
public class ConcreteClass2 extends AbstractClass{
    @Override
    void Behavior1() {
       //具体实现
    }

    @Override
    void Behavior2() {
        //具体实现
    }
}

4.3、模板设计模式的优点:

  • 封装不变的内容,扩展可变的部分,如果我们要添加一个老人类,只需要继承父类就可以了;
  • 提取公共部分的代码便于维护
  • 行为由父类控制,子类实现,基本方法是由子类实现的,因此子类可以通过扩展的方法增加相应的功能,符合开闭原则

4.5、钩子方法

在上面的day方法中,eat()、woke()、sleep()是子类必须实现的,而这三个方法的修改对应了不同的类,这个方法叫做基本方法,基本方法有三种:在抽象类中实现的基本方法叫做具体方法;在抽象类中没有实现,在子类中实现的基本方法叫做抽象方法;栗子所列出的几种方法都是抽象方法,还有一种钩子方法,如下:

钩子方法:模板模式的拓展

上述例子有个问题,人吃饭是默认进食,但吃饭与否的选择并没有实现

在原来设计的类图上增加一个方法:

isEat(),要不要吃饭,这就是钩子方法,修改抽象类:

package com.WFG;

/**
 * @Author WFG
 * @Date 2019/4/19 16:34
 */
public abstract class Behavior {
       protected abstract void eat();//吃饭
       protected abstract void work();//工作
       protected abstract void sleep();//休息

        final void day(){
           if(this.isEat()) {
               this.eat();
           }
            this.work();
            this.sleep();
    }
    //钩子方法(默认做某事)子类视情况用不用覆盖它
    protected boolean isEat() {
        return true;
    }
}

钩子方法是由抽象类实现的,子类可视情况重写:

(1)范例:学生永远不吃饭(好像不太恰当哈)

package com.WFG;

/**
 * @Author WFG
 * @Date 2019/4/19 16:36
 */
public class Student extends Behavior {

    @Override
    protected void eat() {
        System.out.println("学生吃饭");
        }

    @Override
     protected void work() {
        System.out.println("学生学习");
    }

    @Override
    protected void sleep() {
        System.out.println("学生休息");
    }

    @Override
    protected boolean isEat() {
        return false;
    }
}

(2范例:工人决定要吃饭

package com.WFG;

import java.util.Set;

/**
 * @Author WFG
 * @Date 2019/4/19 16:40
 */
public class Woker extends Behavior {
    private boolean eatFlag=true;
    @Override
    protected void eat() {
        System.out.println("工人吃饭");
    }

    @Override
    protected void work() {
        System.out.println("工人工作");
    }

    @Override
    protected void sleep() {
        System.out.println("工人休息");
    }

    @Override
    protected boolean isEat() {
        return this.eatFlag;
    }

    public void setEatFlag(boolean eatFlag) {
        this.eatFlag =isEat();
    }
}

测试代码:

public class TestDemo {
   public static void main(String[]args){
       Behavior student=new Student();
       student.day();


       Behavior woker=new Woker();
       woker.day();
       ((Woker) woker).setEatFlag(true);


       Behavior baby=new Baby();
       baby.day();
   }
}

 

 

 

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值