杂记

115 阅读27分钟

计算机网络

HTTP

HTTP协议,全称超文本传输协议Hypertext Transfer Protocol。HTTP协议定义了浏览器(万维网客户进程)怎样向万维网服务器请求万维网文档,以及服务器怎样把文档传送给浏览器。
HTTP 是一个无状态(stateless)协议,也就是说服务器不维护任何有关客户端过去所发请求的消息。

HTTP协议优点

  • 简单、灵活、跨平台支持性好。
  • HTTP是无状态的,可以轻松实现集群化,扩展性能。
  • HTTP采用明文传输,数据完全肉眼可见,能够方便地研究和分析,但同时也容易被窃听。

HTTP协议缺点

  • 通讯使用明文,未加密,并且TCP/IP协议是可能会被窃听的网络,所以通讯内容可能会被窃听
  • 没有验证通讯方的身份,因此可能会遭遇伪装
  • 没有办法验证报文的完整性,所以可能会被篡改

HTTP各个版本的区别

HTTP 协议从开始立项到现在一共经历了 4 个版本:

    HTTP 0.9 -> HTTP 1.0 -> HTTP 1.1 -> HTTP 2
  • HTTP 0.9
    • 只支持GET请求方式:由于不支持其他请求方式,因此客户端是没办法向服务端传输太多的信息
    • 没有请求头概念:所以不能在请求中指定版本号,服务端也只具有返回 HTML字符串的能力
    • 服务端相响应之后,立即关闭TCP连接
  • HTTP 1.0
    • 请求方式新增了POST,DELETE,PUT,HEADER等方式
    • 增添了请求头和响应头的概念,在通信中指定了 HTTP 协议版本号,以及其他的一些元信息 (比如: 状态码、权限、缓存、内容编码)
    • 扩充了传输内容格式,图片、音视频资源、二进制等都可以进行传输
  • HTTP 1.1
    • 长连接:新增Connection字段,可以设置keep-alive值保持连接不断开
    • 管道化:基于上面长连接的基础,管道化可以不等第一个请求响应继续发送后面的请求,但响应的顺序还是按照请求的顺序返回
    • 缓存处理:新增字段cache-control
    • 断点传输
  • HTTP 2.0
    • 二进制分帧
    • 多路复用: 在共享TCP链接的基础上同时发送请求和响应
    • 头部压缩
    • 服务器推送:服务器可以额外的向客户端推送资源,而无需客户端明确的请求
  • HTTP 3.0
    • 将弃用TCP协议,改为使用基于UDP协议的QUIC协议实现。

HTTP方法

序号方法描述
1GET请求指定的页面信息,并返回实体主体。
2HEAD类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头
3POST向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST 请求可能会导致新的资源的建立和/或已有资源的修改。
4PUT从客户端向服务器传送的数据取代指定的文档的内容。
5DELETE请求服务器删除指定的页面。
6CONNECTHTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。
7OPTIONS允许客户端查看服务器的性能。
8TRACE回显服务器收到的请求,主要用于测试或诊断。
9PATCH是对 PUT 方法的补充,用来对已知资源进行局部更新 。

GET和POST的区别

  • GET在浏览器回退时是无害的,而POST会再次提交请求。
  • GET产生的URL地址可以被书签,而POST不可以。
  • GET请求会被浏览器主动cache,而POST不会,除非手动设置。
  • GET请求只能进行url编码,而POST支持多种编码方式。
  • GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
  • GET请求在URL中传送的参数是有长度限制的,而POST没有。
  • 对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
  • GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
  • GET参数通过URL传递,POST放在Request body中

HTTP状态码

  • 1** : 信息,服务器收到请求,需要请求者继续执行操作
  • 2** : 成功,操作被成功接收并处理
  • 3** : 重定向,需要进一步的操作以完成请求
  • 4** : 客户端错误,请求包含语法错误或无法完成请求
  • 5** : 服务器错误,服务器在处理请求的过程中发生了错误 image.png image.png image.png image.png

