尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

Java11:Java继承详解:让代码复用的艺术更上一层楼

Java11:Java继承详解:让代码复用的艺术更上一层楼
📅 发布时间:2026/7/12 2:50:18

目录

写在前面

一、为什么需要继承?

1.1 没有继承的痛苦

1.2 继承的解决方案

二、继承的基本概念

2.1 什么是继承?

2.2 继承的语法

2.3 使用继承的注意事项

三、子类中访问父类成员

3.1 访问成员变量

3.2 访问成员方法

四、super关键字:访问父类成员的通行证

4.1 为什么需要super?

4.2 super的三种用法

4.3 super的注意事项

五、子类构造方法详解

5.1 一个常见的问题

5.2 正确的做法

5.3 执行顺序

5.4 构造方法中的super规则

六、super和this的区别

七、继承关系上的初始化顺序

7.1 没有继承时的顺序

7.2 有继承时的顺序

八、访问权限修饰符:protected

九、继承方式与final关键字

9.1 Java支持的继承方式

9.2 final关键字

十、继承与组合

10.1 两种关系的区别

10.2 组合的代码示例

10.3 继承 vs 组合

十一、总结


写在前面

在前面的文章中,我们学习了面向对象的基础概念:类与对象、封装、static关键字等。你已经知道如何用类来描述现实世界中的事物,并通过封装来保护数据的安全。

但现实世界是复杂的。猫和狗有很多相似之处——它们都有名字、年龄,都会吃东西、睡觉。如果按照之前的方式,我们需要在Dog类和Cat类中分别定义这些属性和方法,造成大量的重复代码。

有没有一种方式,能把这些共性抽取出来,让猫和狗共享呢?

这就是我们今天要学习的核心主题——继承。


一、为什么需要继承?

1.1 没有继承的痛苦

假设我们要用Java描述猫和狗这两种动物。按照之前的知识,我们会写出这样的代码:

// Dog.java public class Dog { String name; int age; public void eat() { System.out.println(name + "正在吃饭"); } public void sleep() { System.out.println(name + "正在睡觉"); } public void bark() { System.out.println(name + "汪汪汪~~~"); } } // Cat.java public class Cat { String name; int age; public void eat() { System.out.println(name + "正在吃饭"); } public void sleep() { System.out.println(name + "正在睡觉"); } public void mew() { System.out.println(name + "喵喵喵~~~"); } }

仔细观察,你会发现这两个类中存在大量重复的代码:name、age、eat()、sleep()。如果以后还要添加鸟类、鱼类,重复只会越来越多。

1.2 继承的解决方案

面向对象思想中提出了继承的概念,专门用来进行共性抽取,实现代码复用。

继承的核心思想是:将共同的属性和行为抽取到一个父类中,子类通过继承获得父类的所有非私有成员,并在此基础上添加自己特有的内容。

用一句话概括:继承让代码从"复制粘贴"升级为"复用扩展"。


二、继承的基本概念

2.1 什么是继承?

继承是面向对象程序设计中实现代码复用的重要手段。它允许程序员在保持原有类特性的基础上进行扩展,增加新功能,从而产生新的类。

在继承关系中:

  • 父类(也叫基类/超类):被继承的类,存放共性的属性和行为

  • 子类(也叫派生类):继承父类的类,在父类基础上增加自己特有的内容

继承的主要作用是:共性的抽取,实现代码复用。

2.2 继承的语法

在Java中,使用extends关键字来表示继承关系:

