第3章C#语法基础
在深入讨论C#面向对象程序设计之前,首先需要掌握C#语言的基本语法结构。本章将讲解C#语法结构的基本要素,包括数据类型、操作符和表达式,以及程序控制结构。
3.1数据类型
数据类型是对客观数据对象的抽象,它将数据和对数据的操作封装为一个整体。C#语言中的数据类型分为值类型和引用类型两大类。值类型包括整数、字符、实数、布尔数等简单值类型,以及结构(struct)和枚举(enum)两种复合值类型;引用类型包括类、接口(interface)、委托(delegate)和数组。这些类型在本质上都是面向对象的。
3.1.1简单值类型
1.整数类型
整数类型是对数学中的整数的抽象,但由于受到计算机的存储限制,程序设计语言中的值类型总是要设置取值范围限制。C#定义了以下8种整数类型:
● int:32位整数,取值范围为-2147483648(-231)~2147483647(231-1)。
● uint:32位无符号整数(即正整数),取值范围为0~4294967295(233-1)。
● long:64位长整数,取值范围为-9223372036854775808(-263)~9223372036854775807(263-1)。
● ulong:64位无符号整数,取值范围为0~18446744073709551615(264-1)。
● short:16位短整数,取值范围为-32768(-215)~32767(215-1)。
● ushort:16位无符号短整数,取值范围为0~65535(216-1)。
● sbyte:8位字节型整数,取值范围为-128(-27)~127(27-1)。
● byte:8位无符号字节型整数,取值范围为0~255(28-1)。
例如,下面的代码就先定义了一个整数变量x,而后将其赋值为80:
int x;
x=80;
上述两行代码还可以合并为:
int x=80;
如果是同类型的多个变量,C#允许将它们的声明语句简写到一行代码中,例如:
int x1=10,x2,x3=20;//x2还未被赋值
下面这些语句则是错误的:
uint x=-10;//错误:无符号整数不能取负数
byte y=500;//错误:超出了取值范围
long z;
Console.WriteLine(z);//错误:z在被赋值前不能使用
定义变量时应尽量选择最为适合的类型:过短的类型可能不足以表达量的变化范围,过长的类型则会造成资源浪费。例如,要表示人的年龄使用byte类型就足够了,要表示人口的数量则使用unit类型比较适合。
2.字符类型
C#中使用char来表示字符类型。由于使用了16为Unicode字符集,因此char类型不仅包含了基本的ASCII 字符还可以表示汉字等各国语言符号。注意:单个字符值要用一对单引号括起来;如果使用了双引号,那么它表示的就是只有一个字符的字符串。例如:
char a=' s ';
char b='人';
string s="人";
对于像单引号、回车符这样的特殊字符,C#中使用加斜杠“\”的转义符来表示。例如:
char a1=' \' ';//a1表示单引号
char a2=' \\ ';//a2表示单斜杠
char a3=' \r ';//a3表示回车符
表3-1列出了C#常见的一些转义符格式。
表3-1 C#常用转义符
转义符含义转义符含义\' 单引号\n 换行
\" 双引号\r 回车
\\ 单斜杠\f 换页
\a 报警(Alert)\t 水平制表符
\b 退格(Backspace)\v 垂直制表符
\c 取消(Esc)\0 空字符
char类型的变量实际上仍以整数方式存储,其取值范围与ushort相同,因此一个字符会使用16位字节的内存空间。
3.实数类型
C#定义了以下3种实数类型。
● float:32位单精度浮点数类型,取值范围为±1.5×10-45~±3.4×1038。
● double:64位双精度浮点数类型,取值范围为±5.0×10-324~±1.7×10308。
● decimal:128位十进制小数类型,取值范围为±1.0×10-28~±7.9×1028。
除了double类型之外,float和decimal类型的变量值都应在小数后加上后缀F和M,以便让编译器知道以何种类型处理这些值,例如:
double x=1.2;
float y=-0.5F;
decimal z=3.2M;
实数类型使用的位数较多,相应的计算也会消耗更多的资源,因此在程序中应尽量使用低精度的类型。有时还可以使用整数来代替实数类型,例如,在货币计算中,如果只需要精确到小数点后两位,那么使用“分”而不是“元”来做单位,就可以将实数运算转换为整数运算。此外,计算机中的实数运算也受到精度和范围的限制,有时候并不能得出数学上完全正确的结果。
4.布尔类型
bool用来表示布尔类型,其可能取值只有两个:true(真)或false(假)。例如:
bool b1=true;
bool b2=false;
布尔类型在计算机内部实际上是使用二进制1和0表示的,其运算效率在各种数据类型之间也是最高的。
3.1.2复合值类型
1.结构
像int、double这些简单值类型都是在.NET类库中预定义的。很多情况下,人们需要将不同的简单值类型组合起来使用,这时候就可以使用结构类型。考虑“复数”这个概念,.NET类库没有类型定义,那么可以在double类型的基础上定义该类型,它由两个double类型的字段组成,其中a表示复数的实部,b表示虚部:
struct ComplexNumber //定义结构类型
{
public double a;
public double b;
}
之后就可以创建该类型的变量,并通过圆点连接符“.”来访问其公有成员,例如:
ComplexNumber c1; //创建结构类型的变量
c1.a=2.5;
c1.b=5;
结构类型还可以有成员方法,这说明结果实际上是部分面向对象的,它也支持信息隐藏和封装,能够进行对象创建和消息通信。在下面的示例程序中,ComplexNumber结构还包含了一个成员方法Write,程序
主方法就调用该方法在控制台输出了两个复数:
//程序P3_1
using System;
namespace P3_1
{
class Program
{
static void Main()
{
ComplexNumber c1; //定义结构变量c1
c1.a=1.5;
c1.b=3;
ComplexNumber c2=c1; //定义结构变量c2
c2.a=c2.b;
Console.Write("c1= ");
c1.Write();
Console.Write("c2= ");
c2.Write();
}
}
struct ComplexNumber
{
public double a;
public double b;
public void Write()
{
Console.WriteLine("{0}+{1}i",a,b);
}
}
}
上面最后一行代码使用了Console类的WriteLine方法的格式化输出功能,它表示用方法的第2个参数值替换第1个字符串参数中的{0}标记,用第2个参数值替换第1个参数中{1}标记……依此类推。程序P3_1的输出内容如下:
c1=1.5+3i
c2=3+3i
实际上,前一小节中介绍的简单值类型在.NET类库内部都是以结构方式来定义的,这些结构不仅包含了变量所存储的值,还提供了类型本身的有关消息。例如,它们都提供了MinValue和MaxValue字段来表示类型的最小值和最大值,实数类型还提供了NegativeInfinity和PositiveInfinity字段表示正无穷大和负无穷大,此外,他们还能够通过成员方法ToString得到数值的字符串表示,通过方法Parse将字符串转换为数值。下面给出了以下代码示例:
short a=short.MaxValue; //a=32767
short b=short.MinValue; //b=-32768
ushort c=ushort.MinValue; //c=0
double x=double.PositiveInfinity;
double y=-x; //y=double.NegativeInfinity
string s=a.ToString(); //s="32767"
int i=int.Parse(s); //i=32767
2.枚举
还有一种情况是要对变量的取值范围作特殊的限定,可以将其定义为枚举类型,在其中“枚举”出所有可能的取值项。考虑“星期”这个概念,其取值可以是星期一到星期日,可以用如下方式定义枚举类型Weekday:
enum Weekday
{
Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
}
枚举类型的变量值是通过“类型名”加连接符“.”再加“枚举项”来使用的,例如,星期一就是“Weekday.Monday”:
Weekday w1=Weekday.Monday;
枚举类型的成员项是使用文字来进行说明的,这有助于对程序的理解,但他们的实际存储类型都是整数。例如,对于枚举Weekday,其第一个值Monday对应整数0,Tuesday对应整数1……最后一个Sunday对应整数6.而如果将Sunday移到最前面,那么该枚举值就成为0,Monday变为1……依此类推。
C#还允许在程序中明确指定枚举值对应的整数值。例如,对于下面的定义,Monday~Sunday所对应的整数值将分别为1~7:
enum Weekday
{
Monday=1,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
}
提示:程序中不用滥用枚举类型。事实上大多数的值都是有范围的;除了程序中常用的一些特殊概念之外,大部分还是应直接使用整数类型,并在程序逻辑中控制范围,如“月份”为1~12,“年级”为1~6,“鞋码”为10~30等,而不是将它们都定义为枚举。
3.1.3类
要将程序P3_1中ComplexNumber的类型由结构转换成类也很简单,只需将其定义的关键字由struct改为class即可:
class ComplexNumber
{
public double a;
public double b;
public void Write()
{
Console.WriteLine("{0}+{1}i",a,b);
}
}
但此时编译程序,Main方法的第二行代码会出错,提示“使用了未赋值的局部变量c1”。这是应为结构是值类型,声明变量时就会分配内存;而类是引用类型,如果只是声明变量,得到的就是一个什么都没有的空对象(null),当然也就不能访问其成员。纠错办法是将Main方法的第一行代码改为如下内容:ComplexNumber c1=new ComplexNumber(); //创建c1对象
C#中的new关键字用于显示创建类的变量,这样才得到了在程序中真实存在的内存对象。修改后得到的完整程序代码如下:
//程序P3_2
using System;
namespace P3_2
{
class Program
{
static void Main()
{
ComplexNumber c1=new ComplexNumber(); //创建c1对象
c1.a=1.5;
c1.b=3;
ComplexNumber c2=c1; //创建c2对象
c2.a=c2.b;
Console.Write("c1= ");
c1.Write();
Console.Write("c2= ");
c2.Write();
}
}
class ComplexNumber
{
public double a;
public double b;
public void Write()
{
Console.WriteLine("{0}+{1}i",a,b);
}
}
}
但编译运行该程序,会看到控制台的输出结果发生了变化:
c1=3+3i
c2=3+3i
这就说明了值类型和引用类型的第二个区别:将值类型的一个变量赋值给另一个变量,原始变量的数据会被拷贝给新变量,之后两个变量是互相独立的;将引用类型的一个变量赋值给另一个变量,实际上新变量只是包含了指向原始对象的指针,对其中任何一个变量的修改都会影响到另一个变量。在程序P3_2中使用引用类型的类时,对c2对象的修改也就同时改变了c1对象。
此外,结构类型之间不允许继承。如果要充分享受继承和多态性等面向对象的优越性,还是应使用类这种完全面向对象的类型。定义一个新类时,如果要使其从另一个类中继承,那么应在新类的名称后接冒号再接基类的名称。
下面的代码先定义了一个Student类,而后定义了其派生类Graduate,这样Graduate就继承了Student的3个字段和一个方法,而supervisor则是Graduate类自己独有的。
class Student //基类
{
public string name;
public int age;
public int grade;
public void Register() { }
}
class Graduate : Student //派生类
{
public string supervisor;
}
前面出现过的string类型也是.NET类库中定义的一个类,但C#语言对字符串有着特殊的处理方式,对于两个string变量s1和s2,语句“s1=s2”并不会使他们指向同一个对象,因此不存在修改一个字符串而影响到另一个字符串的情况。但所有的string对象仍是引用类型;和其他对象类型一样,可将空值null赋给string变量,而对于任何值类型的变量都是不允许的:
string s1=null; //正确
int i1=null; //错误
3.1.4数组
数组是一种聚合类型,它表示具有相同类型的一组对象的集合,这些对象叫做数组元素。数组元素的类型可以是值类型,也可以是引用类型。
1.一堆数组
一堆数组的声明是在数组元素的类型声明后面加上一对中括号,例如声明一个整型数组:
int[] nums;
作为引用类型,数组对象同样需要初始化之后才能使用,而且new关键字后面的类型声明的中括号里需要指定数组的长度,例如:
nums=new int[3];
要访问数组元素,则需要在数组对象后的中括号里指定数组元素的下标位置,其中第一个数组元素的下标为0,第二个的下标为1……依此类推,例如:
nums[0]=3; //数组的第一个元素赋值为3
nums[1]=6; //数组的第一个元素赋值为6
nums[2]=9; //数组的第一个元素赋值为9
Console.WriteLine(nums[1]); //输出数组的第二个元素值
使用数组要防止“越界”,即要访问的数组元素的下标不能超过数组的长度范围,如对上面的数组不能访问nums[3]。但这种错误在编译时不会被发现,只有在运行到该代码时才会报错,所以在编程过程时应格外注意。
为了简化代码,C#还允许在初始化数组对象的同时为其中的元素赋值,例如:
int[] xs=new int [3]{10,20,30};
double[] ys=new double[4]{1.25,3.5,3.75,5};
上面的代码还可以进一步简写为:
int[] xs={10,20,30};
double[] ys={1.25,3.5,3.75,5};
对于一个数组变量,通过其Length属性可以判断数组的长度,例如:
int[] nums={3,6,9};
Console.WriteLine(nums.Length); //输出3
当数组元素的类型为值类型时,数据直接存放在数组中;当数组元素的类型为引用类型时,数组中存放的只是各个引用对象的地址,这时不排除多个数组元素指向同一个对象的可能。看下面的程序示例:
//程序P3_3
using System;
namespace P3_3
{
class Program
{
static void Main(string[] args)
{
ComplexNumber c1 = new ComplexNumber();
c1.a = 1.5;
c1.b = 3;
ComplexNumber[] cs = new ComplexNumber[4];
cs[0] = c1;
cs[1] = new ComplexNumber();
cs[1].a = 4;
cs[1].b = 5;
cs[2] = c1;
cs[3] = cs[1];
cs[0].a = 8;
cs[3].b = 9;
Console.Write("c1=");
c1.Write();
Console.Write("cs[1]=");
cs[1].Write();
Console.Write("cs[2]=");
cs[2].Write();
Console.ReadLine();
}
}
class ComplexNumber
{
public double a;
public double b;
public void Write()
{
Console.WriteLine("{0}+{1}i", a, b);
}
}
}
由于使用的ComplexNumber是引用类型,c1、cs[0]、cs[2]同时指向一个对象,而cs[1]和cs[3]同时指向另一个对象。程序P3_3的输出内容如下:
c1=8+3i
cs[1]=4+9i
cs[2]=8+3i
2.多维数组
多维数组的声明是在元素类型后的中括号里增加逗号,并在初始化时指定每一维的长度。例如,下面的语句创建了一个三行两列的二维整型数组:
int[,] nums=new int[3,2];
访问数组元素时同样需要指定每一维的下标,例如:
nums[0,0]=1;
nums[0,1]=2;
nums[1,0]=3;
nums[1,1]=5;
nums[1,2]=8; //错误:数组越界!
对于多维数组变量,其Length属性返回的是整个数组的总长度,通过其GetLength方法还可以获取每一维
的长度。下面的程序就输出了3个数组的总长度和各维长度,其中创建数组时还使用了不同的简写方式。书写数组元素时要注意不同层次上大括号对的匹配性(C#编译器可自动推算出数组每一维的长度):
//程序P3_4
using System;
namespace P3_4
{
class Program
{
static void Main()
{
int[,] x = new int[2, 3] { { 1, 2, 3 }, { 3, 5, 8 } };
int[,] y = new int[,] { { 10, 50 }, { 25, 75 }, { 50, 150 }, { 100, 80 } };
int[, ,] z = { { { 1, 2 }, { 3, 5 }, { 8, 13 } }, { { 1, 2 }, { 3, 5 }, { 8, 13 } } }; Console.Write("数组x长度为:");
Console.WriteLine(x.Length);
Console.WriteLine("各维长度:{0}*{1}", x.GetLength(0), x.GetLength(1));
Console.Write("数组y长度为: ");
Console.WriteLine(y.Length);
Console.WriteLine("各维长度: {0}*{1}", y.GetLength(0), y.GetLength(1));
Console.Write("数组z长度为:");
Console.WriteLine(z.Length);
Console.WriteLine("各维长度:{0}*{1}*{2}", z.GetLength(0),
z.GetLength(1),z.GetLength(2));
Console.ReadLine();
}
}
}
程序P3_4的输出内容如下:
数组x长度为:6
各维长度:2*3
数组y长度为:8
各维长度:4*2
数组z长度为:12
各维长度为:2*3*2
提示:为了提高程序的可读性,创建数组时还是尽量写出数组各维的长度。此外,数组的维数过高不仅会大量占用系统资源,还会增加程序的复杂度以及数组越界的危险性,因此要控制使用三维以上的多维数组。
3.1.5类型转换
相容的数据类型的变量之间可以进行类型转换。有的转换是系统默认的,叫作隐式(implicit)转换;有的转换则需要明确指定转换的类型,叫作显式(explicit)转换。显式转换不能保证成功,还有可能发生信息丢失,使用时应格外小心。
1.值类型之间的转换
从低精度的简单值类型到高精度的简单值类型可以进行隐式转换,包括以下几种情况:
●如果一种整数类型的取值范围被另一种整数类型的取值范围所涵盖,那么从前者到后者可以进行隐式转换;
●从整数类型到实数类型可进行隐式转换;
●从float类型到double类型可进行隐式转换;
●从char类型到ushort、uint、int、ulong、long、float、double、decimal这些类型可进行隐式转换(这和ushort 类型到其他类型的隐式转换是等价的)。
下面给出了从低精度整数类型到高精度整数类型的转换示例:
sbyte x1=100;
short x2=x1;
int x3=x2;
long x4=x3;
不满足上述隐式转换条件的简单值类型之间只能进行显式转换,转换办法是在要转换的值之前加上一对圆括号,并在括号中写上要转换的目标类型。例如:
double y1=13.56;
int y2=(int)y1;
显示转换时要注意值的范围。很明显,实数显示转换为整数后,实属原来的小数部分就会丢失。再看下面的代码示例:
long z1=100;
int z2=(int)z1;
short z3=(short)z2;
sbyte z4=(sbyte)z3;
执行完上述转换后,z1~z4的值还是100.但如果将z1值改成1000,z2和z3的值仍然不变,但z4的值就会变成-24,这是因为整数1000所对应的16位二进制码为00000011 11101000,但转换到8位整数类型sbyte时就只剩下了后8位11101000,对应十进制整数就是-24,这时程序不会报错,有经验的开发人员也可能推断出预期结果,但在程序中应避免这种情况的出现,而绝不能将其作为一种非常规的“技巧”来使用。
整数类型和char类型之间的转换所表达的是字符的整数编码,此时要注意数字字符和数字本身的区别。C#还规定从各种整数类型到char类型都只能进行显式转换。看下面的代码示例:
int x=6;
char a=(char)x; //显式转换
Console.WriteLine(a);
char b='6';
int y=b; //隐式转换
Console.WriteLine(b);
Console.WriteLine(y);
整数编码6对应的字符是黑桃符号‘?’,因此第3行代码将输出该符号;而字符‘6’对应的编码是54,因此第6行和第7行代码将分别输出6和54。(Console类会自动根据参数的类型来选择输出的格式)
自定义的枚举类型在本质上也是整数,但枚举类型和整数类型之间必须使用显示转换,唯一的例外是常数0可以直接赋值给枚举变量。看下面的程序示例:
//程序P3_5
using System;
namespace P3_5
{
class Program
{
static void Main()
{
Weekday w1 = 0;
Weekday w2 = (Weekday)3;
Weekday w3 = (Weekday)100;
Console.WriteLine(w1);
Console.WriteLine(w2);
Console.WriteLine(w3);
int x = (int)Weekday.Friday;
Console.WriteLine(x);
Console.ReadLine();
}
}
enum Weekday
{
Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday
}
}
该程序前两行输出的w1和w2的值将分别为“Monday”和“Thursday”;而w3被赋予的值已经超出了枚举的定义范围之外,因此程序第三行将直接输出整数100;而将枚举值“Friday”转换为整数时,得到的值为4。
此外,布尔类型与整数等其他类型之间不存在任何转换关系,这和C/C++等语言中的布尔类型是不同的。
2.引用类型之间的转换
引用类型之间转换的基本原则是:从派生类的对象到基类的对象可以进行隐式转换;而从基类对象到派生类对象只能进行显式转换,且不一定成功。这一点很容易理解,因为特殊对象同时也是一般对象,而一般对象不能保证是特殊对象。转换之后,两个变量指向的仍是同一个对象。
例如,在下面的程序中,Graduate是Student的派生类,那么Graduate对象g1可以隐式转换为Student对象s1,而s1实际上仍是一个Graduate对象,因此可以显示转换得到g2;Student对象s2并不是一个Graduate 对象,试图将其显示转换为Graduate对象s3时就会失败(错误信息为“无法将类型为P3_6.Student的对象强制转换为类型P3_6.Graduate”)。
//程序P3_6
using System;
namespace P3_6
{
class Program
{
static void Main()
{
Graduate g1 = new Graduate();
https://www.doczj.com/doc/eb5219587.html, = "陈亮";
Student s1 = g1; //从派生类到基类(从后往前看),隐式转换
Student s2 = new Student();
https://www.doczj.com/doc/eb5219587.html, = "宋燕燕";
Graduate g2 = (Graduate)s1; //从基类到派生类,显式转换
Console.WriteLine(https://www.doczj.com/doc/eb5219587.html,);
//Graduate g3 = (Graduate)s2; //错误,转换失败
Console.ReadLine();
}
}
class Student //基类
{
public string name;
public int age;
public int grade;
public void Register() { }
}
class Graduate : Student //派生类
{
public string supervisor;
}
}
进一步推广到数组的情况。C#规定,如果两个数组的维数相同,而它们的数组元素之间可以进行引用转换,那么两个数组之间也可以进行引用转换,且转换方式与数组元素之间的转换方式相同。例如,从一个Graduate[]数组可隐式转换为一个Student[]数组,反之则要进行显式转换。
例如:
Graduate[] gs1=new Graduate[]{g1,g2};
Student[] ss1=gs1;
Graduate[] gs2=(Graduate[])ss1;
但要记住一点:在元素为值类型的数组之间(如int[]数组和long[]数组之间)不能进行任何转换。
3.值类型和引用类型之间的转换
在C++等传统语言当中,整数、布尔等值类型属于语言的内置类型,而这些内置类型本身不是面向对象的。C#语言则采用了一种全新的观点:程序设计语言的类型系统应当是一个有机的整体,所有的变量都是对象,所有的类型都有一个共同的基类——object类。
object类本身是引用类型,所有其它的引用类型都可以与它进行转换。object同时又是所有值类型的基类,所有值类型的变量都可以隐式转换为object类型,这个过程叫作装箱(Boxing);object类型可以显示转换到值类型,这个过程叫作拆箱(Unboxing)。这样值类型和引用类型两部分就有机地联系在了一起。下面给出了装箱和拆箱的简单示例代码:
int x=3;
object y=x; //装箱
int z=(int)y; //拆箱
3.2操作符和表达式
和数学运算中的概念类似,表达式由操作数和操作符(也叫运算符)组成,其作用是将操作符施加于操作数以得到相应的计算结果,而结果的数据类型由操作数的数据类型决定。对于包含两个或两个以上操作符的复杂表达式,操作符执行的顺序取决于他们的优先级(高优先级的操作符先执行)和结合性(同一优先级的操作符从左向右或是从右向左执行)。使用圆括号可以控制表达式的计算顺序。
3.2.1算术操作符
C#中的算术操作符有加(+)、减(-)、乘(*)、除(/)和取模(%),它们可以作用于各种整数和实数类型,从而完成基本的算术运算。它们都是从左向右执行计算,且后3个操作符的优先级要高于前两个操作符,例如:
int x=2*4-1; //从左向右计算,x=7
int y=6+4/2; //先计算4/2,y=8
使用括号可以改变表达式的求值顺序,确保括号内的表达式总是被单独计算的,例如:
int x=2*(4-1); //x=6
int y=(6+4)/2; //y=5
除操作符用于求两个数的商,模操作符用于求两个数相除的余数。对于相同类型的操作数,表达式的计
算结果总是和操作数的类型相同,因此整数相除和取模的结果仍为整数,而实数相除和取模的结果为实数,这一点何处等数学中的玉树概念是有所区别的。例如:
int x1=10/3; //x1=3
int x2=10%3; //x2=1
double y1=5.4/1.5; //y1=3.6
double y2=5.4%1.5; //y2=0.9
枚举变量可与整数进行加减运算,这实际上是先将枚举转换为整数,运算之后再将结果重新转换为枚举。两个枚举变量相减将得到一个整数,但它们不能直接相加。例如:
Weekday w1=Weekday.Friday+1; //w1=Weekday.Saturday
Weekday w2=Weekday.Thursday-2; //w2=Weekday.Tuesday
int i1=w1-w2; //il=4
int i2=w1+w2; //错误,枚举变量不能相加
3.2.2自增和自减操作符
计算机程序中会经常用到加1和减1运算,C#中定义了自增操作符“++”和自减操作符“--”。和算术操作符不同,这两个操作符都只有一个操作数,而它们的优先级也高于算术操作符。
在表达式中,当操作符“++”放在变量之前时,变量值先被加1,而后表达式再使用该变量;而当操作符“++”放在变量之后时,表达式先使用该变量,而后变量值被加1.操作符“--”的情况也是类似的。例如,在下面的代码中,6次输出的内容将分别是100、102、102、49、49、48。
int x=100;
Console.WriteLine(x++); //先输出100,而后计算x=x+1=101
Console.WriteLine(++x); //先计算x=x+1=102,而后输出102
Console.WriteLine(x); //输出102
int y=50;
Console.WriteLine(--y); //先计算y=y-1=49,而后输出49
Console.WriteLine(y--); //先输出49,而后计算y=y-1=48
Console.WriteLine(y); //输出48
3.2.3位操作符
位操作符是对数据按二进制位进行运算的操作符,具体包括以下几种。
●取反操作符“~”:作用于一个操作数,且必须放在操作数前,表示对操作数的各二进制位取反(0变为1,1变为0)
●左移位操作符“<<”:作用于两个操作数,表示将左操作数的各二进制位依次左移,左边的高位被舍弃,右边的低位顺序补0,移动的位数由右操作数指定。
●右移位操作符“>>”:作用于两个操作数,表示将左操作数的各二进制位依次右移,右边的低位被舍弃,左边的高位则对正数补0、对负数补1,移动的位数由右操作数指定。
●与操作符“&”:作用于两个操作数,表示对两个操作数的对应二进制位依次进行与运算。
●或操作符“|”:作用于两个操作数,表示对两个操作数的对应二进制位依次进行或运算。
●异或操作符“^”:作用于两个操作数,表示对两个操作数的对应二进制位依次进行异或运算。
取反操作符的优先级高于算术操作符,其他的则低于算术操作符。与、或、异或这3个操作符可以作用于整数类型和布尔类型,它们的运算规则见表3-2;其他的位操作符则只能作用于整数类型(或是能够转换为整数的其他类型)。
表3-2 与、或、异或运算规则
操作数与或异或
0和0 0 0 0
0和1 0 1 1
1和1 1 1 0 false和false false false false
false和true false true true
true和true true true false
进行位运算时,要注意有符号整数的二进制首位表示符号位(0表示正数,1表示负数)。下面各出了一些简单的位运算代码示例,其各行输出内容分别是-21、9、80、-2、20、-10、-30。
byte x = 20; //二进制00010100
sbyte y = -10; //二进制11110110
Console.WriteLine(~x); //11101011(-21)
Console.WriteLine(~y); //00001001(9)
Console.WriteLine(x << 2); //01010000(80)
Console.WriteLine(y >> 3); //11111110(-2)
Console.WriteLine(x & y); //00010100(20)
Console.WriteLine(x | y); //11110110(-10)
Console.WriteLine(x ^ y); //11100010(-30)
位运算的效率一般会高于加减乘除等算术运算。以移位运算为例,在整数的有效范围内将其左移n位相当于乘以2的n次方,右移n位则相当于除以2的n次方。例如,下面的两行代码得到x和y的值是相同的,但第二行代码的效率会大大高于第一行:
int x=256*256; //x=65536
int y=256<<8; //y=65536
3.2.4赋值操作符
前面已经看到了简单赋值操作符“=”的用法,它表示将右操作数的值赋予左操作数,前提是右操作数的类型与左操作数相同,或是可隐式转换为左操作数的类型。
为了简化程序代码,C#还提供了10个复合赋值操作符“+=”、“-=”、“*=”、“/=”、“%=”、“<<=”、“>>=”、“&=”、“|=”、“^=”,它们实际上是将加、减、乘、除、取模、左移位、右移位、与、或、异或这些运算和简单赋值结合起来。例如,“x+=y”等价于“x=x+y”,“x&=y”等价于“x=x&y”等。
和一般算术运算不同,赋值操作符属于右结合的操作符,即表达式会按照从右向左的顺序进行赋值运算。例如,执行完下面的语句后,x的值为100,而y和z的值均为1000:
int x=10,y=10;
int z=y*=x*=10; //先计算x*=10,在计算y*=x,最后执行z=y
赋值操作符在所有操作符中的优先级最低,即表达式总是计算等号右边的部分而再进行赋值运算。例如,表达式“x+=y+z”会先计算y与z的和,再将其加到x上。
3.2.5关系操作符
关系操作符用于对指定的条件进行判断,并返回一个布尔值来表示判断结果。其中“==”、“!=”、“<”、“<=”、“>”、“>=”这6个操作符又叫做比较操作符,它们分别用于判断左操作数是否等于、不等于、小于、小于等于、大于、大于等于右操作数。默认情况下,它们可用于值类型之间的大小比较,而“==”和“!=”操作符还可用于比较两个引用类型的变量是否指向同一个对象。下面的示例代码说明了这一点:
int x1=5;
int x2=x1;
Console.WriteLine(x1==x2); //输出true
object o1=x1;
object o2=x2;
object o3=o1;
Console.WriteLine(o1==o2); //o1和o2指向不同的对象,输出false
Console.WriteLine(o1==o3); //o1和o3指向同一个对象,输出true
提示:在C#中如果将相等操作符“==”误写为赋值操作符“=”,系统将给出语法错误提示,例如,对于整数x和y,语句“bool b=(x=y)”不能通过编译,这有助于发现代码错误。此外,C#语言不支持连续的关系比较,如语句“bool b=(x==y==z)”和“bool b=(x>y>z)”都是错误的。
C#还有一个特殊的关系操作符“is”它用于判断左操作数的类型是否为右操作数,例如:
int x=5;
object o=x;
Console.WriteLine(o is object); //输出true
Console.WriteLine(o is int); //输出true
Console.WriteLine(o is long); //输出false
操作符“is”主要用于引用类型的判断,只要左操作数的类型为右操作数或是右操作数的派生类型,表达式就返回true。不过如果左操作数为null,那么始终返回false。
3.2.6逻辑操作符
C#中提供了3个逻辑操作符:
●逻辑与操作符“&&”:有两个操作数,用于判断它们的值是否都为true。
●逻辑或操作符“||”:有两个操作数,用于判断它们的值是否至少有一个为true。
●逻辑非操作符“!”:只有一个操作数,用于判断其值是否为false。
以枚举Weekday为例,判断某个枚举变量w1是否为周末的表达式可以写成:
bool b1=(w1==Weekday.Saturday)||(w1==Weekday.Sunday);
而判断w1是否为工作日的表达式可以写成以下两种形式:
bool b2=!((w1==Weekday.Saturday)||(w1==Weekday.Sunday));
bool b3=(w1!=Weekday.Saturday)&&(w1!=Weekday.Sunday);
综合使用这些逻辑操作符,可以对各种复杂的逻辑组合条件进行判断。例如,判断某一个整数变量year 是否表示闰年的语句可以写成:
bool bLeap=(year%400==0)||((year%4==0)&&(year%100!=0));
在逻辑操作符的求值过程中有时不需要计算完整个表达式就可以得到结果,这称为逻辑表达式的“短路”效应。例如,对于上面这行代码,当year的值为2000时,第一对括号中的条件表达式的值为true,那么bLeap 的值就直接确定为true,而不再需要计算操作符“||”右侧的部分。
再看一个考核程序实例,考核小组由5名考官(含1名主考官、2名本单位考官和2名另外单位考官)组成,考核通过的条件是:主考官同意,且至少有1名本单位考官和1名外单位考官同意。将每名考官的评分依次放在一个布尔数组x中,下面的程序P3_7就通过一个复合逻辑表达式来判断考核结果,程序同时还输出了表达式中进行关系判断的次数:
//程序P3_7
using System;
namespace P3_7
{
class Program
{
static void Main()
{
bool[] x = new bool[5];
Console.WriteLine("请依次输入每名考官的评分,1通过,0不通过:");
x[0] = (Console.ReadLine() == "1");
x[1] = (Console.ReadLine() == "1");
x[2] = (Console.ReadLine() == "1");
x[3] = (Console.ReadLine() == "1");
x[4] = (Console.ReadLine() == "1");
int i = 0;
bool b = (x[i++] && (x[i++] || x[i]) && (x[++i] || x[++i]));
Console.WriteLine("考核结果为:{0}", b);
Console.WriteLine("判断此次为:{0}", i);
}
}
}
程序P3_7的输出结果如图3-1所示。利用逻辑运算的短路效应,只要主考官不同意,考核就不通过,此时只需进行一次判断;要确定考核通过,至少需要进行3次判断。
图3-1 程序P3_7的输出结果示例
3.2.7条件操作符
C#中还提供了一个条件操作符“?:”,它的特殊性在于它有3个操作数,使用时符号“?”和“:”分别位于3个操作数之间,其形式为:
b ? x : y
其中第一个操作数b为布尔类型,如果其值为true,则返回x的值,否则返回y的值。例如,下面的语句可用于计算两个整数x和y的最小值:
int z=(x<=y)?x:y;
注意:在任何情况下,条件表达式都不会对x和y同时进行求值,但它仍要求x和y的类型是一致的(相同或是能进行隐式转换)。例如,下面最后一行代码是错误的,因为整数i和字符串s属于不同的类型:
int i=5;
string s="abc";
Console.WriteLine((i%2==0)?i:s); //错误:i和s类型不同!
而将该行语句改为如下内容就是正确的了:
Console.WriteLine((i%2==0)?i.ToString():s); //正确
条件操作符的优先级仅高于赋值操作符,但低于其他的任何操作符。如果一个表达式中使用了多个条件操作符,要注意它们是从右向左结合的,例如,求3个整数的最大值的表达式可以写成:
int w=x>=y?x>=z?x:z:y<=z?z:y
该语句相当于:
int w=(x>=y)?((x>=z)?x:z):((y<=z)?z:y)
不过,为了提高程序的可读性,还是应尽量使用括号来明确条件表达式的求值顺序。
3.3控制结构
方法中的各行代码通常是按照顺序依次执行的。若要改变代码的执行流程,就要使用控制结构。结构化程序设计中最常见的3种控制结构分别是选择、循环和跳转。
3.3.1选择结构
选择结构是根据不同的条件来选择要执行的语句,C#中提供了if和switch两种选择结构。
1.if选择结构
最简单的选择结构是if语句,其形式为:
if (b-exp)
{
statement;
}
其中b-exp表示一个布尔类型的表达式,其值为true时才会执行其后的语句statement(如果该语句只有一行代码,那么其外的一对大括号可以省略)。例如,下面的代码判断整数i是否为一个偶数,如果是,则将i 除以2:
if (i%2==0)
i=i/2;
稍微复杂一点的选择结构是if-else语句,其形式为:
if (b-exp)
{
statement1;
}
else
{
statement2;
}
程序在b-exp的值为true时执行语句statement1,否则执行语句statement2。下面的代码就使用了if-else语句来求两个整数的最小值(注意:不要把变量z的声明放在if-else结构之内,否则之后的语句将不能使用该变量):int z;
if(x<=y)
z=x;
else(x<=y)
z=y;
Console.Write(z);
如果要实现两条以上的选择路径,那么可在if-else语句中嵌入多个else if分支,形如:
if(b-exp1)
{
statement_1;
}
else if(b-exp2)
{
statement_2;
}
else if
…
b-exp statement 结束
b-exp False
true statement1 结束
False
true statement2
else {
statement_n; }
程序首先检查布尔表达式b-exp1的值,为true 时执行语句statement_1;否则检查b-exp2的值,为true 时执行语句statement_2……以此类推;如果所有条件都不成立,则执行最后一个else 后的语句statement_n 。 假设整数x 表示考试分数,80~100分为优,60~79分为良,40~59分为中,40分以下为差,那么下面的代码可根据分数来输出成绩的等级: if (x >= 80)
Console.WriteLine("优"); else if (x >= 60)
Console.WriteLine("良"); else if (x >= 40)
Console.WriteLine("中"); else
Console.WriteLine("差");
当然,改变一下各个分支的顺序也不会影响到程序的判断结果: if (x < 40)
Console.WriteLine("差"); else if (x < 60)
Console.WriteLine("中"); else if (x < 80)
Console.WriteLine("良"); else
Console.WriteLine("优");
以上3种if 语句的选择流程可用图3-2来说明。
开始
(a)单if 结构
开始
(b)if-else 结构
开始 b-exp1 b-exp2 statement_1
statement_2
statement_n … 结束
False
False
true
true
(c)if-else if-else 结构 图3-2 if 选择结构流程图
if-else 语句中还可以嵌套if-else 语句,此时每个else 分支都与其前面最近的一条不带else 分支的if 语句组成一个if-else 对。不过在这种情况下,还是应尽量使用大括号对来明确if-else 语句的具体结构。程序P3_8就使用了嵌套if-else 语句来计算3个整数的最大值: //程序P3_8 using System; namespace P3_8 {
class Program {
static void Main() {
Console.Write("请输入x 的值:"); int x = int.Parse(Console.ReadLine()); Console.Write("请输入y 的值:"); int y = int.Parse(Console.ReadLine()); Console.Write("请输入z 的值:"); int z = int.Parse(Console.ReadLine()); Console.Write("最大值为:"); if (x >= y) {
if (x >= z)
Console.WriteLine(x); else
Console.WriteLine(z); } else {
if (y >= z)
Console.WriteLine(y); else
Console.WriteLine(z); } } } }
开始
exp=c-exp1
exp=c-exp2
statement_1
statement_2
statement_n
…
结束
False
False
true true 2.switch 选择结构
实现多条选择路径的另一种方式是使用switch 语句,其形式为: switch(exp) {
case c-exp1: statement_1; break; case c-exp2: statement_2; break; ……
default: //可选 statement_n; break; }
其中switch 标签后的exp 为控制表达式,其类型可以是整数(包括字符和枚举)和字符串,而各个case 标签后的常量表达式c-exp 的类型必须与控制表达式的类型一致(相同或是能进行隐式转换)。程序执行时会将exp 的值与表达式c-exp1、c-exp2……依次进行比较,如果相等,则执行对应case 标签下的statement 语句,然后执行break 语句来结束switch 语句;如果控制表达式的值与所有常量表达式的值均不匹配,则执行default 标签后的语句,这里default 部分是可选的。switch 选择结构执行的流程图如图3-3所示。
图3-3 switch 选择结构流程图
以前面的判断成绩等级的代码为例,使用switch 语句可以将其改写为如下代码: int i=x/20; switch(i) { case 5:
Console.WriteLine("优"); break; case 4:
Console.WriteLine("优"); break; case 3:
Console.WriteLine("良"); break; case 2:
Console.WriteLine("中"); break;
Console.WriteLine("差");
break;
}
显然,switch语句中各个常量表达式的值不能相等。此外,C#还规定每个case分支都必须以break、return、goto或throw语句来结束,而且语句中的代码都不能修改控制表达式的值,这就使得switch语句中case分支的排列顺序不会影响到整个语句的功能。
提示:对于经常使用的选择语句,如果能够判断出不同条件发生的概率,那么应尽量将概率较高的if或case 分支放在选择结构靠前的位置,这样能够减少程序中进行判断的次数,提高程序效率。例如,如果考试成绩x在60~79分之间的学生数量最多,那么可以将相应的条件(x>=60&&x<80)作为第一个if分支,或是将3作为判断(x%20)的第一个case分支。
3.3.2循环结构
循环结构用于代码段的重复执行。C#中提供了while、do-while、for和foreach这4种循环结构。
1.while循环结构
while循环结构指定一个条件,并在该条件为真时反复执行循环体。其形式为:
while(b-exp)
{
statement;
}
假设存款本金为1000元,年利率为8%,下面的代码使用了while循环来计算5年后的本息合计金额:decimal x=1000;
int i=0;
while(i<5)
{
x=x*(1+0.08M);
i++;
}
Console.WriteLine(x);
循环结构在控制台程序中的一个典型应用就是执行某项功能,直至用户按下特定的键后才退出程序。程序P3_9就给出了这样一个例子:
//程序P3_9
using System;
namespace P3_9
{
class Program
{
static void Main()
{
Console.WriteLine("按Q退出,按其他键继续");
while (Console.ReadKey().KeyChar != 'Q')
{
Console.WriteLine("欢迎光临");
Console.WriteLine("按Q退出,按其他键继续");
}
《面向对象程序设计C++》期末考试试卷(B) 一、单项选择题(每小题2分,共40分) 1、关于C++与C语言关系的描述中,()是错误的。 A.C语言是C++语言的一个子集B.C语言与C++语言是兼容的 C.C++语言对C语言进行了一些改进D.C++语言和C语言都是面向对象的 2、已知:int m=10; 下列表示引用的方法中,()是正确的。 A.int &x=m; B.int &y=10; C.int &z; D.float &t=&m; 3、考虑下面的函数原型声明:void testDefaulParam(int a,int b=7,char z='*'); 下面函数调用中,不合法的是()。 A.testDefaulParam(5); B.testDefaulParam(5,8); C.testDefaulParam(5,'#'); D.testDefaulParam(0,0,'*'); 4、系统在调用重载函数时往往根据一些条件确定哪个重载函数被调用,在下列选项中,不能作为依据的是()。 A.函数的返回值类型B.参数的类型C.函数名称D.参数个数 5、下列有关C++类的说法中,不正确的是()。 A.类是一种用户自定义的数据类型 B.只有类中的成员函数或类的友元函数才能存取类中的私有成员 C.在类中,如果不做特别说明,所有成员的访问权限均为私有的 D.在类中,如果不做特别说明,所有成员的访问权限均为公用的 6、已知X类,则当程序执行到语句X array[3];时,调用了()次构造函数。 A.0 B.1 C.2 D.3 7、有关析构函数的说法,不正确的是()。 A.析构函数有且仅有一个 B.析构函数和构造函数一样可以有形参 C.析构函数的功能是在系统释放对象之前作一些内存清理工作 D.析构函数无任何函数类型
一、填空题(每空1分,共14分) 1、观看以下程序: class point{ public: void show() {cout<<”point”< (A)指针(B)引用(C)枚举(D)结构 3、类成员的访问权限中,()只能被本类的成员函数和其友元函数访问。 (A)share (B)public (C)private (D)protected 4、关于构造函数,下列说法不正确的是()。 (A)构造函数名字和类名相同 (B)构造函数在创建对象时自动执行 (C)构造函数无任何函数返回类型 (D)构造函数有且只有一个 5、派生类可以访问其基类的()。 (A)公有成员(B)保护成员 (C)私有派生(D)公有和保护成员 6、下列关于运算符重载的描述中,错误的是()。 (A)运算符重载不可以改变操作数的个数 (B)运算符重载不可以改变运算符的功能 (C)运算符重载不可以改变结合方向 (D)运算符重载不可以改变运算优先级 7、C++语言是从早期的C语言逐渐发展演变而来的.与C语言相比,它在求解问题方法上进行的最大改进是() (A)面向过程(B)面向对象(C)安全性(D)复用性 8、对于类中定义的成员,其隐含访问权限为()。 A.public B.protected C.private D.static 9、下面有关重载函数的说法中正确的是() (A)重载函数必须具有不同的返回值类型;(B)重载函数形参个数必须不同; (C)重载函数必须有不同的形参列表(D)重载函数名可以不同; 10、有如下的对类“CSample”的说明,其中()是错误的。 class CSample { A.int a=23; B.CSample(); public: C.CSample(int val); D.~ CSample(); 11、在int a=3,int *p=&a;中,*p的值是( ) A.变量a的地址值 B.无意义 C.变量p的地址值 D.3 12、每个类()构造函数。 (A)只能有一个(B)只可有公有的 第1章习题答案 名词解释 抽象:抽象(Abstract)就是忽略事物中与当前目标无关的非本质特征,而强调与当前目标有关的本质特征,从而找出事物的共性,并把具有共性的事物划为一类。面向对象方法中的抽象,是指对具体问题(对象)进行概括,抽出一类对象的公共性质并加以描述的过程。 封装:面向对象方法中的封装就是把将抽象出来的对象的属性和行为结合成一个独立的单位,并尽可能隐蔽对象的内部细节。 消息:消息是面向对象程序设计用来描述对象之间通信的机制。一个消息就是一个对象要求另一个对象实施某种操作的一个请求。 填空题 (1) 面向过程的结构化程序设计方法面向对象程序设计方法 (2) 顺序选择循环 (3) 程序=数据结构+算法程序=对象+消息 (4) 过程类 (5) 抽象性封装性继承性多态性 选择题 (1) B (2) C (3) D (4) C (5) D (6) A (7) A (8) B C (9) A D 判断题 (1) t (2) f (3) f (4)f (5) t (6) f (7) f (8) t 简答题 (1) 结构化程序设计方法着眼于系统要实现的功能,从系统的输入和输出出发,分析系统要做哪些事情,进而考虑如何做这些事情,自顶向下地对系统的功能进行分解,来建立系统的功能结构和相应的程序模块结构,有效地将一个较复杂的程序系统设计任务分解成许多易于控制和处理的子任务,便于开发和维护。 随着程序规模与复杂性的增长,这种面向过程的结构化程序设计方法存在明显的不足之处。首先是数据安全性问题,由于数据被每个模块所共用,因此是不安全的,一旦出错,很难查明原因。其次是可维护性及可重用性差,它把数据结构和算法分离为相互独立的实体,一旦数据结构需要改变时,常常要涉及整个程序,修改工作量极大并容易产生新的错误。每一种相对于老问题的新方法都要带来额外的开销。另外,图形用户界面的应用程序,很难用过程来描述和实现,开发和维护也都很困难。 (2) 面向对象的程序设计方法中,将程序设计为一组相互协作的对象(Object)而不是一组相互协作的函数。在程序中,属性用数据表示,用来描述对象静态特征;行为用程序代码实现,用来描述对象动态特征。可见,在面向对象的程序设计方法中,对象是数据结构和算法的封装体。对象之间存在各种联系,它们之间通过消息进行通信。程序可表示为: 程序=对象+消息 在面向对象程序设计中,它着重于类的设计。类正是面向对象语言的基本程序模块,通过类的设计,来完成实体的建模任务。类通过一个简单的外部接口,与外界发生关系。一个类中的操作不会处理到另一个类中的数据,这样程序模块的独立性、数据的安全性就有了良好的保障。程序的执行取决于事件发生的顺序,由顺序产生的消息来驱动程序的执行。不必预先确定消息产生的顺序,更符合客观世界的实际。并且面向对象程序设计方法提供了软件重用、解决大问题和复杂问题的有效途径,具有抽象性、封装性、继承性和多态性等特点。 1 什么是面向对象程序设计,它与传统的结构式程序有什么不同。 面向对象程序设计是一种适用于设计、开发各类软件的范型。它是将软件看成是一个由对象组成的社会:这些对象具有足够的智能,能理解从其他对象接受的信息,并以适当的行为作出响应;允许低层对象从高层对象继承属性和行为。通过这样的设计思想和方法,将所模拟的现实世界中的事物直接映射到软件系统的解空间。 与传统的结构式程序设计相比,面向对象程序设计吸取了结构式程序设计的一切优点(自顶向下、逐步求精的设计原则)。而二者之间的最大差别表现在: ·面向对象程序采用数据抽象和信息隐藏技术使组成类的数据和操作是不可分割的,避免了结构式程序由于数据和过程分离引起的弊病。 · 面向对象程序是由类定义、对象(类实例)和对象之间的动态联系组成的。而结构式程序是由结构化的数据、过程的定义以及调用过程处理相应的数据组成的 2 用面向对象方法建立模型的思维过程是怎样的。 用面向对象方法建立拟建系统的模型的过程就是从被模拟现实世界的感性具体中抽象要解决的问题概念的过程。这种抽象过程分为知性思维和具体思维两个阶段,其中:·知性思维是从感性材料中分解对象,抽象出一般规定,形成了对对象的普遍认识。·具体思维是从知性思维得到出的一般规定中揭示的事物的深刻本质和规律,其目的是把握具体对象的多样性的统一和不同规定的综合。 3 解释以下概念: ①对象:在现实世界中,对象就是可以感觉到的实体。每个对象具有一个特定的名字以 区别于其他对象;具有一组状态用来描述它的某些特性;具有一组操作,每一个操作决定对象的一种功能或行为(为自身服务的操作和为其他对象提供服务的操作)。而在面向对象系统中,对象是可以标识的存储区域。每个对象的状态被保存在此区域中,而实现一类对象行为的操作(代码)被保存在另外相关的存储器区域中。 ②消息:消息是要求某个对象执行其某种功能操作(方法)的规格说明。因此,消息是 由消息的接收者、消息要求提供的操作(消息名)和必要的参数组成的。 ③类:在现实世界中,类是对一组具有共同特性(属性和行为)的客观对象的抽象。而 在面向对象系统中,类是由程序员自定义的具有特定结构和功能的类型,是一种代码共享的手段。 ④实例:任何一个对象都是该对象所属类的一个具体实例。 ⑤公有消息:是由对象外向对象发送的消息,用于激活该对象的某种方法。 ⑥私有消息:是由对象向自身发送的消息,用于内部操作;该类消息不能从对象外向该 对象发送。 ⑦消息序列:在面向对象系统中一个事件的发生总会有多个对象的多次相互作用才能完 成,使得这些对象能够相互作用的消息组成的序列被称为消息序列。 4 类与实例的关系如何? 类是创建对象的模板,而对象是实现类的实例。属于同一类的不同实例必须具有: ·相同的操作集合; ·相同的静态属性集合; ·不同的对象名和属性动态值。 第3章面向对象程序设计基础 第3章面向对象程序设计基础 【1】什么是Java程序使用的类?什么是类库? 答:类是将一类事物的特性描述出来,然后如果用这个类来定义对象,则该对象就拥有了这个类所描述的所有特性。 在Java系统中,系统定义好的类根据实现的功能不同,可以划分成不同的集合,每个集合称为一个包,所有包合称为类库。 【2】如何定义方法?在面向对象程序设计中方法有什么作用? 答:方法的定义由两部分组成:方法声明和方法体。 方法的声明如下: 返回值类型方法名(参数类型形式参数1,参数类型形式参数2…){ 程序代码; 返回值; } 在面向对象程序设计中,方法的作用是完成对类和对象属性操作。 【3】简述构造方法的功能和特点。下面的程序片段是某学生为student类编写的构造方法,请指出其中的错误。 void Student(int no,String name) { studentNo=no; studentName=name; return no; } 答:构造方法的功能是:构造方法是一个特殊的方法,主要用于初始化新创建的对象; 特点:构造方法的方法名要求与类名相同,用户不能直接调用,只能通过new运算符调用,而且构造方法是不返回任何数据类型,甚至也不返回void数据类型,即不能在构造方法前加void。 错误之处:(1)构造方法Student()前不能加void,(2)不能用return语句,(3)类名Student 首字母S改成小写s. 【4】定义一个表示学生的student类,包括的域有学号、姓名、性别、年龄,包括的方法有获得学号、姓名、性别、年龄及修改年龄。编写Java程序创建student类的对象及测试其方法的功能。 class Student { String id; String name; String sex; int age; void talk(){ 面向对象程序设计教程(C++语言描述)题解与课程设计指导 第1章 面向对象程序设计概论 一、名词解释 抽象封装消息 【问题解答】 面向对象方法中的抽象是指对具体问题(对象)进行概括,抽出一类对象的公共性质并加以描述的过程。 面向对象方法中的封装就是把抽象出来的对象的属性和行为结合成一个独立的单位,并尽可能隐蔽对象的内部细节。 消息是面向对象程序设计用来描述对象之间通信的机制。一个消息就是一个对象要求另一个对象实施某种操作的一个请求。 二、填空题 (1)目前有面向过程的结构化程序设计方法和面向对象的程序设计方法两种重要的程序设计方法。 (2)结构化程序设计方法中的模块由顺序、选择和循环3种基本结构组成。(3)在结构化程序设计方法中,程序可表示为程序=数据结构+算法;而面向对象的程序设计方法,程序可表示为程序=对象+消息。 (4)结构化程序设计方法中的基本模块是过程;而面向对象程序设计方法中的基本模块是类。 (5)面向对象程序设计方法具有抽象性、封装性、继承性和多态性等特点。 三、选择题(至少选一个,可以多选) (1)面向对象程序设计着重于( B )的设计。 A. 对象 B. 类 C. 算法 D. 数据 (2)面向对象程序设计中,把对象的属性和行为组织在同一个模块内的机制叫做(C )。 A. 抽象 B. 继承 C. 封装 D. 多态 (3)在面向对象程序设计中,类通过( D )与外界发生关系。 A. 对象 B. 类 C. 消息 D. 接口 (4)面向对象程序设计中,对象与对象之间的通信机制是(C )。 A. 对象 B. 类 C. 消息 D. 接口 (5)关于C++与C语言的关系的描述中,(D )是错误的。 A. C语言是C++的一个子集 B. C语言与C++是兼容的 C. C++对C语言进行了一些改进 D. C++和C语言都是面向对象的 【结果分析】 C语言是面向过程的。C++语言是一种经过改进的更为优化的C语言,是一种混合型语言,既面向过程也面向对象。 (6)面向对象的程序设计将数据结构与( A )放在一起,作为一个相互依存、不可分割的整体来处理。面向对象程序设计教程 (马石安版)
什么是面向对象程序设计
第3章 面向对象程序设计基础
面向对象程序设计教程答案
《面向对象程序设计》答案