20260722-笔记.md 30 KB

20260722 课堂笔记 — 继承与方法重写

日期:2026-07-22
项目:c260722
作者:WanJL


一、继承(Inheritance)

1. 继承的概念

文件:Demo01.java

继承是 Java 三大特征之一(封装、继承、多态)。继承可以让子类具有父类的属性和方法,还可以在子类中重新定义,以及追加属性和方法。

继承的关键字是 extends,直译是「扩展」,含义是子类在父类的基础上进行扩展。

核心思想:

  • 子类具有父类的特征
  • 父类是根据多个具有相同特征的子类所抽取出来的公共部分

类的层次结构示例:

动物 类
  |- 哺乳动物 类
     |- 猫科动物 类
        |- 猫 类
        |- 狮子 类
        |- 老虎 类
     |- 犬科动物 类
        |- 狼 类
        |- 狗 类
  |- 爬行动物 类
  |- 两栖动物 类

2. 继承的格式

文件:Demo01.java

class 子类 extends 父类 {
    // 子类自己的成员
}

示例:

class Animal {                  // 父类
}

class Bird extends Animal {     // 子类 Bird 继承 Animal
}

class Penguin extends Bird {    // 子类 Penguin 继承 Bird(多层继承)
}

3. 继承体系示例

文件:Animal.java、Bird.java、Penguin.java

// 父类 Animal —— 定义所有动物共有的行为
public class Animal {
    public void eat() {
        System.out.println("吃......");
    }
    public void sleep() {
        System.out.println("睡......");
    }
}

// 子类 Bird —— 继承 Animal,追加 fly 方法
public class Bird extends Animal {
    public void fly() {
        System.out.println("飞.......");
    }
}

// 子类 Penguin —— 继承 Bird(多层继承)
public class Penguin extends Bird {
    // 继承了 Animal 的 eat()、sleep() 和 Bird 的 fly()
}

4. 继承的使用

文件:Test01.java

public class Test01 {
    public static void main(String[] args) {
        // 父类对象只能使用自己的方法
        Animal animal = new Animal();
        animal.eat();
        animal.sleep();

        // Bird 对象可以使用父类 Animal 的方法 + 自己的方法
        Bird bird = new Bird();
        bird.eat();     // 继承自 Animal
        bird.sleep();   // 继承自 Animal
        bird.fly();     // 自己的方法

        // Penguin 对象可以使用所有继承来的方法
        Penguin p = new Penguin();
        p.eat();        // 继承自 Animal → Bird → Penguin
        p.sleep();      // 继承自 Animal → Bird → Penguin
    }
}

5. 继承的好处

文件:Demo01.java

好处 说明
代码复用性 多个类的相同成员可以放在同一个父类中,子类自动拥有
代码可维护性 父类方法修改一处,所有子类都继承修改后的版本
建立类与类的关系 产生父子类关系后,子类可使用父类中非私有化成员
// 如果修改 Animal 的 eat() 方法,所有子类都会生效
public class Animal {
    public void eat() {
        System.out.println("正在吃东西......");  // 改这里就行
    }
}

6. 继承的弊端——耦合性

文件:Demo01.java

继承让类和类之间产生了关系,导致耦合性增强:

  • 父类发生变化时,子类也不得不跟着变化
  • 子类依赖父类的存在,如果父类不存在,子类就报错
  • 这削弱了子类的独立性

耦合关系(Coupling):类与类之间的依赖程度。程序设计很多时候都在研究如何解耦。

7. 继承的使用场景——is...a 关系

文件:Demo01.java

使用继承前要考虑类和类之间是否存在 is...a 关系(谁是谁的一种):

正确(符合 is...a) 错误(不符合 is...a)
老师是人的一种 汽车是学生的一种 ✗
学生是人的一种
人是动物的一种
动物是生物的一种
// 正确的继承设计
class Person { }
class Student extends Person { }   // 学生是人的一种 ✓

