抽象方法和类

原文: https://docs.oracle.com/javase/tutorial/java/IandI/abstract.html

抽象类是一个声明为abstract的类 - 它可能包含也可能不包含抽象方法。抽象类无法实例化,但可以进行子类化。

抽象方法是一个声明没有实现的方法(没有大括号,后跟分号),如下所示:

  1. abstract void moveTo(double deltaX, double deltaY);

如果一个类包含抽象方法,则类本身必须声明为abstract,如:

  1. public abstract class GraphicObject {
  2. // declare fields
  3. // declare nonabstract methods
  4. abstract void draw();
  5. }

当抽象类被子类化时,子类通常为其父类中的所有抽象方法提供实现。但是,如果没有,那么子类也必须声明为abstract


Note: Methods in an interface (see the Interfaces section) that are not declared as default or static are implicitly abstract, so the abstract modifier is not used with interface methods. (It can be used, but it is unnecessary.)


抽象类与接口类似。您无法实例化它们,并且它们可能包含使用或不使用实现声明的混合方法。但是,对于抽象类,您可以声明非静态和最终的字段,并定义 public,protected 和 private 具体方法。使用接口,所有字段都自动为 public,static 和 final,并且您声明或定义的所有方法(作为默认方法)都是公共的。此外,您只能扩展一个类,无论它是否是抽象的,而您可以实现任意数量的接口。

你应该使用哪些,抽象类或接口?

  • 如果任何这些语句适用于您的情况,请考虑使用抽象类:
    • 您希望在几个密切相关的类之间共享代码。
    • 您希望扩展抽象类的类具有许多常用方法或字段,或者需要除公共之外的访问修饰符(例如 protected 和 private)。
    • 您想声明非静态或非最终字段。这使您可以定义可以访问和修改它们所属对象的状态的方法。
  • 如果任何这些语句适用于您的情况,请考虑使用接口:
    • 您希望不相关的类可以实现您的接口。例如,接口 ComparableCloneable 由许多不相关的类实现。
    • 您希望指定特定数据类型的行为,但不关心谁实现其行为。
    • 您希望利用类型的多重继承。

JDK 中的抽象类的一个示例是 AbstractMap ,它是 Collections Framework 的一部分。其子类(包括HashMapTreeMapConcurrentHashMap)共享AbstractMap定义的许多方法(包括getputisEmptycontainsKeycontainsValue)。

JDK 中实现多个接口的类的示例是 HashMap ,它实现接口SerializableCloneableMap<K, V>。通过读取此接口列表,您可以推断出HashMap的实例(无论是实现该类的开发人员或公司)是否可以克隆,是可序列化的(这意味着它可以转换为字节流;请参阅部分可序列化对象),并具有地图的功能。此外,Map<K, V>接口已经增强了许多默认方法,例如mergeforEach,实现此接口的旧类不必定义。

请注意,许多软件库都使用抽象类和接口; HashMap类实现了几个接口,并且还扩展了抽象类AbstractMap

抽象类示例

在面向对象的绘图应用程序中,您可以绘制圆形,矩形,线条,贝塞尔曲线和许多其他图形对象。这些对象都具有某些状态(例如:位置,方向,线条颜色,填充颜色)和行为(例如:moveTo,rotate,resize,draw)。对于所有图形对象,这些状态和行为中的一些是相同的(例如:位置,填充颜色和 moveTo)。其他需要不同的实现(例如,调整大小或绘制)。所有GraphicObject必须能够自己绘制或调整大小;他们只是在做不同的事情上有所不同。这是抽象超类的完美情况。您可以利用相似性并声明所有图形对象继承自相同的抽象父对象(例如,GraphicObject),如下图所示。

Classes Rectangle, Line, Bezier, and Circle Inherit from GraphicObject

类 Rectangle,Line,Bezier 和 Circle 从 GraphicObject 继承

首先,声明一个抽象类GraphicObject,以提供由所有子类完全共享的成员变量和方法,例如当前位置和moveTo方法。 GraphicObject还声明方法的抽象方法,例如drawresize,它们需要由所有子类实现,但必须以不同的方式实现。 GraphicObject类看起来像这样:

  1. abstract class GraphicObject {
  2. int x, y;
  3. ...
  4. void moveTo(int newX, int newY) {
  5. ...
  6. }
  7. abstract void draw();
  8. abstract void resize();
  9. }

GraphicObject的每个非抽象子类,如CircleRectangle,必须提供drawresize方法的实现:

  1. class Circle extends GraphicObject {
  2. void draw() {
  3. ...
  4. }
  5. void resize() {
  6. ...
  7. }
  8. }
  9. class Rectangle extends GraphicObject {
  10. void draw() {
  11. ...
  12. }
  13. void resize() {
  14. ...
  15. }
  16. }

当抽象类实现接口时

Interfaces 的部分中,注意到实现接口的类必须实现接口方法的所有。但是,如果该类声明为abstract,则可以定义一个不实现所有接口方法的类。例如,

  1. abstract class X implements Y {
  2. // implements all but one method of Y
  3. }
  4. class XX extends X {
  5. // implements the remaining method in Y
  6. }

在这种情况下,类X必须是abstract,因为它没有完全实现Y,但类XX实际上实现了Y

类成员

抽象类可以具有static字段和static方法。您可以像使用任何其他类一样使用带有类引用的静态成员(例如,AbstractClass.staticMethod())。