上善若水

水善利万物而不争
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

.NET 面试题

Posted on 2022-03-24 10:13  董锡振  阅读(269)  评论(0编辑  收藏  举报

 

1. 简述一下一个引用对象的生命周期?

(创建>使用>释放) new创建对象并分配内存  对象初始化  对象操作、使用  资源清理(非托管资源)  GC垃圾回收

 

2、什么是面向对象?面向对象的三大特性和五大原则是什么?

方法主要是把事物给对象化,包括其属性和行为。面向对象编程更贴近实际生活的思想。底层还是面向过程,面向过程抽象成类,然后封装,方便使用就是面向对象(万物皆对象)。

三大基本特性:封装,继承,多态

封装

封装,就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。一个类就是一个封装了数据以及操作这些数据的代码的逻辑实体。在一个对象内部,某些代码或某些数据可以是私有的,不能被外界访问。通过这种方式,对象对内部数据提供了不同级别的保护,以防止程序中无关的部分意外的改变或错误的使用了对象的私有部分。

继承

继承,指可以让某个类型的对象获得另一个类型的对象的属性的方法。它支持按级分类的概念。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。 通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”或“超类”。继承的过程,就是从一般到特殊的过程。要实现继承,可以通过 “继承”(Inheritance)和“组合”(Composition)来实现。继承概念的实现方式有二类:实现继承与接口继承。实现继承是指直接使用 基类的属性和方法而无需额外编码的能力;接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力。

多态

通过继承实现的不同对象,调用相同的方法,产生不同的执行结果.。

五大基本原则:SPR, OCP, LSP, DIP, ISP

1、单一职责原则SRP(Single Responsibility Principle)

是指一个类的功能要单一,不能包罗万象。如同一个人一样,分配的工作不能太多,否则一天到晚虽然忙忙碌碌的,但效率却高不起来。

2、开放封闭原则OCP(Open-Close Principle)

一个模块在扩展性方面应该是开放的而在更改性方面应该是封闭的。 

3、里式替换原则LSP(the Liskov Substitution Principle LSP)

子类应当可以替换父类并出现在父类能够出现的任何地方。 

4、依赖倒置原则DIP(the Dependency Inversion Principle DIP)

高层模块不应该依赖低层模块,二者都应该依赖其抽象。

5、接口分离原则ISP(the Interface Segregation Principle ISP)

模块间要通过抽象接口隔离开,而不是通过具体的类强耦合起来

 

3. 什么是垃圾? GC是什么,简述一下GC的工作方式?

一个变量如果在其生存期内的某一时刻已经不再被引用,那么,这个对象就有可能成为垃圾。

在公共语言运行时 (CLR) 中,垃圾回收器 (GC) 用作自动内存管理器。 垃圾回收器管理应用程序的内存分配和释放。

她的基本工作原理就是遍历托管堆中的对象,标记哪些被使用对象(哪些没人使用的就是所谓的垃圾),然后把可达对象转移到一个连续的地址空间(也叫压缩),其余的所有没用的对象内存被回收掉。

 

4. GC在哪些情况下回进行回收工作?

(1)系统具有低的物理内存。 这是通过 OS 的内存不足通知或主机指示的内存不足检测出来。

(2)由托管堆上已分配的对象使用的内存超出了可接受的阈值。 随着进程的运行,此阈值会不断地进行调整。

(3)调用 GC.Collect 方法。 几乎在所有情况下,你都不必调用此方法,因为垃圾回收器会持续运行。 此方法主要用于特殊情况和测试。

 

5. using() 语法是如何确保对象资源被释放的?如果内部出现异常依然会释放资源吗?

using() 只是一种语法形式,其本质还是try…finally的结构,可以保证Dispose始终会被执行。

 

6. 解释一下C#里的析构函数?为什么有些编程建议里不推荐使用析构函数呢?

C#里的析构函数其实就是终结器Finalize,因为长得像C++里的析构函数而已。

原因在于:第一是Finalize本身性能并不好;其次很多人搞不清楚Finalize的原理,可能会滥用,导致内存泄露,因此就干脆别用了

 

7. Finalize() 和 Dispose() 之间的区别?

Finalize() 和 Dispose()都是.NET中提供释放非托管资源的方式,他们的主要区别在于执行者和执行时间不同:

finalize由垃圾回收器调用;dispose由对象调用。

finalize无需担心因为没有调用finalize而使非托管资源得不到释放,而dispose必须手动调用。

finalize不能保证立即释放非托管资源,Finalizer被执行的时间是在对象不再被引用后的某个不确定的时间;而dispose一调用便释放非托管资源。

只有class类型才能重写finalize,而结构不能;类和结构都能实现IDispose。

另外一个重点区别就是终结器会导致对象复活一次,也就说会被GC回收两次才最终完成回收工作,这也是有些人不建议开发人员使用终结器的主要原因。

 

8. Dispose和Finalize方法在何时被调用?

Dispose一调用便释放非托管资源;

Finalize不能保证立即释放非托管资源,Finalizer被执行的时间是在对象不再被引用后的某个不确定的时间;

 

9. .NET中的托管堆中是否可能出现内存泄露的现象?

是的,可能会。比如:

不正确的使用静态字段,导致大量数据无法被GC释放;

没有正确执行Dispose(),非托管资源没有得到释放;

不正确的使用终结器Finalize(),导致无法正常释放资源;

其他不正确的引用,导致大量托管对象无法被GC释放;

 

10. 在托管堆上创建新对象有哪几种常见方式?

new一个对象;

字符串赋值,如string s1=”abc”;

值类型装箱;

 

11、什么是多态?

通过继承实现的不同对象,调用相同的方法,产生不同的执行结果.

C#支持两种类型的多态,编译时的多态和运行时的多态

(1)编译时的多态:

编译时的多态是通过重载来实现的,对于非虚的成员来说,系统在编译时,根据传递的参数类型,个数以及返回类型的不同决定实现不同的操作.

public int Sum(int x,int y)  

public int Sum(int x,int y,int z)

public double Sum (Double x,Double y)

重载特点:   方法名称必须相同 参数列表必须不同 返回值类型可以不同 

 

(2)运行时的多态:

运行时的多态是指系统直到运行时,才根据实际情况实现何种操作.

运行时的多态可以通过virtual-override(虚成员覆盖实现)以及abstract-override(抽象方法覆盖实现)两种方式来实现.

通过override实现覆写注意的几点

只有虚方法和抽象方法才能被覆写

子类和基类中的方法必须具有相同的方法名称,参数个数,参数类型以及返回值类型.

总结:

编译时的多态使运行速度更快,就像const编译时解析.

运行时的多态带来了高度灵活以及抽象的特点. 

 

12、EF如何处理并发?

什么叫并发:当多个用户同时更新同一数据的时候,由于更新可能导致数据的不一致性,使得程序的业务数据发生错误,这种情况可以称之为并发。

并发又分为两种:乐观并发 与 悲观并发 

乐观并发:即系统允许多个用户同时修改同一条记录,系统会预先定义由数据并发所引起的并发异常处理模式,去处理修改后可能发生的冲突

当出现乐观并发时应该怎么处理呢,通常有如下三种处理方法   a 保留最后一次对象修改的值  b 保留最初的修改值  c  合并修改值

悲观并发:在同一时刻只允许一个用户修改相同数据,直接用Lock 与 unLock就可以处理.

 

13、事务的四大特性:

1 、原子性 (atomicity):强调事务的不可分割;事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做。

2 、一致性 (consistency):事务的执行的前后数据的完整性保持一致.

3 、隔离性 (isolation):一个事务执行的过程中,不应该受到其他事务的干扰,并发执行的各个事务之间不能互相干扰。

4 、持续性 (durability) :事务一旦结束,数据就持久到数据库

 

14、那么在没有对事务进行隔离时会发生哪些危害了?

脏读:当一个事务读取数据修改后以经SaveChange但事务还没有提交,此时另外一个事务就读取了该数据,此时的数据就是脏数据,这一过程就是脏读

不可重复读:是指在一个事务内,多次读同一数据。

在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的这一过程就是不可重复读

幻读:一个事务针对一张表的所有数据进行读取修改,而此时另一个事务向表中插入了一条数据,则第一个事务数据不包含新数据,像出现幻觉一样,这一过程就是幻读。

避免不可重复读需要锁行。避免幻影读则需要锁表。

 

15、EF四种隔离级别

01:Read uncommitted(读未提交):最低级别,任何情况都会发生。

02:Read Committed(读已提交):可避免脏读的发生。 MySQL 、SQL Server 、Oracle的默认隔离级别。

03:Repeatable read(可重复读):可避免脏读、不可重复读的发生。

04:Serializable(串行化):避免脏读、不可重复读,幻读的发生。 级别最高执行效率越低

 

16、使用EF update 怎么保证在并发时数据正确?

 1、RowVersion ( TimeStamp )  时间戳

EF实现Rowversion 并发控制 需要借助 TimeStamp 标示 ,并且一个类只能有 一个此标示,标示的必须是byte[]类型。使用Rowversion会对整行数据进行并发检测。

2、 ConcurrencyCheck

有些时候并不需要控制针对整条记录的并发,只需要控制某个列的数据不会出现脏操作就ok,这个时候 就使用ConcurrencyCheck 。你必须将ConcurrencyCheck特性添加到实体需要控制并发的非主键属性上,使实体框架可以将所有标记的列包含到更新语句的Where子句中。

3、 DbUpdateConcurrencyException

您可以通过EF实体框架引发的DbUpdateConcurrencyException异常处理来解决冲突。

 

 

17、jwt的组成有那几部分?

      样式:"xxxxxxxxxxxx.xxxxxxxxxxxxx.xxxxxxxxxxxxxxxx"由三部分组成.

(1).Header头部:{\"alg\":\"HS256\",\"typ\":\"JWT\"}基本组成,也可以自己添加别的内容,然后对最后的内容进行Base64编码.

(2).Payload负载:iss、sub、aud、exp、nbf、iat、jti基本参数,也可以自己添加别的内容,然后对最后的内容进行Base64编码.

(3).Signature签名:将Base64后的Header和Payload通过.组合起来,然后利用Hmacsha256+密钥进行加密。


jwt的安全性有哪些建议?

1、SecurityKey一定要保管好;

2、签名一定要有,校验签名并且不接受非法的签名;( 签名解决了数据传输过程中参数被篡改的风险)

3、payload增加时间戳减少重放攻击;

4、payload里不存放敏感信息,若有请用临时票据token形式的;

5、接口传输采用https协议;