本章要点:
面向对象的基本概念
类的定义与对象的声明
构造函数和析构函数
类的静态成员和实例成员
方法重载及运算符重载的编程实现
类的继承与多态性的编程实现
类的属性的实现
7.1循序渐进学理论
7.1.1面向对象程序设计概述
1.面向对象程序设计的由来
面向对象的程序设计是一种基于结构分析的、以数据为中心的程序设计方法。面向对象的程序设计方法总体思路是:将数据及处理这些数据的操作都封装(Encapsulation)到一个称为类(Class)的数据结构中,在程序中使用的是类的实例——对象。对象是代码与数据的集合,是封装好了的一个整体,对象具有一定的功能。也就是说对象是具有一定功能的程序实体。程序是由一个个对象构成的,对象之间通过一定的“相互操作”传递消息,在消息的作用下,完成特定的功能。
2.面向对象程序设计的基本概念
(1)类和对象
通常把具有同样性质和功能的东西所构成的集合叫作类。
(2)属性、方法与事件
属性是对象的状态和特点。
方法是对象能够执行的一些操作,它体现了对象的功能。
事件是对象能够识别和响应的某些操作。
(3)封装
所谓的封装,就是将用来描述客观事物的一组数据和操作组装在一起,形成一个类。
(4)继承
类之间除了有相互交流或访问的关系以外,还可能存在着一种特殊的关系,这就是继承。在VisualC#中只支持单继承,即一个派生类只能有一个基类。
(5)重载
重载指的是方法名称一样,但如果参数不同,就会有不同的具体实现。重载主要有两类:方法重载及运算符重载。
(6)多态性
所谓多态性就是在程序运行时,面向对象的语言会自动判断对象的派生类型,并调用相应的方法。
7.1.2类和对象的声明
1.类的声明
[格式]:[类修饰符]class类名[:基类类名]
{
成员定义列表;
}
定义一个Student类,用来对学生的信息和功能进行描述。假设学生具有学号、姓名、年龄、性别、平均成绩等特征,并且具有设置学生特征和显示学生特征的功能。
2.对象的声明
[格式]:类名 实例名=new类名([参数]);3.类的成员
(1)类成员的分类
类的具体成员如下。
常量:用来定义与类相关的常量值。
字段:类中的变量,相当于C++中的成员变量。
类型:用来定义只能在类中使用的局部类型。
方法:完成类中各种计算或功能的操作。
属性:定义类的特征,并对它们提供读、写操作。
事件:由类产生的通知,用于说明发生了什么事情。
索引器:允许编程人员在访问数组时,通过索引器访问类的多个实例。又称下标指示器。
运算符:定义类的实例能使用的运算符。
构造函数:在类被实例化时首先执行的函数,主要是完成对象初始化操作。
析构函数:在对象被销毁之前最后执行的函数,主要是完成对象结束时的收尾操作。
(2)类成员的可访问性
在编写程序时,可以对类的成员使用不同的访问修饰符,从而定义它们的访问级别,即类成员的可访问性(Accessibility)。
在C#中,根据类成员的可访问性可以把类成员分成四类,分别是公有成员(public)、私有成员(private)、保护成员(protected)、内部成员(internal)。
(3)类的静态成员和实例成员
类的成员又可以分成静态成员和非静态成员。在声明成员时,如果在语句前加上static保留字,则该成员是静态成员,如果没有static保留字,则成员是非静态成员。二者最重要的区别是:静态成员属于类所有,非静态成员属于类的实例所有,所以又称实例成员。
7.1.3类的构造函数和析构函数
1.构造函数
构造函数主要用来为对象分配存储空间,完成初始化操作(如给类的成员变量赋值等)。在C#中,类的构造函数遵循以下规定。
(1)构造函数的函数名和类的名称一样。
(2)当某个类没有构造函数时,系统将自动为其创建构造函数,这种构造函数称为默认构造函数。如例7-2中默认的构造函数为:
Example1()
{
};
(3)构造函数的访问修饰符总是public。如果是private,则表示这个类不能被实例化,这通常用于只含有静态成员的类中。
(4)构造函数由于不需要显式调用,因而不用声明返回类型。
(5)构造函数可以带参数也可以不带参数。
2.析构函数
析构函数在对象销毁时被调用,常用来释放对象占用的存储空间。析构函数具有以下特点。
(1)析构函数不能带有参数。
(2)析构函数不能拥有访问修饰符。
(3)不能显式地调用析构函数。
(4)析构函数的命名规则是在类名前加上一个“~”号。如上例的Example1类的析构函数为:
~Example1()
{
};
(5)析构函数在对象销毁时自动调用。
类的构造函数和析构函数的演示。(程序代码详见例7-3)[执行结果]
7.1.4类的方法及方法的重载
1.方法的定义
[格式]:[方法修饰符]返回值类型方法名([参数列表])
{方法实现部分;
}
2.静态方法和非静态方法
对于静态方法和非静态方法,只需抓住以下几点:(1)静态方法属于类所有,非静态方法属于类定义的对象所有;(2)非静态方法可以访问类中包括静态成员在内的所有成员,而静态方法只能访问类中的静态成员。
静态方法和动态方法的演示。请观察并分析下列程序的执行结果。(程序代码详见例7-4)
[执行结果]
3.参数数组
关于参数数组,需掌握以下几点。
(1)若形参表中含一个参数数组,则该参数数组必须位于形参列表的最后;
(2)参数数组必须是一维数组;
(3)不允许将params修饰符与ref和out修饰符组合起来使用;
(4)与参数数组对应的实参可以是同一类型的数组名,也可以是任意多个与该数组的元素属于同一类型的变量;
(5)若实参是数组则按引用传递,若实参是变量或表达式则按值传递。
参数数组的演示。请观察并分析下列程序的执行结果。(程序代码详见例7-5)
[执行结果]
4.方法的重载
方法重载是指同样的一个方法名,有多种不同的实现方法。方法重载的格式是在一个类中两次或多次定义同名的方法,这些同名的方法包括从基类继承而来的方法,这些方法名称相同,但每个方法的参数类型或个数不同,从而便于在用户调用方法时系统能够自动识别应调用的方法。
方法重载的演示。请观察并分析下列程序的执行结果。(程序代码详见例7-6)
[执行结果]
7.1.5运算符重载
在C#中,运算符重载在类中进行声明,声明的格式如下。
[格式]:返回值类型operator运算符(运算对象列表)
{
重载的实现部分;
};
在C#中,可以重载的运算符主要有:
+-!~++--truefalse
*/%
^==!===
不能重载的运算符有:.
=
?:newtypeofsizeofis
运算符重载的演示。请观察并分析下列程序的执行结果。(程序代码详见例7-7)
[执行结果]
7.1.6域与属性
1.域
域又称字段,它是类的一个成员,这个成员代表与对象或类相关的变量。域的定义格式如下。
[格式]:[域修饰符]域类型域名;
域的演示。请观察并分析下列程序的执行结果。(程序代码详见例7-8)
[执行结果]
2.属性
属性是对现实世界中实体特征的抽象,它提供了一种对类或对象特性进行访问的机制。属性的声明格式如下。
[格式]:[属性修饰符]类型说明符属性名{访问声明}属性的演示。请观察并分析下列程序的执行结果。(程序代码详见例7-9)
[执行结果]
7.1.7this关键字
this关键字用来引用类的当前实例,成员通过this关键字可以知道自己属于哪一个实例。this关键字只能用在类的构造函数、类的实例方法中,在其它地方(如静态方法中)使用this关键字均是错误的。
7.1.8类的继承
继承是面向对象程序设计中实现代码重用的重要机制之一,它起源于现实世界中事物之间的联系。
类的继承的基本格式与功能如下。
[格式]:
class派生类类名:基类类名
{成员声明列表;
}
类继承的演示。请观察并分析下列程序的执行结果。(程序代码详见例7-10)
[执行结果]
7.1.9多态性
多态性是指同一操作作用于不同类的实例,这些类对它进行不同的解释,从而产生不同的执行结果的现象。在C#中有两种多态性:编译时的多态性和运行时的多态性。
运行时的多态性是通过继承和虚成员来实现的。运行时的多态性是指系统在编译时不确定选用哪个重载方法,而是直到程序运行时,才根据实际情况决定采用哪个重载方法。
编译时的多态性具有运行速度快的特点,而运行时的多态性则具有极大的灵活性。
虚函数与多态性的演示。请观察并分析下列程序的执行结果。(程序代码详见例7-11)
[执行结果]
7.1.10密封类与抽象类
把一个类声明为密封类的原因是为了防止该类被其它类继承,密封类的声明方法是在类名前加上sealed修饰符。
抽象类表示一种抽象的概念,一般用于为派生类提供公共接口。在声明类时,在类名前有abstract修饰符则表示该类为抽象类。抽象类只能作为其他类的基类,不能被实例化,在抽象类中可以包含抽象方法和抽象访问器。
7.2典型实例练能力
7.2.1典型实例一:Time类的创建与演示
编写一个名为Time1的类,该类能够存放时间信息,并且具有设置时间和显示时间的功能。然后编程对该类进行测试。测试界面如图7-1所示。
7.2.2典型实例二:复数“*”运算及复数值设置的实现
编写一个复数类,该复数类具有以下功能:(1)乘法运算的功能。如a和b分别是该复数类的两个实例,则可以进行a*b的运算,运算结果是一个复数,该复数是复数a和复数b的乘积。(2)对复数值进行设置的功能,有两种设置方法,分别对复数的实部(虚部为0)进行设置、对复数的实部和虚部同时进行设置。然后编程进行验证。验证界面如图7-2所示。
7.3上机练习重应用
7.3.1上机练习一:栈模型的实现
栈是一种重要的数据结构,在内存中占用连续的存储单元。栈有两个端点,固定的栈底和浮动的栈顶。为指示栈顶位置还应设一个指示成员变量(称为栈顶指示器)。栈有两种基本操作:push(压栈)和pop(出栈),压栈是向栈顶位置写入一个元素,然后使栈顶指示器加1,出栈是先使栈顶指示器减1,再把该位置的元素读出来。栈及其操作模型如图7-3所示,sp代表栈顶指示器。请编程实现栈的模型并对之进行测试,测试的界面如图7-4所示。
7.3.2上机练习二:从shape类派生出Rectangle、Circle等具体形状类
定义一个shape抽象类,利用它作为基类派生出Rectangle、Circle等具体形状类,已知具体形状类均具有两个方法GetArea和GetPerim,分别用来求形状的面积和周长。最后编写一个测试程序对产生的类的功能进行验证,验证程序的运行界面如图7-5所示。