
8.1 面向对象继承性
在面向对象的设计过程中,类是基本的逻辑单位。但是对于这些基本的逻辑单位需要考虑到重用的设计问题,所以在面向对象的设计里提供有继承,并利用这一特点实现类的可重用性定义。
8.1.1 继承问题的引出

一个良好的程序设计结构不仅便于维护,同时还可以提高程序代码的可重用性。在之前所讲解的面向对象的知识中,只是围绕着单一的类进行的,而这样的类之间没有重用性的描述。例如,从下面定义Person类与Student类就可以发现无重用性代码设计的缺陷。


通过以上两段代码的比较,相信读者可以清楚地发现,如果按照之前所学习到的概念进行开发的话,那么程序中就会出现重复代码。而通过分析可以发现,学生本来就属于人,但是学生所表示的范围要比人表示的范围更小,也更加的具体。而如果想要解决代码问题,就只能依靠继承来完成。
8.1.2 类继承定义

严格来讲,继承性是指扩充一个类已有的功能。在Java中,如果要实现继承的关系,可以使用以下的语法完成。
class 子类 extends 父类 {}
在继承结构中,很多情况下会把子类称为派生类,把父类称为超类(SuperClass)。
范例:继承基本实现

本程序在定义Student类时并没有定义任何方法,只是让其继承Person父类,而通过执行可以发现,子类可以继续重用父类中定义的属性与方法。
继承实现的主要目的是子类可以重用父类中的结构,同时可以根据子类功能的需要进行结构扩充,所以子类往往要比父类描述的范围更小。
范例:在子类中扩充父类的功能

本程序Student类在已有的Person类的基础上扩充了新的属性与方法,相比较Person类而言,Student类的描述范围更加具体。
8.1.3 子类对象实例化流程

在继承结构中,子类需要重用父类中的结构,所以在进行子类对象实例化之前往往都会默认调用父类中的无参构造方法,为父类对象实例化(属性初始化),而后再进行子类构造调用,为子类对象实例化(属性初始化)。
范例:子类对象实例化,观察无参构造调用


本程序在实例化Student子类对象时只调用了子类构造,而通过执行结果可以发现,父类构造会被默认调用,执行完毕后才调用了子类构造,所以可以得出结论:子类对象实例化前一定会实例化父类对象,实际上这个时候就相当于子类的构造方法里面隐含了一个super()的形式。
范例:观察子类构造

子类中的super()的作用表示在子类中明确调用父类的无参构造,如果不写也默认会调用父类构造,对于super()构造调用的语句只能够在子类的构造方法中定义,并且必须放在子类构造方法的首行。
如果父类没有提供无参构造方法时,就可以通过“super(参数,…)”的形式调用指定参数的构造方法。
范例:明确调用父类指定构造方法


本程序Person父类不再明确提供无参构造方法,这样在子类构造方法中就必须通过super()明确指明要调用的父类构造,并且该语句必须放在子类构造方法的首行。
提问:有没有不让子类去调用父类构造的可能性?
既然super()和this()都是调用构造方法,而且都要放在构造方法的首行。如果说this()出现了,那么super()应该就不会出现了,所以编写了以下的程序。
范例:疑问的程序

在本程序中,子类B的每一个构造方法,都使用了this()调用本类构造方法,那么这样是不是就表示子类无法调用父类构造呢?
回答:本程序编译有错误。
在之前讲解this关键字的时候强调过一句话:如果一个类中有多个构造方法之间使用this()相互调用的话,那么至少要保留一个构造方法作为出口,而这个出口就一定会去调用父类构造。
或者换一种表达方式:“我们每个人都有父母,父母一定都比我们先出生。在程序中,实例化就表示对象的出生,所以子类出生之前(实例化之前),父类对象一定要先出生(默认调用父类构造,实例化父类对象)。”
8.1.4 继承限制

继承是类重用的一种实现手段,而在Java中针对类继承的合理性设置了相关限制。
限制1:一个子类只能继承一个父类,存在单继承局限。
这个概念实际上是相对于其他语言而言,在其他语言中,一个子类可以同时继承多个父类,这样就可以同时获取多个父类中的方法,但是在Java中是不允许的,以下为错误的继承代码。

以上操作称为多重继承,实际上以上的做法就是希望一个子类,可以同时继承多个父类的功能,在Java中并不支持此类语法,但是可以换种方式完成同样的操作。
范例:正确的程序

C实际上是属于(孙)子类,这样一来就相当于B类继承了A类的全部方法,而C类又继承了A类和B类的方法,这种操作称为多层继承。结论:Java之中只允许多层继承,不允许多重继承,Java存在单继承局限。
注意:继承层次不要过多。
类继承虽然可以实现代码的重用,但是如果在编写项目中类的继承结构过多,也会造成代码阅读的困难。对于大部分的程序编写,不建议继承结构超过3层。
限制2:在一个子类继承的时候,实际上会继承父类的所有操作(属性、方法),但是需要注意的是,对于所有的非私有(no private)操作属于显式继承(可以直接利用对象操作),而所有的私有(private)操作属于隐式继承(间接完成)。
范例:不允许直接访问非私有操作


本程序中Person父类定义的name属性虽然可以被子类使用,但是由于存在private定义,所以在子类中是无法直接进行私有属性访问的,只能通过getter()方法间接访问,所以该属性属于隐式继承。