一、面试种类。
面试一般会有以下几种:
1、HR简单面
HR会询问个人的基本情况、前公司的情况、所用项目技术、薪酬、离职原因。
2、笔试面
对于初级程序员,面试题一般是网上的面试宝典。可在网上搜出很多。
3、技术老大面
这轮会相对比较难些,需要自己对很多知识有较为了解。特别是自己的项目经验、技术会问的最多。
4、经理面
这个一般较少。主要是经理面试会要一个团队当中的某一位置,就看你会不会适合这个位置。
5、机试面
这个也比较少。主要也是看基础扎不扎实。
二、以下是对笔试的常见题目的总结
现在是B/S架构流行,相应地就要求有些前端技术的要求:比如是html+css+javascript+jquery等技术要会用,还有微服务docker、linux系统等技术都会有相应的要求。建议还是别偷懒,多学习,多进步。
简答题:
1、get、post方法的区别。
- 最直观的区别是get方法的参数包含在URL中,post方法通过request body传递参数。
- get比post更不安全,因为参数直接暴露在URL上,不能用来传递敏感信息。
- post方法是在request body中传输,而get方法是在网址URL中。
- get产生一个数据包,post产生两个数据包。
- post方法传输的数据更大;get方法传输的数据一般小于2kb,受限于网址长度。
- get方法还会产生缓存,需要在URL中多加一个时间戳变量来清除缓存。
2、编写一个冒泡排序算法。
class Program
{
static void Main(string[] args)
{
int[] a = new int[8]{1,2,3,1,2,5,4,6} ;
for(int i=0;i<a.Length-1;i++)
{
for(int j=i+1;j<a.Length;j++)
{
if(a[j]>a[i])
{
a[i]=a[i]+a[j];
a[j]=a[i]-a[j];
a[i]=a[i]-a[j];
}
}
}
foreach(var n in a)
{
Console.Write(n+" ");
}
}
}
3、写一个斐波那契数列。
class Program
{
static void Main(string[] args)
{
Console.Write(Foo.Fib(5));
}
}
public class Foo
{
public static int Fib(int n)
{
if (n <= 0) return 0;
else if (n > 0 && n <= 2) return 1;
else return Fib(n - 1) + Fib(n - 2);
}
}
4、编写一个单例(Singleton)类
第一种形式:饿汉式单例
public class Singleton
{
private Singleton(){}
private static Singleton instance = new Singleton();
public static Singleton getInstance()
{
return instance;
}
}
第二种形式:懒汉式单例
public class Singleton
{
private static Singleton instance = null;
private Singleton() {}
public static synchronized Singleton getInstance()
{
if (instance==null) instance=newSingleton();
return instance;
}
}
区别:
“懒汉式”是在你真正用到的时候才去建这个单例对象,线程安全
“饿汉式”是在不管你用的用不上,一开始就建立这个单例对象:比如:有个单例对象,线程不安全
5、编程遍历页面上所有TextBox控件并给它赋值为String.Empty。
foreach (System.Windows.Forms.Control control in this.Controls)
{
if (control is System.Windows.Forms.TextBox)
{
System.Windows.Forms.TextBox tb = (System.Windows.Forms.TextBox)control;
tb.Text = string.Empty;
}
}
6、using关键字的作用是什么?
1、引用命名空间
引用命名空间,这样可以在程序中引用命名空间的类型而不必指定详细的命名空间。
a) 比如在程序中常用的using System.Text;
b) 引用命名空间,并不等于编译器编译时加载该命名空间所在的程序集,程序集的加载决定于程序中对该程序集是否存在调用操作,如果代码中不存在任何调用操作则编译器将不会加载using引入命名空间所在空间的程序集.因此,在源文件开头,引入多个命名空间,并非加载多个程序集,不会造成’过度引用’的弊端。
2、为命名空间或类型创建别名
创建别名的原因在于同一个文件中引入不同的命名空间中包括了相同名称的类型。为了避免出现名称冲突,可以通过设定别名来解决.
例: using SGPoint = ShapMap.Geometries.Point
using SGPoint = System.Drawing.Point
其实也可以通过类型全名称来加以区分,但是最佳解决方案还是使用using
** 3、使用using语句释放非托管资源**
using语句允许程序员指定使用资源的对象应当何时释放资源.using语句中使用的对象必须实现IDisposable接口.此接口提供了Dispose方法,该方法将释放此对象的资源
使用规则:
a) using语句只能用于实现了IDisposable接口的类型,禁止为不支持IDisposable接口类型使用using语句,否则会出现编译错误
b) using语句适用于清理单个非托管资源的情况,而多个非托管对象的清理最好以try-finaly来实现,因为嵌套using语句可能存在隐藏的Bug.内层using块引发异常时,将不能释放外层using块的对象资源
using实质:在程序编译阶段,编译器会自动将using语句生成try-finally语句,并在finally块中调用对象的Dispose方法,来清理资源.所以,using语句等效于try-finally语句。
7、sealed修饰符是干什么的?
sealed表示密封。可以修饰类和方法。
修饰类时,表示密封类,表示该类不能被继承。能和abstract (抽象类)同时使用,因为sealed和abstract的属性相互排斥
修饰方法时,表示密封方法,可以防止该类被重写。密封方法必须由sealde和override共同修饰,要作为密封方法必须对基类的虚方法进行重载
8、string 和 stringBuilder的区别是什么?
1、string 是引用类型,具有“不可变性”的特性,在每次的赋值、拼接时,都会创建一个新的对象,也就是每次操作都会去申请新的内存空间。
2、StringBuilder每次操作都是对自身对象进行操作,而不是生成新的对象,其所占空间会随着内容的增加而扩充,这样,在做大量的修改操作时,不会因生成大量匿名对象而影响系统性能。
9、params有什么用?
params 关键字在方法成员的参数列表中使用,为该方法提供了参数个数可变的能力。
它在只能出现一次并且不能在其后再有参数定义。
例:
class Program { static void Main(string[] args) { App.UseParams(1, "a", "b", "c"); App.UseParams(2, "d", 100, 33.33, new double[] { 1.1, 2.2 }); } } class App { //由于定的是object数组,所有的数据类型都可以做为参数传入 public static void UseParams(params object[] list) { for (int i = 0; i < list.Length; i++) { Console.WriteLine(list[i]); } } }
10、是否可以继承string类
不可以。string类是sealed类,故不可以继承。
11、try里面有return语句,执行完return之后,会不会执行finally里面的语句?如果执行,是在return前还是return后执行?
会执行,在return后。
12、Response.Redict()、Server.transfer()的区别。
1、地址栏里显示的地址不同。
Response.Redict()显示的是跳转后的地址,Server.transfer()显示的是跳转前的地址。
2、跳转的流程不同。
浏览器http请求-->服务器执行-->遇到Response.Redict()-->服务器发送新的URL给客户端浏览器-->浏览器请求执行新URL。
浏览器http请求-->服务器执行-->遇到Server.transfer()-->服务器转向新的文件。
13、三层架构分别是什么?它们的作用是什么?优缺点是什么?
数据访问层——负责与数据源的交互,即数据的插入、删除、修改以及从数 据库中读出数据等操作。对数据的正确性和可用性不负责,对数据的用途不了解, 不负担任何业务逻辑。
业务逻辑层——负责系统业务逻辑的处理,负责逻辑性数据的生成、处理及 转换。 对流入的逻辑性数据的正确性及有效性负责,对流出的逻辑性数据及用户 性数据不负责,对数据的呈现样式不负责。
表示层————负责与用户的交互。接收用户的输入、将输出呈现给用户以及访问安全性验证。 对流入的数据的正确性和有效性负责,对呈现样式负责,对呈现友好的错误信息负责。优点:
- 可以降低层与层之间的依赖
- 开发人员可以只关注整个结构中的其中某一层
- 在后期维护的时候,极大地降低了维护成本和维护时间
- 可以很容易的用新的实现来替换原有层次的实现
缺点:
- 系统架构复杂,不适合小型项目的开发
- 增加开发成本
- 降低系统性能
14、session喜欢丢值而且占内存,cookie不安全。请问用什么方法代替这两种原始的方法?
iis中由于有进程回收机制,系统繁忙的话session会丢失,可用state server或者SQL Server数据库的方式存储session,缺点是这种方式比较慢,而且无法捕捉session的END事件。
15、说一些数据库优化方面的经验
选取最适用的字段属性,避免SELECT *
为搜索字段建索引
多使用临时表
合理使用存储过程、函数等。
16、数据库事务的四大特性:ACID
原子性:事务要么执行要么不执行。
一致性:事务执行后,前后数据是一致的。
隔离性:对表是隔离并发,单用户操作。
持久性:事务提交之后,对数据的影响是永久的。
17、数据库死锁的原因、检测以及处理方法。
产生数据库死锁的原因有:
一. 事务之间对资源访问顺序的交替
1、出现原因:
一个用户A 访问表A(锁住了表A),然后又访问表B;另一个用户B 访问表B(锁住了表B),然后企图访问表A;这时用户A由于用户B已经锁住表B,它必须等待用户B释放表B才能继续,同样用户B要等用户A释放表A才能继续,这就死锁就产生了。
2、解决方法:
这种死锁比较常见,是由于程序的BUG产生的,除了调整的程序的逻辑没有其它的办法。仔细分析程序的逻辑,对于数据库的多表操作时,尽量按照相同的顺序进行处理,尽量避免同时锁定两个资源,如操作A和B两张表时,总是按先A后B的顺序处理, 必须同时锁定两个资源时,要保证在任何时刻都应该按照相同的顺序来锁定资源。二. 并发修改同一记录
1、出现原因:
用户A查询一条纪录,然后修改该条纪录;这时用户B修改该条纪录,这时用户A的事务里锁的性质由查询的共享锁企图上升到独占锁,而用户B里的独占锁由于A有共享锁存在所以必须等A释放掉共享锁,而A由于B的独占锁而无法上升的独占锁也就不可能释放共享锁,于是出现了死锁。这种死锁由于比较隐蔽,但在稍大点的项目中经常发生。
一般更新模式由一个事务组成,此事务读取记录,获取资源(页或行)的共享 (S) 锁,然后修改行,此操作要求锁转换为排它 (X) 锁。如果两个事务获得了资源上的共享模式锁,然后试图同时更新数据,则一个事务尝试将锁转换为排它 (X) 锁。共享模式到排它锁的转换必须等待一段时间,因为一个事务的排它锁与其它事务的共享模式锁不兼容;发生锁等待。第二个事务试图获取排它 (X) 锁以进行更新。由于两个事务都要转换为排它 (X) 锁,并且每个事务都等待另一个事务释放共享模式锁,因此发生死锁。2、解决方法:
a. 使用乐观锁进行控制。乐观锁大多是基于数据版本(Version)记录机制实现。即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个“version”字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。乐观锁机制避免了长事务中的数据库加锁开销(用户A和用户B操作过程中,都没有对数据库数据加锁),大大提升了大并发量下的系统整体性能表现。Hibernate 在其数据访问引擎中内置了乐观锁实现。需要注意的是,由于乐观锁机制是在我们的系统中实现,来自外部系统的用户更新操作不受我们系统的控制,因此可能会造成脏数据被更新到数据库中。
b. 使用悲观锁进行控制。悲观锁大多数情况下依靠数据库的锁机制实现,如Oracle的Select … for update语句,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。如一个金融系统,当某个操作员读取用户的数据,并在读出的用户数据的基础上进行修改时(如更改用户账户余额),如果采用悲观锁机制,也就意味着整个操作过程中(从操作员读出数据、开始修改直至提交修改结果的全过程,甚至还包括操作员中途去煮咖啡的时间),数据库记录始终处于加锁状态,可以想见,如果面对成百上千个并发,这样的情况将导致灾难性的后果。所以,采用悲观锁进行控制时一定要考虑清楚。
c. SqlServer可支持更新锁
为解决死锁,SqlServer引入更新锁,它有如下特征:
(1) 加锁的条件:当一个事务执行update语句时,数据库系统会先为事务分配一把更新锁。
(2) 解锁的条件:当读取数据完毕,执行更新操作时,会把更新锁升级为独占锁。
(3) 与其他锁的兼容性:更新锁与共享锁是兼容的,也就是说,一个资源可以同时放置更新锁和共享锁,但是最多放置一把更新锁。这样,当多个事务更新相同的数据时,只有一个事务能获得更新锁,然后再把更新锁升级为独占锁,其他事务必须等到前一个事务结束后,才能获取得更新锁,这就避免了死锁。
(4) 并发性能:允许多个事务同时读锁定的资源,但不允许其他事务修改它。例子如下:
T1: begin tran select * from table(updlock) (加更新锁) update table set column1='hello' T2: begin tran select * from table(updlock) update table set column1='world'
更新锁的意思是:“我现在只想读,你们别人也可以读,但我将来可能会做更新操作,我已经获取了从共享锁(用来读)到排他锁(用来更新)的资格”。一个事物只能有一个更新锁获此资格。
T1执行select,加更新锁。
T2运行,准备加更新锁,但发现已经有一个更新锁在那儿了,只好等。
当后来有user3、user4…需要查询table表中的数据时,并不会因为T1的select在执行就被阻塞,照样能查询,提高了效率。三. 索引不当导致全表扫描
1、出现原因:
如果在事务中执行了一条不满足条件的语句,执行全表扫描,把行级锁上升为表级锁,多个这样的事务执行后,就很容易产生死锁和阻塞。类似的情况还有当表中的数据量非常庞大而索引建的过少或不合适的时候,使得经常发生全表扫描,最终应用系统会越来越慢,最终发生阻塞或死锁。
2、解决方法:
SQL语句中不要使用太复杂的关联多表的查询;使用“执行计划”对SQL语句进行分析,对于有全表扫描的SQL语句,建立相应的索引进行优化。四.事务封锁范围大且相互等待
18、数据库事务如何处理。
19、设计系统的权限控制,需要设计几张表。
系统的权限控制是一个难点,也是一个常考点。
对于简单的项目,涉及用户、权限两种,因此创建三张表即可:用户表(UserInfo:uid,uname)、角色表(RoleInfo:rid,rname)、权限表(Perm:pid,uid,rid)
稍微复杂些的,涉及用户、角色、菜单三种,因此要创建五张表:用户表(UserInfo:uid,uname)、角色表(RoleInfo:rid,rname)、菜单表(MenuInfo:mid,mname)、用户角色映射表(user_role:urid,uid,rid)、角色菜单映射表(role_menu:rid,mid)。
当然假如要考虑的话。权限控制可以是更加完善、表设计也更加复杂。主要还是要看具体要求。
20、在开发的系统过程中,遇到的最难的问题是什么?你是怎样去解决的?
三、SQL简答题:
1、数据库去除冗余信息的语句。如表A中数据如下
id | name | score |
---|---|---|
1 | 张三 | 86 |
2 | 李四 | 63 |
3 | 王五 | 83 |
4 | 张三 | 86 |
-
需要去除除id以外数据冗余的信息(保留最大的id):
delete from A where id not in (select max(id) from A group by name,score)
-
需要去除除id以外数据冗余的信息(保留最小的id):
delete from A where id not in (select min(id) from A group by name,score)
-
使用having过滤出冗余信息:
select name,score,count(*) from A group by name,score having count(*)>1
-
使用having过滤出非冗余信息:
select name,score,count(*) from A group by name,score having count(*)<=1
本人还遇到过奇葩的题目,如下题,这样的数据要求去除冗余。
这里提供一种思路,本人并不确定它是否正确:
1、先将重复的数据全部查出来,放到临时表中。select * INTO #temp FROM user group by name,score having count(*) > 1
2、将user表中的重复数据全部删除。delete from user where name in (SELECT name from #temp)
3、将临时表#temp的数据重新放回去user表中
这样的就去除了冗余数据,但是会有一定风险,因此需要添加数据库事务。
name | score |
---|---|
张三 | 86 |
李四 | 63 |
王五 | 83 |
张三 | 86 |