Day15 --【Java全栈】面向对象编程[3]继承 继承Inheritance全程沿用奶茶店统一类比体系和封装、访问修饰符、this 知识点完全串联从问题引入到语法实现、从核心特性到常见误区逐层拆解。先思考一个问题 如果我们要写「珍珠奶茶类」「芋泥奶茶类「鲜果茶类」三个类它们都有饮品名称、价格、温度这些通用属性也都有获取价格、饮用这些通用方法。如果每个类都把这些属性和方法重新写一遍会出现什么问题最直接的问题就是代码大量重复修改通用逻辑要改好几个地方维护成本高还容易改漏。 面向对象里专门用来解决「代码复用、层级抽象」问题的特性就是继承。一、继承是什么 通俗理解奶茶店不会给每一款饮品单独从零设计配方模板而是先做一份「通用饮品基础模板」上面写清楚所有饮品都有的要素名称、定价规则、基础制作步骤。 具体的珍珠奶茶、芋泥奶茶、果茶都直接沿用这份基础模板只需要补充自己独有的配料、调整专属的制作步骤就行不用从零开始写。对应到代码里通用饮品模板 父类基类 / 超类抽取所有子类共有的属性和方法是抽象的通用模板具体奶茶品类 子类派生类继承父类的所有通用内容再扩展自己的特有属性和方法一句话理解继承就是「子类复用父类的属性和方法不用重复写还能按需扩展」。 记忆正式定义继承是面向对象三大核心特性之一是实现代码复用的核心机制。它允许一个类子类通过关键字继承另一个类父类的非私有属性与方法子类可以直接复用父类成员也可以定义自己的专属成员甚至重写父类的方法逻辑。继承的三大核心价值代码复用减少冗余 通用属性和方法只在父类写一次所有子类直接复用不用重复编写大幅减少重复代码。层级清晰便于扩展 把通用逻辑向上抽取到父类子类只关注自己的专属逻辑结构分层清晰新增品类时只需要继承父类、补充独有内容即可。为多态提供基础 继承是多态的前提没有父子类的继承关系多态就无法实现多态后续会详细讲。二、继承的基础语法1. 核心关键字extendsJava 中通过extends关键字实现继承语法格式// 子类 extends 父类 public class 子类类名 extends 父类类名 { // 子类自己的属性和方法 }2. 基础代码示例奶茶店场景先定义父类所有饮品的通用模板/** * 父类基础饮品类 * 抽取所有饮品共有的属性和方法 */ public class BaseDrink { // 通用属性所有饮品都有名称、价格、温度 private String name; private double price; private int temperature; // 通用方法获取价格 public double getPrice() { return price; } public void setPrice(double price) { if(price 0) { System.out.println(价格不能为负); return; } this.price price; } // 通用方法饮用 public void drink() { System.out.println(饮用 name 温度 temperature 度); } // 通用get/set方法 public String getName() { return name; } public void setName(String name) { this.name name; } public int getTemperature() { return temperature; } public void setTemperature(int temperature) { this.temperature temperature; } }再定义子类奶茶类继承基础饮品类/** * 子类奶茶类 * 继承BaseDrink复用所有通用属性和方法只需要补充奶茶独有的内容 */ public class MilkTea extends BaseDrink { // 子类独有属性茶底 private String teaBase; // 子类独有方法加小料 public void addTopping(String topping) { System.out.println(给 getName() 添加 topping); } // 茶底的get/set public String getTeaBase() { return teaBase; } public void setTeaBase(String teaBase) { this.teaBase teaBase; } } 效果MilkTea类里虽然只写了茶底和加小料的方法但它自动拥有了父类BaseDrink的 name、price、temperature 属性以及 drink、getPrice 等所有 public 方法创建对象后可以直接调用public class Test { public static void main(String[] args) { MilkTea pearlTea new MilkTea(); // 调用父类继承来的方法 pearlTea.setName(珍珠奶茶); pearlTea.setPrice(12); pearlTea.setTemperature(60); // 调用子类自己的方法 pearlTea.setTeaBase(红茶); pearlTea.addTopping(珍珠); // 调用父类继承来的方法 pearlTea.drink(); } }3. 子类到底能继承到什么继承不是父类所有内容都原封不动拿过来能不能继承完全由访问修饰符决定和之前讲的权限规则完全对应父类成员的修饰符子类能否直接访问public✅ 可以直接访问protected✅ 可以直接访问继承的核心设计之一default包私有同包子类可以不同包子类不行private❌ 不能直接访问 补充父类的 private 属性子类不是 “完全拿不到”只是不能直接用this.属性名访问依然可以通过父类提供的 public get/set 方法间接操作这也是「封装 继承」配合的典型场景。三、继承的核心规则与 super 关键字1. Java 是单继承机制Java 中一个子类只能有一个直接父类不支持同时继承多个类比如不能写class A extends B, C。 但支持多层继承A 继承 BB 继承 CA 会同时拥有 B 和 C 的所有可继承成员形成「爷爷 → 爸爸 → 儿子」的继承树。 比如BaseDrink→MilkTea→PearlMilkTea2. 构造方法不能被继承父类的构造方法子类不能继承复用。但子类构造方法执行时默认会先调用父类的无参构造方法相当于先初始化父类的通用属性再初始化子类的专属属性。 这个默认调用的动作就是靠super()实现的。3. super 关键字父类对象的引用super代表当前对象中父类部分的引用专门用来访问父类的成员和我们之前讲的this是对应关系。 它有三大核心用法和this一一对应用法作用super.属性名访问父类的成员变量super.方法名(参数)调用父类的成员方法super(参数)调用父类的构造方法用法 1调用父类构造方法子类构造方法的第一行默认会有一个隐藏的super();自动调用父类的无参构造。 如果父类没有无参构造比如手动写了有参构造子类就必须手动用super(参数)调用父类的有参构造否则编译报错。代码示例// 父类写了有参构造没有无参构造 public class BaseDrink { private String name; private double price; public BaseDrink(String name, double price) { this.name name; this.price price; } } // 子类必须手动调用父类有参构造 public class MilkTea extends BaseDrink { private String teaBase; public MilkTea(String name, double price, String teaBase) { // 必须写在第一行调用父类有参构造先初始化父类属性 super(name, price); this.teaBase teaBase; } } 记忆super(参数)和this(参数)一样都必须写在构造方法的第一行所以二者不能同时出现在同一个构造方法里。用法 2调用父类的普通方法子类重写父类方法后如果还想用到父类原有的逻辑可以用super.方法名()调用。 这个用法在方法重写里非常常用下面讲重写的时候会结合示例。四、方法重写Override子类继承了父类的方法但父类的通用逻辑不一定完全符合子类的需求这时候子类可以重新实现父类的同名方法这就是方法重写也叫方法覆盖。 通俗理解父类的通用模板里写了「基础制作步骤」但珍珠奶茶有自己的专属制作流程就可以把制作方法重写一遍保留通用部分补充专属步骤不用受父类逻辑的限制。1. 重写的代码示例// 父类 public class BaseDrink { public void make() { System.out.println(准备基础饮品原料); } } // 子类重写make方法 public class MilkTea extends BaseDrink { // Override 是注解用来标记这是重写的方法编译器会帮你检查格式是否正确 Override public void make() { // 可以选择先调用父类的原有逻辑也可以完全重写 super.make(); System.out.println(加入茶底和牛奶); System.out.println(添加配料摇匀); } }2. 重写的核心规则面试高频重写不是随便写个同名方法就行必须遵守以下规则否则就不是重写方法签名必须一致方法名、参数列表、参数顺序必须和父类完全相同返回值类型一致基本类型必须相同引用类型可以是父类返回值的子类访问权限不能更严格父类是 public子类就不能写成 protected 或 private抛出的异常不能更大子类方法不能抛出比父类更宽泛的异常类型3. 重写 vs 重载别再混淆很多不熟悉的同学容易把重写和重载搞混二者完全不是一个维度的概念对比表一目了然对比项方法重写Override方法重载Overload适用场景父子类之间同一个类内部方法签名必须完全一致方法名相同参数列表必须不同返回值必须一致或子类无要求访问权限不能更严格无要求核心作用子类重定义父类方法逻辑实现运行时多态同一个方法名适配不同参数提升易用性 记忆一句话区分 —— 重写是父子类的 “覆盖”重载是同个类的 “同名不同参”。五、所有类的根父类ObjectJava 中有一个特殊的类java.lang.Object它是所有类的顶级父类。 哪怕你写类的时候没写extends编译器也会默认让它继承 Object 类。也就是说所有 Java 对象都拥有 Object 类中定义的方法最常用的有三个toString()把对象转成字符串表示equals()判断两个对象内容是否相等hashCode()返回对象的哈希值这些方法所有类都能直接用也可以根据需求重写比如我们经常重写实体类的toString()和equals()方法就是继承自 Object 的重写应用。六、不熟悉的同学容易踩的认知误区误区 1继承就是把父类的代码复制粘贴到子类里✅ 正解不是复制是复用引用。子类对象里包含了一个完整的父类对象结构二者是包含关系不是复制关系父类修改后所有子类都会同步生效。误区 2private 属性不能被继承所以子类完全没有✅ 正解private 属性子类不能直接访问但它依然存在于子类对象中只是受封装保护只能通过父类的 public 方法间接操作。误区 3继承越多、层级越深越好✅ 正解继承会增强类之间的耦合度 —— 父类修改会影响所有子类层级过深会导致代码难以维护。 继承只适合真正的「is-a」关系奶茶是饮品不要为了单纯复用代码就强行用继承复用代码还可以用组合、工具类等更灵活的方式。误区 4只要方法名一样就是重写✅ 正解必须满足参数列表、返回值、访问权限等所有规则才是重写建议加上Override注解让编译器帮你校验格式避免写错了自己还不知道。七、和之前知识点的串联关联封装父类的私有属性靠封装隐藏子类通过公共 get/set 方法间接访问二者配合实现「内部数据保护 外部代码复用」。关联访问修饰符protected权限就是专门为继承设计的 —— 让子类能访问父类成员同时对外保持隐藏。关联 this 关键字this指向当前对象本身super指向当前对象的父类部分二者配合完成对象内部的成员访问。关联方法重载重载是同类内的多同名方法重写是父子类的方法覆盖二者是完全不同的语法特性。