// 错误的继承设计
class Car { }
class Student extends Car { }      // 学生不是汽车的一种 ✗

8. 方法重写(Method Override)

文件:Demo01.java、Bird.java、Penguin.java

当子类中出现与父类 完全相同的方法名、参数列表和返回值类型 的方法时,称为 方法重写(Override)。子类会覆盖父类的方法,默认调用子类的版本。

// 父类 Animal
public class Animal {
    public void eat() {
        System.out.println("吃......");
    }
}

// 子类 Bird —— 重写父类的 eat() 方法
public class Bird extends Animal {
    // @Override 是重写注解(可选),加上后编译器会检查该方法是否真的重写了父类方法
    // 即使不加 @Override,只要方法签名完全一致,也是重写
    public void eat() {
        System.out.println("鸟吃自己的.....");
    }
}

// 子类 Penguin —— 重写父类 Bird 的 fly() 方法
public class Penguin extends Bird {
    @Override   // 推荐加上,便于阅读和检查
    public void fly() {
        System.out.println("我不飞..我肚皮贴着冰面滑......");
    }
}

重写 vs 非重写:

情况 结果
子类方法与父类方法 签名完全一致 重写(Override)
子类方法与父类方法 方法名不同 子类特有的方法,不是重写

测试效果:

// Test01.java
public class Test01 {
    public static void main(String[] args) {
        Animal animal = new Animal();
        animal.eat();            // 吃......

        Bird bird = new Bird();
        bird.eat();              // 鸟吃自己的.....(重写后调用子类版本)

        Penguin p = new Penguin();
        p.fly();                 // 我不飞..我肚皮贴着冰面滑......(重写后调用子类版本)
    }
}

注意:重写发生在 子类和父类之间,方法签名必须完全一致(方法名 + 参数列表 + 返回值类型)。重写不是必须加 @Override 注解,但建议加上,因为它能让编译器帮助检查是否正确重写。


9. super 关键字

文件:Granddad.java、Dad.java、Son.java、Test02.java

super 关键字用于在子类中 引用父类的成员(方法或属性),主要用在以下场景:

使用方式 说明
super.父类方法() 调用父类中被重写的方法
super.父类属性 访问父类中与子类同名的属性

示例:多层链式调用 super.eat()

// Granddad.java —— 爷爷类(顶层父类)
public class Granddad {
    public void eat() {
        System.out.println("爷爷爱喝酒....");
    }
}

// Dad.java —— 父亲类(继承 Granddad)
public class Dad extends Granddad {
    public void eat() {
        super.eat();               // 先调用爷爷的 eat()
        System.out.println("爸爸爱吃肉....");
    }
}

// Son.java —— 儿子类(继承 Dad)
public class Son extends Dad {
    public void eat() {
        super.eat();               // 先调用爸爸的 eat()(爸爸内部又会调用爷爷的 eat())
        System.out.println("儿子不吃饭....");
    }
}

测试效果:

// Test02.java
public class Test02 {
    public static void main(String[] args) {
        Son s = new Son();
        s.eat();
    }
}

预期输出:

爷爷爱喝酒....
爸爸爱吃肉....
儿子不吃饭....

调用链分析:Son.eat() → super.eat() 调用 Dad.eat() → Dad.eat() 中的 super.eat() 调用 Granddad.eat() → 输出"爷爷爱喝酒...." → 回到 Dad 输出"爸爸爱吃肉...." → 回到 Son 输出"儿子不吃饭...."。形成了方法的 链式调用。


10. 同名属性的处理

文件:Bird.java、Penguin.java、Test01.java

当子类和父类定义了 同名的成员变量 时,两个变量会 同时存在 于子类对象中。访问规则如下:

访问方式 访问的是
变量名 或 this.变量名 子类自己 的属性
super.变量名 父类 的属性

示例:

// Bird.java —— 父类中定义了 s2(默认权限)、s3(protected 权限)
public class Bird extends Animal {
    String s2;                // 默认权限
    protected String s3;      // protected 权限
}

