2023年面向对象编程技术.pdf
第4章面向对象编程技术面向对象的程序设计方法是当前程序设计的大势所趋。面向对象的程序设计方法是对结构化程序设计方法的重新结识。在程序的设计中,方法总是与特定的数据结构密切相关的,方法具有对数据结构的访问,特定的方法只合用于特定的数据结构,因此方法与数据结构在编程中应当是一个密不可分的整体,这个整体叫对象。c#通过类、对象、继承、多态等机制形成一个完善的面向对象的编程体系。4.1类和对象类是C#程序设计的基本单位。4.1.1 类和对象概述现实生活中的类是人们对客观对象不断结识而产生的抽象的概念,而对象则是现实生活中的一个个实体。例如,人们在现实生活中接触了大量的汽车、摩托车、自行车等实体,从而产生了交通工具的概念,交通工具就是一个类,而现实生活中的具体的汽车、摩托车自行车等则是该类的对象。血向对象程序设计的类从本质上和人们现实生活中的这一结识过程是相同的。例如在编程实践中,人们经常使用按钮这一控件,每一个具体的按钮是一个按钮对象,而按钮类则是按钮对象的抽象,并且人们把这一抽象用计算机编程语言表达为数据集合与方法集合的统一体,然后再用这个类创建一个个具体的按钮对象。可以把类比作一种蓝图,而对象则是根据蓝图所创建的实例,可以把类比作生产模具,而对象则是由这种模具产生的实例(产品)。所以人们又把对象叫做类的实例。类是对事物的定义,而对象则是该事物自身.在Visual S t udio.N E T集成环境中的,工具箱中的一个个控件,是被图形文字化的可视的类,而把这些控件添加到窗体设计器中后,窗体设计器中的控件则是对象,即由工具箱中的类创建的对象。类是一种数据类型,在C#中,类这种数据类型可以分为两种:一种是由系统提供的预先定义的,这些类在.NET框架类库中;一种是用户定义数据类型。在创建对象之前必须先定义该对象所属的类。然后由类声明对象.类本质上是一种数据类型,所以类的用法与基本数据类型的用法基本相同,事实上,在Vis ualStudio.NET中 像i nt、fl。at等基本数据类型也是特殊的类。那么,用基本数据类型可以声明变量,用类类型也可以声明变量,只但是类类型的变量叫类的对象或类的实例。4.1.2 类定义类定义的格式与结构定义的格式相似,在类定义中需要使用关键字c 1 a ss,其简朴的定义格式为:c 1 a s s 类名 类体“类名”是一个合法的C#标记符,表达数据类型(类类型)名称,“类体”以一对大括号开始和结束,在一对大括号后面可以跟一个分号。例如:c 1 ass stud e nt/类名为 stude n t(叩u b 1 i c string I D;publ i c string N a me;。p u blic b o o l S ex;I上例中,定义了一个名为“stud en t”(学生)的类,类体中涉及学生的学号-(“ID”)、姓名(“Nam e”)与性别(“Sex”)。在 上 例 的“类体”中声明的数据都使用“pub 1 ic”修饰,“pub 1 i c”(公共的)表达这些数据可以直接进行访问。假如仅从stu d ent”类的定义形式看,其与结构类型除了关键字外几乎没有任何差别。当然这只是为了说明问题方便,这种定义方式并不符合面向对象程序设计的原则,由于类中缺少了对数据的操作。“类体”涉及类中的所有数据及对数据的操作,面向对象程序设计将数据与对数据的操作作为一个整体,以类的形式进行定义,这 种 机 制 叫“封装”。在“类体”中,所有的数据及对数据的操作的集合叫类成员,类成员的种类很多,本章仅 介 绍“字段”、“属性”、“方法”与“构造函数”。“字段”是类定义中的数据,也叫类的变量。在上例中定义的“ID”、“Name”与“Sex”等均为类中的字段。“属性”用于读取和写入“字段”值,“属性”是字段的自然扩展,对用户而言,“属性”等 同 于“字段”自身,对程序员而言,属性是一种读写“字段”的特殊方法。“方法”实质上就是函数,用于对字段进行计算和操作,即对类中的数据进行操作,以实现特定的功能。4.1.3 声明与使用对象定义类之后,可以用定义的类声明对象,声明对象后可以访问对象成员。每一个类对象均具有该类定义中的所有成员,正如每一个整型变量均可以表达同样的数值范围同样。1.声明对象声明对象的格式与声明基本数据类型的格式相同:类名对象名;例如:s t u d e n t S I;/声明,一个学生类对象S I但是,对象声明后,需用“n ew”关键字进行初始化,这样才干为对象在内存中分派保存数据的空间。初始化的格式:对象名=new 类名();例如:S 1 =n e w s t u d e n t();为S 1分派内存空间可以将声明与初始化对象合二为一,例如:s t u d e n t S 1 =n e w s t u d e n t ();/声明对象并初始化2.访问对象访问对象实质是访问对象成员,对对象变量成员的访问与结构变量相同,使 用 运 算符。例如:S l.I D=1 23 45 ;S 1 .N a me=张三;S l.S e x=t r u e;上面的代码为对象S 1数据成员赋值。可以使用对象变量为另一对象变量整体赋值,例如,“s t u d e n t S 2;S 2=S 1;或 s t u d e n tS 2=S I;这时,不需要使用n e w关键字对“S 2”初始化。可以使用对象中的某一成员。例如:s t r i n g s N a m e =S 1.N a m e ;/将对象S I的N a m e成员值赋给字符串变量s N a m e4.1.4值类型与引用类型类定义的类型与结构定义的类型虽然相似,但这两种类型却有很大的差别,这涉及到C#中变量存储方式的问题。从变量的存储方式看,C#中的变量可以分为值类型变量与引用类型变量。1 .值类型值类型变量直接包含其自身的数据,因此每个值类型变量自身就包具有赋给它的值。在 C#中,内置数据类型除了字符串(s t r i n g)类型与对象(o b j e c t)类型外其余均为值类型。枚举类型与结构类型也是值类型。例 如“i n t x =4 2;,整型变量X就是值类型。2 .引用类型与值类型不同,引用类型变量不包含自身的数据,只是存储对数据的引用,数据保存在内存的其他位置。例如:usi n g S y s t em;cla s s Cl a ssl(c lass Te s t。pu b lie int A;。static v oi d Main(s t ri n g|arg s)(。i n I X=42,Y=X;Tes t Tl=new T e s t();“TLA=42;Test T2=TI;Y=100;oT 2,A=1 00;)引用类型与值类型在内存中的存储形式如图4-1 所示。int X=4 2;I 42|Tl.A=4 2;|4 2 的引用|-42图4-1 值类型与引用类型的存储值类型变量可以使用变量来赋值,引用类型变量也可以使用变量来赋值。例如上例中的代码:intX=4 2,Y=X;T est Tl=new Test();T l.A=4 2;Test T2=T1;使用对象变量赋值但是,值类型与引用类型用变量赋值的性质却不同,如图4-2 所示。图4-2 值类型与引用类型用变量赋值从图中可以看到,用变量X”为变量“Y”赋值,是 将“X”所在内存中的值复制给了“Y”;而用变量“T1”为“T2”赋值,则 是 将“T1”对数据的引用复制给了“T2”,即两个对象变量使用的是同一内存中的数据,也就是说,用“T1”为“T2”赋值不需要使用new关键字对对象初始化,也就没有为对象“T2”分派存储数据的内存空间。这样,在改变变量值时,将发生本质差别。例如上例中的代码:Y=1 0 0;/Y 的值为1 0 0,X的值仍是4 2T 2.A=1 0 0;不仅T 2的值为1 0 0,T 1 的值也为1 0 04.1.5 访问控制在上面类定义的例子中,声明类的数据成员(字段)时,均使用public进行修饰,pub lie叫访问修饰符。声明类中的成员时,使用不同的访问修饰符,表达对类成员的访问权限不同,或者说访问修饰符拟定了在什么范围可以访问类成员。C#中最常用的访问修饰符及其意义,如表4-1所示。表 4-1访问修饰符访问修饰符意义P u b lic(公有)访问不受限制,可以被任何其他类访问privat e(私有)访问只限于含该成员的类,即只有该类的其他成员能访问protected(保护)访问只限于含该成员的类、及该类的派生类在类定义中,假如声明成员没有使用任何访问修饰符,则该成员被认为是私有(Priv a te)的。假如不涉及继承,private与protected没有什么区别。在类定义中,假如成员被声明为p riv ate或protected,则不允许在类定义外使用点运算符访问,即在类定义外,点运算符只能访问P ublic成员。例如在下面的类定义中:c 1 ass s tu d en t类名为stud e ntpri v a te s tri n g ID;/私有p ubl i c string Name;公有p ro t e cte d bool S ex;/保护)s tuden t S l=n ew stu den t();S 1.ID=1 2 345;非法,ID 为 p r ivateS 1 .Name=张三;合法,Name 为 p u b 1 i cS 1 .Sex=t rue;/非法,Sex 为 P ro t ected在类定义外使用点运算符访问“ID”与“Sex”是非法的,而访问“Name”则是合法的,由 于 ID 与 S e x 是 pri v ate 或 p r otected 的,而 N am e则是 p ublic 的。通常,在类定义中,数据成员(字段)被声明为pri v a te 或 protec t e d,从而实现所谓的数据隐藏,以保证不能在类外随意访问类的数据成员;而其他种类的成员则被声明为pu b lic,以通过这些成员实现对类的数据成员的访问。4.2属性对私有或保护数据成员常见的访问是读取与设立修改数据值,在类定义外部,这种访问可通过属性成员实现。通过属性可以控制对数据的访问方式,甚至设立数据可接受的值域。4.2.1 声明属性在类定义中声明属性的格式为:访问修饰符类 型 属 性 名 可以将属性声明为读写属性、只读属性或只写属性。1.声明读写属性修改st u den t 类定义添加属性声明:clas s s t u den t类名为s t u d ent。声明字段p r iva t e s t r i n g i d;p r iv a te string n ame;p riva t e bo o 1 s ex;/声明属性publ i c strin g I D(ogeto r e tu r n i d;seta。did=val u e;O d|)p ublic st r i n g N a meIggetreturn name;。setU。nam e=va 1 u e;)public boo 1 Sex。ge tr e tu r n sex;0)s e t(sex=va 1 ue;。)在属性声明中,g e t与s e t叫属性访问器。g e t完毕对数据值的读取,re tu rn用于返回读取的值;se t完毕对数据值的设立修改,v a lu e是一个关键字,表达要写入数据成员的值。属性名应和其要访问的数据成员名相关但不相同,可以采用数据成员名全用小写,而属性名的单词首字母大写的方式,如数据成员名为n a m e,则相应的属性名为N a m e。2.声明只读或只写属性在属性声明中,假如只有g e t访问器,则该属性为只读属性。例如:p u b l i c b o o 1 S e x(g e t(r e t u r n s e x;)只读属性意味着,数据成员的值是不能被修改的。在属性声明中假如只有se t访问器,则该属性为只写属性。只写属性在程序设计中不常使用。4.2.1 使用属性属性成员的使用就如同公有数据成员的使用同样。可认为可写的属性赋值,可以用可读的属性为其他变量赋值。以stud e nt类为例:s t u d e n t S l=n e w s t u d e n t();用属性设立修改数据成员值S 1.I D=1 2 3 4 5;S l.N am e=张三”;S l.S e x =t r u e;/用属性读取数据成员值为其他变量赋值b o o l S S e x=S 1 .S e x;假如属性为只读的,则属性不能出现在赋值运算符的左边。在 C#程序设计中,窗体与控件的属性就是这类成员,其中在属性窗口显示的属性,均为可读写属性,窗体与控件的只读属性只能在代码中使用。4.3 方法方法是把一些相关的语句组织在一起,用于解决某一特定问题的语句块。方法必须放在类定义中。方法同样遵循先声明后使用的规则。4.3.1声明与调用方法方法的使用分声明与调用两个环节。1.声明方法声明方法最常用的格式:访问修饰符返回类型 方 法 名(参数列表)方法的访问修饰符通常是P ublic,以保证在类定义外部可以调用该方法。方法的返回类型用于指定由该方法计算和返回的值的类型,可以是任何值类型或引用类型数据,如,i nt、string及前面定义的s tu d ent 假如方法不返回一个值,则它的返回类型为void。方法名是一个合法的C#标记符。参数列表在一对圆括号中,指定调用该方法时需要使用的参数个数、各个参数的类型,参数之间以逗号分隔。参数可以是任何类型的变量。假如方法在调用时不需要参数,则不用指定参数,但圆括号不能省。实现特定功能的语句块放在一对大括号中,叫方法体,“”表达方法体的开始,“”表达方法体的结束。假如方法有返回值,则方法体中必须包含一个return语句,以指定返回值,该值可以是变量、常量、表达式,其类型必须和方法的返回类型相同。假如方法无返回值,在方法体中可以不包含r e t u r n语句,或包含一个不指定任何值的r e t u r n语句。例如下面的方法声明:p u b l i c i n i I n t M a x (i n t a ,i n t b)(i n t c;i f (a b)。c=a;e l s eo c =b;r e t ur n c;)该方法的功能是求两个整数中较大的整数,并将该整数返回。该方法的返回类型是一个整型值,方法名称为“IntMax”,参数列表中有两个整型变量参数“a”与 b”,方法体中有一 个r e t u m语句,该语句指定的返回值是一个整型变量c。该方法体中的语句块也可以用条件表达式合并为一句:r e t ur n a b?a:b;。这时,r e t ur n指定的返回值是一个条件表达式,r e t ur n语句把该表达式运算的结果返回。2.调用方法从方法被调用的位置,可以分为在方法声明的类定义中调用该方法和在方法声明的类定义外部调用方法。在方法声明的类定义中调用该方法的格式为:方法名(参数列表)在方法声明的类定义中调用该方法,事实上是由类定义内部的其他方法成员调用该方法。例如在类定义内部调用求较大整数函数:c l a s s CLASSMAX(p u b 1 i c i n t G e t M a x(i n t a,i n t b)其他方法成员(o r e t u m I n t M a x(a,b );/在类定义 C L A S S M A X 内部调用方法 I n t M a x)p ub l i c i n t I n t M a x (i n t a ,i n t b)求较大整数函数。r e t ur n a b?a:b ;。)在方法声明的类定义外部调用该方法事实上是通过类声明的对象调用该方法,其格式为:对象名.方法名(参数列表)【例 4-1】创建一个控制台应用程序,并创建一个类,该类仅包含求两个数中较大数的方法成员,在 Main方法中调用该方法。us i n g S y s t e m;c 1 a s s C l a s s i(o c 1 a s s CLASSMAX/定义一个类p u b l i c i n t I n t M a x(i n t a,i n t b )g r et u m ab?a:b;g|)esta t ic v o i d Ma i n(stri ng args)。(。i ntX=4 2,Y;o Y=1 0 0;CLASSMAX cla s smax=new CLASSMAX();/声明一个对象i nt C=classma x.IntMa x(X,Y);/调用对象方法Consol e.Write(较大的值为:”);o Console.W rit e Line(C);00co n s o le.ReadLine();)程序运营的结果为:较大的值为:1004.3.2参数传递在方法的声明与调用中,经常涉及方法参数,在方法声明中使用的参数叫形式参数(形参),在调用方法中使用的参数叫实际参数(实参)。在调用方法时,参数传递就是将实参传递给形参的过程。以【例4-1】为例,声明方法时的形参如下:p ublic int IntM a x(int a,i n t b)调用方法时的实参如下:c 1 a ssmax.ln t Ma x(X,Y)这样就完毕了形参与实参的结合,其传递关系如图4-3所示。方法调用:cla s smax.I ntMax(X,Y)I!方法声明:public i nt I ntMax(i n t a,int b)图4-3 形参与实参方法参数传递按性质可分为按值传递与按引用传递。1.按值传递参数按值的方式传递是指当把实参传递给形参时,是把实参的值复制(拷贝)给形参,实参和形参使用的是两个不同内存中的值,所以这种参数传递方式的特点是形参的值发生改变时,不会影响实参的值,从而保证了实参数据的安全。按值传递如图4 4所示。实参 形参-图4-4按值传递示意基本类型的参数在传递时默认为按值传递。2.按引用传递方法只能返回一个值,但实际应用中经常需要方法可以修改或返回多个值,这时只靠re tu rn语句显然是无能为力的。假如需要方法返回多个值,使用按引用传递参数的方式可以实现这种效果。按引用传递是指实参传递给形参时,不是将实参的值复制给形参,而是将实参的引用传递给形参,实参与形使用的是一个内存中的值。这种参数传递方式的特点是形参的值发生改变时,同时也改变实参的值。按引用传递分基本数据类型与类对象数据类型两种情况,其传递如图4-5.与图4-6所示。实参形参图4-5基本类型按引用传递示意传尼、基本类型参数按引用传递时,形参事实上是实参的别名。基本类型参数按引用传递时,实参与形参前均须使用关键字ref。【例 4 一2】编写一个控制台应用程序,在程序中添加一个互换两个整型变量值的方法。调用该方法将两个整型变量的值进行互换并输出互换前后的结果。u s i n g S y s t em;name s p ac e数值互换cla s s Cl a ss 1opubl i c void Swap(r e f i nt a,r ef int b)/形参a、b为引用类型数值互换i nt c=a;a=b;b=c;s t a t i c v o i d M a i n(s t r i n g 口 a r g s )i n t A=6 0,B =8 0;。C o n s o l e.W r i t e L i n e(互换前A、B 的值:0 ,1 “,A,B);0 8 c lassi c=n e w C l a s s i ();c.S w a p(r e f A,r e f B);以引用方式传递实参A、Bo C o n s o l e.W r i t e L i n e (互 换后 A、B 的值:0 ,1 ”,A,B);。C o n s o l e.R e a d L i n e ();。)程序运营结果:互换前A、B的值:6 0,8 0互换后A、B的值:8 0,6 0由于在Swap方法中,对引用形参a、b 的值的修改,就是对实参A、B 的值的修改,所以方法S w a p 成功地完毕了对A、B 数据的互换功能,相称于返回了两个值,这在按值传递的方式下是无法实现的。类对象参数总是按引用传递的,所以类对象参数传递不需要使用r e f 关键字。类对象参数的传递,事实上是将实参对数据的引用复制给了形参,所以形参与实参共同指向同一个内存区域。【例 4-3】编写一个控制台应用程序,该程序项目名称为“类对象参数”,程序中有两个类定义,一个是创建程序时系统自动创建的类class 1,一个是用户自定义的stu d e n t类 在classl类中声明一个方法,该方法以stu d ent类对象为参数。在 classl类中的Main方法中调用该方法。u sing System;names p ace类对象参数(o c 1 ass stude n t 类名为 s tuden t(声明字段oprivat e stri n g id;o p riv a t e str i ng name;叩 rivate bo o 1 se x;声明属性pu b 1 ic st r i n g I D。(。ogct(return id;。set0(a。i d=v a lue;。叩u b 1 i c string Nam eget r e t urn name;1setnam e=v a lue;)opu b 1 i c b oo 1 Sex(g et(return se x;)s etUes e x=v alue;0 0 )c lass Class 1。p u b lic void Studen t F 1 (s t u dent ST 1 )/以对象 STI 为形参*/修改形参数据成员的值。STI.ID=5 6 789;。STl.Name=张三;ST 1 .S e x=true;。static void M a in(s t r i ng a r g s)str i ng SSex;0 stud e n t SI;声明一个学生类对象SI。S1 =n e w s t udent();/为S I 分派内存空间。S 1 .ID=12345n;61.N a me=”李平“;goSl.Se x=false;g if(Sl.Sex=tru e)。SS e x二“男 ;。el s eo 。SSex二“女”;。Con s ole.W r i t eLi n e(学生学号:”+S 1.ID+姓名:+S 1.Name。+“性别:+SSex);输出对象S 1 的值o Cla s si c=new C 1 as s 1();/声明对象 c 以调用方法 S t ude n tF 1c.S t u de n tF 1 (SI);/以对象S I 为实参调用方法g i f(SLSe x=tru e)。S Sex=男 ;e Ise。6 S e x=女;。,Console.WriteL i ne(学生学号:n+S 1.I D+姓名:+Sl.Nam e。+“性别:+SSex);/输出对象S I的值ox C o n sole.ReadLine();)0)程序运营结果:学生学号:12 345 姓名:李平 性别:女学生学号:56789 姓名:张三性别:男结果的第一行为调用方法前对象S 1 的值,结果的第二行为调用方法后对象S 1 的值。虽然在Main方法中并未对对象S1进行修改,但由于调用方法S t u d e n tF 1 时,将 S1传递给了 ST1,而类对象参数是按引用传递的,所以在方法S t udentFl中对ST1的修改,就是对S1 的修改,因此,结果的第二行,S 1的值发生了变化。4.3.3 重载方法有时候方法实现的功能需要针对多种类型的数据,虽然C#有隐式转换功能,但这种转换在有些情况下会导致运算结果的错误,而有时数据类型无法实现隐式转换甚至主线无法转换。例如:us i n g System;class Classio p u b l i e v o i d S w ap (r e f i n t a,r e f i n t b )。(i n t c=a;。a=b;b=c;)s t at i c v o i d M ai n(s t r i n g a r g s)(i n t A=60,B=8 0;。C o n s o l e.W r i t e L i n e(互换前A、B 的值:0,1 “,A,B);。C l as s i c=n e w C 1 a s s 1();c.S w w(r e f A,r e f B );/调用互换值方法C o n s o l e.W r i t e L i n e(互换后 A、B 的值:0 ,1 *A,B );。C o n s o l e.R e a d L i n e ();0)上例中的互换方法只能实现两个整型变量的值互换,无法通过隐式或显式转换来实现其他类型变量的值互换。例如在上例的主方法中添加如下代码:n o a t f A=5.2f,f B=6.0 f;c.S w ap (r e f f A ,r e f f B );运营程序时,将 出 现“无 法 从re fflo a t转 换 为r e f i n t”的编译错误。为了能使同一功能合用于各种类型的数据,C#提供了方法重载机制。方法重载是声明两个以上的同名方法,实现对不同数据类型的相同解决。方法重载有两点规定:(1)重载的方法名称必须相同;(2)重载的方法,其形参个数或类型必须不同,否则将出现“已经定义了一个具有相同类型参数的方法成员”的编译错误。假如要使上例中的互换方法能同时解决整型与浮点型数据,重载的方法声明如下:pu b li c voi d S wap(r ef i n t a,ref i nt b)pub!i c v o i d Swap(ref fl o at a,ref f loat b)声明了重载方法后,当调用品有重教的方法时,系统会根据参数的类型或个数寻求最匹配的方法予以调用。根据前述的例子,当执行“C.Swap(r e f A,ref B)调用时,系统会调用pub 1 ic void S wap(ref int a,ref int b)方法,当执行c.Swap(ref fA,r ef fB);M 调用时,系统会调用public v oid Swap(re f float a,ref f l o a t b)方法,从而实现对不同的数据类型进行相同解决。【例 4-4】创建一个控制台应用程序,在该程序中实现对两个整型、浮点型、双精度型、十进制型与字符型数据比较大小的功能。us i ng System;n a m e s p ace方法重载一比较值的大小(o c 1 ass Cl a ssl(比较值大小的方法声明pub 1 i c int M a x(i n t a,int b)retur n ab?a:b;。public flo a t Max(flo a t a,float b)ug retu r n ab?a:b;。p u b li c d o u b le M a x (do u ble a,doub 1 e b)0 0 or e t u m ab?a:b;pub 1 ic d ecimal Max(de c imal a,d e cimal b)(re t ur n a b?a:b;)o p u bl i c c har Max(c ha r a,c har b)a(ret u m ab?a:b;*os t atic v o id Mai n(s trin g args)”i nt i A=60,iB=8 0;。f loa t fA=5.2 f,fB=8.8f;do u b 1 e d A=6.2,dB=7.2;oxlecim a 1 m A=8.2m,mB=9.8m;ogch a r c A=a,cB=b;。Cla s si c=new Class 1();。C o nso 1 e.Wr i t e Line(iA 与 i B 较大的值为:0”,c.Max(iAJB);0 coiiso 1 e.Write L i ne(fA 与 fB 较大的值为:0 c.Ma x(f A JB);oConso 1 e.WriteL i ne(”dA 与 dB 较大的值为:0“,c.M a x(dA,dB);ogC o ns o le.Writ e Line(mA 与 mB 较大的值为:0,c.Max(mA,m B);Console.Writ e L i ne(”c A 与 cB 较大的值为:0 ,c.Ma x(c A,cB);ogC o nsole.ReadL i n e();)程序运营结果为:iA 与 iB较大的值为:8 0fA 与 f B 较大的值为:8.8dA与dB 较大的值为:7.2m A与 mB较大的值为:9.8cA 与 c B 较大的值为:b在上面的程序例题中,由于flo a t类型可以隐式转换为doubl e 类型,因此比较float类型的值大小的方法可以省略。i n t 类型可以实现隐式转换,但由于i n t 类型既可以隐式转换为d o u b le 类型,也可以隐式转换为decima 1 类型,所以,比较int类型值大小的方法不能省略,以免产生调用的二义性。4.4 构造函数构造函数是一种特殊的方法成员,构造函数的重要作用是在创建对象(声明对象)时初始化对象。一个类定义必须有至少一个构造函数,假如定义类时,没有声明构造函数,系统会提供一个默认的构造函数,假如声明了构造函数,系统将不再提供构默认造函数。假如只有默认构造函数,在创建对象时,系统将不同类型的数据成员初始化为相应的默认值,如,数值类型被初始化为0,字符类型被初始化为空格,字符串类型被初始化为null(空值),逻 辑(b。1)类型被初始化为false等。例如下面的程序:using System;cl a ss stude n t/类名为 st u d e nt(声明字段p u b lic s trin g id;publ i c str i n g name;pu b lie bool se x;p ubl i c i n t ag e;1cla s s C1 a ss 1(static v o id Ma i n(s t ring args)c h ar SSe x;g s t u dent S1 =ne w s tud e nt();if(Sl.sex=tr u e)ooSSe x 二身;else8s S ex二 女;o n s ol e.W r iteL i ne(“学号:,+Sl.id+”姓名:+SI.name一+”性 别:VSSe x+”年龄:0 ,Sl.a ge);o Cons o 1 e.R e a d Lin e();o)程序运营的结果为:学号:姓名:性别:女年龄:0这是由于系统将s t udent类的数据成员i d 与 n a m e 初始化为n u 11,将 s e x 初始为fa ls e,将 age初始为0 的缘故。假如想在创建对象时,将对象的数据成员初始为指定的值,则需要专门声明构造函数。4.4.1 声明构造函数声明构造函数与声明普通方法相比,有两个特别规定,一是构造函数不允许有返回类型涉及v o id 类型,二是构造函数的名称必须与类名相同。由于通常声明构造函数是为了在创建对象时,对数据成员初始化,所以构造函数往往需要使用形参,例如参考前面的学生(studen t)类,创建一个学生类对象时,需要给出学生的学号、姓名、性别及年龄等,所以学生类构造函数可以声明如下:pub 1 ic student(st r ing I D,s tri n g N AME,b oo 1 S EX,i n t AGE)id=ID;on a me=N AME;s e x=SEX;oage=AGE;)由于声明了上述带参数的构造函数,所以系统不再提供默认构造函数,这样在创建对象时,必须按照声明的构造函数的参数规定给出实际参数,否则将产生编译错误,例如:stu dent S2=new s tudent(1 12 345,张三,t ru e,21);由上述创建对象的语句可知,n e w 关键字后面实际是对构造函数的调用。假如声明构造函数时使用的参数名称与类数据成员名称相同,则构造函数中使用的类数据成员名称需要用关键字t h i s 引导,以免系统分不清形参与数据成员而产生二义性。将上例中的形参名称改为与数据成员同名的构造函数声明如下:pub 1 ic student(s t rin g id,string n ame,b o o 1 se x,in t age)(t h i s.id=i d;this.n a me=name;this,sex=sex;4 h i s.ag e=age;)关 键 字 th is指所创建的对象,是声明对象时,由系统自动传递给构造函数的对象的引用形参。事实上在调用类的非静态方法成员时,系统均会自动传递该引用形参,但是最佳的办法是使形参的名称与数据成员的名称有所区别。4.4.2 重载构造函数构造函数与方法同样可以重载,重载构造函数的重要目的是为了给创建对象提供更大的灵活性,以满足创建对象时的不同需要。例如,在创建一个学生(stu de n t)对象时,有时也许需要只初始化姓名,而不初始化其他值,那么可以在前述声明构造函数的基础上再重载一个只初始化姓名的构造函数,代码如下:p ub l i c s t ud e n t (s t r i n g NAME)(o n a m e=N AME;I由于该构造函数与前述构造函数的参数个数不同,所以是一个合法的构造函数重载。有了这个构造函数后,就可以声明只有一个实参的对象,例如:s t u d e n t S 3=new s t u d e n t(李平)假如在声明了带参数的构造函数后,还想保存默认构造函数,则必须显式声明一个默认构造函数,显式声明的默认构造函数事实上是一个不实现任何功能的空函数。以student类为例,声明默认构造函数如下:p ub l i c s t u d e n t()/显式声明默认构造函数【例4-5】根据本节介绍内容,在控制台应用程序中创建st ude n t类,并声明构造函数及构造函数的重载。us i n g S y s t e m;n a m e s p a c e构造函数c l a s s s t ud e n t类名为s t ud e n t。/声明字段p u b 1 i c s t ring i d;o p u blic s t r ing n a me;p u bli c c h a r sex x;。p u b lie boo 1 s ex;p ublic i n t a g e;。声明构造函数叩ubl i c s t udent()显式声明默认构造函数publ i c stu d e n t(s t r i n g id,s t r i n g n ame,boo 1 sex,in t ag e)。/重载初始化学号、姓名、性别与年龄的构造函数Qthis.id=i d;this.name=n a me;。this.s e x=sex;og t his.age=ag e;叩ublic studen t(s tring NAME)/重载只初始化姓名的构造函数。(。name=NAME;0 0 )c lass Cl a sslo s t a tic v oid Main(string args)c h a r S S e x;/声明无参对象(调用默认构造函数)。s t ud e n t Sl=new s t ud e n t();。i f (S l.s e x =t r ue)。,S S e x=男;e l s e0。S S e x=女;o C o n s o l e.W r i t e L i n e(学号:+S l.i d