S02-03 面向对象-继承
[TOC]
继承
概述
多个类存在相同的属性和方法时,代码冗余,维护成本高。通过继承抽取父类,实现代码复用。
Pupil(小学生)和 Graduate(大学生)都有 name、age、score 属性和 testing()、showInfo()方法,可抽取父类 Student。
继承(Inheritance):是基于已有的类(父类 / 超类)创建新类(子类 / 派生类),子类复用父类的属性和方法,同时可扩展自身的功能。它是实现代码复用、构建类层次结构、支撑多态的核心机制。
本质:代码复用 + 类层次化
- 代码复用:避免重复编写相同的属性和方法(如多个子类都需要 “姓名、年龄” 属性,可抽离到父类);
- 类层次化:构建 “通用 → 具体” 的类结构(如
Animal→Mammal→Dog),符合现实世界的分类逻辑; - 多态基础:继承是多态的前提(子类对象可赋值给父类引用)。
继承关系示意图:

基本语法
基本语法:
使用 extends 关键字声明继承关系,格式如下:
// 父类(超类/基类)
public class 父类名 {
// 属性、方法
}
// 子类(派生类):继承父类
public class 子类名 extends 父类名 {
// 新增属性、方法,或重写父类方法
}核心规则:
规则 1:Java 是单继承
子类只能直接继承一个父类(避免多继承的菱形问题),但可通过 “多层继承” 间接继承多个类的特性:
java// 合法:多层继承 class A {} class B extends A {} class C extends B {} // C 间接继承 A // 错误:多继承(Java 不支持) // class D extends A, B {}规则 2:所有类默认继承 Object 类
Java 中没有显式声明父类的类,默认继承
java.lang.Object类(Object 是所有类的根父类)。Object 类提供了所有对象的通用方法(如
equals()、hashCode()、toString()),所有类都可直接使用或重写这些方法。java// 等价于 class Person extends Object {} public class Person {}规则 3:访问权限决定继承可见性
子类继承了父类的所有属性和方法,但
private成员因访问权限限制无法直接访问:javaclass Parent { public String pubVar = "公共变量"; protected String proVar = "保护变量"; String defVar = "默认变量"; private String priVar = "私有变量"; // 子类无法继承 } class Child extends Parent { public void show() { System.out.println(pubVar); // ✅ 合法:继承public成员 System.out.println(proVar); // ✅ 合法:继承protected成员 System.out.println(defVar); // ✅ 合法:同包下继承default成员 // System.out.println(priVar); // ❌ 错误:无法访问private成员 } }如果子类要访问父类的
private成员,需要通过父类的 非私有方法 访问:javaclass Parent { private String priVar = "私有变量"; // 子类无法继承 // 1. 通过非私有方法(public/protected/default)访问私有属性 public String getPriVar() { return this.priVar; } } class Child extends Parent { public void show() { getPriVar() // 2. 子类访问 } }
快速入门
// 父类:Student
public class Student {
// 共有属性
public String name;
public int age;
private double score; // 私有属性
// 共有方法
public void setScore(double score) {
this.score = score;
}
public void showInfo() {
System.out.println("学生名" + name + " 年龄" + age + " 成绩" + score);
}
}// 子类:Pupil(小学生)
public class Pupil extends Student {
// 特有方法
public void testing() {
System.out.println("小学生" + name + " 正在考小学数学..");
}
}// 子类:Graduate(大学生)
public class Graduate extends Student {
// 特有方法
public void testing() {
System.out.println("大学生" + name + " 正在考大学数学..");
}
}成员变量的继承
成员变量的继承规则:
子类继承父类非私有变量
子类可直接使用父类的
public/protected/default成员变量:javaclass Animal { protected String name; // 父类保护变量 protected int age; } class Dog extends Animal { public void setInfo(String name, int age) { this.name = name; // 直接使用父类的 name this.age = age; // 直接使用父类的 age } }子类同名变量隐藏父类变量
子类声明与父类同名的变量时,会 “隐藏” 父类的变量(而非覆盖),可通过
super关键字访问父类变量:javaclass Parent { String name = "父类姓名"; } class Child extends Parent { String name = "子类姓名"; // 隐藏父类 name public void show() { System.out.println(name); // 子类姓名(就近原则) System.out.println(super.name); // 父类姓名(通过super访问) } }
方法的继承
方法的继承:
子类可直接调用父类的非私有方法:
class Animal {
public void eat() {
System.out.println("动物进食");
}
}
class Cat extends Animal {
public void play() {
this.eat(); // 调用继承的父类方法
System.out.println("猫玩球");
}
}方法重写
概述
方法重写(Override):子类定义与父类 “方法签名完全一致” 的方法,覆盖父类的原有逻辑。
方法签名:方法名 + 参数列表(参数个数、类型、顺序),返回值需兼容(Java 5+ 支持协变返回)。
方法重写规则
方法重写的核心规则:必须遵守,否则编译报错
- 方法签名一致:方法名、参数列表(个数 / 类型 / 顺序)必须完全相同
- 返回值兼容:子类返回值需是父类返回值的子类(或相同)(协变返回)
- 访问权限不降级:子类方法的访问权限不能比父类更严格(如父类 public → 子类不能 private)
- 异常不扩大:子类方法抛出的异常不能比父类更广泛(如父类抛 RuntimeException → 子类不能抛 Exception)
示例:正确 vs 错误
// 父类
class Parent {
public Object show(String msg) throws RuntimeException {
System.out.println("父类方法:" + msg);
return msg;
}
}// ✅ 子类:正确重写
class Child1 extends Parent {
// 协变返回:Object → String(String是Object的子类)
@Override // 注解:强制检查重写规则,推荐添加
public String show(String msg) { // 父类抛RuntimeException,子类可不抛
System.out.println("子类方法:" + msg);
return msg;
}
}// ❌ 子类:错误重写(访问权限降级)
class Child2 extends Parent {
// @Override // 编译报错:父类是public,子类是private
private Object show(String msg) {
return null;
}
}@Override
@Override 注解的作用:
- 强制编译器检查是否符合重写规则,避免拼写错误(如把
show写成shwo); - 增强代码可读性,明确标识该方法是重写父类的方法;
- 推荐所有重写方法都添加该注解。
方法重写 VS 方法重载
| 名称 | 发生范围 | 方法名 | 形参列表 | 返回类型 | 修饰符 |
|---|---|---|---|---|---|
| 重载(overload) | 本类 | 必须相同 | 类型/个数/顺序至少一个不同 | 无要求 | 无要求 |
| 重写(override) | 父子类 | 必须相同 | 完全相同 | 相同或子类 | 子类不能缩小访问范围 |
练习
需求:
- Person 类:私有属性 name、age;构造器;
say()方法返回自我介绍 - Student 类:继承 Person,增加私有属性 id、score;构造器;重写
say()方法
// 父类:Person
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String say() {
return "name=" + name + " age=" + age;
}
// getter/setter
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}// 子类:Student
public class Student extends Person {
private int id;
private double score;
public Student(String name, int age, int id, double score) {
super(name, age); // 调用父类构造器
this.id = id;
this.score = score;
}
// 重写say()方法
@Override
public String say() {
// 复用父类的say()方法,添加子类属性
return super.say() + " id=" + id + " score=" + score;
}
// getter/setter
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public double getScore() { return score; }
public void setScore(double score) { this.score = score; }
}// 测试类
public class OverrideExercise {
public static void main(String[] args) {
Person jack = new Person("jack", 10);
System.out.println(jack.say()); // name=jack age=10
Student smith = new Student("smith", 20, 123456, 99.8);
System.out.println(smith.say()); // name=smith age=20 id=123456 score=99.8
}
}构造方法的继承
构造方法的继承:
构造方法不能被继承(构造方法名必须与类名一致,子类类名不同),但子类构造方法可通过 super() 调用父类构造方法。
核心规则:
子类构造方法默认调用父类的无参构造方法(
super()),且super()必须是构造方法的第一条语句;javaclass Parent { public Parent() { // 无参构造器 System.out.println("父类的无参构造器默认会被调用"); } } class Child extends Parent { public Child() { // super() 子类构造方法默认调用父类的无参构造方法 } }若父类没有无参构造(仅含带参构造),子类构造方法必须显式调用父类的带参构造(
super(参数));java// 父类:仅含带参构造(无默认无参构造) class Parent { private String name; public Parent(String name) { // 带参构造 this.name = name; } }java// 子类:必须显式调用父类带参构造 class Child extends Parent { private int age; // ✅ 正确:显式调用父类带参构造 public Child(String name, int age) { super(name); // 必须是第一条语句 this.age = age; } // ❌ 错误:未调用父类构造(父类无无参构造) // public Child() {} // 编译报错 }不能同时调用
super()和this()(二者都需是第一条语句)。
super
概述
super:访问父类成员
super 是继承场景的核心关键字,作用是在子类中访问父类的成员(变量 / 方法 / 构造),与 this 对应:
this指向当前对象。super指向父类对象。
核心用法
super 的核心用法:
super.成员变量:访问父类的非私有成员变量(解决子类同名变量的隐藏问题)super.方法名(参数):调用父类的非私有原方法(子类重写后,仍可调用父类原方法)super(参数):调用父类的构造方法(必须是构造方法第一条语句)
示例:super 的使用
// 父类
class Parent {
String name = "父类";
public Parent(String name) {
this.name = name;
}
public void show() {
System.out.println("父类show方法:" + name);
}
}// 子类
class Child extends Parent {
String name = "子类";
public Child(String parentName, String childName) {
super(parentName); // 3. 调用父类带参构造
this.name = childName;
}
@Override
public void show() {
super.show(); // 2. 调用父类的show方法
System.out.println("子类show方法:" + this.name);
System.out.println("父类name:" + super.name); // 1. 访问父类name
}
}// 测试
public class TestSuper {
public static void main(String[] args) {
Child child = new Child("父类姓名", "子类姓名");
child.show();
// 输出:
// 父类show方法:父类姓名
// 子类show方法:子类姓名
// 父类name:父类姓名
}
}继承的访问修饰符
不同访问修饰符的成员在继承中的可见性,是继承的核心考点,以下是完整对比表:
| 父类成员修饰符 | 同包子类可见 | 不同包子类可见 | 非子类可见 | 子类能否继承 |
|---|---|---|---|---|
public | ✅ | ✅ | ✅ | ✅ |
protected | ✅ | ✅ | ❌ | ✅ |
default | ✅ | ❌ | ❌ | ✅(同包) |
private | ❌ | ❌ | ❌ | ❌ |
关键说明:
protected是为继承设计的修饰符:不同包的子类可访问,但非子类不可访问;default仅同包的子类可继承,不同包的子类无法访问;private成员虽不能继承,但子类可通过父类的public/protected方法间接访问
继承的内存流程图
核心逻辑:子类对象创建后,会建立属性查找关系:
- 先查找子类是否有该属性,有则访问
- 若无,查找父类,有则访问(需满足访问权限)
- 若无,继续向上查找,直到 Object 类
- 注意:如果在查找的过程中遇到 private 属性,则不会在继续向上查找(即使上面有该属性并且可以访问),而是报错
// 爷类
class GrandPa {
String name = "大头爷爷";
String hobby = "旅游";
}
// 父类
class Father extends GrandPa {
String name = "大头爸爸";
private int age = 39; // 私有属性
public int getAge() {
return age;
}
}
// 子类
class Son extends Father {
String name = "大头儿子";
}
// 测试类
public class Test {
public static void main(String[] args) {
Son son = new Son();
System.out.println(son.name); // 大头儿子(子类有name属性)
System.out.println(son.hobby); // 旅游(子类无,父类无,爷类有)
System.out.println(son.getAge()); // 39(父类私有属性,通过公共方法访问)
}
}内存流程图:
.jpg)
练习
练习 1
javaclass A { A() { System.out.println("a"); } A(String name) { System.out.println("a name"); } } class B extends A { B() { this("abc"); System.out.println("b"); } B(String name) { System.out.println("b name"); } // 有一个默认的 super(); } // main中:B b = new B(); 输出什么? // 输出结果:a → b name → b练习 2
javaclass A { public A() { System.out.println("我是A类"); } } class B extends A { public B() { System.out.println("我是B类的无参构造"); } public B(String name) { System.out.println(name + "我是B类的有参构造"); } } class C extends B { public C() { this("hello"); System.out.println("我是c类的无参构造"); } public C(String name) { super("hahah"); System.out.println("我是c类的有参构造"); } } public class ExtendsExercise02 { public static void main(String[] args) { C c = new C(); // 输出:我是A类 → hahah我是B类的有参构造 → 我是c类的有参构造 → 我是c类的无参构造 } }练习 3
需求:
- 编写 Computer 类:属性 CPU、内存、硬盘;方法
getDetails()返回详细信息 - 编写 PC 子类:继承 Computer,添加特有属性 brand(品牌)
- 编写 NotePad 子类:继承 Computer,添加特有属性 color(颜色)
- 测试类:创建 PC 和 NotePad 对象,赋值并打印信息
java// 父类:Computer public class Computer { private String cpu; private int memory; private int disk; public Computer(String cpu, int memory, int disk) { this.cpu = cpu; this.memory = memory; this.disk = disk; } public String getDetails() { return "cpu=" + cpu + " memory=" + memory + " disk=" + disk; } // getter/setter public String getCpu() { return cpu; } public void setCpu(String cpu) { this.cpu = cpu; } public int getMemory() { return memory; } public void setMemory(int memory) { this.memory = memory; } public int getDisk() { return disk; } public void setDisk(int disk) { this.disk = disk; } } // 子类:PC public class PC extends Computer { private String brand; public PC(String cpu, int memory, int disk, String brand) { super(cpu, memory, disk); // 调用父类构造器 this.brand = brand; } public void printInfo() { System.out.println("PC信息=" + getDetails() + " brand=" + brand); } // getter/setter public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } } // 测试类 public class ExtendsExercise03 { public static void main(String[] args) { PC pc = new PC("intel", 16, 500, "IBM"); pc.printInfo(); // PC信息=cpu=intel memory=16 disk=500 brand=IBM } }- 编写 Computer 类:属性 CPU、内存、硬盘;方法