HTTP与HTTPS的区别

  • HTTP 明文传输,数据都是未加密的,安全性较差,HTTPS(SSL+HTTP) 数据传输过程是加密的,安全性较好。
  • HTTP 页面响应速度比 HTTPS 快,主要是因为 HTTP 使用 TCP 三次握手建立连接,客户端和服务器需要交换 3 个包,而 HTTPS除了 TCP 的三个包,还要加上 ssl 握手需要的 9 个包,所以一共是 12 个包。
  • http 和 https 使用的是完全不同的连接方式,用的端口也不一样,前者是 80,后者是 443。
  • HTTPS 其实就是建构在 SSL/TLS 之上的 HTTP 协议,所以,要比较 HTTPS 比 HTTP 要更耗费服务器资源。
  • 使用 HTTPS 协议需要到CA申请证书,一般免费证书较少,因而需要一定费用。

HTTPS

HTTPS HyperText Transfer Protocol Secure,是以安全为目标的 HTTP 通道。HTTPS经由HTTP进行通信,但利用SSL/TLS来加密数据包。HTTPS开发的主要目的,是提供对网站服务器的身份认证,保护交换资料的隐私与完整性。
HTTPS的默认端口号为443。

HTTPS协议优点

  • 使用HTTPS协议可认证用户和服务器,确保数据发送到正确的客户机和服务器。
  • 使用HTTPS协议可防止数据在传输过程中不被窃取、改变,确保数据的完整性。
  • 尽管HTTPS不是绝对安全,但它大幅增加了中间人攻击的成本。

HTTPS协议缺点

  • HTTPS协议握手阶段比较费时,会使页面的加载时间延长。
  • HTTPS连接缓存不如HTTP高效,会增加数据开销和功耗。
  • HTTPS协议的加密范围也比较有限,而且SSL证书的信用链体系并不安全。

SSL/TLS

区别:
SSL是(Secure Socket Layer,安全套接字层)的缩写,TLS是(Transport Layer Security Protocol,传输层安全)的缩写,都是加密数据和在Internet上移动数据时验证连接的加密协议。
TLS 实际上只是SSL的更新版本,它修复了早期SSL协议中的一些安全漏洞

混合加密

非对称加密使用一对非对称的密钥,一把叫做私钥,一把叫做公钥。顾名思义,私钥不能让任何人知道,公钥任何人都可以获得。使用方式是:发送方将报文用公钥进行加密处理,对方收到后用私钥进行解密,也不用担心密钥被攻击者窃听而盗走。因为非对称加密是基于大数运算,所以速度很,还有一个缺点是相同强度的加密,非对称加密需要更多的位数。

对称加密的通信双方共享唯一密钥 k,加解密算法已知,加密方利用密钥 k 加密,解密方利用密钥 k 解密,保密性依赖于密钥 k 的保密性

HTTPS利用了两者的优势,将两种方式组合起来用于通信。在交换密钥阶段使用非对称加密的方式,之后建立通信交换报文阶段则使用对称加密方式。

数字签名

为了公钥传输的信赖性问题,第三方机构应运而生——证书颁发机构(CA,Certificate Authority)。CA 默认是受信任的第三方。CA 会给各个服务器颁发证书,证书存储在服务器上,并附有 CA 的电子签名

当客户端(浏览器)向服务器发送 HTTPS 请求时,一定要先获取目标服务器的证书,并根据证书上的信息,检验证书的合法性。一旦客户端检测到证书非法,就会发生错误。客户端获取了服务器的证书后,由于证书的信任性是由第三方信赖机构认证的,而证书上又包含着服务器的公钥信息,客户端就可以放心的信任证书上的公钥就是目标服务器的公钥。

数字签名,是 CA 在给服务器颁发证书时,使用散列+加密的组合技术,在证书上盖个章,以此来提供验伪的功能。

CA 知道服务器的公钥,对该公钥采用散列技术生成一个摘要。CA 使用 CA 私钥对该摘要进行加密,并附在证书下方,发送给服务器。

