13 Star 240 Fork 38

andy.niu / 面试宝典

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
Apache-2.0

.NET 面试宝典

锁表原因及如何处理

  1. 锁表发生在insert update 、delete 中
  2. 锁表的原理是 数据库使用独占式封锁机制,当执行上面的语句时,对表进行锁住,直到发生commite 或者 回滚 或者退出数据库用户
  3. 锁表的原因
    • A程序执行了对 tableA 的 insert ,并还未 commite时,B程序也对tableA 进行insert 则此时会发生资源正忙的异常 就是锁表
    • 锁表常发生于并发而不是并行(并行时,一个线程操作数据库时,另一个线程是不能操作数据库的,cpu 和i/o 分配原则)
  4. 减少锁表的概率,
    • 减少insert 、update 、delete 语句执行 到 commite 之间的时间。具体点批量执行改为单个执行、优化sql自身的非执行速度
    • 如果异常对事物进行回滚

乐观锁和悲观锁的区别

悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。

乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。

两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果经常产生冲突,上层应用会不断的进行retry,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。


排它锁和共享锁
在数据库中有两种基本的锁类型:排它锁(Exclusive Locks,即X锁)和共享锁(Share Locks,即S锁)。
当数据对象被加上排它锁时,其他的事务不能对它读取和修改。加了共享锁的数据对象可以被其他事务读取,但不能修改。
数据库利用这两种基本的锁类型来对数据库的事务进行并发控制。

表级锁和行级锁
DML锁的目的在于保证并发情况下的数据完整性,主要包括TM锁和TX锁,其中TM锁称为表级锁,TX锁称为事务锁或行级锁。
当Oracle执行DML语句时,系统自动在所要操作的表上申请TM类型的锁。当TM锁获得后,系统再自动申请TX类型的锁,并将实际锁定的数据行的锁标志位进行置位。这样在事务加锁前检查TX锁相容性时就不用再逐行检查锁标志,而只需检查TM锁模式的相容性即可,大大提高了系统的效率。TM锁包括了SS、SX、S、X等多种模式,在数据库中用0-6来表示。不同的SQL操作产生不同类型的TM锁。

缓存

  • 不需要实时更新但是又极其消耗数据库的数据。比如网站上商品销售排行榜,这种数据一天统计一次就可以了,用户不会关注其是否是实时的。
  • 需要实时更新,但是更新频率不高的数据。比如一个用户的订单列表,他肯定希望能够实时看到自己下的订单,但是大部分用户不会频繁下单。
  • 在某个时刻访问量极大而且更新也很频繁的数据。这种数据有一个很典型的例子就是秒杀,在秒杀那一刻,可能有N倍于平时的流量进来,系统压力会很大。但是这种数据使用的缓存不能和普通缓存一样,这种缓存必须保证不丢失,否则会有大问题。

mcv特点

M:Modle(模型,主要是Service业务逻辑层和Dao和数据库取得连接并发送数据的层) V: view(视图,也就是用户看的界面,通常是我们所熟知的前台页面,jsp等) C: controller(控制层,可以把他看作一个中转,他接收从前台用户发来的请求,并调用service,dao把数据发送到后台,后台经过数据库的操作及业务逻辑分析又将数据返回给controller,最后再返回前台jsp页面)。

说说Mvc的优缺点, 优点:

  1. MVC设计模式可以说实现了分层开发。各个层都有各个层的作用。
  2. 降低了层与层之间的依赖,有利于代码的标准化开发
  3. 再用新的代码业务逻辑替换时,只需要替换相对应的层,大大降低了我们的工作量,分工明确。

大数据存储解决方案

  • 分区 将数据库分区可提高其性能并易于维护。通过将一个大表拆分成更小的单个表,只访问一小部分数据的查询可以执行得更快,因为需要扫描的数据较少。而且可以更快地执行维护任务(如重建索引或备份表)。 实现分区操作时可以不拆分表,而将表物理地放置在个别的磁盘驱动器上。例如,将表放在某个物理驱动器上并将相关的表放在与之分离的驱动器上可提高查询性能,因为当执行涉及表之间联接的查询时,多个磁头同时读取数据。

  • 硬件分区 硬件分区将数据库设计为利用可用的硬件构架。

  • 水平分区 水平分区将一个表分段为多个表,每个表包含相同数目的列和较少的行。例如,可以将一个包含十亿行的表水平分区成12个表,每个小表代表特定年份内一个月的数据。任何需要特定月份数据的查询只引用相应月份的表。

  • 垂直分区 垂直分区将一个表分段为多个表,每个表包含较少的列。垂直分区的两种类型是规范化和行拆分。

redis容灾策略

基本的redis的容灾策略为:

  1. 采用master-slave方式
  2. 为了得到好的读写性能,master不做任何的持久化
  3. slave同时开启Snapshot和AOF来进行持久化,保证数据的安全性
  4. 当master挂掉后,修改slave为master
  5. 恢复原master数据,修改原先master为slave,启动slave
  6. 若master与slave都挂掉后,调用命令通过aof和snapshot进行恢复 恢复时要先确保恢复文件都正确了,才能启动主库;也可以先启动slave,将master与slave对调 开源方案codishttp://navyaijm.blog.51cto.com/4647068/1637688

