From 5e35b50b2e6387a7d27f1d17c8f376eab08ab584 Mon Sep 17 00:00:00 2001
From: "Mr.Hope"
Date: Fri, 5 May 2023 12:19:35 +0800
Subject: [PATCH 1/7] chore: tweaks
---
README.md | 6 +++---
docs/.vuepress/config.ts | 4 ++--
.../feelings-of-half-a-year-from-graduation-to-entry.md | 8 ++++----
docs/about-the-author/zhishixingqiu-two-years.md | 2 +-
docs/books/cs-basics.md | 2 +-
docs/books/java.md | 2 +-
docs/home.md | 6 +++---
docs/readme.md | 2 +-
8 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/README.md b/README.md
index da72e9e40cb..6df0f66bb2a 100755
--- a/README.md
+++ b/README.md
@@ -414,10 +414,10 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle
### 灾备设计和异地多活
-**灾备** = 容灾+备份。
+**灾备** = 容灾 + 备份。
-- **备份** : 将系统所产生的的所有重要数据多备份几份。
-- **容灾** : 在异地建立两个完全相同的系统。当某个地方的系统突然挂掉,整个应用系统可以切换到另一个,这样系统就可以正常提供服务了。
+- **备份**:将系统所产生的的所有重要数据多备份几份。
+- **容灾**:在异地建立两个完全相同的系统。当某个地方的系统突然挂掉,整个应用系统可以切换到另一个,这样系统就可以正常提供服务了。
**异地多活** 描述的是将服务部署在异地并且服务同时对外提供服务。和传统的灾备设计的最主要区别在于“多活”,即所有站点都是同时在对外提供服务的。异地多活是为了应对突发状况比如火灾、地震等自然或者人为灾害。
diff --git a/docs/.vuepress/config.ts b/docs/.vuepress/config.ts
index 9a8553bd00d..e15c9b107e7 100644
--- a/docs/.vuepress/config.ts
+++ b/docs/.vuepress/config.ts
@@ -6,9 +6,9 @@ import theme from "./theme.js";
export default defineUserConfig({
dest: "./dist",
- title: "JavaGuide(Java面试+学习指南)",
+ title: "JavaGuide(Java面试 + 学习指南)",
description:
- "「Java学习指北+Java面试指南」一份涵盖大部分 Java 程序员所需要掌握的核心知识。准备 Java 面试,复习 Java 知识点,首选 JavaGuide! ",
+ "「Java学习指北 + Java面试指南」一份涵盖大部分 Java 程序员所需要掌握的核心知识。准备 Java 面试,复习 Java 知识点,首选 JavaGuide! ",
head: [
// meta
diff --git a/docs/about-the-author/feelings-of-half-a-year-from-graduation-to-entry.md b/docs/about-the-author/feelings-of-half-a-year-from-graduation-to-entry.md
index 1852f446973..9c3997b3636 100644
--- a/docs/about-the-author/feelings-of-half-a-year-from-graduation-to-entry.md
+++ b/docs/about-the-author/feelings-of-half-a-year-from-graduation-to-entry.md
@@ -9,7 +9,7 @@ tag:
简单说一下自己的情况吧!我目前是在一家外企,每天的工作和大部分人一样就是做开发。毕业到现在,差不多也算是工作半年多了,也已经过了公司 6 个月的试用期。目前在公司做过两个偏向于业务方向的项目,其中一个正在做。你很难想象我在公司做的两个业务项目的后端都没有涉及到分布式/微服务,没有接触到 Redis、Kafka 等等比较“高大上”的技术在项目中的实际运用。
-第一个项目做的是公司的内部项目——员工成长系统。抛去员工成长系统这个名字,实际上这个系统做的就是绩效考核比如你在某个项目组的表现。这个项目的技术是 Spring Boot+ JPA+Spring Security + K8S+Docker+React。第二个目前正在做的是一个集成游戏(cocos)、Web 管理端(Spring Boot+Vue)和小程序(Taro)项目。
+第一个项目做的是公司的内部项目——员工成长系统。抛去员工成长系统这个名字,实际上这个系统做的就是绩效考核比如你在某个项目组的表现。这个项目的技术是 Spring Boot+ JPA + Spring Security + K8S + Docker + React。第二个目前正在做的是一个集成游戏(cocos)、Web 管理端(Spring Boot + Vue)和小程序(Taro)项目。
是的,我在工作中的大部分时间都和 CRUD 有关,每天也会写前端页面。之前我认识的一个朋友 ,他听说我做的项目中大部分内容都是写业务代码之后就非常纳闷,他觉得单纯写业务代码得不到提升?what?你一个应届生,连业务代码都写不好你给我说这个!所以,**我就很纳闷不知道为什么现在很多连业务代码都写不好的人为什么人听到 CRUD 就会反感?至少我觉得在我工作这段时间我的代码质量得到了提升、定位问题的能力有了很大的改进、对于业务有了更深的认识,自己也可以独立完成一些前端的开发了。**
@@ -19,9 +19,9 @@ tag:
**实话实说,我在大学的时候就陷入过这个“伪命题”中**。在大学的时候,我大二因为加入了一个学校的偏技术方向的校媒才接触到 Java ,当时我们学习 Java 的目的就是开发一个校园通。 大二的时候,编程相当于才入门水平的我才接触 Java,花了一段时间才掌握 Java 基础。然后,就开始学习安卓开发。
-到了大三上学期,我才真正确定要走 Java 后台的方向,找 Java 后台的开发工作。学习了 3 个月左右的 WEB 开发基础之后,我就开始学习分布式方面内容比如 Redis、Dubbo 这些。我当时是通过看书+视频+博客的方式学习的,自学过程中通过看视频自己做过两个完整的项目,一个普通的业务系统,一个是分布式的系统。**我当时以为自己做完之后就很牛逼了,我觉得普通的 CRUD 工作已经不符合我当前的水平了。哈哈!现在看来,当时的我过于哈皮!**
+到了大三上学期,我才真正确定要走 Java 后台的方向,找 Java 后台的开发工作。学习了 3 个月左右的 WEB 开发基础之后,我就开始学习分布式方面内容比如 Redis、Dubbo 这些。我当时是通过看书 + 视频 + 博客的方式学习的,自学过程中通过看视频自己做过两个完整的项目,一个普通的业务系统,一个是分布式的系统。**我当时以为自己做完之后就很牛逼了,我觉得普通的 CRUD 工作已经不符合我当前的水平了。哈哈!现在看来,当时的我过于哈皮!**
-这不!到了大三暑假跟着老师一起做项目的时候就出问题了。大三的时候,我们跟着老师做的是一个绩效考核系统,业务复杂程度中等。这个项目的技术用的是:SSM+Shiro+JSP。当时,做这个项目的时候我遇到各种问题,各种我以为我会写的代码都不会写了,甚至我写一个简单的 CRUD 都要花费好几天的时间。所以,那时候我都是边复习边学习边写代码。虽然很累,但是,那时候学到了很多,也让我在技术面前变得更加踏实。我觉得这“**这个项目已经没有维护的可能性**”这句话是我对我过的这个项目最大的否定了。
+这不!到了大三暑假跟着老师一起做项目的时候就出问题了。大三的时候,我们跟着老师做的是一个绩效考核系统,业务复杂程度中等。这个项目的技术用的是:SSM + Shiro + JSP。当时,做这个项目的时候我遇到各种问题,各种我以为我会写的代码都不会写了,甚至我写一个简单的 CRUD 都要花费好几天的时间。所以,那时候我都是边复习边学习边写代码。虽然很累,但是,那时候学到了很多,也让我在技术面前变得更加踏实。我觉得这“**这个项目已经没有维护的可能性**”这句话是我对我过的这个项目最大的否定了。
技术千变万化,掌握最核心的才是王道。我们前几年可能还在用 Spring 基于传统的 XML 开发,现在几乎大家都会用 Spring Boot 这个开发利器来提升开发速度,再比如几年前我们使用消息队列可能还在用 ActiveMQ,到今天几乎都没有人用它了,现在比较常用的就是 Rocket MQ、Kafka 。技术更新换代这么快的今天,你是无法把每一个框架/工具都学习一遍的, 。
@@ -31,7 +31,7 @@ tag:
不知道其他公司的程序员是怎么样的?我感觉技术积累很大程度在乎平时,单纯依靠工作绝大部分情况只会加快自己做需求的熟练度,当然,写多了之后或多或少也会提升你对代码质量的认识(前提是你有这个意识)。
-工作之余,我会利用业余时间来学习自己想学的东西。工作中的例子就是我刚进公司的第一个项目用到了 Spring Security+JWT ,因为当时自己对于这个技术不太了解,然后就在工作之外大概花了一周的时间学习写了一个 Demo 分享了出来,Github 地址: 。以次为契机,我还分享了
+工作之余,我会利用业余时间来学习自己想学的东西。工作中的例子就是我刚进公司的第一个项目用到了 Spring Security + JWT ,因为当时自己对于这个技术不太了解,然后就在工作之外大概花了一周的时间学习写了一个 Demo 分享了出来,Github 地址: 。以次为契机,我还分享了
- [《一问带你区分清楚 Authentication,Authorization 以及 Cookie、Session、Token》](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247485626&idx=1&sn=3247aa9000693dd692de8a04ccffeec1&chksm=cea24771f9d5ce675ea0203633a95b68bfe412dc6a9d05f22d221161147b76161d1b470d54b3&token=684071313&lang=zh_CN&scene=21#wechat_redirect)
- [JWT 身份认证优缺点分析以及常见问题解决方案](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247485655&idx=1&sn=583eeeb081ea21a8ec6347c72aa223d6&chksm=cea2471cf9d5ce0aa135f2fb9aa32d98ebb3338292beaccc1aae43d1178b16c0125eb4139ca4&token=1737409938&lang=zh_CN#rd)
diff --git a/docs/about-the-author/zhishixingqiu-two-years.md b/docs/about-the-author/zhishixingqiu-two-years.md
index 46f0e8b84de..de0692ae86d 100644
--- a/docs/about-the-author/zhishixingqiu-two-years.md
+++ b/docs/about-the-author/zhishixingqiu-two-years.md
@@ -12,7 +12,7 @@ star: 2

-截止到今天,星球已经有 1.3w+的同学加入。虽然比不上很多大佬,但这于我来说也算是小有成就了,真的很满足了!我确信自己是一个普通人,能做成这些,也不过是在兴趣和运气的加持下赶上了时代而已。
+截止到今天,星球已经有 1.3w+ 的同学加入。虽然比不上很多大佬,但这于我来说也算是小有成就了,真的很满足了!我确信自己是一个普通人,能做成这些,也不过是在兴趣和运气的加持下赶上了时代而已。
**我有自己的原则,不割韭菜,用心做内容,真心希望帮助到他人!**
diff --git a/docs/books/cs-basics.md b/docs/books/cs-basics.md
index f8aa1514c61..71fed7be3e9 100644
--- a/docs/books/cs-basics.md
+++ b/docs/books/cs-basics.md
@@ -99,7 +99,7 @@ Github 上就有一些名校的计算机网络试验/Project:

-**2、[王道考研的计算机网络](https://www.bilibili.com/video/BV19E411D78Q?from=search&seid=17198507506906312317)** :非常适合 CS 专业考研的小朋友!这个视频目前在哔哩哔哩上已经有 1.6w+的点赞。
+**2、[王道考研的计算机网络](https://www.bilibili.com/video/BV19E411D78Q?from=search&seid=17198507506906312317)** :非常适合 CS 专业考研的小朋友!这个视频目前在哔哩哔哩上已经有 1.6w+ 的点赞。

diff --git a/docs/books/java.md b/docs/books/java.md
index 20cfdfe32dc..f5c0cde6ea5 100644
--- a/docs/books/java.md
+++ b/docs/books/java.md
@@ -18,7 +18,7 @@ icon: "java"
我个人觉得这本书还是挺适合编程新手阅读的,毕竟是 “Head First” 系列。
-**[《Java 核心技术卷 1+卷 2》](https://book.douban.com/subject/34898994/)**
+**[《Java 核心技术卷 1 + 卷 2》](https://book.douban.com/subject/34898994/)**

diff --git a/docs/home.md b/docs/home.md
index 8be07e9149e..d20c4e8f71b 100644
--- a/docs/home.md
+++ b/docs/home.md
@@ -405,10 +405,10 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8](https://docs.oracle.
### 灾备设计和异地多活
-**灾备** = 容灾+备份。
+**灾备** = 容灾 + 备份。
-- **备份** : 将系统所产生的的所有重要数据多备份几份。
-- **容灾** : 在异地建立两个完全相同的系统。当某个地方的系统突然挂掉,整个应用系统可以切换到另一个,这样系统就可以正常提供服务了。
+- **备份**:将系统所产生的的所有重要数据多备份几份。
+- **容灾**:在异地建立两个完全相同的系统。当某个地方的系统突然挂掉,整个应用系统可以切换到另一个,这样系统就可以正常提供服务了。
**异地多活** 描述的是将服务部署在异地并且服务同时对外提供服务。和传统的灾备设计的最主要区别在于“多活”,即所有站点都是同时在对外提供服务的。异地多活是为了应对突发状况比如火灾、地震等自然或者人为灾害。
diff --git a/docs/readme.md b/docs/readme.md
index 0d05c36956a..a40752c0f3e 100644
--- a/docs/readme.md
+++ b/docs/readme.md
@@ -4,7 +4,7 @@ icon: home
title: Java 面试指南
heroImage: /logo.svg
heroText: JavaGuide
-tagline: 「Java学习+面试指南」一份涵盖大部分 Java 程序员所需要掌握的核心知识。准备 Java 面试,首选 JavaGuide!
+tagline: 「Java学习 + 面试指南」一份涵盖大部分 Java 程序员所需要掌握的核心知识。准备 Java 面试,首选 JavaGuide!
actions:
- text: 开始阅读
link: /home/
From 6eb17d9492b640af3c7cd6f76cb2c000e5aa933f Mon Sep 17 00:00:00 2001
From: "Mr.Hope"
Date: Fri, 5 May 2023 12:32:21 +0800
Subject: [PATCH 2/7] chore: tweaks
---
...feelings-after-one-month-of-induction-training.md | 8 ++++----
...elings-of-half-a-year-from-graduation-to-entry.md | 12 ++++++------
docs/about-the-author/javaguide-100k-star.md | 4 ++--
docs/about-the-author/my-college-life.md | 2 +-
4 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/docs/about-the-author/feelings-after-one-month-of-induction-training.md b/docs/about-the-author/feelings-after-one-month-of-induction-training.md
index 773bd98455c..8ecb6eac455 100644
--- a/docs/about-the-author/feelings-after-one-month-of-induction-training.md
+++ b/docs/about-the-author/feelings-after-one-month-of-induction-training.md
@@ -11,14 +11,14 @@ tag:
不得不说 ThoughtWorks 的培训机制还是很不错的。应届生入职之后一般都会安排培训,与往年不同的是,今年的培训多了中国本地班(TWU-C)。作为本地班的第一期学员,说句心里话还是很不错。8 周的培训,除了工作需要用到的基本技术比如 ES6、SpringBoot 等等之外,还会增加一些新员工基本技能的培训比如如何高效开会、如何给别人正确的提 Feedback、如何对代码进行重构、如何进行 TDD 等等。培训期间不定期的有活动,比如 Weekend Trip、 City Tour、Cake time 等等。最后三周还会有一个实际的模拟项目,这个项目基本和我们正式工作的实际项目差不多,我个人感觉很不错。目前这个项目已经正式完成了一个迭代,我觉得在做项目的过程中,收获最大的不是项目中使用的技术,而是如何进行团队合作、如何正确使用 Git 团队协同开发、一个完成的迭代是什么样子的、做项目的过程中可能遇到那些问题、一个项目运作的完整流程等等。
-ThoughtWorks 非常提倡分享、提倡帮助他人成长,这一点在公司的这段时间深有感触。培训期间,我们每个人会有一个 Trainer 负责,Trainer 就是日常带我们上课和做项目的同事,一个 Trainer 大概会负责 5-6 个人。Trainer 不定期都会给我们最近表现的 Feedback( 反馈) ,我个人觉得这个并不是这是走走形式,Trainer 们都很负责,很多时候都是在下班之后找我们聊天。同事们也都很热心,如果你遇到问题,向别人询问,其他人如果知道的话一般都会毫无保留的告诉你,如果遇到大部分都不懂的问题,甚至会组织一次技术 Session 分享。上周五我在我们小组内进行了一次关于 Feign 远程调用的技术分享,因为 team 里面大家对这部分知识都不太熟悉,但是后面的项目进展大概率会用到这部分知识。我刚好研究了这部分内容,所以就分享给了组内的其他同事,以便于项目更好的进行。
+ThoughtWorks 非常提倡分享、提倡帮助他人成长,这一点在公司的这段时间深有感触。培训期间,我们每个人会有一个 Trainer 负责,Trainer 就是日常带我们上课和做项目的同事,一个 Trainer 大概会负责 5 - 6 个人。Trainer 不定期都会给我们最近表现的 Feedback (反馈) ,我个人觉得这个并不是这是走走形式,Trainer 们都很负责,很多时候都是在下班之后找我们聊天。同事们也都很热心,如果你遇到问题,向别人询问,其他人如果知道的话一般都会毫无保留的告诉你,如果遇到大部分都不懂的问题,甚至会组织一次技术 Session 分享。上周五我在我们小组内进行了一次关于 Feign 远程调用的技术分享,因为 team 里面大家对这部分知识都不太熟悉,但是后面的项目进展大概率会用到这部分知识。我刚好研究了这部分内容,所以就分享给了组内的其他同事,以便于项目更好的进行。
-另外,ThoughtWorks 也是一家非常提倡 Feedback( 反馈) 文化的公司,反馈是告诉人们我们对他们的表现的看法以及他们应该如何更好地做到这一点。刚开始我并没有太在意,慢慢地自己确实感觉到正确的进行反馈对他人会有很大的帮助。因为人在做很多事情的时候,会很难发现别人很容易看到的一些小问题。就比如一个很有趣的现象一样,假如我们在做项目的时候没有测试这个角色,如果你完成了自己的模块,并且自己对这个模块测试了很多遍,你发现已经没啥问题了。但是,到了实际使用的时候会很大概率出现你之前从来没有注意的问题。解释这个问题的说法是:每个人的视野或多或少都是有盲点的,这与我们的关注点息息相关。对于自己做的东西,很多地方自己测试很多遍都不会发现,但是如果让其他人帮你进行测试的话,就很大可能会发现很多显而易见的问题。
+另外,ThoughtWorks 也是一家非常提倡 Feedback (反馈) 文化的公司,反馈是告诉人们我们对他们的表现的看法以及他们应该如何更好地做到这一点。刚开始我并没有太在意,慢慢地自己确实感觉到正确的进行反馈对他人会有很大的帮助。因为人在做很多事情的时候,会很难发现别人很容易看到的一些小问题。就比如一个很有趣的现象一样,假如我们在做项目的时候没有测试这个角色,如果你完成了自己的模块,并且自己对这个模块测试了很多遍,你发现已经没啥问题了。但是,到了实际使用的时候会很大概率出现你之前从来没有注意的问题。解释这个问题的说法是:每个人的视野或多或少都是有盲点的,这与我们的关注点息息相关。对于自己做的东西,很多地方自己测试很多遍都不会发现,但是如果让其他人帮你进行测试的话,就很大可能会发现很多显而易见的问题。

-工作之后,平时更新公众号、专栏还有维护 Github 的时间变少了。实际上,很多时候下班回来后,都有自己的时间来干自己的事情,但是自己也总是找工作太累或者时间比较零散的接口来推掉了。到了今天,翻看 Github 突然发现 14 天前别人在 Github 上给我提的 pr 我还没有处理。这一点确实是自己没有做好的地方,没有合理安排好自己的时间。实际上自己有很多想写的东西,后面会慢慢将他们提上日程。工作之后,更加发现下班后的几个小时如何度过确实很重要 ,如果你觉得自己没有完成好自己白天该做的工作的话,下班后你可以继续忙白天没有忙完的工作,如果白天的工作对于你游刃有余的话,下班回来之后,你大可去干自己感兴趣的事情,学习自己感兴趣的技术。做任何事情都要基于自身的基础,切不可好高骛远。
+工作之后,平时更新公众号、专栏还有维护 Github 的时间变少了。实际上,很多时候下班回来后,都有自己的时间来干自己的事情,但是自己也总是找工作太累或者时间比较零散的接口来推掉了。到了今天,翻看 Github 突然发现 14 天前别人在 Github 上给我提的 PR 我还没有处理。这一点确实是自己没有做好的地方,没有合理安排好自己的时间。实际上自己有很多想写的东西,后面会慢慢将他们提上日程。工作之后,更加发现下班后的几个小时如何度过确实很重要 ,如果你觉得自己没有完成好自己白天该做的工作的话,下班后你可以继续忙白天没有忙完的工作,如果白天的工作对于你游刃有余的话,下班回来之后,你大可去干自己感兴趣的事情,学习自己感兴趣的技术。做任何事情都要基于自身的基础,切不可好高骛远。
-工作之后身边也会有很多厉害的人,多从他人身上学习我觉得是每个职场人都应该做的。这一届和我们一起培训的同事中,有一些技术很厉害的,也有一些技术虽然不是那么厉害,但是组织能力以及团队协作能力特别厉害的。有一个特别厉害的同事,在我们还在学 SpringBoot 各种语法的时候,他自己利用业余时间写了一个简化版的 SpringBoot ,涵盖了 Spring 的一些常用注解比如 `@RestController`、`@Autowried`、`@Pathvairable`、`@RestquestParam`等等(已经联系这位同事,想让他开源一下,后面会第一时间同步到公众号,期待一下吧!)。我觉得这位同事对于编程是真的有兴趣,他好像从初中就开始接触编程了,对于各种底层知识也非常感兴趣,自己写过实现过很多比较底层的东西。他的梦想是在 Github 上造一个 20k Star 以上的轮子。我相信以这位同事的能力一定会达成目标的,在这里祝福这位同事,希望他可以尽快实现这个目标。
+工作之后身边也会有很多厉害的人,多从他人身上学习我觉得是每个职场人都应该做的。这一届和我们一起培训的同事中,有一些技术很厉害的,也有一些技术虽然不是那么厉害,但是组织能力以及团队协作能力特别厉害的。有一个特别厉害的同事,在我们还在学 SpringBoot 各种语法的时候,他自己利用业余时间写了一个简化版的 SpringBoot ,涵盖了 Spring 的一些常用注解比如 `@RestController`、`@Autowried`、`@Pathvairable`、`@RestquestParam`等等(已经联系这位同事,想让他开源一下,后面会第一时间同步到公众号,期待一下吧!)。我觉得这位同事对于编程是真的有兴趣,他好像从初中就开始接触编程了,对于各种底层知识也非常感兴趣,自己写过实现过很多比较底层的东西。他的梦想是在 Github 上造一个 20k Star 以上的轮子。我相信以这位同事的能力一定会达成目标的,在这里祝福这位同事,希望他可以尽快实现这个目标。
这是我入职一个多月之后的个人感受,很多地方都是一带而过,后面我会抽时间分享自己在公司或者业余学到的比较有用的知识给各位,希望看过的人都能有所收获。
diff --git a/docs/about-the-author/feelings-of-half-a-year-from-graduation-to-entry.md b/docs/about-the-author/feelings-of-half-a-year-from-graduation-to-entry.md
index 9c3997b3636..043cee54d6f 100644
--- a/docs/about-the-author/feelings-of-half-a-year-from-graduation-to-entry.md
+++ b/docs/about-the-author/feelings-of-half-a-year-from-graduation-to-entry.md
@@ -5,17 +5,17 @@ tag:
- 个人经历
---
-如果大家看过我之前的介绍的话,就会知道我是 19 年毕业的几百万应届毕业生中的一员。这篇文章主要讲了一下我入职大半年的感受,文中有很多自己的主观感受,如果你们有任何不认同的地方都可以直接在评论区说出来, 会很尊重其他人的想法。
+如果大家看过我之前的介绍的话,就会知道我是 19 年毕业的几百万应届毕业生中的一员。这篇文章主要讲了一下我入职大半年的感受,文中有很多自己的主观感受,如果你们有任何不认同的地方都可以直接在评论区说出来,会很尊重其他人的想法。
简单说一下自己的情况吧!我目前是在一家外企,每天的工作和大部分人一样就是做开发。毕业到现在,差不多也算是工作半年多了,也已经过了公司 6 个月的试用期。目前在公司做过两个偏向于业务方向的项目,其中一个正在做。你很难想象我在公司做的两个业务项目的后端都没有涉及到分布式/微服务,没有接触到 Redis、Kafka 等等比较“高大上”的技术在项目中的实际运用。
-第一个项目做的是公司的内部项目——员工成长系统。抛去员工成长系统这个名字,实际上这个系统做的就是绩效考核比如你在某个项目组的表现。这个项目的技术是 Spring Boot+ JPA + Spring Security + K8S + Docker + React。第二个目前正在做的是一个集成游戏(cocos)、Web 管理端(Spring Boot + Vue)和小程序(Taro)项目。
+第一个项目做的是公司的内部项目——员工成长系统。抛去员工成长系统这个名字,实际上这个系统做的就是绩效考核比如你在某个项目组的表现。这个项目的技术是 Spring Boot+ JPA + Spring Security + K8S + Docker + React。第二个目前正在做的是一个集成游戏 (cocos)、Web 管理端 (Spring Boot + Vue) 和小程序 (Taro) 项目。
是的,我在工作中的大部分时间都和 CRUD 有关,每天也会写前端页面。之前我认识的一个朋友 ,他听说我做的项目中大部分内容都是写业务代码之后就非常纳闷,他觉得单纯写业务代码得不到提升?what?你一个应届生,连业务代码都写不好你给我说这个!所以,**我就很纳闷不知道为什么现在很多连业务代码都写不好的人为什么人听到 CRUD 就会反感?至少我觉得在我工作这段时间我的代码质量得到了提升、定位问题的能力有了很大的改进、对于业务有了更深的认识,自己也可以独立完成一些前端的开发了。**
其实,我个人觉得能把业务代码写好也没那么容易,抱怨自己天天做 CRUD 工作之前,看看自己 CRUD 的代码写好没。再换句话说,单纯写 CRUD 的过程中你搞懂了哪些你常用的注解或者类吗?这就像一个只会 `@Service`、`@Autowired`、`@RestController`等等最简单的注解的人说我已经掌握了 Spring Boot 一样。
-不知道什么时候开始大家都会觉得有实际使用 Redis、MQ 的经验就很牛逼了, 这可能和当前的面试环境有关系。你需要和别人有差异,你想进大厂的话,好像就必须要这些技术比较在行,好吧,没有好像,自信点来说对于大部分求职者这些技术都是默认你必备的了。
+不知道什么时候开始大家都会觉得有实际使用 Redis、MQ 的经验就很牛逼了,这可能和当前的面试环境有关系。你需要和别人有差异,你想进大厂的话,好像就必须要这些技术比较在行,好吧,没有好像,自信点来说对于大部分求职者这些技术都是默认你必备的了。
**实话实说,我在大学的时候就陷入过这个“伪命题”中**。在大学的时候,我大二因为加入了一个学校的偏技术方向的校媒才接触到 Java ,当时我们学习 Java 的目的就是开发一个校园通。 大二的时候,编程相当于才入门水平的我才接触 Java,花了一段时间才掌握 Java 基础。然后,就开始学习安卓开发。
@@ -23,7 +23,7 @@ tag:
这不!到了大三暑假跟着老师一起做项目的时候就出问题了。大三的时候,我们跟着老师做的是一个绩效考核系统,业务复杂程度中等。这个项目的技术用的是:SSM + Shiro + JSP。当时,做这个项目的时候我遇到各种问题,各种我以为我会写的代码都不会写了,甚至我写一个简单的 CRUD 都要花费好几天的时间。所以,那时候我都是边复习边学习边写代码。虽然很累,但是,那时候学到了很多,也让我在技术面前变得更加踏实。我觉得这“**这个项目已经没有维护的可能性**”这句话是我对我过的这个项目最大的否定了。
-技术千变万化,掌握最核心的才是王道。我们前几年可能还在用 Spring 基于传统的 XML 开发,现在几乎大家都会用 Spring Boot 这个开发利器来提升开发速度,再比如几年前我们使用消息队列可能还在用 ActiveMQ,到今天几乎都没有人用它了,现在比较常用的就是 Rocket MQ、Kafka 。技术更新换代这么快的今天,你是无法把每一个框架/工具都学习一遍的, 。
+技术千变万化,掌握最核心的才是王道。我们前几年可能还在用 Spring 基于传统的 XML 开发,现在几乎大家都会用 Spring Boot 这个开发利器来提升开发速度,再比如几年前我们使用消息队列可能还在用 ActiveMQ,到今天几乎都没有人用它了,现在比较常用的就是 Rocket MQ、Kafka 。技术更新换代这么快的今天,你是无法把每一个框架/工具都学习一遍的。
**很多初学者上来就想通过做项目学习,特别是在公司,我觉得这个是不太可取的。** 如果的 Java 基础或者 Spring Boot 基础不好的话,建议自己先提前学习一下之后再开始看视频或者通过其他方式做项目。 **还有一点就是,我不知道为什么大家都会说边跟着项目边学习做的话效果最好,我觉得这个要加一个前提是你对这门技术有基本的了解或者说你对编程有了一定的了解。**
@@ -31,9 +31,9 @@ tag:
不知道其他公司的程序员是怎么样的?我感觉技术积累很大程度在乎平时,单纯依靠工作绝大部分情况只会加快自己做需求的熟练度,当然,写多了之后或多或少也会提升你对代码质量的认识(前提是你有这个意识)。
-工作之余,我会利用业余时间来学习自己想学的东西。工作中的例子就是我刚进公司的第一个项目用到了 Spring Security + JWT ,因为当时自己对于这个技术不太了解,然后就在工作之外大概花了一周的时间学习写了一个 Demo 分享了出来,Github 地址: 。以次为契机,我还分享了
+工作之余,我会利用业余时间来学习自己想学的东西。工作中的例子就是我刚进公司的第一个项目用到了 Spring Security + JWT ,因为当时自己对于这个技术不太了解,然后就在工作之外大概花了一周的时间学习写了一个 Demo 分享了出来,Github 地址: 。以次为契机,我还分享了
-- [《一问带你区分清楚 Authentication,Authorization 以及 Cookie、Session、Token》](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247485626&idx=1&sn=3247aa9000693dd692de8a04ccffeec1&chksm=cea24771f9d5ce675ea0203633a95b68bfe412dc6a9d05f22d221161147b76161d1b470d54b3&token=684071313&lang=zh_CN&scene=21#wechat_redirect)
+- [《一问带你区分清楚 Authentication、Authorization 以及 Cookie、Session、Token》](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247485626&idx=1&sn=3247aa9000693dd692de8a04ccffeec1&chksm=cea24771f9d5ce675ea0203633a95b68bfe412dc6a9d05f22d221161147b76161d1b470d54b3&token=684071313&lang=zh_CN&scene=21#wechat_redirect)
- [JWT 身份认证优缺点分析以及常见问题解决方案](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247485655&idx=1&sn=583eeeb081ea21a8ec6347c72aa223d6&chksm=cea2471cf9d5ce0aa135f2fb9aa32d98ebb3338292beaccc1aae43d1178b16c0125eb4139ca4&token=1737409938&lang=zh_CN#rd)
另外一个最近的例子是因为肺炎疫情在家的这段时间,自学了 Kafka,并且正在准备写一系列的入门文章,目前已经完成了:
diff --git a/docs/about-the-author/javaguide-100k-star.md b/docs/about-the-author/javaguide-100k-star.md
index 0523aaf5c0a..f657e26d7d1 100644
--- a/docs/about-the-author/javaguide-100k-star.md
+++ b/docs/about-the-author/javaguide-100k-star.md
@@ -29,11 +29,11 @@ tag:

-累计有 **511** 个 **issue** 和 **575** 个 **pr**。所有的 pr 都已经被处理,仅有 15 个左右的 issue 我还未抽出时间处理。
+累计有 **511** 个 **issue** 和 **575** 个 **PR**。所有的 PR 都已经被处理,仅有 15 个左右的 issue 我还未抽出时间处理。

-其实,相比于 star 数量,你看看仓库的 issue 和 pr 更能说明你的项目是否有价值。
+其实,相比于 star 数量,你看看仓库的 issue 和 PR 更能说明你的项目是否有价值。
那些到处骗 star 甚至是 刷 star 的行为,我就不多说了,有点丢人。人家觉得你的项目还不错,能提供价值,自然就给你点 star 了。
diff --git a/docs/about-the-author/my-college-life.md b/docs/about-the-author/my-college-life.md
index 8431b50c7b8..6d7e5ccd9c8 100644
--- a/docs/about-the-author/my-college-life.md
+++ b/docs/about-the-author/my-college-life.md
@@ -6,7 +6,7 @@ tag:
- 个人经历
---
-> 关于初高中的生活,可以看 2020 年我写的[我曾经也是网瘾少年](https://javaguide.cn/about-the-author/internet-addiction-teenager.html)这篇文章。
+> 关于初高中的生活,可以看 2020 年我写的 [我曾经也是网瘾少年](./internet-addiction-teenager.md) 这篇文章。
2019 年 6 月份毕业,距今已经过去了 3 年。趁着高考以及应届生毕业之际,简单聊聊自己的大学生活。
From 1614aaa47f588662d42884f6f6499660da6c1637 Mon Sep 17 00:00:00 2001
From: "Mr.Hope"
Date: Fri, 5 May 2023 12:33:52 +0800
Subject: [PATCH 3/7] chore: tweaks
---
.../internet-addiction-teenager.md | 2 +-
docs/about-the-author/my-college-life.md | 2 +-
.../zhishixingqiu-two-years.md | 2 +-
docs/books/cs-basics.md | 16 +++----
docs/books/database.md | 2 +-
docs/books/java.md | 4 +-
docs/books/software-quality.md | 6 +--
.../10-classical-sorting-algorithms.md | 2 +-
.../algorithms/the-sword-refers-to-offer.md | 2 +-
docs/cs-basics/data-structure/bloom-filter.md | 6 +--
.../data-structure/linear-data-structure.md | 6 +--
.../computer-network-xiexiren-summary.md | 8 ++--
docs/cs-basics/network/http-status-codes.md | 14 +++---
docs/cs-basics/network/http-vs-https.md | 2 +-
.../cs-basics/network/network-attack-means.md | 2 +-
.../cs-basics/network/osi-and-tcp-ip-model.md | 2 +-
.../network/other-network-questions.md | 6 +--
.../network/other-network-questions2.md | 8 ++--
.../tcp-connection-and-disconnection.md | 2 +-
.../network/tcp-reliability-guarantee.md | 2 +-
.../cs-basics/operating-system/linux-intro.md | 40 ++++++++--------
.../operating-system-basic-questions-01.md | 4 +-
.../operating-system-basic-questions-02.md | 4 +-
.../cs-basics/operating-system/shell-intro.md | 2 +-
docs/database/basis.md | 10 ++--
docs/database/character-set.md | 8 ++--
docs/database/mongodb/mongodb-questions-01.md | 10 ++--
docs/database/mongodb/mongodb-questions-02.md | 4 +-
.../mysql/how-sql-executed-in-mysql.md | 2 +-
...alidation-caused-by-implicit-conversion.md | 2 +-
.../mysql/innodb-implementation-of-mvcc.md | 2 +-
...imization-specification-recommendations.md | 2 +-
docs/database/mysql/mysql-index.md | 2 +-
docs/database/mysql/mysql-logs.md | 2 +-
docs/database/mysql/mysql-questions-01.md | 28 +++++------
.../some-thoughts-on-database-storage-time.md | 2 +-
.../mysql/transaction-isolation-level.md | 10 ++--
docs/database/nosql.md | 4 +-
.../redis-common-blocking-problems-summary.md | 2 +-
.../redis/redis-data-structures-01.md | 16 +++----
docs/database/redis/redis-persistence.md | 2 +-
docs/database/redis/redis-questions-01.md | 20 ++++----
docs/database/redis/redis-questions-02.md | 14 +++---
docs/database/sql/sql-syntax-summary.md | 4 +-
docs/distributed-system/api-gateway.md | 20 ++++----
docs/distributed-system/distributed-id.md | 28 +++++------
docs/distributed-system/distributed-lock.md | 10 ++--
.../zookeeper/zookeeper-in-action.md | 2 +-
.../zookeeper/zookeeper-intro.md | 16 +++----
.../zookeeper/zookeeper-plus.md | 12 ++---
.../protocol/cap-and-base-theorem.md | 4 +-
.../protocol/gossip-protocl.md | 4 +-
.../protocol/paxos-algorithm.md | 10 ++--
docs/distributed-system/rpc/dubbo.md | 10 ++--
docs/distributed-system/rpc/rpc-intro.md | 4 +-
.../spring-cloud-gateway-questions.md | 8 ++--
docs/high-availability/performance-test.md | 2 +-
.../message-queue/disruptor-questions.md | 16 +++----
.../message-queue/kafka-questions-01.md | 6 +--
.../message-queue/message-queue.md | 10 ++--
.../message-queue/rabbitmq-questions.md | 2 +-
.../message-queue/rocketmq-questions.md | 42 ++++++++--------
...d-write-separation-and-library-subtable.md | 2 +-
.../20-bad-habits-of-bad-programmers.md | 2 +-
...wth-strategy-of-the-technological-giant.md | 4 +-
...rammers-in-the-first-test-of-technology.md | 10 ++--
.../my-personal-experience-in-2021.md | 2 +-
.../screen-candidates-for-packaging.md | 2 +-
.../some-secrets-about-alibaba-interview.md | 2 +-
.../summary-of-spring-recruitment.md | 4 +-
.../technical-preliminary-preparation.md | 4 +-
...view-experienced-by-an-older-programmer.md | 4 +-
...of-get-offer-from-over-20-big-companies.md | 2 +-
...develop--experience-in-didi-and-toutiao.md | 2 +-
.../work/employee-performance.md | 2 +-
.../key-points-of-interview.md | 2 +-
.../project-experience-guide.md | 4 +-
docs/interview-preparation/resume-guide.md | 12 ++---
docs/java/basis/java-basic-questions-01.md | 26 +++++-----
docs/java/basis/java-basic-questions-02.md | 14 +++---
docs/java/basis/java-basic-questions-03.md | 20 ++++----
docs/java/basis/java-keyword-summary.md | 8 ++--
docs/java/basis/reflection.md | 2 +-
docs/java/basis/serialization.md | 4 +-
docs/java/basis/syntactic-sugar.md | 4 +-
docs/java/basis/unsafe.md | 4 +-
docs/java/collection/arraylist-source-code.md | 2 +-
.../java-collection-precautions-for-use.md | 2 +-
.../java-collection-questions-01.md | 14 +++---
.../java-collection-questions-02.md | 4 +-
.../concurrent/completablefuture-intro.md | 4 +-
.../concurrent/java-concurrent-collections.md | 2 +-
.../java-concurrent-questions-01.md | 8 ++--
.../java-concurrent-questions-02.md | 6 +--
.../java-concurrent-questions-03.md | 4 +-
.../java-thread-pool-best-practices.md | 4 +-
.../concurrent/java-thread-pool-summary.md | 8 ++--
docs/java/concurrent/jmm.md | 6 +--
docs/java/concurrent/reentrantlock.md | 4 +-
docs/java/io/io-basis.md | 48 +++++++++----------
docs/java/io/io-model.md | 6 +--
docs/java/jvm/class-loading-process.md | 8 ++--
docs/java/jvm/classloader.md | 6 +--
docs/java/jvm/jvm-parameters-intro.md | 6 +--
docs/java/jvm/memory-area.md | 12 ++---
docs/java/new-features/java11.md | 4 +-
docs/java/new-features/java12-13.md | 2 +-
docs/java/new-features/java14-15.md | 2 +-
docs/java/new-features/java19.md | 2 +-
.../new-features/java8-common-new-features.md | 2 +-
.../new-features/java8-tutorial-translate.md | 8 ++--
docs/java/new-features/java9.md | 4 +-
docs/javaguide/history.md | 2 +-
docs/open-source-project/big-data.md | 2 +-
docs/open-source-project/machine-learning.md | 2 +-
docs/open-source-project/practical-project.md | 10 ++--
docs/open-source-project/system-design.md | 16 +++----
docs/open-source-project/tool-library.md | 2 +-
docs/open-source-project/tools.md | 6 +--
docs/open-source-project/tutorial.md | 4 +-
docs/snippets/planet.snippet.md | 2 +-
docs/system-design/basis/naming.md | 6 +--
docs/system-design/basis/refactoring.md | 6 +--
.../basis/software-engineering.md | 4 +-
.../framework/mybatis/mybatis-interview.md | 8 ++--
.../spring-boot-auto-assembly-principles.md | 2 +-
.../spring/spring-common-annotations.md | 8 ++--
.../spring/spring-design-patterns-summary.md | 2 +-
.../spring-knowledge-and-questions-summary.md | 18 +++----
.../framework/spring/spring-transaction.md | 18 +++----
docs/system-design/schedule-task.md | 14 +++---
.../basis-of-authority-certification.md | 2 +-
docs/system-design/security/jwt-intro.md | 4 +-
docs/system-design/security/sso-intro.md | 2 +-
docs/tools/docker/docker-intro.md | 6 +--
docs/tools/git/git-intro.md | 2 +-
docs/tools/git/github-tips.md | 2 +-
docs/tools/gradle/gradle-core-concepts.md | 16 +++----
docs/tools/maven/maven-core-concepts.md | 6 +--
docs/zhuanlan/handwritten-rpc-framework.md | 2 +-
docs/zhuanlan/java-mian-shi-zhi-bei.md | 2 +-
docs/zhuanlan/readme.md | 4 +-
docs/zhuanlan/source-code-reading.md | 4 +-
143 files changed, 501 insertions(+), 501 deletions(-)
diff --git a/docs/about-the-author/internet-addiction-teenager.md b/docs/about-the-author/internet-addiction-teenager.md
index 7bead099c8f..20024ff9976 100644
--- a/docs/about-the-author/internet-addiction-teenager.md
+++ b/docs/about-the-author/internet-addiction-teenager.md
@@ -125,7 +125,7 @@ QQ 飞车这款戏当时还挺火的,很多 90 后的小伙伴应该比较熟
大学生活过的还是挺丰富的,我会偶尔通宵敲代码,也会偶尔半夜发疯跑出去和同学一起走走古城墙、去网吧锤一夜的 LOL。
-大学生活专门写过一篇文章介绍: [害,毕业三年了!](https://javaguide.cn/about-the-author/my-college-life.html) 。
+大学生活专门写过一篇文章介绍:[害,毕业三年了!](https://javaguide.cn/about-the-author/my-college-life.html) 。
## 总结
diff --git a/docs/about-the-author/my-college-life.md b/docs/about-the-author/my-college-life.md
index 6d7e5ccd9c8..a24e899eea4 100644
--- a/docs/about-the-author/my-college-life.md
+++ b/docs/about-the-author/my-college-life.md
@@ -38,7 +38,7 @@ tag:
我不爱出风头,性格有点内向。刚上大学那会,内心还是有一点不自信,干什么事情都畏畏缩缩,还是迫切希望改变自己的!
-于是,凭借着一腔热血,我尝试了很多我之前从未尝试过的事情:**露营**、**户外烧烤**、**公交车演讲**、**环跑古城墙**、**徒步旅行**、**异地求生**、**圣诞节卖苹果** 、**元旦晚会演出**...。
+于是,凭借着一腔热血,我尝试了很多我之前从未尝试过的事情:**露营**、**户外烧烤**、**公交车演讲**、**环跑古城墙**、**徒步旅行**、**异地求生**、**圣诞节卖苹果**、**元旦晚会演出**...。
下面这些都是我和社团的小伙伴利用课外时间自己做的,在圣诞节那周基本都卖完了。我记得,为了能够多卖一些,我们还挨个去每一个寝室推销了一遍。
diff --git a/docs/about-the-author/zhishixingqiu-two-years.md b/docs/about-the-author/zhishixingqiu-two-years.md
index de0692ae86d..c4441fb7d6a 100644
--- a/docs/about-the-author/zhishixingqiu-two-years.md
+++ b/docs/about-the-author/zhishixingqiu-two-years.md
@@ -40,7 +40,7 @@ star: 2
### 专属专栏
-星球更新了 **《Java 面试指北》**、**《Java 必读源码系列》**(目前已经整理了 Dubbo 2.6.x 、Netty 4.x、SpringBoot2.1 的源码)、 **《从零开始写一个 RPC 框架》**(已更新完) 、**《Kafka 常见面试题/知识点总结》** 等多个优质专栏。
+星球更新了 **《Java 面试指北》**、**《Java 必读源码系列》**(目前已经整理了 Dubbo 2.6.x、Netty 4.x、SpringBoot2.1 的源码)、 **《从零开始写一个 RPC 框架》**(已更新完)、**《Kafka 常见面试题/知识点总结》** 等多个优质专栏。

diff --git a/docs/books/cs-basics.md b/docs/books/cs-basics.md
index 71fed7be3e9..c65b9c23a93 100644
--- a/docs/books/cs-basics.md
+++ b/docs/books/cs-basics.md
@@ -36,12 +36,12 @@ head:
其他相关书籍推荐:
-- **[《自己动手写操作系统》](https://book.douban.com/subject/1422377/)** : 不光会带着你详细分析操作系统原理的基础,还会用丰富的实例代码,一步一步地指导你用 C 语言和汇编语言编写出一个具备操作系统基本功能的操作系统框架。
-- **[《现代操作系统》](https://book.douban.com/subject/3852290/)** : 内容很不错,不过,翻译的一般。如果你是精读本书的话,建议把课后习题都做了。
-- **[《操作系统真象还原》](https://book.douban.com/subject/26745156/)** : 这本书的作者毕业于北京大学,前百度运维高级工程师。因为在大学期间曾重修操作系统这一科,后对操作系统进行深入研究,著下此书。
+- **[《自己动手写操作系统》](https://book.douban.com/subject/1422377/)** :不光会带着你详细分析操作系统原理的基础,还会用丰富的实例代码,一步一步地指导你用 C 语言和汇编语言编写出一个具备操作系统基本功能的操作系统框架。
+- **[《现代操作系统》](https://book.douban.com/subject/3852290/)** :内容很不错,不过,翻译的一般。如果你是精读本书的话,建议把课后习题都做了。
+- **[《操作系统真象还原》](https://book.douban.com/subject/26745156/)** :这本书的作者毕业于北京大学,前百度运维高级工程师。因为在大学期间曾重修操作系统这一科,后对操作系统进行深入研究,著下此书。
- **[《深度探索 Linux 操作系统》](https://book.douban.com/subject/25743846/)** :跟着这本书的内容走,可以让你对如何制作一套完善的 GNU/Linux 系统有了清晰的认识。
- **[《操作系统设计与实现》](https://book.douban.com/subject/2044818/)** :操作系统的权威教学教材。
-- **[《Orange'S:一个操作系统的实现》](https://book.douban.com/subject/3735649/)** : 从只有二十行的引导扇区代码出发,一步一步地向读者呈现一个操作系统框架的完成过程。配合《操作系统设计与实现》一起食用更佳!
+- **[《Orange'S:一个操作系统的实现》](https://book.douban.com/subject/3735649/)** :从只有二十行的引导扇区代码出发,一步一步地向读者呈现一个操作系统框架的完成过程。配合《操作系统设计与实现》一起食用更佳!
如果你比较喜欢看视频的话,推荐哈工大李治军老师主讲的慕课 [《操作系统》](https://www.icourse163.org/course/HIT-1002531008),内容质量吊打一众国家精品课程。
@@ -49,7 +49,7 @@ head:

-主要讲了一个基本操作系统中的六个基本模块: CPU 管理、内存管理、外设管理、磁盘管理与文件系统、用户接口和启动模块 。
+主要讲了一个基本操作系统中的六个基本模块:CPU 管理、内存管理、外设管理、磁盘管理与文件系统、用户接口和启动模块 。
课程难度还是比较大的,尤其是课后的 lab。如果大家想要真正搞懂操作系统底层原理的话,对应的 lab 能做尽量做一下。正如李治军老师说的那样:“纸上得来终觉浅,绝知此事要躬行”。
@@ -79,7 +79,7 @@ head:
如果你觉得上面这本书看着比较枯燥的话,我强烈推荐+安利你看看下面这两本非常有趣的网络相关的书籍:
-- [《图解 HTTP》](https://book.douban.com/subject/25863515/ "《图解 HTTP》") : 讲漫画一样的讲 HTTP,很有意思,不会觉得枯燥,大概也涵盖也 HTTP 常见的知识点。因为篇幅问题,内容可能不太全面。不过,如果不是专门做网络方向研究的小伙伴想研究 HTTP 相关知识的话,读这本书的话应该来说就差不多了。
+- [《图解 HTTP》](https://book.douban.com/subject/25863515/ "《图解 HTTP》") :讲漫画一样的讲 HTTP,很有意思,不会觉得枯燥,大概也涵盖也 HTTP 常见的知识点。因为篇幅问题,内容可能不太全面。不过,如果不是专门做网络方向研究的小伙伴想研究 HTTP 相关知识的话,读这本书的话应该来说就差不多了。
- [《网络是怎样连接的》](https://book.douban.com/subject/26941639/ "《网络是怎样连接的》") :从在浏览器中输入网址开始,一路追踪了到显示出网页内容为止的整个过程,以图配文,讲解了网络的全貌,并重点介绍了实际的网络设备和软件是如何工作的。

@@ -185,7 +185,7 @@ Github 上就有一些名校的计算机网络试验/Project:
质量很高,介绍了常用的数据结构和算法。
-类似的还有 **[《数据结构与算法分析 :C 语言描述》](https://book.douban.com/subject/1139426/)** 、**[《数据结构与算法分析:C++ 描述》](https://book.douban.com/subject/1971825/)**
+类似的还有 **[《数据结构与算法分析 :C 语言描述》](https://book.douban.com/subject/1139426/)**、**[《数据结构与算法分析:C++ 描述》](https://book.douban.com/subject/1971825/)**

@@ -267,7 +267,7 @@ Github 上就有一些名校的计算机网络试验/Project:
其他书籍推荐:
- **[《现代编译原理》](https://book.douban.com/subject/30191414/)** :编译原理的入门书。
-- **[《编译器设计》](https://book.douban.com/subject/20436488/)** : 覆盖了编译器从前端到后端的全部主题。
+- **[《编译器设计》](https://book.douban.com/subject/20436488/)** :覆盖了编译器从前端到后端的全部主题。
我上面推荐的书籍的难度还是比较高的,真心很难坚持看完。这里强烈推荐[哈工大的编译原理视频课程](https://www.icourse163.org/course/HIT-1002123007),真心不错,还是国家精品课程,关键还是又漂亮有温柔的美女老师讲的!
diff --git a/docs/books/database.md b/docs/books/database.md
index 03254aa075d..4f2b8733985 100644
--- a/docs/books/database.md
+++ b/docs/books/database.md
@@ -55,7 +55,7 @@ Github 上也已经有大佬用 Java 实现过一个简易的数据库,介绍
一般企业项目开发中,使用 MySQL 比较多。如果你要学习 MySQL 的话,可以看下面这 3 本书籍:
- **[《MySQL 必知必会》](https://book.douban.com/subject/3354490/)** :非常薄!非常适合 MySQL 新手阅读,很棒的入门教材。
-- **[《高性能 MySQL》](https://book.douban.com/subject/23008813/)** : MySQL 领域的经典之作!学习 MySQL 必看!属于进阶内容,主要教你如何更好地使用 MySQL 。既有有理论,又有实践!如果你没时间都看一遍的话,我建议第 5 章(创建高性能的索引) 、第 6 章(查询性能优化) 你一定要认真看一下。
+- **[《高性能 MySQL》](https://book.douban.com/subject/23008813/)** :MySQL 领域的经典之作!学习 MySQL 必看!属于进阶内容,主要教你如何更好地使用 MySQL 。既有有理论,又有实践!如果你没时间都看一遍的话,我建议第 5 章(创建高性能的索引)、第 6 章(查询性能优化) 你一定要认真看一下。
- **[《MySQL 技术内幕》](https://book.douban.com/subject/24708143/)** :你想深入了解 MySQL 存储引擎的话,看这本书准没错!

diff --git a/docs/books/java.md b/docs/books/java.md
index f5c0cde6ea5..06b7a07843b 100644
--- a/docs/books/java.md
+++ b/docs/books/java.md
@@ -125,7 +125,7 @@ _这本书还是非常适合我们用来学习 Java 多线程的。这本书的
- **IDEA** :熟悉基本操作以及常用快捷。你可以通过 Github 上的开源教程 [《IntelliJ IDEA 简体中文专题教程》](https://github.com/judasn/IntelliJ-IDEA-Tutorial) 来学习 IDEA 的使用。
- **Maven** :强烈建议学习常用框架之前可以提前花几天时间学习一下**Maven**的使用。(到处找 Jar 包,下载 Jar 包是真的麻烦费事,使用 Maven 可以为你省很多事情)。
- **Git** :基本的 Git 技能也是必备的,试着在学习的过程中将自己的代码托管在 Github 上。你可以看看这篇 Github 上开源的 [《Git 极简入门》](https://snailclimb.gitee.io/javaguide/#/docs/tools/Git) 。
-- **Docker** :学着用 Docker 安装学习中需要用到的软件比如 MySQL ,这样方便很多,可以为你节省不少时间。你可以看看这篇 Github 上开源的 [《Docker 基本概念解读》](https://snailclimb.gitee.io/javaguide/#/docs/tools/Docker) 、[《一文搞懂 Docker 镜像的常用操作!》](https://snailclimb.gitee.io/javaguide/#/docs/tools/Docker-Image)
+- **Docker** :学着用 Docker 安装学习中需要用到的软件比如 MySQL ,这样方便很多,可以为你节省不少时间。你可以看看这篇 Github 上开源的 [《Docker 基本概念解读》](https://snailclimb.gitee.io/javaguide/#/docs/tools/Docker)、[《一文搞懂 Docker 镜像的常用操作!》](https://snailclimb.gitee.io/javaguide/#/docs/tools/Docker-Image)
除了这些工具之外,我强烈建议你一定要搞懂 Github 的使用。一些使用 Github 的小技巧,你可以看[《Github 小技巧》](https://snailclimb.gitee.io/javaguide/#/docs/tools/Github%E6%8A%80%E5%B7%A7)这篇文章。
@@ -177,7 +177,7 @@ SpringBoot 解析,不适合初学者。我是去年入手的,现在就看了

-这本书可以用来入门 Netty ,内容从 BIO 聊到了 NIO、之后才详细介绍为什么有 Netty 、Netty 为什么好用以及 Netty 重要的知识点讲解。
+这本书可以用来入门 Netty ,内容从 BIO 聊到了 NIO、之后才详细介绍为什么有 Netty、Netty 为什么好用以及 Netty 重要的知识点讲解。
这本书基本把 Netty 一些重要的知识点都介绍到了,而且基本都是通过实战的形式讲解。
diff --git a/docs/books/software-quality.md b/docs/books/software-quality.md
index 2d04d7f0ffc..7978b919967 100644
--- a/docs/books/software-quality.md
+++ b/docs/books/software-quality.md
@@ -121,11 +121,11 @@ Bob 大叔将自己对整洁代码的理解浓缩在了这本书中,真可谓
## 其他
- [《代码的未来》](https://book.douban.com/subject/24536403/) :这本书的作者是 Ruby 之父松本行弘,算是一本年代比较久远的书籍(13 年出版),不过,还是非常值得一读。这本书的内容主要介绍是编程/编程语言的本质。我个人还是比较喜欢松本行弘的文字风格,并且,你看他的文章也确实能够有所收获。
-- [《深入浅出设计模式》](https://book.douban.com/subject/1488876/) : 比较有趣的风格,适合设计模式入门。
-- [《软件架构设计:大型网站技术架构与业务架构融合之道》](https://book.douban.com/subject/30443578/) : 内容非常全面。适合面试前突击一些比较重要的理论知识,也适合拿来扩充/完善自己的技术广度。
+- [《深入浅出设计模式》](https://book.douban.com/subject/1488876/) :比较有趣的风格,适合设计模式入门。
+- [《软件架构设计:大型网站技术架构与业务架构融合之道》](https://book.douban.com/subject/30443578/) :内容非常全面。适合面试前突击一些比较重要的理论知识,也适合拿来扩充/完善自己的技术广度。
- [《微服务架构设计模式》](https://book.douban.com/subject/33425123/) :这本书是世界十大软件架构师之一、微服务架构先驱 Chris Richardson 亲笔撰写,豆瓣评分 9.6。示例代码使用 Java 语言和 Spring 框架。帮助你设计、实现、测试和部署基于微服务的应用程序。
最后再推荐两个相关的文档:
- **阿里巴巴 Java 开发手册** :
-- **Google Java 编程风格指南**:
+- **Google Java 编程风格指南**:
diff --git a/docs/cs-basics/algorithms/10-classical-sorting-algorithms.md b/docs/cs-basics/algorithms/10-classical-sorting-algorithms.md
index 6b0b0ef58eb..3fd96dea1d4 100644
--- a/docs/cs-basics/algorithms/10-classical-sorting-algorithms.md
+++ b/docs/cs-basics/algorithms/10-classical-sorting-algorithms.md
@@ -37,7 +37,7 @@ tag:
- **不稳定**:如果 A 原本在 B 的前面,而 A=B,排序之后 A 可能会出现在 B 的后面。
- **内排序**:所有排序操作都在内存中完成。
- **外排序**:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行。
-- **时间复杂度**: 定性描述一个算法执行所耗费的时间。
+- **时间复杂度**:定性描述一个算法执行所耗费的时间。
- **空间复杂度**:定性描述一个算法执行所需内存的大小。
### 算法分类
diff --git a/docs/cs-basics/algorithms/the-sword-refers-to-offer.md b/docs/cs-basics/algorithms/the-sword-refers-to-offer.md
index e9eeeb08d20..182bc13863a 100644
--- a/docs/cs-basics/algorithms/the-sword-refers-to-offer.md
+++ b/docs/cs-basics/algorithms/the-sword-refers-to-offer.md
@@ -227,7 +227,7 @@ public String replaceSpace(StringBuffer str) {
**问题解析:**
这道题算是比较麻烦和难一点的一个了。我这里采用的是**二分幂**思想,当然也可以采用**快速幂**。
-更具剑指 offer 书中细节,该题的解题思路如下: 1.当底数为 0 且指数<0 时,会出现对 0 求倒数的情况,需进行错误处理,设置一个全局变量; 2.判断底数是否等于 0,由于 base 为 double 型,所以不能直接用==判断 3.优化求幂函数(二分幂)。
+更具剑指 offer 书中细节,该题的解题思路如下:1.当底数为 0 且指数<0 时,会出现对 0 求倒数的情况,需进行错误处理,设置一个全局变量; 2.判断底数是否等于 0,由于 base 为 double 型,所以不能直接用==判断 3.优化求幂函数(二分幂)。
当 n 为偶数,a^n =(a^n/2)_(a^n/2);
当 n 为奇数,a^n = a^[(n-1)/2] _ a^[(n-1)/2] \* a。时间复杂度 O(logn)
diff --git a/docs/cs-basics/data-structure/bloom-filter.md b/docs/cs-basics/data-structure/bloom-filter.md
index 301d58a749b..a9255ddabca 100644
--- a/docs/cs-basics/data-structure/bloom-filter.md
+++ b/docs/cs-basics/data-structure/bloom-filter.md
@@ -20,7 +20,7 @@ tag:
首先,我们需要了解布隆过滤器的概念。
-布隆过滤器(Bloom Filter)是一个叫做 Bloom 的老哥于 1970 年提出的。我们可以把它看作由二进制向量(或者说位数组)和一系列随机映射函数(哈希函数)两部分组成的数据结构。相比于我们平时常用的的 List、Map 、Set 等数据结构,它占用空间更少并且效率更高,但是缺点是其返回的结果是概率性的,而不是非常准确的。理论情况下添加到集合中的元素越多,误报的可能性就越大。并且,存放在布隆过滤器的数据不容易删除。
+布隆过滤器(Bloom Filter)是一个叫做 Bloom 的老哥于 1970 年提出的。我们可以把它看作由二进制向量(或者说位数组)和一系列随机映射函数(哈希函数)两部分组成的数据结构。相比于我们平时常用的的 List、Map、Set 等数据结构,它占用空间更少并且效率更高,但是缺点是其返回的结果是概率性的,而不是非常准确的。理论情况下添加到集合中的元素越多,误报的可能性就越大。并且,存放在布隆过滤器的数据不容易删除。

@@ -265,12 +265,12 @@ root@21396d02c252:/data# redis-cli
### 常用命令一览
-> 注意: key : 布隆过滤器的名称,item : 添加的元素。
+> 注意:key : 布隆过滤器的名称,item : 添加的元素。
1. **`BF.ADD`**:将元素添加到布隆过滤器中,如果该过滤器尚不存在,则创建该过滤器。格式:`BF.ADD {key} {item}`。
2. **`BF.MADD`** : 将一个或多个元素添加到“布隆过滤器”中,并创建一个尚不存在的过滤器。该命令的操作方式`BF.ADD`与之相同,只不过它允许多个输入并返回多个值。格式:`BF.MADD {key} {item} [item ...]` 。
3. **`BF.EXISTS`** : 确定元素是否在布隆过滤器中存在。格式:`BF.EXISTS {key} {item}`。
-4. **`BF.MEXISTS`** : 确定一个或者多个元素是否在布隆过滤器中存在格式:`BF.MEXISTS {key} {item} [item ...]`。
+4. **`BF.MEXISTS`** :确定一个或者多个元素是否在布隆过滤器中存在格式:`BF.MEXISTS {key} {item} [item ...]`。
另外, `BF. RESERVE` 命令需要单独介绍一下:
diff --git a/docs/cs-basics/data-structure/linear-data-structure.md b/docs/cs-basics/data-structure/linear-data-structure.md
index 41d552c6234..ac73f354105 100644
--- a/docs/cs-basics/data-structure/linear-data-structure.md
+++ b/docs/cs-basics/data-structure/linear-data-structure.md
@@ -118,7 +118,7 @@ tag:
> 1. 左括号必须用相同类型的右括号闭合。
> 2. 左括号必须以正确的顺序闭合。
>
-> 比如 "()"、"()[]{}"、"{[]}" 都是有效字符串,而 "(]" 、"([)]" 则不是。
+> 比如 "()"、"()[]{}"、"{[]}" 都是有效字符串,而 "(]"、"([)]" 则不是。
这个问题实际是 Leetcode 的一道题目,我们可以利用栈 `Stack` 来解决这个问题。
@@ -258,7 +258,7 @@ myStack.pop();//报错:java.lang.IllegalArgumentException: Stack is empty.
### 4.1. 队列简介
-**队列(Queue)** 是 **先进先出( FIFO,First In, First Out)** 的线性表。在具体应用中通常用链表或者数组来实现,用数组实现的队列叫作 **顺序队列** ,用链表实现的队列叫作 **链式队列** 。**队列只允许在后端(rear)进行插入操作也就是 入队 enqueue,在前端(front)进行删除操作也就是出队 dequeue**
+**队列(Queue)** 是 **先进先出 (FIFO,First In, First Out)** 的线性表。在具体应用中通常用链表或者数组来实现,用数组实现的队列叫作 **顺序队列** ,用链表实现的队列叫作 **链式队列** 。**队列只允许在后端(rear)进行插入操作也就是入队 enqueue,在前端(front)进行删除操作也就是出队 dequeue**
队列的操作方式和堆栈类似,唯一的区别在于队列只允许新数据在后端进行添加。
@@ -295,7 +295,7 @@ myStack.pop();//报错:java.lang.IllegalArgumentException: Stack is empty.
顺序队列中,我们说 `front==rear` 的时候队列为空,循环队列中则不一样,也可能为满,如上图所示。解决办法有两种:
1. 可以设置一个标志变量 `flag`,当 `front==rear` 并且 `flag=0` 的时候队列为空,当`front==rear` 并且 `flag=1` 的时候队列为满。
-2. 队列为空的时候就是 `front==rear` ,队列满的时候,我们保证数组还有一个空闲的位置,rear 就指向这个空闲位置,如下图所示,那么现在判断队列是否为满的条件就是: `(rear+1) % QueueSize= front` 。
+2. 队列为空的时候就是 `front==rear` ,队列满的时候,我们保证数组还有一个空闲的位置,rear 就指向这个空闲位置,如下图所示,那么现在判断队列是否为满的条件就是:`(rear+1) % QueueSize= front` 。
### 4.3. 常见应用场景
diff --git a/docs/cs-basics/network/computer-network-xiexiren-summary.md b/docs/cs-basics/network/computer-network-xiexiren-summary.md
index 5b039b823c1..7ee06b57c75 100644
--- a/docs/cs-basics/network/computer-network-xiexiren-summary.md
+++ b/docs/cs-basics/network/computer-network-xiexiren-summary.md
@@ -22,7 +22,7 @@ tag:

-5. **IXP(Internet eXchange Point)** : 互联网交换点 IXP 的主要作用就是允许两个网络直接相连并交换分组,而不需要再通过第三个网络来转发分组。
+5. **IXP(Internet eXchange Point)** :互联网交换点 IXP 的主要作用就是允许两个网络直接相连并交换分组,而不需要再通过第三个网络来转发分组。

@@ -31,7 +31,7 @@ tag:
6. **RFC(Request For Comments)** :意思是“请求评议”,包含了关于 Internet 几乎所有的重要的文字资料。
7. **广域网 WAN(Wide Area Network)** :任务是通过长距离运送主机发送的数据。
8. **城域网 MAN(Metropolitan Area Network)**:用来将多个局域网进行互连。
-9. **局域网 LAN(Local Area Network)** : 学校或企业大多拥有多个互连的局域网。
+9. **局域网 LAN(Local Area Network)** :学校或企业大多拥有多个互连的局域网。

@@ -128,7 +128,7 @@ tag:
1. **频分复用(FDM)** :所有用户在同样的时间占用不同的带宽资源。
2. **时分复用(TDM)** :所有用户在不同的时间占用同样的频带宽度(分时不分频)。
3. **统计时分复用 (Statistic TDM)** :改进的时分复用,能够明显提高信道的利用率。
-4. **码分复用(CDM)** : 用户使用经过特殊挑选的不同码型,因此各用户之间不会造成干扰。这种系统发送的信号有很强的抗干扰能力,其频谱类似于白噪声,不易被敌人发现。
+4. **码分复用(CDM)** :用户使用经过特殊挑选的不同码型,因此各用户之间不会造成干扰。这种系统发送的信号有很强的抗干扰能力,其频谱类似于白噪声,不易被敌人发现。
5. **波分复用( WDM)** :波分复用就是光的频分复用。
#### 2.3.3. 几种常用的宽带接入技术,主要是 ADSL 和 FTTx
@@ -289,7 +289,7 @@ HTTP 协议的本质就是一种浏览器与服务器之间约定好的通信格

-10. **代理服务器(Proxy Server)** : 代理服务器(Proxy Server)是一种网络实体,它又称为万维网高速缓存。 代理服务器把最近的一些请求和响应暂存在本地磁盘中。当新请求到达时,若代理服务器发现这个请求与暂时存放的的请求相同,就返回暂存的响应,而不需要按 URL 的地址再次去互联网访问该资源。代理服务器可在客户端或服务器工作,也可以在中间系统工作。
+10. **代理服务器(Proxy Server)** :代理服务器(Proxy Server)是一种网络实体,它又称为万维网高速缓存。 代理服务器把最近的一些请求和响应暂存在本地磁盘中。当新请求到达时,若代理服务器发现这个请求与暂时存放的的请求相同,就返回暂存的响应,而不需要按 URL 的地址再次去互联网访问该资源。代理服务器可在客户端或服务器工作,也可以在中间系统工作。
11. **简单邮件传输协议(SMTP)** : SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。 SMTP 协议属于 TCP/IP 协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。 通过 SMTP 协议所指定的服务器,就可以把 E-mail 寄到收信人的服务器上了,整个过程只要几分钟。SMTP 服务器则是遵循 SMTP 协议的发送邮件服务器,用来发送或中转发出的电子邮件。

diff --git a/docs/cs-basics/network/http-status-codes.md b/docs/cs-basics/network/http-status-codes.md
index d4db8b99f18..82725494461 100644
--- a/docs/cs-basics/network/http-status-codes.md
+++ b/docs/cs-basics/network/http-status-codes.md
@@ -18,7 +18,7 @@ HTTP 状态码用于描述 HTTP 请求的结果,比如 2xx 就代表请求被
- **200 OK** :请求被成功处理。比如我们发送一个查询用户数据的 HTTP 请求到服务端,服务端正确返回了用户数据。这个是我们平时最常见的一个 HTTP 状态码。
- **201 Created** :请求被成功处理并且在服务端创建了一个新的资源。比如我们通过 POST 请求创建一个新的用户。
- **202 Accepted** :服务端已经接收到了请求,但是还未处理。
-- **204 No Content** : 服务端已经成功处理了请求,但是没有返回任何内容。
+- **204 No Content** :服务端已经成功处理了请求,但是没有返回任何内容。
这里格外提一下 204 状态码,平时学习/工作中见到的次数并不多。
@@ -46,20 +46,20 @@ HTTP 状态码用于描述 HTTP 请求的结果,比如 2xx 就代表请求被
### 3xx Redirection(重定向状态码)
-- **301 Moved Permanently** : 资源被永久重定向了。比如你的网站的网址更换了。
+- **301 Moved Permanently** :资源被永久重定向了。比如你的网站的网址更换了。
- **302 Found** :资源被临时重定向了。比如你的网站的某些资源被暂时转移到另外一个网址。
### 4xx Client Error(客户端错误状态码)
-- **400 Bad Request** : 发送的 HTTP 请求存在问题。比如请求参数不合法、请求方法错误。
-- **401 Unauthorized** : 未认证却请求需要认证之后才能访问的资源。
+- **400 Bad Request** :发送的 HTTP 请求存在问题。比如请求参数不合法、请求方法错误。
+- **401 Unauthorized** :未认证却请求需要认证之后才能访问的资源。
- **403 Forbidden** :直接拒绝 HTTP 请求,不处理。一般用来针对非法请求。
-- **404 Not Found** : 你请求的资源未在服务端找到。比如你请求某个用户的信息,服务端并没有找到指定的用户。
-- **409 Conflict** : 表示请求的资源与服务端当前的状态存在冲突,请求无法被处理。
+- **404 Not Found** :你请求的资源未在服务端找到。比如你请求某个用户的信息,服务端并没有找到指定的用户。
+- **409 Conflict** :表示请求的资源与服务端当前的状态存在冲突,请求无法被处理。
### 5xx Server Error(服务端错误状态码)
-- **500 Internal Server Error** : 服务端出问题了(通常是服务端出 Bug 了)。比如你服务端处理请求的时候突然抛出异常,但是异常并未在服务端被正确处理。
+- **500 Internal Server Error** :服务端出问题了(通常是服务端出 Bug 了)。比如你服务端处理请求的时候突然抛出异常,但是异常并未在服务端被正确处理。
- **502 Bad Gateway** :我们的网关将请求转发到服务端,但是服务端返回的却是一个错误的响应。
### 参考
diff --git a/docs/cs-basics/network/http-vs-https.md b/docs/cs-basics/network/http-vs-https.md
index 40c319b05d3..c55105bb8b7 100644
--- a/docs/cs-basics/network/http-vs-https.md
+++ b/docs/cs-basics/network/http-vs-https.md
@@ -137,4 +137,4 @@ SSL/TLS 介绍到这里,了解信息安全的朋友又会想到一个安全隐
- **端口号** :HTTP 默认是 80,HTTPS 默认是 443。
- **URL 前缀** :HTTP 的 URL 前缀是 `http://`,HTTPS 的 URL 前缀是 `https://`。
-- **安全性和资源消耗** : HTTP 协议运行在 TCP 之上,所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份。HTTPS 是运行在 SSL/TLS 之上的 HTTP 协议,SSL/TLS 运行在 TCP 之上。所有传输的内容都经过加密,加密采用对称加密,但对称加密的密钥用服务器方的证书进行了非对称加密。所以说,HTTP 安全性没有 HTTPS 高,但是 HTTPS 比 HTTP 耗费更多服务器资源。
+- **安全性和资源消耗** :HTTP 协议运行在 TCP 之上,所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份。HTTPS 是运行在 SSL/TLS 之上的 HTTP 协议,SSL/TLS 运行在 TCP 之上。所有传输的内容都经过加密,加密采用对称加密,但对称加密的密钥用服务器方的证书进行了非对称加密。所以说,HTTP 安全性没有 HTTPS 高,但是 HTTPS 比 HTTP 耗费更多服务器资源。
diff --git a/docs/cs-basics/network/network-attack-means.md b/docs/cs-basics/network/network-attack-means.md
index ab7156e0f9f..9dcd1bb5425 100644
--- a/docs/cs-basics/network/network-attack-means.md
+++ b/docs/cs-basics/network/network-attack-means.md
@@ -141,7 +141,7 @@ HTTP 洪水攻击是“第 7 层”DDoS 攻击的一种。第 7 层是 OSI 模
HTTP 洪水攻击有两种:
- **HTTP GET 攻击** :在这种攻击形式下,多台计算机或其他设备相互协调,向目标服务器发送对图像、文件或其他资产的多个请求。当目标被传入的请求和响应所淹没时,来自正常流量源的其他请求将被拒绝服务。
-- **HTTP POST 攻击** : 一般而言,在网站上提交表单时,服务器必须处理传入的请求并将数据推送到持久层(通常是数据库)。与发送 POST 请求所需的处理能力和带宽相比,处理表单数据和运行必要数据库命令的过程相对密集。这种攻击利用相对资源消耗的差异,直接向目标服务器发送许多 POST 请求,直到目标服务器的容量饱和并拒绝服务为止。
+- **HTTP POST 攻击** :一般而言,在网站上提交表单时,服务器必须处理传入的请求并将数据推送到持久层(通常是数据库)。与发送 POST 请求所需的处理能力和带宽相比,处理表单数据和运行必要数据库命令的过程相对密集。这种攻击利用相对资源消耗的差异,直接向目标服务器发送许多 POST 请求,直到目标服务器的容量饱和并拒绝服务为止。
### 如何防护 HTTP Flood?
diff --git a/docs/cs-basics/network/osi-and-tcp-ip-model.md b/docs/cs-basics/network/osi-and-tcp-ip-model.md
index 69cd6dc2aa3..966444c9554 100644
--- a/docs/cs-basics/network/osi-and-tcp-ip-model.md
+++ b/docs/cs-basics/network/osi-and-tcp-ip-model.md
@@ -181,7 +181,7 @@ OSI 七层模型虽然失败了,但是却提供了很多不错的理论基础
1. **各层之间相互独立**:各层之间相互独立,各层之间不需要关心其他层是如何实现的,只需要知道自己如何调用下层提供好的功能就可以了(可以简单理解为接口调用)**。这个和我们对开发时系统进行分层是一个道理。**
2. **提高了整体灵活性** :每一层都可以使用最适合的技术来实现,你只需要保证你提供的功能以及暴露的接口的规则没有改变就行了。**这个和我们平时开发系统的时候要求的高内聚、低耦合的原则也是可以对应上的。**
-3. **大问题化小** : 分层可以将复杂的网络问题分解为许多比较小的、界线比较清晰简单的小问题来处理和解决。这样使得复杂的计算机网络系统变得易于设计,实现和标准化。 **这个和我们平时开发的时候,一般会将系统功能分解,然后将复杂的问题分解为容易理解的更小的问题是相对应的,这些较小的问题具有更好的边界(目标和接口)定义。**
+3. **大问题化小** :分层可以将复杂的网络问题分解为许多比较小的、界线比较清晰简单的小问题来处理和解决。这样使得复杂的计算机网络系统变得易于设计,实现和标准化。 **这个和我们平时开发的时候,一般会将系统功能分解,然后将复杂的问题分解为容易理解的更小的问题是相对应的,这些较小的问题具有更好的边界(目标和接口)定义。**
我想到了计算机世界非常非常有名的一句话,这里分享一下:
diff --git a/docs/cs-basics/network/other-network-questions.md b/docs/cs-basics/network/other-network-questions.md
index c9e6f1095ee..22fd82eaf77 100644
--- a/docs/cs-basics/network/other-network-questions.md
+++ b/docs/cs-basics/network/other-network-questions.md
@@ -56,7 +56,7 @@ tag:
1. **各层之间相互独立**:各层之间相互独立,各层之间不需要关心其他层是如何实现的,只需要知道自己如何调用下层提供好的功能就可以了(可以简单理解为接口调用)**。这个和我们对开发时系统进行分层是一个道理。**
2. **提高了整体灵活性** :每一层都可以使用最适合的技术来实现,你只需要保证你提供的功能以及暴露的接口的规则没有改变就行了。**这个和我们平时开发系统的时候要求的高内聚、低耦合的原则也是可以对应上的。**
-3. **大问题化小** : 分层可以将复杂的网络问题分解为许多比较小的、界线比较清晰简单的小问题来处理和解决。这样使得复杂的计算机网络系统变得易于设计,实现和标准化。 **这个和我们平时开发的时候,一般会将系统功能分解,然后将复杂的问题分解为容易理解的更小的问题是相对应的,这些较小的问题具有更好的边界(目标和接口)定义。**
+3. **大问题化小** :分层可以将复杂的网络问题分解为许多比较小的、界线比较清晰简单的小问题来处理和解决。这样使得复杂的计算机网络系统变得易于设计,实现和标准化。 **这个和我们平时开发的时候,一般会将系统功能分解,然后将复杂的问题分解为容易理解的更小的问题是相对应的,这些较小的问题具有更好的边界(目标和接口)定义。**
我想到了计算机世界非常非常有名的一句话,这里分享一下:
@@ -90,7 +90,7 @@ tag:

-- **IP(Internet Protocol,网际协议)** : TCP/IP 协议中最重要的协议之一,属于网络层的协议,主要作用是定义数据包的格式、对数据包进行路由和寻址,以便它们可以跨网络传播并到达正确的目的地。目前 IP 协议主要分为两种,一种是过去的 IPv4,另一种是较新的 IPv6,目前这两种协议都在使用,但后者已经被提议来取代前者。
+- **IP(Internet Protocol,网际协议)** :TCP/IP 协议中最重要的协议之一,属于网络层的协议,主要作用是定义数据包的格式、对数据包进行路由和寻址,以便它们可以跨网络传播并到达正确的目的地。目前 IP 协议主要分为两种,一种是过去的 IPv4,另一种是较新的 IPv6,目前这两种协议都在使用,但后者已经被提议来取代前者。
- **ARP(Address Resolution Protocol,地址解析协议)** :ARP 协议解决的是网络层地址和链路层地址之间的转换问题。因为一个 IP 数据报在物理上传输的过程中,总是需要知道下一跳(物理上的下一个目的地)该去往何处,但 IP 地址属于逻辑地址,而 MAC 地址才是物理地址,ARP 协议解决了 IP 地址转 MAC 地址的一些问题。
- **ICMP(Internet Control Message Protocol,互联网控制报文协议)** :一种用于传输网络状态和错误消息的协议,常用于网络诊断和故障排除。例如,Ping 工具就使用了 ICMP 协议来测试网络连通性。
- **NAT(Network Address Translation,网络地址转换协议)** :NAT 协议的应用场景如同它的名称——网络地址转换,应用于内部网到外部网的地址转换过程中。具体地说,在一个小的子网(局域网,LAN)内,各主机使用的是同一个 LAN 下的 IP 地址,但在该 LAN 以外,在广域网(WAN)中,需要一个统一的 IP 地址来标识该 LAN 在整个 Internet 上的位置。
@@ -175,7 +175,7 @@ HTTP 状态码用于描述 HTTP 请求的结果,比如 2xx 就代表请求被
- **端口号** :HTTP 默认是 80,HTTPS 默认是 443。
- **URL 前缀** :HTTP 的 URL 前缀是 `http://`,HTTPS 的 URL 前缀是 `https://`。
-- **安全性和资源消耗** : HTTP 协议运行在 TCP 之上,所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份。HTTPS 是运行在 SSL/TLS 之上的 HTTP 协议,SSL/TLS 运行在 TCP 之上。所有传输的内容都经过加密,加密采用对称加密,但对称加密的密钥用服务器方的证书进行了非对称加密。所以说,HTTP 安全性没有 HTTPS 高,但是 HTTPS 比 HTTP 耗费更多服务器资源。
+- **安全性和资源消耗** :HTTP 协议运行在 TCP 之上,所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份。HTTPS 是运行在 SSL/TLS 之上的 HTTP 协议,SSL/TLS 运行在 TCP 之上。所有传输的内容都经过加密,加密采用对称加密,但对称加密的密钥用服务器方的证书进行了非对称加密。所以说,HTTP 安全性没有 HTTPS 高,但是 HTTPS 比 HTTP 耗费更多服务器资源。
- **SEO(搜索引擎优化)** :搜索引擎通常会更青睐使用 HTTPS 协议的网站,因为 HTTPS 能够提供更高的安全性和用户隐私保护。使用 HTTPS 协议的网站在搜索结果中可能会被优先显示,从而对 SEO 产生影响。
关于 HTTP 和 HTTPS 更详细的对比总结,可以看我写的这篇文章:[HTTP vs HTTPS(应用层)](./http-vs-https.md) 。
diff --git a/docs/cs-basics/network/other-network-questions2.md b/docs/cs-basics/network/other-network-questions2.md
index aa828d08c98..17e9a47be0a 100644
--- a/docs/cs-basics/network/other-network-questions2.md
+++ b/docs/cs-basics/network/other-network-questions2.md
@@ -15,7 +15,7 @@ tag:
2. **是否是可靠传输**:远地主机在收到 UDP 报文后,不需要给出任何确认,并且不保证数据不丢失,不保证是否顺序到达。TCP 提供可靠的传输服务,TCP 在传递数据之前,会有三次握手来建立连接,而且在数据传递时,有确认、窗口、重传、拥塞控制机制。通过 TCP 连接传输的数据,无差错、不丢失、不重复、并且按序到达。
3. **是否有状态** :这个和上面的“是否可靠传输”相对应。TCP 传输是有状态的,这个有状态说的是 TCP 会去记录自己发送消息的状态比如消息是否发送了、是否被接收了等等。为此 ,TCP 需要维持复杂的连接状态表。而 UDP 是无状态服务,简单来说就是不管发出去之后的事情了(**这很渣男!**)。
4. **传输效率** :由于使用 TCP 进行传输的时候多了连接、确认、重传等机制,所以 TCP 的传输效率要比 UDP 低很多。
-5. **传输形式** : TCP 是面向字节流的,UDP 是面向报文的。
+5. **传输形式** :TCP 是面向字节流的,UDP 是面向报文的。
6. **首部开销** :TCP 首部开销(20 ~ 60 字节)比 UDP 首部开销(8 字节)要大。
7. **是否提供广播或多播服务** :TCP 只支持点对点通信,UDP 支持一对一、一对多、多对一、多对多;
8. ......
@@ -34,7 +34,7 @@ tag:
### 什么时候选择 TCP,什么时候选 UDP?
-- **UDP 一般用于即时通信**,比如: 语音、 视频 、直播等等。这些场景对传输数据的准确性要求不是特别高,比如你看视频即使少个一两帧,实际给人的感觉区别也不大。
+- **UDP 一般用于即时通信**,比如:语音、 视频、直播等等。这些场景对传输数据的准确性要求不是特别高,比如你看视频即使少个一两帧,实际给人的感觉区别也不大。
- **TCP 用于对传输准确性要求特别高的场景**,比如文件传输、发送和接收邮件、远程登录等等。
### HTTP 基于 TCP 还是 UDP?
@@ -56,7 +56,7 @@ tag:
2. **HTTPS 协议** :更安全的超文本传输协议(HTTPS,Hypertext Transfer Protocol Secure),身披 SSL 外衣的 HTTP 协议
3. **FTP 协议**:文件传输协议 FTP(File Transfer Protocol)是一种用于在计算机之间传输文件的协议,可以屏蔽操作系统和文件存储方式。注意 ⚠️:FTP 是一种不安全的协议,因为它在传输过程中不会对数据进行加密。建议在传输敏感数据时使用更安全的协议,如 SFTP。
4. **SMTP 协议**:简单邮件传输协议(SMTP,Simple Mail Transfer Protocol)的缩写,是一种用于发送电子邮件的协议。注意 ⚠️:SMTP 协议只负责邮件的发送,而不是接收。要从邮件服务器接收邮件,需要使用 POP3 或 IMAP 协议。
-5. **POP3/IMAP 协议**: 两者都是负责邮件接收的协议。IMAP 协议是比 POP3 更新的协议,它在功能和性能上都更加强大。IMAP 支持邮件搜索、标记、分类、归档等高级功能,而且可以在多个设备之间同步邮件状态。几乎所有现代电子邮件客户端和服务器都支持 IMAP。
+5. **POP3/IMAP 协议**:两者都是负责邮件接收的协议。IMAP 协议是比 POP3 更新的协议,它在功能和性能上都更加强大。IMAP 支持邮件搜索、标记、分类、归档等高级功能,而且可以在多个设备之间同步邮件状态。几乎所有现代电子邮件客户端和服务器都支持 IMAP。
6. **Telnet 协议**:用于通过一个终端登陆到其他服务器。Telnet 协议的最大缺点之一是所有数据(包括用户名和密码)均以明文形式发送,这有潜在的安全风险。这就是为什么如今很少使用 Telnet,而是使用一种称为 SSH 的非常安全的网络传输协议的主要原因。
7. **SSH 协议** : SSH( Secure Shell)是目前较可靠,专为远程登录会话和其他网络服务提供安全性的协议。利用 SSH 协议可以有效防止远程管理过程中的信息泄露问题。SSH 建立在可靠的传输协议 TCP 之上。
8. ......
@@ -64,7 +64,7 @@ tag:
**运行于 UDP 协议之上的协议** :
1. **DHCP 协议**:动态主机配置协议,动态配置 IP 地址
-2. **DNS** : **域名系统(DNS,Domain Name System)将人类可读的域名 (例如,www.baidu.com) 转换为机器可读的 IP 地址 (例如,220.181.38.148)。** 我们可以将其理解为专为互联网设计的电话薄。实际上 DNS 同时支持 UDP 和 TCP 协议。
+2. **DNS** :**域名系统(DNS,Domain Name System)将人类可读的域名 (例如,www.baidu.com) 转换为机器可读的 IP 地址 (例如,220.181.38.148)。** 我们可以将其理解为专为互联网设计的电话薄。实际上 DNS 同时支持 UDP 和 TCP 协议。
3. ......
### TCP 三次握手和四次挥手(非常重要)
diff --git a/docs/cs-basics/network/tcp-connection-and-disconnection.md b/docs/cs-basics/network/tcp-connection-and-disconnection.md
index 0d830295d3a..eec56a1aac1 100644
--- a/docs/cs-basics/network/tcp-connection-and-disconnection.md
+++ b/docs/cs-basics/network/tcp-connection-and-disconnection.md
@@ -56,7 +56,7 @@ TCP 是全双工通信,可以双向传输数据。任何一方都可以在数
举个例子:A 和 B 打电话,通话即将结束后。
-1. **第一次挥手** : A 说“我没啥要说的了”
+1. **第一次挥手** :A 说“我没啥要说的了”
2. **第二次挥手** :B 回答“我知道了”,但是 B 可能还会有要说的话,A 不能要求 B 跟着自己的节奏结束通话
3. **第三次挥手** :于是 B 可能又巴拉巴拉说了一通,最后 B 说“我说完了”
4. **第四次挥手** :A 回答“知道了”,这样通话才算结束。
diff --git a/docs/cs-basics/network/tcp-reliability-guarantee.md b/docs/cs-basics/network/tcp-reliability-guarantee.md
index 9423ae583df..a9dc6ae8441 100644
--- a/docs/cs-basics/network/tcp-reliability-guarantee.md
+++ b/docs/cs-basics/network/tcp-reliability-guarantee.md
@@ -66,7 +66,7 @@ TCP 为全双工(Full-Duplex, FDX)通信,双方可以进行双向通信,客
为了进行拥塞控制,TCP 发送方要维持一个 **拥塞窗口(cwnd)** 的状态变量。拥塞控制窗口的大小取决于网络的拥塞程度,并且动态变化。发送方让自己的发送窗口取为拥塞窗口和接收方的接受窗口中较小的一个。
-TCP 的拥塞控制采用了四种算法,即 **慢开始** 、 **拥塞避免** 、**快重传** 和 **快恢复**。在网络层也可以使路由器采用适当的分组丢弃策略(如主动队列管理 AQM),以减少网络拥塞的发生。
+TCP 的拥塞控制采用了四种算法,即 **慢开始**、 **拥塞避免**、**快重传** 和 **快恢复**。在网络层也可以使路由器采用适当的分组丢弃策略(如主动队列管理 AQM),以减少网络拥塞的发生。
- **慢开始:** 慢开始算法的思路是当主机开始发送数据时,如果立即把大量数据字节注入到网络,那么可能会引起网络阻塞,因为现在还不知道网络的符合情况。经验表明,较好的方法是先探测一下,即由小到大逐渐增大发送窗口,也就是由小到大逐渐增大拥塞窗口数值。cwnd 初始值为 1,每经过一个传播轮次,cwnd 加倍。
- **拥塞避免:** 拥塞避免算法的思路是让拥塞窗口 cwnd 缓慢增大,即每经过一个往返时间 RTT 就把发送方的 cwnd 加 1.
diff --git a/docs/cs-basics/operating-system/linux-intro.md b/docs/cs-basics/operating-system/linux-intro.md
index 765ad51fa16..14d29f7543b 100644
--- a/docs/cs-basics/operating-system/linux-intro.md
+++ b/docs/cs-basics/operating-system/linux-intro.md
@@ -18,9 +18,9 @@ head:
通过以下三点可以概括 Linux 到底是什么:
-- **类 Unix 系统** : Linux 是一种自由、开放源码的类似 Unix 的操作系统
-- **Linux 本质是指 Linux 内核** : 严格来讲,Linux 这个词本身只表示 Linux 内核,单独的 Linux 内核并不能成为一个可以正常工作的操作系统。所以,就有了各种 Linux 发行版。
-- **Linux 之父(林纳斯·本纳第克特·托瓦兹 Linus Benedict Torvalds)** : 一个编程领域的传奇式人物,真大佬!我辈崇拜敬仰之楷模。他是 **Linux 内核** 的最早作者,随后发起了这个开源项目,担任 Linux 内核的首要架构师。他还发起了 Git 这个开源项目,并为主要的开发者。
+- **类 Unix 系统** :Linux 是一种自由、开放源码的类似 Unix 的操作系统
+- **Linux 本质是指 Linux 内核** :严格来讲,Linux 这个词本身只表示 Linux 内核,单独的 Linux 内核并不能成为一个可以正常工作的操作系统。所以,就有了各种 Linux 发行版。
+- **Linux 之父(林纳斯·本纳第克特·托瓦兹 Linus Benedict Torvalds)** :一个编程领域的传奇式人物,真大佬!我辈崇拜敬仰之楷模。他是 **Linux 内核** 的最早作者,随后发起了这个开源项目,担任 Linux 内核的首要架构师。他还发起了 Git 这个开源项目,并为主要的开发者。

@@ -116,11 +116,11 @@ inode 是 Linux/Unix 文件系统的基础。那 inode 到是什么?有什么作
Linux 支持很多文件类型,其中非常重要的文件类型有: **普通文件**,**目录文件**,**链接文件**,**设备文件**,**管道文件**,**Socket 套接字文件** 等。
-- **普通文件(-)** : 用于存储信息和数据, Linux 用户可以根据访问权限对普通文件进行查看、更改和删除。比如:图片、声音、PDF、text、视频、源代码等等。
+- **普通文件(-)** :用于存储信息和数据, Linux 用户可以根据访问权限对普通文件进行查看、更改和删除。比如:图片、声音、PDF、text、视频、源代码等等。
- **目录文件(d,directory file)** :目录也是文件的一种,用于表示和管理系统中的文件,目录文件中包含一些文件名和子目录名。打开目录事实上就是打开目录文件。
- **符号链接文件(l,symbolic link)** :保留了指向文件的地址而不是文件本身。
- **字符设备(c,char)** :用来访问字符设备比如键盘。
-- **设备文件(b,block)** : 用来访问块设备比如硬盘、软盘。
+- **设备文件(b,block)** :用来访问块设备比如硬盘、软盘。
- **管道文件(p,pipe)** : 一种特殊类型的文件,用于进程之间的通信。
- **套接字文件(s,socket)** :用于进程间的网络通信,也可以用于本机之间的非网络通信。
@@ -174,10 +174,10 @@ Linux 使用一种称为目录树的层次结构来组织文件和目录。目
### 目录切换
-- `cd usr`: 切换到该目录下 usr 目录
-- `cd ..(或cd../)`: 切换到上一层目录
-- `cd /`: 切换到系统根目录
-- `cd ~`: 切换到用户主目录
+- `cd usr`:切换到该目录下 usr 目录
+- `cd ..(或cd../)`:切换到上一层目录
+- `cd /`:切换到系统根目录
+- `cd ~`:切换到用户主目录
- **`cd -`:** 切换到上一个操作所在目录
### 目录操作
@@ -199,7 +199,7 @@ Linux 使用一种称为目录树的层次结构来组织文件和目录。目
- `touch [选项] 文件名..`:创建新文件或更新已存在文件(增)。例如:`touch file1.txt file2.txt file3.txt` ,创建 3 个文件。
- `ln [选项] <源文件> <硬链接/软链接文件>`:创建硬链接/软链接。例如:`ln -s file.txt file_link`,创建名为 `file_link` 的软链接,指向 `file.txt` 文件。`-s` 选项代表的就是创建软链接,s 即 symbolic(软链接又名符号链接) 。
- `cat/more/less/tail 文件名` :文件的查看(查) 。命令 `tail -f 文件` 可以对某个文件进行动态监控,例如 Tomcat 的日志文件, 会随着程序的运行,日志会变化,可以使用 `tail -f catalina-2016-11-11.log` 监控 文 件的变化 。
-- `vim 文件名`: 修改文件的内容(改)。vim 编辑器是 Linux 中的强大组件,是 vi 编辑器的加强版,vim 编辑器的命令和快捷方式有很多,但此处不一一阐述,大家也无需研究的很透彻,使用 vim 编辑修改文件的方式基本会使用就可以了。在实际开发中,使用 vim 编辑器主要作用就是修改配置文件,下面是一般步骤: `vim 文件------>进入文件----->命令模式------>按i进入编辑模式----->编辑文件 ------->按Esc进入底行模式----->输入:wq/q!` (输入 wq 代表写入内容并退出,即保存;输入 q!代表强制退出不保存)。
+- `vim 文件名`:修改文件的内容(改)。vim 编辑器是 Linux 中的强大组件,是 vi 编辑器的加强版,vim 编辑器的命令和快捷方式有很多,但此处不一一阐述,大家也无需研究的很透彻,使用 vim 编辑修改文件的方式基本会使用就可以了。在实际开发中,使用 vim 编辑器主要作用就是修改配置文件,下面是一般步骤:`vim 文件------>进入文件----->命令模式------>按i进入编辑模式----->编辑文件 ------->按Esc进入底行模式----->输入:wq/q!` (输入 wq 代表写入内容并退出,即保存;输入 q!代表强制退出不保存)。
### 文件压缩
@@ -214,7 +214,7 @@ Linux 中的打包文件一般是以 `.tar` 结尾的,压缩的命令一般是
- v:显示运行过程
- f:指定文件名
-比如:假如 test 目录下有三个文件分别是:`aaa.txt`、 `bbb.txt` 、`ccc.txt`,如果我们要打包 `test` 目录并指定压缩后的压缩包名称为 `test.tar.gz` 可以使用命令:`tar -zcvf test.tar.gz aaa.txt bbb.txt ccc.txt` 或 `tar -zcvf test.tar.gz /test/` 。
+比如:假如 test 目录下有三个文件分别是:`aaa.txt`、 `bbb.txt`、`ccc.txt`,如果我们要打包 `test` 目录并指定压缩后的压缩包名称为 `test.tar.gz` 可以使用命令:`tar -zcvf test.tar.gz aaa.txt bbb.txt ccc.txt` 或 `tar -zcvf test.tar.gz /test/` 。
**2)解压压缩包:**
@@ -251,9 +251,9 @@ Linux 中的打包文件一般是以 `.tar` 结尾的,压缩的命令一般是
**文件的类型:**
-- d: 代表目录
-- -: 代表文件
-- l: 代表软链接(可以认为是 window 中的快捷方式)
+- d:代表目录
+- -:代表文件
+- l:代表软链接(可以认为是 window 中的快捷方式)
**Linux 中权限分为以下几种:**
@@ -281,7 +281,7 @@ Linux 中的打包文件一般是以 `.tar` 结尾的,压缩的命令一般是
| w | 可以创建和删除目录下文件 |
| x | 可以使用 cd 进入目录 |
-需要注意的是: **超级用户可以无视普通用户的权限,即使文件目录权限是 000,依旧可以访问。**
+需要注意的是:**超级用户可以无视普通用户的权限,即使文件目录权限是 000,依旧可以访问。**
**在 linux 中的每个用户必须属于一个组,不能独立于组外。在 linux 中每个文件有所有者、所在组、其它组的概念。**
@@ -344,7 +344,7 @@ Linux 系统是一个多用户多任务的分时操作系统,任何一个要
- `df [选项] [文件系统]`:用于查看系统的磁盘空间使用情况,包括磁盘空间的总量、已使用量和可用量等,可以指定文件系统上。例如:`df -a`,查看全部文件系统。
- `du [选项] [文件]`:用于查看指定目录或文件的磁盘空间使用情况,可以指定不同的选项来控制输出格式和单位。
- `sar [选项] [时间间隔] [重复次数]`:用于收集、报告和分析系统的性能统计信息,包括系统的 CPU 使用、内存使用、磁盘 I/O、网络活动等详细信息。它的特点是可以连续对系统取样,获得大量的取样数据。取样数据和分析的结果都可以存入文件,使用它时消耗的系统资源很小。
-- `ps [选项]`:用于查看系统中的进程信息,包括进程的 ID、状态、资源使用情况等。`ps -ef`/`ps -aux`: 这两个命令都是查看当前系统正在运行进程,两者的区别是展示格式不同。如果想要查看特定的进程可以使用这样的格式:`ps aux|grep redis` (查看包括 redis 字符串的进程),也可使用 `pgrep redis -a`。
+- `ps [选项]`:用于查看系统中的进程信息,包括进程的 ID、状态、资源使用情况等。`ps -ef`/`ps -aux`:这两个命令都是查看当前系统正在运行进程,两者的区别是展示格式不同。如果想要查看特定的进程可以使用这样的格式:`ps aux|grep redis` (查看包括 redis 字符串的进程),也可使用 `pgrep redis -a`。
- `systemctl [命令] [服务名称]`:用于管理系统的服务和单元,可以查看系统服务的状态、启动、停止、重启等。
### 网络通信
@@ -357,10 +357,10 @@ Linux 系统是一个多用户多任务的分时操作系统,任何一个要
### 其他
- `sudo + 其他命令`:以系统管理者的身份执行指令,也就是说,经由 sudo 所执行的指令就好像是 root 亲自执行。
-- `grep 要搜索的字符串 要搜索的文件 --color`: 搜索命令,--color 代表高亮显示。
-- `kill -9 进程的pid`: 杀死进程(-9 表示强制终止)先用 ps 查找进程,然后用 kill 杀掉。
-- `shutdown`: `shutdown -h now`: 指定现在立即关机;`shutdown +5 "System will shutdown after 5 minutes"`:指定 5 分钟后关机,同时送出警告信息给登入用户。
-- `reboot`: `reboot`: 重开机。`reboot -w`: 做个重开机的模拟(只有纪录并不会真的重开机)。
+- `grep 要搜索的字符串 要搜索的文件 --color`:搜索命令,--color 代表高亮显示。
+- `kill -9 进程的pid`:杀死进程(-9 表示强制终止)先用 ps 查找进程,然后用 kill 杀掉。
+- `shutdown`:`shutdown -h now`:指定现在立即关机;`shutdown +5 "System will shutdown after 5 minutes"`:指定 5 分钟后关机,同时送出警告信息给登入用户。
+- `reboot`:`reboot`:重开机。`reboot -w`:做个重开机的模拟(只有纪录并不会真的重开机)。
## Linux 环境变量
diff --git a/docs/cs-basics/operating-system/operating-system-basic-questions-01.md b/docs/cs-basics/operating-system/operating-system-basic-questions-01.md
index 23a3fc793c8..691fc05c335 100644
--- a/docs/cs-basics/operating-system/operating-system-basic-questions-01.md
+++ b/docs/cs-basics/operating-system/operating-system-basic-questions-01.md
@@ -192,7 +192,7 @@ _玩玩电脑游戏还是必须要有 Windows 的,所以我现在是一台 Win
再深入到计算机底层来探讨:
-- **单核时代**: 在单核时代多线程主要是为了提高单进程利用 CPU 和 IO 系统的效率。 假设只运行了一个 Java 进程的情况,当我们请求 IO 的时候,如果 Java 进程中只有一个线程,此线程被 IO 阻塞则整个进程被阻塞。CPU 和 IO 设备只有一个在运行,那么可以简单地说系统整体效率只有 50%。当使用多线程的时候,一个线程被 IO 阻塞,其他线程还可以继续使用 CPU。从而提高了 Java 进程利用系统资源的整体效率。
+- **单核时代**:在单核时代多线程主要是为了提高单进程利用 CPU 和 IO 系统的效率。 假设只运行了一个 Java 进程的情况,当我们请求 IO 的时候,如果 Java 进程中只有一个线程,此线程被 IO 阻塞则整个进程被阻塞。CPU 和 IO 设备只有一个在运行,那么可以简单地说系统整体效率只有 50%。当使用多线程的时候,一个线程被 IO 阻塞,其他线程还可以继续使用 CPU。从而提高了 Java 进程利用系统资源的整体效率。
- **多核时代**: 多核时代多线程主要是为了提高进程利用多核 CPU 的能力。举个例子:假如我们要计算一个复杂的任务,我们只用一个线程的话,不论系统有几个 CPU 核心,都只会有一个 CPU 核心被利用到。而创建多个线程,这些线程可以被映射到底层多个 CPU 上执行,在任务中的多个线程没有资源竞争的情况下,任务执行的效率会有显著性的提高,约等于(单核时执行时间/CPU 核心数)。
### 线程间的同步的方式有哪些?
@@ -256,7 +256,7 @@ PCB 主要包含下面几部分的内容:
- **短作业优先的调度算法(SJF,Shortest Job First)** : 从就绪队列中选出一个估计运行时间最短的进程为之分配资源,使它立即执行并一直执行到完成或发生某事件而被阻塞放弃占用 CPU 时再重新调度。
- **时间片轮转调度算法(RR,Round-Robin)** : 时间片轮转调度是一种最古老,最简单,最公平且使用最广的算法。每个进程被分配一个时间段,称作它的时间片,即该进程允许运行的时间。
- **多级反馈队列调度算法(MFQ,Multi-level Feedback Queue)** :前面介绍的几种进程调度的算法都有一定的局限性。如**短进程优先的调度算法,仅照顾了短进程而忽略了长进程** 。多级反馈队列调度算法既能使高优先级的作业得到响应又能使短作业(进程)迅速完成。,因而它是目前**被公认的一种较好的进程调度算法**,UNIX 操作系统采取的便是这种调度算法。
-- **优先级调度算法(Priority)** : 为每个流程分配优先级,首先执行具有最高优先级的进程,依此类推。具有相同优先级的进程以 FCFS 方式执行。可以根据内存要求,时间要求或任何其他资源要求来确定优先级。
+- **优先级调度算法(Priority)** :为每个流程分配优先级,首先执行具有最高优先级的进程,依此类推。具有相同优先级的进程以 FCFS 方式执行。可以根据内存要求,时间要求或任何其他资源要求来确定优先级。
### 什么是僵尸进程和孤儿进程?
diff --git a/docs/cs-basics/operating-system/operating-system-basic-questions-02.md b/docs/cs-basics/operating-system/operating-system-basic-questions-02.md
index ef3bbebfa17..e0cd1da737e 100644
--- a/docs/cs-basics/operating-system/operating-system-basic-questions-02.md
+++ b/docs/cs-basics/operating-system/operating-system-basic-questions-02.md
@@ -87,7 +87,7 @@ head:
- **简化内存管理** :进程都有一个一致且私有的虚拟地址空间,程序员不用和真正的物理内存打交道,而是借助虚拟地址空间访问物理内存,从而简化了内存管理。
- **多个进程共享物理内存**:进程在运行过程中,会加载许多操作系统的动态库。这些库对于每个进程而言都是公用的,它们在内存中实际只会加载一份,这部分称为共享内存。
- **提高内存使用安全性** :控制进程对物理内存的访问,隔离不同进程的访问权限,提高系统的安全性。
-- **提供更大的可使用内存空间** : 可以让程序拥有超过系统物理内存大小的可用内存空间。这是因为当物理内存不够用时,可以利用磁盘充当,将物理内存页(通常大小为 4 KB)保存到磁盘文件(会影响读写速度),数据或代码页会根据需要在物理内存与磁盘之间移动。
+- **提供更大的可使用内存空间** :可以让程序拥有超过系统物理内存大小的可用内存空间。这是因为当物理内存不够用时,可以利用磁盘充当,将物理内存页(通常大小为 4 KB)保存到磁盘文件(会影响读写速度),数据或代码页会根据需要在物理内存与磁盘之间移动。
#### 没有虚拟内存有什么问题?
@@ -404,7 +404,7 @@ LRU 算法是实际使用中应用的比较多,也被认为是最接近 OPT
- 《深入理解计算机系统》
- 《重学操作系统》
- 《现代操作系统原理与实现》
-- 王道考研操作系统知识点整理: https://wizardforcel.gitbooks.io/wangdaokaoyan-os/content/13.html
+- 王道考研操作系统知识点整理:https://wizardforcel.gitbooks.io/wangdaokaoyan-os/content/13.html
- 内存管理之伙伴系统与 SLAB:https://blog.csdn.net/qq_44272681/article/details/124199068
- 为什么 Linux 需要虚拟内存:https://draveness.me/whys-the-design-os-virtual-memory/
- 程序员的自我修养(七):内存缺页错误:https://liam.page/2017/09/01/page-fault/
diff --git a/docs/cs-basics/operating-system/shell-intro.md b/docs/cs-basics/operating-system/shell-intro.md
index c718db501ec..9956bc1f045 100644
--- a/docs/cs-basics/operating-system/shell-intro.md
+++ b/docs/cs-basics/operating-system/shell-intro.md
@@ -242,7 +242,7 @@ echo $length2 #输出:5
# 输出数组第三个元素
echo ${array[2]} #输出:3
unset array[1]# 删除下标为1的元素也就是删除第二个元素
-for i in ${array[@]};do echo $i ;done # 遍历数组,输出: 1 3 4 5
+for i in ${array[@]};do echo $i ;done # 遍历数组,输出:1 3 4 5
unset array; # 删除数组中的所有元素
for i in ${array[@]};do echo $i ;done # 遍历数组,数组元素为空,没有任何输出内容
```
diff --git a/docs/database/basis.md b/docs/database/basis.md
index 148cd72f610..e062466b333 100644
--- a/docs/database/basis.md
+++ b/docs/database/basis.md
@@ -16,12 +16,12 @@ tag:
## 什么是元组, 码, 候选码, 主码, 外码, 主属性, 非主属性?
-- **元组** : 元组(tuple)是关系数据库中的基本概念,关系是一张表,表中的每行(即数据库中的每条记录)就是一个元组,每列就是一个属性。 在二维表里,元组也称为行。
+- **元组** :元组(tuple)是关系数据库中的基本概念,关系是一张表,表中的每行(即数据库中的每条记录)就是一个元组,每列就是一个属性。 在二维表里,元组也称为行。
- **码** :码就是能唯一标识实体的属性,对应表中的列。
-- **候选码** : 若关系中的某一属性或属性组的值能唯一的标识一个元组,而其任何、子集都不能再标识,则称该属性组为候选码。例如:在学生实体中,“学号”是能唯一的区分学生实体的,同时又假设“姓名”、“班级”的属性组合足以区分学生实体,那么{学号}和{姓名,班级}都是候选码。
+- **候选码** :若关系中的某一属性或属性组的值能唯一的标识一个元组,而其任何、子集都不能再标识,则称该属性组为候选码。例如:在学生实体中,“学号”是能唯一的区分学生实体的,同时又假设“姓名”、“班级”的属性组合足以区分学生实体,那么{学号}和{姓名,班级}都是候选码。
- **主码** : 主码也叫主键。主码是从候选码中选出来的。 一个实体集中只能有一个主码,但可以有多个候选码。
- **外码** : 外码也叫外键。如果一个关系中的一个属性是另外一个关系中的主码则这个属性为外码。
-- **主属性** : 候选码中出现过的属性称为主属性。比如关系 工人(工号,身份证号,姓名,性别,部门). 显然工号和身份证号都能够唯一标示这个关系,所以都是候选码。工号、身份证号这两个属性就是主属性。如果主码是一个属性组,那么属性组中的属性都是主属性。
+- **主属性** :候选码中出现过的属性称为主属性。比如关系 工人(工号,身份证号,姓名,性别,部门). 显然工号和身份证号都能够唯一标示这个关系,所以都是候选码。工号、身份证号这两个属性就是主属性。如果主码是一个属性组,那么属性组中的属性都是主属性。
- **非主属性:** 不包含在任何一个候选码中的属性称为非主属性。比如在关系——学生(学号,姓名,年龄,性别,班级)中,主码是“学号”,那么其他的“姓名”、“年龄”、“性别”、“班级”就都可以称为非主属性。
## 什么是 ER 图?
@@ -63,7 +63,7 @@ ER 图由下面 3 个要素组成:
- **函数依赖(functional dependency)** :若在一张表中,在属性(或属性组)X 的值确定的情况下,必定能确定属性 Y 的值,那么就可以说 Y 函数依赖于 X,写作 X → Y。
- **部分函数依赖(partial functional dependency)** :如果 X→Y,并且存在 X 的一个真子集 X0,使得 X0→Y,则称 Y 对 X 部分函数依赖。比如学生基本信息表 R 中(学号,身份证号,姓名)当然学号属性取值是唯一的,在 R 关系中,(学号,身份证号)->(姓名),(学号)->(姓名),(身份证号)->(姓名);所以姓名部分函数依赖与(学号,身份证号);
- **完全函数依赖(Full functional dependency)** :在一个关系中,若某个非主属性数据项依赖于全部关键字称之为完全函数依赖。比如学生基本信息表 R(学号,班级,姓名)假设不同的班级学号有相同的,班级内学号不能相同,在 R 关系中,(学号,班级)->(姓名),但是(学号)->(姓名)不成立,(班级)->(姓名)不成立,所以姓名完全函数依赖与(学号,班级);
-- **传递函数依赖** : 在关系模式 R(U)中,设 X,Y,Z 是 U 的不同的属性子集,如果 X 确定 Y、Y 确定 Z,且有 X 不包含 Y,Y 不确定 X,(X∪Y)∩Z=空集合,则称 Z 传递函数依赖(transitive functional dependency) 于 X。传递函数依赖会导致数据冗余和异常。传递函数依赖的 Y 和 Z 子集往往同属于某一个事物,因此可将其合并放到一个表中。比如在关系 R(学号 , 姓名, 系名,系主任)中,学号 → 系名,系名 → 系主任,所以存在非主属性系主任对于学号的传递函数依赖。。
+- **传递函数依赖** :在关系模式 R(U)中,设 X,Y,Z 是 U 的不同的属性子集,如果 X 确定 Y、Y 确定 Z,且有 X 不包含 Y,Y 不确定 X,(X∪Y)∩Z=空集合,则称 Z 传递函数依赖(transitive functional dependency) 于 X。传递函数依赖会导致数据冗余和异常。传递函数依赖的 Y 和 Z 子集往往同属于某一个事物,因此可将其合并放到一个表中。比如在关系 R(学号 , 姓名, 系名,系主任)中,学号 → 系名,系名 → 系主任,所以存在非主属性系主任对于学号的传递函数依赖。。
### 3NF(第三范式)
@@ -85,7 +85,7 @@ ER 图由下面 3 个要素组成:
为什么不要用外键呢?大部分人可能会这样回答:
1. **增加了复杂性:** a. 每次做 DELETE 或者 UPDATE 都必须考虑外键约束,会导致开发的时候很痛苦, 测试数据极为不方便; b. 外键的主从关系是定的,假如那天需求有变化,数据库中的这个字段根本不需要和其他表有关联的话就会增加很多麻烦。
-2. **增加了额外工作**: 数据库需要增加维护外键的工作,比如当我们做一些涉及外键字段的增,删,更新操作之后,需要触发相关操作去检查,保证数据的的一致性和正确性,这样会不得不消耗资源;(个人觉得这个不是不用外键的原因,因为即使你不使用外键,你在应用层面也还是要保证的。所以,我觉得这个影响可以忽略不计。)
+2. **增加了额外工作**:数据库需要增加维护外键的工作,比如当我们做一些涉及外键字段的增,删,更新操作之后,需要触发相关操作去检查,保证数据的的一致性和正确性,这样会不得不消耗资源;(个人觉得这个不是不用外键的原因,因为即使你不使用外键,你在应用层面也还是要保证的。所以,我觉得这个影响可以忽略不计。)
3. **对分库分表不友好** :因为分库分表下外键是无法生效的。
4. ......
diff --git a/docs/database/character-set.md b/docs/database/character-set.md
index 5b183c3d9f9..e63b778e43e 100644
--- a/docs/database/character-set.md
+++ b/docs/database/character-set.md
@@ -76,7 +76,7 @@ BIG5 主要针对的是繁体中文,收录了 13000 多个汉字。

-这样我们就搞懂了乱码的本质: **编码和解码时用了不同或者不兼容的字符集** 。
+这样我们就搞懂了乱码的本质:**编码和解码时用了不同或者不兼容的字符集** 。

@@ -112,8 +112,8 @@ MySQL 支持很多种字符编码的方式,比如 UTF-8、GB2312、GBK、BIG5
MySQL 字符编码集中有两套 UTF-8 编码实现:
-- **`utf8`** : `utf8`编码只支持`1-3`个字节 。 在 `utf8` 编码中,中文是占 3 个字节,其他数字、英文、符号占一个字节。但 emoji 符号占 4 个字节,一些较复杂的文字、繁体字也是 4 个字节。
-- **`utf8mb4`** : UTF-8 的完整实现,正版!最多支持使用 4 个字节表示字符,因此,可以用来存储 emoji 符号。
+- **`utf8`** :`utf8`编码只支持`1-3`个字节 。 在 `utf8` 编码中,中文是占 3 个字节,其他数字、英文、符号占一个字节。但 emoji 符号占 4 个字节,一些较复杂的文字、繁体字也是 4 个字节。
+- **`utf8mb4`** :UTF-8 的完整实现,正版!最多支持使用 4 个字节表示字符,因此,可以用来存储 emoji 符号。
**为什么有两套 UTF-8 编码实现呢?** 原因如下:
@@ -151,7 +151,7 @@ Incorrect string value: '\xF0\x9F\x98\x98\xF0\x9F...' for column 'name' at row 1
## 参考
-- 字符集和字符编码(Charset & Encoding):
+- 字符集和字符编码(Charset & Encoding):
- 十分钟搞清字符集和字符编码:
- Unicode-维基百科:
- GB2312-维基百科:
diff --git a/docs/database/mongodb/mongodb-questions-01.md b/docs/database/mongodb/mongodb-questions-01.md
index d7b0af9ffcb..7c9fec7161b 100644
--- a/docs/database/mongodb/mongodb-questions-01.md
+++ b/docs/database/mongodb/mongodb-questions-01.md
@@ -153,7 +153,7 @@ WiredTiger maintains a table's data in memory using a data structure called a B-
使用 B+ 树时,WiredTiger 以 **page** 为基本单位往磁盘读写数据。B+ 树的每个节点为一个 page,共有三种类型的 page:
-- **root page(根节点)** : B+ 树的根节点。
+- **root page(根节点)** :B+ 树的根节点。
- **internal page(内部节点)** :不实际存储数据的中间索引节点。
- **leaf page(叶子节点)**:真正存储数据的叶子节点,包含一个页头(page header)、块头(block header)和真正的数据(key/value),其中页头定义了页的类型、页中实际载荷数据的大小、页中记录条数等信息;块头定义了此页的 checksum、块在磁盘上的寻址位置等信息。
@@ -241,10 +241,10 @@ db.orders.aggregate([
与关系型数据库一样,MongoDB 事务同样具有 ACID 特性:
-- **原子性**(`Atomicity`) : 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
-- **一致性**(`Consistency`): 执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的;
-- **隔离性**(`Isolation`): 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的。WiredTiger 存储引擎支持读未提交( read-uncommitted )、读已提交( read-committed )和快照( snapshot )隔离,MongoDB 启动时默认选快照隔离。在不同隔离级别下,一个事务的生命周期内,可能出现脏读、不可重复读、幻读等现象。
-- **持久性**(`Durability`): 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
+- **原子性**(`Atomicity`) :事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
+- **一致性**(`Consistency`):执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的;
+- **隔离性**(`Isolation`):并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的。WiredTiger 存储引擎支持读未提交( read-uncommitted )、读已提交( read-committed )和快照( snapshot )隔离,MongoDB 启动时默认选快照隔离。在不同隔离级别下,一个事务的生命周期内,可能出现脏读、不可重复读、幻读等现象。
+- **持久性**(`Durability`):一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
关于事务的详细介绍这篇文章就不多说了,感兴趣的可以看看我写的[MySQL 常见面试题总结](https://javaguide.cn/database/mysql/mysql-questions-01.html)这篇文章,里面有详细介绍到。
diff --git a/docs/database/mongodb/mongodb-questions-02.md b/docs/database/mongodb/mongodb-questions-02.md
index fc725e356bb..acbf96e8be0 100644
--- a/docs/database/mongodb/mongodb-questions-02.md
+++ b/docs/database/mongodb/mongodb-questions-02.md
@@ -213,8 +213,8 @@ MongoDB 支持两种分片算法来满足不同的查询需求(摘自[MongoDB
MongoDB 按照分片键(Shard Key)的值的范围将数据拆分为不同的块(Chunk),每个块包含了一段范围内的数据。当分片键的基数大、频率低且值非单调变更时,范围分片更高效。
-- 优点: Mongos 可以快速定位请求需要的数据,并将请求转发到相应的 Shard 节点中。
-- 缺点: 可能导致数据在 Shard 节点上分布不均衡,容易造成读写热点,且不具备写分散性。
+- 优点:Mongos 可以快速定位请求需要的数据,并将请求转发到相应的 Shard 节点中。
+- 缺点:可能导致数据在 Shard 节点上分布不均衡,容易造成读写热点,且不具备写分散性。
- 适用场景:分片键的值不是单调递增或单调递减、分片键的值基数大且重复的频率低、需要范围查询等业务场景。
**2、基于 Hash 值的分片**
diff --git a/docs/database/mysql/how-sql-executed-in-mysql.md b/docs/database/mysql/how-sql-executed-in-mysql.md
index 26adab40ed5..bd7a4a592f5 100644
--- a/docs/database/mysql/how-sql-executed-in-mysql.md
+++ b/docs/database/mysql/how-sql-executed-in-mysql.md
@@ -30,7 +30,7 @@ tag:
简单来说 MySQL 主要分为 Server 层和存储引擎层:
- **Server 层**:主要包括连接器、查询缓存、分析器、优化器、执行器等,所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图,函数等,还有一个通用的日志模块 binlog 日志模块。
-- **存储引擎**: 主要负责数据的存储和读取,采用可以替换的插件式架构,支持 InnoDB、MyISAM、Memory 等多个存储引擎,其中 InnoDB 引擎有自有的日志模块 redolog 模块。**现在最常用的存储引擎是 InnoDB,它从 MySQL 5.5 版本开始就被当做默认存储引擎了。**
+- **存储引擎**:主要负责数据的存储和读取,采用可以替换的插件式架构,支持 InnoDB、MyISAM、Memory 等多个存储引擎,其中 InnoDB 引擎有自有的日志模块 redolog 模块。**现在最常用的存储引擎是 InnoDB,它从 MySQL 5.5 版本开始就被当做默认存储引擎了。**
### 1.2 Server 层基本组件介绍
diff --git a/docs/database/mysql/index-invalidation-caused-by-implicit-conversion.md b/docs/database/mysql/index-invalidation-caused-by-implicit-conversion.md
index 56f4cbcc392..b433a40e571 100644
--- a/docs/database/mysql/index-invalidation-caused-by-implicit-conversion.md
+++ b/docs/database/mysql/index-invalidation-caused-by-implicit-conversion.md
@@ -103,7 +103,7 @@ CALL pre_test1();
查阅 MySQL 相关文档发现是隐式转换造成的,看一下官方的描述:
-> 官方文档: [12.2 Type Conversion in Expression Evaluation](https://dev.mysql.com/doc/refman/5.7/en/type-conversion.html?spm=5176.100239.blogcont47339.5.1FTben)
+> 官方文档:[12.2 Type Conversion in Expression Evaluation](https://dev.mysql.com/doc/refman/5.7/en/type-conversion.html?spm=5176.100239.blogcont47339.5.1FTben)
>
> 当操作符与不同类型的操作数一起使用时,会发生类型转换以使操作数兼容。某些转换是隐式发生的。例如,MySQL 会根据需要自动将字符串转换为数字,反之亦然。以下规则描述了比较操作的转换方式:
>
diff --git a/docs/database/mysql/innodb-implementation-of-mvcc.md b/docs/database/mysql/innodb-implementation-of-mvcc.md
index 0f4877f9a36..a98231b42f1 100644
--- a/docs/database/mysql/innodb-implementation-of-mvcc.md
+++ b/docs/database/mysql/innodb-implementation-of-mvcc.md
@@ -83,7 +83,7 @@ private:
- 当事务回滚时用于将数据恢复到修改前的样子
- 另一个作用是 `MVCC` ,当读取记录时,若该记录被其他事务占用或当前版本对该事务不可见,则可以通过 `undo log` 读取之前的版本数据,以此实现非锁定读
-**在 `InnoDB` 存储引擎中 `undo log` 分为两种: `insert undo log` 和 `update undo log`:**
+**在 `InnoDB` 存储引擎中 `undo log` 分为两种:`insert undo log` 和 `update undo log`:**
1. **`insert undo log`** :指在 `insert` 操作中产生的 `undo log`。因为 `insert` 操作的记录只对事务本身可见,对其他事务不可见,故该 `undo log` 可以在事务提交后直接删除。不需要进行 `purge` 操作
diff --git a/docs/database/mysql/mysql-high-performance-optimization-specification-recommendations.md b/docs/database/mysql/mysql-high-performance-optimization-specification-recommendations.md
index cc92705e818..f2c9107977e 100644
--- a/docs/database/mysql/mysql-high-performance-optimization-specification-recommendations.md
+++ b/docs/database/mysql/mysql-high-performance-optimization-specification-recommendations.md
@@ -90,7 +90,7 @@ InnoDB 支持事务,支持行级锁,更好的恢复性,高并发下性能
MySQL 提供了两个方法来处理 ip 地址
-- `INET_ATON()` : 把 ip 转为无符号整型 (4-8 位)
+- `INET_ATON()` :把 ip 转为无符号整型 (4-8 位)
- `INET_NTOA()` :把整型的 ip 转为地址
插入数据前,先用 `INET_ATON()` 把 ip 地址转为整型,显示数据时,使用 `INET_NTOA()` 把整型的 ip 地址转为地址显示即可。
diff --git a/docs/database/mysql/mysql-index.md b/docs/database/mysql/mysql-index.md
index 54e28dc7a46..a4d447e42b4 100644
--- a/docs/database/mysql/mysql-index.md
+++ b/docs/database/mysql/mysql-index.md
@@ -161,7 +161,7 @@ PS: 不懂的同学可以暂存疑,慢慢往下看,后面会有答案的,
**缺点** :
- **依赖于有序的数据** :因为 B+树是多路平衡树,如果索引的数据不是有序的,那么就需要在插入时排序,如果数据是整型还好,否则类似于字符串或 UUID 这种又长又难比较的数据,插入或查找的速度肯定比较慢。
-- **更新代价大** : 如果对索引列的数据被修改时,那么对应的索引也将会被修改,而且聚簇索引的叶子节点还存放着数据,修改代价肯定是较大的,所以对于主键索引来说,主键一般都是不可被修改的。
+- **更新代价大** :如果对索引列的数据被修改时,那么对应的索引也将会被修改,而且聚簇索引的叶子节点还存放着数据,修改代价肯定是较大的,所以对于主键索引来说,主键一般都是不可被修改的。
### 非聚簇索引(非聚集索引)
diff --git a/docs/database/mysql/mysql-logs.md b/docs/database/mysql/mysql-logs.md
index 13540a67e3c..1a89e5b6304 100644
--- a/docs/database/mysql/mysql-logs.md
+++ b/docs/database/mysql/mysql-logs.md
@@ -118,7 +118,7 @@ tag:
相信大家都知道 `redo log` 的作用和它的刷盘时机、存储形式。
-现在我们来思考一个问题: **只要每次把修改后的数据页直接刷盘不就好了,还有 `redo log` 什么事?**
+现在我们来思考一个问题:**只要每次把修改后的数据页直接刷盘不就好了,还有 `redo log` 什么事?**
它们不都是刷盘么?差别在哪里?
diff --git a/docs/database/mysql/mysql-questions-01.md b/docs/database/mysql/mysql-questions-01.md
index 41f06196266..e4dab6102ca 100644
--- a/docs/database/mysql/mysql-questions-01.md
+++ b/docs/database/mysql/mysql-questions-01.md
@@ -85,7 +85,7 @@ MySQL 主要具有下面这些优点:
- **分析器:** 没有命中缓存的话,SQL 语句就会经过分析器,分析器说白了就是要先看你的 SQL 语句要干嘛,再检查你的 SQL 语句语法是否正确。
- **优化器:** 按照 MySQL 认为最优的方案去执行。
- **执行器:** 执行语句,然后从存储引擎返回数据。 执行语句之前会先判断是否有权限,如果没有权限的话,就会报错。
-- **插件式存储引擎** : 主要负责数据的存储和读取,采用的是插件式架构,支持 InnoDB、MyISAM、Memory 等多种存储引擎。
+- **插件式存储引擎** :主要负责数据的存储和读取,采用的是插件式架构,支持 InnoDB、MyISAM、Memory 等多种存储引擎。
## MySQL 存储引擎
@@ -237,7 +237,7 @@ InnoDB 的性能比 MyISAM 更强大,不管是在读写混合模式下还是
## MySQL 索引
-MySQL 索引相关的问题比较多,对于面试和工作都比较重要,于是,我单独抽了一篇文章专门来总结 MySQL 索引相关的知识点和问题: [MySQL 索引详解](https://javaguide.cn/database/mysql/mysql-index.html) 。
+MySQL 索引相关的问题比较多,对于面试和工作都比较重要,于是,我单独抽了一篇文章专门来总结 MySQL 索引相关的知识点和问题:[MySQL 索引详解](https://javaguide.cn/database/mysql/mysql-index.html) 。
## MySQL 查询缓存
@@ -335,10 +335,10 @@ COMMIT;

-1. **原子性**(`Atomicity`) : 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
-2. **一致性**(`Consistency`): 执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的;
-3. **隔离性**(`Isolation`): 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
-4. **持久性**(`Durability`): 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
+1. **原子性**(`Atomicity`) :事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
+2. **一致性**(`Consistency`):执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的;
+3. **隔离性**(`Isolation`):并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
+4. **持久性**(`Durability`):一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
🌈 这里要额外补充一点:**只有保证了事务的持久性、原子性、隔离性之后,一致性才能得到保障。也就是说 A、I、D 是手段,C 是目的!** 想必大家也和我一样,被 ACID 这个概念被误导了很久! 我也是看周志明老师的公开课[《周志明的软件架构课》](https://time.geekbang.org/opencourse/intro/100064201)才搞清楚的(多看好书!!!)。
@@ -426,10 +426,10 @@ MVCC 在 MySQL 中实现所依赖的手段主要是: **隐藏字段、read view
SQL 标准定义了四个隔离级别:
-- **READ-UNCOMMITTED(读取未提交)** : 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
-- **READ-COMMITTED(读取已提交)** : 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
-- **REPEATABLE-READ(可重复读)** : 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
-- **SERIALIZABLE(可串行化)** : 最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
+- **READ-UNCOMMITTED(读取未提交)** :最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
+- **READ-COMMITTED(读取已提交)** :允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
+- **REPEATABLE-READ(可重复读)** :对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
+- **SERIALIZABLE(可串行化)** :最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
---
@@ -606,13 +606,13 @@ CREATE TABLE `sequence_id` (
| 1 | 连续模式(MySQL 8.0 之前默认) |
| 2 | 交错模式(MySQL 8.0 之后默认) |
-交错模式下,所有的“INSERT-LIKE”语句(所有的插入语句,包括: `INSERT`、`REPLACE`、`INSERT…SELECT`、`REPLACE…SELECT`、`LOAD DATA`等)都不使用表级锁,使用的是轻量级互斥锁实现,多条插入语句可以并发执行,速度更快,扩展性也更好。
+交错模式下,所有的“INSERT-LIKE”语句(所有的插入语句,包括:`INSERT`、`REPLACE`、`INSERT…SELECT`、`REPLACE…SELECT`、`LOAD DATA`等)都不使用表级锁,使用的是轻量级互斥锁实现,多条插入语句可以并发执行,速度更快,扩展性也更好。
不过,如果你的 MySQL 数据库有主从同步需求并且 Binlog 存储格式为 Statement 的话,不要将 InnoDB 自增锁模式设置为交叉模式,不然会有数据不一致性问题。这是因为并发情况下插入语句的执行顺序就无法得到保障。
> 如果 MySQL 采用的格式为 Statement ,那么 MySQL 的主从同步实际上同步的就是一条一条的 SQL 语句。
-最后,再推荐一篇文章: [为什么 MySQL 的自增主键不单调也不连续](https://draveness.me/whys-the-design-mysql-auto-increment/) 。
+最后,再推荐一篇文章:[为什么 MySQL 的自增主键不单调也不连续](https://draveness.me/whys-the-design-mysql-auto-increment/) 。
## MySQL 性能优化
@@ -638,7 +638,7 @@ CREATE TABLE `sequence_id` (
MySQL 提供了两个方法来处理 ip 地址
-- `INET_ATON()` : 把 ip 转为无符号整型 (4-8 位)
+- `INET_ATON()` :把 ip 转为无符号整型 (4-8 位)
- `INET_NTOA()` :把整型的 ip 转为地址
插入数据前,先用 `INET_ATON()` 把 ip 地址转为整型,显示数据时,使用 `INET_NTOA()` 把整型的 ip 地址转为地址显示即可。
@@ -692,7 +692,7 @@ mysql> EXPLAIN SELECT `score`,`name` FROM `cus_order` ORDER BY `score` DESC;
### 读写分离和分库分表了解吗?
-读写分离和分库分表相关的问题比较多,于是,我单独写了一篇文章来介绍: [读写分离和分库分表详解](https://javaguide.cn/high-performance/read-and-write-separation-and-library-subtable.html)。
+读写分离和分库分表相关的问题比较多,于是,我单独写了一篇文章来介绍:[读写分离和分库分表详解](https://javaguide.cn/high-performance/read-and-write-separation-and-library-subtable.html)。
## MySQL 学习资料推荐
diff --git a/docs/database/mysql/some-thoughts-on-database-storage-time.md b/docs/database/mysql/some-thoughts-on-database-storage-time.md
index ace7dd3a860..7994e4a22af 100644
--- a/docs/database/mysql/some-thoughts-on-database-storage-time.md
+++ b/docs/database/mysql/some-thoughts-on-database-storage-time.md
@@ -103,7 +103,7 @@ SET GLOBAL time_zone = 'Europe/Helsinki';
Timestamp 只需要使用 4 个字节的存储空间,但是 DateTime 需要耗费 8 个字节的存储空间。但是,这样同样造成了一个问题,Timestamp 表示的时间范围更小。
- DateTime :1000-01-01 00:00:00 ~ 9999-12-31 23:59:59
-- Timestamp: 1970-01-01 00:00:01 ~ 2037-12-31 23:59:59
+- Timestamp:1970-01-01 00:00:01 ~ 2037-12-31 23:59:59
> Timestamp 在不同版本的 MySQL 中有细微差别。
diff --git a/docs/database/mysql/transaction-isolation-level.md b/docs/database/mysql/transaction-isolation-level.md
index bab41ba116f..d143906aba5 100644
--- a/docs/database/mysql/transaction-isolation-level.md
+++ b/docs/database/mysql/transaction-isolation-level.md
@@ -13,10 +13,10 @@ tag:
SQL 标准定义了四个隔离级别:
-- **READ-UNCOMMITTED(读取未提交)** : 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
-- **READ-COMMITTED(读取已提交)** : 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
-- **REPEATABLE-READ(可重复读)** : 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
-- **SERIALIZABLE(可串行化)** : 最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
+- **READ-UNCOMMITTED(读取未提交)** :最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
+- **READ-COMMITTED(读取已提交)** :允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
+- **REPEATABLE-READ(可重复读)** :对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
+- **SERIALIZABLE(可串行化)** :最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
---
@@ -43,7 +43,7 @@ MySQL> SELECT @@tx_isolation;
但是!InnoDB 实现的 REPEATABLE-READ 隔离级别其实是可以解决幻读问题发生的,主要有下面两种情况:
- **快照读** :由 MVCC 机制来保证不出现幻读。
-- **当前读** : 使用 Next-Key Lock 进行加锁来保证不出现幻读,Next-Key Lock 是行锁(Record Lock)和间隙锁(Gap Lock)的结合,行锁只能锁住已经存在的行,为了避免插入新行,需要依赖间隙锁。
+- **当前读** :使用 Next-Key Lock 进行加锁来保证不出现幻读,Next-Key Lock 是行锁(Record Lock)和间隙锁(Gap Lock)的结合,行锁只能锁住已经存在的行,为了避免插入新行,需要依赖间隙锁。
因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是 **READ-COMMITTED** ,但是你要知道的是 InnoDB 存储引擎默认使用 **REPEATABLE-READ** 并不会有任何性能损失。
diff --git a/docs/database/nosql.md b/docs/database/nosql.md
index 5c3afd1bd66..b1b392280b0 100644
--- a/docs/database/nosql.md
+++ b/docs/database/nosql.md
@@ -13,7 +13,7 @@ NoSQL(Not Only SQL 的缩写)泛指非关系型的数据库,主要针对
一个常见的误解是 NoSQL 数据库或非关系型数据库不能很好地存储关系型数据。NoSQL 数据库可以存储关系型数据—它们与关系型数据库的存储方式不同。
-NoSQL 数据库代表:HBase 、Cassandra、MongoDB、Redis。
+NoSQL 数据库代表:HBase、Cassandra、MongoDB、Redis。

@@ -23,7 +23,7 @@ NoSQL 数据库代表:HBase 、Cassandra、MongoDB、Redis。
| :----------- | -------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| 数据存储模型 | 结构化存储,具有固定行和列的表格 | 非结构化存储。文档:JSON 文档,键值:键值对,宽列:包含行和动态列的表,图:节点和边 |
| 发展历程 | 开发于 1970 年代,重点是减少数据重复 | 开发于 2000 年代后期,重点是提升可扩展性,减少大规模数据的存储成本 |
-| 例子 | Oracle、MySQL、Microsoft SQL Server 、PostgreSQL | 文档:MongoDB、CouchDB,键值:Redis 、DynamoDB,宽列:Cassandra 、 HBase,图表:Neo4j 、 Amazon Neptune、Giraph |
+| 例子 | Oracle、MySQL、Microsoft SQL Server、PostgreSQL | 文档:MongoDB、CouchDB,键值:Redis、DynamoDB,宽列:Cassandra、 HBase,图表:Neo4j、 Amazon Neptune、Giraph |
| ACID 属性 | 提供原子性、一致性、隔离性和持久性 (ACID) 属性 | 通常不支持 ACID 事务,为了可扩展、高性能进行了权衡,少部分支持比如 MongoDB 。不过,MongoDB 对 ACID 事务 的支持和 MySQL 还是有所区别的。 |
| 性能 | 性能通常取决于磁盘子系统。要获得最佳性能,通常需要优化查询、索引和表结构。 | 性能通常由底层硬件集群大小、网络延迟以及调用应用程序来决定。 |
| 扩展 | 垂直(使用性能更强大的服务器进行扩展)、读写分离、分库分表 | 横向(增加服务器的方式横向扩展,通常是基于分片机制) |
diff --git a/docs/database/redis/redis-common-blocking-problems-summary.md b/docs/database/redis/redis-common-blocking-problems-summary.md
index 6249c42e78c..ffd3f99113e 100644
--- a/docs/database/redis/redis-common-blocking-problems-summary.md
+++ b/docs/database/redis/redis-common-blocking-problems-summary.md
@@ -11,7 +11,7 @@ tag:
## O(n) 命令
-使用` O(n)` 命令可能会导致阻塞,例如`keys *` 、`hgetall`、`lrange`、`smembers`、`zrange`、`sinter` 、`sunion` 命令。这些命令时间复杂度是 O(n),有时候也会全表扫描,随着 n 的增大耗时也会越大从而导致客户端阻塞。
+使用` O(n)` 命令可能会导致阻塞,例如`keys *`、`hgetall`、`lrange`、`smembers`、`zrange`、`sinter`、`sunion` 命令。这些命令时间复杂度是 O(n),有时候也会全表扫描,随着 n 的增大耗时也会越大从而导致客户端阻塞。
不过, 这些命令并不是一定不能使用,但是需要明确 N 的值。有遍历的需求可以使用 `hscan`、`sscan`、`zscan` 代替。
diff --git a/docs/database/redis/redis-data-structures-01.md b/docs/database/redis/redis-data-structures-01.md
index 1d2eb28b262..0b5f0b68c8e 100644
--- a/docs/database/redis/redis-data-structures-01.md
+++ b/docs/database/redis/redis-data-structures-01.md
@@ -121,7 +121,7 @@ OK
**需要存储常规数据的场景**
- 举例 :缓存 session、token、图片地址、序列化后的对象(相比较于 Hash 存储更节省内存)。
-- 相关命令 : `SET`、`GET`。
+- 相关命令 :`SET`、`GET`。
**需要计数的场景**
@@ -214,7 +214,7 @@ Redis 中的 List 其实就是链表数据结构的实现。我在 [线性数据
**信息流展示**
- 举例 :最新文章、最新动态。
-- 相关命令 : `LPUSH`、`LRANGE`。
+- 相关命令 :`LPUSH`、`LRANGE`。
**消息队列**
@@ -329,7 +329,7 @@ Redis 中的 Set 类型是一种无序集合,集合中的元素没有先后顺
```
- `mySet` : `value1`、`value2` 。
-- `mySet2` : `value2` 、`value3` 。
+- `mySet2` :`value2`、`value3` 。
**求交集** :
@@ -367,7 +367,7 @@ Redis 中的 Set 类型是一种无序集合,集合中的元素没有先后顺
**需要获取多个数据源交集、并集和差集的场景**
-- 举例 :共同好友(交集)、共同粉丝(交集)、共同关注(交集)、好友推荐(差集)、音乐推荐(差集) 、订阅号推荐(差集+交集) 等场景。
+- 举例 :共同好友(交集)、共同粉丝(交集)、共同关注(交集)、好友推荐(差集)、音乐推荐(差集)、订阅号推荐(差集+交集) 等场景。
- 相关命令:`SINTER`(交集)、`SINTERSTORE` (交集)、`SUNION` (并集)、`SUNIONSTORE`(并集)、`SDIFF`(差集)、`SDIFFSTORE` (差集)。

@@ -422,7 +422,7 @@ Sorted Set 类似于 Set,但和 Set 相比,Sorted Set 增加了一个权重
```
- `myZset` : `value1`(2.0)、`value2`(1.0) 。
-- `myZset2` : `value2` (4.0)、`value3`(3.0) 。
+- `myZset2` :`value2` (4.0)、`value3`(3.0) 。
**获取指定元素的排名** :
@@ -470,7 +470,7 @@ value1
**需要随机获取数据源中的元素根据某个权重进行排序的场景**
- 举例 :各种排行榜比如直播间送礼物的排行榜、朋友圈的微信步数排行榜、王者荣耀中的段位排行榜、话题热度排行榜等等。
-- 相关命令 :`ZRANGE` (从小到大排序) 、 `ZREVRANGE` (从大到小排序)、`ZREVRANK` (指定元素排名)。
+- 相关命令 :`ZRANGE` (从小到大排序)、 `ZREVRANGE` (从大到小排序)、`ZREVRANK` (指定元素排名)。

@@ -481,11 +481,11 @@ value1
**需要存储的数据有优先级或者重要程度的场景** 比如优先级任务队列。
- 举例 :优先级任务队列。
-- 相关命令 :`ZRANGE` (从小到大排序) 、 `ZREVRANGE` (从大到小排序)、`ZREVRANK` (指定元素排名)。
+- 相关命令 :`ZRANGE` (从小到大排序)、 `ZREVRANGE` (从大到小排序)、`ZREVRANK` (指定元素排名)。
## 参考
- Redis Data Structures :https://redis.com/redis-enterprise/data-structures/ 。
-- Redis Commands : https://redis.io/commands/ 。
+- Redis Commands :https://redis.io/commands/ 。
- Redis Data types tutorial:https://redis.io/docs/manual/data-types/data-types-tutorial/ 。
- Redis 存储对象信息是用 Hash 还是 String : https://segmentfault.com/a/1190000040032006
diff --git a/docs/database/redis/redis-persistence.md b/docs/database/redis/redis-persistence.md
index b3666e77cad..75bb6db46b5 100644
--- a/docs/database/redis/redis-persistence.md
+++ b/docs/database/redis/redis-persistence.md
@@ -80,7 +80,7 @@ AOF 持久化功能的实现可以简单分为 5 步:
这里对上面提到的一些 Linux 系统调用再做一遍解释:
- `write` :写入系统内核缓冲区之后直接返回(仅仅是写到缓冲区),不会立即同步到硬盘。虽然提高了效率,但也带来了数据丢失的风险。同步硬盘操作通常依赖于系统调度机制,Linux 内核通常为 30s 同步一次,具体值取决于写出的数据量和 I/O 缓冲区的状态。
-- `fsync` : `fsync`用于强制刷新系统内核缓冲区(同步到到磁盘),确保写磁盘操作结束才会返回。
+- `fsync` :`fsync`用于强制刷新系统内核缓冲区(同步到到磁盘),确保写磁盘操作结束才会返回。
AOF 工作流程图如下:
diff --git a/docs/database/redis/redis-questions-01.md b/docs/database/redis/redis-questions-01.md
index 06d3c49694a..7538b82c1a1 100644
--- a/docs/database/redis/redis-questions-01.md
+++ b/docs/database/redis/redis-questions-01.md
@@ -20,7 +20,7 @@ head:
[Redis](https://redis.io/) 是一个基于 C 语言开发的开源数据库(BSD 许可),与传统数据库不同的是 Redis 的数据是存在内存中的(内存数据库),读写速度非常快,被广泛应用于缓存方向。并且,Redis 存储的是 KV 键值对数据。
-为了满足不同的业务场景,Redis 内置了多种数据类型实现(比如 String、Hash、Sorted Set、Bitmap、HyperLogLog、GEO)。并且,Redis 还支持事务 、持久化、Lua 脚本、多种开箱即用的集群方案(Redis Sentinel、Redis Cluster)。
+为了满足不同的业务场景,Redis 内置了多种数据类型实现(比如 String、Hash、Sorted Set、Bitmap、HyperLogLog、GEO)。并且,Redis 还支持事务、持久化、Lua 脚本、多种开箱即用的集群方案(Redis Sentinel、Redis Cluster)。
Redis 没有外部依赖,Linux 和 OS X 是 Redis 开发和测试最多的两个操作系统,官方推荐生产环境使用 Linux 部署 Redis。
@@ -103,7 +103,7 @@ Memcached 是分布式缓存最开始兴起的那会,比较常用的。后来
### Redis 除了做缓存,还能做什么?
-- **分布式锁** : 通过 Redis 来做分布式锁是一种比较常见的方式。通常情况下,我们都是基于 Redisson 来实现分布式锁。关于 Redis 实现分布式锁的详细介绍,可以看我写的这篇文章:[分布式锁详解](https://javaguide.cn/distributed-system/distributed-lock.html) 。
+- **分布式锁** :通过 Redis 来做分布式锁是一种比较常见的方式。通常情况下,我们都是基于 Redisson 来实现分布式锁。关于 Redis 实现分布式锁的详细介绍,可以看我写的这篇文章:[分布式锁详解](https://javaguide.cn/distributed-system/distributed-lock.html) 。
- **限流** :一般是通过 Redis + Lua 脚本的方式来实现限流。相关阅读:[《我司用了 6 年的 Redis 分布式限流器,可以说是非常厉害了!》](https://mp.weixin.qq.com/s/kyFAWH3mVNJvurQDt4vchA)。
- **消息队列** :Redis 自带的 list 数据结构可以作为一个简单的队列使用。Redis 5.0 中增加的 Stream 类型的数据结构更加适合用来做消息队列。它比较类似于 Kafka,有主题和消费组的概念,支持消息持久化以及 ACK 机制。
- **复杂业务场景** :通过 Redis 以及 Redis 扩展(比如 Redisson)提供的数据结构,我们可以很方便地完成很多复杂的业务场景比如通过 bitmap 统计活跃用户、通过 sorted set 维护排行榜。
@@ -267,8 +267,8 @@ struct __attribute__ ((__packed__)) sdshdr64 {
SDS 相比于 C 语言中的字符串有如下提升:
1. **可以避免缓冲区溢出** :C 语言中的字符串被修改(比如拼接)时,一旦没有分配足够长度的内存空间,就会造成缓冲区溢出。SDS 被修改时,会先根据 len 属性检查空间大小是否满足要求,如果不满足,则先扩展至所需大小再进行修改操作。
-2. **获取字符串长度的复杂度较低** : C 语言中的字符串的长度通常是经过遍历计数来实现的,时间复杂度为 O(n)。SDS 的长度获取直接读取 len 属性即可,时间复杂度为 O(1)。
-3. **减少内存分配次数** : 为了避免修改(增加/减少)字符串时,每次都需要重新分配内存(C 语言的字符串是这样的),SDS 实现了空间预分配和惰性空间释放两种优化策略。当 SDS 需要增加字符串时,Redis 会为 SDS 分配好内存,并且根据特定的算法分配多余的内存,这样可以减少连续执行字符串增长操作所需的内存重分配次数。当 SDS 需要减少字符串时,这部分内存不会立即被回收,会被记录下来,等待后续使用(支持手动释放,有对应的 API)。
+2. **获取字符串长度的复杂度较低** :C 语言中的字符串的长度通常是经过遍历计数来实现的,时间复杂度为 O(n)。SDS 的长度获取直接读取 len 属性即可,时间复杂度为 O(1)。
+3. **减少内存分配次数** :为了避免修改(增加/减少)字符串时,每次都需要重新分配内存(C 语言的字符串是这样的),SDS 实现了空间预分配和惰性空间释放两种优化策略。当 SDS 需要增加字符串时,Redis 会为 SDS 分配好内存,并且根据特定的算法分配多余的内存,这样可以减少连续执行字符串增长操作所需的内存重分配次数。当 SDS 需要减少字符串时,这部分内存不会立即被回收,会被记录下来,等待后续使用(支持手动释放,有对应的 API)。
4. **二进制安全** :C 语言中的字符串以空字符 `\0` 作为字符串结束的标识,这存在一些问题,像一些二进制文件(比如图片、视频、音频)就可能包括空字符,C 字符串无法正确保存。SDS 使用 len 属性判断字符串是否结束,不存在这个问题。
🤐 多提一嘴,很多文章里 SDS 的定义是下面这样的:
@@ -306,7 +306,7 @@ struct sdshdr {
Redis 中有一个叫做 `sorted set` 的数据结构经常被用在各种排行榜的场景,比如直播间送礼物的排行榜、朋友圈的微信步数排行榜、王者荣耀中的段位排行榜、话题热度排行榜等等。
-相关的一些 Redis 命令: `ZRANGE` (从小到大排序) 、 `ZREVRANGE` (从大到小排序)、`ZREVRANK` (指定元素排名)。
+相关的一些 Redis 命令: `ZRANGE` (从小到大排序)、 `ZREVRANGE` (从大到小排序)、`ZREVRANK` (指定元素排名)。

@@ -321,7 +321,7 @@ Redis 中 `Set` 是一种无序集合,集合中的元素没有先后顺序但
Set 的常见应用场景如下:
- 存放的数据不能重复的场景:网站 UV 统计(数据量巨大的场景还是 `HyperLogLog`更适合一些)、文章点赞、动态点赞等等。
-- 需要获取多个数据源交集、并集和差集的场景:共同好友(交集)、共同粉丝(交集)、共同关注(交集)、好友推荐(差集)、音乐推荐(差集) 、订阅号推荐(差集+交集) 等等。
+- 需要获取多个数据源交集、并集和差集的场景:共同好友(交集)、共同粉丝(交集)、共同关注(交集)、好友推荐(差集)、音乐推荐(差集)、订阅号推荐(差集+交集) 等等。
- 需要随机获取数据源中的元素的场景:抽奖系统、随机点名等等。
### 使用 Set 实现抽奖系统怎么做?
@@ -329,7 +329,7 @@ Set 的常见应用场景如下:
如果想要使用 Set 实现一个简单的抽奖系统的话,直接使用下面这几个命令就可以了:
- `SADD key member1 member2 ...`:向指定集合添加一个或多个元素。
-- `SPOP key count` : 随机移除并获取指定集合中一个或多个元素,适合不允许重复中奖的场景。
+- `SPOP key count` :随机移除并获取指定集合中一个或多个元素,适合不允许重复中奖的场景。
- `SRANDMEMBER key count` : 随机获取指定集合中指定数量的元素,适合允许重复中奖的场景。
### 使用 Bitmap 统计活跃用户怎么做?
@@ -392,7 +392,7 @@ PFCOUNT PAGE_1:UV
## Redis 持久化机制(重要)
-Redis 持久化机制(RDB 持久化、AOF 持久化、RDB 和 AOF 的混合持久化) 相关的问题比较多,也比较重要,于是我单独抽了一篇文章来总结 Redis 持久化机制相关的知识点和问题: [Redis 持久化机制详解](./redis-persistence.md) 。
+Redis 持久化机制(RDB 持久化、AOF 持久化、RDB 和 AOF 的混合持久化) 相关的问题比较多,也比较重要,于是我单独抽了一篇文章来总结 Redis 持久化机制相关的知识点和问题:[Redis 持久化机制详解](./redis-persistence.md) 。
## Redis 线程模型(重要)
@@ -415,7 +415,7 @@ Redis 持久化机制(RDB 持久化、AOF 持久化、RDB 和 AOF 的混合持
Redis 通过 **IO 多路复用程序** 来监听来自客户端的大量连接(或者说是监听多个 socket),它会将感兴趣的事件及类型(读、写)注册到内核中并监听每个事件是否发生。
-这样的好处非常明显: **I/O 多路复用技术的使用让 Redis 不需要额外创建多余的线程来监听客户端的大量连接,降低了资源的消耗**(和 NIO 中的 `Selector` 组件很像)。
+这样的好处非常明显:**I/O 多路复用技术的使用让 Redis 不需要额外创建多余的线程来监听客户端的大量连接,降低了资源的消耗**(和 NIO 中的 `Selector` 组件很像)。
文件事件处理器(file event handler)主要是包含 4 个部分:
@@ -563,7 +563,7 @@ typedef struct redisDb {
常用的过期数据的删除策略就两个(重要!自己造缓存轮子的时候需要格外考虑的东西):
1. **惰性删除** :只会在取出 key 的时候才对数据进行过期检查。这样对 CPU 最友好,但是可能会造成太多过期 key 没有被删除。
-2. **定期删除** : 每隔一段时间抽取一批 key 执行删除过期 key 操作。并且,Redis 底层会通过限制删除操作执行的时长和频率来减少删除操作对 CPU 时间的影响。
+2. **定期删除** :每隔一段时间抽取一批 key 执行删除过期 key 操作。并且,Redis 底层会通过限制删除操作执行的时长和频率来减少删除操作对 CPU 时间的影响。
定期删除对内存更加友好,惰性删除对 CPU 更加友好。两者各有千秋,所以 Redis 采用的是 **定期删除+惰性/懒汉式删除** 。
diff --git a/docs/database/redis/redis-questions-02.md b/docs/database/redis/redis-questions-02.md
index 8d90c05ddd1..286a530ea95 100644
--- a/docs/database/redis/redis-questions-02.md
+++ b/docs/database/redis/redis-questions-02.md
@@ -134,7 +134,7 @@ Redis 官网相关介绍 [https://redis.io/topics/transactions](https://redis.io
### Redis 事务支持原子性吗?
-Redis 的事务和我们平时理解的关系型数据库的事务不同。我们知道事务具有四大特性: **1. 原子性**,**2. 隔离性**,**3. 持久性**,**4. 一致性**。
+Redis 的事务和我们平时理解的关系型数据库的事务不同。我们知道事务具有四大特性:**1. 原子性**,**2. 隔离性**,**3. 持久性**,**4. 一致性**。
1. **原子性(Atomicity):** 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
2. **隔离性(Isolation):** 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
@@ -168,7 +168,7 @@ appendfsync everysec #每秒钟调用fsync函数同步一次AOF文件
appendfsync no #让操作系统决定何时进行同步,一般为30秒一次
```
-AOF 持久化的`fsync`策略为 no 、everysec 时都会存在数据丢失的情况 。always 下可以基本是可以满足持久性要求的,但性能太差,实际开发过程中不会使用。
+AOF 持久化的`fsync`策略为 no、everysec 时都会存在数据丢失的情况 。always 下可以基本是可以满足持久性要求的,但性能太差,实际开发过程中不会使用。
因此,Redis 事务的持久性也是没办法保证的。
@@ -349,9 +349,9 @@ Biggest string found '"ballcat:oauth:refresh_auth:f6cdb384-9a9d-4f2f-af01-dc3f28
**1)缓存无效 key**
-如果缓存和数据库都查不到某个 key 的数据就写一个到 Redis 中去并设置过期时间,具体命令如下: `SET key value EX 10086` 。这种方式可以解决请求的 key 变化不频繁的情况,如果黑客恶意攻击,每次构建不同的请求 key,会导致 Redis 中缓存大量无效的 key 。很明显,这种方案并不能从根本上解决此问题。如果非要用这种方式来解决穿透问题的话,尽量将无效的 key 的过期时间设置短一点比如 1 分钟。
+如果缓存和数据库都查不到某个 key 的数据就写一个到 Redis 中去并设置过期时间,具体命令如下:`SET key value EX 10086` 。这种方式可以解决请求的 key 变化不频繁的情况,如果黑客恶意攻击,每次构建不同的请求 key,会导致 Redis 中缓存大量无效的 key 。很明显,这种方案并不能从根本上解决此问题。如果非要用这种方式来解决穿透问题的话,尽量将无效的 key 的过期时间设置短一点比如 1 分钟。
-另外,这里多说一嘴,一般情况下我们是这样设计 key 的: `表名:列名:主键名:主键值` 。
+另外,这里多说一嘴,一般情况下我们是这样设计 key 的:`表名:列名:主键名:主键值` 。
如果用 Java 代码展示的话,差不多是下面这样的:
@@ -386,7 +386,7 @@ public Object getObjectInclNullById(Integer id) {

-但是,需要注意的是布隆过滤器可能会存在误判的情况。总结来说就是: **布隆过滤器说某个元素存在,小概率会误判。布隆过滤器说某个元素不在,那么这个元素一定不在。**
+但是,需要注意的是布隆过滤器可能会存在误判的情况。总结来说就是:**布隆过滤器说某个元素存在,小概率会误判。布隆过滤器说某个元素不在,那么这个元素一定不在。**
_为什么会出现误判的情况呢? 我们还要从布隆过滤器的原理来说!_
@@ -468,7 +468,7 @@ Cache Aside Pattern 中遇到写请求是这样的:更新 DB,然后直接删
如果更新数据库成功,而删除缓存这一步失败的情况的话,简单说两个解决方案:
1. **缓存失效时间变短(不推荐,治标不治本)** :我们让缓存数据的过期时间变短,这样的话缓存就会从数据库中加载数据。另外,这种解决办法对于先操作缓存后操作数据库的场景不适用。
-2. **增加 cache 更新重试机制(常用)**: 如果 cache 服务当前不可用导致缓存删除失败的话,我们就隔一段时间进行重试,重试次数可以自己定。如果多次重试还是失败的话,我们可以把当前更新失败的 key 存入队列中,等缓存服务可用之后,再将缓存中对应的 key 删除即可。
+2. **增加 cache 更新重试机制(常用)**:如果 cache 服务当前不可用导致缓存删除失败的话,我们就隔一段时间进行重试,重试次数可以自己定。如果多次重试还是失败的话,我们可以把当前更新失败的 key 存入队列中,等缓存服务可用之后,再将缓存中对应的 key 删除即可。
相关文章推荐:[缓存和数据库一致性问题,看这篇就够了 - 水滴与银弹](https://mp.weixin.qq.com/s?__biz=MzIyOTYxNDI5OA==&mid=2247487312&idx=1&sn=fa19566f5729d6598155b5c676eee62d&chksm=e8beb8e5dfc931f3e35655da9da0b61c79f2843101c130cf38996446975014f958a6481aacf1&scene=178&cur_album_id=1699766580538032128#rd)。
@@ -505,7 +505,7 @@ Cache Aside Pattern 中遇到写请求是这样的:更新 DB,然后直接删
实际使用 Redis 的过程中,我们尽量要准守一些常见的规范,比如:
1. 使用连接池:避免频繁创建关闭客户端连接。
-2. 尽量不使用 O(n)指令,使用 O(N)命令时要关注 N 的数量 :例如 `hgetall`、`lrange`、`smembers`、`zrange`、`sinter` 、`sunion` 命令并非不能使用,但是需要明确 N 的值。有遍历的需求可以使用 `hscan`、`sscan`、`zscan` 代替。
+2. 尽量不使用 O(n)指令,使用 O(N)命令时要关注 N 的数量 :例如 `hgetall`、`lrange`、`smembers`、`zrange`、`sinter`、`sunion` 命令并非不能使用,但是需要明确 N 的值。有遍历的需求可以使用 `hscan`、`sscan`、`zscan` 代替。
3. 使用批量操作减少网络传输 :原生批量操作命令(比如 `mget`、`mset`等等)、pipeline、Lua 脚本。
4. 尽量不适用 Redis 事务:Redis 事务实现的功能比较鸡肋,可以使用 Lua 脚本代替。
5. 禁止长时间开启 monitor:对性能影响比较大。
diff --git a/docs/database/sql/sql-syntax-summary.md b/docs/database/sql/sql-syntax-summary.md
index 13601ad62a1..52777c678c5 100644
--- a/docs/database/sql/sql-syntax-summary.md
+++ b/docs/database/sql/sql-syntax-summary.md
@@ -40,7 +40,7 @@ SQL 语法结构包括:
#### SQL 语法要点
-- **SQL 语句不区分大小写**,但是数据库表名、列名和值是否区分,依赖于具体的 DBMS 以及配置。例如:`SELECT` 与 `select` 、`Select` 是相同的。
+- **SQL 语句不区分大小写**,但是数据库表名、列名和值是否区分,依赖于具体的 DBMS 以及配置。例如:`SELECT` 与 `select`、`Select` 是相同的。
- **多条 SQL 语句必须以分号(`;`)分隔**。
- 处理 SQL 语句时,**所有空格都被忽略**。
@@ -1146,7 +1146,7 @@ MySQL 不允许在触发器中使用 CALL 语句 ,也就是不能调用存储
- 在 `INSERT` 型触发器中,`NEW` 用来表示将要(`BEFORE`)或已经(`AFTER`)插入的新数据;
- 在 `UPDATE` 型触发器中,`OLD` 用来表示将要或已经被修改的原数据,`NEW` 用来表示将要或已经修改为的新数据;
- 在 `DELETE` 型触发器中,`OLD` 用来表示将要或已经被删除的原数据;
-- 使用方法: `NEW.columnName` (columnName 为相应数据表某一列名)
+- 使用方法:`NEW.columnName` (columnName 为相应数据表某一列名)
### 创建触发器
diff --git a/docs/distributed-system/api-gateway.md b/docs/distributed-system/api-gateway.md
index 0b8339525d5..e35e651c15f 100644
--- a/docs/distributed-system/api-gateway.md
+++ b/docs/distributed-system/api-gateway.md
@@ -74,8 +74,8 @@ Zuul 主要通过过滤器(类似于 AOP)来过滤请求,从而实现网

-- Github 地址 :
-- 官方 Wiki :
+- Github 地址 :
+- 官方 Wiki :
### Spring Cloud Gateway
@@ -89,8 +89,8 @@ Spring Cloud Gateway 不仅提供统一的路由方式,并且基于 Filter 链
Spring Cloud Gateway 和 Zuul 2.x 的差别不大,也是通过过滤器来处理请求。不过,目前更加推荐使用 Spring Cloud Gateway 而非 Zuul,Spring Cloud 生态对其支持更加友好。
-- Github 地址 :
-- 官网 :
+- Github 地址 :
+- 官网 :
### Kong
@@ -117,8 +117,8 @@ $ curl -X POST http://kong:8001/services/{service}/plugins \

-- Github 地址:
-- 官网地址 :
+- Github 地址:
+- 官网地址 :
### APISIX
@@ -144,7 +144,7 @@ APISIX 同样支持定制化的插件开发。开发者除了能够使用 Lua

- Github 地址 :
-- 官网地址:
+- 官网地址:
相关阅读:
@@ -159,10 +159,10 @@ Shenyu 是一款基于 WebFlux 的可扩展、高性能、响应式网关,Apac

-Shenyu 通过插件扩展功能,插件是 ShenYu 的灵魂,并且插件也是可扩展和热插拔的。不同的插件实现不同的功能。Shenyu 自带了诸如限流、熔断、转发 、重写、重定向、和路由监控等插件。
+Shenyu 通过插件扩展功能,插件是 ShenYu 的灵魂,并且插件也是可扩展和热插拔的。不同的插件实现不同的功能。Shenyu 自带了诸如限流、熔断、转发、重写、重定向、和路由监控等插件。
-- Github 地址:
-- 官网地址 :
+- Github 地址:
+- 官网地址 :
## 参考
diff --git a/docs/distributed-system/distributed-id.md b/docs/distributed-system/distributed-id.md
index a0dccb94e41..5e6f613e82f 100644
--- a/docs/distributed-system/distributed-id.md
+++ b/docs/distributed-system/distributed-id.md
@@ -36,7 +36,7 @@ category: 分布式
一个最基本的分布式 ID 需要满足下面这些要求:
- **全局唯一** :ID 的全局唯一性肯定是首先要满足的!
-- **高性能** : 分布式 ID 的生成速度要快,对本地资源消耗要小。
+- **高性能** :分布式 ID 的生成速度要快,对本地资源消耗要小。
- **高可用** :生成分布式 ID 的服务要保证可用性无限接近于 100%。
- **方便易用** :拿来即用,使用方便,快速接入!
@@ -83,14 +83,14 @@ COMMIT;
插入数据这里,我们没有使用 `insert into` 而是使用 `replace into` 来插入数据,具体步骤是这样的:
-- 第一步: 尝试把数据插入到表中。
+- 第一步:尝试把数据插入到表中。
-- 第二步: 如果主键或唯一索引字段出现重复数据错误而插入失败时,先从表中删除含有重复关键字值的冲突行,然后再次尝试把数据插入到表中。
+- 第二步:如果主键或唯一索引字段出现重复数据错误而插入失败时,先从表中删除含有重复关键字值的冲突行,然后再次尝试把数据插入到表中。
这种方式的优缺点也比较明显:
- **优点** :实现起来比较简单、ID 有序递增、存储消耗空间小
-- **缺点** : 支持的并发量不大、存在数据库单点问题(可以使用数据库集群解决,不过增加了复杂度)、ID 没有具体业务含义、安全问题(比如根据订单 ID 的递增规律就能推算出每天的订单量,商业机密啊! )、每次获取 ID 都要访问一次数据库(增加了对数据库的压力,获取速度也慢)
+- **缺点** :支持的并发量不大、存在数据库单点问题(可以使用数据库集群解决,不过增加了复杂度)、ID 没有具体业务含义、安全问题(比如根据订单 ID 的递增规律就能推算出每天的订单量,商业机密啊! )、每次获取 ID 都要访问一次数据库(增加了对数据库的压力,获取速度也慢)
#### 数据库号段模式
@@ -115,7 +115,7 @@ CREATE TABLE `sequence_id_generator` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```
-`current_max_id` 字段和`step`字段主要用于获取批量 ID,获取的批量 id 为: `current_max_id ~ current_max_id+step`。
+`current_max_id` 字段和`step`字段主要用于获取批量 ID,获取的批量 id 为:`current_max_id ~ current_max_id+step`。

@@ -190,8 +190,8 @@ OK
**Redis 方案的优缺点:**
-- **优点** : 性能不错并且生成的 ID 是有序递增的
-- **缺点** : 和数据库主键自增方案的缺点类似
+- **优点** :性能不错并且生成的 ID 是有序递增的
+- **缺点** :和数据库主键自增方案的缺点类似
除了 Redis 之外,MongoDB ObjectId 经常也会被拿来当做分布式 ID 的解决方案。
@@ -200,14 +200,14 @@ OK
MongoDB ObjectId 一共需要 12 个字节存储:
- 0~3:时间戳
-- 3~6: 代表机器 ID
+- 3~6:代表机器 ID
- 7~8:机器进程 ID
- 9~11 :自增值
**MongoDB 方案的优缺点:**
-- **优点** : 性能不错并且生成的 ID 是有序递增的
-- **缺点** : 需要解决重复 ID 问题(当机器时间不对的情况下,可能导致会产生重复 ID) 、有安全性问题(ID 生成有规律性)
+- **优点** :性能不错并且生成的 ID 是有序递增的
+- **缺点** :需要解决重复 ID 问题(当机器时间不对的情况下,可能导致会产生重复 ID)、有安全性问题(ID 生成有规律性)
### 算法
@@ -262,13 +262,13 @@ int version = uuid.version();// 4
最后,我们再简单分析一下 **UUID 的优缺点** (面试的时候可能会被问到的哦!) :
- **优点** :生成速度比较快、简单易用
-- **缺点** : 存储消耗空间大(32 个字符串,128 位) 、 不安全(基于 MAC 地址生成 UUID 的算法会造成 MAC 地址泄露)、无序(非自增)、没有具体业务含义、需要解决重复 ID 问题(当机器时间不对的情况下,可能导致会产生重复 ID)
+- **缺点** :存储消耗空间大(32 个字符串,128 位)、 不安全(基于 MAC 地址生成 UUID 的算法会造成 MAC 地址泄露)、无序(非自增)、没有具体业务含义、需要解决重复 ID 问题(当机器时间不对的情况下,可能导致会产生重复 ID)
#### Snowflake(雪花算法)
Snowflake 是 Twitter 开源的分布式 ID 生成算法。Snowflake 由 64 bit 的二进制数字组成,这 64bit 的二进制被分成了几部分,每一部分存储的数据都有特定的含义:
-- **第 0 位**: 符号位(标识正负),始终为 0,没有用,不用管。
+- **第 0 位**:符号位(标识正负),始终为 0,没有用,不用管。
- **第 1~41 位** :一共 41 位,用来表示时间戳,单位是毫秒,可以支撑 2 ^41 毫秒(约 69 年)
- **第 42~52 位** :一共 10 位,一般来说,前 5 位表示机房 ID,后 5 位表示机器 ID(实际项目中可以根据实际情况调整)。这样就可以区分不同集群/机房的节点。
- **第 53~64 位** :一共 12 位,用来表示序列号。 序列号为自增值,代表单台机器每毫秒能够产生的最大 ID 数(2^12 = 4096),也就是说单台机器每毫秒最多可以生成 4096 个 唯一 ID。
@@ -282,7 +282,7 @@ Snowflake 是 Twitter 开源的分布式 ID 生成算法。Snowflake 由 64 bit
我们再来看看 Snowflake 算法的优缺点 :
- **优点** :生成速度比较快、生成的 ID 有序递增、比较灵活(可以对 Snowflake 算法进行简单的改造比如加入业务 ID)
-- **缺点** : 需要解决重复 ID 问题(依赖时间,当机器时间不对的情况下,可能导致会产生重复 ID)。
+- **缺点** :需要解决重复 ID 问题(依赖时间,当机器时间不对的情况下,可能导致会产生重复 ID)。
### 开源框架
@@ -304,7 +304,7 @@ UidGenerator 官方文档中的介绍如下:
#### Leaf(美团)
-**[Leaf](https://github.com/Meituan-Dianping/Leaf)** 是美团开源的一个分布式 ID 解决方案 。这个项目的名字 Leaf(树叶) 起源于德国哲学家、数学家莱布尼茨的一句话: “There are no two identical leaves in the world”(世界上没有两片相同的树叶) 。这名字起得真心挺不错的,有点文艺青年那味了!
+**[Leaf](https://github.com/Meituan-Dianping/Leaf)** 是美团开源的一个分布式 ID 解决方案 。这个项目的名字 Leaf(树叶) 起源于德国哲学家、数学家莱布尼茨的一句话:“There are no two identical leaves in the world”(世界上没有两片相同的树叶) 。这名字起得真心挺不错的,有点文艺青年那味了!
Leaf 提供了 **号段模式** 和 **Snowflake(雪花算法)** 这两种模式来生成分布式 ID。并且,它支持双号段,还解决了雪花 ID 系统时钟回拨问题。不过,时钟问题的解决需要弱依赖于 Zookeeper 。
diff --git a/docs/distributed-system/distributed-lock.md b/docs/distributed-system/distributed-lock.md
index 3bdd0e5484b..696b8503cc4 100644
--- a/docs/distributed-system/distributed-lock.md
+++ b/docs/distributed-system/distributed-lock.md
@@ -74,7 +74,7 @@ end
### 为什么要给锁设置一个过期时间?
-为了避免锁无法被释放,我们可以想到的一个解决办法就是: **给这个 key(也就是锁) 设置一个过期时间** 。
+为了避免锁无法被释放,我们可以想到的一个解决办法就是:**给这个 key(也就是锁) 设置一个过期时间** 。
```bash
127.0.0.1:6379> SET lockKey uniqueValue EX 3 NX
@@ -90,7 +90,7 @@ OK
这样确实可以解决问题,不过,这种解决办法同样存在漏洞:**如果操作共享资源的时间大于过期时间,就会出现锁提前过期的问题,进而导致分布式锁直接失效。如果锁的超时时间设置过长,又会影响到性能。**
-你或许在想: **如果操作共享资源的操作还未完成,锁过期时间能够自己续期就好了!**
+你或许在想:**如果操作共享资源的操作还未完成,锁过期时间能够自己续期就好了!**
### 如何实现锁的优雅续期?
@@ -98,7 +98,7 @@ OK

-Redisson 是一个开源的 Java 语言 Redis 客户端,提供了很多开箱即用的功能,不仅仅包括多种分布式锁的实现。并且,Redisson 还支持 Redis 单机、Redis Sentinel 、Redis Cluster 等多种部署架构。
+Redisson 是一个开源的 Java 语言 Redis 客户端,提供了很多开箱即用的功能,不仅仅包括多种分布式锁的实现。并且,Redisson 还支持 Redis 单机、Redis Sentinel、Redis Cluster 等多种部署架构。
Redisson 中的分布式锁自带自动续期机制,使用起来非常简单,原理也比较简单,其提供了一个专门用来监控和续期锁的 **Watch Dog( 看门狗)**,如果操作共享资源的线程还未执行完成的话,Watch Dog 会不断地延长锁的过期时间,进而保证锁不会因为超时而被释放。
@@ -300,7 +300,7 @@ client.close();
- **持久(PERSISTENT)节点** :一旦创建就一直存在即使 ZooKeeper 集群宕机,直到将其删除。
- **临时(EPHEMERAL)节点** :临时节点的生命周期是与 **客户端会话(session)** 绑定的,**会话消失则节点消失** 。并且,**临时节点只能做叶子节点** ,不能创建子节点。
-- **持久顺序(PERSISTENT_SEQUENTIAL)节点** :除了具有持久(PERSISTENT)节点的特性之外, 子节点的名称还具有顺序性。比如 `/node1/app0000000001` 、`/node1/app0000000002` 。
+- **持久顺序(PERSISTENT_SEQUENTIAL)节点** :除了具有持久(PERSISTENT)节点的特性之外, 子节点的名称还具有顺序性。比如 `/node1/app0000000001`、`/node1/app0000000002` 。
- **临时顺序(EPHEMERAL_SEQUENTIAL)节点** :除了具备临时(EPHEMERAL)节点的特性之外,子节点的名称还具有顺序性。
可以看出,临时节点相比持久节点,最主要的是对会话失效的情况处理不一样,临时节点会话消失则对应的节点消失。这样的话,如果客户端发生异常导致没来得及释放锁也没关系,会话失效节点自动被删除,不会发生死锁的问题。
@@ -315,7 +315,7 @@ client.close();
同一时间段内,可能会有很多客户端同时获取锁,但只有一个可以获取成功。如果获取锁失败,则说明有其他的客户端已经成功获取锁。获取锁失败的客户端并不会不停地循环去尝试加锁,而是在前一个节点注册一个事件监听器。
-这个事件监听器的作用是: **当前一个节点对应的客户端释放锁之后(也就是前一个节点被删除之后,监听的是删除事件),通知获取锁失败的客户端(唤醒等待的线程,Java 中的 `wait/notifyAll` ),让它尝试去获取锁,然后就成功获取锁了。**
+这个事件监听器的作用是:**当前一个节点对应的客户端释放锁之后(也就是前一个节点被删除之后,监听的是删除事件),通知获取锁失败的客户端(唤醒等待的线程,Java 中的 `wait/notifyAll` ),让它尝试去获取锁,然后就成功获取锁了。**
### 如何实现可重入锁?
diff --git a/docs/distributed-system/distributed-process-coordination/zookeeper/zookeeper-in-action.md b/docs/distributed-system/distributed-process-coordination/zookeeper/zookeeper-in-action.md
index f3de17fc990..55fc1b10b56 100644
--- a/docs/distributed-system/distributed-process-coordination/zookeeper/zookeeper-in-action.md
+++ b/docs/distributed-system/distributed-process-coordination/zookeeper/zookeeper-in-action.md
@@ -218,7 +218,7 @@ zkClient.start();
- **持久(PERSISTENT)节点** :一旦创建就一直存在即使 ZooKeeper 集群宕机,直到将其删除。
- **临时(EPHEMERAL)节点** :临时节点的生命周期是与 **客户端会话(session)** 绑定的,**会话消失则节点消失** 。并且,临时节点 **只能做叶子节点** ,不能创建子节点。
-- **持久顺序(PERSISTENT_SEQUENTIAL)节点** :除了具有持久(PERSISTENT)节点的特性之外, 子节点的名称还具有顺序性。比如 `/node1/app0000000001` 、`/node1/app0000000002` 。
+- **持久顺序(PERSISTENT_SEQUENTIAL)节点** :除了具有持久(PERSISTENT)节点的特性之外, 子节点的名称还具有顺序性。比如 `/node1/app0000000001`、`/node1/app0000000002` 。
- **临时顺序(EPHEMERAL_SEQUENTIAL)节点** :除了具备临时(EPHEMERAL)节点的特性之外,子节点的名称还具有顺序性。
你在使用的 ZooKeeper 的时候,会发现 `CreateMode` 类中实际有 7 种 znode 类型 ,但是用的最多的还是上面介绍的 4 种。
diff --git a/docs/distributed-system/distributed-process-coordination/zookeeper/zookeeper-intro.md b/docs/distributed-system/distributed-process-coordination/zookeeper/zookeeper-intro.md
index 0d7933f6244..6f0da6eb2e7 100644
--- a/docs/distributed-system/distributed-process-coordination/zookeeper/zookeeper-intro.md
+++ b/docs/distributed-system/distributed-process-coordination/zookeeper/zookeeper-intro.md
@@ -66,7 +66,7 @@ ZooKeeper 概览中,我们介绍到使用其通常被用于实现诸如数据
1. **命名服务** :可以通过 ZooKeeper 的顺序节点生成全局唯一 ID。
2. **数据发布/订阅** :通过 **Watcher 机制** 可以很方便地实现数据发布/订阅。当你将数据发布到 ZooKeeper 被监听的节点上,其他机器可通过监听 ZooKeeper 上节点的变化来实现配置的动态更新。
-3. **分布式锁** : 通过创建唯一节点获得分布式锁,当获得锁的一方执行完相关代码或者是挂掉之后就释放锁。分布式锁的实现也需要用到 **Watcher 机制** ,我在 [分布式锁详解](https://javaguide.cn/distributed-system/distributed-lock.html) 这篇文章中有详细介绍到如何基于 ZooKeeper 实现分布式锁。
+3. **分布式锁** :通过创建唯一节点获得分布式锁,当获得锁的一方执行完相关代码或者是挂掉之后就释放锁。分布式锁的实现也需要用到 **Watcher 机制** ,我在 [分布式锁详解](https://javaguide.cn/distributed-system/distributed-lock.html) 这篇文章中有详细介绍到如何基于 ZooKeeper 实现分布式锁。
实际上,这些功能的实现基本都得益于 ZooKeeper 可以保存数据的功能,但是 ZooKeeper 不适合保存大量数据,这一点需要注意。
@@ -92,13 +92,13 @@ ZooKeeper 数据模型采用层次化的多叉树形结构,每个节点上都
- **持久(PERSISTENT)节点** :一旦创建就一直存在即使 ZooKeeper 集群宕机,直到将其删除。
- **临时(EPHEMERAL)节点** :临时节点的生命周期是与 **客户端会话(session)** 绑定的,**会话消失则节点消失** 。并且,**临时节点只能做叶子节点** ,不能创建子节点。
-- **持久顺序(PERSISTENT_SEQUENTIAL)节点** :除了具有持久(PERSISTENT)节点的特性之外, 子节点的名称还具有顺序性。比如 `/node1/app0000000001` 、`/node1/app0000000002` 。
+- **持久顺序(PERSISTENT_SEQUENTIAL)节点** :除了具有持久(PERSISTENT)节点的特性之外, 子节点的名称还具有顺序性。比如 `/node1/app0000000001`、`/node1/app0000000002` 。
- **临时顺序(EPHEMERAL_SEQUENTIAL)节点** :除了具备临时(EPHEMERAL)节点的特性之外,子节点的名称还具有顺序性
每个 znode 由 2 部分组成:
- **stat** :状态信息
-- **data** : 节点存放的数据的具体内容
+- **data** :节点存放的数据的具体内容
如下所示,我通过 get 命令来获取 根目录下的 dubbo 节点的内容。(get 命令在下面会介绍到)。
@@ -143,8 +143,8 @@ Stat 类中包含了一个数据节点的所有状态信息的字段,包括事
在前面我们已经提到,对应于每个 znode,ZooKeeper 都会为其维护一个叫作 **Stat** 的数据结构,Stat 中记录了这个 znode 的三个相关的版本:
- **dataVersion** :当前 znode 节点的版本号
-- **cversion** : 当前 znode 子节点的版本
-- **aclVersion** : 当前 znode 的 ACL 的版本。
+- **cversion** :当前 znode 子节点的版本
+- **aclVersion** :当前 znode 的 ACL 的版本。
### ACL(权限控制)
@@ -162,9 +162,9 @@ ZooKeeper 采用 ACL(AccessControlLists)策略来进行权限控制,类似
对于身份认证,提供了以下几种方式:
-- **world** : 默认方式,所有用户都可无条件访问。
+- **world** :默认方式,所有用户都可无条件访问。
- **auth** :不使用任何 id,代表任何已认证的用户。
-- **digest** :用户名:密码认证方式: _username:password_ 。
+- **digest** :用户名:密码认证方式:_username:password_ 。
- **ip** : 对指定 ip 进行限制。
### Watcher(事件监听器)
@@ -191,7 +191,7 @@ Session 有一个属性叫做:`sessionTimeout` ,`sessionTimeout` 代表会
上图中每一个 Server 代表一个安装 ZooKeeper 服务的服务器。组成 ZooKeeper 服务的服务器都会在内存中维护当前的服务器状态,并且每台服务器之间都互相保持着通信。集群间通过 ZAB 协议(ZooKeeper Atomic Broadcast)来保持数据的一致性。
-**最典型集群模式: Master/Slave 模式(主备模式)**。在这种模式中,通常 Master 服务器作为主服务器提供写服务,其他的 Slave 服务器从服务器通过异步复制的方式获取 Master 服务器最新的数据提供读服务。
+**最典型集群模式:Master/Slave 模式(主备模式)**。在这种模式中,通常 Master 服务器作为主服务器提供写服务,其他的 Slave 服务器从服务器通过异步复制的方式获取 Master 服务器最新的数据提供读服务。
### ZooKeeper 集群角色
diff --git a/docs/distributed-system/distributed-process-coordination/zookeeper/zookeeper-plus.md b/docs/distributed-system/distributed-process-coordination/zookeeper/zookeeper-plus.md
index 7c9c32bb174..20fabd2bd0d 100644
--- a/docs/distributed-system/distributed-process-coordination/zookeeper/zookeeper-plus.md
+++ b/docs/distributed-system/distributed-process-coordination/zookeeper/zookeeper-plus.md
@@ -230,7 +230,7 @@ tag:
了解了 `ZAB` 协议还不够,它仅仅是 `Zookeeper` 内部实现的一种方式,而我们如何通过 `Zookeeper` 去做一些典型的应用场景呢?比如说集群管理,分布式锁,`Master` 选举等等。
-这就涉及到如何使用 `Zookeeper` 了,但在使用之前我们还需要掌握几个概念。比如 `Zookeeper` 的 **数据模型** 、**会话机制**、**ACL**、**Watcher 机制** 等等。
+这就涉及到如何使用 `Zookeeper` 了,但在使用之前我们还需要掌握几个概念。比如 `Zookeeper` 的 **数据模型**、**会话机制**、**ACL**、**Watcher 机制** 等等。
### 数据模型
@@ -247,12 +247,12 @@ tag:
- 临时节点:临时节点的生命周期是与 **客户端会话** 绑定的,**会话消失则节点消失** 。临时节点 **只能做叶子节点** ,不能创建子节点。
- 临时顺序节点:父节点可以创建一个维持了顺序的临时节点(和前面的持久顺序性节点一样)。
-节点状态中包含了很多节点的属性比如 `czxid` 、`mzxid` 等等,在 `zookeeper` 中是使用 `Stat` 这个类来维护的。下面我列举一些属性解释。
+节点状态中包含了很多节点的属性比如 `czxid`、`mzxid` 等等,在 `zookeeper` 中是使用 `Stat` 这个类来维护的。下面我列举一些属性解释。
- `czxid`:`Created ZXID`,该数据节点被 **创建** 时的事务 ID。
- `mzxid`:`Modified ZXID`,节点 **最后一次被更新时** 的事务 ID。
- `ctime`:`Created Time`,该节点被创建的时间。
-- `mtime`: `Modified Time`,该节点最后一次被修改的时间。
+- `mtime`:`Modified Time`,该节点最后一次被修改的时间。
- `version`:节点的版本号。
- `cversion`:**子节点** 的版本号。
- `aversion`:节点的 `ACL` 版本号。
@@ -265,7 +265,7 @@ tag:
我想这个对于后端开发的朋友肯定不陌生,不就是 `session` 吗?只不过 `zk` 客户端和服务端是通过 **`TCP` 长连接** 维持的会话机制,其实对于会话来说你可以理解为 **保持连接状态** 。
-在 `zookeeper` 中,会话还有对应的事件,比如 `CONNECTION_LOSS 连接丢失事件` 、`SESSION_MOVED 会话转移事件` 、`SESSION_EXPIRED 会话超时失效事件` 。
+在 `zookeeper` 中,会话还有对应的事件,比如 `CONNECTION_LOSS 连接丢失事件`、`SESSION_MOVED 会话转移事件`、`SESSION_EXPIRED 会话超时失效事件` 。
### ACL
@@ -305,7 +305,7 @@ tag:
### 分布式锁
-分布式锁的实现方式有很多种,比如 `Redis` 、数据库 、`zookeeper` 等。个人认为 `zookeeper` 在实现分布式锁这方面是非常非常简单的。
+分布式锁的实现方式有很多种,比如 `Redis`、数据库、`zookeeper` 等。个人认为 `zookeeper` 在实现分布式锁这方面是非常非常简单的。
上面我们已经提到过了 **zk 在高并发的情况下保证节点创建的全局唯一性**,这玩意一看就知道能干啥了。实现互斥锁呗,又因为能在分布式的情况下,所以能实现分布式锁呗。
@@ -359,7 +359,7 @@ tag:
- 分布式与集群的区别
-- `2PC` 、`3PC` 以及 `paxos` 算法这些一致性框架的原理和实现。
+- `2PC`、`3PC` 以及 `paxos` 算法这些一致性框架的原理和实现。
- `zookeeper` 专门的一致性算法 `ZAB` 原子广播协议的内容(`Leader` 选举、崩溃恢复、消息广播)。
diff --git a/docs/distributed-system/protocol/cap-and-base-theorem.md b/docs/distributed-system/protocol/cap-and-base-theorem.md
index cb53b5192c5..53871589274 100644
--- a/docs/distributed-system/protocol/cap-and-base-theorem.md
+++ b/docs/distributed-system/protocol/cap-and-base-theorem.md
@@ -53,7 +53,7 @@ CAP 理论的提出者布鲁尔在提出 CAP 猜想的时候,并没有详细
**选择 CP 还是 AP 的关键在于当前的业务场景,没有定论,比如对于需要确保强一致性的场景如银行一般会选择保证 CP 。**
-另外,需要补充说明的一点是: **如果网络分区正常的话(系统在绝大部分时候所处的状态),也就说不需要保证 P 的时候,C 和 A 能够同时保证。**
+另外,需要补充说明的一点是:**如果网络分区正常的话(系统在绝大部分时候所处的状态),也就说不需要保证 P 的时候,C 和 A 能够同时保证。**
### CAP 实际应用案例
@@ -93,7 +93,7 @@ CAP 理论的提出者布鲁尔在提出 CAP 猜想的时候,并没有详细
### 简介
-**BASE** 是 **Basically Available(基本可用)** 、**Soft-state(软状态)** 和 **Eventually Consistent(最终一致性)** 三个短语的缩写。BASE 理论是对 CAP 中一致性 C 和可用性 A 权衡的结果,其来源于对大规模互联网系统分布式实践的总结,是基于 CAP 定理逐步演化而来的,它大大降低了我们对系统的要求。
+**BASE** 是 **Basically Available(基本可用)**、**Soft-state(软状态)** 和 **Eventually Consistent(最终一致性)** 三个短语的缩写。BASE 理论是对 CAP 中一致性 C 和可用性 A 权衡的结果,其来源于对大规模互联网系统分布式实践的总结,是基于 CAP 定理逐步演化而来的,它大大降低了我们对系统的要求。
### BASE 理论的核心思想
diff --git a/docs/distributed-system/protocol/gossip-protocl.md b/docs/distributed-system/protocol/gossip-protocl.md
index 71331737f69..08365419863 100644
--- a/docs/distributed-system/protocol/gossip-protocl.md
+++ b/docs/distributed-system/protocol/gossip-protocl.md
@@ -28,7 +28,7 @@ Gossip 协议最早是在 ACM 上的一篇 1987 年发表的论文 [《Epidemic
在 Gossip 协议下,没有所谓的中心节点,每个节点周期性地随机找一个节点互相同步彼此的信息,理论上来说,各个节点的状态最终会保持一致。
-下面我们来对 Gossip 协议的定义做一个总结: **Gossip 协议是一种允许在分布式系统中共享状态的去中心化通信协议,通过这种通信协议,我们可以将信息传播给网络或集群中的所有成员。**
+下面我们来对 Gossip 协议的定义做一个总结:**Gossip 协议是一种允许在分布式系统中共享状态的去中心化通信协议,通过这种通信协议,我们可以将信息传播给网络或集群中的所有成员。**
## Gossip 协议应用
@@ -133,7 +133,7 @@ Gossip 设计了两种可能的消息传播模式:**反熵(Anti-Entropy)**
## 总结
- Gossip 协议是一种允许在分布式系统中共享状态的通信协议,通过这种通信协议,我们可以将信息传播给网络或集群中的所有成员。
-- Gossip 协议被 Redis 、Apache Cassandra、Consul 等项目应用。
+- Gossip 协议被 Redis、Apache Cassandra、Consul 等项目应用。
- 谣言传播(Rumor-Mongering)比较适合节点数量比较多或者节点动态变化的场景。
## 参考
diff --git a/docs/distributed-system/protocol/paxos-algorithm.md b/docs/distributed-system/protocol/paxos-algorithm.md
index 5f5a65f5c52..fd7d1fdc64e 100644
--- a/docs/distributed-system/protocol/paxos-algorithm.md
+++ b/docs/distributed-system/protocol/paxos-algorithm.md
@@ -36,14 +36,14 @@ Paxos 算法是第一个被证明完备的分布式系统共识算法。共识
兰伯特当时提出的 Paxos 算法主要包含 2 个部分:
-- **Basic Paxos 算法** : 描述的是多节点之间如何就某个值(提案 Value)达成共识。
-- **Multi-Paxos 思想** : 描述的是执行多个 Basic Paxos 实例,就一系列值达成共识。Multi-Paxos 说白了就是执行多次 Basic Paxos ,核心还是 Basic Paxos 。
+- **Basic Paxos 算法** :描述的是多节点之间如何就某个值(提案 Value)达成共识。
+- **Multi-Paxos 思想** :描述的是执行多个 Basic Paxos 实例,就一系列值达成共识。Multi-Paxos 说白了就是执行多次 Basic Paxos ,核心还是 Basic Paxos 。
由于 Paxos 算法在国际上被公认的非常难以理解和实现,因此不断有人尝试简化这一算法。到了 2013 年才诞生了一个比 Paxos 算法更易理解和实现的共识算法—[Raft 算法](https://javaguide.cn/distributed-system/theorem&algorithm&protocol/raft-algorithm.html) 。更具体点来说,Raft 是 Multi-Paxos 的一个变种,其简化了 Multi-Paxos 的思想,变得更容易被理解以及工程实现。
-针对没有恶意节点的情况,除了 Raft 算法之外,当前最常用的一些共识算法比如 **ZAB 协议** 、 **Fast Paxos** 算法都是基于 Paxos 算法改进的。
+针对没有恶意节点的情况,除了 Raft 算法之外,当前最常用的一些共识算法比如 **ZAB 协议**、 **Fast Paxos** 算法都是基于 Paxos 算法改进的。
-针对存在恶意节点的情况,一般使用的是 **工作量证明(POW,Proof-of-Work)** 、 **权益证明(PoS,Proof-of-Stake )** 等共识算法。这类共识算法最典型的应用就是区块链,就比如说前段时间以太坊官方宣布其共识机制正在从工作量证明(PoW)转变为权益证明(PoS)。
+针对存在恶意节点的情况,一般使用的是 **工作量证明(POW,Proof-of-Work)**、 **权益证明(PoS,Proof-of-Stake )** 等共识算法。这类共识算法最典型的应用就是区块链,就比如说前段时间以太坊官方宣布其共识机制正在从工作量证明(PoW)转变为权益证明(PoS)。
区块链系统使用的共识算法需要解决的核心问题是 **拜占庭将军问题** ,这和我们日常接触到的 ZooKeeper、Etcd、Consul 等分布式中间件不太一样。
@@ -69,7 +69,7 @@ Basic Paxos 中存在 3 个重要的角色:
Basic Paxos 算法的仅能就单个值达成共识,为了能够对一系列的值达成共识,我们需要用到 Basic Paxos 思想。
-⚠️**注意** : Multi-Paxos 只是一种思想,这种思想的核心就是通过多个 Basic Paxos 实例就一系列值达成共识。也就是说,Basic Paxos 是 Multi-Paxos 思想的核心,Multi-Paxos 就是多执行几次 Basic Paxos。
+⚠️**注意** :Multi-Paxos 只是一种思想,这种思想的核心就是通过多个 Basic Paxos 实例就一系列值达成共识。也就是说,Basic Paxos 是 Multi-Paxos 思想的核心,Multi-Paxos 就是多执行几次 Basic Paxos。
由于兰伯特提到的 Multi-Paxos 思想缺少代码实现的必要细节(比如怎么选举领导者),所以在理解和实现上比较困难。
diff --git a/docs/distributed-system/rpc/dubbo.md b/docs/distributed-system/rpc/dubbo.md
index 2fcdbc1b62c..5776fe1063c 100644
--- a/docs/distributed-system/rpc/dubbo.md
+++ b/docs/distributed-system/rpc/dubbo.md
@@ -5,7 +5,7 @@ tag:
- rpc
---
-> 说明: Dubbo3 已经发布,这篇文章是基于 Dubbo2 写的。Dubbo3 基于 Dubbo2 演进而来,在保持原有核心功能特性的同时, Dubbo3 在易用性、超大规模微服务实践、云原生基础设施适配、安全设计等几大方向上进行了全面升级。
+> 说明:Dubbo3 已经发布,这篇文章是基于 Dubbo2 写的。Dubbo3 基于 Dubbo2 演进而来,在保持原有核心功能特性的同时, Dubbo3 在易用性、超大规模微服务实践、云原生基础设施适配、安全设计等几大方向上进行了全面升级。
这篇文章是我根据官方文档以及自己平时的使用情况,对 Dubbo 所做的一个总结。欢迎补充!
@@ -28,7 +28,7 @@ tag:

-简单来说就是: **Dubbo 不光可以帮助我们调用远程服务,还提供了一些其他开箱即用的功能比如智能负载均衡。**
+简单来说就是:**Dubbo 不光可以帮助我们调用远程服务,还提供了一些其他开箱即用的功能比如智能负载均衡。**
Dubbo 目前已经有接近 34.4 k 的 Star 。
@@ -40,7 +40,7 @@ Dubbo 是由阿里开源,后来加入了 Apache 。正是由于 Dubbo 的出
### 为什么要用 Dubbo?
-随着互联网的发展,网站的规模越来越大,用户数量越来越多。单一应用架构 、垂直应用架构无法满足我们的需求,这个时候分布式服务架构就诞生了。
+随着互联网的发展,网站的规模越来越大,用户数量越来越多。单一应用架构、垂直应用架构无法满足我们的需求,这个时候分布式服务架构就诞生了。
分布式服务架构下,系统被拆分成不同的服务比如短信服务、安全服务,每个服务独立提供系统的某个核心服务。
@@ -48,8 +48,8 @@ Dubbo 是由阿里开源,后来加入了 Apache 。正是由于 Dubbo 的出
不过,Dubbo 的出现让上述问题得到了解决。**Dubbo 帮助我们解决了什么问题呢?**
-1. **负载均衡** : 同一个服务部署在不同的机器时该调用哪一台机器上的服务。
-2. **服务调用链路生成** : 随着系统的发展,服务越来越多,服务间依赖关系变得错踪复杂,甚至分不清哪个应用要在哪个应用之前启动,架构师都不能完整的描述应用的架构关系。Dubbo 可以为我们解决服务之间互相是如何调用的。
+1. **负载均衡** :同一个服务部署在不同的机器时该调用哪一台机器上的服务。
+2. **服务调用链路生成** :随着系统的发展,服务越来越多,服务间依赖关系变得错踪复杂,甚至分不清哪个应用要在哪个应用之前启动,架构师都不能完整的描述应用的架构关系。Dubbo 可以为我们解决服务之间互相是如何调用的。
3. **服务访问压力以及时长统计、资源调度和治理** :基于访问压力实时管理集群容量,提高集群利用率。
4. ......
diff --git a/docs/distributed-system/rpc/rpc-intro.md b/docs/distributed-system/rpc/rpc-intro.md
index b52b6093220..762d9057cc7 100644
--- a/docs/distributed-system/rpc/rpc-intro.md
+++ b/docs/distributed-system/rpc/rpc-intro.md
@@ -24,8 +24,8 @@ tag:
为了能够帮助小伙伴们理解 RPC 原理,我们可以将整个 RPC 的 核心功能看作是下面 👇 5 个部分实现的:
1. **客户端(服务消费端)** :调用远程方法的一端。
-1. **客户端 Stub(桩)** : 这其实就是一代理类。代理类主要做的事情很简单,就是把你调用方法、类、方法参数等信息传递到服务端。
-1. **网络传输** : 网络传输就是你要把你调用的方法的信息比如说参数啊这些东西传输到服务端,然后服务端执行完之后再把返回结果通过网络传输给你传输回来。网络传输的实现方式有很多种比如最近基本的 Socket 或者性能以及封装更加优秀的 Netty(推荐)。
+1. **客户端 Stub(桩)** :这其实就是一代理类。代理类主要做的事情很简单,就是把你调用方法、类、方法参数等信息传递到服务端。
+1. **网络传输** :网络传输就是你要把你调用的方法的信息比如说参数啊这些东西传输到服务端,然后服务端执行完之后再把返回结果通过网络传输给你传输回来。网络传输的实现方式有很多种比如最近基本的 Socket 或者性能以及封装更加优秀的 Netty(推荐)。
1. **服务端 Stub(桩)** :这个桩就不是代理类了。我觉得理解为桩实际不太好,大家注意一下就好。这里的服务端 Stub 实际指的就是接收到客户端执行方法的请求后,去执行对应的方法然后返回结果给客户端的类。
1. **服务端(服务提供端)** :提供远程方法的一端。
diff --git a/docs/distributed-system/spring-cloud-gateway-questions.md b/docs/distributed-system/spring-cloud-gateway-questions.md
index b93bc958329..4318b7c1e96 100644
--- a/docs/distributed-system/spring-cloud-gateway-questions.md
+++ b/docs/distributed-system/spring-cloud-gateway-questions.md
@@ -17,8 +17,8 @@ Spring Cloud Gateway 不仅提供统一的路由方式,并且基于 Filter 链
Spring Cloud Gateway 和 Zuul 2.x 的差别不大,也是通过过滤器来处理请求。不过,目前更加推荐使用 Spring Cloud Gateway 而非 Zuul,Spring Cloud 生态对其支持更加友好。
-- Github 地址 :
-- 官网 :
+- Github 地址 :
+- 官网 :
## Spring Cloud Gateway 的工作流程?
@@ -94,7 +94,7 @@ Spring Cloud Gateway 作为微服务的入口,需要尽量避免重启,而
```yaml
filters: #过滤器
- - RewritePath=/api/(?.*),/$\{segment} # 将跳转路径中包含的 “api” 替换成空
+ - RewritePath=/api/(?.*),/$\{segment} # 将跳转路径中包含的 “api” 替换成空
```
当然我们也可以自定义过滤器,本篇不做展开。
@@ -152,4 +152,4 @@ public class GlobalErrorWebExceptionHandler implements ErrorWebExceptionHandler
- Spring Cloud Gateway 官方文档:
- Creating a custom Spring Cloud Gateway Filter:
-- 全局异常处理:
\ No newline at end of file
+- 全局异常处理:
diff --git a/docs/high-availability/performance-test.md b/docs/high-availability/performance-test.md
index b58e929b416..7ac4ade6bcf 100644
--- a/docs/high-availability/performance-test.md
+++ b/docs/high-availability/performance-test.md
@@ -80,7 +80,7 @@ category: 高可用
1. QPS(Query Per Second):服务器每秒可以执行的查询次数;
2. TPS(Transaction Per Second):服务器每秒处理的事务数(这里的一个事务可以理解为客户发出请求到收到服务器的过程);
3. 并发数;系统能同时处理请求的数目即同时提交请求的用户数目。
-4. 响应时间: 一般取多次请求的平均响应时间
+4. 响应时间:一般取多次请求的平均响应时间
理清他们的概念,就很容易搞清楚他们之间的关系了。
diff --git a/docs/high-performance/message-queue/disruptor-questions.md b/docs/high-performance/message-queue/disruptor-questions.md
index f3add3289b5..66a62d2d0b3 100644
--- a/docs/high-performance/message-queue/disruptor-questions.md
+++ b/docs/high-performance/message-queue/disruptor-questions.md
@@ -17,7 +17,7 @@ Disruptor 是一个相对冷门一些的知识点,不过,如果你的项目
Disruptor 是一个开源的高性能内存队列,诞生初衷是为了解决内存队列的性能和内存安全问题,由英国外汇交易公司 LMAX 开发。
-根据 Disruptor 官方介绍,基于 Disruptor 开发的系统 LMAX(新的零售金融交易平台),单线程就能支撑每秒 600 万订单。Martin Fowler 在 2011年写的一篇文章 [The LMAX Architecture](https://martinfowler.com/articles/lmax.html) 中专门介绍过这个 LMAX 系统的架构,感兴趣的可以看看这篇文章。。
+根据 Disruptor 官方介绍,基于 Disruptor 开发的系统 LMAX(新的零售金融交易平台),单线程就能支撑每秒 600 万订单。Martin Fowler 在 2011 年写的一篇文章 [The LMAX Architecture](https://martinfowler.com/articles/lmax.html) 中专门介绍过这个 LMAX 系统的架构,感兴趣的可以看看这篇文章。。
LMAX 公司 2010 年在 QCon 演讲后,Disruptor 获得了业界关注,并获得了 2011 年的 Oracle 官方的 Duke's Choice Awards(Duke 选择大奖)。
@@ -25,14 +25,14 @@ LMAX 公司 2010 年在 QCon 演讲后,Disruptor 获得了业界关注,并
> “Duke 选择大奖”旨在表彰过去一年里全球个人或公司开发的、最具影响力的 Java 技术应用,由甲骨文公司主办。含金量非常高!
-我专门找到了 Oracle 官方当年颁布获得 Duke's Choice Awards 项目的那篇文章(文章地址:https://blogs.oracle.com/java/post/and-the-winners-arethe-dukes-choice-award) 。从文中可以看出,同年获得此大奖荣誉的还有大名鼎鼎的 Netty 、JRebel 等项目。
+我专门找到了 Oracle 官方当年颁布获得 Duke's Choice Awards 项目的那篇文章(文章地址:https://blogs.oracle.com/java/post/and-the-winners-arethe-dukes-choice-award) 。从文中可以看出,同年获得此大奖荣誉的还有大名鼎鼎的 Netty、JRebel 等项目。

Disruptor 提供的功能优点类似于 Kafka、RocketMQ 这类分布式队列,不过,其作为范围是 JVM(内存)。
- Github 地址:
-- 官方教程:
+- 官方教程:
关于如何在 Spring Boot 项目中使用 Disruptor,可以看这篇文章:[Spring Boot + Disruptor 实战入门](https://mp.weixin.qq.com/s/0iG5brK3bYF0BgSjX4jRiA) 。
@@ -91,7 +91,7 @@ Disruptor 真的很快,关于它为什么这么快这个问题,会在后文
- **ProducerType** :指定是单个事件发布者模式还是多个事件发布者模式(发布者和生产者的意思类似,我个人比较喜欢用发布者)。
- **Sequencer** :Sequencer 是 Disruptor 的真正核心。此接口有两个实现类 `SingleProducerSequencer`、`MultiProducerSequencer` ,它们定义在生产者和消费者之间快速、正确地传递数据的并发算法。
-下面这张图摘自Disruptor 官网,展示了 LMAX 系统使用 Disruptor 的示例。
+下面这张图摘自 Disruptor 官网,展示了 LMAX 系统使用 Disruptor 的示例。

@@ -116,17 +116,17 @@ Disruptor 真的很快,关于它为什么这么快这个问题,会在后文
- **RingBuffer(环形数组)** : Disruptor 内部的 RingBuffer 是通过数组实现的。由于这个数组中的所有元素在初始化时一次性全部创建,因此这些元素的内存地址一般来说是连续的。这样做的好处是,当生产者不断往 RingBuffer 中插入新的事件对象时,这些事件对象的内存地址就能够保持连续,从而利用 CPU 缓存的局部性原理,将相邻的事件对象一起加载到缓存中,提高程序的性能。这类似于 MySQL 的预读机制,将连续的几个页预读到内存里。除此之外,RingBuffer 基于数组还支持批量操作(一次处理多个元素)、还可以避免频繁的内存分配和垃圾回收(RingBuffer 是一个固定大小的数组,当向数组中添加新元素时,如果数组已满,则新元素将覆盖掉最旧的元素)。
- **避免了伪共享问题** :CPU 缓存内部是按照 Cache Line(缓存行)管理的,一般的 Cache Line 大小在 64 字节左右。Disruptor 为了确保目标字段独占一个 Cache Line,会在目标字段前后增加了 64 个字节的填充(前 56 个字节和后 8 个字节),这样可以避免 Cache Line 的伪共享(False Sharing)问题。
-- **无锁设计** :Disruptor 采用无锁设计,避免了传统锁机制带来的竞争和延迟。Disruptor 的无锁实现起来比较复杂,主要是基于 CAS 、内存屏障(Memory Barrier)、RingBuffer 等技术实现的。
+- **无锁设计** :Disruptor 采用无锁设计,避免了传统锁机制带来的竞争和延迟。Disruptor 的无锁实现起来比较复杂,主要是基于 CAS、内存屏障(Memory Barrier)、RingBuffer 等技术实现的。
综上所述,Disruptor 之所以能够如此快,是基于一系列优化策略的综合作用,既充分利用了现代 CPU 缓存结构的特点,又避免了常见的并发问题和性能瓶颈。
关于 Disruptor 高性能队列原理的详细介绍,可以查看这篇文章:[Disruptor 高性能队列原理浅析](https://qin.news/disruptor/) (参考了美团技术团队的[高性能队列——Disruptor](https://tech.meituan.com/2016/11/18/disruptor.html)这篇文章)。
-🌈 这里额外补充一点 : **数组中对象元素地址连续为什么可以提高性能?**
+🌈 这里额外补充一点 :**数组中对象元素地址连续为什么可以提高性能?**
CPU 缓存是通过将最近使用的数据存储在高速缓存中来实现更快的读取速度,并使用预取机制提前加载相邻内存的数据以利用局部性原理。
-在计算机系统中,CPU 主要访问高速缓存和内存。高速缓存是一种速度非常快、容量相对较小的内存,通常被分为多级缓存,其中L1、L2、L3 分别表示一级缓存、二级缓存、三级缓存。越靠近 CPU 的缓存,速度越快,容量也越小。相比之下,内存容量相对较大,但速度较慢。
+在计算机系统中,CPU 主要访问高速缓存和内存。高速缓存是一种速度非常快、容量相对较小的内存,通常被分为多级缓存,其中 L1、L2、L3 分别表示一级缓存、二级缓存、三级缓存。越靠近 CPU 的缓存,速度越快,容量也越小。相比之下,内存容量相对较大,但速度较慢。

@@ -135,4 +135,4 @@ CPU 缓存是通过将最近使用的数据存储在高速缓存中来实现更
## 参考
- Disruptor 高性能之道-等待策略:
-- 《Java 并发编程实战》- 40 | 案例分析(三):高性能队列Disruptor:https://time.geekbang.org/column/article/98134
\ No newline at end of file
+- 《Java 并发编程实战》- 40 | 案例分析(三):高性能队列 Disruptor:https://time.geekbang.org/column/article/98134
diff --git a/docs/high-performance/message-queue/kafka-questions-01.md b/docs/high-performance/message-queue/kafka-questions-01.md
index 5f0e81b0133..4f3b0733f30 100644
--- a/docs/high-performance/message-queue/kafka-questions-01.md
+++ b/docs/high-performance/message-queue/kafka-questions-01.md
@@ -12,7 +12,7 @@ Kafka 是一个分布式流式处理平台。这到底是什么意思呢?
流平台具有三个关键功能:
1. **消息队列**:发布和订阅消息流,这个功能类似于消息队列,这也是 Kafka 也被归类为消息队列的原因。
-2. **容错的持久方式存储记录消息流**: Kafka 会把消息持久化到磁盘,有效避免了消息丢失的风险。
+2. **容错的持久方式存储记录消息流**:Kafka 会把消息持久化到磁盘,有效避免了消息丢失的风险。
3. **流式处理平台:** 在消息发布的时候进行处理,Kafka 提供了一个完整的流式处理类库。
Kafka 主要有两大应用场景:
@@ -104,7 +104,7 @@ ZooKeeper 主要为 Kafka 提供元数据的管理的功能。
从图中我们可以看出,Zookeeper 主要为 Kafka 做了下面这些事情:
1. **Broker 注册** :在 Zookeeper 上会有一个专门**用来进行 Broker 服务器列表记录**的节点。每个 Broker 在启动时,都会到 Zookeeper 上进行注册,即到 `/brokers/ids` 下创建属于自己的节点。每个 Broker 就会将自己的 IP 地址和端口等信息记录到该节点中去
-2. **Topic 注册** : 在 Kafka 中,同一个**Topic 的消息会被分成多个分区**并将其分布在多个 Broker 上,**这些分区信息及与 Broker 的对应关系**也都是由 Zookeeper 在维护。比如我创建了一个名字为 my-topic 的主题并且它有两个分区,对应到 zookeeper 中会创建这些文件夹:`/brokers/topics/my-topic/Partitions/0`、`/brokers/topics/my-topic/Partitions/1`
+2. **Topic 注册** :在 Kafka 中,同一个**Topic 的消息会被分成多个分区**并将其分布在多个 Broker 上,**这些分区信息及与 Broker 的对应关系**也都是由 Zookeeper 在维护。比如我创建了一个名字为 my-topic 的主题并且它有两个分区,对应到 zookeeper 中会创建这些文件夹:`/brokers/topics/my-topic/Partitions/0`、`/brokers/topics/my-topic/Partitions/1`
3. **负载均衡** :上面也说过了 Kafka 通过给特定 Topic 指定多个 Partition, 而各个 Partition 可以分布在不同的 Broker 上, 这样便能提供比较好的并发能力。 对于同一个 Topic 的不同 Partition,Kafka 会尽力将这些 Partition 分布到不同的 Broker 服务器上。当生产者产生消息后也会尽量投递到不同 Broker 的 Partition 里面。当 Consumer 消费的时候,Zookeeper 可以根据当前的 Partition 数量以及 Consumer 数量来实现动态负载均衡。
4. ......
@@ -220,5 +220,5 @@ acks 的默认值即为 1,代表我们的消息被 leader 副本接收之后
### Reference
-- Kafka 官方文档: https://kafka.apache.org/documentation/
+- Kafka 官方文档:https://kafka.apache.org/documentation/
- 极客时间—《Kafka 核心技术与实战》第 11 节:无消息丢失配置怎么实现?
diff --git a/docs/high-performance/message-queue/message-queue.md b/docs/high-performance/message-queue/message-queue.md
index 93394884a86..4556c677b17 100644
--- a/docs/high-performance/message-queue/message-queue.md
+++ b/docs/high-performance/message-queue/message-queue.md
@@ -31,11 +31,11 @@ tag:
> 中间件(英语:Middleware),又译中间件、中介层,是一类提供系统软件和应用软件之间连接、便于软件各部件之间的沟通的软件,应用软件可以借助中间件在不同的技术架构之间共享信息与资源。中间件位于客户机服务器的操作系统之上,管理着计算资源和网络通信。
-简单来说: **中间件就是一类为应用软件服务的软件,应用软件是为用户服务的,用户不会接触或者使用到中间件。**
+简单来说:**中间件就是一类为应用软件服务的软件,应用软件是为用户服务的,用户不会接触或者使用到中间件。**
除了消息队列之外,常见的中间件还有 RPC 框架、分布式组件、HTTP 服务器、任务调度框架、配置中心、数据库层的分库分表工具和数据迁移工具等等。
-关于中间件比较详细的介绍可以参考阿里巴巴淘系技术的一篇回答: https://www.zhihu.com/question/19730582/answer/1663627873 。
+关于中间件比较详细的介绍可以参考阿里巴巴淘系技术的一篇回答:https://www.zhihu.com/question/19730582/answer/1663627873 。
随着分布式和微服务系统的发展,消息队列在系统设计中有了更大的发挥空间,使用消息队列可以降低系统耦合性、实现任务异步、有效地进行流量削峰,是分布式和微服务系统中重要的组件之一。
@@ -85,7 +85,7 @@ tag:
我们知道分布式事务的解决方案之一就是 MQ 事务。
-RocketMQ 、 Kafka、Pulsar 、QMQ 都提供了事务相关的功能。事务允许事件流应用将消费,处理,生产消息整个过程定义为一个原子操作。
+RocketMQ、 Kafka、Pulsar、QMQ 都提供了事务相关的功能。事务允许事件流应用将消费,处理,生产消息整个过程定义为一个原子操作。
详细介绍可以查看 [分布式事务详解(付费)](https://javaguide.cn/distributed-system/distributed-transaction.html) 这篇文章。
@@ -173,7 +173,7 @@ Kafka 是 LinkedIn 开源的一个分布式流式处理平台,已经成为 Apa
流式处理平台具有三个关键功能:
1. **消息队列**:发布和订阅消息流,这个功能类似于消息队列,这也是 Kafka 也被归类为消息队列的原因。
-2. **容错的持久方式存储记录消息流**: Kafka 会把消息持久化到磁盘,有效避免了消息丢失的风险。
+2. **容错的持久方式存储记录消息流**:Kafka 会把消息持久化到磁盘,有效避免了消息丢失的风险。
3. **流式处理平台:** 在消息发布的时候进行处理,Kafka 提供了一个完整的流式处理类库。
Kafka 是一个分布式系统,由通过高性能 TCP 网络协议进行通信的服务器和客户端组成,可以部署在在本地和云环境中的裸机硬件、虚拟机和容器上。
@@ -276,7 +276,7 @@ Pulsar 更新记录(可以直观看到项目是否还在维护):https://gi
**总结:**
- ActiveMQ 的社区算是比较成熟,但是较目前来说,ActiveMQ 的性能比较差,而且版本迭代很慢,不推荐使用,已经被淘汰了。
-- RabbitMQ 在吞吐量方面虽然稍逊于 Kafka 、RocketMQ 和 Pulsar,但是由于它基于 Erlang 开发,所以并发能力很强,性能极其好,延时很低,达到微秒级。但是也因为 RabbitMQ 基于 Erlang 开发,所以国内很少有公司有实力做 Erlang 源码级别的研究和定制。如果业务场景对并发量要求不是太高(十万级、百万级),那这几种消息队列中,RabbitMQ 或许是你的首选。
+- RabbitMQ 在吞吐量方面虽然稍逊于 Kafka、RocketMQ 和 Pulsar,但是由于它基于 Erlang 开发,所以并发能力很强,性能极其好,延时很低,达到微秒级。但是也因为 RabbitMQ 基于 Erlang 开发,所以国内很少有公司有实力做 Erlang 源码级别的研究和定制。如果业务场景对并发量要求不是太高(十万级、百万级),那这几种消息队列中,RabbitMQ 或许是你的首选。
- RocketMQ 和 Pulsar 支持强一致性,对消息一致性要求比较高的场景可以使用。
- RocketMQ 阿里出品,Java 系开源项目,源代码我们可以直接阅读,然后可以定制自己公司的 MQ,并且 RocketMQ 有阿里巴巴的实际业务场景的实战考验。
- Kafka 的特点其实很明显,就是仅仅提供较少的核心功能,但是提供超高的吞吐量,ms 级的延迟,极高的可用性以及可靠性,而且分布式可以任意扩展。同时 Kafka 最好是支撑较少的 topic 数量即可,保证其超高吞吐量。Kafka 唯一的一点劣势是有可能消息重复消费,那么对数据准确性会造成极其轻微的影响,在大数据领域中以及日志采集中,这点轻微影响可以忽略这个特性天然适合大数据实时计算以及日志收集。如果是大数据领域的实时计算、日志采集等场景,用 Kafka 是业内标准的,绝对没问题,社区活跃度很高,绝对不会黄,何况几乎是全世界这个领域的事实性规范。
diff --git a/docs/high-performance/message-queue/rabbitmq-questions.md b/docs/high-performance/message-queue/rabbitmq-questions.md
index e32af32535f..95b1d89afe4 100644
--- a/docs/high-performance/message-queue/rabbitmq-questions.md
+++ b/docs/high-performance/message-queue/rabbitmq-questions.md
@@ -164,7 +164,7 @@ RabbitMQ 中的交换器、交换器类型、队列、绑定、路由键等都
## 说说 Broker 服务节点、Queue 队列、Exchange 交换器?
-- **Broker** : 可以看做 RabbitMQ 的服务节点。一般请下一个 Broker 可以看做一个 RabbitMQ 服务器。
+- **Broker** :可以看做 RabbitMQ 的服务节点。一般请下一个 Broker 可以看做一个 RabbitMQ 服务器。
- **Queue** :RabbitMQ 的内部对象,用于存储消息。多个消费者可以订阅同一队列,这时队列中的消息会被平摊(轮询)给多个消费者进行处理。
- **Exchange** : 生产者将消息发送到交换器,由交换器将消息路由到一个或者多个队列中。当路由不到时,或返回给生产者或直接丢弃。
diff --git a/docs/high-performance/message-queue/rocketmq-questions.md b/docs/high-performance/message-queue/rocketmq-questions.md
index 11d201ebdc5..81c467201c5 100644
--- a/docs/high-performance/message-queue/rocketmq-questions.md
+++ b/docs/high-performance/message-queue/rocketmq-questions.md
@@ -154,7 +154,7 @@ tag:
的确,早期的消息中间件是通过 **队列** 这一模型来实现的,可能是历史原因,我们都习惯把消息中间件成为消息队列。
-但是,如今例如 `RocketMQ` 、`Kafka` 这些优秀的消息中间件不仅仅是通过一个 **队列** 来实现消息存储的。
+但是,如今例如 `RocketMQ`、`Kafka` 这些优秀的消息中间件不仅仅是通过一个 **队列** 来实现消息存储的。
### 队列模型
@@ -188,11 +188,11 @@ tag:

-我们可以看到在整个图中有 `Producer Group` 、`Topic` 、`Consumer Group` 三个角色,我来分别介绍一下他们。
+我们可以看到在整个图中有 `Producer Group`、`Topic`、`Consumer Group` 三个角色,我来分别介绍一下他们。
-- `Producer Group` 生产者组: 代表某一类的生产者,比如我们有多个秒杀系统作为生产者,这多个合在一起就是一个 `Producer Group` 生产者组,它们一般生产相同的消息。
-- `Consumer Group` 消费者组: 代表某一类的消费者,比如我们有多个短信系统作为消费者,这多个合在一起就是一个 `Consumer Group` 消费者组,它们一般消费相同的消息。
-- `Topic` 主题: 代表一类消息,比如订单消息,物流消息等等。
+- `Producer Group` 生产者组:代表某一类的生产者,比如我们有多个秒杀系统作为生产者,这多个合在一起就是一个 `Producer Group` 生产者组,它们一般生产相同的消息。
+- `Consumer Group` 消费者组:代表某一类的消费者,比如我们有多个短信系统作为消费者,这多个合在一起就是一个 `Consumer Group` 消费者组,它们一般消费相同的消息。
+- `Topic` 主题:代表一类消息,比如订单消息,物流消息等等。
你可以看到图中生产者组中的生产者会向主题发送消息,而 **主题中存在多个队列**,生产者每次生产消息之后是指定主题中的某个队列发送消息的。
@@ -222,11 +222,11 @@ tag:
讲完了消息模型,我们理解起 `RocketMQ` 的技术架构起来就容易多了。
-`RocketMQ` 技术架构中有四大角色 `NameServer` 、`Broker` 、`Producer` 、`Consumer` 。我来向大家分别解释一下这四个角色是干啥的。
+`RocketMQ` 技术架构中有四大角色 `NameServer`、`Broker`、`Producer`、`Consumer` 。我来向大家分别解释一下这四个角色是干啥的。
-- `Broker`: 主要负责消息的存储、投递和查询以及服务高可用保证。说白了就是消息队列服务器嘛,生产者生产消息到 `Broker` ,消费者从 `Broker` 拉取消息并消费。
+- `Broker`:主要负责消息的存储、投递和查询以及服务高可用保证。说白了就是消息队列服务器嘛,生产者生产消息到 `Broker` ,消费者从 `Broker` 拉取消息并消费。
- 这里,我还得普及一下关于 `Broker` 、`Topic` 和 队列的关系。上面我讲解了 `Topic` 和队列的关系——一个 `Topic` 中存在多个队列,那么这个 `Topic` 和队列存放在哪呢?
+ 这里,我还得普及一下关于 `Broker`、`Topic` 和 队列的关系。上面我讲解了 `Topic` 和队列的关系——一个 `Topic` 中存在多个队列,那么这个 `Topic` 和队列存放在哪呢?
**一个 `Topic` 分布在多个 `Broker`上,一个 `Broker` 可以配置多个 `Topic` ,它们是多对多的关系**。
@@ -238,17 +238,17 @@ tag:
> 所以说我们需要配置多个 Broker。
-- `NameServer`: 不知道你们有没有接触过 `ZooKeeper` 和 `Spring Cloud` 中的 `Eureka` ,它其实也是一个 **注册中心** ,主要提供两个功能:**Broker 管理** 和 **路由信息管理** 。说白了就是 `Broker` 会将自己的信息注册到 `NameServer` 中,此时 `NameServer` 就存放了很多 `Broker` 的信息(Broker 的路由表),消费者和生产者就从 `NameServer` 中获取路由表然后照着路由表的信息和对应的 `Broker` 进行通信(生产者和消费者定期会向 `NameServer` 去查询相关的 `Broker` 的信息)。
+- `NameServer`:不知道你们有没有接触过 `ZooKeeper` 和 `Spring Cloud` 中的 `Eureka` ,它其实也是一个 **注册中心** ,主要提供两个功能:**Broker 管理** 和 **路由信息管理** 。说白了就是 `Broker` 会将自己的信息注册到 `NameServer` 中,此时 `NameServer` 就存放了很多 `Broker` 的信息(Broker 的路由表),消费者和生产者就从 `NameServer` 中获取路由表然后照着路由表的信息和对应的 `Broker` 进行通信(生产者和消费者定期会向 `NameServer` 去查询相关的 `Broker` 的信息)。
-- `Producer`: 消息发布的角色,支持分布式集群方式部署。说白了就是生产者。
+- `Producer`:消息发布的角色,支持分布式集群方式部署。说白了就是生产者。
-- `Consumer`: 消息消费的角色,支持分布式集群方式部署。支持以 push 推,pull 拉两种模式对消息进行消费。同时也支持集群方式和广播方式的消费,它提供实时消息订阅机制。说白了就是消费者。
+- `Consumer`:消息消费的角色,支持分布式集群方式部署。支持以 push 推,pull 拉两种模式对消息进行消费。同时也支持集群方式和广播方式的消费,它提供实时消息订阅机制。说白了就是消费者。
听完了上面的解释你可能会觉得,这玩意好简单。不就是这样的么?

-嗯?你可能会发现一个问题,这老家伙 `NameServer` 干啥用的,这不多余吗?直接 `Producer` 、`Consumer` 和 `Broker` 直接进行生产消息,消费消息不就好了么?
+嗯?你可能会发现一个问题,这老家伙 `NameServer` 干啥用的,这不多余吗?直接 `Producer`、`Consumer` 和 `Broker` 直接进行生产消息,消费消息不就好了么?
但是,我们上文提到过 `Broker` 是需要保证高可用的,如果整个系统仅仅靠着一个 `Broker` 来维持的话,那么这个 `Broker` 的压力会不会很大?所以我们需要使用多个 `Broker` 来保证 **负载均衡** 。
@@ -276,7 +276,7 @@ tag:
在上面我介绍 `RocketMQ` 的技术架构的时候我已经向你展示了 **它是如何保证高可用的** ,这里不涉及运维方面的搭建,如果你感兴趣可以自己去官网上照着例子搭建属于你自己的 `RocketMQ` 集群。
-> 其实 `Kafka` 的架构基本和 `RocketMQ` 类似,只是它注册中心使用了 `Zookeeper` 、它的 **分区** 就相当于 `RocketMQ` 中的 **队列** 。还有一些小细节不同会在后面提到。
+> 其实 `Kafka` 的架构基本和 `RocketMQ` 类似,只是它注册中心使用了 `Zookeeper`、它的 **分区** 就相当于 `RocketMQ` 中的 **队列** 。还有一些小细节不同会在后面提到。
### 顺序消费
@@ -380,8 +380,8 @@ emmm,就两个字—— **幂等** 。在编程中一个*幂等* 操作的特
上面的同步刷盘和异步刷盘是在单个结点层面的,而同步复制和异步复制主要是指的 `Borker` 主从模式下,主节点返回消息给客户端的时候是否需要同步从节点。
-- 同步复制: 也叫 “同步双写”,也就是说,**只有消息同步双写到主从节点上时才返回写入成功** 。
-- 异步复制: **消息写入主节点之后就直接返回写入成功** 。
+- 同步复制:也叫 “同步双写”,也就是说,**只有消息同步双写到主从节点上时才返回写入成功** 。
+- 异步复制:**消息写入主节点之后就直接返回写入成功** 。
然而,很多事情是没有完美的方案的,就比如我们进行消息写入的节点越多就更能保证消息的可靠性,但是随之的性能也会下降,所以需要程序员根据特定业务场景去选择适应的主从复制方案。
@@ -405,11 +405,11 @@ emmm,就两个字—— **幂等** 。在编程中一个*幂等* 操作的特
还记得上面我们一开始的三个问题吗?到这里第三个问题已经解决了。
-但是,在 `Topic` 中的 **队列是以什么样的形式存在的?队列中的消息又是如何进行存储持久化的呢?** 还未解决,其实这里涉及到了 `RocketMQ` 是如何设计它的存储结构了。我首先想大家介绍 `RocketMQ` 消息存储架构中的三大角色——`CommitLog` 、`ConsumeQueue` 和 `IndexFile` 。
+但是,在 `Topic` 中的 **队列是以什么样的形式存在的?队列中的消息又是如何进行存储持久化的呢?** 还未解决,其实这里涉及到了 `RocketMQ` 是如何设计它的存储结构了。我首先想大家介绍 `RocketMQ` 消息存储架构中的三大角色——`CommitLog`、`ConsumeQueue` 和 `IndexFile` 。
-- `CommitLog`: **消息主体以及元数据的存储主体**,存储 `Producer` 端写入的消息主体内容,消息内容不是定长的。单个文件大小默认 1G ,文件名长度为 20 位,左边补零,剩余为起始偏移量,比如 00000000000000000000 代表了第一个文件,起始偏移量为 0,文件大小为 1G=1073741824;当第一个文件写满了,第二个文件为 00000000001073741824,起始偏移量为 1073741824,以此类推。消息主要是**顺序写入日志文件**,当文件满了,写入下一个文件。
-- `ConsumeQueue`: 消息消费队列,**引入的目的主要是提高消息消费的性能**(我们再前面也讲了),由于`RocketMQ` 是基于主题 `Topic` 的订阅模式,消息消费是针对主题进行的,如果要遍历 `commitlog` 文件中根据 `Topic` 检索消息是非常低效的。`Consumer` 即可根据 `ConsumeQueue` 来查找待消费的消息。其中,`ConsumeQueue`(逻辑消费队列)**作为消费消息的索引**,保存了指定 `Topic` 下的队列消息在 `CommitLog` 中的**起始物理偏移量 `offset` **,消息大小 `size` 和消息 `Tag` 的 `HashCode` 值。**`consumequeue` 文件可以看成是基于 `topic` 的 `commitlog` 索引文件**,故 `consumequeue` 文件夹的组织方式如下:topic/queue/file 三层组织结构,具体存储路径为:$HOME/store/consumequeue/{topic}/{queueId}/{fileName}。同样 `consumequeue` 文件采取定长设计,每一个条目共 20 个字节,分别为 8 字节的 `commitlog` 物理偏移量、4 字节的消息长度、8 字节 tag `hashcode`,单个文件由 30W 个条目组成,可以像数组一样随机访问每一个条目,每个 `ConsumeQueue`文件大小约 5.72M;
-- `IndexFile`: `IndexFile`(索引文件)提供了一种可以通过 key 或时间区间来查询消息的方法。这里只做科普不做详细介绍。
+- `CommitLog`:**消息主体以及元数据的存储主体**,存储 `Producer` 端写入的消息主体内容,消息内容不是定长的。单个文件大小默认 1G ,文件名长度为 20 位,左边补零,剩余为起始偏移量,比如 00000000000000000000 代表了第一个文件,起始偏移量为 0,文件大小为 1G=1073741824;当第一个文件写满了,第二个文件为 00000000001073741824,起始偏移量为 1073741824,以此类推。消息主要是**顺序写入日志文件**,当文件满了,写入下一个文件。
+- `ConsumeQueue`:消息消费队列,**引入的目的主要是提高消息消费的性能**(我们再前面也讲了),由于`RocketMQ` 是基于主题 `Topic` 的订阅模式,消息消费是针对主题进行的,如果要遍历 `commitlog` 文件中根据 `Topic` 检索消息是非常低效的。`Consumer` 即可根据 `ConsumeQueue` 来查找待消费的消息。其中,`ConsumeQueue`(逻辑消费队列)**作为消费消息的索引**,保存了指定 `Topic` 下的队列消息在 `CommitLog` 中的**起始物理偏移量 `offset` **,消息大小 `size` 和消息 `Tag` 的 `HashCode` 值。**`consumequeue` 文件可以看成是基于 `topic` 的 `commitlog` 索引文件**,故 `consumequeue` 文件夹的组织方式如下:topic/queue/file 三层组织结构,具体存储路径为:$HOME/store/consumequeue/{topic}/{queueId}/{fileName}。同样 `consumequeue` 文件采取定长设计,每一个条目共 20 个字节,分别为 8 字节的 `commitlog` 物理偏移量、4 字节的消息长度、8 字节 tag `hashcode`,单个文件由 30W 个条目组成,可以像数组一样随机访问每一个条目,每个 `ConsumeQueue`文件大小约 5.72M;
+- `IndexFile`:`IndexFile`(索引文件)提供了一种可以通过 key 或时间区间来查询消息的方法。这里只做科普不做详细介绍。
总结来说,整个消息存储的结构,最主要的就是 `CommitLoq` 和 `ConsumeQueue` 。而 `ConsumeQueue` 你可以大概理解为 `Topic` 中的队列。
@@ -431,7 +431,7 @@ emmm,是不是有一点复杂 🤣,看英文图片和英文文档的时候
首先,在最上面的那一块就是我刚刚讲的你现在可以直接 **把 `ConsumerQueue` 理解为 `Queue`**。
-在图中最左边说明了红色方块代表被写入的消息,虚线方块代表等待被写入的。左边的生产者发送消息会指定 `Topic` 、`QueueId` 和具体消息内容,而在 `Broker` 中管你是哪门子消息,他直接 **全部顺序存储到了 CommitLog**。而根据生产者指定的 `Topic` 和 `QueueId` 将这条消息本身在 `CommitLog` 的偏移(offset),消息本身大小,和 tag 的 hash 值存入对应的 `ConsumeQueue` 索引文件中。而在每个队列中都保存了 `ConsumeOffset` 即每个消费者组的消费位置(我在架构那里提到了,忘了的同学可以回去看一下),而消费者拉取消息进行消费的时候只需要根据 `ConsumeOffset` 获取下一个未被消费的消息就行了。
+在图中最左边说明了红色方块代表被写入的消息,虚线方块代表等待被写入的。左边的生产者发送消息会指定 `Topic`、`QueueId` 和具体消息内容,而在 `Broker` 中管你是哪门子消息,他直接 **全部顺序存储到了 CommitLog**。而根据生产者指定的 `Topic` 和 `QueueId` 将这条消息本身在 `CommitLog` 的偏移(offset),消息本身大小,和 tag 的 hash 值存入对应的 `ConsumeQueue` 索引文件中。而在每个队列中都保存了 `ConsumeOffset` 即每个消费者组的消费位置(我在架构那里提到了,忘了的同学可以回去看一下),而消费者拉取消息进行消费的时候只需要根据 `ConsumeOffset` 获取下一个未被消费的消息就行了。
上述就是我对于整个消息存储架构的大概理解(这里不涉及到一些细节讨论,比如稀疏索引等等问题),希望对你有帮助。
@@ -451,7 +451,7 @@ emmm,是不是有一点复杂 🤣,看英文图片和英文文档的时候
2. 消息队列的作用(异步,解耦,削峰)
3. 消息队列带来的一系列问题(消息堆积、重复消费、顺序消费、分布式事务等等)
4. 消息队列的两种消息模型——队列和主题模式
-5. 分析了 `RocketMQ` 的技术架构(`NameServer` 、`Broker` 、`Producer` 、`Comsumer`)
+5. 分析了 `RocketMQ` 的技术架构(`NameServer`、`Broker`、`Producer`、`Comsumer`)
6. 结合 `RocketMQ` 回答了消息队列副作用的解决方案
7. 介绍了 `RocketMQ` 的存储机制和刷盘策略。
diff --git a/docs/high-performance/read-and-write-separation-and-library-subtable.md b/docs/high-performance/read-and-write-separation-and-library-subtable.md
index 4e06d8b46cc..4e91f93962e 100644
--- a/docs/high-performance/read-and-write-separation-and-library-subtable.md
+++ b/docs/high-performance/read-and-write-separation-and-library-subtable.md
@@ -169,7 +169,7 @@ MySQL binlog(binary log 即二进制日志文件) 主要记录了 MySQL 数据
引入分库分表之后,会给系统带来什么挑战呢?
-- **join 操作** : 同一个数据库中的表分布在了不同的数据库中,导致无法使用 join 操作。这样就导致我们需要手动进行数据的封装,比如你在一个数据库中查询到一个数据之后,再根据这个数据去另外一个数据库中找对应的数据。
+- **join 操作** :同一个数据库中的表分布在了不同的数据库中,导致无法使用 join 操作。这样就导致我们需要手动进行数据的封装,比如你在一个数据库中查询到一个数据之后,再根据这个数据去另外一个数据库中找对应的数据。
- **事务问题** :同一个数据库中的表分布在了不同的数据库中,如果单个操作涉及到多个数据库,那么数据库自带的事务就无法满足我们的要求了。
- **分布式 id** :分库之后, 数据遍布在不同服务器上的数据库,数据库的自增主键已经没办法满足生成的主键唯一了。我们如何为不同的数据节点生成全局唯一主键呢?这个时候,我们就需要为我们的系统引入分布式 id 了。
- ......
diff --git a/docs/high-quality-technical-articles/advanced-programmer/20-bad-habits-of-bad-programmers.md b/docs/high-quality-technical-articles/advanced-programmer/20-bad-habits-of-bad-programmers.md
index 7054cb46842..379fe2a7d17 100644
--- a/docs/high-quality-technical-articles/advanced-programmer/20-bad-habits-of-bad-programmers.md
+++ b/docs/high-quality-technical-articles/advanced-programmer/20-bad-habits-of-bad-programmers.md
@@ -6,7 +6,7 @@ tag:
- 练级攻略
---
-> **推荐语** : Kaito 大佬的一篇文章,很实用的建议!
+> **推荐语** :Kaito 大佬的一篇文章,很实用的建议!
>
>
>
diff --git a/docs/high-quality-technical-articles/advanced-programmer/the-growth-strategy-of-the-technological-giant.md b/docs/high-quality-technical-articles/advanced-programmer/the-growth-strategy-of-the-technological-giant.md
index 5d3c2c9a6fd..0c66fab1c16 100644
--- a/docs/high-quality-technical-articles/advanced-programmer/the-growth-strategy-of-the-technological-giant.md
+++ b/docs/high-quality-technical-articles/advanced-programmer/the-growth-strategy-of-the-technological-giant.md
@@ -6,7 +6,7 @@ tag:
- 练级攻略
---
-> **推荐语** : 波波老师的一篇文章,写的非常好,不光是对技术成长有帮助,其他领域也是同样适用的!建议反复阅读,形成一套自己的技术成长策略。
+> **推荐语** :波波老师的一篇文章,写的非常好,不光是对技术成长有帮助,其他领域也是同样适用的!建议反复阅读,形成一套自己的技术成长策略。
>
>
>
@@ -18,7 +18,7 @@ tag:
**技术人为啥焦虑?** 恕我直言,说白了是胆识不足格局太小。胆就是胆量,焦虑的人一般对未来的不确定性怀有恐惧。识就是见识,焦虑的人一般看不清楚周围世界,也看不清自己和适合自己的道路。格局也称志向,容易焦虑的人通常视野窄志向小。如果从战略和管理的视角来看,就是对自己和周围世界的认知不足,没有一个清晰和长期的学习成长战略,也没有可执行的阶段性目标计划+严格的执行。
-因为问此类问题的学员很多,让我感觉有点烦了,为了避免重复回答,所以我专门总结梳理了这篇长文,试图统一来回答这类问题。如果后面还有学员问类似问题,我会引导他们来读这篇文章,然后让他们用三个月、一年甚至更长的时间,去思考和回答这样一个问题: **你的技术成长战略究竟是什么?** 如果你想清楚了这个问题,有清晰和可落地的答案,那么恭喜你,你只需按部就班执行就好,根本无需焦虑,你实现自己的战略目标并做出成就只是一个时间问题;否则,你仍然需要通过不断磨炼+思考,务必去搞清楚这个人生的大问题!!!
+因为问此类问题的学员很多,让我感觉有点烦了,为了避免重复回答,所以我专门总结梳理了这篇长文,试图统一来回答这类问题。如果后面还有学员问类似问题,我会引导他们来读这篇文章,然后让他们用三个月、一年甚至更长的时间,去思考和回答这样一个问题:**你的技术成长战略究竟是什么?** 如果你想清楚了这个问题,有清晰和可落地的答案,那么恭喜你,你只需按部就班执行就好,根本无需焦虑,你实现自己的战略目标并做出成就只是一个时间问题;否则,你仍然需要通过不断磨炼+思考,务必去搞清楚这个人生的大问题!!!
下面我们来看一些行业技术大牛是怎么做的。
diff --git a/docs/high-quality-technical-articles/interview/how-to-examine-the-technical-ability-of-programmers-in-the-first-test-of-technology.md b/docs/high-quality-technical-articles/interview/how-to-examine-the-technical-ability-of-programmers-in-the-first-test-of-technology.md
index 9f36a152ab4..b1b9ee3bdf2 100644
--- a/docs/high-quality-technical-articles/interview/how-to-examine-the-technical-ability-of-programmers-in-the-first-test-of-technology.md
+++ b/docs/high-quality-technical-articles/interview/how-to-examine-the-technical-ability-of-programmers-in-the-first-test-of-technology.md
@@ -35,12 +35,12 @@ tag:
技术基础是基石(冰山之下的东西),占七分, 解决问题的思路和能力是落地(冰山之上露出的部分),占三分。 业务和技术基础考察,三七开。
-核心考察目标: 分析和解决问题的能力。
+核心考察目标:分析和解决问题的能力。
技术层面:深度 + 应用能力 + 广度。 对于校招或社招 P6 级别以下,要多注重 深度 + 应用能力,广度是加分项; 在 P6 之上,可增加 广度。
-- 校招: 基础扎实,思维敏捷。 主要考察内容:基础数据结构与算法、进程与并发、内存管理、系统调用与 IO 机制、网络协议、数据库范式与设计、设计模式、设计原则、编程习惯;
-- 社招: 经验丰富,里外兼修。 主要考察内容:有一定深度的基础技术机制,比如 Java 内存模型及内存泄露、 JVM 机制、类加载机制、数据库索引及查询优化、缓存、消息中间件、项目、架构设计、工程规范等。
+- 校招:基础扎实,思维敏捷。 主要考察内容:基础数据结构与算法、进程与并发、内存管理、系统调用与 IO 机制、网络协议、数据库范式与设计、设计模式、设计原则、编程习惯;
+- 社招:经验丰富,里外兼修。 主要考察内容:有一定深度的基础技术机制,比如 Java 内存模型及内存泄露、 JVM 机制、类加载机制、数据库索引及查询优化、缓存、消息中间件、项目、架构设计、工程规范等。
### 技术基础是什么?
@@ -78,7 +78,7 @@ tag:
**引导-横向发问-深入发问**
-引导性,比如 “你对 java 同步工具熟悉吗?” 作个试探,得到肯定答复后,可以进一步问: “你熟悉哪些同步工具类?” 了解候选者的广度;
+引导性,比如 “你对 java 同步工具熟悉吗?” 作个试探,得到肯定答复后,可以进一步问:“你熟悉哪些同步工具类?” 了解候选者的广度;
获取候选者的回答后,可以进一步问:“ 谈谈 ConcurrentHashMap 或 AQS 的实现原理?”
@@ -313,7 +313,7 @@ tag:
如果候选人答不上,可以问:如果你来设计这样一个 XXX, 你会怎么做?
-时间占比大概为 : 技术基础(25-30 分钟) + 项目(20-25 分钟) + 候选人提问(5-10 分钟)
+时间占比大概为 :技术基础(25-30 分钟) + 项目(20-25 分钟) + 候选人提问(5-10 分钟)
## 给候选人的话
diff --git a/docs/high-quality-technical-articles/interview/my-personal-experience-in-2021.md b/docs/high-quality-technical-articles/interview/my-personal-experience-in-2021.md
index 73131440eae..5963059dfae 100644
--- a/docs/high-quality-technical-articles/interview/my-personal-experience-in-2021.md
+++ b/docs/high-quality-technical-articles/interview/my-personal-experience-in-2021.md
@@ -10,7 +10,7 @@ tag:
>
>
>
-> **原文地址** : https://www.ihewro.com/archives/1217/
+> **原文地址** :https://www.ihewro.com/archives/1217/
## 基本情况
diff --git a/docs/high-quality-technical-articles/interview/screen-candidates-for-packaging.md b/docs/high-quality-technical-articles/interview/screen-candidates-for-packaging.md
index 4d576f8310f..9ea906e2716 100644
--- a/docs/high-quality-technical-articles/interview/screen-candidates-for-packaging.md
+++ b/docs/high-quality-technical-articles/interview/screen-candidates-for-packaging.md
@@ -10,7 +10,7 @@ tag:
>
>
>
-> **原文地址** : https://my.oschina.net/hooker/blog/3014656
+> **原文地址** :https://my.oschina.net/hooker/blog/3014656
## 前言
diff --git a/docs/high-quality-technical-articles/interview/some-secrets-about-alibaba-interview.md b/docs/high-quality-technical-articles/interview/some-secrets-about-alibaba-interview.md
index 806f00f3b8f..089b011684e 100644
--- a/docs/high-quality-technical-articles/interview/some-secrets-about-alibaba-interview.md
+++ b/docs/high-quality-technical-articles/interview/some-secrets-about-alibaba-interview.md
@@ -6,7 +6,7 @@ tag:
- 面试
---
-> **推荐语** : 详细介绍了求职者在面试中应该具备哪些能力才会有更大概率脱颖而出。
+> **推荐语** :详细介绍了求职者在面试中应该具备哪些能力才会有更大概率脱颖而出。
>
>
>
diff --git a/docs/high-quality-technical-articles/interview/summary-of-spring-recruitment.md b/docs/high-quality-technical-articles/interview/summary-of-spring-recruitment.md
index 8cfb4f13a8d..0288681822a 100644
--- a/docs/high-quality-technical-articles/interview/summary-of-spring-recruitment.md
+++ b/docs/high-quality-technical-articles/interview/summary-of-spring-recruitment.md
@@ -6,7 +6,7 @@ tag:
- 面试
---
-> **推荐语** : 牛客网热帖,写的很全面!暑期实习,投了阿里、腾讯、字节,拿到了阿里和腾讯的 offer。
+> **推荐语** :牛客网热帖,写的很全面!暑期实习,投了阿里、腾讯、字节,拿到了阿里和腾讯的 offer。
>
>
>
@@ -63,7 +63,7 @@ tag:
我上面谈到的学习路线,我建议是跟着视频学,尚硅谷和黑马的教程都可以,一定要手敲一遍。
-- [2021 南京大学 “操作系统:设计与实现” (蒋炎岩)](https://www.bilibili.com/video/BV1HN41197Ko): 我不多说了,看评论就知道了。
+- [2021 南京大学 “操作系统:设计与实现” (蒋炎岩)](https://www.bilibili.com/video/BV1HN41197Ko):我不多说了,看评论就知道了。
- [SpringSecurity-Social-OAuth2 社交登录接口授权鉴权系列课程](https://www.bilibili.com/video/BV16J41127jq) :字母哥讲的 Spring Security 也很好,Spring Security 或者 Shiro 是做项目必备的,会一个就好,根据实际场景以及个人喜好(笑)来选型。
- [清华大学邓俊辉数据结构与算法](https://www.bilibili.com/video/BV1jt4y117KR) :清华不解释了。
- [MySQL 实战 45 讲](https://time.geekbang.org/column/intro/100020801):前 27 讲多看几遍基本可以秒杀面试中遇到的 MySQL 问题了。
diff --git a/docs/high-quality-technical-articles/interview/technical-preliminary-preparation.md b/docs/high-quality-technical-articles/interview/technical-preliminary-preparation.md
index 60c0388975a..446799ca380 100644
--- a/docs/high-quality-technical-articles/interview/technical-preliminary-preparation.md
+++ b/docs/high-quality-technical-articles/interview/technical-preliminary-preparation.md
@@ -6,7 +6,7 @@ tag:
- 面试
---
-> **推荐语** : 从面试官和面试者两个角度探讨了技术面试!非常不错!
+> **推荐语** :从面试官和面试者两个角度探讨了技术面试!非常不错!
>
> **内容概览:**
>
@@ -48,7 +48,7 @@ tag:
#### 引导-横向发问-深入发问
-引导性,比如 “你对 Java 同步工具熟悉吗?” 作个试探,得到肯定答复后,可以进一步问: “你熟悉哪些同步工具类?” 了解候选者的广度;
+引导性,比如 “你对 Java 同步工具熟悉吗?” 作个试探,得到肯定答复后,可以进一步问:“你熟悉哪些同步工具类?” 了解候选者的广度;
获取候选者的回答后,可以进一步问:“ 谈谈 `ConcurrentHashMap` 或 `AQS` 的实现原理?”
diff --git a/docs/high-quality-technical-articles/interview/the-experience-and-thinking-of-an-interview-experienced-by-an-older-programmer.md b/docs/high-quality-technical-articles/interview/the-experience-and-thinking-of-an-interview-experienced-by-an-older-programmer.md
index 5d3f1e7eb6c..234cbda5e38 100644
--- a/docs/high-quality-technical-articles/interview/the-experience-and-thinking-of-an-interview-experienced-by-an-older-programmer.md
+++ b/docs/high-quality-technical-articles/interview/the-experience-and-thinking-of-an-interview-experienced-by-an-older-programmer.md
@@ -155,8 +155,8 @@ tag:
- Redis : 数据结构、缓存、分布式锁、持久化机制、复制机制;
- 分布式:分布式事务、一致性问题;
- 消息中间件:原理、对比;
-- 架构: 架构设计方法、架构经验、设计模式;
-- 性能优化: JVM、GC、应用层面的性能优化;
+- 架构:架构设计方法、架构经验、设计模式;
+- 性能优化:JVM、GC、应用层面的性能优化;
- 并发基础:ConcurrentHashMap, AQS, CAS,线程池等;
- 高并发:IO 多路复用;缓存问题及方案;
- 稳定性:稳定性的思想及经验;
diff --git a/docs/high-quality-technical-articles/interview/the-experience-of-get-offer-from-over-20-big-companies.md b/docs/high-quality-technical-articles/interview/the-experience-of-get-offer-from-over-20-big-companies.md
index bcb42dfc63b..2e6357419c3 100644
--- a/docs/high-quality-technical-articles/interview/the-experience-of-get-offer-from-over-20-big-companies.md
+++ b/docs/high-quality-technical-articles/interview/the-experience-of-get-offer-from-over-20-big-companies.md
@@ -10,7 +10,7 @@ tag:
>
>
>
-> **原文地址** : https://mp.weixin.qq.com/s/HXKg6-H0kGUU2OA1DS43Bw
+> **原文地址** :https://mp.weixin.qq.com/s/HXKg6-H0kGUU2OA1DS43Bw
突然回想起当年,我也在秋招时也斩获了 20+的互联网各大厂 offer。现在想起来也是有点唏嘘,毕竟拿得再多也只能选择一家。不过许多朋友想让我分享下互联网面试方法,今天就来给大家仔细讲讲打法!
diff --git a/docs/high-quality-technical-articles/personal-experience/two-years-of-back-end-develop--experience-in-didi-and-toutiao.md b/docs/high-quality-technical-articles/personal-experience/two-years-of-back-end-develop--experience-in-didi-and-toutiao.md
index 6db454a72cd..80a1cfc1ea4 100644
--- a/docs/high-quality-technical-articles/personal-experience/two-years-of-back-end-develop--experience-in-didi-and-toutiao.md
+++ b/docs/high-quality-technical-articles/personal-experience/two-years-of-back-end-develop--experience-in-didi-and-toutiao.md
@@ -38,7 +38,7 @@ tag:
还有的同学说了,我就每天跟 PM 撕撕逼,做做需求,也不做性能优化啊。先不讨论是否可以搞性能优化,单就做业务需求来讲,也有可以总结的地方。比如说,如何做系统建设?系统核心能力,系统边界,系统瓶颈,服务分层拆分,服务治理这些问题有思考过吗?每天跟 PM 讨论需求,那作为技术同学该如何培养产品思维,引导产品走向,如何做到架构先行于业务,这些问题也是可以思考和总结的吧。就想一下,连接手维护别人烂代码这种蛋疼的事情,都能让 Martin Fowler 整出来一套重构理论,还显得那么高大上,我们确实也没啥必要对自己的工作妄自菲薄...
-所以说: **学习和成长是一个自驱的过程,如果觉得没什么可学的,大概率并不是真的没什么可学的,而是因为自己太懒了,不仅是行动上太懒了,思维上也太懒了。可以多写技术文章,多分享,强迫自己去思考和总结,毕竟如果文章深度不够,大家也不好意思公开分享。**
+所以说:**学习和成长是一个自驱的过程,如果觉得没什么可学的,大概率并不是真的没什么可学的,而是因为自己太懒了,不仅是行动上太懒了,思维上也太懒了。可以多写技术文章,多分享,强迫自己去思考和总结,毕竟如果文章深度不够,大家也不好意思公开分享。**
## 积极学习,保持技术热情
diff --git a/docs/high-quality-technical-articles/work/employee-performance.md b/docs/high-quality-technical-articles/work/employee-performance.md
index 8f40afb1bf8..46deda73b0d 100644
--- a/docs/high-quality-technical-articles/work/employee-performance.md
+++ b/docs/high-quality-technical-articles/work/employee-performance.md
@@ -85,7 +85,7 @@ tag:
上面讲了一堆潜规则,是不是意味着绩效考核是可以投机取巧,完全不看工作业绩呢,当然不是。
-“你的努力不一定会被看见” 、“你的努力应该有的放矢”,大家先记住这两条。
+“你的努力不一定会被看见”、“你的努力应该有的放矢”,大家先记住这两条。
下面我再展开聊聊,大家最最关心的 A 和 C,它们背后的逻辑。
diff --git a/docs/interview-preparation/key-points-of-interview.md b/docs/interview-preparation/key-points-of-interview.md
index 226d9c30e67..6375c878100 100644
--- a/docs/interview-preparation/key-points-of-interview.md
+++ b/docs/interview-preparation/key-points-of-interview.md
@@ -14,7 +14,7 @@ icon: star
给你几点靠谱的建议:
-1. Java 基础、集合、并发、MySQL、Redis 、Spring、Spring Boot 这些 Java 后端开发必备的知识点。大厂以及中小厂的面试问的比较多的就是这些知识点(不信的话,你可以去多找一些面经看看)。我这里没有提到计算机基础相关的内容,这个会在下面提到。
+1. Java 基础、集合、并发、MySQL、Redis、Spring、Spring Boot 这些 Java 后端开发必备的知识点。大厂以及中小厂的面试问的比较多的就是这些知识点(不信的话,你可以去多找一些面经看看)。我这里没有提到计算机基础相关的内容,这个会在下面提到。
2. 你的项目经历涉及到的知识点,有水平的面试官都是会根据你的项目经历来问的。举个例子,你的项目经历使用了 Redis 来做限流,那 Redis 相关的八股文(比如 Redis 常见数据结构)以及限流相关的八股文(比如常见的限流算法)你就应该多花更多心思来搞懂!吃透!你把项目经历上的知识点吃透之后,再把你简历上哪些写熟练掌握的技术给吃透。最后,再去花时间准备其他知识点。
3. 针对自身找工作的需求,你又可以适当地调整复习的重点。像中小厂一般问计算机基础比较少一些,有些大厂比如字节比较重视计算机基础尤其是算法。这样的话,如果你的目标是中小厂的话,计算机基础就准备面试来说不是那么重要了。如果复习时间不够的话,可以暂时先放放。
4. 一般校招的面试不会强制要求你会分布式/微服务、高并发的知识(不排除个别岗位有这方面的硬性要求),所以到底要不要掌握还是要看你个人当前的实际情况。如果你会这方面的知识的话,对面试相对来说还是会更有利一些(想要让项目经历有亮点,还是得会一些性能优化的知识。性能优化的知识这也算是高并发知识的一个小分支了)。如果你的技能介绍或者项目经历涉及到分布式/微服务、高并发的知识,那建议你尽量也要抽时间去认真准备一下,面试中很可能会被问到,尤其是项目经历用到的时候。不过,也还是主要准备写在简历上的那些知识点就好。
diff --git a/docs/interview-preparation/project-experience-guide.md b/docs/interview-preparation/project-experience-guide.md
index 58606158e24..0692d2b73ab 100644
--- a/docs/interview-preparation/project-experience-guide.md
+++ b/docs/interview-preparation/project-experience-guide.md
@@ -42,7 +42,7 @@ Github 或者码云上面有很多实战类别项目,你可以选择一个来

-一定要记住: **不光要做,还要改进,改善。不论是实战项目视频或者专栏还是实战类开源项目,都一定会有很多可以完善改进的地方。**
+一定要记住:**不光要做,还要改进,改善。不论是实战项目视频或者专栏还是实战类开源项目,都一定会有很多可以完善改进的地方。**
### 从头开始做
@@ -94,7 +94,7 @@ Github 或者码云上面有很多实战类别项目,你可以选择一个来
2. **项目的技术选型优化** :比如使用 Guava 做本地缓存的地方可以换成 **Caffeine** 。Caffeine 的各方面的表现要更加好!再比如 Controller 层是否放了太多的业务逻辑。
3. **数据库方面** :数据库设计可否优化?索引是否使用使用正确?SQL 语句是否可以优化?是否需要进行读写分离?
4. **缓存** :项目有没有哪些数据是经常被访问的?是否引入缓存来提高响应速度?
-5. **安全** : 项目是否存在安全问题?
+5. **安全** :项目是否存在安全问题?
6. ......
另外,我在星球分享过常见的性能优化方向实践案例,涉及到多线程、异步、索引、缓存等方向,强烈推荐你看看: 。
diff --git a/docs/interview-preparation/resume-guide.md b/docs/interview-preparation/resume-guide.md
index 647e9b9ffbd..a496c1e256e 100644
--- a/docs/interview-preparation/resume-guide.md
+++ b/docs/interview-preparation/resume-guide.md
@@ -23,7 +23,7 @@ icon: jianli
**2、简历上的内容很大程度上决定了面试官提问的侧重点。**
-- 一般情况下你的简历上注明你会的东西才会被问到(Java 基础、集合、并发、MySQL、Redis 、Spring、Spring Boot 这些算是每个人必问的),比如写了你熟练使用 Redis,那面试官就很大概率会问你 Redis 的一些问题,再比如你写了你在项目中使用了消息队列,那面试官大概率问很多消息队列相关的问题。
+- 一般情况下你的简历上注明你会的东西才会被问到(Java 基础、集合、并发、MySQL、Redis、Spring、Spring Boot 这些算是每个人必问的),比如写了你熟练使用 Redis,那面试官就很大概率会问你 Redis 的一些问题,再比如你写了你在项目中使用了消息队列,那面试官大概率问很多消息队列相关的问题。
- 技能熟练度在很大程度上也决定了面试官提问的深度。
在不夸大自己能力的情况下,写出一份好的简历也是一项很棒的能力。
@@ -37,13 +37,13 @@ icon: jianli
下面是我收集的一些还不错的简历模板:
- 适合中文的简历模板收集(推荐,免费):https://github.com/dyweb/awesome-resume-for-chinese
-- 木及简历(部分收费) : https://www.mujicv.com/。
+- 木及简历(部分收费) :https://www.mujicv.com/。
- 简单简历(付费):https://easycv.cn/
- 站长简历:https://jianli.chinaz.com/
- typora+markdown+css 自定义简历模板 :https://github.com/Snailclimb/typora-markdown-resume
-- 极简简历 : https://www.polebrief.com/index
+- 极简简历 :https://www.polebrief.com/index
- Markdown 简历排版工具:https://resume.mdnice.com/
-- 超级简历(部分收费) : https://www.wondercv.com/
+- 超级简历(部分收费) :https://www.wondercv.com/
上面这些简历模板大多是只有 1 页内容,很难展现足够的信息量。如果你不是顶级大牛(比如 ACM 大赛获奖)的话,我建议还是尽可能多写一点可以突出你自己能力的内容(校招生 2 页之内,社招生 3 页之内,记得精炼语言,不要过多废话)。
@@ -58,7 +58,7 @@ icon: jianli
### 个人信息
- 最基本的 :姓名(身份证上的那个)、年龄、电话、籍贯、联系方式、邮箱地址
-- 潜在加分项 : Github 地址、博客地址(如果技术博客和 Github 上没有什么内容的话,就不要写了)
+- 潜在加分项 :Github 地址、博客地址(如果技术博客和 Github 上没有什么内容的话,就不要写了)
示例:
@@ -240,4 +240,4 @@ FAB 法则由下面 3 个单词组成(FAB 法则的名字就是由它们的首
- 项目经历建议以时间倒序排序,另外项目经历不在于多(精选 2~3 即可),而在于有亮点。
- 个人评价就是对自己的解读,一定要用简洁的语言突出自己的特点和优势,避免废话! 像勤奋、吃苦这些比较虚的东西就不要扯了,面试官看着这种个人评价就烦。
- 准备面试的过程中应该将你写在简历上的东西作为重点,尤其是项目经历上和技能介绍上的。
-- 面试和工作是两回事,聪明的人会把面试官往自己擅长的领域领,其他人则被面试官牵着鼻子走。虽说面试和工作是两回事,但是你要想要获得自己满意的 offer ,你自身的实力必须要强。
\ No newline at end of file
+- 面试和工作是两回事,聪明的人会把面试官往自己擅长的领域领,其他人则被面试官牵着鼻子走。虽说面试和工作是两回事,但是你要想要获得自己满意的 offer ,你自身的实力必须要强。
diff --git a/docs/java/basis/java-basic-questions-01.md b/docs/java/basis/java-basic-questions-01.md
index a6256070fd5..5f7ffda88b0 100644
--- a/docs/java/basis/java-basic-questions-01.md
+++ b/docs/java/basis/java-basic-questions-01.md
@@ -27,7 +27,7 @@ head:
7. 支持网络编程并且很方便( Java 语言诞生本身就是为简化网络编程设计的,因此 Java 语言不仅支持网络编程而且很方便);
8. 编译与解释并存;
-> **🐛 修正(参见: [issue#544](https://github.com/Snailclimb/JavaGuide/issues/544))** :C++11 开始(2011 年的时候),C++就引入了多线程库,在 windows、linux、macos 都可以使用`std::thread`和`std::async`来创建线程。参考链接:http://www.cplusplus.com/reference/thread/thread/?kw=thread
+> **🐛 修正(参见:[issue#544](https://github.com/Snailclimb/JavaGuide/issues/544))** :C++11 开始(2011 年的时候),C++就引入了多线程库,在 windows、linux、macos 都可以使用`std::thread`和`std::async`来创建线程。参考链接:http://www.cplusplus.com/reference/thread/thread/?kw=thread
🌈 拓展一下:
@@ -126,7 +126,7 @@ AOT 可以提前编译节省启动时间,那为什么不全部使用这种编
2. **是否免费** :Oracle JDK 会提供免费版本,但一般有时间限制。JDK17 之后的版本可以免费分发和商用,但是仅有 3 年时间,3 年后无法免费商用。不过,JDK8u221 之前只要不升级可以无限期免费。OpenJDK 是完全免费的。
3. **功能性** :Oracle JDK 在 OpenJDK 的基础上添加了一些特有的功能和工具,比如 Java Flight Recorder(JFR,一种监控工具)、Java Mission Control(JMC,一种监控工具)等工具。不过,在 Java 11 之后,OracleJDK 和 OpenJDK 的功能基本一致,之前 OracleJDK 中的私有组件大多数也已经被捐赠给开源组织。
4. **稳定性** :OpenJDK 不提供 LTS 服务,而 OracleJDK 大概每三年都会推出一个 LTS 版进行长期支持。不过,很多公司都基于 OpenJDK 提供了对应的和 OracleJDK 周期相同的 LTS 版。因此,两者稳定性其实也是差不多的。
-5. **协议** : Oracle JDK 使用 BCL/OTN 协议获得许可,而 OpenJDK 根据 GPL v2 许可获得许可。
+5. **协议** :Oracle JDK 使用 BCL/OTN 协议获得许可,而 OpenJDK 根据 GPL v2 许可获得许可。
> 既然 Oracle JDK 这么好,那为什么还要有 OpenJDK?
>
@@ -148,8 +148,8 @@ AOT 可以提前编译节省启动时间,那为什么不全部使用这种编
🌈 拓展一下:
-- BCL 协议(Oracle Binary Code License Agreement): 可以使用 JDK(支持商用),但是不能进行修改。
-- OTN 协议(Oracle Technology Network License Agreement): 11 及之后新发布的 JDK 用的都是这个协议,可以自己私下用,但是商用需要付费。
+- BCL 协议(Oracle Binary Code License Agreement):可以使用 JDK(支持商用),但是不能进行修改。
+- OTN 协议(Oracle Technology Network License Agreement):11 及之后新发布的 JDK 用的都是这个协议,可以自己私下用,但是商用需要付费。

@@ -263,7 +263,7 @@ static final int hash(Object key) {
```
-在 Java 代码里使用 `<<` 、 `>>` 和`>>>`转换成的指令码运行起来会更高效些。
+在 Java 代码里使用 `<<`、 `>>` 和`>>>`转换成的指令码运行起来会更高效些。
掌握最基本的移位运算符知识还是很有必要的,这不光可以帮助我们在代码中使用,还可以帮助我们理解源码中涉及到移位运算符的代码。
@@ -289,8 +289,8 @@ Java 中有三种移位运算符:
```java
int i = -1;
-System.out.println("初始数据: " + i);
-System.out.println("初始数据对应的二进制字符串: " + Integer.toBinaryString(i));
+System.out.println("初始数据:" + i);
+System.out.println("初始数据对应的二进制字符串:" + Integer.toBinaryString(i));
i <<= 10;
System.out.println("左移 10 位后的数据 " + i);
System.out.println("左移 10 位后的数据对应的二进制字符 " + Integer.toBinaryString(i));
@@ -299,8 +299,8 @@ System.out.println("左移 10 位后的数据对应的二进制字符 " + Intege
输出:
```
-初始数据: -1
-初始数据对应的二进制字符串: 11111111111111111111111111111111
+初始数据:-1
+初始数据对应的二进制字符串:11111111111111111111111111111111
左移 10 位后的数据 -1024
左移 10 位后的数据对应的二进制字符 11111111111111111111110000000000
```
@@ -309,8 +309,8 @@ System.out.println("左移 10 位后的数据对应的二进制字符 " + Intege
```java
int i = -1;
-System.out.println("初始数据: " + i);
-System.out.println("初始数据对应的二进制字符串: " + Integer.toBinaryString(i));
+System.out.println("初始数据:" + i);
+System.out.println("初始数据对应的二进制字符串:" + Integer.toBinaryString(i));
i <<= 42;
System.out.println("左移 10 位后的数据 " + i);
System.out.println("左移 10 位后的数据对应的二进制字符 " + Integer.toBinaryString(i));
@@ -420,7 +420,7 @@ Java 中有 8 种基本数据类型,分别为:
**为什么说是几乎所有对象实例都存在于堆中呢?** 这是因为 HotSpot 虚拟机引入了 JIT 优化之后,会对对象进行逃逸分析,如果发现某一个对象并没有逃逸到方法外部,那么就可能通过标量替换来实现栈上分配,而避免堆上分配内存
-⚠️ 注意 : **基本数据类型存放在栈中是一个常见的误区!** 基本数据类型的成员变量如果没有被 `static` 修饰的话(不建议这么使用,应该要使用基本数据类型对应的包装类型),就存放在堆中。
+⚠️ 注意 :**基本数据类型存放在栈中是一个常见的误区!** 基本数据类型的成员变量如果没有被 `static` 修饰的话(不建议这么使用,应该要使用基本数据类型对应的包装类型),就存放在堆中。
```java
class BasicTypeVar{
@@ -719,7 +719,7 @@ public class ConstantVariableExample {
- **形式** : 字符常量是单引号引起的一个字符,字符串常量是双引号引起的 0 个或若干个字符。
- **含义** : 字符常量相当于一个整型值( ASCII 值),可以参加表达式运算; 字符串常量代表一个地址值(该字符串在内存中存放位置)。
-- **占内存大小** : 字符常量只占 2 个字节; 字符串常量占若干个字节。
+- **占内存大小** :字符常量只占 2 个字节; 字符串常量占若干个字节。
⚠️ 注意 `char` 在 Java 中占两个字节。
diff --git a/docs/java/basis/java-basic-questions-02.md b/docs/java/basis/java-basic-questions-02.md
index 391210f6e78..260e07fe17b 100644
--- a/docs/java/basis/java-basic-questions-02.md
+++ b/docs/java/basis/java-basic-questions-02.md
@@ -122,7 +122,7 @@ true
从上面的代码输出结果可以看出:
- `str1` 和 `str2` 不相等,而 `str1` 和 `str3` 相等。这是因为 `==` 运算符比较的是字符串的引用是否相等。
-- `str1` 、 `str2` 、`str3` 三者的内容都相等。这是因为`equals` 方法比较的是字符串的内容,即使这些字符串的对象引用不同,只要它们的内容相等,就认为它们是相等的。
+- `str1`、 `str2`、`str3` 三者的内容都相等。这是因为`equals` 方法比较的是字符串的内容,即使这些字符串的对象引用不同,只要它们的内容相等,就认为它们是相等的。
### 如果一个类没有声明构造方法,该程序能正确执行吗?
@@ -425,12 +425,12 @@ public boolean equals(Object anObject) {

-`hashCode()` 定义在 JDK 的 `Object` 类中,这就意味着 Java 中的任何类都包含有 `hashCode()` 函数。另外需要注意的是: `Object` 的 `hashCode()` 方法是本地方法,也就是用 C 语言或 C++ 实现的。
+`hashCode()` 定义在 JDK 的 `Object` 类中,这就意味着 Java 中的任何类都包含有 `hashCode()` 函数。另外需要注意的是:`Object` 的 `hashCode()` 方法是本地方法,也就是用 C 语言或 C++ 实现的。
-> ⚠️ 注意 :该方法在 **Oracle OpenJDK8** 中默认是 "使用线程局部状态来实现 Marsaglia's xor-shift 随机数生成", 并不是 "地址" 或者 "地址转换而来", 不同JDK/VM可能不同在 **Oracle OpenJDK8** 中有六种生成方式 (其中第五种是返回地址), 通过添加VM参数: -XX:hashCode=4 启用第五种。参考源码:
+> ⚠️ 注意 :该方法在 **Oracle OpenJDK8** 中默认是 "使用线程局部状态来实现 Marsaglia's xor-shift 随机数生成", 并不是 "地址" 或者 "地址转换而来", 不同 JDK/VM 可能不同在 **Oracle OpenJDK8** 中有六种生成方式 (其中第五种是返回地址), 通过添加 VM 参数: -XX:hashCode=4 启用第五种。参考源码:
>
> - https://hg.openjdk.org/jdk8u/jdk8u/hotspot/file/87ee5ee27509/src/share/vm/runtime/globals.hpp(1127行)
->- https://hg.openjdk.org/jdk8u/jdk8u/hotspot/file/87ee5ee27509/src/share/vm/runtime/synchronizer.cpp(537行开始)
+> - https://hg.openjdk.org/jdk8u/jdk8u/hotspot/file/87ee5ee27509/src/share/vm/runtime/synchronizer.cpp(537行开始)
```java
public native int hashCode();
@@ -536,7 +536,7 @@ public final class String implements java.io.Serializable, Comparable, C
}
```
-> 🐛 修正 : 我们知道被 `final` 关键字修饰的类不能被继承,修饰的方法不能被重写,修饰的变量是基本数据类型则值不能改变,修饰的变量是引用类型则不能再指向其他对象。因此,`final` 关键字修饰的数组保存字符串并不是 `String` 不可变的根本原因,因为这个数组保存的字符串是可变的(`final` 修饰引用类型变量的情况)。
+> 🐛 修正 :我们知道被 `final` 关键字修饰的类不能被继承,修饰的方法不能被重写,修饰的变量是基本数据类型则值不能改变,修饰的变量是引用类型则不能再指向其他对象。因此,`final` 关键字修饰的数组保存字符串并不是 `String` 不可变的根本原因,因为这个数组保存的字符串是可变的(`final` 修饰引用类型变量的情况)。
>
> `String` 真正不可变有下面几点原因:
>
@@ -545,7 +545,7 @@ public final class String implements java.io.Serializable, Comparable, C
>
> 相关阅读:[如何理解 String 类型值的不可变? - 知乎提问](https://www.zhihu.com/question/20618891/answer/114125846)
>
-> 补充(来自[issue 675](https://github.com/Snailclimb/JavaGuide/issues/675)):在 Java 9 之后,`String` 、`StringBuilder` 与 `StringBuffer` 的实现改用 `byte` 数组存储字符串。
+> 补充(来自[issue 675](https://github.com/Snailclimb/JavaGuide/issues/675)):在 Java 9 之后,`String`、`StringBuilder` 与 `StringBuffer` 的实现改用 `byte` 数组存储字符串。
>
> ```java
> public final class String implements java.io.Serializable,Comparable, CharSequence {
@@ -562,7 +562,7 @@ public final class String implements java.io.Serializable, Comparable, C
>
> **Java 9 为何要将 `String` 的底层实现由 `char[]` 改成了 `byte[]` ?**
>
-> 新版的 String 其实支持两个编码方案: Latin-1 和 UTF-16。如果字符串中包含的汉字没有超过 Latin-1 可表示范围内的字符,那就会使用 Latin-1 作为编码方案。Latin-1 编码方案下,`byte` 占一个字节(8 位),`char` 占用 2 个字节(16),`byte` 相较 `char` 节省一半的内存空间。
+> 新版的 String 其实支持两个编码方案:Latin-1 和 UTF-16。如果字符串中包含的汉字没有超过 Latin-1 可表示范围内的字符,那就会使用 Latin-1 作为编码方案。Latin-1 编码方案下,`byte` 占一个字节(8 位),`char` 占用 2 个字节(16),`byte` 相较 `char` 节省一半的内存空间。
>
> JDK 官方就说了绝大部分字符串对象只包含 Latin-1 可表示的字符。
>
diff --git a/docs/java/basis/java-basic-questions-03.md b/docs/java/basis/java-basic-questions-03.md
index 6f6f90afce5..6fae3189679 100644
--- a/docs/java/basis/java-basic-questions-03.md
+++ b/docs/java/basis/java-basic-questions-03.md
@@ -33,7 +33,7 @@ head:

-除了`RuntimeException`及其子类以外,其他的`Exception`类及其子类都属于受检查异常 。常见的受检查异常有: IO 相关的异常、`ClassNotFoundException` 、`SQLException`...。
+除了`RuntimeException`及其子类以外,其他的`Exception`类及其子类都属于受检查异常 。常见的受检查异常有:IO 相关的异常、`ClassNotFoundException`、`SQLException`...。
**Unchecked Exception** 即 **不受检查异常** ,Java 代码在编译过程中 ,我们即使不处理不受检查异常也可以正常通过编译。
@@ -60,9 +60,9 @@ head:
### try-catch-finally 如何使用?
-- `try`块 : 用于捕获异常。其后可接零个或多个 `catch` 块,如果没有 `catch` 块,则必须跟一个 `finally` 块。
-- `catch`块 : 用于处理 try 捕获到的异常。
-- `finally` 块 : 无论是否捕获或处理异常,`finally` 块里的语句都会被执行。当在 `try` 块或 `catch` 块中遇到 `return` 语句时,`finally` 语句块将在方法返回之前被执行。
+- `try`块 :用于捕获异常。其后可接零个或多个 `catch` 块,如果没有 `catch` 块,则必须跟一个 `finally` 块。
+- `catch`块 :用于处理 try 捕获到的异常。
+- `finally` 块 :无论是否捕获或处理异常,`finally` 块里的语句都会被执行。当在 `try` 块或 `catch` 块中遇到 `return` 语句时,`finally` 语句块将在方法返回之前被执行。
代码示例:
@@ -150,7 +150,7 @@ Catch Exception -> RuntimeException
1. 程序所在的线程死亡。
2. 关闭 CPU。
-相关 issue: 。
+相关 issue:。
🧗🏻 进阶一下:从字节码角度分析`try catch finally`这个语法糖背后的实现原理。
@@ -163,7 +163,7 @@ Catch Exception -> RuntimeException
> 面对必须要关闭的资源,我们总是应该优先使用 `try-with-resources` 而不是`try-finally`。随之产生的代码更简短,更清晰,产生的异常对我们也更有用。`try-with-resources`语句让我们更容易编写必须要关闭的资源的代码,若采用`try-finally`则几乎做不到这点。
-Java 中类似于`InputStream`、`OutputStream` 、`Scanner` 、`PrintWriter`等的资源都需要我们调用`close()`方法来手动关闭,一般情况下我们都是通过`try-catch-finally`语句来实现这个需求,如下:
+Java 中类似于`InputStream`、`OutputStream`、`Scanner`、`PrintWriter`等的资源都需要我们调用`close()`方法来手动关闭,一般情况下我们都是通过`try-catch-finally`语句来实现这个需求,如下:
```java
//读取文本文件的内容
@@ -307,7 +307,7 @@ class GeneratorImpl implements Generator{
使用:
```java
-// 创建不同类型数组: Integer, Double 和 Character
+// 创建不同类型数组:Integer, Double 和 Character
Integer[] intArray = { 1, 2, 3 };
String[] stringArray = { "Hello", "World" };
printArray( intArray );
@@ -394,14 +394,14 @@ public interface Override extends Annotation{
}
```
-JDK 提供了很多内置的注解(比如 `@Override` 、`@Deprecated`),同时,我们还可以自定义注解。
+JDK 提供了很多内置的注解(比如 `@Override`、`@Deprecated`),同时,我们还可以自定义注解。
### 注解的解析方法有哪几种?
注解只有被解析之后才会生效,常见的解析方法有两种:
- **编译期直接扫描** :编译器在编译 Java 代码的时候扫描对应的注解并处理,比如某个方法使用`@Override` 注解,编译器在编译的时候就会检测当前的方法是否重写了父类对应的方法。
-- **运行期通过反射处理** :像框架中自带的注解(比如 Spring 框架的 `@Value` 、`@Component`)都是通过反射来进行处理的。
+- **运行期通过反射处理** :像框架中自带的注解(比如 Spring 框架的 `@Value`、`@Component`)都是通过反射来进行处理的。
## SPI
@@ -450,7 +450,7 @@ SPI 将服务接口和具体的服务实现分离开来,将服务调用方和
简单来说:
-- **序列化**: 将数据结构或对象转换成二进制字节流的过程
+- **序列化**:将数据结构或对象转换成二进制字节流的过程
- **反序列化**:将在序列化过程中所生成的二进制字节流转换成数据结构或者对象的过程
对于 Java 这种面向对象编程语言来说,我们序列化的都是对象(Object)也就是实例化后的类(Class),但是在 C++这种半面向对象的语言中,struct(结构体)定义的是数据结构类型,而 class 对应的是对象类型。
diff --git a/docs/java/basis/java-keyword-summary.md b/docs/java/basis/java-keyword-summary.md
index 43cbe8673cb..543f20d6498 100644
--- a/docs/java/basis/java-keyword-summary.md
+++ b/docs/java/basis/java-keyword-summary.md
@@ -245,11 +245,11 @@ bar.method2();
### `static{}`静态代码块与`{}`非静态代码块(构造代码块)
-相同点: 都是在 JVM 加载类时且在构造方法执行之前执行,在类中都可以定义多个,定义多个时按定义的顺序执行,一般在代码块中对一些 static 变量进行赋值。
+相同点:都是在 JVM 加载类时且在构造方法执行之前执行,在类中都可以定义多个,定义多个时按定义的顺序执行,一般在代码块中对一些 static 变量进行赋值。
-不同点: 静态代码块在非静态代码块之前执行(静态代码块 -> 非静态代码块 -> 构造方法)。静态代码块只在第一次 new 执行一次,之后不再执行,而非静态代码块在每 new 一次就执行一次。 非静态代码块可在普通方法中定义(不过作用不大);而静态代码块不行。
+不同点:静态代码块在非静态代码块之前执行(静态代码块 -> 非静态代码块 -> 构造方法)。静态代码块只在第一次 new 执行一次,之后不再执行,而非静态代码块在每 new 一次就执行一次。 非静态代码块可在普通方法中定义(不过作用不大);而静态代码块不行。
-> **🐛 修正(参见: [issue #677](https://github.com/Snailclimb/JavaGuide/issues/677))** :静态代码块可能在第一次 new 对象的时候执行,但不一定只在第一次 new 的时候执行。比如通过 `Class.forName("ClassDemo")`创建 Class 对象的时候也会执行,即 new 或者 `Class.forName("ClassDemo")` 都会执行静态代码块。
+> **🐛 修正(参见:[issue #677](https://github.com/Snailclimb/JavaGuide/issues/677))** :静态代码块可能在第一次 new 对象的时候执行,但不一定只在第一次 new 的时候执行。比如通过 `Class.forName("ClassDemo")`创建 Class 对象的时候也会执行,即 new 或者 `Class.forName("ClassDemo")` 都会执行静态代码块。
> 一般情况下,如果有些代码比如一些项目最常用的变量或对象必须在项目启动的时候就执行的时候,需要使用静态代码块,这种代码是主动执行的。如果我们想要设计不需要创建对象就可以调用类中的方法,例如:`Arrays` 类,`Character` 类,`String` 类等,就需要使用静态方法, 两者的区别是 静态代码块是自动执行的而静态方法是被调用的时候才执行的.
Example:
@@ -298,7 +298,7 @@ public class Test {
静态代码块!--非静态代码块!--默认构造方法!--
```
-非静态代码块与构造函数的区别是: 非静态代码块是给所有对象进行统一初始化,而构造函数是给对应的对象初始化,因为构造函数是可以多个的,运行哪个构造函数就会建立什么样的对象,但无论建立哪个对象,都会先执行相同的构造代码块。也就是说,构造代码块中定义的是不同对象共性的初始化内容。
+非静态代码块与构造函数的区别是:非静态代码块是给所有对象进行统一初始化,而构造函数是给对应的对象初始化,因为构造函数是可以多个的,运行哪个构造函数就会建立什么样的对象,但无论建立哪个对象,都会先执行相同的构造代码块。也就是说,构造代码块中定义的是不同对象共性的初始化内容。
### 参考
diff --git a/docs/java/basis/reflection.md b/docs/java/basis/reflection.md
index 3adf27515f5..23e5a179984 100644
--- a/docs/java/basis/reflection.md
+++ b/docs/java/basis/reflection.md
@@ -53,7 +53,7 @@ public class DebugInvocationHandler implements InvocationHandler {
## 谈谈反射机制的优缺点
-**优点** : 可以让咱们的代码更加灵活、为各种框架提供开箱即用的功能提供了便利
+**优点** :可以让咱们的代码更加灵活、为各种框架提供开箱即用的功能提供了便利
**缺点** :让我们在运行时有了分析操作类的能力,这同样也增加了安全问题。比如可以无视泛型参数的安全检查(泛型参数的安全检查发生在编译时)。另外,反射的性能也要稍差点,不过,对于框架来说实际是影响不大的。相关阅读:[Java Reflection: Why is it so slow?](https://stackoverflow.com/questions/1392351/java-reflection-why-is-it-so-slow)
diff --git a/docs/java/basis/serialization.md b/docs/java/basis/serialization.md
index 9220ba519c3..6e588a668eb 100644
--- a/docs/java/basis/serialization.md
+++ b/docs/java/basis/serialization.md
@@ -11,7 +11,7 @@ tag:
简单来说:
-- **序列化**: 将数据结构或对象转换成二进制字节流的过程
+- **序列化**:将数据结构或对象转换成二进制字节流的过程
- **反序列化**:将在序列化过程中所生成的二进制字节流转换成数据结构或者对象的过程
对于 Java 这种面向对象编程语言来说,我们序列化的都是对象(Object)也就是实例化后的类(Class),但是在 C++这种半面向对象的语言中,struct(结构体)定义的是数据结构类型,而 class 对应的是对象类型。
@@ -109,7 +109,7 @@ public class RpcRequest implements Serializable {
- **不支持跨语言调用** : 如果调用的是其他语言开发的服务的时候就不支持了。
- **性能差** :相比于其他序列化框架性能更低,主要原因是序列化之后的字节数组体积较大,导致传输成本加大。
-- **存在安全问题** :序列化和反序列化本身并不存在问题。但当输入的反序列化的数据可被用户控制,那么攻击者即可通过构造恶意输入,让反序列化产生非预期的对象,在此过程中执行构造的任意代码。相关阅读:[应用安全:JAVA 反序列化漏洞之殇 - Cryin](https://cryin.github.io/blog/secure-development-java-deserialization-vulnerability/) 、[Java 反序列化安全漏洞怎么回事? - Monica](https://www.zhihu.com/question/37562657/answer/1916596031)。
+- **存在安全问题** :序列化和反序列化本身并不存在问题。但当输入的反序列化的数据可被用户控制,那么攻击者即可通过构造恶意输入,让反序列化产生非预期的对象,在此过程中执行构造的任意代码。相关阅读:[应用安全:JAVA 反序列化漏洞之殇 - Cryin](https://cryin.github.io/blog/secure-development-java-deserialization-vulnerability/)、[Java 反序列化安全漏洞怎么回事? - Monica](https://www.zhihu.com/question/37562657/answer/1916596031)。
### Kryo
diff --git a/docs/java/basis/syntactic-sugar.md b/docs/java/basis/syntactic-sugar.md
index d6e4cad2b0c..ca2d38c38b1 100644
--- a/docs/java/basis/syntactic-sugar.md
+++ b/docs/java/basis/syntactic-sugar.md
@@ -107,7 +107,7 @@ public class switchDemoString
也就是说,**对于 Java 虚拟机来说,他根本不认识`Map map`这样的语法。需要在编译阶段通过类型擦除的方式进行解语法糖。**
-类型擦除的主要过程如下: 1.将所有的泛型参数用其最左边界(最顶级的父类型)类型替换。 2.移除所有的类型参数。
+类型擦除的主要过程如下:1.将所有的泛型参数用其最左边界(最顶级的父类型)类型替换。 2.移除所有的类型参数。
以下代码:
@@ -337,7 +337,7 @@ public class OutterClass {
}
```
-以上代码编译后会生成两个 class 文件:`OutterClass$InnerClass.class` 、`OutterClass.class` 。当我们尝试对`OutterClass.class`文件进行反编译的时候,命令行会打印以下内容:`Parsing OutterClass.class...Parsing inner class OutterClass$InnerClass.class... Generating OutterClass.jad` 。他会把两个文件全部进行反编译,然后一起生成一个`OutterClass.jad`文件。文件内容如下:
+以上代码编译后会生成两个 class 文件:`OutterClass$InnerClass.class`、`OutterClass.class` 。当我们尝试对`OutterClass.class`文件进行反编译的时候,命令行会打印以下内容:`Parsing OutterClass.class...Parsing inner class OutterClass$InnerClass.class... Generating OutterClass.jad` 。他会把两个文件全部进行反编译,然后一起生成一个`OutterClass.jad`文件。文件内容如下:
```java
public class OutterClass
diff --git a/docs/java/basis/unsafe.md b/docs/java/basis/unsafe.md
index 6d8b669884f..e99ddfd7a78 100644
--- a/docs/java/basis/unsafe.md
+++ b/docs/java/basis/unsafe.md
@@ -396,7 +396,7 @@ public native int arrayIndexScale(Class> arrayClass);
#### 典型应用
-这两个与数据操作相关的方法,在 `java.util.concurrent.atomic` 包下的 `AtomicIntegerArray`(可以实现对 `Integer` 数组中每个元素的原子性操作)中有典型的应用,如下图 `AtomicIntegerArray` 源码所示,通过 `Unsafe` 的 `arrayBaseOffset` 、`arrayIndexScale` 分别获取数组首元素的偏移地址 `base` 及单个元素大小因子 `scale` 。后续相关原子性操作,均依赖于这两个值进行数组中元素的定位,如下图二所示的 `getAndAdd` 方法即通过 `checkedByteOffset` 方法获取某数组元素的偏移地址,而后通过 CAS 实现原子性操作。
+这两个与数据操作相关的方法,在 `java.util.concurrent.atomic` 包下的 `AtomicIntegerArray`(可以实现对 `Integer` 数组中每个元素的原子性操作)中有典型的应用,如下图 `AtomicIntegerArray` 源码所示,通过 `Unsafe` 的 `arrayBaseOffset`、`arrayIndexScale` 分别获取数组首元素的偏移地址 `base` 及单个元素大小因子 `scale` 。后续相关原子性操作,均依赖于这两个值进行数组中元素的定位,如下图二所示的 `getAndAdd` 方法即通过 `checkedByteOffset` 方法获取某数组元素的偏移地址,而后通过 CAS 实现原子性操作。

@@ -519,7 +519,7 @@ public native boolean tryMonitorEnter(Object var1);
#### 典型应用
-Java 锁和同步器框架的核心类 `AbstractQueuedSynchronizer` (AQS),就是通过调用`LockSupport.park()`和`LockSupport.unpark()`实现线程的阻塞和唤醒的,而 `LockSupport` 的 `park` 、`unpark` 方法实际是调用 `Unsafe` 的 `park` 、`unpark` 方式实现的。
+Java 锁和同步器框架的核心类 `AbstractQueuedSynchronizer` (AQS),就是通过调用`LockSupport.park()`和`LockSupport.unpark()`实现线程的阻塞和唤醒的,而 `LockSupport` 的 `park`、`unpark` 方法实际是调用 `Unsafe` 的 `park`、`unpark` 方式实现的。
```java
public static void park(Object blocker) {
diff --git a/docs/java/collection/arraylist-source-code.md b/docs/java/collection/arraylist-source-code.md
index a6c66c79197..cc1e2e5e9a2 100644
--- a/docs/java/collection/arraylist-source-code.md
+++ b/docs/java/collection/arraylist-source-code.md
@@ -719,7 +719,7 @@ public class ArrayList extends AbstractList
#### `hugeCapacity()` 方法。
-从上面 `grow()` 方法源码我们知道: 如果新容量大于 MAX_ARRAY_SIZE,进入(执行) `hugeCapacity()` 方法来比较 minCapacity 和 MAX_ARRAY_SIZE,如果 minCapacity 大于最大容量,则新容量则为`Integer.MAX_VALUE`,否则,新容量大小则为 MAX_ARRAY_SIZE 即为 `Integer.MAX_VALUE - 8`。
+从上面 `grow()` 方法源码我们知道:如果新容量大于 MAX_ARRAY_SIZE,进入(执行) `hugeCapacity()` 方法来比较 minCapacity 和 MAX_ARRAY_SIZE,如果 minCapacity 大于最大容量,则新容量则为`Integer.MAX_VALUE`,否则,新容量大小则为 MAX_ARRAY_SIZE 即为 `Integer.MAX_VALUE - 8`。
```java
private static int hugeCapacity(int minCapacity) {
diff --git a/docs/java/collection/java-collection-precautions-for-use.md b/docs/java/collection/java-collection-precautions-for-use.md
index 7ed80b4c04f..b6bb9a8935e 100644
--- a/docs/java/collection/java-collection-precautions-for-use.md
+++ b/docs/java/collection/java-collection-precautions-for-use.md
@@ -17,7 +17,7 @@ tag:
这是因为 `isEmpty()` 方法的可读性更好,并且时间复杂度为 O(1)。
-绝大部分我们使用的集合的 `size()` 方法的时间复杂度也是 O(1),不过,也有很多复杂度不是 O(1) 的,比如 `java.util.concurrent` 包下的某些集合(`ConcurrentLinkedQueue` 、`ConcurrentHashMap`...)。
+绝大部分我们使用的集合的 `size()` 方法的时间复杂度也是 O(1),不过,也有很多复杂度不是 O(1) 的,比如 `java.util.concurrent` 包下的某些集合(`ConcurrentLinkedQueue`、`ConcurrentHashMap`...)。
下面是 `ConcurrentHashMap` 的 `size()` 方法和 `isEmpty()` 方法的源码。
diff --git a/docs/java/collection/java-collection-questions-01.md b/docs/java/collection/java-collection-questions-01.md
index df18fa72cd4..d7b08627e2d 100644
--- a/docs/java/collection/java-collection-questions-01.md
+++ b/docs/java/collection/java-collection-questions-01.md
@@ -37,9 +37,9 @@ Java 集合框架如下图所示:
#### List
-- `ArrayList`: `Object[]` 数组
+- `ArrayList`:`Object[]` 数组
- `Vector`:`Object[]` 数组
-- `LinkedList`: 双向链表(JDK1.6 之前为循环链表,JDK1.7 取消了循环)
+- `LinkedList`:双向链表(JDK1.6 之前为循环链表,JDK1.7 取消了循环)
#### Set
@@ -56,10 +56,10 @@ Java 集合框架如下图所示:
#### Map
-- `HashMap`: JDK1.8 之前 `HashMap` 由数组+链表组成的,数组是 `HashMap` 的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突)。JDK1.8 以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间
-- `LinkedHashMap`: `LinkedHashMap` 继承自 `HashMap`,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,`LinkedHashMap` 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。详细可以查看:[《LinkedHashMap 源码详细分析(JDK1.8)》](https://www.imooc.com/article/22931)
-- `Hashtable`: 数组+链表组成的,数组是 `Hashtable` 的主体,链表则是主要为了解决哈希冲突而存在的
-- `TreeMap`: 红黑树(自平衡的排序二叉树)
+- `HashMap`:JDK1.8 之前 `HashMap` 由数组+链表组成的,数组是 `HashMap` 的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突)。JDK1.8 以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间
+- `LinkedHashMap`:`LinkedHashMap` 继承自 `HashMap`,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,`LinkedHashMap` 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。详细可以查看:[《LinkedHashMap 源码详细分析(JDK1.8)》](https://www.imooc.com/article/22931)
+- `Hashtable`:数组+链表组成的,数组是 `Hashtable` 的主体,链表则是主要为了解决哈希冲突而存在的
+- `TreeMap`:红黑树(自平衡的排序二叉树)
### 如何选用集合?
@@ -88,7 +88,7 @@ Java 集合框架如下图所示:
- **底层数据结构:** `ArrayList` 底层使用的是 **`Object` 数组**;`LinkedList` 底层使用的是 **双向链表** 数据结构(JDK1.6 之前为循环链表,JDK1.7 取消了循环。注意双向链表和双向循环链表的区别,下面有介绍到!)
- **插入和删除是否受元素位置的影响:**
- `ArrayList` 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。 比如:执行`add(E e)`方法的时候, `ArrayList` 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是 O(1)。但是如果要在指定位置 i 插入和删除元素的话(`add(int index, E element)`)时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。
- - `LinkedList` 采用链表存储,所以,如果是在头尾插入或者删除元素不受元素位置的影响(`add(E e)`、`addFirst(E e)`、`addLast(E e)`、`removeFirst()` 、 `removeLast()`),时间复杂度为 O(1),如果是要在指定位置 `i` 插入和删除元素的话(`add(int index, E element)`,`remove(Object o)`), 时间复杂度为 O(n) ,因为需要先移动到指定位置再插入。
+ - `LinkedList` 采用链表存储,所以,如果是在头尾插入或者删除元素不受元素位置的影响(`add(E e)`、`addFirst(E e)`、`addLast(E e)`、`removeFirst()`、 `removeLast()`),时间复杂度为 O(1),如果是要在指定位置 `i` 插入和删除元素的话(`add(int index, E element)`,`remove(Object o)`), 时间复杂度为 O(n) ,因为需要先移动到指定位置再插入。
- **是否支持快速随机访问:** `LinkedList` 不支持高效的随机元素访问,而 `ArrayList`(实现了 RandomAccess 接口) 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于`get(int index)`方法)。
- **内存空间占用:** `ArrayList` 的空 间浪费主要体现在在 list 列表的结尾会预留一定的容量空间,而 LinkedList 的空间花费则体现在它的每一个元素都需要消耗比 ArrayList 更多的空间(因为要存放直接后继和直接前驱以及数据)。
diff --git a/docs/java/collection/java-collection-questions-02.md b/docs/java/collection/java-collection-questions-02.md
index 391da1a90a3..e29d656de71 100644
--- a/docs/java/collection/java-collection-questions-02.md
+++ b/docs/java/collection/java-collection-questions-02.md
@@ -290,9 +290,9 @@ final void treeifyBin(Node[] tab, int hash) {
[HashMap 的 7 种遍历方式与性能分析!](https://mp.weixin.qq.com/s/zQBN3UvJDhRTKP6SzcZFKw)
-**🐛 修正(参见: [issue#1411](https://github.com/Snailclimb/JavaGuide/issues/1411))** :
+**🐛 修正(参见:[issue#1411](https://github.com/Snailclimb/JavaGuide/issues/1411))** :
-这篇文章对于 parallelStream 遍历方式的性能分析有误,先说结论: **存在阻塞时 parallelStream 性能最高, 非阻塞时 parallelStream 性能最低** 。
+这篇文章对于 parallelStream 遍历方式的性能分析有误,先说结论:**存在阻塞时 parallelStream 性能最高, 非阻塞时 parallelStream 性能最低** 。
当遍历不存在阻塞时, parallelStream 的性能是最低的:
diff --git a/docs/java/concurrent/completablefuture-intro.md b/docs/java/concurrent/completablefuture-intro.md
index aaebca7f7a3..a7f26bb8d5f 100644
--- a/docs/java/concurrent/completablefuture-intro.md
+++ b/docs/java/concurrent/completablefuture-intro.md
@@ -28,7 +28,7 @@ public class CompletableFuture implements Future, CompletionStage {
- `boolean cancel(boolean mayInterruptIfRunning)` :尝试取消执行任务。
- `boolean isCancelled()` :判断任务是否被取消。
-- `boolean isDone()` : 判断任务是否已经被执行完成。
+- `boolean isDone()` :判断任务是否已经被执行完成。
- `get()` :等待任务执行完成并获取运算结果。
- `get(long timeout, TimeUnit unit)` :多了一个超时时间。
@@ -47,7 +47,7 @@ public class CompletableFuture implements Future, CompletionStage {
常见的创建 `CompletableFuture` 对象的方法如下:
1. 通过 new 关键字。
-2. 基于 `CompletableFuture` 自带的静态工厂方法:`runAsync()` 、`supplyAsync()` 。
+2. 基于 `CompletableFuture` 自带的静态工厂方法:`runAsync()`、`supplyAsync()` 。
#### new 关键字
diff --git a/docs/java/concurrent/java-concurrent-collections.md b/docs/java/concurrent/java-concurrent-collections.md
index 17aabbc11ff..cf5bdc232a1 100644
--- a/docs/java/concurrent/java-concurrent-collections.md
+++ b/docs/java/concurrent/java-concurrent-collections.md
@@ -110,7 +110,7 @@ Java 提供的线程安全的 `Queue` 可以分为**阻塞队列**和**非阻塞

-下面主要介绍一下 3 个常见的 `BlockingQueue` 的实现类:`ArrayBlockingQueue`、`LinkedBlockingQueue` 、`PriorityBlockingQueue` 。
+下面主要介绍一下 3 个常见的 `BlockingQueue` 的实现类:`ArrayBlockingQueue`、`LinkedBlockingQueue`、`PriorityBlockingQueue` 。
### ArrayBlockingQueue
diff --git a/docs/java/concurrent/java-concurrent-questions-01.md b/docs/java/concurrent/java-concurrent-questions-01.md
index 38c49cd8c0c..7ebfc0bd0f5 100644
--- a/docs/java/concurrent/java-concurrent-questions-01.md
+++ b/docs/java/concurrent/java-concurrent-questions-01.md
@@ -91,7 +91,7 @@ public class MultiThread {
### 虚拟机栈和本地方法栈为什么是私有的?
- **虚拟机栈:** 每个 Java 方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、常量池引用等信息。从方法调用直至执行完成的过程,就对应着一个栈帧在 Java 虚拟机栈中入栈和出栈的过程。
-- **本地方法栈:** 和虚拟机栈所发挥的作用非常相似,区别是: **虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。** 在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一。
+- **本地方法栈:** 和虚拟机栈所发挥的作用非常相似,区别是:**虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。** 在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一。
所以,为了**保证线程中的局部变量不被别的线程访问到**,虚拟机栈和本地方法栈是线程私有的。
@@ -108,7 +108,7 @@ public class MultiThread {
## 同步和异步的区别
-- **同步** : 发出一个调用之后,在没有得到结果之前, 该调用就不可以返回,一直等待。
+- **同步** :发出一个调用之后,在没有得到结果之前, 该调用就不可以返回,一直等待。
- **异步** :调用在发出之后,不用等待返回结果,该调用直接返回。
## 为什么要使用多线程?
@@ -120,7 +120,7 @@ public class MultiThread {
再深入到计算机底层来探讨:
-- **单核时代**: 在单核时代多线程主要是为了提高单进程利用 CPU 和 IO 系统的效率。 假设只运行了一个 Java 进程的情况,当我们请求 IO 的时候,如果 Java 进程中只有一个线程,此线程被 IO 阻塞则整个进程被阻塞。CPU 和 IO 设备只有一个在运行,那么可以简单地说系统整体效率只有 50%。当使用多线程的时候,一个线程被 IO 阻塞,其他线程还可以继续使用 CPU。从而提高了 Java 进程利用系统资源的整体效率。
+- **单核时代**:在单核时代多线程主要是为了提高单进程利用 CPU 和 IO 系统的效率。 假设只运行了一个 Java 进程的情况,当我们请求 IO 的时候,如果 Java 进程中只有一个线程,此线程被 IO 阻塞则整个进程被阻塞。CPU 和 IO 设备只有一个在运行,那么可以简单地说系统整体效率只有 50%。当使用多线程的时候,一个线程被 IO 阻塞,其他线程还可以继续使用 CPU。从而提高了 Java 进程利用系统资源的整体效率。
- **多核时代**: 多核时代多线程主要是为了提高进程利用多核 CPU 的能力。举个例子:假如我们要计算一个复杂的任务,我们只用一个线程的话,不论系统有几个 CPU 核心,都只会有一个 CPU 核心被利用到。而创建多个线程,这些线程可以被映射到底层多个 CPU 上执行,在任务中的多个线程没有资源竞争的情况下,任务执行的效率会有显著性的提高,约等于(单核时执行时间/CPU 核心数)。
## 使用多线程可能带来什么问题?
@@ -316,4 +316,4 @@ Process finished with exit code 0
new 一个 `Thread`,线程进入了新建状态。调用 `start()`方法,会启动一个线程并使线程进入了就绪状态,当分配到时间片后就可以开始运行了。 `start()` 会执行线程的相应准备工作,然后自动执行 `run()` 方法的内容,这是真正的多线程工作。 但是,直接执行 `run()` 方法,会把 `run()` 方法当成一个 main 线程下的普通方法去执行,并不会在某个线程中执行它,所以这并不是多线程工作。
-**总结: 调用 `start()` 方法方可启动线程并使线程进入就绪状态,直接执行 `run()` 方法的话不会以多线程的方式执行。**
+**总结:调用 `start()` 方法方可启动线程并使线程进入就绪状态,直接执行 `run()` 方法的话不会以多线程的方式执行。**
diff --git a/docs/java/concurrent/java-concurrent-questions-02.md b/docs/java/concurrent/java-concurrent-questions-02.md
index cf8185b27e2..dd6236639d4 100644
--- a/docs/java/concurrent/java-concurrent-questions-02.md
+++ b/docs/java/concurrent/java-concurrent-questions-02.md
@@ -14,7 +14,7 @@ head:
## JMM(Java 内存模型)
-JMM(Java 内存模型)相关的问题比较多,也比较重要,于是我单独抽了一篇文章来总结 JMM 相关的知识点和问题: [JMM(Java 内存模型)详解](./jmm.md) 。
+JMM(Java 内存模型)相关的问题比较多,也比较重要,于是我单独抽了一篇文章来总结 JMM 相关的知识点和问题:[JMM(Java 内存模型)详解](./jmm.md) 。
## volatile 关键字
@@ -138,7 +138,7 @@ public class VolatoleAtomicityDemo {
这也就导致两个线程分别对 `inc` 进行了一次自增操作后,`inc` 实际上只增加了 1。
-其实,如果想要保证上面的代码运行正确也非常简单,利用 `synchronized` 、`Lock`或者`AtomicInteger`都可以。
+其实,如果想要保证上面的代码运行正确也非常简单,利用 `synchronized`、`Lock`或者`AtomicInteger`都可以。
使用 `synchronized` 改进:
@@ -692,7 +692,7 @@ public long tryOptimisticRead() {
## Atomic 原子类
-Atomic 原子类部分的内容我单独写了一篇文章来总结: [Atomic 原子类总结](./atomic-classes.md) 。
+Atomic 原子类部分的内容我单独写了一篇文章来总结:[Atomic 原子类总结](./atomic-classes.md) 。
## 参考
diff --git a/docs/java/concurrent/java-concurrent-questions-03.md b/docs/java/concurrent/java-concurrent-questions-03.md
index 98d8a7db0be..c7ebf5ee781 100644
--- a/docs/java/concurrent/java-concurrent-questions-03.md
+++ b/docs/java/concurrent/java-concurrent-questions-03.md
@@ -211,7 +211,7 @@ static class Entry extends WeakReference>> {
我们可以创建多种类型的 `ThreadPoolExecutor`:
-- **`FixedThreadPool`** : 该方法返回一个固定线程数量的线程池。该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被暂存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。
+- **`FixedThreadPool`** :该方法返回一个固定线程数量的线程池。该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被暂存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。
- **`SingleThreadExecutor`:** 该方法返回一个只有一个线程的线程池。若多余一个任务被提交到该线程池,任务会被保存在一个任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务。
- **`CachedThreadPool`:** 该方法返回一个可根据实际情况调整线程数量的线程池。线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复用。
- **`ScheduledThreadPool`** :该返回一个用来在给定的延迟后运行任务或者定期执行任务的线程池。
@@ -232,7 +232,7 @@ static class Entry extends WeakReference>> {
`Executors` 返回线程池对象的弊端如下(后文会详细介绍到):
-- **`FixedThreadPool` 和 `SingleThreadExecutor`** : 使用的是无界的 `LinkedBlockingQueue`,任务队列最大长度为 `Integer.MAX_VALUE`,可能堆积大量的请求,从而导致 OOM。
+- **`FixedThreadPool` 和 `SingleThreadExecutor`** :使用的是无界的 `LinkedBlockingQueue`,任务队列最大长度为 `Integer.MAX_VALUE`,可能堆积大量的请求,从而导致 OOM。
- **`CachedThreadPool`** :使用的是同步队列 `SynchronousQueue`, 允许创建的线程数量为 `Integer.MAX_VALUE` ,可能会创建大量线程,从而导致 OOM。
- **`ScheduledThreadPool` 和 `SingleThreadScheduledExecutor`** : 使用的无界的延迟阻塞队列`DelayedWorkQueue`,任务队列最大长度为 `Integer.MAX_VALUE`,可能堆积大量的请求,从而导致 OOM。
diff --git a/docs/java/concurrent/java-thread-pool-best-practices.md b/docs/java/concurrent/java-thread-pool-best-practices.md
index 56223be59ae..41cab8924af 100644
--- a/docs/java/concurrent/java-thread-pool-best-practices.md
+++ b/docs/java/concurrent/java-thread-pool-best-practices.md
@@ -13,7 +13,7 @@ tag:
`Executors` 返回线程池对象的弊端如下(后文会详细介绍到):
-- **`FixedThreadPool` 和 `SingleThreadExecutor`** : 使用的是无界的 `LinkedBlockingQueue`,任务队列最大长度为 `Integer.MAX_VALUE`,可能堆积大量的请求,从而导致 OOM。
+- **`FixedThreadPool` 和 `SingleThreadExecutor`** :使用的是无界的 `LinkedBlockingQueue`,任务队列最大长度为 `Integer.MAX_VALUE`,可能堆积大量的请求,从而导致 OOM。
- **`CachedThreadPool`** :使用的是同步队列 `SynchronousQueue`, 允许创建的线程数量为 `Integer.MAX_VALUE` ,可能会创建大量线程,从而导致 OOM。
- **`ScheduledThreadPool` 和 `SingleThreadScheduledExecutor` ** : 使用的无界的延迟阻塞队列`DelayedWorkQueue`,任务队列最大长度为 `Integer.MAX_VALUE`,可能堆积大量的请求,从而导致 OOM。
@@ -295,4 +295,4 @@ server.tomcat.max-threads=1
解决上述问题比较建议的办法是使用阿里巴巴开源的 `TransmittableThreadLocal`(`TTL`)。`TransmittableThreadLocal`类继承并加强了 JDK 内置的`InheritableThreadLocal`类,在使用线程池等会池化复用线程的执行组件情况下,提供`ThreadLocal`值的传递功能,解决异步执行时上下文传递的问题。
-`TransmittableThreadLocal` 项目地址: https://github.com/alibaba/transmittable-thread-local 。
+`TransmittableThreadLocal` 项目地址:https://github.com/alibaba/transmittable-thread-local 。
diff --git a/docs/java/concurrent/java-thread-pool-summary.md b/docs/java/concurrent/java-thread-pool-summary.md
index a544d83c1a9..2f006585522 100644
--- a/docs/java/concurrent/java-thread-pool-summary.md
+++ b/docs/java/concurrent/java-thread-pool-summary.md
@@ -138,7 +138,7 @@ public class ScheduledThreadPoolExecutor
- **`ThreadPoolExecutor.AbortPolicy`** :抛出 `RejectedExecutionException`来拒绝新任务的处理。
- **`ThreadPoolExecutor.CallerRunsPolicy`** :调用执行自己的线程运行任务,也就是直接在调用`execute`方法的线程中运行(`run`)被拒绝的任务,如果执行程序已关闭,则会丢弃该任务。因此这种策略会降低对于新任务提交速度,影响程序的整体性能。如果您的应用程序可以承受此延迟并且你要求任何一个任务请求都要被执行的话,你可以选择这个策略。
- **`ThreadPoolExecutor.DiscardPolicy`** :不处理新任务,直接丢弃掉。
-- **`ThreadPoolExecutor.DiscardOldestPolicy`** : 此策略将丢弃最早的未处理的任务请求。
+- **`ThreadPoolExecutor.DiscardOldestPolicy`** :此策略将丢弃最早的未处理的任务请求。
举个例子:
@@ -154,7 +154,7 @@ Spring 通过 `ThreadPoolTaskExecutor` 或者我们直接通过 `ThreadPoolExecu
我们可以创建多种类型的 `ThreadPoolExecutor`:
-- **`FixedThreadPool`** : 该方法返回一个固定线程数量的线程池。该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被暂存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。
+- **`FixedThreadPool`** :该方法返回一个固定线程数量的线程池。该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被暂存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。
- **`SingleThreadExecutor`:** 该方法返回一个只有一个线程的线程池。若多余一个任务被提交到该线程池,任务会被保存在一个任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务。
- **`CachedThreadPool`:** 该方法返回一个可根据实际情况调整线程数量的线程池。线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复用。
- **`ScheduledThreadPool`** :该返回一个用来在给定的延迟后运行任务或者定期执行任务的线程池。
@@ -167,7 +167,7 @@ Spring 通过 `ThreadPoolTaskExecutor` 或者我们直接通过 `ThreadPoolExecu
`Executors` 返回线程池对象的弊端如下(后文会详细介绍到):
-- **`FixedThreadPool` 和 `SingleThreadExecutor`** : 使用的是无界的 `LinkedBlockingQueue`,任务队列最大长度为 `Integer.MAX_VALUE`,可能堆积大量的请求,从而导致 OOM。
+- **`FixedThreadPool` 和 `SingleThreadExecutor`** :使用的是无界的 `LinkedBlockingQueue`,任务队列最大长度为 `Integer.MAX_VALUE`,可能堆积大量的请求,从而导致 OOM。
- **`CachedThreadPool`** :使用的是同步队列 `SynchronousQueue`, 允许创建的线程数量为 `Integer.MAX_VALUE` ,可能会创建大量线程,从而导致 OOM。
- **`ScheduledThreadPool` 和 `SingleThreadScheduledExecutor`** : 使用的无界的延迟阻塞队列`DelayedWorkQueue`,任务队列最大长度为 `Integer.MAX_VALUE`,可能堆积大量的请求,从而导致 OOM。
@@ -513,7 +513,7 @@ pool-1-thread-2 End. Time = Sun Apr 12 11:14:47 CST 2020
没搞懂的话,也没关系,可以看看我的分析:
-> 我们在代码中模拟了 10 个任务,我们配置的核心线程数为 5 、等待队列容量为 100 ,所以每次只可能存在 5 个任务同时执行,剩下的 5 个任务会被放到等待队列中去。当前的 5 个任务中如果有任务被执行完了,线程池就会去拿新的任务执行。
+> 我们在代码中模拟了 10 个任务,我们配置的核心线程数为 5、等待队列容量为 100 ,所以每次只可能存在 5 个任务同时执行,剩下的 5 个任务会被放到等待队列中去。当前的 5 个任务中如果有任务被执行完了,线程池就会去拿新的任务执行。
### 几个常见的对比
diff --git a/docs/java/concurrent/jmm.md b/docs/java/concurrent/jmm.md
index c5bb01a6132..d31da1beb99 100644
--- a/docs/java/concurrent/jmm.md
+++ b/docs/java/concurrent/jmm.md
@@ -26,7 +26,7 @@ JMM(Java 内存模型)主要定义了对于一个共享变量,当另一个线
为了更好地理解,我画了一个简单的 CPU Cache 示意图如下所示。
-> **🐛 修正(参见: [issue#1848](https://github.com/Snailclimb/JavaGuide/issues/1848))**:对 CPU 缓存模型绘图不严谨的地方进行完善。
+> **🐛 修正(参见:[issue#1848](https://github.com/Snailclimb/JavaGuide/issues/1848))**:对 CPU 缓存模型绘图不严谨的地方进行完善。

@@ -199,7 +199,7 @@ happens-before 与 JMM 的关系用《Java 并发编程的艺术》这本书中
一次操作或者多次操作,要么所有的操作全部都得到执行并且不会受到任何因素的干扰而中断,要么都不执行。
-在 Java 中,可以借助`synchronized` 、各种 `Lock` 以及各种原子类实现原子性。
+在 Java 中,可以借助`synchronized`、各种 `Lock` 以及各种原子类实现原子性。
`synchronized` 和各种 `Lock` 可以保证任一时刻只有一个线程访问该代码块,因此可以保障原子性。各种原子类是利用 CAS (compare and swap) 操作(可能也会用到 `volatile`或者`final`关键字)来保证原子操作。
@@ -207,7 +207,7 @@ happens-before 与 JMM 的关系用《Java 并发编程的艺术》这本书中
当一个线程对共享变量进行了修改,那么另外的线程都是立即可以看到修改后的最新值。
-在 Java 中,可以借助`synchronized` 、`volatile` 以及各种 `Lock` 实现可见性。
+在 Java 中,可以借助`synchronized`、`volatile` 以及各种 `Lock` 实现可见性。
如果我们将变量声明为 `volatile` ,这就指示 JVM,这个变量是共享且不稳定的,每次使用它都到主存中进行读取。
diff --git a/docs/java/concurrent/reentrantlock.md b/docs/java/concurrent/reentrantlock.md
index 679fc839ebf..517ed44db14 100644
--- a/docs/java/concurrent/reentrantlock.md
+++ b/docs/java/concurrent/reentrantlock.md
@@ -218,7 +218,7 @@ private volatile int state;

-> 🐛 修正(参见: [issue#1761](https://github.com/Snailclimb/JavaGuide/issues/1761)): 图中的一处小错误,(AQS)CAS 修改共享资源 State 成功之后应该是获取锁成功(非公平锁)。
+> 🐛 修正(参见:[issue#1761](https://github.com/Snailclimb/JavaGuide/issues/1761)): 图中的一处小错误,(AQS)CAS 修改共享资源 State 成功之后应该是获取锁成功(非公平锁)。
>
> 对应的源码如下:
>
@@ -418,7 +418,7 @@ public final boolean hasQueuedPredecessors() {
看到这里,我们理解一下 h != t && ((s = h.next) == null || s.thread != Thread.currentThread());为什么要判断的头结点的下一个节点?第一个节点储存的数据是什么?
-> 双向链表中,第一个节点为虚节点,其实并不存储任何信息,只是占位。真正的第一个有数据的节点,是在第二个节点开始的。当 h != t 时: 如果(s = h.next) == null,等待队列正在有线程进行初始化,但只是进行到了 Tail 指向 Head,没有将 Head 指向 Tail,此时队列中有元素,需要返回 True(这块具体见下边代码分析)。 如果(s = h.next) != null,说明此时队列中至少有一个有效节点。如果此时 s.thread == Thread.currentThread(),说明等待队列的第一个有效节点中的线程与当前线程相同,那么当前线程是可以获取资源的;如果 s.thread != Thread.currentThread(),说明等待队列的第一个有效节点线程与当前线程不同,当前线程必须加入进等待队列。
+> 双向链表中,第一个节点为虚节点,其实并不存储任何信息,只是占位。真正的第一个有数据的节点,是在第二个节点开始的。当 h != t 时:如果(s = h.next) == null,等待队列正在有线程进行初始化,但只是进行到了 Tail 指向 Head,没有将 Head 指向 Tail,此时队列中有元素,需要返回 True(这块具体见下边代码分析)。 如果(s = h.next) != null,说明此时队列中至少有一个有效节点。如果此时 s.thread == Thread.currentThread(),说明等待队列的第一个有效节点中的线程与当前线程相同,那么当前线程是可以获取资源的;如果 s.thread != Thread.currentThread(),说明等待队列的第一个有效节点线程与当前线程不同,当前线程必须加入进等待队列。
```java
// java.util.concurrent.locks.AbstractQueuedSynchronizer#enq
diff --git a/docs/java/io/io-basis.md b/docs/java/io/io-basis.md
index 566dd1f41cf..68c1104d9a5 100755
--- a/docs/java/io/io-basis.md
+++ b/docs/java/io/io-basis.md
@@ -21,20 +21,20 @@ Java IO 流的 40 多个类都是从如下 4 个抽象类基类中派生出来
`InputStream`用于从源头(通常是文件)读取数据(字节信息)到内存中,`java.io.InputStream`抽象类是所有字节输入流的父类。
-`InputStream` 常用方法 :
+`InputStream` 常用方法:
-- `read()` :返回输入流中下一个字节的数据。返回的值介于 0 到 255 之间。如果未读取任何字节,则代码返回 `-1` ,表示文件结束。
+- `read()`:返回输入流中下一个字节的数据。返回的值介于 0 到 255 之间。如果未读取任何字节,则代码返回 `-1` ,表示文件结束。
- `read(byte b[ ])` : 从输入流中读取一些字节存储到数组 `b` 中。如果数组 `b` 的长度为零,则不读取。如果没有可用字节读取,返回 `-1`。如果有可用字节读取,则最多读取的字节数最多等于 `b.length` , 返回读取的字节数。这个方法等价于 `read(b, 0, b.length)`。
-- `read(byte b[], int off, int len)` :在`read(byte b[ ])` 方法的基础上增加了 `off` 参数(偏移量)和 `len` 参数(要读取的最大字节数)。
-- `skip(long n)` :忽略输入流中的 n 个字节 ,返回实际忽略的字节数。
-- `available()` :返回输入流中可以读取的字节数。
-- `close()` :关闭输入流释放相关的系统资源。
+- `read(byte b[], int off, int len)`:在`read(byte b[ ])` 方法的基础上增加了 `off` 参数(偏移量)和 `len` 参数(要读取的最大字节数)。
+- `skip(long n)`:忽略输入流中的 n 个字节 ,返回实际忽略的字节数。
+- `available()`:返回输入流中可以读取的字节数。
+- `close()`:关闭输入流释放相关的系统资源。
从 Java 9 开始,`InputStream` 新增加了多个实用的方法:
-- `readAllBytes()` :读取输入流中的所有字节,返回字节数组。
-- `readNBytes(byte[] b, int off, int len)` :阻塞直到读取 `len` 个字节。
-- `transferTo(OutputStream out)` : 将所有字节从一个输入流传递到一个输出流。
+- `readAllBytes()`:读取输入流中的所有字节,返回字节数组。
+- `readNBytes(byte[] b, int off, int len)`:阻塞直到读取 `len` 个字节。
+- `transferTo(OutputStream out)`:将所有字节从一个输入流传递到一个输出流。
`FileInputStream` 是一个比较常用的字节输入流对象,可直接指定文件路径,可以直接读取单字节数据,也可以读取至字节数组中。
@@ -106,13 +106,13 @@ input.close();
`OutputStream`用于将数据(字节信息)写入到目的地(通常是文件),`java.io.OutputStream`抽象类是所有字节输出流的父类。
-`OutputStream` 常用方法 :
+`OutputStream` 常用方法:
-- `write(int b)` :将特定字节写入输出流。
+- `write(int b)`:将特定字节写入输出流。
- `write(byte b[ ])` : 将数组`b` 写入到输出流,等价于 `write(b, 0, b.length)` 。
- `write(byte[] b, int off, int len)` : 在`write(byte b[ ])` 方法的基础上增加了 `off` 参数(偏移量)和 `len` 参数(要读取的最大字节数)。
-- `flush()` :刷新此输出流并强制写出所有缓冲的输出字节。
-- `close()` :关闭输出流释放相关的系统资源。
+- `flush()`:刷新此输出流并强制写出所有缓冲的输出字节。
+- `close()`:关闭输出流释放相关的系统资源。
`FileOutputStream` 是最常用的字节输出流对象,可直接指定文件路径,可以直接输出单字节数据,也可以输出指定的字节数组。
@@ -190,12 +190,12 @@ The content read from file:§å®¶å¥½
`Reader` 用于读取文本, `InputStream` 用于读取原始字节。
-`Reader` 常用方法 :
+`Reader` 常用方法:
- `read()` : 从输入流读取一个字符。
- `read(char[] cbuf)` : 从输入流中读取一些字符,并将它们存储到字符数组 `cbuf`中,等价于 `read(cbuf, 0, cbuf.length)` 。
-- `read(char[] cbuf, int off, int len)` :在`read(char[] cbuf)` 方法的基础上增加了 `off` 参数(偏移量)和 `len` 参数(要读取的最大字符数)。
-- `skip(long n)` :忽略输入流中的 n 个字符 ,返回实际忽略的字符数。
+- `read(char[] cbuf, int off, int len)`:在`read(char[] cbuf)` 方法的基础上增加了 `off` 参数(偏移量)和 `len` 参数(要读取的最大字符数)。
+- `skip(long n)`:忽略输入流中的 n 个字符 ,返回实际忽略的字符数。
- `close()` : 关闭输入流并释放相关的系统资源。
`InputStreamReader` 是字节流转换为字符流的桥梁,其子类 `FileReader` 是基于该基础上的封装,可以直接操作字符文件。
@@ -240,16 +240,16 @@ The content read from file:我是Guide。
`Writer`用于将数据(字符信息)写入到目的地(通常是文件),`java.io.Writer`抽象类是所有字符输出流的父类。
-`Writer` 常用方法 :
+`Writer` 常用方法:
- `write(int c)` : 写入单个字符。
-- `write(char[] cbuf)` :写入字符数组 `cbuf`,等价于`write(cbuf, 0, cbuf.length)`。
-- `write(char[] cbuf, int off, int len)` :在`write(char[] cbuf)` 方法的基础上增加了 `off` 参数(偏移量)和 `len` 参数(要读取的最大字符数)。
-- `write(String str)` :写入字符串,等价于 `write(str, 0, str.length())` 。
-- `write(String str, int off, int len)` :在`write(String str)` 方法的基础上增加了 `off` 参数(偏移量)和 `len` 参数(要读取的最大字符数)。
-- `append(CharSequence csq)` :将指定的字符序列附加到指定的 `Writer` 对象并返回该 `Writer` 对象。
-- `append(char c)` :将指定的字符附加到指定的 `Writer` 对象并返回该 `Writer` 对象。
-- `flush()` :刷新此输出流并强制写出所有缓冲的输出字符。
+- `write(char[] cbuf)`:写入字符数组 `cbuf`,等价于`write(cbuf, 0, cbuf.length)`。
+- `write(char[] cbuf, int off, int len)`:在`write(char[] cbuf)` 方法的基础上增加了 `off` 参数(偏移量)和 `len` 参数(要读取的最大字符数)。
+- `write(String str)`:写入字符串,等价于 `write(str, 0, str.length())` 。
+- `write(String str, int off, int len)`:在`write(String str)` 方法的基础上增加了 `off` 参数(偏移量)和 `len` 参数(要读取的最大字符数)。
+- `append(CharSequence csq)`:将指定的字符序列附加到指定的 `Writer` 对象并返回该 `Writer` 对象。
+- `append(char c)`:将指定的字符附加到指定的 `Writer` 对象并返回该 `Writer` 对象。
+- `flush()`:刷新此输出流并强制写出所有缓冲的输出字符。
- `close()`:关闭输出流释放相关的系统资源。
`OutputStreamWriter` 是字符流转换为字节流的桥梁,其子类 `FileWriter` 是基于该基础上的封装,可以直接将字符写入到文件。
diff --git a/docs/java/io/io-model.md b/docs/java/io/io-model.md
index 8be30bbdada..8e0932700ad 100644
--- a/docs/java/io/io-model.md
+++ b/docs/java/io/io-model.md
@@ -55,7 +55,7 @@ I/O(**I**nput/**O**utpu) 即**输入/输出** 。
### 有哪些常见的 IO 模型?
-UNIX 系统下, IO 模型一共有 5 种: **同步阻塞 I/O**、**同步非阻塞 I/O**、**I/O 多路复用**、**信号驱动 I/O** 和**异步 I/O**。
+UNIX 系统下, IO 模型一共有 5 种:**同步阻塞 I/O**、**同步非阻塞 I/O**、**I/O 多路复用**、**信号驱动 I/O** 和**异步 I/O**。
这也是我们经常提到的 5 种 IO 模型。
@@ -97,8 +97,8 @@ IO 多路复用模型中,线程首先发起 select 调用,询问内核数据
> 目前支持 IO 多路复用的系统调用,有 select,epoll 等等。select 系统调用,目前几乎在所有的操作系统上都有支持。
>
-> - **select 调用** :内核提供的系统调用,它支持一次查询多个系统调用的可用状态。几乎所有的操作系统都支持。
-> - **epoll 调用** :linux 2.6 内核,属于 select 调用的增强版本,优化了 IO 的执行效率。
+> - **select 调用**:内核提供的系统调用,它支持一次查询多个系统调用的可用状态。几乎所有的操作系统都支持。
+> - **epoll 调用**:linux 2.6 内核,属于 select 调用的增强版本,优化了 IO 的执行效率。
**IO 多路复用模型,通过减少无效的系统调用,减少了对 CPU 资源的消耗。**
diff --git a/docs/java/jvm/class-loading-process.md b/docs/java/jvm/class-loading-process.md
index 721945a2db0..2957b40e53f 100644
--- a/docs/java/jvm/class-loading-process.md
+++ b/docs/java/jvm/class-loading-process.md
@@ -31,7 +31,7 @@ tag:
2. 将字节流所代表的静态存储结构转换为方法区的运行时数据结构。
3. 在内存中生成一个代表该类的 `Class` 对象,作为方法区这些数据的访问入口。
-虚拟机规范上面这 3 点并不具体,因此是非常灵活的。比如:"通过全类名获取定义此类的二进制字节流" 并没有指明具体从哪里获取( `ZIP` 、 `JAR`、`EAR`、`WAR` 、网络、动态代理技术运行时动态生成、其他文件生成比如 `JSP`...)、怎样获取。
+虚拟机规范上面这 3 点并不具体,因此是非常灵活的。比如:"通过全类名获取定义此类的二进制字节流" 并没有指明具体从哪里获取( `ZIP`、 `JAR`、`EAR`、`WAR`、网络、动态代理技术运行时动态生成、其他文件生成比如 `JSP`...)、怎样获取。
加载这一步主要是通过我们后面要讲到的 **类加载器** 完成的。类加载器有很多种,当我们想要加载一个类的时候,具体是哪个类加载器加载由 **双亲委派模型** 决定(不过,我们也能打破由双亲委派模型)。
@@ -83,7 +83,7 @@ tag:
2. 从概念上讲,类变量所使用的内存都应当在 **方法区** 中进行分配。不过有一点需要注意的是:JDK 7 之前,HotSpot 使用永久代来实现方法区的时候,实现是完全符合这种逻辑概念的。 而在 JDK 7 及之后,HotSpot 已经把原本放在永久代的字符串常量池、静态变量等移动到堆中,这个时候类变量则会随着 Class 对象一起存放在 Java 堆中。相关阅读:[《深入理解 Java 虚拟机(第 3 版)》勘误#75](https://github.com/fenixsoft/jvm_book/issues/75 "《深入理解Java虚拟机(第3版)》勘误#75")
3. 这里所设置的初始值"通常情况"下是数据类型默认的零值(如 0、0L、null、false 等),比如我们定义了`public static int value=111` ,那么 value 变量在准备阶段的初始值就是 0 而不是 111(初始化阶段才会赋值)。特殊情况:比如给 value 变量加上了 final 关键字`public static final int value=111` ,那么准备阶段 value 的值就被赋值为 111。
-**基本数据类型的零值** : (图片来自《深入理解 Java 虚拟机》第 3 版 7.33 )
+**基本数据类型的零值** :(图片来自《深入理解 Java 虚拟机》第 3 版 7.33 )

@@ -103,13 +103,13 @@ tag:
**初始化阶段是执行初始化方法 ` ()`方法的过程,是类加载的最后一步,这一步 JVM 才开始真正执行类中定义的 Java 程序代码(字节码)。**
-> 说明: ` ()`方法是编译之后自动生成的。
+> 说明:` ()`方法是编译之后自动生成的。
对于` ()` 方法的调用,虚拟机会自己确保其在多线程环境中的安全性。因为 ` ()` 方法是带锁线程安全,所以在多线程环境下进行类初始化的话可能会引起多个线程阻塞,并且这种阻塞很难被发现。
对于初始化阶段,虚拟机严格规范了有且只有 5 种情况下,必须对类进行初始化(只有主动去使用类才会初始化类):
-1. 当遇到 `new` 、 `getstatic`、`putstatic` 或 `invokestatic` 这 4 条字节码指令时,比如 `new` 一个类,读取一个静态字段(未被 final 修饰)、或调用一个类的静态方法时。
+1. 当遇到 `new`、 `getstatic`、`putstatic` 或 `invokestatic` 这 4 条字节码指令时,比如 `new` 一个类,读取一个静态字段(未被 final 修饰)、或调用一个类的静态方法时。
- 当 jvm 执行 `new` 指令时会初始化类。即当程序创建一个类的实例对象。
- 当 jvm 执行 `getstatic` 指令时会初始化类。即程序访问类的静态变量(不是静态常量,常量会被加载到运行时常量池)。
- 当 jvm 执行 `putstatic` 指令时会初始化类。即程序给类的静态变量赋值。
diff --git a/docs/java/jvm/classloader.md b/docs/java/jvm/classloader.md
index d487bc5e838..b7ff4e42290 100644
--- a/docs/java/jvm/classloader.md
+++ b/docs/java/jvm/classloader.md
@@ -86,13 +86,13 @@ public abstract class ClassLoader {
JVM 中内置了三个重要的 `ClassLoader`:
-1. **`BootstrapClassLoader`(启动类加载器)** :最顶层的加载类,由 C++实现,通常表示为 null,并且没有父级,主要用来加载 JDK 内部的核心类库( `%JAVA_HOME%/lib`目录下的 `rt.jar` 、`resources.jar` 、`charsets.jar`等 jar 包和类)以及被 `-Xbootclasspath`参数指定的路径下的所有类。
+1. **`BootstrapClassLoader`(启动类加载器)** :最顶层的加载类,由 C++实现,通常表示为 null,并且没有父级,主要用来加载 JDK 内部的核心类库( `%JAVA_HOME%/lib`目录下的 `rt.jar`、`resources.jar`、`charsets.jar`等 jar 包和类)以及被 `-Xbootclasspath`参数指定的路径下的所有类。
2. **`ExtensionClassLoader`(扩展类加载器)** :主要负责加载 `%JRE_HOME%/lib/ext` 目录下的 jar 包和类以及被 `java.ext.dirs` 系统变量所指定的路径下的所有类。
3. **`AppClassLoader`(应用程序类加载器)** :面向我们用户的加载器,负责加载当前应用 classpath 下的所有 jar 包和类。
> 🌈 拓展一下:
>
-> - **`rt.jar`** : rt 代表“RunTime”,`rt.jar`是 Java 基础类库,包含 Java doc 里面看到的所有的类的类文件。也就是说,我们常用内置库 `java.xxx.* `都在里面,比如`java.util.*`、`java.io.*`、`java.nio.*`、`java.lang.*`、`java.sql.*`、`java.math.*`。
+> - **`rt.jar`** :rt 代表“RunTime”,`rt.jar`是 Java 基础类库,包含 Java doc 里面看到的所有的类的类文件。也就是说,我们常用内置库 `java.xxx.* `都在里面,比如`java.util.*`、`java.io.*`、`java.nio.*`、`java.lang.*`、`java.sql.*`、`java.math.*`。
> - Java 9 引入了模块系统,并且略微更改了上述的类加载器。扩展类加载器被改名为平台类加载器(platform class loader)。Java SE 中除了少数几个关键模块,比如说 `java.base` 是由启动类加载器加载之外,其他的模块均由平台类加载器所加载。
除了这三种类加载器之外,用户还可以加入自定义的类加载器来进行拓展,以满足自己的特殊需求。就比如说,我们可以对 Java 类的字节码( `.class` 文件)进行加密,加载时再利用自定义的类加载器对其解密。
@@ -217,7 +217,7 @@ public abstract class ClassLoader {
}
```
-在面向对象编程中,有一条非常经典的设计原则: **组合优于继承,多用组合少用继承。**
+在面向对象编程中,有一条非常经典的设计原则:**组合优于继承,多用组合少用继承。**
### 双亲委派模型的执行流程
diff --git a/docs/java/jvm/jvm-parameters-intro.md b/docs/java/jvm/jvm-parameters-intro.md
index b633409949b..c20dceafc36 100644
--- a/docs/java/jvm/jvm-parameters-intro.md
+++ b/docs/java/jvm/jvm-parameters-intro.md
@@ -29,7 +29,7 @@ tag:
```
- **heap size** 表示要初始化内存的具体大小。
-- **unit** 表示要初始化内存的单位。单位为**_“ g”_** (GB) 、**_“ m”_**(MB)、**_“ k”_**(KB)。
+- **unit** 表示要初始化内存的单位。单位为**_“ g”_** (GB)、**_“ m”_**(MB)、**_“ k”_**(KB)。
举个栗子 🌰,如果我们要为 JVM 分配最小 2 GB 和最大 5 GB 的堆内存大小,我们的参数应该这样来写:
@@ -99,7 +99,7 @@ JDK 1.8 之前永久代还没被彻底移除的时候通常通过下面这些参
-XX:MaxMetaspaceSize=N #设置 Metaspace 的最大大小
```
-**🐛 修正(参见: [issue#1947](https://github.com/Snailclimb/JavaGuide/issues/1947))**:
+**🐛 修正(参见:[issue#1947](https://github.com/Snailclimb/JavaGuide/issues/1947))**:
1、Metaspace 的初始容量并不是 `-XX:MetaspaceSize` 设置,无论 `-XX:MetaspaceSize` 配置什么值,对于 64 位 JVM 来说,Metaspace 的初始容量都是 21807104(约 20.8m)。
@@ -125,7 +125,7 @@ void MetaspaceGC::initialize() {
}
```
-相关阅读: [issue 更正:MaxMetaspaceSize 如果不指定大小的话,不会耗尽内存 #1204 ](https://github.com/Snailclimb/JavaGuide/issues/1204) 。
+相关阅读:[issue 更正:MaxMetaspaceSize 如果不指定大小的话,不会耗尽内存 #1204 ](https://github.com/Snailclimb/JavaGuide/issues/1204) 。
## 3.垃圾收集相关
diff --git a/docs/java/jvm/memory-area.md b/docs/java/jvm/memory-area.md
index 59b76e720fc..ece066442d7 100644
--- a/docs/java/jvm/memory-area.md
+++ b/docs/java/jvm/memory-area.md
@@ -99,7 +99,7 @@ Java 方法有两种返回方式,一种是 return 语句正常返回,一种
### 本地方法栈
-和虚拟机栈所发挥的作用非常相似,区别是: **虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。** 在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一。
+和虚拟机栈所发挥的作用非常相似,区别是:**虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。** 在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一。
本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。
@@ -149,7 +149,7 @@ Java 堆是垃圾收集器管理的主要区域,因此也被称作 **GC 堆(
堆这里最容易出现的就是 `OutOfMemoryError` 错误,并且出现这种错误之后的表现形式还会有几种,比如:
-1. **`java.lang.OutOfMemoryError: GC Overhead Limit Exceeded`** : 当 JVM 花太多时间执行垃圾回收并且只能回收很少的堆空间时,就会发生此错误。
+1. **`java.lang.OutOfMemoryError: GC Overhead Limit Exceeded`** :当 JVM 花太多时间执行垃圾回收并且只能回收很少的堆空间时,就会发生此错误。
2. **`java.lang.OutOfMemoryError: Java heap space`** :假如在创建新的对象时, 堆内存中的空间不足以存放新创建的对象, 就会引发此错误。(和配置的最大堆内存有关,且受制于物理内存大小。最大堆内存可通过`-Xmx`参数配置,若没有特别配置,将会使用默认值,详见:[Default Java 8 max heap size](https://stackoverflow.com/questions/28272923/default-xmxsize-in-java-8-max-heap-size))
3. ......
@@ -173,7 +173,7 @@ Java 堆是垃圾收集器管理的主要区域,因此也被称作 **GC 堆(
1、整个永久代有一个 JVM 本身设置的固定大小上限,无法进行调整,而元空间使用的是本地内存,受本机可用内存的限制,虽然元空间仍旧可能溢出,但是比原来出现的几率会更小。
-> 当元空间溢出时会得到如下错误: `java.lang.OutOfMemoryError: MetaSpace`
+> 当元空间溢出时会得到如下错误:`java.lang.OutOfMemoryError: MetaSpace`
你可以使用 `-XX:MaxMetaspaceSize` 标志设置最大元空间大小,默认值为 unlimited,这意味着它只受系统内存的限制。`-XX:MetaspaceSize` 调整标志定义元空间的初始大小如果未指定此标志,则 Metaspace 将根据运行时的应用程序需求动态地重新调整大小。
@@ -285,7 +285,7 @@ Java 对象的创建过程我建议最好是能默写出来,并且要掌握每
- 原理 :用过的内存全部整合到一边,没有用过的内存放在另一边,中间有一个分界指针,只需要向着没用过的内存方向将该指针移动对象内存大小位置即可。
- 使用该分配方式的 GC 收集器:Serial, ParNew
- 空闲列表 :
- - 适用场合 : 堆内存不规整的情况下。
+ - 适用场合 :堆内存不规整的情况下。
- 原理 :虚拟机会维护一个列表,该列表中会记录哪些内存块是可用的,在分配的时候,找一块儿足够大的内存块儿来划分给对象实例,最后更新列表记录。
- 使用该分配方式的 GC 收集器:CMS
@@ -346,8 +346,8 @@ HotSpot 虚拟机主要使用的就是这种方式来进行对象访问。
- 《自己动手写 Java 虚拟机》
- Chapter 2. The Structure of the Java Virtual Machine:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html
- JVM 栈帧内部结构-动态链接:https://chenxitag.com/archives/368
-- Java 中 new String("字面量") 中 "字面量" 是何时进入字符串常量池的? - 木女孩的回答 - 知乎: https://www.zhihu.com/question/55994121/answer/147296098
-- JVM 常量池中存储的是对象还是引用呢? - RednaxelaFX 的回答 - 知乎: https://www.zhihu.com/question/57109429/answer/151717241
+- Java 中 new String("字面量") 中 "字面量" 是何时进入字符串常量池的? - 木女孩的回答 - 知乎:https://www.zhihu.com/question/55994121/answer/147296098
+- JVM 常量池中存储的是对象还是引用呢? - RednaxelaFX 的回答 - 知乎:https://www.zhihu.com/question/57109429/answer/151717241
-
-
-
diff --git a/docs/java/new-features/java11.md b/docs/java/new-features/java11.md
index f42eb0bdd14..ab7cecce45d 100644
--- a/docs/java/new-features/java11.md
+++ b/docs/java/new-features/java11.md
@@ -88,7 +88,7 @@ ZGC 目前 **处在实验阶段**,只支持 Linux/x64 平台。
在 ZGC 中出现 Stop The World 的情况会更少!
-详情可以看 : [《新一代垃圾回收器 ZGC 的探索与实践》](https://tech.meituan.com/2020/08/06/new-zgc-practice-in-meituan.html)
+详情可以看 :[《新一代垃圾回收器 ZGC 的探索与实践》](https://tech.meituan.com/2020/08/06/new-zgc-practice-in-meituan.html)
## Lambda 参数的局部变量语法
@@ -126,4 +126,4 @@ Consumer consumer = (String i) -> System.out.println(i);
## 参考
- JDK 11 Release Notes:https://www.oracle.com/java/technologies/javase/11-relnote-issues.html
-- Java 11 – Features and Comparison:
+- Java 11 – Features and Comparison:
diff --git a/docs/java/new-features/java12-13.md b/docs/java/new-features/java12-13.md
index 41b6ca7ae22..e2e119155af 100644
--- a/docs/java/new-features/java12-13.md
+++ b/docs/java/new-features/java12-13.md
@@ -288,7 +288,7 @@ public String translateEscapes() {
## 参考
-- JDK Project Overview :
+- JDK Project Overview :
- Oracle Java12 ReleaseNote :https://www.oracle.com/java/technologies/javase/12all-relnotes.htm
- What is new in Java 12:https://mkyong.com/java/what-is-new-in-java-12/
- Oracle Java13 ReleaseNote
diff --git a/docs/java/new-features/java14-15.md b/docs/java/new-features/java14-15.md
index 7746cdf319d..a6a8acf81ec 100644
--- a/docs/java/new-features/java14-15.md
+++ b/docs/java/new-features/java14-15.md
@@ -237,5 +237,5 @@ Java 15 并没有对此特性进行调整,继续预览特性,主要用于接
- **Nashorn JavaScript 引擎彻底移除** :Nashorn 从 Java8 开始引入的 JavaScript 引擎,Java9 对 Nashorn 做了些增强,实现了一些 ES6 的新特性。在 Java 11 中就已经被弃用,到了 Java 15 就彻底被删除了。
- **DatagramSocket API 重构**
-- **禁用和废弃偏向锁(Biased Locking)** : 偏向锁的引入增加了 JVM 的复杂性大于其带来的性能提升。不过,你仍然可以使用 `-XX:+UseBiasedLocking` 启用偏向锁定,但它会提示 这是一个已弃用的 API。
+- **禁用和废弃偏向锁(Biased Locking)** :偏向锁的引入增加了 JVM 的复杂性大于其带来的性能提升。不过,你仍然可以使用 `-XX:+UseBiasedLocking` 启用偏向锁定,但它会提示 这是一个已弃用的 API。
- ......
diff --git a/docs/java/new-features/java19.md b/docs/java/new-features/java19.md
index 9bfaa7bd321..1dc31576be9 100644
--- a/docs/java/new-features/java19.md
+++ b/docs/java/new-features/java19.md
@@ -37,7 +37,7 @@ Java 程序可以通过该 API 与 Java 运行时之外的代码和数据进行
Foreign Function & Memory API (FFM API) 定义了类和接口:
- 分配外部内存 :`MemorySegment`、、`MemoryAddress`和`SegmentAllocator`);
-- 操作和访问结构化的外部内存: `MemoryLayout`, `VarHandle`;
+- 操作和访问结构化的外部内存:`MemoryLayout`, `VarHandle`;
- 控制外部内存的分配和释放:`MemorySession`;
- 调用外部函数:`Linker`、`FunctionDescriptor`和`SymbolLookup`。
diff --git a/docs/java/new-features/java8-common-new-features.md b/docs/java/new-features/java8-common-new-features.md
index d87d187464b..301573edcb3 100644
--- a/docs/java/new-features/java8-common-new-features.md
+++ b/docs/java/new-features/java8-common-new-features.md
@@ -733,7 +733,7 @@ public Optional filter(Predicate super T> predicate) {
### 小结
-看完 `Optional` 源码,`Optional` 的方法真的非常简单,值得注意的是如果坚决不想看见 `NPE`,就不要用 `of() `、 `get()` 、`flatMap(..)`。最后再综合用一下 `Optional` 的高频方法。
+看完 `Optional` 源码,`Optional` 的方法真的非常简单,值得注意的是如果坚决不想看见 `NPE`,就不要用 `of() `、 `get()`、`flatMap(..)`。最后再综合用一下 `Optional` 的高频方法。
```java
Optional.ofNullable(zoo).map(o -> o.getDog()).map(d -> d.getAge()).filter(v->v==1).orElse(3);
diff --git a/docs/java/new-features/java8-tutorial-translate.md b/docs/java/new-features/java8-tutorial-translate.md
index 6f948544654..39f9c25317e 100644
--- a/docs/java/new-features/java8-tutorial-translate.md
+++ b/docs/java/new-features/java8-tutorial-translate.md
@@ -244,7 +244,7 @@ Formula formula = (a) -> sqrt(a * 100);
### 内置函数式接口(Built-in Functional Interfaces)
-JDK 1.8 API 包含许多内置函数式接口。 其中一些接口在老版本的 Java 中是比较常见的比如: `Comparator` 或`Runnable`,这些接口都增加了`@FunctionalInterface`注解以便能用在 lambda 表达式上。
+JDK 1.8 API 包含许多内置函数式接口。 其中一些接口在老版本的 Java 中是比较常见的比如:`Comparator` 或`Runnable`,这些接口都增加了`@FunctionalInterface`注解以便能用在 lambda 表达式上。
但是 Java 8 API 同样还提供了很多全新的函数式接口来让你的编程工作更加方便,有一些接口是来自 [Google Guava](https://code.google.com/p/guava-libraries/) 库里的,即便你对这些很熟悉了,还是有必要看看这些是如何扩展到 lambda 上使用的。
@@ -384,7 +384,7 @@ Optional 是一个简单的容器,其值可能是 null 或者不是 null。在
```java
//of():为非null的值创建一个Optional
Optional optional = Optional.of("bam");
-// isPresent(): 如果值存在返回true,否则返回false
+// isPresent():如果值存在返回true,否则返回false
optional.isPresent(); // true
//get():如果Optional有值则将其返回,否则抛出NoSuchElementException
optional.get(); // "bam"
@@ -537,7 +537,7 @@ concat = Stream.of("a", "B", "c", "D", "e", "F").
reduce("", String::concat);
```
-上面代码例如第一个示例的 reduce(),第一个参数(空白字符)即为起始值,第二个参数(String::concat)为 BinaryOperator。这类有起始值的 reduce() 都返回具体的对象。而对于第四个示例没有起始值的 reduce(),由于可能没有足够的元素,返回的是 Optional,请留意这个区别。更多内容查看: [IBM:Java 8 中的 Streams API 详解](https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/index.html)
+上面代码例如第一个示例的 reduce(),第一个参数(空白字符)即为起始值,第二个参数(String::concat)为 BinaryOperator。这类有起始值的 reduce() 都返回具体的对象。而对于第四个示例没有起始值的 reduce(),由于可能没有足够的元素,返回的是 Optional,请留意这个区别。更多内容查看:[IBM:Java 8 中的 Streams API 详解](https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/index.html)
## Parallel Streams(并行流)
@@ -775,7 +775,7 @@ DateTimeFormatter formatter=DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss");
System.out.println(formatter.format(rightNow));//2019-03-12 16:26:48
```
-**🐛 修正(参见: [issue#1157](https://github.com/Snailclimb/JavaGuide/issues/1157))**:使用 `YYYY` 显示年份时,会显示当前时间所在周的年份,在跨年周会有问题。一般情况下都使用 `yyyy`,来显示准确的年份。
+**🐛 修正(参见:[issue#1157](https://github.com/Snailclimb/JavaGuide/issues/1157))**:使用 `YYYY` 显示年份时,会显示当前时间所在周的年份,在跨年周会有问题。一般情况下都使用 `yyyy`,来显示准确的年份。
跨年导致日期显示错误示例:
diff --git a/docs/java/new-features/java9.md b/docs/java/new-features/java9.md
index d5195f867df..70ecd7a8d06 100644
--- a/docs/java/new-features/java9.md
+++ b/docs/java/new-features/java9.md
@@ -7,7 +7,7 @@ tag:
**Java 9** 发布于 2017 年 9 月 21 日 。作为 Java 8 之后 3 年半才发布的新版本,Java 9 带来了很多重大的变化其中最重要的改动是 Java 平台模块系统的引入,其他还有诸如集合、`Stream` 流......。
-你可以在 [Archived OpenJDK General-Availability Releases](http://jdk.java.net/archive/) 上下载自己需要的 JDK 版本!官方的新特性说明文档地址: https://openjdk.java.net/projects/jdk/ 。
+你可以在 [Archived OpenJDK General-Availability Releases](http://jdk.java.net/archive/) 上下载自己需要的 JDK 版本!官方的新特性说明文档地址:https://openjdk.java.net/projects/jdk/ 。
**概览(精选了一部分)** :
@@ -242,7 +242,7 @@ System.out.println(currentProcess.info());
## 其它
-- **平台日志 API 改进** : Java 9 允许为 JDK 和应用配置同样的日志实现。新增了 `System.LoggerFinder` 用来管理 JDK 使 用的日志记录器实现。JVM 在运行时只有一个系统范围的 `LoggerFinder` 实例。我们可以通过添加自己的 `System.LoggerFinder` 实现来让 JDK 和应用使用 SLF4J 等其他日志记录框架。
+- **平台日志 API 改进** :Java 9 允许为 JDK 和应用配置同样的日志实现。新增了 `System.LoggerFinder` 用来管理 JDK 使 用的日志记录器实现。JVM 在运行时只有一个系统范围的 `LoggerFinder` 实例。我们可以通过添加自己的 `System.LoggerFinder` 实现来让 JDK 和应用使用 SLF4J 等其他日志记录框架。
- **`CompletableFuture`类增强** :新增了几个新的方法(`completeAsync` ,`orTimeout` 等)。
- **Nashorn 引擎的增强** :Nashorn 从 Java8 开始引入的 JavaScript 引擎,Java9 对 Nashorn 做了些增强,实现了一些 ES6 的新特性(Java 11 中已经被弃用)。
- **I/O 流的新特性** :增加了新的方法来读取和复制 `InputStream` 中包含的数据。
diff --git a/docs/javaguide/history.md b/docs/javaguide/history.md
index 40e03a788c6..c539426fafd 100644
--- a/docs/javaguide/history.md
+++ b/docs/javaguide/history.md
@@ -15,5 +15,5 @@ category: 走近项目
- [2022-04-09](https://github.com/Snailclimb/JavaGuide/commit/618477f96c4a976cfe5a3bba2f646c0d20e3137e):添加[技术书籍精选](https://javaguide.cn/books/)。
- [2021-03-13](https://github.com/Snailclimb/JavaGuide/commit/5c45af9c7a7cff0d3bc905b09b3bc42f2ee8a88a) :添加[开源项目精选](https://javaguide.cn/open-source-project/)。
- [2021-03-03](https://github.com/Snailclimb/JavaGuide/commit/827996e7722fd51718863d4bee156a8c6c759ff5) : 移除了开发工具的部分内容。
-- [2021-03-03](https://github.com/Snailclimb/JavaGuide/commit/5a5f8ccb3bfb8d6ba8ac41295f1a1e4555395260) : vuepress-theme-hope 主题更新升级(比较重要的一次主题更新,从 1.x 版本升级到 2.x 版本,网站性能提升)。
+- [2021-03-03](https://github.com/Snailclimb/JavaGuide/commit/5a5f8ccb3bfb8d6ba8ac41295f1a1e4555395260) :vuepress-theme-hope 主题更新升级(比较重要的一次主题更新,从 1.x 版本升级到 2.x 版本,网站性能提升)。
- [2021-11-09](https://github.com/Snailclimb/JavaGuide/commit/dc45389934b7763f4f9789168f71c72ef303d3c4) : 基于 vuepress 重构整个项目,提升阅读体验。
diff --git a/docs/open-source-project/big-data.md b/docs/open-source-project/big-data.md
index 6592c6b5234..11f71cc6b81 100644
--- a/docs/open-source-project/big-data.md
+++ b/docs/open-source-project/big-data.md
@@ -6,6 +6,6 @@ icon: big-data
- **[Spark](https://github.com/apache/spark)** :Spark 是用于大规模数据处理的统一分析引擎。
- **[Flink](https://github.com/apache/flink "flink")** :Apache Flink 是一个框架和分布式处理引擎,用于在*无边界和有边界*数据流上进行有状态的计算。Flink 能在所有常见集群环境中运行,并能以内存速度和任意规模进行计算。
-- **[HBase](https://hbase.apache.org/)**: HBase – Hadoop Database,是一个高可靠性、高性能、面向列、可伸缩的分布式存储系统,利用 HBase 技术可在廉价 PC Server 上搭建起大规模结构化存储集群。
+- **[HBase](https://hbase.apache.org/)**:HBase – Hadoop Database,是一个高可靠性、高性能、面向列、可伸缩的分布式存储系统,利用 HBase 技术可在廉价 PC Server 上搭建起大规模结构化存储集群。
- **[Flume](https://flume.apache.org/)** :Apache Flume 是一个分布式的、可靠的、可用的,从多种不同的源收集、聚集、移动大量日志数据到集中数据存储的系统。
- **[Storm](https://storm.apache.org/)** : 一个分布式,高容错的实时计算系统。
diff --git a/docs/open-source-project/machine-learning.md b/docs/open-source-project/machine-learning.md
index 1fbbe86caa3..dafb509a1ae 100644
--- a/docs/open-source-project/machine-learning.md
+++ b/docs/open-source-project/machine-learning.md
@@ -4,7 +4,7 @@ category: 开源项目
icon: a-MachineLearning
---
-- **[Deeplearning4j](https://github.com/eclipse/deeplearning4j)** : Deeplearning4j 是第一个为 Java 和 Scala 编写的商业级,开源,分布式深度学习库。
+- **[Deeplearning4j](https://github.com/eclipse/deeplearning4j)** :Deeplearning4j 是第一个为 Java 和 Scala 编写的商业级,开源,分布式深度学习库。
- **[Smile](https://github.com/haifengl/smile)** :基于 Java 和 Scala 的机器学习库。
相关阅读:[Java 能用于机器学习和数据科学吗?-InfoQ](https://www.infoq.cn/article/GA9UeYlv8ohBzBso9eph)
diff --git a/docs/open-source-project/practical-project.md b/docs/open-source-project/practical-project.md
index a242fc5607b..0c264ac0e1e 100644
--- a/docs/open-source-project/practical-project.md
+++ b/docs/open-source-project/practical-project.md
@@ -9,8 +9,8 @@ icon: project
- [Snowy](https://gitee.com/xiaonuobase/snowy):国内首个国密前后端分离快速开发平台,定位不是深度封装的框架,也不是无代码平台,更不是某个领域的产品。详细介绍:[5.1k!这是我见过最强的前后端分离快速开发脚手架!!](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247534316&idx=1&sn=69938397674fc33ecda43c8c9d0a4039&chksm=cea10927f9d68031bc862485c6be984ade5af233d4d871d498c38f22164a84314678c0c67cd7&token=1464380539&lang=zh_CN#rd)。
- [RuoYi](https://gitee.com/y_project/RuoYi) :RuoYi 一款基于基于 SpringBoot 的权限管理系统 易读易懂、界面简洁美观,直接运行即可用 。
- [AgileBoot-Back-End](https://github.com/valarchie/AgileBoot-Back-End) :基于 Ruoyi 做了大量重构优化的基础快速开发框架。
-- [RuoYi-Vue-Pro](https://github.com/YunaiV/ruoyi-vue-pro) : RuoYi-Vue 全新 Pro 版本,优化重构所有功能。
-- [Guns](https://gitee.com/stylefeng/guns) : 现代化的 Java 应用开发基础框架。
+- [RuoYi-Vue-Pro](https://github.com/YunaiV/ruoyi-vue-pro) :RuoYi-Vue 全新 Pro 版本,优化重构所有功能。
+- [Guns](https://gitee.com/stylefeng/guns) :现代化的 Java 应用开发基础框架。
- [JeecgBoot](https://github.com/zhangdaiscott/jeecg-boot):一款基于代码生成器的 J2EE 低代码快速开发平台,支持生成前后端分离架构的项目。
- [Erupt](https://gitee.com/erupt/erupt) : 低代码全栈类框架,它使用 Java 注解 动态生成页面以及增、删、改、查、权限控制等后台功能。
- [SmartAdmin](https://gitee.com/lab1024/smart-admin) : 一套简洁、易用的低代码中后台解决方案。
@@ -31,7 +31,7 @@ icon: project
- [favorites-web](https://github.com/cloudfavorites/favorites-web) :云收藏 Spring Boot 2.X 开源项目。云收藏是一个使用 Spring Boot 构建的开源网站,可以让用户在线随时随地收藏的一个网站,在网站上分类整理收藏的网站或者文章。
- [community](https://github.com/codedrinker/community) :开源论坛、问答系统,现有功能提问、回复、通知、最新、最热、消除零回复功能。功能持续更新中…… 技术栈 Spring、Spring Boot、MyBatis、MySQL/H2、Bootstrap。
- [VBlog](https://github.com/lenve/VBlog) :V 部落,Vue+SpringBoot 实现的多用户博客管理平台!
-- [My-Blog](https://github.com/ZHENFENG13/My-Blog) : My Blog 是由 SpringBoot + Mybatis + Thymeleaf 等技术实现的 Java 博客系统,页面美观、功能齐全、部署简单及完善的代码,一定会给使用者无与伦比的体验。
+- [My-Blog](https://github.com/ZHENFENG13/My-Blog) :My Blog 是由 SpringBoot + Mybatis + Thymeleaf 等技术实现的 Java 博客系统,页面美观、功能齐全、部署简单及完善的代码,一定会给使用者无与伦比的体验。
## 考试/刷题系统
@@ -45,7 +45,7 @@ icon: project
- [mall](https://github.com/macrozheng/mall "mall") :mall 项目是一套电商系统,包括前台商城系统及后台管理系统,基于 SpringBoot+MyBatis 实现。
- [mall-swarm](https://github.com/macrozheng/mall-swarm "mall-swarm") : mall-swarm 是一套微服务商城系统,采用了 Spring Cloud Greenwich、Spring Boot 2、MyBatis、Docker、Elasticsearch 等核心技术,同时提供了基于 Vue 的管理后台方便快速搭建系统。
- [onemall](https://github.com/YunaiV/onemall) :mall 商城,基于微服务的思想,构建在 B2C 电商场景下的项目实战。核心技术栈,是 Spring Boot + Dubbo 。未来,会重构成 Spring Cloud Alibaba 。
-- [litemall](https://github.com/linlinjava/litemall "litemall") : 又一个小商城。litemall = Spring Boot 后端 + Vue 管理员前端 + 微信小程序用户前端 + Vue 用户移动端。
+- [litemall](https://github.com/linlinjava/litemall "litemall") :又一个小商城。litemall = Spring Boot 后端 + Vue 管理员前端 + 微信小程序用户前端 + Vue 用户移动端。
- [xmall](https://github.com/Exrick/xmall) :基于 SOA 架构的分布式电商购物商城 前后端分离 前台商城:Vue 全家桶 后台管理系统:Spring/Dubbo/SSM/Elasticsearch/Redis/MySQL/ActiveMQ/Shiro/Zookeeper 等
- [newbee-mall](https://github.com/newbee-ltd/newbee-mall) :newbee-mall 项目(新蜂商城)是一套电商系统,包括 newbee-mall 商城系统及 newbee-mall-admin 商城后台管理系统,基于 Spring Boot 2.X 及相关技术栈开发。
@@ -56,7 +56,7 @@ icon: project
- [Spring-Cloud-Admin](https://github.com/wxiaoqi/Spring-Cloud-Admin "Spring-Cloud-Admin") :Cloud-Admin 是国内首个基于 Spring Cloud 微服务化开发平台,具有统一授权、认证后台管理系统,其中包含具备用户管理、资源权限管理、网关 API 管理等多个模块,支持多业务系统并行开发,可以作为后端服务的开发脚手架。代码简洁,架构清晰,适合学习和直接项目中使用。核心技术采用 Spring Boot2 以及 Spring Cloud Gateway 相关核心组件,前端采用 vue-element-admin 组件。
- [pig](https://gitee.com/log4j/pig "pig"):(gitee)基于 Spring Boot 2.2、 Spring Cloud Hoxton & Alibaba、 OAuth2 的 RBAC 权限管理系统。
- [FEBS-Shiro](https://github.com/wuyouzhuguli/FEBS-Shiro "FEBS-Shiro") :Spring Boot 2.1.3,Shiro1.4.0 & Layui 2.5.4 权限管理系统。
-- [eladmin](https://github.com/elunez/eladmin) : 项目基于 Spring Boot 2.1.0 、 Jpa、 Spring Security、redis、Vue 的前后端分离的后台管理系统,项目采用分模块开发方式, 权限控制采用 RBAC,支持数据字典与数据权限管理,支持一键生成前后端代码,支持动态路由。
+- [eladmin](https://github.com/elunez/eladmin) : 项目基于 Spring Boot 2.1.0、 Jpa、 Spring Security、redis、Vue 的前后端分离的后台管理系统,项目采用分模块开发方式, 权限控制采用 RBAC,支持数据字典与数据权限管理,支持一键生成前后端代码,支持动态路由。
- [SpringBoot-Shiro-Vue](https://github.com/Heeexy/SpringBoot-Shiro-Vue) :提供一套基于 Spring Boot-Shiro-Vue 的权限管理思路.前后端都加以控制,做到按钮/接口级别的权限。
## 造轮子
diff --git a/docs/open-source-project/system-design.md b/docs/open-source-project/system-design.md
index 73d682aa8cb..7f2ba7c3c0f 100644
--- a/docs/open-source-project/system-design.md
+++ b/docs/open-source-project/system-design.md
@@ -23,7 +23,7 @@ icon: "xitongsheji"
### 数据库框架
- [MyBatis-Plus](https://github.com/baomidou/mybatis-plus) : [MyBatis-Plus](https://github.com/baomidou/mybatis-plus)(简称 MP)是一个 [MyBatis](http://www.mybatis.org/mybatis-3/) 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
-- [Redisson](https://github.com/redisson/redisson "redisson") : Redis 基础上的一个 Java 驻内存数据网格(In-Memory Data Grid),支持超过 30 个对象和服务:`Set`,`SortedSet`, `Map`, `List`, `Queue`, `Deque` ......,并且提供了多种分布式锁的实现。更多介绍请看:[《Redisson 项目介绍》](https://github.com/redisson/redisson/wiki/Redisson%E9%A1%B9%E7%9B%AE%E4%BB%8B%E7%BB%8D "Redisson项目介绍")。
+- [Redisson](https://github.com/redisson/redisson "redisson") :Redis 基础上的一个 Java 驻内存数据网格(In-Memory Data Grid),支持超过 30 个对象和服务:`Set`,`SortedSet`, `Map`, `List`, `Queue`, `Deque` ......,并且提供了多种分布式锁的实现。更多介绍请看:[《Redisson 项目介绍》](https://github.com/redisson/redisson/wiki/Redisson%E9%A1%B9%E7%9B%AE%E4%BB%8B%E7%BB%8D "Redisson项目介绍")。
### 数据同步
@@ -63,7 +63,7 @@ icon: "xitongsheji"
- [JUnit](http://junit.org/) : Java 测试框架。
- [Mockito](https://github.com/mockito/mockito) :Mockito 是一个模拟测试框架,可以让你用优雅,简洁的接口写出漂亮的单元测试。(对那些不容易构建的对象用一个虚拟对象来代替,使其在调试期间用来作为真实对象的替代品)
-- [PowerMock](https://github.com/powermock/powermock) : 编写单元测试仅靠 Mockito 是不够。因为 Mockito 无法 mock 私有方法、final 方法及静态方法等。PowerMock 这个 framework,主要是为了扩展其他 mock 框架,如 Mockito、EasyMock。它使用一个自定义的类加载器,纂改字节码,突破 Mockito 无法 mock 静态方法、构造方法、final 类、final 方法以及私有方法的限制。
+- [PowerMock](https://github.com/powermock/powermock) :编写单元测试仅靠 Mockito 是不够。因为 Mockito 无法 mock 私有方法、final 方法及静态方法等。PowerMock 这个 framework,主要是为了扩展其他 mock 框架,如 Mockito、EasyMock。它使用一个自定义的类加载器,纂改字节码,突破 Mockito 无法 mock 静态方法、构造方法、final 类、final 方法以及私有方法的限制。
- [WireMock](https://github.com/tomakehurst/wiremock) :模拟 HTTP 服务的工具(Mock your APIs)。
- [Testcontainers](https://github.com/testcontainers/testcontainers-java) :一个支持 JUnit 的测试工具库,提供轻量级的且一次性的常见数据库测试支持、Selenium Web 浏览器或者其他任何可以在 Docker 容器中运行的实例支持。
@@ -88,7 +88,7 @@ icon: "xitongsheji"
- [Quartz](https://github.com/quartz-scheduler/quartz) :一个很火的开源任务调度框架,Java 定时任务领域的老大哥或者说参考标准, 很多其他任务调度框架都是基于 `quartz` 开发的,比如当当网的`elastic-job`就是基于`quartz`二次开发之后的分布式调度解决方案
- [XXL-JOB](https://github.com/xuxueli/xxl-job) :XXL-JOB 是一个分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线,开箱即用。
- [Elastic-Job](http://elasticjob.io/index_zh.html) :Elastic-Job 是当当网开源的一个基于 Quartz 和 Zookeeper 的分布式调度解决方案,由两个相互独立的子项目 Elastic-Job-Lite 和 Elastic-Job-Cloud 组成,一般我们只要使用 Elastic-Job-Lite 就好。
-- [EasyScheduler](https://github.com/analysys/EasyScheduler "EasyScheduler") (已经更名为 DolphinScheduler,已经成为 Apache 孵化器项目): Easy Scheduler 是一个分布式工作流任务调度系统,主要解决“复杂任务依赖但无法直接监控任务健康状态”的问题。Easy Scheduler 以 DAG 方式组装任务,可以实时监控任务的运行状态。同时,它支持重试,重新运行等操作... 。
+- [EasyScheduler](https://github.com/analysys/EasyScheduler "EasyScheduler") (已经更名为 DolphinScheduler,已经成为 Apache 孵化器项目):Easy Scheduler 是一个分布式工作流任务调度系统,主要解决“复杂任务依赖但无法直接监控任务健康状态”的问题。Easy Scheduler 以 DAG 方式组装任务,可以实时监控任务的运行状态。同时,它支持重试,重新运行等操作... 。
- [PowerJob](https://gitee.com/KFCFans/PowerJob) :新一代分布式任务调度与计算框架,支持 CRON、API、固定频率、固定延迟等调度策略,提供工作流来编排任务解决依赖关系,使用简单,功能强大,文档齐全,欢迎各位接入使用! 。
- [DolphinScheduler](https://github.com/apache/dolphinscheduler) :分布式易扩展的可视化工作流任务调度平台。
@@ -116,7 +116,7 @@ icon: "xitongsheji"
- [Apollo](https://github.com/ctripcorp/apollo "apollo")(推荐) :Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。
- [Nacos](https://github.com/alibaba/nacos)(推荐):Nacos 是 Spring Cloud Alibaba 提供的服务注册发现组件,类似于 Consul、Eureka。并且,提供了分布式配置管理功能。
-- [Spring Cloud Config](https://github.com/spring-cloud/spring-cloud-config) : Spring Cloud Config 是 Spring Cloud 家族中最早的配置中心,虽然后来又发布了 Consul 可以代替配置中心功能,但是 Config 依然适用于 Spring Cloud 项目,通过简单的配置即可实现功能。
+- [Spring Cloud Config](https://github.com/spring-cloud/spring-cloud-config) :Spring Cloud Config 是 Spring Cloud 家族中最早的配置中心,虽然后来又发布了 Consul 可以代替配置中心功能,但是 Config 依然适用于 Spring Cloud 项目,通过简单的配置即可实现功能。
- [Consul](https://github.com/hashicorp/consul) :Consul 是 HashiCorp 公司推出的开源软件,提供了微服务系统中的服务治理、配置中心、控制总线等功能。这些功能中的每一个都可以根据需要单独使用,也可以一起使用以构建全方位的服务网格,总之 Consul 提供了一种完整的服务网格解决方案。
### 链路追踪
@@ -125,9 +125,9 @@ icon: "xitongsheji"
1. [Skywalking](https://github.com/apache/skywalking "skywalking") : 针对分布式系统的应用性能监控,尤其是针对微服务、云原生和面向容器的分布式系统架构。
2. [Zipkin](https://github.com/openzipkin/zipkin "zipkin") :Zipkin 是一个分布式跟踪系统。它有助于收集解决服务体系结构中的延迟问题所需的时序数据。功能包括该数据的收集和查找。
-3. [CAT](https://github.com/dianping/cat "cat") : CAT 作为服务端项目基础组件,提供了 Java, C/C++, Node.js, Python, Go 等多语言客户端,已经在美团点评的基础架构中间件框架(MVC 框架,RPC 框架,数据库框架,缓存框架等,消息队列,配置系统等)深度集成,为美团点评各业务线提供系统丰富的性能指标、健康状况、实时告警等。
+3. [CAT](https://github.com/dianping/cat "cat") :CAT 作为服务端项目基础组件,提供了 Java, C/C++, Node.js, Python, Go 等多语言客户端,已经在美团点评的基础架构中间件框架(MVC 框架,RPC 框架,数据库框架,缓存框架等,消息队列,配置系统等)深度集成,为美团点评各业务线提供系统丰富的性能指标、健康状况、实时告警等。
-相关阅读: [Skywalking 官网对于主流开源链路追踪系统的对比](https://skywalking.apache.org/zh/blog/2019-03-29-introduction-of-skywalking-and-simple-practice.html)
+相关阅读:[Skywalking 官网对于主流开源链路追踪系统的对比](https://skywalking.apache.org/zh/blog/2019-03-29-introduction-of-skywalking-and-simple-practice.html)
## 高性能
@@ -152,7 +152,7 @@ icon: "xitongsheji"
**内存队列** :
-- [Disruptor](https://github.com/LMAX-Exchange/disruptor) : Disruptor 是英国外汇交易公司 LMAX 开发的一个高性能队列,研发的初衷是解决内存队列的延迟问题(在性能测试中发现竟然与 I/O 操作处于同样的数量级)。相关阅读 :[《高性能内存队列——Disruptor》](https://tech.meituan.com/2016/11/18/disruptor.html) 。
+- [Disruptor](https://github.com/LMAX-Exchange/disruptor) :Disruptor 是英国外汇交易公司 LMAX 开发的一个高性能队列,研发的初衷是解决内存队列的延迟问题(在性能测试中发现竟然与 I/O 操作处于同样的数量级)。相关阅读 :[《高性能内存队列——Disruptor》](https://tech.meituan.com/2016/11/18/disruptor.html) 。
**可视化管理工具** :
@@ -190,7 +190,7 @@ icon: "xitongsheji"
### 日志
-- EKL 老三件套 : 最原始的时候,ELK 是由 3 个开源项目的首字母构成,分别是 Elasticsearch 、Logstash、Kibana。
+- EKL 老三件套 : 最原始的时候,ELK 是由 3 个开源项目的首字母构成,分别是 Elasticsearch、Logstash、Kibana。
- 新一代 ELK 架构 : Elasticsearch+Logstash+Kibana+Beats。
- EFK : EFK 中的 F 代表的是 [Fluentd](https://github.com/fluent/fluentd)。
- [TLog](https://gitee.com/dromara/TLog) :一个轻量级的分布式日志标记追踪神器,10 分钟即可接入,自动对日志打标签完成微服务的链路追踪。
diff --git a/docs/open-source-project/tool-library.md b/docs/open-source-project/tool-library.md
index 54277d9ae68..a8dd744d39e 100644
--- a/docs/open-source-project/tool-library.md
+++ b/docs/open-source-project/tool-library.md
@@ -10,7 +10,7 @@ icon: codelibrary-fill
- [guava](https://github.com/google/guava "guava") :Guava 是一组核心库,其中包括新的集合类型(例如 multimap 和 multiset),不可变集合,图形库以及用于并发、I / O、哈希、原始类型、字符串等的实用程序!
- [hutool](https://github.com/looly/hutool "hutool") : Hutool 是一个 Java 工具包,也只是一个工具包,它帮助我们简化每一行代码,减少每一个方法,让 Java 语言也可以“甜甜的”。
- [p3c](https://github.com/alibaba/p3c "p3c") :Alibaba Java Coding Guidelines pmd implements and IDE plugin。Eclipse 和 IDEA 上都有该插件,推荐使用!
-- [arthas](https://github.com/alibaba/arthas "arthas") : Arthas 是 Alibaba 开源的 Java 诊断工具。
+- [arthas](https://github.com/alibaba/arthas "arthas") :Arthas 是 Alibaba 开源的 Java 诊断工具。
- [sonarqube](https://github.com/SonarSource/sonarqube "sonarqube") :SonarQube 支持所有开发人员编写更干净,更安全的代码。
- [checkstyle](https://github.com/checkstyle/checkstyle "checkstyle") :Checkstyle 是一种开发工具,可帮助程序员编写符合编码标准的 Java 代码。它使检查 Java 代码的过程自动化,从而使人们不必执行这项无聊(但很重要)的任务。这使其成为想要实施编码标准的项目的理想选择。
- [pmd](https://github.com/pmd/pmd "pmd") : 可扩展的多语言静态代码分析器。
diff --git a/docs/open-source-project/tools.md b/docs/open-source-project/tools.md
index 1bbc3d90cf4..740f39b94ca 100644
--- a/docs/open-source-project/tools.md
+++ b/docs/open-source-project/tools.md
@@ -7,7 +7,7 @@ icon: tool
## Java
- [JADX](https://github.com/skylot/jadx) :一款功能强大的反编译工具。
-- [Recaf](https://github.com/Col-E/Recaf) : Java 字节码编辑器,基于 ASM(Java 字节码操作框架) 来修改字节码,可简化编辑已编译 Java 应用程序的过程。
+- [Recaf](https://github.com/Col-E/Recaf) :Java 字节码编辑器,基于 ASM(Java 字节码操作框架) 来修改字节码,可简化编辑已编译 Java 应用程序的过程。
## 数据库
@@ -42,8 +42,8 @@ icon: tool
## ZooKeeper
-- [PrettyZoo](https://github.com/vran-dev/PrettyZoo) : 一个基于 Apache Curator 和 JavaFX 实现的 ZooKeeper 图形化管理客户端,颜值非常高,支持 Mac / Windows / Linux 。你可以使用 PrettyZoo 来实现对 ZooKeeper 的可视化增删改查。
-- [zktools](https://zktools.readthedocs.io/en/latest/#installing) : 一个低延迟的 ZooKeeper 图形化管理客户端,颜值非常高,支持 Mac / Windows / Linux 。你可以使用 zktools 来实现对 ZooKeeper 的可视化增删改查。
+- [PrettyZoo](https://github.com/vran-dev/PrettyZoo) :一个基于 Apache Curator 和 JavaFX 实现的 ZooKeeper 图形化管理客户端,颜值非常高,支持 Mac / Windows / Linux 。你可以使用 PrettyZoo 来实现对 ZooKeeper 的可视化增删改查。
+- [zktools](https://zktools.readthedocs.io/en/latest/#installing) :一个低延迟的 ZooKeeper 图形化管理客户端,颜值非常高,支持 Mac / Windows / Linux 。你可以使用 zktools 来实现对 ZooKeeper 的可视化增删改查。
## Markdown
diff --git a/docs/open-source-project/tutorial.md b/docs/open-source-project/tutorial.md
index 5da7aea3b06..9774a1a5ec3 100644
--- a/docs/open-source-project/tutorial.md
+++ b/docs/open-source-project/tutorial.md
@@ -14,7 +14,7 @@ icon: "book"
- **[technology-talk](https://github.com/aalansehaiyang/technology-talk)** : 汇总 java 生态圈常用技术框架、开源中间件,系统架构、数据库、大公司架构案例、常用三方类库、项目管理、线上问题排查、个人成长、思考等知识
- **[JCSprout](https://github.com/crossoverJie/JCSprout "JCSprout")** :处于萌芽阶段的 Java 核心知识库。
- **[bestJavaer](https://github.com/crisxuan/bestJavaer)** : 这是一个成为更好的 Java 程序员的系列教程。
-- **[java-design-patterns](https://github.com/iluwatar/java-design-patterns "java-design-patterns")** : 用 Java 实现的设计模式。
+- **[java-design-patterns](https://github.com/iluwatar/java-design-patterns "java-design-patterns")** :用 Java 实现的设计模式。
## 计算机基础
@@ -28,7 +28,7 @@ icon: "book"
- **[springboot-guide](https://github.com/Snailclimb/springboot-guide)** :SpringBoot 核心知识点总结。 基于 Spring Boot 2.19+。
- **[SpringAll](https://github.com/wuyouzhuguli/SpringAll "SpringAll")** :循序渐进,学习 Spring Boot、Spring Boot & Shiro、Spring Cloud、Spring Security & Spring Security OAuth2,博客 Spring 系列源码。
-- **[Springboot-Notebook](https://github.com/chengxy-nds/Springboot-Notebook)** :一系列以 Spring Boot 为基础开发框架,整合 Redis 、 Rabbitmq 、ES 、MongoDB 、Spring Cloud、Kafka、Skywalking 等互联网主流技术,实现各种常见功能点的综合性案例。
+- **[Springboot-Notebook](https://github.com/chengxy-nds/Springboot-Notebook)** :一系列以 Spring Boot 为基础开发框架,整合 Redis、 Rabbitmq、ES、MongoDB、Spring Cloud、Kafka、Skywalking 等互联网主流技术,实现各种常见功能点的综合性案例。
- **[springboot-learning-example](https://github.com/JeffLi1993/springboot-learning-example "springboot-learning-example")** :Spring Boot 实践学习案例,是 Spring Boot 初学者及核心技术巩固的最佳实践。
- **[spring-boot-demo](https://github.com/xkcoding/spring-boot-demo "spring-boot-demo")** :spring boot demo 是一个用来深度学习并实战 spring boot 的项目,目前总共包含 63 个集成 demo,已经完成 52 个。
- **[SpringBoot-Labs](https://github.com/YunaiV/SpringBoot-Labs)** :Spring Boot 系列教程。
diff --git a/docs/snippets/planet.snippet.md b/docs/snippets/planet.snippet.md
index 2e03fd156a6..1284e56da20 100644
--- a/docs/snippets/planet.snippet.md
+++ b/docs/snippets/planet.snippet.md
@@ -14,7 +14,7 @@
**我有自己的原则,不割韭菜,用心做内容,真心希望帮助到你!**
-如果你感兴趣的话,不妨花 3 分钟左右看看星球的详细介绍: [JavaGuide 知识星球详细介绍](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html)。
+如果你感兴趣的话,不妨花 3 分钟左右看看星球的详细介绍:[JavaGuide 知识星球详细介绍](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html)。
## 如何加入?
diff --git a/docs/system-design/basis/naming.md b/docs/system-design/basis/naming.md
index 00a8aa0955b..9b27c6f85fe 100644
--- a/docs/system-design/basis/naming.md
+++ b/docs/system-design/basis/naming.md
@@ -152,9 +152,9 @@ void shouldGet200StatusCodeWhenRequestIsValid() {
**4、包名统一使用小写,尽量使用单个名词作为包名,各个单词通过 "." 分隔符连接,并且各个单词必须为单数。**
-正例: `org.apache.dubbo.common.threadlocal`
+正例:`org.apache.dubbo.common.threadlocal`
-反例: ~~`org.apache_dubbo.Common.threadLocals`~~
+反例:~~`org.apache_dubbo.Common.threadLocals`~~
**5、抽象类命名使用 Abstract 开头**。
@@ -209,7 +209,7 @@ POJO 类中布尔类型的变量,都不要加 is 前缀,否则部分框架
**4、避免命名过长(50 个字符以内最好),过长的命名难以阅读并且丑陋。**
-**5、不要使用拼音,更不要使用中文。** 不过像 alibaba 、wuhan、taobao 这种国际通用名词可以当做英文来看待。
+**5、不要使用拼音,更不要使用中文。** 不过像 alibaba、wuhan、taobao 这种国际通用名词可以当做英文来看待。
正例:discount
diff --git a/docs/system-design/basis/refactoring.md b/docs/system-design/basis/refactoring.md
index f3a50f994d7..05d3753c365 100644
--- a/docs/system-design/basis/refactoring.md
+++ b/docs/system-design/basis/refactoring.md
@@ -14,7 +14,7 @@ category: 代码质量
> - 重构(名词):对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。
> - 重构(动词):使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。
-用更贴近工程师的语言来说: **重构就是利用设计模式(如组合模式、策略模式、责任链模式)、软件设计原则(如 SOLID 原则、YAGNI 原则、KISS 原则)和重构手段(如封装、继承、构建测试体系)来让代码更容易理解,更易于修改。**
+用更贴近工程师的语言来说:**重构就是利用设计模式(如组合模式、策略模式、责任链模式)、软件设计原则(如 SOLID 原则、YAGNI 原则、KISS 原则)和重构手段(如封装、继承、构建测试体系)来让代码更容易理解,更易于修改。**
软件设计原则指导着我们组织和规范代码,同时,重构也是为了能够尽量设计出尽量满足软件设计原则的软件。
@@ -38,7 +38,7 @@ category: 代码质量
如果对应到一个真实的项目,重构具体能为我们带来什么好处呢?
-1. **让代码更容易理解** : 通过添加注释、命名规范、逻辑优化等手段可以让我们的代码更容易被理解;
+1. **让代码更容易理解** :通过添加注释、命名规范、逻辑优化等手段可以让我们的代码更容易被理解;
2. **避免代码腐化** :通过重构干掉坏味道代码;
3. **加深对代码的理解** :重构代码的过程会加深你对某部分代码的理解;
4. **发现潜在 bug** :是这样的,很多潜在的 bug ,都是我们在重构的过程中发现的;
@@ -134,7 +134,7 @@ Code Review 可以非常有效提高代码的整体质量,它会帮助我们
- [重构实战练习](https://linesh.gitbook.io/refactoring/) :通过几个小案例一步一步带你学习重构!
- [设计模式+重构学习网站](https://refactoringguru.cn/) :免费在线学习代码重构、 设计模式、 SOLID 原则 (单一职责、 开闭原则、 里氏替换、 接口隔离以及依赖反转) 。
-- [IDEA 官方文档的代码重构教程](https://www.jetbrains.com/help/idea/refactoring-source-code.html#popular-refactorings) : 教你如何使用 IDEA 进行重构。
+- [IDEA 官方文档的代码重构教程](https://www.jetbrains.com/help/idea/refactoring-source-code.html#popular-refactorings) :教你如何使用 IDEA 进行重构。
## 参考
diff --git a/docs/system-design/basis/software-engineering.md b/docs/system-design/basis/software-engineering.md
index ac1cf672a04..f5c17b20a71 100644
--- a/docs/system-design/basis/software-engineering.md
+++ b/docs/system-design/basis/software-engineering.md
@@ -32,7 +32,7 @@ Dijkstra(Dijkstra 算法的作者) 在 1972 年图灵奖获奖感言中也
> 软件开发过程(英语:software development process),或软件过程(英语:software process),是软件开发的开发生命周期(software development life cycle),其各个阶段实现了软件的需求定义与分析、设计、实现、测试、交付和维护。软件过程是在开发与构建系统时应遵循的步骤,是软件开发的路线图。
- 需求分析 :分析用户的需求,建立逻辑模型。
-- 软件设计 : 根据需求分析的结果对软件架构进行设计。
+- 软件设计 :根据需求分析的结果对软件架构进行设计。
- 编码 :编写程序运行的源代码。
- 测试 : 确定测试用例,编写测试报告。
- 交付 :将做好的软件交付给客户。
@@ -54,7 +54,7 @@ Dijkstra(Dijkstra 算法的作者) 在 1972 年图灵奖获奖感言中也
> **敏捷开发** 是一种以人为核心、迭代、循序渐进的开发方法。在敏捷开发中,软件项目的构建被切分成多个子项目,各个子项目的成果都经过测试,具备集成和可运行的特征。换言之,就是把一个大项目分为多个相互联系,但也可独立运行的小项目,并分别完成,在此过程中软件一直处于可使用状态。
-像现在比较常见的一些概念比如 **持续集成** 、**重构** 、**小版本发布** 、**低文档** 、**站会** 、**结对编程** 、**测试驱动开发** 都是敏捷开发的核心。
+像现在比较常见的一些概念比如 **持续集成**、**重构**、**小版本发布**、**低文档**、**站会**、**结对编程**、**测试驱动开发** 都是敏捷开发的核心。
## 软件开发的基本策略
diff --git a/docs/system-design/framework/mybatis/mybatis-interview.md b/docs/system-design/framework/mybatis/mybatis-interview.md
index dc55430c7b1..8bc6482146c 100644
--- a/docs/system-design/framework/mybatis/mybatis-interview.md
+++ b/docs/system-design/framework/mybatis/mybatis-interview.md
@@ -28,13 +28,13 @@ head:
注:这道题是京东面试官面试我时问的。
-答:还有很多其他的标签, `` 、 `` 、 `` 、 `` 、 `` ,加上动态 sql 的 9 个标签, `trim|where|set|foreach|if|choose|when|otherwise|bind` 等,其中 `` 为 sql 片段标签,通过 `` 标签引入 sql 片段, `` 为不支持自增的主键生成策略标签。
+答:还有很多其他的标签, ``、 ``、 ``、 ``、 `` ,加上动态 sql 的 9 个标签, `trim|where|set|foreach|if|choose|when|otherwise|bind` 等,其中 `` 为 sql 片段标签,通过 `` 标签引入 sql 片段, `` 为不支持自增的主键生成策略标签。
### Dao 接口的工作原理是什么?Dao 接口里的方法,参数不同时,方法能重载吗?
注:这道题也是京东面试官面试我被问的。
-答:最佳实践中,通常一个 xml 映射文件,都会写一个 Dao 接口与之对应。Dao 接口就是人们常说的 `Mapper` 接口,接口的全限名,就是映射文件中的 namespace 的值,接口的方法名,就是映射文件中 `MappedStatement` 的 id 值,接口方法内的参数,就是传递给 sql 的参数。 `Mapper` 接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为 key 值,可唯一定位一个 `MappedStatement` ,举例: `com.mybatis3.mappers. StudentDao.findStudentById` ,可以唯一找到 namespace 为 `com.mybatis3.mappers. StudentDao` 下面 `id = findStudentById` 的 `MappedStatement` 。在 MyBatis 中,每一个 `