# JavaInterview
**Repository Path**: cdsuifeng/JavaInterview
## Basic Information
- **Project Name**: JavaInterview
- **Description**: Java开发面试题整理含答案(计网、Java、操作系统、数据库、框架)
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 1
- **Created**: 2022-06-29
- **Last Updated**: 2022-06-29
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Java开发一轮复习
## **一、网络篇**
**1、OSI七层模型与TCP/IP 五层模型**
OSI七层:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层
TCP/IP五层:物理层、数据链路层、网络层、传输层、应用层
**2、常见应用层协议和运输层、网络层协议,以及硬件如路由器之类在哪一层**
应用层:HTTP、SMTP、DNS、FTP
传输层:TCP 、UDP
网络层:ICMP 、IP、路由器、防火墙
数据链路层:网卡、网桥、交换机
物理层:中继器、集线器
**3、TCP与UDP区别和应用场景,基于TCP的协议有哪些,基于UDP的有哪些**
| 类型 | 特点 | 性能 | 应用过场景 | 首部字节 | |
| ---- | :------------------------: | :--------------------: | :--------------: | :------: | ---- |
| TCP | 面向连接、可靠、字节流 | 传输效率慢、所需资源多 | 文件、邮件传输 | 20-60 | |
| UDP | 无连接、不可靠、数据报文段 | 传输效率快、所需资源少 | 语音、视频、直播 | 8个字节 | |
**基于TCP的协议:**HTTP、FTP、SMTP
**基于UDP的协议:**RIP、DNS、SNMP
**4、TCP可靠传输的保证,拥塞控制目的和过程**
**TCP通过:**应用数据分割、对数据包进行编号、校验和、流量控制、拥塞控制、ARP协议、超时重传等措施保证数据的可靠传输;
**拥塞控制目的:**为了防止过多的数据注入到网络中,避免网络中的路由器、链路过载
**拥塞控制过程:**TCP发送将维护一个拥塞窗口的状态变量,该变量随着网络拥塞程度动态变化,通过慢开始、拥塞避免等算法减少网络拥塞的发生。
**5、TCP粘包现象原因和解决方法**
**TCP粘包是指**:发送方发送的若干包数据到接收方接收时粘成一包
**发送方原因:**
TCP默认使用Nagle算法(主要作用:减少网络中报文段的数量),而Nagle算法主要做两件事:
只有上一个分组得到确认,才会发送下一个分组
收集多个小分组,在一个确认到来时一起发送
Nagle算法造成了发送方可能会出现粘包问题
**接收方原因:**
TCP接收到数据包时,并不会马上交到应用层进行处理,或者说应用层并不会立即处理。实际上, TCP将接收到的数据包保存在接收缓存里,然后应用程序主动从缓存读取收到的分组。这样一来,如果 TCP 接收数据包到缓存的速度大于应用程序从缓存中读取数据包的速度,多个包就会被缓存,应用程 序就有可能读取到多个首尾相接粘到一起的包。
**解决粘包问题:**
最本质原因在与接收对等方无法分辨消息与消息之间的边界在哪,通过使用某种方案给出边界,例如:
- 发送定长包。如果每个消息的大小都是一样的,那么在接收对等方只要累计接收数据,直到数据等于一个定长的数值就将它作为一个消息。
- 包尾加上\r\n标记。FTP协议正是这么做的。但问题在于如果数据正文中也含有\r\n,则会误判为消息的边界。
- 包头加上包体长度。包头是定长的4个字节,说明了包体的长度。接收对等方先接收包体长度,依据包体长度来接收包体。
**6、TCP三次握手过程以及每次握手后的状态改变,为什么三次? 为什么两次不行?**
**三次握手过程:**
客户端——发送带有SYN标志的数据包——服务端 **一次握手** Client进入syn_sent状态
服务端——发送带有SYN/ACK标志的数据包——客户端 **二次握手** 服务端进入syn_rcvd
客户端——发送带有ACK标志的数据包——服务端 **三次握手** 连接就进入Established状态
**为什么三次:**
主要是为了建立可靠的通信信道,保证客户端与服务端同时具备发送、接收数据的能力
**为什么两次不行?**
1、防止已失效的请求报文又传送到了服务端,建立了多余的链接,浪费资源
2、 两次握手只能保证单向连接是畅通的。(为了实现可靠数据传输, TCP 协议的通信双方, 都必须维 护一个序列号, 以标识发送出去的数据包中, 哪些是已经被对方收到的。 三次握手的过程即是通信双方 相互告知序列号起始值, 并确认对方已经收到了序列号起始值的必经步骤;如果只是两次握手, 至多只 有连接发起方的起始序列号能被确认, 另一方选择的序列号则得不到确认)
**7、TCP四次挥手过程以及状态改变,为什么四次?CLOSE-WAIT和TIME-WAIT存在的意义?如何查看TIME-WAIT状态的链接数量?为什么会TIME-WAIT过多?解决方法是怎样的?**
**四次挥手过程:**
客户端——发送带有FIN标志的数据包——服务端,关闭与服务端的连接 ,客户端进入FIN-WAIT-1状态
服务端收到这个 FIN,它发回⼀ 个 ACK,确认序号为收到的序号加1,服务端就进入了CLOSE-WAIT状态
服务端——发送⼀个FIN数据包——客户端,关闭与客户端的连接,客户端就进入FIN-WAIT-2状态
客户端收到这个 FIN,发回 ACK 报⽂确认,并将确认序号设置为收到序号加1,TIME-WAIT状态
**为什么四次:**
因为需要确保客户端与服务端的数据能够完成传输。
**CLOSE-WAIT:**
这种状态的含义其实是表示在等待关闭
**TIME-WAIT:**
为了解决网络的丢包和网络不稳定所带来的其他问题,确保连接方能在时间范围内,关闭自己的连接
**如何查看TIME-WAIT状态的链接数量?**
netstat -an |grep TIME_WAIT|wc -l 查看连接数等待time_wait状态连接数
**为什么会TIME-WAIT过多?解决方法是怎样的?**
**可能原因:** 高并发短连接的TCP服务器上,当服务器处理完请求后立刻按照主动正常关闭连接
**解决:**负载均衡服务器;Web服务器首先关闭来自负载均衡服务器的连接
**8、TCP、UDP、IP、以太网报文格式以及重要字段,报文从一端到另一端传递的过程。**
**TCP报文格式:**

