这篇Java教程基于JDK1.8。教程中的示例和实践不会使用未来发行版中的优化建议。
5.2.8、抽象方法和抽象类
抽象类是指 通过 abstract 描述的类 — 它可以有或者没有抽象方法。抽象类不能实例化,但可以子类化。
抽象方法是指只有声明没有实现的方法(指没有括号,分号结尾):
abstract void moveTo(double deltaX, double deltaY);
如果类包含抽象方法,该类必须用 abstract 予以修饰:
public abstract class GraphicObject {
// declare fields
// declare nonabstract methods
abstract void draw();
}
当扩展抽象类时,子类通常要实现抽象类的所有抽象方法,如若不然,该子类也要声明为abstract的。
抽象类和接口比较
抽象类和接口很像,不能实例化,可以同时有方法声明和方法实现。但在抽象类中,可以声明非静态和非 final 的字段,也可以定义 public、protected、private 概念的方法。在接口中,所有的字段都是public static 和 final 的,所有声明和定义的方法都是 public 的。而且,你只能扩展一个类(不管它是否是抽象类),但可以实现任意数量的接口。
那么你应该如何选择 抽象类 或 接口呢?
-
以下场景使用抽象类
- 要在相关联的类之间共享代码
- 扩展抽象类有许多公共的方法和字段,或者需要修改访问控制符
- 要声明 non-static 和 non-final 的字段。这使你定义能够访问和修改它们所属对象的状态的方法
-
以下场景使用接口
- 不相关的类要实现接口
- 指定一种特殊数据类型的行为,但不确定谁来实现它的行为
- 发挥类型多继承的优势
抽象类的一个例子是 JDK 的 * AbstractMap* ,集合框架的一部分。它的子类共享了 * AbstractMap* 定义的许多方法。
JDK 中实现多个接口的一个例子是 HashMap 。依次实现了接口 Serializable、Cloneable 和 Map<K, V>,通过阅读接口列表,可以推断 HashMap 的实例可以克隆、序列化,并且拥有 map 的能力。 Map<K, V> 接口通过许多默认方法(merge 、forEach)予以增强,实现该接口的那些旧类不需要重新定义这些方法。
注意许多软件包会同时使用抽象类和接口,比如 HashMap 类实现了许多方法同时也扩展了 AbstractMap 类。
抽象类示例
在面向对象绘图应用中,可以绘制圆形、方形、线、曲线以及其他图形对象。这些对象都会有状态(位置、取向、颜色、填充色)和 行为(比如:moveTo, rotate, resize, draw等等)。其中有些状态和行为对所有图形都是一样的(比如:position, fill color, and moveTo)。另外一些则需要不同的实现(比如:resize or draw)。所有的 GraphicObjects 都必须能 draw 和 resize ,不同的如何实现。这是抽象类的一个完美场景。可以发挥图形相似性的优势,让他们继承同一个抽象父类(比如:GraphicObject)如下图所示:
首先,声明抽象类 GraphicObject ,提供所有子类可以共享的成员变量和方法,比如position 和 moveTo方法。GraphicObject 也会声明抽象方法,比如draw 和 resize ,必须被子类实现。GraphicObject 类看起来是下面的样子:
abstract class GraphicObject {
int x, y;
...
void moveTo(int newX, int newY) {
...
}
abstract void draw();
abstract void resize();
}
GraphicObject 的每一个子类,比如 Circle 和 Rectangle ,必须提供 draw 和 resize 的实现:
class Circle extends GraphicObject {
void draw() {
...
}
void resize() {
...
}
}
class Rectangle extends GraphicObject {
void draw() {
...
}
void resize() {
...
}
}
抽象类实现接口
在接口那一节,注意到类要实现接口就必须实现接口的所有方法。但是只要实现接口的类 声明为 abstract 的,它就不需要实现接口的所有方法。比如:
abstract class X implements Y {
// implements all but one method of Y
}
class XX extends X {
// implements the remaining method in Y
}
本例中,类 X 必须声明为 abstract 的,因为它没有完全实现接口 Y ;但类 XX 就必须完全实现接口 Y 的方法。
类成员
抽象类可以有 static 的变量和方法。可以使用类引用来访问静态成员(比如:AbstractClass.staticMethod()),就如同在其他类中所使用的那样。