日期:2026-07-22
项目:c260722
作者:WanJL
文件:Demo01.java
继承是 Java 三大特征之一(封装、继承、多态)。继承可以让子类具有父类的属性和方法,还可以在子类中重新定义,以及追加属性和方法。
继承的关键字是 extends,直译是「扩展」,含义是子类在父类的基础上进行扩展。
核心思想:
类的层次结构示例:
动物 类
|- 哺乳动物 类
|- 猫科动物 类
|- 猫 类
|- 狮子 类
|- 老虎 类
|- 犬科动物 类
|- 狼 类
|- 狗 类
|- 爬行动物 类
|- 两栖动物 类
文件:Demo01.java
class 子类 extends 父类 {
// 子类自己的成员
}
示例:
class Animal { // 父类
}
class Bird extends Animal { // 子类 Bird 继承 Animal
}
class Penguin extends Bird { // 子类 Penguin 继承 Bird(多层继承)
}
文件: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()
}
文件: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
}
}
文件:Demo01.java
| 好处 | 说明 |
|---|---|
| 代码复用性 | 多个类的相同成员可以放在同一个父类中,子类自动拥有 |
| 代码可维护性 | 父类方法修改一处,所有子类都继承修改后的版本 |
| 建立类与类的关系 | 产生父子类关系后,子类可使用父类中非私有化成员 |
// 如果修改 Animal 的 eat() 方法,所有子类都会生效
public class Animal {
public void eat() {
System.out.println("正在吃东西......"); // 改这里就行
}
}
文件:Demo01.java
继承让类和类之间产生了关系,导致耦合性增强:
耦合关系(Coupling):类与类之间的依赖程度。程序设计很多时候都在研究如何解耦。
文件:Demo01.java
使用继承前要考虑类和类之间是否存在 is...a 关系(谁是谁的一种):
| 正确(符合 is...a) | 错误(不符合 is...a) |
|---|---|
| 老师是人的一种 | 汽车是学生的一种 ✗ |
| 学生是人的一种 | |
| 人是动物的一种 | |
| 动物是生物的一种 |
// 正确的继承设计
class Person { }
class Student extends Person { } // 学生是人的一种 ✓
// 错误的继承设计
class Car { }
class Student extends Car { } // 学生不是汽车的一种 ✗
文件: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注解,但建议加上,因为它能让编译器帮助检查是否正确重写。
文件: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 输出"儿子不吃饭...."。形成了方法的 链式调用。
文件: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访问父类的。
文件:Demo01.java
Java 的继承机制有以下重要限制:
Java 中一个子类 只能有一个直接父类,不支持 C++ 中的多继承。
// ❌ 错误:Java 不支持多继承
class C extends A, B { // 编译报错!
}
// ✅ 正确:一个子类只能有一个直接父类
class B extends A { // 正确
}
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) |
文件:Demo01.java
Java 中 所有的类都默认继承自 Object 类。如果一个类没有显式使用 extends 关键字,它也会隐式继承 Object。
// 以下两种写法等价:
public class Animal { } // 隐式继承 Object
public class Animal extends Object { } // 显式继承 Object
Object 类中定义了一些所有对象都具备的基本方法(如 toString()、equals()、hashCode() 等)。
文件:Bird.java
子类能访问的父类成员范围取决于父类成员的访问修饰符:
| 修饰符 | 本类 | 同包子类 | 异包子类 | 所有类 |
|---|---|---|---|---|
private |
✅ | ❌ | ❌ | ❌ |
| 默认(缺省) | ✅ | ✅ | ❌ | ❌ |
protected |
✅ | ✅ | ✅ | ❌ |
public |
✅ | ✅ | ✅ | ✅ |
public class Bird extends Animal {
private String s1; // 私有的 —— 仅本类可访问
String s2; // 默认的 —— 同包可访问
protected String s3; // protected —— 同包 + 子类可访问
public String s4; // 公共的 —— 所有类可访问
}
包名:exercise.studentSystem
使用构造方法完成完整的学生成绩管理系统,包含三个类:
文件: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.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 防止越界 |
包名: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() + " 次");
}
}
包名: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 访问父类的 |
| 单继承 | ⭐⭐⭐⭐⭐ | Java 一个子类只能有一个直接父类 |
| 多层继承 | ⭐⭐⭐⭐ | 支持链式继承(Son → Dad → Granddad) |
| Object 根类 | ⭐⭐⭐⭐ | Java 所有类都隐式继承 Object |
| 访问修饰符在继承中 | ⭐⭐⭐⭐ | private 不可继承,protected 子类可访问 |
| 构造方法 + 对象数组 + 冒泡排序综合应用 | ⭐⭐⭐⭐⭐ | 学生管理系统、图书管理系统 |
private 修饰的成员super(参数) 调用父类构造方法@Override 注解:不强制但建议加上,可以帮助编译器检查是否真正重写了父类方法final 关键字对继承的限制(final 类不能被继承,final 方法不能被重写)Object 类的常见方法:toString()、equals()、hashCode()、getClass()