第 4章 面向对象程序设计基本概念
对象 ( object)
我们可以把生活所在的真实世界当作是由许多大小不同的对象所组成的。对象可以是有生命的个体,比如一个人或一只鸟。
对象也可以是无生命的个体,比如一辆汽车或一台计算机 。
对象也可以是一件抽象的概念,如天气的变化或鼠标所产生的事件 。
对象的特征
对象有两个特征:状态和行为 。 例如:一个人有他的身高或体重作状态,并有他的行为 —— 如唱歌、打球、骑摩托车、
开汽车。一只狗有它的颜色作状态,也有它的行为,如吠叫或跳跃 。
而在程序设计中,软件对象的概念由真实世界对象而来。对象的概念是面向对象软件对象将状态保存在变量或称数据字段里。而行为则借助方法( methods) 为工具来实现 。
data field 1
data field n
method 1
method n
数据 字段 (状态)方法(行为)
软件对象的定义
我们可以对软件对象作以下的定义 ——
data field 1
data field n
method 1
method n
私用数据细节公用方法定义:对象是由数据字段(变量)及相关方法所组成的软件包 (software bundle)
汽车对象
以汽车为例,我们可定义其状态与方法如,
换档开大灯煞车开冷气颜色排 档 数排气量轮胎型号
OOP三大特性:封装、继承与多态
封装
从软件对象的表示图里,我们可以看到对象的核心是由对象的变量所构成。对象的方法包围此核心,使核心对其他的对象是隐藏的,而将对象的变量包裹在其对象方法的保护性监护之下就称之为封装 。 封装用来将对其他对象不是重要的实行细节隐藏起来。就好比你开车换档时,并不须要知道变速箱、齿轮等机械是如何运作的,你只要知道将档排到那里即可。同样在软件程序中,你并不需要知道一个类的完整结构是如何,你只要知道要调用哪一个方法即可。 OOP将数据成员和属于此数据的操作方法,都放在同一个实体或称对象( object) 中,这就是所谓的封装。
封装
封装的用意,是避免数据成员被不正当的存取,以达到信息隐藏的效果,避免错误的存取发生。封装相关的变量及方法到一个软件包里,是一个简单但却很有能力的理念,此法对软件开发者提供了两个主要的好处,
1.模块化,一个对象的原始文件可以独立地被撰写及维护而不影响其他对象。而且对象可以轻易地在系统中来回地传递使用。就好像你借车给朋友,而它仍能正常地运作一样 。
2.信息隐藏,一个对象有一个公开的接口可供其他的对象与之沟通,但对象仍然维持私有的信息及方法,
这些信息及方法可以在任何时间被修改,而不影响那些依赖此对象的其他对象 。
消息 ( message)
单一对象本身并不是很有用处。而通常是成为一个包含许多对象的较大型程序的一个组件时,通过程序中这些对象的交互,程序可以达成更高级的功能以及更复杂的行为,就如汽车自己本身并不会产生行为,而是当你(另一个对象)发动汽车,踩油门
(交互)后,汽车内部就发生一连串复杂的行为 。
软件对象是通过传送消息给其他对象来达到交互及沟通的作用。如下图所示,
data field 1
data field n
method n
method 1
对象 A 对象 B
消 息
有时候接受消息的对象需要更多的信息,以正确地知道要做什么事,例如开车换档时要指明是要换低速或高速档,
一个信息是由 3个元素所组成,
1.信息目标对象(你的汽车 )。
2.执行方法的名字(换档 )。
3.执行方法所需用的参数 (parameters)( 低、高速档 )。
信息的好处
1.一个对象的行为是通过它的方法来表达,所以(除了直接的变量存取外)信息传递已支持所有对象间可能的交互 。
2.对象不需要在相同的程序中,或者相同的机器上送出或接受与其他对象间的信息 。
消息 ( message)
类 ( class)
在真实世界里,有许多同“种类”的对象。而这些同“种类”的对象可被归类为一个“类”。例如我们可将世界上所有的汽车归类为汽车类,所有的动物归为动物类 。
实例 ( instance)
汽车类有些共同的状态
(汽缸排气量,排档数,
颜色,轮胎数 …… )和行为(换档,开灯,开冷气 …… ),但每一台汽车个别的状态及方法可不同于且独立于其他汽车 。
某个人的汽车只是这世界中许多汽车中的一个。
我们就称此汽车对象是汽车类中的一个实例
( instance) 。
软件对象蓝图 —— 类 ( calsses)
关于类与实例的概念,我们需要进一步来探讨。汽车制造商是如何造汽车的?他们会为每一台车设计一种蓝图吗?当然不可能!他们会使用同一个蓝图来制造许多相同的汽车,或者只要稍加修改,即可成为不同“款”的车。这样可大大提高生产效率 。
在面向对象软件设计里,也可以利用此原理来制造一些软件对象蓝图。这些软件对象蓝图就称作类( calsses) 。
定义:一个类就是一个蓝图或原型,定义了通用于一特定种类的所有对象之变量及方法。
data field 1
data field n
method 1
method n
私用数据细节公用方法软件对象蓝图 —— 类 ( calsses)
注意,类表示图没有阴影而对象表示图则有 。
换档开大灯煞车开冷气颜色排 档 数排气量轮胎型号对象(实例)的产生 —— New关键字
类就好比一个蓝图。那实例( instance) 就是从一种类里所产生具有此类的状态(变量)与行为
(方法)的真实对象。在面向对象程序设计里是用,New”这个关键字来产生实例。
类实例 1 实例 2 实例 3
NEW NEW NEW
<蓝图>
<真实对象>
类变量、方法
所谓的类变量 (class variables),类方法 (class methods),指的是用,static”修饰符声明的变量、方法。其地位与类等同。在内存中只有一个地方存放。
不会因实例产生而变动其参考( referance)。 但其数值( value) 却可通过实例来变更。当系统在程序中第一次遇到一个类时,便会拷贝一份所有的类变量(在内存里。然后那些属于此类的实例便分享这些类变量。因此,
你可以借着实例来存取类变量和类方法,而不用将类实例化后才取用 。

new new
实例 2实例 1
类实例 1
实例 2
内存实例变量( 字段 )
实例变量( 字段 )
类变量( 字段 )
实例变量( 字段 )
实例变量、方法
当你从一种类产生一个实例时,就等于制造了一个属于此类类型的对象。而实例产生后,便拥有与类“同类型”但内存地址不同的变量
(除了类变量外),称之为实例变量( instance
variables)。 每个实例各自拥有自己的变量,
而系统就会为这些实例变量个别配置内存。但实例方法却仍留在类里,并不会再复制到实例中。但你仍可以调用这些方法来令实例对象做些事。实例方法指的是那些并没有用 static修饰符的 mathods。 (参考前图 )
继承 (inheritance)
OOP便是以类来定义一个对象的。当我们要使用一个对象(的变量或方法)时,首先要想到它是属于哪一种类的。不仅对象是以类来定义,更进一步地,类也可以其他类来定义,现以下图来说明:
继承
轿车、出租车、巴士都是汽车,故属汽车类,我们称其继承汽车类,而轿车、出租车、巴士也都可自成一种类。这样汽车类就称为超类( superclass),基类( base class) 或父类,而轿车、
出租车、巴士就称作次类( subclass),衍生类( derived class)
或子类 。
从这里我们可发现“汽车类”是比较通用,概念性的类。故在汽车类中定义了一些通用的状态与行为。比如引擎数量,汽缸数,排气量,外观颜色,开大灯,开窗户等,但这些状态与行为可不用实现在汽车类中,而在子类(轿车、出租车、巴士)
中实现。比如“外观颜色”在汽车类中只定义有这样的状态,
而到了出租车类中才实现为“黄色”。这样在超类中只定义一些通用的状态与实现部分的行为,到了次类中才实现细节,我们称此超类为抽象类 (abstract class)。 在抽象类中只定义一些状态,和实现少部分行为。这样其他的程序设计师就可依照他们所要的特定次类进行实现与定义,就像轿车、出租车、巴士都有它们特定的状态与行为,例如像颜色等。
类层级
类继承关系可不仅只是一层关系而已,它可以有好几层。这种树状关系,我们可称作类层级 。 至于层级数可依照实际需要而定 。
R o ot C l a s s
次类继承了超类的变量及方法。如此便可重复使用所有继承之所有超类的变量及方法。
这就是继承的好处。不仅如此,次类不受超类的限制,还可增加自定的变量及方法。例如在出租车中加了一个无线电调用方法,或是在轿车里加了一个卫星导航方法。都是按类特殊的需要来加的。因此我们可发现愈下层的类,其行为愈特别。
继承的产生 —— entends 关键字
类继承关系的产生很简单,是使用 extends 关键字,例如 public class JApplet_1 extends Applet
继承的好处,
1.重复利用超类程序代码。这样在撰写次类时,
只要针对其所需的特别状态与行为来写即可。
提高程序撰写的效率 。
2.先写出定义好却尚未实现的抽象超类,可使得在设计次类时,简化设计时程,只要将定义好的方法填满即可 。
例如方法 init(),start(),stop(),destroy(),paint()
等,均是 Java.Applet 类先定义好的方法。要取用时只要写好内容,用覆盖的方式即执行 。
this与 super关键字
类借着 extends产生上下类关系后,有时候会需要用到超类的方法或变量,有时候会用到本身的方法或变量。有 this与 super来帮助我们。 this
与 super分别指着有继承关系的下、超类。 This
出现在程序代码中,指的是所在的该类对象。
Super指的是所继承的超类对象。 this与 super 均不用先声明即可自行使用 。
方法覆盖
次类想变更超类的方法,则可用覆盖的方式来变更。覆盖的意思是不用考虑超类的方法是如何写的,只要重新定义改写,就可“盖”过去。若与程序性语言做比较,就知它的好处。在程序性语言中,若要修改一个方法,
则需彻底了解此法的每一细节,然后才能增修程序代码,否则牵一发动全身,程序出错的机会很高,但 OOP用覆盖的方式,则可大量降低修改的成本。想改哪一个方法,只要重新写一个类继承原有的类,然后将新方法写在其中(新方法名与原来的相同),
这样就可完全改写超类方法的程序代码 。
method xxx
method xxx
超类次类多态
多态意思是“许多态状态”或“同名异式”的。
在同一个类中可有许多同名的方法,但其参数数量与类型( type) 不同,而且运作
( operation) 过程与回传值( return value) 也可能会不同。在 Java里,多态指的是在运行时间中,可决定使用哪一个多态方法的能力 。
构造函数
类的构造函数是常用的多态之一。构造函数是一种特别的类方法,其命名与类同名,但不具有回传值。可接受多种类型、数量的传入参数,然后做不同的运算。构造函数通常用于初始值设置 。
例如 java.lang包中的 String类里,有多种的构造函数:
String()
String(byte[] bytes)
String(byte[] ascii,int hibyte)
String(byte[] bytes,int offset,int length)
String(byte[] ascii,int hibyte,int offset,int count)
String(byte[] bytes,int offset,int length,String enc)
String(byte[] bytes,String enc)
String(char[] value)
String(char[] value,int offset,int count)
String(String value)
String(StringBuffer buffer)
方法重载
所谓的方法重载,是指调用一个类中具有同名异式方法,但在执行时期才根据其参数数量与类型来判断要调用此方法的哪一种 operation。
这些同名异式方法可按需要自行定义。
范例:继承与上下类
此范例有上下两个类,Account及
CheckingAccount。 其各自定义了一个构造函数与一个方法。下类继承了超类,将参数传给超类并调用超类的方法。超类便以构造函数来处理所传进来的参数,使其成为成员变量
( member variable)。 然后超类的方法便抓取自己的这些成员变量做处理(不是用所传进来的参数)。下类也定义了一个与超类同名的方法,用意是要作方法覆盖( method
overriding),而在此新的方法里可继承原有的方法内容(用 super调用),也可改写或新增指令 。
超类,? public class Account{
int accountNo;
float balance ;
Account(int accountNo1,float balance1){//构造函数
this.accountNo=accountNo1;
this.balance=balance1;
}
public void printAccData(){
System.out.println("帐号 AccountNo,"+accountNo);
System.out.println("余额 Balance,"+balance);
}
public static void main(String args[]){
Account A1=new Account(124654,50000.0f);
A1.printAccData();
}
}
执行结果:
帐号 AccountNo,124654
余额 Balance,50000.0
下类,? public class CheckingAccount extends Account{//继承 Account
int checkCount;
//***构造函数 ***
CheckingAccount(int accountNo2,float
balance2,int checkCount1){
super(accountNo2,balance2);//调用超类的构造函数
this.checkCount=checkCount1;
}
//***覆盖的方法 ***
public void printAccData(){
super.printAccData();/n/调用超类中的方法 System.out.println("新增的指令 ");
System.out.println("checkAccount:="+checkCo
unt);//新增的指令
}
执行结果:
--调用超类的方法 --
帐号 AccountNo,127333
余额 Balance,57730.0
--调用本类的方法 --
帐号 AccountNo,837444
余额 Balance,18730.0
新增的指令
checkAccount:=20
public static void main(String args[]){
System.out.println("--调用超类的方法 --");
Account A1=new Account(127333,57730.0f);
A1.printAccData();//n调用超类的方法 System.out.println("--调用本类的方法 --");
CheckingAccount C1=new CheckingAccount(837444,18730.0f,20);
C1.printAccData();//调用本类的方法
}
}
多重继承
一个对象可以隶属于数种不同的类。例如,一位父亲可以是属于父亲类、丈夫类、公司主管类等。出租车可以是属于营业性汽车类、汽油车类、小型客车类。这种多元性的继承关系就叫做多重继承 。
接口
Java 为了免去 C++ 因着多重继承所衍生的问题,因而限定类的继承只能单一继承。但实际上又有多重继承的需要,故发明了接口( interface ) 来解决问题。
Interface 这个英文字的意思是一种装置或系统,使得不同性质的实体能够交互地运作。比如,计算机的操作系统,可把它称作是一种“人机接口”。可使得使用者(人)与计算机(机),通过鼠标或键盘等装置,而作为一种沟通接口,
来与计算机做沟通,以指挥计算机做事。另外像 RS232串行端口,常作为输入机传输接口,是一种计算机与输入机间的接口装置 。
而在 Java里,设计接口的用意,是可使得类不必受限于单一继承的关系,而可灵活地同时继承一些共有的特性,达到多重继承的目的,而免去 C++中多重继承的复杂关系
像下图中的出租车,可继承“营业车”,“小车”、“汽油车”等类,却不一定要与同样可继承“自用车”、“小车”,“汽油车”的轿车有上下继承的关系 。 汽车类自用车 营业车 小车 大车 汽油车 柴油车轿车 计程车 巴士上 类接口次类接口的实现 —— implements关键字
使用 implements关键字,可使类能同时实现多种的接口,例如,
public class JApplet_1 extends Applet implements
AppletContext,AudioClip;
包 (package )
JAVA将其相关的类及接口组织成一个包( package)。
这样的好处是:你不必将需要用到的类及接口一个个引用进来,你只要用 import关键字引入一个包,便可将此包中所有的接口及类都引用进来 。
包的引用 —— import关键字
举例来说,在 Japplet_1.java中,开头便是引入包
import java.applet.Applet;
import java.awt.Graphics;
包的另一个好处是,在不同包中,可用相同的类名称而不产生冲突 。