面试流程常见问题总结
计算机网络常见问题
-
TCP三次握手,四次挥手
三次握手:主动方发送建立连接请求,被动方表示可以,主动方收到对方同意的信号,开始发数据
四次挥手:主动方发完数据请求断开连接,被动方收到回复,等被动方发完数据也发送断开请求,主动方收到回复,双方断开连接
-
TCP为何是可靠传输
- 收到确认、超时重发、滑动窗口等机制确保没有数据丢失,并且保证数据报是有序的
- 拥塞控制机制可以根据网络情况控制传输速率
-
拥塞控制
- 慢开始:指数方式启动到阈值
- 拥塞避免:开始线性增长,可能会遇到超时或者丢包等情况,超时重传则阈值减半,重新慢开始;收到三个重复确认则快重传、快恢复
- 快重传:从阈值速度开始
- 快恢复:直接进入拥塞避免限行速度增长
-
计算机网络五层协议
- 物理层:物理组成,集线器、传输介质等
- 数据链路层:制定信号传输的规则,交换机等
- 网络层:建立主机到主机的通信,IP协议等
- 传输层:建立端口到端口的通信,TCP、UDP等协议
- 应用层是最接近用户的,应用程序提供网络服务,HTTP协议等
-
Socket具体是什么
- socket是对TCP/IP协议的封装,它的出现只是使得程序员更方便地使用TCP/IP协议栈而已
- socket本身并不是协议,它是应用层与TCP/IP协议族通信的中间软件抽象层,是一组调用接口(TCP/IP网络的API函数)
操作系统常见问题
-
进程与线程的区别
-
进程是资源分配的最小单位,一个进程包括多个线程,多个线程共享进程内独立的资源
-
线程是CPU调度的最小单位,比进程更加轻量级
-
多CPU多进程可以并行,多线程是并发
-
-
进程间通信与进程同步(具体怎么做需要了解)
- 管道( pipe ):半双工的(即数据只能在一个方向上流动),用于具有亲缘关系的进程之间的通信,可以看成是一种特殊的文件,对于它的读写也可以使用普通的
read、write
等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存
中。 - 消息队列:是消息的链接表,存放在内核中并由消息队列标识符标识
- 共享内存:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问
- 套接字(socket ):可用于不同机器间的进程通信,双方以客户端与服务器通信的方式建立连接,并相互发送、接收数据,交互完成之后会断开连接节省资源
- 管道( pipe ):半双工的(即数据只能在一个方向上流动),用于具有亲缘关系的进程之间的通信,可以看成是一种特殊的文件,对于它的读写也可以使用普通的
-
同步方式:互斥锁(mutex)、条件变量(cond)、信号量(sem)
-
进程调度算法:1.先来先去服务2.时间片轮转法3.多级反馈队列算法4.最短进程优先5.最短剩余时间优先6.最高响应比优先7.多级反馈队列调度算法
-
缓存的替换策略
- FIFO
:缓存首先删除第一个块,而不考虑它以前访问的次数或次数。 - LIFO
:首先删除最近访问的块,而不考虑它以前访问的次数或次数。 - LRU
:替换掉最近被请求最少的对象,这种传统策略在实际中应用最广 - LFU :替换掉访问次数最少的缓存,这一策略意图是保留最常用的、最流行的对象
- FIFO
数据库常见问题
-
数据库mysql内核:InnoDB,索引,事务,几种隔离,几种范式,意向锁、排他锁等
-
mysql的结构、二级索引等
- B+树,子节点比较多,读取磁盘的次数比二叉树要少很多
- 二级索引:每一个索引只存放了主键的位置,然后通过主键查找完整的数据记录
JAVA常见问题
-
sprintboot背后原理
-
线程池参数信息如下
- corePoolSize:核心池的大小,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中
- maximumPoolSize:线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程
- keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止
- unit:参数keepAliveTime的时间单位
- workQueue:一个阻塞队列,用来存储等待执行的任务
- threadFactory:线程工厂,主要用来创建线程
- handler:表示当拒绝处理任务时的策略
-
sleep()、wait()的区别
- sleep方法使当前线程暂停执行指定的时间,让出cpu给其他线程,但是它的监控状态依然保持着,当指定的时间到了又会自动恢复运行状态。在调用sleep方法后,线程不会释放对象锁
- 当调用wait方法时,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池处于准备状态。
-
java库中的数据结构
- ArrayList是一种数组队列,基于动态数组实现的,LinkedList是基于循环双向链表的
- hashmap是基于哈希表的map接口的实现,加载因子的默认值为0.75,JDK1.8采用位桶数组+红黑树
-
java垃圾回收机制
垃圾回收的过程其实简单概括就两步:1.找到堆上没被引用的对象 2.删除该对象,释放内存。
-
java中String为final的好处
- 不可修改的值更便于散列
- 不可变对象线程安全
-
java字符串拼接的几种方式和区别
- 结论StringBuilder效率最高
- str1+str2:原理是(new StringBuilder()).append(str1).append(str2)
- str1.concat(str2):原理是new了一个新String并返回
- StringBuffer.append():
StringBuffer
类的对象是可以扩充和修改的 StringBuilder
和StringBuffer
类似
-
java单例模式
- 一个类只有一个对象,在类中创建实例并返回
- 饿汉模式就是立即加载,直接创建实例
- 懒汉模式就是延迟加载,在程序需要用到的时候再创建实例
-
java七大原则
- 单一职责原则:不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。
- 里氏替换原则:继承必须确保超类所拥有的性质在子类中仍然成立。
- 依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。
- 接口隔离原则:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。
- 迪米特法则:一个对象应该对其他对象保持最少的了解。
- 开闭原则:当应用的需求改变时,在不修改软件实体的源代码或者二进制代码的前提下,可以扩展模块的功能,使其满足新的需求。
-
java设计模式
单例模式:某个类只能有一个实例,提供一个全局的访问点。
简单工厂:一个工厂类根据传入的参量决定创建出那一种产品类的实例。
工厂方法:定义一个创建对象的接口,让子类决定实例化那个类。
抽象工厂:创建相关或依赖对象的家族,而无需明确指定具体类。
建造者模式:封装一个复杂对象的构建过程,并可以按步骤构造。
原型模式:通过复制现有的实例来创建新的实例。
适配器模式:将一个类的方法接口转换成客户希望的另外一个接口。
组合模式:将对象组合成树形结构以表示“”部分-整体“”的层次结构。
装饰模式:动态的给对象添加新的功能。
代理模式:为其他对象提供一个代理以便控制这个对象的访问。
亨元(蝇量)模式:通过共享技术来有效的支持大量细粒度的对象。
外观模式:对外提供一个统一的方法,来访问子系统中的一群接口。
桥接模式:将抽象部分和它的实现部分分离,使它们都可以独立的变化。
模板模式:定义一个算法结构,而将一些步骤延迟到子类实现。
解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器。
策略模式:定义一系列算法,把他们封装起来,并且使它们可以相互替换。
状态模式:允许一个对象在其对象内部状态改变时改变它的行为。
观察者模式:对象间的一对多的依赖关系。
备忘录模式:在不破坏封装的前提下,保持对象的内部状态。
中介者模式:用一个中介对象来封装一系列的对象交互。
命令模式:将命令请求封装为一个对象,使得可以用不同的请求来进行参数化。
访问者模式:在不改变数据结构的前提下,增加作用于一组对象元素的新功能。
责任链模式:将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会。
迭代器模式:一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构。
算法常见问题
-
哈希表长度为何是素数
合数的因子比素数更多,对于随机的一组数据来说应该是区别不大,但是如果是一个很有规律的数列的话,刚好他们和哈希表的长度有共同的因子,就有可能很有规律的在哈希表中产生冲突,但是如果是素数的话,产生冲突的位置就比较均匀,比较分散,没这么有规律
-
hash表解决冲突的方法
-
开放地址法:线性探查法、二次探查法、双重散列法
-
链地址法:存放链表或者红黑树的根节点
-
常见算法题
-
hadoop:100G的文件里包含id信息,有十台机器,每台机器可处理1G的信息,如何求出重复id的top100,并输出个数
思路:100G文件通过将id进行哈希,这样两台机器不会出现相同的id,每个机器里使用大堆维护,得出top100(大数据里就这么处理
-
求X的平方根
-
首选牛顿公式:一个求平方根的多项式
-
二分法:类似穷举,在0-X之间使用二分法依次尝试
-
-
链表反转
递归创建反向链表
-
无序数组的中位数
- 所有数据5个一组,求出每一组的中位数,分为大于中位数、中位数、小于中位数三组
- 在所有的中位数中递归寻找中位数中的中位数m*
- 使用m*分隔所有数据为五个部分,并淘汰两个部分
- 使用m*将所有元素分成两个部分,递归寻找中位数
-
快排代码
public void quickSort(int[] arr, int l, int r){ if(l < r){ int pivot = getPivot(arr, l, r); quickSort(arr, l, pivot - 1); quickSort(arr, pivot + 1, r); } } public int getPivot(int[] arr, int l, int r){ int value = arr[l]; int pivot = l; int index = l; while(index < r){ if(arr[++index] < value){ swap(arr, ++pivot, index); } } swap(arr, l, pivot) return pivot } public void swap(int[] arr, int l, int r){ int tmp = arr[l]; arr[l] = arr[r]; arr[r] = tmp; }