Skip to content

Java 抽象

抽象(Abstraction)是面向对象编程(Object-Oriented Programming, OOP)的核心概念之一。它侧重于隐藏复杂的实现细节,只暴露对象或系统的基本特征或功能。

想象一下驾驶汽车:你只需要与方向盘、油门、刹车等基本控制装置交互(抽象),而无需了解发动机内部燃烧或刹车系统液压等复杂的实现细节(implementation details)。

在 Java 中,抽象主要通过以下方式实现:

  • 抽象类(Abstract Classes): 不能被实例化,并且可能包含抽象方法(abstract methods)的类。
  • 接口(Interfaces): 定义抽象方法(以及可能的默认方法/静态方法)契约的引用类型。

本章重点介绍抽象类。

抽象类是其他类的模板或蓝图。它使用 abstract 关键字声明。

  • 不能被实例化(Cannot be Instantiated): 你不能使用 new 关键字直接创建抽象类的对象。
  • 可以包含抽象方法(Can Have Abstract Methods): 使用 abstract 关键字声明且没有具体实现(没有方法体,以分号结束)的方法。如果一个类包含至少一个抽象方法,则该类必须被声明为抽象类。
  • 可以包含具体方法(Can Have Concrete Methods): 可以包含带有实现的普通方法。
  • 可以包含构造方法(Can Have Constructors): 抽象类可以包含构造方法,当创建具体子类的实例时会调用这些构造方法(通常通过子类构造方法中的 super() 调用)。
  • 可以包含字段(Can Have Fields): 可以包含实例变量(状态)和静态变量。
  • 可以被继承(Subclassing): 旨在被继承(使用 extends 关键字)。具体(非抽象)子类必须为其继承的所有抽象方法提供实现。
  • 代码复用(Code Reusability): 提供子类可以继承的通用字段和方法实现。
  • 强制结构(Enforce Structure): 定义所有子类必须实现的通用结构和必需方法(抽象方法),确保一致性。
  • 模板设计(Template Design): 作为相关类的基础,定义一个过于抽象而不能独立存在的通用概念或实体,但为特定类型提供了基础。
  • 多态(Polymorphism): 允许通过抽象类类型的引用来处理不同子类的对象。

在 class 关键字之前使用 abstract 关键字。

/* 文件名:Shape.java */
public abstract class Shape {
private String color;
// Constructor (called by subclasses)
public Shape(String color) {
System.out.println("Shape constructor called for color: " + color);
this.color = color;
}
// Concrete method (shared implementation)
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
// Abstract method (must be implemented by concrete subclasses)
public abstract double calculateArea();
// Another abstract method
public abstract void draw();
}

尝试实例化抽象类会导致编译时错误:

// 这会导致编译错误:
// Shape myShape = new Shape("Red"); // 错误:Shape 是抽象的;不能被实例化

具体类使用 extends 关键字继承抽象类。它必须为其从抽象父类继承的所有抽象方法提供实现。使用 @Override 注解可以提高清晰度并进行编译器检查。

Circle.java
public class Circle extends Shape { // 继承抽象类 Shape
private double radius;
public Circle(String color, double radius) {
super(color); // 调用抽象父类的构造方法
System.out.println("Circle constructor called.");
this.radius = radius;
}
public double getRadius() {
return radius;
}
// 实现 Shape 中的抽象方法
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
// 实现 Shape 中的另一个抽象方法
@Override
public void draw() {
System.out.println("Drawing a " + getColor() + " circle with radius " + radius);
}
}
Rectangle.java
public class Rectangle extends Shape {
private double width;
private double height;
public Rectangle(String color, double width, double height) {
super(color);
System.out.println("Rectangle constructor called.");
this.width = width;
this.height = height;
}
@Override
public double calculateArea() {
return width * height;
}
@Override
public void draw() {
System.out.println("Drawing a " + getColor() + " rectangle with width " + width + " and height " + height);
}
}

你可以创建具体子类的对象,并使用多态性通过抽象类类型引用它们。

/* 文件名:AbstractionDemo.java */
public class AbstractionDemo {
public static void main(String[] args) {
// 不能创建 Shape 对象:Shape s1 = new Shape("Blue"); // 错误!
// 创建具体子类的对象
Shape circle = new Circle("Red", 5.0);
Shape rectangle = new Rectangle("Blue", 4.0, 6.0);
System.out.println("\n--- Circle Info ---");
System.out.println("Color: " + circle.getColor()); // 继承的具体方法
System.out.println("Area: " + circle.calculateArea()); // 重写的抽象方法
circle.draw(); // 重写的抽象方法
// 错误:getRadius() 不在 Shape 中。需要强制类型转换:
if (circle instanceof Circle) {
Circle c = (Circle) circle;
System.out.println("Radius: " + c.getRadius());
}
System.out.println("\n--- Rectangle Info ---");
System.out.println("Color: " + rectangle.getColor());
System.out.println("Area: " + rectangle.calculateArea());
rectangle.draw();
System.out.println("\n--- 数组与多态 ---");
Shape[] shapes = { circle, rectangle, new Circle("Green", 2.5) };
for (Shape currentShape : shapes) {
currentShape.draw(); // 调用 Circle 或 Rectangle 特定的 draw() 方法
System.out.println("Area: " + currentShape.calculateArea());
System.out.println("--");
}
}
}

预期输出(示意):

Shape constructor called for color: Red
Circle constructor called.
Shape constructor called for color: Blue
Rectangle constructor called.
--- Circle Info ---
Color: Red
Area: 78.53981633974483
Drawing a Red circle with radius 5.0
Radius: 5.0
--- Rectangle Info ---
Color: Blue
Area: 24.0
Drawing a Blue rectangle with width 4.0 and height 6.0
--- Polymorphism with Array ---
Shape constructor called for color: Green
Circle constructor called.
Drawing a Red circle with radius 5.0
Area: 78.53981633974483
--
Drawing a Blue rectangle with width 4.0 and height 6.0
Area: 24.0
--
Drawing a Green circle with radius 2.5
Area: 19.634954084936208
--

抽象类和接口都用于实现抽象,但它们之间有关键区别:

  • 继承: 一个类只能继承(extend)一个抽象类,但可以实现(implement)多个接口。
  • 字段: 抽象类可以有实例变量(状态)。接口主要包含 public static final 常量(虽然技术上可以使用默认方法模拟状态,但这并非其预期用途)。
  • 方法: 抽象类可以包含抽象方法和具体方法。接口包含抽象方法,并且自 Java 8 起,也可以包含默认方法(已实现)和静态方法。
  • 构造方法: 抽象类可以有构造方法;接口不能有。
  • 用途: 对于子类共享通用状态和实现的“is-a”关系,使用抽象类。对于“has-a”或“can-do”关系,使用接口来定义一个可能不相关的类可以实现的某种能力或契约。