// Penguin.java —— 子类中定义了同名的 s2、s3
public class Penguin extends Bird {
    String s3;                // 与父类 Bird 的 s3 同名

    @Override
    public void fly() {
        System.out.println("调用父类的s2属性:" + super.s2);   // 访问 Bird 的 s2
        System.out.println("调用自己的s2属性:" + this.s2);    // 访问 Penguin 的 s2(如果没有则继承 Bird 的)
        System.out.println("调用父类的s3属性:" + super.s3);   // 访问 Bird 的 s3
        System.out.println("调用自己的s3属性:" + this.s3);    // 访问 Penguin 的 s3
    }
}

测试效果:

// Test01.java
Penguin p = new Penguin();
p.s3 = "哈哈哈哈";      // 设置 Penguin 自己的 s3
p.s2 = "s2222222";      // 设置 Bird 的 s2(Penguin 没有同名 s2,所以直接继承使用)
p.fly();

总结:如果子类没有定义同名的属性,this.属性 和 super.属性 访问的是同一个父类属性。如果子类定义了同名属性,则 this 访问自己的,super 访问父类的。


11. Java 继承的限制

文件:Demo01.java

Java 的继承机制有以下重要限制:

① 单继承(Single Inheritance)

Java 中一个子类 只能有一个直接父类,不支持 C++ 中的多继承。

// ❌ 错误:Java 不支持多继承
class C extends A, B {  // 编译报错!
}

// ✅ 正确:一个子类只能有一个直接父类
class B extends A {  // 正确
}

② 多层继承(Multi-level Inheritance)

Java 支持多层继承,即一个类的父类可以继承自其他类。

// ✅ 多层继承:Son → Dad → Granddad
class Granddad { }
class Dad extends Granddad { }     // Dad 的直接父类是 Granddad
class Son extends Dad { }          // Son 的直接父类是 Dad
                                   // Son 的间接父类是 Granddad
概念 说明
直接父类 子类通过 extends 直接继承的类(如 Son 的 Dad)
间接父类 子类的父类的父类……(如 Son 的 Granddad)

③ Object——所有类的根父类

文件:Demo01.java

Java 中 所有的类都默认继承自 Object 类。如果一个类没有显式使用 extends 关键字,它也会隐式继承 Object。

// 以下两种写法等价:
public class Animal { }                // 隐式继承 Object
public class Animal extends Object { } // 显式继承 Object

Object 类中定义了一些所有对象都具备的基本方法(如 toString()、equals()、hashCode() 等)。


12. 访问修饰符在继承中的体现

文件:Bird.java

子类能访问的父类成员范围取决于父类成员的访问修饰符:

修饰符 本类 同包子类 异包子类 所有类
private ✅ ❌ ❌ ❌
默认(缺省) ✅ ✅ ❌ ❌
protected ✅ ✅ ✅ ❌
public ✅ ✅ ✅ ✅
public class Bird extends Animal {
    private String s1;      // 私有的 —— 仅本类可访问
    String s2;              // 默认的 —— 同包可访问
    protected String s3;    // protected —— 同包 + 子类可访问
    public String s4;       // 公共的 —— 所有类可访问
}

13. 继承中变量的访问特点——就近原则

文件:Demo02.java、Son.java

在子类中访问一个变量时,采用 就近原则 查找:

查找顺序 查找范围 示例(Son 中访问 a)
1️⃣ 子类的 局部范围(方法内部) int a = 10;(局部变量)
2️⃣ 子类的 成员范围(成员变量) int a;(子类成员变量)
3️⃣ 父类 的成员范围 int c;(Dad 的成员变量)
4️⃣ 父类的父类……直到 Object int d;(Granddad 的成员变量)
❌ 如果都没找到 编译报错
// Son.java —— 演示就近原则
public class Son extends Dad {
    int a;          // 子类的成员变量(第2层)
    int b;          // 子类的成员变量(第2层)

