面向对象程序设计的概念

1                       面向对象程序设计的概念

1.1     简介

在这一章中,我们将看到继承、重写等面向对象编程的概念是如何在C#中实现的。我们还将学习有关操作符的重载。

1.2     构造函数

构告函数是类中的方法,每次创建类的实例时,都将调用该方法。构造函数用于初始化成员变量。特点在于构造函数与类同名,并且不返回值。

下面我们来研究下示例:

示例1:

using System;

public class DaysInYear

{

private int days;

public DaysInYear()

{

days=365;

}

static void Main(String[] args)

{

DaysInYear newDaysInYear=new DaysInYear();

Console.WriteLine(newDaysInYear.days);

}

}

以上示例说明,我们在实例化类的对象时,首先执行的便是类的构造函数。此示例最终显示结果为365,如果我们去掉构造函数,得到的将会是值类型变量days的默认值0。

1.3     带参数的构造函数

类是可以有多个构造函数的,可以将不同个数或不同类型的参数传递给这些构造函数,使它们产生不同的运行结果。

我们来看看下面的例子:

示例2:

using System;

public class DaysInYear

{

private int days;

public DaysInYear()

{

days=365;

}

public DaysInYear(int day)

{

days=day;

}

public DaysInYear(String dayOne)

{

days=Convert.ToInt32(dayOne);

}

public void setDay(int newDays)

{

days=newDays;

}

static void Main(String[] args)

{

DaysInYear newDaysInYear=new DaysInYear();

DaysInYear newDaysInYearInt=new DaysInYear(366);

DaysInYear newDaysInYearSrt=new DaysInYear(“366”);

Console.WriteLine(“默认值={0}”,newDaysInYear.days);

Console.WriteLine(“传递的整型值={0}”,newDaysInYearInt.days);

Console.WriteLine(“传递的字符串值={0}”,newDaysInYearStr.days);

}

}

1.4     析构函数

C#的析构函数由垃圾回收器调用。垃圾回收器是定期地、或在内存状态要求(内存已满)时执行清理工作。这是C#的高级机制。

在之前我们已经讲过析构函数的实现方式了。那么我们接下来讲一下垃圾回收器是如何工作的。

不能对结构使用析构函数。只能对类使用析构函数。

一个类只能有一个析构函数。

无法继承或重载析构函数。

无法调用析构函数。它们是被自动调用的。

析构函数既没有修饰符,也没有参数。

1.4.1  垃圾回收器和析构函数

垃圾回收器负责释放内存,这是通过销毁不再被引用或不再使用的对象。工作原理如下:

如果为定义析构函数的对象分配了内存,运行库将把该对象添加到需要销毁的对象的列表中。运行库把析构函数当作销毁器,但在C#中,将它称为析构函数。

垃圾回收器定期检查是否有未被引用的对象。

如果找到了其名称没有列在销毁器列表中的对象,就立即清除该对象。

如果对象名称列在需要销毁的对象列表中,则将它标记为“准备销毁”。

完成垃圾回收后,将调用销毁器线程,该线程则调用所有标记为“准备销毁”的对象的销毁方法(析构函数)。

对象销毁过程发生后,就将该对象从需要销毁的对象列表中删除。

因为该对象没有被引用,也没有列在销毁器列表中,所以下次进行垃圾回收时就会清除该对象。

当然,垃圾回收器也有某些缺陷。这些缺陷包括:

有析构函数的对象占用的资料较多,因为即使不再需要它们,它们仍会在内存中驻留罗长的时间。

销毁过程作为独立的线程执行,从而使资源被大量占用。

所以,建议只在必要时才使用析构函数。

1.5     C#中的方法重载

在C#中,可以用以下两种方式重载方法:

指定不同个数的参数

指定不同参数的参数类型

C#不支持对方法返回类型的重载

在示例2中,我们已经看见构造函数的名称相同,但其中两个接收参数,有一个不接收

参数。参数也有差别。这已经实现了构造函数重载。方法重载的方式与此类似。

1.5.1  不同参数个数的方法重载

我们研究一下示例:

示例3:

using System;

public class Area

{

private int areaVal;

public void AreaCal(int radius)

{

areaVal=(22/7) * radius * radius;

}

public void AreaCal(int length,int breadth)

{

areaVal=length * breadth;

}

public void AreaCal(int length, int breadth, int height)

{

areaVal=length * breadth * height;

}

static void Main(String[] args)

{

Area newArea=new Area();

Console.WriteLine(newArea.areaVal);

NewArea.AreaCal(6,8,10);

Console.WriteLine(newArea.areaVal);

NewArea.AreaCal(4,6);

Console.WriteLine(newArea.areaVal);

}

}

