跳到主要内容

第10章-继承

概述

  • 生活场景

    • 父母与子女的遗传关系,很多人,跟父亲或是母亲像一个模子刻出来的一般,遗传了父母的样貌、脾气、饮食习惯等。正所谓 龙生龙,凤生凤,老鼠的儿子会打洞 ---这是典型的继承

    • 动物界的继承关系。---这也是典型的继承

      继承-动物继承关系

    • 实际软件项目中示例的继承关系。---这更是我们根据需要设计的继承关系

      面向对象基础-示例

    • ...

  • 继承是广泛存在于现在生活中的实际需要和场景

  • 继承是通过类与类之间实现,是更高层次的抽象

  • 继承是从已有的类中派生出新的类,新的类能继承已有类的属性和方法,并能扩展新的属性和方法

  • 新的类称为派生类、子类、扩展类;被继承类称为基类、父类、超类

定义继承类

  • 继承使用关键字extends实现

  • 语法规则如下

    访问修饰符 [其他修饰符] class 子类名称 extends 父类名称{  
    //类体,包括子类特有的属性、方法
    }

    语法解释:

    • extends:类继承关键字
    • 父类名称:在子类定义时确定,子类名称后通过extends关键字标识了该类的父类
    • 其他与定义类的语法相同
  • 应用实例

    • 实例1,定义继承类,参考面向对象示例图,定义工作人员类Employee,再定义老师类,老师类继承于工作人员类
      • 父类工作人员类Employee代码,见本章inheritance项目下的文件Employee.java
      • 子类老师类Teacher代码,见本章inheritance项目下的文件Teacher.java

使用继承类

  • 跟使用普通类一样
  • 但,可以使用从父类那继承的、可访问的属性与方法
  • 应用实例
    • 实例1,使用继承类
      • 测试类SchoolTest代码,演示通过子类对象对自己定义方法和从父类继承方法的调用,见本章inheritance项目下的文件SchoolTest.java
      • 输出结果,执行执行输出

优点

  • 符合现实世界的业务需要
  • 提高了代码复用性 ,将公共部分提取到父类中,多个子类继承于父类,都具备了父类公共部分的特点
  • 代码的维护性更高,公共部分逻辑如果有变更,只要改一个地方,所以子类也跟着变化,无需修改
  • 多态的基础,可以在子类中对继承的方法体现不同的行为;下章讲解

