第10章-继承
概述
生活场景
父母与子女的遗传关系,很多人,跟父亲或是母亲像一个模子刻出来的一般,遗传了父母的样貌、脾气、饮食习惯等。正所谓 龙生龙,凤生凤,老鼠的儿子会打洞 ---这是典型的继承
动物界的继承关系。---这也是典型的继承
实际软件项目中示例的继承关系。---这更是我们根据需要设计的继承关系
...
继承是广泛存在于现在生活中的实际需要和场景
继承是通过类与类之间实现,是更高层次的抽象
继承是从已有的类中派生出新的类,新的类能继承已有类的属性和方法,并能扩展新的属性和方法
新的类称为派生类、子类、扩展类;被继承类称为基类、父类、超类
定义继承类
继承使用关键字extends实现
语法规则如下
访问修饰符 [其他修饰符] class 子类名称 extends 父类名称{
//类体,包括子类特有的属性、方法
}语法解释:
- extends:类继承关键字
- 父类名称:在子类定义时确定,子类名称后通过extends关键字标识了该类的父类
- 其他与定义类的语法相同
应用实例
- 实例1,定义继承类,参考面向对象示例图,定义工作人员类Employee,再定义老师类,老师类继承于工作人员类
- 父类工作人员类Employee代码,见本章inheritance项目下的文件Employee.java
- 子类老师类Teacher代码,见本章inheritance项目下的文件Teacher.java
- 实例1,定义继承类,参考面向对象示例图,定义工作人员类Employee,再定义老师类,老师类继承于工作人员类
使用继承类
- 跟使用普通类一样
- 但,可以使用从父类那继承的、可访问的属性与方法
- 应用实例
- 实例1,使用继承类
- 测试类SchoolTest代码,演示通过子类对象对自己定义方法和从父类继承方法的调用,见本章inheritance项目下的文件SchoolTest.java
- 输出结果,执行执行输出
- 实例1,使用继承类
优点
- 符合现实世界的业务需要
- 提高了代码复用性 ,将公共部分提取到父类中,多个子类继承于父类,都具备了父类公共部分的特点
- 代码的维护性更高,公共部分逻辑如果有变更,只要改一个地方,所以子类也跟着变化,无需修改
- 多态的基础,可以在子类中对继承的方法体现不同的行为;下章讲解
继承规则
单继承: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修饰的静态代码,父类的静态代码块先被执行
【练习】
- 练习实例内容,完成代码编写;掌握继承特性
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
【练习】
在普通方法里面,使用下面的代码正确吗?
//输出super
System.out.println(super);下面的代码会输出什么?如果使用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();
}
}练习实例内容,完成代码编写;掌握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
- 应用实例1,自定义toString方法
hashCode方法
- 返回唯一标识一个类整数,对象的唯一标识
- 如果要作对象比较时,会覆盖该方法
equals方法
- 返回该类型的两个对象比较的结果,是个布尔型
- 如果比较的两个对象相同(业务上的相同),返回true,如果不同,返回false,逻辑按业务确定
- 默认比较的是地址是否指向同两个位置
finalize方法
- JVM对该对象进行垃圾回收时,会调用的方法
- 调用时间不可控,可使用System.gc()强制进行垃圾回收
- 一般回收非Jvm垃圾回收机制可控的资源,你C++调用的内容等
实战和作业
编写程序,要求如下:
定义动物类,具有名字属性,具备移动方法
定义鱼类,具有名字、颜色属性,具备移动、冒泡方法
定义鸟类,具有名字、是否迁徙属性,具备移动、下蛋方法
注意:使用继承,让代码具有复用性
编写程序,完成下图面向对象基础示例中的类定义,包括老师类和销售人员类,注意使用继承
【扩展】编写一个模拟项目管理程序,要求
- 定义项目类Project,
- 具有项目ID、项目名称等属性
- 具备启动项目start方法,输出”xxx项目启动...“
- 具备异常中止项目abort方法,输出”xxx项目中止!“
- 具备完成项目complete方法,输出”xxx项目正常完成。“
- 定义员工类Employee,
- 具有工号、姓名、性别、工龄、所属项目(Project类型)等属性
- 具备工作、睡觉方法
- 企业主要有三类员工,即Employee的子类,分别是开发人员类Developer、测试人员类Tester、项目经理类Manager,分别有如下要求
- 开发人员类,
- 具有技术栈属性
- 具备编程coding方法,输出”开发人员xxx正在使用xxx技术栈开发xxx项目“
- 具备代码检查review方法,返回结果为是否通过,方法内部逻辑自己确定
- 测试人员类,
- 具有测试工具属性
- 具备项目测试test方法,输出”测试人员xxx正在使用xxx工具测试xxx项目“
- 具备出具项目测试报告issueTestReport方,返回参数为整数型,其中:1代表测试通过可以发布,2代表修复严重bug后可以发布,9表示测试不通过不可以发布,方法内部逻辑自已确定
- 项目经理类,
- 具有管理年限属性
- 具备启动项目startProject方法,并调用所属项目属性的start方法
- 具备中止项目abortProject方法,并调用所属项目属性的abort方法
- 具备中止项目completeProject方法,并调用所属项目属性的complete方法
- 开发人员类,
- 定义ProjectTest类,用于测试上述三类员工,实现每个自身方法的测试和父类方法的测试
- 定义项目类Project,