**源端口号和目的端口号**:
用于寻找发端和收端应用进程。这两个值加上ip首部源端ip地址和目的端ip地址唯一确定一个tcp连接。
**序号字段:**
序号用来标识从T C P发端向T C P收端发送的数据字节流,它表示在这个报文段中的的第一个数据字节。如果将字节流看作在两个应用程序间的单向流动,则 T C P用序号对每个字节进行计数。序号是32 bit的无符号数,序号到达 2^32-1后又从0开始。
当建立一个新的连接时,SYN标志变1。序号字段包含由这个主机选择的该连接的初始序号ISN(Initial Sequence Number)。该主机要发送数据的第一个字节序号为这个ISN加1,因为SYN标志消耗了一个序号
**确认序号**:
既然每个传输的字节都被计数,确认序号包含发送确认的一端所期望收到的下一个序号。因此,确认序号应当是上次已成功收到数据字节序号加 1。只有ACK标志为 1时确认序号字段才有效。发送ACK无需任何代价,因为 32 bit的确认序号字段和A C K标志一样,总是T C P首部的一部分。因此,我们看到一旦一个连接建立起来,这个字段总是被设置, ACK标志也总是被设置为1。TCP为应用层提供全双工服务。这意味数据能在两个方向上独立地进行传输。因此,连接的每一端必须保持每个方向上的传输数据序号。
**首都长度**:
首部长度给出首部中 32 bit字的数目。需要这个值是因为任选字段的长度是可变的。这个字段占4 bit,因此T C P最多有6 0字节的首部。然而,没有任选字段,正常的长度是 2 0字节。
**标志字段**:在T C P首部中有 6个标志比特。它们中的多个可同时被设置为1.
URG紧急指针(u rgent pointer)有效
ACK确认序号有效。
PSH接收方应该尽快将这个报文段交给应用层。
RST重建连接。
SYN同步序号用来发起一个连接。这个标志和下一个标志将在第 1 8章介绍。
FIN发端完成发送任务。
**窗口大小**:
T C P的流量控制由连接的每一端通过声明的窗口大小来提供。窗口大小为字节数,起始于确认序号字段指明的值,这个值是接收端期望接收的字节。窗口大小是一个 16 bit字段,因而窗口大小最大为 65535字节。
**检验和:**
检验和覆盖了整个的 T C P报文段:T C P首部和T C P数据。这是一个强制性的字段,一定是由发端计算和存储,并由收端进行验证。
**紧急指针**:
只有当URG标志置1时紧急指针才有效。紧急指针是一个正的偏移量,和序号字段中的值相加表示紧急数据最后一个字节的序号。 T C P的紧急方式是发送端向另一端发送紧急数据的一种方式。
**选项**:
最常见的可选字段是最长报文大小,又称为 MSS (Maximum Segment Size)。每个连接方通常都在通信的第一个报文段(为建立连接而设置 S Y N标志的那个段)中指明这个选项。它指明本端所能接收的最大长度的报文段。
**UDP报文格式:**

