Menu

Java 抽象类:abstract 关键字详解

Java 抽象类是什么,如何声明抽象方法,为什么不能实例化抽象类,以及什么时候该选抽象类而不是接口。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

什么是抽象类

接口只声明行为,没有状态。普通类是完整实现的,可以被实例化。抽象类介于两者之间:它可以像普通类一样拥有字段、构造方法和已实现的方法,但也可以让某些方法不实现,并且禁止被直接实例化。用 abstract 关键字来标记它。

它的思路是把子类的所有共同点集中放在一处,同时强制每个子类补全真正不同的部分。

Animal 为所有子类统一定义了一次 getName(),并把 sound() 声明为 abstract:这是一个只有签名、没有方法体的方法,Dog 必须提供它的实现。

不能实例化抽象类

抽象类可能有未完成的方法,直接创建它会得到一个不完整的对象。编译器会拒绝这样做:

Animal a = new Animal("???");   // error: Animal is abstract; cannot be instantiated

你总是实例化一个具体子类,也就是实现了每个抽象方法的子类。然后可以把这个子类实例存到抽象类型的变量中,这正是使用抽象的方式。

抽象方法强制子类做出决定

abstract 方法是子类必须兑现的承诺。如果子类忘了实现某个抽象方法,这个子类本身也会变成抽象类,编译器会告诉你这一点。这是抽象类最主要的作用:保证某种行为一定存在,却不规定它具体做什么。

describe() 只在 Shape 中写了一次,却会调用每个子类自己的 area()。抽象类提供共同的框架,子类提供具体的细节。

共享状态与构造方法

和传统的接口不同,抽象类可以拥有实例字段并定义构造方法。构造方法永远不会单独创建出一个 Animal 或 Shape,它在创建子类时通过 super(...) 运行,用来初始化共享状态。

balance 字段、deposit 和 applyInterest 都集中在一处。只有真正会变化的策略 interestRate() 被保留为抽象方法。子类的构造方法必须调用 super(...) 来初始化继承来的状态。

一个陷阱:同时使用 abstract 和 final

abstract 和 final 是相反的。abstract 方法要求被重写,final 方法禁止被重写。把同一个东西同时标记成这两种,或者写一个 final 抽象类,都会导致编译错误。另外要记住,抽象类可以一个抽象方法都没有:仅仅为了防止被实例化而把类声明为 abstract 是合法的,对于只打算被继承的基类型有时也很有用。

abstract final class Bad { }        // error: abstract and final conflict

abstract class Base {
    abstract final void f();        // error: an abstract method can't be final
}

抽象类与接口

两者有重叠,选择哪个取决于你需要共享什么:

  • 抽象类:用于共享状态和代码、关系紧密的类。Savings 和 Checking 都继承 Account,继承了 balance 字段和 deposit 逻辑。一个类只能继承一个类。
  • 接口:用于互不相关的类可以共享的某种能力。Bird 和 Airplane 都可以是 Flyable,却不共享任何实现。一个类可以实现多个接口。

常见的模式是把两者结合起来:接口定义契约,抽象类实现样板代码,这样具体子类只需补全自己独有的部分。

下一步:多态

注意,上面每个例子都把子类实例存到抽象类型的变量中,然后调用方法,自动得到子类的行为。这种能力,即一个引用类型对应多种运行时行为,就是多态,它让抽象类和接口真正发挥价值。这就是下一页的内容。

常见问题

Java 抽象类是什么?

抽象类是用 abstract 关键字声明、不能单独实例化的类。它就是用来被继承的。它既可以包含完整实现的方法和字段(供子类共享的状态和代码),也可以包含没有方法体的 abstract 方法,这些方法由每个子类负责实现。

Java 中可以实例化抽象类吗?

不可以。new AbstractType() 会导致编译错误,因为抽象类可能有未实现的(abstract)方法,这样创建出的对象是不完整的。你应该实例化一个实现了所有抽象方法的具体子类,再把它存到抽象类型的变量中。

Java 中抽象类和接口有什么区别?

抽象类可以有实例字段、构造方法和部分实现的逻辑,但一个类只能继承一个抽象类。接口声明行为,没有实例状态,一个类可以实现多个接口。要在关系紧密的子类之间共享状态和代码,就用抽象类;要让互不相关的类具备某种共同能力,就用接口。

Coddy 编程语言插图

和 Coddy 一起学编程

开始