# JavaSE 基础 — Day08 笔记 > **日期**:2026-07-22 > **项目**:`c260722` > **作者**:WanJL --- ## 一、继承(Inheritance) ### 1. 继承的概念 **文件**:`Demo01.java` 继承是 Java 三大特征之一(封装、继承、多态)。继承可以让**子类**具有父类的属性和方法,还可以在子类中重新定义,以及追加属性和方法。 继承的关键字是 `extends`,直译是「扩展」,含义是子类在父类的基础上进行扩展。 **核心思想**: - 子类具有父类的特征 - 父类是根据多个具有相同特征的子类所抽取出来的公共部分 **类的层次结构示例**: ``` 动物 类 |- 哺乳动物 类 |- 猫科动物 类 |- 猫 类 |- 狮子 类 |- 老虎 类 |- 犬科动物 类 |- 狼 类 |- 狗 类 |- 爬行动物 类 |- 两栖动物 类 ``` ### 2. 继承的格式 **文件**:`Demo01.java` ```java class 子类 extends 父类 { // 子类自己的成员 } ``` **示例**: ```java class Animal { // 父类 } class Bird extends Animal { // 子类 Bird 继承 Animal } class Penguin extends Bird { // 子类 Penguin 继承 Bird(多层继承) } ``` ### 3. 继承体系示例 **文件**:`Animal.java`、`Bird.java`、`Penguin.java` ```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` ```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` | 好处 | 说明 | |------|------| | **代码复用性** | 多个类的相同成员可以放在同一个父类中,子类自动拥有 | | **代码可维护性** | 父类方法修改一处,所有子类都继承修改后的版本 | | **建立类与类的关系** | 产生父子类关系后,子类可使用父类中非私有化成员 | ```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) | |---------------------|----------------------| | 老师**是**人的一种 | 汽车**是**学生的一种 ✗ | | 学生**是**人的一种 | | | 人**是**动物的一种 | | | 动物**是**生物的一种 | | ```java // 正确的继承设计 class Person { } class Student extends Person { } // 学生是人的一种 ✓ // 错误的继承设计 class Car { } class Student extends Car { } // 学生不是汽车的一种 ✗ ``` --- ### 8. 方法重写(Method Override) **文件**:`Demo01.java`、`Bird.java`、`Penguin.java` 当子类中出现与父类 **完全相同的方法名、参数列表和返回值类型** 的方法时,称为 **方法重写(Override)**。子类会覆盖父类的方法,默认调用子类的版本。 ```java // 父类 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)** | | 子类方法与父类方法 **方法名不同** | 子类特有的方法,不是重写 | **测试效果**: ```java // 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()`** ```java // 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("儿子不吃饭...."); } } ``` **测试效果**: ```java // 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.变量名` | **父类** 的属性 | **示例**: ```java // 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 } } ``` **测试效果**: ```java // 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 // ❌ 错误:Java 不支持多继承 class C extends A, B { // 编译报错! } // ✅ 正确:一个子类只能有一个直接父类 class B extends A { // 正确 } ``` #### ② 多层继承(Multi-level Inheritance) Java **支持多层继承**,即一个类的父类可以继承自其他类。 ```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`。 ```java // 以下两种写法等价: public class Animal { } // 隐式继承 Object public class Animal extends Object { } // 显式继承 Object ``` `Object` 类中定义了一些所有对象都具备的基本方法(如 `toString()`、`equals()`、`hashCode()` 等)。 --- ### 12. 访问修饰符在继承中的体现 **文件**:`Bird.java` 子类能访问的父类成员范围取决于父类成员的访问修饰符: | 修饰符 | 本类 | 同包子类 | 异包子类 | 所有类 | |--------|:----:|:--------:|:--------:|:------:| | `private` | ✅ | ❌ | ❌ | ❌ | | 默认(缺省) | ✅ | ✅ | ❌ | ❌ | | `protected` | ✅ | ✅ | ✅ | ❌ | | `public` | ✅ | ✅ | ✅ | ✅ | ```java 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 的成员变量) | | ❌ | 如果都没找到 | 编译报错 | ```java // 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(参数)` —— 调用父类的构造方法 | ```java // Demo02.java(注释中总结) /* this 代表本类对象的引用 super 代表父类对象的引用 成员变量中: this.成员变量 --> 访问本类的成员变量 super.成员变量 --> 访问父类的成员变量 成员方法中: this.成员方法 --> 调用本类的成员方法 super.成员方法 --> 调用父类的成员方法 构造方法中: this() --> 访问本类的无参构造方法 this(参数) --> 访问本类的有参构造方法 super() --> 访问父类的无参构造方法 super(参数) --> 访问父类的有参构造方法 */ ``` > **注意**:`this()` 和 `super()` 都必须写在构造方法的 **第一行**,且 **不能同时出现**。 ```java // 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()`。 ```java // 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` **情况一**:父类 **没有** 无参构造方法(只有有参构造),子类必须手动调用父类的有参构造。 ```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 修饰**,子类也无法调用。 ```java // Fu.java —— 父类有无参构造但是 private // private Fu() { } // private 构造方法,子类不可见 ``` #### 15.3 综合示例:Person → Student **文件**:`Person.java`、`Student.java` ```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` ```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` ```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 类**: ```java 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 测试类**: ```java 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 类**(实体类): ```java 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 类**(业务类): ```java 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 测试类**: ```java 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()` - 方法重写的规则(访问权限不能更低、返回值类型可以协变、抛出异常不能更宽等)