
🏠个人主页:黎雁
🎬作者简介:C/C++/JAVA后端开发学习者
❄️个人专栏:C语言、数据结构(C语言)、EasyX、JAVA、游戏、规划、程序人生
✨ 从来绝巘须孤往,万里同尘即玉京

文章目录
- Java继承细节:子类继承父类成员的底层逻辑
-
- [📝 文章摘要](#📝 文章摘要)
- [一、先明确:子类继承父类的**核心边界** 🚨](#一、先明确:子类继承父类的核心边界 🚨)
- [二、分类型拆解:子类继承父类成员的核心规则 📦](#二、分类型拆解:子类继承父类成员的核心规则 📦)
-
- [2.1 构造方法:**完全不能继承**,但会**默认调用**](#2.1 构造方法:完全不能继承,但会默认调用)
- [2.2 成员变量:**非私有可直接继承**,**私有无法直接继承**](#2.2 成员变量:非私有可直接继承,私有无法直接继承)
- [2.3 成员方法:**非私有可直接继承**,**私有无法继承**,**静态方法类级别共享**](#2.3 成员方法:非私有可直接继承,私有无法继承,静态方法类级别共享)
- [三、核心拓展:虚方法表在继承中的作用 🧠](#三、核心拓展:虚方法表在继承中的作用 🧠)
-
- [3.1 虚方法表的定义](#3.1 虚方法表的定义)
- [3.2 继承中虚方法表的生成规则](#3.2 继承中虚方法表的生成规则)
- [3.3 核心作用](#3.3 核心作用)
- [四、高频误区&避坑指南 ⚠️](#四、高频误区&避坑指南 ⚠️)
- [五、实战:排查继承中成员访问的错误 🕵️](#五、实战:排查继承中成员访问的错误 🕵️)
- [✍️ 写在最后](#✍️ 写在最后)

Java继承细节:子类继承父类成员的底层逻辑
✨ 知识回顾
上一篇我们入门了Java继承的核心内容,掌握了继承的定义、语法、3大核心好处,以及单继承、多层继承、所有类继承Object 的核心规则,还通过动物类体系实战体会了继承的代码复用价值。但很多同学只知道"子类能继承父类成员",却不清楚哪些能继承、哪些不能继承、底层如何存储,这篇我们就深挖继承的细节,讲透子类继承父类成员的底层逻辑,包括构造方法、成员变量、成员方法的继承规则,还有虚方法表的核心作用💡!
📝 文章摘要
- 核心摘要:本文从底层存储和语法规则双角度,拆解子类继承父类的3类成员(构造方法、成员变量、成员方法)的核心规则,明确"能继承/不能继承"的边界,讲解虚方法表在继承中的作用,结合实战案例验证规则并分析报错原因,帮你彻底理解继承的底层实现。
- 阅读时长:10分钟
- 适合人群&阅读重点
🎯 Java初学者:重点牢记各类成员的继承规则,能快速判断子类能否访问父类成员。
📚 高校计算机专业学生:从内存存储、虚方法表角度理解继承的底层逻辑,掌握面向对象的设计原理。
💻 初级开发工程师:排查继承中成员访问的报错问题,规范类的继承设计。
📖 面试备考者:熟记"构造方法不能继承""私有成员无法直接继承"等核心考点,应对继承细节类面试题。
一、先明确:子类继承父类的核心边界 🚨
很多同学误以为"子类能继承父类的所有成员",这是典型的误区!子类对父类成员的继承,受访问修饰符 和成员类型的双重限制,核心结论先记牢:
✅ 能继承:父类的非私有 成员变量、非私有成员方法(含默认/protected/public修饰);
❌ 不能继承:父类的构造方法、私有成员(private修饰)、父类的静态成员(静态变量/方法不属于对象,是类级别共享,并非子类继承)。
💡 关键补充:
- 父类私有成员并非完全无法访问 ,子类可通过父类提供的公共getter/setter方法间接访问(封装的标准做法);
- 父类的静态成员子类能直接使用 ,但这不是"继承",而是静态成员的类级别共享(所有子类共用父类的静态成员)。
二、分类型拆解:子类继承父类成员的核心规则 📦
我们按构造方法、成员变量、成员方法三类逐一讲解,结合底层逻辑和实战案例,让规则更易理解、不易混淆。
2.1 构造方法:完全不能继承 ,但会默认调用
这是继承中最易混淆的点,核心结论:构造方法属于当前类,无法被子类继承,但子类构造方法会默认调用父类的无参构造方法。
底层原因
构造方法的方法名必须与类名完全一致 ,子类的类名与父类不同,因此无法继承父类的构造方法;而Java为了保证父类的成员先初始化,规定子类创建对象时,必须先执行父类的构造方法,再执行子类的构造方法。
实战验证1:子类无构造方法,默认调用父类无参构造
java
// 父类:Animal
public class Animal {
// 父类无参构造
public Animal() {
System.out.println("Animal无参构造执行");
}
}
// 子类:Cat,无显式构造方法
public class Cat extends Animal {
}
// 测试类
public class TestConstructor {
public static void main(String[] args) {
new Cat(); // 输出:Animal无参构造执行
}
}
结果分析 :子类Cat没有显式定义构造方法,JVM会为其生成默认无参构造,该构造方法会隐式调用父类的无参构造 (通过super(),后续讲解),因此创建Cat对象时,先执行父类Animal的构造方法。
实战验证2:子类有构造方法,依然默认调用父类无参构造
java
// 子类:Cat,显式定义构造方法
public class Cat extends Animal {
// 子类无参构造
public Cat() {
// 隐式存在:super(); 调用父类无参构造
System.out.println("Cat无参构造执行");
}
}
// 测试
new Cat();
/**
* 输出顺序:
* Animal无参构造执行
* Cat无参构造执行
*/
核心规律 :子类构造方法的第一行,默认隐式添加super(),用于调用父类的无参构造方法,保证父类成员先初始化。
重要注意事项
若父类没有无参构造 (仅定义了有参构造),子类构造方法会编译报错 (因为默认的super()找不到父类无参构造),示例:
java
// 父类:仅定义有参构造,JVM不会生成默认无参构造
public class Animal {
public Animal(String name) {
System.out.println("Animal有参构造执行");
}
}
// 子类:编译报错:Implicit super constructor Animal() is undefined
public class Cat extends Animal {
public Cat() {
// 隐式super(),找不到父类无参构造,报错
}
}
解决方案 :子类构造方法中通过super(参数)显式调用父类的有参构造 (后续继承进阶篇会详细讲解super的用法)。
2.2 成员变量:非私有可直接继承 ,私有无法直接继承
子类对父类成员变量的继承,核心受private访问修饰符 限制,这是Java封装性的体现。
规则1:父类非私有成员变量,子类可直接继承、直接使用
java
// 父类:Animal,非私有成员变量
public class Animal {
String name; // 默认修饰符,同包可访问
protected int age; // protected修饰,子类可访问
public String color; // public修饰,所有地方可访问
}
// 子类:Cat,直接使用父类非私有成员变量
public class Cat extends Animal {
public void show() {
name = "加菲猫";
age = 3;
color = "橘色";
System.out.println(name + "," + age + "," + color);
}
}
// 测试
public class TestVar {
public static void main(String[] args) {
new Cat().show(); // 输出:加菲猫,3,橘色
}
}
规则2:父类私有成员变量,子类无法直接继承、直接访问
java
// 父类:Animal,私有成员变量
public class Animal {
private String name; // private修饰,仅本类可访问
}
// 子类:Cat,直接访问父类私有变量 → 编译报错
public class Cat extends Animal {
public void show() {
name = "加菲猫"; // 报错:Cannot access private field 'name'
}
}
解决方案:父类提供公共getter/setter方法,子类间接访问
这是企业开发的标准做法,既保证了封装性(私有成员不被直接修改),又能让子类访问父类的私有成员:
java
// 父类:Animal,提供getter/setter
public class Animal {
private String name; // 私有变量
// getter:获取name
public String getName() {
return name;
}
// setter:设置name
public void setName(String name) {
this.name = name;
}
}
// 子类:Cat,通过getter/setter间接访问
public class Cat extends Animal {
public void show() {
setName("加菲猫"); // 调用父类setter设置值
System.out.println(getName()); // 调用父类getter获取值
}
}
// 测试
new Cat().show(); // 输出:加菲猫
2.3 成员方法:非私有可直接继承 ,私有无法继承 ,静态方法类级别共享
子类对父类成员方法的继承规则,与成员变量基本一致,但静态方法需要单独说明(并非继承,而是类级别共享)。
规则1:父类非私有成员方法,子类可直接继承、直接调用
java
// 父类:Animal,非私有方法
public class Animal {
public void eat() {
System.out.println("动物吃饭");
}
protected void sleep() {
System.out.println("动物睡觉");
}
}
// 子类:Cat,直接调用父类非私有方法
public class Cat extends Animal {
public void show() {
eat(); // 调用父类eat()
sleep(); // 调用父类sleep()
}
}
// 测试
new Cat().show();
/**
* 输出:
* 动物吃饭
* 动物睡觉
*/
规则2:父类私有成员方法,子类无法继承、无法访问
java
// 父类:Animal,私有方法
public class Animal {
private void run() {
System.out.println("动物跑");
}
}
// 子类:Cat,访问父类私有方法 → 报错
public class Cat extends Animal {
public void show() {
run(); // 报错:Cannot find symbol method run()
}
}
规则3:父类的静态方法,子类可直接使用但并非继承
父类的静态方法属于类级别 ,存储在方法区,子类能直接通过子类名.静态方法名调用,这是类级别共享,而非子类继承,示例:
java
// 父类:Animal,静态方法
public class Animal {
public static void showInfo() {
System.out.println("动物类的静态方法");
}
}
// 子类:Cat,直接使用父类静态方法
public class Cat extends Animal {
}
// 测试:两种调用方式均可
public class TestStaticMethod {
public static void main(String[] args) {
Animal.showInfo(); // 父类名调用(推荐)
Cat.showInfo(); // 子类名调用(语法允许,类级别共享)
}
}
核心区别 :继承的成员属于对象级别 (随对象创建而存在),而静态方法属于类级别(随类加载而存在),子类使用父类静态方法,只是JVM的语法支持,并非真正的继承。
三、核心拓展:虚方法表在继承中的作用 🧠
对于非私有成员方法,JVM会通过虚方法表(Virtual Method Table) 管理父类和子类的方法,这是继承中方法调用的底层核心,也是后续方法重写、多态的基础。
3.1 虚方法表的定义
虚方法表是JVM为每个类加载时生成的方法地址表 ,存储了该类所有可被重写的非私有成员方法 的地址,子类的虚方法表会继承并扩展父类的虚方法表。
3.2 继承中虚方法表的生成规则
- 父类加载时,生成父类的虚方法表,存储父类的非私有成员方法;
- 子类加载时,先复制父类虚方法表的所有方法地址,再将自身的非私有成员方法地址添加到表中;
- 若子类重写了父类的方法,会覆盖虚方法表中对应的父类方法地址(后续方法重写篇详细讲解)。
3.3 核心作用
JVM通过虚方法表快速查找方法地址,调用对象的方法时,直接从虚方法表中获取地址执行,无需遍历类的所有方法,大幅提升方法调用的效率。
💡 简单理解:虚方法表就是JVM为每个类准备的方法索引表,继承时子类直接复制父类的索引,实现方法的快速继承和调用。
四、高频误区&避坑指南 ⚠️
误区1:认为子类能继承父类的所有成员
❌ 错误认知:子类继承父类的构造方法、私有成员、静态成员;
✅ 正确结论:仅继承非私有成员变量和非私有成员方法,构造方法不能继承,私有成员无法直接访问,静态成员是类级别共享而非继承。
误区2:子类直接访问父类的私有成员
❌ 错误示例:子类直接调用父类的private变量/方法;
✅ 正确做法:父类提供公共的getter/setter方法,子类间接访问(保证封装性)。
误区3:认为父类静态方法是被子类继承的
❌ 错误认知:Cat类继承了Animal类的static showInfo()方法;
✅ 正确结论:Cat类能使用showInfo()是因为静态方法的类级别共享,并非继承,静态方法始终属于定义它的类(Animal)。
误区4:父类无无参构造,子类不做任何处理
❌ 错误示例:父类仅定义有参构造,子类构造方法默认调用super();
✅ 正确做法:子类构造方法中通过super(参数)显式调用父类的有参构造,避免编译报错。
五、实战:排查继承中成员访问的错误 🕵️
问题代码
java
// 父类:Person
public class Person {
private String name;
public int age;
public Person(String name) {
this.name = name;
}
private void showName() {
System.out.println(name);
}
}
// 子类:Student
public class Student extends Person {
public String id;
public Student() {
id = "2026001";
}
public void test() {
name = "张三"; // 报错1
showName(); // 报错2
age = 20; // 正常
}
}
错误分析
- 报错1:子类直接访问父类私有变量name,违反"私有成员无法直接访问"规则;
- 报错2:子类直接调用父类私有方法showName(),违反"私有方法无法继承访问"规则;
- 隐藏报错:父类仅定义有参构造,子类构造方法默认调用super(),找不到父类无参构造,编译报错。
解决方案
java
// 父类:Person,提供getter/setter
public class Person {
private String name;
public int age;
public Person(String name) {
this.name = name;
}
// 提供getter/setter访问name
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 改为非私有方法,让子类访问
public void showName() {
System.out.println(name);
}
}
// 子类:Student,显式调用父类有参构造
public class Student extends Person {
public String id;
// 显式调用父类有参构造,解决super()报错
public Student() {
super("默认姓名"); // 调用父类Person(String name)
id = "2026001";
}
public void test() {
setName("张三"); // 间接设置name
showName(); // 调用非私有方法
age = 20; // 正常访问
}
}
// 测试
public class TestFix {
public static void main(String[] args) {
Student s = new Student();
s.test(); // 输出:张三
}
}
✍️ 写在最后
- 子类继承父类成员的核心边界:仅继承非私有成员变量和非私有成员方法,构造方法不能继承,私有成员无法直接访问,静态成员是类级别共享而非继承;
- 构造方法的关键规律:子类构造方法默认调用父类无参构造 ,若父类无无参构造,子类需通过
super(参数)显式调用父类有参构造; - 封装性与继承的平衡:父类私有成员通过getter/setter方法让子类间接访问,这是企业开发的标准做法;
- 底层核心:JVM通过虚方法表管理继承的方法,实现方法的快速查找和调用,这是后续方法重写、多态的基础。
下一篇我们将聚焦继承中的高频考点------成员变量的访问原则 ,讲解就近原则以及this和super在访问成员变量时的核心用法,解决"父子类同名变量如何区分访问"的问题~
❤️ 我是黎雁,专注Java基础与实战分享,关注我,一起从0到1吃透Java面向对象!
📚 后续文章预告:《Java继承:成员变量访问(就近原则+this/super用法)》
💬 评论区交流:你在继承成员访问中遇到过哪些报错?或者对虚方法表有哪些疑惑,欢迎留言讨论~