现在服务器将该证书发送给客户端,客户端需要验证该证书的身份。客户端找到第三方机构 CA,获知 CA 的公钥,并用 CA 公钥对证书的签名进行解密,获得了 CA 生成的摘要。

客户端对证书数据(也就是服务器的公钥)做相同的散列处理,得到摘要,并将该摘要与之前从签名中解码出的摘要做对比,如果相同,则身份验证成功;否则验证失败。

HTTPS建立连接的过程

先SSL再TCP image.png

  1. 浏览器发送Client Hello消息,信息如下:

    • 客户端支持的SSL/TLS版本号,如TLS1.2版本
    • 支持的密码套件,如ECDHE
    • 客户端产生的随机数C,用于生成后续的会话密钥
  2. 服务器收到Client Hello消息后,会发送Server Hello

    • 确认SSL/TLS协议版本,如果浏览器不支持,则关闭加密通信
    • 确认密码套件列表,选择最合适的加密算法
    • 服务器产生的随机数S,用于生成后续的会话密钥
    • 服务端为了证明身份,把证书也发送给客户端
  3. 客户端回应,Client Key Exchange。客户端收到服务器的消息后,用浏览器或者系统中的CA公钥验证数字证书的真实性,如果证书没问题,客户端从数字证书中取出服务器的公钥,用其加密报文。

    • 一个随机数,该随机数会被上面提到的服务器的公钥加密,以防被黑客破解。这个随机数加上上文生成的其他两个随机数,使用会话中约定的加密算法,会各自生成加密会话的主密钥
    • 加密通信算法改变通知,随后的信息都将用会话密钥加密通信
    • Finished消息,结束通知。把之前所有发送的数据做一个摘要再加密一下,让服务器做验证
  4. 服务器的最后回应。

    • 加密通信算法改变通知
    • Finished消息,结束通知。把之前所有发送的数据做一个摘要再加密一下,让客户端做验证

TCP

连接与关闭

三次握手:

  1. 客户端–发送带有 SYN 标志的数据包–一次握手–服务端
  2. 服务端–发送带有 SYN/ACK 标志的数据包–二次握手–客户端
  3. 客户端–发送带有带有 ACK 标志的数据包–三次握手–服务端 image.png

为什么还要第三次握手?
主要防止已经失效的连接请求报文突然又传送到了服务器,从而产生错误。

如果使用的是两次握手建立连接,假设有这样一种场景,客户端发送了第一个请求连接并且没有丢失,只是因为在网络结点中滞留的时间太长了,由于TCP的客户端迟迟没有收到确认报文,以为服务器没有收到,此时重新向服务器发送这条报文,此后客户端和服务器经过两次握手完成连接,传输数据,然后关闭连接。此时此前滞留的那一次请求连接,网络通畅了到达了服务器,这个报文本该是失效的,但是,两次握手的机制将会让客户端和服务器再次建立连接,这将导致不必要的错误和资源的浪费。
如果采用的是三次握手,就算是那一次失效的报文传送过来了,服务端接受到了那条失效报文并且回复了确认报文,但是客户端不会再次发出确认。由于服务器收不到确认,就知道客户端并没有请求连接。

四次挥手:

  1. 客户端-发送一个 FIN,用来关闭客户端到服务器的数据传送
  2. 服务器-收到这个 FIN,它发回一个 ACK,确认序号为收到的序号加 1 。和 SYN 一样,一个 FIN将占用一个序号
  3. 服务器-关闭与客户端的连接,发送一个 FIN 给客户端
  4. 客户端-发回 ACK 报文确认,并将确认序号设置为收到序号加 1 image-20211213010252694.png

为什么四次挥手?

由于TCP半关闭(half-close)特性,TCP提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。
任何一方都可以在数据传送结束后发出连接释放的通知,待对方确认后进入半关闭状态。当另一方也没有数据再发送的时候,则发出连接释放通知,对方确认后就完全关闭了TCP连接。

