OOP-面向对象编程,核心概念:对象,类,继承,接口,软件包。
对象(Object)
An object is a software bundle of related state and behavior.
对象是理解OOP的关键。看看周围,你会在现实世界里发现很多对象的例子:你的狗,你的办公桌,你的电视机,你的自行车。
现实世界的对象都有两个属性:
- 状态
- 行为
狗有状态(名字,颜色,品种,饥饿)和行为(吠叫,摇尾巴)。
自行车有状态(当前档位,当前踏板节奏,当前速度)和行为(改变档位,改变踏板节奏,刹车)。
识别现实世界对象的状态和行为是开始面向对象编程思考的好方法。
现在花一分钟观察在你附近的真实世界的对象。对于你看到的每个对象,问自己两个问题:
- 这个对象可能存在什么状态?
- 这个对象可能可以执行什么样的行为?
写下你的观察,你会注意到,现实世界的诸多对象在复杂性上有所不同;台灯可能只有两种状态(开着的和关着的)和两种可能的行为(打开,关闭),但是收音机可能具有额外的其他状态(开,关,当前音量,当前电台) 和其他操作行为(关闭,调大音量,降低音量,寻台,扫描和调谐)。你也可能注意到,一些对象,也包含着其他的对象。这些真实世界的观察都会转化为面向对象编程的世界。
软件对象在概念上类似于现实世界中的对象︰ 他们也是由状态和相关的行为组成的。软件对象将“状态”存储在域 (某些编程语言中也叫变量) 和通过方法 (在某些编程语言中也叫函数) 来表达其“行为”。
方法可用来操作对象的内部状态,并作为对象与对象之间通信的主要机制。隐藏内部状态,要求所有的交互(包括与内部状态相关的访问操作等交互)都通过对象的方法来执行,这被称作“数据封装” —— 面向对象编程的一个基本原则。
以自行车为例
通过状态(当前速度,当前踏板节奏和当前档位)并提供用于改变状态的方法,对象保持了对允许外部怎么样来使用对象的控制 。 例如,如果自行车仅具有6个齿轮,则改变齿轮的方法(Change gears)可以拒绝小于1或大于6的数值。
将代码集中到一个独立的软件对象内部有许多好处,包括:
- 1.模块化:对象的源代码可以独立于其他对象的源代码进行编写和维护。一旦被创建,一个对象可以很容易地在系统内部传递。
- 2.信息隐藏:只能通过对象的方法与对象进行交互,其内部实现的细节对外部是隐藏的。
- 3.代码重用:如果一个对象已经存在(可能由另一个软件开发者编写),你可以在你自己的程序中使用该对象。这样允许软件专家来实现/测试/调试一些复杂的有特定功能的对象,然后你可以信任它们在你自己的代码中运行。
- 4.可替换性和易调试性:如果一个特定对象有问题,可以方便地从应用程序中移除这个对象,并插入另一个对象作为替换。这类似于在现实世界中修理机械问题,如果一个螺栓(对象)断裂,则更换这个螺栓,而不是整个机器(应用程序)。
类(Class)
A class is a blueprint or prototype from which objects are created
在现实世界中,你经常会发现许多同样的物体,比如有成千上万的其他自行车,他们可能有相同的品牌和型号。 每辆自行车是从同一套“设计图”(blueprint)建造的,因此包含了相同的组件。 在面向对象的术语中,我们说你的自行车是被称为自行车的对象类的一个实例。 一个类就是创建单个对象的设计图。
下面这个自行车类是自行车的一种可能的实现方式(设计图):
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);
}
}
这个类的设计基于前面对自行车对象的讨论。 节奏,速度和齿轮表示对象的状态,方法(changeCadence,changeGear,speedUp等)定义对象与外部的交互行为。
你可能已经注意到,Bicycle类不包含main方法。这是因为它不是一个完整的应用程序;它只是可能在应用程序中使用的自行车的设计图,创建和使用新的Bicycle对象在应用程序的其他类里实现。
下面的BicycleDemo类,创建了两个单独的自行车对象,并调用了他们的方法:
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
继承(Inheritance)
通常不同的对象也可以具有一些共同的状态和行为。 例如,山地自行车,公路自行车和双人自行车都具有自行车的状态(当前速度,当前踏板节奏,当前档位)。 当然他们各自还有一些不同的其他特征:双人自行车具有两个座椅和两组把手; 公路自行车的把手是向下弯的; 一些山地自行车有一个额外的链环,使得车有一个较低的齿轮比。
面向对象编程允许类从其他类继承公共的状态和行为。 在这个例子中,自行车是山地自行车,公路自行车和双人自行车的超类。 在Java编程语言中,每个类只允许有一个超类,但每个超类可以有多个子类:
创建子类的语法很简单,在类声明的开头,使用extends关键字,后面是超类的名称:
class MountainBike extends Bicycle {
// new fields and methods defining
// a mountain bike would go here
}
继承让类MountainBike也拥有类Bicycle全部的域和方法,类MountainBike的代码可以专注于描述它特有的域和方法,这样代码更容易阅读。 但是,你必须正确地描述超类的状态和行为,因为描述超类的状态和行为的代码不会出现在子类的源文件中。
接口(Interface)
An interface is a contract between a class and the outside world.
对象通过方法来定义它们与外部的交互行为,方法形成对象与外界的接口。例如,电视机上的电源按钮是您与其塑料外壳另一侧的电气接线之间的接口。 您按下“电源”按钮打开和关闭电视。
一般来说,接口是一组“空”方法。 自行车的行为如果被指定为接口,如下所示:
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关键字。
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.
public void changeCadence(int newValue) {
cadence = newValue;
}
...
...
}
软件包(Package)
A package is a namespace that organizes a set of related classes and interfaces.
你可以把软件包看作类似于你计算机上不同的文件夹。 您会将HTML页面保存在一个文件夹中,将图像保存在一个文件夹中,将脚本或应用程序保存在另一个文件夹中。 因为用Java编程语言编写的软件可能是由数百或数千个类组成的,将相关的类和接口放入一个软件包更有条理。
Java平台提供了一个巨大的类库(一组软件包),你可以在自己的应用程序中使用使用它。 这个类库称为“应用程序编程接口”,简称“API”。类库中的软件包代表了通用编程中最常见的任务。例如,String对象包含字符串的状态和行为; File对象允许程序员创建,删除,检查,比较或修改文件系统上的文件; Socket对象允许创建和使用网络套接字; 各种GUI对象控制按钮和复选框以及与图形用户界面相关的内容。类库中有数千个类可供选择使用。 这样你可以专注于你自己的应用程序的设计,而不需要操心这些底层的东西。
Java API规格文档包含由Java SE平台提供的所有软件包,接口,类,域(字段)和方法的完整列表。