Java开发技术大全第三章.docx
- 文档编号:16970199
- 上传时间:2023-07-20
- 格式:DOCX
- 页数:68
- 大小:112.95KB
Java开发技术大全第三章.docx
《Java开发技术大全第三章.docx》由会员分享,可在线阅读,更多相关《Java开发技术大全第三章.docx(68页珍藏版)》请在冰点文库上搜索。
Java开发技术大全第三章
第3章对象和类
在当今的计算机大型应用软件开发领域,面向对象技术正在逐步取代面向过程的程序设计技术。
本章将介绍面向对象的基本知识和Java实现面向对象程序设计的主要工具--类。
如果读者缺乏关于面向对象程序设计的背景,一定要仔细地阅读本章。
如果读者有C++编程经验,也要注意二者之间的区别,毕竟Java在类的具体实现上与C++有较大的差别。
3.1面向对象的基本概念
面向对象(ObjectOriented,OO)是当前计算机界关心的重点,它是90年代软件开发方法的主流。
面向对象的概念和应用已超越了程序设计和软件开发,扩展到很广的范围。
例如,数据库系统、交互式界面、应用结构、应用平台、分布式系统、网络管理结构、CAD技术、人工智能等领域。
谈到面向对象,这方面的文章非常多。
但是,明确地给出"面向对象"的定义却非常少。
最初,"面向对象"是专指在程序设计中采用封装、继承、抽象等设计方法。
可是,这个定义显然不能再适合现在的情况。
面向对象的思想已经涉及到软件开发的各个方面。
例如,面向对象的分析(ObjectOrientedAnalysis,OOA),面向对象的设计(ObjectOrientedDesign,OOD)以及经常说的面向对象的编程(ObjectOrientedProgramming,OOP)。
许多有关面向对象的文章,都只是讲述在面向对象的开发中所需要注意的问题,或所采用的比较好的设计方法。
看这些文章只有真正懂得什么是对象,什么是面向对象,才能最大程度地收获知识。
说明:
在本章中,着重讨论OOP,有关OOA和OOD请读者查阅有关软件工程的书籍。
OOP从所处理的数据入手,以数据为中心而不是以服务(功能)为中心来描述系统。
它把编程问题视为一个数据集合,因为数据相对于功能而言,具有更强的稳定性。
OOP同结构化程序设计相比最大的区别就在于:
前者首先关心的是所要处理的数据,而后者首先关心的是功能。
3.1.1对象
OOP是一种围绕真实世界的概念来组织模型的程序设计方法,它采用对象来描述问题空间的实体。
可以说,"对象"这个概念是OOP最本质的概念之一。
但是,如何给"对象"下一个严谨的定义,却是一个棘手的问题,目前还没有统一的认识。
在现实生活中,一般认为对象是行动或思考时作为目标的各种事物。
对象所代表的本体可能是一个物理存在,也可能是一个概念存在。
例如一枝花、一个人、一项计划等。
在使用计算机解决问题时,对象是作为计算机模拟真实世界的一个抽象,一个对象就是一个物理实体或逻辑实体,它反映了系统为之保存信息和(或)与它交互的能力。
在计算机程序中,对象相当于一个"基本程序模块",它包含了属性(数据)和加在这些数据上的操作(行为)。
对象的属性是描述对象的数据,属性值的集合称为对象的状态。
对象的行为则会修改这些数据值并改变对象的状态。
因此,在程序设计领域,可以用"对象=数据+作用于这些数据上的操作"这一公式来表达。
下面以一个生活中常见的例子来说明对象这个概念。
例如"椅子"这个对象,它是"家具"这个更大的一类对象的一个成员。
椅子应该具有家具所具有的一些共性,如:
价格、重量、所有者等属性。
它们的值也说明了椅子这个对象的状态。
例如,价格为100元,重量为5公斤,所有者是小王等。
类似地,家具中的桌子、沙发等对象也具有这些属性。
这些对象所包含的成分可以用图3.1来说明。
对象的操作是对对象属性的修改。
在面向对象的程序设计中,对象属性的修改只能通过对象的操作来进行,这种操作又称为方法。
比如上面的对象都有"所有者"这一个属性,修改该属性的方法可能是"卖出",一旦执行了"卖出"操作,"所有者"这个属性就会发生变化,对象的状态也就发生了改变。
现在的问题是,所有的对象都有可能执行"卖出"操作,那么如何具体区分卖出了哪个对象,这是需要考虑的。
面向对象的设计思路把"卖出"这个操作包含在对象里面,执行"卖出"操作,只对包含了该操作的对象有效。
因此,整个对象就会变成图3.2这个样子。
图3.1对象的属性集合
图3.2封装了属性和操作的对象
由于对象椅子已经包含了"卖出"操作,因此,当执行"卖出"操作时,对象外部的使用者并不需要关心它的实现细节,只需要知道如何来调用该操作,以及会获得怎样的结果就可以了,甚至不需要知道它到底修改了哪个属性值。
这样做不仅实现了模块化和信息隐藏,有利于程序的可移植性和安全性,也有利于对复杂对象的管理。
3.1.2类
"物以类聚"是人们区分、归纳客观事物的方法。
在面向对象系统中,人们不需要逐个去描述各个具体的对象,而是关注具有同类特性的一类对象,抽象出这样一类对象共有的结构和行为,进行一般性描述,这就引出了类的概念。
椅子、桌子、沙发等对象都具有一些相同的特征,由于这些相同的特征,它们可以归为一类,称为家具。
因此,家具就是一个类,它的每个对象都有价格、重量及所有者这些属性。
也可以将家具看成是产生椅子、桌子、沙发等对象的一个模板。
椅子、桌子、沙发等对象的属性和行为都是由家具类所决定的。
家具和椅子之间的关系就是类与类的成员对象之间的关系。
类是具有共同属性、共同操作的对象的集合。
而单个的对象则是所属类的一个成员,或称为实例(instance)。
在描述一个类时,定义了一组属性和操作,而这些属性和操作可被该类所有的成员所继承,如图3.3所示。
图3.3由类到对象的继承
图3.3表明,对象会自动拥有它所属类的全部属性和操作。
正因为这一点,人们才会知道一种物品是家具时,主动去询问它的价格、尺寸、材质等属性。
对于初学者而言,类和对象的概念最容易混淆。
类属于类型的范畴,用于描述对象的特性。
对象属于值的范畴,是类的实例。
从集合的角度看,类是对象的集合,它们是从属关系。
也可以将类看成是一个抽象的概念,而对象是一个具体的概念。
例如苹果是一个类,而"桌子上的那个苹果"则是一个对象。
从编程的角度看,类和对象的关系可以看成是数据类型和变量的关系。
还可以认为类是一个静态的概念,而对象是一个动态的概念,它具有生命力。
类和对象的关系可以用下面这个实例来演示,如图3.4所示。
(点击查看大图)图3.4类与对象的关系
3.1.3消息
由上述内容可知,对象的行为是通过其定义的一组方法来描述,对象的结构特征是由它的属性来表现。
但是,对象不会无缘无故地执行某个操作,只有在接受了其他对象的请求之后,才会进行某一操作,这种请求对象执行某一操作,或是回答某些信息的要求称为消息。
对象之间通过消息的传递来实现相互作用。
消息一般有3个组成部分:
消息接收者(接收对象名)、接收对象应采用的方法以及方法所需要的参数。
同时,接收消息的对象在执行完相应的方法后,可能会给发送者返回一些信息。
例如,教师向学生布置作业:
"07级计算机1班做5道习题"。
其中,教师和学生都是对象,"07级计算机1班"是消息的接收者,"做习题"是要求目标对象--学生执行的方法,"5道"是要求对象执行方法时所需要的参数。
学生也可以向教师返回作业信息。
这样,对象之间通过消息机制,建立起了相互关系。
由于任何一个对象的所有行为都可以用方法来描述,所以通过消息机制可以完全实现对象之间的交互。
在Java程序设计中,所需完成的功能任务就在对象之间的消息传递与相互作用之间完成。
3.1.4面向对象的4个基本特征
在上述面向对象的基本概念基础之上,不可避免地要涉及到面向对象程序设计所具有的4个共同特征:
抽象性、封装性、继承性和多态性。
1.抽象
抽象是人们认识事物的常用方法,比如地图的绘制。
抽象的过程就是如何简化、概括所观察到的现实世界,并为人们所用的过程。
抽象是软件开发的基础。
软件开发离不开现实环境,但需要对信息细节进行提炼、抽象,找到事物的本质和重要属性。
抽象包括两个方面:
过程抽象和数据抽象。
过程抽象把一个系统按功能划分成若干个子系统,进行"自顶向下逐步求精"的程序设计。
数据抽象以数据为中心,把数据类型和施加在该类型对象上的操作作为一个整体(对象)来进行描述,形成抽象数据类型ADT。
所有编程语言的最终目的都是提供一种"抽象"方法。
一种较有争议的说法是:
解决问题的复杂程度直接取决于抽象的种类及质量。
其中,"种类"是指准备对什么进行"抽象"。
汇编语言是对基础机器的少量抽象。
后来的许多"命令式"语言(如FORTRAN、BASIC和C)是对汇编语言的一种抽象。
与汇编语言相比,这些语言已有了较大的进步,但它们的抽象原理依然要求程序设计者着重考虑计算机的结构,而非考虑问题本身的结构。
在机器模型(位于"方案空间")与实际解决的问题模型(位于"问题空间")之间,程序员必须建立起一种联系。
这个过程要求人们付出较大的精力,而且由于它脱离了编程语言本身的范围,造成程序代码很难编写,而且要花较大的代价进行维护。
由此造成的副作用便是一门完善的"编程方法"学科。
为机器建模的另一个方法是为要解决的问题制作模型。
对一些早期语言来说,如LISP和APL,它们的做法是"从不同的角度观察世界"、"所有问题都归纳为列表"或"所有问题都归纳为算法"。
PROLOG则将所有问题都归纳为决策链。
对于这些语言,可以认为它们一部分是面向基于"强制"的编程,另一部分则是专为处理图形符号设计的。
每种方法都有自己特殊的用途,适合解决某一类的问题。
但只要超出了它们力所能及的范围,就会显得非常笨拙。
面向对象的程序设计在此基础上则跨出了一大步,程序员可利用一些工具来表达问题空间内的元素。
由于这种表达非常普遍,所以不必受限于特定类型的问题。
人们将问题空间中的元素以及它们在方案空间的表示物称作"对象"。
当然,还有一些在问题空间没有对应体的其他对象。
通过添加新的对象类型,程序可进行灵活的调整,以便与特定的问题配合。
所以在阅读方案的描述代码时,会读到对问题进行表达的话语。
与以前的方法相比,这无疑是一种更加灵活、更加强大的语言抽象方法。
总之,OOP允许人们根据问题,而不是根据方案来描述问题。
然而,仍有一个联系途径回到计算机。
每个对象都类似一台小计算机;它们有自己的状态,而且可要求它们进行特定的操作。
与现实世界的"对象"或者"物体"相比,编程"对象"与它们也存在共通的地方:
它们都有自己的特征和行为。
2.封装
封装是面向对象编程的特征之一,也是类和对象的主要特征。
封装将数据以及加在这些数据上的操作组织在一起,成为有独立意义的构件。
外部无法直接访问这些封装了的数据,从而保证了这些数据的正确性。
如果这些数据发生了差错,也很容易定位错误是由哪个操作引起的。
如果外部需要访问类里面的数据,就必须通过接口(Interface)进行访问。
接口规定了可对一个特定的对象发出哪些请求。
当然,必须在某个地方存在着一些代码,以便满足这些请求。
这些代码与那些隐藏起来的数据叫作"隐藏的实现"。
站在过程化程序编写(ProceduralProgramming)的角度,整个问题并不显得复杂。
一种类型含有与每种可能的请求关联起来的函数。
一旦向对象发出一个特定的请求,就会调用那个函数。
通常将这个过程总结为向对象"发送一条消息"(提出一个请求)。
对象的职责就是决定如何对这条消息作出反应(执行相应的代码)。
若任何人都能使用一个类的所有成员,那么可对这个类做任何事情,则没有办法强制他们遵守任何约束--所有东西都会暴露无遗。
有两方面的原因促使了类的编制者控制对成员的访问。
第一个原因是防止程序员接触他们不该接触的东西--通常是内部数据类型的设计思想。
若只是为了解决特定的问题,用户只需操作接口即可,无需明白这些信息。
类向用户提供的实际是一种服务,因为他们很容易就可看出哪些对自己非常重要,以及哪些可忽略不计。
进行访问控制的第二个原因是允许库设计人员修改内部结构,不用担心它会对客户程序员造成什么影响。
例如,编制者最开始可能设计了一个形式简单的类,以便简化开发。
以后又决定进行改写,使其更快地运行。
若接口与实现方法早已隔离开,并分别受到保护,就可放心做到这一点,只要求用户重新链接一下即可。
封装考虑的是内部实现,抽象考虑的是外部行为。
符合模块化的原则,使得软件的可维护性、扩充性大为改观。
3.继承
继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。
对象的一个新类可以从现有的类中派生,这个过程称为类的继承。
新类继承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类)。
派生类可以从它的基类那里继承方法和实例变量,并且派生类可以修改或增加新的方法使之更适合特殊的需求。
这也体现了大自然中一般与特殊的关系。
继承性很好地解决了软件的可重用性问题。
比如说,所有的Windows应用程序都有一个窗口,它们可以看作都是从一个窗口类派生出来的。
但是有的应用程序用于文字处理,有的应用程序用于绘图,这是由于派生出了不同的子类,各个子类添加了不同的特性。
关于继承的详细讨论,将在第4.1~4.2节进行。
4.多态性
多态性是指允许不同类的对象对同一消息作出响应。
比如同样的加法,把两个时间加在一起和把两个整数加在一起肯定完全不同。
又比如,同样的选择"编辑"、"粘贴"操作,在字处理程序和绘图程序中有不同的效果。
多态性包括参数化多态性和运行时多态性。
多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好地解决了应用程序函数同名问题。
关于多态性的讨论,将在4.7~4.9节进行。
最后,以AlanKay的话作为本节的结束语。
他总结了Smalltalk(这是第一种成功的面向对象程序设计语言,也是Java的基础语言)的五大基本特征。
通过这些特征,读者可以理解"纯粹"的面向对象程序设计方法。
(1)所有东西都是对象。
可将对象想象成一种新型变量,它保存着数据,但可要求它对自身进行操作。
理论上讲,可从要解决的问题上,提出所有概念性的组件,然后在程序中将其表达为一个对象。
(2)程序是一大堆对象的组合。
通过消息传递,各对象知道自己该做些什么。
为了向对象发出请求,需向那个对象"发送一条消息"。
更具体地讲,可将消息想象为一个调用请求,它调用的是从属于目标对象的一个子例程或函数。
(3)每个对象都有自己的存储空间,可容纳其他对象。
或者说,通过封装现有对象,可制作出新型对象。
所以,尽管对象的概念非常简单,但在程序中却可达到任意高的复杂程度。
(4)每个对象都有一种类型。
根据语法,每个对象都是某个"类(Class)"的一个"实例"。
其中,"类"是"类型(Type)"的同义词。
一个类最重要的特征就是"能将什么消息发给它?
"。
(5)同一类所有对象都能接收相同的消息。
这实际是别有含义的一种说法,读者不久便能理解。
由于类型为"圆(Circle)"的一个对象也属于类型为"形状(Shape)"的一个对象,所以一个"圆"完全能接收"形状"的消息。
这意味着可让程序代码统一指挥"形状",令其自动控制所有符合"形状"描述的对象,其中自然包括"圆"。
这一特性称为对象的"可替换性",是OOP最重要的概念之一。
3.2类与对象
3.1节从理论角度阐述了对象和类的有关概念。
类是组成Java程序的基本要素,在Java中有两种类:
一种是在JDK中已经设计好的类,可以在程序中直接使用;另一种需要程序员根据现有的任务,自行设计和编写,这被称为用户自定义类。
本节将着重介绍在Java中如何来自定义类以及如何使用这些类。
3.2.1类的基本结构
在前两章的例子中已经定义了一些简单的类,如HelloWorldApp类:
publicclassHelloWorldApp{
publicstaticvoidmain(Stringargs[]){
System.out.println("HelloWorld!
");
}
}
这个类虽然非常简单,但仍然可以看出,一个类的实现至少包含两部分的内容:
classDeclaration{//类的声明
classBody//类体
}
下面分别对每一部分详细讲述。
3.2.2类的声明
一个最简单的类声明如下:
classclassName
其中,class是关键字,用于定义类。
classname是类的名字,它必须遵循用户标识符的定义规则。
例如:
classPoint
同时,在类声明中还可以包含类的父类(超类),类所实现的接口以及访问权修饰符、abstract或final,所以更一般的声明如下:
[类修饰符]class类名[extends父类名][implements接口名列表]
class、extends和implements都是关键字。
类名、父类名和接口名列表都是用户标识符。
父类。
新类必须在已有类的基础上构造,原有类即为父类,新类即为子类。
Java中的每个类都有父类,如果不含父类名,则默认其父类为Object类。
接口。
接口也是一种复合数据类型,它是在没有指定方法实现的情况下声明一组方法和常量的手段,也是多态性的体现。
修饰符。
规定了本类的一些特殊属性,它可以是下面这些关键字之一:
final--最终类,它不能拥有子类。
如果没有此修饰符,则可以被子类所继承。
abstract--抽象类,类中的某些方法没有实现,必须由其子类来实现。
所以这种类不能被实例化。
如果没有此修饰符,则类中间所有的方法都必须实现。
public--公共类,public表明本类可以被所属包以外的类访问。
如果没有此修饰符,则禁止这种外部访问,只能被同一包中的其他类所访问。
final和abstract是互斥的,其他情况下可以组合使用。
下面声明了一个公共最终类,它同时还是Human的子类,并实现了professor接口:
publicfinalclassTeacherextendsHumanimplementsprofessor
3.2.3创建类体
类体中定义了该类所有的变量和该类所支持的方法。
通常变量在方法前面部分定义(并不一定要求)。
方法分为构造方法和成员方法,如下所示:
publicclassclassName{
[memberVariableDeclarations]//定义成员变量
[constructorDeclarations]//定义构造方法
[methodDeclarations]//定义成员方法
}
从以上代码中可以看出,类体中有三个部分:
成员变量、成员方法和构造方法。
其中,成员变量又被称作属性,成员方法又被称作行为,二者也可统称为类的成员。
下例定义了一个Point类,并且声明了它的两个变量x、y坐标。
定义了一个成员方法move(),可以改变x、y的值。
定义了一个构造方法Point(),可以为x、y赋初值。
【例3.1】一个简单的Point类。
//-----------文件名Point.java,程序编号3.1------------------
publicclassPoint{
intx,y;
Point(intix,intiy){
x=ix;
y=iy;
}
voidmove(intix,intiy){
x+=ix;
y+=iy;
}
}
类中所定义的变量和方法都是类的成员。
对类的成员可以规定访问权限,来限定其他对象对它的访问。
访问权限有以下几种:
private、protected、pubic和friendly,这些将在3.3节中详细讨论。
同时,对类的成员来说,又可以分为实例成员和类(静态)成员两种,这些将在3.8节中详细讨论。
至此,类完整的声明和定义形式已经全部给出。
注意类体定义中,并非每个部分都需要。
一个最简单的类可能是下面这个样子,它什么事情也不能做。
classempty{
}
3.2.4对象的生命周期
定义类的最终目的是要使用它。
一般情况下,要使用类需要通过类的实例--对象来实现。
在定义类时,只是通知编译器需要准备多大的内存空间,并没有为它分配内存空间。
只有用类创建了对象后,才会真正占用内存空间。
Java的对象就像有生命的事物一样,也要经历三个阶段:
对象的创建,对象的使用,对象的清除。
这被称为对象的生命周期。
对象的生命周期长度可用如下的表达式表示:
T=T1+T2+T3
其中,T1表示对象的创建时间,T2表示对象的使用时间,而T3则表示其清除时间。
由此可以看出,只有T2才是真正有效的时间,而T1、T3则是对象本身的开销。
3.2.5对象的创建
创建一个对象也被称为实例化一个类。
它需要用到下面的语句:
类名对象名=new构造方法名([参数列表]);
例如,使用例3.1的Point类来创建一个对象p:
Pointpt=newPoint(1,2);
该语句创建了一个新对象,它的名字叫做pt。
new是Java的关键字,用于创建对象。
表达式newPoint(1,2)创建了一个Point类的实例对象,它同时指明坐标值为(1,2)。
对象的引用被保存在变量pt中。
它虽然是一条语句,但实际上包括三个步骤:
声明,实例化,初始化。
一般情况下,创建和使用对象都要经过这三个步骤。
1.声明对象
对象的声明和基本类型的数据声明在形式上是一样的:
类名对象名;
对象名也是用户标识符,和基本类型的变量遵循同样的命名规则和使用规则。
例如,声明一个Point类型的变量pt:
Pointpt;
和C++不同,Java中像上面这样声明一个变量,并不会分配一个完整的对象所需要的内存空间,这一点也和简单数据类型的变量不同。
它会将pt看成是一个引用变量,并为它分配所需内存空间,它所占用的空间远远小于一个Point对象所需要的空间。
如此处理,使得Java中声明一个对象的消耗很小,但也有一个副作用,就是对象不能马上使用,还需要对它进行实例化。
2.实例化对象
Java中使用new关键字创建一个新对象,即进行实例化。
格式如下:
new构造方法([参数列表])
实例化的过程就是为对象分配内存空间的过程,此时,对象才成为类的实例。
new所执行的具体操作是调用相应类中的构造方法(包括祖先类的构造方法),来完成内存分配以及变量的初始化工作,然后将分配的内存地址返回给所定义的变量。
使用new创建Point对象的示例如下:
pt=newPoint(1,2);
用new来创建对象需要比较大的时间开销,远远比声明一个对象的消耗要大得多。
一些常见操作的时间消耗如表3.1所示。
表3.1一些操作所耗费时间的对照表
运算操作
示例
标准化时间
运算操作
示例
标准化时间
本地赋值
i=n
1.0
新建对象
newObject()
980
实例赋值
this.i=n
1.2
新建数组
newint[10]
3100
方法调用
Funct()
5.9
从表3.1中可以看出,新建一个对象需要980个单位的时间,是本地赋值时间的980倍,是方法调用时间的166倍,而若新建一个数组所花费的时间就更多了。
3.初始化对象
当一个对象生成时,通常要为这个对象确定初始状态,这个过程就是对象的初始化。
由于创建对象是通过类及其祖先类的构造方法来进行的,所以初始化工作也会在这里完成。
注意到前面的这个说明:
类名对象名=new构造方法名([参数列表]);
其中的参数列表就是传递给构造方法的一些值。
构造方法获得这些值后,就可以为成员变量赋初始值。
比如:
Pointpt=newPoint(1,2);
它会将成员变量x和y分别赋值为1和2。
Java还规定,如果成员变量没有被显示地赋初值,系统将自动为它们赋初值。
具体规定为:
所有的简单变量除boolean类型外均赋值为0,boolean类型赋值为false,
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Java 开发 技术 大全 第三