TIME-WAIT状态为什么是2MSL?

  1. 保证客户端发送的最后一个ACK报文能够到达服务器,因为这个ACK报文可能丢失,站在服务器的角度看来,我已经发送了FIN+ACK报文请求断开了,客户端还没有给我回应,应该是我发送的请求断开报文它没有收到,于是服务器又会重新发送一次,而客户端就能在这个2MSL时间段内收到这个重传的报文,接着给出回应报文,并且会重启2MSL计时器。
  2. 防止类似与“三次握手”中提到了的“已经失效的连接请求报文段”出现在本连接中。客户端发送完最后一个确认报文后,在这个2MSL时间中,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样新的连接中不会出现旧连接的请求报文。

如果已经建立了连接,但是客户端突然出现故障了怎么办?

TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。

流量控制

流量控制是作用于接收者的,它是控制发送者的发送速度从而使接收者来得及接收,防止分组丢失的。

滑动窗口:
TCP利用滑动窗口实现流量控制。 接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将窗口字段设置为 0,则发送方不能发送数据。发送方的发送窗口取接收窗口rwnd拥塞窗口cwnd的最小值

拥塞控制

拥塞控制是作用于网络的,它是防止过多的数据注入到网络中,避免出现网络负载过大的情况。
为了进行拥塞控制,TCP 发送方要维持一个 拥塞窗口(cwnd) 的状态变量。拥塞控制窗口的大小取决于网络的拥塞程度,并且动态变化。

TCP 的拥塞控制采用了四种算法,即慢开始 、拥塞避免快重传快恢复

  • 慢开始
    • 初始化拥塞窗口cwnd为1
    • 每接收一个ACKcwnd大小增加1
    • 每当过了一个往返延迟时间RTTcwnd大小乘2
    • 有一个ssthresh值,当cwnd>=ssthresh时,算法变为拥塞避免算法
  • 拥塞避免
    • 收到一个ACK时,cwnd = cwnd + 1/cwnd
    • 每过一个RTT时,cwnd++
  • 发生丢包:
    • ssthresh = cwnd/2
    • cwnd = 1
    • 进入慢开始
  • 快重传
    • 接收方在收到一个失序的报文段后就立即发出重复确认),而不要等到自己发送数据时捎带确认。
    • 发送方只要一连收到3个重复确认就应当立即重传对方尚未收到的报文段,而不必继续等待设置的重传计数器时间到期。
  • 快恢复
    • ssthresh = cwnd/2
    • cwnd = cwnd/
    • 进入拥塞控制算法

UDP

TCP协议和UDP协议区别

  • TCP是面向连接的服务,而UDP再传输数据之前不需要建立连接;
  • TCP提供可靠的传输服务,而UDP提供不可靠的传输服务;
  • TCP在服务时使用流量控制和拥塞控制,而UDP不使用;
  • TCP仅支持一对一通信,而UDP支持一对一、一对多和多对多交互通信;
  • TCP首部开销最小20字节(,最大60字节),而UDP首部开销下,仅8字节。

Cookie & Session

  • cookie实际上是一小段的文本信息。浏览器发送请求到服务器,如果服务器需要记录该用户的状态(比如:用户访问网页的次数,登录状态等),就使用response向客户端浏览器颁发一个cookie。客户端浏览器会把cookie保存起来。当浏览器再次请求该网站时,浏览器就会把请求地址和cookie一同给服务器。服务器检查该cookie,从而判断用户的状态。服务器还可以根据需要修改cookie的内容。
  • session也是类似的记录用户状态的机制。不同的是cookie保存在客户端浏览器中,而session保存在服务器上。session比cookie安全,别人可以分析存放在本地的cookie并进行cookie欺骗。cookie能保存的数据量比session小;

如果说cookie机制是通过检查客户身上的“通信证”,那么session机制就是通过检查服务器上的“客户明细表”来确认客户身份。

操作系统

MySQL

索引

事务

