对象、类、接口、包和继承

如果您以前从未使用过面向对象的编程语言,那么在开始编写任何代码之前,您需要学习一些基本概念。本节将向您介绍对象、类、继承、接口和包。每个讨论都侧重于这些概念如何与现实世界相关,同时提供对 Java 编程语言语法的介绍。

 

什么是对象?

对象是相关状态和行为的软件捆绑。本节解释了如何在对象中表示状态和行为,介绍了数据封装的概念,并解释了以这种方式设计软件的好处。

对象共享两个特征:它们都有状态和行为。狗有状态(名字、颜色、品种、饥饿)和行为(吠叫、取物、摇尾巴)。自行车也有状态(当前档位、当前踏板节奏、当前速度)和行为(换档、改变踏板节奏、刹车)。识别现实世界对象的​​状态和行为是开始用面向对象编程思维方式思考的好方法。

现在花点时间观察一下您周围的现实世界对象。对于您看到的每个对象,问自己两个问题:“这个对象可能处于什么状态?” 和“这个对象可以执行什么行为?”。确保记下您的观察结果。在您这样做的时候,您会注意到现实世界中的对象在复杂性方面有所不同;您的台灯可能只有两种可能的状态(开和关)和两种可能的行为(打开、关闭),但您的台式收音机可能具有其他状态(开、关、当前音量、当前电台)和行为(打开、关闭、增加音量、降低音量、搜索、扫描和调谐)。您可能还会注意到,某些对象反过来也会包含其他对象。这些现实世界的观察是理解面向对象编程世界的起点。

A software object

软件对象

软件对象由状态和相关行为组成。对象将其状态存储在字段(某些编程语言中的变量)中,并通过方法(某些编程语言中的函数)公开其行为。方法对对象的内部状态进行操作,并作为对象之间通信的主要机制。隐藏内部状态并要求所有交互都通过对象的​​方法执行称为数据封装——面向对象编程的基本原则。

例如,考虑一辆自行车

A bicycle modeled as a software object

作为软件对象的自行车模型

通过将状态(当前速度、当前踏板节奏和当前档位)归因并提供用于更改该状态的方法,对象可以控制外部世界如何使用它。例如,如果自行车只有 6 个档位,则用于换档的方法可以拒绝任何小于 1 或大于 6 的值。

将代码捆绑到各个软件对象中提供了许多好处,包括

  1. 模块化:对象的源代码可以独立于其他对象的源代码编写和维护。创建后,对象可以轻松地在系统内部传递。
  2. 信息隐藏:通过仅与对象的​​方法交互,其内部实现细节对外部世界隐藏。
  3. 代码重用:如果对象已经存在(可能是由另一个软件开发人员编写),您可以在程序中使用该对象。这允许专家实现/测试/调试复杂的任务特定对象,然后您可以信任它们在自己的代码中运行。
  4. 可插拔性和调试简便性:如果某个特定对象出现问题,您可以简单地将其从应用程序中删除,并插入另一个对象作为其替代品。这类似于修复现实世界中的机械问题。如果螺栓断裂,您会更换它,而不是整个机器。

 

什么是类?

在您的应用程序中,您经常会发现许多相同类型的单个对象。可能存在数千辆其他自行车,它们都是同一品牌和型号。每辆自行车都是从同一套蓝图中制造出来的,因此包含相同的组件。在面向对象术语中,我们说您的自行车是称为自行车的对象类的实例。是创建单个对象的蓝图。

以下Bicycle类是自行车的一种可能的实现

class Bicycle {

    int cadence = 0;
    int speed = 0;
    int gear = 1;

    void changeCadence(int newValue) {
         cadence = newValue;
    }

    void changeGear(int newValue) {
         gear = newValue;
    }

    void speedUp(int increment) {
         speed = speed + increment;   
    }

    void applyBrakes(int decrement) {
         speed = speed - decrement;
    }

    void printStates() {
         System.out.println("cadence:" +
             cadence + " speed:" + 
             speed + " gear:" + gear);
    }
}

Java 编程语言的语法可能对您来说很陌生,但此类的设计基于之前关于自行车对象的讨论。cadencespeedgear 字段表示对象的​​状态,而方法(changeCadence()changeGear()speedUp() 等)定义了它与外部世界的交互。

您可能已经注意到Bicycle类不包含main()方法。这是因为它不是一个完整的应用程序;它只是可能在应用程序中使用的自行车的蓝图。创建和使用新Bicycle对象的责任属于应用程序中的其他类。

这是一个BicycleDemo类,它创建了两个独立的Bicycle对象并调用它们的方法

class BicycleDemo {
    public static void main(String[] args) {

        // Create two different 
        // Bicycle objects
        Bicycle bike1 = new Bicycle();
        Bicycle bike2 = new Bicycle();

        // Invoke methods on 
        // those objects
        bike1.changeCadence(50);
        bike1.speedUp(10);
        bike1.changeGear(2);
        bike1.printStates();

        bike2.changeCadence(50);
        bike2.speedUp(10);
        bike2.changeGear(2);
        bike2.changeCadence(40);
        bike2.speedUp(10);
        bike2.changeGear(3);
        bike2.printStates();
    }
}

