回顾
this.score = score;- 左边
this.score:当前对象的成员属性。 - 右边
score:构造方法或普通方法接收到的参数,不是对象属性。 如果写成:
score = score;相当于参数给自己赋值,对象的成绩属性仍为默认值 0。
static 表示成员属于类,所有对象共享一份;不代表所有位置都能访问,访问范围仍由 private/public 决定。
学习内容
- 继承
extends - 父类与子类
super- 方法重写
- 多态
掌握:
extends、super、方法重写以及父类引用指向子类对象。 问题:重载与重写的区别、多态的实际执行方法、父子类构造顺序。
模块一:继承
继承表示一个类可以在已有类的基础上继续扩展。 例如:
public class Account { private String username = "admin";
public String getUsername() { return username; }
public void login() { System.out.println(username + "登录系统"); }}管理员账户也是一种账户,因此可以继承 Account:
public class AdminAccount extends Account { public void manageUsers() { System.out.println(getUsername() + "管理用户"); }}测试类:
public class InheritanceDemo { public static void main(String[] args) { AdminAccount admin = new AdminAccount();
admin.login(); admin.manageUsers(); System.out.println(admin.getUsername()); }}运行结果:
admin登录系统admin管理用户admin这里 AdminAccount 自己只定义了 manageUsers(),但对象还能调用父类提供的 login() 和 getUsername()。
实际开发中可以表达:
AdminAccount 是一种 AccountJavaDeveloper 是一种 EmployeeDog 是一种 Animal继承适合表达“子类是一种父类”的关系,也就是 is-a 关系。
常见错误:
- 为了复用几行代码,强行建立没有
is-a关系的继承。 - 子类直接访问父类的
private属性。 - 把
extends理解成复制粘贴父类代码。 - Java 类只能直接继承一个父类。
模块二:super
父类中如果使用了构造方法 子类中需要使用super进行辅助初始化 形式:
super(name);调用父类构造方法,必须位于子类构造方法的第一条有效语句。 为什么需要: 父类只有这个构造方法:
public Employee(String name) { this.name = name;}没有无参数构造方法:
public Employee() {}如果子类构造方法不写 super(name),编译器会尝试自动添加:
super();但是父类没有 Employee(),所以会编译失败。
super 调用父类普通方法
super 不仅能调用父类构造方法,还能调用父类的方法:
@Overridepublic void work() { super.work(); System.out.println( getName() + "正在编写Java后端代码,技能:" + getSkill() );}这里:
super.work();明确调用父类 Employee 的 work()。
因此会依次输出:
员工正在工作张三正在编写Java后端代码,技能:Spring Bootsuper(name):调用父类构造方法。super.work():调用父类普通方法。
方法重写 Override
父类已经提供一个方法,但子类需要不同的实现时,可以重写这个方法。 父类:
public class Employee { public void work() { System.out.println("员工正在工作"); }}子类重写:
public class JavaDeveloper extends Employee { @Override public void work() { System.out.println("Java开发人员正在编写代码"); }}调用:
JavaDeveloper developer = new JavaDeveloper(...);developer.work();执行的是子类重写后的 work()。
@Override 是什么?
它告诉编译器:
我准备重写父类的方法,请检查我的写法是否正确。
@Overridepublic void works() {}编译器会立即报错,防止你以为自己完成了重写。因此实际开发中,重写方法时应主动添加
@Override 只是帮助编译器检查,不决定一个方法是不是重写。即使把正确的 work() 上面的 @Override 删除,只要方法签名符合要求,它仍然是重写,只是失去了编译器的额外检查。
@Override。
重写的基本要求
子类方法需要与父类方法保持一致:
- 方法名相同
- 参数列表相同
- 返回类型相同或符合兼容规则
- 子类的访问权限不能比父类更严格
例如父类是
public,子类不能改成private。
为什么需要方法重写?
不同员工都可以“工作”,但工作方式不同:
Employee:处理普通工作JavaDeveloper:编写Java后端代码Designer:设计页面外部代码可以统一调用 work(),具体行为由不同子类决定。这会成为后面“多态”的基础。
模块四:多态
Employee是父类,JavaDeveloper是子类
Employee employee = new JavaDeveloper("张三", "Spring Boot");
| 内容 | 类型 |
|---|---|
| employee 的声明类型 | Employee(父类) |
| 实际创建的对象类型 | JavaDeveloper(子类) |
| 创建的对象数量 | 1个 |
两条核心规则
第一条:编译时看左边。
编译器根据声明类型 Employee 判断可以调用哪些方法。因此只能直接调用 Employee 中已经声明的方法:
employee.getName(); // 可以employee.clockIn(); // 可以employee.work(); // 可以employee.getSkill(); // 不可以虽然实际对象中存在 getSkill(),但是 Employee 类型中没有这个方法,所以编译器不允许调用。
而且 skill 是子类的 private 属性,更不能直接访问。
第二条:运行时看右边。
Employee 和 JavaDeveloper 都有 work(),而实际对象是 JavaDeveloper,所以:
employee.work();运行的是子类重写后的版本。 可以记成:
能调用什么:看左边的声明类型实际执行哪个重写方法:看右边的对象类型如果确实需要直接使用子类特有的 getSkill(),目前可以声明为:
JavaDeveloper developer = new JavaDeveloper("张三", "Spring Boot"); developer.getSkill();
向下类型转换和安全判断会在后续结合 instanceof 学习。
问题:
假设 JavaDeveloper 重写了 work(),但没有重写 clockIn():
Employee employee = new JavaDeveloper("李四", "MySQL");
employee.getName();employee.clockIn();employee.work();employee.getSkill();clockIn()执行父类还是子类版本? 执行父类Employee中的Kens clockIn()。 原因是 JavaDeveloper没有重写clockIn()`,它只是继承了父类现有的实现。实际对象是子类,不代表所有方法都会变成子类版本;只有被子类重写的方法才会执行子类版本。
clockIn():没有重写 → 使用父类实现work():已经重写 → 使用子类实现