@wjcper2008
2017-05-16T05:26:29.000000Z
字数 4590
阅读 2027
Java
参考自《Java语言程序设计 基础篇 10版》, 仅供内部教学讲义使用, 由Teacher Chen整理.
知识点:
父类中定义了相关子类中的共同行为. 接口可以用于定义类的共同行为 (包括非相关的类)
例如使用 java.util.Arrays.sort() 方法来对数值和字符串进行排序. 那么可以应用同样的 sort 方法对一个几何对象的数组进行排序吗? 为了编写这样的代码, 必须要了解接口. 接口是为了定义多个类的共同行为. 在讨论接口之前, 我们介绍一个非常接近的相关主題: 抽象类.
抽象类不可以用于创建对象. 抽象类可以包含抽象方法, 这些方法将在具体的子类中实现.
在继承的层次结构中, 每次的继承:
抽象类(abstract class): 类的设计应该确保父类包含它的子类的共同特征. 有时候, 一个父类设计得非常抽象, 以至于它都没有任何具体的实例. 这样的类称为抽象类(abstract class).
例如, GeometricObject 类定义成 Circle 类和 Rectangle 类的父类. GeometricObject 类模拟了几何对象的共同特征. Circle 类和Rectangle 类分别包含计算圆和矩形的面积和周长的方法 getArea() 和 getPerimeter(). 因为可以计算所有几何对象的面积和周长, 所以最好在 GeometricObject 类中定义 getArea() 和 getPerimeter() 方法. 但是, 这些方法不能在 GeometricObject 类中实现, 因为它们的实现取决于几何对象的具体类型.
这样的方法称为抽象方法 (abstract method), 在方法签名中使用 abstract 修饰符表示. 在GeometricObject 类中定义了这些抽象方法后, GeometricObject 就成为一个抽象类. 在类定义时, 使用 abstract 修饰符表示该类为抽象类. 在 UML 图形记号中, 抽象类和抽象方法的名字用斜体表示.
代码清单: GeometricObject.java
public abstract class GeometricObject {
private String color = "white";
private boolean filled;
private java.util.Date dateCreated;
/** Construct a default geometric object */
protected GeometricObject() {
dateCreated = new java.util.Date();
}
/** Construct a geometric object with color and filled value */
protected GeometricObject(String color, boolean filled) {
dateCreated = new java.util.Date();
this.color = color;
this.filled = filled;
}
/** Return color */
public String getColor() {
return color;
}
/** Set a new color */
public void setColor(String color) {
this.color = color;
}
/** Return filled. Since filled is boolean,
* the get method is named isFilled */
public boolean isFilled() {
return filled;
}
/** Set a new filled */
public void setFilled(boolean filled) {
this.filled = filled;
}
/** Get dateCreated */
public java.util.Date getDateCreated() {
return dateCreated;
}
/** Return a string representation of this object */
public String toString() {
return "created on " + dateCreated + "\ncolor: " + color +
" and filled: " + filled;
}
/** Abstract method getArea */
public abstract double getArea();
/** Abstract method getPerimeter */
public abstract double getPerimeter();
}
抽象类和常规类很像, 但是不能使用 new 操作符创建它的实例. 抽象方法只有定义而没有实现. 它的实现由子类提供.
一个包含抽象方法的类必须声明为抽象类. 抽象类的构造方法定义为 Protected, 因为它只被子类使用.
创建一个具体子类的实例时, 它的父类的构造方法被调用以初始化父类中定义的数据域. 抽象类 GeometricObject 为几何对象定义了共同特征 (数据和方法), 并且提供了合适的构造方法. 因为不知道如何计算几何对象的面积和周长, 所以, getArea() 和getPerimeter() 定义为抽象方法. 这些方法在子类中实现.
你可能会疑惑在 GeometricObject 类中定义方法 getArea() 和 getPerimeter() 为抽象的而不是在每个子类中定义它们会有什么好处.
下面程序清单的例子就能看出在 GeometricObject中定义它们的好处. 程序创建了两个几何对象: 一个圆和一个矩形, 调用 equalArea() 方法来检査它们的面积是否相同, 然后调用 displayGeometricObject 方法来显示它们.
代码清单: TestGeometricObject.java
public class TestGeometricObject {
/** Main method */
public static void main(String[] args) {
// Declare and initialize two geometric objects
GeometricObject geoObject1 = new Circle(5);
GeometricObject geoObject2 = new Rectangle(5, 3);
System.out.println("The two objects have the same area? " + equalArea(geoObject1, geoObject2));
// Display circle
displayGeometricObject(geoObject1);
// Display rectangle
displayGeometricObject(geoObject2);
}
/** A method for comparing the areas of two geometric objects */
public static boolean equalArea(GeometricObject object1, GeometricObject object2) {
return object1.getArea() == object2.getArea();
}
/** A method for displaying a geometric object */
public static void displayGeometricObject(GeometricObject object) {
System.out.println();
System.out.println("The area is " + object.getArea());
System.out.println("The perimeter is " + object.getPerimeter());
}
}
Circle 类和 Rectangle 类中覆盖了定义在 GeometricObject 类中的 getArea() 和 getPerimeter() 方法.
GeometricObject geoObject1 = new Circle(5);
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 中定义抽象方法的好处.
下面是关于抽象类值得注意的几点:
抽象方法不能包含在非抽象类中. 如果抽象父类的子类不能实现所有的抽象方法, 那么子类也必须定义为抽象的. 换句话说, 在抽象类扩展的非抽象子类中, 必须实现所有的抽象方法. 还要注意到, 抽象方法是非静态的.
抽象类是不能使用 new 操作符来初始化的. 但是, 仍然可以定义它的构造方法, 这个构造方法在它的子类的构造方法中调用. 例如, CeometricObject 类的构造方法在Circle 类和 Recfange 类中调用.
包含抽象方法的类必须是抽象的. 但是, 可以定义一个不包含抽象方法的抽象类.
在这种情况下, 不能使用 new 操作符创建该类的实例. 这种类是用来定义新子类的基类的.
子类可以覆盖父类的方法并将它定义为 abstract. 这是很少见的, 但是它在当父类的方法实现在子类中变得无效时是很有用的. 在这种情况下, 子类必须定义为abstract.
即使子类的父类是具体的, 这个子类也可以是抽象的. 例如, Object 类是具体的, 但是它的子类如 CeometricObject 可以是抽象的.
不能使用 new 操作符从一个抽象类创建一个实例, 但是抽象类可以用作一种数据类型. 因此, 下面的语句创建一个元素是 CeometricObject 类型的数组是正确的:
GeometricObject[] objects = new GeometricObject[10];
然后可以创建一个 CeometricObject 的实例, 并将它的引用陚值给数组, 如下所示:
objects[0] = new Circle();