修饰符 class 子类 extends 父类 { // 子类特有的成员 }

现在,我们用继承来重新设计猫和狗的代码:

// Animal.java —— 父类 public class Animal { String name; int age; public void eat() { System.out.println(name + "正在吃饭"); } public void sleep() { System.out.println(name + "正在睡觉"); } } // Dog.java —— 子类 public class Dog extends Animal { void bark() { System.out.println(name + "汪汪汪~~~"); } } // Cat.java —— 子类 public class Cat extends Animal { void mew() { System.out.println(name + "喵喵喵~~~"); } } // Test.java —— 测试类 public class Test { public static void main(String[] args) { Dog dog = new Dog(); dog.name = "旺财"; dog.age = 3; // 子类可以访问父类继承下来的成员 dog.eat(); // 旺财正在吃饭 dog.sleep(); // 旺财正在睡觉 dog.bark(); // 旺财汪汪汪~~~ Cat cat = new Cat(); cat.name = "咪咪"; cat.eat(); // 咪咪正在吃饭 cat.mew(); // 咪咪喵喵喵~~~ } }

关键理解:Dog类中并没有定义name、age、eat()、sleep(),但这些成员确实存在于Dog对象中——它们是从Animal类继承下来的。

2.3 使用继承的注意事项

  1. 子类会将父类中的成员变量和成员方法继承到子类中

  2. 子类继承父类之后,必须添加自己特有的成员,体现出与父类的不同,否则就没有必要继承

  3. Java只支持单继承,一个子类只能有一个直接父类(但可以多层继承)


三、子类中访问父类成员

3.1 访问成员变量

情况一:子类和父类不存在同名成员变量

class Base { int a; int b; } class Derived extends Base { int c; public void method() { a = 10; // 访问父类的a b = 20; // 访问父类的b c = 30; // 访问自己的c } }

这种情况下,访问规则很简单:子类有就用子类的,子类没有就去父类找。

情况二:子类和父类存在同名成员变量

class Base { int a; int b; } class Derived extends Base { int a; // 与父类同名 public void method() { a = 100; // 访问的是子类自己的a b = 200; // 父类没有同名变量,访问父类的b } }

核心规则:成员变量访问遵循就近原则。子类自己有就优先用自己的,子类没有再到父类中找。

3.2 访问成员方法

情况一:方法名不同

class Base { public void methodA() { System.out.println("Base中的methodA()"); } } class Derived extends Base { public void methodB() { System.out.println("Derived中的methodB()"); } public void test() { methodA(); // 子类没有,调用父类的methodA() methodB(); // 调用自己的methodB() } }

情况二:方法名相同(重写)

class Base { public void methodA() { System.out.println("Base中的methodA()"); } } class Derived extends Base { // 重写了父类的methodA() public void methodA() { System.out.println("Derived中的methodA()"); } public void test() { methodA(); // 调用的是子类自己的methodA() } }

核心规则:子类优先调用自己的方法,没有则去父类找。如果子类重写了父类的方法,默认调用子类的版本。


四、super关键字:访问父类成员的通行证

4.1 为什么需要super?

当子类和父类中存在同名成员时,直接在子类中访问,永远访问的是子类自己的成员。那如果我们想在子类中访问父类的同名成员,该怎么办?

这就需要super关键字了。

4.2 super的三种用法

用法一:访问父类的成员变量

class Base { int a = 100; } class Derived extends Base { int a = 200; public void show() { System.out.println(a); // 200,访问子类的a System.out.println(this.a); // 200,this也表示当前对象 System.out.println(super.a); // 100,访问父类的a } }

用法二:访问父类的成员方法

class Base { public void method() { System.out.println("父类方法"); } } class Derived extends Base { public void method() { System.out.println("子类方法"); } public void test() { method(); // 子类方法 super.method(); // 父类方法 } }

用法三:调用父类的构造方法

class Base { public Base() { System.out.println("Base()"); } } class Derived extends Base { public Derived() { super(); // 调用父类无参构造,可以省略 System.out.println("Derived()"); } }

4.3 super的注意事项

  1. 只能在非静态方法中使用

  2. 在子类方法中,用于访问父类的成员变量和方法

  3. 在构造方法中,super(...)必须是第一条语句

  4. super(...)和this(...)不能同时出现在同一个构造方法中


五、子类构造方法详解

5.1 一个常见的问题

先看这段代码:

class Base { public Base(int n) { System.out.println("Base(" + n + ")"); } } class Derived extends Base { public Derived() { System.out.println("Derived()"); } }

这段代码会编译报错!为什么呢?

原因:当构造子类对象时,需要先调用父类的构造方法来完成父类部分的初始化。如果父类没有无参构造方法(或者没有显式定义无参构造),编译器就无法自动生成super()调用,从而导致编译失败。

5.2 正确的做法

class Base { public Base(int n) { System.out.println("Base(" + n + ")"); } } class Derived extends Base { public Derived() { super(10); // 显式调用父类的带参构造 System.out.println("Derived()"); } }

5.3 执行顺序

class Base { public Base() { System.out.println("Base()"); } } class Derived extends Base { public Derived() { // 这里默认有 super() System.out.println("Derived()"); } } // 创建子类对象 Derived d = new Derived(); // 输出: // Base() // Derived()

执行顺序总结:

  1. 先执行父类的构造方法

  2. 再执行子类的构造方法

这是因为子类对象由两部分组成:父类继承下来的部分 + 子类新增的部分。先有父后有子,所以必须先完成父类部分的初始化。

5.4 构造方法中的super规则

  1. 如果父类有无参构造(显式定义或默认生成),子类构造方法第一行默认有隐含的super()调用

  2. 如果父类只有带参构造,子类必须显式调用父类的某个构造方法

  3. super(...)必须是子类构造方法中的第一条语句

  4. super(...)在子类构造方法中只能出现一次

  5. super(...)和this(...)不能同时出现


六、super和this的区别

对比维度

this

super

概念

当前对象的引用

子类对象中从父类继承下来的部分的引用

访问范围

本类的成员

父类的成员

构造方法调用

this(...)调用本类其他构造方法

super(...)调用父类构造方法

能否同时出现

不能与super同时出现

不能与this同时出现

在构造方法中的位置

必须是第一条语句

必须是第一条语句

一句话总结:this指向自己,super指向父类部分。


七、继承关系上的初始化顺序

7.1 没有继承时的顺序

class Person { static { System.out.println("Person:静态代码块执行"); } { System.out.println("Person:实例代码块执行"); } public Person() { System.out.println("Person:构造方法执行"); } }

创建两次对象:

Person p1 = new Person(); System.out.println("========================"); Person p2 = new Person();

输出结果:

Person:静态代码块执行 Person:实例代码块执行 Person:构造方法执行 ======================== Person:实例代码块执行 Person:构造方法执行

规律:

  • 静态代码块在类加载时执行,且只执行一次

  • 实例代码块和构造方法在每次创建对象时执行

  • 实例代码块先于构造方法执行

7.2 有继承时的顺序

class Person { static { System.out.println("Person:静态代码块执行"); } { System.out.println("Person:实例代码块执行"); } public Person() { System.out.println("Person:构造方法执行"); } } class Student extends Person { static { System.out.println("Student:静态代码块执行"); } { System.out.println("Student:实例代码块执行"); } public Student() { System.out.println("Student:构造方法执行"); } }

创建两次Student对象:

Student s1 = new Student(); System.out.println("========================"); Student s2 = new Student();

输出结果:

Person:静态代码块执行 Student:静态代码块执行 Person:实例代码块执行 Person:构造方法执行 Student:实例代码块执行 Student:构造方法执行 ======================== Person:实例代码块执行 Person:构造方法执行 Student:实例代码块执行 Student:构造方法执行

完整执行顺序总结:

类加载阶段(仅一次): 1. 父类静态代码块 2. 子类静态代码块 创建对象阶段(每次创建都执行): 3. 父类实例代码块 4. 父类构造方法 5. 子类实例代码块 6. 子类构造方法

八、访问权限修饰符:protected

在继承体系中,protected关键字扮演着重要的角色。它介于default和public之间:

访问权限

同类

同包

不同包子类

不同包非子类

private

✅

❌

❌

❌

default

✅

✅

❌

❌

protected

✅

✅

✅

❌

public

✅

✅

✅

✅

protected的意义:如果一个成员希望被子类访问(即使是不同包的子类),但又不想被所有类访问,就应该使用protected。

使用建议:

  • 成员变量:一般用private,通过getter/setter访问

  • 希望子类访问的方法:用protected

  • 对外公开的接口:用public


九、继承方式与final关键字

9.1 Java支持的继承方式

Java中只支持以下几种继承方式:

  1. 单继承:一个子类只有一个直接父类

  2. 多层继承:A → B → C(C继承B,B继承A)

  3. 不同类继承同一个父类:A → B,A → C(B和C都继承A)

Java不支持多继承(一个子类有多个直接父类),这是为了规避菱形继承带来的复杂性。

9.2 final关键字

final可以用来限制继承:

// 修饰类:该类不能被继承 final class Animal { // ... } // 编译错误!不能继承final类 class Dog extends Animal { // ❌ } // 修饰方法:该方法不能被重写 class Base { public final void method() { // ... } } class Derived extends Base { // 编译错误!不能重写final方法 public void method() { // ❌ // ... } } // 修饰变量:变成常量,不能修改 final int MAX_VALUE = 100; MAX_VALUE = 200; // ❌ 编译错误

实际应用:我们常用的String类就是用final修饰的,所以不能被继承。


十、继承与组合

10.1 两种关系的区别

除了继承,组合也是一种表达类之间关系的方式,同样能达到代码复用的效果。

  • 继承:表示is-a(是一个)的关系。比如:狗是动物,猫是动物

  • 组合:表示has-a(有一个)的关系。比如:汽车有轮胎,汽车有发动机

10.2 组合的代码示例

// 轮胎类 class Tire { // ... } // 发动机类 class Engine { // ... } // 汽车类:通过组合使用Tire和Engine class Car { private Tire tire; // 汽车有轮胎 private Engine engine; // 汽车有发动机 public Car() { tire = new Tire(); engine = new Engine(); } }

10.3 继承 vs 组合

对比维度

继承

组合

关系

is-a

has-a

耦合度

高(子类依赖父类实现)

低(通过接口交互)

灵活性

较差(编译期确定)

较好(运行时可替换)

代码复用

继承父类所有非私有成员

通过对象调用方法

行业建议:能用组合尽量用组合。组合的耦合度更低,更易于维护和扩展。


十一、总结

今天我们学习了Java继承的全部核心知识:

  1. 继承的概念:子类继承父类,复用父类的非私有成员

  2. 继承的语法:使用extends关键字

  3. 成员访问规则:就近原则,子类优先

  4. super关键字:访问父类成员和构造方法

  5. 子类构造方法:必须先调用父类构造方法

  6. 初始化顺序:静态代码块→实例代码块→构造方法,父类先于子类

  7. protected权限:包访问权限的增强版,允许不同包的子类访问

  8. final关键字:限制继承和重写

  9. 继承与组合:is-a vs has-a,优先使用组合

继承是面向对象三大特性之一,它为代码复用和多态奠定了坚实的基础。下一篇文章,我们将学习继承的进阶应用——多态,敬请期待!

相关新闻

  • CTF Web备份泄露实战:Python脚本+ihoneyBakFileScan 2工具扫描5类后缀
  • 虚拟内存实战:3种页面置换算法(FIFO/LRU/CLOCK)缺页率模拟分析
  • TC78H651AFNG与PIC18F97J94的直流有刷电机驱动方案

最新新闻

  • TLA2518 ADC芯片特性与PIC24EP512GU814接口设计详解
  • 基于YOLOv8的无人机红外目标检测系统:低成本实战指南
  • Krea2 ControlNet深度控制网络:AI图像生成的结构精确控制指南
  • 验证码识别:OCR与打码平台实战全解析
  • 拳击视频智能剪辑工具:AI动作识别与自动化处理实战指南
  • C++模板编程实践指南:从环境搭建到项目运行

日新闻

  • IX9104 PCIe5.0 高速交换芯片@ACP#完整规格 + 应用场景总结
  • Unity游戏集成Coze智能体:实现NPC智能对话与知识库联动
  • SAP EPIC 建行回单查询:从标准类CL_EPIC_EXAMPLE_CN_CCB_GHTD到Z类的5处关键修改

周新闻

  • IX9104 PCIe5.0 高速交换芯片@ACP#完整规格 + 应用场景总结
  • Unity游戏集成Coze智能体:实现NPC智能对话与知识库联动
  • SAP EPIC 建行回单查询:从标准类CL_EPIC_EXAMPLE_CN_CCB_GHTD到Z类的5处关键修改

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号