隔离级别的实现

  • 读未提交:
    • 读不加锁
    • 写加行级锁
  • 读已提交:使用MVCC技术,在每一行加入隐藏的字段
    • 读不加锁:在读取时,如果该行被其它事务锁定,则顺着隐藏列DATA_POLL_PTR指针,找到上一个有效的历史记录
    • 写加行锁:事务开始后,会在 UNDO 日志中写入修改记录,数据行中的隐藏列DATA_POLL_PTR存储指向该行的UNDO记录的指针。
    • 数据的可见性是该数据的最新记录
  • 可重复读:使用MVCC技术来实现不加锁的读操作
    • 读不加锁:在读取时,如果该行被其它事务锁定,则顺着隐藏列DATA_POLL_PTR指针,找到上一个有效的历史记录
    • 写加行锁:事务开始后,会在 UNDO 日志中写入修改记录,数据行中的隐藏列DATA_POLL_PTR存储指向该行的UNDO记录的指针。
    • 数据的可见性是事务开始时,该数据的记录
  • 串行化:
    • 读加共享锁
    • 写加排它锁

MVCC

  • DB_TRX_ID:修改该行的最后一个事务的id
  • DB_ROLL_PTR:指向当前行的undo log日志
  • DB_ROW_ID:行标识
  • DELETE_BIT:删除标志

Redis

读写策略

旁路缓存

  •  :
    • 先更新 DB
    • 然后直接删除 cache 。
  •  :
    • 从 cache 中读取数据,读取到就直接返回
    • cache中读取不到的话,就从 DB 中读取数据返回
    • 再把数据放到 cache 中。

读写穿透

  • 写(Write Through):
    • 先查 cache,cache 中不存在,直接更新 DB。
    • cache 中存在,则先更新 cache,然后 cache 服务自己更新 DB(同步更新 cache 和 DB
  • 读(Read Through):
    • 从 cache 中读取数据,读取到就直接返回 。
    • 读取不到的话,先从 DB 加载,写入到 cache 后返回响应。

异步缓存写入
与读写穿透相似,两者都是由 cache 服务来负责 cache 和 DB 的读写。
但是,两个又有很大的不同:Read/Write Through 是同步更新 cache 和 DB,而 Write Behind Caching 则是只更新缓存,不直接更新 DB,而是改为异步批量的方式来更新 DB。

Java

集合

集合的类型:

  • Lsit
    • ArrayList:基于动态数组实现,支持随机访问
    • Vector:与ArrayList类似,但它是线程安全的
    • LinkedList:基于双向链表实现,只能顺序访问,但是可以快速地在链表中间插入和删除元素。不仅如此,LinkedList 还可以用作栈、队列和双向队列。
  • Set
    • TreeSet:基于红黑树实现,支持有序性操作,例如根据一个范围查找元素的操作。但是查找效率不如 HashSet,HashSet 查找的时间复杂度为 O(1),TreeSet 则为 O(logN)。
    • HashSet:基于 HashMap 实现,支持快速查找,但不支持有序性操作。并且失去了元素的插入顺序信息。
    • LinkedHashSet:具有 HashSet 的查找效率,且内部使用双向链表维护元素的插入顺序。
  • Queue
    • LinkedList:可以用它来实现双向队列
    • PriorityQueue:基于堆结构实现,可以用它来实现优先队列

ArrayList的底层?
ArrayList实现了List接口,是顺序容器,即元素存放的数据与放进去的顺序相同,允许放入null元素,底层通过数组实现。除该类未实现同步外,其余跟Vector大致相同。每个ArrayList都有一个容量(capacity),表示底层数组的实际大小,容器内存储元素的个数不能多于当前容量。当向容器中添加元素时,如果容量不足,容器会自动增大底层数组的大小。
Java泛型只是编译器提供的语法糖,所以这里的数组是一个Object数组,以便能够容纳任何类型的对象。

ArrayList自动扩容:
每当向数组中添加元素时,都要去检查添加后元素的个数是否会超出当前数组的长度,如果超出,数组将会进行扩容,以满足添加数据的需求。数组扩容通过ensureCapacity(int minCapacity)方法来实现。在实际添加大量元素前,我也可以使用ensureCapacity来手动增加ArrayList实例的容量,以减少递增式再分配的数量。
数组进行扩容时,会将老数组中的元素重新拷贝一份到新的数组中,每次数组容量的增长大约是其原容量的1.5倍。这种操作的代价是很高的,因此在实际使用时,我们应该尽量避免数组容量的扩张。当我们可预知要保存的元素的多少时,要在构造ArrayList实例时,就指定其容量,以避免数组扩容的发生。或者根据实际需求,通过调用ensureCapacity方法来手动增加ArrayList实例的容量。

  • 1.ArrayList创建对象时,若未指定集合大小初始化大小为0;若已指定大小,集合大小为指定的大小;
  • 2.当第一次调用add方法时,集合长度变为10和addAll内容之间的较大值;
  • 3.之后再调用add方法,先将集合扩大1.5倍,如果仍然不够,新长度为传入集合大小;

ArrayList和LinkedList的区别:

  1. 是否保证线程安全:  ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;
  2. 底层数据结构:  Arraylist 底层使用的是 Object 数组LinkedList 底层使用的是 双向链表 数据结构(JDK1.6 之前为循环链表,JDK1.7 取消了循环。注意双向链表和双向循环链表的区别,下面有介绍到!)
  3. 插入和删除是否受元素位置的影响:
    • ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。 比如:执行add(E e)方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是 O(1)。但是如果要在指定位置 i 插入和删除元素的话(add(int index, E element))时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。
    • LinkedList 采用链表存储,所以,如果是在头尾插入或者删除元素不受元素位置的影响(add(E e)addFirst(E e)addLast(E e)removeFirst() 、 removeLast()),近似 O(1),如果是要在指定位置 i 插入和删除元素的话(add(int index, E element)remove(Object o)) 时间复杂度近似为 O(n) ,因为需要先移动到指定位置再插入。
  4. 是否支持快速随机访问:  LinkedList 不支持高效的随机元素访问,而 ArrayList 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index)方法)。
  5. 内存空间占用:  ArrayList 的空 间浪费主要体现在在 list 列表的结尾会预留一定的容量空间,而 LinkedList 的空间花费则体现在它的每一个元素都需要消耗比 ArrayList 更多的空间(因为要存放直接后继和直接前驱以及数据)。

