抽象类
Java抽象类是一个不能被实例化的类,这意味着你不能创建一个抽象类的新实例。抽象类的目的是作为子类的基础。本篇教程会讲解如何在Java中创建抽象类,抽象类适用于哪些规则。
声明一个抽象类
在Java中,通过将abstract关键字添加到类声明中从而声明一个类是抽象的。示例如下:
public abstract class MyAbstractClass {
}
这就是在Java中声明一个抽象类的样子。现在你不能创建MyAbstractClass的实例。因此,以下Java代码不再有效:
MyAbstractClass myClassInstance =
new MyAbstractClass(); //无效
如果你尝试编译上面的代码,Java编译器将会产生一个错误,告诉你不能实例化MyAbstractClass,因为它是一个抽象类。
抽象方法
抽象类可以有抽象的方法。你通过在方法声明前添加abstract关键字来声明抽象方法。下面是一个Java抽象方法的例子:
public abstract class MyAbstractClass {
public abstract void abstractMethod();
}
抽象方法没有实现。它只是有一个方法签名。就像Java接口中的方法一样。
如果一个类有一个抽象方法,那么整个类就必须声明为抽象的。抽象类中的所有方法并不是都必须是抽象方法。抽象类可以有抽象和非抽象方法的混合。
抽象类的子类必须实现(覆盖)其抽象超类的所有抽象方法。超类的非抽象方法可以被继承。当然如果需要,这些方法也可以被覆盖。
下面是抽象类MyAbstractClass的示例子类:
public class MySubClass extends MyAbstractClass {
public void abstractMethod() {
System.out.println("My method implementation");
}
}
注意MySubClass如何从其抽象超类MyAbstractClass中实现抽象方法abstractMethod()的。
只有当抽象类的子类也是抽象类时,才允许子类不用完全实现其抽象超类的所有抽象方法。
使用抽象类的目的
抽象类的目的是作为基类,它可以被子类扩展来创建一个完整的实现。例如,假设某个过程需要3个步骤:
- 行动前准备步骤。
- 行动。
- 行动之后的步骤。
如果行动前后的步骤总是一样的话,那么可以用下面实例代码来模拟这个过程:
public abstract class MyAbstractProcess {
public void process() {
stepBefore();
action();
stepAfter();
}
public void stepBefore() {
//直接在抽象超类中实现该方法
}
public abstract void action(); // 由子类实现
public void stepAfter() {
//直接在抽象超类中实现该方法
}
}
注意action()方法是抽象的。MyAbstractProcess的子类可以扩展MyAbstractProcess,并覆盖action()方法。
当调用子类的process()方法时,将执行完整的过程,包括抽象超类的stepBefore()和stepAfter()以及子类的action()方法。
当然,MyAbstractProcess不一定是抽象类来作为基类。 action()方法也不一定是抽象的。你可以用一个普通的类来实现。然而,通过实现抽象类的方式,你清楚地向这个抽象类的用户发出这样的信息:不应该直接使用这个类。相反,它应该用作子类的基类,并且抽象方法应该在子类中实现。
上面的例子没有action()方法的默认实现。在某些情况下,你超类中方法可能有一个默认实现。在这种情况下,你不能将该方法声明为抽象的。此外,即使类中没有抽象方法,该类仍然可以声明为抽象类。
下面是一个更具体的例子,它打开一个URL,处理它,然后关闭与URL的连接。
public abstract class URLProcessorBase {
public void process(URL url) throws IOException {
URLConnection urlConnection = url.openConnection();
InputStream input = urlConnection.getInputStream();
try{
processURLData(input);
} finally {
input.close();
}
}
protected abstract void processURLData(InputStream input)
throws IOException;
}
注意processURLData()是一个抽象方法,而URLProcessorBase是一个抽象类。 URLProcessorBase的子类必须实现processURLData()方法,因为它是一个抽象方法。
URLProcessorBase抽象类的子类可以处理从URL下载的数据,而不用关心打开和关闭与URL的网络连接。这部分是由URLProcessorBase完成。子类只需要担心处理从传递给processURLData()方法的InputStream中的数据。这样使得实现一个从URL处理数据的类更加容易。
子类示例如下:
public class URLProcessorImpl extends URLProcessorBase {
@Override
protected void processURLData(InputStream input) throws IOException {
int data = input.read();
while(data != -1){
System.out.println((char) data);
data = input.read();
}
}
}
注意子类是如何实现processURLData()方法的,除此之外,其余的代码是从URLProcessorBase超类继承的。
下面是使用URLProcessorImpl类的一个例子:
URLProcessorImpl urlProcessor = new URLProcessorImpl();
urlProcessor.process(new URL("http://jenkov.com"));
process()方法将被调用,它是在URLProcessorBase超类中实现。这个方法又调用URLProcessorImpl类中的processURLData()方法。
抽象类与模板方法设计模式
上面的例子实际上是Template Method(模板方法)设计模式的一个例子。 Template Method设计模式提供了部分方法的实现,在扩展Template Method基类时,子类可以实现其余未实现的方法。
下一篇: 接口