    public void eat() {
        int a = 10;   // 局部变量(第1层)
        super.eat();

        System.out.println(a);  // 10  —— 局部范围找到 a
        System.out.println(b);  // 0   —— 局部无 b → 子类成员找到 b
        System.out.println(c);  // 0   —— 子类无 c → 父类 Dad 找到 c
        System.out.println(d);  // 0   —— 父类无 d → 祖父 Granddad 找到 d
    }
}

// Granddad.java —— 顶层父类定义了 d
public class Granddad {
    int d;
    public void eat() {
        System.out.println("爷爷爱喝酒....");
    }
}

// Dad.java —— 父类定义了 c
public class Dad extends Granddad {
    int c;
    public void eat() {
        super.eat();
        System.out.println("爸爸爱吃肉....");
    }
}

总结:变量的查找路径为 局部 → 本类成员 → 父类成员 → 祖父类成员 → … → Object。如果一直找到 Object 都没有,则编译报错。


14. this vs super 完整对比

文件:Demo02.java

this 和 super 是 Java 中两个非常重要的关键字:

关键字 含义 示例
this 代表 本类 对象的引用 this.name、this.eat()、this()
super 代表 父类 对象的引用 super.name、super.eat()、super()

在三种场景中的使用对比:

场景 this 用法 super 用法
成员变量 this.成员变量 —— 访问本类的成员变量 super.成员变量 —— 访问父类的成员变量
成员方法 this.成员方法() —— 调用本类的成员方法 super.成员方法() —— 调用父类的成员方法
构造方法 this() / this(参数) —— 调用本类的其他构造方法 super() / super(参数) —— 调用父类的构造方法
// Demo02.java(注释中总结)
/*
this 代表本类对象的引用
super 代表父类对象的引用

成员变量中:
    this.成员变量  --> 访问本类的成员变量
    super.成员变量 --> 访问父类的成员变量
成员方法中:
    this.成员方法  --> 调用本类的成员方法
    super.成员方法 --> 调用父类的成员方法
构造方法中:
    this()      --> 访问本类的无参构造方法
    this(参数)   --> 访问本类的有参构造方法
    super()      --> 访问父类的无参构造方法
    super(参数)   --> 访问父类的有参构造方法
*/

注意:this() 和 super() 都必须写在构造方法的 第一行,且 不能同时出现。

// Zi.java —— 演示 super() 和 this() 不能共存
public class Zi extends Fu {
    public Zi(String a) {
        super();    // ✅ 正确:放在第一行
        // this();  // ❌ 错误:如果取消注释,会和 super() 冲突
    }
}

15. 继承中构造方法的特点

文件:Demo02.java、Animal.java、Dog.java、Test03.java、Fu.java、Zi.java、Person.java、Student.java

15.1 子类构造方法默认调用父类无参构造

文件:Animal.java、Dog.java、Test03.java

子类的所有构造方法(有参或无参)都会 默认调用父类的无参构造方法。每个子类构造方法的 第一句 默认都是 super()。

// Animal.java —— 父类:添加无参构造方法打印信息
public class Animal {
    public Animal() {
        System.out.println("动物类的无参构造方法");
    }
}

// Dog.java —— 子类:没有写任何构造方法,默认有无参构造
public class Dog extends Animal {
    // 隐藏的默认构造方法:
    // public Dog() {
    //     super();  // 自动调用父类的无参构造
    // }
}

// Test03.java —— 测试
public class Test03 {
    public static void main(String[] args) {
        Dog d = new Dog();
    }
}

输出结果:

动物类的无参构造方法

原理:子类在初始化(创建对象)之前,一定要先完成对父类对象的创建。因为子类会继承父类的数据,可能还会使用父类的数据,所以必须先创建父类对象。这就是为什么每个子类构造方法的第一句默认都是 super()。

15.2 父类没有无参构造时的处理

文件:Fu.java、Zi.java、Person.java、Student.java

情况一:父类 没有 无参构造方法(只有有参构造),子类必须手动调用父类的有参构造。

