auto commit

This commit is contained in:
CyC2018
2018-03-14 20:44:44 +08:00
parent 9f986651b0
commit 55628c8a42
21 changed files with 317 additions and 315 deletions
+11 -11
View File
@@ -39,20 +39,20 @@ Git 的中心服务器用来交换每个用户的修改。没有中心服务器
# Git 工作流
<div align="center"> <img src="index_files/a1198642-9159-4d88-8aec-c3b04e7a2563.jpg"/> </div><br>
<div align="center"> <img src="../pics//a1198642-9159-4d88-8aec-c3b04e7a2563.jpg"/> </div><br>
新建一个仓库之后,当前目录就成为了工作区,工作区下有一个隐藏目录 .git,它属于 Git 的版本库。
Git 版本库有一个称为 stage 的暂存区,还有自动创建的 master 分支以及指向分支的 HEAD 指针。
<div align="center"> <img src="index_files/46f66e88-e65a-4ad0-a060-3c63fe22947c.png"/> </div><br>
<div align="center"> <img src="../pics//46f66e88-e65a-4ad0-a060-3c63fe22947c.png"/> </div><br>
- git add files 把文件的修改添加到暂存区
- git commit 把暂存区的修改提交到当前分支,提交之后暂存区就被清空了
- git reset -- files 使用当前分支上的修改覆盖暂缓区,用来撤销最后一次 git add files
- git checkout -- files 使用暂存区的修改覆盖工作目录,用来撤销本地修改
<div align="center"> <img src="index_files/17976404-95f5-480e-9cb4-250e6aa1d55f.png"/> </div><br>
<div align="center"> <img src="../pics//17976404-95f5-480e-9cb4-250e6aa1d55f.png"/> </div><br>
可以跳过暂存区域直接从分支中取出修改或者直接提交修改到分支中
@@ -63,25 +63,25 @@ Git 版本库有一个称为 stage 的暂存区,还有自动创建的 master
Git 把每次提交都连成一条时间线。分支使用指针来实现,例如 master 分支指针指向时间线的最后一个节点,也就是最后一次提交。HEAD 指针指向的是当前分支。
<div align="center"> <img src="index_files/fb546e12-e1fb-4b72-a1fb-8a7f5000dce6.jpg"/> </div><br>
<div align="center"> <img src="../pics//fb546e12-e1fb-4b72-a1fb-8a7f5000dce6.jpg"/> </div><br>
新建分支是新建一个指针指向时间线的最后一个节点,并让 HEAD 指针指向新分支表示新分支成为当前分支。
<div align="center"> <img src="index_files/bc775758-89ab-4805-9f9c-78b8739cf780.jpg"/> </div><br>
<div align="center"> <img src="../pics//bc775758-89ab-4805-9f9c-78b8739cf780.jpg"/> </div><br>
每次提交只会让当前分支向前移动,而其它分支不会移动。
<div align="center"> <img src="index_files/5292faa6-0141-4638-bf0f-bb95b081dcba.jpg"/> </div><br>
<div align="center"> <img src="../pics//5292faa6-0141-4638-bf0f-bb95b081dcba.jpg"/> </div><br>
合并分支也只需要改变指针即可。
<div align="center"> <img src="index_files/1164a71f-413d-494a-9cc8-679fb6a2613d.jpg"/> </div><br>
<div align="center"> <img src="../pics//1164a71f-413d-494a-9cc8-679fb6a2613d.jpg"/> </div><br>
# 冲突
当两个分支都对同一个文件进行了修改,在分支合并时就会产生冲突。
<div align="center"> <img src="index_files/58e57a21-6b6b-40b6-af85-956dd4e0f55a.jpg"/> </div><br>
<div align="center"> <img src="../pics//58e57a21-6b6b-40b6-af85-956dd4e0f55a.jpg"/> </div><br>
Git 会使用 <<<<<<< ,======= ,>>>>>>> 标记出不同分支的内容,只需要把不同分支中冲突部分修改成一样就能解决冲突。
@@ -103,7 +103,7 @@ Creating a new branch is quick AND simple.
$ git merge --no-ff -m "merge with no-ff" dev
```
<div align="center"> <img src="index_files/dd78a1fe-1ff3-4bcf-a56f-8c003995beb6.jpg"/> </div><br>
<div align="center"> <img src="../pics//dd78a1fe-1ff3-4bcf-a56f-8c003995beb6.jpg"/> </div><br>
# 分支管理策略
@@ -111,7 +111,7 @@ master 分支应该是非常稳定的,只用来发布新版本;
日常开发在开发分支 dev 上进行。
<div align="center"> <img src="index_files/245fd2fb-209c-4ad5-bc5e-eb5664966a0e.jpg"/> </div><br>
<div align="center"> <img src="../pics//245fd2fb-209c-4ad5-bc5e-eb5664966a0e.jpg"/> </div><br>
# 储藏(Stashing)
@@ -151,6 +151,6 @@ $ ssh-keygen -t rsa -C "youremail@example.com"
# Git 命令一览
<div align="center"> <img src="index_files/7a29acce-f243-4914-9f00-f2988c528412.jpg"/> </div><br>
<div align="center"> <img src="../pics//7a29acce-f243-4914-9f00-f2988c528412.jpg"/> </div><br>
比较详细的地址:http://www.cheat-sheets.org/saved-copy/git-cheat-sheet.pdf
+13 -13
View File
@@ -61,17 +61,17 @@
URI 包含 URL 和 URN,目前 WEB 只有 URL 比较流行,所以见到的基本都是 URL。
<div align="center"> <img src="index_files/4102b7d0-39b9-48d8-82ae-ac4addb7ebfb.jpg"/> </div><br>
<div align="center"> <img src="../pics//4102b7d0-39b9-48d8-82ae-ac4addb7ebfb.jpg"/> </div><br>
## 请求和响应报文
**请求报文**
<div align="center"> <img src="index_files/22b39f77-ac47-4978-91ed-84aaf457644c.jpg"/> </div><br>
<div align="center"> <img src="../pics//22b39f77-ac47-4978-91ed-84aaf457644c.jpg"/> </div><br>
**响应报文**
<div align="center"> <img src="index_files/00d8d345-cd4a-48af-919e-209d2788eca7.jpg"/> </div><br>
<div align="center"> <img src="../pics//00d8d345-cd4a-48af-919e-209d2788eca7.jpg"/> </div><br>
# HTTP 方法
@@ -152,7 +152,7 @@ DELETE /file.html HTTP/1.1
CONNECT www.example.com:443 HTTP/1.1
```
<div align="center"> <img src="index_files/5994928c-3d2d-45bd-abb1-adc4f5f4d775.jpg"/> </div><br>
<div align="center"> <img src="../pics//5994928c-3d2d-45bd-abb1-adc4f5f4d775.jpg"/> </div><br>
## TRACE:追踪路径
@@ -162,7 +162,7 @@ CONNECT www.example.com:443 HTTP/1.1
通常不会使用 TRACE,并且它容易受到 XST 攻击(Cross-Site Tracing,跨站追踪),因此更不会去使用它。
<div align="center"> <img src="index_files/c8637fd2-3aaa-46c4-b7d9-f24d3fa04781.jpg"/> </div><br>
<div align="center"> <img src="../pics//c8637fd2-3aaa-46c4-b7d9-f24d3fa04781.jpg"/> </div><br>
# HTTP 状态码
@@ -204,7 +204,7 @@ CONNECT www.example.com:443 HTTP/1.1
- **401 Unauthorized** :该状态码表示发送的请求需要有认证信息(BASIC 认证、DIGEST 认证)。如果之前已进行过一次请求,则表示用户认证失败。
<div align="center"> <img src="index_files/b1b4cf7d-c54a-4ff1-9741-cd2eea331123.jpg"/> </div><br>
<div align="center"> <img src="../pics//b1b4cf7d-c54a-4ff1-9741-cd2eea331123.jpg"/> </div><br>
- **403 Forbidden** :请求被拒绝,服务器端没有必要给出拒绝的详细理由。
@@ -385,13 +385,13 @@ Expires 字段也可以用于告知缓存服务器该资源什么时候会过期
当浏览器访问一个包含多张图片的 HTML 页面时,除了请求访问 HTML 页面资源,还会请求图片资源,如果每进行一次 HTTP 通信就要断开一次 TCP 连接,连接建立和断开的开销会很大。持久连接只需要建立一次 TCP 连接就能进行多次 HTTP 通信。
<div align="center"> <img src="index_files/c73a0b78-5f46-4d2d-a009-dab2a999b5d8.jpg"/> </div><br>
<div align="center"> <img src="../pics//c73a0b78-5f46-4d2d-a009-dab2a999b5d8.jpg"/> </div><br>
持久连接需要使用 Connection 首部字段进行管理。HTTP/1.1 开始 HTTP 默认是持久化连接的,如果要断开 TCP 连接,需要由客户端或者服务器端提出断开,使用 Connection : close;而在 HTTP/1.1 之前默认是非持久化连接的,如果要维持持续连接,需要使用 Connection : Keep-Alive。
**管线化方式** 可以同时发送多个请求和响应,而不需要发送一个请求然后等待响应之后再发下一个请求。
<div align="center"> <img src="index_files/6943e2af-5a70-4004-8bee-b33d60f39da3.jpg"/> </div><br>
<div align="center"> <img src="../pics//6943e2af-5a70-4004-8bee-b33d60f39da3.jpg"/> </div><br>
## 编码
@@ -448,7 +448,7 @@ Content-Length: 1024
涉及以下首部字段:Accept、Accept-Charset、Accept-Encoding、Accept-Language、Content-Language。
<div align="center"> <img src="index_files/39a27cca-c9af-482a-8a87-5522557a309e.jpg"/> </div><br>
<div align="center"> <img src="../pics//39a27cca-c9af-482a-8a87-5522557a309e.jpg"/> </div><br>
## 虚拟主机
@@ -464,19 +464,19 @@ Content-Length: 1024
使用代理的主要目的是:缓存、网络访问控制以及访问日志记录。
<div align="center"> <img src="index_files/c07035c3-a9ba-4508-8e3c-d8ae4c6ee9ee.jpg"/> </div><br>
<div align="center"> <img src="../pics//c07035c3-a9ba-4508-8e3c-d8ae4c6ee9ee.jpg"/> </div><br>
**网关**
与代理服务器不同的是,网关服务器会将 HTTP 转化为其它协议进行通信,从而请求其它非 HTTP 服务器的服务。
<div align="center"> <img src="index_files/81375888-6be1-476f-9521-42eea3e3154f.jpg"/> </div><br>
<div align="center"> <img src="../pics//81375888-6be1-476f-9521-42eea3e3154f.jpg"/> </div><br>
**隧道**
使用 SSL 等加密手段,为客户端和服务器之间建立一条安全的通信线路。
<div align="center"> <img src="index_files/64b95403-d976-421a-8b45-bac89c0b5185.jpg"/> </div><br>
<div align="center"> <img src="../pics//64b95403-d976-421a-8b45-bac89c0b5185.jpg"/> </div><br>
# HTTPs
@@ -496,7 +496,7 @@ HTTPs 并不是新协议,而是 HTTP 先和 SSL(Secure Socket Layer)通信
HTTPs 采用 **混合的加密机制** ,使用公开密钥加密用于传输对称密钥,之后使用对称密钥加密进行通信。(下图中,共享密钥即对称密钥)
<div align="center"> <img src="index_files/110b1a9b-87cd-45c3-a21d-824623715b33.jpg"/> </div><br>
<div align="center"> <img src="../pics//110b1a9b-87cd-45c3-a21d-824623715b33.jpg"/> </div><br>
## 认证
+13 -13
View File
@@ -65,7 +65,7 @@
# 内存模型
<div align="center"> <img src="index_files/dc695f48-4189-4fc7-b950-ed25f6c80f82.jpg"/> </div><br>
<div align="center"> <img src="../pics//dc695f48-4189-4fc7-b950-ed25f6c80f82.jpg"/> </div><br>
注:白色区域为线程私有的,蓝色区域为线程共享的。
@@ -224,7 +224,7 @@ finalize() 类似 C++ 的析构函数,用来做关闭外部资源等工作。
### 2.1 标记-清除算法
<div align="center"> <img src="index_files/a4248c4b-6c1d-4fb8-a557-86da92d3a294.jpg"/> </div><br>
<div align="center"> <img src="../pics//a4248c4b-6c1d-4fb8-a557-86da92d3a294.jpg"/> </div><br>
将需要回收的对象进行标记,然后清除。
@@ -237,7 +237,7 @@ finalize() 类似 C++ 的析构函数,用来做关闭外部资源等工作。
### 2.2 复制算法
<div align="center"> <img src="index_files/e6b733ad-606d-4028-b3e8-83c3a73a3797.jpg"/> </div><br>
<div align="center"> <img src="../pics//e6b733ad-606d-4028-b3e8-83c3a73a3797.jpg"/> </div><br>
将内存划分为大小相等的两块,每次只使用其中一块,当这一块内存用完了就将还存活的对象复制到另一块上面,然后再把使用过的内存空间进行一次清理。
@@ -247,7 +247,7 @@ finalize() 类似 C++ 的析构函数,用来做关闭外部资源等工作。
### 2.3 标记-整理算法
<div align="center"> <img src="index_files/902b83ab-8054-4bd2-898f-9a4a0fe52830.jpg"/> </div><br>
<div align="center"> <img src="../pics//902b83ab-8054-4bd2-898f-9a4a0fe52830.jpg"/> </div><br>
让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
@@ -262,13 +262,13 @@ finalize() 类似 C++ 的析构函数,用来做关闭外部资源等工作。
## 3. 垃圾收集器
<div align="center"> <img src="index_files/c625baa0-dde6-449e-93df-c3a67f2f430f.jpg"/> </div><br>
<div align="center"> <img src="../pics//c625baa0-dde6-449e-93df-c3a67f2f430f.jpg"/> </div><br>
以上是 HotSpot 虚拟机中的 7 个垃圾收集器,连线表示垃圾收集器可以配合使用。
### 3.1 Serial 收集器
<div align="center"> <img src="index_files/22fda4ae-4dd5-489d-ab10-9ebfdad22ae0.jpg"/> </div><br>
<div align="center"> <img src="../pics//22fda4ae-4dd5-489d-ab10-9ebfdad22ae0.jpg"/> </div><br>
它是单线程的收集器,不仅意味着只会使用一个线程进行垃圾收集工作,更重要的是它在进行垃圾收集时,必须暂停所有其他工作线程,往往造成过长的等待时间。
@@ -278,7 +278,7 @@ finalize() 类似 C++ 的析构函数,用来做关闭外部资源等工作。
### 3.2 ParNew 收集器
<div align="center"> <img src="index_files/81538cd5-1bcf-4e31-86e5-e198df1e013b.jpg"/> </div><br>
<div align="center"> <img src="../pics//81538cd5-1bcf-4e31-86e5-e198df1e013b.jpg"/> </div><br>
它是 Serial 收集器的多线程版本。
@@ -300,7 +300,7 @@ finalize() 类似 C++ 的析构函数,用来做关闭外部资源等工作。
### 3.4 Serial Old 收集器
<div align="center"> <img src="index_files/08f32fd3-f736-4a67-81ca-295b2a7972f2.jpg"/> </div><br>
<div align="center"> <img src="../pics//08f32fd3-f736-4a67-81ca-295b2a7972f2.jpg"/> </div><br>
Serial Old 是 Serial 收集器的老年代版本,也是给 Client 模式下的虚拟机使用。如果用在 Server 模式下,它有两大用途:
@@ -309,7 +309,7 @@ Serial Old 是 Serial 收集器的老年代版本,也是给 Client 模式下
### 3.5 Parallel Old 收集器
<div align="center"> <img src="index_files/278fe431-af88-4a95-a895-9c3b80117de3.jpg"/> </div><br>
<div align="center"> <img src="../pics//278fe431-af88-4a95-a895-9c3b80117de3.jpg"/> </div><br>
是 Parallel Scavenge 收集器的老年代版本。
@@ -317,7 +317,7 @@ Serial Old 是 Serial 收集器的老年代版本,也是给 Client 模式下
### 3.6 CMS 收集器
<div align="center"> <img src="index_files/62e77997-6957-4b68-8d12-bfd609bb2c68.jpg"/> </div><br>
<div align="center"> <img src="../pics//62e77997-6957-4b68-8d12-bfd609bb2c68.jpg"/> </div><br>
CMS(Concurrent Mark Sweep),从 Mark Sweep 可以知道它是基于标记-清除算法实现的。
@@ -342,7 +342,7 @@ CMS(Concurrent Mark Sweep),从 Mark Sweep 可以知道它是基于标记-
### 3.7 G1 收集器
<div align="center"> <img src="index_files/f99ee771-c56f-47fb-9148-c0036695b5fe.jpg"/> </div><br>
<div align="center"> <img src="../pics//f99ee771-c56f-47fb-9148-c0036695b5fe.jpg"/> </div><br>
G1(Garbage-First)收集器是当今收集器技术发展最前沿的成果之一,它是一款面向服务端应用的垃圾收集器,HotSpot 开发团队赋予它的使命是(在比较长期的)未来可以替换掉 JDK 1.5 中发布的 CMS 收集器。
@@ -439,7 +439,7 @@ JVM 并不是永远地要求对象的年龄必须达到 MaxTenuringThreshold 才
## 1 类的生命周期
<div align="center"> <img src="index_files/32b8374a-e822-4720-af0b-c0f485095ea2.jpg"/> </div><br>
<div align="center"> <img src="../pics//32b8374a-e822-4720-af0b-c0f485095ea2.jpg"/> </div><br>
包括以下 7 个阶段:
@@ -627,7 +627,7 @@ public static void main(String[] args) {
应用程序都是由三种类加载器相互配合进行加载的,如果有必要,还可以加入自己定义的类加载器。下图展示的类加载器之间的层次关系,称为类加载器的双亲委派模型(Parents Delegation Model)。该模型要求除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载器,这里类加载器之间的父子关系一般通过组合(Composition)关系来实现,而不是通过继承(Inheritance)的关系实现。
<div align="center"> <img src="index_files/2cdc3ce2-fa82-4c22-baaa-000c07d10473.jpg"/> </div><br>
<div align="center"> <img src="../pics//2cdc3ce2-fa82-4c22-baaa-000c07d10473.jpg"/> </div><br>
**工作过程**
+9 -9
View File
@@ -49,7 +49,7 @@ File 类可以用于表示文件和目录,但是它只用于表示文件的信
# 字节操作
<div align="center"> <img src="index_files/8143787f-12eb-46ea-9bc3-c66d22d35285.jpg"/> </div><br>
<div align="center"> <img src="../pics//8143787f-12eb-46ea-9bc3-c66d22d35285.jpg"/> </div><br>
Java I/O 使用了装饰者模式来实现。以 InputStream 为例,InputStream 是抽象组件,FileInputStream 是 InputStream 的子类,属于具体组件,提供了字节流的输入操作。FilterInputStream 属于抽象装饰者,装饰者用于装饰组件,为组件提供额外的功能,例如 BufferedInputStream 为 FileInputStream 提供缓存的功能。
@@ -150,7 +150,7 @@ is.close();
- Socket:客户端类
- 服务器和客户端通过 InputStream 和 OutputStream 进行输入输出。
<div align="center"> <img src="index_files/fa4101d7-19ce-4a69-a84f-20bbe64320e5.jpg"/> </div><br>
<div align="center"> <img src="../pics//fa4101d7-19ce-4a69-a84f-20bbe64320e5.jpg"/> </div><br>
## 4. Datagram
@@ -211,19 +211,19 @@ I/O 包和 NIO 已经很好地集成了,java.io.\* 已经以 NIO 为基础重
状态变量的改变过程:
1. 新建一个大小为 8 个字节的缓冲区,此时 position 为 0,而 limit = capacity = 9。capacity 变量不会改变,下面的讨论会忽略它。
<div align="center"> <img src="index_files/1bea398f-17a7-4f67-a90b-9e2d243eaa9a.png"/> </div><br>
<div align="center"> <img src="../pics//1bea398f-17a7-4f67-a90b-9e2d243eaa9a.png"/> </div><br>
2. 从输入通道中读取 3 个字节数据写入缓冲区中,此时 position 移动设为 3,limit 保持不变。
<div align="center"> <img src="index_files/4628274c-25b6-4053-97cf-d1239b44c43d.png"/> </div><br>
<div align="center"> <img src="../pics//4628274c-25b6-4053-97cf-d1239b44c43d.png"/> </div><br>
3. 以下图例为已经从输入通道读取了 5 个字节数据写入缓冲区中。在将缓冲区的数据写到输出通道之前,需要先调用 flip() 方法,这个方法将 limit 设置为当前 position,并将 position 设置为 0。
<div align="center"> <img src="index_files/952e06bd-5a65-4cab-82e4-dd1536462f38.png"/> </div><br>
<div align="center"> <img src="../pics//952e06bd-5a65-4cab-82e4-dd1536462f38.png"/> </div><br>
4. 从缓冲区中取 4 个字节到输出缓冲中,此时 position 设为 4。
<div align="center"> <img src="index_files/b5bdcbe2-b958-4aef-9151-6ad963cb28b4.png"/> </div><br>
<div align="center"> <img src="../pics//b5bdcbe2-b958-4aef-9151-6ad963cb28b4.png"/> </div><br>
5. 最后需要调用 clear() 方法来清空缓冲区,此时 position 和 limit 都被设置为最初位置。
<div align="center"> <img src="index_files/67bf5487-c45d-49b6-b9c0-a058d8c68902.png"/> </div><br>
<div align="center"> <img src="../pics//67bf5487-c45d-49b6-b9c0-a058d8c68902.png"/> </div><br>
## 4. 文件 NIO 实例
@@ -284,7 +284,7 @@ buffer.clear();
服务端都会为每个连接的客户端创建一个线程来处理读写请求,阻塞式的特点会造成服务器会创建大量线程,并且大部分线程处于阻塞的状态,因此对服务器的性能会有很大的影响。
<div align="center"> <img src="index_files/edc23f99-c46c-4200-b64e-07516828720d.jpg"/> </div><br>
<div align="center"> <img src="../pics//edc23f99-c46c-4200-b64e-07516828720d.jpg"/> </div><br>
### 5.2 非阻塞式 I/O
@@ -294,7 +294,7 @@ buffer.clear();
线程通信:线程之间通过 wait()、notify() 等方式通信,保证每次上下文切换都是有意义的,减少无谓的线程切换。
<div align="center"> <img src="index_files/7fcb2fb0-2cd9-4396-bc2d-282becf963c3.jpg"/> </div><br>
<div align="center"> <img src="../pics//7fcb2fb0-2cd9-4396-bc2d-282becf963c3.jpg"/> </div><br>
## 6. 套接字 NIO 实例
+4 -4
View File
@@ -145,13 +145,13 @@ protected void finalize() throws Throwable {}
引用类型引用同一个对象。clone() 方法默认就是浅拷贝实现。
<div align="center"> <img src="index_files/d990c0e7-64d1-4ba3-8356-111bc91e53c5.png"/> </div><br>
<div align="center"> <img src="../pics//d990c0e7-64d1-4ba3-8356-111bc91e53c5.png"/> </div><br>
**深拷贝**
可以使用序列化实现。
<div align="center"> <img src="index_files/2e5620c4-b558-46fe-8f12-00c9dd597a61.png"/> </div><br>
<div align="center"> <img src="../pics//2e5620c4-b558-46fe-8f12-00c9dd597a61.png"/> </div><br>
> [How do I copy an object in Java?](https://stackoverflow.com/questions/869033/how-do-i-copy-an-object-in-java)
@@ -293,7 +293,7 @@ StringBuilder 不是线程安全的;StringBuffer 是线程安全的,使用 s
如果 String 已经被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,才可能使用 String Pool。
<div align="center"> <img src="index_files/f76067a5-7d5f-4135-9549-8199c77d8f1c.jpg"/> </div><br>
<div align="center"> <img src="../pics//f76067a5-7d5f-4135-9549-8199c77d8f1c.jpg"/> </div><br>
**安全性**
@@ -448,7 +448,7 @@ Throwable 可以用来表示任何可以作为异常抛出的类,分为两种
Exception 分为两种: **受检异常** 和 **非受检异常**。受检异常需要用 try...catch... 语句捕获并进行处理,并且可以从异常中恢复;非受检异常是程序运行时错误,例如除 0 会引发 Arithmetic Exception,此时程序奔溃并且无法恢复。
<div align="center"> <img src="index_files/48f8f98e-8dfd-450d-8b5b-df4688f0d377.jpg"/> </div><br>
<div align="center"> <img src="../pics//48f8f98e-8dfd-450d-8b5b-df4688f0d377.jpg"/> </div><br>
更详细的内容:
- [Java 入门之异常处理](https://www.tianmaying.com/tutorial/Java-Exception)
+3 -3
View File
@@ -32,7 +32,7 @@
# 概览
<div align="center"> <img src="index_files/ebf03f56-f957-4435-9f8f-0f605661484d.jpg"/> </div><br>
<div align="center"> <img src="../pics//ebf03f56-f957-4435-9f8f-0f605661484d.jpg"/> </div><br>
容器主要包括 Collection 和 Map 两种,Collection 又包含了 List、Set 以及 Queue。
@@ -257,13 +257,13 @@ transient Entry[] table;
其中,Entry 就是存储数据的键值对,它包含了四个字段。从 next 字段我们可以看出 Entry 是一个链表,即每个桶会存放一个链表。
<div align="center"> <img src="index_files/ce039f03-6588-4f0c-b35b-a494de0eac47.png"/> </div><br>
<div align="center"> <img src="../pics//ce039f03-6588-4f0c-b35b-a494de0eac47.png"/> </div><br>
### 拉链法的工作原理
使用默认构造函数新建一个 HashMap,默认大小为 16。Entry 的类型为 &lt;String, Integer>。先后插入三个元素:("sachin", 30), ("vishal", 20) 和 ("vaibhav", 20)。计算 "sachin" 的 hashcode 为 115,使用除留余数法得到 115 % 16 = 3,因此 ("sachin", 30) 键值对放到第 3 个桶上。同样得到 ("vishal", 20) 和 ("vaibhav", 20) 都应该放到第 6 个桶上,因此需要把 ("vaibhav", 20) 链接到 ("vishal", 20) 之后。
<div align="center"> <img src="index_files/b9a39d2a-618c-468b-86db-2e851f1a0057.jpg"/> </div><br>
<div align="center"> <img src="../pics//b9a39d2a-618c-468b-86db-2e851f1a0057.jpg"/> </div><br>
当进行查找时,需要分成两步进行,第一步是先根据 hashcode 计算出所在的桶,第二步是在链表上顺序查找。由于 table 是数组形式的,具有随机读取的特性,因此这一步的时间复杂度为 O(1),而第二步需要在链表上顺序查找,时间复杂度显然和链表的长度成正比。
+9 -9
View File
@@ -403,7 +403,7 @@ interrupted() 方法在检查完中断状态之后会清除中断状态,这样
# 线程状态转换
<div align="center"> <img src="index_files/38b894a7-525e-4204-80de-ecc1acc52c46.jpg"/> </div><br>
<div align="center"> <img src="../pics//38b894a7-525e-4204-80de-ecc1acc52c46.jpg"/> </div><br>
1. NEW(新建):创建后尚未启动的线程。
2. RUNNABLE(运行):处于此状态的线程有可能正在执行,也有可能正在等待着 CPU 为它分配执行时间。
@@ -454,7 +454,7 @@ volatile 关键字通过添加内存屏障的方式来进制指令重排,即
每个处理器都有一个高速缓存,但是所有处理器共用一个主内存,因此高速缓存引入了一个新问题:缓存一致性。当多个处理器的运算都涉及同一块主内存区域时,将可能导致各自的缓存数据不一致。缓存不一致问题通常需要使用一些协议来解决。
<div align="center"> <img src="index_files/352dd00d-d1bb-4134-845d-16a75bcb0e02.jpg"/> </div><br>
<div align="center"> <img src="../pics//352dd00d-d1bb-4134-845d-16a75bcb0e02.jpg"/> </div><br>
除了增加高速缓存之外,为了使得处理器内部的运算单元能尽量被充分利用,处理器可能会对输入代码进行乱序执行(Out-Of-Order Execution)优化,处理器会在计算之后将乱序执行的结果重组,保证该结果与顺序执行的结果是一致的,但并不保证程序中各个语句计算的先后顺序与输入代码中的顺序一致,因此,如果存在一个计算任务依赖另外一个计算任务的中间结果,那么其顺序性并不能靠代码的先后顺序来保证。与处理器的乱序执行优化类似,Java 虚拟机的即时编译器中也有类似的指令重排序(Instruction Reorder)优化。
@@ -468,7 +468,7 @@ Java 内存模型的主要目标是定义程序中各个变量的访问规则,
Java 内存模型规定了所有的变量都存储在主内存(Main Memory)中。每条线程还有自己的工作内存,线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成,线程、主内存、工作内存三者的交互关系如图所示。
<div align="center"> <img src="index_files/b02a5492-5dcf-4a69-9b5b-c2298b2cb81c.jpg"/> </div><br>
<div align="center"> <img src="../pics//b02a5492-5dcf-4a69-9b5b-c2298b2cb81c.jpg"/> </div><br>
## 4. 内存间交互操作
@@ -718,10 +718,10 @@ Thread printThread = new Thread(new Runnable() {
如果需要使用上述功能,选用 ReentrantLock 是一个很好的选择,那如果是基于性能考虑呢?关于 synchronized 和 ReentrantLock 的性能问题,Brian Goetz 对这两种锁在 JDK 1.5 与单核处理器,以及 JDK 1.5 与双 Xeon 处理器环境下做了一组吞吐量对比的实验,实验结果如图 13-1 和图 13-2 所示。
<div align="center"> <img src="index_files/d4a05b9c-f423-4137-9510-b6851f089edb.jpg"/> </div><br>
<div align="center"> <img src="../pics//d4a05b9c-f423-4137-9510-b6851f089edb.jpg"/> </div><br>
<div align="center"> JDK 1.5、单核处理器下两种锁的吞吐量对比 </div><br>
<div align="center"> <img src="index_files/acc42b0f-10ba-4fa2-8694-cf2aab1fb434.jpg"/> </div><br>
<div align="center"> <img src="../pics//acc42b0f-10ba-4fa2-8694-cf2aab1fb434.jpg"/> </div><br>
<div align="center"> JDK 1.5、双 Xeon 处理器下两种锁的吞吐量对比 </div><br>
多线程环境下 synchronized 的吞吐量下降得非常严重,而 ReentrantLock 则能基本保持在同一个比较稳定的水平上。与其说 ReentrantLock 性能好,还不如说 synchronized 还有非常大的优化余地。后续的技术发展也证明了这一点,JDK 1.6 中加入了很多针对锁的优化措施,JDK 1.6 发布之后,人们就发现 synchronized 与 ReentrantLock 的性能基本上是完全持平了。因此,如果读者的程序是使用 JDK 1.6 或以上部署的话,性能因素就不再是选择 ReentrantLock 的理由了,虚拟机在未来的性能改进中肯定也会更加偏向于原生的 synchronized,所以还是提倡在 synchronized 能实现需求的情况下,优先考虑使用 synchronized 来进行同步。
@@ -886,15 +886,15 @@ public static String concatString(String s1, String s2, String s3) {
对象头信息是与对象自身定义的数据无关的额外存储成本,考虑到虚拟机的空间效率,Mark Work 被设计成一个非固定的数据结构以便在极小的空间内存储尽量多的信息,它会根据对象的状态复用自己的存储空间。例如,在 32 位的 HotSpot 虚拟机中对象未被锁定的状态下,Mark Word 的 32bit 空间中的 25bit 用于存储对象哈希码(HashCode),4bit 用于存储对象分代年龄,2bit 用于存储锁标志位,1bit 固定为 0,在其他状态(轻量级锁定、重量级锁定、GC 标记、可偏向)下对象的存储内容见表 13-1。
<div align="center"> <img src="index_files/30edea19-3507-423c-bbb0-5184292692d7.png"/> </div><br>
<div align="center"> <img src="../pics//30edea19-3507-423c-bbb0-5184292692d7.png"/> </div><br>
简单地介绍了对象的内存布局后,我们把话题返回到轻量级锁的执行过程上。在代码进入同步块的时候,如果此同步对象没有被锁定(锁标志位为 “01” 状态)虚拟机首先将在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的 Mark Word 的拷贝(官方把这份拷贝加上了一个 Displaced 前缀,即 Displaced Mark Word),这时候线程堆栈与对象头的状态如图 13-3 所示。
<div align="center"> <img src="index_files/643a2587-08ae-4d92-94fb-d9a1c448cd13.png"/> </div><br>
<div align="center"> <img src="../pics//643a2587-08ae-4d92-94fb-d9a1c448cd13.png"/> </div><br>
然后,虚拟机将使用 CAS 操作尝试将对象的 Mark Word 更新为指向 Lock Record 的指针。如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象 Mark Word 的锁标志位 (Mark Word 的最后 2bit)将转变为 “00”,即表示此对象处于轻量级锁定状态,这时候线程堆栈与对象头的状态如图 12-4 所示。
<div align="center"> <img src="index_files/0126ff14-d52d-4a6e-b8ca-e429881e23b7.png"/> </div><br>
<div align="center"> <img src="../pics//0126ff14-d52d-4a6e-b8ca-e429881e23b7.png"/> </div><br>
如果这个更新操作失败了,虚拟机首先会检查对象的 Mark Word 是否指向当前线程的栈帧,如果只说明当前线程已经拥有了这个对象的锁,那就可以直接进入同步块继续执行,否则说明这个锁对象以及被其他线程线程抢占了。如果有两条以上的线程争用同一个锁,那轻量级锁就不再有效,要膨胀为重量级锁,所标志的状态变为 “10”,Mark Word 中存储的就是指向重量级锁(互斥量)的指针,后面等待锁的线程也要进入阻塞状态。
@@ -912,7 +912,7 @@ public static String concatString(String s1, String s2, String s3) {
当有另外一个线程去尝试获取这个锁时,偏向模式就宣告结束。根据锁对象目前是否处于被锁定的状态,撤销偏向(Revoke Bias)后恢复到未锁定(标志位为 “01”)或轻量级锁定(标志位为 “00”)的状态,后续的同步操作就如上面介绍的轻量级锁那样执行。偏向锁、轻量级锁的状态转换及对象 Mark Word 的关系如图 13-5 所示。
<div align="center"> <img src="index_files/b202eeb9-5e84-4dfb-a6a1-4f4b7ed5d3e4.jpg"/> </div><br>
<div align="center"> <img src="../pics//b202eeb9-5e84-4dfb-a6a1-4f4b7ed5d3e4.jpg"/> </div><br>
偏向锁可以提高带有同步但无竞争的程序性能。它同样是一个带有效益权衡(Trade Off)性质的优化,也就是说,它并不一定总是对程序运行有利,如果程序中大多数的锁总是被多个不同的线程访问,那偏向模式就是多余的。在具体问题具体分析的前提下,有时候使用参数 -XX:-UseBiasedLocking 来禁止偏向锁优化反而可以提升性能。
+9 -9
View File
@@ -745,7 +745,7 @@ public List<Integer> topKFrequent(int[] nums, int k) {
### BFS
<div align="center"> <img src="index_files/4ff355cf-9a7f-4468-af43-e5b02038facc.jpg"/> </div><br>
<div align="center"> <img src="../pics//4ff355cf-9a7f-4468-af43-e5b02038facc.jpg"/> </div><br>
广度优先搜索的搜索过程有点像一层一层地进行遍历:从节点 0 出发,遍历到 6、2、1 和 5 这四个新节点。
@@ -801,7 +801,7 @@ private class Position {
### DFS
<div align="center"> <img src="index_files/f7f7e3e5-7dd4-4173-9999-576b9e2ac0a2.png"/> </div><br>
<div align="center"> <img src="../pics//f7f7e3e5-7dd4-4173-9999-576b9e2ac0a2.png"/> </div><br>
广度优先搜索一层一层遍历,每一层遍历到的所有新节点,要用队列先存储起来以备下一层遍历的时候再遍历;而深度优先搜索在遍历到一个新节点时立马对新节点进行遍历:从节点 0 出发开始遍历,得到到新节点 6 时,立马对新节点 6 进行遍历,得到新节点 4;如此反复以这种方式遍历新节点,直到没有新节点了,此时返回。返回到根节点 0 的情况是,继续对根节点 0 进行遍历,得到新节点 2,然后继续以上步骤。
@@ -1087,7 +1087,7 @@ private void dfs(int r, int c, boolean[][] canReach) {
[Leetcode : 51. N-Queens (Hard)](https://leetcode.com/problems/n-queens/description/)
<div align="center"> <img src="index_files/1f080e53-4758-406c-bb5f-dbedf89b63ce.jpg"/> </div><br>
<div align="center"> <img src="../pics//1f080e53-4758-406c-bb5f-dbedf89b63ce.jpg"/> </div><br>
题目描述:在 n\*n 的矩阵中摆放 n 个皇后,并且每个皇后不能在同一行,同一列,同一对角线上,要求解所有的 n 皇后解。
@@ -1095,11 +1095,11 @@ private void dfs(int r, int c, boolean[][] canReach) {
45 度对角线标记数组的维度为 2\*n - 1,通过下图可以明确 (r,c) 的位置所在的数组下标为 r + c。
<div align="center"> <img src="index_files/85583359-1b45-45f2-9811-4f7bb9a64db7.jpg"/> </div><br>
<div align="center"> <img src="../pics//85583359-1b45-45f2-9811-4f7bb9a64db7.jpg"/> </div><br>
135 度对角线标记数组的维度也是 2\*n - 1,(r,c) 的位置所在的数组下标为 n - 1 - (r - c)。
<div align="center"> <img src="index_files/9e80f75a-b12b-4344-80c8-1f9ccc2d5246.jpg"/> </div><br>
<div align="center"> <img src="../pics//9e80f75a-b12b-4344-80c8-1f9ccc2d5246.jpg"/> </div><br>
```java
private List<List<String>> ret;
@@ -1156,7 +1156,7 @@ private void backstracking(int row) {
[Leetcode : 17. Letter Combinations of a Phone Number (Medium)](https://leetcode.com/problems/letter-combinations-of-a-phone-number/description/)
<div align="center"> <img src="index_files/a3f34241-bb80-4879-8ec9-dff2d81b514e.jpg"/> </div><br>
<div align="center"> <img src="../pics//a3f34241-bb80-4879-8ec9-dff2d81b514e.jpg"/> </div><br>
```html
Input:Digit string "23"
@@ -1598,7 +1598,7 @@ private boolean isPalindrome(String s, int begin, int end) {
[Leetcode : 37. Sudoku Solver (Hard)](https://leetcode.com/problems/sudoku-solver/description/)
<div align="center"> <img src="index_files/1ca52246-c443-48ae-b1f8-1cafc09ec75c.png"/> </div><br>
<div align="center"> <img src="../pics//1ca52246-c443-48ae-b1f8-1cafc09ec75c.png"/> </div><br>
```java
private boolean[][] rowsUsed = new boolean[9][10];
@@ -2519,7 +2519,7 @@ public int minDistance(String word1, String word2) {
题目描述:交易之后需要有一天的冷却时间。
<div align="center"> <img src="index_files/ac9b31ec-cef1-4880-a875-fc4571ca10e1.png"/> </div><br>
<div align="center"> <img src="../pics//ac9b31ec-cef1-4880-a875-fc4571ca10e1.png"/> </div><br>
```html
s0[i] = max(s0[i - 1], s2[i - 1]); // Stay at s0, or rest from s2
@@ -4797,7 +4797,7 @@ private void inorder(TreeNode node, int k) {
### Trie
<div align="center"> <img src="index_files/5c638d59-d4ae-4ba4-ad44-80bdc30f38dd.jpg"/> </div><br>
<div align="center"> <img src="../pics//5c638d59-d4ae-4ba4-ad44-80bdc30f38dd.jpg"/> </div><br>
Trie,又称前缀树或字典树,用于判断字符串是否存在或者是否具有某种字符串前缀。
+8 -8
View File
@@ -219,7 +219,7 @@ GPT 第 1 个区块记录了 MBR,紧接着是 33 个区块记录分区信息
GPT 没有扩展分区概念,都是主分区,最多可以分 128 个分区。
<div align="center"> <img src="index_files/a5c25452-6fa5-49e7-9322-823077442775.jpg"/> </div><br>
<div align="center"> <img src="../pics//a5c25452-6fa5-49e7-9322-823077442775.jpg"/> </div><br>
## 开机检测程序
@@ -229,7 +229,7 @@ BIOS 是开机的时候计算机执行的第一个程序,这个程序知道可
MBR 中的开机管理程序提供以下功能:选单、载入核心文件以及转交其它开机管理程序。转交这个功能可以用来实现了多重引导,只需要将另一个操作系统的开机管理程序安装在其它分区的启动扇区上,在启动 MBR 中的开机管理程序时,就可以选择启动当前的操作系统或者转交给其它开机管理程序从而启动另一个操作系统。
<div align="center"> <img src="index_files/f900f266-a323-42b2-bc43-218fdb8811a8.jpg"/> </div><br>
<div align="center"> <img src="../pics//f900f266-a323-42b2-bc43-218fdb8811a8.jpg"/> </div><br>
安装多重引导,最好先安装 Windows 再安装 Linux。因为安装 Windows 时会覆盖掉 MBR,而 Linux 可以选择将开机管理程序安装在 MBR 或者其它分区的启动扇区,并且可以设置开机管理程序的选单。
@@ -241,7 +241,7 @@ UEFI 相比于 BIOS 来说功能更为全面,也更为安全。
挂载利用目录作为分区的进入点,也就是说,进入目录之后就可以读取分区的数据。
<div align="center"> <img src="index_files/249f3bb1-feee-4805-a259-a72699d638ca.jpg"/> </div><br>
<div align="center"> <img src="../pics//249f3bb1-feee-4805-a259-a72699d638ca.jpg"/> </div><br>
# 文件权限与目录配置
@@ -340,7 +340,7 @@ UEFI 相比于 BIOS 来说功能更为全面,也更为安全。
完整的目录树如下:
<div align="center"> <img src="index_files/27ace615-558f-4dfb-8ad4-7ac769c10118.jpg"/> </div><br>
<div align="center"> <img src="../pics//27ace615-558f-4dfb-8ad4-7ac769c10118.jpg"/> </div><br>
# 文件与目录
@@ -501,7 +501,7 @@ find 可以使用文件的属性和权限进行搜索。
+4、4 和 -4 的指示的时间范围如下:
<div align="center"> <img src="index_files/658fc5e7-79c0-4247-9445-d69bf194c539.png"/> </div><br>
<div align="center"> <img src="../pics//658fc5e7-79c0-4247-9445-d69bf194c539.png"/> </div><br>
#### 4.2 与文件拥有者和所属群组有关的选项
@@ -543,7 +543,7 @@ find 可以使用文件的属性和权限进行搜索。
Ext2 文件系统使用了上述的文件结构,并在此之上加入了 block 群组的概念,也就是将一个文件系统划分为多个 block 群组,方便管理。
<div align="center"> <img src="index_files/1974a836-aa6b-4fb8-bce1-6eb11969284a.jpg"/> </div><br>
<div align="center"> <img src="../pics//1974a836-aa6b-4fb8-bce1-6eb11969284a.jpg"/> </div><br>
## inode
@@ -551,7 +551,7 @@ Ext2 文件系统支持的 block 大小有 1k、2k 和 4k 三种,不同的 blo
inode 中记录了文件内容所在的 block,但是每个 block 非常小,一个大文件随便都需要几十万的 block。而一个 inode 大小有限,无法直接引用这么多 block。因此引入了间接、双间接、三间接引用。间接引用是指,让 inode 记录的引用 block 块当成 inode 用来记录引用信息。
<div align="center"> <img src="index_files/89091427-7b2b-4923-aff6-44681319a8aa.jpg"/> </div><br>
<div align="center"> <img src="../pics//89091427-7b2b-4923-aff6-44681319a8aa.jpg"/> </div><br>
inode 具体包含以下信息:
@@ -1030,7 +1030,7 @@ daemon 2
# vim 三个模式
<div align="center"> <img src="index_files/341c632a-1fc1-4068-9b9f-bf7ef68ebb4c.jpg"/> </div><br>
<div align="center"> <img src="../pics//341c632a-1fc1-4068-9b9f-bf7ef68ebb4c.jpg"/> </div><br>
在指令列模式下,有以下命令用于离开或者存储文件。
+5 -5
View File
@@ -247,7 +247,7 @@ customer_id_selectivity: 0.0373
### 3.5 聚簇索引
<div align="center"> <img src="index_files/b9e9ae8c-e216-4c01-b267-a50dbeb98fa4.jpg"/> </div><br>
<div align="center"> <img src="../pics//b9e9ae8c-e216-4c01-b267-a50dbeb98fa4.jpg"/> </div><br>
聚簇索引并不是一种索引类型,而是一种数据存储方式。
@@ -282,7 +282,7 @@ customer_id_selectivity: 0.0373
### 4. 1 B-Tree
<div align="center"> <img src="index_files/5ed71283-a070-4b21-85ae-f2cbfd6ba6e1.jpg"/> </div><br>
<div align="center"> <img src="../pics//5ed71283-a070-4b21-85ae-f2cbfd6ba6e1.jpg"/> </div><br>
为了描述 B-Tree,首先定义一条数据记录为一个二元组 [key, data],key 为记录的键,data 为数据记录除 key 外的数据。
@@ -298,7 +298,7 @@ B-Tree 是满足下列条件的数据结构:
### 4.2 B+Tree
<div align="center"> <img src="index_files/63cd5b50-d6d8-4df6-8912-ef4a1dd5ba13.jpg"/> </div><br>
<div align="center"> <img src="../pics//63cd5b50-d6d8-4df6-8912-ef4a1dd5ba13.jpg"/> </div><br>
与 B-Tree 相比,B+Tree 有以下不同点:
@@ -307,7 +307,7 @@ B-Tree 是满足下列条件的数据结构:
### 4.3 带有顺序访问指针的 B+Tree
<div align="center"> <img src="index_files/1ee5f0a5-b8df-43b9-95ab-c516c54ec797.jpg"/> </div><br>
<div align="center"> <img src="../pics//1ee5f0a5-b8df-43b9-95ab-c516c54ec797.jpg"/> </div><br>
一般在数据库系统或文件系统中使用的 B+Tree 结构都在经典 B+Tree 基础上进行了优化,在叶子节点增加了顺序访问指针,做这个优化的目的是为了提高区间访问的性能。
@@ -424,7 +424,7 @@ do {
通过代理,可以路由流量到可以使用的服务器上。
<div align="center"> <img src="index_files/fabd5fa0-b75e-48d0-9e2c-31471945ceb9.jpg"/> </div><br>
<div align="center"> <img src="../pics//fabd5fa0-b75e-48d0-9e2c-31471945ceb9.jpg"/> </div><br>
**在应用中处理故障转移**
+17 -15
View File
@@ -10,9 +10,11 @@
* [发布与订阅](#发布与订阅)
* [事务](#事务)
* [持久化](#持久化)
* [1. 快照持久化](#1-快照持久化)
* [2. AOF 持久化](#2-aof-持久化)
* [快照持久化](#快照持久化)
* [AOF 持久化](#aof-持久化)
* [复制](#复制)
* [从服务器连接主服务器的过程](#从服务器连接主服务器的过程)
* [主从链](#主从链)
* [处理故障](#处理故障)
* [分片](#分片)
* [事件](#事件)
@@ -212,7 +214,7 @@ MULTI 和 EXEC 中的操作将会一次性发送给服务器,而不是一条
Redis 是内存型数据库,为了保证数据在断电后不会丢失,需要将内存中的数据持久化到硬盘上。
## 1. 快照持久化
## 快照持久化
将某个时间点的所有数据都存放到硬盘上。
@@ -220,7 +222,7 @@ Redis 是内存型数据库,为了保证数据在断电后不会丢失,需
如果系统发生故障,将会丢失最后一次创建快照之后的数据。并且如果数据量很大,保存快照的时间也会很长。
## 2. AOF 持久化
## AOF 持久化
AOF 持久化将写命令添加到 AOF 文件(Append Only File)的末尾。
@@ -242,17 +244,17 @@ always 选项会严重减低服务器的性能;everysec 选项比较合适,
一个从服务器只能有一个主服务器,并且不支持主主复制。
**1. 从服务器连接主服务器的过程**
## 从服务器连接主服务器的过程
(1) 主服务器创建快照文件,发送给从服务器,并在发送期间使用缓冲区记录执行的写命令。快照文件发送完毕之后,开始向从服务器发送存储在缓冲区中的写命令;
(2) 从服务器丢弃所有旧数据,载入主服务器发来的快照文件,之后从服务器开始接受主服务器发来的写命令;
(3) 主服务器每执行一次写命令,就向从服务器发送相同的写命令。
- 主服务器创建快照文件,发送给从服务器,并在发送期间使用缓冲区记录执行的写命令。快照文件发送完毕之后,开始向从服务器发送存储在缓冲区中的写命令;
- 从服务器丢弃所有旧数据,载入主服务器发来的快照文件,之后从服务器开始接受主服务器发来的写命令;
- 主服务器每执行一次写命令,就向从服务器发送相同的写命令。
**2. 主从链**
## 主从链
随着负载不断上升,主服务器可能无法很快地更新所有从服务器,或者重新连接和重新同步从服务器而导致系统超载。为了解决这个问题,可以创建一个中间层来分担主服务器的复制工作。中间层的服务器是最上层服务器的从服务器,又是最下层服务器的主服务器。
随着负载不断上升,主服务器可能无法很快地更新所有从服务器,或者重新连接和重新同步从服务器将导致系统超载。为了解决这个问题,可以创建一个中间层来分担主服务器的复制工作。中间层的服务器是最上层服务器的从服务器,又是最下层服务器的主服务器。
<div align="center"> <img src="index_files/b242fafc-5945-42a8-805e-6e3f1f2f89b4.jpg"/> </div><br>
<div align="center"> <img src="../pics//b242fafc-5945-42a8-805e-6e3f1f2f89b4.jpg"/> </div><br>
# 处理故障
@@ -338,7 +340,7 @@ def main():
事件处理的角度下服务器运行流程如下:
<div align="center"> <img src="index_files/73b73189-9e95-47e5-91d0-9378b8462e15.png"/> </div><br>
<div align="center"> <img src="../pics//73b73189-9e95-47e5-91d0-9378b8462e15.png"/> </div><br>
# Redis 与 Memcached 的区别
@@ -411,7 +413,7 @@ Redis 这种内存数据库才能支持计数器的频繁读写操作。
Redis 没有表的概念将同类型的数据存放在一起,而是使用命名空间的方式来实现这一功能。键名的前面部分存储命名空间,后面部分的内容存储 ID,通常使用 : 来进行分隔。例如下面的 HASH 的键名为 article:92617,其中 article 为命名空间,ID 为 92617。
<div align="center"> <img src="index_files/2d078e08-3a49-46d0-b784-df780b7e4bc3.jpg"/> </div><br>
<div align="center"> <img src="../pics//2d078e08-3a49-46d0-b784-df780b7e4bc3.jpg"/> </div><br>
**2. 点赞功能**
@@ -419,13 +421,13 @@ Redis 没有表的概念将同类型的数据存放在一起,而是使用命
为了节约内存,规定一篇文章发布满一周之后,就不能再对它进行投票,而文章的已投票集合也会被删除,可以为文章的已投票集合设置一个一周的过期时间就能实现这个规定。
<div align="center"> <img src="index_files/0e4c8a7f-f84c-4c4e-9544-49cd40167af8.png"/> </div><br>
<div align="center"> <img src="../pics//0e4c8a7f-f84c-4c4e-9544-49cd40167af8.png"/> </div><br>
**3. 对文章进行排序**
为了按发布时间和点赞数进行排序,可以建立一个文章发布时间的有序集合和一个文章点赞数的有序集合。(下图中的 score 就是这里所说的点赞数;下面所示的有序集合分值并不直接是时间和点赞数,而是根据它们间接计算出来的)
<div align="center"> <img src="index_files/ea5e434a-a218-44b5-aa72-4cd08991abcf.jpg"/> </div><br>
<div align="center"> <img src="../pics//ea5e434a-a218-44b5-aa72-4cd08991abcf.jpg"/> </div><br>
# 参考资料
+1 -1
View File
@@ -710,7 +710,7 @@ SHOW GRANTS FOR myuser;
GRANT SELECT, INSERT ON mydatabase.* TO myuser;
```
<div align="center"> <img src="index_files/c73aa08e-a987-43c9-92be-adea4a884c25.png"/> </div><br>
<div align="center"> <img src="../pics//c73aa08e-a987-43c9-92be-adea4a884c25.png"/> </div><br>
账户用 username@host 的形式定义,username@% 使用的是默认主机名。
+1 -1
View File
@@ -44,7 +44,7 @@
用 min、max 表示数量范围;用 first、last 表示访问空间的包含范围,begin、end 表示访问空间的排除范围,即 end 不包含尾部。
<div align="center"> <img src="index_files/05907ab4-42c5-4b5e-9388-6617f6c97bea.jpg"/> </div><br>
<div align="center"> <img src="../pics//05907ab4-42c5-4b5e-9388-6617f6c97bea.jpg"/> </div><br>
布尔相关的命名加上 is、can、should、has 等前缀。
+17 -17
View File
@@ -302,7 +302,7 @@ private TreeNode reConstructBinaryTree(int[] pre, int preL, int preR, int[] in,
- 如果一个节点有右子树不为空,那么该节点的下一个节点是右子树的最左节点;
- 否则,向上找第一个左链接指向的树包含该节点的祖先节点。
<div align="center"> <img src="index_files/6fec7f56-a685-4232-b03e-c92a8dfba486.png"/> </div><br>
<div align="center"> <img src="../pics//6fec7f56-a685-4232-b03e-c92a8dfba486.png"/> </div><br>
```java
public TreeLinkNode GetNext(TreeLinkNode pNode) {
@@ -679,12 +679,12 @@ private void printNumber(char[] number) {
- 如果链表不是尾节点,那么可以直接将下一个节点的值赋给节点,令节点指向下下个节点,然后删除下一个节点,时间复杂度为 O(1)。
<div align="center"> <img src="index_files/72f9bc11-06a9-40b4-8939-14f72e5cb4c3.png"/> </div><br>
<div align="center"> <img src="../pics//72f9bc11-06a9-40b4-8939-14f72e5cb4c3.png"/> </div><br>
- 否则,就需要先遍历链表,找到节点的前一个节点,然后让前一个节点指向节点的下一个节点,时间复杂度为 O(N)。
<div align="center"> <img src="index_files/2a398239-ee47-4ea1-b2d8-0ced638839ef.png"/> </div><br>
<div align="center"> <img src="../pics//2a398239-ee47-4ea1-b2d8-0ced638839ef.png"/> </div><br>
- 综上,如果进行 N 次操作,那么大约需要移动节点的次数为 N-1+N=2N-1,其中 N-1 表示不是链表尾节点情况下的移动次数,N 表示是尾节点情况下的移动次数。(2N-1)/N \~ 2,因此该算法的时间复杂度为 O(1)。
@@ -832,7 +832,7 @@ public void reOrderArray(int[] array) {
设链表的长度为 N。设两个指针 P1 和 P2,先让 P1 移动 K 个节点,则还有 N - K 个节点可以移动。此时让 P1 和 P2 同时移动,可以知道当 P1 移动到链表结尾时,P2 移动到 N - K 个节点处,该位置就是倒数第 K 个节点。
<div align="center"> <img src="index_files/40d96c0d-156f-4eee-a183-2d597344f1cd.png"/> </div><br>
<div align="center"> <img src="../pics//40d96c0d-156f-4eee-a183-2d597344f1cd.png"/> </div><br>
```java
public ListNode FindKthToTail(ListNode head, int k) {
@@ -857,7 +857,7 @@ public ListNode FindKthToTail(ListNode head, int k) {
在相遇点,slow 要到环的入口点还需要移动 z 个节点,如果让 fast 重新从头开始移动,并且速度变为每次移动一个节点,那么它到环入口点还需要移动 x 个节点。在上面已经推导出 x=z,因此 fast 和 slow 将在环入口点相遇。
<div align="center"> <img src="index_files/e7d7dc0d-fc22-4f95-8768-b8a216168792.jpg"/> </div><br>
<div align="center"> <img src="../pics//e7d7dc0d-fc22-4f95-8768-b8a216168792.jpg"/> </div><br>
```java
public ListNode EntryNodeOfLoop(ListNode pHead) {
@@ -902,7 +902,7 @@ public ListNode ReverseList(ListNode head) {
**题目描述**
<div align="center"> <img src="index_files/79f28233-f5cb-492a-9dc4-696cb714d434.png"/> </div><br>
<div align="center"> <img src="../pics//79f28233-f5cb-492a-9dc4-696cb714d434.png"/> </div><br>
```java
public ListNode Merge(ListNode list1, ListNode list2) {
@@ -928,7 +928,7 @@ public ListNode Merge(ListNode list1, ListNode list2) {
**题目描述**
<div align="center"> <img src="index_files/26223561-eea4-463c-8ddb-3bb456c76267.png"/> </div><br>
<div align="center"> <img src="../pics//26223561-eea4-463c-8ddb-3bb456c76267.png"/> </div><br>
```java
public boolean HasSubtree(TreeNode root1, TreeNode root2) {
@@ -951,7 +951,7 @@ private boolean isSubtree(TreeNode root1, TreeNode root2) {
**题目描述**
<div align="center"> <img src="index_files/7cfcfdf7-63a7-4111-a677-2eca29fbcf24.png"/> </div><br>
<div align="center"> <img src="../pics//7cfcfdf7-63a7-4111-a677-2eca29fbcf24.png"/> </div><br>
```java
public void Mirror(TreeNode root) {
@@ -1027,7 +1027,7 @@ private int height(TreeNode root) {
下图的矩阵顺时针打印结果为:1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10
<div align="center"> <img src="index_files/8615d9f7-bd1d-4240-8bb4-02b941d54a6f.png"/> </div><br>
<div align="center"> <img src="../pics//8615d9f7-bd1d-4240-8bb4-02b941d54a6f.png"/> </div><br>
```java
public ArrayList<Integer> printMatrix(int[][] matrix) {
@@ -1105,7 +1105,7 @@ public boolean IsPopOrder(int[] pushA, int[] popA) {
例如,以下二叉树层次遍历的结果为 8, 6, 10, 5, 7, 9, 11
<div align="center"> <img src="index_files/c8cdfa3d-d610-4731-9d89-564252378e7d.png"/> </div><br>
<div align="center"> <img src="../pics//c8cdfa3d-d610-4731-9d89-564252378e7d.png"/> </div><br>
**解题思路**
@@ -1201,7 +1201,7 @@ public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
例如,下图中后序遍历序列 5, 7, 6, 9, 11, 10, 8 所对应的二叉搜索树。
<div align="center"> <img src="index_files/b5af9ee6-97e6-446b-9551-6dfe96770d1a.png"/> </div><br>
<div align="center"> <img src="../pics//b5af9ee6-97e6-446b-9551-6dfe96770d1a.png"/> </div><br>
```java
public boolean VerifySquenceOfBST(int[] sequence) {
@@ -1229,7 +1229,7 @@ private boolean verify(int[] sequence, int first, int last) {
下图的二叉树有两条和为 22 的路径:10, 5, 7 和 10, 12
<div align="center"> <img src="index_files/a1ced733-02f5-4091-8f5a-ab9b4e5a3525.png"/> </div><br>
<div align="center"> <img src="../pics//a1ced733-02f5-4091-8f5a-ab9b4e5a3525.png"/> </div><br>
```java
private ArrayList<ArrayList<Integer>> ret = new ArrayList<>();
@@ -1259,21 +1259,21 @@ private void dfs(TreeNode node, int target, int curSum, ArrayList<Integer> path)
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的 head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
<div align="center"> <img src="index_files/4f67aa74-5bf5-4ea4-9a6e-2e07d8f5fa86.png"/> </div><br>
<div align="center"> <img src="../pics//4f67aa74-5bf5-4ea4-9a6e-2e07d8f5fa86.png"/> </div><br>
**解题思路**
第一步,在每个节点的后面插入复制的节点。
<div align="center"> <img src="index_files/52726d32-e97a-49dd-8419-04d30a9f9050.png"/> </div><br>
<div align="center"> <img src="../pics//52726d32-e97a-49dd-8419-04d30a9f9050.png"/> </div><br>
第二步,对复制节点的 random 链接进行赋值。
<div align="center"> <img src="index_files/c5409a64-81fc-48c1-9790-ffa0afdb7bf5.png"/> </div><br>
<div align="center"> <img src="../pics//c5409a64-81fc-48c1-9790-ffa0afdb7bf5.png"/> </div><br>
第三步,拆分。
<div align="center"> <img src="index_files/15e1edf0-8908-4815-af5e-a74e456da23b.png"/> </div><br>
<div align="center"> <img src="../pics//15e1edf0-8908-4815-af5e-a74e456da23b.png"/> </div><br>
```java
public RandomListNode Clone(RandomListNode pHead) {
@@ -1315,7 +1315,7 @@ public RandomListNode Clone(RandomListNode pHead) {
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
<div align="center"> <img src="index_files/9cd97f41-fd9c-405c-aca1-9c82d24e20dc.png"/> </div><br>
<div align="center"> <img src="../pics//9cd97f41-fd9c-405c-aca1-9c82d24e20dc.png"/> </div><br>
```java
private TreeNode pre = null;
+10 -10
View File
@@ -128,7 +128,7 @@ MySQL 中主要提供了两种封锁粒度:行级锁以及表级锁。
## 三级封锁协议
<div align="center"> <img src="index_files/785806ed-c46b-4dca-b756-cebe7bf8ac3a.jpg"/> </div><br>
<div align="center"> <img src="../pics//785806ed-c46b-4dca-b756-cebe7bf8ac3a.jpg"/> </div><br>
**一级封锁协议**
@@ -191,7 +191,7 @@ MySQL InnoDB 采用的是两阶段锁协议。在事务执行过程中,随时
以下关系中,Sno 表示学号,Sname 表示学生姓名,Sdept 表示学院,Cname 表示课程名,Mname 表示院长姓名。函数依赖为 {Sno, Cname} -> {Sname, Sdept, Mname}。
<div align="center"> <img src="index_files/b6a678c0-c875-4038-afba-301846620786.jpg"/> </div><br>
<div align="center"> <img src="../pics//b6a678c0-c875-4038-afba-301846620786.jpg"/> </div><br>
不符合范式的关系,会产生很多异常。主要有以下四种异常:
@@ -228,13 +228,13 @@ MySQL InnoDB 采用的是两阶段锁协议。在事务执行过程中,随时
<div align="center"> <div align="center"><img src="https://latex.codecogs.com/gif.latex?Sdept->Mname"/></div> <br> </div><br>
<div align="center"> <img src="index_files/8ef22836-8800-4765-b4b8-ade80096b323.jpg"/> </div><br>
<div align="center"> <img src="../pics//8ef22836-8800-4765-b4b8-ade80096b323.jpg"/> </div><br>
<div align="center"> <div align="center"><img src="https://latex.codecogs.com/gif.latex?S2(Sno,Cname,Grade)"/></div> <br> </div><br>
<div align="center"> <div align="center"><img src="https://latex.codecogs.com/gif.latex?Sno,Cname->Grade"/></div> <br> </div><br>
<div align="center"> <img src="index_files/b0748916-1acd-4138-b24c-69326cb452fe.jpg"/> </div><br>
<div align="center"> <img src="../pics//b0748916-1acd-4138-b24c-69326cb452fe.jpg"/> </div><br>
## 第三范式 (3NF)
@@ -242,7 +242,7 @@ MySQL InnoDB 采用的是两阶段锁协议。在事务执行过程中,随时
上述 S1 存在传递依赖,Mname 依赖于 Sdept,而 Sdept 又依赖于 Sno,可以继续分解。
<div align="center"> <img src="index_files/923896c1-937e-4a38-b8a6-cec3040b4e2a.jpg"/> </div><br>
<div align="center"> <img src="../pics//923896c1-937e-4a38-b8a6-cec3040b4e2a.jpg"/> </div><br>
## BC 范式(BCNF)
@@ -324,29 +324,29 @@ Entity-Relationship,有三个组成部分:实体、属性、联系。
如果 A 到 B 是 1 对多关系,那么画个带箭头的线段指向 B;如果是 1 对 1,画两个带箭头的线段;如果是多对多,画两个不带箭头的线段。下图的 Course 和 Student 是 1 对多的关系。
<div align="center"> <img src="index_files/292b4a35-4507-4256-84ff-c218f108ee31.jpg"/> </div><br>
<div align="center"> <img src="../pics//292b4a35-4507-4256-84ff-c218f108ee31.jpg"/> </div><br>
## 表示出现多次的关系
一个实体在联系出现几次,就要用几条线连接。下图表示一个课程的先修关系,先修关系出现两个 Course 实体,第一个是先修课程,后一个是后修课程,因此需要用两条线来表示这种关系。
<div align="center"> <img src="index_files/8b798007-e0fb-420c-b981-ead215692417.jpg"/> </div><br>
<div align="center"> <img src="../pics//8b798007-e0fb-420c-b981-ead215692417.jpg"/> </div><br>
## 联系的多向性
虽然老师可以开设多门课,并且可以教授多名学生,但是对于特定的学生和课程,只有一个老师教授,这就构成了一个三元联系。
<div align="center"> <img src="index_files/423f2a40-bee1-488e-b460-8e76c48ee560.png"/> </div><br>
<div align="center"> <img src="../pics//423f2a40-bee1-488e-b460-8e76c48ee560.png"/> </div><br>
一般只使用二元联系,可以把多元关系转换为二元关系。
<div align="center"> <img src="index_files/de9b9ea0-1327-4865-93e5-6f805c48bc9e.png"/> </div><br>
<div align="center"> <img src="../pics//de9b9ea0-1327-4865-93e5-6f805c48bc9e.png"/> </div><br>
## 表示子类
用 IS-A 联系来表示子类,具体做法是用一个三角形和两条线来连接类和子类。与子类有关的属性和联系都连到子类上,而与父类和子类都有关的连到父类上。
<div align="center"> <img src="index_files/7ec9d619-fa60-4a2b-95aa-bf1a62aad408.jpg"/> </div><br>
<div align="center"> <img src="../pics//7ec9d619-fa60-4a2b-95aa-bf1a62aad408.jpg"/> </div><br>
# 一些概念
+47 -47
View File
File diff suppressed because it is too large Load Diff
+13 -13
View File
@@ -177,7 +177,7 @@
## 进程状态的切换
<div align="center"> <img src="index_files/1706ce58-a081-4fed-9b36-c3c0d7e22b3a.jpg"/> </div><br>
<div align="center"> <img src="../pics//1706ce58-a081-4fed-9b36-c3c0d7e22b3a.jpg"/> </div><br>
阻塞状态是缺少需要的资源从而由运行状态转换而来,但是该资源不包括 CPU,缺少 CPU 会让进程从运行态转换为就绪态。
@@ -227,7 +227,7 @@ shortest remaining time next(SRTN)。
#### 2.3 多级反馈队列
<div align="center"> <img src="index_files/042cf928-3c8e-4815-ae9c-f2780202c68f.png"/> </div><br>
<div align="center"> <img src="../pics//042cf928-3c8e-4815-ae9c-f2780202c68f.png"/> </div><br>
如果一个进程需要执行 100 个时间片,如果采用轮转调度算法,那么需要交换 100 次。多级队列是为这种需要连续执行多个时间片的进程考虑,它设置了多个队列,每个队列时间片大小都不同,例如 1,2,4,8,..。进程在第一个队列没执行完,就会被移到下一个队列。这种方式下,之前的进程只需要 7 (包括最初的装入)的交换。
@@ -475,7 +475,7 @@ void writer() {
### 2. 哲学家进餐问题
<div align="center"> <img src="index_files/a9077f06-7584-4f2b-8c20-3a8e46928820.jpg"/> </div><br>
<div align="center"> <img src="../pics//a9077f06-7584-4f2b-8c20-3a8e46928820.jpg"/> </div><br>
五个哲学家围着一张圆桌,每个哲学家面前放着食物。哲学家的生活有两种交替活动:吃饭以及思考。当一个哲学家吃饭时,需要先拿起筷子左右的两根筷子,并且一次只能拿起一根筷子。
@@ -553,7 +553,7 @@ void test(i) { // 尝试拿起两把筷子
## 死锁的必要条件
<div align="center"> <img src="index_files/c037c901-7eae-4e31-a1e4-9d41329e5c3e.png"/> </div><br>
<div align="center"> <img src="../pics//c037c901-7eae-4e31-a1e4-9d41329e5c3e.png"/> </div><br>
1. 互斥:每个资源要么已经分配给了一个进程,要么就是可用的。
2. 占有和等待:已经得到了某个资源的进程可以再请求新的资源。
@@ -576,7 +576,7 @@ void test(i) { // 尝试拿起两把筷子
#### 2.1 每种类型一个资源的死锁检测
<div align="center"> <img src="index_files/b1fa0453-a4b0-4eae-a352-48acca8fff74.png"/> </div><br>
<div align="center"> <img src="../pics//b1fa0453-a4b0-4eae-a352-48acca8fff74.png"/> </div><br>
上图为资源分配图,其中方框表示资源,圆圈表示进程。资源指向进程表示该资源已经分配给该进程,进程指向资源表示进程请求获取该资源。
@@ -586,7 +586,7 @@ void test(i) { // 尝试拿起两把筷子
#### 2.2 每种类型多个资源的死锁检测
<div align="center"> <img src="index_files/e1eda3d5-5ec8-4708-8e25-1a04c5e11f48.png"/> </div><br>
<div align="center"> <img src="../pics//e1eda3d5-5ec8-4708-8e25-1a04c5e11f48.png"/> </div><br>
上图中,有三个进程四个资源,每个数据代表的含义如下:
@@ -635,7 +635,7 @@ void test(i) { // 尝试拿起两把筷子
#### 4.1 安全状态
<div align="center"> <img src="index_files/ed523051-608f-4c3f-b343-383e2d194470.png"/> </div><br>
<div align="center"> <img src="../pics//ed523051-608f-4c3f-b343-383e2d194470.png"/> </div><br>
图 a 的第二列 Has 表示已拥有的资源数,第三列 Max 表示总共需要的资源数,Free 表示还有可以使用的资源数。从图 a 开始出发,先让 B 拥有所需的所有资源(图 b),运行结束后释放 B,此时 Free 变为 5(图 c);接着以同样的方式运行 C 和 A,使得所有进程都能成功运行,因此可以称图 a 所示的状态时安全的。
@@ -647,13 +647,13 @@ void test(i) { // 尝试拿起两把筷子
一个小城镇的银行家,他向一群客户分别承诺了一定的贷款额度,算法要做的是判断对请求的满足是否会进入不安全状态,如果是,就拒绝请求;否则予以分配。
<div align="center"> <img src="index_files/d160ec2e-cfe2-4640-bda7-62f53e58b8c0.png"/> </div><br>
<div align="center"> <img src="../pics//d160ec2e-cfe2-4640-bda7-62f53e58b8c0.png"/> </div><br>
上图 c 为不安全状态,因此算法会拒绝之前的请求,从而避免进入图 c 中的状态。
#### 4.3 多个资源的银行家算法
<div align="center"> <img src="index_files/62e0dd4f-44c3-43ee-bb6e-fedb9e068519.png"/> </div><br>
<div align="center"> <img src="../pics//62e0dd4f-44c3-43ee-bb6e-fedb9e068519.png"/> </div><br>
上图中有五个进程,四个资源。左边的图表示已经分配的资源,右边的图表示还需要分配的资源。最右边的 E、P 以及 A 分别表示:总资源、已分配资源以及可用资源,注意这三个为向量,而不是具体数值,例如 A=(1020),表示 4 个资源分别还剩下 1/0/2/0。
@@ -675,7 +675,7 @@ void test(i) { // 尝试拿起两把筷子
大部分虚拟内存系统都使用分页技术。把由程序产生的地址称为虚拟地址,它们构成了一个虚拟地址空间。例如有一台计算机可以产生 16 位地址,它的虚拟地址空间为 0\~64K,然而计算机只有 32KB 的物理内存,因此虽然可以编写 64KB 的程序,但它们不能被完全调入内存运行。
<div align="center"> <img src="index_files/7b281b1e-0595-402b-ae35-8c91084c33c1.png"/> </div><br>
<div align="center"> <img src="../pics//7b281b1e-0595-402b-ae35-8c91084c33c1.png"/> </div><br>
虚拟地址空间划分成固定大小的页,在物理内存中对应的单元称为页框,页和页框大小通常相同,它们之间通过页表进行映射。
@@ -683,11 +683,11 @@ void test(i) { // 尝试拿起两把筷子
### 2. 分段
<div align="center"> <img src="index_files/22de0538-7c6e-4365-bd3b-8ce3c5900216.png"/> </div><br>
<div align="center"> <img src="../pics//22de0538-7c6e-4365-bd3b-8ce3c5900216.png"/> </div><br>
上图为一个编译器在编译过程中建立的多个表,有 4 个表是动态增长的,如果使用分页系统的一维地址空间,动态增长的特点会导致覆盖问题的出现。
<div align="center"> <img src="index_files/e0900bb2-220a-43b7-9aa9-1d5cd55ff56e.png"/> </div><br>
<div align="center"> <img src="../pics//e0900bb2-220a-43b7-9aa9-1d5cd55ff56e.png"/> </div><br>
分段的做法是把每个表分成段,一个段构成一个独立的地址空间。每个段的长度可以不同,并且可以动态增长。
@@ -741,7 +741,7 @@ void test(i) { // 尝试拿起两把筷子
<div align="center"> <div align="center"><img src="https://latex.codecogs.com/gif.latex?4,7,0,7,1,0,1,2,1,2,6"/></div> <br> </div><br>
<div align="center"> <img src="index_files/eb859228-c0f2-4bce-910d-d9f76929352b.png"/> </div><br>
<div align="center"> <img src="../pics//eb859228-c0f2-4bce-910d-d9f76929352b.png"/> </div><br>
### 4. 时钟(Clock)
File diff suppressed because it is too large Load Diff
+43 -43
View File
File diff suppressed because it is too large Load Diff
+12 -12
View File
@@ -124,7 +124,7 @@
包括三个类:Movie、Rental 和 Customer,Rental 包含租赁的 Movie 以及天数。
<div align="center"> <img src="index_files/25d6d3d4-4726-47b1-a9cb-3316d1ff5dd5.png"/> </div><br>
<div align="center"> <img src="../pics//25d6d3d4-4726-47b1-a9cb-3316d1ff5dd5.png"/> </div><br>
最开始的实现是把所有的计费代码都放在 Customer 类中。
@@ -159,19 +159,19 @@ double getTotalCharge() {
以下是继承 Movie 的多态解决方案,这种方案可以解决上述的 switch 问题,因为每种电影类别的计费方式都被放到了对应 Movie 子类中,当变化发生时,只需要去修改对应子类中的代码即可。
<div align="center"> <img src="index_files/76b48b4c-8999-4967-893b-832602e73285.png"/> </div><br>
<div align="center"> <img src="../pics//76b48b4c-8999-4967-893b-832602e73285.png"/> </div><br>
但是由于 Movie 可以在其生命周期内修改自己的类别,一个对象却不能在生命周期内修改自己所属的类,因此这种方案不可行。可以使用策略模式来解决这个问题(原书写的是使用状态模式,但是这里应该为策略模式,具体可以参考设计模式内容)。
下图中,Price 有多种实现,Movie 组合了一个 Price 对象,并且在运行时可以改变组合的 Price 对象,从而使得它的计费方式发生改变。
<div align="center"> <img src="index_files/2a842a14-e4ab-4f37-83fa-f82c206fe426.png"/> </div><br>
<div align="center"> <img src="../pics//2a842a14-e4ab-4f37-83fa-f82c206fe426.png"/> </div><br>
重构后整体的类图和时序图如下:
<div align="center"> <img src="index_files/9d549816-60b7-4899-9877-23b01503ab13.png"/> </div><br>
<div align="center"> <img src="../pics//9d549816-60b7-4899-9877-23b01503ab13.png"/> </div><br>
<div align="center"> <img src="index_files/2c8a7a87-1bf1-4d66-9ba9-225a1add0a51.png"/> </div><br>
<div align="center"> <img src="../pics//2c8a7a87-1bf1-4d66-9ba9-225a1add0a51.png"/> </div><br>
# 重构原则
@@ -579,7 +579,7 @@ Hide Delegate 有很大好处,但是它的代价是:每当客户要使用受
将该数据赋值到一个领域对象中,建立一个 Oberver 模式,用以同步领域对象和 GUI 对象内的重复数据。
<div align="center"> <img src="index_files/e024bd7e-fb4e-4239-9451-9a6227f50b00.jpg"/> </div><br>
<div align="center"> <img src="../pics//e024bd7e-fb4e-4239-9451-9a6227f50b00.jpg"/> </div><br>
## 7. Change Unidirectional Association to Bidirectional(将单向关联改为双向关联)
@@ -636,13 +636,13 @@ public 字段应当改为 private,并提供相应的访问函数。
类中有一个数值类型码,但它并不影响类的行为,就用一个新类替换该数值类型码。如果类型码出现在 switch 语句中,需要使用 Replace Conditional with Polymorphism 去掉 switch,首先必须运用 Replace Type Code with Subcalss 或 Replace Type Code with State/Strategy 去掉类型码。
<div align="center"> <img src="index_files/27c2e0b3-8f95-453d-bedc-6398a8566ce9.jpg"/> </div><br>
<div align="center"> <img src="../pics//27c2e0b3-8f95-453d-bedc-6398a8566ce9.jpg"/> </div><br>
## 14. Replace Type Code with Subcalsses(以子类取代类型码)
有一个不可变的类型码,它会影响类的行为,以子类取代这个类型码。
<div align="center"> <img src="index_files/c41d3977-e0e7-4ee4-93e1-d84f1ae3e20e.jpg"/> </div><br>
<div align="center"> <img src="../pics//c41d3977-e0e7-4ee4-93e1-d84f1ae3e20e.jpg"/> </div><br>
## 15. Replace Type Code with State/Strategy (以 State/Strategy 取代类型码)
@@ -650,13 +650,13 @@ public 字段应当改为 private,并提供相应的访问函数。
和 Replace Type Code with Subcalsses 的区别是 Replace Type Code with State/Strategy 的类型码是动态可变的,前者通过继承的方式来实现,后者通过组合的方式来实现。因为类型码可变,如果通过继承的方式,一旦一个对象的类型码改变,那么就要改变用新的对象来取代旧对象,而客户端难以改变新的对象。但是通过组合的方式,改变引用的状态类是很容易的。
<div align="center"> <img src="index_files/81fd1d6f-a3b2-4160-9a0a-1f7cb50ba440.jpg"/> </div><br>
<div align="center"> <img src="../pics//81fd1d6f-a3b2-4160-9a0a-1f7cb50ba440.jpg"/> </div><br>
## 16. Replace Subclass with Fields(以字段取代子类)
各个子类的唯一差别只在“返回常量数据”的函数上。
<div align="center"> <img src="index_files/f2e0cee9-ecdc-4a96-853f-d9f6a1ad6ad1.jpg"/> </div><br>
<div align="center"> <img src="../pics//f2e0cee9-ecdc-4a96-853f-d9f6a1ad6ad1.jpg"/> </div><br>
# 简化条件表达式
@@ -776,7 +776,7 @@ double getSpeed() {
}
```
<div align="center"> <img src="index_files/1c8432c8-2552-457f-b117-1da36c697221.jpg"/> </div><br>
<div align="center"> <img src="../pics//1c8432c8-2552-457f-b117-1da36c697221.jpg"/> </div><br>
## 7. Introduce Null Object(引入Null对象)
@@ -916,7 +916,7 @@ double finalPrice = discountedPrice (basePrice);
以一个对象取代这些参数。
<div align="center"> <img src="index_files/08738dd0-ae8e-404a-ba78-a6b1b7d225b3.jpg"/> </div><br>
<div align="center"> <img src="../pics//08738dd0-ae8e-404a-ba78-a6b1b7d225b3.jpg"/> </div><br>
## 10. Remove Setting Method(移除设值函数)

Some files were not shown because too many files have changed in this diff Show More