Map

Map有哪些类:

  • TreeMap 基于红黑树实现。
  • HashMap 1.7基于哈希表实现,1.8基于数组+链表+红黑树。
  • HashTable 和 HashMap 类似,但它是线程安全的,这意味着同一时刻多个线程可以同时写入 HashTable 并且不会导致数据不一致。它是遗留类,不应该去使用它。现在可以使用 ConcurrentHashMap 来支持线程安全,并且 ConcurrentHashMap 的效率会更高(1.7 ConcurrentHashMap 引入了分段锁, 1.8 引入了红黑树)。
  • LinkedHashMap 使用双向链表来维护元素的顺序,顺序为插入顺序或者最近最少使用(LRU)顺序。

HashMap和HashTable的区别:

  1. 线程是否安全:  HashMap 是非线程安全的,Hashtable 是线程安全的,因为 Hashtable 内部的方法基本都经过synchronized 修饰。(如果你要保证线程安全的话就使用 ConcurrentHashMap 吧!);
  2. 效率:  因为线程安全的问题,HashMap 要比 Hashtable 效率高一点。另外,Hashtable 基本被淘汰,不要在代码中使用它;
  3. 对 Null key 和 Null value 的支持:  HashMap 可以存储 null 的 key 和 value,但 null 作为键只能有一个,null 作为值可以有多个;Hashtable 不允许有 null 键和 null 值,否则会抛出 NullPointerException
  4. 初始容量大小和每次扩充容量大小的不同 :  ① 创建时如果不指定容量初始值,Hashtable 默认的初始大小为 11,之后每次扩充,容量变为原来的 2n+1。HashMap 默认的初始化大小为 16。之后每次扩充,容量变为原来的 2 倍。② 创建时如果给定了容量初始值,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为 2 的幂次方大小(HashMap 中的tableSizeFor()方法保证,下面给出了源代码)。也就是说 HashMap 总是使用 2 的幂作为哈希表的大小,后面会介绍到为什么是 2 的幂次方。
  5. 底层数据结构:  JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。Hashtable 没有这样的机制。

