Java基础2-3:继承与实现、多继承、组合与继承

一、前言

  本文内容摘自《深入理解Java核心技术:写给Java工程师的干货笔记(基础篇)》一书,2022年出版,作者 张洪亮(@Hollis),阿里巴巴技术专家,著有《Java工程师成神之路》系列文章,《Java工程师成神之路》电子书已开源,可在阿里云开发者社区免费下载。书籍内容比电子书内容要丰富,内容有修改,有需要的读者可以购买正版书籍。
 
  【如何成神:先搬砖,再砌砖,后造砖!】
 
  本文由 @大白有点菜 原创,请勿盗用,转载请说明出处!如果觉得文章还不错,请点点赞,加关注,谢谢!

  《Java工程师成神之路》下载地址为:
  https://developer.aliyun.com/ebook/395?spm=a2c6h.20345107.ebook-index.24.4c927863j10ats

Java基础2-3:继承与实现、多继承、组合与继承_第1张图片 

二、第2章 面向对象的核心概念【继承与实现、多继承、组合与继承】

1、继承与实现

(1)继承(Inheritance):如果多个类的某个部分的功能相同,那么可以抽象出一个类,把它们的相同部分都放到父类里,让它们都继承这个类。

(2)实现(Implement):如果多个类处理的目标是一样的,但是处理的方法、方式不同,那么就定义一个接口,也就是一个标准,让它们都实现这个接口,各自实现自己具体的处理方法。
 

  继承指的是一个类(称为子类、子接口)继承另外一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力。所以,继承的根本原因是因为要复用,而实现的根本原因是需要定义一个标准。
 
  在 Java 中,类的继承使用 extends 关键字,而接口的实现使用 implements 关键字。

同样是一台汽车,既可以是电动车,也可以是汽油车,还可以是油电混合汽车,只要遵守不同的标准就行,但是一台车只能属于一个品牌、一个厂商。示例代码:
 
class Car extends Benz implements GasolineCar, ElectroCar { }

  代码中定义了一辆汽车,它实现了电动车和汽油车两个标准,但是它属于奔驰这个品牌,我们可以最大限度地遵守标准,并且复用奔驰车所有已有的一些功能组件。
 
  在接口中只能定义全局常量(static final)和无实现的方法(Java 8 以后可以有defult 方法);而在继承中可以定义属性方法、变量和常量等。
 

2、多继承

(1)多继承简介
 
  对于一个类只有一个父类的情况,叫单继承。而一个类同时有多个父类的情况叫多继承。
 
  在Java中,一个类只能通过**extends**关键字继承另一个类,不允许多继承。但其它面向对象语言中,如C++是支持多继承的。

(2)菱形继承问题
 
  假设类B和类C都继承了相同的A,类D通过多重继承机制继承了类B和类C,如图:
Java基础2-3:继承与实现、多继承、组合与继承_第2张图片 
  因为类D同时继承了类B和类C,并且类B和类C又同时继承了类A,那么,类D就会因为多重继承而继承了两份来自类A中的属性和方法。
 
  在使用类D时,如果想调用一个定义在类A中的方法时,就会出现歧义。
 
  C++为了解决菱形继承问题,引入了虚继承。
 
  因为支持多继承,所以引入了菱形继承问题,又因为要解决菱形继承问题,所以又引入了虚继承。经过分析,实际想要使用多继承的情况并不多。
 
  在Java中,不允许“实现多继承”,即一个类不允许继承多个父类。但是允许“声明多继承”,即一个类可以实现多个接口,一个接口也可以继承多个父接口。Java8以前,接口只允许方法声明而不允许有方法实现
 
  但在Java8中支持了默认函数(Default Method)之后,“Java不支持多继承”。就不是那么绝对了。
 
  虽然无法使用extends同时继承多个类,但是因为有了默认函数,就可能通过implements从多个接口中继承多个默认函数。
 

3、组合与继承

(1)继承复用
 
  继承是类与类或者接口与接口之间最常见的一种关系。继承是一种is-a的关系。如图:

is-a:表示“是一个”的关系。

Java基础2-3:继承与实现、多继承、组合与继承_第3张图片
(2)组合
 
  组合(Composition)体现的是整体与部分之间拥有的关系,即has-a的关系,如图:

has-a:表示“有一个”的关系。

Java基础2-3:继承与实现、多继承、组合与继承_第4张图片
 
(3)组合与继承的区别和联系
 
  首先,在**类的关系确定的时间点**上,组合和继承是有区别的:

  • 继承:因为在写代码的时候就要指名具体继承哪个类,所以在编译期就确定了类的关系。并且从基类继承的实现是无法在运行期动态改变的,因此降低了应用的灵活性。
  • 组合:在写代码时可以采用面向接口编程,所以类的组合关系一般在运行期确定。

 
  另外,在**代码复用方式**上也有区别:

  • 在继承结构中,父类的内部细节对于子类是可见的。所以通常说通过继承的代码复用是一种白盒式代码复用。如果基类的实现发生改变,那么派生类的实现也将随之改变。这样就导致了子类行为的不可预知性。
  • 组合是通过对现有的对象进行拼装(组合)产生新的、更复杂的功能。因为在对象之间,各自的内部细节是不可见的,所以也说这种方式的代码复用是黑盒式代码复用。因为在组合中一般都定义一个类型,所以在编译期根本不知道具体会调用哪个实现类的方法。

 
  最后,Java中不支持多继承,而组合是没有限制的。
 
(4)优缺点对比
 

组合关系 继承关系
优点:不破坏封装,整体类与局部类之间松耦合,彼此相对独立 缺点:破坏封装,子类与父类之间紧密耦合,子类依赖于父类的实现,子类缺乏独立性
优点:具有较好的可扩展性 缺点:支持扩展,但是往往以增加系统结构的复杂度为代价
优点:支持动态组合。在运行时,整体对象可以选择不同类型的局部对象 缺点:不支持动态继承。在运行时,子类无法选择不同的父类
优点:整体类可以对局部类进行包装,封装局部类的接口,提供新的接口 缺点:子类不能改变父类的接口
缺点:整体类不能自动获得和局部类同样的接口 优点:子类能自动继承父类的接口
缺点:创建整体类的对象时,需要创建所有局部类的对象 优点:创建子类的对象时,无须创建父类的对象

 
(5)如何选择
 
  相信很多人都知道面向对象中有一个比较重要的原则“多用组合、少用继承”,或者说“组合优于继承”。在《阿里巴巴Java开发手册》中有一条规定:谨慎使用继承的方式进行扩展,优先使用组合的方式实现
 
  所以,建议在同样可行的情况下,优先使用组合而不是继承。因为组合更安全、更简单、更灵活、更高效
 
  注意,并不是说继承就一点用都没有了,前面说的是“在同样可行的情况下”。有一些场景还是需要使用继承的,或者是更适合使用继承。

  继承要慎用,其使用场合仅限于你确信使用该技术有效的情况。一个判断方法是,问一问自己是否需要从新类向基类进行向上转型。如果是必须的,则继承是必要的。反之则应该好好考虑是否需要继承。——《Java编程思想》
 
  只有当子类真正是超类的子类型时,才适合用继承。换句话说,对于两个类 A 和 B,只有当两者之间确实存在 is-a 关系的时候,类 B 才应该继承类 A。——《Effective Java》

你可能感兴趣的:(Java基础,java,开发语言)