跳到主要内容

第12章-抽象类

概述

  • 生活场景

    • 我们生活中经常说电器这个抽象概念,并且有打开的操作

      • 电视打开,能看到画面
      • 空调打开,能带来习习凉风
      • 抽油烟机打开,能带走厨房仙气
      • ...

      但,就这个抽象的电器来说,他的打开,并不具备实际的意义,因为本身并不会产生特定的真实产品,只是针对各种实际电器品类的更高层次的抽象

      主要是用于,在抽象这个概念时,从观念上给电器类的产品下了个规则,一定都要有”打开“这个行为

  • 像上述这样的场景 ,为了规范所有子类(后代类)行为而规划出来的类,但自身的这类行为并不具备实际能力的类,就是抽象类

  • 如上一章实战和作业中铲屎官投喂的宠物Pet,只是个更高层次的抽象概念,其并不能确定”吃“的具体行为内涵,而是由具体的子类狗、猫、猪等来实现其行为内涵

  • 此时,就可以将这种主要用于更高层次抽象,规范子类行为的类,会定义成抽象类

  • 抽象类在实际的开发应用有广泛的应用,在Java自带类库、第三方的工具库中也有广泛的使用

定义抽象类

  • 语法规则

    • 关键字是abstract

    • 语法规则如下:

      /**
      * 【抽象类】
      */
      访问修饰符 abstract class 类名称{
      //【抽象方法】没有实现逻辑,只有方法声明(确定方法签名),需要在子类覆盖
      访问修饰符 abstract 返回值类型 方法名();
      //其他属性和普通方法
      }

      语法解释:

      • abstract:抽象类、抽象方法修饰的关键字
  • 抽象类具体规则

    • 抽象类、抽象方法前必须添加abstract修饰符
    • 一个抽象类可以有0~n个抽象方法,还可以有任意多个其他属性和普通方法
    • 区别于普通类,由于抽象类提一个更高层次抽象的纯抽象的类,只能用于被继承,多用于实现多态,不能使用new关键字进行实例化
    • 抽象类的子类,一般需要覆盖抽象类的所有抽象方法,否则,子类也必须定义成抽象类
  • 应用实例

    • 应用实例1,定义一个纯抽象的动物类

      实例代码

      package com.bjpowernode.demo;

      /**
      * 动物类,【抽象类】
      */
      public abstract class Animal {
      //名称
      private String name;

      //getter/setter,同时也是【普通方法】(非抽象方法),有具体实现
      public String getName() {
      return name;
      }
      public void setName(String name) {
      this.name = name;
      }

      /**
      * 吃东西,【抽象方法】,没有具体实现
      * @param foodName 食物名称
      */
      public abstract void eatFood(String foodName);

      /**
      * 发声,【抽象方法】,没有具体实现
      */
      public abstract void sound();
      }

使用抽象类

  • 抽象类一般只用于被继承,继承的方式与继承普通方类似,但一般会需要覆盖抽象类的抽象方法

  • 为了使用抽象类实现多态,一般会让抽象类的引用指向子类的对象,也即父类的引用指向子类的对象

  • 应用实例

    • 应用实例1,定义动物类的子类狗、鸟等

      • 狗类

        package com.bjpowernode.demo;

        /**
        * 狗类,继承于抽象类动物类Animal,需要覆盖抽象类的所有抽象方法
        */
        public class Dog extends Animal {
        @Override
        public void eatFood(String foodName) {
        System.out.println("狗子" + this.getName() + "啃完了" + foodName);
        }

        @Override
        public void sound() {
        System.out.println("狗子" + this.getName() + "在叫,汪汪汪...");
        }
        }
      • 鸟类

        package com.bjpowernode.demo;

        /**
        * 鸟类,继承于抽象类动物类Animal,需要覆盖抽象类的所有抽象方法
        */
        public class Bird extends Animal {
        @Override
        public void eatFood(String foodName) {
        System.out.println("鸟儿" + this.getName() + "一小口一小口吃完了" + foodName);
        }

        @Override
        public void sound() {
        System.out.println("鸟儿" + this.getName() + "在叫,叽叽叽...");
        }
        }
      • 测试类

        package com.bjpowernode.demo;

        /**
        * 动物测试类
        */
        public class AnimalTest {
        /**
        * 入口方法
        * @param args
        */
        public static void main(String[] args) {
        //定义抽象类动物类引用
        Animal animal;

        //新建抽象类对象,将会报错
        // animal = new Animal();

        //【多态】新建Dog类对象,使用父类Animal引用指向Dog类对象
        animal = new Dog();
        animal.setName("旺财");
        animal.eatFood("骨头"); //吃饭
        animal.sound(); //发声

        //【多态】新建Bird类对象,使用父类Animal引用指向Bird类对象
        animal = new Bird();
        animal.setName("火鸟");
        animal.eatFood("海鱼"); //吃饭
        animal.sound(); //发声
        }
        }
  • 覆盖抽象类方法的快捷方式,在extends后的抽象类上按Alt+回车,选择implement methods项,然后选择要覆盖的抽象方法,将会自动生成方法签名和空方法体

