http://book.51cto.com/art/201009/224017.htm
第4章 面向对象程序设计
本章覆盖了.NET面试笔试中的对象、事件和委托等方面的题目。这一类题目属于实战类问题,涉及的知识点在面试中经常会出现,但有不少程序员无法正确回答此类问题。建议读者在实际研发的过程中,多多反思本章列出的各类题目,以期加强对这些问题的理解。
4.1 对象(1)
C#是主流的面向对象编程语言之一,它不仅语法简洁优美,而且它完美地支持面向对象的封装、继承和多态,在使用面向对象编程语言开发之前,笔者提倡先了解掌握面向对象的编程思想。在本节中,将集中讲解覆盖.NET面试中和面向对象思想有关的面试题。本节也是比较重要的章节之一,希望读者能认真阅读本章节的内容。
面试题46 类和结构有什么区别
这是一个关于C#语法的面试题,了解和记住这样的语法并不困难。但建议读者在得知答案后反复推敲类和结构的特性,充分掌握它们的特性。
【出现频率】★★★★★
【关键考点】
类
结构
【考题分析】
在C#中,类是功能最为强大的数据类型,类定义了数据类型的数据和行为。程序员可以创建类的实例对象。下面代码展示了一个标准的C#类。
- public class MyClass
- {
- public string name; //定义字段
- public MyClass() //构造方法
- {
- name = "unknown";
- }
- public void SetName(string newName) //无返回值的普通方法
- {
- name = newName;
- }
- }
C#中的结构是使用struct关键字进行定义的,结构是值类型。下面代码展示了一个标准的C#结构。
- public struct Book //定义一个结构
- {
- /*定义结构中的成员变量*/
- public decimal price;
- public string title;
- public string author;
- }
结构与类有以下3个方面的区别
结构是值类型,而类是引用类型。
结构不能被另外一个结构或者类继承,自身也不能被继承。而类完全可以被其他的类继承,其自身也能被继承。
结构没有默认的构造函数,也没有析构函数,并且不能有protected修饰符,但可以不使用new进行初始化。而类有默认的构造函数,也有析构函数,还有protected修饰符,并且必须使用new进行初始化。
注意:结构不能直接被类或结构继承,但结构能够继承接口。
【答案】
虽然结构与类的初始化都能使用new操作符,但它们之间的差别较大,主要体现在3个方面:结构是值类型,而类是引用类型;结构不能被继承而类可以;结构与类的内部结构不同。
面试题47 简述C#中的虚方法
C#中的虚方法是最常见的考题之一,应聘者需对这个题目做好充分的准备。本小节将介绍这方面的知识。
【出现频率】★★★★★
【关键考点】
虚方法(virtual)
虚方法的实现
【考题分析】
使用virtual关键字修饰的方法就是虚方法。下面展示一段标准的C#虚方法的实现。
- public class contact //定义contact类
- {
- public virtual string print() //用关键字virtual定义一个虚方法
- {
- return ("这是虚方法");
- }
- }
- public class MyClass : contact //继承contact类
- {
- public override string print() //重写基类的print()方法
- {
- return ("这是新的方法");
- }
- }
- public static void Main()
- {
- MyClass m = new MyClass(); //初始化MyClass()方法
- Console.WriteLine(m.print()); //调用print()方法
- Console.Read();
- }
在上面的代码中,基类contact定义了一个虚方法print(),而子类里也定义了一个用override 关键字修饰的print()方法。当笔者在主程序中调用子类的print()时,程序输出的结果是:
- 这是新的方法
可以通过运行结果进行分析,程序调用的是子类的print()方法,而不是基类的print()方法,说明override关键字的作用是覆盖基类的虚方法。当然程序员也可以注销子类中的print()方法,再次运行上面的代码,程序输出结果是:
- 这是虚方法
此次程序调用的是基类print方法,说明虚方法提供了实现部分,当子类没有重载基类的方法,默认调用的就是基类方法中的实现部分。
注意:当使用virtual关键字修饰符后,不允许再同时使用abstract、static或override关键字进行修饰。
【答案】
使用virtual关键字修饰的方法就是虚方法,虚方法(virtual)的关键字用于修饰属性、方法、索引器或事件声明,并使它们可以在派生类中被重写。虚方法必须并提供派生类覆盖该方法的选项,并且必须有实现部分。虚方法的作用是可以在派生类中被重写。
4.1 对象(2)
面试题48 简述C#中的密封类和密封方法
C#中的密封类和密封方法是一个语法类的面试题,也是最常见的考题之一。本小节将重点介绍这方面的知识。
【出现频率】★★★★★
【关键考点】
密封类
密封方法
【考题分析】
密封类使用sealed关键字进行修饰,它不能用作其他类的基类,并且它没有派生类。下面是一段简单的实例代码。
- class A {} //定义一个普通的class
- sealed class B : A {} //定义密封类
- class C : B {} //下面是错误的代码,
密封类是不能够被其他类型继承
密封方法(sealed)是使用sealed关键字进行修饰的方法,它并不影响类的继承,但它可以防止重写基类中特定的虚方法。下面是一段简单的实例代码。
- class A
- {
- protected virtual void F() { Console.
WriteLine("A.F");}//定义一个虚方法- }
- class B : A
- {
- sealed protected override void F() {
Console.WriteLine("B.F");}
//定义密封方法- }
- class C : B
- {
- //下面是一段错误的代码,密封方法不能够重写
- protected override void F() { Console.WriteLine("C.F"); }
- }
【答案】
密封类使用sealed关键字进行修饰,它不能用作其他类的基类,并且它没有派生类。密封类的作用是防止其他类继承该类。密封方法是使用sealed关键字进行修饰的方法,它并不影响类的继承,但它可以防止重写基类中特定的虚方法。
面试题49 请介绍C#中静态类构造方法的特点
C#中静态类构造方法及特点是一道常见的面试题,静态类构造是很有用的一个功能,程序员应该掌握这方面的应用。本小节将简单地介绍静态类中的构造方法。
【出现频率】★★★★★
【关键考点】
静态类
构造方法
构造方法的特点
【考题分析】
构造方法是一种特殊的方法,一般用于初始化对象,并且在类实例化之前执行,用于完成对象创建前所需的相关设定。构造方法也称为构造函数,只要创建类或结构,就会调用它的构造函数。类或结构可能有多个接收不同参数的构造函数。
下面的示例代码展示了一个构造方法。
- class StaticSimple
- {
- public static int k =100; //定义静态变量
- static StaticSimple() //创建
自定义的静态构造方法- {
- Console.WriteLine("请注意方法的执行顺序。");
- }
- //注释的这段代码是错误的代码,在一个类里只允许有一
个无参的静态构造方法- //static StaticSimple(String str)
- //{
- // Console.WriteLine("在一个类里只允许有一个
无参的静态构造方法。");- //}
- public StaticSimple() //定义无
参的构造器,同时给j和s赋值- {
- Console.WriteLine("这是无参构造器");
- }
- }
- class Program
- {
- static void Main(string[] args)
- {
- //先调用静态成员k的值,结果会是先执行静态构造方
法,再显示k=100- Console.WriteLine("读者请注意,在输入k值请是
否有其他的输出。k=" + Static- Simple.k);
- //此时不会再出现static构造器的内容,因为前面已经执行了一次
- StaticSimple A = new StaticSimple();
- Console.Read();
- }
- }
通过运行上面的代码可以发现,C#中的构造方法有以下4个特点。
只允许有一个无参的静态构造方法在一个类中存在。
静态的构造方法不会被继承。
在所有静态成员被引用之前执行静态构造方法。
在所有的构造方法中最先被执行的是静态的构造方法。
说明:静态构造方法,是在构造方法的名字前使用static关键字修饰符的构造方法。
【答案】
4.1 对象(3)
面试题50 简述C#派生类中的构造函数
这个问题是一道基础的面试题,主要考察应聘者对派生类和构造方法的概念。本小节将简单地介绍派生类与构造方法。
【出现频率】★★★★
【关键考点】
派生类
构造方
【考题分析】
派生类中的对象不但包含从基类继承的成员对象,也包含了局部定义的成员对象。这时会出现一个问题:基类中有一部分构造函数,而在派生类中也有一些构造函数,当创建派生类对象时,到底运行的是那些构造函数呢?带着这个问题先看下面一段代码。
- class Circle
- {
- public Circle() //默认构造函数
- {
- Console.WriteLine("基类无参构造方法。");
- }
- public Circle(double initialRadius) //带一个参数的构造函数
- {
- Console.WriteLine("基类有参构造方法。");
- }
- }
- class UnitCircle : Circle //派生类
- {
- public UnitCircle(double unitRadius) :base(unitRadius) {}
- //调用基类的构造函数
- public UnitCircle():this(1,2) //调用本类中的构造函数
- {
- Console.WriteLine("派生类无参构造方法。");
- }
- public UnitCircle(int i,int j) //定义
一个带两个参数的构造函数- {
- Console.WriteLine("派生类有参构造方法。"+i.ToString());
- }
- }
- class Program
- {
- static void Main(string[] args)
- {
- UnitCircle u = new UnitCircle(); //调用
派生类中的无参构造函数- UnitCircle u1 = new UnitCircle(2);
- //调用派
生类中的有一个参数的构造函数- Console.Read();
- }
- }
运行上面的代码,请读者注意base与this这两个关键字的作用:
- public UnitCircle(double unitRadius) :base(unitRadius) {}
派生类中的base关键字表示当调用UnitCircle(double unitRadius)构造函数时,它实际调用的是基类中的Circle(double initialRadius)构造函数。
而派生类中的this关键字表示当调用的构造函数,是本派生类中的构造函数。
- public UnitCircle():this(1,2)
【答案】
使用C#派生类中的构造函数时,需要注意关键字base与this的区别,关键字base表示调用基类中的构造函数,而this表示调用本类中的构造函数。
面试题51 简述接口及接口继承
在大多数面向对象的语言中,都有接口这个概念,这些机制是设计高可扩展性的面向对象程序的基础。读者应该做到不仅了解它们的功能、读懂包含这些概念的代码,也能进一步地运用它们设计出面向对象的程序。
【出现频率】★★★★★
【关键考点】
接口(Interface)
接口特性
接口继承
【考题分析】
接口是面向对象编程思想重要特性之一,接口是当把多个继承类中的公共对象部分抽象出来、并封装这些公共对象的行为。接口是为了继承而存在的,如果没有继承,也就不需要接口的存在。
注意:在C#中,类可以通过继承多个接口来丰富自己的行为机制,但类是不可以继承多个类的。
在C#中,接口具有以下9大特性。
接口只定义,不包含方法的实现。
接口可以包含方法、属性、事件和索引器。
接口成员必须是公共的。
接口不能被直接实例化。
接口不能包含任何字段。
接口描述可属于任何类或结构的一组相关行为。
接口自身均可以从多个接口继承。
类和结构均可以从多个接口继承。
接口类似于抽象类,但继承接口的类型必须实现接口中的所有定义的成员对象。
下面代码实现了一个简单的信号灯功能,代码如下:
- interface ITrafficRule //定义
一个接口(交通规则)- {
- void CrossTheRoad(int trafficLight); //定义
一个方法(十字路口)- }
- public class MyPupil : ITrafficRule
- {
- public void CrossTheRoad(int trafficLight)
- //实现
十字路口的信号灯功能的方法- {
- switch (trafficLight)
- {
- case 0: //红灯停
- …
- break;
- case 1: //黄灯等一等
- …
- break;
- case 2: //绿灯行
- …
- break;
- default: //当信号故障,且
两侧没有直行车辆时,可以穿过马路- …
- break;
- }
- }
- }
在代码中,笔者首先定义了一个信号灯规则的接口,随后在接口中定义了一个实现信号的方法。代码如下:
- interface ITrafficRule
- {
- void CrossTheRoad(int trafficLight);
//定义一个方法(十字路口)- }
注意:在笔者定义的接口中仅仅是声明定义了一个方法,并没有任何代码功能的实现。信号灯功能的实现是在它的继承类MyPupil中实现。结合前面对接口及接口继承的特性的说明,就不难理解这段实例代码。
【答案】
接口是把隐式公共方法和属性组合起来,以封装特定功能的一个集合。当定义了接口,就必须在继承类中实现它,这样类就支持接口中所指定的所有属性和成员。
联系客服