最近找工作面的面试题目汇总(一)
简介
最近面了几间公司,每次面试完我都特意把题目记住并写在云笔记上,用于写博客文章,也为了给新手们补补基础。
1. 抽象类的介绍,抽象类里的虚函数和抽象函数
参考文档
-
简介
- 一个抽象类可以包含抽象和非抽象方法,当一个类继承于抽象类,那么这个派生类必须实现所有的,一个抽象方法是一个没有方法体的方法。
- 通过声明派生类也为抽象,我们可以避免所有或特定的虚方法的实现,这就是抽象类的部分实现。
- 在 C# 中,一个抽象类能够继承另一个非抽象类,另外,继承了基类的方法,添加新的抽象和非抽象方法是可行的。
- 一个抽象类也能从一个接口来实现,这种情况,我们必须为所有的方法提供方法体,这些方法是来自接口。
- 我们不能把关键字 abstract 和 sealed 一起用在 C# 中,因为一个密封类不能够被抽象。
- 一个抽象类必须为所有的接口成员提供实现
- interface I
- {
- void M();
- }
- abstract class C: I
- {
- public abstract void M();
- }
抽象类特征
- 抽象类不能被实例化;
- 抽象类可以包含抽象方法和访问器;
- 不能把抽象类用密封 (sealed) 来修饰,那就意味着类不能被继承,这违反抽象类被继承的原则;
- 一个派生于一个抽象类的非抽象类必须包括所有的继承来的抽象方法和访问器的实现;
- 在方法和属性中使用 abstract 关键字意味着包含它们的实现
抽象方法特征
- 抽象方法是隐式的虚方法;
- 抽象方法的声明只能在抽象类中;
- 因为抽象方法声明只提供一个无实现的方式,没有方法体;
- 方法体的实现被覆写方法提供,覆写方法是一个非抽象类的成员;
- 抽象属性的行为和抽象方法相像,除了不同的声明形式。
- 在一个静态属性中使用 abstract 是一个错误。
* 一个抽象属性能够通过派生类使用 override 实现.
2. 虚函数和抽象方法
参考文档
虚方法的特点
- 虚方法前不允许有 static,abstract, 或 override 修饰符;
- 虚方法不能是私有的,因此不能使用 private 修饰符;
- 我们知道一般函数在编译时就静态地编译到了执行文件中,其相对地址在程序运行期间是不发生变化的,而虚函数在编译期间是不被静态编译的,它的相对地址是不确定的,它会根据运行时期对象实例来动态判断要调用的函数,
其中那个申明时定义的类叫申明类,那个执行时实例化的类叫实例类。
如:A a =new B(); 其中 A 是申明类,B 是实例类。
- 当调用一个对象的函数时,系统会直接去检查这个对象申明定义的类,即申明类,看所调用的函数是否为虚函数;
- 如果不是虚函数,那么它就直接执行该函数。而如果是一个虚函数,那么这个时候它就不会立刻执行该函数了,而是开始检查对象的实例类。
- 在这个实例类里,他会检查这个实例类的定义中是否有实现该虚函数或者重新实现该虚函数(通过 override 关键字)的方法,如果有,它就不会再找了,而是马上执行该实例类中实现的虚函数的方法。而如果没有的话,系统就会不停地往上找实例类的父类,并对父类重复刚才在实例类里的检查,直到找到第一个重载了该虚函数的父类为止,然后执行该父类里重载后的函数。
虚方法 (virtual) 和抽象方法 (abstract) 的区别
- 虚方法必须有实现部分,抽象方法没有提供实现部分,抽象方法是一种强制派生类覆盖的方法,否则派生类将不能被实例化。如:
- //抽象方法
- public abstract class Animal
- {
- public abstract void Sleep();
- public abstract void Eat();
- }
- //虚方法
- public class Animal
- {
- public virtual void Sleep(){}
- public virtual void Eat(){}
- }
- 抽象方法只能在抽象类中声明,虚方法不是。其实如果类包含抽象方法,那么该类也是抽象的,也必须声明为抽象的。如:
- public class Animal
- {
- public abstract void Sleep();
- public abstract void Eat();
- }
- 抽象方法必须在派生类中重写,这一点跟接口类似,虚方法不必。
- public abstract class Animal
- {
- public abstract void Sleep();
- public abstract void Eat();
- }
- public class Cat : Animal
- {
- public override void Sleep()
- {
- Console.WriteLine( "Cat is sleeping" );
- }
- // we need implement Animal.Eat() here
- }
3. 静态类和静态类成员
参考文档
(一).C#静态方法与非静态方法比较一、C# 静态成员
- 静态成员属于类所有,非静态成员属于类的实例所有。
- 每创建一个类的实例,都会在内存中为非静态成员新分配一块存储;非静态成员属于类所有,为各个类的实例所公用,无论类创建了多少实例;类的静态成员在内存中只占同一块区域。
(二).C#静态方法与非静态方法比较二、C# 静态方法
- C#静态方法属于类所有,类实例化前即可使用。
- 非静态方法可以访问类中的任何成员,静态方法只能访问类中的静态成员。
- 因为静态方法在类实例化前就可以使用,而类中的非静态变量必须在实例化之后才能分配内存,这样,C# 静态方法调用时无法判断非静态变量使用的内存地址。所以无法使用。而静态变量的地址对类来说是固定的,故可以使用。
(三).C#静态方法与非静态方法比较三、C# 静态方法是一种特殊的成员方法
- static 方法是类中的一个成员方法, 属于整个类, 即不用创建任何对象也可以直接调用!
- 静态方法效率上要比实例化高,静态方法的缺点是不自动进行销毁,而实例化的则可以做销毁。
- 静态方法和静态变量创建后始终使用同一块内存,而使用实例的方式会创建多个内存.
- C# 中的方法有两种: 实例方法, 静态方法.
(四).C#静态方法与非静态方法比较四、C# 静态方法中获取类的名称
静态方法中用:
- string className =
- System.Reflection.MethodBase.
- GetCurrentMethod().ReflectedType.FullName;
非静态方法中还可以用:
- string className = this.GetType().FullName;
4. 静态构造函数
静态构造函数具有以下特点:
- 静态构造函数既没有访问修饰符,也没有参数。
- 在创建第一个实例或引用任何静态成员之前,将自动调用静态构造函数来初始化类。
- 无法直接调用静态构造函数。
- 在程序中,用户无法控制何时执行静态构造函数。
- 静态构造函数的典型用途是:当类使用日志文件时,将使用这种构造函数向日志文件中写入项。
- 静态构造函数在为非托管代码创建包装类时也很有用,此时该构造函数可以调用 LoadLibrary 方法。
- 如果静态构造函数引发异常,运行时将不会再次调用该构造函数,并且在程序运行所在的应用程序域的生存期内,类型将保持未初始化。
5. 接口和抽象类
参考文档
①. 抽象类和接口的区别:
- 类是对对象的抽象,可以把抽象类理解为把类当作对象,抽象成的类叫做抽象类. 而接口只是一个行为的规范或规定,微软的自定义接口总是后带 able 字段,证明其是表述一类类 "我能做。。。"。抽象类更多的是定义在一系列紧密相关的类间,而接口大多数是关系疏松但都实现某一功能的类中;
- 接口基本上不具备继承的任何具体特点,它仅仅承诺了能够调用的方法;
- 一个类一次可以实现若干个接口,但是只能扩展一个父类;
- 接口可以用于支持回调,而继承并不具备这个特点;
- 抽象类不能被密封;
- 抽象类实现的具体方法默认为虚的,但实现接口的类中的接口方法却默认为非虚的,当然您也可以声明为虚的;
- (接口)与非抽象类类似,抽象类也必须为在该类的基类列表中列出的接口的所有成员提供它自己的实现。但是,允许抽象类将接口方法映射到抽象方法上;
- 抽象类实现了 oop 中的一个原则,把可变的与不可变的分离。抽象类和接口就是定义为不可变的,而把可变的座位子类去实现;
- 好的接口定义应该是具有专一功能性的,而不是多功能的,否则造成接口污染。如果一个类只是实现了这个接口的中一个功能,而不得不去实现接口中的其他方法,就叫接口污染;
- 尽量避免使用继承来实现组建功能,而是使用黑箱复用,即对象组合。因为继承的层次增多,造成最直接的后果就是当你调用这个类群中某一类,就必须把他们全部加载到栈中!后果可想而知。(结合堆栈原理理解)。同时,有心的朋友可以留意到微软在构建一个类时,很多时候用到了对象组合的方法。比如 asp.net 中,Page 类,有 Server Request 等属性,但其实他们都是某个类的对象。使用 Page 类的这个对象来调用另外的类的方法和属性,这个是非常基本的一个设计原则;
- 如果抽象类实现接口,则可以把接口中方法映射到抽象类中作为抽象方法而不必实现,而在抽象类的子类中实现接口中方法。
②. 抽象类和接口的使用
- 如果预计要创建组件的多个版本,则创建抽象类。抽象类提供简单的方法来控制组件版本;
- 如果创建的功能将在大范围的全异对象间使用,则使用接口。如果要设计小而简练的功能块,则使用接口;
- 如果要设计大的功能单元,则使用抽象类。如果要在组件的所有实现间提供通用的已实现功能,则使用抽象类;
- 抽象类主要用于关系密切的对象;而接口适合为不相关的类提供通用功能。
来源: http://www.cnblogs.com/renyiqiu/p/6504839.html