哨兵的作用

  • 监控:监控主从是否正常
  • 通知:出现问题时,可以通知相关人员
  • 故障迁移:自动主从切换
  • 统一的配置管理:连接者询问sentinel取得主从的地址 Raft算法核心: 可视图

Redis常用五大数据类型

  1. String(字符串) string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。 string类型是Redis最基本的数据类型,一个redis中字符串value最多可以是512M
  2. Hash(哈希) Redis hash 是一个键值对集合。 Redis hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。 类似Java里面的Map<String,Object>
  3. List(列表) Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素导列表的头部(左边)或者尾部(右边)。 它的底层实际是个链表
  4. Set(集合) Redis的Set是string类型的无序集合。它是通过HashTable实现实现的,
  5. zset(sorted set:有序集合) Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。 不同的是每个元素都会关联一个double类型的分数。 redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。

Solr

rabbitMQ

rabbitMQ 特点
  1. 可靠性(Reliability)RabbitMQ 使用一些机制来保证可靠性,如持久化、传输确认、发布确认。
  2. 灵活的路由(Flexible Routing)在消息进入队列之前,通过 Exchange 来路由消息的。对于典型的路由功能,RabbitMQ 已经提供了一些内置的 Exchange 来实现。针对更复杂的路由功能,可以将多个 Exchange 绑定在一起,也通过插件机制实现自己的 Exchange 。
  3. 消息集群(Clustering)多个 RabbitMQ 服务器可以组成一个集群,形成一个逻辑 Broker 。
  4. 高可用(Highly Available Queues)队列可以在集群中的机器上进行镜像,使得在部分节点出问题的情况下队列仍然可用。
  5. 多种协议(Multi-protocol)RabbitMQ 支持多种消息队列协议,比如 STOMP、MQTT 等等。
  6. 多语言客户端(Many Clients)RabbitMQ 几乎支持所有常用语言,比如 Java、.NET、Ruby 等等。
  7. 管理界面(Management UI)RabbitMQ 提供了一个易用的用户界面,使得用户可以监控和管理消息 Broker 的许多方面。
  8. 跟踪机制(Tracing)如果消息异常,RabbitMQ 提供了消息跟踪机制,使用者可以找出发生了什么。
  9. 插件机制(Plugin System)RabbitMQ 提供了许多插件,来从多方面进行扩展,也可以编写自己的插件。
RabbitMQ概念

ConnectionFactory、Connection、Channel都是RabbitMQ对外提供的API中最基本的对象。Connection是RabbitMQ的socket链接,它封装了socket协议相关部分逻辑。ConnectionFactory为Connection的制造工厂。
Channel是我们与RabbitMQ打交道的最重要的一个接口,我们大部分的业务操作是在Channel这个接口中完成的,包括定义Queue、定义Exchange、绑定Queue与Exchange、发布消息等。

消息回执(Message acknowledgment)

在实际应用中,可能会发生消费者收到Queue中的消息,但没有处理完成就宕机(或出现其他意外)的情况,这种情况下就可能会导致消息丢失。为了避免这种情况发生,我们可以要求消费者在消费完消息后发送一个回执给RabbitMQ,RabbitMQ收到消息回执(Message acknowledgment)后才将该消息从Queue中移除;如果RabbitMQ没有收到回执并检测到消费者的RabbitMQ连接断开,则RabbitMQ会将该消息发送给其他消费者(如果存在多个消费者)进行处理。这里不存在timeout概念,一个消费者处理消息时间再长也不会导致该消息被发送给其他消费者,除非它的RabbitMQ连接断开

消息持久化(Message durability)

如果我们希望即使在RabbitMQ服务重启的情况下,也不会丢失消息,我们可以将Queue与Message都设置为可持久化的(durable),这样可以保证绝大部分情况下我们的RabbitMQ消息不会丢失。但依然解决不了小概率丢失事件的发生(比如RabbitMQ服务器已经接收到生产者的消息,但还没来得及持久化该消息时RabbitMQ服务器就断电了),如果我们需要对这种小概率事件也要管理起来,那么我们要用到事务。

交换器 Exchange

RabbitMQ中的Exchange有四种类型,不同的类型有着不同的路由策略

routing key

生产者在将消息发送给Exchange的时候,一般会指定一个routing key,来指定这个消息的路由规则,而这个routing key需要与Exchange Type及binding key联合使用才能最终生效。
在Exchange Type与binding key固定的情况下(在正常使用时一般这些内容都是固定配置好的),我们的生产者就可以在发送消息给Exchange时,通过指定routing key来决定消息流向哪里。
RabbitMQ为routing key设定的长度限制为255 bytes

Exchange Types

