×

假设你要开发一个员工管理系统。张三是一名普通员工,李四是一名经理,王五是一名技术总监。

  • 普通员工有:姓名、工号、工资、打卡、领工资
  • 经理有:姓名、工号、工资、打卡、领工资、管理团队、审批报销
  • 技术总监有:姓名、工号、工资、打卡、领工资、制定技术方案、面试候选人

写三个独立类,你会写出大量重复代码——三个类都有姓名、工号、工资,都有打卡和领工资的方法。

这不合理。现实世界中”经理”是”员工”的一种特殊类型——经理”“(is-a)员工,只是比普通员工多了某些功能。

继承正是为了解决这个问题而存在的:把公共属性和方法抽取到父类(基类/超类),子类(派生类)通过 extends 关键字继承父类,并在此基础上扩展自己的特性。

为什么需要继承

在编写了前三篇类与对象的内容后,你已经会用类来描述现实世界的实体。但遇到多个类有共性时,逐个编写会导致:

  • 代码冗余——同样的字段和方法在多个类中重复
  • 维护困难——修改公共逻辑需要到处同步
  • 扩展性差——新增一种员工类型又得复制一遍

继承让代码可复用、可扩展、能分层

extends 关键字与继承语法

Java 使用 extends 关键字实现继承。

基本语法

// 父类
class Employee {
    String name;
    int id;
    double salary;

    void punchClock() {
        System.out.println(name + " 打卡上班");
    }

    void getPaid() {
        System.out.println(name + " 领取工资:" + salary);
    }
}

// 子类继承父类
class Manager extends Employee {
    int teamSize;

    void manageTeam() {
        System.out.println(name + " 管理 " + teamSize + " 人团队");
    }

    void approveExpense() {
        System.out.println(name + " 审批报销");
    }
}

👉 Manager 没有定义 namesalarypunchClock(),但这些它都有——从 Employee 继承而来。

测试继承

public class InheritanceDemo {
    public static void main(String[] args) {
        Manager mgr = new Manager();
        mgr.name = "李四";       // 继承自 Employee
        mgr.salary = 25000;      // 继承自 Employee
        mgr.teamSize = 8;        // 自己的字段

        mgr.punchClock();        // 继承自 Employee
        mgr.manageTeam();        // 自己的方法
    }
}

输出:

李四 打卡上班
李四 管理 8 人团队

继承的核心特性

① 子类拥有父类所有的非 private 成员

访问修饰符当前类同包子类其他包
private
默认(包级)
protected
public

子类无法继承 private 成员,但可以通过父类提供的 public/protected 方法间接访问。

② Java 只支持单继承

class A extends B, C { }  // ❌ 编译错误!Java 不支持多继承

一个子类只能有一个直接父类。那如果确实需要”多重特性”怎么办?后续文章会讲——接口(interface)可以解决。

③ 继承关系是 “is-a” 关系

适用场景用一句话判断:子类 is-a 父类

Manager is-a Employee?  ✅ 成立
Dog is-a Animal?        ✅ 成立
Circle is-a Shape?      ✅ 成立

如果这句话读不通,就不应该用继承——应该用组合(后面会详细讲)。

④ 传递性

class A { }
class B extends A { }
class C extends B { }

C 继承了 B 的所有非私有成员,B 继承了 A 的所有非私有成员——所以 C 间接也拥有 A 的成员。这叫多层继承

每个类都隐式继承 Object

你可能没写过 extends,但每个 Java 类都默认继承 java.lang.Object

class MyClass { }           // 等价于 class MyClass extends Object { }

Object 提供了几个所有对象都有的方法:

方法作用
toString()返回对象的字符串表示
equals(Object obj)比较两个对象是否”相等”
hashCode()返回对象的哈希码
getClass()返回对象的运行时类信息
clone()创建并返回对象的一个副本
public class ObjectDemo {
    public static void main(String[] args) {
        Employee emp = new Employee();
        System.out.println(emp.toString());  // 来自 Object
        System.out.println(emp.getClass());  // 来自 Object
    }
}

输出类似:

Employee@1b6d3586
class Employee

toString() 默认输出”类名@哈希值”——后续文章会讲如何重写它,让它输出更有意义的信息。

💡 思考:既然所有类都继承 Object,那 intdouble 等基本类型呢?它们不是类,不继承 Object。但它们的包装类(IntegerDouble)继承 Object——第 ⑧ 篇会讲枚举与包装类。

继承 vs 组合(Composition)

这是一个常被忽视的设计思考题。很多初学者一发现继承好用就到处用,但其实优先使用组合,谨慎使用继承

什么是组合?

组合就是在一个类中持有另一个类的引用,表示”has-a“关系。

// 组合 - 汽车有引擎
class Engine {
    void start() { System.out.println("引擎启动"); }
}

class Car {
    private Engine engine = new Engine();  // 组合

    void start() {
        engine.start();
        System.out.println("汽车启动");
    }
}

什么时候用继承,什么时候用组合?

场景用继承用组合原因
经理”是”一种员工is-a 关系
汽车”有”引擎has-a 关系
需要复用一个类的实现组合更灵活,不破坏封装
需要利用多态(后续篇讲)多态依赖继承
只想复用代码,不是表达is-a组合是更好的重用方式

一个反例

// ❌ 不要为了复用 getTotalPrice() 就让 Order 继承 ArrayList
class Order extends ArrayList<Item> { ... }

Order 不是一种 ArrayList——它是”订单有商品列表”,应该用组合:

// ✅ 组合更合理
class Order {
    private List<Item> items = new ArrayList<>();
    // ...
}

💡 一句话原则:继承是用来表达”是什么”的,不是用来”拿代码”的。

本篇小结

今天你学到了:

  • 继承的本质——通过 extends 关键字让子类复用父类的非私有成员,表达 is-a 关系
  • 语法——class Manager extends Employee 即可完成继承
  • 访问性——子类可以访问父类的 publicprotected 成员,不能访问 private
  • 单继承——Java 一个子类只能有一个直接父类
  • Object 祖先——所有类默认继承 java.lang.Object
  • 继承 vs 组合——能用组合优先用组合,继承只用在 is-a 场景

下一篇预告: 继承不能只靠继承字段和方法——子类对象如何初始化?父类构造器和子类构造器谁先执行?super 关键字如何灵活调用父类成员?

下一篇 《继承进阶:super 关键字与构造器链》,带你深入继承的底层机制。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

作者

2248768396@qq.com

相关文章

20. 接口基础:语法、特性与抽象类对比

抽象类讲完了。你知道父类可以只声明方法签名,...

读出全部

19. 多态的应用:参数多态化、工厂模式与支付系统实战

上一篇我们拆解了多态的底层机制——动态绑定、...

读出全部

18. 多态的本质:动态绑定、向上转型与向下转型

抽象类学完了,你现在应该知道 「父类只声明、...

读出全部