// Fu.java —— 父类:只有有参构造
public class Fu {
    private String name;

    public Fu(String name) {   // 有参构造覆盖了默认无参构造
        this.name = name;
    }
}

// Zi.java —— 子类:必须手动调用父类的有参构造
public class Zi extends Fu {
    public Zi() {
        super("默认名");  // ❗ 必须手动调用父类的有参构造,否则编译报错
        System.out.println("子类构造方法");
    }
}

情况二:父类 有无参构造但被 private 修饰,子类也无法调用。

// Fu.java —— 父类有无参构造但是 private
// private Fu() { }   // private 构造方法,子类不可见

15.3 综合示例:Person → Student

文件:Person.java、Student.java

// Person.java —— 父类只有有参构造
public class Person {
    private String name;
    private Integer age;

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
}

// Student.java —— 子类必须手动调用 super(name, age)
public class Student extends Person {
    private String school;

    public Student(String name, Integer age, String school) {
        super(name, age);    // 调用父类的有参构造,传入参数
        this.school = school;
    }
}

核心规则:

  1. 子类构造方法 默认第一句 是 super()(调用父类无参构造)
  2. 如果父类 没有无参构造,子类必须通过 super(参数) 指定调用父类的有参构造
  3. 如果父类 有无参构造但是 private,子类同样无法调用,也需要指定其他构造
  4. super(参数) 必须在子类构造方法的 第一行

包名:exercise.studentSystem

课堂练习题

题目要求

使用构造方法完成完整的学生成绩管理系统,包含三个类:

Student 类(实体类)

文件:Student.java

public class Student {
    private int id;           // 学号
    private String name;      // 姓名
    private double javaScore; // Java 成绩
    private double mysqlScore;// MySQL 成绩

    // 全参构造方法
    public Student(int id, String name, double javaScore, double mysqlScore) {
        this.id = id;
        this.name = name;
        this.javaScore = javaScore;
        this.mysqlScore = mysqlScore;
    }

    // getter/setter...

    // 计算平均分的方法
    public double getAverage() {
        return (this.javaScore + this.mysqlScore) / 2;
    }
}

StudentSystem 类(业务类)

文件:StudentSystem.java

public class StudentSystem {
    private Student[] students;  // 学生数组
    private int count;           // 当前学生数量

    // 构造方法:初始化指定容量的学生数组
    public StudentSystem(int capacity) {
        students = new Student[capacity];
        count = 0;
    }

    // 添加学生到数组中
    public void addStudent(Student s) {
        if (count >= this.students.length) {
            System.out.println("数组已满,添加失败");
        } else {
            students[count] = s;
            count++;
        }
    }

    // 输出所有学生信息(含平均分)
    public void printAll() {
        for (Student s : students) {
            System.out.println("学号:" + s.getId() + ",姓名:" + s.getName()
                + ",Java成绩:" + s.getJavaScore() + ",MySQL成绩:" + s.getMysqlScore()
                + ",平均成绩:" + s.getAverage());
        }
    }

    // 按平均分降序排序(冒泡排序)
    public void sortByAverage() {
        for (int i = 0; i < students.length - 1; i++) {
            for (int j = 0; j < students.length - 1 - i; j++) {
                if (students[j].getAverage() < students[j + 1].getAverage()) {
                    Student temp = students[j];
                    students[j] = students[j + 1];
                    students[j + 1] = temp;
                }
            }
        }
    }

    // 返回平均分最高的学生对象
    public Student getTopStudent() {
        Student s = students[0];
        for (int i = 1; i < students.length; i++) {
            if (s.getAverage() < students[i].getAverage()) {
                s = students[i];
            }
        }
        return s;
    }
}

涉及知识点

知识点 说明
全参构造方法 创建对象时直接初始化所有属性
对象数组 存储多个学生对象
增强 for 循环 遍历数组输出信息
冒泡排序 按对象属性(平均分)排序
方法调用链 students[j].getAverage() 获取对象属性进行比较
数组满判断 count >= students.length 防止越界