此测试的输出打印了两辆自行车的最终踏板节奏、速度和档位

cadence:50 speed:10 gear:2
cadence:40 speed:20 gear:3

 

什么是继承?

不同类型的对象通常彼此之间具有一定的共性。例如,山地车、公路车和双人自行车都具有自行车的特征(当前速度、当前踏板节奏、当前档位)。然而,每种自行车也定义了使它们不同的附加功能:双人自行车有两个座位和两套车把;公路车有下弯车把;一些山地车有一个额外的链环,使它们具有更低的齿轮比。

面向对象编程允许类从其他类继承常用状态和行为。在此示例中,Bicycle现在成为MountainBikeRoadBikeTandemBike 的超类。在 Java 编程语言中,每个类都允许有一个直接超类,并且每个超类都有可能拥有无限数量的子类

A hierarchy of bicycle classes

自行车类的层次结构

创建子类的语法很简单。在您的类声明的开头,使用extends关键字,后跟要继承的类的名称

class MountainBike extends Bicycle {

    // new fields and methods defining 
    // a mountain bike would go here

}

这使MountainBike具有与Bicycle相同的所有字段和方法,但允许其代码专门关注使其独特的特征。这使得子类的代码易于阅读。但是,您必须注意正确记录每个超类定义的状态和行为,因为该代码不会出现在每个子类的源文件中。

 

什么是接口?

正如您已经了解到的,对象通过它们公开的方法来定义它们与外部世界的交互。方法构成了对象与外部世界的接口;例如,您电视机正面上的按钮是您与塑料外壳另一侧的电线之间的接口。您按下“电源”按钮来打开和关闭电视。

在最常见的形式中,接口是一组具有空主体​​的相关方法。如果将自行车的行为指定为接口,它可能如下所示

interface Bicycle {

    //  wheel revolutions per minute
    void changeCadence(int newValue);

    void changeGear(int newValue);

    void speedUp(int increment);

    void applyBrakes(int decrement);
}

要实现此接口,您的类的名称将更改(例如,更改为特定品牌的自行车,例如ACMEBicycle),并且您将在类声明中使用implements关键字

class ACMEBicycle implements Bicycle {

    int cadence = 0;
    int speed = 0;
    int gear = 1;

   // The compiler will now require that methods
   // changeCadence, changeGear, speedUp, and applyBrakes
   // all be implemented. Compilation will fail if those
   // methods are missing from this class.

    void changeCadence(int newValue) {
         cadence = newValue;
    }

    void changeGear(int newValue) {
         gear = newValue;
    }

    void speedUp(int increment) {
         speed = speed + increment;   
    }

    void applyBrakes(int decrement) {
         speed = speed - decrement;
    }

    void printStates() {
         System.out.println("cadence:" +
             cadence + " speed:" + 
             speed + " gear:" + gear);
    }
}

实现接口允许类在它承诺提供的行为方面变得更加正式。接口在类和外部世界之间形成契约,并且此契约在编译时由编译器强制执行。如果您的类声称实现接口,则该接口定义的所有方法都必须出现在其源代码中,然后该类才能成功编译。

注意:要实际编译ACMEBicycle类,您需要在已实现的接口方法的开头添加public关键字。您将在后面的关于类和对象接口继承的部分中了解原因。

 

什么是包?

包是一个命名空间,用于组织一组相关的类和接口。从概念上讲,您可以将包视为类似于计算机上的不同文件夹。您可能会将 HTML 页面保存在一个文件夹中,将图像保存在另一个文件夹中,并将脚本或应用程序保存在另一个文件夹中。由于用 Java 编程语言编写的软件可能由数百或数千个单独的类组成,因此将相关类和接口放入包中以保持组织是有意义的。

Java 平台提供了一个庞大的类库(一组包),适合在您自己的应用程序中使用。这个库被称为“应用程序编程接口”,简称“API”。它的包代表了与通用编程最常相关的任务。例如,一个String对象包含字符字符串的状态和行为;一个File对象允许程序员轻松创建、删除、检查、比较或修改文件系统上的文件;一个Socket对象允许创建和使用网络套接字;各种 GUI 对象控制按钮和复选框以及与图形用户界面相关的任何其他内容。实际上有数千个类可供选择。这使您(程序员)能够专注于您特定应用程序的设计,而不是使其正常运行所需的架构。

Java 平台 API 规范包含 Java SE 平台提供的所有包、接口、类、字段和方法的完整列表。在您的浏览器中加载页面并将其添加为书签。作为程序员,它将成为您最重要的参考文档。

最后更新: 2021年9月14日


返回教程列表