Java学习——面向对象三大特性:继承的本质、使用限制与最佳实践
·
目录
1. 基础继承 + 方法重写 + super 用法(标准示例)
4. 方法重写(Override)和重载(Overload)的区别?
一、核心定义与设计思想
1. 核心定义
继承(Inheritance) 是面向对象三大特性之一,使用 extends 关键字实现:
- 允许一个类(子类 / 派生类)复用另一个类(父类 / 基类 / 超类)的非私有属性和方法;
- 子类可以在父类基础上扩展新功能,或重写(Override) 父类原有方法;
- Java 规定:类只能单继承,支持多层继承,接口可多实现。
2. 核心设计思想
- 代码复用:抽取多个类的公共属性 / 方法到父类,避免重复代码;
- 层级抽象:构建类的层级关系,符合现实世界逻辑(如
Person → Student → CollegeStudent); - 多态基础:继承是多态的前提(没有继承就没有方法重写,就没有多态);
- 功能扩展:不修改父类,仅通过子类扩展功能,符合开闭原则。
3. 继承的核心规则(必记)
- 单继承:一个子类只能有一个直接父类(Java 禁止类多继承,避免菱形问题);
- 多层继承:支持链式继承(A→B→C);
- 权限限制:子类不能继承父类私有成员(
private变量 / 方法); - 构造方法:子类不能继承父类构造方法,只能通过
super()调用; - 所有类默认继承:
java.lang.Object是所有类的顶级父类。
4. 访问权限与继承(核心)
| 修饰符 | 本类 | 同包 | 子类 | 任意类 | 能否被继承 |
|---|---|---|---|---|---|
private | ✅ | ❌ | ❌ | ❌ | ❌ 不能 |
| 默认(包) | ✅ | ✅ | ❌ | ❌ | ✅ 同包子类 |
protected | ✅ | ✅ | ✅ | ❌ | ✅ 全包子类 |
public | ✅ | ✅ | ✅ | ✅ | ✅ 可以 |
二、底层实现原理(含 JDK 源码 / 反编译验证)
1. 类加载与初始化底层(执行顺序)
子类对象创建时,JVM 严格执行父类优先机制:
- 加载子类 → 先加载父类(递归至
Object); - 父类静态代码块 → 子类静态代码块;
- 父类构造代码块 → 父类构造方法;
- 子类构造代码块 → 子类构造方法。
2. 内存模型
子类对象在堆内存中,会完整包含父类对象的实例:
- 子类对象 = 子类独有成员 + 父类继承的成员;
super关键字本质:指向子类对象中父类部分的引用。
3. 方法重写底层:动态绑定
- 静态方法:编译期绑定(静态绑定),不存在重写;
- 实例方法:运行期绑定(动态绑定),JVM 根据实际对象类型调用重写后的方法。
4. 反编译验证(默认 super () 调用)
测试代码:
class Parent {}
class Child extends Parent {
public Child() {}
}
反编译命令:javap -c Child核心字节码:
0: aload_0
1: invokespecial #1 // Method Parent."<init>":()V
结论:子类构造方法默认第一行隐藏 super(),强制调用父类无参构造。
5. JDK 源码中的继承典范
// AbstractList 封装了List的公共逻辑
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {}
// ArrayList 复用AbstractList,仅实现独有功能
public class ArrayList<E> extends AbstractList<E> implements List<E> {}
三、代码示例
1. 基础继承 + 方法重写 + super 用法(标准示例)
// 父类:抽取公共属性和方法
class Person {
protected String name;
protected int age;
// 父类构造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 公共方法
public void showInfo() {
System.out.println("姓名:" + name + ",年龄:" + age);
}
}
// 子类:继承Person,扩展功能
class Student extends Person {
private String studentId; // 子类独有属性
// 子类构造:必须先调用父类构造
public Student(String name, int age, String studentId) {
super(name, age); // 调用父类有参构造(必须在第一行)
this.studentId = studentId;
}
// 方法重写:Override 父类方法
@Override
public void showInfo() {
super.showInfo(); // 调用父类原有方法
System.out.println("学号:" + studentId); // 扩展功能
}
}
// 测试
public class ExtendsDemo {
public static void main(String[] args) {
Student student = new Student("张三", 20, "S1001");
student.showInfo();
}
}
2. 执行顺序验证示例
class Parent {
static { System.out.println("父类静态代码块"); }
{ System.out.println("父类构造代码块"); }
public Parent() { System.out.println("父类构造方法"); }
}
class Child extends Parent {
static { System.out.println("子类静态代码块"); }
{ System.out.println("子类构造代码块"); }
public Child() { System.out.println("子类构造方法"); }
}
// 输出结果:严格遵循 父静态→子静态→父构造块→父构造→子构造块→子构造
四、高频踩坑点与避坑方案
坑点 1:子类继承父类私有成员(误区)
- 问题:误以为
private变量 / 方法能被继承; - 真相:
private成员属于父类私有,子类无法访问、无法继承; - 避坑:父类私有属性通过
public/protected的getter/setter访问。
坑点 2:子类构造方法未调用父类构造
- 问题:父类无无参构造,子类编译报错;
- 原因:子类默认调用
super(),父类无无参构造时必须手动写super(参数); - 避坑:父类提供无参构造,或子类手动调用有参构造。
坑点 3:方法重写违反规则(编译报错)
重写强制规则:
- 方法名、参数列表、返回值(协变)必须完全一致;
- 子类方法权限不能小于父类(父类
public,子类不能写private); - 子类异常不能大于父类;
- 静态方法不能重写(只能隐藏)。
坑点 4:滥用继承,导致代码强耦合
- 问题:为了复用代码随意继承,破坏类的关系(如
Dog extends Car); - 原则:继承必须满足
is-a关系(Student is a Person); - 避坑:组合优于继承(优先用包含关系复用代码)。
坑点 5:混淆 super 和 this
- 问题:
super()不写在第一行,编译报错; - 规则:
super()/this()必须在构造方法第一行,且不能同时存在。
坑点 6:成员变量隐藏
- 问题:子类和父类定义同名变量,子类覆盖父类变量;
- 避坑:尽量避免同名成员变量,必须使用时用
super.变量区分。
五、面试高频考点与标准答案
1. 什么是继承?继承的作用是什么?
标准答案:继承是子类使用 extends 复用父类非私有成员的机制;作用:代码复用、层级抽象、扩展功能、是多态的前提。
2. Java 为什么只能单继承?
标准答案:为了避免菱形问题(钻石问题):多个父类有同名方法,子类无法确定调用哪个;Java 类单继承,接口支持多实现,兼顾安全与灵活。
3. super 和 this 的区别?
标准答案:
this:指向当前对象,访问本类成员;super:指向父类对象,访问父类成员;- 都必须在构造方法第一行,不能同时使用;
this可单独使用,super依赖继承。
4. 方法重写(Override)和重载(Overload)的区别?
标准答案:
| 特性 | 重写(Override) | 重载(Overload) |
|---|---|---|
| 位置 | 父子类中 | 同一个类中 |
| 参数列表 | 必须相同 | 必须不同 |
| 权限 | 不能缩小 | 无限制 |
| 多态 | 运行期多态 | 编译期无多态 |
5. 子类对象初始化的执行顺序?
标准答案:父类静态代码块 → 子类静态代码块 → 父类构造代码块 → 父类构造方法 → 子类构造代码块 → 子类构造方法。
6. 私有成员能被继承吗?构造方法能被继承吗?
标准答案:
private成员不能被继承;- 构造方法不能被继承,只能通过
super()调用。
六、项目改造 / 落地记录
核心设计原则:组合优于继承(企业开发第一准则)
1. 改造前(滥用继承,强耦合,严禁使用)
// 错误:为了复用代码,强行继承(Dog不是Car)
class Car {
public void run(){}
}
class Dog extends Car {
// 逻辑混乱,耦合度极高
}
2. 改造后(合理继承 + 组合优先)
场景 1:合理继承(满足 is-a 关系)
// 父类:公共业务实体
public class BaseEntity {
protected Long id;
protected Date createTime;
protected Date updateTime;
}
// 子类:业务实体继承父类,复用公共字段
public class User extends BaseEntity {
private String username;
}
public class Order extends BaseEntity {
private String orderNo;
}
场景 2:组合替代继承(推荐)
// 工具类
class RunUtils {
public void run(){}
}
// 组合:包含工具类对象,复用功能
class Dog {
private RunUtils runUtils = new RunUtils();
public void run() {
runUtils.run();
}
}
3. 改造落地好处
- 低耦合:组合不依赖继承关系,修改父类不影响子类;
- 逻辑清晰:仅
is-a关系使用继承,符合业务语义; - 易维护:公共属性抽取到父类,统一管理;
- 符合规范:遵循阿里 Java 开发手册,避免滥用继承。
总结
- 核心本质:继承 =
extends+ 代码复用 + 扩展功能,Java 类单继承,必须满足is-a关系; - 底层关键:子类对象包含父类实例,初始化父类优先,方法重写是动态绑定;
- 避坑铁律:私有成员 / 构造方法不能继承,重写遵守权限规则,组合优于继承;
- 实战规范:公共属性 / 方法抽取父类,业务无关类用组合复用代码,禁止滥用继承。
更多推荐



所有评论(0)