三、课后作业

作业 1:猜数游戏(面向对象版)⭐️⭐️⭐️⭐️

包名:homework.p08_guessgame

文件:GuessGame.java、GuessGameTest.java

使用构造方法 + Random + 方法,将猜数字游戏改造为面向对象版本。

GuessGame 类:

public class GuessGame {
    private int target;     // 目标数字(随机生成)
    private int maxRange;   // 最大范围
    private int attempts;   // 已猜次数

    // 构造方法:生成随机目标数字
    public GuessGame(int maxRange) {
        Random random = new Random();
        this.maxRange = maxRange;
        this.attempts = 0;
        target = random.nextInt(this.maxRange) + 1;  // 1~maxRange
    }

    // 猜数字方法
    public String guess(int num) {
        this.attempts++;
        if (num > target) {
            return "猜大了";
        } else if (num < this.target) {
            return "猜小了";
        } else {
            return "猜对了";
        }
    }

    public int getAttempts() {
        return this.attempts;
    }
}

GuessGameTest 测试类:

public class GuessGameTest {
    public static void main(String[] args) {
        GuessGame gg = new GuessGame(100);
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.print("请输入你猜的数字:");
            int num = sc.nextInt();
            String guess = gg.guess(num);
            System.out.println(guess);
            if (guess == "猜对了") {  // 注意:字符串比较建议用 equals()
                break;
            }
        }
        System.out.println("恭喜你!一共猜了 " + gg.getAttempts() + " 次");
    }
}

作业 2:BookManager 图书管理系统 ⭐️⭐️⭐️⭐️

包名:homework.p09_bookmanager

文件:Book.java、BookManager.java、BookManagerTest.java

综合练习:构造方法 + 封装 + 对象数组 + 排序 + 查找

Book 类(实体类):

public class Book {
    private int id;        // 编号
    private String title;  // 书名
    private double price;  // 价格

    // 全参构造方法
    public Book(int id, String title, double price) {
        this.id = id;
        this.title = title;
        this.price = price;
    }
    // getter/setter...
}

BookManager 类(业务类):

public class BookManager {
    // 初始化 5 本图书
    public Book[] initBooks() {
        Book b1 = new Book(1001, "Java从入门到精通", 79.0);
        Book b2 = new Book(1002, "数据结构与算法", 65.0);
        Book b3 = new Book(1003, "设计模式之禅", 58.0);
        Book b4 = new Book(1004, "深入理解JVM", 89.0);
        Book b5 = new Book(1005, "计算机网络", 45.0);
        Book[] books = {b1, b2, b3, b4, b5};  // 静态初始化
        return books;
    }

    // 遍历所有图书
    public void printBooks(Book[] books) {
        for (Book b : books) {
            System.out.println("编号:" + b.getId() + ",书名:" + b.getTitle()
                + ",价格:" + b.getPrice());
        }
    }

    // 按价格从高到低排序(冒泡排序)
    public void sortByPrice(Book[] books) {
        for (int i = 0; i < books.length - 1; i++) {
            for (int j = 0; j < books.length - 1 - i; j++) {
                if (books[j].getPrice() < books[j + 1].getPrice()) {
                    Book temp = books[j];
                    books[j] = books[j + 1];
                    books[j + 1] = temp;
                }
            }
        }
    }

    // 根据书名查找图书
    public Book searchByTitle(Book[] books, String title) {
        for (Book b : books) {
            if (b.getTitle() == title) {  // 建议用 equals()
                return b;
            }
        }
        return null;
    }
}

BookManagerTest 测试类:

