auto commit
This commit is contained in:
parent
9493851ad4
commit
aeaa7740c0
369
notes/HTTP.md
369
notes/HTTP.md
@ -41,738 +41,369 @@
|
||||
<!-- GFM-TOC -->
|
||||
|
||||
|
||||
|
||||
|
||||
# 基础概念
|
||||
|
||||
|
||||
|
||||
## Web 基础
|
||||
|
||||
|
||||
|
||||
- HTTP(HyperText Transfer Protocol,超文本传输协议)。
|
||||
|
||||
- WWW(World Wide Web)的三种技术:HTML、HTTP、URL。
|
||||
|
||||
- RFC(Request for Comments,征求修正意见书),互联网的设计文档。
|
||||
|
||||
|
||||
|
||||
## 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。
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//4102b7d0-39b9-48d8-82ae-ac4addb7ebfb.jpg)
|
||||
|
||||
|
||||
|
||||
## 请求和响应报文
|
||||
|
||||
|
||||
|
||||
**请求报文**
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//22b39f77-ac47-4978-91ed-84aaf457644c.jpg)
|
||||
|
||||
|
||||
|
||||
**响应报文**
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//00d8d345-cd4a-48af-919e-209d2788eca7.jpg)
|
||||
|
||||
|
||||
|
||||
# HTTP 方法
|
||||
|
||||
|
||||
|
||||
客户端发送的请求报文第一行为请求行,包含了方法字段。
|
||||
|
||||
|
||||
|
||||
## GET:获取资源
|
||||
|
||||
|
||||
|
||||
## POST:传输实体主体
|
||||
|
||||
|
||||
|
||||
POST 主要目的不是获取资源,而是传输实体主体数据。
|
||||
|
||||
|
||||
|
||||
GET 和 POST 的请求都能使用额外的参数,但是 GET 的参数是以查询字符串出现在 URL 中,而 POST 的参数存储在实体主体部分。
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
GET /test/demo_form.asp?name1=value1&name2=value2 HTTP/1.1
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
POST /test/demo_form.asp HTTP/1.1
|
||||
|
||||
Host: w3schools.com
|
||||
|
||||
name1=value1&name2=value2
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
GET 的传参方式相比于 POST 安全性较差,因为 GET 传的参数在 URL 是可见的,可能会泄露私密信息。并且 GET 只支持 ASCII 字符,如果参数为中文则可能会出现乱码,而 POST 支持标准字符集。
|
||||
|
||||
|
||||
|
||||
## HEAD:获取报文首部
|
||||
|
||||
|
||||
|
||||
和 GET 方法一样,但是不返回报文实体主体部分。
|
||||
|
||||
|
||||
|
||||
主要用于确认 URL 的有效性以及资源更新的日期时间等。
|
||||
|
||||
|
||||
|
||||
## PUT:上传文件
|
||||
|
||||
|
||||
|
||||
由于自身不带验证机制,任何人都可以上传文件,因此存在安全性问题,一般 WEB 网站不使用该方法。
|
||||
|
||||
|
||||
|
||||
## DELETE:删除文件
|
||||
|
||||
|
||||
|
||||
与 PUT 功能相反,并且同样不带验证机制。
|
||||
|
||||
|
||||
|
||||
## OPTIONS:查询支持的方法
|
||||
|
||||
|
||||
|
||||
查询指定的 URL 能够支持的方法。
|
||||
|
||||
|
||||
|
||||
会返回 Allow: GET, POST, HEAD, OPTIONS 这样的内容。
|
||||
|
||||
|
||||
|
||||
## TRACE:追踪路径
|
||||
|
||||
|
||||
|
||||
服务器会将通信路径返回给客户端。
|
||||
|
||||
|
||||
|
||||
发送请求时,在 Max-Forwards 首部字段中填入数值,每经过一个服务器就会减 1,当数值为 0 时就停止传输。
|
||||
|
||||
|
||||
|
||||
TRACE 一般不会使用,并且它容易受到 XST 攻击(Cross-Site Tracing,跨站追踪),因此更不会去使用它。
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//c8637fd2-3aaa-46c4-b7d9-f24d3fa04781.jpg)
|
||||
|
||||
|
||||
|
||||
## CONNECT:要求用隧道协议连接代理
|
||||
|
||||
|
||||
|
||||
主要使用 SSL(Secure Sokets Layer,安全套接字)和 TLS(Transport Layer Security,传输层安全)协议把通信内容加密后经网络隧道传输。
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//5994928c-3d2d-45bd-abb1-adc4f5f4d775.jpg)
|
||||
|
||||
|
||||
|
||||
# HTTP 状态码
|
||||
|
||||
|
||||
|
||||
服务器返回的响应报文中第一行为状态行,包含了状态码以及原因短语,来告知客户端请求的结果。
|
||||
|
||||
|
||||
|
||||
| 状态码 | 类别 | 原因短语 |
|
||||
|
||||
| --- | --- | --- |
|
||||
|
||||
| 1XX | Informational(信息性状态码) | 接收的请求正在处理 |
|
||||
|
||||
| 2XX | Success(成功状态码) | 请求正常处理完毕 |
|
||||
|
||||
| 3XX | Redirection(重定向状态码) | 需要进行附加操作以完成请求 |
|
||||
|
||||
| 4XX | Client Error(客户端错误状态码) | 服务器无法处理请求 |
|
||||
|
||||
| 5XX | Server Error(服务器错误状态码) | 服务器处理请求出错 |
|
||||
|
||||
|
||||
|
||||
## 2XX 成功
|
||||
|
||||
|
||||
|
||||
- **200 OK**
|
||||
|
||||
|
||||
|
||||
- **204 No Content**:请求已经成功处理,但是返回的响应报文不包含实体的主体部分。一般在只需要从客户端往服务器发送信息,而不需要返回数据时使用。
|
||||
|
||||
|
||||
|
||||
- **206 Partial Content**
|
||||
|
||||
|
||||
|
||||
## 3XX 重定向
|
||||
|
||||
|
||||
|
||||
- **301 Moved Permanently**:永久性重定向
|
||||
|
||||
|
||||
|
||||
- **302 Found**:临时性重定向
|
||||
|
||||
|
||||
|
||||
- **303 See Other**
|
||||
|
||||
|
||||
|
||||
- 注:虽然 HTTP 协议规定 301、302 状态下重定向时不允许把 POST 方法改成 GET 方法,但是大多数浏览器都会 在 301、302 和 303 状态下的重定向把 POST 方法改成 GET 方法。
|
||||
|
||||
|
||||
|
||||
- **304 Not Modified**:如果请求报文首部包含一些条件,例如:If-Match,If-ModifiedSince,If-None-Match,If-Range,If-Unmodified-Since,但是不满足条件,则服务器会返回 304 状态码。
|
||||
|
||||
|
||||
|
||||
- **307 Temporary Redirect**:临时重定向,与 302 的含义类似,但是 307 要求浏览器不会把重定向请求的 POST 方法改成 GET 方法。
|
||||
|
||||
|
||||
|
||||
## 4XX 客户端错误
|
||||
|
||||
|
||||
|
||||
- **400 Bad Request**:请求报文中存在语法错误
|
||||
|
||||
|
||||
|
||||
- **401 Unauthorized**:该状态码表示发送的请求需要有通过 HTTP 认证(BASIC 认证、DIGEST 认证)的认证信息。如果之前已进行过一次请求,则表示用户认证失败。
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//b1b4cf7d-c54a-4ff1-9741-cd2eea331123.jpg)
|
||||
|
||||
|
||||
|
||||
- **403 Forbidden**:请求被拒绝,服务器端没有必要给出拒绝的详细理由。
|
||||
|
||||
|
||||
|
||||
- **404 Not Found**
|
||||
|
||||
|
||||
|
||||
## 5XX 服务器错误
|
||||
|
||||
|
||||
|
||||
- **500 Internal Server Error**:服务器正在执行请求时发生错误
|
||||
|
||||
|
||||
|
||||
- **503 Service Unavilable**:该状态码表明服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。
|
||||
|
||||
|
||||
|
||||
# HTTP 首部
|
||||
|
||||
|
||||
|
||||
有 4 种类型的首部字段:通用首部字段、请求首部字段、响应首部字段和实体首部字段。
|
||||
|
||||
|
||||
|
||||
各种首部字段及其含义如下(不需要全记,仅供查阅):
|
||||
|
||||
|
||||
|
||||
## 通用首部字段
|
||||
|
||||
|
||||
|
||||
| 首部字段名 | 说明 |
|
||||
|
||||
| -- | -- |
|
||||
|
||||
| Cache-Control | 控制缓存的行为 |
|
||||
|
||||
| Connection | 逐跳首部、 连接的管理 |
|
||||
|
||||
| Date | 创建报文的日期时间 |
|
||||
|
||||
| Pragma | 报文指令 |
|
||||
|
||||
| Trailer | 报文末端的首部一览 |
|
||||
|
||||
| Transfer-Encoding | 指定报文主体的传输编码方式 |
|
||||
|
||||
| Upgrade | 升级为其他协议 |
|
||||
|
||||
| Via | 代理服务器的相关信息 |
|
||||
|
||||
| Warning | 错误通知 |
|
||||
|
||||
|
||||
|
||||
## 请求首部字段
|
||||
|
||||
|
||||
|
||||
| 首部字段名 | 说明 |
|
||||
|
||||
| -- | -- |
|
||||
|
||||
| Accept | 用户代理可处理的媒体类型 |
|
||||
|
||||
| Accept-Charset | 优先的字符集 |
|
||||
|
||||
| Accept-Encoding | 优先的内容编码 |
|
||||
|
||||
| Accept-Language | 优先的语言(自然语言) |
|
||||
|
||||
| Authorization | Web 认证信息 |
|
||||
|
||||
| Expect | 期待服务器的特定行为 |
|
||||
|
||||
| From | 用户的电子邮箱地址 |
|
||||
|
||||
| Host | 请求资源所在服务器 |
|
||||
|
||||
| If-Match | 比较实体标记(ETag) |
|
||||
|
||||
| If-Modified-Since | 比较资源的更新时间 |
|
||||
|
||||
| If-None-Match | 比较实体标记(与 If-Match 相反) |
|
||||
|
||||
| If-Range | 资源未更新时发送实体 Byte 的范围请求 |
|
||||
|
||||
| If-Unmodified-Since | 比较资源的更新时间(与 If-Modified-Since 相反) |
|
||||
|
||||
| Max-Forwards | 最大传输逐跳数 |
|
||||
|
||||
| Proxy-Authorization | 代理服务器要求客户端的认证信息 |
|
||||
|
||||
| Range | 实体的字节范围请求 |
|
||||
|
||||
| Referer | 对请求中 URI 的原始获取方 |
|
||||
|
||||
| TE | 传输编码的优先级 |
|
||||
|
||||
| User-Agent | HTTP 客户端程序的信息 |
|
||||
|
||||
|
||||
|
||||
## 响应首部字段
|
||||
|
||||
|
||||
|
||||
| 首部字段名 | 说明 |
|
||||
|
||||
| -- | -- |
|
||||
|
||||
| Accept-Ranges | 是否接受字节范围请求 |
|
||||
|
||||
| Age | 推算资源创建经过时间 |
|
||||
|
||||
| ETag | 资源的匹配信息 |
|
||||
|
||||
| Location | 令客户端重定向至指定 URI |
|
||||
|
||||
| Proxy-Authenticate | 代理服务器对客户端的认证信息 |
|
||||
|
||||
| Retry-After | 对再次发起请求的时机要求 |
|
||||
|
||||
| Server | HTTP 服务器的安装信息 |
|
||||
|
||||
| Vary | 代理服务器缓存的管理信息 |
|
||||
|
||||
| WWW-Authenticate | 服务器对客户端的认证信息 |
|
||||
|
||||
|
||||
|
||||
## 实体首部字段
|
||||
|
||||
|
||||
|
||||
| 首部字段名 | 说明 |
|
||||
|
||||
| -- | -- |
|
||||
|
||||
| Allow | 资源可支持的 HTTP 方法 |
|
||||
|
||||
| Content-Encoding | 实体主体适用的编码方式 |
|
||||
|
||||
| Content-Language | 实体主体的自然语言 |
|
||||
|
||||
| Content-Length | 实体主体的大小(单位: 字节) |
|
||||
|
||||
| Content-Location | 替代对应资源的 URI |
|
||||
|
||||
| Content-MD5 | 实体主体的报文摘要 |
|
||||
|
||||
| Content-Range | 实体主体的位置范围 |
|
||||
|
||||
| Content-Type | 实体主体的媒体类型 |
|
||||
|
||||
| Expires | 实体主体过期的日期时间 |
|
||||
|
||||
| Last-Modified | 资源的最后修改日期时间 |
|
||||
|
||||
|
||||
|
||||
# 具体应用
|
||||
|
||||
|
||||
|
||||
## Cookie
|
||||
|
||||
|
||||
|
||||
HTTP 协议是无状态的,主要是为了让 HTTP 协议尽可能简单,使得它能够处理大量事务。HTTP/1.1 引入 Cookie 来保存状态信息。
|
||||
|
||||
|
||||
|
||||
服务器发送的响应报文包含 Set-Cookie 字段,客户端得到响应报文后把 Cookie 内容保存到浏览器中。下次再发送请求时,从浏览器中读出 Cookie 值,在请求报文中包含 Cookie 字段,这样服务器就知道客户端的状态信息了。Cookie 状态信息保存在客户端浏览器中,而不是服务器上。
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//ff17c103-750a-4bb8-9afa-576327023af9.png)
|
||||
|
||||
|
||||
|
||||
Set-Cookie 字段有以下属性:
|
||||
|
||||
|
||||
|
||||
| 属性 | 说明 |
|
||||
|
||||
| -- | -- |
|
||||
|
||||
| NAME=VALUE | 赋予 Cookie 的名称和其值(必需项) |
|
||||
|
||||
| expires=DATE | Cookie 的有效期(若不明确指定则默认为浏览器关闭前为止) |
|
||||
|
||||
| path=PATH | 将服务器上的文件目录作为 Cookie 的适用对象(若不指定则默认为文档所在的文件目录) |
|
||||
|
||||
| domain= 域名 | 作为 Cookie 适用对象的域名(若不指定则默认为创建 Cookie 的服务器的域名) |
|
||||
|
||||
| Secure | 仅在 HTTPS 安全通信时才会发送 Cookie |
|
||||
|
||||
| HttpOnly | 加以限制,使 Cookie 不能被 JavaScript 脚本访问 |
|
||||
|
||||
|
||||
|
||||
**Session 和 Cookie 区别**
|
||||
|
||||
|
||||
|
||||
Session 是服务器用来跟踪用户的一种手段,每个 Session 都有一个唯一标识:Session ID。当服务器创建了一个 Session 时,给客户端发送的响应报文就包含了 Set-Cookie 字段,其中有一个名为 sid 的键值对,这个键值对就是 Session ID。客户端收到后就把 Cookie 保存在浏览器中,并且之后发送的请求报文都包含 Session ID。HTTP 就是通过 Session 和 Cookie 这两种方式一起合作来实现跟踪用户状态的,Session 用于服务器端,Cookie 用于客户端。
|
||||
|
||||
|
||||
|
||||
**浏览器禁用 Cookie 的情况**
|
||||
|
||||
|
||||
|
||||
会使用 URL 重写技术,在 URL 后面加上 sid=xxx 。
|
||||
|
||||
|
||||
|
||||
**使用 Cookie 实现用户名和密码的自动填写**
|
||||
|
||||
|
||||
|
||||
网站脚本会自动从 Cookie 中读取用户名和密码,从而实现自动填写。
|
||||
|
||||
|
||||
|
||||
## 缓存
|
||||
|
||||
|
||||
|
||||
有两种缓存方法:让代理服务器进行缓存和让客户端浏览器进行缓存。
|
||||
|
||||
|
||||
|
||||
Cache-Control 用于控制缓存的行为。Cache-Control: no-cache 有两种含义,如果是客户端向缓存服务器发送的请求报文中含有该指令,表示客户端不想要缓存的资源;如果是源服务器向缓存服务器发送的响应报文中含有该指令,表示缓存服务器不能对资源进行缓存。
|
||||
|
||||
|
||||
|
||||
Expires 字段可以用于告知缓存服务器该资源什么时候会过期。当首部字段 Cache-Control 有指定 max-age 指令时,比起首部字段 Expires,会优先处理 max-age 指令。
|
||||
|
||||
|
||||
|
||||
## 持久连接
|
||||
|
||||
|
||||
|
||||
当浏览器访问一个包含多张图片的 HTML 页面时,除了请求访问 HTML 页面资源,还会请求图片资源,如果每进行一次 HTTP 通信就要断开一次 TCP 连接,连接建立和断开的开销会很大。**持久连接** 只需要进行一次 TCP 连接就能进行多次 HTTP 通信。HTTP/1.1 开始,所有的连接默认都是持久连接。
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//c73a0b78-5f46-4d2d-a009-dab2a999b5d8.jpg)
|
||||
|
||||
|
||||
|
||||
持久连接需要使用 Connection 首部字段进行管理。HTTP/1.1 开始 HTTP 默认是持久化连接的,如果要断开 TCP 连接,需要由客户端或者服务器端提出断开,使用 Connection: close;而在 HTTP/1.1 之前默认是非持久化连接的,如果要维持持续连接,需要使用 Keep-Alive。
|
||||
|
||||
|
||||
|
||||
管线化方式可以同时发送多个请求和响应,而不需要发送一个请求然后等待响应之后再发下一个请求。
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//6943e2af-5a70-4004-8bee-b33d60f39da3.jpg)
|
||||
|
||||
|
||||
|
||||
## 编码
|
||||
|
||||
|
||||
|
||||
编码(Encoding)主要是为了对实体进行压缩。常用的编码有:gzip、compress、deflate、identity,其中 identity 表示不执行压缩的编码格式。
|
||||
|
||||
|
||||
|
||||
## 分块传输
|
||||
|
||||
|
||||
|
||||
分块传输(Chunked Transfer Coding)可以把数据分割成多块,让浏览器逐步显示页面。
|
||||
|
||||
|
||||
|
||||
## 多部分对象集合
|
||||
|
||||
|
||||
|
||||
一份报文主体内可含有多种类型的实体同时发送,每个部分之间用 boundary 字段定义的分隔符进行分隔;每个部分都可以有首部字段。
|
||||
|
||||
|
||||
|
||||
例如,上传多个表单时可以使用如下方式:
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//2279cc60-9714-4e0e-aac9-4c348e0c2165.png)
|
||||
|
||||
|
||||
|
||||
## 范围请求
|
||||
|
||||
|
||||
|
||||
如果网络出现中断,服务器只发送了一部分数据,范围请求使得客户端能够只请求未发送的那部分数据,从而避免服务器端重新发送所有数据。
|
||||
|
||||
|
||||
|
||||
在请求报文首部中添加 Range 字段,然后指定请求的范围,例如 Range:bytes=5001-10000。请求成功的话服务器发送 206 Partial Content 状态。
|
||||
|
||||
|
||||
|
||||
## 内容协商
|
||||
|
||||
|
||||
|
||||
通过内容协商返回最合适的内容,例如根据浏览器的默认语言选择返回中文界面还是英文界面。
|
||||
|
||||
|
||||
|
||||
涉及以下首部字段:Accept、Accept-Charset、Accept-Encoding、Accept-Language、Content-Language。
|
||||
|
||||
|
||||
|
||||
## 虚拟主机
|
||||
|
||||
|
||||
|
||||
使用虚拟主机技术,使得一台服务器拥有多个域名,并且在逻辑上可以看成多个服务器。
|
||||
|
||||
|
||||
|
||||
## 通信数据转发
|
||||
|
||||
|
||||
|
||||
**代理**
|
||||
|
||||
|
||||
|
||||
代理服务器接受客户端的请求,并且转发给其它服务器。
|
||||
|
||||
|
||||
|
||||
代理服务器一般是透明的,不会改变 URL。
|
||||
|
||||
|
||||
|
||||
使用代理的主要目的是:缓存、网络访问控制以及访问日志记录。
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//c07035c3-a9ba-4508-8e3c-d8ae4c6ee9ee.jpg)
|
||||
|
||||
|
||||
|
||||
**网关**
|
||||
|
||||
|
||||
|
||||
与代理服务器不同的是,网关服务器会将 HTTP 转化为其它协议进行通信,从而请求其它非 HTTP 服务器的服务。
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//81375888-6be1-476f-9521-42eea3e3154f.jpg)
|
||||
|
||||
|
||||
|
||||
**隧道**
|
||||
|
||||
|
||||
|
||||
使用 SSL 等加密手段,为客户端和服务器之间建立一条安全的通信线路。
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//64b95403-d976-421a-8b45-bac89c0b5185.jpg)
|
||||
|
||||
|
||||
|
||||
# HTTPs
|
||||
|
||||
|
||||
|
||||
HTTP 有以下安全性问题:
|
||||
|
||||
|
||||
|
||||
1. 使用明文进行通信,内容可能会被窃听;
|
||||
|
||||
2. 不验证通信方的身份,通信方的身份有可能遭遇伪装;
|
||||
|
||||
3. 无法证明报文的完整性,报文有可能遭篡改。
|
||||
|
||||
|
||||
|
||||
HTTPs 并不是新协议,而是 HTTP 先和 SSL(Secure Socket Layer)通信,再由 SSL 和 TCP 通信。通过使用 SSL,HTTPs 提供了加密、认证和完整性保护。
|
||||
|
||||
|
||||
|
||||
## 加密
|
||||
|
||||
|
||||
|
||||
有两种加密方式:对称密钥加密和公开密钥加密。对称密钥加密的加密和解密使用同一密钥,而公开密钥加密使用一对密钥用于加密和解密,分别为公开密钥和私有密钥。公开密钥所有人都可以获得,通信发送方获得接收方的公开密钥之后,就可以使用公开密钥进行加密,接收方收到通信内容后使用私有密钥解密。
|
||||
|
||||
|
||||
|
||||
对称密钥加密的缺点:无法安全传输密钥;公开密钥加密的缺点:相对来说更耗时。
|
||||
|
||||
|
||||
|
||||
HTTPs 采用 **混合的加密机制**,使用公开密钥加密用于传输对称密钥,之后使用对称密钥加密进行通信。(下图中,共享密钥即对称密钥)
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//110b1a9b-87cd-45c3-a21d-824623715b33.jpg)
|
||||
|
||||
|
||||
|
||||
## 认证
|
||||
|
||||
|
||||
|
||||
通过使用 **证书** 来对通信方进行认证。证书中有公开密钥数据,如果可以验证公开密钥的确属于通信方的,那么就可以确定通信方是可靠的。
|
||||
|
||||
|
||||
|
||||
数字证书认证机构(CA,Certificate Authority)可以对其颁发的公开密钥证书对其进行验证。
|
||||
|
||||
|
||||
|
||||
进行 HTTPs 通信时,服务器会把证书发送给客户端,客户端取得其中的公开密钥之后,就可以开始通信。
|
||||
|
||||
|
||||
|
||||
使用 OpenSSL 这套开源程序,每个人都可以构建一套属于自己的认证机构,从而自己给自己颁发服务器证书。浏览器在访问该服务器时,会显示“无法确认连接安全性”或“该网站的安全证书存在问题”等警告消息。
|
||||
|
||||
|
||||
|
||||
客户端证书需要用户自行安装,只有在业务需要非常高的安全性时才使用客户端证书,例如网上银行。
|
||||
|
||||
|
||||
|
||||
## 完整性
|
||||
|
||||
|
||||
|
||||
SSL 提供摘要功能来验证完整性。
|
||||
|
||||
|
||||
|
||||
# HTTP/1.0 与 HTTP/1.1 的区别
|
||||
|
||||
|
||||
|
||||
HTTP/1.1 新增了以下内容:
|
||||
|
||||
|
||||
|
||||
- 默认为长连接;
|
||||
|
||||
- 提供了范围请求功能;
|
||||
|
||||
- 提供了虚拟主机的功能;
|
||||
|
||||
- 多了一些缓存处理字段;
|
||||
|
||||
- 多了一些状态码;
|
||||
|
||||
|
||||
|
611
notes/JVM.md
611
notes/JVM.md
File diff suppressed because it is too large
Load Diff
444
notes/Java 基础.md
444
notes/Java 基础.md
@ -29,658 +29,331 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 关键字
|
||||
|
||||
|
||||
|
||||
## 1. final
|
||||
|
||||
|
||||
|
||||
**数据**
|
||||
|
||||
|
||||
|
||||
声明数据为常量,可以是编译时常量,也可以是在运行时被初始化后不能被改变的常量。
|
||||
|
||||
|
||||
|
||||
对于基本类型,final 使数值不变;对于引用对象,final 使引用不变,也就不能引用其它对象,但是被引用的对象本身是可以修改的。
|
||||
|
||||
|
||||
|
||||
**方法**
|
||||
|
||||
|
||||
|
||||
声明方法不能被子类覆盖。
|
||||
|
||||
|
||||
|
||||
private 方法隐式地被指定为 final,如果在子类中定义的方法和基类中的一个 private 方法签名相同,此时子类的方法不是覆盖基类方法,而是重载了。
|
||||
|
||||
|
||||
|
||||
**类**
|
||||
|
||||
|
||||
|
||||
声明类不允许被继承。
|
||||
|
||||
|
||||
|
||||
## 2. static
|
||||
|
||||
|
||||
|
||||
**变量**
|
||||
|
||||
|
||||
|
||||
静态变量在内存中只存在一份,只在类第一次实例化时初始化一次,同时类所有的实例都共享静态变量,可以直接通过类名来访问它。
|
||||
|
||||
|
||||
|
||||
但是实例变量则不同,它是伴随着实例的,每创建一个实例就会产生一个实例变量,它与该实例同生共死。
|
||||
|
||||
|
||||
|
||||
**方法**
|
||||
|
||||
|
||||
|
||||
静态方法在类加载的时候就存在了,它不依赖于任何实例,所以 static 方法必须实现,也就是说他不能是抽象方法 abstract。
|
||||
|
||||
|
||||
|
||||
**静态语句块**
|
||||
|
||||
|
||||
|
||||
静态语句块和静态变量一样在类第一次实例化时运行一次。
|
||||
|
||||
|
||||
|
||||
**初始化顺序**
|
||||
|
||||
|
||||
|
||||
静态数据优先于其它数据的初始化,静态变量和静态语句块哪个先运行取决于它们在代码中的顺序。
|
||||
|
||||
|
||||
|
||||
```java
|
||||
|
||||
public static String staticField = " 静态变量 ";
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
```java
|
||||
|
||||
static {
|
||||
|
||||
System.out.println(" 静态初始化块 ");
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
实例变量和普通语句块的初始化在静态变量和静态语句块初始化结束之后。
|
||||
|
||||
|
||||
|
||||
```java
|
||||
|
||||
public String field = " 变量 ";
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
```java
|
||||
|
||||
{
|
||||
|
||||
System.out.println(" 初始化块 ");
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
最后才是构造函数中的数据进行初始化
|
||||
|
||||
|
||||
|
||||
```java
|
||||
|
||||
public InitialOrderTest() {
|
||||
|
||||
System.out.println(" 构造器 ");
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
存在继承的情况下,初始化顺序为:
|
||||
|
||||
|
||||
|
||||
1. 父类(静态变量、静态初始化块)
|
||||
|
||||
2. 子类(静态变量、静态初始化块)
|
||||
|
||||
3. 父类(变量、初始化块)
|
||||
|
||||
4. 父类(构造器)
|
||||
|
||||
5. 子类(变量、初始化块)
|
||||
|
||||
6. 子类(构造器)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Object 通用方法
|
||||
|
||||
|
||||
|
||||
## 1. 概览
|
||||
|
||||
|
||||
|
||||
- public final native Class<?> getClass()
|
||||
|
||||
- public native int hashCode()
|
||||
|
||||
- public boolean equals(Object obj)
|
||||
|
||||
- protected native Object clone() throws CloneNotSupportedException
|
||||
|
||||
- public String toString()
|
||||
|
||||
- public final native void notify()
|
||||
|
||||
- public final native void notifyAll()
|
||||
|
||||
- public final native void wait(long timeout) throws InterruptedException
|
||||
|
||||
- public final void wait(long timeout, int nanos) throws InterruptedException
|
||||
|
||||
- public final void wait() throws InterruptedException
|
||||
|
||||
- protected void finalize() throws Throwable { }
|
||||
|
||||
|
||||
|
||||
## 2. clone()
|
||||
|
||||
|
||||
|
||||
**浅拷贝**
|
||||
|
||||
|
||||
|
||||
引用类型引用的是同一个对象,clone() 方法默认就是浅拷贝实现。
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//d990c0e7-64d1-4ba3-8356-111bc91e53c5.png)
|
||||
|
||||
|
||||
|
||||
**深拷贝**
|
||||
|
||||
|
||||
|
||||
可以使用序列化实现。
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//2e5620c4-b558-46fe-8f12-00c9dd597a61.png)
|
||||
|
||||
|
||||
|
||||
> [How do I copy an object in Java?](https://stackoverflow.com/questions/869033/how-do-i-copy-an-object-in-java)
|
||||
|
||||
|
||||
|
||||
## 3. equals()
|
||||
|
||||
|
||||
|
||||
- 对于基本类型,== 就是判断两个值是否相等;
|
||||
|
||||
- 对于引用类型,== 是判断两个引用是否引用同一个对象,而 equals() 是判断引用的对象是否等价。
|
||||
|
||||
|
||||
|
||||
等价性:[ 散列 ](https://github.com/CyC2018/InterviewNotes/blob/master/notes/Java%20%E5%AE%B9%E5%99%A8.md#%E6%95%A3%E5%88%97)
|
||||
|
||||
|
||||
|
||||
# 继承
|
||||
|
||||
|
||||
|
||||
## 1. 访问权限
|
||||
|
||||
|
||||
|
||||
Java 中有三个访问权限修饰符:private、protected 以及 public,如果不加访问修饰符,表示包级可见。
|
||||
|
||||
|
||||
|
||||
可以对类或类中的成员(字段以及方法)加上访问修饰符。成员可见表示其它类可以用成员所在类的对象访问到该成员;类可见表示其它类可以用这个类创建对象,可以把类当做包中的一个成员,然后包表示一个类,这样就好理解了。
|
||||
|
||||
|
||||
|
||||
protected 用于修饰成员,表示在继承体系中成员对于子类可见。但是这个访问修饰符对于类没有意义,因为包没有继承体系。
|
||||
|
||||
|
||||
|
||||
更详细的内容:[ 浅析 Java 中的访问权限控制 ](http://www.importnew.com/18097.html)
|
||||
|
||||
|
||||
|
||||
## 2. 抽象类与接口的区别
|
||||
|
||||
|
||||
|
||||
抽象类至少包含一个抽象方法,该抽象方法必须在子类中实现。由于抽象类没有抽象方法的具体实现,因此不能对抽象类进行实例化。
|
||||
|
||||
|
||||
|
||||
```java
|
||||
|
||||
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
|
||||
|
||||
// abstract method
|
||||
|
||||
abstract void service(ServletRequest req, ServletResponse res);
|
||||
|
||||
|
||||
|
||||
|
||||
void init() {
|
||||
|
||||
// Its implementation
|
||||
|
||||
}
|
||||
|
||||
// other method related to Servlet
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
接口定义了一组方法,但是接口都没有方法的实现,也就是说这些方法都是抽象方法。
|
||||
|
||||
|
||||
|
||||
```java
|
||||
|
||||
public interface Externalizable extends Serializable {
|
||||
|
||||
|
||||
|
||||
void writeExternal(ObjectOutput out) throws IOException;
|
||||
|
||||
|
||||
|
||||
void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
更详细的内容:[Java 抽象类与接口的区别 ](http://www.importnew.com/12399.html)
|
||||
|
||||
|
||||
|
||||
## 3. super()
|
||||
|
||||
|
||||
|
||||
用来访问父类的构造函数父类的方法,第二种情况中,子类需要重载父类的方法。
|
||||
|
||||
|
||||
|
||||
```java
|
||||
|
||||
public class Subclass extends Superclass {
|
||||
|
||||
// overrides printMethod in Superclass
|
||||
|
||||
public void printMethod() {
|
||||
|
||||
super.printMethod();
|
||||
|
||||
System.out.println("Printed in Subclass");
|
||||
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
Subclass s = new Subclass();
|
||||
|
||||
s.printMethod();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
> [Using the Keyword super](https://docs.oracle.com/javase/tutorial/java/IandI/super.html)
|
||||
|
||||
|
||||
|
||||
# String
|
||||
|
||||
|
||||
|
||||
## 1. String, StringBuffer and StringBuilder
|
||||
|
||||
|
||||
|
||||
**是否可变**
|
||||
|
||||
|
||||
|
||||
String 不可变,StringBuffer 和 StringBuilder 可变。
|
||||
|
||||
|
||||
|
||||
**是否线程安全**
|
||||
|
||||
|
||||
|
||||
String 不可变,因此是线程安全的。
|
||||
|
||||
|
||||
|
||||
StringBuilder 不是线程安全的;StringBuffer 是线程安全的,使用 synchronized 来同步。
|
||||
|
||||
|
||||
|
||||
> [String, StringBuffer, and StringBuilder](https://stackoverflow.com/questions/2971315/string-stringbuffer-and-stringbuilder)
|
||||
|
||||
|
||||
|
||||
## 2. String 不可变的原因
|
||||
|
||||
|
||||
|
||||
**可以缓存 hash 值**
|
||||
|
||||
|
||||
|
||||
因为 String 的 hash 值经常被使用,例如 String 用做 HashMap 等。不可变的特性可以使得 hash 值也不可变,因此就只需要进行一次计算。
|
||||
|
||||
|
||||
|
||||
**String Pool 的需要**
|
||||
|
||||
|
||||
|
||||
如果 String 已经被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,才可能使用 String Pool。
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//f76067a5-7d5f-4135-9549-8199c77d8f1c.jpg)
|
||||
|
||||
|
||||
|
||||
**安全性**
|
||||
|
||||
|
||||
|
||||
String 经常作为参数,例如网络连接参数等,在作为网络连接参数的情况下,如果 String 是可变的,那么在网络连接过程中,String 被改变,改变 String 对象的那一方以为现在连接的是其它主机,而实际情况却不一定是。String 不可变性可以保证参数不可变。
|
||||
|
||||
|
||||
|
||||
**线程安全**
|
||||
|
||||
|
||||
|
||||
String 不可变性天生具备线程安全,可以在多个线程中使用。
|
||||
|
||||
|
||||
|
||||
> [Why String is immutable in Java?](https://www.programcreek.com/2013/04/why-string-is-immutable-in-java/)
|
||||
|
||||
|
||||
|
||||
## 3. String.intern()
|
||||
|
||||
|
||||
|
||||
使用 String.intern() 可以保证所有相同内容的字符串变量引用相同的内存对象。
|
||||
|
||||
|
||||
|
||||
更详细的内容:[ 揭开 String.intern() 那神秘的面纱 ](https://www.jianshu.com/p/95f516cb75ef)
|
||||
|
||||
|
||||
|
||||
# 基本类型与运算
|
||||
|
||||
|
||||
|
||||
## 1. 包装类型
|
||||
|
||||
|
||||
|
||||
八个基本类型:boolean 1 byte 8 char 16 short 16 int 32 float 32 long 64 double 64
|
||||
|
||||
|
||||
|
||||
基本类型都有对应的包装类型,它们之间的赋值使用自动装箱与拆箱完成。
|
||||
|
||||
|
||||
|
||||
```java
|
||||
|
||||
Integer x = 2; // 装箱
|
||||
|
||||
int y = x; // 拆箱
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
new Integer(123) 与 Integer.valueOf(123) 的区别在于,Integer.valueOf(123) 可能会使用缓存对象,因此多次使用 Integer.valueOf(123) 会取得同一个对象的引用。
|
||||
|
||||
|
||||
|
||||
```java
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
|
||||
|
||||
Integer a = new Integer(1);
|
||||
|
||||
Integer b = new Integer(1);
|
||||
|
||||
|
||||
|
||||
System.out.println("a==b? " + (a==b));
|
||||
|
||||
|
||||
|
||||
Integer c = Integer.valueOf(1);
|
||||
|
||||
Integer d = Integer.valueOf(1);
|
||||
|
||||
|
||||
|
||||
System.out.println("c==d? " + (c==d));
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
```html
|
||||
|
||||
a==b? false
|
||||
|
||||
c==d? true
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
valueOf() 方法的实现比较简单,就是先判断值是否在缓存池中,如果在的话就直接使用缓存池的内容。
|
||||
|
||||
|
||||
|
||||
```java
|
||||
|
||||
public static Integer valueOf(int i) {
|
||||
|
||||
final int offset = 128;
|
||||
|
||||
if (i >= -128 && i <= 127) { // must cache
|
||||
|
||||
return IntegerCache.cache[i + offset];
|
||||
|
||||
}
|
||||
|
||||
return new Integer(i);
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
The following is the list of primitives stored as immutable objects:
|
||||
|
||||
|
||||
|
||||
- boolean values true and false
|
||||
|
||||
- all byte values
|
||||
|
||||
- short values between -128 and 127
|
||||
|
||||
- int values between -128 and 127
|
||||
|
||||
- char in the range \u0000 to \u007F
|
||||
|
||||
|
||||
|
||||
自动装箱过程编译器会调用 valueOf() 方法,因此多个 Integer 对象使用装箱来创建并且值相同,那么就会引用相同的对象,这样做很显然是为了节省内存开销。
|
||||
|
||||
|
||||
|
||||
```java
|
||||
|
||||
Integer x = 1;
|
||||
|
||||
Integer y = 1;
|
||||
|
||||
System.out.println(c==d); // true
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
> [Differences between new Integer(123), Integer.valueOf(123) and just 123
|
||||
|
||||
](https://stackoverflow.com/questions/9030817/differences-between-new-integer123-integer-valueof123-and-just-123)
|
||||
|
||||
|
||||
|
||||
## 2. switch
|
||||
|
||||
|
||||
|
||||
A switch works with the byte, short, char, and int primitive data types. It also works with enumerated types (discussed in Classes and Inheritance) and a few special classes that "wrap" certain primitive types: Character, Byte, Short, and Integer (discussed in Simple Data Objects).
|
||||
|
||||
|
||||
|
||||
In the JDK 7 release, you can use a String object in the expression of a switch statement.
|
||||
|
||||
|
||||
|
||||
switch 不支持 long,是因为 swicth 的设计初衷是为那些只需要对少数几个值进行等值判断,如果值过于复杂,那么还是用 if 比较合适。
|
||||
|
||||
|
||||
|
||||
> [Why can't your switch statement data type be long, Java?](https://stackoverflow.com/questions/2676210/why-cant-your-switch-statement-data-type-be-long-java)
|
||||
|
||||
|
||||
|
||||
switch 使用查找表的方式来实现,JVM 中使用的指令是 lookupswitch。
|
||||
|
||||
|
||||
|
||||
```java
|
||||
|
||||
public static void main(String... args) {
|
||||
|
||||
switch (1) {
|
||||
|
||||
case 1:
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static void main(java.lang.String[]);
|
||||
|
||||
Code:
|
||||
|
||||
Stack=1, Locals=1, Args_size=1
|
||||
|
||||
0: iconst_1
|
||||
|
||||
1: lookupswitch{ //2
|
||||
|
||||
1: 28;
|
||||
|
||||
2: 31;
|
||||
|
||||
default: 31 }
|
||||
|
||||
28: goto 31
|
||||
|
||||
31: return
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
> [How does Java's switch work under the hood?](https://stackoverflow.com/questions/12020048/how-does-javas-switch-work-under-the-hood)
|
||||
|
||||
|
||||
@ -688,221 +361,108 @@ public static void main(java.lang.String[]);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 反射
|
||||
|
||||
|
||||
|
||||
每个类都有一个 **Class** 对象,包含了与类有关的信息。当编译一个新类时,会产生一个同名的 .class 文件,该文件内容保存着 Class 对象。
|
||||
|
||||
|
||||
|
||||
类加载相当于 Class 对象的加载。类在第一次使用时才动态加载到 JVM 中,可以使用 Class.forName('com.mysql.jdbc.Driver.class') 这种方式来控制类的加载,该方法会返回一个 Class 对象。
|
||||
|
||||
|
||||
|
||||
反射可以提供运行时的类信息,并且这个类可以在运行时才加载进来,甚至在编译时期该类的 .class 不存在也可以加载进来。
|
||||
|
||||
|
||||
|
||||
Class 和 java.lang.reflect 一起对反射提供了支持,java.lang.reflect 类库包含了 **Field**、**Method** 以及 **Constructor** 类。可以使用 get() 和 set() 方法读取和修改 Field 对象关联的字段,可以使用 invoke() 方法调用与 Method 对象关联的方法,可以用 Constructor 创建新的对象。
|
||||
|
||||
|
||||
|
||||
IDE 使用反射机制获取类的信息,在使用一个类的对象时,能够把类的字段、方法和构造函数等信息列出来供用户选择。
|
||||
|
||||
|
||||
|
||||
更详细的内容:[ 深入解析 Java 反射(1)- 基础 ](http://www.sczyh30.com/posts/Java/java-reflection-1/)
|
||||
|
||||
|
||||
|
||||
# 异常
|
||||
|
||||
|
||||
|
||||
Throwable 可以用来表示任何可以作为异常抛出的类,分为两种:**Error** 和 **Exception**,其中 Error 用来表示编译时系统错误。
|
||||
|
||||
|
||||
|
||||
Exception 分为两种:**受检异常** 和 **非受检异常**。受检异常需要用 try...catch... 语句捕获并进行处理,并且可以从异常中恢复;非受检异常是程序运行时错误,例如除 0 会引发 Arithmetic Exception,此时程序奔溃并且无法恢复。
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//48f8f98e-8dfd-450d-8b5b-df4688f0d377.jpg)
|
||||
|
||||
|
||||
|
||||
更详细的内容:
|
||||
|
||||
- [Java 入门之异常处理 ](https://www.tianmaying.com/tutorial/Java-Exception)
|
||||
|
||||
- [Java 异常的面试问题及答案 -Part 1](http://www.importnew.com/7383.html)
|
||||
|
||||
|
||||
|
||||
# 泛型
|
||||
|
||||
|
||||
|
||||
泛型提供了编译时的类型检测机制,该机制允许程序员在编译时检测到非法的类型。泛型是 Java 中一个非常重要的知识点,在 Java 集合类框架中泛型被广泛应用。
|
||||
|
||||
|
||||
|
||||
```java
|
||||
|
||||
public class Box<T> {
|
||||
|
||||
// T stands for "Type"
|
||||
|
||||
private T t;
|
||||
|
||||
public void set(T t) { this.t = t; }
|
||||
|
||||
public T get() { return t; }
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
更详细的内容:
|
||||
|
||||
|
||||
|
||||
- [Java 泛型详解 ](https://www.ziwenxie.site/2017/03/01/java-generic/)
|
||||
|
||||
- [10 道 Java 泛型面试题 ](https://cloud.tencent.com/developer/article/1033693)
|
||||
|
||||
|
||||
|
||||
# 特性
|
||||
|
||||
|
||||
|
||||
## 1. 三大特性
|
||||
|
||||
|
||||
|
||||
[ 封装、继承、多态 ](https://github.com/CyC2018/InterviewNotes/blob/master/notes/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E6%80%9D%E6%83%B3.md#%E5%B0%81%E8%A3%85%E7%BB%A7%E6%89%BF%E5%A4%9A%E6%80%81)
|
||||
|
||||
|
||||
|
||||
## 2. Java 各版本的新特性
|
||||
|
||||
|
||||
|
||||
New highlights in Java SE 8
|
||||
|
||||
|
||||
|
||||
1. Lambda Expressions
|
||||
|
||||
2. Pipelines and Streams
|
||||
|
||||
3. Date and Time API
|
||||
|
||||
4. Default Methods
|
||||
|
||||
5. Type Annotations
|
||||
|
||||
6. Nashhorn JavaScript Engine
|
||||
|
||||
7. Concurrent Accumulators
|
||||
|
||||
8. Parallel operations
|
||||
|
||||
9. PermGen Error Removed
|
||||
|
||||
|
||||
|
||||
New highlights in Java SE 7
|
||||
|
||||
|
||||
|
||||
1. Strings in Switch Statement
|
||||
|
||||
2. Type Inference for Generic Instance Creation
|
||||
|
||||
3. Multiple Exception Handling
|
||||
|
||||
4. Support for Dynamic Languages
|
||||
|
||||
5. Try with Resources
|
||||
|
||||
6. Java nio Package
|
||||
|
||||
7. Binary Literals, Underscore in literals
|
||||
|
||||
8. Diamond Syntax
|
||||
|
||||
|
||||
|
||||
> [Difference between Java 1.8 and Java 1.7?](http://www.selfgrowth.com/articles/difference-between-java-18-and-java-17)
|
||||
|
||||
|
||||
|
||||
更详细的内容:[Java 8 特性 ](http://www.importnew.com/19345.html)
|
||||
|
||||
|
||||
|
||||
## 3. Java 与 C++ 的区别
|
||||
|
||||
|
||||
|
||||
Java 是纯粹的面向对象语言,所有的对象都继承自 java.lang.Object,C++ 为了兼容 C 即支持面向对象也支持面向过程。
|
||||
|
||||
|
||||
|
||||
比较详细的内容:
|
||||
|
||||
|
||||
|
||||
| Java | C++ |
|
||||
|
||||
| -- | -- |
|
||||
|
||||
| Java does not support pointers, templates, unions, operator overloading, structures etc. The Java language promoters initially said "No pointers!", but when many programmers questioned how you can work without pointers, the promoters began saying "Restricted pointers." Java supports what it calls "references". References act a lot like pointers in C++ languages but you cannot perform arithmetic on pointers in Java. References have types, and they're type-safe. These references cannot be interpreted as raw address and unsafe conversion is not allowed. | C++ supports structures, unions, templates, operator overloading, pointers and pointer arithmetic.|
|
||||
|
||||
| Java support automatic garbage collection. It does not support destructors as C++ does. | C++ support destructors, which is automatically invoked when the object is destroyed. |
|
||||
|
||||
| Java does not support conditional compilation and inclusion. | Conditional inclusion (#ifdef #ifndef type) is one of the main features of C++. |
|
||||
|
||||
| Java has built in support for threads. In Java, there is a `Thread` class that you inherit to create a new thread and override the `run()` method. | C++ has no built in support for threads. C++ relies on non-standard third-party libraries for thread support. |
|
||||
|
||||
| Java does not support default arguments. There is no scope resolution operator (::) in Java. The method definitions must always occur within a class, so there is no need for scope resolution there either. | C++ supports default arguments. C++ has scope resolution operator (::) which is used to to define a method outside a class and to access a global variable within from the scope where a local variable also exists with the same name. |
|
||||
|
||||
| There is no _goto_ statement in Java. The keywords `const` and `goto` are reserved, even though they are not used. | C++ has _goto_ statement. However, it is not considered good practice to use of _goto_ statement. |
|
||||
|
||||
| Java doesn't provide multiple inheritance, at least not in the same sense that C++ does. | C++ does support multiple inheritance. The keyword `virtual` is used to resolve ambiguities during multiple inheritance if there is any. |
|
||||
|
||||
| Exception handling in Java is different because there are no destructors. Also, in Java, try/catch must be defined if the function declares that it may throw an exception. | While in C++, you may not include the try/catch even if the function throws an exception. |
|
||||
|
||||
| Java has method overloading, but no operator overloading. The `String` class does use the `+` and `+=` operators to concatenate strings and `String`expressions use automatic type conversion, but that's a special built-in case. | C++ supports both method overloading and operator overloading. |
|
||||
|
||||
| Java has built-in support for documentation comments (`/** ... */`); therefore, Java source files can contain their own documentation, which is read by a separate tool usually `javadoc` and reformatted into HTML. This helps keeping documentation maintained in easy way. | C++ does not support documentation comments. |
|
||||
|
||||
| Java is interpreted for the most part and hence platform independent. | C++ generates object code and the same code may not run on different platforms. |
|
||||
|
||||
|
||||
|
||||
> [What are the main differences between Java and C++?](http://cs-fundamentals.com/tech-interview/java/differences-between-java-and-cpp.php)
|
||||
|
||||
|
||||
|
||||
## 4. JRE or JDK
|
||||
|
||||
|
||||
|
||||
- JRE is the JVM program, Java application need to run on JRE.
|
||||
|
||||
- JDK is a superset of JRE, JRE + tools for developing java programs. e.g, it provides the compiler "javac"
|
||||
|
||||
|
||||
|
5134
notes/Leetcode 题解.md
5134
notes/Leetcode 题解.md
File diff suppressed because it is too large
Load Diff
1049
notes/Linux.md
1049
notes/Linux.md
File diff suppressed because it is too large
Load Diff
384
notes/MySQL.md
384
notes/MySQL.md
@ -40,765 +40,383 @@
|
||||
<!-- GFM-TOC -->
|
||||
|
||||
|
||||
|
||||
# 存储引擎
|
||||
|
||||
|
||||
|
||||
## 1. InnoDB
|
||||
|
||||
|
||||
|
||||
InnoDB 是 MySQL 的默认事务型引擎,只有在需要 InnoDB 不支持的特性时,才考虑使用其它存储引擎。
|
||||
|
||||
|
||||
|
||||
采用 MVCC 来支持高并发,并且实现了四个标准的隔离级别,默认级别是可重复读。
|
||||
|
||||
|
||||
|
||||
表是基于聚簇索引建立的,它对主键的查询性能有很高的提升。
|
||||
|
||||
|
||||
|
||||
内部做了很多优化,包括从磁盘读取数据时采用的可预测性读,能够自动在内存中创建 hash 索引以加速读操作的自适应哈希索引,以及能够加速插入操作的插入缓冲区等。
|
||||
|
||||
|
||||
|
||||
通过一些机制和工具支持真正的热备份。
|
||||
|
||||
|
||||
|
||||
## 2. MyISAM
|
||||
|
||||
|
||||
|
||||
MyISAM 提供了大量的特性,包括全文索引、压缩、空间函数(GIS)等。但 MyISAM 不支持事务和行级锁,而且奔溃后无法安全恢复。
|
||||
|
||||
|
||||
|
||||
只能对整张表加锁,而不是针对行。
|
||||
|
||||
|
||||
|
||||
可以手工或者自动执行检查和修复操作,但是和事务恢复以及奔溃恢复不同,可能导致一些数据丢失,而且修复操作是非常慢的。
|
||||
|
||||
|
||||
|
||||
可以包含动态或者静态的行。
|
||||
|
||||
|
||||
|
||||
如果指定了 DELAY_KEY_WRITE 选项,在每次修改执行完成时,不会立即将修改的索引数据写入磁盘,而是会写到内存中的键缓冲区,只有在清理键缓冲区或者关闭表的时候才会将对应的索引块写入磁盘。这种方式可以极大的提升写入性能,但是在数据库或者主机奔溃时会造成索引损坏,需要执行修复操作。
|
||||
|
||||
|
||||
|
||||
如果表在创建并导入数据以后,不会再进行修改操作,那么这样的表适合采用 MyISAM 压缩表。
|
||||
|
||||
|
||||
|
||||
对于只读数据,或者表比较小、可以容忍修复操作,则依然可以继续使用 MyISAM。
|
||||
|
||||
|
||||
|
||||
MyISAM 设计简单,数据以紧密格式存储,所以在某些场景下性能很好。
|
||||
|
||||
|
||||
|
||||
## 3. InnoDB 与 MyISAM 的比较
|
||||
|
||||
|
||||
|
||||
**事务**
|
||||
|
||||
|
||||
|
||||
InnoDB 是事务型的。
|
||||
|
||||
|
||||
|
||||
**备份**
|
||||
|
||||
|
||||
|
||||
InnoDB 支持在线热备份。
|
||||
|
||||
|
||||
|
||||
**奔溃恢复**
|
||||
|
||||
|
||||
|
||||
MyISAM 奔溃后发生损坏的概率比 InnoDB 高很多,而且恢复的速度也更慢。
|
||||
|
||||
|
||||
|
||||
**并发**
|
||||
|
||||
|
||||
|
||||
MyISAM 只支持表级锁,而 InnoDB 还支持行级锁。
|
||||
|
||||
|
||||
|
||||
**其它特性**
|
||||
|
||||
|
||||
|
||||
MyISAM 支持全文索引,地理空间索引;
|
||||
|
||||
|
||||
|
||||
# 数据类型
|
||||
|
||||
|
||||
|
||||
## 1. 整型
|
||||
|
||||
|
||||
|
||||
TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT 分别使用 8, 16, 24, 32,, 64 位存储空间,一般情况下越小的列越好。
|
||||
|
||||
|
||||
|
||||
INT(11) 中的数字只是规定了交互工具显示字符的个数,对于存储和计算来说是没有意义的。
|
||||
|
||||
|
||||
|
||||
## 2. 浮点数
|
||||
|
||||
|
||||
|
||||
FLOAT 和 DOUBLE 为浮点类型,DECIMAL 为高精度小数类型。CPU 原生支持浮点运算,但是不支持 DECIMAl 类型的计算,因此 DECIMAL 的计算比浮点类型需要更高的代价。
|
||||
|
||||
|
||||
|
||||
FLOAT、DOUBLE 和 DECIMAL 都可以指定列宽,例如 DECIMAL(18, 9) 表示总共 18 位,取 9 位存储小数部分,剩下 9 位存储整数部分。
|
||||
|
||||
|
||||
|
||||
## 3. 字符串
|
||||
|
||||
|
||||
|
||||
主要有 CHAR 和 VARCHAR 两种类型,一种是定长的,一种是变长的。
|
||||
|
||||
|
||||
|
||||
VARCHAR 这种变长类型能够节省空间,因为只需要存储必要的内容。但是在执行 UPDATE 时可能会使行变得比原来长,当超出一个页所能容纳的大小时,就要执行额外的操作,MyISAM 会将行拆成不同的片段存储,而 InnoDB 则需要分裂页来使行放进页内。
|
||||
|
||||
|
||||
|
||||
VARCHAR 会保留字符串末尾的空格,而 CHAR 会删除。
|
||||
|
||||
|
||||
|
||||
## 4. 时间和日期
|
||||
|
||||
|
||||
|
||||
MySQL 提供了两种相似的日期时间类型:DATATIME 和 TIMESTAMP。
|
||||
|
||||
|
||||
|
||||
**DATATIME**
|
||||
|
||||
|
||||
|
||||
能够保存从 1001 年到 9999 年的日期和时间,精度为秒,使用 8 字节的存储空间。
|
||||
|
||||
|
||||
|
||||
它与时区无关。
|
||||
|
||||
|
||||
|
||||
默认情况下,MySQL 以一种可排序的、无歧义的格式显示 DATATIME 值,例如“2008-01016 22:37:08”,这是 ANSI 标准定义的日期和时间表示方法。
|
||||
|
||||
|
||||
|
||||
**TIMESTAMP**
|
||||
|
||||
|
||||
|
||||
和 UNIX 时间戳相同,保存从 1970 年 1 月 1 日午夜(格林威治时间)以来的秒数,使用 4 个字节,只能表示从 1970 年 到 2038 年。
|
||||
|
||||
|
||||
|
||||
它和时区有关。
|
||||
|
||||
|
||||
|
||||
MySQL 提供了 FROM_UNIXTIME() 函数把 Unxi 时间戳转换为日期,并提供了 UNIX_TIMESTAMP() 函数把日期转换为 Unix 时间戳。
|
||||
|
||||
|
||||
|
||||
默认情况下,如果插入时没有指定 TIMESTAMP 列的值,会将这个值设置为当前时间。
|
||||
|
||||
|
||||
|
||||
应该尽量使用 TIMESTAMP,因为它比 DATETIME 空间效率更高。
|
||||
|
||||
|
||||
|
||||
# 索引
|
||||
|
||||
|
||||
|
||||
索引是在存储引擎层实现的,而不是在服务器层实现的,所以不同存储引擎具有不同的索引类型和实现。
|
||||
|
||||
|
||||
|
||||
索引能够轻易将查询性能提升几个数量级。
|
||||
|
||||
|
||||
|
||||
对于非常小的表、大部分情况下简单的全表扫描比建立索引更高效。对于中到大型的表,索引就非常有效。但是对于特大型的表,建立和使用索引的代价将会随之增长。这种情况下,需要用到一种技术可以直接区分出需要查询的一组数据,而不是一条记录一条记录地匹配,例如可以使用分区技术。
|
||||
|
||||
|
||||
|
||||
## 1. 索引分类
|
||||
|
||||
|
||||
|
||||
### 1.1 B-Tree 索引
|
||||
|
||||
|
||||
|
||||
B-Tree 索引是大多数 MySQL 存储引擎的默认索引类型。
|
||||
|
||||
|
||||
|
||||
因为不再需要进行全表扫描,只需要对树进行搜索即可,因此查找速度快很多。
|
||||
|
||||
|
||||
|
||||
可以指定多个列作为索引列,多个索引列共同组成键。B-Tree 索引适用于全键值、键值范围和键前缀查找,其中键前缀查找只适用于最左前缀查找。
|
||||
|
||||
|
||||
|
||||
除了用于查找,还可以用于排序和分组。
|
||||
|
||||
|
||||
|
||||
如果不是按照索引列的顺序进行查找,则无法使用索引。
|
||||
|
||||
|
||||
|
||||
### 1.2 哈希索引
|
||||
|
||||
|
||||
|
||||
基于哈希表实现,优点是查找非常快。
|
||||
|
||||
|
||||
|
||||
在 MySQL 中只有 Memory 引擎显式支持哈希索引。
|
||||
|
||||
|
||||
|
||||
InnoDB 引擎有一个特殊的功能叫“自适应哈希索引”,当某个索引值被使用的非常频繁时,会在 B-Tree 索引之上再创建一个哈希索引,这样就让 B-Tree 索引具有哈希索引的一些优点,比如快速的哈希查找。
|
||||
|
||||
|
||||
|
||||
限制:哈希索引只包含哈希值和行指针,而不存储字段值,所以不能使用索引中的值来避免读取行。不过,访问内存中的行的速度很快,所以大部分情况下这一点对性能影响并不明显;无法用于分组与排序;只支持精确查找,无法用于部分查找和范围查找;如果哈希冲突很多,查找速度会变得很慢。
|
||||
|
||||
|
||||
|
||||
### 1.3. 空间索引数据(R-Tree)
|
||||
|
||||
|
||||
|
||||
MyISAM 存储引擎支持空间索引,可以用于地理数据存储。
|
||||
|
||||
|
||||
|
||||
空间索引会从所有维度来索引数据,可以有效地使用任意维度来进行组合查询。
|
||||
|
||||
|
||||
|
||||
### 1.4 全文索引
|
||||
|
||||
|
||||
|
||||
MyISAM 存储引擎支持全文索引,用于查找文本中的关键词,而不是直接比较索引中的值。
|
||||
|
||||
|
||||
|
||||
使用 MATCH AGAINST,而不是普通的 WHERE。
|
||||
|
||||
|
||||
|
||||
## 2. 索引的优点
|
||||
|
||||
|
||||
|
||||
- 大大减少了服务器需要扫描的数据量;
|
||||
|
||||
|
||||
|
||||
- 帮助服务器避免进行排序和创建临时表;
|
||||
|
||||
|
||||
|
||||
- 将随机 I/O 变为顺序 I/O。
|
||||
|
||||
|
||||
|
||||
## 3. 索引优化
|
||||
|
||||
|
||||
|
||||
### 3.1 独立的列
|
||||
|
||||
|
||||
|
||||
在进行查询时,索引列不能是表达式的一部分,也不能是函数的参数,否则无法使用索引。
|
||||
|
||||
|
||||
|
||||
例如下面的查询不能使用 actor_id 列的索引:
|
||||
|
||||
|
||||
|
||||
```sql
|
||||
|
||||
SELECT actor_id FROM sakila.actor WHERE actor_id + 1 = 5;
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 3.2 前缀索引
|
||||
|
||||
|
||||
|
||||
对于 BLOB、TEXT 和 VARCHAR 类型的列,必须使用前缀索引,只索引开始的部分字符。
|
||||
|
||||
|
||||
|
||||
对于前缀长度的选取需要根据 **索引选择性** 来确定:不重复的索引值和记录总数的比值。选择性越高,查询效率也越高。最大值为 1 ,此时每个记录都有唯一的索引与其对应。
|
||||
|
||||
|
||||
|
||||
### 3.3 多列索引
|
||||
|
||||
|
||||
|
||||
在需要使用多个列作为条件进行查询时,使用多列索引比使用多个单列索引性能更好。例如下面的语句中,最好把 actor_id 和 file_id 设置为多列索引。
|
||||
|
||||
|
||||
|
||||
```sql
|
||||
|
||||
SELECT file_id, actor_ id FROM sakila.film_actor
|
||||
|
||||
WhERE actor_id = 1 OR film_id = 1;
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 3.4 索引列的顺序
|
||||
|
||||
|
||||
|
||||
让选择性最强的索引列放在前面,例如下面显示的结果中 customer_id 的选择性比 staff_id 更高,因此最好把 customer_id 列放在多列索引的前面。
|
||||
|
||||
|
||||
|
||||
```sql
|
||||
|
||||
SELECT COUNT(DISTINCT staff_id)/COUNT(*) AS staff_id_selectivity,
|
||||
|
||||
COUNT(DISTINCT customer_id)/COUNT(*) AS customer_id_selectivity,
|
||||
|
||||
COUNT(*)
|
||||
|
||||
FROM payment;
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
```html
|
||||
|
||||
staff_id_selectivity: 0.0001
|
||||
|
||||
customer_id_selectivity: 0.0373
|
||||
|
||||
COUNT(*): 16049
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 3.5 聚簇索引
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//b9e9ae8c-e216-4c01-b267-a50dbeb98fa4.jpg)
|
||||
|
||||
|
||||
|
||||
聚簇索引并不是一种索引类型,而是一种数据存储方式。
|
||||
|
||||
|
||||
|
||||
术语“聚簇”表示数据行和相邻的键值紧密地存储在一起,InnoDB 的聚簇索引的数据行存放在 B-Tree 的叶子页中。
|
||||
|
||||
|
||||
|
||||
因为无法把数据行存放在两个不同的地方,所以一个表只能有一个聚簇索引。
|
||||
|
||||
|
||||
|
||||
**优点**
|
||||
|
||||
|
||||
|
||||
1. 可以把相关数据保存在一起,减少 I/O 操作;
|
||||
|
||||
2. 因为数据保存在 B-Tree 中,因此数据访问更快。
|
||||
|
||||
|
||||
|
||||
**缺点**
|
||||
|
||||
|
||||
|
||||
1. 聚簇索引最大限度提高了 I/O 密集型应用的性能,但是如果数据全部放在内存,就没必要用聚簇索引。
|
||||
|
||||
2. 插入速度严重依赖于插入顺序,按主键的顺序插入是最快的。
|
||||
|
||||
3. 更新操作代价很高,因为每个被更新的行都会移动到新的位置。
|
||||
|
||||
4. 当插入到某个已满的页中,存储引擎会将该页分裂成两个页面来容纳该行,页分裂会导致表占用更多的磁盘空间。
|
||||
|
||||
5. 如果行比较稀疏,或者由于页分裂导致数据存储不连续时,聚簇索引可能导致全表扫描速度变慢。
|
||||
|
||||
|
||||
|
||||
### 3.6 覆盖索引
|
||||
|
||||
|
||||
|
||||
索引包含所有需要查询的字段的值。
|
||||
|
||||
|
||||
|
||||
## 4. B-Tree 和 B+Tree 原理
|
||||
|
||||
|
||||
|
||||
### 4. 1 B-Tree
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//5ed71283-a070-4b21-85ae-f2cbfd6ba6e1.jpg)
|
||||
|
||||
|
||||
|
||||
为了描述 B-Tree,首先定义一条数据记录为一个二元组 [key, data],key 为记录的键,data 为数据记录除 key 外的数据。
|
||||
|
||||
|
||||
|
||||
B-Tree 是满足下列条件的数据结构:
|
||||
|
||||
|
||||
|
||||
- 所有叶节点具有相同的深度,也就是说 B-Tree 是平衡的;
|
||||
|
||||
- 一个节点中的 key 从左到右非递减排列;
|
||||
|
||||
- 如果某个指针的左右相邻 key 分别是 key<sub>i</sub> 和 key<sub>i+1</sub>,且不为 null,则该指针指向节点的所有 key 大于 key<sub>i</sub> 且小于 key<sub>i+1</sub>。
|
||||
|
||||
|
||||
|
||||
在 B-Tree 中按 key 检索数据的算法非常直观:首先从根节点进行二分查找,如果找到则返回对应节点的 data,否则对相应区间的指针指向的节点递归进行查找,直到找到节点或找到 null 指针,前者查找成功,后者查找失败。
|
||||
|
||||
|
||||
|
||||
由于插入删除新的数据记录会破坏 B-Tree 的性质,因此在插入删除时,需要对树进行一个分裂、合并、转移等操作以保持 B-Tree 性质。
|
||||
|
||||
|
||||
|
||||
### 4.2 B+Tree
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//63cd5b50-d6d8-4df6-8912-ef4a1dd5ba13.jpg)
|
||||
|
||||
|
||||
|
||||
与 B-Tree 相比,B+Tree 有以下不同点:
|
||||
|
||||
|
||||
|
||||
- 每个节点的指针上限为 2d 而不是 2d+1;
|
||||
|
||||
- 内节点不存储 data,只存储 key,叶子节点不存储指针。
|
||||
|
||||
|
||||
|
||||
### 4.3 带有顺序访问指针的 B+Tree
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//1ee5f0a5-b8df-43b9-95ab-c516c54ec797.jpg)
|
||||
|
||||
|
||||
|
||||
一般在数据库系统或文件系统中使用的 B+Tree 结构都在经典 B+Tree 基础上进行了优化,在叶子节点增加了顺序访问指针,做这个优化的目的是为了提高区间访问的性能。
|
||||
|
||||
|
||||
|
||||
### 4.4 为什么使用 B-Tree 和 B+Tree
|
||||
|
||||
|
||||
|
||||
红黑树等数据结构也可以用来实现索引,但是文件系统及数据库系统普遍采用 B-/+Tree 作为索引结构。
|
||||
|
||||
|
||||
|
||||
页是计算机管理存储器的逻辑块,硬件及操作系统往往将主存和磁盘存储区分割为连续的大小相等的块,每个存储块称为一页(在许多操作系统中,页得大小通常为 4k),主存和磁盘以页为单位交换数据。
|
||||
|
||||
|
||||
|
||||
一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。为了减少磁盘 I/O,磁盘往往不是严格按需读取,而是每次都会预读。这样做的理论依据是计算机科学中著名的局部性原理:当一个数据被用到时,其附近的数据也通常会马上被使用。数据库系统的设计者巧妙利用了磁盘预读原理,将一个节点的大小设为等于一个页,这样每个节点只需要一次 I/O 就可以完全载入。B-Tree 中一次检索最多需要 h-1 次 I/O(根节点常驻内存),渐进复杂度为 O(h)=O(logdN)。一般实际应用中,出度 d 是非常大的数字,通常超过 100,因此 h 非常小(通常不超过 3)。而红黑树这种结构,h 明显要深的多。并且于逻辑上很近的节点(父子)物理上可能很远,无法利用局部性,效率明显比 B-Tree 差很多。
|
||||
|
||||
|
||||
|
||||
B+Tree 更适合外存索引,原因和内节点出度 d 有关。由于 B+Tree 内节点去掉了 data 域,因此可以拥有更大的出度,拥有更好的性能。
|
||||
|
||||
|
||||
|
||||
# 查询性能优化
|
||||
|
||||
|
||||
|
||||
## 1. Explain
|
||||
|
||||
|
||||
|
||||
用来分析 SQL 语句,分析结果中比较重要的字段有:
|
||||
|
||||
|
||||
|
||||
- select_type : 查询类型,有简单查询、联合查询和子查询
|
||||
|
||||
|
||||
|
||||
- key : 使用的索引
|
||||
|
||||
|
||||
|
||||
- rows : 扫描的行数
|
||||
|
||||
|
||||
|
||||
## 2. 减少返回的列
|
||||
|
||||
|
||||
|
||||
慢查询主要是因为访问了过多数据,除了访问过多行之外,也包括访问过多列。
|
||||
|
||||
|
||||
|
||||
最好不要使用 SELECT * 语句,要根据需要选择查询的列。
|
||||
|
||||
|
||||
|
||||
## 3. 减少返回的行
|
||||
|
||||
|
||||
|
||||
最好使用 LIMIT 语句来取出想要的那些行。
|
||||
|
||||
|
||||
|
||||
还可以建立索引来减少条件语句的全表扫描。例如对于下面的语句,不适用索引的情况下需要进行全表扫描,而使用索引只需要扫描几行记录即可,使用 Explain 语句可以通过观察 rows 字段来看出这种差异。
|
||||
|
||||
|
||||
|
||||
```sql
|
||||
|
||||
SELECT * FROM sakila.film_actor WHERE film_id = 1;
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 4. 拆分大的 DELETE 或 INSERT 语句
|
||||
|
||||
|
||||
|
||||
如果一次性执行的话,可能一次锁住很多数据、占满整个事务日志、耗尽系统资源、阻塞很多小的但重要的查询。
|
||||
|
||||
|
||||
|
||||
```sql
|
||||
|
||||
DELEFT FROM messages WHERE create < DATE_SUB(NOW(), INTERVAL 3 MONTH);
|
||||
|
||||
```
|
||||
|
||||
```sql
|
||||
|
||||
rows_affected = 0
|
||||
|
||||
do {
|
||||
|
||||
rows_affected = do_query(
|
||||
|
||||
"DELETE FROM messages WHERE create < DATE_SUB(NOW(), INTERVAL 3 MONTH) LIMIT 10000")
|
||||
|
||||
} while rows_affected > 0
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
# 分库与分表
|
||||
|
||||
|
||||
|
||||
**1. 分表与分区的不同**
|
||||
|
||||
|
||||
|
||||
分表,就是讲一张表分成多个小表,这些小表拥有不同的表名;而分区是将一张表的数据分为多个区块,这些区块可以存储在同一个磁盘上,也可以存储在不同的磁盘上,这种方式下表仍然只有一个。
|
||||
|
||||
|
||||
|
||||
**2. 使用分库与分表的原因**
|
||||
|
||||
|
||||
|
||||
随着时间和业务的发展,数据库中的表会越来越多,并且表中的数据量也会越来越大,那么读写操作的开销也会随着增大。
|
||||
|
||||
|
||||
|
||||
**3. 垂直切分**
|
||||
|
||||
|
||||
|
||||
将表按功能模块、关系密切程度划分出来,部署到不同的库上。例如,我们会建立商品数据库 payDB、用户数据库 userDB 等,分别用来存储项目与商品有关的表和与用户有关的表。
|
||||
|
||||
|
||||
|
||||
**4. 水平切分**
|
||||
|
||||
|
||||
|
||||
把表中的数据按照某种规则存储到多个结构相同的表中,例如按 id 的散列值、性别等进行划分,
|
||||
|
||||
|
||||
|
||||
**5. 垂直切分与水平切分的选择**
|
||||
|
||||
|
||||
|
||||
如果数据库中的表太多,并且项目各项业务逻辑清晰,那么垂直切分是首选。
|
||||
|
||||
|
||||
|
||||
如果数据库的表不多,但是单表的数据量很大,应该选择水平切分。
|
||||
|
||||
|
||||
|
||||
**6. 水平切分的实现方式**
|
||||
|
||||
|
||||
|
||||
最简单的是使用 merge 存储引擎。
|
||||
|
||||
|
||||
|
||||
**7. 分库与分表存在的问题**
|
||||
|
||||
|
||||
|
||||
(1) 事务问题
|
||||
|
||||
|
||||
|
||||
在执行分库分表之后,由于数据存储到了不同的库上,数据库事务管理出现了困难。如果依赖数据库本身的分布式事务管理功能去执行事务,将付出高昂的性能代价;如果由应用程序去协助控制,形成程序逻辑上的事务,又会造成编程方面的负担。
|
||||
|
||||
|
||||
|
||||
(2) 跨库跨表连接问题
|
||||
|
||||
|
||||
|
||||
在执行了分库分表之后,难以避免会将原本逻辑关联性很强的数据划分到不同的表、不同的库上。这时,表的连接操作将受到限制,我们无法连接位于不同分库的表,也无法连接分表粒度不同的表,导致原本只需要一次查询就能够完成的业务需要进行多次才能完成。
|
||||
|
||||
|
||||
|
||||
# 故障转移和故障恢复
|
||||
|
||||
|
||||
|
||||
故障转移也叫做切换,当主库出现故障时就切换到备库,使备库成为主库。故障恢复顾名思义就是从故障中恢复过来,并且保证数据的正确性。
|
||||
|
||||
|
||||
|
||||
## 1. 故障转移
|
||||
|
||||
|
||||
|
||||
**1.1 提升备库或切换角色**
|
||||
|
||||
|
||||
|
||||
提升一台备库为主库,或者在一个主 - 主复制结构中调整主动和被动角色。
|
||||
|
||||
|
||||
提升一台备库为主库,或者在一个主-主复制结构中调整主动和被动角色。
|
||||
|
||||
**1.2 虚拟 IP 地址和 IP 托管**
|
||||
|
||||
|
||||
|
||||
为 MySQL 实例指定一个逻辑 IP 地址,当 MySQL 实例失效时,可以将 IP 地址转移到另一台 MySQL 服务器上。
|
||||
|
||||
|
||||
|
||||
**1.3 中间件解决方案**
|
||||
|
||||
|
||||
|
||||
通过代理,可以路由流量到可以使用的服务器上。
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//fabd5fa0-b75e-48d0-9e2c-31471945ceb9.jpg)
|
||||
|
||||
|
||||
|
||||
**1.4 在应用中处理故障转移**
|
||||
|
||||
|
||||
|
||||
将故障转移整合到应用中可能导致应用变得太过笨拙。
|
||||
|
||||
|
||||
|
||||
## 2. 故障恢复
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 参考资料
|
||||
|
||||
|
||||
|
||||
- 高性能 MySQL
|
||||
|
||||
- [MySQL 索引背后的数据结构及算法原理 ](http://blog.codinglabs.org/articles/theory-of-mysql-index.html)
|
||||
|
||||
- [MySQL 索引优化全攻略 ](http://www.runoob.com/w3cnote/mysql-index.html)
|
||||
|
||||
- [20+ 条 MySQL 性能优化的最佳经验 ](https://www.jfox.info/20-tiao-mysql-xing-nen-you-hua-de-zui-jia-jing-yan.html)
|
||||
|
||||
|
||||
|
765
notes/SQL 语法.md
765
notes/SQL 语法.md
File diff suppressed because it is too large
Load Diff
1974
notes/剑指 offer 题解.md
1974
notes/剑指 offer 题解.md
File diff suppressed because it is too large
Load Diff
1252
notes/算法.md
1252
notes/算法.md
File diff suppressed because it is too large
Load Diff
688
notes/计算机操作系统.md
688
notes/计算机操作系统.md
File diff suppressed because it is too large
Load Diff
765
notes/计算机网络.md
765
notes/计算机网络.md
File diff suppressed because it is too large
Load Diff
1794
notes/设计模式.md
1794
notes/设计模式.md
File diff suppressed because it is too large
Load Diff
285
notes/面向对象思想.md
285
notes/面向对象思想.md
@ -16,571 +16,286 @@
|
||||
<!-- GFM-TOC -->
|
||||
|
||||
|
||||
|
||||
# S.O.L.I.D
|
||||
|
||||
|
||||
|
||||
S.O.L.I.D 是面向对象设计和编程 (OOD&OOP) 中几个重要编码原则 (Programming Priciple) 的首字母缩写。
|
||||
|
||||
|
||||
|
||||
| 简写 | 全拼 | 中文翻译 |
|
||||
|
||||
| -- | -- | -- |
|
||||
|
||||
|SRP| The Single Responsibility Principle | 单一责任原则 |
|
||||
|
||||
|OCP| The Open Closed Principle | 开放封闭原则 |
|
||||
|
||||
|LSP| The Liskov Substitution Principle | 里氏替换原则 |
|
||||
|
||||
|ISP| The Interface Segregation Principle | 接口分离原则 |
|
||||
|
||||
|DIP| The Dependency Inversion Principle | 依赖倒置原则 |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 1. 单一责任原则
|
||||
|
||||
|
||||
|
||||
当需要修改某个类的时候原因有且只有一个。换句话说就是让一个类只做一种类型责任,当这个类需要承当其他类型的责任的时候,就需要分解这个类。
|
||||
|
||||
|
||||
|
||||
## 2. 开放封闭原则
|
||||
|
||||
|
||||
|
||||
软件实体应该是可扩展,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的。
|
||||
|
||||
|
||||
|
||||
## 3. 里氏替换原则
|
||||
|
||||
|
||||
|
||||
当一个子类的实例应该能够替换任何其超类的实例时,它们之间才具有 is-a 关系。
|
||||
|
||||
|
||||
|
||||
## 4. 接口分离原则
|
||||
|
||||
|
||||
|
||||
不能强迫用户去依赖那些他们不使用的接口。换句话说,使用多个专门的接口比使用单一的总接口总要好。
|
||||
|
||||
|
||||
|
||||
## 5. 依赖倒置原则
|
||||
|
||||
|
||||
|
||||
1. 高层模块不应该依赖于低层模块,二者都应该依赖于抽象
|
||||
|
||||
2. 抽象不应该依赖于细节,细节应该依赖于抽象
|
||||
|
||||
|
||||
|
||||
# 封装、继承、多态
|
||||
|
||||
|
||||
|
||||
封装、继承、多态是面向对象的三大特性。
|
||||
|
||||
|
||||
|
||||
## 1. 封装
|
||||
|
||||
|
||||
|
||||
利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体,数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。用户是无需知道对象内部的细节,但可以通过该对象对外的提供的接口来访问该对象。
|
||||
|
||||
|
||||
|
||||
封装有三大好处:
|
||||
|
||||
|
||||
|
||||
1. 良好的封装能够减少耦合。
|
||||
|
||||
2. 类内部的结构可以自由修改。
|
||||
|
||||
3. 可以对成员进行更精确的控制。
|
||||
|
||||
4. 隐藏信息,实现细节。
|
||||
|
||||
|
||||
|
||||
以下 Person 类封装 name、gender、age 等属性,外界只能通过 get() 方法获取一个 Person 对象的 name 属性和 gender 属性,而无法获取 age 属性,但是 age 属性可以供 work() 方法使用。
|
||||
|
||||
|
||||
|
||||
注意到 gender 属性使用 int 数据类型进行存储,封装使得用户注意不到这种实现细节。并且在需要修改使用的数据类型时,也可以在不影响客户端代码的情况下进行。
|
||||
|
||||
|
||||
|
||||
```java
|
||||
|
||||
public class Person {
|
||||
|
||||
private String name;
|
||||
|
||||
private int gender;
|
||||
|
||||
private int age;
|
||||
|
||||
|
||||
|
||||
public String getName() {
|
||||
|
||||
return name;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String getGender() {
|
||||
|
||||
return gender == 0 ? "man" : "woman";
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void work() {
|
||||
|
||||
if(18 <= age && age <= 50) {
|
||||
|
||||
System.out.println(name + " is working very hard!");
|
||||
|
||||
} else {
|
||||
|
||||
System.out.println(name + " can't work!");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 2. 继承
|
||||
|
||||
|
||||
|
||||
继承实现了 **is-a** 关系,例如 Cat 和 Animal 就是一种 is-a 关系,因此可以将 Cat 继承自 Animal,从而获得 Animal 非 private 的属性和方法。
|
||||
|
||||
|
||||
|
||||
Cat 可以当做 Animal 来使用,也就是可以使用 Animal 引用 Cat 对象,这种子类转换为父类称为 **向上转型**。
|
||||
|
||||
|
||||
|
||||
继承应该遵循里氏替换原则:当一个子类的实例应该能够替换任何其超类的实例时,它们之间才具有 is-a 关系。
|
||||
|
||||
|
||||
|
||||
```java
|
||||
|
||||
Animal animal = new Cat();
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 3. 多态
|
||||
|
||||
|
||||
|
||||
多态分为编译时多态和运行时多态。编译时多态主要指方法的重装,运行时多态指程序中定义的对象引用所指向的具体类型在运行期间才确定。
|
||||
|
||||
|
||||
|
||||
多态有三个条件:1. 继承;2. 覆盖父类方法;3. 向上转型。
|
||||
|
||||
|
||||
|
||||
下面的代码中,乐器类(Instrument)有两个子类:Wind 和 Percussion,它们都覆盖了 play() 方法,并且在 main() 方法中使用父类 Instrument 来引用 Wind 和 Percussion 对象。在 Instrument 引用调用 play() 方法时,会执行实际引用对象所在类的 play() 方法,而不是 Instrument 类的方法。
|
||||
|
||||
|
||||
|
||||
```java
|
||||
|
||||
public class Instrument {
|
||||
|
||||
public void play() {
|
||||
|
||||
System.out.println("Instument is playing...");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class Wind extends Instrument {
|
||||
|
||||
public void play() {
|
||||
|
||||
System.out.println("Wind is playing...");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class Percussion extends Instrument {
|
||||
|
||||
public void play() {
|
||||
|
||||
System.out.println("Percussion is playing...");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class Music {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
List<Instrument> instruments = new ArrayList<>();
|
||||
|
||||
instruments.add(new Wind());
|
||||
|
||||
instruments.add(new Percussion());
|
||||
|
||||
for(Instrument instrument : instruments) {
|
||||
|
||||
instrument.play();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# UML
|
||||
|
||||
|
||||
|
||||
## 1. 类图
|
||||
|
||||
|
||||
|
||||
**1.1 继承相关**
|
||||
|
||||
|
||||
|
||||
继承有两种形式 : 泛化(generalize)和实现(realize),表现为 is-a 关系。
|
||||
|
||||
|
||||
|
||||
① 泛化关系 (generalization)
|
||||
|
||||
|
||||
|
||||
从具体类中继承
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//29badd92-109f-4f29-abb9-9857f5973928.png)
|
||||
|
||||
|
||||
|
||||
② 实现关系 (realize)
|
||||
|
||||
|
||||
|
||||
从抽象类或者接口中继承
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//4b16e1d3-3a60-472c-9756-2f31b1c48abe.png)
|
||||
|
||||
|
||||
|
||||
**1.2 整体和部分**
|
||||
|
||||
|
||||
|
||||
① 聚合关系 (aggregation)
|
||||
|
||||
|
||||
|
||||
表示整体由部分组成,但是整体和部分不是强依赖的,整体不存在了部分还是会存在。以下表示 B 由 A 组成:
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//34259bb8-ca3a-4872-8771-9e946782d9c3.png)
|
||||
|
||||
|
||||
|
||||
② 组合关系 (composition)
|
||||
|
||||
|
||||
|
||||
和聚合不同,组合中整体和部分是强依赖的,整体不存在了部分也不存在了。比如公司和部门,公司没了部门就不存在了。但是公司和员工就属于聚合关系了,因为公司没了员工还在。
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//7dda050d-ac35-4f47-9f51-18f18ed6fa9a.png)
|
||||
|
||||
|
||||
|
||||
**1.3 相互联系**
|
||||
|
||||
|
||||
|
||||
① 关联关系 (association)
|
||||
|
||||
|
||||
|
||||
表示不同类对象之间有关联,这是一种静态关系,与运行过程的状态无关,在最开始就可以确定。因此也可以用 1 对 1、多对 1、多对多这种关联关系来表示。比如学生和学校就是一种关联关系,一个学校可以有很多学生,但是一个学生只属于一个学校,因此这是一种多对一的关系,在运行开始之前就可以确定。
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//4ccd294c-d6b2-421b-839e-d88336ff5fb7.png)
|
||||
|
||||
|
||||
|
||||
② 依赖关系 (dependency)
|
||||
|
||||
|
||||
|
||||
和关联关系不同的是 , 依赖关系是在运行过程中起作用的。一般依赖作为类的构造器或者方法的参数传入。双向依赖时一种不好的设计。
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//47ca2614-509f-476e-98fc-50ec9f9d43c0.png)
|
||||
|
||||
|
||||
|
||||
## 2. 时序图
|
||||
|
||||
|
||||
|
||||
http://www.cnblogs.com/wolf-sun/p/UML-Sequence-diagram.html
|
||||
|
||||
|
||||
|
||||
**2.1 定义**
|
||||
|
||||
|
||||
|
||||
时序图描述了对象之间传递消息的时间顺序,它用来表示用例的行为顺序。它的主要作用是通过对象间的交互来描述用例(注意是对象),从而寻找类的操作。
|
||||
|
||||
|
||||
|
||||
**2.2 赤壁之战时序图**
|
||||
|
||||
|
||||
|
||||
从虚线从上往下表示时间的推进。
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//80c5aff8-fc46-4810-aeaa-215b5c60a003.png)
|
||||
|
||||
|
||||
|
||||
可见,通过时序图可以知道每个类具有以下操作:
|
||||
|
||||
|
||||
|
||||
```java
|
||||
|
||||
publc class 刘备 {
|
||||
|
||||
public void 应战 ();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
publc class 孔明 {
|
||||
|
||||
public void 拟定策略 ();
|
||||
|
||||
public void 联合孙权 ();
|
||||
|
||||
private void 借东风火攻 ();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class 关羽 {
|
||||
|
||||
public void 防守荊州 ();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class 张飞 {
|
||||
|
||||
public void 防守荆州前线 ();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class 孙权 {
|
||||
|
||||
public void 领兵相助 ();
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
**2.3 活动图、时序图之间的关系**
|
||||
|
||||
|
||||
|
||||
活动图示从用户的角度来描述用例;
|
||||
|
||||
|
||||
|
||||
时序图是从计算机的角度(对象间的交互)描述用例。
|
||||
|
||||
|
||||
|
||||
**2.4 类图与时序图的关系**
|
||||
|
||||
|
||||
|
||||
类图描述系统的静态结构,时序图描述系统的动态行为。
|
||||
|
||||
|
||||
|
||||
**2.5 时序图的组成**
|
||||
|
||||
|
||||
|
||||
① 对象
|
||||
|
||||
|
||||
|
||||
有三种表现形式
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//25b8adad-2ef6-4f30-9012-c306b4e49897.png)
|
||||
|
||||
|
||||
|
||||
在画图时,应该遵循以下原则:
|
||||
|
||||
|
||||
|
||||
1. 把交互频繁的对象尽可能地靠拢。
|
||||
|
||||
|
||||
|
||||
2. 把初始化整个交互活动的对象(有时是一个参与者)放置在最左边。
|
||||
|
||||
|
||||
|
||||
② 生命线
|
||||
|
||||
|
||||
|
||||
生命线从对象的创建开始到对象销毁时终止
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//b7b0eac6-e7ea-4fb6-8bfb-95fec6f235e2.png)
|
||||
|
||||
|
||||
|
||||
③ 消息
|
||||
|
||||
|
||||
|
||||
对象之间的交互式通过发送消息来实现的。
|
||||
|
||||
|
||||
|
||||
消息有 4 种类型:
|
||||
|
||||
|
||||
|
||||
1\. 简单消息,不区分同步异步。
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//a13b62da-0fa8-4224-a615-4cadacc08871.png)
|
||||
|
||||
|
||||
|
||||
2\. 同步消息,发送消息之后需要暂停活动来等待回应。
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//33821037-dc40-4266-901c-e5b38e618426.png)
|
||||
|
||||
|
||||
|
||||
3\. 异步消息,发送消息之后不需要等待。
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//dec6c6cc-1b5f-44ed-b8fd-464fcf849dac.png)
|
||||
|
||||
|
||||
|
||||
4\. 返回消息,可选。
|
||||
|
||||
|
||||
|
||||
④ 激活
|
||||
|
||||
|
||||
|
||||
生命线上的方框表示激活状态,其它时间处于休眠状态。
|
||||
|
||||
|
||||
|
||||
![](https://github.com/CyC2018/InterviewNotes/blob/master/pics//6ab5de9b-1c1e-4118-b2c3-fb6c7ed7de6f.png)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 参考资料
|
||||
|
||||
|
||||
|
||||
- Java 编程思想
|
||||
|
||||
- [ 面向对象设计的 SOLID 原则 ](http://www.cnblogs.com/shanyou/archive/2009/09/21/1570716.html)
|
||||
|
||||
- [ 看懂 UML 类图和时序图 ](http://design-patterns.readthedocs.io/zh_CN/latest/read_uml.html#generalization)
|
||||
|
||||
- [UML 系列——时序图(顺序图)sequence diagram](http://www.cnblogs.com/wolf-sun/p/UML-Sequence-diagram.html)
|
||||
|
||||
- [ 面向对象编程三大特性 ------ 封装、继承、多态 ](http://blog.csdn.net/jianyuerensheng/article/details/51602015)
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user