[关闭]
@wjcper2008 2017-05-16T05:26:29.000000Z 字数 4590 阅读 2027

面向对象部分3: 抽象与接口

Java


参考自《Java语言程序设计 基础篇 10版》, 仅供内部教学讲义使用, 由Teacher Chen整理.

1. 概述

知识点:
父类中定义了相关子类中的共同行为. 接口可以用于定义类的共同行为 (包括非相关的类)

例如使用 java.util.Arrays.sort() 方法来对数值和字符串进行排序. 那么可以应用同样的 sort 方法对一个几何对象的数组进行排序吗? 为了编写这样的代码, 必须要了解接口. 接口是为了定义多个类的共同行为. 在讨论接口之前, 我们介绍一个非常接近的相关主題: 抽象类.

2. 抽象类

抽象类不可以用于创建对象. 抽象类可以包含抽象方法, 这些方法将在具体的子类中实现.

在继承的层次结构中, 每次的继承:

抽象类(abstract class): 类的设计应该确保父类包含它的子类的共同特征. 有时候, 一个父类设计得非常抽象, 以至于它都没有任何具体的实例. 这样的类称为抽象类(abstract class).

例如, GeometricObject 类定义成 Circle 类和 Rectangle 类的父类. GeometricObject 类模拟了几何对象的共同特征. Circle 类和Rectangle 类分别包含计算圆和矩形的面积和周长的方法 getArea() 和 getPerimeter(). 因为可以计算所有几何对象的面积和周长, 所以最好在 GeometricObject 类中定义 getArea() 和 getPerimeter() 方法. 但是, 这些方法不能在 GeometricObject 类中实现, 因为它们的实现取决于几何对象的具体类型.

这样的方法称为抽象方法 (abstract method), 在方法签名中使用 abstract 修饰符表示. 在GeometricObject 类中定义了这些抽象方法后, GeometricObject 就成为一个抽象类. 在类定义时, 使用 abstract 修饰符表示该类为抽象类. 在 UML 图形记号中, 抽象类和抽象方法的名字用斜体表示.

image_1bg7bhl85163q1pkelb3i355qi9.png-274.9kB

代码清单: GeometricObject.java

  1. public abstract class GeometricObject {
  2. private String color = "white";
  3. private boolean filled;
  4. private java.util.Date dateCreated;
  5. /** Construct a default geometric object */
  6. protected GeometricObject() {
  7. dateCreated = new java.util.Date();
  8. }
  9. /** Construct a geometric object with color and filled value */
  10. protected GeometricObject(String color, boolean filled) {
  11. dateCreated = new java.util.Date();
  12. this.color = color;
  13. this.filled = filled;
  14. }
  15. /** Return color */
  16. public String getColor() {
  17. return color;
  18. }
  19. /** Set a new color */
  20. public void setColor(String color) {
  21. this.color = color;
  22. }
  23. /** Return filled. Since filled is boolean,
  24. * the get method is named isFilled */
  25. public boolean isFilled() {
  26. return filled;
  27. }
  28. /** Set a new filled */
  29. public void setFilled(boolean filled) {
  30. this.filled = filled;
  31. }
  32. /** Get dateCreated */
  33. public java.util.Date getDateCreated() {
  34. return dateCreated;
  35. }
  36. /** Return a string representation of this object */
  37. public String toString() {
  38. return "created on " + dateCreated + "\ncolor: " + color +
  39. " and filled: " + filled;
  40. }
  41. /** Abstract method getArea */
  42. public abstract double getArea();
  43. /** Abstract method getPerimeter */
  44. public abstract double getPerimeter();
  45. }

抽象类和常规类很像, 但是不能使用 new 操作符创建它的实例. 抽象方法只有定义而没有实现. 它的实现由子类提供.

一个包含抽象方法的类必须声明为抽象类. 抽象类的构造方法定义为 Protected, 因为它只被子类使用.

创建一个具体子类的实例时, 它的父类的构造方法被调用以初始化父类中定义的数据域. 抽象类 GeometricObject 为几何对象定义了共同特征 (数据和方法), 并且提供了合适的构造方法. 因为不知道如何计算几何对象的面积和周长, 所以, getArea() 和getPerimeter() 定义为抽象方法. 这些方法在子类中实现.

2.1 为何要使用抽象方法

你可能会疑惑在 GeometricObject 类中定义方法 getArea() 和 getPerimeter() 为抽象的而不是在每个子类中定义它们会有什么好处.

下面程序清单的例子就能看出在 GeometricObject中定义它们的好处. 程序创建了两个几何对象: 一个圆和一个矩形, 调用 equalArea() 方法来检査它们的面积是否相同, 然后调用 displayGeometricObject 方法来显示它们.

代码清单: TestGeometricObject.java

  1. public class TestGeometricObject {
  2. /** Main method */
  3. public static void main(String[] args) {
  4. // Declare and initialize two geometric objects
  5. GeometricObject geoObject1 = new Circle(5);
  6. GeometricObject geoObject2 = new Rectangle(5, 3);
  7. System.out.println("The two objects have the same area? " + equalArea(geoObject1, geoObject2));
  8. // Display circle
  9. displayGeometricObject(geoObject1);
  10. // Display rectangle
  11. displayGeometricObject(geoObject2);
  12. }
  13. /** A method for comparing the areas of two geometric objects */
  14. public static boolean equalArea(GeometricObject object1, GeometricObject object2) {
  15. return object1.getArea() == object2.getArea();
  16. }
  17. /** A method for displaying a geometric object */
  18. public static void displayGeometricObject(GeometricObject object) {
  19. System.out.println();
  20. System.out.println("The area is " + object.getArea());
  21. System.out.println("The perimeter is " + object.getPerimeter());
  22. }
  23. }

image_1bg7j0rsp1g2sagg19ub1r9q1u8k9.png-41.2kB

Circle 类和 Rectangle 类中覆盖了定义在 GeometricObject 类中的 getArea() 和 getPerimeter() 方法.

  1. GeometricObject geoObject1 = new Circle(5);
  2. GeometricObject geoObject2 = new Rectangle(5, 3);

创建了一个新圆和一个新矩形, 并把它们赋值给变量 geoObject1 和 geoObject2. 这两个变量都是 GeometricObject类型的. 当调用 equalArea(geoObject1, geoObject2)时, 由于 geoObjectl 是一个圆 ,
所以 objectl. getArea() 使用的是 Circle 类定义的 getArea() 方法, 而 geoObject2 是一个矩形, 所以 object2. getArea() 使用的是 Rectangle 类的 getArea() 方法.

类似地, 当调用 displayGeometricObject(geoObjectl) 时, 使用在 Circle类中定义的 getArea() 和 getPeHmeter() 方法, displayGeometricObject(geoObject2)时, 使用的是在 Rectangle 类中定义的 getArea() 和 getPerimeter() 方法. JVM在运行时根据对象的类型动态地决定调用哪一个方法.

注意, 如果 GeometricObject 里没有定义 getArea() 方法, 就不能在该程序中定义 equalArea() 方法来计算这两个几何对象的面积是否相同. 所以, 现在可以看出在GeometricObject 中定义抽象方法的好处.

2.2 抽象类的几点说明

下面是关于抽象类值得注意的几点:

  1. GeometricObject[] objects = new GeometricObject[10];

然后可以创建一个 CeometricObject 的实例, 并将它的引用陚值给数组, 如下所示:

  1. objects[0] = new Circle();
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注