diff --git a/.gitignore b/.gitignore
index e43b0f98..c91a56b6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
.DS_Store
+*.txt
diff --git a/BOOKLIST.md b/BOOKLIST.md
index bdd8eb92..4805a7dc 100644
--- a/BOOKLIST.md
+++ b/BOOKLIST.md
@@ -79,4 +79,4 @@
- [JavaScript 语言精粹](https://book.douban.com/subject/3590768/)
- [利用 Python 进行数据分析](https://book.douban.com/subject/25779298/)
-- [概率论与数理统计](https://book.douban.com/subject/2201479/)
\ No newline at end of file
+- [概率论与数理统计](https://book.douban.com/subject/2201479/)
diff --git a/README.md b/README.md
index 89625ec3..42788e54 100644
--- a/README.md
+++ b/README.md
@@ -1,137 +1,169 @@
-
| Ⅰ | Ⅱ | Ⅲ | Ⅳ | Ⅴ | Ⅵ | Ⅶ | Ⅷ | Ⅸ | Ⅹ |
| :--------: | :---------: | :---------: | :---------: | :---------: | :---------:| :---------: | :-------: | :-------:| :------:|
-| 算法[:pencil2:](#算法-pencil2) | 操作系统[:computer:](#操作系统-computer)|网络[:cloud:](#网络-cloud) | 面向对象[:couple:](#面向对象-couple) |数据库[:floppy_disk:](#数据库-floppy_disk)| Java [:coffee:](#java-coffee)| 分布式 [:sweat_drops:](#分布式-sweat_drops)| 工具[:hammer:](#工具-hammer)| 编码实践[:speak_no_evil:](#编码实践-speak_no_evil)| 后记[:memo:](#后记-memo) |
+| 算法[:pencil2:](#算法-pencil2) | 操作系统[:computer:](#操作系统-computer)|网络[:cloud:](#网络-cloud) | 面向对象[:couple:](#面向对象-couple) |数据库[:floppy_disk:](#数据库-floppy_disk)| Java [:coffee:](#java-coffee)| 系统设计[:bulb:](#系统设计-bulb)| 工具[:hammer:](#工具-hammer)| 编码实践[:speak_no_evil:](#编码实践-speak_no_evil)| 后记[:memo:](#后记-memo) |
+
+
+
+
+
## 算法 :pencil2:
-> [剑指 Offer 题解](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/剑指%20offer%20题解.md)
+- [剑指 Offer 题解](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/剑指%20offer%20题解.md)
-目录根据原书第二版进行编排。
+ 目录根据原书第二版进行编排,代码和原书有所不同,尽量比原书更简洁。
-> [Leetcode 题解](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Leetcode%20题解.md)
+- [Leetcode 题解](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Leetcode%20题解.md)
-做了一个大致分类,并对每种分类题型的解题思路做了总结。
+ 对题目做了一个大致分类,并对每种题型的解题思路做了总结。
-> [算法](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/算法.md)
+- [算法](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/算法.md)
-主要参考 Robert Sedgewick 的算法书进行实现,源代码以及测试代码可在另一个仓库获取。
+ 排序、并查集、栈和队列、红黑树、散列表。
## 操作系统 :computer:
-> [计算机操作系统](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/计算机操作系统.md)
+- [计算机操作系统](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/计算机操作系统.md)
-参考 现代操作系统、Unix 环境高级编程、深入理解计算机系统。
+ 进程管理、内存管理、设备管理、链接。
-> [Linux](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Linux.md)
+- [Linux](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Linux.md)
-参考 鸟哥的 Linux 私房菜。
+ 基本实现原理以及基本操作。
## 网络 :cloud:
-> [计算机网络](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/计算机网络.md)
+- [计算机网络](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/计算机网络.md)
-参考 谢希仁的计算机网络、计算机网络 自顶向下方法、TCP/IP 详解。
+ 物理层、链路层、网络层、运输层、应用层。
-> [HTTP](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/HTTP.md)
+- [HTTP](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/HTTP.md)
-参考 图解 HTTP,更多的是参考网上的文档,比如 MDN、维基百科等。
+ 方法、状态码、Cookie、缓存、连接管理、HTTPs、HTTP 2.0。
-> [Socket](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Socket.md)
+- [Socket](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Socket.md)
-参考 Unix 网络编程。
+ I/O 模型、I/O 多路复用。
## 面向对象 :couple:
-> [设计模式](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/设计模式.md)
+- [设计模式](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/设计模式.md)
-参考 Head First 设计模式、设计模式 可复用面向对象软件的基础,实现了 Gof 的 23 种设计模式。
+ 实现了 Gof 的 23 种设计模式。
-> [面向对象思想](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/面向对象思想.md)
+- [面向对象思想](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/面向对象思想.md)
-内容包括三大原则(继承、封装、多态)、类图、设计原则。
+ 三大原则(继承、封装、多态)、类图、设计原则。
## 数据库 :floppy_disk:
-> [数据库系统原理](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/数据库系统原理.md)
+- [数据库系统原理](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/数据库系统原理.md)
-参考 数据库系统原理。
+ 事务、锁、隔离级别、MVCC、间隙锁、范式。
-> [SQL](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/SQL.md)
+- [SQL](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/SQL.md)
-参考 SQL 必知必会。
+ SQL 基本语法。
-> [Leetcode-Database 题解](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Leetcode-Database%20题解.md)
+- [Leetcode-Database 题解](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Leetcode-Database%20题解.md)
-Leetcode 上数据库题目的解题记录。
+ Leetcode 上数据库题目的解题记录。
-> [MySQL](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/MySQL.md)
+- [MySQL](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/MySQL.md)
-参考 高性能 MySQL。
+ 存储引擎、索引、查询优化、切分、复制。
-> [Redis](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Redis.md)
+- [Redis](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Redis.md)
-参考 Redis 设计与实现、Redis 实战。
+ 五种数据类型、字典和跳跃表数据结构、使用场景、和 Memcache 的比较、淘汰策略、持久化、文件事件的 Reactor 模式、复制。
## Java :coffee:
-> [Java 基础](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Java%20基础.md)
+- [Java 基础](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Java%20基础.md)
-参考 Effective Java、Java 编程思想,也有部分内容参考官方文档以及 StackOverflow。
+ 不会涉及很多基本语法介绍,主要是一些实现原理以及关键特性。
-> [Java 虚拟机](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Java%20虚拟机.md)
+- [Java 容器](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Java%20容器.md)
-参考 深入理解 Java 虚拟机。
+ 源码分析:ArrayList、Vector、CopyOnWriteArrayList、LinkedList、HashMap、ConcurrentHashMap、LinkedHashMap、WeekHashMap。
-> [Java 并发](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Java%20并发.md)
+- [Java 并发](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Java%20并发.md)
-参考 Java 编程思想、深入理解 Java 虚拟机。
+ 线程使用方式、两种互斥同步方法、线程协作、JUC、线程安全、内存模型、锁优化。
-> [Java 容器](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Java%20容器.md)
+- [Java 虚拟机](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Java%20虚拟机.md)
-包含容器源码的分析。
+ 运行时数据区域、垃圾收集、类加载。
-> [Java I/O](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Java%20IO.md)
+- [Java I/O](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Java%20IO.md)
-包含 NIO 的原理以及实例。
+ NIO 的原理以及实例。
-## 分布式 :sweat_drops:
+## 系统设计 :bulb:
-> [一致性](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/一致性.md)
+- [系统设计基础](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/系统设计基础.md)
-CAP、BASE、Paxos、Raft
+ 性能、伸缩性、扩展性、可用性、安全性
->[分布式问题分析](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/分布式问题分析.md)
+- [分布式](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/分布式.md)
-分布式事务、分布式锁、分布式 Session、负载均衡
+ 分布式锁、分布式事务、CAP、BASE、Paxos、Raft
+
+- [集群](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/集群.md)
+
+ 负载均衡、Session 管理
+
+- [攻击技术](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/攻击技术.md)
+
+ XSS、CSRF、SQL 注入、DDoS
+
+- [缓存](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/缓存.md)
+
+ 缓存特征、缓存位置、缓存问题、数据分布、一致性哈希、LRU、CDN
+
+- [消息队列](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/消息队列.md)
+
+ 消息处理模型、使用场景、可靠性
## 工具 :hammer:
-> [Git](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Git.md)
+- [Git](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Git.md)
-一些 Git 的使用和概念。
+ 一些 Git 的使用和概念。
-> [正则表达式](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/正则表达式.md)
+- [Docker](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/Docker.md)
-参考 正则表达式必知必会。
+ Docker 基本原理。
+
+- [正则表达式](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/正则表达式.md)
+
+ 正则表达式基本语法。
+
+- [构建工具](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/构建工具.md)
+
+ 构建工具的基本概念、主流构建工具介绍。
## 编码实践 :speak_no_evil:
-> [重构](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/重构.md)
+- [重构](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/重构.md)
-参考 重构 改善既有代码的设计。
+ 参考 重构 改善既有代码的设计。
-> [代码可读性](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/代码可读性.md)
+- [代码可读性](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/代码可读性.md)
-参考 编写可读代码的艺术。
+ 参考 编写可读代码的艺术。
-> [代码风格规范](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/代码风格规范.md)
+- [代码风格规范](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/代码风格规范.md)
-Google 开源项目的代码风格规范。
+ Google 开源项目的代码风格规范。
## 后记 :memo:
-**关于仓库**
+### About
这个仓库是笔者的一个学习笔记,主要总结一些比较重要的知识点,希望对大家有所帮助。
@@ -139,38 +171,69 @@ Google 开源项目的代码风格规范。
[BOOKLIST](https://github.com/CyC2018/Interview-Notebook/blob/master/BOOKLIST.md),这个书单是笔者至今看的一些比较好的技术书籍,虽然没有全都看完,但每本书多多少少都看了一部分。
-**如何贡献**
+### How To Contribute
笔记内容是笔者一个字一个字打上去的,难免会有一些笔误,如果发现笔误可直接在相应文档进行编辑修改。
-欢迎提交对本仓库的改进建议~
+如果想要提交一个仓库现在还没有的全新内容,可以先将相应的文档放到 other 目录下。
-**授权相关**
+欢迎在 Issue 中提交对本仓库的改进建议~
+
+### Authorization
虽然没有加开源协议,但是允许非商业性使用。
转载使用请注明出处,谢谢!
-**上传方案**
-
-笔者在本地使用为知笔记软件进行书写,为了方便将本地笔记内容上传到 Github 上,实现了一整套自动化上传方案,包括文本文件的导出、提取图片、Markdown 文档转换、Git 同步。
-
-进行 Markdown 文档转换是因为 Github 使用的 GFM 不支持 MathJax 公式和 TOC 标记,所以需要替换 MathJax 公式为 CodeCogs 的云服务和重新生成 TOC 目录。
-
-这里提供了笔者实现的 GFM 文档转换工具的链接:[GFM-Converter](https://github.com/CyC2018/GFM-Converter)。
-
-**排版指南**
+### Typesetting
笔记内容按照 [中文文案排版指北](http://mazhuang.org/wiki/chinese-copywriting-guidelines/) 进行排版,以保证内容的可读性。
笔记不使用 `![]()` 这种方式来引用图片,而是用 `
` 标签。一方面是为了能够控制图片以合适的大小显示,另一方面是因为 GFM 不支持 ` ![]() ` 让图片居中显示,只能使用 `` 达到居中的效果。
-这里提供了笔者实现的中英混排文档在线排版工具的链接:[Text-Typesetting](https://github.com/CyC2018/Markdown-Typesetting)。
+笔者将自己实现的文档排版功能提取出来,放在 Github Page 中,无需下载安装即可免费使用:[Text-Typesetting](https://github.com/CyC2018/Markdown-Typesetting)。
-**声明**
+### Uploading
+
+笔者在本地使用为知笔记软件进行书写,为了方便将本地笔记内容上传到 Github 上,实现了一整套自动化上传方案,包括文本文件的导出、提取图片、Markdown 文档转换、Git 同步。
+
+进行 Markdown 文档转换是因为 Github 使用的 GFM 不支持 MathJax 公式和 TOC 标记,所以需要替换 MathJax 公式为 CodeCogs 的云服务和重新生成 TOC 目录。
+
+笔者将自己实现文档转换功能提取出来,方便大家在需要将本地 Markdown 上传到 Github,或者制作项目 README 文档时生成目录时使用:[GFM-Converter](https://github.com/CyC2018/GFM-Converter)。
+
+### Statement
本仓库不参与商业行为,不向读者收取任何费用。(This repository is not engaging in business activities, and does not charge readers any fee.)
-**鸣谢**
+### Logo
+
+Power by [logomakr](https://logomakr.com/).
+
+### Acknowledgements
+
+感谢以下人员对本仓库做出的贡献,当然不仅仅只有这些贡献者,这里就不一一列举了。如果你希望被添加到这个名单中,并且提交过 Issue 或者 PR,请与笔者联系。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-[TeeKee](https://github.com/linw7) [g10guang](https://github.com/g10guang) [crossoverJie](https://github.com/crossoverJie)
diff --git a/SUMMARY.md b/SUMMARY.md
index e60f018e..69b6103c 100644
--- a/SUMMARY.md
+++ b/SUMMARY.md
@@ -1,3 +1,5 @@
+This file used to generate gitbook catalogue.
+
# Summary
* 算法
@@ -32,5 +34,3 @@
-
-
diff --git a/notes/Docker.md b/notes/Docker.md
new file mode 100644
index 00000000..f230f762
--- /dev/null
+++ b/notes/Docker.md
@@ -0,0 +1,102 @@
+
+* [一、解决的问题](#一解决的问题)
+* [二、与虚拟机的比较](#二与虚拟机的比较)
+* [三、优势](#三优势)
+* [四、使用场景](#四使用场景)
+* [五、镜像与容器](#五镜像与容器)
+
+
+
+
+
+# 一、解决的问题
+
+由于不同的机器有不同的操作系统,以及不同的库和组件,在将一个应用部署到多台机器上需要进行大量的环境配置操作。
+
+Docker 主要解决环境配置问题,它是一种虚拟化技术,对进程进行隔离,被隔离的进程独立于宿主操作系统和其它隔离的进程。使用 Docker 可以不修改应用程序代码,不需要开发人员学习特定环境下的技术,就能够将现有的应用程序部署在其他机器中。
+
+参考资料:
+
+- [DOCKER 101: INTRODUCTION TO DOCKER WEBINAR RECAP](https://blog.docker.com/2017/08/docker-101-introduction-docker-webinar-recap/)
+- [Docker 入门教程](http://www.ruanyifeng.com/blog/2018/02/docker-tutorial.html)
+
+# 二、与虚拟机的比较
+
+虚拟机也是一种虚拟化技术,它与 Docker 最大的区别在于它是通过模拟硬件,并在硬件上安装操作系统来实现。
+
+
+
+
+
+## 启动速度
+
+启动虚拟机需要启动虚拟机的操作系统,再启动相应的应用,这个过程会非常慢;
+
+而启动 Docker 相当于启动宿主操作系统上的一个进程。
+
+## 占用资源
+
+虚拟机是一个完整的操作系统,需要占用大量的磁盘空间、内存和 CPU,一台机器只能开启几十个的虚拟机。
+
+而 Docker 只是一个进程,只需要将应用以及相应的组件打包,在运行时占用很少的资源,一台机器可以开启成千上万个 Docker。
+
+参考资料:
+
+- [Docker container vs Virtual machine](http://www.bogotobogo.com/DevOps/Docker/Docker_Container_vs_Virtual_Machine.php)
+
+# 三、优势
+
+除了启动速度快以及占用资源少之外,Docker 具有以下优势:
+
+## 更容易迁移
+
+Docker 可以提供一致性的运行环境,可以在不同的机器上进行迁移,而不用担心环境变化导致无法运行。
+
+## 更容易维护
+
+Docker 使用分层技术和镜像,使得应用可以更容易复用重复部分。复用程度越高,维护工作也越容易。
+
+## 更容易扩展
+
+可以使用基础镜像进一步扩展得到新的镜像,并且官方和开源社区提供了大量的镜像,通过扩展这些镜像得到我们想要的镜像非常容易。
+
+参考资料:
+
+- [为什么要使用 Docker?](https://yeasy.gitbooks.io/docker_practice/introduction/why.html)
+
+# 四、使用场景
+
+## 持续集成
+
+持续集成指的是频繁地将代码集成到主干上,这样能够更快地发现错误。
+
+Docker 具有轻量级以及隔离性的特点,在将代码集成到一个 Docker 中不会对其它 Docker 产生影响。
+
+## 提供可伸缩的云服务
+
+根据应用的负载情况,可以很容易地增加或者减少 Docker。
+
+## 搭建微服务架构
+
+Docker 轻量级的特点使得它很适合用于部署、维护、组合微服务。
+
+参考资料:
+
+- [What is Docker](https://www.docker.com/what-docker)
+- [持续集成是什么?](http://www.ruanyifeng.com/blog/2015/09/continuous-integration.html)
+
+# 五、镜像与容器
+
+镜像是一种静态的结构,可以看成面向对象里面的类,而容器是镜像的一个实例。
+
+镜像包含着容器运行时所需要的代码以及其它组件,它是一种分层结构,每一层都是只读的(read-only layers)。构建镜像时,会一层一层构建,前一层是后一层的基础。镜像的这种分层存储结构很适合镜像的复用以及定制。
+
+在构建容器时,通过在镜像的基础上添加一个可写层(writable layer),用来保存着容器运行过程中的修改。
+
+
+
+参考资料:
+
+- [How to Create Docker Container using Dockerfile](https://linoxide.com/linux-how-to/dockerfile-create-docker-container/)
+- [理解 Docker(2):Docker 镜像](http://www.cnblogs.com/sammyliu/p/5877964.html)
+
diff --git a/notes/HTTP.md b/notes/HTTP.md
index 7d64c901..537b24fe 100644
--- a/notes/HTTP.md
+++ b/notes/HTTP.md
@@ -40,12 +40,12 @@
* [完整性保护](#完整性保护)
* [HTTPs 的缺点](#https-的缺点)
* [配置 HTTPs](#配置-https)
-* [七、Web 攻击技术](#七web-攻击技术)
- * [跨站脚本攻击](#跨站脚本攻击)
- * [跨站请求伪造](#跨站请求伪造)
- * [SQL 注入攻击](#sql-注入攻击)
- * [拒绝服务攻击](#拒绝服务攻击)
-* [八、GET 和 POST 的区别](#八get-和-post-的区别)
+* [七、HTTP/2.0](#七http20)
+ * [HTTP/1.x 缺陷](#http1x-缺陷)
+ * [二进制分帧层](#二进制分帧层)
+ * [服务端推送](#服务端推送)
+ * [首部压缩](#首部压缩)
+* [八、GET 和 POST 比较](#八get-和-post-比较)
* [作用](#作用)
* [参数](#参数)
* [安全](#安全)
@@ -53,11 +53,6 @@
* [可缓存](#可缓存)
* [XMLHttpRequest](#xmlhttprequest)
* [九、HTTP/1.0 与 HTTP/1.1 的区别](#九http10-与-http11-的区别)
-* [十、HTTP/2.0](#十http20)
- * [HTTP/1.x 缺陷](#http1x-缺陷)
- * [二进制分帧层](#二进制分帧层)
- * [服务端推送](#服务端推送)
- * [首部压缩](#首部压缩)
* [参考资料](#参考资料)
@@ -66,13 +61,13 @@
## URL
-- URI(Uniform Resource Indentifier,统一资源标识符)
-- URL(Uniform Resource Locator,统一资源定位符)
-- URN(Uniform Resource Name,统一资源名称),例如 urn:isbn:0-486-27557-4。
-
URI 包含 URL 和 URN,目前 WEB 只有 URL 比较流行,所以见到的基本都是 URL。
-
+- URI(Uniform Resource Identifier,统一资源标识符)
+- URL(Uniform Resource Locator,统一资源定位符)
+- URN(Uniform Resource Name,统一资源名称)
+
+
## 请求和响应报文
@@ -202,7 +197,7 @@ CONNECT www.example.com:443 HTTP/1.1
- **204 No Content** :请求已经成功处理,但是返回的响应报文不包含实体的主体部分。一般在只需要从客户端往服务器发送信息,而不需要返回数据时使用。
-- **206 Partial Content** :表示客户端进行了范围请求。响应报文包含由 Content-Range 指定范围的实体内容。
+- **206 Partial Content** :表示客户端进行了范围请求,响应报文包含由 Content-Range 指定范围的实体内容。
## 3XX 重定向
@@ -224,7 +219,7 @@ CONNECT www.example.com:443 HTTP/1.1
- **401 Unauthorized** :该状态码表示发送的请求需要有认证信息(BASIC 认证、DIGEST 认证)。如果之前已进行过一次请求,则表示用户认证失败。
-- **403 Forbidden** :请求被拒绝,服务器端没有必要给出拒绝的详细理由。
+- **403 Forbidden** :请求被拒绝。
- **404 Not Found**
@@ -232,7 +227,7 @@ CONNECT www.example.com:443 HTTP/1.1
- **500 Internal Server Error** :服务器正在执行请求时发生错误。
-- **503 Service Unavilable** :服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。
+- **503 Service Unavailable** :服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。
# 四、HTTP 首部
@@ -313,7 +308,9 @@ CONNECT www.example.com:443 HTTP/1.1
HTTP 协议是无状态的,主要是为了让 HTTP 协议尽可能简单,使得它能够处理大量事务。HTTP/1.1 引入 Cookie 来保存状态信息。
-Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。它用于告知服务端两个请求是否来自同一浏览器,并保持用户的登录状态。
+Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器之后向同一服务器再次发起请求时被携带上,用于告知服务端两个请求是否来自同一浏览器。由于之后每次请求都会需要携带 Cookie 数据,因此会带来额外的性能开销(尤其是在移动环境下)。
+
+Cookie 曾一度用于客户端数据的存储,因为当时并没有其它合适的存储办法而作为唯一的存储手段,但现在随着现代浏览器开始支持各种各样的存储方式,Cookie 渐渐被淘汰。新的浏览器 API 已经允许开发者直接将数据存储到本地,如使用 Web storage API (本地存储和会话存储)或 IndexedDB。
### 1. 用途
@@ -321,8 +318,6 @@ Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据
- 个性化设置(如用户自定义设置、主题等)
- 浏览器行为跟踪(如跟踪分析用户行为等)
-Cookie 曾一度用于客户端数据的存储,因为当时并没有其它合适的存储办法而作为唯一的存储手段,但现在随着现代浏览器开始支持各种各样的存储方式,Cookie 渐渐被淘汰。由于服务器指定 Cookie 后,浏览器的每次请求都会携带 Cookie 数据,会带来额外的性能开销(尤其是在移动环境下)。新的浏览器 API 已经允许开发者直接将数据存储到本地,如使用 Web storage API (本地存储和会话存储)或 IndexedDB。
-
### 2. 创建过程
服务器发送的响应报文包含 Set-Cookie 首部字段,客户端得到响应报文后把 Cookie 内容保存到浏览器中。
@@ -336,7 +331,7 @@ Set-Cookie: tasty_cookie=strawberry
[page content]
```
-客户端之后对同一个服务器发送请求时,会从浏览器中读出 Cookie 信息通过 Cookie 请求首部字段发送给服务器。
+客户端之后对同一个服务器发送请求时,会从浏览器中取出 Cookie 信息并通过 Cookie 请求首部字段发送给服务器。
```html
GET /sample_page.html HTTP/1.1
@@ -365,9 +360,9 @@ console.log(document.cookie);
### 5. Secure 和 HttpOnly
-标记为 Secure 的 Cookie 只应通过被 HTTPS 协议加密过的请求发送给服务端。但即便设置了 Secure 标记,敏感信息也不应该通过 Cookie 传输,因为 Cookie 有其固有的不安全性,Secure 标记也无法提供确实的安全保障。
+标记为 Secure 的 Cookie 只能通过被 HTTPS 协议加密过的请求发送给服务端。但即便设置了 Secure 标记,敏感信息也不应该通过 Cookie 传输,因为 Cookie 有其固有的不安全性,Secure 标记也无法提供确实的安全保障。
-标记为 HttpOnly 的 Cookie 不能被 JavaScript 脚本调用。因为跨域脚本 (XSS) 攻击常常使用 JavaScript 的 `Document.cookie` API 窃取用户的 Cookie 信息,因此使用 HttpOnly 标记可以在一定程度上避免 XSS 攻击。
+标记为 HttpOnly 的 Cookie 不能被 JavaScript 脚本调用。跨站脚本攻击 (XSS) 常常使用 JavaScript 的 `Document.cookie` API 窃取用户的 Cookie 信息,因此使用 HttpOnly 标记可以在一定程度上避免 XSS 攻击。
```html
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly
@@ -387,15 +382,15 @@ Path 标识指定了主机下的哪些路径可以接受 Cookie(该 URL 路径
除了可以将用户信息通过 Cookie 存储在用户浏览器中,也可以利用 Session 存储在服务器端,存储在服务器端的信息更加安全。
-Session 可以存储在服务器上的文件、数据库或者内存中,现在最常见的是将 Session 存储在内存型数据库中,比如 Redis。
+Session 可以存储在服务器上的文件、数据库或者内存中。也可以将 Session 存储在 Redis 这种内存型数据库中,效率会更高。
-使用 Session 维护用户登录的过程如下:
+使用 Session 维护用户登录状态的过程如下:
- 用户进行登录时,用户提交包含用户名和密码的表单,放入 HTTP 请求报文中;
- 服务器验证该用户名和密码;
-- 如果正确则把用户信息存储到 Redis 中,它在 Redis 中的 ID 称为 Session ID;
+- 如果正确则把用户信息存储到 Redis 中,它在 Redis 中的 Key 称为 Session ID;
- 服务器返回的响应报文的 Set-Cookie 首部字段包含了这个 Session ID,客户端收到响应报文之后将该 Cookie 值存入浏览器中;
-- 客户端之后对同一个服务器进行请求时会包含该 Cookie 值,服务器收到之后提取出 Session ID,从 Redis 中取出用户信息,继续之后的业务操作。
+- 客户端之后对同一个服务器进行请求时会包含该 Cookie 值,服务器收到之后提取出 Session ID,从 Redis 中取出用户信息,继续之前的业务操作。
应该注意 Session ID 的安全性问题,不能让它被恶意攻击者轻易获取,那么就不能产生一个容易被猜到的 Session ID 值。此外,还需要经常重新生成 Session ID。在对安全性要求极高的场景下,例如转账等操作,除了使用 Session 管理用户状态之外,还需要对用户进行重新验证,比如重新输入密码,或者使用短信验证码等方式。
@@ -414,7 +409,7 @@ Session 可以存储在服务器上的文件、数据库或者内存中,现在
### 1. 优点
- 缓解服务器压力;
-- 降低客户端获取资源的延迟(缓存资源比服务器上的资源离客户端更近)。
+- 降低客户端获取资源的延迟:缓存通常位于内存中,读取缓存的速度更快。并且缓存在地理位置上也有可能比源服务器来得近,例如浏览器缓存。
### 2. 实现方法
@@ -465,7 +460,10 @@ max-age 指令出现在响应报文中,表示缓存资源在缓存服务器中
Cache-Control: max-age=31536000
```
-Expires 首部字段也可以用于告知缓存服务器该资源什么时候会过期。在 HTTP/1.1 中,会优先处理 Cache-Control : max-age 指令;而在 HTTP/1.0 中,Cache-Control : max-age 指令会被忽略掉。
+Expires 首部字段也可以用于告知缓存服务器该资源什么时候会过期。
+
+- 在 HTTP/1.1 中,会优先处理 max-age 指令;
+- 在 HTTP/1.0 中,max-age 指令会被忽略掉。
```html
Expires: Wed, 04 Jul 2012 08:26:05 GMT
@@ -485,7 +483,7 @@ ETag: "82e22293907ce725faf67773957acd12"
If-None-Match: "82e22293907ce725faf67773957acd12"
```
-Last-Modified 首部字段也可以用于缓存验证,它包含在源服务器发送的响应报文中,指示源服务器对资源的最后修改时间。但是它是一种弱校验器,因为只能精确到一秒,所以它通常作为 ETag 的备用方案。如果响应首部字段里含有这个信息,客户端可以在后续的请求中带上 If-Modified-Since 来验证缓存。服务器只在所请求的资源在给定的日期时间之后对内容进行过修改的情况下才会将资源返回,状态码为 200 OK。如果请求的资源从那时起未经修改,那么返回一个不带有消息主体的 304 Not Modified 响应,
+Last-Modified 首部字段也可以用于缓存验证,它包含在源服务器发送的响应报文中,指示源服务器对资源的最后修改时间。但是它是一种弱校验器,因为只能精确到一秒,所以它通常作为 ETag 的备用方案。如果响应首部字段里含有这个信息,客户端可以在后续的请求中带上 If-Modified-Since 来验证缓存。服务器只在所请求的资源在给定的日期时间之后对内容进行过修改的情况下才会将资源返回,状态码为 200 OK。如果请求的资源从那时起未经修改,那么返回一个不带有消息主体的 304 Not Modified 响应。
```html
Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT
@@ -501,13 +499,16 @@ If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT
### 1. 短连接与长连接
-当浏览器访问一个包含多张图片的 HTML 页面时,除了请求访问 HTML 页面资源,还会请求图片资源,如果每进行一次 HTTP 通信就要断开一次 TCP 连接,连接建立和断开的开销会很大。长连接只需要建立一次 TCP 连接就能进行多次 HTTP 通信。
+当浏览器访问一个包含多张图片的 HTML 页面时,除了请求访问 HTML 页面资源,还会请求图片资源。如果每进行一次 HTTP 通信就要新建一个 TCP 连接,那么开销会很大。
-从 HTTP/1.1 开始默认是长连接的,如果要断开连接,需要由客户端或者服务器端提出断开,使用 Connection : close;而在 HTTP/1.1 之前默认是短连接的,如果需要长连接,则使用 Connection : Keep-Alive。
+长连接只需要建立一次 TCP 连接就能进行多次 HTTP 通信。
+
+- 从 HTTP/1.1 开始默认是长连接的,如果要断开连接,需要由客户端或者服务器端提出断开,使用 `Connection : close`;
+- 在 HTTP/1.1 之前默认是短连接的,如果需要使用长连接,则使用 `Connection : Keep-Alive`。
### 2. 流水线
-默认情况下,HTTP 请求是按顺序发出的,下一个请求只有在当前请求收到应答过后才会被发出。由于会受到网络延迟和带宽的限制,在下一个请求被发送到服务器之前,可能需要等待很长时间。
+默认情况下,HTTP 请求是按顺序发出的,下一个请求只有在当前请求收到响应之后才会被发出。由于会受到网络延迟和带宽的限制,在下一个请求被发送到服务器之前,可能需要等待很长时间。
流水线是在同一条长连接上发出连续的请求,而不用等待响应返回,这样可以避免连接延迟。
@@ -517,17 +518,17 @@ If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT
### 1. 类型
-**(一)服务端驱动型内容协商**
+**(一)服务端驱动型**
客户端设置特定的 HTTP 首部字段,例如 Accept、Accept-Charset、Accept-Encoding、Accept-Language、Content-Languag,服务器根据这些字段返回特定的资源。
它存在以下问题:
- 服务器很难知道客户端浏览器的全部信息;
-- 客户端提供的信息相当冗长(HTTP/2 协议的首部压缩机制缓解了这个问题),并且存在隐私风险(HTTP 指纹识别技术)。
+- 客户端提供的信息相当冗长(HTTP/2 协议的首部压缩机制缓解了这个问题),并且存在隐私风险(HTTP 指纹识别技术);
- 给定的资源需要返回不同的展现形式,共享缓存的效率会降低,而服务器端的实现会越来越复杂。
-**(二)代理驱动型协商**
+**(二)代理驱动型**
服务器返回 300 Multiple Choices 或者 406 Not Acceptable,客户端从中选出最合适的那个资源。
@@ -543,9 +544,11 @@ Vary: Accept-Language
## 内容编码
-内容编码将实体主体进行压缩,从而减少传输的数据量。常用的内容编码有:gzip、compress、deflate、identity。
+内容编码将实体主体进行压缩,从而减少传输的数据量。
-浏览器发送 Accept-Encoding 首部,其中包含有它所支持的压缩算法,以及各自的优先级,服务器则从中选择一种,使用该算法对响应的消息主体进行压缩,并且发送 Content-Encoding 首部来告知浏览器它选择了哪一种算法。由于该内容协商过程是基于编码类型来选择资源的展现形式的,在响应中,Vary 首部中至少要包含 Content-Encoding,这样的话,缓存服务器就可以对资源的不同展现形式进行缓存。
+常用的内容编码有:gzip、compress、deflate、identity。
+
+浏览器发送 Accept-Encoding 首部,其中包含有它所支持的压缩算法,以及各自的优先级。服务器则从中选择一种,使用该算法对响应的消息主体进行压缩,并且发送 Content-Encoding 首部来告知浏览器它选择了哪一种算法。由于该内容协商过程是基于编码类型来选择资源的展现形式的,在响应的 Vary 首部至少要包含 Content-Encoding。
## 范围请求
@@ -627,10 +630,14 @@ HTTP/1.1 使用虚拟主机技术,使得一台服务器拥有多个域名,
- 网络访问控制
- 访问日志记录
-代理服务器分为正向代理和反向代理两种,用户察觉得到正向代理的存在,而反向代理一般位于内部网络中,用户察觉不到。
+代理服务器分为正向代理和反向代理两种:
+
+- 用户察觉得到正向代理的存在。
+- 而反向代理一般位于内部网络中,用户察觉不到。
+
### 2. 网关
@@ -639,7 +646,7 @@ HTTP/1.1 使用虚拟主机技术,使得一台服务器拥有多个域名,
### 3. 隧道
-使用 SSL 等加密手段,为客户端和服务器之间建立一条安全的通信线路。
+使用 SSL 等加密手段,在客户端和服务器之间建立一条安全的通信线路。
# 六、HTTPs
@@ -649,7 +656,7 @@ HTTP 有以下安全性问题:
- 不验证通信方的身份,通信方的身份有可能遭遇伪装;
- 无法证明报文的完整性,报文有可能遭篡改。
-HTTPs 并不是新协议,而是让 HTTP 先和 SSL(Secure Sockets Layer)通信,再由 SSL 和 TCP 通信。也就是说 HTTPs 使用了隧道进行通信。
+HTTPs 并不是新协议,而是让 HTTP 先和 SSL(Secure Sockets Layer)通信,再由 SSL 和 TCP 通信,也就是说 HTTPs 使用了隧道进行通信。
通过使用 SSL,HTTPs 具有了加密(防窃听)、认证(防伪装)和完整性保护(防篡改)。
@@ -681,7 +688,7 @@ HTTPs 并不是新协议,而是让 HTTP 先和 SSL(Secure Sockets Layer)
### 3. HTTPs 采用的加密方式
-HTTPs 采用混合的加密机制,使用非对称密钥加密用于传输对称密钥来保证安全性,之后使用对称密钥加密进行通信来保证效率。(下图中的 Session Key 就是对称密钥)
+HTTPs 采用混合的加密机制,使用非对称密钥加密用于传输对称密钥来保证传输过程的安全性,之后使用对称密钥加密进行通信来保证通信过程的效率。(下图中的 Session Key 就是对称密钥)
@@ -710,221 +717,53 @@ HTTPs 的报文摘要功能之所以安全,是因为它结合了加密和认
## HTTPs 的缺点
- 因为需要进行加密解密等过程,因此速度会更慢;
-- 需要支付证书授权的高费用。
+- 需要支付证书授权的高额费用。
## 配置 HTTPs
[Nginx 配置 HTTPS 服务器](https://aotu.io/notes/2016/08/16/nginx-https/index.html)
-# 七、Web 攻击技术
+# 七、HTTP/2.0
-## 跨站脚本攻击
+## HTTP/1.x 缺陷
-### 1. 概念
+ HTTP/1.x 实现简单是以牺牲性能为代价的:
-跨站脚本攻击(Cross-Site Scripting, XSS),可以将代码注入到用户浏览的网页上,这种代码包括 HTML 和 JavaScript。
+- 客户端需要使用多个连接才能实现并发和缩短延迟;
+- 不会压缩请求和响应首部,从而导致不必要的网络流量;
+- 不支持有效的资源优先级,致使底层 TCP 连接的利用率低下。
-例如有一个论坛网站,攻击者可以在上面发布以下内容:
+## 二进制分帧层
-```html
-
-```
+HTTP/2.0 将报文分成 HEADERS 帧和 DATA 帧,它们都是二进制格式的。
-之后该内容可能会被渲染成以下形式:
+
-```html
-
-```
+在通信过程中,只会有一个 TCP 连接存在,它承载了任意数量的双向数据流(Stream)。
-另一个用户浏览了含有这个内容的页面将会跳转到 domain.com 并携带了当前作用域的 Cookie。如果这个论坛网站通过 Cookie 管理用户登录状态,那么攻击者就可以通过这个 Cookie 登录被攻击者的账号了。
+- 一个数据流都有一个唯一标识符和可选的优先级信息,用于承载双向信息。
+- 消息(Message)是与逻辑请求或响应消息对应的完整的一系列帧。
+- 帧(Fram)是最小的通信单位,来自不同数据流的帧可以交错发送,然后再根据每个帧头的数据流标识符重新组装。
-### 2. 危害
+
-- 窃取用户的 Cookie 值
-- 伪造虚假的输入表单骗取个人信息
-- 显示伪造的文章或者图片
+## 服务端推送
-### 3. 防范手段
+HTTP/2.0 在客户端请求一个资源时,会把相关的资源一起发送给客户端,客户端就不需要再次发起请求了。例如客户端请求 page.html 页面,服务端就把 script.js 和 style.css 等与之相关的资源一起发给客户端。
-**(一)设置 Cookie 为 HttpOnly**
+
-设置了 HttpOnly 的 Cookie 可以防止 JavaScript 脚本调用,就无法通过 document.cookie 获取用户 Cookie 信息。
+## 首部压缩
-**(二)过滤特殊字符**
+HTTP/1.1 的首部带有大量信息,而且每次都要重复发送。
-例如将 `<` 转义为 `<`,将 `>` 转义为 `>`,从而避免 HTML 和 Jascript 代码的运行。
+HTTP/2.0 要求客户端和服务器同时维护和更新一个包含之前见过的首部字段表,从而避免了重复传输。
-**(三)富文本编辑器的处理**
+不仅如此,HTTP/2.0 也使用 Huffman 编码对首部字段进行压缩。
-富文本编辑器允许用户输入 HTML 代码,就不能简单地将 `<` 等字符进行过滤了,极大地提高了 XSS 攻击的可能性。
+
-富文本编辑器通常采用 XSS filter 来防范 XSS 攻击,可以定义一些标签白名单或者黑名单,从而不允许有攻击性的 HTML 代码的输入。
-
-以下例子中,form 和 script 等标签都被转义,而 h 和 p 等标签将会保留。
-
-[XSS 过滤在线测试](http://jsxss.com/zh/try.html)
-
-```html
-XSS Demo
-
-
-Sanitize untrusted HTML (to prevent XSS) with a configuration specified by a Whitelist.
-
-
-
-
-hello
-
-
- http
-
-
-Features:
-
- - Specifies HTML tags and their attributes allowed with whitelist
- - Handle any tags or attributes using custom function
-
-
-
-```
-
-```html
-XSS Demo
-
-
-Sanitize untrusted HTML (to prevent XSS) with a configuration specified by a Whitelist.
-
-
-<form>
- <input type="text" name="q" value="test">
- <button id="submit">Submit</button>
-</form>
-
-hello
-
-
- http
-
-
-Features:
-
- - Specifies HTML tags and their attributes allowed with whitelist
- - Handle any tags or attributes using custom function
-
-
-<script type="text/javascript">
-alert(/xss/);
-</script>
-```
-
-## 跨站请求伪造
-
-### 1. 概念
-
-跨站请求伪造(Cross-site request forgery,CSRF),是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并执行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去执行。
-
-XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户浏览器的信任。
-
-假如一家银行用以执行转账操作的 URL 地址如下:
-
-```
-http://www.examplebank.com/withdraw?account=AccoutName&amount=1000&for=PayeeName。
-```
-
-那么,一个恶意攻击者可以在另一个网站上放置如下代码:
-
-```
-
。
-```
-
-如果有账户名为 Alice 的用户访问了恶意站点,而她之前刚访问过银行不久,登录信息尚未过期,那么她就会损失 1000 资金。
-
-这种恶意的网址可以有很多种形式,藏身于网页中的许多地方。此外,攻击者也不需要控制放置恶意网址的网站。例如他可以将这种地址藏在论坛,博客等任何用户生成内容的网站中。这意味着如果服务器端没有合适的防御措施的话,用户即使访问熟悉的可信网站也有受攻击的危险。
-
-透过例子能够看出,攻击者并不能通过 CSRF 攻击来直接获取用户的账户控制权,也不能直接窃取用户的任何信息。他们能做到的,是欺骗用户浏览器,让其以用户的名义执行操作。
-
-### 2. 防范手段
-
-**(一)检查 Referer 首部字段**
-
-Referer 首部字段位于 HTTP 报文中,用于标识请求来源的地址。检查这个首部字段并要求请求来源的地址在同一个域名下,可以极大的防止 XSRF 攻击。
-
-这种办法简单易行,工作量低,仅需要在关键访问处增加一步校验。但这种办法也有其局限性,因其完全依赖浏览器发送正确的 Referer 字段。虽然 HTTP 协议对此字段的内容有明确的规定,但并无法保证来访的浏览器的具体实现,亦无法保证浏览器没有安全漏洞影响到此字段。并且也存在攻击者攻击某些浏览器,篡改其 Referer 字段的可能。
-
-**(二)添加校验 Token**
-
-在访问敏感数据请求时,要求用户浏览器提供不保存在 Cookie 中,并且攻击者无法伪造的数据作为校验。例如服务器生成随机数并附加在表单中,并要求客户端传回这个随机数。
-
-**(三)输入验证码**
-
-因为 CSRF 攻击是在用户无意识的情况下发生的,所以要求用户输入验证码可以让用户知道自己正在做的操作。
-
-也可以要求用户输入验证码来进行校验。
-
-## SQL 注入攻击
-
-### 1. 概念
-
-服务器上的数据库运行非法的 SQL 语句,主要通过拼接来完成。
-
-### 2. 攻击原理
-
-例如一个网站登录验证的 SQL 查询代码为:
-
-```sql
-strSQL = "SELECT * FROM users WHERE (name = '" + userName + "') and (pw = '"+ passWord +"');"
-```
-
-如果填入以下内容:
-
-```sql
-userName = "1' OR '1'='1";
-passWord = "1' OR '1'='1";
-```
-
-那么 SQL 查询字符串为:
-
-```sql
-strSQL = "SELECT * FROM users WHERE (name = '1' OR '1'='1') and (pw = '1' OR '1'='1');"
-```
-
-此时无需验证通过就能执行以下查询:
-
-```sql
-strSQL = "SELECT * FROM users;"
-```
-
-### 3. 防范手段
-
-**(一)使用参数化查询**
-
-以下以 Java 中的 PreparedStatement 为例,它是预先编译的 SQL 语句,可以传入适当参数并且多次执行。由于没有拼接的过程,因此可以防止 SQL 注入的发生。
-
-```java
-PreparedStatement stmt = connection.prepareStatement("SELECT * FROM users WHERE userid=? AND password=?");
-stmt.setString(1, userid);
-stmt.setString(2, password);
-ResultSet rs = stmt.executeQuery();
-```
-
-**(二)单引号转换**
-
-将传入的参数中的单引号转换为连续两个单引号,PHP 中的 Magic quote 可以完成这个功能。
-
-## 拒绝服务攻击
-
-拒绝服务攻击(denial-of-service attack,DoS),亦称洪水攻击,其目的在于使目标电脑的网络或系统资源耗尽,使服务暂时中断或停止,导致其正常用户无法访问。
-
-分布式拒绝服务攻击(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)
-
-# 八、GET 和 POST 的区别
+# 八、GET 和 POST 比较
## 作用
@@ -932,7 +771,9 @@ GET 用于获取资源,而 POST 用于传输实体主体。
## 参数
-GET 和 POST 的请求都能使用额外的参数,但是 GET 的参数是以查询字符串出现在 URL 中,而 POST 的参数存储在实体主体中。
+GET 和 POST 的请求都能使用额外的参数,但是 GET 的参数是以查询字符串出现在 URL 中,而 POST 的参数存储在实体主体中。不能因为 POST 参数存储在实体主体中就认为它的安全性更高,因为照样可以通过一些抓包工具(Fiddler)查看。
+
+因为 URL 只支持 ASCII 码,因此 GET 的参数中如果存在中文等字符就需要先进行编码。例如 `中文` 会转换为 `%E4%B8%AD%E6%96%87`,而空格会转换为 `%20`。POST 参考支持标准字符集。
```
GET /test/demo_form.asp?name1=value1&name2=value2 HTTP/1.1
@@ -944,10 +785,6 @@ Host: w3schools.com
name1=value1&name2=value2
```
-不能因为 POST 参数存储在实体主体中就认为它的安全性更高,因为照样可以通过一些抓包工具(Fiddler)查看。
-
-因为 URL 只支持 ASCII 码,因此 GET 的参数中如果存在中文等字符就需要先进行编码,例如`中文`会转换为`%E4%B8%AD%E6%96%87`,而空格会转换为`%20`。POST 支持标准字符集。
-
## 安全
安全的 HTTP 方法不会改变服务器状态,也就是说它只是可读的。
@@ -960,9 +797,13 @@ GET 方法是安全的,而 POST 却不是,因为 POST 的目的是传送实
## 幂等性
-幂等的 HTTP 方法,同样的请求被执行一次与连续执行多次的效果是一样的,服务器的状态也是一样的。换句话说就是,幂等方法不应该具有副作用(统计用途除外)。在正确实现的条件下,GET,HEAD,PUT 和 DELETE 等方法都是幂等的,而 POST 方法不是。所有的安全方法也都是幂等的。
+幂等的 HTTP 方法,同样的请求被执行一次与连续执行多次的效果是一样的,服务器的状态也是一样的。换句话说就是,幂等方法不应该具有副作用(统计用途除外)。
-GET /pageX HTTP/1.1 是幂等的。连续调用多次,客户端接收到的结果都是一样的:
+所有的安全方法也都是幂等的。
+
+在正确实现的条件下,GET,HEAD,PUT 和 DELETE 等方法都是幂等的,而 POST 方法不是。
+
+GET /pageX HTTP/1.1 是幂等的,连续调用多次,客户端接收到的结果都是一样的:
```
GET /pageX HTTP/1.1
@@ -971,7 +812,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 -> Adds a 1nd row
@@ -1001,7 +842,8 @@ DELETE /idX/delete HTTP/1.1 -> Returns 404
> XMLHttpRequest 是一个 API,它为客户端提供了在客户端和服务器之间传输数据的功能。它提供了一个通过 URL 来获取数据的简单方式,并且不会使整个页面刷新。这使得网页只更新一部分页面而不会打扰到用户。XMLHttpRequest 在 AJAX 中被大量使用。
-在使用 XMLHttpRequest 的 POST 方法时,浏览器会先发送 Header 再发送 Data。但并不是所有浏览器会这么做,例如火狐就不会。而 GET 方法 Header 和 Data 会一起发送。
+- 在使用 XMLHttpRequest 的 POST 方法时,浏览器会先发送 Header 再发送 Data。但并不是所有浏览器会这么做,例如火狐就不会。
+- 而 GET 方法 Header 和 Data 会一起发送。
# 九、HTTP/1.0 与 HTTP/1.1 的区别
@@ -1021,37 +863,6 @@ DELETE /idX/delete HTTP/1.1 -> Returns 404
- HTTP/1.1 新增缓存处理指令 max-age
-# 十、HTTP/2.0
-
-## HTTP/1.x 缺陷
-
- HTTP/1.x 实现简单是以牺牲应用性能为代价的:
-
-- 客户端需要使用多个连接才能实现并发和缩短延迟;
-- 不会压缩请求和响应首部,从而导致不必要的网络流量;
-- 不支持有效的资源优先级,致使底层 TCP 连接的利用率低下。
-
-## 二进制分帧层
-
-HTTP/2.0 将报文分成 HEADERS 帧和 DATA 帧,它们都是二进制格式的。
-
-
-
-在通信过程中,只会有一个 TCP 连接存在,它承载了任意数量的双向数据流(Stream)。一个数据流都有一个唯一标识符和可选的优先级信息,用于承载双向信息。消息(Message)是与逻辑请求或响应消息对应的完整的一系列帧。帧(Fram)是最小的通信单位,来自不同数据流的帧可以交错发送,然后再根据每个帧头的数据流标识符重新组装。
-
-
-
-## 服务端推送
-
-HTTP/2.0 在客户端请求一个资源时,会把相关的资源一起发送给客户端,客户端就不需要再次发起请求了。例如客户端请求 page.html 页面,服务端就把 script.js 和 style.css 等与之相关的资源一起发给客户端。
-
-
-
-## 首部压缩
-
-HTTP/1.1 的首部带有大量信息,而且每次都要重复发送。HTTP/2.0 要求客户端和服务器同时维护和更新一个包含之前见过的首部字段表,从而避免了重复传输。不仅如此,HTTP/2.0 也使用 Huffman 编码对首部字段进行压缩。
-
-
# 参考资料
@@ -1059,6 +870,7 @@ HTTP/1.1 的首部带有大量信息,而且每次都要重复发送。HTTP/2.0
- [MDN : HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP)
- [HTTP/2 简介](https://developers.google.com/web/fundamentals/performance/http2/?hl=zh-cn)
- [htmlspecialchars](http://php.net/manual/zh/function.htmlspecialchars.php)
+- [Difference between file URI and URL in java](http://java2db.com/java-io/how-to-get-and-the-difference-between-file-uri-and-url-in-java)
- [How to Fix SQL Injection Using Java PreparedStatement & CallableStatement](https://software-security.sans.org/developer-how-to/fix-sql-injection-in-java-using-prepared-callable-statement)
- [浅谈 HTTP 中 Get 与 Post 的区别](https://www.cnblogs.com/hyddd/archive/2009/03/31/1426026.html)
- [Are http:// and www really necessary?](https://www.webdancers.com/are-http-and-www-necesary/)
@@ -1075,10 +887,6 @@ HTTP/1.1 的首部带有大量信息,而且每次都要重复发送。HTTP/2.0
- [COOKIE 和 SESSION 有什么区别](https://www.zhihu.com/question/19786827)
- [Cookie/Session 的机制与安全](https://harttle.land/2015/08/10/cookie-session.html)
- [HTTPS 证书原理](https://shijianan.com/2017/06/11/https/)
-- [维基百科:跨站脚本](https://zh.wikipedia.org/wiki/%E8%B7%A8%E7%B6%B2%E7%AB%99%E6%8C%87%E4%BB%A4%E7%A2%BC)
-- [维基百科:SQL 注入攻击](https://zh.wikipedia.org/wiki/SQL%E8%B3%87%E6%96%99%E9%9A%B1%E7%A2%BC%E6%94%BB%E6%93%8A)
-- [维基百科:跨站点请求伪造](https://zh.wikipedia.org/wiki/%E8%B7%A8%E7%AB%99%E8%AF%B7%E6%B1%82%E4%BC%AA%E9%80%A0)
-- [维基百科:拒绝服务攻击](https://zh.wikipedia.org/wiki/%E9%98%BB%E6%96%B7%E6%9C%8D%E5%8B%99%E6%94%BB%E6%93%8A)
- [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/)
diff --git a/notes/Java IO.md b/notes/Java IO.md
index 08dc80f4..1e3aedcd 100644
--- a/notes/Java IO.md
+++ b/notes/Java IO.md
@@ -40,8 +40,7 @@ File 类可以用于表示文件和目录的信息,但是它不表示文件的
递归地输出一个目录下所有文件:
```java
-public static void listAllFiles(File dir)
-{
+public static void listAllFiles(File dir) {
if (dir == null || !dir.exists()) {
return;
}
@@ -60,17 +59,19 @@ public static void listAllFiles(File dir)
使用字节流操作进行文件复制:
```java
-public static void copyFile(String src, String dist) throws IOException
-{
+public static void copyFile(String src, String dist) throws IOException {
+
FileInputStream in = new FileInputStream(src);
FileOutputStream out = new FileOutputStream(dist);
byte[] buffer = new byte[20 * 1024];
+
// read() 最多读取 buffer.length 个字节
// 返回的是实际读取的个数
// 返回 -1 的时候表示读到 eof,即文件尾
while (in.read(buffer, 0, buffer.length) != -1) {
out.write(buffer);
}
+
in.close();
out.close();
}
@@ -78,7 +79,11 @@ public static void copyFile(String src, String dist) throws IOException
-Java I/O 使用了装饰者模式来实现。以 InputStream 为例,InputStream 是抽象组件,FileInputStream 是 InputStream 的子类,属于具体组件,提供了字节流的输入操作。FilterInputStream 属于抽象装饰者,装饰者用于装饰组件,为组件提供额外的功能,例如 BufferedInputStream 为 FileInputStream 提供缓存的功能。
+Java I/O 使用了装饰者模式来实现。以 InputStream 为例,
+
+- InputStream 是抽象组件;
+- FileInputStream 是 InputStream 的子类,属于具体组件,提供了字节流的输入操作;
+- FilterInputStream 属于抽象装饰者,装饰者用于装饰组件,为组件提供额外的功能,例如 BufferedInputStream 为 FileInputStream 提供缓存的功能。
实例化一个具有缓存功能的字节流对象时,只需要在 FileInputStream 对象上再套一层 BufferedInputStream 对象即可。
@@ -99,8 +104,7 @@ DataInputStream 装饰者提供了对更多数据类型进行输入的操作,
逐行输出文本文件的内容:
```java
-public static void readFileContent(String filePath) throws IOException
-{
+public static void readFileContent(String filePath) throws IOException {
FileReader fileReader = new FileReader(filePath);
BufferedReader bufferedReader = new BufferedReader(fileReader);
String line;
@@ -126,7 +130,7 @@ UTF-16be 中的 be 指的是 Big Endian,也就是大端。相应地也有 UTF-
Java 使用双字节编码 UTF-16be,这不是指 Java 只支持这一种编码方式,而是说 char 这种类型使用 UTF-16be 进行编码。char 类型占 16 位,也就是两个字节,Java 使用这种双字节编码是为了让一个中文或者一个英文都能使用一个 char 来存储。
-String 可以看成一个字符序列,可以指定一个编码方式将它转换为字节序列,也可以指定一个编码方式将一个字节序列转换为 String。
+String 可以看成一个字符序列,可以指定一个编码方式将它编码为字节序列,也可以指定一个编码方式将一个字节序列解码为 String。
```java
String str1 = "中文";
@@ -151,8 +155,7 @@ byte[] bytes = str1.getBytes();
序列化的类需要实现 Serializable 接口,它只是一个标准,没有任何方法需要实现,但是如果不去实现它的话而进行序列化,会抛出异常。
```java
-public static void main(String[] args) throws IOException, ClassNotFoundException
-{
+public static void main(String[] args) throws IOException, ClassNotFoundException {
A a1 = new A(123, "abc");
String objectFile = "file/a1";
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(objectFile));
@@ -165,20 +168,17 @@ public static void main(String[] args) throws IOException, ClassNotFoundExceptio
System.out.println(a2);
}
-private static class A implements Serializable
-{
+private static class A implements Serializable {
private int x;
private String y;
- A(int x, String y)
- {
+ A(int x, String y) {
this.x = x;
this.y = y;
}
@Override
- public String toString()
- {
+ public String toString() {
return "x = " + x + " " + "y = " + y;
}
}
@@ -188,7 +188,7 @@ private static class A implements Serializable
transient 关键字可以使一些属性不会被序列化。
-**ArrayList 序列化和反序列化的实现** :ArrayList 中存储数据的数组是用 transient 修饰的,因为这个数组是动态扩展的,并不是所有的空间都被使用,因此就不需要所有的内容都被序列化。通过重写序列化和反序列化方法,使得可以只序列化数组中有内容的那部分数据。
+ArrayList 中存储数据的数组是用 transient 修饰的,因为这个数组是动态扩展的,并不是所有的空间都被使用,因此就不需要所有的内容都被序列化。通过重写序列化和反序列化方法,使得可以只序列化数组中有内容的那部分数据。
```java
private transient Object[] elementData;
@@ -205,7 +205,7 @@ Java 中的网络支持:
## InetAddress
-没有公有构造函数,只能通过静态方法来创建实例。
+没有公有的构造函数,只能通过静态方法来创建实例。
```java
InetAddress.getByName(String host);
@@ -217,19 +217,24 @@ InetAddress.getByAddress(byte[] address);
可以直接从 URL 中读取字节流数据。
```java
-public static void main(String[] args) throws IOException
-{
+public static void main(String[] args) throws IOException {
+
URL url = new URL("http://www.baidu.com");
- // 字节流
+
+ /* 字节流 */
InputStream is = url.openStream();
- // 字符流
+
+ /* 字符流 */
InputStreamReader isr = new InputStreamReader(is, "utf-8");
+
+ /* 提供缓存功能 */
BufferedReader br = new BufferedReader(isr);
- String line = br.readLine();
- while (line != null) {
+
+ String line;
+ while ((line = br.readLine()) != null) {
System.out.println(line);
- line = br.readLine();
}
+
br.close();
}
```
@@ -253,7 +258,7 @@ public static void main(String[] args) throws IOException
- [Java NIO 浅析](https://tech.meituan.com/nio.html)
- [IBM: NIO 入门](https://www.ibm.com/developerworks/cn/education/java/j-nio/j-nio.html)
-新的输入/输出 (NIO) 库是在 JDK 1.4 中引入的。NIO 弥补了原来的 I/O 的不足,提供了高速的、面向块的 I/O。
+新的输入/输出 (NIO) 库是在 JDK 1.4 中引入的,弥补了原来的 I/O 的不足,提供了高速的、面向块的 I/O。
## 流与块
@@ -271,7 +276,7 @@ I/O 包和 NIO 已经很好地集成了,java.io.\* 已经以 NIO 为基础重
通道 Channel 是对原 I/O 包中的流的模拟,可以通过它读取和写入数据。
-通道与流的不同之处在于,流只能在一个方向上移动,(一个流必须是 InputStream 或者 OutputStream 的子类),而通道是双向的,可以用于读、写或者同时用于读写。
+通道与流的不同之处在于,流只能在一个方向上移动(一个流必须是 InputStream 或者 OutputStream 的子类),而通道是双向的,可以用于读、写或者同时用于读写。
通道包括以下类型:
@@ -329,21 +334,41 @@ I/O 包和 NIO 已经很好地集成了,java.io.\* 已经以 NIO 为基础重
以下展示了使用 NIO 快速复制文件的实例:
```java
-public static void fastCopy(String src, String dist) throws IOException
-{
- FileInputStream fin = new FileInputStream(src); /* 获取源文件的输入字节流 */
- FileChannel fcin = fin.getChannel(); /* 获取输入字节流的文件通道 */
- FileOutputStream fout = new FileOutputStream(dist); /* 获取目标文件的输出字节流 */
- FileChannel fcout = fout.getChannel(); /* 获取输出字节流的通道 */
- ByteBuffer buffer = ByteBuffer.allocateDirect(1024); /* 为缓冲区分配 1024 个字节 */
+public static void fastCopy(String src, String dist) throws IOException {
+
+ /* 获得源文件的输入字节流 */
+ FileInputStream fin = new FileInputStream(src);
+
+ /* 获取输入字节流的文件通道 */
+ FileChannel fcin = fin.getChannel();
+
+ /* 获取目标文件的输出字节流 */
+ FileOutputStream fout = new FileOutputStream(dist);
+
+ /* 获取输出字节流的通道 */
+ FileChannel fcout = fout.getChannel();
+
+ /* 为缓冲区分配 1024 个字节 */
+ ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
+
while (true) {
- int r = fcin.read(buffer); /* 从输入通道中读取数据到缓冲区中 */
- if (r == -1) { /* read() 返回 -1 表示 EOF */
+
+ /* 从输入通道中读取数据到缓冲区中 */
+ int r = fcin.read(buffer);
+
+ /* read() 返回 -1 表示 EOF */
+ if (r == -1) {
break;
}
- buffer.flip(); /* 切换读写 */
- fcout.write(buffer); /* 把缓冲区的内容写入输出文件中 */
- buffer.clear(); /* 清空缓冲区 */
+
+ /* 切换读写 */
+ buffer.flip();
+
+ /* 把缓冲区的内容写入输出文件中 */
+ fcout.write(buffer);
+
+ /* 清空缓冲区 */
+ buffer.clear();
}
}
```
@@ -448,10 +473,10 @@ while (true) {
## 套接字 NIO 实例
```java
-public class NIOServer
-{
- public static void main(String[] args) throws IOException
- {
+public class NIOServer {
+
+ public static void main(String[] args) throws IOException {
+
Selector selector = Selector.open();
ServerSocketChannel ssChannel = ServerSocketChannel.open();
@@ -463,32 +488,45 @@ public class NIOServer
serverSocket.bind(address);
while (true) {
+
selector.select();
Set keys = selector.selectedKeys();
Iterator keyIterator = keys.iterator();
+
while (keyIterator.hasNext()) {
+
SelectionKey key = keyIterator.next();
+
if (key.isAcceptable()) {
+
ServerSocketChannel ssChannel1 = (ServerSocketChannel) key.channel();
+
// 服务器会为每个新连接创建一个 SocketChannel
SocketChannel sChannel = ssChannel1.accept();
sChannel.configureBlocking(false);
+
// 这个新连接主要用于从客户端读取数据
sChannel.register(selector, SelectionKey.OP_READ);
+
} else if (key.isReadable()) {
+
SocketChannel sChannel = (SocketChannel) key.channel();
System.out.println(readDataFromSocketChannel(sChannel));
sChannel.close();
}
+
keyIterator.remove();
}
}
}
private static String readDataFromSocketChannel(SocketChannel sChannel) throws IOException {
+
ByteBuffer buffer = ByteBuffer.allocate(1024);
StringBuilder data = new StringBuilder();
+
while (true) {
+
buffer.clear();
int n = sChannel.read(buffer);
if (n == -1) {
@@ -509,10 +547,9 @@ public class NIOServer
```
```java
-public class NIOClient
-{
- public static void main(String[] args) throws IOException
- {
+public class NIOClient {
+
+ public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 8888);
OutputStream out = socket.getOutputStream();
String s = "hello world";
@@ -526,9 +563,9 @@ public class NIOClient
内存映射文件 I/O 是一种读和写文件数据的方法,它可以比常规的基于流或者基于通道的 I/O 快得多。
-向内存映射文件写入可能是危险的,仅只是改变数组的单个元素这样的简单操作,就可能会直接修改磁盘上的文件。修改数据与将数据保存到磁盘是没有分开的。
+向内存映射文件写入可能是危险的,只是改变数组的单个元素这样的简单操作,就可能会直接修改磁盘上的文件。修改数据与将数据保存到磁盘是没有分开的。
-下面代码行将文件的前 1024 个字节映射到内存中,map() 方法返回一个 MappedByteBuffer,它是 ByteBuffer 的子类。因此,您可以像使用其他任何 ByteBuffer 一样使用新映射的缓冲区,操作系统会在需要时负责执行映射。
+下面代码行将文件的前 1024 个字节映射到内存中,map() 方法返回一个 MappedByteBuffer,它是 ByteBuffer 的子类。因此,可以像使用其他任何 ByteBuffer 一样使用新映射的缓冲区,操作系统会在需要时负责执行映射。
```java
MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, 1024);
diff --git a/notes/Java 基础.md b/notes/Java 基础.md
index af1c6530..bcb94acc 100644
--- a/notes/Java 基础.md
+++ b/notes/Java 基础.md
@@ -4,7 +4,7 @@
* [缓存池](#缓存池)
* [二、String](#二string)
* [概览](#概览)
- * [String 不可变的好处](#string-不可变的好处)
+ * [不可变的好处](#不可变的好处)
* [String, StringBuffer and StringBuilder](#string,-stringbuffer-and-stringbuilder)
* [String.intern()](#stringintern)
* [三、运算](#三运算)
@@ -153,7 +153,7 @@ public final class String
private final char value[];
```
-## String 不可变的好处
+## 不可变的好处
**1. 可以缓存 hash 值**
@@ -212,7 +212,7 @@ String s5 = "bbb";
System.out.println(s4 == s5); // true
```
-在 Java 7 之前,字符串常量池被放在运行时常量池中,它属于永久代。而在 Java 7,字符串常量池被放在堆中。这是因为永久代的空间有限,在大量使用字符串的场景下会导致 OutOfMemoryError 错误。
+在 Java 7 之前,字符串常量池被放在运行时常量池中,它属于永久代。而在 Java 7,字符串常量池被移到 Native Method 中。这是因为永久代的空间有限,在大量使用字符串的场景下会导致 OutOfMemoryError 错误。
- [StackOverflow : What is String interning?](https://stackoverflow.com/questions/10578984/what-is-string-interning)
- [深入解析 String#intern](https://tech.meituan.com/in_depth_understanding_string_intern.html)
@@ -223,7 +223,7 @@ System.out.println(s4 == s5); // true
Java 的参数是以值传递的形式传入方法中,而不是引用传递。
-Dog dog 的 dog 是一个指针,存储的是对象的地址。在将一个参数传入一个方法时,本质上是将对象的地址以值的方式传递到形参中。
+以下代码中 Dog dog 的 dog 是一个指针,存储的是对象的地址。在将一个参数传入一个方法时,本质上是将对象的地址以值的方式传递到形参中。但是如果在方法中改变对象的字段值会改变原对象该字段值,因为改变的是同一个地址指向的内容。
```java
public class Dog {
@@ -549,7 +549,7 @@ SuperExtendExample.func()
## 重写与重载
-- 重写(Override)存在于继承体系中,指子类实现了一个与父类在方法声明上完全相同的一个方法;
+- 重写(Override)存在于继承体系中,指子类实现了一个与父类在方法声明上完全相同的一个方法,子类的返回值类型要等于或者小于父类的返回值;
- 重载(Overload)存在于同一个类中,指一个方法与已经存在的方法名称上相同,但是参数类型、个数、顺序至少有一个不同。应该注意的是,返回值不同,其它都相同不算是重载。
@@ -583,19 +583,7 @@ protected void finalize() throws Throwable {}
## equals()
-**1. equals() 与 == 的区别**
-
-- 对于基本类型,== 判断两个值是否相等,基本类型没有 equals() 方法。
-- 对于引用类型,== 判断两个实例是否引用同一个对象,而 equals() 判断引用的对象是否等价。
-
-```java
-Integer x = new Integer(1);
-Integer y = new Integer(1);
-System.out.println(x.equals(y)); // true
-System.out.println(x == y); // false
-```
-
-**2. 等价关系**
+**1. 等价关系**
(一)自反性
@@ -632,6 +620,18 @@ x.equals(y) == x.equals(y); // true
x.equals(null); // false;
```
+**2. equals() 与 ==**
+
+- 对于基本类型,== 判断两个值是否相等,基本类型没有 equals() 方法。
+- 对于引用类型,== 判断两个实例是否引用同一个对象,而 equals() 判断引用的对象是否等价,根据引用对象 equals() 方法的具体实现来进行比较。
+
+```java
+Integer x = new Integer(1);
+Integer y = new Integer(1);
+System.out.println(x.equals(y)); // true
+System.out.println(x == y); // false
+```
+
**3. 实现**
- 检查是否为同一个对象的引用,如果是直接返回 true;
@@ -763,10 +763,10 @@ try {
```
```html
-java.lang.CloneNotSupportedException: CloneTest
+java.lang.CloneNotSupportedException: CloneExample
```
-以上抛出了 CloneNotSupportedException,这是因为 CloneTest 没有实现 Cloneable 接口。
+以上抛出了 CloneNotSupportedException,这是因为 CloneExample 没有实现 Cloneable 接口。
```java
public class CloneExample implements Cloneable {
@@ -1005,7 +1005,7 @@ public class A {
**4. 静态内部类**
-非静态内部类依赖于需要外部类的实例,而静态内部类不需要。
+非静态内部类依赖于外部类的实例,而静态内部类不需要。
```java
public class OuterClass {
@@ -1177,7 +1177,6 @@ Java 注解是附加在代码中的一些元信息,用于一些工具在编译
- Java 支持自动垃圾回收,而 C++ 需要手动回收。
- Java 不支持多重继承,只能通过实现多个接口来达到相同目的,而 C++ 支持多重继承。
- Java 不支持操作符重载,虽然可以对两个 String 对象支持加法运算,但是这是语言内置支持的操作,不属于操作符重载,而 C++ 可以。
-- Java 内置了线程的支持,而 C++ 需要依靠第三方库。
- Java 的 goto 是保留字,但是不可用,C++ 可以使用 goto。
- Java 不支持条件编译,C++ 通过 #ifdef #ifndef 等预处理命令从而实现条件编译。
diff --git a/notes/Java 容器.md b/notes/Java 容器.md
index 9e6e2ac3..bd4ecfdb 100644
--- a/notes/Java 容器.md
+++ b/notes/Java 容器.md
@@ -8,9 +8,13 @@
* [三、源码分析](#三源码分析)
* [ArrayList](#arraylist)
* [Vector](#vector)
+ * [CopyOnWriteArrayList](#copyonwritearraylist)
* [LinkedList](#linkedlist)
* [HashMap](#hashmap)
* [ConcurrentHashMap](#concurrenthashmap)
+ * [LinkedHashMap](#linkedhashmap)
+ * [WeekHashMap](#weekhashmap)
+* [附录](#附录)
* [参考资料](#参考资料)
@@ -21,39 +25,39 @@
## Collection
-
+
### 1. Set
-- HashSet:基于哈希实现,支持快速查找,但不支持有序性操作,例如根据一个范围查找元素的操作。并且失去了元素的插入顺序信息,也就是说使用 Iterator 遍历 HashSet 得到的结果是不确定的;
+- HashSet:基于哈希表实现,支持快速查找。但不支持有序性操作,例如根据一个范围查找元素的操作。并且失去了元素的插入顺序信息,也就是说使用 Iterator 遍历 HashSet 得到的结果是不确定的。
-- TreeSet:基于红黑树实现,支持有序性操作,但是查找效率不如 HashSet,HashSet 查找时间复杂度为 O(1),TreeSet 则为 O(logN);
+- TreeSet:基于红黑树实现,支持有序性操作,但是查找效率不如 HashSet,HashSet 查找时间复杂度为 O(1),TreeSet 则为 O(logN)。
-- LinkedHashSet:具有 HashSet 的查找效率,且内部使用链表维护元素的插入顺序。
+- LinkedHashSet:具有 HashSet 的查找效率,且内部使用双向链表维护元素的插入顺序。
### 2. List
-- ArrayList:基于动态数组实现,支持随机访问;
+- ArrayList:基于动态数组实现,支持随机访问。
-- Vector:和 ArrayList 类似,但它是线程安全的;
+- Vector:和 ArrayList 类似,但它是线程安全的。
- LinkedList:基于双向链表实现,只能顺序访问,但是可以快速地在链表中间插入和删除元素。不仅如此,LinkedList 还可以用作栈、队列和双向队列。
### 3. Queue
-- LinkedList:可以用它来支持双向队列;
+- LinkedList:可以用它来实现双向队列。
- PriorityQueue:基于堆结构实现,可以用它来实现优先队列。
## Map
-
+
-- HashMap:基于哈希实现;
+- HashMap:基于哈希表实现;
- HashTable:和 HashMap 类似,但它是线程安全的,这意味着同一时刻多个线程可以同时写入 HashTable 并且不会导致数据不一致。它是遗留类,不应该去使用它。现在可以使用 ConcurrentHashMap 来支持线程安全,并且 ConcurrentHashMap 的效率会更高,因为 ConcurrentHashMap 引入了分段锁。
-- LinkedHashMap:使用链表来维护元素的顺序,顺序为插入顺序或者最近最少使用(LRU)顺序。
+- LinkedHashMap:使用双向链表来维护元素的顺序,顺序为插入顺序或者最近最少使用(LRU)顺序。
- TreeMap:基于红黑树实现。
@@ -61,7 +65,7 @@
## 迭代器模式
-
+
Collection 实现了 Iterable 接口,其中的 iterator() 方法能够产生一个 Iterator 对象,通过这个对象就可以迭代遍历 Collection 中的元素。
@@ -85,14 +89,14 @@ java.util.Arrays#asList() 可以把数组类型转换为 List 类型。
public static List asList(T... a)
```
-如果要将数组类型转换为 List 类型,应该注意的是 asList() 的参数为泛型的变长参数,因此不能使用基本类型数组作为参数,只能使用相应的包装类型数组。
+应该注意的是 asList() 的参数为泛型的变长参数,不能使用基本类型数组作为参数,只能使用相应的包装类型数组。
```java
Integer[] arr = {1, 2, 3};
List list = Arrays.asList(arr);
```
-也可以使用以下方式生成 List。
+也可以使用以下方式调用 asList():
```java
List list = Arrays.asList(1,2,3);
@@ -108,7 +112,7 @@ List list = Arrays.asList(1,2,3);
### 1. 概览
-实现了 RandomAccess 接口,因此支持随机访问,这是理所当然的,因为 ArrayList 是基于数组实现的。
+实现了 RandomAccess 接口,因此支持随机访问。这是理所当然的,因为 ArrayList 是基于数组实现的。
```java
public class ArrayList extends AbstractList
@@ -123,7 +127,9 @@ private static final int DEFAULT_CAPACITY = 10;
### 2. 序列化
-基于数组实现,保存元素的数组使用 transient 修饰,该关键字声明数组默认不会被序列化。ArrayList 具有动态扩容特性,因此保存元素的数组不一定都会被使用,那么就没必要全部进行序列化。ArrayList 重写了 writeObject() 和 readObject() 来控制只序列化数组中有元素填充那部分内容。
+ArrayList 基于数组实现,并且具有动态扩容特性,因此保存元素的数组不一定都会被使用,那么就没必要全部进行序列化。
+
+保存元素的数组 elementData 使用 transient 修饰,该关键字声明数组默认不会被序列化。ArrayList 重写了 writeObject() 和 readObject() 来控制只序列化数组中有元素填充那部分内容。
```java
transient Object[] elementData; // non-private to simplify nested class access
@@ -171,7 +177,7 @@ private void grow(int minCapacity) {
### 4. 删除元素
-需要调用 System.arraycopy() 将 index+1 后面的元素都复制到 index 位置上。
+需要调用 System.arraycopy() 将 index+1 后面的元素都复制到 index 位置上,该操作的时间复杂度为 O(N),可以看出 ArrayList 删除元素的代价是非常高的。
```java
public E remove(int index) {
@@ -235,12 +241,12 @@ public synchronized E get(int index) {
}
```
-### 2. ArrayList 与 Vector
+### 2. 与 ArrayList 的区别
- Vector 是同步的,因此开销就比 ArrayList 要大,访问速度更慢。最好使用 ArrayList 而不是 Vector,因为同步操作完全可以由程序员自己来控制;
- Vector 每次扩容请求其大小的 2 倍空间,而 ArrayList 是 1.5 倍。
-### 3. Vector 替代方案
+### 3. 替代方案
为了获得线程安全的 ArrayList,可以使用 `Collections.synchronizedList();` 得到一个线程安全的 ArrayList。
@@ -255,7 +261,15 @@ List synList = Collections.synchronizedList(list);
List list = new CopyOnWriteArrayList<>();
```
-CopyOnWriteArrayList 是一种 CopyOnWrite 容器,从以下源码看出:读取元素是从原数组读取;添加元素是在复制的新数组上。读写分离,因而可以在并发条件下进行不加锁的读取,读取效率高,适用于读操作远大于写操作的场景。
+## CopyOnWriteArrayList
+
+### 读写分离
+
+写操作在一个复制的数组上进行,读操作还是在原始数组中进行,读写分离,互不影响。
+
+写操作需要加锁,防止同时并发写入时导致的写入数据丢失。
+
+写操作结束之后需要把原始数组指向新的复制数组。
```java
public boolean add(E e) {
@@ -264,7 +278,7 @@ public boolean add(E e) {
try {
Object[] elements = getArray();
int len = elements.length;
- Object[] newElements = Arrays.copyOf(elements, len + 1);
+ Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
@@ -276,18 +290,31 @@ public boolean add(E e) {
final void setArray(Object[] a) {
array = a;
}
+```
+```java
@SuppressWarnings("unchecked")
private E get(Object[] a, int index) {
return (E) a[index];
}
```
+### 适用场景
+
+CopyOnWriteArrayList 在写操作的同时允许读操作,大大提高了读操作的性能,因此很适合读多写少的应用场景。
+
+但是 CopyOnWriteArrayList 有其缺陷:
+
+- 内存占用:在写操作时需要复制一个新的数组,使得内存占用为原来的两倍左右;
+- 数据不一致:读操作不能读取实时性的数据,因为部分写操作的数据还未同步到读数组中。
+
+所以 CopyOnWriteArrayList 不适合内存敏感以及对实时性要求很高的场景。
+
## LinkedList
### 1. 概览
-基于双向链表实现,内部使用 Node 来存储链表节点信息。
+基于双向链表实现,使用 Node 存储链表节点信息。
```java
private static class Node {
@@ -297,14 +324,14 @@ private static class Node {
}
```
-每个链表存储了 Head 和 Tail 指针:
+每个链表存储了 first 和 last 指针:
```java
transient Node first;
transient Node last;
```
-
+
### 2. ArrayList 与 LinkedList
@@ -450,7 +477,7 @@ public V put(K key, V value) {
}
```
-HashMap 允许插入键为 null 的键值对。因为无法调用 null 的 hashCode(),也就无法确定该键值对的桶下标,只能通过强制指定一个桶下标来存放。HashMap 使用第 0 个桶存放键为 null 的键值对。
+HashMap 允许插入键为 null 的键值对。但是因为无法调用 null 的 hashCode() 方法,也就无法确定该键值对的桶下标,只能通过强制指定一个桶下标来存放。HashMap 使用第 0 个桶存放键为 null 的键值对。
```java
private V putForNullKey(V value) {
@@ -577,10 +604,10 @@ static int indexFor(int h, int length) {
| 参数 | 含义 |
| :--: | :-- |
-| capacity | table 的容量大小,默认为 16,需要注意的是 capacity 必须保证为 2 的 n 次方。|
+| capacity | table 的容量大小,默认为 16。需要注意的是 capacity 必须保证为 2 的 n 次方。|
| size | table 的实际使用量。 |
| threshold | size 的临界值,size 必须小于 threshold,如果大于等于,就必须进行扩容操作。 |
-| load_factor | 装载因子,table 能够使用的比例,threshold = capacity * load_factor。|
+| loadFactor | 装载因子,table 能够使用的比例,threshold = capacity * loadFactor。|
```java
static final int DEFAULT_INITIAL_CAPACITY = 16;
@@ -650,14 +677,14 @@ void transfer(Entry[] newTable) {
在进行扩容时,需要把键值对重新放到对应的桶上。HashMap 使用了一个特殊的机制,可以降低重新计算桶下标的操作。
-假设原数组长度 capacity 为 8,扩容之后 new capacity 为 16:
+假设原数组长度 capacity 为 16,扩容之后 new capacity 为 32:
```html
capacity : 00010000
new capacity : 00100000
```
-对于一个 Key,它的哈希值如果在第 6 位上为 0,那么取模得到的结果和之前一样;如果为 1,那么得到的结果为原来的结果 + 8。
+对于一个 Key,它的哈希值如果在第 6 位上为 0,那么取模得到的结果和之前一样;如果为 1,那么得到的结果为原来的结果 +16。
### 7. 扩容-计算数组容量
@@ -825,15 +852,287 @@ public int size() {
}
```
-
### 3. JDK 1.8 的改动
-JDK 1.7 使用分段锁机制来实现并发更新操作,核心类为 Segment,它继承自重入锁 ReentrantLock,并发程度与 Segment 数量相等。
+JDK 1.7 使用分段锁机制来实现并发更新操作,核心类为 Segment,它继承自重入锁 ReentrantLock,并发度与 Segment 数量相等。
JDK 1.8 使用了 CAS 操作来支持更高的并发度,在 CAS 操作失败时使用内置锁 synchronized。
并且 JDK 1.8 的实现也在链表过长时会转换为红黑树。
+## LinkedHashMap
+
+### 存储结构
+
+继承自 HashMap,因此具有和 HashMap 一样的快速查找特性。
+
+```java
+public class LinkedHashMap extends HashMap implements Map
+```
+
+内存维护了一个双向链表,用来维护插入顺序或者 LRU 顺序。
+
+```java
+/**
+ * The head (eldest) of the doubly linked list.
+ */
+transient LinkedHashMap.Entry head;
+
+/**
+ * The tail (youngest) of the doubly linked list.
+ */
+transient LinkedHashMap.Entry tail;
+```
+
+accessOrder 决定了顺序,默认为 false,此时使用的是插入顺序。
+
+```java
+final boolean accessOrder;
+```
+
+LinkedHashMap 最重要的是以下用于维护顺序的函数,它们会在 put、get 等方法中调用。
+
+```java
+void afterNodeAccess(Node p) { }
+void afterNodeInsertion(boolean evict) { }
+```
+
+### afterNodeAccess()
+
+当一个节点被访问时,如果 accessOrder 为 true,则会将 该节点移到链表尾部。也就是说指定为 LRU 顺序之后,在每次访问一个节点时,会将这个节点移到链表尾部,保证链表尾部是最近访问的节点,那么链表首部就是最近最久未使用的节点。
+
+```java
+void afterNodeAccess(Node e) { // move node to last
+ LinkedHashMap.Entry last;
+ if (accessOrder && (last = tail) != e) {
+ LinkedHashMap.Entry p =
+ (LinkedHashMap.Entry)e, b = p.before, a = p.after;
+ p.after = null;
+ if (b == null)
+ head = a;
+ else
+ b.after = a;
+ if (a != null)
+ a.before = b;
+ else
+ last = b;
+ if (last == null)
+ head = p;
+ else {
+ p.before = last;
+ last.after = p;
+ }
+ tail = p;
+ ++modCount;
+ }
+}
+```
+
+### afterNodeInsertion()
+
+在 put 等操作之后执行,当 removeEldestEntry() 方法返回 ture 时会移除最晚的节点,也就是链表首部节点 first。
+
+evict 只有在构建 Map 的时候才为 false,在这里为 true。
+
+```java
+void afterNodeInsertion(boolean evict) { // possibly remove eldest
+ LinkedHashMap.Entry first;
+ if (evict && (first = head) != null && removeEldestEntry(first)) {
+ K key = first.key;
+ removeNode(hash(key), key, null, false, true);
+ }
+}
+```
+
+removeEldestEntry() 默认为 false,如果需要让它为 true,需要继承 LinkedHashMap 并且覆盖这个方法的实现,这在实现 LRU 的缓存中特别有用,通过移除最近最久未使用的节点,从而保证缓存空间足够,并且缓存的数据都是热点数据。
+
+```java
+protected boolean removeEldestEntry(Map.Entry eldest) {
+ return false;
+ }
+```
+
+### LRU 缓存
+
+以下是使用 LinkedHashMap 实现的一个 LRU 缓存:
+
+- 设定最大缓存空间 MAX_ENTRIES 为 3;
+- 使用 LinkedHashMap 的构造函数将 accessOrder 设置为 true,开启 LUR 顺序;
+- 覆盖 removeEldestEntry() 方法实现,在节点多于 MAX_ENTRIES 就会将最近最久未使用的数据移除。
+
+```java
+class LRUCache extends LinkedHashMap {
+ private static final int MAX_ENTRIES = 3;
+
+ protected boolean removeEldestEntry(Map.Entry eldest) {
+ return size() > MAX_ENTRIES;
+ }
+
+ LRUCache() {
+ super(MAX_ENTRIES, 0.75f, true);
+ }
+}
+```
+
+```java
+public static void main(String[] args) {
+ LRUCache cache = new LRUCache<>();
+ cache.put(1, "a");
+ cache.put(2, "b");
+ cache.put(3, "c");
+ cache.get(1);
+ cache.put(4, "d");
+ System.out.println(cache.keySet());
+}
+```
+
+```html
+[3, 1, 4]
+```
+
+## WeekHashMap
+
+### 存储结构
+
+WeakHashMap 的 Entry 继承自 WeakReference,被 WeakReference 关联的对象在下一次垃圾回收时会被回收。
+
+WeakHashMap 主要用来实现缓存,通过使用 WeakHashMap 来引用缓存对象,由 JVM 对这部分缓存进行回收。
+
+```java
+private static class Entry extends WeakReference