有些情况下,你希望定义一个超类,该超类定义了一种给定结构的抽象但是不提供任何完整的方法实现。也就是说,有时你希望创建一个只定义一个被它的所有子类共享的通用形式,由每个子类自己去填写细节。

这样的类决定了子类所必须实现的方法的本性。这类情形下一种可能发生的情况是超类不能创建一个方法的有意义的实现。前面的例子中用到的类Figure就属于这种情况。area( )的定义仅是一个占位符。它不会计算和显示任何类型对象的面积。

当创建自己的类库时你会看到,超类中的方法没有实际意义并不罕见。你有两种方法可以处理这种情况。第一种,如前面的例子所示,仅仅是报告一个出错消息。

尽管这种方式在某些场合是有用的——例如调试——但是它不是很适用的。你还有一种方法就是通过子类重载该方法以使它对子类有意义。

考虑Triangle类,如果不定义area( )它是毫无意义的。这种情况下,你希望有方法确保子类真正重载了所有必须的方法。Java对于这个问题的解决是用抽象方法。

你可以通过指定abstract类型修饰符由子类重载某些方法。这些方法有时被作为子类责任引用,因为它们没有在超类中指定的实现。这样子类必须重载它们——它们不能简单地使用超类中定义的版本。声明一个抽象方法,用下面的通用形式:

abstract type name(parameter-list);

正如你所看到的,不存在方法体。任何含有一个或多个抽象方法的类都必须声明成抽象类。声明一个抽象类,只需在类声明开始时在关键字class前使用关键字abstract。抽象类没有对象。

也就是说,一个抽象类不能通过new操作符直接实例化。这样的对象是无用的,因为抽象类是不完全定义的。而且,你不能定义抽象构造函数或抽象静态方法。所有抽象类的子类都必须执行超类中的所有抽象方法或者是它自己也声明成abstract。

下面是具有一个抽象方法类的简单例题。该类后面是一个执行抽象方法的类:

// A Simple demonstration of abstract. 
abstract class A { 
 abstract void callme(); 
 // concrete methods are still allowed in abstract classes 
 void callmetoo() { 
 System.out.println("This is a concrete method."); 
 } 
} 
class B extends A { 
 void callme() { 
 System.out.println("B's implementation of callme."); 
 } 
} 
class AbstractDemo { 
 public static void main(String args[]) { 
 B b = new B(); 
 b.callme(); 
 b.callmetoo(); 
 } 
}

注意程序中声明A的对象。刚刚讲过,实例化一个抽象类是不可能的。另外一点要注意:类A实现一个具体的方法callmetoo( )。这是完全可接受的,抽象类可以包括它们合适的很多实现。

因为Java的运行时多态是通过使用超类引用实现的,所以尽管抽象类不能用来实例化,它们可以用来创建对象引用。这样,创建一个抽象类的引用是可行的,这样它可以用来指向一个子类对象。在下面的程序中你将会看到这种特性的运用。

运用抽象类,你可以改善前面所显示的Figure类。因为对于一个未定义的二维图形,面积的概念是没有意义的,下面的程序在Figure内将area( )定义成抽象方法。这样当然意味着从Figure派生的所有类都必须重载area( )方法。

// Using abstract methods and classes. 
abstract class Figure { 
 double dim1; 
 double dim2; 
 Figure(double a, double b) { 
 dim1 = a; 
 dim2 = b; 
 } 
 // area is now an abstract method 
 abstract double area(); 
} 
class Rectangle extends Figure { 
 Rectangle(double a, double b) { 
 super(a, b); 
 } 
 // override area for rectangle 
 double area() { 
 System.out.println("Inside Area for Rectangle."); 
 return dim1 * dim2; 
 } 
} 
class Triangle extends Figure { 
 Triangle(double a, double b) { 
 super(a, b); 
 } 
 // override area for right triangle 
 double area() { 
 System.out.println("Inside Area for Triangle."); 
 return dim1 * dim2 / 2; 
 } 
} 
class AbstractAreas { 
 public static void main(String args[]) { 
 // Figure f = new Figure(10, 10); // illegal now 
 Rectangle r = new Rectangle(9, 5); 
 Triangle t = new Triangle(10, 8); 
 Figure figref; // this is OK, no object is created 
 figref = r; 
 System.out.println("Area is " + figref.area()); 
 figref = t; 
 System.out.println("Area is " + figref.area()); 
 } 
}

Main()内的注释暗示,定义Figure类型的对象不再是可能的了,因为现在它是抽象类。而且,所有Figure的子类都必须重载area( )方法。为证明这点,试着创建不重载area( )的子类。你会收到一个编译时错误。

尽管不可能创建一个Figure类型的对象,你可以创建一个Figure类型的引用变量。变量figref声明成Figure的一个引用,意思是说它可以用来引用任何从Figure派生的对象。刚才解释过的,通过超类引用变量重载方法在运行时解决。