5.1 面向对象程序设计概述
为什么要使用面向对象的程序设计方法?
面向对象的程序设计(OOP:Object-Oriented Programming)是一种基于结构分析的、以数据为中心的程序设计方法。它的主要思想是将数据及处理这些数据的操作都封装(Encapsulation)到一个称为类(Class)的数据结构中。使用这个类时,只需要定义一个类的变量即可,这个变量叫做对象(Object)。通过调用对象的数据成员完成对类的使用。
示例:编制“五子棋”的游戏程序
5.2 类与对象
5.2.1 类与对象概述
在面向对象的程序设计方法中,类是对具有相同特征的一类事物所做的抽象(或者说,归纳)。
对象是类的实例,是现实存在的并且可唯一区分的。
5.2.2 面向对象程序设计相关概念
(1) 属性
属性(Property)说明了这个类的特点。
(2) 方法
类的方法(method)代表了一类事物所具备的动作,是对属性的操作。
(3) 封装
封装(Encapsulation)是指隐藏属性,方法或实现细节的过程,仅对外公开接口。
(4) 消息
消息(Message)是对象之间发出的行为请求。
(5) 继承
继承是指面向对象中一个类自动拥有另一个类的全部属性和方法。继承后的类仍具有被继承类的特点,同时又出现新的特点。
(6) 多态
在面向对象技术中,多态性是指相同的对象收到相同的消息时,或不同的对象收到相同的消息时,产生不同的行为方式。
封装性、继承性、多态性被称为面向对象程序设计的三大特性。
5.2.3 类的声明
类的声明包括类的头部和类的实现两个部分。
类修饰符
5.2.4 对象的声明与对象的实例化
在C#中,对象的声明定义格式为:
4.2.5 类成员
在C#中,按照类的成员是否为函数将其分为两大类,一种不以函数形式体现,称为“域”(数据成员或成员变量);另一种是以函数形式体现,称为成员方法。
类的具体成员有以下类型:
定义类成员时需要注意以下几个原则:
- 类中的常量、变量、属性、事件或类型不能与其他类成员同名。
- 类中的方法名,不能和类中其他成员同名,既包括其他非方法成员,又包括其他方法成员。
- 如果没有显式指定类成员访问修饰符,默认类型为私有类型修饰符。
类的每个成员都需要设定访问修饰符,不同的修饰符会造成对成员访问能力不同。
namespace ConsoleApplication1
{
public class Person
{
public string name;
public int age;
public void Show()
{
Console.WriteLine("姓名:{0}\n年龄:{1}", name, age);
}
}
class Program
{
public static void Main()
{
Person objPerson = new Person();
objPerson.name = "张三";
objPerson.age = 22;
objPerson.Show();
Console.ReadLine();
}
}
}
5.2.7 类成员的分类
在C#中,类成员可以分为静态成员和非静态成员。声明一个静态成员只需要在声明成员的指令前加上static保留字。如果没有这个保留字就默认为非静态成员。
相应的成员变量分为静态成员变量和非静态成员变量;成员方法又分为静态成员方法和非静态成员方法。
5.2.7.1 类的静态成员
静态成员变量:
如果在声明类的成员变量时添加了static修饰符,该成员变量就称为静态成员变量。静态成员变量用于存储一些属于某个类的全部对象的信息,它是被类的全体对象共享的数据。
- 静态成员变量必须通过 “类名.静态成员变量名”的形式来进行访问, 而不能用对象来引用。
- 一个静态成员变量只标识一个存储位置。无论创建了一个类的多少个实例,它的静态成员变量在内存中都只占同一块区域。
静态方法:
使用static关键字声明的方法被称为“静态方法”。 静态方法是通过“类名.方法名”的形式来访问。
静态方法是不属于特定对象的方法,静态方法只可以访问静态成员变量,不可以直接访问非静态成员变量(因为非静态成员变量属于对象所有,不属于类所有)。
静态成员与非静态成员比较
示例:定义一个类(Teacher),并在该类中定义3个public访问权限的成员变量,分别表示name(姓名)、 title(职称)和department(所属院系),其中所属院系为静态成员变量。在该类中定义一个非静态成员方法(Show)用于向控制台输出用户的姓名和职称,定义一个静态成员方法(ShowDepartment)用于向控制台输出用户的所在院系。在Main函数中对该类进行访问时,要求动态输入用户的信息,并完成该用户信息的输出,程序运行结果如图所示。
public class Teacher
{
public string name;
public string title;
public static string department;
public void Show()
{
Console.WriteLine("姓名:{0}\r\n职称:{1}", name, title);
}
public static void ShowDepartment()
{
Console.WriteLine("所属院系:{0}",department);
}
}
class Program
{
static void Main(string[] args)
{
Console.Write("请输入所属院系:");
Teacher.department = Console.ReadLine();
Teacher t1=new Teacher();
Console.Write("请输入第一位教师的姓名:");
t1.name = Console.ReadLine();
Console.Write("请输入第一位教师的职称:");
t1.title = Console.ReadLine();
Console.WriteLine("\r\n第一位教师的信息如下:");
t1.Show();
Teacher.ShowDepartment();
Console.ReadLine();
}
}
5.3 类的构造函数与析构函数
5.3.1 构造函数
构造函数是类的一个特殊的方法,用于在创建对象时完成对对象的初始化操作。在C#语言中,每当创建一个对象时,都会首先调用类中定义的构造函数。使用构造函数的好处是它能够确保用户所创建的每一个对象在被使用之前都适当地进行了初始化的动作,即为对象的成员赋给一定的初值。
构造函数具有如下特点:
- 每个类至少有一个构造函数。如果程序代码中没有显示的定义构造函数,则系统会自动提供一个默认的构造函数,而如果显示定义了构造函数,则系统就不会提供默认构造函数。
- 一个构造函数总是和它的类名相同。
- 构造函数不包含任何返回值。
- 构造函数一般使用访问修饰符public,以便在其他函数中可以创建该类的实例。
- 在创建一个类的新对象(使用new关键字)时,编译系统会自动调用类的构造函数初始化新对象。
5.3.2 默认构造函数
如果在类中不显示定义构造函数,系统会自动提供一个默认的构造函数。默认构造函数没有参数,默认构造函数将非静态成员初始化为下面的值:
- 对数值型,如int、double等,初始化为0;
- 对bool类型,初始化为false;
- 对引用类型,初始化为null。
5.3.3 实例构造函数
5.3.4 重载构造函数
类的构造函数可以没有参数,也可以拥有多个参数。一个类也可以有多个构造函数,它们的参数个数和类型必须有一个不同的,这就是构造函数的重载。在类实例化的时候,系统会自动根据参数的类型和个数去寻找匹配的构造函数。
namespace Example3of7
{
class Area
{
public double myArea;
public void showResult()
{
Console.WriteLine(" {0}", myArea);
}
//构造函数1
public Area()
{
myArea = 0;
}
//构造函数2
public Area(double x, double y)
{
myArea = x * y;
}
//构造函数3
public Area(double r)
{
myArea = 3.1415926 * r * r;
}
//构造函数4
public Area(double a, double b, double h)
{
myArea = (a + b) * h / 2;
}
}
class Test
{
static void Main(string[] args)
{
//点面积
Console.WriteLine("点面积为:");
Area pointArea = new Area();
pointArea.showResult();
//矩形面积
Console.WriteLine("矩形面积为:");
Area rectangleArea = new Area(5,4);
rectangleArea.showResult();
//圆面积
Console.WriteLine("圆面积为:");
Area circleArea = new Area(2.5);
circleArea.showResult();
//梯形面积
Console.WriteLine("梯形面积为:");
Area trapezoidArea = new Area(2,4,5);
trapezoidArea.showResult();
Console.ReadLine();
}
}
}
5.3.5 this关键字
C#中的this关键字是用来代表对象自身。this一般用在构造函数中,以便区别同名的构造函数参数和类的成员变量。
5.3.6 析构函数
析构函数在类撤销时运行,常用来处理类用完后的收尾工作。由于类一旦被撤销就将不复存在,因此这里所说的收尾工作主要是对象在运行过程中动态申请内存的回收工作。
- 析构函数不能带有参数
- 不能拥有访问修饰符
- 不能显式地被调用
- 一个对象的析构函数在该对象被撤销时由系统自动调用
- 析构函数不能被继承
- 析构函数的名字与类名也相同,只是在前面加了一个符号“~”
class Decon1
{
public Decon1()
{
Console.WriteLine("调用构造函数Decon1");
}
~Decon1()
{
Console.WriteLine("调用析构函数Decon1");
}
}
class Decon2
{
public Decon2()
{
Console.WriteLine("调用构造函数Decon2");
}
~Decon2()
{
Console.WriteLine("调用析构函数Decon2");
}
}
class Test
{
public static void Main()
{
Decon1 dec1 = new Decon1();
Decon2 dec2 = new Decon2();
Console.ReadLine();
}
}
5.3.7 静态构造函数
静态构造函数也称类构造函数,在第一次使用类之前调用。通过在构造函数名称的前面使用关键字“static”,就可以定义一个静态的构造函数。静态构造函数自动被调用,它的调用原则:
- 静态构造函数总是在该类的所有静态变量初始化之后执行。
- 静态构造函数总是在该类被使用(如访问静态变量、生成实例)之前被调用。
- 静态构造函数最多被调用一次。
namespace ConsoleApplication5
{
class A
{
static A()
{
Console.WriteLine("Init A");
}
public static void F()
{
Console.WriteLine(“Welcome c#");
}
}
class Program
{
static void Main(string[] args)
{
A.F();
Console.ReadLine();
}
}
}