public class BookManagerTest {
    public static void main(String[] args) {
        BookManager bm = new BookManager();
        Book[] books = bm.initBooks();

        System.out.println("========== 原始顺序 ==========");
        bm.printBooks(books);

        bm.sortByPrice(books);
        System.out.println("========== 按价格从高到低排序 ==========");
        bm.printBooks(books);

        System.out.println("========== 查找图书 ==========");
        String bookName = "设计模式之禅";
        Book book = bm.searchByTitle(books, bookName);
        if (book == null) {
            System.out.println("未找到该图书!");
        } else {
            System.out.println("找到了!编号:" + book.getId() + ",价格:" + book.getPrice());
        }
    }
}

四、今日知识点总结

核心知识点

知识点 重要程度 说明
继承的概念 ⭐⭐⭐⭐⭐ Java 三大特征之一,子类 extends 父类
继承的格式 ⭐⭐⭐⭐⭐ class 子类 extends 父类 {}
继承的好处与弊端 ⭐⭐⭐⭐ 提高复用性但增强耦合性
is...a 关系判断 ⭐⭐⭐⭐ 使用继承前必须判断是否符合 is...a
方法重写(Override) ⭐⭐⭐⭐⭐ 子类覆盖父类方法,方法签名必须完全一致
@Override 注解 ⭐⭐⭐⭐ 可选的检查注解,推荐加上帮助编译器校验
super 关键字 ⭐⭐⭐⭐⭐ super.方法() 调用父类方法,super.属性 访问父类属性
同名属性处理 ⭐⭐⭐⭐ 父子类同名属性同时存在,this 访问自己的,super 访问父类的
变量的就近原则 ⭐⭐⭐⭐⭐ 变量查找路径:局部 → 本类成员 → 父类成员 → 祖父类成员 → Object
this vs super 对比 ⭐⭐⭐⭐ this=本类引用,super=父类引用,在变量/方法/构造方法三场景对比
构造方法继承特点 ⭐⭐⭐⭐⭐ 子类构造方法默认第一句 super() 调用父类无参构造
父类无无参构造处理 ⭐⭐⭐⭐⭐ 父类无无参构造时,子类必须手动 super(参数) 指定有参构造
单继承 ⭐⭐⭐⭐⭐ Java 一个子类只能有一个直接父类
多层继承 ⭐⭐⭐⭐ 支持链式继承(Son → Dad → Granddad)
Object 根类 ⭐⭐⭐⭐ Java 所有类都隐式继承 Object
访问修饰符在继承中 ⭐⭐⭐⭐ private 不可继承,protected 子类可访问
构造方法 + 对象数组 + 冒泡排序综合应用 ⭐⭐⭐⭐⭐ 学生管理系统、图书管理系统

注意事项

  1. 单继承:Java 中一个类只能有一个直接父类(区别于 C++ 的多继承)
  2. 多层继承 ≠ 多继承:Java 支持 Son → Dad → Granddad 这样的链式继承,但不支持同时继承多个类
  3. 私有成员不可访问:子类不能直接使用父类 private 修饰的成员
  4. 构造方法不可继承:父类的构造方法不会被子类继承,子类通过 super(参数) 调用父类构造方法
  5. 子类构造方法默认调用 super():每个子类构造方法的第一句默认都是 super(),如果父类没有无参构造,必须手动 super(参数)
  6. 方法重写 vs 方法重载:
    • 重写(Override):父子类之间,方法签名完全一致
    • 重载(Overload):同一个类中,方法名相同参数列表不同
  7. @Override 注解:不强制但建议加上,可以帮助编译器检查是否真正重写了父类方法
  8. 变量访问就近原则:局部 → 本类成员 → 父类成员 → 祖父类成员 → …… → Object
  9. super() 和 this() 不能同时出现:都必须写在构造方法的第一行
  10. 继承的耦合性:合理设计继承层次,避免过深的继承链,优先考虑组合而非继承

拓展阅读

  • final 关键字对继承的限制(final 类不能被继承,final 方法不能被重写)
  • 继承与组合(Composition)的对比——优先使用组合而非继承
  • Object 类的常见方法:toString()、equals()、hashCode()、getClass()
  • 方法重写的规则(访问权限不能更低、返回值类型可以协变、抛出异常不能更宽等)