抽象类的作用

  • 具备更高层次的抽象能力,将实际业务中众多的具有共性的各种类型,抽象出一个并不存在的的类型,程序代码更简单,通用性更强
  • 子类提供模板,提供规范性约束,规范子类具备的行为
  • 增强程序的可扩展性,使用一些抽象类引用可动态的切换实现的子类

应用实例

实战和作业

  1. 提问:

    1. 下面的代码正确吗?为什么?

      package com.bjpowernode.demo;

      /**
      * 抽象类
      */
      public abstract class People {
      /**
      * 打招呼
      */
      public abstract void speakHi(){

      }
      }
    2. 下面的代码正确吗?为什么?

      package com.bjpowernode.demo;

      /**
      * 抽象类
      */
      public abstract class People {
      /**
      * 打招呼
      */
      public void speakHi(){

      }

      /**
      * 玩
      */
      public abstract void play();

      /**
      * 支付
      */
      public abstract void pay();
      }
      package com.bjpowernode.demo;

      /**
      * 中国人类
      */
      public class ChinaPeople extends People {
      @Override
      public void play() {

      }
      }
  2. 重构程序,将“第11章-多态”的people项目中的People类重构成抽象类,将适当的方法重构成抽象方法,并添加一个新加坡类,并进行测试

  3. 重构程序,将“第11章-多态”的school项目中的Employee类重构成抽象类,将适当的方法重构成抽象方法,并添加两种销售员工,都具有销售人员的特性,具体要求如下

    • 电话销售:具有拨出电话数量;汇报工作时,保留销售人员汇报逻辑,添加汇报时输出本月拔出电话信息
    • 网络销售:具有发帖数量、获得线索数量;汇报工作时,不保留销售人员汇报信息,汇报时输出本月汇报网络发帖数、获得线索数量
  4. 【扩展,此题阅读懂代码即可,代码见附件】编写程序,模拟kfc服务人员制作各种套餐的流程,kfc能够制作出单个的食物,如鸡肉汉堡、牛肉汉堡、薯条、老北京、烤翅、原味鸡、薯条、可乐、雪碧、咖啡等;并且会将这些食物组合以套餐形式售卖,如鸡肉汉堡套餐、下午茶套餐、原味鸡套餐等。程序设计和实现如下

    1. 食物系列,设计抽象类AbstractFood,由所有食物抽象而来,用于被其他食物继承

      1. 具有名称、价格、备注属性
      2. 具备显示食物内容的方法toString

      注意:这样定义,是为了让后面的套餐中,只要知道套餐由多种食物组成,而无需关注是具体的哪类食物,以降低复杂度,让制作套餐变得简单

    2. 套餐系列,设计抽象类AbstractSuite,由所有套餐抽象而来,用于被其他套餐继承

      1. 具有套餐价格、食物列表(AbstractFood[])属性

      2. 具备制作所有单个食物的能力,并将这些能力给到每个实际套餐子类共用

      3. 具备制作套餐的行为规范,具体制作的套餐内容由每个实际套餐类覆盖;且在实际套餐子类制作具体套餐时,确定由哪些食物组合而成;对于部分食物,还需要用户参与选择的,提供选择功能,如可乐是否要加冰

        注意:这样定义,使用套餐具备更大的灵活性;当增加一种新的食物时,只需要在抽象类AbstractSuite中添加一个制作方法,子类都可以直接使用;当新增一种套餐时,直接使用父类AbstractSuite的食物制作方法组合成套餐即可

      套餐举例:

      • 鸡肉汉堡套餐:包含鸡肉汉堡、薯条、可乐各一份
      • 牛肉汉堡套餐:包含牛肉汉堡、薯条、雪碧各一份
      • 原味鸡套餐:包含原味鸡五份
      • 下午茶套餐:包含原味鸡一份、薯条两份、雪碧一份
      • ...
    3. 服务员类,提供创建套餐功能,根据用户选择的套餐名称,创建具体的套餐

    4. 用户选择套餐类,提供套餐选择菜单,让用户指定需要的套餐名称,交由服务员类完成套餐创建,再自动完成套餐制作,最后显示套餐内容、套餐价格,完成出餐