diff --git a/README.md b/README.md
index 21ddb6a7..0493e185 100644
--- a/README.md
+++ b/README.md
@@ -4,9 +4,9 @@
|网络[:cloud:](#网络-cloud) |操作系统[:computer:](#操作系统-computer)| 算法[:pencil2:](#数据结构与算法-pencil2)| 面向对象[:couple:](#面向对象-couple) |数据库[:floppy_disk:](#数据库-floppy_disk)| Java [:coffee:](#java-coffee)| 分布式[:sweat_drops:](#分布式-sweat_drops)| 工具[:hammer:](#工具-hammer)| 编码实践[:speak_no_evil:](#编码实践-speak_no_evil)| 后记[:memo:](#后记-memo) |
-:loudspeaker: 本仓库的内容不涉及商业行为,不向读者收取任何费用。
+:loudspeaker: 本仓库不参与商业行为,不向读者收取任何费用。
-:loudspeaker: This repository is not involving commercial activities, and does not charge readers any fee.
+:loudspeaker: This repository is not engaging in business activities, and does not charge readers any fee.
## 网络 :cloud:
@@ -109,7 +109,8 @@ File, InputStream OutputStream, Reader Writer, Serializable, Socket, NIO
> [分布式问题分析](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/分布式问题分析.md)
-分布式事务、复杂均衡算法与实现、分布式锁、分布式 Session、分库分表的分布式困境与应对之策。
+分布式事务、负载均衡算法与实现、分布式锁、分布式 Session、分库分表的分布式困境与应对之策。
+
## 工具 :hammer:
diff --git a/notes/HTTP.md b/notes/HTTP.md
index 4dd80655..82c4a36a 100644
--- a/notes/HTTP.md
+++ b/notes/HTTP.md
@@ -14,6 +14,7 @@
* [CONNECT](#connect)
* [TRACE](#trace)
* [三、HTTP 状态码](#三http-状态码)
+ * [1XX 信息](#1xx-信息)
* [2XX 成功](#2xx-成功)
* [3XX 重定向](#3xx-重定向)
* [4XX 客户端错误](#4xx-客户端错误)
@@ -195,7 +196,7 @@ CONNECT www.example.com:443 HTTP/1.1
| 4XX | Client Error(客户端错误状态码) | 服务器无法处理请求 |
| 5XX | Server Error(服务器错误状态码) | 服务器处理请求出错 |
-### 1XX 信息
+## 1XX 信息
- **100 Continue** :表明到目前为止都很正常,客户端可以继续发送请求或者忽略这个响应。
@@ -516,7 +517,7 @@ HTTP 有以下安全性问题:
2. 不验证通信方的身份,通信方的身份有可能遭遇伪装;
3. 无法证明报文的完整性,报文有可能遭篡改。
-HTTPs 并不是新协议,而是 HTTP 先和 SSL(Secure Sockets Layer)通信,再由 SSL 和 TCP 通信。也就是说使用了隧道进行通信。
+HTTPs 并不是新协议,而是 HTTP 先和 SSL(Secure Sockets Layer)通信,再由 SSL 和 TCP 通信。也就是说 HTTPs 使用了隧道进行通信。
通过使用 SSL,HTTPs 具有了加密、认证和完整性保护。
@@ -526,21 +527,21 @@ HTTPs 并不是新协议,而是 HTTP 先和 SSL(Secure Sockets Layer)通
### 1. 对称密钥加密
-Symmetric-Key Encryption,加密的加密和解密使用同一密钥。
+对称密钥加密(Symmetric-Key Encryption),加密的加密和解密使用同一密钥。
- 优点:运算速度快;
- 缺点:密钥容易被获取。
-
+
### 2. 公开密钥加密
-Public-Key Encryption,使用一对密钥用于加密和解密,分别为公开密钥和私有密钥。公开密钥所有人都可以获得,通信发送方获得接收方的公开密钥之后,就可以使用公开密钥进行加密,接收方收到通信内容后使用私有密钥解密。
+公开密钥加密(Public-Key Encryption),也称为非对称密钥加密,使用一对密钥用于加密和解密,分别为公开密钥和私有密钥。公开密钥所有人都可以获得,通信发送方获得接收方的公开密钥之后,就可以使用公开密钥进行加密,接收方收到通信内容后使用私有密钥解密。
- 优点:更为安全;
- 缺点:运算速度慢;
-
+
### 3. HTTPs 采用的加密方式
@@ -584,13 +585,13 @@ SSL 提供报文摘要功能来验证完整性。
例如有一个论坛网站,攻击者可以在上面发表以下内容:
-```html
+```
```
之后该内容可能会被渲染成以下形式:
-```html
+```
```
@@ -659,7 +660,7 @@ HTTP 头中有一个 Referer 字段,这个字段用以标明请求来源于哪
(二)添加校验 Token
-由于 CSRF 的本质在于攻击者欺骗用户去访问自己设置的地址,所以如果要求在访问敏感数据请求时,要求用户浏览器提供不保存在 cookie 中,并且攻击者无法伪造的数据作为校验,那么攻击者就无法再执行 CSRF 攻击。这种数据通常是表单中的一个数据项。服务器将其生成并附加在表单中,其内容是一个伪乱数。当客户端通过表单提交请求时,这个伪乱数也一并提交上去以供校验。正常的访问时,客户端浏览器能够正确得到并传回这个伪乱数,而通过 CSRF 传来的欺骗性攻击中,攻击者无从事先得知这个伪乱数的值,服务器端就会因为校验 token 的值为空或者错误,拒绝这个可疑请求。
+由于 CSRF 的本质在于攻击者欺骗用户去访问自己设置的地址,所以如果要求在访问敏感数据请求时,要求用户浏览器提供不保存在 Cookie 中,并且攻击者无法伪造的数据作为校验,那么攻击者就无法再执行 CSRF 攻击。这种数据通常是表单中的一个数据项。服务器将其生成并附加在表单中,其内容是一个伪乱数。当客户端通过表单提交请求时,这个伪乱数也一并提交上去以供校验。正常的访问时,客户端浏览器能够正确得到并传回这个伪乱数,而通过 CSRF 传来的欺骗性攻击中,攻击者无从事先得知这个伪乱数的值,服务器端就会因为校验 Token 的值为空或者错误,拒绝这个可疑请求。
## SQL 注入攻击
@@ -715,9 +716,9 @@ strSQL = "SELECT * FROM users;"
### 1. 概念
-(denial-of-service attack,DoS),亦称洪水攻击,其目的在于使目标电脑的网络或系统资源耗尽,使服务暂时中断或停止,导致其正常用户无法访问。
+拒绝服务攻击(denial-of-service attack,DoS),亦称洪水攻击,其目的在于使目标电脑的网络或系统资源耗尽,使服务暂时中断或停止,导致其正常用户无法访问。
-(distributed denial-of-service attack,DDoS),指攻击者使用网络上两个或以上被攻陷的电脑作为“僵尸”向特定的目标发动“拒绝服务”式攻击。
+分布式拒绝服务攻击(distributed denial-of-service attack,DDoS),指攻击者使用网络上两个或以上被攻陷的电脑作为“僵尸”向特定的目标发动“拒绝服务”式攻击。
> [维基百科:拒绝服务攻击](https://zh.wikipedia.org/wiki/%E9%98%BB%E6%96%B7%E6%9C%8D%E5%8B%99%E6%94%BB%E6%93%8A)
@@ -725,7 +726,7 @@ strSQL = "SELECT * FROM users;"
## 参数
-GET 和 POST 的请求都能使用额外的参数,但是 GET 的参数是以查询字符串出现在 URL 中,而 POST 的参数存储在内容实体。
+GET 和 POST 的请求都能使用额外的参数,但是 GET 的参数是以查询字符串出现在 URL 中,而 POST 的参数存储在内容实体中。
GET 的传参方式相比于 POST 安全性较差,因为 GET 传的参数在 URL 中是可见的,可能会泄露私密信息。并且 GET 只支持 ASCII 字符,如果参数为中文则可能会出现乱码,而 POST 支持标准字符集。
@@ -743,7 +744,7 @@ name1=value1&name2=value2
安全的 HTTP 方法不会改变服务器状态,也就是说它只是可读的。
-GET 方法是安全的,而 POST 却不是。
+GET 方法是安全的,而 POST 却不是,因为 POST 的目的是传送实体主体内容,这个内容可能是用户上传的表单数据,上传成功之后,服务器可能把这个数据存储到数据库中,因此状态也就发生了改变。
安全的方法除了 GET 之外还有:HEAD、OPTIONS。
@@ -751,9 +752,9 @@ GET 方法是安全的,而 POST 却不是。
## 幂等性
-幂等的 HTTP 方法,同样的请求被执行一次与连续执行多次的效果是一样的,服务器的状态也是一样的。换句话说就是,幂等方法不应该具有副作用(统计用途除外)。在正确实现的条件下,GET,HEAD,PUT和 DELETE 等方法都是幂等的,而 POST 方法不是。所有的安全方法也都是幂等的。
+幂等的 HTTP 方法,同样的请求被执行一次与连续执行多次的效果是一样的,服务器的状态也是一样的。换句话说就是,幂等方法不应该具有副作用(统计用途除外)。在正确实现的条件下,GET,HEAD,PUT 和 DELETE 等方法都是幂等的,而 POST 方法不是。所有的安全方法也都是幂等的。
-GET /pageX HTTP/1.1是幂等的。连续调用多次,客户端接收到的结果都是一样的:
+GET /pageX HTTP/1.1 是幂等的。连续调用多次,客户端接收到的结果都是一样的:
```
GET /pageX HTTP/1.1
@@ -762,7 +763,7 @@ GET /pageX HTTP/1.1
GET /pageX HTTP/1.1
```
-POST /add_row HTTP/1.1不是幂等的。如果调用多次,就会增加多行记录:
+POST /add_row HTTP/1.1 不是幂等的。如果调用多次,就会增加多行记录:
```
POST /add_row HTTP/1.1
@@ -770,7 +771,7 @@ POST /add_row HTTP/1.1 -> Adds a 2nd row
POST /add_row HTTP/1.1 -> Adds a 3rd row
```
-DELETE /idX/delete HTTP/1.1是幂等的,即便是不同请求之间接收到的状态码不一样:
+DELETE /idX/delete HTTP/1.1 是幂等的,即便是不同请求之间接收到的状态码不一样:
```
DELETE /idX/delete HTTP/1.1 -> Returns 200 if idX exists
@@ -782,8 +783,8 @@ DELETE /idX/delete HTTP/1.1 -> Returns 404
如果要对响应进行缓存,需要满足以下条件:
-1. 请求报文的 HTTP 方法本身是可缓存的,包括 GET 和 HEAD,PUT 和 DELETE 不可缓存,POST 在多数情况下不可缓存的。
-2. 响应报文的 状态码是可缓存的,包括: 200, 203, 204, 206, 300, 301, 404, 405, 410, 414, and 501。
+1. 请求报文的 HTTP 方法本身是可缓存的,包括 GET 和 HEAD,但是 PUT 和 DELETE 不可缓存,POST 在多数情况下不可缓存的。
+2. 响应报文的状态码是可缓存的,包括:200, 203, 204, 206, 300, 301, 404, 405, 410, 414, and 501。
3. 响应报文的 Cache-Control 首部字段没有指定不进行缓存。
## XMLHttpRequest
@@ -798,12 +799,12 @@ DELETE /idX/delete HTTP/1.1 -> Returns 404
## HTTP/1.0 与 HTTP/1.1 的区别
-1. 持久连接
-2. 管线化处理
-3. 虚拟主机
-4. 状态码 100
-5. 分块传输编码
-6. 缓存处理字段
+1. HTTP/1.1 默认是持久连接
+2. HTTP/1.1 支持管线化处理
+3. HTTP/1.1 支持虚拟主机
+4. HTTP/1.1 新增状态码 100
+5. HTTP/1.1 只是分块传输编码
+6. HTTP/1.1 新增缓存处理指令 max-age
具体内容见上文
@@ -846,3 +847,4 @@ HTTP/1.1 的解析是基于文本的,而 HTTP/2.0 采用二进制格式。
- [What is the difference between a URI, a URL and a URN?](https://stackoverflow.com/questions/176264/what-is-the-difference-between-a-uri-a-url-and-a-urn)
- [XMLHttpRequest](https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest)
- [XMLHttpRequest (XHR) Uses Multiple Packets for HTTP POST?](https://blog.josephscott.org/2009/08/27/xmlhttprequest-xhr-uses-multiple-packets-for-http-post/)
+- [Symmetric vs. Asymmetric Encryption – What are differences?](https://www.ssl2buy.com/wiki/symmetric-vs-asymmetric-encryption-what-are-differences)
diff --git a/notes/Java IO.md b/notes/Java IO.md
index 1c1e4e1b..b48c90c3 100644
--- a/notes/Java IO.md
+++ b/notes/Java IO.md
@@ -156,7 +156,7 @@ I/O 与 NIO 最重要的区别是数据打包和传输的方式,I/O 以流的
面向流的 I/O 一次处理一个字节数据,一个输入流产生一个字节数据,一个输出流消费一个字节数据。为流式数据创建过滤器非常容易,链接几个过滤器,以便每个过滤器只负责单个复杂处理机制的一部分,这样也是相对简单的。不利的一面是,面向流的 I/O 通常相当慢。
-一个面向块的 I/O 系统以块的形式处理数据,一次处理数据块。按块处理数据比按流处理数据要快得多。但是面向块的 I/O 缺少一些面向流的 I/O 所具有的优雅性和简单性。
+一个面向块的 I/O 系统以块的形式处理数据,一次处理一个数据块。按块处理数据比按流处理数据要快得多。但是面向块的 I/O 缺少一些面向流的 I/O 所具有的优雅性和简单性。
I/O 包和 NIO 已经很好地集成了,java.io.\* 已经以 NIO 为基础重新实现了,所以现在它可以利用 NIO 的一些特性。例如,java.io.\* 包中的一些类包含以块的形式读写数据的方法,这使得即使在面向流的系统中,处理速度也会更快。
@@ -177,7 +177,7 @@ I/O 包和 NIO 已经很好地集成了,java.io.\* 已经以 NIO 为基础重
### 2. 缓冲区
-发送给一个通道的所有对象都必须首先放到缓冲区中,同样地,从通道中读取的任何数据都要读到缓冲区中。也就是说,不会直接对通道进行读写数据,而是要先经过缓冲区。
+发送给一个通道的所有数据都必须首先放到缓冲区中,同样地,从通道中读取的任何数据都要读到缓冲区中。也就是说,不会直接对通道进行读写数据,而是要先经过缓冲区。
缓冲区实质上是一个数组,但它不仅仅是一个数组。缓冲区提供了对数据的结构化访问,而且还可以跟踪系统的读/写进程。
@@ -199,7 +199,7 @@ I/O 包和 NIO 已经很好地集成了,java.io.\* 已经以 NIO 为基础重
状态变量的改变过程举例:
-① 新建一个大小为 8 个字节的缓冲区,此时 position 为 0,而 limit = capacity = 9。capacity 变量不会改变,下面的讨论会忽略它。
+① 新建一个大小为 8 个字节的缓冲区,此时 position 为 0,而 limit = capacity = 8。capacity 变量不会改变,下面的讨论会忽略它。
diff --git a/notes/Java 基础.md b/notes/Java 基础.md
index 6cc8a936..2a4e0221 100644
--- a/notes/Java 基础.md
+++ b/notes/Java 基础.md
@@ -203,8 +203,6 @@ java.lang.CloneNotSupportedException: CloneTest
**2. 深拷贝与浅拷贝**
-
-
- 浅拷贝:拷贝对象和原对象的引用类型引用同一个对象;
- 深拷贝:引用不同对象。
@@ -535,14 +533,12 @@ Reflection is powerful, but should not be used indiscriminately. If it is possib
# 八、异常
-Throwable 可以用来表示任何可以作为异常抛出的类,分为两种: **Error** 和 **Exception**,其中 Error 用来表示 JVM 无法处理的错误(比如 java.lang.OutOfMemoryError)。
-
-Exception 分为两种:
+Throwable 可以用来表示任何可以作为异常抛出的类,分为两种: **Error** 和 **Exception**。其中 Error 用来表示 JVM 无法处理的错误,Exception 分为两种:
1. **受检异常** :需要用 try...catch... 语句捕获并进行处理,并且可以从异常中恢复;
2. **非受检异常** :是程序运行时错误,例如除 0 会引发 Arithmetic Exception,此时程序奔溃并且无法恢复。
-
+
> - [Java 入门之异常处理](https://www.tianmaying.com/tutorial/Java-Exception)
> - [Java 异常的面试问题及答案 -Part 1](http://www.importnew.com/7383.html)
@@ -558,8 +554,7 @@ public class Box {
}
```
-> - [Java 泛型详解](https://www.ziwenxie.site/2017/03/01/java-generic/)
-> - [10 道 Java 泛型面试题](https://cloud.tencent.com/developer/article/1033693)
+> [Java 泛型详解](https://www.ziwenxie.site/2017/03/01/java-generic/)[10 道 Java 泛型面试题](https://cloud.tencent.com/developer/article/1033693)
# 十、注解
@@ -598,8 +593,7 @@ Java 注解是附加在代码中的一些元信息,用于一些工具在编译
7. Binary Literals, Underscore in literals
8. Diamond Syntax
-> - [Difference between Java 1.8 and Java 1.7?](http://www.selfgrowth.com/articles/difference-between-java-18-and-java-17)
-> - [Java 8 特性 ](http://www.importnew.com/19345.html)
+> [Difference between Java 1.8 and Java 1.7?](http://www.selfgrowth.com/articles/difference-between-java-18-and-java-17) [Java 8 特性 ](http://www.importnew.com/19345.html)
## Java 与 C++ 的区别
diff --git a/notes/Java 容器.md b/notes/Java 容器.md
index 2a05225b..82e4e0e6 100644
--- a/notes/Java 容器.md
+++ b/notes/Java 容器.md
@@ -31,7 +31,7 @@
- HashSet:基于哈希实现,支持快速查找,但不支持有序性操作,例如根据一个范围查找元素的操作。并且失去了元素的插入顺序信息,也就是说使用 Iterator 遍历 HashSet 得到的结果是不确定的。
-- TreeSet:基于红黑树实现,支持有序性操作,但是查找效率不如 HashSet,HashSet 查找时间复杂度为 O(1),TreeSet 则为 O(logn);
+- TreeSet:基于红黑树实现,支持有序性操作,但是查找效率不如 HashSet,HashSet 查找时间复杂度为 O(1),TreeSet 则为 O(logN);
- LinkedHashSet:具有 HashSet 的查找效率,且内部使用链表维护元素的插入顺序。
@@ -47,7 +47,7 @@
- LinkedList:可以用它来支持双向队列;
-- PriorityQueue 是基于堆结构实现,可以用它来实现优先级队列。
+- PriorityQueue:基于堆结构实现,可以用它来实现优先级队列。
## Map
@@ -80,8 +80,6 @@ for (String item : list) {
}
```
-> [迭代器模式](https://github.com/CyC2018/Interview-Notebook/blob/master/notes/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md#%E5%8D%81%E4%BA%8C%E8%BF%AD%E4%BB%A3%E5%99%A8%E6%A8%A1%E5%BC%8F)
-
## 适配器模式
java.util.Arrays#asList() 可以把数组类型转换为 List 类型。
@@ -91,7 +89,7 @@ java.util.Arrays#asList() 可以把数组类型转换为 List 类型。
public static List asList(T... a)
```
-如果要将数组类型转换为 List 类型,应该注意的是参数列表为泛型的变长参数,因此不能使用基本类型数组作为参数,只能使用相应的包装类型数组。
+如果要将数组类型转换为 List 类型,应该注意的是 asList() 的参数为泛型的变长参数,因此不能使用基本类型数组作为参数,只能使用相应的包装类型数组。
```java
Integer[] arr = {1, 2, 3};
@@ -104,8 +102,6 @@ List list = Arrays.asList(arr);
List list = Arrays.asList(1,2,3);
```
-> [适配器模式](https://github.com/CyC2018/Interview-Notebook/blob/master/notes/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md#%E5%8D%81%E9%80%82%E9%85%8D%E5%99%A8%E6%A8%A1%E5%BC%8F)
-
# 三、散列
hasCode() 返回散列值,使用的是对象的地址。
@@ -171,7 +167,7 @@ public class ArrayList extends AbstractList
implements List, RandomAccess, Cloneable, java.io.Serializable
```
-基于数组实现,保存元素的数组使用 transient 修饰,该关键字声明数组默认不会被序列化。这是 ArrayList 具有动态扩容特性,因此保存元素的数组不一定都会被使用,那么就没必要全部进行序列化。ArrayList 重写了 writeObject() 和 readObject() 来控制只序列化数组中有元素填充那部分内容。
+基于数组实现,保存元素的数组使用 transient 修饰,该关键字声明数组默认不会被序列化。ArrayList 具有动态扩容特性,因此保存元素的数组不一定都会被使用,那么就没必要全部进行序列化。ArrayList 重写了 writeObject() 和 readObject() 来控制只序列化数组中有元素填充那部分内容。
```java
transient Object[] elementData; // non-private to simplify nested class access
@@ -291,7 +287,7 @@ transient Entry[] table;
其中,Entry 就是存储数据的键值对,它包含了四个字段。从 next 字段我们可以看出 Entry 是一个链表,即每个桶会存放一个链表。
-
+
JDK 1.8 使用 Node 类型存储一个键值对,它依然继承自 Entry,因此可以按照上面的存储结构来理解。
@@ -342,32 +338,35 @@ static class Node implements Map.Entry {
### 2. 拉链法的工作原理
```java
-HashMap map = new HashMap<>(); // 默认大小为 16
-map.put("sachin", 30);
-map.put("vishal", 20);
-map.put("vaibhav", 20);
+HashMap map = new HashMap<>();
+map.put("K1", "V1");
+map.put("K2", "V2");
+map.put("K3", "V3");
```
-- 计算 "sachin" 的 hashcode 为 115,使用除留余数法得到 115 % 16 = 3,因此 ("sachin", 30) 键值对放到第 3 个桶上。
-- 同样得到 ("vishal", 20) 和 ("vaibhav", 20) 都应该放到第 6 个桶上。("vishal", 20) 先放入, ("vaibhav", 20) 链接到 ("vishal", 20) 之后。
+- 新建一个 HashMap,默认大小为 16;
+- 插入 <K1,V1> 键值对,先计算 K1 的 hashCode 为 115,使用除留余数法得到所在的桶下标 115%16=3。
+- 插入 <K2,V2> 键值对,先计算 K2 的 hashCode 为 118,使用除留余数法得到所在的桶下标 118%16=6。
+- 插入 <K3,V3> 键值对,先计算 K3 的 hashCode 为 118,使用除留余数法得到所在的桶下标 118%16=6,它需要插在 <K2,V2> 之前。
-
+
-当进行查找时,需要分成两步进行,第一步是先根据 hashcode 计算出所在的桶,第二步是在链表上顺序查找。由于 table 是数组形式的,具有随机读取的特性,因此第一步的时间复杂度为 O(1),而第二步需要在链表上顺序查找,时间复杂度显然和链表的长度成正比。
+查找需要分成两步进行:
+
+- 计算键值对所在的桶;
+- 在链表上顺序查找,时间复杂度显然和链表的长度成正比。
### 3. 链表转红黑树
应该注意到,从 JDK 1.8 开始,一个桶存储的链表长度大于 8 时会将链表转换为红黑树。
-
-
### 4. 扩容
因为从 JDK 1.8 开始引入了红黑树,因此扩容操作较为复杂,为了便于理解,以下内容使用 JDK 1.7 的内容。
设 HashMap 的 table 长度为 M,需要存储的键值对数量为 N,如果哈希函数满足均匀性的要求,那么每条链表的长度大约为 N/M,因此平均查找次数的数量级为 O(N/M)。
-为了让查找的成本降低,应该尽可能使得 N/M 尽可能小,因此需要保证 M 尽可能大,可就是说 table 要尽可能大。HashMap 采用动态扩容来根据当前的 N 值来调整 M 值,使得空间效率和时间效率都能得到保证。
+为了让查找的成本降低,应该尽可能使得 N/M 尽可能小,因此需要保证 M 尽可能大,也就是说 table 要尽可能大。HashMap 采用动态扩容来根据当前的 N 值来调整 M 值,使得空间效率和时间效率都能得到保证。
和扩容相关的参数主要有:capacity、size、threshold 和 load_factor。
@@ -445,11 +444,11 @@ void transfer(Entry[] newTable) {
### 5. 确定桶下标
-需要三步操作:计算 Key 的 hashCode、高位运算、除留余数法取模。
+很多操作都需要先确定一个键值对所在的桶下标,需要分三步进行。
-
+(一)hashCode()
-**(一)hashcode()**
+调用 Key 的 hashCode() 方法得到 hashCode。
```java
public final int hashCode() {
@@ -457,9 +456,9 @@ public final int hashCode() {
}
```
-**(二)高位运算**
+(二)高位运算
-通过 hashCode() 的高 16 位异或低 16 位,使得数组比较小时,也能保证高低位都参与到了哈希计算中。
+将 hashCode 的高 16 位和低 16 位进行异或操作,使得在数组比较小时,也能保证高低位都参与到了哈希计算中。
```java
static final int hash(Object key) {
@@ -468,7 +467,7 @@ static final int hash(Object key) {
}
```
-**(三)除留余数**
+(三)除留余数法
令 x = 1<<4,即 x 为 2 的 4 次方,它具有以下性质:
@@ -520,11 +519,11 @@ new capacity : 00100000
### 7. 扩容-计算数组容量
-先考虑如何求一个数的补码,对于 10100000,它的补码为 11111111,可以使用以下方法得到:
+先考虑如何求一个数的补码,对于 10010000,它的掩码为 11111111,可以使用以下方法得到:
```
-mask |= mask >> 1 11000000
-mask |= mask >> 2 11110000
+mask |= mask >> 1 11011000
+mask |= mask >> 2 11111100
mask |= mask >> 4 11111111
```
@@ -738,13 +737,8 @@ ConcurrentHashMap 的高并发性主要来自于三个方面:
[ConcurrentHashMap.java](https://github.com/CyC2018/JDK-Source-Code/blob/master/src/ConcurrentHashMap.java)
-
-
-
JDK 1.7 分段锁机制来实现并发更新操作,核心类为 Segment,它继承自重入锁 ReentrantLock。
-
-
JDK 1.8 的实现不是用了 Segment,Segment 属于重入锁 ReentrantLock。而是使用了内置锁 synchronized,主要是出于以下考虑:
1. synchronized 的锁粒度更低;
diff --git a/notes/Java 虚拟机.md b/notes/Java 虚拟机.md
index 81c5dff0..2cf8718b 100644
--- a/notes/Java 虚拟机.md
+++ b/notes/Java 虚拟机.md
@@ -1,9 +1,9 @@
* [一、运行时数据区域](#一运行时数据区域)
* [程序计数器](#程序计数器)
- * [Java 虚拟机栈](#java-虚拟机栈)
+ * [虚拟机栈](#虚拟机栈)
* [本地方法栈](#本地方法栈)
- * [Java 堆](#java-堆)
+ * [堆](#堆)
* [方法区](#方法区)
* [运行时常量池](#运行时常量池)
* [直接内存](#直接内存)
@@ -27,17 +27,17 @@
# 一、运行时数据区域
-
+
## 程序计数器
记录正在执行的虚拟机字节码指令的地址(如果正在执行的是本地方法则为空)。
-## Java 虚拟机栈
+## 虚拟机栈
-每个 Java 方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在 Java 虚拟机栈中入栈和出栈的过程。
+每个 Java 方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、常量池引用等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在 Java 虚拟机栈中入栈和出栈的过程。
-
+
可以通过 -Xss 这个虚拟机参数来指定一个程序的 Java 虚拟机栈内存大小:
@@ -56,13 +56,13 @@ java -Xss=512M HackTheJava
与 Java 虚拟机栈类似,它们之间的区别只不过是本地方法栈为本地方法服务。
-
+
-## Java 堆
+## 堆
所有对象实例都在这里分配内存。
-是垃圾收集的主要区域("GC 堆 "),现代的垃圾收集器基本都是采用分代收集算法,该算法的思想是针对不同的对象采取不同的垃圾回收算法,因此虚拟机把 Java 堆分成以下三块:
+是垃圾收集的主要区域("GC 堆"),现代的垃圾收集器基本都是采用分代收集算法,该算法的思想是针对不同的对象采取不同的垃圾回收算法,因此虚拟机把 Java 堆分成以下三块:
- 新生代(Young Generation)
- 老年代(Old Generation)
@@ -102,7 +102,7 @@ Class 文件中的常量池(编译器生成的各种字面量和符号引用
## 直接内存
-在 JDK 1.4 中新加入了 NIO 类,引入了一种基于通道(Channel)与缓冲区(Buffer)的 I/O 方式,它可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆里的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在 Java 堆和 Native 堆中来回复制数据。
+在 JDK 1.4 中新加入了 NIO 类,它可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆里的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在 Java 堆和 Native 堆中来回复制数据。
# 二、垃圾收集
@@ -221,11 +221,9 @@ finalize() 类似 C++ 的析构函数,用来做关闭外部资源等工作。
不足:
-1. 标记和清除过程效率都不高
+1. 标记和清除过程效率都不高;
2. 会产生大量碎片,内存碎片过多可能导致无法给大对象分配内存。
-之后的算法都是基于该算法进行改进。
-
### 2. 复制
@@ -394,7 +392,7 @@ JVM 为对象定义年龄计数器,经过 Minor GC 依然存活,并且能被
### 4. 动态对象年龄判定
-JVM 并不是永远地要求对象的年龄必须达到 MaxTenuringThreshold 才能晋升老年代,如果在 Survivor 区中相同年龄所有对象大小的总和大于 Survivor 空间的一半,则年龄大于或等于该年龄的对象可以直接进入老年代,无序等待 MaxTenuringThreshold 中要求的年龄。
+JVM 并不是永远地要求对象的年龄必须达到 MaxTenuringThreshold 才能晋升老年代,如果在 Survivor 区中相同年龄所有对象大小的总和大于 Survivor 空间的一半,则年龄大于或等于该年龄的对象可以直接进入老年代,无需等待 MaxTenuringThreshold 中要求的年龄。
### 5. 空间分配担保
@@ -418,7 +416,7 @@ JVM 并不是永远地要求对象的年龄必须达到 MaxTenuringThreshold 才
### 4. JDK 1.7 及以前的永久代空间不足
-在 JDK 1.7 及以前,HotSpot 虚拟机中的方法区是用永久代实现的,永久代中存放的为一些 class 的信息、常量、静态变量等数据,当系统中要加载的类、反射的类和调用的方法较多时,永久代可能会被占满,在未配置为采用 CMS GC 的情况下也会执行 Full GC。如果经过 Full GC 仍然回收不了,那么 JVM 会抛出 java.lang.OutOfMemoryError,为避免以上原因引起的 Full GC,可采用的方法为增大永久代空间或转为使用 CMS GC。
+在 JDK 1.7 及以前,HotSpot 虚拟机中的方法区是用永久代实现的,永久代中存放的为一些 Class 的信息、常量、静态变量等数据,当系统中要加载的类、反射的类和调用的方法较多时,永久代可能会被占满,在未配置为采用 CMS GC 的情况下也会执行 Full GC。如果经过 Full GC 仍然回收不了,那么 JVM 会抛出 java.lang.OutOfMemoryError,为避免以上原因引起的 Full GC,可采用的方法为增大永久代空间或转为使用 CMS GC。
### 5. Concurrent Mode Failure
@@ -448,7 +446,7 @@ JVM 并不是永远地要求对象的年龄必须达到 MaxTenuringThreshold 才
虚拟机规范中并没有强制约束何时进行加载,但是规范严格规定了有且只有下列五种情况必须对类进行初始化(加载、验证、准备都会随着发生):
-1. 遇到 new、getstatic、putstatic、invokestatic 这四条字节码指令时,如果类没有进行过初始化,则必须先触发其初始化。最常见的生成这 4 条指令的场景是:使用 new 关键字实例化对象的时候;读取或设置一个类的静态字段(被 final 修饰、已在编译器把结果放入常量池的静态字段除外)的时候;以及调用一个类的静态方法的时候。
+1. 遇到 new、getstatic、putstatic、invokestatic 这四条字节码指令时,如果类没有进行过初始化,则必须先触发其初始化。最常见的生成这 4 条指令的场景是:使用 new 关键字实例化对象的时候;读取或设置一个类的静态字段(被 final 修饰、已在编译期把结果放入常量池的静态字段除外)的时候;以及调用一个类的静态方法的时候。
2. 使用 java.lang.reflect 包的方法对类进行反射调用的时候,如果类没有进行初始化,则需要先触发其初始化。
@@ -507,19 +505,19 @@ System.out.println(ConstClass.HELLOWORLD);
主要有以下 4 个阶段:
-**(一)文件格式验证**
+(一)文件格式验证
验证字节流是否符合 Class 文件格式的规范,并且能被当前版本的虚拟机处理。
-**(二)元数据验证**
+(二)元数据验证
对字节码描述的信息进行语义分析,以保证其描述的信息符合 Java 语言规范的要求。
-**(三)字节码验证**
+(三)字节码验证
通过数据流和控制流分析,确保程序语义是合法、符合逻辑的。
-**(四)符号引用验证**
+(四)符号引用验证
发生在虚拟机将符号引用转换为直接引用的时候,对类自身以外(常量池中的各种符号引用)的信息进行匹配性校验。
@@ -610,11 +608,11 @@ public static void main(String[] args) {
从 Java 开发人员的角度看,类加载器可以划分得更细致一些:
-- 启动类加载器(Bootstrap ClassLoader) 此类加载器负责将存放在 <JAVA_HOME>\lib 目录中的,或者被 -Xbootclasspath 参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如 rt.jar,名字不符合的类库即使放在 lib 目录中也不会被加载)类库加载到虚拟机内存中。 启动类加载器无法被 Java 程序直接引用,用户在编写自定义类加载器时,如果需要把加载请求委派给启动类加载器,直接使用 null 代替即可。
+- 启动类加载器(Bootstrap ClassLoader)此类加载器负责将存放在 <JAVA_HOME>\lib 目录中的,或者被 -Xbootclasspath 参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如 rt.jar,名字不符合的类库即使放在 lib 目录中也不会被加载)类库加载到虚拟机内存中。 启动类加载器无法被 Java 程序直接引用,用户在编写自定义类加载器时,如果需要把加载请求委派给启动类加载器,直接使用 null 代替即可。
-- 扩展类加载器(Extension ClassLoader) 这个类加载器是由 ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现的。它负责将 <JAVA_HOME>/lib/ext 或者被 java.ext.dir 系统变量所指定路径中的所有类库加载到内存中,开发者可以直接使用扩展类加载器。
+- 扩展类加载器(Extension ClassLoader)这个类加载器是由 ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现的。它负责将 <JAVA_HOME>/lib/ext 或者被 java.ext.dir 系统变量所指定路径中的所有类库加载到内存中,开发者可以直接使用扩展类加载器。
-- 应用程序类加载器(Application ClassLoader) 这个类加载器是由 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。由于这个类加载器是 ClassLoader 中的 getSystemClassLoader() 方法的返回值,因此一般称为系统类加载器。它负责加载用户类路径(ClassPath)上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
+- 应用程序类加载器(Application ClassLoader)这个类加载器是由 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。由于这个类加载器是 ClassLoader 中的 getSystemClassLoader() 方法的返回值,因此一般称为系统类加载器。它负责加载用户类路径(ClassPath)上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
### 3. 双亲委派模型
@@ -634,7 +632,7 @@ public static void main(String[] args) {
```java
protected synchronized Class> loadClass(String name, boolean resolve) throws ClassNotFoundException{
- //check the class has been loaded or not
+ // 先检查请求的类是否已经被加载过了
Class c = findLoadedClass(name);
if(c == null) {
try{
@@ -644,9 +642,10 @@ protected synchronized Class> loadClass(String name, boolean resolve) throws C
c = findBootstrapClassOrNull(name);
}
} catch(ClassNotFoundException e) {
- //if throws the exception , the father can not complete the load
+ // 如果父类加载器抛出 ClassNotFoundException,说明父类加载器无法完成加载请求
}
if(c == null) {
+ // 如果父类加载器无法完成加载请求,再调用自身的 findClass() 来进行加载
c = findClass(name);
}
}
diff --git a/notes/Leetcode 题解.md b/notes/Leetcode 题解.md
index 6be5727b..9e49dff5 100644
--- a/notes/Leetcode 题解.md
+++ b/notes/Leetcode 题解.md
@@ -15,7 +15,7 @@
* [动态规划](#动态规划)
* [斐波那契数列](#斐波那契数列)
* [最长递增子序列](#最长递增子序列)
- * [最长公共子系列](#最长公共子系列)
+ * [最长公共子序列](#最长公共子序列)
* [0-1 背包](#0-1-背包)
* [数组区间](#数组区间)
* [字符串编辑](#字符串编辑)
@@ -1960,7 +1960,7 @@ public int wiggleMaxLength(int[] nums) {
}
```
-### 最长公共子系列
+### 最长公共子序列
对于两个子序列 S1 和 S2,找出它们最长的公共子序列。
@@ -1970,7 +1970,7 @@ public int wiggleMaxLength(int[] nums) {
② 当 S1i != S2j 时,此时最长公共子序列为 S1 的前 i-1 个字符和 S2 的前 j 个字符最长公共子序列,与 S1 的前 i 个字符和 S2 的前 j-1 个字符最长公共子序列,它们的最大者,即 dp[i][j] = max{ dp[i-1][j], dp[i][j-1] }。
-综上,最长公共子系列的状态转移方程为:
+综上,最长公共子序列的状态转移方程为:
diff --git a/notes/MySQL.md b/notes/MySQL.md
index 3588d9f9..99fc6797 100644
--- a/notes/MySQL.md
+++ b/notes/MySQL.md
@@ -216,14 +216,14 @@ customer_id_selectivity: 0.0373
聚簇索引并不是一种索引类型,而是一种数据存储方式。
-术语“聚簇”表示数据行和相邻的键值紧密地存储在一起,InnoDB 的聚簇索引的数据行存放在 B-Tree 的叶子页中。
+术语“聚簇”表示数据行和相邻的键值紧密地存储在一起,InnoDB 的聚簇索引的数据行存放在 B+Tree 的叶子页中。
因为无法把数据行存放在两个不同的地方,所以一个表只能有一个聚簇索引。
**优点**
1. 可以把相关数据保存在一起,减少 I/O 操作;
-2. 因为数据保存在 B-Tree 中,因此数据访问更快。
+2. 因为数据保存在 B+Tree 中,因此数据访问更快。
**缺点**
diff --git a/notes/Redis.md b/notes/Redis.md
index dc15edc1..08b1f54f 100644
--- a/notes/Redis.md
+++ b/notes/Redis.md
@@ -212,7 +212,14 @@ Redis 可以为每个键设置过期时间,当键过期时,会自动删除
# 四、发布与订阅
-发布与订阅实际上是观察者模式,订阅者订阅了频道之后,发布者向频道发送字符串消息会被所有订阅者接收到。
+订阅者订阅了频道之后,发布者向频道发送字符串消息会被所有订阅者接收到。
+
+发布与订阅模式和观察者模式有以下不同:
+
+- 观察者模式中,观察者和主题都知道对方的存在;而在发布与订阅模式中,发布者与订阅者不知道对方的存在,它们之间通过频道进行通信。
+- 观察者模式是同步的,当事件触发时,主题会去调度观察者的方法;而发布与订阅模式是异步的;
+
+
发布与订阅有一些问题,很少使用它,而是使用替代的解决方案。问题如下:
@@ -241,7 +248,7 @@ Redis 是内存型数据库,为了保证数据在断电后不会丢失,需
## 2. AOF 持久化
-AOF 持久化将写命令添加到 AOF 文件(Append Only File)的末尾。
+将写命令添加到 AOF 文件(Append Only File)的末尾。
对硬盘的文件进行写入时,写入的内容首先会被存储到缓冲区,然后由操作系统决定什么时候将该内容同步到硬盘,用户可以调用 file.flush() 方法请求操作系统尽快将缓冲区存储的数据同步到硬盘。
@@ -365,7 +372,7 @@ def main():
从事件处理的角度来看,服务器运行流程如下:
-
+
# 十一、Redis 与 Memcached 的区别
@@ -424,7 +431,7 @@ Redis 这种内存数据库能支持计数器频繁的读写操作。
如果使用 Redis 来缓存数据时,要保证所有数据都是热点数据,可以将内存最大使用量设置为热点数据占用的内存量,然后启用 allkeys-lru 淘汰策略,将最近最少使用的数据淘汰。
-作为内存数据库,出于对性能和内存消耗的考虑,Redis的淘汰算法(LRU、TTL)实际实现上并非针对所有key,而是抽样一小部分key从中选出被淘汰key。抽样数量可通过`maxmemory-samples`配置。
+作为内存数据库,出于对性能和内存消耗的考虑,Redis 的淘汰算法(LRU、TTL)实际实现上并非针对所有 key,而是抽样一小部分 key 从中选出被淘汰 key。抽样数量可通过 maxmemory-samples 配置。
# 十四、一个简单的论坛系统分析
@@ -464,3 +471,4 @@ Redis 没有关系型数据库中的表这一概念来将同类型的数据存
- [论述 Redis 和 Memcached 的差异](http://www.cnblogs.com/loveincode/p/7411911.html)
- [Redis 3.0 中文版- 分片](http://wiki.jikexueyuan.com/project/redis-guide)
- [Redis 应用场景](http://www.scienjus.com/redis-use-case/)
+- [Observer vs Pub-Sub](http://developers-club.com/posts/270339/)
diff --git a/notes/分布式问题分析.md b/notes/分布式问题分析.md
index 7bf1d686..d88966f3 100644
--- a/notes/分布式问题分析.md
+++ b/notes/分布式问题分析.md
@@ -127,6 +127,7 @@
+
### 6.源地址哈希法(ip hash)
源地址哈希通过对客户端IP哈希计算得到的一个数值,用该数值对服务器数量进行取模运算,取模结果便是目标服务器的序号。
- 优点:保证同一IP的客户端都会被hash到同一台服务器上。
@@ -134,6 +135,7 @@
+
## 实现
### 1. HTTP 重定向
diff --git a/notes/正则表达式.md b/notes/正则表达式.md
index d890273b..07f49139 100644
--- a/notes/正则表达式.md
+++ b/notes/正则表达式.md
@@ -19,7 +19,7 @@
正则表达式内置于其它语言或者软件产品中,它本身不是一种语言或者软件。
-[正则表达式在线工具](http://tool.chinaz.com/regex)
+[正则表达式在线工具](http://tool.oschina.net/regex/)
# 二、匹配单个字符
@@ -118,7 +118,7 @@ abc[^0-9]
**正则表达式**
```
-[\w.]+@\w+.\w+
+[\w.]+@\w+\.\w+
```
[\w.] 匹配的是字母数字或者 . ,在其后面加上 + ,表示匹配多次。在字符集合 [ ] 里,. 不是元字符;
@@ -130,8 +130,8 @@ abc[^0-9]
为了可读性,常常把转义的字符放到字符集合 [ ] 中,但是含义是相同的。
```
-[\w.]+@\w+.\w+
-[\w.]+@[\w]+.[\w]+
+[\w.]+@\w+\.\w+
+[\w.]+@[\w]+\.[\w]+
```
**{n}** 匹配 n 个字符,**{m, n}** 匹配 m\~n 个字符,**{m,}** 至少匹配 m 个字符;
diff --git a/notes/计算机操作系统.md b/notes/计算机操作系统.md
index 86f82e10..e992bd63 100644
--- a/notes/计算机操作系统.md
+++ b/notes/计算机操作系统.md
@@ -564,7 +564,7 @@ Linux 中管道通过空文件实现。
1. 互斥:每个资源要么已经分配给了一个进程,要么就是可用的。
2. 占有和等待:已经得到了某个资源的进程可以再请求新的资源。
-3. 不可抢占:已经分配给一个进程的资源不能强制性地被抢占,它只能被占有它的进程显示地释放。
+3. 不可抢占:已经分配给一个进程的资源不能强制性地被抢占,它只能被占有它的进程显式地释放。
4. 环路等待:有两个或者两个以上的进程组成一条环路,该环路中的每个进程都在等待下一个进程所占有的资源。
## 死锁的处理方法
diff --git a/pics/39ccb299-ee99-4dd1-b8b4-2f9ec9495cb4.png b/pics/39ccb299-ee99-4dd1-b8b4-2f9ec9495cb4.png
new file mode 100644
index 00000000..8e363e47
Binary files /dev/null and b/pics/39ccb299-ee99-4dd1-b8b4-2f9ec9495cb4.png differ
diff --git a/pics/540631a4-6018-40a5-aed7-081e2eeeaeea.png b/pics/540631a4-6018-40a5-aed7-081e2eeeaeea.png
new file mode 100644
index 00000000..e22b2c83
Binary files /dev/null and b/pics/540631a4-6018-40a5-aed7-081e2eeeaeea.png differ
diff --git a/pics/7fffa4b8-b36d-471f-ad0c-a88ee763bb76.png b/pics/7fffa4b8-b36d-471f-ad0c-a88ee763bb76.png
new file mode 100644
index 00000000..b636edf5
Binary files /dev/null and b/pics/7fffa4b8-b36d-471f-ad0c-a88ee763bb76.png differ
diff --git a/pics/8fe838e3-ef77-4f63-bf45-417b6bc5c6bb.png b/pics/8fe838e3-ef77-4f63-bf45-417b6bc5c6bb.png
new file mode 100644
index 00000000..2e53fbe3
Binary files /dev/null and b/pics/8fe838e3-ef77-4f63-bf45-417b6bc5c6bb.png differ
diff --git a/pics/PPjwP.png b/pics/PPjwP.png
new file mode 100644
index 00000000..80631505
Binary files /dev/null and b/pics/PPjwP.png differ
diff --git a/pics/bee1ff1d-c80f-4b3c-b58c-7073a8896ab2.jpg b/pics/bee1ff1d-c80f-4b3c-b58c-7073a8896ab2.jpg
new file mode 100644
index 00000000..e4becc19
Binary files /dev/null and b/pics/bee1ff1d-c80f-4b3c-b58c-7073a8896ab2.jpg differ
diff --git a/pics/c812c28a-1513-4a82-bfda-ab6a40981aa0.png b/pics/c812c28a-1513-4a82-bfda-ab6a40981aa0.png
new file mode 100644
index 00000000..b1142bff
Binary files /dev/null and b/pics/c812c28a-1513-4a82-bfda-ab6a40981aa0.png differ
diff --git a/pics/f5757d09-88e7-4bbd-8cfb-cecf55604854.png b/pics/f5757d09-88e7-4bbd-8cfb-cecf55604854.png
new file mode 100644
index 00000000..1664247b
Binary files /dev/null and b/pics/f5757d09-88e7-4bbd-8cfb-cecf55604854.png differ