RabbitMQ常用的Exchange Type有fanout、direct、topic、headers这四种(AMQP规范里还提到两种Exchange Type,分别为system与自定义,这里不予以描述),下面分别进行介绍。

  1. Fanout Exchange 不处理路由键。你只需要简单的将队列绑定到交换机上。一个发送到交换机的消息都会被转发到与该交换机绑定的所有队列上。很像子网广播,每台子网内的主机都获得了一份复制的消息。Fanout交换机转发消息是最快的。
  2. Direct Exchange 处理路由键。需要将一个队列绑定到交换机上,要求该消息与一个特定的路由键完全匹配。这是一个完整的匹配。如果一个队列绑定到该交换机上要求路由键 “test”,则只有被标记为“test”的消息才被转发,不会转发test.aaa,也不会转发dog.123,只会转发test。
  3. Topic Exchange 将路由键和某模式进行匹配。此时队列需要绑定要一个模式上。符号 # 匹配一个或多个词,符号 * 匹配不多不少一个词。因此“audit.#”能够匹配到“audit.irs.corporate”,但是“audit.*” 只会匹配到“audit.irs”。

详细资料

C# 垃圾回收机制

用到GC,命名空间System.GC; 可以强制回收 GC.Collect()

  • 什么是GC GC如其名,就是垃圾收集,当然这里仅就内存而言。Garbage Collector(垃圾收集器,在不至于混淆的情况下也成为GC)以应用程序的root为基础,遍历应用程序在Heap上动态分配的所有对象[2],通过识别它们是否被引用来确定哪些对象是已经死亡的、哪些仍需要被使用。已经不再被应用程序的root或者别的对象所引用的对象就是已经死亡的对象,即所谓的垃圾,需要被回收。这就是GC工作的原理。为了实现这个原理,GC有多种算法。比较常见的算法有Reference Counting,Mark Sweep,Copy Collection等等。目前主流的虚拟系统.NET CLR,Java VM和Rotor都是采用的Mark Sweep算法。
.NET的GC机制有这样两个问题:
  • 首先,GC并不是能释放所有的资源。它不能自动释放非托管资源。
  • 第二,GC并不是实时性的,这将会造成系统性能上的瓶颈和不确定性。

GC并不是实时性的,这会造成系统性能上的瓶颈和不确定性。所以有了IDisposable接口,IDisposable接口定义了Dispose方法,这个方法用来供程序员显式调用以释放非托管资源。使用using语句可以简化资源管理。

GC注意事项:
  1. 只管理内存,非托管资源,如文件句柄,GDI资源,数据库连接等还需要用户去管理。
  2. 循环引用,网状结构等的实现会变得简单。GC的标志-压缩算法能有效的检测这些关系,并将不再被引用的网状结构整体删除。
  3. GC通过从程序的根对象开始遍历来检测一个对象是否可被其他对象访问,而不是用类似于COM中的引用计数方法。
  4. GC在一个独立的线程中运行来删除不再被引用的内存。
  5. GC每次运行时会压缩托管堆。
  6. 你必须对非托管资源的释放负责。可以通过在类型中定义Finalizer来保证资源得到释放。
  7. 对象的Finalizer被执行的时间是在对象不再被引用后的某个不确定的时间。注意并非和C++中一样在对象超出声明周期时立即执行析构函数
  8. Finalizer的使用有性能上的代价。需要Finalization的对象不会立即被清除,而需要先执行Finalizer.Finalizer,不是在GC执行的线程被调用。GC把每一个需要执行Finalizer的对象放到一个队列中去,然后启动另一个线程来执行所有这些Finalizer,而GC线程继续去删除其他待回收的对象。在下一个GC周期,这些执行完Finalizer的对象的内存才会被回收。
  9. .NET GC使用"代"(generations)的概念来优化性能。代帮助GC更迅速的识别那些最可能成为垃圾的对象。在上次执行完垃圾回收后新创建的对象为第0代对象。经历了一次GC周期的对象为第1代对象。经历了两次或更多的GC周期的对象为第2代对象。代的作用是为了区分局部变量和需要在应用程序生存周期中一直存活的对象。大部分第0代对象是局部变量。成员变量和全局变量很快变成第1代对象并最终成为第2代对象。
  10. GC对不同代的对象执行不同的检查策略以优化性能。每个GC周期都会检查第0代对象。大约1/10的GC周期检查第0代和第1代对象。大约1/100的GC周期检查所有的对象。重新思考Finalization的代价:需要Finalization的对象可能比不需要Finalization在内存中停留额外9个GC周期。如果此时它还没有被Finalize,就变成第2代对象,从而在内存中停留更长时间。

.net 异常处理

C# 语言的异常处理功能有助于处理在程序运行期间发生的任何意外或异常情况。 异常处理功能使用 try、catch 和 finally 关键字来尝试执行可能失败的操作、在你确定合理的情况下处理故障,以及在事后清除资源。 公共语言运行时 (CLR)、.NET Framework/任何第三方库或应用程序代码都可以生成异常。 异常是使用 throw 关键字创建而成。

.net 不能被继承类

在C#中定义了关键字sealed,被sealed修饰的类不能够被继承。在Java中同样也有关键字final表示一个类不能被继承。C++11提供final关键字使得类不能够被继承。

C#栈和堆

