1、Java面向对象设计类和对象理解面向对象程序设计面向对象程序(Object-oriented programming, OOP)设计是继面向过程又一 具有里程碑意义的编程思想,是现实世界模型的自然延伸。下面从结构化程序 设计说起,逐步展示面向对象程序设计。结构化程序设计简介早期的程序设计,大量使用共享变量(全局变量)和GOTO语句,这使得代码结 构比较混乱,不容易改错和复用,后来有人证明所有的有意义的程序流程都可 以使用顺序、选择和循环来实现,并由此提出结构化程序设计。其概念最早由在1965年提出的,是软件发展的一个重要的里程碑。它的主要图中座椅设计图就是“类”,由这个图纸设计出来的若干的座椅
2、就是按照该类 产生的“对象”。可见,类描述了对象的属性和对象的行为,类是对象的模 板。对象是类的实例,是一个实实在在的个体,一个类可以对应多个对象。可 见,如果将对象比做座椅,那么类就是座椅的设计图纸,所以面向对象程序设 计的重点是类的设计,而不是对象的设计。一个类按同种方法产生出来的多个对象,其开始的状态都是一样的,但是修改 其中一个对象的时候,其他的对象是不会受到影响的,例如,修改第1把座椅(如锯短椅子腿)的属性时,其他的座椅是不会受到影响。再举一个例子来说明类与对象的关系。17世纪德国著名的哲学家、数学家莱布 尼茨(Leibniz, 1646年一1716年)曾有个著名的哲学论断:“世界上
3、没有两 片完全相同的树叶。”这里,我们用“类”与“对象”的关系来解释:类相同 它们都叫树叶,而对象各异一一树叶的各个属性值(品种、大小、颜色 等)是有区别的,如上图所示。从这个案例也可以得知,类(树叶)是一个抽 象的概念,它是从所有对象(各片不同的树叶)提取出来的共有特征描述。而 对象(各片具体的不同树叶)则是类(树叶这个概念)的实例化。类的声明与定义在使用类之前,必须先声明它,然后才可以声明变量,并创建对象。类声明的 语法如下。可以看到,声明类使用的是class关键字。声明一个类时,在class关键字后 面加上类的名称,这样就创建了一个类,然后在类的里面定义成员变量和方 法。在上面的语法格式
4、中,标识符可以是public、private、protected或者完全省 略这个修饰符,类名称只要是一个合法的标识符即可,但从程序的可读性方面 来看,类名称最好是由一个或多个有意义的单词连缀而成,每个单词首字母大 写,单词间不要使用其他分隔符。类的标识符可以是访问控制符。Java提供了一系列的访问控制符来设置基于类(class)、变量(variable)、方法(method)及构造方法(constructor) 等不同等级的访问权限。Java的访问权限主要有4类。默认模式(default)。在默认模式下,不需为某个类、方法等不加任何访 问修饰符。这类方式声明的方法和类,只允许在同一个包(pa
5、ckage)内是可访 问的。private (私有)。这是Java语言中对访问权限控制最严格的修饰符。如果 一个方法、变量和构造方法被声明为“私有”访问,那么它仅能在当前声明它 的类内部访问。类和接口 (interface)的访问方式是不能被声明为私有的。public (公有)。这是Java语言中访问权限控制最宽松的修饰符。如果一 个类、方法、构造方法和接口等被声明为“公有”访问,那么它不仅可以被跨 类访问,而且允许跨包访问。如果需要访问其他包里的公有成员,则需要事先 导入(import)那个包含所需公有类、变量和方法等的那个包。protected (保护)。介于public和private之
6、间的一种访问修饰符。如 果一个变量、方法和构造方法在父类中被声明为“保护”访问类型,只能被类 本身的方法及子类访问,即使子类在不同的包中也可以访问。类和接口(interface)的访问方式是不能声明为保护类型的。类的标识符除了上述的4个访问控制符,还可以是finale关键字“final”有 “无法改变的”或者“终态的”含义。一个类一旦被声明为final,那这个 final类不能被继承,因此final类的成员方法没有机会被覆盖,默认情况下 类都是default的。在设计类时候,如果这个类不需要有子类,类的实现细节 不允许改变,并且确信这个类不会再被扩展,那么就设计为final类。类的组成使用(P
7、erson, java)。程序首先用class声明了一个名为Person的类,在这里Person是类的名称。第3、4行先声明了两个属性(即描述数据的变量)name和age, name为String (字符串类型)型,age为int (整型)型。第58行声明了一个talk。方法操作数据(如name和age)的方法,此类的定义对一个类定义而言,构造方法(constructor,又称构造器或构造函数)、属性和方法是三种最常见的成员,它们都可以定义零个或多个。如果三种成员都只 定义零个,那实际上是定义了一个空类,那就失去了定义类的意义了。类中各个成员之间,定义的先后顺序没有任何影响。各成员可相互调用,
8、但值 得注意的是,static修饰的成员不能访问没有static修饰的成员。属性用于定义该类或该类的实例所包含的各种数据。方法则用于定义类中的行 为特征或功能实现(即对数据的各种操作)。构造方法是一种特殊的方法,专 用于构造该类的实例(如实例的初始化、分配实例内存空间等),Java语言通 过new关键字来调用构造方法,从而返回该类的实例。定义一个类后,就可以创建类的实例了,创建类实例通过new关键字完成。下 面通过一个实例讲解如何定义并使用类。在ColorDefine这个类中,在第03行定义了一个String类型的属性color,并赋初值“黑色”。在第05行第08行,定义了一个普通的方法get
9、Mes (), 其完成的功能是向屏幕输出字符串“定义类”。第10行15行,定义了一个 公有访问的静态方法一一main方法。在main方法中,代码第12行中,定义了 ColorDefine的对象b,第13行输出了对象b的数据成员color,第14行调用 了对象的方法getMes 0 o还可以看出,在类ColorDefine中,没有构造方法(即与类同名的方法)。但 事实上,如果用户没有显式定义构造方法,Java编译器会提供一个默认的无参 构造方法。类的属性类的基本组成部分包括属性和方法。通过前面的学习,其实大家对方法这个概念并不陌生。例如,在前面内容中, 基本上每个范例都使用了 System, o
10、ut. printin()语句,那么它代表什么含义 呢?事实上,System是系统类(class), out是标准输出对象(object),而 printin()是对象out中的一个方法(method) 0这句话的完整含义就是调用系统 类System中的标准输出对象out中的方法printlnO。一言蔽之,方法就是解决一类问题的步骤的有序组合。由于它涉及的概念很多,我会在后期文章详细探讨这个概念。这里仅做简单的提及,让大家有个初 步的认知。下面我们先来讲解类的属性,类的屈性也称为字段或成员变量,不过习惯上将 它称为属性。属性的定义类的属性是变量。定义属性的语法如下。属性语法格式的详细说明如下。
11、(1)修饰符:修饰符可省略,也可是访问控制符public、protected private 及static、final,其中三个访问控制符public protected和private只能 使用其中之一,static和final则可组合起来修饰属性。属性类型:属性类型可以是Java允许的任何数据类型,包括基本类型(int、float等)和引用类型(类、数组、接口等)。属性名:从语法角度来说,属性名则只要是一个合法的标识符即可。但如果 从程序可读性角度来看,属性名应该由一个或多个有意义的单词(或能见名知 意的简写)连缀而成,推荐的风格是第一个单词应以小写字母作为开头,后面 的单词则用大写字母
12、开头,其他字母全部小写,单词间不使用其他分隔符。如:StringstudentNumber;默认值:定义属性还可以定义一个可选的默认值。提示属性是一种比较符合汉语习惯的说法,在Java的官方文献中,属性被称为Field,因此有些书籍也把“属性”翻译为“字段”或“域”,它们本质上是相 同的。属性的使用下面通过一个实例来讲解类的属性的使用,通过这个实例可以看出在Java中类 属性和对象属性的不同使用方法。类的属性组使用(usingAttribute. java)观点是采用自顶向下、逐步求精及模块化的程序设计方法,使用三种基本控制 结构构造程序,任何程序都可由顺序、选择、循环这三种基本控制结构来构
13、造。结构化程序设计主要强调的是程序的易读性。在该程序设计思想的指导下,编程基本是通过写不同目的的函数/过程来实现,故又称为“面向过程编程(ProcedureOriented Programming, POP)。面向过程开发方式是对计算机底层结构的一层抽象,它把程序的内容分为数据和操纵数据的操纵两个部分。这结构化程序设计方法可以用一句话概括:程序=算法+数据结构这里的“算法”可以用顺序、选择、循环这三种基本控制结构来实现。这里的“数据结构”是指数据以及其相应的存取方式。程序与算法和数据结构之间的关系如上图所示。面向对象程序设计简介代码第03-04行,定义了两个String类型的属性a和b,由于它
14、们是静态的, 所以它们是属于类的,也就是属于所有这个类定义的对象共有的,所有对象看 到的静态属性值是相同的。代码第06-07行,定义了两个String类型的属性c和d,由于它们是非静态 的,所以它们是属于这个类所定义的对象私有的,每个对象都有这个属性,且 它们各自的属性值可不同。 代码第09-14行,定义了静态方法块,它没有名称。使用static关键字加以修 饰并用大括号 ”括起来的代码块称为静态代码块,用来初始化静态成员变 量。如静态变量b被初始化为string-b。代码第23-28行,定义了一个构造方法usingAttribute (),在这个方法中, 使用了类中的各个属性。构造方法与类同
15、名,且无返回值(包括void),它的 主要目的是创建对象。这里仅是为了演示,才使用了若干输出语句。实际使用 过程中,这些输出语句不是必需的。代码30-37行,定义了公有方法print 0,用于打印所有属性值,包括静态成 员值。代码39-45行,定义了常见的主方法mainO,在这个方法中,第44行使用关 键字new和构造方法usingAttribute ()来创建一个匿名对象。由输出结果可以看出,Java类属性和对象属性的初始化顺序如下。类属性(静态变量)定义时的初始化,如范例中的static String a= “string-a” 。static块中的初始化代码,如范例中的static ()
16、中的b = string- b” o对象属性(非静态变量)定义时的初始化,如范例中的String c = stirng-c。 构造方法(函数)中的初始化代码,如范例构造方法中的d二“string- d。当然这里只是为了演示Java类的属性和对象属性的初始化顺序。在实际的应用 中,并不建议在类中定义属性时实施初始化,如例子中的字符串变量“a”和“C” O注意,被static修饰的变量称为类变量(class s variables),它们被类的 实例所共享。也就是说,某一个类的实例改变了这个静态值,其他这个类的实 例也会受到影响。而成员变量(member variable)则是没有被static修
17、饰的 变量,为实例所私有,也就是说,每个类的实例都有一份自己专属的成员变 量,只有当前实例才可更改它们的值。static是一个特殊的关键字,其在英文中直译就是静态的意思。它不仅用于修 饰属性(变量),成员,还可用于修饰类中的方法。被static修饰的方法,同 样表明它是属于这个类共有的,而不是属于该类的单个实例,通常把static修 饰的方法也称为类方法。对象的声明与使用在上述范例中,已创建好了一个Person的类,相信类的基本形式读者应该已经 很清楚了。但是在实际中单单有类是不够的,类提供的只是一个模板,必须依 照它创建出对象之后才可以使用。对象的声明下面定义了由类产生对象的基本形式。创建属
18、于某类的对象,需要通过下面两个步骤实现。 声明指向“由类所创建的对象的变量。利用new创建新的对象,并指派给先前所创建的变量。 举例来说,如果要创建Person类的对象,可用下列语句实现。当然也可以用下面的这种形式来声明变量。提示对象只有在实例化之后才能被使用,而实例化对象的关键字就是newo对象实例化的过程如下图所示。从图中可以看出,当语句执行到Person pl的时候,只是在“栈内存”中声明 了一个Person对象pl的引用,但是这个时候pl并没有在“堆内存”中开辟空 间。对象的“引用”本质上就是一个对象在堆内存的地址,所不同的是,在Java中,用户无法向C/C+那样直接操作这个地址。本质
19、上,“new Person。”就是使用new关键字,来调用构造方法PersonO , 创建一个真实的对象,并把这个对象在“堆内存”中的占据的内存首地址赋予 pl,这时pl才能称为一个实例化的对象。这里做个对比来说明“栈内存”和“堆内存”的区别。在医院里,为了迎接一 个新生命的诞生,护士会先在自己的登记本上留下一行位置,来记录婴儿床的 编号,一旦婴儿诞生后,就会将其安置在育婴房内的某个婴儿床上。然后护士 就在登记木上记录下婴儿床编号,这个编号不那么好记,就给这个编号取个好 记的名称,例如pl,那么这个pl (本质上就为婴儿床编号)就是这个婴儿“对 象”的引用,找到这个引用,就能很方便找到育婴房里
20、的婴儿。这里,护士的 登记表就好比是“栈内存”,它由护士管理,无需婴儿父母费心。而育婴房就 好比是“堆内存”,它由婴儿爸妈显式申请(使用new操作)才能有床位,但 一旦使用完毕,会由一个专门的护工(编译器)来清理回收这个床位一一在 Java中,有专门的内存垃圾回收(Garbage Collection, GO)机制来负责回收 不再使用的内存。对象的使用如果要访问对象里的某个成员变量或方法,可以通过下面的语法来实现。例如,想访问Person类中的name和age属性,可用如下方法来访问。因此若想将Person类的对象p中的属性name赋值为“张三,年龄赋值为25,则可采用下面的写法。25,则可采
21、用下面的写法。如果想调用Person中的talk()方法,可以采用下面的写法。对于对象属性和方法点操作符”,这里建议大家直接读成“的”,例如,pl. name = “张三”,可以读成“pl的name被赋值为张三。再例如,“pl. talkO 可以读成“pl的talk。方法”。这样读是有原因的:点操作符 ”对应的英文为“dot dot w ,通常“t”的发音弱化而读成“do ”(大家可以尝试用英文读一下来体会一下),而“ do的发音很接 近汉语“的”的发音de,如下图所示。此外,“的”在含义上也有“所属” 关系。因此将点操作符”读成“的”,音和意皆有内涵。使用Person类的对象调用类中的属性与
22、方法的过程(ObjectDemo. java)。第06行声明了一个Person类的实例对象pl,并通过new操作,调用构造方法PersonO ,直接实例化此对象。第07-08行,对pl对象中的属性(name和age)进行赋值。第9行调用pl对象中talk。方法,实现在屏幕上输出信息。代码12-20行,是Person类的定义。对照上述程序代码与下图的内容,即可了解到Java是如何对对象成员进行访问匿名对象匿名对象是指就是没有名字的对象。实际上,根据前面的分析,对于一个对象 实例化的操作来讲,对象真正有用的部分是在堆内存里面,而栈内存只是保存 了一个对象的引用名称(严格来讲是对象在堆内存的地址),
23、所以所谓的匿名 对象就是指,只开辟了堆内存空间,而没有栈内存指向的对象。创建匿名对象代码第11行,创建匿名对象,没有被其他对象所引用。如果第11行定义一个有名对象,如:那么调用类中的方法say(),可很自然的写成:面向对象的思想主要是基于抽象数据类型(Abstract Data Type, ADT),在结 构化编程过程中,人们发现把某种数据结构和专用于操纵它的各种操作以某种 模块化方式绑定到一起会非常方便,做到“特定数据对应特定处理方法”,使 用这种方式进行编程时数据结构的接口是固定的。如果对抽象数据类型进一步 抽象,就会发现把这种数据类型的实例当作一个具体的东西、事物、对象,就 可以引发人们
24、对编程过程中怎样看待所处理的问题的一次大的改变。抽象数据类型方法虽然也有一定的抽象能力,但其核心仍然是数据结构和算 法。而面向对象方法直接把所有事物都当作独立的对象,处理问题过程中所思 考的不再主要是怎样用数据结构来描述问题,而是直接考虑重现问题中各个对 象之间的关系。可以说,面向对象革命的最重要价值就在于改变了人们看待和 处理问题的方式。例如,在现实世界中桌子代表了所有具有桌子特征的事物,人类代表了所有具 有人特征的生物。这个事物的类别映射到计算机程序中,就是面向对象中“类(class) ”的概念。可以将现实世界中的任何实体都看作是对象,例如在人类 中有个叫张三的人,张三就是人类中的实体,对
25、象之间通过消息相互作用,比 如张三这个对象和李四这个对象通过说话的方式相互传递消息。现实世界中的 对象均有属性和行为,例如张三有属性:手、脚、脸等,行为有:说话、走 路、吃饭等。类似的,映射到计算机程序上,属性则表示对象的数据,行为表示对象的方法(其作用是处理数据或同外界交互)。现实世界中的任何实体都可归属于某类 事物,任何对象都是某一类事物的实例。所以在面向对象的程序设计中一个类 但是由于anew NoNameObject() w创建的是匿名对象,所以就用 NoNameObjectO 整体来作为新构造匿名对象的引用,它访问类中的方法, 就如同普通对象一下,使用点操作符(.):匿名对象有如下两
26、个特点。 匿名对象是没有被其他对象所引用,即没有栈内存指向。由于匿名对象没有栈内存指向,所以其只能使用一次,之后就变成无法找寻 的垃圾对象,故此会被垃圾回收器收回。对象的比较有两种方式可用于对象间的比较。(1)利用“二二”运算符;(2)利用 equals0方法。“二二”运算符用于比较两个对象的内存地址值(引用值)是 否相等,equals()方法用于比较两个对象的内容是否一致。回到那个“婴儿床编号,和“婴儿,的比喻,“二=,运算符完成的是比较两个 婴儿床的编号是否相等(相等则说明是同一个婴儿床),而equals()方法完成 的是婴儿床内的婴儿是否相同(相同则说明是一个婴儿)。下面的两个案例分 别
27、说明了这两种方法的使用。运算符用于比较(CompareObjectl. java)。由程序的输出结果可以发现,strl不等于str2,有些人可能会问,strl与str2的内容完全一样,为什么会不等于呢?大家可以发现在程序的第5和第6 行分别用new实例化了两个String类对象,此时这两个对象在“堆内存”中处 于不同的内存位置,也就是它们的内存地址是不一样的。这个时候程序中是用 的“二=”比较,比较的是内存地址值(即引用值),所以输出strl!=str2o 程序第7行将str2的引用值直接赋给str3,这个时候就相当于str3也指向了 str2的引用,此时这两个对象指向的是同一内存地址,所以比
28、较值的结果是 str2=str3 strl str2和str3的内存布局模拟如下图所示。大家可能会问,那该如何去比较里面的内容呢?这就需要采用另外一种对象比 较方法一一 “equals。”。请看下面的程序。equals方法用于对象内容的比较(CompareObject2. java)相比于上一个范例,在第08行代码处,将比较方式从“strl = str2换成了ustrl. equals ( str2 ) , equals ()方法的宿主是 String 类的对象 strl。所有String类的对象都有equals ()方法,因此第08行代码换成。达到的比较效果是同原来的代码是一样的。在这里需要
29、大家记住,是比较对象内存地址值(即所谓的引用值)的,而“equals”是比较对象内容的。对象数组的使用我们可以把类理解为用户自定义的数据类型,它和基本数据类型(如ini、 float等)具有相同的地位。在前面章节中我们己介绍过如何以数组来保存基 本数据类型的变量。类似地,对象也可以用数组来存放,可通过下面两个步骤 来实现。声明以类为数据类型的数组变量,并用new分配内存空间给数组。用new产生新的对象,并分配内存空间给它。例如,要创建3个Person类型的数组元素,语法如下。创建好数组元素之后,便可把数组元素指向由Person类所定义的对象。当然也可以写成如下形式。当然,也可以利用for循环来
30、完成对象数组内的初始化操作,此方式属于动态 初始化。或者也可以采用静态方式来初始化对象数组,如下所示。用静态方式初始化对象数组(ObjectArray. java)。程序第2024行用静态声明方式声明了 Person类的对象数组p,它包含了 3 个对象。事实上,在21-23行,每一行都是返回一个对象的引用地址,而对象 数组的三个元素就是这三个对象的引用地址。程序第2528行用for循环输出对象数组p中的所有对象,并分别调用它们 talk。方法,打印出个人信息。序第06-10行构造方法Person()的定义,在这个代码段里,有个关键词this, 容易让初学者困惑。下面给予简要介绍,有关this的
31、详细使用说明,在后期的 文章中我会再详细介绍。当创建一个对象后,Java虚拟机(JVM)就会给这个对象分配一个自身的引用 一一thiso由于this是和对象本身相关联的,所以this只能在类中的非静态 方法中使用。静态属性及静态方法属于类,它们与具体的对象无关,所以静态 属性及静态方法是没有this的。同一个类定义下的不同对象,每个对象都有自 己的this,虽然都叫this,但指向的对象不同。这好比一个班里的众多同学来 做自我介绍:“我叫XXX”,虽然说的都是“我”,但每个“我”指向的对象 是不同的。为什么第08-09行中有赋值运算符(二)左侧的变量使用this引用呢?这是因 为构造方法Per
32、sonO的参数列表有形参name和age,它们是隶属于构造方法Person ()的局部变量,而Person对象中有同名的屈性变量name和age (分别 在第03行利04行定义),如果将构造方法Person()中的形参给给同名的对象 属性赋值,第08-09行就变成如下的语句。这样会让部分读者产生“误解” o为什么这两个变量会自己给自己赋值呢?其 实,范例中第08-09行中的this的确并不是必需的,但是为了增强代码的可读 性,赋值运算符(二)左侧的变量使用this,来表明左侧变量是指当前对象的 成员变量,而非Person方法内的同名形数。因此,“this, name =name; M这 个语句
33、就可以比较清晰的解读为,用Person方法内形参name给本对象的成员 变量name赋值。代码第09和13行,也可以有类似的解读,这里就不一一赘述 了。1.栈内存和堆内存的区别在Java中,栈(stack)是由编译器自动分配和释放的一块内存区域,主要用 于存放一些基本类型(如int、float等)的变量、指令代码、常量及对象句 柄(也就是对象的引用地址)。栈内存的操作方式类似于数据结构中的栈(仅在表尾进行插入或删除操作的线 性表)。栈的优势在于,它的存取速度比较快,仅次于寄存器,栈中的数据还 可以共享。其缺点表现在,存在栈中的数据大小与生存期必须是确定的,缺乏 灵活性。堆(heap)是一个程序
34、运行动态分配的内存区域,在Java中,构建对象时所需 要的内存从堆中分配。这些对象通过new指令“显式”建立,放弃分配方式类 似于数据结构中的链表。堆内存在使用完毕后,是由垃圾回收(Garbage Collection,简称GC)器“隐式”回收的。在这一点上,是和C/C+是有显著 不同的,在C/C+中,堆内存的分配和回收都是显式的,均由用户负责,如果 用户申请了堆内存,而在使用后忘记释放,则会产生“内存溢出”问题一一可 用内存存在,而其他用户却无法使用。堆的优势是在于动态地分配内存大小,可以“按需分配”,其生存期也不必事 先告诉编译器,在使用完毕后,Java的垃圾收集器会自动收走这些不再使用的
35、 内存块。其缺点为,由于要在运行时才动态分配内存,相比于栈内存,它的存 取速度较慢。由于栈内存比较小,如果栈内存不慎耗尽,就会产生著名的堆栈溢出(stackoverflow)问题,这能导致整个运行中的程序崩溃(crash) 由于这 个问题的普通性,全球IT界最受欢迎的技术问答网站也取名为Stack Overflow (. com/),大家如果有 IT 问题,中文网站如 不能找到合适的中文解决方案,可以尝试在这个网站上找到高质量的英文解 答。类似的,如果堆内存使用不当也会产生问题,典型的问题就是内存碎片(fragmentation),当回收堆内存时,可能会导致一些小块的但不连续的内存 存在。当用
36、户申请一块较大的堆内存,虽然可用的小块内存总和足够大,本可 以满足申请所需,但是由于它们不连续,导致申请失败。这些不可用的非连续 的小块内存就是所谓的内存碎片。 可以实例化多个相同类型的对象。面向对象编程达到了软件工程的三个主要目 标:重用性、灵活性和扩展性。面向对象程序设计的基本特征下面,我们简述面向对象的程序设计的3个主要特征:封装性、继承性、多态 性。封装性(encapsulation):封装是一种信息隐蔽技术,它体现于类的说明,是 对象的重要特性。封装使数据和加工该数据的方法(函数)封装为一个整体, 以实现独立性很强的模块,使得用户只能见到对象的外特性(对象能接受哪些 消息,具有哪些处
37、理能力),而对象的内特性(保存内部状态的私有数据和实 现加工能力的算法)对用户是隐蔽的。封装的目的在于把对象的设计者和对象 的使用者分开,使用者不必知晓其行为实现的细节,只须用设计者提供的消息 来访问该对象。继承性:继承性是子类共享其父类数据和方法的机制。它由类的派生功能体 现。一个类直接继承其他类的全部描述,同时可修改和扩充。继承具有传递 性。继承分为单继承(一个子类有一父类)和多重继承(一个类有多个父 类)。类的对象是各自封闭的,如果没继承性机制,则类的对象中的数据、方 法就会出现大量重复。继承不仅支持系统的可重用性,而且还促进系统的可扩 充性。多态性:对象根据所接收的消息而做出动作。同一
38、消息被不同的对象接受时可 产生完全不同的行动,这种现象称为多态性。利用多态性用户可发送一个通用 的信息,而将所有的实现细节都留给接受消息的对象自行决定,如是,同一消2.面向对象编程与面向过程编程的感性认知前面我们说到,面向过程程序设计主要的弊病是上一步和下一步环环相扣,如 果需求发生变化那么代码的改动会很大,这样很不利于对软件的后期维护和扩 展。这仅仅是理论上的描述,很多读者对此并没有感性认识,因此无法理解深 刻,下面我们用实例来说明二者的区别。假设有两个程序员分别叫POP (面向过程编程的英语简写)和OOP (面向对象编 程的英语简写),他们分别来完成用户图形界面(GUI)上的显示矩形(sq
39、uare)、圆形(circle),当用户点击这两个图形时,这些图形分别要旋 转180。,并播放一段歌曲。下面项目经理让这两个程序员POP和OOP分别来 实现这个功能。由上面的描述可知,看起来面向过程代码更加简洁。如果程序比较短,面向过 程要比面向对象更加的清晰。但是用户的需求是一直在变的,软件的升级基本上是不可避免的。如果项目经理要求增加新的要求一一新的升级软件需要支持“三角形”的旋转和播放歌曲。那么程序员POP和OOP是如何完成自己的工作呢?维护中,“牵一发而动全身”,过程Rotate(). PlaySong()是全局性的,在前期版本Rotate0 PlaySong()可以正确响应“矩形”和
40、“圆形”的变化,但是 在维护这两个“新版本”过程中有一点错误,都会让前期的“无辜”的“矩形”和“圆形”受到牵连一一无法正确运行。而面向对象代码OOP,虽然代码过程看起来复杂一点,但是如果前期版本的软 件可以正确响应“矩形”和“圆形”的变化,那么在新版本维护过程中,增加 了 “三角形”,即使出现了错误(不管是逻辑上的还是语法上的),那这些错 误仅仅局限性于“三角形”类一一这样程序的错误就可控,很方便维护。如果代码很短,面向对象编程的模式优势并不明显,但是如果读者把Rotate 0. PlaySong()过程想象成上万行的代码,就会知道将代码错误局部 化、可控化,对程序的后期维护有多重要!落后的软
41、件生产方式无法满足迅速增长的计算机软件需求,从而导致软件开发 与维护过程中出现一系列严重问题的现象。这就是所谓的软件危机。在学习了 类的三个特点一一封装、继承和多态,大家会发现,相比于面向过程编程,面 向对象编程还有更多的优点。-全文完-息即可调用不同的方法。例如:同样是run方法,飞鸟调用时是飞,野兽调用 时是奔跑。多态性的实现受到继承性的支持,利用类继承的层次关系,把具有 通用功能的协议存放在类层次中尽可能高的地方,而将实现这一功能的不同方 法置于较低层次,这样,在这些低层次上生成的对象就能给通用消息以不同的 响应。在OOPL中可通过在派生类中重定义基类函数(定义为重载函数或虚函 数)来实
42、现多态性。综上可知,在面对象方法中,对象和传递消息分别表现事物及事物间相互联系 的概念。类和继承是适应人们一般思维方式的描述范式。方法是允许作用于该 类对象上的各种操作。这种对象、类、消息和方法的程序设计范式的基本点在 于对象的封装性和类的继承性。通过封装能将对象的定义和对象的实现分开, 通过继承能体现类与类之间的关系,以及由此实现动态联编和实体的多态性, 从而构成了面向对象的基本特征。面向对象编程和面向过程编程的比较面向对象编程和面向过程编程是现在主流的两种编程模式,它们既有区别也有 联系。下面就其区别和联系进行叙述以助大家更加深入的理解面向对象编程。1.两种编程范式之间的区别在面向对象编程
43、出现以前,面向过程颇受程序人员的青睐,因为面向过程编程 采用的是“自上而下,步步求精”的编程思想,人们更易于理解这种思想。将 程序一步一步的分解,并按照一定的顺序执行。这就是面向过程程序设计,以 过程为中心,以算法为驱动 程序=算法+数据结构但是面向过程程序设计的不足之处在于,面向过程的程序上一步和下一步环环 相扣,如果需求发生变化那么代码的改动会很大,这样很不利于软件的后期维 护和扩展。例如,写一个图形界面的软件,想将它改为控制台下的,如果是使 用面向过程写的,那么代码的改动将是巨大的,因为前台代码和后台联系过于 紧密。这对于开发一个大型的复杂的软件来说是致命的。而面向对象程序设计的出现就可
44、以很好的解决了这一问题,它的设计思想是, 程序二对象+消息传递用户首先自定义的数据结构一“类”,然后用该类型下的“对象”组装程序。 对象之间通过“消息”进行通讯。每个对象包括数据以及对数据的处理,每个 对象都像是一个小型的“机器”。面向对象设计使程序更容易扩展,也更加符 合现实世界的模型。但是“任何事物都有两面性”,面向对象程序设计有其优点,但也带来了 “副 作用”一一执行效率要低于面向过程程序设计。所以进行科学计算和要求高效 率的程序中,面向过程设计要好于面向对象设计。而且面向对象程序的复杂度 要高于面向过程的程序,如果程序比较小,面向过程要比面向对象更加清晰。更为具体来说,为解决某个任务,
45、面向过程程序设计首先强调的“该怎么做(Howto do?) ”这里的“How”对应的解决方案就形成一个个功能块一 function (函数),而面向对象程序设计首先考虑的是“该让谁来做(Who to do?),这里的“Who”就是对象,这些对象完成某项任务的能力就构成一个 个method (方法),最后一系列具备一定的方法的对象“合力”能把任务完 成。例如,对于“吃东西”这个任务,面向过程强调的是“如何去吃”,“人”只是一个参数;而面向对象强调的是“人”,“吃东西”只是人内部能从上面的分析可知,前面章节的所有的例子虽然用了 Java中class来“包 装”,但本质上都是“面向过程”的思路来解决
46、问题,因为前面的范例中没有 任何“对象”存在,只存在解决问题的“方法(method) ”,脱离对象的方法 其实就是面向过程程序设计中的“函数(function) ” o2.两种编程范式之间的联系面向对象是在面向过程的基础上发展而来的,只是添加了它独有的一些特性。 面向对象程序中的对象就是由数据和方法构成,所以完整的面向对象概念应该 是,对象二数据+方法更进一步的可以描述为, 程序二对象+消息传递二(数据+方法)+消息传递面向对象的基本概念类将具有相同属性及相同行为的一组对象称为类(class) o广义地讲,具有共同 性质的事物的集合就称为类。在面向对象程序设计中,类是一个独立的单位, 它有一个
47、类名,其内部包括成员变量,用于描述对象的属性;还包括类的成员 方法,用于描述对象的行为。在Java程序设计中,类被认为是一种抽象的数据 类型,这种数据类型不但包括数据,还包括方法,这大大地扩充了数据类型的 概念。类是一个抽象的概念,要利用类的方式来解决问题,必须用类创建一个实例化 的对象,然后通过对象去访问类的成员变量,去调用类的成员方法来实现程序 的功能。就如同“汽车”本身是一个抽象的概念,只有使用了一辆具体的汽 车,才能感受到汽车的功能。一个类可创建多个类对象,它们具有相同的属性模式,但可以具有不同的属性 值。Java程序为每一个对象都开辟了内存空间,以便保存各自的属性值。对象对象(object)是类的实例化后的产物。对象的特征分为静态特征和动态特征 两种。静态特征指对象的外观、性质、属性等。动态特征指对象具有的功能、 行为等。客观事物是错综复杂的,但人们总是从某一日的出发,运用抽象分析 的能力,从众多的特征中抽取最具代表性、最能反映对象本质的若干特征加以 详细研究。 人们将对象的静态特征抽象为属性,用数据来描述,在Java语言中称之为变 量,将对象的动态特征抽象为行为,用一组代码来表示,完成对数据的操作, 在Java语言中称之为方法(method)。一个对象由一组属性和一系列对属性进 行操作的方法构成。在现实世界中,所有事物都可视为对象,对象是客观世界里的实