0

0

在Java中组合和继承如何取舍_Java面向对象设计原则解析

P粉602998670

P粉602998670

发布时间:2026-02-11 09:51:10

|

655人浏览过

|

来源于php中文网

原创

应优先使用组合而非继承,因继承仅适用于“是”关系(如Car是Vehicle),而“有”关系(如Car有Engine)须用组合;滥用继承导致ClassCastException、空实现、维护困难等问题,且易违反Liskov替换原则。

在java中组合和继承如何取舍_java面向对象设计原则解析

继承用在“是”关系,组合用在“有”关系

判断该不该用继承,就看子类是不是父类的一种。比如 CarVehicle 的一种,用继承合理;但 CarEngine,就不能让 Car extends Engine——这是典型的组合误用。

常见错误现象:ClassCastException 频繁出现、子类被迫重写大量空实现、父类一改,多个子类全崩。这些问题往往源于把“能复用”当成了“该继承”。

  • 如果只是为了代码复用,优先考虑组合 + 接口(比如让 Car 持有 Engine 类型的字段)
  • 若使用继承,父类应尽量抽象、稳定,避免含具体实现逻辑(尤其是非 protected 的可变字段)
  • Java 中 final 类(如 String)或 private 构造器类(如 Collections 工具类)天然排斥继承,这时组合是唯一选择

组合更易测试和替换,继承破坏封装

组合对象可以轻松被 mock 或替换,比如单元测试中把真实的 DatabaseService 换成 MockDatabaseService;而继承关系下,子类直接依赖父类的实现细节,测试时几乎无法隔离。

性能上差异极小,但可维护性差距明显:继承链越深,修改成本越高;组合则通过接口契约解耦,各模块演进互不干扰。

立即学习Java免费学习笔记(深入)”;

ChatGPT Website Builder
ChatGPT Website Builder

ChatGPT网站生成器,AI对话快速生成网站

下载
  • 用组合时,优先依赖接口而非具体类(private DataSource dataSource 而非 private MySQLDataSource dataSource
  • 避免在父类里暴露 protected 方法供子类调用——这等于把内部逻辑“拱手让人”,后续想改都得同步通知所有子类
  • IDE 重构时,“提取超类”(Extract Superclass)容易诱使人盲目建继承,要警惕;而“提取接口”(Extract Interface)+ 组合更安全

Liskov 替换原则(LSP)是继承的硬门槛

如果子类不能在任何场景下无缝替代父类,那继承就是错的。典型反例:Square extends Rectangle —— 当调用 setHeight() 时,正方形必须同步改宽度,违背了父类对长宽独立控制的契约。

这种问题不会在编译时报错,但运行时行为诡异,且难以调试。组合则天然规避这类陷阱,因为“拥有”不承诺行为一致性。

  • 检查子类是否重写了父类方法并改变了前置/后置条件(比如父类允许 null 参数,子类抛 NullPointerException
  • 子类不应缩小父类方法的访问权限(如父类 public,子类改成 protected
  • 一旦发现子类需要 instanceof 判断自身类型再分支处理,大概率已违反 LSP,应回退到组合 + 策略模式

Java 8+ 默认方法让接口也能复用逻辑,进一步削弱继承必要性

过去接口只能定义契约,现在 default 方法允许提供默认实现,配合组合,能覆盖大部分“想复用又不想继承”的场景。

例如日志能力,不必让所有业务类继承 BaseLogger,而是定义 Loggable 接口带 default void log(String msg),再由各类自行 implements Loggable 并组合具体日志器。

  • 注意 default 方法不能访问实现类的私有字段,所以它适合通用行为(格式化、转发),不适合强状态耦合逻辑
  • 若多个 default 方法存在冲突(如两个接口都定义了同签名 default 方法),实现类必须显式重写,这反而强化了设计意图的表达
  • 比起继承,这种方式更轻量,且不强制类层级结构,对已有类添加能力零侵入
实际项目里最麻烦的不是选错组合或继承,而是混用:既继承又组合,又在父类里暴露内部状态,还让子类去“修复”父类设计缺陷。这种叠加态会让后续所有人不敢动、不敢测、不敢删。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

688

2023.08.02

c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

243

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

685

2024.03.01

go语言 面向对象
go语言 面向对象

本专题整合了go语言面向对象相关内容,阅读专题下面的文章了解更多详细内容。

56

2025.09.05

java面向对象
java面向对象

本专题整合了java面向对象相关内容,阅读专题下面的文章了解更多详细内容。

57

2025.11.27

javascriptvoid(o)怎么解决
javascriptvoid(o)怎么解决

javascriptvoid(o)的解决办法:1、检查语法错误;2、确保正确的执行环境;3、检查其他代码的冲突;4、使用事件委托;5、使用其他绑定方式;6、检查外部资源等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

180

2023.11.23

java中void的含义
java中void的含义

本专题整合了Java中void的相关内容,阅读专题下面的文章了解更多详细内容。

109

2025.11.27

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1390

2023.10.19

2026春节习俗大全
2026春节习俗大全

本专题整合了2026春节习俗大全,阅读专题下面的文章了解更多详细内容。

68

2026.02.11

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 3.5万人学习

C# 教程
C# 教程

共94课时 | 9.2万人学习

Java 教程
Java 教程

共578课时 | 63.4万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号