我们把内存分为堆空间和栈空间

  • 线程堆栈:简称栈 Stack 栈空间比较小,但是读取速度快
  • 托管堆: 简称堆 Heap 堆空间比较大,但是读取速度慢

栈的特征:数据只能从栈的顶端插入和删除把数据放入栈顶称为入栈(push)从栈顶删除数据称为出栈(pop)
堆:堆是一块内存区域,与栈不同,堆里的内存能够以任意顺序存入和移除

栈 vs 堆:区别?

栈通常保存着我们代码执行的步骤,而堆上存放的则多是对象,数据等。我们可以把栈想象成一个接着一个叠放在一起的盒子。当我们使用的时候,每次从最顶部取走一个盒子。栈也是如此,当一个方法(或类型)被调用完成的时候,就从栈顶取走,接着下一个。堆则不然,像是一个仓库,储存着我们使用的各种对象等信息,跟栈不同的是他们被调用完毕不会立即被清理掉。


栈存储的是基本值类型,堆存储的是new出来的对象。引用类型在栈中存储一个引用,其实际的存储位置位于托管堆。 当我们程序执行的时候,在栈和堆中分配有四种主要的类型:值类型,引用类型,指针,指令。


栈:所分配的内存是在一块连续的内存区域内.当我们声明变量时,那么编译器会自动接着当前栈区的结尾来分配内存
堆:一般由程序员分配释放(new), 若程序员不释放,程序结束时可能由操作系统回收

数据库如何优化

  1. 数据库运维方面的优化:启用数据库缓存。对于一些比较常用的查询可以采用数据库缓存的机制,部署的时候需要注意设置好缓存依赖项,防止“过期”数据的产生。
  2. 数据库索引方面的优化:比如常用的字段建索引,联合查询考虑联合索引。(PS:如果你有基础,可以敞开谈谈聚集索引和非聚集索引的使用场景和区别)
  3. 数据库查询方面的优化:避免select * 的写法、尽量不用in和not in 这种耗性能的用法等等
  4. 数据库算法方面的优化:尽量避免大事务操作、减少循环算法,对于大数据量的操作,避免使用游标的用法等等

你在你以前的项目中,主要解决了什么技术性难题,如果出现问题,你如何迅速找到问题,你遇到问题都是怎么解决的

  1. 先根据出现的问题,大概排查出问出问题的几个点,逐个排查, 错误日志- sql查询计划-定位程序错误,
  2. 技术性难题: 自己解决---csdn(博客园)---谷歌---请教技术大牛
  3. 解决的技术难题:
    • 微信支付,苹果和安卓的支付力度不一样
    • 消息推送 signalr(以前是长轮询)

项目中你用到的设计模式有哪些,举例说明

单例:可以保证系统中一个类只有一个实例,并且自行实例化向整个系统提供(例如一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务)
最简单的就是写考虑线程安全的单例模式

