Java 语言本身不支持 private 继承。

与 C++ 不同,C++ 允许你指定 public、protected 或 private 的继承方式,这会改变基类成员在派生类中的访问权限,Java 只支持 public 继承。
你为什么会看到关于 "private 继承" 的讨论呢?这通常指的是一种设计模式或编程技巧,它通过使用 private 修饰符来模拟 private 继承的效果,从而达到特定的设计目的。
为什么 Java 不支持 private 继承?
Java 的设计哲学是“简单性”和“清晰性”。private 继承会带来一些复杂性和模糊性:
- 违反“is-a”关系:
public继承在 Java 中代表一种清晰的 "is-a"(是一个)关系。Dog是一个Animal,如果继承是private,Dog就不是一个Animal了,这种关系就被破坏了,代码的可读性和意图会变得模糊。 - 访问控制混乱:在 C++ 中,
private继承会使所有基类的public和protected成员在派生类中变为private,这使得派生类本身无法向上转型(upcast)为基类类型,这破坏了多态性的核心思想,Java 为了保持其多态模型的纯粹性,不允许这样做。 - 接口与实现的分离:Java 强调通过接口(
interface)来定义行为,通过类来实现。private继承模糊了接口和实现的界限。
Java 中如何模拟 private 继承的效果?
虽然 Java 没有 private 继承的语法,但我们可以通过组合和内部类等技术来达到类似的目的,这种模拟方式的核心思想是:

“有一个”(has-a)关系,而不是“是一个”(is-a)关系。
我们使用一个 private 的内部类来持有父类的实例,从而将父类的实现完全“隐藏”在外部类中。
模拟方法一:使用 private 内部类
这是最常见也是最推荐的方式来模拟 private 继承。
场景:假设我们有一个基类 Engine,我们想创建一个 Car 类,它“拥有”一个 Engine,但不希望 Car 的使用者能够直接访问 Engine 的公共接口,也不希望 Car 被当作 Engine 来使用。

代码示例:
// 基类 - 父类
class Engine {
public void start() {
System.out.println("Engine is starting...");
}
public void stop() {
System.out.println("Engine is stopping.");
}
}
// 模拟 private 继承的类
class Car {
// 核心:将父类的实例作为私有内部类的一个成员
// 这是一种组合,并且组合关系是私有的
private final Engine engine;
// 构造器中创建 Engine 实例
public Car() {
this.engine = new Engine();
}
// Car 提供自己的公共方法,内部调用 engine 的方法
// 这就是“委托”(Delegation)
public void startCar() {
System.out.println("Car is about to start...");
engine.start(); // 委托给 engine
}
public void stopCar() {
System.out.println("Car is about to stop...");
engine.stop(); // 委托给 engine
}
}
// 使用者
public class Main {
public static void main(String[] args) {
Car myCar = new Car();
// 我们可以启动和停止汽车
myCar.startCar();
myCar.stopCar();
// 我们无法直接访问 Car 内部的 Engine 实例
// myCar.engine.start(); // 编译错误!因为 engine 是 private 的
// Car 也不是一个 Engine,无法向上转型
// Engine e = myCar; // 编译错误!
}
}
这种方式的优点:
- 实现封装:
Car完全控制了Engine的使用方式,外部代码只能通过Car提供的startCar()和stopCar()方法来间接使用Engine的功能。 - 破坏 "is-a" 关系:
Car绝不是一个Engine,这符合我们的设计意图。 - 灵活性:可以在
Car的公共接口中添加额外的逻辑,或者在将来替换Engine的实现(用一个ElectricEngine替换Engine),而不会影响Car的使用者。
为什么需要模拟 private 继承?(使用场景)
模拟 private 继承(即上面的组合+委托模式)主要解决以下问题:
-
实现代码复用,而不暴露接口
- 你想使用一个现有类的功能(
Engine的start和stop),但不希望你的类(Car)继承它的公共接口,你可能只想使用其中一小部分功能,或者想以不同的方式暴露这些功能。 - 这避免了因继承带来的不必要的方法污染。
- 你想使用一个现有类的功能(
-
阻止向上转型
- 这是最关键的一点。
private继承的核心目的之一就是禁止派生类对象被当作基类对象使用,上面的例子中,Car对象绝不能被当作Engine对象处理,这正是通过不继承实现的。
- 这是最关键的一点。
-
控制实例化
- 通过将父类对象作为私有成员,你可以精确控制它的生命周期和行为,你可以在
Car的构造函数中初始化Engine,并在Car销毁时做一些清理工作。
- 通过将父类对象作为私有成员,你可以精确控制它的生命周期和行为,你可以在
与 C++ private 继承的对比
| 特性 | C++ private 继承 |
Java 模拟 private 继承 (组合+委托) |
|---|---|---|
| 语法 | class Derived : private Base { ... }; |
class Car { private final Engine engine; ... } |
| 关系 | 语法上是继承,但语义上是 "implemented-in-terms-of" (根据...实现) | 明确是 "has-a" (有一个) 关系 |
| 成员访问 | 基类的 public/protected 成员在派生类中变为 private。 |
派生类(外部类)通过 private 成员访问基类实例,可以精细控制哪些方法可以被调用。 |
| 向上转型 | 不允许。Derived* 不能安全地转换为 Base*。 |
不允许。Car 对象不能被转换为 Engine 对象。 |
| 多态性 | 不适用,因为无法向上转型,所以不能通过基类指针调用派生类方法。 | 不适用,这是模拟 private 继承的目的之一。 |
| 构造/析构 | 自动调用基类的构造/析构函数。 | 需要在外部类中手动管理基类成员的构造和销毁(通过构造函数和 finalize 或其他清理机制)。 |
- Java 没有
private继承,这是与 C++ 的一个关键区别。 - 当人们谈论 Java 中的 "private 继承" 时,他们指的是一种设计模式,即使用
private成员变量来组合一个父类对象,并通过委托来使用其功能。 - 这种模式的核心目的是复用代码实现,同时隐藏父类的接口并阻止“is-a”关系,从而获得更好的封装性和灵活性。
- 在 Java 中,优先考虑组合(has-a)而不是继承(is-a),这通常被认为是更健壮、更灵活的设计原则,模拟
private继承正是这一原则的体现。