**端口号**:
用来表示发送和接受进程。由于 I P层已经把I P数据报分配给T C P或U D P(根据I P首部中协议字段值),因此T C P端口号由T C P来查看,而 U D P端口号由UDP来查看。T C P端口号与UDP端口号是相互独立的。
**长度**:
UDP长度字段指的是UDP首部和UDP数据的字节长度。该字段的最小值为 8字节(发送一份0字节的UDP数据报是 O K)。
**检验和**:
UDP检验和是一个端到端的检验和。它由发送端计算,然后由接收端验证。其目的是为了发现UDP首部和数据在发送端到接收端之间发生的任何改动。
**IP报文格式:**普通的IP首部长为20个字节,除非含有可选项字段。

**4位版本**:
目前协议版本号是4,因此IP有时也称作IPV4.
**4位首部长度**:
首部长度指的是首部占32bit字的数目,包括任何选项。由于它是一个4比特字段,因此首部长度最长为60个字节。
**服务类型(TOS)**:
服务类型字段包括一个3bit的优先权字段(现在已经被忽略),4bit的TOS子字段和1bit未用位必须置0。4bit的TOS分别代表:最小时延,最大吞吐量,最高可靠性和最小费用。4bit中只能置其中1比特。如果所有4bit均为0,那么就意味着是一般服务。
**总长度**:
总长度字段是指整个IP数据报的长度,以字节为单位。利用首部长度和总长度字段,就可以知道IP数据报中数据内容的起始位置和长度。由于该字段长16bit,所以IP数据报最长可达65535字节。当数据报被分片时,该字段的值也随着变化。
**标识字段**:
标识字段唯一地标识主机发送的每一份数据报。通常每发送一份报文它的值就会加1。
**生存时间**:
TTL(time-to-live)生存时间字段设置了数据报可以经过的最多路由器数。它指定了数据报的生存时间。TTL的初始值由源主机设置(通常为 3 2或6 4),一旦经过一个处理它的路由器,它的值就减去 1。当该字段的值为 0时,数据报就被丢弃,并发送 ICMP 报文通知源主机。
**首部检验和**:
首部检验和字段是根据 I P首部计算的检验和码。它不对首部后面的数据进行计算。 ICMP、IGMP、UDP和TCP在它们各自的首部中均含有同时覆盖首部和数据检验和码。
**以太网报文格式:**

