CORE
1.POP、OOP、AOP区别。AOP解决什么问题
POP是面向过程 耦合度高 OOP是面向对象 具有封装 继承 多态的特性 AOP面向切面
OOP
封装,隐藏细节,减少耦合,便于维护
继承,代码重用
多态,多种状态
AOP
理解:将模块化的程序中涉及多个模块的公共部分进一步提取成模块,提高程序的模块化
优点:提高可维护性,降低耦合度
2.AOP的实现
过滤器 拦截器 缓存
值类型和引用类型的转换
public class Main {
public static void main(String[] args) {
Integer i = 10;
int n = i;
}
}
4.抽象类和接口的区别和使用场景
1.抽象类的使用场景
既想约束子类具有共同的行为(但不再乎其如何实现),又想拥有缺省的方法,又能拥有实例变量
如:模板方法设计模式,模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中某些步骤的具体实现。
2.接口的应用场景
① 约束多个实现类具有统一的行为,但是不在乎每个实现类如何具体实现
② 作为能够实现特定功能的标识存在,也可以是什么接口方法都没有的纯粹标识。
③ 实现类需要具备很多不同的功能,但各个功能之间可能没有任何联系。
④ 使用接口的引用调用具体实现类中实现的方法(多态)
5.锁:乐观锁,悲观锁 举例说明应用场景
悲观锁,顾名思义就是很悲观,意思就是我每次去拿数据的时候都会上锁,这个时候如果别人也来拿这个数据,就会阻塞,一直等到我获取结束释放锁之后,别人才会去拿锁接着去执行!
乐观锁其实就是很乐观,我每次去拿数据的时候,不会上锁,只有在更新的时候,会结合版本号这样的方式,判断当前有没有人在更新,如果有人更新,我就不会被修改,否则修改!这样就提高了吞吐量!
悲观锁和乐观锁并没有优先使用之分,只有适不适合之分,悲观锁适用于写的场景比较多,也就是冲突比较多的时候,而乐观锁适用于写的场景比较少,冲突比较少的情况!
使用 FOR UPDATE
6.什么是死锁?如何保证你实现的锁结果不发生死锁
多个进程或线程互相等待对方的资源,在得到新的资源之前不会释放自己的资源,这样就形成了循环等待,这种现象被称为死锁。
预防死锁
1、线程一次请求所有资源。
2、当占有一个资源,无法请求到新的资源就释放占有的资源,等待一定时间后再次重新请求。
3、资源进行编号,必须按照顺序请求资源。
7.数组和链表的区别
(1)数组的元素个数是固定的,而链表的结点个数可按需要增减。
(2)数组元素的存储单元在定义时分配,链表节点的存储单元在执行时动态向系统申请。
(3)数组的元素顺序关系由元素在数组中的位置(即下标)确定,链表中的节点关系由节点所包含的指针来体现。
(4)对于不是固定长度的列表,用可能最大长度的数组来描述,会浪费许多的存储空间。
(5)对于元素的插入、删除操作非常频繁的列表处理场合,用数组表示列表也不是不合适。若用链表实现,会使程序结构清晰,处理的方法也比较简便。
8.WebAPI 和 webservice的区别
1、webapi用的是http协议,webservice用的是soap协议。
2、webapi无状态,相对webservice更轻量级。webapi支持get、post等http操作。
web计算平台包含了广泛的功能,其中的大部分均可以通过API(应用程序编程接口)访问。从简 单的社会书签服务del.icio.us,到复杂得多的amazon s3全虚拟化存储平台,想想能用这些web api做点什么,真是惊人。 在本贴中,我把web平台归为6个基本设施,并简要概述些相关产品。其间的线索是这些产品都提供了API,这意味着他们本身可以被其他服务整合。
WebService是一个平台独立的,低耦合的,自包含的、基于可编程的web的应用程序,可使用开放的XML(标准通用标记语言下的一个子集)标准来描述、发布、发现、协调和配置这些应用程序,用于开发分布式的交互操作的应用程序。Web Service技术, 能使得运行在不同机器上的不同应用无须借助附加的、专门的第三方软件或硬件, 就可相互交换数据或集成。依据Web Service规范实施的应用之间, 无论它们所使用的语言、 平台或内部协议是什么, 都可以相互交换数据。
SOAP是简单对象访问协议是交换数据的一种协议规范,是一种轻量的、简单的、基于XML(标准通用标记语言下的一个子集)的协议,它被设计成在WEB上交换结构化的和固化的信息。
9.什么是线程安全和线程非安全
多个线程在并行执行且使用到了共享数据的操作中,最终能正确执行得到正确结果的就是线程安全的,反之就是线程不安全的
多线程开发过程中对共享数据进行操作引起变量引用中前后不一致会形成计算错误就是非线程安全,可以通过共享数据加信号量锁、原子性、副本解决。
10.C# 常用数据结构
数组、链表、队列、哈希表
哈希表(Hash table,也叫散列表),是一种有限连续的地址空间,用以存储按散列函数计算得到相应散列地址的数据记录。通常散列表的存储空间是一个一维数组,散列地址是数组的下标
优点:
如果关键字已知则存取速度极快,支持高效的数据插入、删除和查找操作。
缺点:
不支持快速顺序遍历散列表中的数据。
使用场景:
哈希表适用于那种查找性能要求高,数据元素之间无逻辑关系要求的情况,同时哈希表在海量数据处理中有着广泛应用。
11.Dictionary的实现和使用场景
1、Dictionary<string, string>是一个泛型
2、他本身有集合的功能,有时候也可以把它看成数组
3、构造:Dictionary<[key], [value]>
4、它可以存储一个Key值和一个泛型,然后通过某一个一定的[key]去找到对应的值
5、Key不允许重复就像数据库主键一样,但是value值可以
搭建数据字典:因为有唯一的Key值就像数据库索引一样可以快速定位数据进行读取,还有一个可以存储各种类型的值,所以一般用Dictionary来搭建一个字典库,比如库存,价格,单据数据等。一次性读取数据后进行数据存储在其他地方进行调用,可以减少对数据库的访问减少数据库服务器的压力
12.Entity Framework 如何实现 left join
Linq表达式
var query=(from a in db.TableA
join b in db.TableB
on a.ID equals b.NodeID into c
from x in c.DefaultIfEmpty()
where a.ID==param
select new
{
a,
x.Key
}).FirstOrDefault();
Lambda表达式 读取指定返回列表字段的左连接信息:
var GJoinList = db.Sys_User.GroupJoin(db.Sys_Department, u => u.DepartmentId, d => d.DepartmentId, (u,d) => new { UserId=u.UserId, Account=u.Account, RealName=u.RealName, EnabledMark=u.EnabledMark, DeleteMark=u.DeleteMark,DepartmentName = d.FirstOrDefault(x=>x.DepartmentId==u.DepartmentId).FullName}).Select(o=>o);
13.描述一下依赖注入后的服务生命周期
单实例服务, 通过add singleton方法来添加。在注册时即创建服务, 在随后的请求中都使用这一个服务。
短暂服务, 通过add transient方法来添加。是一种轻量级的服务,用于无状态服务的操作。
作用域服务,一个新的请求会创建一个服务实例。使用add scoped方法来添加。
14.IOC容器中实现了IDispose接口的类,需要主动释放吗
正常来说不需要,除非出现了异常可能需要主动释放。
15.列举一下.net mvc 中AOP的示例,以及作用和场景
AuthorizationFilter 鉴权授权
ResourceFilter 资源过滤器
ExceptionFilter 异常过滤器
ActionFilter 动作/方法过滤器
ResultFilter 结果过滤器
注释Attribute 属性
实现过滤器
public class MyAction :Attribute,IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
var controllerName = context.RouteData.Values["controller"];
var actionName = context.RouteData.Values["action"];
Console.WriteLine($"行为过滤器OnActionExecuted作用于{controllerName }控制器下的{actionName }方法运行之后</br>", Encoding.Default);
}
public void OnActionExecuting(ActionExecutingContext context)
{
var controllerName = context.RouteData.Values["controller"];
var actionName = context.RouteData.Values["action"];
Console.WriteLine($"行为过滤器OnActionExecuting作用于{controllerName }控制器下的{actionName }方法运行之前</br>", Encoding.UTF8);
}
}
services.AddTransient(typeof(MyAction));将过滤器注册成瞬态服务
[MyAction]通过标记来使用
16.描述一下管道模式,以及在.net core 中的使用
请求管道描述的是一个请求进到我们的后端应用,后端应用如何处理的过程,从接收到请求,之后请求怎么流转,经过哪些处理,最后怎么返回响应。请求管道就是一次请求在后端应用的生命周期。了解请求管道,有助于我们明白后端应用是怎么工作的,我们的代码是怎么工作的,在我们的业务代码执行前后经过哪些步骤,有助于我们之后更好的实现一些AOP操作。
当一个http请求被送入到HttpRuntime之后,这个Http请求会继续被送入到一个被称之为HttpApplication Factory的一个容器当中,而这个容器会给出一个HttpApplication实例来处理传递进来的http请求,而后这个Http请求会依次进入到如下几个容器中:HttpModule --> HttpHandler Factory --> HttpHandler。当系统内部的HttpHandler的ProcessRequest方法处理完毕之后,整个Http Request就被处理完成
// .NET Core3.1默认代码
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
// 中间件
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
注册
终结者模式(Run())
Run 终结式只是执行,没有去调用Next ,一般作为终结点。
所谓Run终结式注册,其实只是一个扩展方法,最终还是调用Use方法
Use 方法注册
use 方式注册中间件得出的结论是:Use注册动作 不是终结点 ,执行next,就可以执行下一个中间件
如果不执行,就等于Run
app.Use(async (context, next) =>
{
await context.Response.WriteAsync("Hello 1 开始");
await next();//调用下一个中间件
await context.Response.WriteAsync("Hello 1 结束");
});
app.Use(async (context, next) =>
{
await context.Response.WriteAsync("Hello 2 开始");
await next();
});
封装中间件
public class CustomMiddleWare
{
private readonly RequestDelegate _next;
//依赖注入
public CustomMiddleWare(RequestDelegate next)
{
this._next = next;
}
public async Task Invoke(HttpContext context)
{
await context.Response.WriteAsync($"{nameof(CustomMiddleWare)},Hello World1!<br/>");
await _next(context);
await context.Response.WriteAsync($"{nameof(CustomMiddleWare)},Hello World2!<br/>");
}
}
注册使用
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseMiddleware<CustomMiddleWare>();
}
进一步封装
使用扩展方法,将这个类中的逻辑作为IApplicationBuilder的扩展方法(ApplicationBuilder 应用程序生成器)
public static class MiddleExtend
{
public static IApplicationBuilder UseCustomMiddleWare(this IApplicationBuilder builder)
{
return builder.UseMiddleware<CustomMiddleWare>();
}
}
注册使用
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseCustomMiddleWare();
}
17.谈谈.net中的GC,垃圾回收策略,如何回收非托管资源
手动强制回收:GC.Collect() GC.Collect(0/1/2)0:新生代,1:旧生代,2:持久代
托管:可借助GC从内存中释放的数据对象(以下要描述的内容点)
非托管:必须手工借助Dispose释放资源(实现自IDisposable)的对象
根据实例的生命周期和对象的调用频率来判断堆中的数据是否为新生代、旧生代、持久代(静态)来选择回收内存。
数据库:
1.事务的特性ACID
1. 原子性(Atomicity)
事务被视为不可分割的最小单元,事务的所有操作要么全部提交成功,要么全部失败回滚。
回滚可以用回滚日志来实现,回滚日志记录着事务所执行的修改操作,在回滚时反向执行这些修改操作即可。
2. 一致性(Consistency)
数据库在事务执行前后都保持一致性状态。在一致性状态下,所有事务对一个数据的读取结果都是相同的。
3. 隔离性(Isolation)
一个事务所做的修改在最终提交以前,对其它事务是不可见的。
4. 持久性(Durability)
一旦事务提交,则其所做的修改将会永远保存到数据库中。即使系统发生崩溃,事务执行的结果也不能丢失。使用重做日志来保证持久性。
2.简单的理解事务的隔离级别,以及你对他的理解
1. read uncommited(会出现脏读问题)
读未提交,就是一个事务可以读取另一个事务未提交的数据
举例:
小明的工资是20000,人事在输入工资的时候多打了个0,但是还没有提交,此时小明会查到自己的工资变成了20000
问题:会出现脏读,即:一个事务读取另一个事务未提交的数据
2. read commited(会出现不可重复读问题)
读已提交,一个事务只能读取另一个事务已提交的事务
举例:
程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(程序员事务开启),收费系统事先检测到他的卡里有3.6万,就在这个时候!!程序员的妻子要把钱全部转出充当家用,并提交。当收费系统准备扣款时,再检测卡里的金额,发现已经没钱了(第二次检测金额当然要等待妻子转出金额事务提交完)。程序员就会很郁闷,明明卡里是有钱的
问题:解决了脏读问题,但是会出现不可重复读问题,即:出现了一个事务范围内两个相同的查询却返回了不同数据,这就是不可重复读。
3. Repeatable read(幻读问题)
重复读,就是在开始读取数据(事务开启)时,不再允许修改操作
事例:
程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(事务开启,不允许其他事务的UPDATE修改操作),收费系统事先检测到他的卡里有3.6万。这个时候他的妻子不能转出金额了。接下来收费系统就可以扣款了。
分析:重复读可以解决不可重复读问题。写到这里,应该明白的一点就是,**不可重复读对应的是修改,即UPDATE操作。**但是可能还会有幻读问题。因为幻读问题对应的是插入INSERT操作,而不是UPDATE操作。