Java 继承 —— 从底层彻底吃透

Java 继承 —— 从底层彻底吃透 目录继承是什么继承的语法规则单继承与多层继承Object —— 所有类的终极父类子类到底能从父类继承什么构造方法 —— 不能继承但能调用成员变量 —— 能继承但有权限之分成员方法 —— 虚方法表才是关键虚方法表 —— 底层核心机制继承中成员变量的访问 —— 就近原则继承中构造方法的执行 —— super 的奥秘this vs super —— 彻底分清总结1. 继承是什么继承是 Java 中类与类之间的一种关系。通过extends关键字一个类子类可以继承另一个类父类的属性和行为。大白话子类不需要重新写一遍父类已有的代码直接拿来用就行。比如你写了一个Animal类里面有eat()方法Dog类继承Animal后Dog对象也可以调用eat()不用再写一遍。2. 继承的语法规则public class 子类 extends 父类 { // 子类自己的代码 }2.1 单继承与多层继承Java 只支持单继承—— 一个类只能有一个直接父类。// ❌ 错误Java 不支持多继承 public class Son extends Father, Mother { // 编译报错 } ​ // ✅ 正确单继承 public class Son extends Father { }但 Java 支持多层继承—— A 继承 BB 继承 C这是允许的。class Grandfather { public void say() { System.out.println(我是爷爷); } } ​ class Father extends Grandfather { public void shout() { System.out.println(我是爸爸); } } ​ class Son extends Father { public void whisper() { System.out.println(我是儿子); } } ​ public class Test { public static void main(String[] args) { Son s new Son(); s.say(); // 我是爷爷 —— 从 Grandfather 继承 s.shout(); // 我是爸爸 —— 从 Father 继承 s.whisper(); // 我是儿子 —— 自己的方法 } }调用链Son → Father → Grandfather沿着继承链一层层往上找。2.2 Object —— 所有类的终极父类Java 中所有的类都直接或间接继承于Object类。你自己写的类如果没有写extends编译器会自动补上extends Object。// 你写的 public class Person { } ​ // 实际编译后等价于 public class Person extends Object { }Object类提供了toString()、equals()、hashCode()等基础方法所以任何一个 Java 对象都能调用这些方法。3. 子类到底能从父类继承什么这个问题是面试高频考点。我们把继承内容分为三类构造方法、成员变量、成员方法。3.1 构造方法 —— 不能继承但能调用规则构造方法无论 private 还是非 private都不能被子类继承。但子类可以通过super(...)调用父类的构造方法。class Animal { String name; ​ // 无参构造 public Animal() { System.out.println(Animal 无参构造执行); } ​ // 有参构造 public Animal(String name) { this.name name; System.out.println(Animal 有参构造执行); } } ​ class Dog extends Animal { public Dog() { super(); // 调用父类无参构造默认就有不写也行 System.out.println(Dog 无参构造执行); } ​ public Dog(String name) { super(name); // 调用父类有参构造 —— 必须手动写 System.out.println(Dog 有参构造执行); } } ​ public class Test { public static void main(String[] args) { Dog d1 new Dog(); // 输出 // Animal 无参构造执行 // Dog 无参构造执行 ​ System.out.println(); ​ Dog d2 new Dog(旺财); // 输出 // Animal 有参构造执行 // Dog 有参构造执行 } }为什么子类构造方法必须先调用父类构造方法因为子类对象在内存中包含一个完整的父类对象父类空间父类的成员变量需要先初始化子类才能正常使用它们。3.2 成员变量 —— 能继承但有权限之分权限修饰符子类能否继承子类能否直接访问public能继承能访问protected能继承能访问默认缺省能继承同包能访问不同包不能private能继承不能直接访问重点private 成员变量也能被继承但子类中无法直接访问像这样——class Father { private int money 1000000; // 私有的子类继承到了但不能直接访问 public int getMoney() { // 通过 public 方法间接访问 return money; } } ​ class Son extends Father { public void show() { // System.out.println(money); // ❌ 编译报错不能直接访问 System.out.println(getMoney()); // ✅ 通过父类的 public 方法访问 } }Son对象在堆内存中确实有一块空间存储了money但语法上不允许直接写this.money来访问。3.3 成员方法 —— 虚方法表才是关键父类的成员方法并不是全部都能被子类继承的。子类能继承的只有父类虚方法表里的方法。4. 虚方法表 —— 底层核心机制什么是虚方法表每个类在 JVM 的方法区元空间中都有一张虚方法表vtable。当一个类被加载时JVM 会为这个类创建一个虚方法表里面记录了该类所有可以被覆盖重写的方法的入口地址。什么样的方法能进入虚方法表// 能进入虚方法表能被继承/重写 // 1. 非 private 的方法 // 2. 非 static 的方法 // 3. 非 final 的方法 ​ // 不能进入虚方法表不能被继承/重写 // 1. private 方法 // 2. static 方法 // 3. final 方法 // 4. 构造方法一句话总结只有虚方法表里的方法子类才能继承和重写。class Father { // 能进入虚方法表子类能继承 public void method1() { System.out.println(父类 public 方法); } ​ protected void method2() { System.out.println(父类 protected 方法); } ​ // 不能进入虚方法表子类不能继承 private void method3() { System.out.println(父类 private 方法); } ​ public static void method4() { System.out.println(父类 static 方法); } ​ public final void method5() { System.out.println(父类 final 方法); } } ​ class Son extends Father { public void test() { method1(); // ✅ 继承到了 method2(); // ✅ 继承到了 ​ // method3(); // ❌ 编译报错private 不在虚方法表 // method4(); // ❌ 编译报错static 不在虚方法表但可以通过 Father.method4() 调用 // method5(); // ❌ 编译报错final 不在虚方法表 } }为什么要有虚方法表虚方法表是实现多态的底层关键。当调用Father f new Son(); f.method1()时JVM 通过虚方法表找到实际对象的method1方法地址从而实现动态绑定。5. 继承中成员变量的访问 —— 就近原则当子类和父类有同名成员变量时访问规则是就近原则局部位置—— 方法内部定义的变量本类成员位置—— 子类的成员变量父类成员位置—— 父类的成员变量逐层往上—— 沿着继承链继续往上找class Father { String name Father; } ​ class Son extends Father { String name Son; ​ public void show(String name) { System.out.println(name); // ① 局部变量 System.out.println(this.name); // ② 本类成员变量 System.out.println(super.name); // ③ 父类成员变量 } } ​ public class Test { public static void main(String[] args) { Son s new Son(); s.show(参数); } }输出结果参数 Son Father图解┌─────────────────────────────┐ │ 方法调用栈 │ │ show(String name 参数) │ ← ① name → 参数 │ this → Son 对象 │ ← ② this.name → Son │ super → Father 空间 │ ← ③ super.name → Father └─────────────────────────────┘6. 继承中构造方法的执行 —— super 的奥秘四条铁律子类不能继承父类的构造方法但可以通过super()调用子类每个构造方法的第一行默认有一个super()调用父类无参构造必须先执行父类构造方法再执行子类构造方法如果想调用父类有参构造必须手动写super(参数)class Person { String name; int age; ​ public Person() { System.out.println(Person 无参构造); } ​ public Person(String name, int age) { this.name name; this.age age; System.out.println(Person 有参构造); } } ​ class Student extends Person { String school; ​ public Student() { // super(); ← 默认就有可以不写 System.out.println(Student 无参构造); } ​ public Student(String name, int age, String school) { super(name, age); // ← 必须手动写调用父类有参构造 this.school school; System.out.println(Student 有参构造); } } ​ public class Test { public static void main(String[] args) { System.out.println( 调用无参构造 ); Student s1 new Student(); // 输出 // Person 无参构造 // Student 无参构造 ​ System.out.println( 调用有参构造 ); Student s2 new Student(张三, 18, 清华); // 输出 // Person 有参构造 // Student 有参构造 } }为什么必须先调用父类构造方法看内存图堆内存中 Student 对象布局 ┌────────────────────┐ │ Father 部分 │ ← 先初始化通过 super() │ ├─ name │ │ └─ age │ ├────────────────────┤ │ Student 部分 │ ← 再初始化 │ ├─ school │ │ └─ 自己的方法... │ └────────────────────┘如果父类部分不先初始化name和age就是默认值null / 0子类使用父类的成员变量时就会出错。7. this vs super —— 彻底分清关键字含义本质this当前方法调用者的地址值是一个引用变量指向调用该方法的对象super父类存储空间用来访问父类内容的标识符不是引用class Father { public void show() { System.out.println(Father show); } } ​ class Son extends Father { public void show() { System.out.println(Son show); } ​ public void call() { this.show(); // 调用本类的 show() super.show(); // 调用父类的 show() } } ​ public class Test { public static void main(String[] args) { Son s new Son(); s.call(); } }输出Son show Father showthis的底层本质public class Test { public static void main(String[] args) { Son s1 new Son(); Son s2 new Son(); ​ // 调用 s1.show() 时JVM 会把 s1 的地址隐式传入 show 方法 // 方法内部 this 就指向 s1 // 调用 s2.show() 时this 指向 s2 } }可以把this理解为方法的隐含参数它让同一个方法在不同的对象上执行时操作的是不同的数据。8. 总结知识点一句话总结单继承一个类只能有一个直接父类多层继承A→B→C 是允许的继承链可以多层Object所有类最终继承自 Object构造方法不能继承但能通过super()调用成员变量非私有能继承能直接访问成员变量private能继承但不能直接访问成员方法只有虚方法表中的方法能被继承虚方法表非 private、非 static、非 final 的方法变量访问就近原则局部 → 本类成员 → 父类成员构造执行顺序父类构造先执行 → 子类构造后执行this当前方法调用者的地址值super父类存储空间的标识如果这篇文章对你有帮助欢迎收藏转发。 学 Java不仅要会用还要知道底层为什么这么设计。