**目的地址和源地址:**
是指网卡的硬件地址(也叫MAC 地址),长度是48 位,是在网卡出厂时固化的。
**数据:**
以太网帧中的数据长度规定最小46 字节,最大1500 字节,ARP 和RARP 数据包的长度不够46 字节,要在后面补填充位。最大值1500 称为以太网的最大传输单元(MTU),不同的网络类型有不同的MTU,如果一个数据包从以太网路由到拨号链路上,数据包度大于拨号链路的MTU了,则需要对数据包进行分片fragmentation)。ifconfig 命令的输出中也有“MTU:1500”。注意,MTU 个概念指数据帧中有效载荷的最大长度,不包括帧首部的长度。
**9、浏览器输入URL并回车的过程以及相关协议,DNS查询过程。**
**过程:**DNS解析、TCP连接、发送HTTP请求、服务器处理请求并返回HTTP报文、浏览器渲染、结束
| 过程 | 使用的协议 |
| ------------------------------------------------------------ | --------------------- |
| 1、浏览器查找域名DNS的IP地址
DNS查找过程(浏览器缓存、路由器缓存、DNS缓存) | DNS:获取域名对应的ip |
| 2、根据ip建立TCP连接 | TCP:与服务器建立连接 |
| 3、浏览器向服务器发送HTTP请求 | HTTP:发送请求 |
| 4、服务器响应HTTP响应 | HTTP |
| 5、浏览器进行渲染 | |
**10、HTTP1.0、1.1、2.0之间的区别**
**HTTP1.0:**默认使用Connection:cloose,浏览器每次请求都需要与服务器建立一个TCP连接,服务器处理完成后立即断开TCP连接(无连接),服务器不跟踪每个客户端也不记录过去的请求(无状态)。
**HTTP1.1:**默认使用Connection:keep-alive(长连接),避免了连接建立和释放的开销;通过Content-Length字段来判断当前请求的数据是否已经全部接受。不允许同时存在两个并行的响应。
**HTTP2.0:**引入二进制数据帧和流的概念,其中帧对数据进行顺序标识;因为有了序列,服务器可以并行的传输数据。
**http1.0和http1.1的主要区别如下:**
1、缓存处理:1.1添加更多的缓存控制策略(如:Entity tag,If-Match)
2、网络连接的优化:1.1支持断点续传
3、错误状态码的增多:1.1新增了24个错误状态响应码,丰富的错误码更加明确各个状态
4、Host头处理:支持Host头域,不在以IP为请求方标志
5、长连接:减少了建立和关闭连接的消耗和延迟。
**http1.1和http2.0的主要区别:**
1、新的传输格式:2.0使用二进制格式,1.0依然使用基于文本格式
2、多路复用:连接共享,不同的request可以使用同一个连接传输(最后根据每个request上的id号组合成 正常的请求)
3、header压缩:由于1.X中header带有大量的信息,并且得重复传输,2.0使用encoder来减少需要传输的 hearder大小
4、服务端推送:同google的SPDUY(1.0的一种升级)一样
**11、HTTP与HTTPS之间的区别,HTTPS链接建立的过程,了解对称加密算法和非对称加密算法不?**
**HTTP与HTTPS之间的区别:**
| HTTP | HTTPS |
| :----------------------------: | :--------------------------------------: |
| 默认端口80 | HTTPS默认使用端口443 |
| 明文传输、数据未加密、安全性差 | 传输过程ssl加密、安全性较好 |
| 响应速度快、消耗资源少 | 响应速度较慢、消耗资源多、需要用到CA证书 |
**HTTPS链接建立的过程:**
1.首先客户端先给服务器发送一个请求
2.服务器发送一个SSL证书给客户端,内容包括:证书的发布机构、有效期、所有者、签名以及公钥
3.客户端对发来的公钥进行真伪校验,校验为真则使用公钥对对称加密算法以及对称密钥进行加密
4.服务器端使用私钥进行解密并使用对称密钥加密确认信息发送给客户端
5.随后客户端和服务端就使用对称密钥进行信息传输
**对称加密算法:**
双方持有相同的密钥,且加密速度快,典型对称加密算法:DES、AES
**非对称加密算法:**
密钥成对出现(私钥、公钥),私钥只有自己知道,不在网络中传输;而公钥可以公开。相比对称加密速度较慢,典型的非对称加密算法有:RSA、DSA
**12、HTTP请求有哪些。get和Post区别。**
**HTTP请求:**
| 方法 | 描述 |
| :------ | ------------------------------------------------------------ |
| GET | 向特定资源发送请求,查询数据,并返回实体 |
| POST | 向指定资源提交数据进行处理请求,可能会导致新的资源建立、已有资源修改 |
| PUT | 向服务器上传新的内容 |
| HEAD | 类似GET请求,返回的响应中没有具体的内容,用于获取报头 |
| DELETE | 请求服务器删除指定标识的资源 |
| OPTIONS | 可以用来向服务器发送请求来测试服务器的功能性 |
| TRACE | 回显服务器收到的请求,用于测试或诊断 |
| CONNECT | HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器 |
**get和Post区别:**
| | GET | POST |
| -------- | ------------------------------------------------------------ | ---------------------------------------------------------- |
| 可见性 | 数据在URL中对所有人可见 | 数据不会显示在URL中 |
| 安全性 | 与post相比,get的安全性较差,因为所
发送的数据是URL的一部分 | 安全,因为参数不会被保存在浏览器
历史或web服务器日志中 |
| 数据长度 | 受限制,最长2kb | 无限制 |
| 编码类型 | application/x-www-form-urlencoded | multipart/form-data |
| 缓存 | 能被缓存 | 不能被缓存 |
| | | |
**13、HTTP常见响应状态码,从1xx到5xx**
100:Continue --- 继续。客户端应继续其请求。
200:OK --- 请求成功。一般用于GET与POST请求。
301:Moved Permanently --- 永久重定向。
302:Found --- 暂时重定向。
400:Bad Request --- 客户端请求的语法错误,服务器无法理解。
403:Forbideen --- 服务器理解请求客户端的请求,但是拒绝执行此请求。
404:Not Found --- 服务器无法根据客户端的请求找到资源(网页)。
500:Internal Server Error --- 服务器内部错误,无法完成请求。
502:Bad Gateway --- 作为网关或者代理服务器尝试执行请求时,从远程服务器接收到了无效的响应。
**14、重定向和转发区别**
**重定向:redirect:**
地址栏发生变化
重定向可以访问其他站点(服务器)的资源
重定向是两次请求。不能使用request对象来共享数据
**转发:forward:**
转发地址栏路径不变
转发只能访问当前服务器下的资源
转发是一次请求,可以使用request对象共享数据
**15、cookie和session区别。**
Cookie 和 Session都是用来跟踪浏览器用户身份的会话方式,但两者有所区别:
Cookie 数据保存在客户端(浏览器端),Session 数据保存在服务器端。
cookie不是很安全,别人可以分析存放在本地的COOKIE并进行欺骗,考虑到安全应当使用session。
Cookie ⼀般⽤来保存⽤户信息,Session 的主要作⽤就是通过服务端记录⽤户的状态
## **二、操作系统篇**
**1、进程和线程的区别**
**进程:**是资源分配的最小单位,是程序的执行过程,一个进程可以有多个线程,多个线程共享进程的堆和方法区资源,但每个线程又有属于自己的本地方法栈、虚拟机栈、程序计数器
**线程:**是任务调度和执行的最小单位,线程间可能存在相互影响,执行开销较小,不利于资源的管理和保护,线程间是共享进程中的资源的
**2、协程?**
是一种比线程更加轻量级的存在,正如一个进程可以拥有多个线程一样,一个线程可以拥有多个协程。
**3、进程间通信方式IPC**
参考:https://www.jianshu.com/p/c1015f5ffa74
**匿名管道pipe:**
匿名管道是半双工的,数据只能单向通信;需要双方通信时,需要建立起两个管道;只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程)。
**命名管道FIFO:**
不同于匿名管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存在于文件系统中。这样,即使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过FIFO相互通信(能够访问该路径的进程以及FIFO的创建进程之间),因此,通过FIFO不相关的进程也能交换数据。值得注意的是,FIFO严格遵循先进先出(first in first out),对管道及FIFO的读总是从开始处返回数据,对它们的写则把数据添加到末尾。
**信号:**
信号是一种比较复杂的通信方式,信号产生的条件:按键、硬件异常、进程调用kill函数将信号发送给另一个进程、用户调用kill命令将信号发送给其他进程,信号传递的消息比较少,主要用于通知接收进程某个时间已经发生。
**消息队列:**
消息队列是消息的链表,存放在内核中并由消息队列标识符标识,消息队列克服了信号传递信息少,管道只能承载无格式字节流以及缓冲区大小受限等特点。消息队列起信箱作用,到了就挂在那里,需要的时候去取。消息队列提供了一种在两个不相关进程间传递数据的简单有效的方法。与命名管道相比:消息队列的优势在于,它独立于发送和接收进程而存在,这消除了在同步命名管道的打开和关闭时可能产生的一些困难。消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。而且,每个数据块被认为含有一个类型,接收进程可以独立地接收含有不同类型值的数据块。
**优点:**
A. 我们可以通过发送消息来几乎完全避免命名管道的同步和阻塞问题。
B. 我们可以用一些方法来提前查看紧急消息。
**缺点:**
A. 与管道一样,每个数据块有一个最大长度的限制。
B. 系统中所有队列所包含的全部数据块的总长度也有一个上限。
**共享内存(share memory):**
- 使得多个进程可以可以直接读写同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。
- 为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间。进程就可以直接读写这一块内存而不需要进行数据的拷贝,从而大大提高效率。
- 由于多个进程共享一段内存,因此需要依靠某种同步机制(如信号量)来达到进程间的同步及互斥。
**信号量(Semaphores) :**
信号量是⼀个计数器,⽤于多进程对共享数据的访问,信号量的意图在于进程间同步。这种通信⽅式主要⽤于解决与同步相关的问题并避免竞争条件。
**套接字(Sockets) :**
此⽅法主要⽤于在客户端和服务器之间通过⽹络进⾏通信。套接字是⽀持TCP/IP 的⽹络通信的基本操作单元,可以看做是不同主机之间的进程进⾏双向通信的端点,简单的说就是通信的两⽅的⼀种约定,⽤套接字中的相关函数来完成通信过程。
**4、用户态和核心态**
在计算机系统中,分两种程序:系统程序和应用程序,为了保证系统程序不被应用程序有意或无意地破坏,为计算机设置了两种状态——用户态、核心态
**用户态:**只能受限的访问内存,运行所有的应用程序
**核心态:**运行操作系统程序,cpu可以访问内存的所有数据,包括外围设备
**为什么要有用户态和内核态:**
由于需要限制不同的程序之间的访问能力, 防止他们获取别的程序的内存数据, 或者获取外围设备的数据, 并发送到网络
**用户态切换到内核态的3种方式:**
**a. 系统调用**
这是用户态进程主动要求切换到内核态的一种方式,用户态进程通过系统调用申请使用操作系统提供的服务程序完成工作,比如前例中fork()实际上就是执行了一个创建新进程的系统调用。而系统调用的机制其核心还是使用了操作系统为用户特别开放的一个中断来实现,例如Linux的int 80h中断。
**b. 异常**
当CPU在执行运行在用户态下的程序时,发生了某些事先不可知的异常,这时会触发由当前运行进程切换到处理此异常的内核相关程序中,也就转到了内核态,比如缺页异常。
**c. 外围设备的中断**
当外围设备完成用户请求的操作后,会向CPU发出相应的中断信号,这时CPU会暂停执行下一条即将要执行的指令转而去执行与中断信号对应的处理程序,如果先前执行的指令是用户态下的程序,那么这个转换的过程自然也就发生了由用户态到内核态的切换。比如硬盘读写操作完成,系统会切换到硬盘读写的中断处理程序中执行后续操作等。
这3种方式是系统在运行时由用户态转到内核态的最主要方式,其中系统调用可以认为是用户进程主动发起的,异常和外围设备中断则是被动的。
**5、操作系统分配的进程空间是怎样的?线程能共享哪些?**
栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。
堆区(heap)— 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。
静态区(static)—存放全局变量和静态变量的存储
代码区(text)—存放函数体的二进制代码。
**线程共享堆区、静态区**
**6、操作系统内存管理方式,分页分段以及段页式的优缺点**
参考地址:https://blog.csdn.net/qq_37189082/article/details/97963763
**存管理方式:**块式管理、页式管理、段式管理、段页式管理
**分段管理:**
在段式存储管理中,将程序的地址空间划分为若干段(segment),如代码段,数据段,堆栈段;这样每个进程有一个二维地址空间,相互独立,互不干扰。段式管理的优点是:没有内碎片(因为段大小可变,改变段大小来消除内碎片)。但段换入换出时,会产生外碎片(比如4k的段换5k的段,会产生1k的外碎片)
**分页管理:**
在页式存储管理中,将程序的逻辑地址划分为固定大小的页(page),而物理内存划分为同样大小的页框,程序加载时,可以将任意一页放入内存中任意一个页框,这些页框不必连续,从而实现了离散分离。页式存储管理的优点是:没有外碎片(因为页的大小固定),但会产生内碎片(一个页可能填充不满)
**段页式管理:**
段⻚式管理机制结合了段式管理和⻚式管理的优点。简单来说段⻚式管理机制就是把主存先分成若⼲ 段,每个段⼜分成若⼲⻚,也就是说 段⻚式管理机制 中段与段之间以及段的内部的都是离散的。
**7、页面置换算法有哪些,FIFO为什么不好?如何改进?LRU思想,手写LRU**
**置换算法:**先进先出FIFO、最近最久未使用LRU、最佳置换算法OPT
**先进先出FIFO:**
原理:把内存中驻留时间最久的页面置换算法予以淘汰
优点:实现简单、直观
缺点:没有考虑到实际的页面使用频率,性能差、与通常页面使用的规则不符合,实际应用较少
改进:给每个页面增加一个R位,每次先从链表头开始查找,如果R置位,清除R位并且把该页面节点放 到链表结尾;如果R是0,那么就是又老又没用到,替换掉。
**最近最久未使用LRU:**
原理:选择最近且最久未使用的页面进行淘汰
优点:考虑到了程序访问的时间局部性,有较好的性能,实际应用也比较多
缺点:实现需要比较多的硬件支持,会增加一些硬件成本
手写LRU: 参考 https://www.jianshu.com/p/ec1952b9d84a
```java
/**
* @program: Java
* @description: LRU最近最久未使用置换算法,通过LinkedHashMap实现
* @author: Mr.Li
* @create: 2020-07-17 10:29
**/
public class LRUCache {
private LinkedHashMap cache;
private int capacity; //容量大小
/**
*初始化构造函数
* @param capacity
*/
public LRUCache(int capacity) {
cache = new LinkedHashMap<>(capacity);
this.capacity = capacity;
}
public int get(int key) {
//缓存中不存在此key,直接返回
if(!cache.containsKey(key)) {
return -1;
}
int res = cache.get(key);
cache.remove(key); //先从链表中删除
cache.put(key,res); //再把该节点放到链表末尾处
return res;
}
public void put(int key,int value) {
if(cache.containsKey(key)) {
cache.remove(key); //已经存在,在当前链表移除
}
if(capacity == cache.size()) {
//cache已满,删除链表头位置
Set keySet = cache.keySet();
Iterator iterator = keySet.iterator();
cache.remove(iterator.next());
}
cache.put(key,value); //插入到链表末尾
}
}
```
```java
/**
* @program: Java
* @description: LRU最近最久未使用置换算法,通过LinkedHashMap内部removeEldestEntry方法实现
* @author: Mr.Li
* @create: 2020-07-17 10:59
**/
class LRUCache {
private Map map;
private int capacity;
/**
*初始化构造函数
* @param capacity
*/
public LRUCache(int capacity) {
this.capacity = capacity;
map = new LinkedHashMap(capacity, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > capacity; // 容量大于capacity 时就删除
}
};
}
public int get(int key) {
//返回key对应的value值,若不存在,返回-1
return map.getOrDefault(key, -1);
}
public void put(int key, int value) {
map.put(key, value);
}
}
```
**最佳置换算法OPT:**
原理:每次选择当前物理块中的页面在未来长时间不被访问的或未来不再使用的页面进行淘汰
优点:具有较好的性能,可以保证获得最低的缺页率
缺点:过于理想化,但是实际上无法实现(没办法预知未来的页面)
**8、死锁条件,解决方式。**
死锁是指两个或两个以上进程在执行过程中,因争夺资源而造成的下相互等待的现象;
**死锁的条件:**
互斥条件:进程对所分配到的资源不允许其他进程访问,若其他进程访问该资源,只能等待,直至占有该资源的进程使用完成后释放该资源;
请求与保持条件:进程获得一定的资源后,又对其他资源发出请求,但是该资源可能被其他进程占有,此时请求阻塞,但该进程不会释放自己已经占有的资源
非剥夺条件:进程已获得的资源,在未完成使用之前,不可被剥夺,只能在使用后自己释放
循环等待条件:系统中若干进程组成环路,环路中每个进程都在等待相邻进程占用的资源
**解决方法:**破坏死锁的任意一条件
资源一次性分配,从而剥夺请求和保持条件
可剥夺资源:即当进程新的资源未得到满足时,释放已占有的资源,从而破坏不可剥夺的条件
资源有序分配法:系统给每类资源赋予一个序号,每个进程按编号递增的请求资源,释放则相反,从而破坏环路等待的条件
## **三、Java基础篇**
**1、Java面向对象特性介绍、与C++区别**
**特性:**封装、继承、多态
**封装:**对抽象的事物抽象化成一个对象,并对其对象的属性私有化,同时提供一些能被外界访问属性的方法,这样一个对象便有存在的意义了;
**继承:**在已存在类的基础上,建立新类并对其增加新的数据域或功能,同时该类可以复用父类的属性与功能,这种思路可以称为继承;通过使用继承能够方便地复用旧代码,减少不必要的代码量;
**多态:**指程序中的某个引用变量,它所指向的具体类型以及该引用变量发出的方法调用,在编程时不能确定,要在程序运行并使用时由机器自己判别确定;实现多态的方式有两种方式,可以通过继承(多个⼦类对同⼀⽅法的重写)、也可以通过接⼝(实现接⼝并覆盖接⼝中同⼀⽅法)
**Java与C++区别:**
相同点:都是面向对象语言,并且都支持封装、继承、多态
不同点:c++支持多继承,并且有指针的概念,由程序员自己管理内存;Java是单继承,可以用接口实现多继承,Java 不提供指针来直接访问内存,程序内存更加安全,并且Java有JVM⾃动内存管理机制,不需要程序员⼿动释放⽆⽤内存
**2、多态实现原理**
参考:https://www.baidu.com/link?url=qCqeY8tJInTtjEXuotB712TJtEpKeLJ9ds1ACGEnYwMyJdjf6J0C7JvOEwbV7qNG&wd=&eqid=fe3b2106001ee6ab000000065f111b6c
多态的底层实现是动态绑定,即在运行时才把方法调用与方法实现关联起来。
**静态绑定与动态绑定:**
JVM 的方法调用指令有五个,分别是:
invokestatic:调用静态方法;
invokespecial:调用实例构造器方法、私有方法和父类方法;
invokevirtual:调用虚方法;
invokeinterface:调用接口方法,运行时确定具体实现;
invokedynamic:运行时动态解析所引用的方法,然后再执行,用于支持动态类型语言。
invokestatic 和 invokespecial 用于静态绑定
invokevirtual 和 invokeinterface 用于动态绑定
可以看出,动态绑定主要应用于虚方法和接口方法。
虚方法的方法调用与方法实现的关联(也就是分派)有两种,一种是在编译期确定,被称为静态分派,比如方法的重载;一种是在运行时确定,被称为动态分派,比如方法的覆盖(重写)。对象方法基本上都是虚方法。
**多态的实现**
虚拟机栈中会存放当前方法调用的栈帧(局部变量表、操作栈、动态连接 、返回地址)。多态的实现过程,就是方法调用动态分派的过程,通过栈帧的信息去找到被调用方法的具体实现,然后使用这个具体实现的直接引用完成方法调用。
以 invokevirtual 指令为例,在执行时,大致可以分为以下几步:
1. 先从操作栈中找到对象的实际类型 class;
2. 找到 class 中与被调用方法签名相同的方法,如果有访问权限就返回这个方法的直接引用,如果没有访问权限就报错 java.lang.IllegalAccessError ;
3. 如果第 2 步找不到相符的方法,就去搜索 class 的父类,按照继承关系自下而上依次执行第 2 步的操作;
4. 如果第 3 步找不到相符的方法,就报错 java.lang.AbstractMethodError ;
可以看到,如果子类覆盖了父类的方法,则在多态调用中,动态绑定过程会首先确定实际类型是子类,从而先搜索到子类中的方法。这个过程便是方法覆盖的本质。
**3、抽象类和接口区别,以及各自的使用场景**
**抽象类:**包含抽象方法的类,即使用abstract修饰的类;不能使用final修饰,final修饰的类不能被继承;抽象类不能被实例化,只能被继承
**接口:**接口是一个抽象类型,是抽象方法的集合,接口以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法;接口只能继承接口,不能继承类,接口支持多继承;接口中的定义的成员变量,默认是public static final修饰的静态常量;接口中定义的方法,默认是public abstract修饰的抽象方法
**相同点:**
① 抽象类和接口都不能被实例化
② 抽象类和接口都可以定义抽象方法,子类/实现类必须覆写这些抽象方法
**不同点:**
① 抽象类有构造方法,接口没有构造方法
③抽象类可以包含普通方法,接口中只能是public abstract修饰抽象方法(Java8之后可以)
③ 抽象类只能单继承,接口可以多继承
④ 抽象类可以定义各种类型的成员变量,接口中只能是public static final修饰的静态常量
**抽象类的使用场景:**
既想约束子类具有共同的行为(但不再乎其如何实现),又想拥有缺省的方法,又能拥有实例变量
**接口的应用场景:**
约束多个实现类具有统一的行为,但是不在乎每个实现类如何具体实现;实现类需要具备很多不同的功能,但各个功能之间可能没有任何联系
**4、泛型以及泛型擦除。List类型的list,可以加入无继承关系的B类型对象吗?如何加入?**
参考:https://blog.csdn.net/baoyinwang/article/details/107341997
**泛型:**
泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口和泛型方法。
**泛型擦除:**
Java的泛型是伪泛型,这是因为Java在编译期间,所有的泛型信息都会被擦掉,正确理解泛型概念的首要前提是理解类型擦除。Java的泛型基本上都是在编译器这个层次上实现的,在生成的字节码中是不包含泛型中的类型信息的,使用泛型的时候加上类型参数,在编译器编译的时候会去掉,这个过程成为类型擦除。
如在代码中定义的 List