mirror of
https://github.com/CyC2018/Interview-Notebook.git
synced 2026-10-30 15:58:44 +00:00
+2
-2
@@ -36,7 +36,7 @@ Docker 主要解决环境配置问题,它是一种虚拟化技术,对进程
|
||||
|
||||
虚拟机是一个完整的操作系统,需要占用大量的磁盘、内存和 CPU 资源,一台机器只能开启几十个的虚拟机。
|
||||
|
||||
而 Docker 只是一个进程,只需要将应用以及相关的组件打包,在运行时占用很少的资源,一台机器可以开启成千上万个 Docker。
|
||||
而容器只是宿主机上隔离运行的进程,只需要将应用以及相关的组件打包,在运行时占用很少的资源,一台机器可以运行成千上万个容器。
|
||||
|
||||
## 三、优势
|
||||
|
||||
@@ -152,7 +152,7 @@ spec:
|
||||
在单节点 K8s(k3s v1.31)上实际操作的结果如下:
|
||||
|
||||
- kubectl apply 之后约 20 秒,3 个 Pod 全部就绪,Service 得到虚拟 IP 10.43.189.89,在集群内用 http://order-service 就能访问;
|
||||
- 删除一个 Pod,Deployment 发现副本数少于 3,立即创建新的 Pod,约 1.2 秒后恢复为 3/3,新 Pod 的 IP 与原来不同,Service 自动更新了后端列表;
|
||||
- 删除一个 Pod,Deployment 创建的 ReplicaSet 发现副本数少于 3,立即创建新的 Pod,约 1.2 秒后恢复为 3/3,新 Pod 的 IP 与原来不同,Service 自动更新了后端列表;
|
||||
- kubectl scale 把副本数改为 5;kubectl set image 修改镜像版本后,Deployment 创建新的 ReplicaSet,逐个用新版本的 Pod 替换旧的,约 12 秒完成滚动升级,期间一直有就绪的 Pod 在提供服务,需要时可以用 kubectl rollout undo 回滚。
|
||||
|
||||
readinessProbe 是就绪探针:Pod 启动后要通过检查才会加入 Service 的后端列表,避免把请求转发给还没启动完成的实例。
|
||||
|
||||
+7
-7
@@ -71,7 +71,7 @@ Git 的版本库有一个称为 Stage 的暂存区以及最后的 History 版本
|
||||
|
||||
<div align="center"> <img src="pics/git-branch-commit.png" width="760px"/> </div><br>
|
||||
|
||||
合并分支也只需要改变指针即可:切换回 master 后合并 dev,master 直接移动到 dev 所指的提交。
|
||||
合并分支也只需要改变指针即可:切换回 master 后合并 dev,master 直接移动到 dev 所指的提交。这种情况称为快进合并(Fast-forward),两个分支都有新的提交时,合并会生成一个新的提交。
|
||||
|
||||
<div align="center"> <img src="pics/git-branch-merge.png" width="760px"/> </div><br>
|
||||
|
||||
@@ -105,9 +105,9 @@ $ git merge --no-ff -m "merge with no-ff" dev
|
||||
|
||||
## 储藏(Stashing)
|
||||
|
||||
在一个分支上操作之后,如果还没有将修改提交到分支上,此时进行切换分支,那么另一个分支上也能看到新的修改。这是因为所有分支都共用一个工作区的缘故。
|
||||
在一个分支上操作之后,如果还没有将修改提交到分支上,此时进行切换分支,那么另一个分支上也能看到新的修改。这是因为所有分支都共用一个工作区的缘故。如果这些修改涉及的文件在两个分支上不同,切换会覆盖掉修改,Git 会拒绝切换。
|
||||
|
||||
可以使用 git stash 将当前分支的修改储藏起来,此时当前工作区的所有修改都会被存到栈中,也就是说当前工作区是干净的,没有任何未提交的修改。此时就可以安全的切换到其它分支上了。
|
||||
可以使用 git stash 将当前分支的修改储藏起来,此时当前工作区中已跟踪文件的修改都会被存到栈中,工作区回到干净的状态;未跟踪的新文件默认不会储藏,需要加上 -u 选项。此时就可以安全的切换到其它分支上了。
|
||||
|
||||
```bash
|
||||
$ git stash
|
||||
@@ -119,15 +119,15 @@ HEAD is now at 049d078 added the index file (To restore them type "git stash app
|
||||
|
||||
## SSH 传输设置
|
||||
|
||||
Git 仓库和 GitHub 中心仓库之间的传输是通过 SSH 加密。
|
||||
Git 仓库和 GitHub 中心仓库之间可以通过 HTTPS 或者 SSH 加密传输,使用 SSH 时需要配置 SSH Key。
|
||||
|
||||
如果用户主目录下没有 .ssh 目录,或者该目录下没有 id_rsa 和 id_rsa.pub 这两个文件,可以通过以下命令来创建 SSH Key:
|
||||
如果用户主目录下没有 .ssh 目录,或者该目录下没有 id_ed25519 和 id_ed25519.pub 这两个文件,可以通过以下命令来创建 SSH Key:
|
||||
|
||||
```bash
|
||||
$ ssh-keygen -t rsa -C "youremail@example.com"
|
||||
$ ssh-keygen -t ed25519 -C "youremail@example.com"
|
||||
```
|
||||
|
||||
然后把公钥 id_rsa.pub 的内容复制到 GitHub “Settings” 的 SSH Keys 中。
|
||||
然后把公钥 id_ed25519.pub 的内容复制到 GitHub “Settings” 的 SSH Keys 中。
|
||||
|
||||
## .gitignore 文件
|
||||
|
||||
|
||||
+3
-3
@@ -125,7 +125,7 @@ DataInputStream 装饰者提供了对更多数据类型进行输入的操作,
|
||||
|
||||
UTF-16be 中的 be 指的是 Big Endian,也就是大端。相应地也有 UTF-16le,le 指的是 Little Endian,也就是小端。
|
||||
|
||||
Java 的内存编码使用双字节编码 UTF-16be,这不是指 Java 只支持这一种编码方式,而是说 char 这种类型使用 UTF-16be 进行编码。char 类型占 16 位,也就是两个字节,Java 使用这种双字节编码是为了让一个中文或者一个英文都能使用一个 char 来存储。
|
||||
Java 的 char 类型占 16 位,存放的是字符的 UTF-16 编码,这不是指 Java 只支持这一种编码方式。常用的中文和英文字符都能用一个 char 存储,编号超过 U+FFFF 的字符(例如一部分生僻字和 emoji)要用两个 char 表示。
|
||||
|
||||
### String 的编码方式
|
||||
|
||||
@@ -138,7 +138,7 @@ String str2 = new String(bytes, "UTF-8");
|
||||
System.out.println(str2);
|
||||
```
|
||||
|
||||
在调用无参数 getBytes() 方法时,默认的编码方式不是 UTF-16be。双字节编码的好处是可以使用一个 char 存储中文和英文,而将 String 转为 bytes[] 字节数组就不再需要这个好处,因此也就不再需要双字节编码。getBytes() 的默认编码方式与平台有关,一般为 UTF-8。
|
||||
在调用无参数 getBytes() 方法时,默认的编码方式不是 UTF-16。双字节编码的好处是可以使用一个 char 存储中文和英文,而将 String 转为 bytes[] 字节数组就不再需要这个好处,因此也就不再需要双字节编码。JDK 18 起 getBytes() 默认使用 UTF-8(JEP 400),之前的版本与平台有关。
|
||||
|
||||
```java
|
||||
byte[] bytes = str1.getBytes();
|
||||
@@ -254,7 +254,7 @@ InetAddress.getByAddress(byte[] address);
|
||||
```java
|
||||
public static void main(String[] args) throws IOException {
|
||||
|
||||
URL url = new URL("http://www.baidu.com");
|
||||
URL url = URI.create("http://www.baidu.com").toURL();
|
||||
|
||||
/* 字节流 */
|
||||
InputStream is = url.openStream();
|
||||
|
||||
+5
-5
@@ -228,7 +228,7 @@ SAS(Serial Attached SCSI)是新一代的 SCSI 技术,和 SATA 硬盘相同
|
||||
|
||||
Linux 中每个硬件都被当做一个文件,包括磁盘。磁盘以磁盘接口类型进行命名,常见磁盘的文件名如下:
|
||||
|
||||
- IDE 磁盘:/dev/hd[a-d]
|
||||
- IDE 磁盘:/dev/hd[a-d](旧驱动的命名,Linux 5.14 移除了旧的 IDE 驱动,现在由 libata 驱动 IDE 磁盘,也命名为 /dev/sd*)
|
||||
- SATA/SCSI/SAS 磁盘:/dev/sd[a-p]
|
||||
- NVMe 磁盘:/dev/nvme0n1、/dev/nvme1n1
|
||||
|
||||
@@ -278,7 +278,7 @@ BIOS 是开机的时候计算机执行的第一个程序,这个程序知道可
|
||||
|
||||
#### 2. UEFI
|
||||
|
||||
BIOS 不可以读取 GPT 分区表,而 UEFI 可以。
|
||||
BIOS 只会执行 MBR 中的开机管理程序,本身不读取分区表,从 GPT 磁盘启动要依靠支持 GPT 的开机管理程序(例如 GRUB);UEFI 可以直接读取 GPT 分区表。
|
||||
|
||||
## 四、文件系统
|
||||
|
||||
@@ -450,7 +450,7 @@ rmdir [-p] 目录名称
|
||||
```text
|
||||
## touch [-acdmt] filename
|
||||
-a : 更新 atime
|
||||
-c : 更新 ctime,若该文件不存在则不建立新文件
|
||||
-c : 若该文件不存在则不建立新文件
|
||||
-m : 更新 mtime
|
||||
-d : 后面可以接更新日期而不使用当前日期,也可以使用 --date="日期或时间"
|
||||
-t : 后面可以接更新时间而不使用当前时间,格式为[YYYYMMDDhhmm]
|
||||
@@ -760,7 +760,7 @@ $ xz [-dtlkc#] filename
|
||||
$ tar [-z|-j|-J] [cv] [-f 新建的 tar 文件] filename... ==打包压缩
|
||||
$ tar [-z|-j|-J] [tv] [-f 已有的 tar 文件] ==查看
|
||||
$ tar [-z|-j|-J] [xv] [-f 已有的 tar 文件] [-C 目录] ==解压缩
|
||||
-z :使用 zip;
|
||||
-z :使用 gzip;
|
||||
-j :使用 bzip2;
|
||||
-J :使用 xz;
|
||||
-c :新建打包文件;
|
||||
@@ -785,7 +785,7 @@ $ tar [-z|-j|-J] [xv] [-f 已有的 tar 文件] [-C 目录] ==解压缩
|
||||
|
||||
- 命令历史:记录使用过的命令
|
||||
- 命令与文件补全:快捷键:tab
|
||||
- 命令别名:例如 ll 通常是 ls -l 或者 ls -alF 的别名,具体取决于发行版的配置
|
||||
- 命令别名:例如 Ubuntu 默认的 ~/.bashrc 中,ll 是 ls -alF 的别名
|
||||
- shell scripts
|
||||
- 通配符:例如 ls -l /usr/bin/X\* 列出 /usr/bin 下面所有以 X 开头的文件
|
||||
|
||||
|
||||
+1
-1
@@ -764,7 +764,7 @@ VALUES
|
||||
### Solution
|
||||
|
||||
```sql
|
||||
CREATE FUNCTION getNthHighestSalary ( N INT ) RETURNS INT BEGIN
|
||||
CREATE FUNCTION getNthHighestSalary ( N INT ) RETURNS INT READS SQL DATA BEGIN
|
||||
|
||||
SET N = N - 1;
|
||||
RETURN (
|
||||
|
||||
+8
-7
@@ -42,7 +42,7 @@
|
||||
|
||||
模式定义了数据如何存储、存储什么样的数据以及数据如何分解等信息,数据库和表都有模式。
|
||||
|
||||
主键的值不允许修改,也不允许复用(不能将已经删除的主键值赋给新数据行的主键)。
|
||||
主键的值一般不修改,也不复用(不把已经删除的主键值赋给新数据行的主键),这是普遍遵循的习惯,数据库本身并不禁止。
|
||||
|
||||
SQL(Structured Query Language),标准 SQL 由 ANSI 标准委员会管理,从而称为 ANSI SQL。各个 DBMS 都有自己的实现,如 PL/SQL、Transact-SQL 等。
|
||||
|
||||
@@ -403,7 +403,7 @@ HAVING num >= 2;
|
||||
|
||||
## 十四、子查询
|
||||
|
||||
子查询中只能返回一个字段的数据。
|
||||
作为 IN 等条件与一个字段比较的子查询,只能返回一个字段的数据;MySQL 也支持 (col1, col2) IN (SELECT x, y ...) 这样同时比较多个字段。
|
||||
|
||||
可以将子查询的结果作为 WHERE 语句的过滤条件:
|
||||
|
||||
@@ -581,9 +581,7 @@ WHERE col5 = val;
|
||||
|
||||
包含 in、out 和 inout 三种参数。
|
||||
|
||||
给变量赋值都需要用 select into 语句。
|
||||
|
||||
每次只能给一个变量赋值,不支持集合的操作。
|
||||
可以用 SET 或者 SELECT ... INTO 给变量赋值。SELECT ... INTO 可以一次给多个变量赋值,但查询结果只能有一行,否则报错 1172(Result consisted of more than one row),多行结果要用下面的游标逐行处理。
|
||||
|
||||
```sql
|
||||
delimiter //
|
||||
@@ -633,7 +631,10 @@ create procedure myprocedure(out ret int)
|
||||
|
||||
repeat
|
||||
fetch mycursor into ret;
|
||||
select ret;
|
||||
# 最后一次 fetch 没有取到数据时 done 已经为 1,不能再输出 ret
|
||||
if not done then
|
||||
select ret;
|
||||
end if;
|
||||
until done end repeat;
|
||||
|
||||
close mycursor;
|
||||
@@ -658,7 +659,7 @@ SELECT @result; -- 获取结果
|
||||
|
||||
DELETE 触发器包含一个名为 OLD 的虚拟表,并且是只读的。
|
||||
|
||||
UPDATE 触发器包含一个名为 NEW 和一个名为 OLD 的虚拟表,其中 NEW 是可以被修改的,而 OLD 是只读的。
|
||||
UPDATE 触发器包含一个名为 NEW 和一个名为 OLD 的虚拟表,其中 NEW 只能在 BEFORE 触发器中修改(SET NEW.col = ...),而 OLD 是只读的。
|
||||
|
||||
MySQL 的触发器可以使用 CALL 语句调用存储过程,但是该存储过程不能向客户端返回数据,也不能使用动态 SQL。
|
||||
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ Spring Cloud 与 Spring Boot 的关系:Spring Boot 用来快速构建单个服
|
||||
| --- | --- | --- | --- |
|
||||
| 服务的实例在哪里 | DiscoveryClient 接口 | Nacos | Eureka |
|
||||
| 选择哪个实例 | Spring Cloud LoadBalancer | Nacos 的权重负载均衡 | Ribbon |
|
||||
| 怎么调用其它服务 | OpenFeign | — | — |
|
||||
| 怎么调用其它服务 | OpenFeign | — | Feign |
|
||||
| 配置的集中管理和动态修改 | Spring Cloud Config | Nacos | Archaius |
|
||||
| 外部请求从哪里进入 | Spring Cloud Gateway | — | Zuul |
|
||||
| 下游故障时怎么保护自己 | Spring Cloud Circuit Breaker(Resilience4j) | Sentinel | Hystrix |
|
||||
|
||||
+4
-4
@@ -188,9 +188,9 @@ strSQL = "SELECT * FROM users;"
|
||||
Java 中的 PreparedStatement 是预先编译的 SQL 语句,可以传入适当参数并且多次执行。SQL 语句的结构在预编译时已经确定,传入的参数只会被当作数据,因此可以防止 SQL 注入的发生。
|
||||
|
||||
```java
|
||||
PreparedStatement stmt = connection.prepareStatement("SELECT * FROM users WHERE userid=? AND password=?");
|
||||
stmt.setString(1, userid);
|
||||
stmt.setString(2, password);
|
||||
PreparedStatement stmt = connection.prepareStatement("SELECT * FROM users WHERE name = ? AND pw = ?");
|
||||
stmt.setString(1, name);
|
||||
stmt.setString(2, pw);
|
||||
ResultSet rs = stmt.executeQuery();
|
||||
```
|
||||
|
||||
@@ -204,7 +204,7 @@ Query SELECT * FROM users WHERE name = '1'' OR ''1''=''1' AND pw = '1'' OR ''
|
||||
|
||||
#### 2. 单引号转换
|
||||
|
||||
将传入的参数中的单引号转换为连续两个单引号。PHP 的 Magic Quotes 曾经自动完成这个功能,但是因为不可靠已经被移除,应该使用数据库驱动提供的转义函数,并且只在无法使用参数化查询时使用。
|
||||
将传入的参数中的单引号转换为连续两个单引号,或者在单引号前加反斜线(MySQL 默认两种都支持)。PHP 的 Magic Quotes 曾经自动给单引号等字符加反斜线,但是因为不可靠,已经在 PHP 5.4 中移除,应该使用数据库驱动提供的转义函数,并且只在无法使用参数化查询时使用。
|
||||
|
||||
#### 3. 最小权限
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ gcc -o hello hello.c
|
||||
|
||||
- 可执行目标文件:可以直接在内存中执行;
|
||||
- 可重定位目标文件:可与其它可重定位目标文件在链接阶段合并,创建一个可执行目标文件;
|
||||
- 共享目标文件:这是一种特殊的可重定位目标文件,可以在运行时被动态加载进内存并链接;
|
||||
- 共享目标文件:这是一种特殊的可重定位目标文件,可以在加载或者运行时被动态加载进内存并链接;
|
||||
|
||||
## 动态链接
|
||||
|
||||
|
||||
+2
-2
@@ -52,7 +52,7 @@
|
||||
|
||||
### 2. 分组交换
|
||||
|
||||
每个分组都有首部和尾部,包含了源地址和目的地址等控制信息,在同一个传输线路上同时传输多个分组互相不会影响,因此在同一条传输线路上允许同时传输多个分组,也就是说分组交换不需要占用传输线路。
|
||||
每个分组都有首部,包含了源地址和目的地址等控制信息,在同一个传输线路上同时传输多个分组互相不会影响,因此在同一条传输线路上允许同时传输多个分组,也就是说分组交换不需要独占传输线路。
|
||||
|
||||
在一个邮局通信系统中,邮局收到一份邮件之后,先存储下来,然后把相同目的地的邮件一起转发到下一个目的地,这个过程就是存储转发过程,分组交换也使用了存储转发过程。
|
||||
|
||||
@@ -123,7 +123,7 @@ TCP/IP 体系结构不严格遵循 OSI 分层概念,应用层可能会直接
|
||||
|
||||
在向下的过程中,需要添加下层协议所需要的首部或者尾部,而在向上的过程中不断拆开首部和尾部。
|
||||
|
||||
路由器只有下面三层协议,因为路由器位于网络核心中,不需要为进程或者应用程序提供服务,因此也就不需要传输层和应用层。
|
||||
路由器转发分组时只用到下面三层协议,因为路由器位于网络核心中,转发时不需要为进程或者应用程序提供服务,因此也就不需要传输层和应用层;路由器运行的路由协议另当别论,例如 BGP 使用 TCP 传输。
|
||||
|
||||
## 参考资料
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
<div align="center"> <img src="pics/modulation.png" width="720px"/> </div><br>
|
||||
|
||||
实际的系统中还会把振幅和相位组合起来使用,例如正交振幅调制(Quadrature Amplitude Modulation, QAM)用 16 种不同的振幅和相位组合表示 4 个比特,一个码元就能传输更多的数据。
|
||||
实际的系统中还会把振幅和相位组合起来使用,例如正交振幅调制(Quadrature Amplitude Modulation, QAM)中的 16-QAM 用 16 种不同的振幅和相位组合表示 4 个比特,一个码元就能传输更多的数据。
|
||||
|
||||
## 参考资料
|
||||
|
||||
|
||||
@@ -136,7 +136,8 @@ CSMA/CD 表示载波监听多点接入 / 碰撞检测。
|
||||
PPP 的帧格式:
|
||||
|
||||
- F 字段为帧的定界符;
|
||||
- A 和 C 字段暂时没有意义;
|
||||
- A 和 C 字段的值是固定的,分别为 0xFF 和 0x03,不携带信息;
|
||||
- 协议字段指明信息部分是哪种协议的数据,例如 0x0021 表示 IP 数据报;
|
||||
- FCS 字段是使用 CRC 的检验序列;
|
||||
- 信息部分的长度不超过 1500 字节。
|
||||
|
||||
@@ -192,7 +193,7 @@ MAC 地址是链路层地址,长度为 6 字节(48 位),用于唯一标
|
||||
|
||||
<div align="center"> <img src="pics/vlan-broadcast.png" width="760px"/> </div><br>
|
||||
|
||||
使用 VLAN 干线连接来建立虚拟局域网,每台交换机上的一个特殊接口被设置为干线接口,以互连 VLAN 交换机。IEEE 定义了一种扩展的以太网帧格式 802.1Q,它在标准以太网帧上加进了 4 字节首部 VLAN 标签,用于表示该帧属于哪一个虚拟局域网。
|
||||
使用 VLAN 干线连接来建立虚拟局域网,每台交换机上的一个特殊接口被设置为干线接口,以互连 VLAN 交换机。IEEE 定义了一种扩展的以太网帧格式 802.1Q,它在标准以太网帧的源地址和类型字段之间插入了 4 字节的 VLAN 标签,用于表示该帧属于哪一个虚拟局域网。
|
||||
|
||||
<div align="center"> <img src="pics/vlan-tag.png" width="760px"/> </div><br>
|
||||
|
||||
|
||||
+17
-16
@@ -11,7 +11,7 @@
|
||||
<div align="center"> <img src="pics/dp-singleton.png" width="760px"/> </div><br>
|
||||
|
||||
### 实现
|
||||
#### Ⅰ 懒汉式-线程不安全
|
||||
#### 1. 懒汉式-线程不安全
|
||||
|
||||
以下实现中,私有静态变量 uniqueInstance 被延迟实例化,这样做的好处是,如果没有用到该类,那么就不会实例化 uniqueInstance,从而节约资源。
|
||||
|
||||
@@ -34,7 +34,7 @@ public class Singleton {
|
||||
}
|
||||
```
|
||||
|
||||
#### Ⅱ 饿汉式-线程安全
|
||||
#### 2. 饿汉式-线程安全
|
||||
|
||||
线程不安全问题主要是由于 uniqueInstance 被实例化多次,采取直接实例化 uniqueInstance 的方式就不会产生线程不安全问题。
|
||||
|
||||
@@ -44,7 +44,7 @@ public class Singleton {
|
||||
private static Singleton uniqueInstance = new Singleton();
|
||||
```
|
||||
|
||||
#### Ⅲ 懒汉式-线程安全
|
||||
#### 3. 懒汉式-线程安全
|
||||
|
||||
只需要对 getUniqueInstance() 方法加锁,那么在一个时间点只能有一个线程能够进入该方法,从而避免了实例化多次 uniqueInstance。
|
||||
|
||||
@@ -59,7 +59,7 @@ public static synchronized Singleton getUniqueInstance() {
|
||||
}
|
||||
```
|
||||
|
||||
#### Ⅳ 双重校验锁-线程安全
|
||||
#### 4. 双重校验锁-线程安全
|
||||
|
||||
uniqueInstance 只需要被实例化一次,之后就可以直接使用了。加锁操作只需要对实例化那部分的代码进行,只有当 uniqueInstance 没有被实例化时,才需要进行加锁。
|
||||
|
||||
@@ -98,15 +98,15 @@ if (uniqueInstance == null) {
|
||||
|
||||
uniqueInstance 采用 volatile 关键字修饰也是很有必要的, `uniqueInstance = new Singleton();` 这段代码其实是分为三步执行:
|
||||
|
||||
1. 为 uniqueInstance 分配内存空间
|
||||
2. 初始化 uniqueInstance
|
||||
1. 为对象分配内存空间
|
||||
2. 初始化对象
|
||||
3. 将 uniqueInstance 指向分配的内存地址
|
||||
|
||||
但是由于 JVM 具有重排序的特性,执行顺序有可能变成 1>3>2。重排序在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T<sub>1</sub> 执行了 1 和 3,此时 T<sub>2</sub> 调用 getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此时 uniqueInstance 还未被初始化。
|
||||
|
||||
使用 volatile 可以禁止 JVM 的重排序,保证在多线程环境下也能正常运行。
|
||||
|
||||
#### Ⅴ 静态内部类实现
|
||||
#### 5. 静态内部类实现
|
||||
|
||||
当 Singleton 类被加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用 `getUniqueInstance()` 方法从而触发 `SingletonHolder.INSTANCE` 时 SingletonHolder 才会被加载,此时初始化 INSTANCE 实例,并且 JVM 能确保 INSTANCE 只被实例化一次。
|
||||
|
||||
@@ -128,9 +128,11 @@ public class Singleton {
|
||||
}
|
||||
```
|
||||
|
||||
#### Ⅵ 枚举实现
|
||||
#### 6. 枚举实现
|
||||
|
||||
```java
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
public enum Singleton {
|
||||
|
||||
INSTANCE;
|
||||
@@ -159,14 +161,13 @@ public enum Singleton {
|
||||
System.out.println(firstSingleton.getObjName());
|
||||
System.out.println(secondSingleton.getObjName());
|
||||
|
||||
// 反射获取实例测试
|
||||
// 通过反射调用构造函数创建新的实例会失败
|
||||
try {
|
||||
Singleton[] enumConstants = Singleton.class.getEnumConstants();
|
||||
for (Singleton enumConstant : enumConstants) {
|
||||
System.out.println(enumConstant.getObjName());
|
||||
}
|
||||
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor(String.class, int.class);
|
||||
constructor.setAccessible(true);
|
||||
constructor.newInstance("INSTANCE2", 1);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.out.println(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -176,7 +177,7 @@ public enum Singleton {
|
||||
firstName
|
||||
secondName
|
||||
secondName
|
||||
secondName
|
||||
java.lang.IllegalArgumentException: Cannot reflectively create enum objects
|
||||
```
|
||||
|
||||
该实现可以防止反射攻击。在其它实现中,通过 setAccessible() 方法可以将私有构造函数的访问级别设置为 public,然后调用构造函数从而实例化对象,如果要防止这种攻击,需要在构造函数中添加防止多次实例化的代码。该实现是由 JVM 保证只会实例化一次,因此不会出现上述的反射攻击。
|
||||
@@ -192,7 +193,7 @@ secondName
|
||||
### JDK 中的应用
|
||||
- [java.lang.Runtime#getRuntime()](http://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#getRuntime%28%29)
|
||||
- [java.awt.Desktop#getDesktop()](http://docs.oracle.com/javase/8/docs/api/java/awt/Desktop.html#getDesktop--)
|
||||
- [java.lang.System#getSecurityManager()](https://docs.oracle.com/javase/8/docs/api/java/lang/System.html#getSecurityManager--)
|
||||
- [java.lang.System#getSecurityManager()](https://docs.oracle.com/javase/8/docs/api/java/lang/System.html#getSecurityManager--)(JDK 17 起已废弃,JEP 411)
|
||||
|
||||
## 参考资料
|
||||
|
||||
|
||||
+1
-1
@@ -73,7 +73,7 @@ ExtrinsicState: y
|
||||
```
|
||||
|
||||
### JDK 中的应用
|
||||
Java 利用缓存来加速大量小对象的访问时间。
|
||||
Java 利用缓存复用大量的小对象,减少对象的创建和内存占用。
|
||||
|
||||
- java.lang.Integer#valueOf(int)
|
||||
- java.lang.Boolean#valueOf(boolean)
|
||||
|
||||
+1
-1
@@ -87,7 +87,7 @@ public class ImageViewer {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String image = "http://image.jpg";
|
||||
URL url = new URL(image);
|
||||
URL url = URI.create(image).toURL();
|
||||
HighResolutionImage highResolutionImage = new HighResolutionImage(url);
|
||||
ImageProxy imageProxy = new ImageProxy(highResolutionImage);
|
||||
imageProxy.showImage();
|
||||
|
||||
+1
-1
@@ -119,7 +119,7 @@ public class Client {
|
||||
|
||||
### JDK 中的应用
|
||||
- [java.lang.Runnable](http://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html)
|
||||
- [Netflix Hystrix](https://github.com/Netflix/Hystrix/wiki)
|
||||
- [Netflix Hystrix](https://github.com/Netflix/Hystrix/wiki)(开源库,不属于 JDK)
|
||||
- [javax.swing.Action](http://docs.oracle.com/javase/8/docs/api/javax/swing/Action.html)
|
||||
|
||||
## 参考资料
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
## 备忘录(Memento)
|
||||
|
||||
### 意图
|
||||
在不违反封装的情况下获得对象的内部状态,从而在需要时可以将对象恢复到最初状态。
|
||||
在不违反封装的情况下获得对象的内部状态,从而在需要时可以将对象恢复到之前保存的状态。
|
||||
|
||||
### 类图
|
||||
- Originator:原始对象
|
||||
|
||||
@@ -52,7 +52,6 @@ public class ConcreteFactory2 extends Factory {
|
||||
- [java.nio.charset.Charset](https://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html#forName-java.lang.String-)
|
||||
- [java.net.URLStreamHandlerFactory](http://docs.oracle.com/javase/8/docs/api/java/net/URLStreamHandlerFactory.html#createURLStreamHandler-java.lang.String-)
|
||||
- [java.util.EnumSet](https://docs.oracle.com/javase/8/docs/api/java/util/EnumSet.html#of-E-)
|
||||
- [javax.xml.bind.JAXBContext](https://docs.oracle.com/javase/8/docs/api/javax/xml/bind/JAXBContext.html#createMarshaller--)
|
||||
|
||||
## 参考资料
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ Tea.addCondiments
|
||||
### JDK 中的应用
|
||||
- java.util.Collections#sort()
|
||||
- java.io.InputStream#skip()
|
||||
- java.io.InputStream#read()
|
||||
- java.io.InputStream#read(byte[], int, int)
|
||||
- java.util.AbstractList#indexOf()
|
||||
|
||||
## 参考资料
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
以下是一个简易的 StringBuilder 实现,参考了 JDK 1.8 源码。
|
||||
|
||||
```java
|
||||
import java.util.Arrays;
|
||||
|
||||
public class AbstractStringBuilder {
|
||||
protected char[] value;
|
||||
|
||||
|
||||
+1
-1
@@ -80,7 +80,7 @@ public class Leaf extends Component {
|
||||
|
||||
@Override
|
||||
public void add(Component component) {
|
||||
throw new UnsupportedOperationException(); // 牺牲透明性换取单一职责原则,这样就不用考虑是叶子节点还是组合节点
|
||||
throw new UnsupportedOperationException(); // 牺牲单一职责原则换取透明性,这样就不用考虑是叶子节点还是组合节点
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+3
-3
@@ -124,10 +124,10 @@ StatisticsDisplay.update: 1.0 1.0 1.0
|
||||
```
|
||||
|
||||
### JDK 中的应用
|
||||
- [java.util.Observer](http://docs.oracle.com/javase/8/docs/api/java/util/Observer.html)
|
||||
- [java.util.Observer](http://docs.oracle.com/javase/8/docs/api/java/util/Observer.html)(JDK 9 起已废弃)
|
||||
- [java.util.EventListener](http://docs.oracle.com/javase/8/docs/api/java/util/EventListener.html)
|
||||
- [javax.servlet.http.HttpSessionBindingListener](http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpSessionBindingListener.html)
|
||||
- [RxJava](https://github.com/ReactiveX/RxJava)
|
||||
- [javax.servlet.http.HttpSessionBindingListener](http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpSessionBindingListener.html)(Servlet API,不属于 JDK)
|
||||
- [RxJava](https://github.com/ReactiveX/RxJava)(开源库,不属于 JDK)
|
||||
|
||||
## 参考资料
|
||||
|
||||
|
||||
+5
-5
@@ -12,7 +12,7 @@
|
||||
### 实现
|
||||
以下是一个规则检验器实现,具有 and 和 or 规则,通过规则可以构建一棵解析树,用来检验一个文本是否满足解析树定义的规则。
|
||||
|
||||
例如一棵解析树为 D And (A Or (B C)),文本 "D A" 满足该解析树定义的规则。
|
||||
例如一棵解析树为 D And (A Or (B Or C)),文本 "D A" 满足该解析树定义的规则。
|
||||
|
||||
这里的 Context 指的是 String。
|
||||
|
||||
@@ -89,11 +89,11 @@ public class Client {
|
||||
Expression terminal2 = new TerminalExpression("B");
|
||||
Expression terminal3 = new TerminalExpression("C");
|
||||
Expression terminal4 = new TerminalExpression("D");
|
||||
// B C
|
||||
// B Or C
|
||||
Expression alternation1 = new OrExpression(terminal2, terminal3);
|
||||
// A Or (B C)
|
||||
// A Or (B Or C)
|
||||
Expression alternation2 = new OrExpression(terminal1, alternation1);
|
||||
// D And (A Or (B C))
|
||||
// D And (A Or (B Or C))
|
||||
return new AndExpression(terminal4, alternation2);
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ false
|
||||
- [java.util.Pattern](http://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html)
|
||||
- [java.text.Normalizer](http://docs.oracle.com/javase/8/docs/api/java/text/Normalizer.html)
|
||||
- All subclasses of [java.text.Format](http://docs.oracle.com/javase/8/docs/api/java/text/Format.html)
|
||||
- [javax.el.ELResolver](http://docs.oracle.com/javaee/7/api/javax/el/ELResolver.html)
|
||||
- [javax.el.ELResolver](http://docs.oracle.com/javaee/7/api/javax/el/ELResolver.html)(EL API,不属于 JDK)
|
||||
|
||||
## 参考资料
|
||||
|
||||
|
||||
+2
-2
@@ -121,8 +121,8 @@ request2 is handle by ConcreteHandler2
|
||||
|
||||
### JDK 中的应用
|
||||
- [java.util.logging.Logger#log()](http://docs.oracle.com/javase/8/docs/api/java/util/logging/Logger.html#log%28java.util.logging.Level,%20java.lang.String%29)
|
||||
- [Apache Commons Chain](https://commons.apache.org/dormant/commons-chain/)
|
||||
- [javax.servlet.Filter#doFilter()](http://docs.oracle.com/javaee/7/api/javax/servlet/Filter.html#doFilter-javax.servlet.ServletRequest-javax.servlet.ServletResponse-javax.servlet.FilterChain-)
|
||||
- [Apache Commons Chain](https://commons.apache.org/dormant/commons-chain/)(开源库,不属于 JDK)
|
||||
- [javax.servlet.Filter#doFilter()](http://docs.oracle.com/javaee/7/api/javax/servlet/Filter.html#doFilter-javax.servlet.ServletRequest-javax.servlet.ServletResponse-javax.servlet.FilterChain-)(Servlet API,不属于 JDK)
|
||||
|
||||
## 参考资料
|
||||
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
### 类图
|
||||
- Aggregate 是聚合类,其中 createIterator() 方法可以产生一个 Iterator;
|
||||
- Iterator 主要定义了 hasNext() 和 next() 方法;
|
||||
- Client 组合了 Aggregate,为了迭代遍历 Aggregate,也需要组合 Iterator。
|
||||
- Client 依赖 Aggregate,为了迭代遍历 Aggregate,也需要依赖 Iterator。
|
||||
|
||||
<div align="center"> <img src="pics/dp-iterator.png" width="760px"/> </div><br>
|
||||
|
||||
|
||||
@@ -63,7 +63,6 @@ public class Client {
|
||||
- [java.util.Arrays#asList()](http://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#asList%28T...%29)
|
||||
- [java.util.Collections#list()](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#list-java.util.Enumeration-)
|
||||
- [java.util.Collections#enumeration()](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#enumeration-java.util.Collection-)
|
||||
- [javax.xml.bind.annotation.adapters.XMLAdapter](http://docs.oracle.com/javase/8/docs/api/javax/xml/bind/annotation/adapters/XmlAdapter.html#marshal-BoundType-)
|
||||
|
||||
## 参考资料
|
||||
|
||||
|
||||
+2
-2
@@ -149,7 +149,7 @@ HTTP 重定向负载均衡服务器使用某种负载均衡算法计算得到服
|
||||
|
||||
#### 4. 网络层
|
||||
|
||||
在操作系统内核进程获取网络数据包,根据负载均衡算法计算源服务器的 IP 地址,并修改请求数据包的目的 IP 地址,最后进行转发。
|
||||
在操作系统内核中获取网络数据包,根据负载均衡算法计算源服务器的 IP 地址,并修改请求数据包的目的 IP 地址,最后进行转发。
|
||||
|
||||
源服务器返回的响应也需要经过负载均衡服务器,通常是让负载均衡服务器同时作为集群的网关服务器来实现。对应 LVS 的 NAT 模式。
|
||||
|
||||
@@ -169,7 +169,7 @@ HTTP 重定向负载均衡服务器使用某种负载均衡算法计算得到服
|
||||
|
||||
这是一种三角传输模式,被称为直接路由,对应 LVS 的 DR 模式,要求源服务器和负载均衡服务器位于同一个局域网内。对于提供下载和视频服务的网站来说,直接路由避免了大量的网络传输数据经过负载均衡服务器。
|
||||
|
||||
网络层和链路层的方式不解析应用层协议,属于四层负载均衡。链路层是目前大型网站使用最广的负载均衡转发方式,在 Linux 平台可以使用的负载均衡服务器为 LVS(Linux Virtual Server),通常用 LVS 做四层负载均衡,再用 Nginx 做七层负载均衡。
|
||||
网络层和链路层的方式不解析应用层协议,属于四层负载均衡。链路层负载均衡的响应不经过负载均衡服务器,性能很好,在 Linux 平台可以使用的负载均衡服务器为 LVS(Linux Virtual Server),通常用 LVS 做四层负载均衡,再用 Nginx 做七层负载均衡。
|
||||
|
||||
参考:
|
||||
|
||||
|
||||
+4
-4
@@ -246,11 +246,11 @@ School "1" - "*" Student
|
||||
|
||||
### 依赖关系 (Dependency)
|
||||
|
||||
和关联关系不同的是,依赖关系是在运行过程中起作用的。A 类和 B 类是依赖关系主要有三种形式:
|
||||
和关联关系不同的是,依赖关系是在运行过程中起作用的。A 类依赖 B 类主要有三种形式:
|
||||
|
||||
- A 类是 B 类方法的局部变量;
|
||||
- A 类是 B 类方法的参数;
|
||||
- A 类向 B 类发送消息,从而影响 B 类发生变化。
|
||||
- B 类是 A 类方法中的局部变量;
|
||||
- B 类是 A 类方法的参数;
|
||||
- A 类向 B 类发送消息(调用 B 类的方法),从而影响 B 类发生变化。
|
||||
|
||||
<div align="center"> <img src="pics/uml-dependency.png" width="640px"/> </div><br>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user