继承规则

  • 单继承:Java是单继承,即一个子类只能继承于一个父类;下面的代码是不符合规则的:

    public class Teacher extends Employee,Object {
    }
  • 先构造父类、再构造子类:创建子类对象之前会先初始化父类属性初始化,再进行子类初始化;即,先调用父类构造方法,再调用子类构造方法,参考下面的代码中父类构造方法、子类构造方法的输出顺序:

    • 父类

      package com.bjpowernode.demo.school;

      /**
      * 工作人员类
      */
      public class Employee {
      //姓名,private修饰的属性,只能在当前类对象可以访问,子类对象不可以
      private String name;

      public String getName() {
      return name;
      }

      public void setName(String name) {
      this.name = name;
      }

      /**
      * 默认无参构造
      */
      public Employee() {
      System.out.println("父类--工作人员类Employee默认无参构造完成。");
      }

      /**
      * 有参构造
      *
      * @param name 姓名
      */
      public Employee(String name) {
      this.name = name;
      System.out.println("父类--工作人员类Employee有参构造完成。构造参数姓名:" + name);
      }

      /**
      * 验证姓名,private修饰的方法,只能在当前类对象可以访问,子类对象不可以
      * @return 验证是否通过
      */
      private boolean validateName() {
      boolean result = false;

      if (this.name != null && this.name.contains("贤")) {
      result = true;
      }

      return result;
      }
      }
    • 子类

      package com.bjpowernode.demo.school;

      /**
      * 老师类,继承于工作人员类
      */
      public class Teacher extends Employee {
      /**
      * 无参构造方法,会先默认调用父类的无参构造
      */
      public Teacher(){
      System.out.println("子类--老师类Teacher默认无参构造完成。");
      }

      /**
      * 有参构造方法
      * @param name
      */
      public Teacher(String name) {
      //通过super关键字指定父类的构造方法;不然,会使用父类默认的无参构造方法;注意:super()必须在第一行
      super(name);

      System.out.println("子类--老师类Teacher有参构造完成。构造参数姓名:" + name);
      }
      }
    • 测试类

      package com.bjpowernode.demo;

      import com.bjpowernode.demo.school.Employee;
      import com.bjpowernode.demo.school.Teacher;

      /**
      * 学校业务测试类
      */
      public class SchoolTest {
      /**
      * 入口方法
      *
      * @param args
      */
      public static void main(String[] args) {
      System.out.println("----------使用父类默认无参构造方法----------");
      //【使用默认无参构造方法】,定义老师类型引用,并指定老师对象,观察输出内容顺序
      Teacher teacher1 = new Teacher();

      System.out.println("----------使用父类有参构造方法----------");
      //【使用有参构造方法】,定义老师类型引用,并指定老师对象,观察输出内容顺序
      Teacher teacher2 = new Teacher("赵四");
      }
      }
    • 输出

      继承-构造方法顺序

  • 不能访问父类private的属性与方法:子类不能访问父类的private修改的属性、方法;上一点的子类Teacher类中构造方法、普通方法中,都不能通过this.name访问父类定义的private修饰的属性name,只能通过父类定义的public修饰的方法getName、setName访问;上一点中的validateName方法也是如此

  • 可以有无限的继承层次:Java虽然不支持多继承,但多层次继承是允许的,即层次为下面这种层次:子祖先类->...->太爷类->爷爷类->父类->子类->...

  • 默认继承于Object:一个类如果不继承任何类,默认继承java.lang.Object,Object是Java中的祖宗类;则如下代码符合规则

    //Object类型的引用,可以指向任意的对象;同时也是父类的引用指向子类的对象
    Object object = new Employee();
  • 父类的静态代码块优先执行:如果父类、子类都有static修饰的静态代码,父类的静态代码块先被执行

【练习】

  1. 练习实例内容,完成代码编写;掌握继承特性

super

概述

  • 在对象内部存在,指代当前对象的父类的一个关键字
  • 定义于类(模板)中,但应用在实际的对象中
  • 一般在普通方法和构造方法中使用
    • 普通方法中,用于调用父类属性或方法,直接通过“super.属性”、"super.方法名()"调用,简单、快捷;但不能单独使用super关键字
    • 构造方法中放置于第1行,用于调用父类构造方法,直接通过“super(参数列表)”调用父类存在的构造方法,用于优先构造父类,减少多个构造方法中重复的初始化代码;注意,”super(参数列表)“必须在构造方法的第一行
  • 不能在static修饰的静态方法中使用

应用实例

  • 实例1,在普通方法中使用super
    • 老师类Teacher.goByTrain方法中的“super.eatFood("火车豪华米饭套餐")”方法调用,见本章inheritance项目下的文件Teacher.java
  • 实例2,在构造方法中使用super
    • 老师类Teacher构造方法中的"super(name, sex, age)"方法调用,见本章inheritance项目下的文件Teacher.java

【练习】

  1. 在普通方法里面,使用下面的代码正确吗?

    //输出super
    System.out.println(super);
  2. 下面的代码会输出什么?如果使用operate方法更合理?

    父类

    package com.bjpowernode.demo;

    /**
    * 父类
    */
    public class Parent {
    /**
    * 操作
    *
    * @param one 操作数1
    * @param two 操作数2
    */
    public long operate(long one, long two) {
    return one + two;
    }
    }

    子类

    package com.bjpowernode.demo;

    /**
    * 子类,继承于Parent
    */
    public class Son extends Parent {
    /**
    * 操作
    *
    * @param one
    * @param two
    */
    public int operate(int one, int two) {
    return one * two;
    }

    /**
    * 调用操作方法
    */
    public void invoke() {
    byte a = 3;
    byte b = 5;

    //此处会输出什么?
    System.out.println(operate(a, b));
    }

    /**
    * 入口方法
    *
    * @param args
    */
    public static void main(String[] args) {
    Son son = new Son();
    son.invoke();
    }
    }
  3. 练习实例内容,完成代码编写;掌握super关键字的使用

