7.5 抽象——abstract
抽象不仅是面向对象设计的重要指导思想,在
Java
中也是编写类的实际语法格式。这一小节首先从继承中超类的抽象特性,讨论如何设计和编写抽象类。并且利用实例解释抽象在编程中的具体应用。
7.5.1 抽象就是高度概括
观察和分析一些继承链和超类,如
API
中的继承链,以及一些超类,如
Object
和
Exception
,以及编程人员自己设计的继承链,如
CircleShape
,不难看出,沿着继承链越往上的类就越抽象。正因为抽象,所以涵盖的面就越广。因而可以说,抽象就是高度概括。
抽象类的设计思想正是基于这个事实和出发点。例如在计算圆形物体的面积和体积的程序中,作为超类的
CircleShape
正是抽象性地概括了所有圆形物体具有的共同特性——半径。无论是什么样的圆形物体,都具有半径这个事实。当然,给出圆形物体上的任意两点,也可以确定其半径,因而它也涵盖了用两点来确定半径的运算和操作。当然,一些特殊的圆形物体,如圆棱柱、环形物体,扇形物体,等等,不只仅有半径,而且必须有其他参数来确定和描述它们。这些特殊性,不是高度概括的特性,也不具有代表性,因而不能包括在超类,如
CircleShape
中。
如果把问题再提升到广义,如定义所有的几何物体,而不只是圆形。则可在
CircleShape
上加入更抽象的超类,如
Shape
。
Shape
类不仅包括圆形几何体,也包括如长方形体、三角形体,以及多边形体在内的其他几何物体。甚至涵盖这些基本几何形体的综合形体,等等。那么,经过高度抽象的
Shape
这个类涵盖所有几何物体的物理特征即是两点的坐标。
抽象类除高度概括它代表的所有对象的形态描述之外,还起到制定运算和操作规范以及模式化的作用。虽然在抽象类这一级,要具体完善运算和操作内容是不现实,也是不可能的。但它对具体的重要的运算和操作,可以起到政策制定者和指导者的领导角色。其他所有在这个继承链上的子类,都以它为楷模,来执行具体完善和补充代码编写任务。如,
Shape
类可以对计算表面积和体积的方法进行签名性的描述。对它所代表的所有对象共享的静态数据和静态方法进行定义,等等。
由于抽象类的高度概括性,由它来创建对象将是毫无意义的。对这一点,
Java
已经做了相应的规定,即抽象类只能用来引用,不能够创建对象。关于这个内容,将在第
8
章多态性中详细讨论。
下面讨论如何编写抽象类。
7.5.2 抽象类和抽象方法
抽象类使用关键字
abstract
来定义。其语法格式如下:
public abstract class ClassName {
//
定义数据
...
//
定义方法
...
//
也可能定义抽象方法
public abstract returnTye methodName(argumentList)
;
}
一个抽象类可以包括各种类型数据、构造器以及方法。虽然无抽象方法的抽象类属合法,但通常抽象类定义抽象方法。抽象方法的定义也使用关键字
abstract
。抽象方法只是对这个方法的声明,是没有程序体的还未实现的方法,必须由继承它的某个子类来完善。这也证明了抽象类不能够用来创建对象;或者说使用抽象类来创建对象是没有实际意义的。抽象类的构造器只能在内部通过子类创建的对象调用。
如上一小节讨论,抽象类是高层次的类,通常在一个继承链的顶端,对它所代表的所有对象进行高度概括和描述。一个解决更复杂实际问题的继承链可能有多个抽象类。
下面来讨论一些具体例子。
例
1.
将
CircleShape
修改为抽象类。
//
完整的程序存在本书配套资源目录
Ch7
中名为
CircleShape.java
public abstract class CircleShape {
protected double radius;
protected double x1, y1, x2, y2;
//
所有其他语句
...
public abstract void computeArea(); //
定义抽象方法
public abstract void computeValume();
}
例
2.
定义一个广义上的代表所有人的抽象类
Person
。
//
这个程序存在本书配套资源目录
Ch7
中名为
Person.java
//abstract class
public abstract class Person {
protected String lastName, firstName;
protected char sex;
protected byte age;
protected String address;
public Person(String lastName, String firstName, char sex, byte age,
String address) {
String address) {
this.lastName = lastName;
this.firstName = firstName;
this.sex = sex;
this.age = age;
this.address = address;
}
public String toString() {
String message = "Last name: " + lastName + ", " + "first name: "
+ firstName + "\n"
+ firstName + "\n"
+ "Sex: " + sex + "\n"
+ "Age: " + age + "\n"
+ "Address: " + address + "\n";
return message;
}
}
这个抽象类可以应用在所有与人有关的软件上,因为它涵盖代表所有人的最基本信息和操作。由于在这个层次没有涉及到解决问题的领域,所以只提供覆盖的
toString()
方法,来返回
Person
的信息。
如果编写一个计算雇员工资的软件,可以利用这个超抽象类继承出另一个计算雇员工资的抽象超类
Employee2
,如:
//
这个程序存在本书配套资源目录
Ch7
中名为
Employees.java
public abstract class Employee2 extends Person {
protected String employeeID;
protected String jobTitle;
protected byte seniority;
protected double salary;
public Employee2(String lastName, String firstName, char sex, byte age,
String address, String employeeID, String jobTitle, byte
seniority) {
seniority) {
super(lastName, firstName, sex, age, address);
this.jobTitle = jobTitle;
this.seniority = seniority;
}
public abstract void computeSalary(); //
声明抽象方法
public String toString() { //
覆盖
Person
的
toString()
方法
String message = super.toString() + "EmployeeID: " + employeeID +
"\n"
"\n"
+ "jobTitle: " + jobTitle + "\n"
+ "Seniority: " + seniority + "\n";
return message;
}
}
3W
抽象类是对由它代表的所有对象的高度概括,处于继承链的顶部。抽象类通过关键字
abstract
来定义。抽象类中可以定义任何类型的数据、构造器以及方法。一般情况下,抽象类中定义抽象方法。抽象方法通过关键字
abstract
定义。抽象方法只是方法的声明,而没有程序体。抽象类正是通过定义抽象方法对其子类的运算和操作制定规范。继承链上的非抽象子类必须完善抽象方法。