首页 > 编程笔记 > Java笔记

Java抽象方法和抽象类详解

对于面向对象编程来说,抽象是其重要特征之一。

Java 中,可以通过两种形式来体现面向对象编程的抽象,即抽象方法和抽象类。

抽象方法

当定义一个类时,通常需要定义一些方法来描述该类的行为特征。如果定义的方法没有方法体,即没有“{}”包含的部分,就将这个方法称为抽象方法,也就是说,抽象方法只需要声明不需要实现。

当一个方法为抽象方法时,意味着这个方法必须被子类的方法重写。抽象方法使用关键字 abstract 修饰,只有声明部分,直接以“;”结束。

抽象方法的例子如下:
public abstract void move();
假设上面的 move() 方法表示所有类型的船的移动行为。由于现实中不同类型的船的移动方式是不同的,如木船通过划桨来移动,快艇通过螺旋桨旋转来移动,在 move() 方法中定义任何方法体都不能代表所有船的移动特征,此时可以将 move() 方法定义为抽象方法,由子类根据自身的移动特征,重写父类的 move() 方法,并添加具体的执行逻辑。

在子类中实现父类定义的抽象方法时,其方法名、返回值和参数类型要与父类中的抽象方法的方法名、返回值和参数类型一致。由于一个父类可能有多个子类,而每个子类又对父类中的同一个抽象方法给出了不同的实现过程,因此会出现同一个名称、同一种类型的返回值的方法在不同的子类中实现的方法体不同的情况,这正是多态性的体现。

抽象类

抽象类是指包含抽象方法的类,包含一个或多个抽象方法的类必须被声明为抽象类。

抽象类使用关键字 abstract 修饰,具体的语法格式如下:
abstract class 抽象类名{
    //定义抽象方法
}
抽象类不能实例化,没有继承的抽象类没有意义,因为抽象类就是为了继承而存在的。抽象类的子类如果不是抽象类,就必须实现抽象类所有的抽象方法。

除此之外,抽象类和普通类的成员相同,均可包含成员变量、成员方法和构造方法等成员。

下面的实例演示了抽象类的应用。
abstract class Pet {
    // 定义抽象方法 say()
    abstract void say();
}

class Cat extends Pet {
    // 实现抽象方法 say()
    void say() {
        System.out.println("喵喵喵");
    }
}

public class Test {
    public static void main(String[] args) {
        // 创建Cat类的实例对象
        Cat p = new Cat();
        // 调用p对象的 say() 方法
        p.say();
    }
}
运行结果为:

喵喵喵

由运行结果可知,当子类实现了父类的抽象方法之后,可以正常进行实例化,并且通过实例化对象调用方法。

除了可以从代码的角度来理解抽象类,还可以从面向对象编程的角度进行理解。

人类会对现实世界中的一些事物进行抽象,如“宠物”是对人类饲养的猫、狗、鸟和鱼等各类具体的动物进行抽象而得到的。在代码世界中,抽象类用来与现实世界中抽象的概念相对应。由于现实世界中抽象的概念不存在直接的实例,因此在代码世界中无法通过抽象类直接创建对象。

面向抽象编程

面向抽象编程,是指当设计一个类的特定属性时,不使用具体类,而是使用抽象类。

面向抽象编程是指为了应对用户需求的变化,将某个类中经常因需求变化需要改动的代码分离出去。当需求发生变化时,不需要修改原有类的代码,而是通过切换抽象类的实现类改变执行逻辑。

下面通过一个案例的两种不同版本来展示面向抽象编程的作用。

不使用面向抽象编程的版本如下图所示。


图 1 不使用面向抽象编程的版本

在该案例假设的“20 世纪 80 年代”编写代码时,可能核电站的解决方案还不存在,也无法预见未来会产生核电站的解决方案。此时设计 City 类时,直接使用当时流行的热电站的类。这在当时是没有问题的,如果 City 类永远没有更换电站的需求,那么上面的代码是没有问题的。

但是进入“20 世纪 90 年代”,提出了核电站的解决方案,City 类需要升级为使用核电站。此时,必须对 City 类中的代码进行修改才能完成升级。在实际开发时,这样的修改不仅费时费力,还会增加程序出现 Bug 的概率。

使用面向抽象编程的版本如下图所示。


图 2 使用面向抽象编程的版本

在此版本中,“20 世纪 80 年代”的设计使用了抽象类 PowerStation,City 类使用这个抽象类作为保存电站对象的属性。“20 世纪 90 年代”的设计直接让 NuclearPowerStation 类继承 PowerStation类。在这个版本中,无须修改 City 类中的属性即可完成电站的切换。

相关文章