PDF 超过 6000 页,2021 最新 Java 面试题及答案
一、网络编程基础回顾
1. Socket
Socket
本身有“插座”的意思,不是 Java 中特有的概念,而是一个语言无关的标准,任何可以实现网络编程的编程语言都有Socket
。在Linux
环境下,用于表示进程间网络通信的特殊文件类型,其本质为内核借助缓冲区形成的伪文件。既然是文件,那么理所当然的,我们可以使用文件描述符引用套接字。
与管道类似的,Linux
系统将其封装成文件的目的是为了统一接口,使得读写套接字和读写文件的操作一致。区别是管道主要应用于本地进程间通信,而套接字多应用于网络进程间数据的传递。
可以这么理解:Socket
就是网络上的两个应用程序通过一个双向通信连接实现数据交换的编程接口 API。
Socket
通信的基本流程具体步骤如下所示:
(1)服务端通过Listen
开启监听,等待客户端接入。
(2)客户端的套接字通过Connect
连接服务器端的套接字,服务端通过Accept
接收客户端连接。在connect-accept
过程中,操作系统将会进行三次握手。
(3)客户端和服务端通过write
和read
发送和接收数据,操作系统将会完成TCP
数据的确认、重发等步骤。
(4)通过close
关闭连接,操作系统会进行四次挥手。
针对 Java 编程语言,java.net
包是网络编程的基础类库。其中ServerSocket
和Socket
是网络编程的基础类型。
SeverSocket
是服务端应用类型。Socket
是建立连接的类型。当连接建立成功后,服务器和客户端都会有一个Socket
对象示例,可以通过这个Socket
对象示例,完成会话的所有操作。对于一个完整的网络连接来说,Socket
是平等的,没有服务器客户端分级情况。
2. IO 模型介绍
对于一次 IO 操作,数据会先拷贝到内核空间中,然后再从内核空间拷贝到用户空间中,所以一次read
操作,会经历两个阶段:
(1)等待数据准备
(2)数据从内核空间拷贝到用户空间
基于以上两个阶段就产生了五种不同的 IO 模式。
阻塞 IO:从进程发起 IO 操作,一直等待上述两个阶段完成,此时两阶段一起阻塞。
非阻塞 IO:进程一直询问 IO 准备好了没有,准备好了再发起读取操作,这时才把数据从内核空间拷贝到用户空间。第一阶段不阻塞但要轮询,第二阶段阻塞。
多路复用 IO:多个连接使用同一个 select 去询问 IO 准备好了没有,如果有准备好了的,就返回有数据准备好了,然后对应的连接再发起读取操作,把数据从内核空间拷贝到用户空间。两阶段分开阻塞。
信号驱动 IO:进程发起读取操作会立即返回,当数据准备好了会以通知的形式告诉进程,进程再发起读取操作,把数据从内核空间拷贝到用户空间。第一阶段不阻塞,第二阶段阻塞。
异步 IO:进程发起读取操作会立即返回,等到数据准备好且已经拷贝到用户空间了再通知进程拿数据。两个阶段都不阻塞。
这五种 IO 模式不难发现存在这两对关系:同步和异步、阻塞和非阻塞。那么稍微解释一下:
同步和异步
同步: 同步就是发起一个调用后,被调用者未处理完请求之前,调用不返回。
异步: 异步就是发起一个调用后,立刻得到被调用者的回应表示已接收到请求,但是被调用者并没有返回结果,此时我们可以处理其他的请求,被调用者通常依靠事件,回调等机制来通知调用者其返回结果。
同步和异步的区别最大在于异步的话调用者不需要等待处理结果,被调用者会通过回调等机制来通知调用者其返回结果。
阻塞和非阻塞
阻塞: 阻塞就是发起一个请求,调用者一直等待请求结果返回,也就是当前线程会被挂起,无法从事其他任务,只有当条件就绪才能继续。
非阻塞: 非阻塞就是发起一个请求,调用者不用一直等着结果返回,可以先去干其他事情。
阻塞和非阻塞是针对进程在访问数据的时候,根据 IO 操作的就绪状态来采取的不同方式,说白了是一种读取或者写入操作方法的实现方式,阻塞方式下读取或者写入函数将一直等待,而非阻塞方式下,读取或者写入方法会立即返回一个状态值。
如果组合后的同步阻塞(blocking-IO
)简称BIO
、同步非阻塞(non-blocking-IO
)简称NIO
和异步非阻塞(asynchronous-non-blocking-IO
)简称AIO
又代表什么意思呢?
BIO (同步阻塞 I/O 模式): 数据的读取写入必须阻塞在一个线程内等待其完成。这里使用那个经典的烧开水例子,这里假设一个烧开水的场景,有一排水壶在烧开水,BIO 的工作模式就是, 叫一个线程停留在一个水壶那,直到这个水壶烧开,才去处理下一个水壶。但是实际上线程在等待水壶烧开的时间段什么都没有做。
NIO(同步非阻塞): 同时支持阻塞与非阻塞模式,但这里我们以其同步非阻塞 I/O 模式来说明,那么什么叫做同步非阻塞?如果还拿烧开水来说,NIO 的做法是叫一个线程不断的轮询每个水壶的状态,看看是否有水壶的状态发生了改变,从而进行下一步的操作。
AIO(异步非阻塞 I/O 模型): 异步非阻塞与同步非阻塞的区别在哪里?异步非阻塞无需一个线程去轮询所有 IO 操作的状态改变,在相应的状态改变后,系统会通知对应的线程来处理。对应到烧开水中就是,为每个水壶上面装了一个开关,水烧开之后,水壶会自动通知我水烧开了。
java
中的 BIO
、NIO
和AIO
理解为是 Java 语言
在操作系统层面对这三种 IO
模型的封装。程序员在使用这些 封装 API 的时候,不需要关心操作系统层面的知识,也不需要根据不同操作系统编写不同的代码,只需要使用Java
的 API 就可以了。由此,为了使读者对这三种模型有个比较具体和递推式的了解,并且和本文主题NIO
有个清晰的对比,下面继续延伸。
Java BIO
BIO
编程方式通常是是 Java 的上古产品,自 JDK 1.0-JDK1.4 就有的东西。编程实现过程为:首先在服务端启动一个ServerSocket
来监听网络请求,客户端启动Socket
发起网络请求,默认情况下SeverSocket
会建立一个线程来处理此请求,如果服务端没有线程可用,客户端则会阻塞等待或遭到拒绝。服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理。大致结构如下:
如果要让 BIO
通信模型能够同时处理多个客户端请求,就必须使用多线程(主要原因是 socket.accept()
、socket.read()
、 socket.write()
涉及的三个主要函数都是同步阻塞的),也就是说它在接收到客户端连接请求之后为每个客户端创建一个新的线程进行链路处理,处理完成之后,通过输出流返回应答给客户端,线程销毁。这就是典型的 一请求一应答通信模型 。我们可以设想一下如果这个连接不做任何事情的话就会造成不必要的线程开销,不过可以通过线程池机制改善,线程池还可以让线程的创建和回收成本相对较低。使用线程池机制改善后的 BIO
模型图如下:
BIO
方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,是 JDK1.4 以前的唯一选择,但程序直观简单易懂。Java BIO
编程示例网上很多,这里就不进行 coding 举例了,毕竟后面NIO
才是重点。
Java NIO
NIO
(New IO 或者 No-Blocking IO),从 JDK1.4 开始引入的非阻塞IO
,是一种非阻塞
+ 同步
的通信模式。这里的No Blocking IO
用于区分上面的BIO
。
NIO
本身想解决 BIO
的并发问题,通过Reactor模式
的事件驱动机制来达到Non Blocking
的。当 socket
有流可读或可写入 socket
时,操作系统会相应的通知应用程序进行处理,应用再将流读取到缓冲区或写入操作系统。
也就是说,这个时候,已经不是一个连接就 要对应一个处理线程了,而是有效的请求,对应一个线程,当连接没有数据时,是没有工作线程来处理的。
当一个连接创建后,不需要对应一个线程,这个连接会被注册到 多路复用器
上面,所以所有的连接只需要一个线程就可以搞定,当这个线程中的多路复用器
进行轮询的时候,发现连接上有请求的话,才开启一个线程进行处理,也就是一个请求一个线程模式。
NIO
提供了与传统 BIO 模型中的Socket
和ServerSocket
相对应的SocketChannel
和ServerSocketChannel
两种不同的套接字通道实现,如下图结构所示。这里涉及的Reactor
设计模式、多路复用Selector
、Buffer
等暂时不用管,后面会讲到。
NIO 方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局 限于应用中,编程复杂,JDK1.4 开始支持。同时,NIO
和普通 IO 的区别主要可以从存储数据的载体、是否阻塞等来区分:
Java AIO
与 NIO
不同,当进行读写操作时,只须直接调用 API 的 read
或 write
方法即可。这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入 read
方 法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将 write
方法传递的流写入完毕时,操作系统主动通知应用程序。即可以理解为,read/write
方法都是异步的,完成后会主动调用回调函数。在 JDK7
中,提供了异步文件通道和异步套接字通道的实现,这部分内容被称作 NIO
.
AIO
方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用 OS
参与并发操作,编程比较复杂,JDK7
开始支持。
目前来说 AIO
的应用还不是很广泛,Netty
之前也尝试使用过 AIO
,不过又放弃了。
二、NIO 核心组件介绍
1. Channel
在NIO
中,基本所有的 IO 操作都是从Channel
开始的,Channel
通过Buffer(缓冲区)
进行读写操作。
read()
表示读取通道中数据到缓冲区,write()
表示把缓冲区数据写入到通道。
Channel
有好多实现类,这里有三个最常用:
SocketChannel
:一个客户端发起 TCP 连接的 ChannelServerSocketChannel
:一个服务端监听新连接的 TCP Channel,对于每一个新的 Client 连接,都会建立一个对应的 SocketChannelFileChannel
:从文件中读写数据
其中SocketChannel
和ServerSocketChannel
是网络编程中最常用的,一会在最后的示例代码中会有讲解到具体用法。
2. Buffer
概念
Buffer
也被成为内存缓冲区,本质上就是内存中的一块,我们可以将数据写入这块内存,之后从这块内存中读取数据。也可以将这块内存封装成NIO Buffer
对象,并提供一组常用的方法,方便我们对该块内存进行读写操作。
Buffer
在java.nio
中被定义为抽象类:
我们可以将Buffer
理解为一个数组的封装,我们最常用的ByteBuffer
对应的数据结构就是byte[]
属性
Buffer
中有 4 个非常重要的属性:capacity、limit、position、mark
最后
小编在这里分享些我自己平时的学习资料,由于篇幅限制,pdf 文档的详解资料太全面,细节内容实在太多啦,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!
CodeChina开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频】
程序员代码面试指南 IT 名企算法与数据结构题目最优解
这是” 本程序员面试宝典!书中对 IT 名企代码面试各类题目的最优解进行了总结,并提供了相关代码实现。针对当前程序员面试缺乏权威题目汇总这一-痛点, 本书选取将近 200 道真实出现过的经典代码面试题,帮助广“大程序员的面试准备做到万无一失。 “刷”完本书后,你就是“题王”!
《TCP-IP 协议组(第 4 版)》
本书是介绍 TCP/IP 协议族的经典图书的最新版本。本书自第 1 版出版以来,就广受读者欢迎。
本书最新版进行」护元,以体境计算机网络技不的最新发展,全书古有七大部分共 30 草和 7 个附录:第一部分介绍一些基本概念和基础底层技术:第二部分介绍网络层协议:第三部分介绍运输层协议;第四部分介绍应用层协议:第五部分介绍下一代协议,即 IPv6 协议:第六部分介绍网络安全问题:第七部分给出了 7 个附录。
Java 开发手册(嵩山版)
这个不用多说了,阿里的开发手册,每次更新我都会看,这是 8 月初最新更新的**(嵩山版)**
MySQL 8 从入门到精通
本书主要内容包括 MySQL 的安装与配置、数据库的创建、数据表的创建、数据类型和运算符、MySQL 函数、查询数据、数据表的操作(插入、更新与删除数据)、索引、存储过程和函数、视图、触发器、用户管理、数据备份与还原、MySQL 日志、性能优化、MySQL Repl ication、MySQL Workbench、 MySQL Utilities、 MySQL Proxy、PHP 操作 MySQL 数据库和 PDO 数据库抽象类库等。最后通过 3 个综合案例的数据库设计,进步讲述 MySQL 在实际工作中的应用。
Spring5 高级编程(第 5 版)
本书涵盖 Spring 5 的所有内容,如果想要充分利用这一领先的企业级 Java 应用程序开发框架的强大功能,本书是最全面的 Spring 参考和实用指南。
本书第 5 版涵盖核心的 Spring 及其与其他领先的 Java 技术(比如 Hibemate JPA 2.Tls、Thymeleaf 和 WebSocket)的集成。本书的重点是介绍如何使用 Java 配置类、lambda 表达式、Spring Boot 以及反应式编程。同时,将与企业级应用程序开发人员分享一些见解和实际经验,包括远程处理、事务、Web 和表示层,等等。
JAVA 核心知识点+1000 道 互联网 Java 工程师面试题
企业 IT 架构转型之道 阿里巴巴中台战略思想与架构实战
本书讲述了阿里巴巴的技术发展史,同时也是-部互联网技 术架构的实践与发展史。
评论