JAVA的继承、接口与抽象

继承是java面向对象的基石,因为它允许创建分等级层次的类。继承就是使得子类可以继承父类的特征和行为(就是一些变量和方法)。

为什么需要继承,这就是我们要讨论的重点问题了,还记得之前讨论过,面向对象相比面向过程具有的一些优点,其中最大的一个就是简化了代码的重复,最简单的写好了一个方法就能重复被调用,不需要每次用都写一次,而继承就是进一步发挥这个优点,如果父类有的变量和方法,其他类也需要,那么就可以认为它是子类,然后继承父类,直接就可以用父类的方法,不需要重新写一次。这个特点有点像css和html的关系,在css写好了对元素的修饰,html就能重复调用,这样既可以保证整个页面风格的统一,也减少的代码量,最重要的是,如果想修改,直接修改css就能完成对所有调用元素的修改了(也就是修改父类的变量方法),也可以针对局部元素进行修改(对子类override),使得对代码的维护更加高效。

继承有两种关键字,extends(只能继承一个父类)和implements(可继承多个接口)。

提到这个implements,就先提一下什么是接口吧,接口(interface)是一个抽象类型,是抽象方法的集合,它不是类,虽然编写的方法很相似,但是两者属于不同的概念。首先类是描述对象的属性和方法,而接口则包含类要实现的方法。接口是无法实例化,但可以通过被类继承(其实应该称为实现)而实现。需要注意的是,一旦一个类继承了这个接口,就必须实现其中所有的方法(意思就是,原来的接口只是描述了方法的特征,比如方法名字、参数数量和种类,没有写出具体是怎么实现的,而实现接口之后,就必须补充这些方法是怎么实现的)。问题来了,既然原来接口里面的方法都没有写清楚是怎么实现的,为什么还需要接口,为什么不直接在类里面写好方法?

事实上,接口是一种规范,就是告诉其他人,假如这个类implements了这个接口,就会具有一些方法,比如飞机和鸟,都可以implements fly这个接口,但是飞机和鸟飞行的方式肯定不一样嘛,所以就需要具体描述它们各自是怎么飞的。

在接口里也提到抽象这个概念,可以分为抽象类和抽象方法。抽象类就是那些不具有足够信息去描述一个类(实例化)的一种类,主要是用来被继承的。最常见的就是那些父类,比如animal,我们不能实现一个animal,因为实例化了也不知道是什么,但是我们可以去继承他,比如老虎可以继承animal,继承它的吃喝拉撒这些方法。抽象方法就是只有方法体,没有方法体(具体怎么实现的),如果一个类包含了抽象方法,那么它就一定是抽象类。比如吃这个方法,我定义了,很多子类都可以继承,但是我没有办法在这个方法中统一描述那些子类都是怎么吃的,可能有人用左手吃有人用右手之类的,那就可以先定义一个抽象方法,等子类继承了再补充具体是怎么实现的,所以如果子类继承了一个抽象类,就必须实现那些抽象方法,或者再次声明那些是抽象方法(这时候这个子类也是抽象类,因为包含抽象方法)。最后还有一个注意的地方,抽象类可能不包含抽象方法,包含抽象方法的一定是抽象类。

终于说完了接口和抽象,再回到一开始的继承,其实关于继承父类还是实现接口的区别基本上也说清楚了,感觉implements接口不能完全说是继承,更应该是接口的实现,实现接口就是一种规范,说明这个类具有这些方法。而extends父类则是真正的继承,可以拿父类的方法和变量来用,不适合的话也可以修改父类的方法或者变量(通过super访问父类的变量)。还有一点就是,虽然java只支持单继承,但是我们可以通过a继承b,b继承c这样实现多重继承,从结果上来说也没有区别。

最后再来谈一谈如果继承了父类,想修改一些方法,应该怎么做,主要有override和overload。override是子类对父类的方法的实现过程进行重新编写,返回值和形参都不能变,也就是外壳不变,核心重写。至于overload就是参数都可以修改,只保留方法名是相同的,注意的是,不论是重写还是重载,只要根据实际需求修改方法就好,不需要override这些关键字的。