第 7章 面向对象技术导论
面向对象 (Object-Oriented)技术体现了计算机程序设计的一种思想,这种技术体现在具体的开发语言中,如 Java语言。一种语言完全或部分的以面向对象的思想设计和实现就称该语言为面向对象的计算机程序开发语言。
本节既然是导论,目的是希望读者对面向对象编程具有初步认识,当然这需要具体内容来介绍。面向对象技术主要体现在面向对象的思想,进而讨论类和对象(类的实体),
而继承、多态、封装又是面向对象思想不可替代的优势体现,所以本章将对面向对象的主要内容做细致的讲解,该章是面向对象程序设计的基础,具有抽象性的特点,但是只有确实理解和把握了这些思想才能更好的利用 Java语言进行程序设计和代码的编写。
7.1 万事万物皆对象
在以往的程序开发语言如 C语言中,整个程序是过程式的。面向对象的思想出现的比较早。在 80年代软件开发方面面向对象技术再次成为研究的热点,其中,Booch,Coad/Yourdon,Jacobson在面向对象的研究中获得了业界的广泛认可。尤其是统一建模语言综合了 Booch,Coad/Yourdon、
Jacobson的各自优点,并且吸收了许多工程实践经验的理念和技术,成为 OMG面向对象方法的标准。
应用到计算机编程领域后,它的突出优势体现在对象概念上。这种把万物抽象化为对象的思想,
符合人类对事物理解的思维方式,把这种思维方式应用到计算机程序设计上可以流畅的表达程序员的思想,简化系统的分析和设计。
7.1.1 程序设计中的抽象化认识
我们日常生活中的对象( Object)可以是具体的实物,如桌子、灯泡、电视等。也可以是抽象的事物,如一个想法。
抽象的事物摸不着、看不到,但是人类的思想可以感受到,
最终它可以通过某种方式体现出来,所以抽象和具体是事物的两端,是人类智慧的体现。
计算机程序设计在程序员角度讲就是对待解决问题的建模,
这种建模的过程就是对问题域进行抽象化认识的过程。将问题空间中诸元素表示成对象。将对象的行为描述为具体的实现方法,把对象的静态特性描述为具体的静态属性。
程序可以根据特定的问题领域而灵活的添加新类型的对象。
因此在理解分析待求解的问题时也就完成了对问题的抽象化认识,把抽象化的结果用面向对象的程序设计语言实现。
在面向对象的程序设计中,对象无处不在。整个程序是由功能各异的对象组成的。对象间通过消息通信,协调完成一系列任务。
7.1.2 如何获得和操控对象
Java语言本身是一种面向对象的程序设计语言。
所以采用该语言进行程序设计之前必须接纳并理解面向对象的思想。而一旦转换到 OO的编程语言中,将极大地提高编程能力和编程效率。 7.1.1节讲过,面向对象的程序全部由对象组成,对象无处不在,对象之间相互通信,互相协调完成软件的功能要求。
那么在 Java中如何操纵对象呢? Java给出了统一的对象操控方式即采用对象的“引用”,通过引用来操控对象,如修改其属性、向对象发消息、
调用对象的行为等。我们以一个具体的
Employee(雇员类 )类说明如何获得和操控对象。
7.1.3 对象的存储空间
理解了程序设计中的抽象概念,把握了如何获得和操控对象后,有必要知道程序运行时,对象在内存中是如何存储的。
首先分析一下对象相关的哪些内容需要存放,这里介绍的流对象和持久化对象在以后的章节还会讲解,这里读者知道存在这样的东西,关键是知道它们对应的存储空间。
引用:在 7.1.2节介绍了引用,引用是操控对象的一个句柄,就如用遥控器操纵电视一样,此时遥控器是引用,电视是电视类的一个具体实体 。 这样引用就可操控对象的属性和行为了 。
Java对象,Java对象是通过 new关键字创建的一个类实体,这个对象不是像类定义那样是个概念性的东西,而是具体的可以操控的实体了 。
静态数据:静态数据是指由 static关键字修饰的数据,如 static float
rate = 0.523;这类数据存储在内存中的固定位置 。 当然 static关键字即可以修饰类,也可以修饰方法,但这些 static类和方法存放的地点与静态数据不同 。
常量数据:常量数据是在整个程序中永远不会改变的数据 。
流对象和持久化对象,Java程序中的对象一般实在程序运行时存在的,可以随时供系统调用,这些对象存活于程序之中 。 但 Java存在一种存活于程序之外的对象或数据,可以不受运行时刻程序控制,而独立存在 。 典型的是,流对象,和,持久化对象,。 二者都把对象存储在磁盘上,并保存了当时对象的状态 。 在需要该对象时,可以通过某种方式生成常规的对象 。
7.1.4 对象的生存空间
人类的生命是有限的。一个人类对象也就是一个具体的人在生命周期内可以完成很多事情。而在生命周期外则无能为力了。每个具体生命不会无休止的生活在这个世界上,不然地球的各种资源也无法承受。每个生命个体在离开这个世界时得到适当的清理。对于程序中的对象也有类似的问题。
Java对象具有生命周期,在生命周期内系统可以随时调用。但这样的对象不可能全部存活于内存中,不然很快就会耗尽内存资源。所以,系统会适当销毁一些暂时不用的对象,以释放空间给新的对象使用。
7.2 一种新的数据类型:类( Class)
类是面向对象思想的重要概念。其实,面向对象程序设计的本质就是类的设计,在分析问题域后,
抽象出适当的类,完成类的属性、行为和类间的通信接口设计,从而完成一个软件系统。类也是
Java中的一种数据类型。 本节重点讲解类的组成成份,辅助介绍其他相应的构件。
7.2.1 类( class)概述
在 Java中万物皆对象 。 一个对象必定区别于另一个对象而成为自己 。 对象具有静态属性和动态行为 。 其实,正是这些静态属性和行为是一个对象区别于另一个对象的本质 。 但对象具有一定的外观,正如人的名字一样 。 所以从外在看,一个对象可从命名的角度区别于另一个对象,而内在是对象的属性和行为上有区别 。
Java使用 class关键字命名类,在关键字 class后书写类名 。 如:
class ClassName { }
这样就定义了一个类类型,此时类主体 { }内什么也没有 。 所以,该类不能完成任何任务 。 但它已经是符合 Java规范定义的类了 。 可以生成该类的对象,并且不受对象数目的限制 。
ClassName newClass = new ClassName();
显然这个对象是不能做任何事情的,因为类主体内什么也没定义,没有静态的属性,也没定义合适的方法。下节将介绍类的属性和方法。
7.2.2 类的属性详解
在 Java程序设计中所有的工作就是定义类。定义一个类就需要向类的主体内增加两种元素,一是属性、一是方法。本节重点讲述类的属性。
属性是说明对象的静态属性的。如汽车类,该类的对象具有某些共有的属性,如车的颜色、品牌、加速度、外观款式等。这些可以从静态的角度描述这类事物。类的属性可以是内置数据类型,如 int型,byte[]型,boolean型,char型等。也可以是类类型。假设已经定义了类 Worker类,下面定义一个 Car类。
在 Car类中把 Worker类对象引用作为其一个属性。
class Car {
String color;
float velocity;
char style;
Worker worker;
}
7.2.3 类的方法详解
这里从两个方面详细地介绍方法,一个是方法概述、一个是参数和返回值。
( 1)方法概述,若想让对象可以做些事情,就必须定义对象的方法( method)。在过程式语言中用函数来表述一个子程序,这里的函数功能和 Java中对象的方法是异名同工。
不过既然 Java规范中定了对象的行为是方法,本书就沿用规范的概念。
( 2)参数和返回值,方法的参数是在对象调用此方法或向对象发送消息时,需要向方法提供的信息,方法可以把提供的信息经过方法主体的处理从而完成方法的功能。参数要求有参数类型和参数引用,其实说到引用读者或许有疑问,引用是相对于类对象而言的,引用是操控对象的句柄。
其实,Java中类方法中的参数就是对象形式的。传入参数的对象类型必须和参数要求的对象类型相同。
7.2.3 一种特殊的方法 -类的构造函数
构造函数是类的一种特殊方法 。 该方法的作用是在类的实例化过程中初始化一些参数,如在界面编程中,会在类的构造函数中初始化用户界面控件,完成界面元素的布局等 。 有的类具有多个构造函数,多个构造函数之间参数不同,可以完成不同参数条件下的对象实例化操作 。
Java提供默认的构造函数,如果没有为类设计自己的构造函数,编译器会自动为该类添加一个构造函数 。 这也是该构造函数与普通方法的区别 。 代码缺省构造函数示例 。
代码 缺省构造函数示例
1 //定义一个类 Tree
2 class Tree {
3 int height ; //声明树的高度变量 。
4 }
5 //定义一个类 DefaultConstructor 该类生成一个 Tree类的对象
6 Public class DefaultConstructor{
7 Public static void main(String args[]){
8 //创建类 Tree的对象,调用了默认构造函数 Tree();
9 Tree tree = new Tree();
10 }
11}
7.2.4 关键字 static
在 Java中经常会看到 static关键字修饰的数据或方法,static
是静态的意思,表示该数据或对象在内存中只有一份。 Static
关键字可以修饰数据、方法和类。其实 Static可以修饰任何类型的数据,这里进行分类使读者可以更清晰地理解其用法。
1,static关键字修饰内置数据,Static关键字修饰内置数据的格式是在数据声明前放置关键字 static,修饰一个浮点型数据。
Class StaticFloatTest{
static float rate = 1.12f;
}
2,static关键字修饰方法,方法是类的组成成分,也就是说类把方法包裹起来,一般在调用对象的方法时首先需要 new一个对象,产生该对象的实例,再通过对象引用来调用属性数据或调用方法(也称为向对象发消息)。然而使用 static修饰的方法可以直接用该类调用。访问格式是:
className.staticMethod();
7.2.5 关键字 this
this是 Java的一个关键字 。 一旦创建一个对象实例,虚拟机就为该对象创建一个默认的指向自己的指针 。 this只能用在方法中,就是指当前对象 。 为了说明
this指针就是指向,这个对象,,给出一个示例,该示例中定义一个方法在该方法中使用 this调用对象的属性信息和方法成员 。 代码 this指针的使用 。
代码 使用 this关键字示例 1
1 class ThisTest1 {
2 private String name ;
3 private void thisRoo(){/*方法主体 */}
4 private void thisFoo(String name){
5 this.name = name; // 为类属性 name赋值,参数名也为 name
6 }
7 private void thicCoo(){
8 this.thisRoo() ;
//在方法 thisCoo()中调用该类的方法 thisRoo()
9 }
10 }
7.3 访问权限
面向对象技术的一个特点就是封装,把数据和方法放在一个类的内部,而对使用该类的用户只开放必要的接口,对敏感数据或不需要用户知道的数据和方法则隐藏在类的内部,外部用户不可见。这样就把类的创建者和类的使用者之间隔离开,类的创建者隐藏了部分细节而只公开用户需要的部分,这样只要类对外(这里的对外是指使用类的用户可见的部分)公开的接口不变,无论类的创建者如何改变类内隐藏的方法或数据结构,都不会影响用户的使用。
因此设置访问权限的一个原因就是让使用类的用户只能操控类的设计者允许的内容,而不能触及对使用者没用的部分。这部分数据属于类的内部操作,对用户来讲这些操作不能提供直接的服务,不是解决特定问题所需的接口。
7.3.1 包( package)
包是一种类的集合,为程序的开发提供了良好的接口或现成的工具类,本节介绍有关包的基本概念和如何自定义包,
实现程序员自己定义的工具类集合。
1.包概述,包( package)是类的集合,程序员既可以自己编写工具制作成包来使用,也可以使用系统提供的类库,
这些类库也是以包的形式出现。只要用户在程序的开始处标记,导出相应的工具包。用户就可以方便的调用包中的工具类。
2.包名的约束,可以想象如果在两个包具有相同的名称,
编译器根本无法辨别到底使用哪一个,此时编译器就会提示错误。所以如何避免包名称的重复是必须认真对待的问题。设置包名字的宗旨就是保证包名的独一无二。
7.3.2 设置 Java访问权限
Public,protected和 private是 java设置访问权限的修饰词。使用方式都是把该关键字放在需要修饰的成分前,不论该成分是属性还是方法。如:
1 public Sting username;
2 private static int RATE = 0.77;
3 protected void setUserName(String username){
4 this.username = username;
5 }
显然,无论是在属性或方法前,关键字仅仅修饰其后定义的成分。当然用户也可以不写访问权限的修饰词,此时是默认的包访问权限。下面一次介绍 Java的访问权限关键字 。
7.4 继承
继承是面向对象编程的重要组成部分,也是 Java
语言的重要成分。继承是很好理解的一个概念。
在现实生活中如儿子继承父亲的家业,从而利用父亲的资源发展自己的事业,实现了资源利用,
避免了自己去打拼,这便是现实生活中的继承概念。生活中的继承概念可以与 Java程序设计中的继承概念相类比,这样有助于理解继承概念。通过继承最大的好处就是“代码重用”(继承资源),提高代码编写效率。 Java的继承与 C++不同,
Java不支持多继承只支持单继承。下面就如何引入继承、如何实现继承等问题继续介绍 Java的继承。
7.4.1 什么是继承
继承是面向对象程序设计的基本特点之一,通过继承使得代码重用得以实现,程序员自己编写的设计良好的类,可以制作成类库即 Jar文件,这样这些类不但可以供自己使用也可以通过释放接口给其他程序员使用,如 Java就设计了功能强大的类库,通过继承方式使用户可以很好地实现所需功能,极大的提高了编程效率。如果用户需要实现多线程程序,在 Java中只需要继承 Thread类就可以了,其他与操作系统相关的操作都交给父类去实现,子类只需要实现父类的 run()方法,把需要多线程实现的操作放在该方法中。
其实,当用户创建一个类时,总是在使用继承,
因为 Java默认所有用户创建的类继承自 Object。
7.4.2 如何实现继承
Java提供了 extends关键字实现继承的语法。在继承过程中,需要首先声明一个类继承另一个类,即继承自父类。在子类名称的右边紧跟 extends关键字,随后是父类的名称。一旦完成上述操作,
子类会自动获得父类的所有属性和方法。为了说明继承的使用方式,和一些实现细节,设计了一个父类,如代码所示:
7.4.3 super关键字
在继承中,如果子类需要覆盖父类的某个方法,同时还需要调用父类中同名的方法,此时如果直接调用该方法,显然会出现循环。
所以,Java给出 super关键字来解决该问题。示例程序如代码所示。
7.5 多态
多态是面向对象程序设计的组成部分,多态和面向对象的其他技术结合在一起,它不单独存在,
而是同数据抽象和继承技术结合使用。能多态的概念简单的说就是一个事物可以完成多项功能。
在 C++中,多态就是使用基类的指针指向派生类的对象,看起来同样的指针来调用同样的函数却产生了不同的行为能力,即实现了多态。
7.5.1 什么是多态
首先通过一个例子获得对多态的直观认识。该程序首先设计一个基类,三个派生类(或叫做子类),基类和派生类中都设计了方法 draw(),但是不同的子类该方法的具体实现是不同的,都具有自己的特征行为。我们在实现类中通过实现类对象调用了该类中的方法 foo(),虽然设计该方法时,其参数为基类 Shape对象,但是,我们传入的是子类的对象,该函数照样正常运行,并得到我们预期的结果,分别调用了子类自己的 draw()方法,这就是多态的具体体现。示例程序如代码多态示例所示。
7.5.2 方法的重载
方法重载的作用是在子类中对于在父类中出现的方法赋予新的行为能力。从重载的作用可以看到,
方法的重载发生在子类中,并且是对父类中出现的方法赋予新的生命。这里暂不讨论重载的缺陷,
通过一个例子的分析重载的注意事项。该示例有一个父类三个子类,父类拥有两个方法,即
setColor()和 draw(),其中前者具有 private访问权限,后者具有 public访问权限,我们的目的是在子类中通过重载来实现子类和父类同名的且具有不同行为的方法,从而实现多态,但是这里会出现问题,在阅读了代码后,我们通过编译该程序看代码的执行结果。
7.5.3 抽象类和抽象函数
Java提供了一种抽象类机制 。 我们先给出抽象类的实现和相关语法,再给出 Java设计该机制的初衷 。
如果一个类包含抽象方法 ( abstract method),该类就是抽象类,但抽象类不一定必须具有抽象方法 。 而抽象方法是一个不完整的方法,它没有声明方法体,通过关键字
abstract声明抽象方法 。 如下所示是抽象方法的声明语法:
Abstract void methodname();
具有抽象方法的类就是抽象类,这里说明只要有一个方法是抽象方法该类就是抽象类。抽象类可以被子类继承,但是子类必须实现抽象类的所有抽象方法,否则该子类也是抽象类,并且编译器会要求使用 abstract关键字修饰该子类,抽象类也不能产生对象,即通过 new关键子无法创建新的抽象类对象。
7.6 接口
在上节学习了抽象类和抽象方法,在抽象类中允许一部分方法有自己的定义或实现,而抽象方法则没有提供具体的方法实现,需要其子类去实现。
相对于抽象类而言,接口是更纯粹的抽象类,它的所有方法没有提供任何实现,只是给出方法的声明,接口一旦被实现( implements)就象普通类一样使用了,但是必须实现其声明的全部方法,
另外接口可以定义属性,也可以为属性赋予初始值。 Java使用关键字 interface创建一个接口。本节将介绍接口的定义、语法实现和相应的注意事项。
7.6.1 接口定义
接口是 Java定义的一个更加纯粹的抽象类,它声明的方法只有返回类型,方法名和方法参数,但是没有函数体,这些方法必须声明为 public的,即使不声明为 public的,接口中方法的默认访问属性也是 public的 。 代码定义了一个接口 。
代码接口定义程序示例
1 //通过关键字 interface声明接口
2 interface Shape{
3 String shapetype =,hello” ;
4 Int number = 1000;
5 public void draw();
6 public void setColor();
7 }
7.6.2 接口和抽象类
接口比抽象类更纯粹,这种纯粹体现在其方法只有声明却没有具体实现,而且接口中所有的方法都是如此,如果在接口中定义的方法有函数体,则编译器会报错。而抽象类的方法可以有函数体,只有抽象方法才没有函数体,只有函数声明。为便于对比给出两个例子。
接口的定义:
interface Shape{
String shapetype =,hello”;
Int number = 1000;
public void draw(); //如果定义了函数体,则编译时会出错,因为和接口的定义不符
public void setColor();
}
7.6.3 接口的使用
一旦接口按照定义实现,就可以象普通类一样的使用,可以被实现,可以创建接口引用等。代码
7-20首先定义了一个接口,随后定义了三个类实现该接口。下面是接口的结构图,通过该关系可以很好的掌握类和接口的层次关系。如图所示为接口与实现类的层次结构图。
7.7 本章习题
( 1)学完本章后,读者需要回答:
1.如何理解 Java中的对象概念?
2.解释对象实例和对象引用的关系。
3.解释 Java对象、静态数据、常量对应的存储空间。
4.如何理解类的定义,类包含的基本元素是什么?
5,在 Java中 this关键字的作用。
6,解释包的含义。
7.解释类中的函数具有 public,private和 protected关键字的含义。
8.解释继承的概念,给出一个示例程序。
9.理解 super关键字的含义?
10,多态是如何定义的?
11.解释多态中的方法重载。
12.抽象函数的作用。
13.抽象类与接口的区别,二者的使用环境如何?
7.7 本章习题
( 2)注意事项
1.本章需要读者理解对象的概念,这是面向对象编程语言的基本概念,是核心内容,理解对象的实例、引用、存储空间和生存空间的概念。
2.在理解类的定义时关键是理解类的构成,即属性和方法,其中属性说明类的静态属性而方法说明类的动态属性。
3.虽然继承是面向对象编程的基本概念,但是建议读者在写程序时最好少使用多态,在熟练掌握了 Java编程语言后再区分使用多态和组合的区别。