从学习Java的第一天开始,我们就知道Java是一门面向对象的语言,到学习Java的第二天,我们就知道了面向对象的三个基本特性:封装、继承、多态。因此,对于很多开发者来说,继承当然并不陌生。然而,继承是不是适用于所有场景呢?毫无忌讳地使用继承来进行代码扩展真的好吗?为什么《阿里巴巴Java开发手册》里面有一条规则:谨慎使用继承进行扩展,优先使用组合。本文旨在对这些问题进行简要分析。一、面向对象的复用技术大家在初学继承的时候或多或少都会有这样一个印象:继承可以帮我实现类的复用。所以很多开发者在需要复用一些代码的时候自然会使用类的继承方式,因为书上是这么写的(老师就是这么教的)。然而,这样做是错误的。长期大量使用继承会给代码带来高昂的维护成本。前面提到了复用,这里简单介绍一下面向对象的复用技术。可重用性是面向对象技术的巨大潜在好处之一。如果用得好,可以帮助我们节省大量的开发时间,提高开发效率。但是,如果被滥用,它会产生大量不可维护的代码。作为一种面向对象的开发语言,代码重用是Java吸引人的特性之一。Java代码的重用有继承、组合和代理三种具体表现形式。2.继承继承(Inheritance)是连接类与类的层次模型。是指一个类(称为子类,子接口)继承另一个类(称为父类,父接口)的功能,并增加自己的新功能的能力。继承是类与类或接口与接口之间最常见的关系。继承是一种is-a关系。比如苹果是水果,狗是动物,哈士奇是狗。3.组合组合体现了整体与部分的关系、所有权。组合是一种has-a关系。比如汽车有发动机,学校有老师等。4.组合与继承的区别首先,组合与继承在确定类间关系的时间点上是有区别的:继承,写代码的时候需要指定继承哪个类,所以类的继承关系是编译期就确定的。并且从基类继承的实现在运行时不能动态改变,从而降低了应用程序的灵活性。组合,在写代码的时候可以使用面向接口的编程。因此,类的组成关系一般是在运行时确定的。另外,代码复用的方式也有一定的区别:在继承结构中,父类的内部细节对子类是可见的。所以我们通常可以说通过继承进行代码复用是一种白盒代码复用。如果基类的实现发生变化,派生类的实现也会发生变化。这导致子类行为的不可预测性。组合是通过组装(组合)已有的对象来产生新的更复杂的功能。因为对象之间,各自的内部细节是不可见的,所以我们也说通过组合进行代码复用是黑盒代码复用。因为组合中一般都定义了一个类型,所以编译时并不知道会调用实现类的哪个方法。最后,Java不支持多重继承,组合是无限的。就像一个人只能有一个爸爸,却可以有很多辆车。5、优缺点比较6、为什么组合比继承好相信很多人都知道面向对象中有一个更重要的原则“多用组合,少用继承”或者说“组合比继承好”。从前面介绍的优缺点对比也可以看出,组合比继承更灵活,也更利于代码维护。它的优点是不破坏封装,可扩展性更好,支持动态组合,整体类可以改变局部类的行为。因此,建议在同样可行的情况下使用组合而不是继承。因为组合更安全、更简单、更灵活、更高效。注意,并不是说继承一点用都没有,我上面说的是【同等可行条件下】。有一些场景还是需要用到继承,或者用继承更合适。另外,除了《阿里巴巴Java开发手册》,在其他很多资料中也有关于组合和继承的介绍和使用限制:继承要慎用,它的使用仅限于你确信该技术有效的情况。一种判断方法是问问自己是否需要从新类向上转换到基类。如果有必要,继承是必要的。相反,你应该仔细考虑你是否需要继承。《Java编程思想》只有当子类确实是超类的子类型时,继承才是合适的。换句话说,对于两个类A和B,只有当两者之间确实存在is-a关系时,B类才应该继续类A。《Effective Java》
