文章目录
- 前言
- 进程和线程有什么区别?
- 关系型数据库和非关系型数据库有什么区别?
- HTTS 和 HTTP 有什么区别?
- 什么是单例设计模式?
- 什么是中间件?
- Java实现线程池
- 消息队列(MQ)
- 如何判断二叉树是一个平衡树?
- Python 中的元组?
- Java简单实现大文件分包上传服务器大致思路?
- TCP 协议
- TCP 与 UDP 的区别
- 循环冗余校验码(CRC)
- SQL 基本语法
- Java 中 List、ArrayList 和 LinkedList 的区别
- 悲观锁和乐观锁
- HTTP状态码
- git 中 rebase 和 merge 的区别
- 大端序、小端序是什么?
- synchronized 原理
- ACID 法则
- 什么是线程安全?
- get、post的区别
- Java 基本数据类型
- 什么是“跨域”
- HTTPS 的加密过程
前言
BAT 的实习面试题比较基础,但是想回答的精彩还是需要提前准备一下的,下面的问题都是博主亲身试水换来的。
进程和线程有什么区别?
功能不同
- 进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
- 线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
工作原理不同
- 在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
- 线程是独立调度和分派的基本单位。线程可以为操作系统内核调度的内核线程,如 Win32 线程;由用户进程自行调度的用户线程,如 Linux 平台的POSIX Thread;或者由内核与用户进程,如 Windows 7 的线程,进行混合调度。
作用不同
- 进程是操作系统中最基本、重要的概念。是多道程序系统出现后,为了刻画系统内部出现的动态情况,描述系统内部各道程序的活动规律引进的一个概念,所有多道程序设计操作系统都建立在进程的基础上。
- 通常在一个进程中可以包含若干个线程,它们可以利用进程所拥有的资源。在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。
关系型数据库和非关系型数据库有什么区别?
数据存储方式不同
关系型和非关系型数据库的主要差异是数据存储的方式。
- 关系型数据天然就是表格式的,因此存储在数据表的行和列中。数据表可以彼此关联协作存储,也很容易提取数据。
- 非关系型数据不适合存储在数据表的行和列中,而是大块组合在一起。非关系型数据通常存储在数据集中,就像文档、键值对或者图结构。你的数据及其特性是选择数据存储和提取方式的首要影响因素。
扩展方式不同
SQL 和 NoSQL 数据库最大的差别可能是在扩展方式上,要支持日益增长的需求当然要扩展。
- 要支持更多并发量,SQL 数据库是纵向扩展,也就是说提高处理能力,使用速度更快速的计算机,这样处理相同的数据集就更快了。因为数据存储在关系表中,操作的性能瓶颈可能涉及很多个表,这都需要通过提高计算机性能来客服。虽然 SQL 数据库有很大扩展空间,但最终肯定会达到纵向扩展的上限。
- 而 NoSQL 数据库是横向扩展的。而非关系型数据存储天然就是分布式的,NoSQL 数据库的扩展可以通过给资源池添加更多普通的数据库服务器(节点)来分担负载。
对事务性的支持不同
如果数据操作需要高事务性或者复杂数据查询需要控制执行计划,那么传统的 SQL 数据库从性能和稳定性方面考虑是你的最佳选择。SQL 数据库支持对事务原子性细粒度控制,并且易于回滚事务。
虽然 NoSQL 数据库也可以使用事务操作,但稳定性方面没法和关系型数据库比较,所以它们真正闪亮的价值是在操作的扩展性和大数据量处理方面。
HTTS 和 HTTP 有什么区别?
超文本传输协议 HTTP 协议被用于在 Web 浏览器和网站服务器之间传递信息,HTTP 协议以明文方式发送内容,不提供任何方式的数据加密,如果攻击者截取了Web浏览器和网站服务器之间的传输报文,就可以直接读懂其中的信息,因此,HTTP 协议不适合传输一些敏感信息,比如:信用卡号、密码等支付信息。
为了解决 HTTP 协议的这一缺陷,需要使用另一种协议:安全套接字层超文本传输协议 HTTPS,为了数据传输的安全,HTTPS 在 HTTP 的基础上加入了 SSL(Secure Sockets Laye,安全套接层)/TLS(Transport Layer Security,传输层安全协议) 协议,SSL/TLS 依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。
HTTPS 协议是由 SSL/TLS + HTTP 协议构建的可进行加密传输、身份认证的网络协议,要比 HTTP 协议安全。
HTTPS 和 HTTP 的主要区别
-
HTTPS 协议需要到 CA 申请证书,一般免费证书较少,因而需要一定费用。
-
HTTP 是超文本传输协议,信息是明文传输,HTTPS 则是具有安全性的 SSL/TLS 加密传输协议。
-
HTTP 和 HTTPS 使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
-
HTTP 的连接很简单,是无状态的;HTTPS 协议是由 SSL/TLS + HTTP协议构建的可进行加密传输、身份认证的网络协议,比 HTTP 协议安全。
什么是单例设计模式?
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意:
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
什么是中间件?
中间件是独立的系统级软件,连接操作系统层和应用程序层,将不同操作系统提供应用的接口标准化,协议统一化,屏蔽具体操作的细节。
通俗:“适配器”。需要利用服务的人(前端写业务的),不需要知道底层逻辑(提供服务的)的具体实现,只要拿着中间件结果来用就好了。
Java实现线程池
电脑的 CPU 资源是有限的,任务的处理速度与线程数量之间并不是正相关。当线程数量过多,CPU 要频繁的在不同线程切换,反而会引起处理性能的下降。线程池中最大的线程数,是考虑多种因素来事先设定的,比如硬件的条件,业务的类型等等。
当我们向一个固定大小的的线程池中请求一个线程时,当线程池中没有空闲资源了,这个时候线程池如何处理这个请求?是拒绝请求还是排队请求?各种策略又是如何实现的呢?实际上,这些问题的处理并不复杂,底层的数据结构,就是队列(queue)。
线程池的作用
- 限制系统中执行线程的数量。
- 减少了创建和销毁线程的次数,重复利用线程。
主要的类
- Executor:执行线程的接口
- ExecutorSerivce: 线程池接口
- ThreadPoolExecutor :线程池类
- Executors:常用线程池工厂
常用的线程池
配置线程池是比较复杂的过程,所有可以使用现有的线程池工厂生成常用的线程池:
- newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。为了合理利用资源,我们通常把定长池的长度设置为当前PC机获取cpu核心数,
Runtime.getRuntime().availableProcessors()
,获取当前CPU核心数; - newCachedThreadPool 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程;
- newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行;
- newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
消息队列(MQ)
为什么使用消息队列?
- 解耦:如果 A 系统要发送数据给 B、C、D 三个系统,之后可能还有系统加进入进来,我们用消息队列的话 A 系统只管把消息发到消息队列,其他需要这个消息的来订阅就可以了,
- 异步:A 系统需要发送个请求给 B 系统处理,由于 B 系统需要查询数据库花费时间较长,以至于 A 系统要等待 B 系统处理完毕后再发送下个请求,造成 A 系统资源浪费。使用消息队列后,A 系统生产完消息后直接丢进消息队列,不用等待 B 系统的结果,直接继续去干自己的事情了。
- 削峰:A 系统调用 B 系统处理数据,如果 A 系统的请求突然变得特别大,全都打到 B 系统,那 B 系统可能就会崩掉。我们让 A 系统把请求发到消息队列,这样 B 系统就可以按自己的需求去拉取消息进行消费。
使用了消息队列会有什么缺点?
- 降低系统的可用性:系统引入的外部依赖越多,越容易挂掉;
- 系统复杂度提高:使用 MQ 后可能需要保证消息没有被重复消费、处理消息丢失的情况、保证消息传递的顺序性等等问题;
- 一致性问题:A 系统处理完了直接返回成功了,但问题是:要是 B、C、D 三个系统那里,B 和 D 两个系统写库成功了,结果 C 系统写库失败了,就造成数据不一致了。
如何判断二叉树是一个平衡树?
平衡二叉树是一种高度自平衡树,它的任何一个结点的左右子树的高度之差不会超过 1。
递归方法:从定义就可以写出一个递归的解法,先求左右子树是否平衡然后,再判断自己是否平衡。
Python 中的元组?
Python 的元组与列表类似,不同之处在于元组的元素不能修改。
元组使用小括号,列表使用方括号。
元组创建很简单,只需要在括号中添加元素,并使用逗号隔开即可。
Java简单实现大文件分包上传服务器大致思路?
Step1:准备,预上传文件信息
-
客户端计算即将上传的文件的大小、CRC 校验码;
-
发送文件信息到服务器,包括文件大小、校验码、文件格式;
-
Web服务端在接收到文件上传请求后,生成唯一
fileId
,同时以该fileId
作为缓存主键,缓存文件信息。并在指定路径生成以fileId
命名的临时文件。 -
Web服务器将该fileid做为请求响应,反馈给客户端。
String fileId= UUID.randomUUID().toString()
String parentPath = request.getServletContext().getRealPath("/")+"tempFile/";
File file = new File(parentPath,fileName);
file.createNewFile();
Step2:数据分包上传
-
客户端将要上报文件按照一定大小进行分包。
-
客户端通过 HTTP 上传分包数据,消息头包括
fileid
,分包数据在整个文件的偏移量(offset
)、分包数据的长度(dataLen
)、分包数据的校验码(CRC
)以及分包数据消息体。 -
Web 服务端在收到数据请求时,先判断该
fileid
缓存是否存在,如果存在该fileid
缓存,读取请求的消息体数据,先进行数据校验。校验成功,根据解析的fileid
,打开在指定路径的临时文件,将消息体数据追加写入临时文件:
String filePath = request.getServletContext().getRealPath("/tempFile/");
File file = new File(filePath,fileName);
FileOutputStream fileOutputStream = new FileOutputStream(file, true);
- 客户端和Web服务器重复第 2、3 步。判断已接收的分包数据总大小是否和文件大小一致。如果一致,表明文件已经接收完整,计算接收完毕的文件 CRC 校验码,和在请求阶段上传的总文件的 CRC 校验码进行比对。一致则该文件成功接收,否则删除该临时文件,并通知客户端文件接收失败。
InputStream inputStream = request.getInputStream();
byte buffer[] = new byte[content_length];
int off_set=1;
int totalRead=0;
while (off_set>0){
off_set = inputStream.read(buffer,totalRead,buffer.length-totalRead);
if(off_set>-1)
totalRead=totalRead+off_set;
}
inputStream.close();
TCP 协议
传输控制协议(TCP,Transmission Control Protocol)
TCP 三次握手
- 客户端发送 SYN(SEQ=x)报文给服务器端,进入SYN_SEND 状态。
- 服务器端收到 SYN 报文,回应一个 SYN (SEQ=y)ACK(ACK=x+1)报文,进入 SYN_RECV 状态。
- 客户端收到服务器端的SYN报文,回应一个ACK(ACK=y+1)报文,进入 Established 状态。
三次握手完成,TCP客户端和服务器端成功地建立连接,可以开始传输数据了。
以下是人话:
- 主机 A 告诉主机 B:我想要和你通信;你可以用哪个序列号作为起始数据段来回应我?
- 主机 B 收到主机 A 的请求后,用一个带有确认应答(ACK)和同步序列号(SYN)标志位的数据段响应主机 A,也告诉主机 A 两件事:我已经收到你的请求了,你可以传输数据了;你要用那个序列号作为起始数据段来回应我?
- 主机 A 收到这个数据段后,再发送一个确认应答,确认已收到主机 B 的数据段:“我已收到回复,我现在要开始传输实际数据了”,这样3次握手就完成了,主机 A 和主机 B 就可以传输数据了。
TCP 四次握手
建立一个连接需要三次握手,而终止一个连接要经过四次握手,这是由 TCP 的半关闭(half-close)造成的。
- 某个应用进程首先调用 close,称该端执行“主动关闭”(active close)。该端的 TCP 于是发送一个 FIN 分节,表示数据发送完毕。
- 接收到这个 FIN 的对端执行 “被动关闭”(passive close),这个 FIN 由 TCP 确认。
注意:FIN的接收也作为一个文件结束符(end-of-file)传递给接收端应用进程,放在已排队等候该应用进程接收的任何其他数据之后,因为,FIN的接收意味着接收端应用进程在相应连接上再无额外数据可接收。 - 一段时间后,接收到这个文件结束符的应用进程将调用 close 关闭它的套接字。这导致它的 TCP 也发送一个 FIN。
- 接收这个最终 FIN 的原发送端 TCP(即执行主动关闭的那一端)确认这个FIN。
既然每个方向都需要一个 FIN 和一个 ACK,因此通常需要 4 个分节。
TCP 与 UDP 的区别
TCP/IP 是互联网相关的各类协议族的总称,比如:TCP,UDP,IP,FTP,HTTP,ICMP,SMTP 等都属于 TCP/IP 族内的协议。
TCP:传输控制协议(Transmission Control Protocol)是面向连接的协议,也就是说,在收发数据前,必须和对方建立可靠的连接。
UDP:用户数据包协议(User Datagram Protocol),UDP 不止支持一对一的传输方式,同样支持一对多,多对多,多对一的方式,也就是说 UDP 提供了单播,多播,广播的功能。不可靠,常用于实时性要求高的场景(比如电话会议)。
循环冗余校验码(CRC)
循环冗余校验(Cyclic Redundancy Check, CRC)是一种根据网络数据包或计算机文件等数据产生简短固定位数校验码的一种信道编码技术,主要用来检测或校验数据传输或者保存后可能出现的错误。它是利用除法及余数的原理来作错误侦测的。
- 模二运算
- 二进制系数多项式
SQL 基本语法
- select 查询(选择字段)
- where 过滤筛选
- insert into 插入
- update 更改
- delete 删除
- ================================================
- INNER JOIN 交集
- FULL OUTER JOIN 并集
- GROUP BY 聚合分组
- HAVING 筛选分组(WHERE 关键字无法与聚合函数一起使用)
- ================================================
- CREATE DATABASE - 创建新数据库
- ALTER DATABASE - 修改数据库
- CREATE TABLE - 创建新表
- ALTER TABLE - 变更(改变)数据库表
- DROP TABLE - 删除表
- CREATE INDEX - 创建索引(搜索键)
- DROP INDEX - 删除索引
Java 中 List、ArrayList 和 LinkedList 的区别
ArrayList
和 LinkedList
都是 List
的实现类,List
主要有两个特点:① 有序;②可重复。
ArrayList 和 LinkedList 的区别:
-
二者实现结构不同 Arraylist 是基于数组,Linkedlist 是基于链表,他们的特性也是由其数据结构决定的。
-
随机遍历访问时 Linkedlist 的性能要低于 Arraylist。
-
Arraylist 的初始化时默认 10 容量,而 Linkedlist 默认初始化为空。
-
Linkedlis t的增删要优于 Arraylist。
悲观锁和乐观锁
悲观锁(Pessimistic Lock)
之所以叫做悲观锁,是因为这是一种对数据的修改持有悲观态度的并发控制方式。总是假设最坏的情况,每次读取数据的时候都默认其他线程会更改数据,因此需要进行加锁操作,当其他线程想要访问数据时,都需要阻塞挂起。
悲观锁主要分为共享锁和排他锁:
共享锁【shared locks】又称为读锁,简称S锁。顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。
排他锁【exclusive locks】又称为写锁,简称X锁。顾名思义,排他锁就是不能与其他锁并存,如果一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务是可以对数据行读取和修改。
乐观锁(Optimistic Locking)
乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则返回给用户错误的信息,让用户决定如何去做。乐观锁适用于读操作多的场景,这样可以提高程序的吞吐量。
HTTP状态码
- 200 - 请求成功
- 301 - 资源(网页等)被永久转移到其它URL
- 404 - 请求的资源(网页等)不存在
- 500 - 内部服务器错误
3**: 重定向,需要进一步的操作以完成请求
4**: 客户端错误,请求包含语法错误或无法完成请求
5**: 服务器错误,服务器在处理请求的过程中发生了错误
git 中 rebase 和 merge 的区别
- merge 是一个合并操作,会将两个分支的修改合并在一起,默认操作的情况下会提交合并中修改的内容。
- merge 的提交历史忠实地记录了实际发生过什么,关注点在真实的提交历史上面。
- rebase 并没有进行合并操作,只是提取了当前分支的修改,将其复制在了目标分支的最新提交后面。
大端序、小端序是什么?
计算机硬件有两种储存数据的方式:大端字节序(big endian)和小端字节序(little endian)。
举例来说,数值0x2211使用两个字节储存:高位字节是0x22,低位字节是0x11。
- 大端字节序:高位字节在前,低位字节在后,这是人类读写数值的方法。
- 小端字节序:低位字节在前,高位字节在后,即以0x1122形式储存。
为什么会有小端字节序?
计算机电路先处理低位字节,效率比较高,因为计算都是从低位开始的。所以,计算机的内部处理都是小端字节序。
但是,人类还是习惯读写大端字节序。所以,除了计算机的内部处理,其他的场合几乎都是大端字节序,比如网络传输和文件储存。
synchronized 原理
开发过程中可以使用它来解决线程安全问题中提到的原子性,可见性,以及顺序性。
- synchronized 可以保证在同一个时刻,只有一个线程可以执行某个方法或者某个代码块。
- synchronized可保证一个线程的变化被其他线程所看到。、
ACID 法则
事务具有4个特征,分别是原子性、一致性、隔离性和持久性,简称事务的 ACID 特性;
原子性(Atomicity)
一个事务要么全部提交成功,要么全部失败回滚,不能只执行其中的一部分操作,这就是事务的原子性
一致性(Consistency)
事务的执行不能破坏数据库数据的完整性和一致性,一个事务在执行之前和执行之后,数据库都必须处于一致性状态。
如果数据库系统在运行过程中发生故障,有些事务尚未完成就被迫中断,这些未完成的事务对数据库所作的修改有一部分已写入物理数据库,这是数据库就处于一种不正确的状态,也就是不一致的状态
隔离性(Isolation)
事务的隔离性是指在并发环境中,并发的事务时相互隔离的,一个事务的执行不能不被其他事务干扰。不同的事务并发操作相同的数据时,每个事务都有各自完成的数据空间,即一个事务内部的操作及使用的数据对其他并发事务时隔离的,并发执行的各个事务之间不能相互干扰。
持久性(Durability)
一旦事务提交,那么它对数据库中的对应数据的状态的变更就会永久保存到数据库中。即使发生系统崩溃或机器宕机等故障,只要数据库能够重新启动,那么一定能够将其恢复到事务成功结束的状态。
什么是线程安全?
多个线程同一时刻对同一个全局变量(同一份资源)做写操作(读操作不会涉及线程安全)时,如果跟我们预期的结果一样,我们就称之为线程安全,反之,线程不安全。
get、post的区别
-
get 传参方式是通过地址栏 URL 传递,是可以直接看到 get 传递的参数,post 传参方式参数 URL 不可见,get把请求的数据在URL后通过?连接,通过&进行参数分割。post 将参数存放在 HTTP 的包体内
-
get传递数据是通过URL进行传递,对传递的数据长度是受到URL大小的限制,URL最大长度是2048个字符。post没有长度限制
-
get后退不会有影响,post后退会重新进行提交
-
get请求可以被缓存,post不可以被缓存
-
get请求只URL编码,post支持多种编码方式
-
get请求的记录会留在历史记录中,post请求不会留在历史记录
-
get只支持ASCII字符,post没有字符类型限制
Java 基本数据类型
- 原始类型:boolean,char,byte,short,int,long,float,double
- 包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double
应用场景
int 是 Java 提供的 8 种原始数据类型之一。Java 为每个原始类型提供了封装类,Integer 是 java 为int提供的封装类。int 的默认值为0,而 Integer 的默认值为null。
- Integer 可以区分出未赋值和值为0的区别,int 则无法表达出未赋值的情况,例如,要想表达出没有参加考试和考试成绩为0的区别,则只能使用Integer。
- 在 JSP 开发中,Integer 的默认为 null,所以用 el 表达式在文本框中显示时,值为空白字符串,而 int 默认的默认值为0,所以用 el 表达式在文本框中显示时,结果为 0,所以,int 不适合作为 web 层的表单数据的类型。
- 在 Hibernate 中,如果将 OID 定义为 Integer 类型,那么 Hibernate 就可以根据其值是否为 null 而判断一个对象是否是临时的,如果将 OID 定义为了 int 类型,还需要在 hbm 映射文件中设置其 unsaved-value 属性为0。
- Integer 提供了多个与整数相关的操作方法,例如,将一个字符串转换成整数,Integer 中还定义了表示整数的最大值和最小值的常量。
什么是“跨域”
当一个请求 url 的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域。