Object类

概述

  • Object是所有类的祖先类
  • 任意的类不管是否继承于其他类,都具备Object类的方法
  • Object类中有很多重要的方法,基础阶段要了解的有
    • toString方法,输出对象的字符串形式
    • equals方法,类对象的比较方式;比如,工作人员类中,如果想让两个姓名相同的对象就认为是同一个对象,就覆盖equals方法(覆盖在多态中会讲)
    • hashCode方法,输出对象的唯一标识
    • finalize方法,对象资源释放方法,主要针对非GC(垃圾回收机制)处理的资源,但释放时间由GC确定,程序不能控制

toString方法

  • 返回对象的字符串表达形式

  • 很多类会覆盖该方法

  • 语法规则

    //默认的实现方式
    public String toString() {
    return super.toString();
    }
  • 应用实例

    • 应用实例1,自定义toString方法
      • 老师类Teacher.toString方法实现,见本章inheritance项目下的文件Teacher.java

hashCode方法

  • 返回唯一标识一个类整数,对象的唯一标识
  • 如果要作对象比较时,会覆盖该方法

equals方法

  • 返回该类型的两个对象比较的结果,是个布尔型
  • 如果比较的两个对象相同(业务上的相同),返回true,如果不同,返回false,逻辑按业务确定
  • 默认比较的是地址是否指向同两个位置

finalize方法

  • JVM对该对象进行垃圾回收时,会调用的方法
  • 调用时间不可控,可使用System.gc()强制进行垃圾回收
  • 一般回收非Jvm垃圾回收机制可控的资源,你C++调用的内容等

实战和作业

  1. 编写程序,要求如下:

    1. 定义动物类,具有名字属性,具备移动方法

    2. 定义鱼类,具有名字、颜色属性,具备移动、冒泡方法

    3. 定义鸟类,具有名字、是否迁徙属性,具备移动、下蛋方法

      注意:使用继承,让代码具有复用性

  2. 编写程序,完成下图面向对象基础示例中的类定义,包括老师类和销售人员类,注意使用继承

    面向对象基础-示例

  3. 【扩展】编写一个模拟项目管理程序,要求

    1. 定义项目类Project,
      1. 具有项目ID、项目名称等属性
      2. 具备启动项目start方法,输出”xxx项目启动...“
      3. 具备异常中止项目abort方法,输出”xxx项目中止!“
      4. 具备完成项目complete方法,输出”xxx项目正常完成。“
    2. 定义员工类Employee,
      1. 具有工号、姓名、性别、工龄、所属项目(Project类型)等属性
      2. 具备工作、睡觉方法
    3. 企业主要有三类员工,即Employee的子类,分别是开发人员类Developer、测试人员类Tester、项目经理类Manager,分别有如下要求
      1. 开发人员类,
        1. 具有技术栈属性
        2. 具备编程coding方法,输出”开发人员xxx正在使用xxx技术栈开发xxx项目“
        3. 具备代码检查review方法,返回结果为是否通过,方法内部逻辑自己确定
      2. 测试人员类,
        1. 具有测试工具属性
        2. 具备项目测试test方法,输出”测试人员xxx正在使用xxx工具测试xxx项目“
        3. 具备出具项目测试报告issueTestReport方,返回参数为整数型,其中:1代表测试通过可以发布,2代表修复严重bug后可以发布,9表示测试不通过不可以发布,方法内部逻辑自已确定
      3. 项目经理类,
        1. 具有管理年限属性
        2. 具备启动项目startProject方法,并调用所属项目属性的start方法
        3. 具备中止项目abortProject方法,并调用所属项目属性的abort方法
        4. 具备中止项目completeProject方法,并调用所属项目属性的complete方法
    4. 定义ProjectTest类,用于测试上述三类员工,实现每个自身方法的测试和父类方法的测试