抽象类
通常可以说四边形具有 4 条边,或者更具体一点,平行四边形是具有对边平行且相等特性的特殊四边形,等腰三角形是其中两条边相等的三角形,这些描述都是合乎情理的。但仅对于“图形”这个词却不能使用具体的语言进行描述,它有几条边?它有几个角?它有多大?没有人能说清楚。同理,仅用来描述特征且极具抽象性类,在 Java 中被定义为抽象类。
在解决实际问题时,一般将父类定义为抽象类,需要使用这个父类进行继承与多态处理。回想继承和多态原理,继承树中越是在上方的类越抽象,如鸽子类继承鸟类、鸟类继承动物类等。在多态机制中,并不需要将父类初始化为对象,我们需要的只是子类对象,因此在 Java 语言中设置抽象类不可以被实例化为对象。
使用 abstract 关键字定义的类被称为抽象类,而使用这个关键字定义的方法被称为抽象方法。抽象方法没有方法体,这个方法本身没有任何意义,除非它被重写,而承载这个抽象方法的抽象类必须被继承,实际上抽象类除了被继承没有任何意义。定义抽象类的语法如下:
public abstract class Parent {
abstract void testAbstract(); // 定义抽象方法
}
反过来讲,如果声明一个抽象方法,就必须将承载这个抽象方法的类定义为抽象类,不能在非抽象类中获取抽象方法。换句话说,只要类中有一个抽象方法,此类就被标记为抽象类。
抽象类被继承后需要实现其中所有的抽象方法,也就是保证以相同的方法名称、参数列表和返回值类型创建出非抽象方法,当然也可以是抽象方法。图7.10说明了抽象类的继承关系。
从图7.10中可以看出,继承抽象类的所有子类需要将抽象类中的抽象方法进行覆盖。这样在多态机制中,就可以将父类修改为抽象类,将 draw() 方法设置为抽象方法,然后每个子类都重写这个方法来处理。但这又会出现我们刚探讨多态时讨论的问题,程序中会有太多冗余的代码,同时这样的父类局限性很大,也许某个不需要 draw() 方法的子类也不得不重写 draw() 方法。如果将 draw() 方法放置在另一个类中,让那些需要 draw() 方法的类继承该类,不需要 draw() 方法的类继承图形类,又会产生新的问题:所有的子类都需要继承图形类,因为这些类是从图形类中导出的,同时某些类还需要 draw() 方法,而 Java 中规定类不能同时继承多个父类。为了应对这种问题,接口的概念便出现了。