public class Singleton {
    // 定义一个静态变量来保存类的实例
    private static Singleton uniqueInstance;
    // 定义一个标识确保线程同步
    private static readonly object locker = new object();
    // 定义私有构造函数,使外界不能创建该类实例
    private Singleton() {}
    // 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
    public static Singleton GetInstance() {
        // 双重锁定只需要一句判断就可以了
        if (uniqueInstance == null) {
            lock(locker) {
                // 如果类的实例不存在则创建,否则直接返回
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

数据库的索引失效?

删掉索引,然后再重建索引

IIS 经典模式和集成模式的区别

  • 经典形式是为了与之前的版本兼容,运用ISAPI扩展来调用ASP.NET运转库
  • 集成形式是一种统一的请求处置管道,它将ASP.NET请求管道与IIS中心管道组合在一同,这种形式可以提供更好的性能,可以完成配置和管理的模块化,而且增加了运用托管代码模块扩展IIS时的灵敏性。

假设老的Web应用程序运转于IIS7.0的集成形式下, 可能需求对应用程序的web.config文件停止修正,特别是运用了完成IHttpHandler接口的自定义模块的状况。IIS7.0在同一个效劳器上可以同时支持两种形式的应用程序。

多线程 有几种启动方式

使用new Thread()和new Thread(Runnable)形式

  • 第一种直接调用thread的run方法,所以,往往使用Thread子类,new Thread(){}.start();这表示调用Thread子类对象的run方法, new Thread(){}表示一个Thread的匿名子类的实例对象。
  • 第二种调用Runnable的run方法。 new Thread(new Runnable(){}).start();这表示调用Thread对象接受的Runnable对象的run方法,new Runnable(){}表示一个Runnable的匿名子类的实例对象

数据库锁有几种

  • 共享(S)锁:多个事务可封锁一个共享页;任何事务都不能修改该页; 通常是该页被读取完毕,S锁立即被释放。
  • 排它(X)锁:仅允许一个事务封锁此页;其他任何事务必须等到X锁被释放才能对该页进行访问;X锁一直到事务结束才能被释放。
  • 更新(U)锁:用来预定要对此页施加X锁,它允许其他事务读,但不允许再施加U锁或X锁;当被读取的页将要被更新时,则升级为X锁;U锁一直到事务结束时才能被释放。

两个html页面之间怎么传递参数值

  1. 如果页面A和页面B是同域的情况,使用Cookie传递参数 ,a页面保存Cookie,b页面读取
  2. 如果页面A和页面B不同域的情况,页面A通过点击链接跳转到页面B的话,那么数据可以通过search和hash附加在B页面的URL上,传递给页面B,window对象它们是共享的,因此我们可以通过window.xx来传递数据,比如window.name=123;
  3. 如果页面A和B,不同域,也没有跳转关系,如果浏览器支持HTML5的话,我们可以使用window.postMessage来跨域发送数据。

ViewData和ViewBag区别

ViewData
  • ViewData是一个继承自ViewDataDictionary类的Dictionary对象。
  • ViewData用来从Controller向对应的View传递值。
  • ViewData的只在当前当前的请求中有效,生命周期和View相同,其值不能在多个请求中共享。
  • 在重定向(redirection)后,ViewData中存储的变量值将变为null。
  • 在取出ViewData中的变量值是,必须进行合适的类型转换(隐式或显式)和空值检查。
ViewBag
  • ViewBag是一个动态类型变量(dynamic),这是C# 4.0引入的新特性,变量类型会在运行时进行解析。
  • ViewBag基本上是ViewData的包装,也是用来从Controller向View来传递值的。
  • ViewBag也只在当前的请求中有效。
  • 在重定向(redirection)后,ViewBag中存储的变量值将变为null
  • 因为ViewBag是动态类型,所以我们在取得其值时,不需要进行类型转换。

装箱和取消装箱

装箱是将值类型转换为 object 类型或由此值类型实现的任何接口类型的过程。 当 CLR 对值类型进行装箱时,会将该值包装到 System.Object内部,再将后者存储在托管堆上。 取消装箱将从对象中提取值类型。 装箱是隐式的;取消装箱是显式的。 装箱和取消装箱的概念是类型系统 C#统一视图的基础,其中任一类型的值都被视为一个对象。

数据库索引

数据库索引有

  • sqlserver 主键索引、唯一索引、聚集索引、非聚集索引
  • mysql 唯一索引(主键索引)、聚集索引、非聚集索引、全文索引

唯一索引
唯一索引不允许两行具有相同的索引值。
如果现有数据中存在重复的键值,则大多数数据库都不允许将新创建的唯一索引与表一起保存。当新数据将使表中的键值重复时,数据库也拒绝接受此数据。例如,如果在 employee 表中的职员姓氏(lname) 列上创建了唯一索引,则所有职员不能同姓。

主键索引
主键索引是唯一索引的特殊类型。
数据库表通常有一列或列组合,其值用来唯一标识表中的每一行。该列称为表的主键。 在数据库关系图中为表定义一个主键将自动创建主键索引,主键索引是唯一索引的特殊类型。主键索引要求主键中的每个值是唯一的。当在查询中使用主键索引时,它还允许快速访问数据。

它们的一些比较:

  1. 对于主健/unique constraint , oracle/sql server/mysql等都会自动建立唯一索引;
  2. 主键不一定只包含一个字段,所以如果你在主键的其中一个字段建唯一索引还是必要的;
  3. 主健可作外健,唯一索引不可;
  4. 主健不可为空,唯一索引可;
  5. 主健也可是多个字段的组合;
  6. 主键与唯一索引不同的是:
    • a.有not null属性;
    • b.每个表只能有一个。

聚集索引
一种索引,该索引中键值的逻辑顺序决定了表中相应行的物理顺序。

非聚集索引
一种索引,该索引中索引的逻辑顺序与磁盘上行的物理存储顺序不同。

索引是通过二叉树的数据结构来描述的,我们可以这么理解聚簇索引:索引的叶节点就是数据节点。而非聚簇索引的叶节点仍然是索引节点,只不过有一个指针指向对应的数据块

聚集索引:可以帮助把很大的范围,迅速减小范围。但是查找该记录,就要从这个小范围中Scan了。
非聚集索引:把一个很大的范围,转换成一个小的地图。你需要在这个小地图中找你要寻找的信息的位置。然后通过这个位置,再去找你所需要的记录。

使用场景
基于上述的两种规则,那么在什么时候适合聚集索引,什么时候适合非聚集索引?
使用场景

WebApi和MVC有什么区别

Web API更倾向于基于HTTP协议的服务,直接返回用户的数据请求。MVC是建站的一种框架,倾向于返回用户的页面请求。 ASP.NET Web API 的特性,更能说明Web API是一种数据请求框架:

  1. ASP.NET Web API 可以根据请求报文来返回的相应数据格式。包括JSON和XML。
  2. ASP.NET Web API 单独做数据请求和MVC做页面请求可以让Web前端和后台更好的解耦,减少开发难度。
  3. Web API 可以更好地用在移动端网页、桌面端网页或者桌面程序,调用方更加多样化。
  4. Web API 的宿主可以选择多样:WebHost,,ConsoleHost,甚至是windows Services。

WebAPI主演提供了一套RestfulAPI的开发框架,提供了较为完整的http语义支持,主要用来做开放API,更抽象,更不注重View的生成。

MVC的主要使用场景在于Web站点的开发,他在后端实现了一套完整的MVC开发框架,能提供方便的页面开发,默认使用Razor视图引擎,提供了后端html构造,用户可以方便地开发出带页面的站点。

Array和ArrayList的区别

  1. Array类型的变量在声明的同时必须进行实例化(至少得初始化数组的大小),而ArrayList可以只是先声明。
  2. Array只能存储同构的对象,而ArrayList可以存储异构的对象。

同构的对象是指类型相同的对象,若声明为int[]的数组就只能存放整形数据,string[]只能存放字符型数据,但声明为object[]的数组除外。

  1. 在CLR托管对中的存放方式,Array是始终是连续存放的,而ArrayList的存放不一定连续。
  2. 初始化大小 Array对象的初始化必须只定指定大小,且创建后的数组大小是固定的
  3. Array不能够随意添加和删除其中的项,而ArrayList可以在任意位置插入和删除项。

Web Service,WCF, Web API

Web Service

  1. 它是基于SOAP协议的,数据格式是XML
  2. 只支持HTTP协议
  3. 它不是开源的,但可以被任意一个了解XML的人使用
  4. 它只能部署在IIS上

WCF

  1. 这个也是基于SOAP的,数据格式是XML
  2. 这个是Web Service(ASMX)的进化版,可以支持各种各样的协议,像TCP,HTTP,HTTPS,Named Pipes, MSMQ.
  3. WCF的主要问题是,它配置起来特别的繁琐
  4. 它不是开源的,但可以被任意一个了解XML的人使用
  5. 它可以部署应用程序中或者IIS上或者Windows服务中

WCF Rest

  1. 想使用WCF Rest service,你必须在WCF中使用webHttpBindings
  2. 它分别用[WebGet]和[WebInvoke]属性,实现了HTTP的GET和POST动词
  3. 要想使用其他的HTTP动词,你需要在IIS中做一些配置,使.svc文件可以接受这些动词的请求
  4. 使用WebGet通过参数传输数据,也需要配置。而且必须指定UriTemplate
  5. 它支持XML、JSON以及ATOM这些数据格式

Web API

  1. 这是一个简单的构建HTTP服务的新框架
  2. 在.net平台上Web API 是一个开源的、理想的、构建REST-ful 服务的技术
  3. 不像WCF REST Service.它可以使用HTTP的全部特点(比如URIs、request/response头,缓存,版本控制,多种内容格式)
  4. 它也支持MVC的特征,像路由、控制器、action、filter、模型绑定、控制反转(IOC)或依赖注入(DI),单元测试。这些可以使程序更简单、更健壮
  5. 它可以部署在应用程序和IIS上
  6. 这是一个轻量级的框架,并且对限制带宽的设备,比如智能手机等支持的很好
  7. Response可以被Web API的MediaTypeFormatter转换成Json、XML 或者任何你想转换的格式。
WCF和WEB API我该选择哪个?
  1. 当你想创建一个支持消息、消息队列、双工通信的服务时,你应该选择WCF
  2. 当你想创建一个服务,可以用更快速的传输通道时,像TCP、Named Pipes或者甚至是UDP(在WCF4.5中),在其他传输通道不可用的时候也可以支持HTTP。
  3. 当你想创建一个基于HTTP的面向资源的服务并且可以使用HTTP的全部特征时(比如URIs、request/response头,缓存,版本控制,多种内容格式),你应该选择Web API
  4. 当你想让你的服务用于浏览器、手机、iPhone和平板电脑时,你应该选择Web API

const和readonly关键字

先来了解静态常量和动态常量。

  • 静态常量:所谓静态常量就是在编译期间会对变量进行解析,再将常量的值替换成初始化的值。
  • 动态常量:所谓动态常量就是编译期间会将变量标记只读常量,而不用常量的值代替,这样在声明时可以不初始化,可以延迟到构造函数初始化。

const修饰的常量是上述中的第一种,即静态常量,而readonly是上述中第二种即动态常量。他们的区别可以从静态常量和动态常量的特性来说明:

  • const修饰的常量在声明时必须初始化值;readonly修饰的常量可以不初始化值,且可以延迟到构造函数。
  • cons修饰的常量在编译期间会被解析,并将常量的值替换成初始化的值;而readonly延迟到运行的时候。
  • const修饰的常量注重的是效率;readonly修饰的常量注重灵活。
  • const修饰的常量没有内存消耗;readonly因为需要保存常量,所以有内存消耗。
  • const只能修饰基元类型、枚举类、或者字符串类型;readonly却没有这个限制。

值类型、引用类型

  1. 值类型(ValueType) 值类型包括:数值类型,结构体,bool型,用户定义的结构体,枚举,可空类型。
    值类型的变量直接存储数据,分配在托管栈中。变量会在创建它们的方法返回时自动释放,例如在一个方法中声明Char型的变量name=’C’,当实例化它的方法结束时,name变量在栈上占用的内存就会自动释放
    C#的所有值类型均隐式派生自System.ValueType。

  2. 引用类型(ReferenceType) 引用类型包括:数组,用户定义的类、接口、委托,object,字符串,null类型,类。
    引用类型的变量持有的是数据的引用,数据存储在数据堆,分配在托管堆中,变量并不会在创建它们的方法结束时释放内存,它们所占用的内存会被CLR中的垃圾回收机制释放。

装箱和拆箱

装箱就是将一个值类型转换成等值的引用类型  
在堆上为新生成的对象(该对象包含数据,对象本身没有名称)分配内存。  
将堆栈上值类型变量的值拷贝到堆上的对象中。  
将堆上创建的对象的地址返回给引用类型变量(从程序员角度看,这个变量的名称就好像堆上对象的名称一样)。  

拆箱就是将一个引用类型转换成等值的值类型   
将引用类型变量堆上的值拷贝到栈上面。  

ref与out

ref和out都是按地址传递,使用后都将改变原来参数的数值。

ref关键字
  1. 方法定义和调用方法都必须显式使用 ref 关键字
  2. 传递到 ref 参数的参数必须初始化,否则程序会报错
  3. 通过ref的这个特性,一定程度上解决了C#中的函数只能有一个返回值的问题
out关键字

方法定义和调用方法都必须显式使用 out关键字

  1. 方法定义和调用方法都必须显式使用 out关键字
  2. out关键字无法将参数值传递到out参数所在的方法中,只能传递参数的引用(个人理解),所以out参数的参数值初始化必须在其方法内进行,否则程序会报错
  3. 通过out的这个特性,一定程度上解决了C#中的函数只能有一个返回值的问题

new和override的区别

  • override是指“覆盖”,是指子类覆盖了父类的方法。子类的对象无法再访问父类中的该方法。
  • new是指“隐藏”,是指子类隐藏了父类的方法,当然,通过一定的转换,可以在子类的对象中访问父类的方法。

Attribute介绍

MSDN中定义为:公共语言运行时运行添加类似关键字的描述声明,叫做Attribute,它对程序中的元素进行标注,如类型、方法、字段和属性等。attribute和Microsoft.Net Framework文件的元数据保存在一起,可以用来在运行时描述你的代码,或者在程序运行时影响应用程序的行为。

我们简单地总结:定制特性attribute,本质上是一个类,其为目标元素提供关联附加信息,并在运行时以反射的方式来获取附件信息。

attribute通用规则
  1. 特性可以应用的目标元素包括:程序集(assemby)、模块(module)、类型(Type)、属性(Property)、事件(Event)、字段(Field)、方法(Method)、参数(param)、返回值(return).
  2. 特性以[,]形式展示。放在紧挨着元素上
  3. attribute实例,是在编译期进行初始化,而不是运行期。

C# .NET内置委托

委托是什么

委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。

委托是一种特殊的类型(class),用途是来实现对一种方法的封装。在某种事件发生时,自动调用该方法。好处显然易见,它使用户可以自定义自己的方法实现,通过封装,CLR会在相应事件激发时调用你定义的方法,实现你的功能。

C#内置委托 Action、Action、Func、Predicate
CLR环境中给我们内置了几个常用委托Action、 Action、Func、Predicate,一般我们要用到委托的时候,尽量不要自己再定义一 个委托了,就用系统内置的这几个已经能够满足大部分的需求,且让代码符合规范。

什么是AOP?

这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。
AOP面向切面编程链接

Asp.Net MVC -Filter

输入图片说明

lock 应锁定什么类型对象

首先先上官方Msdn的说法

lock 关键字可确保当一个线程位于代码的临界区时,另一个线程不会进入该临界区。 如果其他线程尝试进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。
lock 关键字在块的开始处调用 Enter,而在块的结尾处调用 Exit。 ThreadInterruptedException 引发,如果 Interrupt 中断等待输入 lock 语句的线程。 通常,应避免锁定 public 类型,否则实例将超出代码的控制范围。

常见的结构 lock (this)、lock (typeof (MyType)) 和 lock ("myLock") 违反此准则:
如果实例可以被公共访问,将出现 lock (this) 问题。
如果 MyType 可以被公共访问,将出现 lock (typeof (MyType)) 问题。
由于进程中使用同一字符串的任何其他代码都将共享同一个锁,所以出现 lock("myLock") 问题。
最佳做法是定义 private 对象来锁定, 或 private static 对象变量来保护所有实例所共有的数据。
在 lock 语句的正文不能使用 等待 关键字。

打个比方,有这样一个情景,很多公司所在的大厦的厕所的蹲位都是小单间型的,也就是一次只能进去一个人,那么为了避免每次进去一个人,那怎么做呢?不就是一个人进去之后顺手把门锁上么?这样你在里面干啥事,外边的人也只能等待你解放完了,才能进入。而蹲位的资源(蹲位,手纸等)是共享的。

最常使用的锁是如下格式的代码段:

private static object objlock = new object();
lock (objlock )
{
    //要执行的代码逻辑
}

为什么锁的对象是私有的呢?还是以厕所为例子吧,私有就好比,这把锁只有你能访问到,而且最好这把锁不会因为外力而有所改变,别人访问不到,这样才能保证你进去了,别人就进不去了,如果是公有的,就好比你蹲位小单间的锁不是安装在里面而是安装在外边的,别人想不想进就不是你所能控制的了,这样也不安全。

关于lock的介绍就到这里,有下面几点需要注意的地方
  1. lock的是引用类型的对象,string类型除外。
  2. lock推荐的做法是使用静态的、只读的、私有的对象。
  3. 保证lock的对象在外部无法修改才有意义,如果lock的对象在外部改变了,对其他线程就会畅通无阻,失去了lock的意义。

RabbitMQ 优点

  • 安装部署简单,上手门槛低,功能丰富,符合AMQP标准;
  • 企业级消息队列,经过大量实践考验的高可靠;
  • 集群易扩展,可以轻松的增减集群节点;
  • 有强大的WEB管理页面。

AMQP,即Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。Erlang中的实现有 RabbitMQ等。

什么是AMQP 高级消息队列协议使得遵从该规范的客户端应用和消息中间件服务器的全功能互操作成为可能。


javascript

//快速排序 递归
function quickSort(arr){
    if(arr.length<=1) return arr;
    var num = Math.floor(arr.length/2);//找到中间数的索引值,如果是浮点数,则向下取整
    var numValue = arr.splice(num,1);//找到中间数的值
    var left = [] , right = [];    
    for(var i = 0; i< arr.length ; i++){
        if(arr[i]<numValue ){            
            left.push(arr[i]);//基准点的左边的数传到左边数组
        }else{
            right.push(arr[i]);//基准点的右边的数传到右边数组
        }
    }
    return quickSort(left).concat([numValue],quickSort(right));//递归不断重复比较
}
// parseInt 实现 
function _parseInt (str, radix) {
  let str_type = typeof str
  let res = 0
  if (str_type !== 'string' && str_type !== 'number') {
     // 如果类型不是 string 或 number 类型返回NaN
     return NaN
  }

  // 字符串处理
  str = String(str).trim().split('.')[0]
  let length = str.length
  if (!length) {
    // 如果为空则返回 NaN
    return NaN
  }

  if (!radix) {
    // 如果 radix 为0 null undefined
    // 则转化为 10
    radix = 10
  }
  if (typeof radix !== 'number' || radix < 2 || radix > 36) {
    return NaN
  }

  for (let i = 0; i < length; i++) {
    let arr = str.split('').reverse().join('')
    res += Math.floor(arr[i]) * Math.pow(radix, i)
  }
  return res
}

//获取url参数返回object对象
function fn(){
    var _array = location.href.split('?'),obj = {};
    if(_array.length==1) return obj;
    var array = _array[1].split('&');
    for(var i = 0 ;i < array.length; i++){
    	var temp = array[i].split('=')
    	obj[temp[0]] = temp[1];
    }
    return obj;
}

function fn2(){
    var _array = location.href.match(/([^?&=]+)=([^?&=]*)/g),obj = {};
    if(_array){
        for(var i = 0 ;i < _array.length; i++){
           var temp = _array[i].split('=')
           obj[temp[0]] = temp[1];
	}
    }
    return obj;
}

//去除空格
string.replace(/(^\s*)|(\s*$)/gi,'');

//call()方法和apply()
//1. 每个函数都包含两个非继承而来的方法:call()方法和apply()方法。
//2. 相同点:这两个方法的作用是一样的。
//3. 不同点:接收参数的方式不同。
//apply()方法 接收两个参数,一个是函数运行的作用域(this),另一个是参数数组。
apply([thisObj [,argArray] ]);
//call()方法 第一个参数和apply()方法的一样,但是传递给函数的参数必须列举出来。
call([thisObject[,arg1 [,arg2 [,...,argn]]]]);

//闭包
//定义和用法:当一个函数的返回值是另外一个函数,而返回的那个函数如果调用了其父函数内部的其它变量,如果返回的这个函数在外部被执行,就产生了闭包。
//表现形式:使函数外部能够调用函数内部定义的变量。
//什么是跨域?
//由于浏览器同源策略,凡是发送请求url的协议、域名、端口三者之间任意一与当前页面地址不同即为跨域
//porxy代理 通过nginx代理
//CORS 【Cross-Origin Resource Sharing】
res.writeHead(200, {
    "Content-Type": "text/html; charset=UTF-8",
    "Access-Control-Allow-Origin":'http://localhost',
    'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
    'Access-Control-Allow-Headers': 'X-Requested-With, Content-Type'
});
//jsonp
function testjsonp(data) {
    console.log(data.name); // 获取返回的结果
}
var _script = document.createElement('script');
_script.type = "text/javascript";
_script.src = "http://localhost:8888/jsonp?callback=testjsonp";
document.head.appendChild(_script);
Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

简介

.net 面试问题整理 展开 收起
汇编
Apache-2.0
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
汇编
1
https://gitee.com/baily/mianshi.git
git@gitee.com:baily/mianshi.git
baily
mianshi
面试宝典
master

搜索帮助