HashMap和TreeMap:

  1. HashMap和TreeMap都是线程不安全的。

  2. HashMap是通过hashcode()对其内容进行快速查找的;HashMap中的元素是没有顺序的。TreeMap中所有的元素都是有某一固定顺序的,如果需要得到一个有序的结果,就应该使用TreeMap。

  3. HashMap继承AbstractMap类,覆盖了hashcode() 和equals() 方法,以确保两个相等的映射返回相同的哈希值。TreeMap继承SortedMap类;他保持键的有序顺序。

  4. HashMap:适用于Map插入,删除,定位元素。TreeMap:适用于按自然顺序或自定义顺序遍历键(key);

  5. HashMap:基于hash表实现的;使用HashMap要求添加的键类明确定义了hashcode() 和equals() (可以重写该方法);为了优化HashMap的空间使用,可以调优初始容量和负载因子。TreeMap:基于红黑树实现的;TreeMap就没有调优选项,因为红黑树总是处于平衡的状态;

HashMap扩容:
当链表长度大于阈值(默认为 8)时,会首先调用 treeifyBin()方法。这个方法会根据 HashMap 数组来决定是否转换为红黑树。只有当数组长度大于或者等于 64 的情况下,才会执行转换红黑树操作,以减少搜索时间。否则,就是只是执行 resize() 方法对数组扩容。
HashMap的长度默认是16,当使用了0.75*length时候,开始扩容,为之前2倍。

HaspMap长度是2的幂次方?
image.png

HashSet重复性检查:
当你把对象加入HashSet时,HashSet 会先计算对象的hashcode值来判断对象加入的位置,同时也会与其他加入的对象的 hashcode 值作比较,如果没有相符的 hashcodeHashSet 会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用equals()方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让加入操作成功。 在openjdk8中,HashSetadd()方法只是简单的调用了HashMapput()方法,并且判断了一下返回值以确保是否有重复元素。

JWT

Json Web Token
JWT是一段被base64编码过的字符序列,用点号分隔,一共由三部分组成,头部header,消息体playload和签名sign。

  • 头部
        {
          "alg": "HS256",
          "typ": "JWT"
        }
    
    • alg可以指定加密算法。
  • 荷载
    {
      "sub": "1234567890",
      "name": "John Doe",
      "iat": 1516239022
    }
    
    • iss: 该JWT的签发者
    • sub: 该JWT所面向的用户
    • aud: 接收该JWT的一方
    • exp(expires): 什么时候过期,这里是一个Unix时间戳
    • iat(issued at): 在什么时候签发的
    • 可以自定义其他数据,这里可以加入任何内容,但是注意这些数据都是公开的,不可以加入敏感信息。
  • 签名 这便是JWT认证的安全核心。因为base64编码基本等于公开,也很容易被篡改,如何保证可信。那就在签名上面。签名是头部(header)、荷载(payload)与一个密钥产生的哈希值。纵然前面的数据很容易被篡改,可是如果不知道签名的密钥则很难保证签名正确。

Spring

Spring事务失效的原因

  1. 访问权限问题->方法不是Public
  2. 方法被final修饰
  3. 方法内部调用->一个事务方法调用了另一个事务方法
  4. 事务未被Spring管理
  5. 多线程调用
  6. 数据库不支持事务
  7. 未开启事务
  8. 自己处理了异常try...catch...
  9. 抛出异常错误
  10. 自定义了回滚事务
  11. 错误的传播特性
  12. 嵌套事务太多