以上示例类Area的三个方法名称相同,都是AreaCal()。但是邮于使用不同个数的参数重载了这些方法。

1.5.2  不同参数类型的方法重载

另一种重载方法的方式是指定不同的参数类型。传弟的参数个数无关紧要。参数的个数可以相同。

代码段2:

public void AreaCal(Int radius)

{

areaVal=(22/7)* radius * radius;

}

public void AreaCal(string shapeName)

{

Console.WriteLine(“{0}”,shapeName);

}

1.5.3  操作符重载

C#允许重载操作符。重载操作符意味着使操作符具有与其正常行为不同的行为。

下面我们来研究两段代码,这两段代码使用方法执行两个整数变量的加法运算。

代码段3:

//第1行

int result=Decimal.Add(54,200)

//第2行

int result2=54+200;

第1行使用值类型为Decimail的数据函数类的Add方法计算变量result。第2行使用加法运算符”+”符号计算结果。两者完成的是同样的任务。但是使用操作符的优点是方便而且简单易于理解。假设要建立一个二次方程。或使用方法计算恒星与地球之间的距离的公式,那么不用操作符编写这种算式将是非常困难的。

操作符可以使代码具有更好的可读性,同样,随着程序变得越来越复杂,将会有更多的类,那么这就间味着将要对这些对象执行很多运算。在这种情况下,重载操作符就会很有意义。重载操作符简化了对这些对象所执行的操作。当然也不是所有的操作符都可以重载的。重载操作符的方过程非常类似于创建方法的过程。所有重载的操作符都是静态方法。我们来看看可重载的操作符列表。

表4.1:可以重载的操作符

+ - ! ~ ++ --

* / % & | ^

<< >> != <, > <= >=

示例4:

using System;

public struct Distance

{

public Distance(int lon, int mag)

{

this.longitude=lon;

this.latitude=mag;

}

int longitude,latitude;

public static Distance operator – (Distance first,Distance second)

{

return new Distance(first.longitude – second.longitude,first.latitude – second.latitude)

}

public static void Main()

{

Distance start =new Distance();

Distance newDistance =new Distance();

Distance finish =new Distance();

start.longitude=12;

start.latitude=10;

finish.longitude=2;

finish.latitude=1;

newDistance=start – finish;

Console.WirteLine(“坐标X轴为{0},Y轴为{1}”,newDistance.longitude,newDistance.latitude);

}

}

在示例4中:

声明一个名为Distance结构,它有两个名为longitude和latitude的数据成员。

重载-(减号)操作符,当把结构为Distance的两个对象传递给它时该操作符执行两个对象的longitude和latitude分别相减,并返回Distance类型的另一个对象。

在Main()函数中声明三个Distance类型的对象。我们已经设置对象start和finish的longitude和latitude。newDistance对象使用重载的操作符”-”来接受其它两个对象的longitude和latitude相减后的结果。

将newDistance对象的longitude和latitude差值(即start减去finish对象的结果)显示给用户

重载操作符的过程可以详细解释为下列步骤。

请注意示例4的黑体部份,语法非常类似于声明方法的语法。

首先指定访问修饰符。在该示例中,我们使用public 访问修饰符。

关键字static使该操作符成为类的公共操作符,而不是特定对象的操作符。这是强制关建字。

然后,我们定义操作符的返回类型。返回类型是操作符的结果所使用的类型。在该示例中,返回类型是Distance。

在返回类型之后,指定关键字operator,该关键字必须在被子重载的操作符之前指定。这是强制关键字。

然后必须指定被重载的操作符。要注意保证该操作符在表4.1中。

必须指定该操作符执行运算时所用的参数。在该示例中,参数是两个Distance类型的对象。

在该示例中,我们只是将所传递的两个对象的longitude和latitude相减,并用另一个Distance对象返回结果。减法的结果被传回Distance()方法,以便newDistance对象的longitude属性和latitude属性赋值。

1.6     C#中的继承

面向对象的编程可以将新类作为另一个类的后代来声明和使用。这个过程称为继承。继承使我们不用重写代码。并提供代码重用的巨大好处。C#中不支持多重继承,因为在应用程序变得很复杂时它会引起较大的混乱,并且就内存过载而言它也被视为效率不高的编程方式。现在我们学习一下单一继承。

示例5:

using System;

class characterVal

{

public int setCharVal(char ch)

{

char charVal=ch;

Console.WriteLine(“输入的字符是:{0}”, charVal);

return(0);

}

}

class StringVal : CharacterVal

{

public string strVal;

public int setStrVal(string str)

{

strVal=str;

Console.WriteLine(“输入的字符串是:{0}”,strVal);

return(0);

}

}

