OOP啊,给跪了
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()方法,实现上述功能,并在用户的控制台上显示输出结果。