class Inheritance

{

StringVal objec1=new StringVal();

object1.setCharVal(“j”);

object1.setStrVal(“enjoy”);

}

示例5中,没有为类StringVal声明setCharVal()方法。我们创建了StringVal的对象object1。使用该object1对象,我们能够访问setCharVal方法。这是因为类StringVal继承了CharacterVal类。

1.6.1  密封类

在C#中,通过对类的继承进行密封可以避免对该类的继承。密封类使用的语法如下。

代码段5:

sealed class classOne

{

//类的实现

}

通过指定sealed关键字,可以密封任何类。此类保存在堆中。

当类只包括静态成员时,可能需要必须密封该类。此外在某些情况下密封类可以提高应用程序的性能。

1.7     C#中的方法重写

我们已经知道在C#中如何实现继承。现在让我们考查如何实现方法重写。方法重写提供了重写或跳过基类的方法的灵活性。

重写基类的方法时,我们在继续类或派生类中声明一个与基类中的方法同名的新方法,并在方法名称前附加new关键字。

下面我们来研究下:

示例6:

using System;

class intAddition

{

public void Add()

{

int firstNum=1;

int secondNum=2;

Console.WriteLine(“两数之和为:{0}”, firstNum+secondNum);

}

}

class StringAddition :IntAdditon

{

new public Add()

{

string firstStr=”a”;

string secondStr=”b”;

Console.WriteLine(“两字符串之和为:{0}”,firstStr+secondStr);

}

}

class MethodOverride

{

public static void Main()

{

StringAddition objStringAddition = new StringAddition();

ObjStringAddition.add();

}

}

示例输出结果为:ab

在两个不同的类中都有一个名为Add()的方法。请注意,类StringAddition重官了类IntAddition中方法Add(),因为类StringAddition在定义add方法前指定了new关键字。注意的是就算没有指定new关键字,也是执行重写,输出结果也将相同。编译器会报没有生成new关键字的警告。使用new关键字是比较谨慎的编程做法。

1.8     小结

带参数的构造函数是接受参数的是构造函数。

我们将不同个数或不同类型的参数传递给构造函数,以便运行库能够区分它们。

在C#中,析构函数由垃圾回收器调用。

垃圾回收器负责释放内存,这是通过销毁不再被引用的对象实现的。

在C#中,可以用两种方式中的任何一种来重载方法。

指定不同个数的参数

指定不同类型的参数

C#允许重载操作符

重载操作符意味着使操作符(例如,加法操作符+)在应用于结构或类的特定对象时具有不同的行为。

C#不支持多重继承。

要重写基类的现有方法,需要在继承类中声明一个同名的新方法,并在方法名称前附加new关键字。

1.9     练习

1. 可以创建带参数的静态构造函数

a.对 b.错

2. 析构函数的语法是

classMaster()

{

//析构函数的实现

}

a.对 b.错

3. C#不支持_______________继承。

a.单一 b.多重

4. 可以使用____________关键字重写方法。

a.Override b.new

2.10 作业

1. 在C#中创建一类EmplyTest,它有以下数据成员/字段

标识符 类型 值

EmplyNo Integer 接收输入

EmplyName String Admin

EmplyMoney Integer 接收输入

及以下成员函数

标识符 返回类型 参数

CreateNewEmplyNo void Integer EmplyNo

ShowEmplyName void String EmplyName

CountEmplyMoney void Integer EmplyMoney

EmplyTest类有构造函数,析构函数。一个无参构造函数,一个带一个参数的构造函数。参数为EmplyNo。另外重载ShowEmplyName方法改变参数为两个String类型的变量EmplyName,EmplyNameOne。

再写一个子类,继承EmplyTest类,重写父类EmplyTest的ShowEmplyName方法。重写父类CountEmplyMoney方法,接收两个参数。EmplyNo及EmplyMoney,计算出他们的和赋给EmplyNo。打印出EmplyName.和EmplyNo。

2. 创建成有以下数据成员的类Time:

标识符 类型

Hours Integer

Minutes Integer

Seconds Integer

重载+操作符,用于将Seconds字段中包含的值加1。当Seconds的值等于60时,将Minutes的值加1,然后将Seconds的值恢复为0。同样当Minutes的值等于60时,必须执行下列操作:

Minutes的值回复为0

Seconds的值回复为0

Hours的值加1

当Hours的值等于24时,所有三个字段的值必须回复为0。必须将这三个字段的值用为参数传弟。编写Main()方法,实现上述功能,并在用户的控制台上显示输出结果。

本文转自:http://www.cnntec.com

原文地址:https://www.cnblogs.com/drgraph/p/2364621.html