From 92f53527909c505df63830aea1f602c91dbf329a Mon Sep 17 00:00:00 2001
From: tyson
Date: Sun, 30 Oct 2022 17:28:42 +0800
Subject: [PATCH 01/75] update1030
---
README.md | 154 ++--
...76\350\256\241\346\250\241\345\274\217.md" | 714 ++++++++++++++----
.../docker-overview.md" | 0
.../git-overview.md" | 0
"\345\267\245\345\205\267/linux-overview.md" | 647 ++++++++++++++++
.../maven-overview.md" | 0
.../RabbitMQ.md" | 103 +--
.../kafka.md" | 168 +++++
8 files changed, 1505 insertions(+), 281 deletions(-)
rename "\345\267\245\345\205\267/docker.md" => "\345\267\245\345\205\267/docker-overview.md" (100%)
rename "\345\267\245\345\205\267/progit2.md" => "\345\267\245\345\205\267/git-overview.md" (100%)
create mode 100644 "\345\267\245\345\205\267/linux-overview.md"
rename "\345\267\245\345\205\267/Maven\345\256\236\346\210\230.md" => "\345\267\245\345\205\267/maven-overview.md" (100%)
create mode 100644 "\346\266\210\346\201\257\351\230\237\345\210\227/kafka.md"
diff --git a/README.md b/README.md
index 21e4c06..3ea05f6 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,14 @@
[点击此处](https://zhuanlan.zhihu.com/p/395162772) 查看我的**自学路线**。
+# 面试网站
+
+大彬自己搭建了一个**小破站**,将**本仓库所有的面试题**都整理到小破站了,欢迎大家访问~
+
+网站地址:https://topjavaer.cn
+
+
+
# 仓库简介
**本仓库用于分享互联网大厂高频面试题、Java核心知识总结,包括Java基础、并发、MySQL、Springboot、MyBatis、Redis、RabbitMQ等等,面试必备!**
@@ -30,6 +38,7 @@
+
# 简历很重要
[简历投递之后总是石沉大海?](https://zhuanlan.zhihu.com/p/406982597)
@@ -42,25 +51,6 @@
- [Java优质项目推荐](https://www.zhihu.com/question/325011850/answer/2257046656)
- [优质视频教程推荐](https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247487149&idx=1&sn=aa883c9f020945d3f210550bd688c7d0&chksm=ce98f3ebf9ef7afdae0b37c4d0751806b0fbbf08df783fba536e5ec20ec6a6e1512198dc6206&token=104697471&lang=zh_CN#rd)(推荐 :+1:)
-# 大厂面试系列
-
-1. [字节跳动一面面经](https://mp.weixin.qq.com/s/RH-SunzjqUTTx8HWaCmCcw)
-2. [别再问我Java List八股文了!](https://mp.weixin.qq.com/s/doyy_GYGWoH_YHgyMijStA)
-3. [腾讯面试,你真的懂HTTP吗?](https://mp.weixin.qq.com/s/kC7XRBfO7Z5hZcX6Dz2viw)
-4. [美团二面面经,最后竟然有惊喜?](https://mp.weixin.qq.com/s/3HvOtTU29HGALqmeeOZNWw)
-5. [Java多线程,被面试官难倒了!](https://mp.weixin.qq.com/s/tv8pOLaS6hpwgbKOB9w0Zw)
-6. [京东二面,Redis为什么这么快?](https://mp.weixin.qq.com/s/S3vN5T9HpziRd2s5ysLaSg)
-7. [MySQL索引,给我整不会了!](https://mp.weixin.qq.com/s/Q5CrDlNInpnckJaBQSrA7w)
-8. [别再问我Java虚拟机八股文了!](https://mp.weixin.qq.com/s/npo5-VqQt5sqZiSwPv6LVw)
-9. [计算机网络,问傻了!](https://mp.weixin.qq.com/s/WXcMLa_tdxpRLhO4U8LHIQ)
-10. [Spring这几道题你会吗?](https://mp.weixin.qq.com/s/DtgYRFfOQxQdtQosCU-6aw)
-10. [面向对象编程?](https://mp.weixin.qq.com/s/M8jDnLat61YAbM1-jIhJIA)
-10. [Java内功深厚?](https://mp.weixin.qq.com/s/v_kWSHX9GMS_aoqfwUMKsg)
-10. [面试初级Java开发,面试官问我MySQL架构?](https://mp.weixin.qq.com/s/JvDZCk4IecmaEYfFsRhpjQ)
-10. [手写红黑树?](https://mp.weixin.qq.com/s/yznh_IfMg4hWqU62U-t9GQ)
-10. [面完阿里,直接入职!](https://mp.weixin.qq.com/s/49QJ1FzaGTe-_54PT8_8jA)
-10. [华为面经](https://mp.weixin.qq.com/s/KmjwoG7pNvAHiX1UNnef6g)
-
# Java
## 基础
@@ -86,20 +76,23 @@
1. [【大厂面试】—— MySQL高频面试题50道](数据库/MySQL高频面试题.md)(**知乎1k+收藏,推荐** :+1:)
2. [图解索引下推](https://mp.weixin.qq.com/s/W1XQYmihtSdbLWQKeNwZvQ)(推荐 :+1:)
-3. [MySQL执行计划](数据库/MySQL执行计划.md)(推荐 :+1:)
+3. [MySQL执行计划详解](数据库/MySQL执行计划.md)(推荐 :+1:)
## Redis
1. [【大厂面试】——Redis30问](Redis/Redis面试题.md)(牛客高赞,推荐 :+1:)
-2. [Redis分布式锁(推荐 :+1:)](Redis/Redis分布式锁.md)
-4. [缓存穿透、缓存雪崩、缓存击穿](Redis/缓存穿透、缓存雪崩、缓存击穿.md)
+2. [缓存穿透、缓存雪崩、缓存击穿](Redis/缓存穿透、缓存雪崩、缓存击穿.md)
+
+## ElasticSearch
+
+1. [ElasticSearch高频面试题](https://mp.weixin.qq.com/s/Ffb8NDgavf9QAWYBm0qAVg)
# 框架
## Spring
1. [Spring高频面试题30道](框架/Spring面试题.md)(推荐 :+1:)
-3. [Spring用到哪些设计模式?](框架/Spring用到哪些设计模式.md)
+2. [Spring用到哪些设计模式?](框架/Spring用到哪些设计模式.md)
## Spring Boot
@@ -121,60 +114,116 @@
## SpringCloud
+[[SpringCloud面试题](https://topjavaer.cn/framework/springcloud-interview.html)(推荐 :+1:)
+
[SpringCloud总结](框架/SpringCloud微服务实战.md)
## Netty
[Netty实战笔记](框架/netty实战.md)
+# 分布式
+
+## 微服务
+
+[微服务面试题](https://topjavaer.cn/distributed/micro-service.html)
+
+## RPC
+
+[RPC面试题](https://topjavaer.cn/distributed/rpc.html)
+
+## 全局唯一ID
+
+[全局唯一ID](https://topjavaer.cn/distributed/global-unique-id.html)
+
+## 分布式事务
+
+[分布式事务总结](https://topjavaer.cn/distributed/distributed-transaction.html)
+
+## 分布式架构
+
+[分布式架构演进](https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247490543&idx=1&sn=ee34bee96511d5e548381e0576f8b484&chksm=ce98e6a9f9ef6fbf7db9c2b6d2fed26853a3bc13a50c3228ab57bea55afe0772008cdb1f957b&token=1594696656&lang=zh_CN#rd)
+
+# 高并发
+
+## 限流
+
+[限流算法总结](https://topjavaer.cn/advance/concurrent/1-current-limiting.html)
+
+## 负载均衡
+
+[负载均衡](https://topjavaer.cn/advance/concurrent/2-load-balance.html)
+
# 消息队列
## RabbitMQ
1. [消息队列面试题](消息队列/消息队列面试题.md)
-1. [RabbitMQ核心知识总结](消息队列/RabbitMQ.md) (推荐 :+1:)
-2. [死信队列](消息队列/死信队列.md)
+2. [RabbitMQ面试题总结](消息队列/rabbitmq.md) (推荐 :+1:)
+3. [Kafka面试题总结](消息队列/kafka.md) (推荐 :+1:)
+4. [死信队列](消息队列/死信队列.md)
# 计算机网络
[【大厂面试】—— 计算机网络常见面试题总结](计算机基础/网络/计算机网络高频面试题.md) (**知乎1k+收藏!推荐 :+1:**)
-[session和cookie详解](计算机基础/网络/session和cookie.md)
-
# 数据结构与算法
[如何高效的刷LeetCode?](https://www.zhihu.com/question/280279208/answer/2377906738)
-[7种常见的排序算法Java代码实现](计算机基础/数据结构与算法/常见的排序算法Java代码实现.md)
+[120道Leetcode题解(高频)](https://topjavaer.cn/leetcode/hot120/)
-[二叉树前序、中序、后序、层序遍历代码实现](计算机基础/数据结构与算法/二叉树前序、中序、后序、层序遍历代码实现.md)
-
-[常见数据结构总结](计算机基础/数据结构与算法/数据结构.md)
+[常见数据结构总结](https://topjavaer.cn/computer-basic/data-structure.html)
# 设计模式
[字节跳动大佬总结的设计模式PDF](https://t.1yb.co/y96J)
-[设计模式总结](其他/设计模式.md)
+[设计模式总结](https://topjavaer.cn/advance/design-pattern/)
+
+# 海量数据场景题
+
+1. [统计不同电话号码的个数](https://topjavaer.cn/system-design/1-scan-code-login.html)
+2. [出现频率最高的100个词](https://topjavaer.cn/mass-data/2-find-hign-frequency-word.html)
+3. [查找两个大文件共同的URL](https://topjavaer.cn/mass-data/3-find-same-url.html)
+4. [如何在100亿的数据中找到中位数](https://topjavaer.cn/mass-data/4-find-mid-num.html)
+5. [找出最热门的查询串](https://topjavaer.cn/mass-data/5-find-hot-string.html)
+6. [如何找出排名前500的数字](https://topjavaer.cn/mass-data/6-top-500-num.html)
# 工具
-[Git 超详细总结!](工具/progit2.md)(推荐 :+1:)
+[Git 超详细总结!](工具/git-overview.md)(推荐 :+1:)
-# 其他精选文章
+[Linux 常用命令总结!](工具/linux-overview.md)
-1. [记一次OOM问题排查](https://mp.weixin.qq.com/s/40C1OdtfGJSrecBoL9Mjbg)
-2. [推荐一款画图神器](https://mp.weixin.qq.com/s/vDtWku41rpeux8ZhZyA81w)
-3. [有哪些值得推荐的Java书籍?](https://mp.weixin.qq.com/s/MUB07APbyDH2-iftGDdGlA)
-4. [这款MarkDown神器,要收费啦!](https://mp.weixin.qq.com/s/aCEi-4vb3kNPF0-E4rB5Kg)
-5. [在B站学Java](https://mp.weixin.qq.com/s/_xfT2Tr6rT8qSAFrtRQ3wQ)
-6. [你见过这样使用Github的吗?](https://mp.weixin.qq.com/s/U_AItugf8KaUPyIun_xagg)
+[Docker 基础总结!](工具/docker-overview.md)
+[Maven 基础总结!](tools/maven-overview.md)
+# 大厂面试系列
-**本仓库持续更新中,欢迎star和pr~**
+1. [字节跳动一面面经](https://mp.weixin.qq.com/s/RH-SunzjqUTTx8HWaCmCcw)
+2. [别再问我Java List八股文了!](https://mp.weixin.qq.com/s/doyy_GYGWoH_YHgyMijStA)
+3. [腾讯面试,你真的懂HTTP吗?](https://mp.weixin.qq.com/s/kC7XRBfO7Z5hZcX6Dz2viw)
+4. [美团二面面经,最后竟然有惊喜?](https://mp.weixin.qq.com/s/3HvOtTU29HGALqmeeOZNWw)
+5. [Java多线程,被面试官难倒了!](https://mp.weixin.qq.com/s/tv8pOLaS6hpwgbKOB9w0Zw)
+6. [京东二面,Redis为什么这么快?](https://mp.weixin.qq.com/s/S3vN5T9HpziRd2s5ysLaSg)
+7. [MySQL索引,给我整不会了!](https://mp.weixin.qq.com/s/Q5CrDlNInpnckJaBQSrA7w)
+8. [别再问我Java虚拟机八股文了!](https://mp.weixin.qq.com/s/npo5-VqQt5sqZiSwPv6LVw)
+9. [计算机网络,问傻了!](https://mp.weixin.qq.com/s/WXcMLa_tdxpRLhO4U8LHIQ)
+10. [Spring这几道题你会吗?](https://mp.weixin.qq.com/s/DtgYRFfOQxQdtQosCU-6aw)
+11. [面向对象编程?](https://mp.weixin.qq.com/s/M8jDnLat61YAbM1-jIhJIA)
+12. [Java内功深厚?](https://mp.weixin.qq.com/s/v_kWSHX9GMS_aoqfwUMKsg)
+13. [面试初级Java开发,面试官问我MySQL架构?](https://mp.weixin.qq.com/s/JvDZCk4IecmaEYfFsRhpjQ)
+14. [手写红黑树?](https://mp.weixin.qq.com/s/yznh_IfMg4hWqU62U-t9GQ)
+15. [面完阿里,直接入职!](https://mp.weixin.qq.com/s/49QJ1FzaGTe-_54PT8_8jA)
+16. [华为面经](https://mp.weixin.qq.com/s/KmjwoG7pNvAHiX1UNnef6g)
+
+# 其他优质文章
+[优质文章汇总,持续更新中~](https://topjavaer.cn/advance/excellent-article/)
+
# 交流
@@ -185,24 +234,27 @@
+
# 赞赏
如果觉得**本仓库**对您有帮助的话,可以请大彬**喝一杯咖啡**(小伙伴们赞赏的时候可以备注下哦~)
-| 微信 | 支付宝 |
-| ----------------------------------------------------------- | ------------------------------------------------------------ |
+| 微信 | 支付宝 |
+| ------------------------------------------------- | ----------------------------------------------------- |
|  |  |
每笔赞赏我会在下面记录下来,感谢你们,我会更加努力,砥砺前行~
-| 日期 | 来源 | **用户** | **金额** | 备注 |
-| ---------- | ------------ | -------- | -------- | ------ |
-| 2021.11.19 | 微信收款码 | *张 | 6.66元 | 支持! |
-| 2021.11.25 | 支付宝收款码 | *海 | 1元 | |
-| 2021.12.10 | 微信收款码 | 浩*y | 10元 | |
-| 2021.12.15 | 微信收款码 | biubiu* | 6.66元 | 好 |
-| 2022.02.17 | 微信收款码 | *齐 | 8元 | |
-| 2022.05.03 | 微信收款码 | *哈 | 2元 | |
+| 日期 | 来源 | **用户** | **金额** | 备注 |
+| ---------- | ------------ | -------- | -------- | ------------------------ |
+| 2021.11.19 | 微信收款码 | *张 | 6.66元 | 支持! |
+| 2021.11.25 | 支付宝收款码 | *海 | 1元 | |
+| 2021.12.10 | 微信收款码 | 浩*y | 10元 | |
+| 2021.12.15 | 微信收款码 | biubiu* | 6.66元 | 好 |
+| 2022.02.17 | 微信收款码 | *齐 | 8元 | |
+| 2022.05.03 | 微信收款码 | *哈 | 2元 | |
+| 2022.06.12 | 微信收款码 | *可 | 8.8元 | |
+| 2022.10.19 | 微信收款码 | *斌 | 10元 | 支持一下,希望能持续更新 |
diff --git "a/\345\205\266\344\273\226/\350\256\276\350\256\241\346\250\241\345\274\217.md" "b/\345\205\266\344\273\226/\350\256\276\350\256\241\346\250\241\345\274\217.md"
index 3808e3a..ebbd8e1 100644
--- "a/\345\205\266\344\273\226/\350\256\276\350\256\241\346\250\241\345\274\217.md"
+++ "b/\345\205\266\344\273\226/\350\256\276\350\256\241\346\250\241\345\274\217.md"
@@ -1,27 +1,3 @@
-
-
-
-
-- [单例模式](#%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F)
- - [饿汉模式](#%E9%A5%BF%E6%B1%89%E6%A8%A1%E5%BC%8F)
- - [双重检查锁定](#%E5%8F%8C%E9%87%8D%E6%A3%80%E6%9F%A5%E9%94%81%E5%AE%9A)
- - [静态内部类](#%E9%9D%99%E6%80%81%E5%86%85%E9%83%A8%E7%B1%BB)
-- [装饰模式](#%E8%A3%85%E9%A5%B0%E6%A8%A1%E5%BC%8F)
-- [适配器模式](#%E9%80%82%E9%85%8D%E5%99%A8%E6%A8%A1%E5%BC%8F)
-- [观察者模式](#%E8%A7%82%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F)
-- [代理模式](#%E4%BB%A3%E7%90%86%E6%A8%A1%E5%BC%8F)
- - [静态代理](#%E9%9D%99%E6%80%81%E4%BB%A3%E7%90%86)
- - [动态代理](#%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86)
- - [JDK动态代理](#jdk%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86)
-- [工厂模式](#%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F)
- - [简单工厂模式](#%E7%AE%80%E5%8D%95%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F)
- - [工厂方法模式](#%E5%B7%A5%E5%8E%82%E6%96%B9%E6%B3%95%E6%A8%A1%E5%BC%8F)
- - [抽象工厂模式](#%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F)
-- [模板模式](#%E6%A8%A1%E6%9D%BF%E6%A8%A1%E5%BC%8F)
-- [建造者模式](#%E5%BB%BA%E9%80%A0%E8%80%85%E6%A8%A1%E5%BC%8F)
-
-
-
## 设计模式的六大原则
- 开闭原则:对扩展开放,对修改关闭,多使用抽象类和接口。
@@ -37,6 +13,10 @@
### 饿汉模式
+JVM在类的初始化阶段,会执行类的静态方法。在执行类的初始化期间,JVM会去获取Class对象的锁。这个锁可以同步多个线程对同一个类的初始化。
+
+饿汉模式只在类加载的时候创建一次实例,没有多线程同步的问题。单例没有用到也会被创建,而且在类加载之后就被创建,内存就被浪费了。
+
```
public class Singleton {
private static Singleton instance = new Singleton();
@@ -46,12 +26,10 @@ public class Singleton {
}
}
```
-JVM在类的初始化阶段,会执行类的静态方法。在执行类的初始化期间,JVM会去获取Class对象的锁。这个锁可以同步多个线程对同一个类的初始化。
-
-饿汉模式只在类加载的时候创建一次实例,没有多线程同步的问题。单例没有用到也会被创建,而且在类加载之后就被创建,内存就被浪费了。
-
### 双重检查锁定
+双重校验锁先判断 instance 是否已经被实例化,如果没有被实例化,那么才对实例化语句进行加锁。
+
instance使用static修饰的原因:getInstance为静态方法,因为静态方法的内部不能直接使用非静态变量,只有静态成员才能在没有创建对象时进行初始化,所以返回的这个实例必须是静态的。
```
@@ -104,6 +82,12 @@ instance = memory; // 3:设置instance指向刚分配的内存地址
### 静态内部类
+它与饿汉模式一样,也是利用了类初始化机制,因此不存在多线程并发的问题。不一样的是,它是在内部类里面去创建对象实例。这样的话,只要应用中不使用内部类,JVM就不会去加载这个单例类,也就不会创建单例对象,从而实现懒汉式的延迟加载。也就是说这种方式可以同时保证延迟加载和线程安全。
+
+
+
+基于类初始化的方案的实现代码更简洁。
+
```
public class Instance {
private static class InstanceHolder {
@@ -115,62 +99,564 @@ public class Instance {
}
}
```
-它与饿汉模式一样,也是利用了类初始化机制,因此不存在多线程并发的问题。不一样的是,它是在内部类里面去创建对象实例。这样的话,只要应用中不使用内部类,JVM就不会去加载这个单例类,也就不会创建单例对象,从而实现懒汉式的延迟加载。也就是说这种方式可以同时保证延迟加载和线程安全。
+但基于volatile的双重检查锁定的方案有一个额外的优势:除了可以对静态字段实现延迟初始化外,还可以对实例字段实现延迟初始化。字段延迟初始化降低了初始化类或创建实例的开销,但增加了访问被延迟初始化的字段的开销。在大多数时候,正常的初始化要优于延迟初始化。
+
+## 工厂模式
+
+工厂模式是用来封装对象的创建。
+
+### 简单工厂模式
+
+把实例化的操作单独放到一个类中,这个类就成为简单工厂类,让简单工厂类来决定应该用哪个具体子类来实例化,这样做能把客户类和具体子类的实现解耦,客户类不再需要知道有哪些子类以及应当实例化哪个子类。
+
+```java
+ public class ShapeFactory {
+ public static final String TAG = "ShapeFactory";
+ public static Shape getShape(String type) {
+ Shape shape = null;
+ if (type.equalsIgnoreCase("circle")) {
+ shape = new CircleShape();
+ } else if (type.equalsIgnoreCase("rect")) {
+ shape = new RectShape();
+ } else if (type.equalsIgnoreCase("triangle")) {
+ shape = new TriangleShape();
+ }
+ return shape;
+ }
+ }
+```
+
+优点:只需要一个工厂创建对象,代码量少。
+
+缺点:系统扩展困难,新增产品需要修改工厂逻辑,当产品较多时,会造成工厂逻辑过于复杂,不利于系统扩展和维护。
+
+### 工厂方法模式
+
+针对不同的对象提供不同的工厂。每个对象都有一个与之对应的工厂。
+
+```java
+public interface Reader {
+ void read();
+}
+
+public class JpgReader implements Reader {
+ @Override
+ public void read() {
+ System.out.print("read jpg");
+ }
+}
+
+public class PngReader implements Reader {
+ @Override
+ public void read() {
+ System.out.print("read png");
+ }
+}
+
+public interface ReaderFactory {
+ Reader getReader();
+}
+
+public class JpgReaderFactory implements ReaderFactory {
+ @Override
+ public Reader getReader() {
+ return new JpgReader();
+ }
+}
+
+public class PngReaderFactory implements ReaderFactory {
+ @Override
+ public Reader getReader() {
+ return new PngReader();
+ }
+}
+```
+
+客户端通过子类来指定创建对应的对象。
+
+```java
+ReaderFactory factory=new JpgReaderFactory();
+Reader reader=factory.getReader();
+reader.read();
+```
+
+优点:增加新的产品类时无须修改现有系统,只需增加新产品和对应的工厂类即可。
+
+### 抽象工厂模式
+
+抽象工厂模式创建的是对象家族,也就是很多对象而不是一个对象,并且这些对象是相关的,也就是说必须一起创建出来。而工厂方法模式只是用于创建一个对象,这和抽象工厂模式有很大不同。
+
+多了一层抽象,减少了工厂的数量(HpMouseFactory和HpKeyboFactory合并为HpFactory)。
+
+
+
+
+
+## 模板模式
+
+模板模式:一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。 这种类型的设计模式属于行为型模式。定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
+
+**模板模式**主要由抽象模板(Abstract Template)角色和具体模板(Concrete Template)角色组成。
+
+- 抽象模板(Abstract Template): 定义了一个或多个抽象操作,以便让子类实现。这些抽象操作叫做基本操作,它们是一个顶级逻辑的组成步骤;定义并实现了一个模板方法。这个模板方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。顶级逻辑也有可能调用一些具体方法。
+- 具体模板(Concrete Template): 实现父类所定义的一个或多个抽象方法,它们是一个顶级逻辑的组成步骤;每一个抽象模板角色都可以有任意多个具体模板角色与之对应,而每一个具体模板角色都可以给出这些抽象方法(也就是顶级逻辑的组成步骤)的不同实现,从而使得顶级逻辑的实现各不相同。
+
+示例图如下:
+
+
+
+以游戏为例。创建一个抽象类,它的模板方法被设置为 final,这样它就不会被重写。
+
+```java
+public abstract class Game {
+ abstract void initialize();
+ abstract void startPlay();
+ abstract void endPlay();
+
+ //模板
+ public final void play(){
+ //初始化游戏
+ initialize();
+
+ //开始游戏
+ startPlay();
+
+ //结束游戏
+ endPlay();
+ }
+}
+```
+
+Football类:
+
+```java
+public class Football extends Game {
+
+ @Override
+ void endPlay() {
+ System.out.println("Football Game Finished!");
+ }
+
+ @Override
+ void initialize() {
+ System.out.println("Football Game Initialized! Start playing.");
+ }
+
+ @Override
+ void startPlay() {
+ System.out.println("Football Game Started. Enjoy the game!");
+ }
+}
+```
+
+使用Game的模板方法 play() 来演示游戏的定义方式。
+
+```java
+public class TemplatePatternDemo {
+ public static void main(String[] args) {
+
+ Game game = new Cricket();
+ game.play();
+ System.out.println();
+ game = new Football();
+ game.play();
+ }
+}
+```
+
+**模板模式优点** :
+
+1. 封装不变部分,扩展可变部分。
+2. 提取公共代码,便于维护。
+3. 行为由父类控制,子类实现。
+
+**模板模式缺点**:
+
+- 每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
+
+## 策略模式
+
+策略模式(Strategy Pattern)属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。
+
+其主要目的是通过定义相似的算法,替换if else 语句写法,并且可以随时相互替换。
+
+**策略模式**主要由这三个角色组成,环境角色(Context)、抽象策略角色(Strategy)和具体策略角色(ConcreteStrategy)。
+
+- 环境角色(Context):持有一个策略类的引用,提供给客户端使用。
+- 抽象策略角色(Strategy):这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
+- 具体策略角色(ConcreteStrategy):包装了相关的算法或行为。
+
+示例图如下:
+
+
+
+以计算器为例,如果我们想得到两个数字相加的和,我们需要用到“+”符号,得到相减的差,需要用到“-”符号等等。虽然我们可以通过字符串比较使用if/else写成通用方法,但是计算的符号每次增加,我们就不得不加在原先的方法中进行增加相应的代码,如果后续计算方法增加、修改或删除,那么会使后续的维护变得困难。
+
+但是在这些方法中,我们发现其基本方法是固定的,这时我们就可以通过策略模式来进行开发,可以有效避免通过if/else来进行判断,即使后续增加其他的计算规则也可灵活进行调整。
+
+首先定义一个抽象策略角色,并拥有一个计算的方法。
+
+```java
+interface CalculateStrategy {
+ int doOperation(int num1, int num2);
+}
+```
+
+然后再定义加减乘除这些具体策略角色并实现方法。代码如下:
+
+```java
+class OperationAdd implements CalculateStrategy {
+ @Override
+ public int doOperation(int num1, int num2) {
+ return num1 + num2;
+ }
+}
+
+class OperationSub implements CalculateStrategy {
+ @Override
+ public int doOperation(int num1, int num2) {
+ return num1 - num2;
+ }
+}
+
+class OperationMul implements CalculateStrategy {
+ @Override
+ public int doOperation(int num1, int num2) {
+ return num1 * num2;
+ }
+}
+
+class OperationDiv implements CalculateStrategy {
+ @Override
+ public int doOperation(int num1, int num2) {
+ return num1 / num2;
+ }
+}
+```
+
+最后在定义一个环境角色,提供一个计算的接口供客户端使用。代码如下:
+
+```java
+class CalculatorContext {
+ private CalculateStrategy strategy;
+
+ public CalculatorContext(CalculateStrategy strategy) {
+ this.strategy = strategy;
+ }
+
+ public int executeStrategy(int num1, int num2) {
+ return strategy.doOperation(num1, num2);
+ }
+}
+```
+
+测试代码如下:
+
+```java
+public static void main(String[] args) {
+ int a=4,b=2;
+ CalculatorContext context = new CalculatorContext(new OperationAdd());
+ System.out.println("a + b = "+context.executeStrategy(a, b));
+
+ CalculatorContext context2 = new CalculatorContext(new OperationSub());
+ System.out.println("a - b = "+context2.executeStrategy(a, b));
+
+ CalculatorContext context3 = new CalculatorContext(new OperationMul());
+ System.out.println("a * b = "+context3.executeStrategy(a, b));
+
+ CalculatorContext context4 = new CalculatorContext(new OperationDiv());
+ System.out.println("a / b = "+context4.executeStrategy(a, b));
+}
+```
+
+**策略模式优点:**
+
+- 扩展性好,可以在不修改对象结构的情况下,为新的算法进行添加新的类进行实现;
+- 灵活性好,可以对算法进行自由切换;
+
+**策略模式缺点:**
+
+- 使用策略类变多,会增加系统的复杂度。;
+- 客户端必须知道所有的策略类才能进行调用;
+
+**使用场景:**
+
+- 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为;
+ 一个系统需要动态地在几种算法中选择一种;
+- 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现;
+
+## 责任链模式
+
+为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
+
+在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,请求会自动进行传递。所以责任链将请求的发送者和请求的处理者解耦了。
+
+**责任链模式是一种对象行为型模式,其主要优点如下。**
+
+1. 降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
+2. 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
+3. 增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
+4. 责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
+5. 责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
+
+代码实现如下:
+
+请假条类
+
+```java
+public class LeaveRequest {
+ //姓名
+ private String name;
+ // 请假天数
+ private int num;
+ // 请假内容
+ private String content;
+
+ public LeaveRequest(String name, int num, String content) {
+ this.name = name;
+ this.num = num;
+ this.content = content;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getNum() {
+ return num;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+}
+```
+
+处理类
+
+```java
+public abstract class Handler {
+ protected final static int NUM_ONE = 1;
+ protected final static int NUM_THREE = 3;
+ protected final static int NUM_SEVEN = 7;
+
+ //该领导处理的请假天数区间
+ private int numStart;
+ private int numEnd;
+
+
+ //领导上还有领导
+ private Handler nextHandler;
+
+ //设置请假天数范围
+ public Handler(int numStart) {
+ this.numStart = numStart;
+ }
+
+ //设置请假天数范围
+ public Handler(int numStart, int numEnd) {
+ this.numStart = numStart;
+ this.numEnd = numEnd;
+ }
+
+ //设置上级领导
+ public void setNextHandler(Handler nextHandler) {
+ this.nextHandler = nextHandler;
+ }
+
+ //提交请假条
+ public final void submit(LeaveRequest leaveRequest) {
+ if (this.numStart == 0) {
+ return;
+ }
+ //请假天数达到领导处理要求
+ if (leaveRequest.getNum() >= this.numStart) {
+ this.handleLeave(leaveRequest);
+
+ //如果还有上级 并且请假天数超过当前领导的处理范围
+ if (this.nextHandler != null && leaveRequest.getNum() > numEnd) {
+ //继续提交
+ this.nextHandler.submit(leaveRequest);
+ } else {
+ System.out.println("流程结束!!!");
+ }
+ }
+ }
+
+ //各级领导处理请假条方法
+ protected abstract void handleLeave(LeaveRequest leave);
+
+}
+```
+
+小组长类
+
+```java
+public class GroupLeader extends Handler {
+ //1-3天的假
+ public GroupLeader() {
+ super(Handler.NUM_ONE, Handler.NUM_THREE);
+ }
+
+ @Override
+ protected void handleLeave(LeaveRequest leave) {
+ System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "!");
+ System.out.println("小组长审批通过:同意!");
+ }
+}
+```
+
+部门经理类
+
+```java
+public class Manager extends Handler {
+ //3-7天的假
+ public Manager() {
+ super(Handler.NUM_THREE, Handler.NUM_SEVEN);
+ }
+
+ @Override
+ protected void handleLeave(LeaveRequest leave) {
+ System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "!");
+ System.out.println("部门经理审批通过:同意!");
+ }
+}
+```
+
+总经理类
+
+```java
+public class GeneralManager extends Handler{
+ //7天以上的假
+ public GeneralManager() {
+ super(Handler.NUM_THREE, Handler.NUM_SEVEN);
+ }
+
+ @Override
+ protected void handleLeave(LeaveRequest leave) {
+ System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "!");
+ System.out.println("总经理审批通过:同意!");
+ }
+}
+```
+
+测试类
+
+```java
+public class Client {
+ public static void main(String[] args) {
+ //请假条
+ LeaveRequest leave = new LeaveRequest("小庄", 3, "出去旅游");
+
+ //各位领导
+ Manager manager = new Manager();
+ GroupLeader groupLeader = new GroupLeader();
+ GeneralManager generalManager = new GeneralManager();
+
+ /*
+ * 小组长上司是经理 经理上司是总经理
+ */
+ groupLeader.setNextHandler(manager);
+ manager.setNextHandler(generalManager);
+
+ //提交
+ groupLeader.submit(leave);
+
+ }
+}
+```
+
+应用场景:
+
+1. 多个对象可以处理一个请求,但具体由哪个对象处理该请求在运行时自动确定。
+2. 可动态指定一组对象处理请求,或添加新的处理者。
+3. 需要在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。
+
+[参考链接](https://segmentfault.com/a/1190000040450513)
+
+## 迭代器模式
+
+提供一种方法顺序访问一个聚合对象中的各个元素, 而又不暴露其内部的表示。
+
+把在元素之间游走的责任交给迭代器,而不是聚合对象。
-
+**应用实例:**JAVA 中的 iterator。
-基于类初始化的方案的实现代码更简洁。但基于volatile的双重检查锁定的方案有一个额外的优势:除了可以对静态字段实现延迟初始化外,还可以对实例字段实现延迟初始化。字段延迟初始化降低了初始化类或创建实例的开销,但增加了访问被延迟初始化的字段的开销。在大多数时候,正常的初始化要优于延迟初始化。
+**优点:** 1、它支持以不同的方式遍历一个聚合对象。 2、迭代器简化了聚合类。 3、在同一个聚合上可以有多个遍历。 4、在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。
-[参考:单例模式](https://blog.csdn.net/goodlixueyong/article/details/51935526)
+**缺点:**由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
+
+**使用场景:** 1、访问一个聚合对象的内容而无须暴露它的内部表示。 2、需要为聚合对象提供多种遍历方式。 3、为遍历不同的聚合结构提供一个统一的接口。
+
+**迭代器模式在JDK中的应用**
+
+ArrayList的遍历:
+
+```java
+Iterator iter = null;
+
+System.out.println("ArrayList:");
+iter = arrayList.iterator();
+while (iter.hasNext()) {
+ System.out.print(iter.next() + "\t");
+}
+```
## 装饰模式
+
+装饰者模式(decorator pattern):动态地将责任附加到对象上, 若要扩展功能, 装饰者提供了比继承更有弹性的替代方案。
+
装饰模式以对客户端透明的方式拓展对象的功能,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不创造更多子类的情况下,将对象的功能加以扩展。
+比如设置FileInputStream,先用BufferedInputStream装饰它,再用自己写的LowerCaseInputStream过滤器去装饰它。
+
```
InputStream in = new LowerCaseInputStream(
new BufferedInputStream(
new FileInputStream("test.txt")));
```
-设置FileInputStream,先用BufferedInputStream装饰它,再用自己写的LowerCaseInputStream过滤器去装饰它。
-
在装饰模式中的角色有:
-抽象组件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
-具体组件(ConcreteComponent)角色:定义一个将要接收附加责任的类。
-装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
-具体装饰(ConcreteDecorator)角色:负责给构件对象“贴上”附加的责任。
-
+
+- 抽象组件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
+- 具体组件(ConcreteComponent)角色:定义一个将要接收附加责任的类。
+- 装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
+- 具体装饰(ConcreteDecorator)角色:负责给构件对象“贴上”附加的责任。
## 适配器模式
-将现成的对象通过适配变成我们需要的接口。
+适配器模式将现成的对象通过适配变成我们需要的接口。 适配器让原本接口不兼容的类可以合作。
+
适配器模式有类的适配器模式和对象的适配器模式两种不同的形式。
对象适配器模式通过组合对象进行适配。
-
+
类适配器通过继承来完成适配。
-
+
-适配器模式的优点
+适配器模式的**优点**:
1. 更好的复用性。系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。
2. 更好的扩展性。在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。
## 观察者模式
+
+定义对象之间的一对多依赖,当一个对象状态改变时,它的所有依赖都会收到通知并且自动更新状态。
+
+主题(Subject)是被观察的对象,而其所有依赖者(Observer)称为观察者。
+
多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。其作用是让主题对象和观察者松耦合。
观察者模式所涉及的角色有:
-抽象主题(Subject)角色:抽象主题角色把所有对观察者对象的引用保存在一个聚集(比如ArrayList对象)里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,抽象主题角色又叫做抽象被观察者(Observable)角色。
-具体主题(ConcreteSubject)角色:将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者(Concrete Observable)角色。
-抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在得到主题的通知时更新自己,这个接口叫做更新接口。
-具体观察者(ConcreteObserver)角色:存储与主题的状态自恰的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。如果需要,具体观察者角色可以保持一个指向具体主题对象的引用。
-Observer接口 Observable类
-被观察者类都是java.util.Observable类的子类。
-
-[观察者模式](https://www.cnblogs.com/luohanguo/p/7825656.html)
+
+- 抽象主题(Subject)角色:抽象主题角色把所有对观察者对象的引用保存在一个聚集(比如ArrayList对象)里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,抽象主题角色又叫做抽象被观察者(Observable)角色。
+- 具体主题(ConcreteSubject)角色:将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者(Concrete Observable)角色。
+- 抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在得到主题的通知时更新自己,这个接口叫做更新接口。
+- 具体观察者(ConcreteObserver)角色:存储与主题的状态自恰的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。如果需要,具体观察者角色可以保持一个指向具体主题对象的引用。
@@ -188,7 +674,7 @@ Observer接口 Observable类
动态代理:代理类在程序运行时创建,在内存中临时生成一个代理对象,在运行期间对业务方法进行增强。
-#### JDK动态代理
+**JDK动态代理**
JDK实现代理只需要使用newProxyInstance方法:
@@ -196,9 +682,13 @@ JDK实现代理只需要使用newProxyInstance方法:
static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h )
```
-ClassLoader loader:指定当前目标对象使用的类加载器
-Class>[] interfaces:目标对象实现的接口的类型
-InvocationHandler h:当代理对象调用目标对象的方法时,会触发事件处理器的invoke方法()
+三个入参:
+
+- ClassLoader loader:指定当前目标对象使用的类加载器
+- Class>[] interfaces:目标对象实现的接口的类型
+- InvocationHandler:当代理对象调用目标对象的方法时,会触发事件处理器的invoke方法()
+
+示例代码:
```
public class DynamicProxyDemo {
@@ -247,119 +737,11 @@ class MyInvacationHandler implements InvocationHandler {
}
```
+## 建造者模式
+建造者模式:封装一个对象的构造过程,并允许按步骤构造。
-## 工厂模式
-工厂模式是用来封装对象的创建。
-
-### 简单工厂模式
-
-只有一个工厂类。适用于需要创建的对象较少的场景。
-
-```java
- public class ShapeFactory {
- public static final String TAG = "ShapeFactory";
- public static Shape getShape(String type) {
- Shape shape = null;
- if (type.equalsIgnoreCase("circle")) {
- shape = new CircleShape();
- } else if (type.equalsIgnoreCase("rect")) {
- shape = new RectShape();
- } else if (type.equalsIgnoreCase("triangle")) {
- shape = new TriangleShape();
- }
- return shape;
- }
- }
-```
-
-优点:只需要一个工厂创建对象,代码量少。
-
-缺点:系统扩展困难,新增产品需要修改工厂逻辑,当产品较多时,会造成工厂逻辑过于复杂,不利于系统扩展和维护。
-
-### 工厂方法模式
-
-针对不同的对象提供不同的工厂。每个对象都有一个与之对应的工厂。
-
-```java
-public interface Reader {
- void read();
-}
-
-public class JpgReader implements Reader {
- @Override
- public void read() {
- System.out.print("read jpg");
- }
-}
-
-public class PngReader implements Reader {
- @Override
- public void read() {
- System.out.print("read png");
- }
-}
-
-public interface ReaderFactory {
- Reader getReader();
-}
-
-public class JpgReaderFactory implements ReaderFactory {
- @Override
- public Reader getReader() {
- return new JpgReader();
- }
-}
-
-public class PngReaderFactory implements ReaderFactory {
- @Override
- public Reader getReader() {
- return new PngReader();
- }
-}
-```
-
-客户端通过子类来指定创建对应的对象。
-
-```java
-ReaderFactory factory=new JpgReaderFactory();
-Reader reader=factory.getReader();
-reader.read();
-```
-
-优点:增加新的产品类时无须修改现有系统,只需增加新产品和对应的工厂类即可。
-
-### 抽象工厂模式
-
-多了一层抽象,减少了工厂的数量(HpMouseFactory和HpKeyboFactory合并为HpFactory)。
-
-
-
-
-## 模板模式
-
-在抽象类中定义一个操作中算法的骨架,子类按照需要重写方法实现。
-
-```java
-public abstract class DodishTemplate {
- //模板
- protected void dodish(){
- this.preparation();
- this.doing();
- this.carriedDishes();
- }
-
- public abstract void preparation();
-
- public abstract void doing();
-
- public abstract void carriedDishes ();
-}
-```
-
-
-
-## 建造者模式
+有两种形式:传统建造者模式和传统建造者模式变种。
传统建造者模式:
diff --git "a/\345\267\245\345\205\267/docker.md" "b/\345\267\245\345\205\267/docker-overview.md"
similarity index 100%
rename from "\345\267\245\345\205\267/docker.md"
rename to "\345\267\245\345\205\267/docker-overview.md"
diff --git "a/\345\267\245\345\205\267/progit2.md" "b/\345\267\245\345\205\267/git-overview.md"
similarity index 100%
rename from "\345\267\245\345\205\267/progit2.md"
rename to "\345\267\245\345\205\267/git-overview.md"
diff --git "a/\345\267\245\345\205\267/linux-overview.md" "b/\345\267\245\345\205\267/linux-overview.md"
new file mode 100644
index 0000000..f3cd728
--- /dev/null
+++ "b/\345\267\245\345\205\267/linux-overview.md"
@@ -0,0 +1,647 @@
+# 基本操作
+
+## Linux关机,重启
+
+```
+#关机
+shutdown -h now
+
+#重启
+shutdown -r now
+```
+
+## 查看系统,CPU信息
+
+```
+#查看系统内核信息
+uname -a
+
+#查看系统内核版本
+cat /proc/version
+
+#查看当前用户环境变量
+env
+
+cat /proc/cpuinfo
+
+#查看有几个逻辑cpu, 包括cpu型号
+cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c
+
+#查看有几颗cpu,每颗分别是几核
+cat /proc/cpuinfo | grep physical | uniq -c
+
+#查看当前CPU运行在32bit还是64bit模式下, 如果是运行在32bit下也不代表CPU不支持64bit
+getconf LONG_BIT
+
+#结果大于0, 说明支持64bit计算. lm指long mode, 支持lm则是64bit
+cat /proc/cpuinfo | grep flags | grep ' lm ' | wc -l
+```
+
+## 建立软连接
+
+```
+ln -s /usr/local/jdk1.8/ jdk
+```
+
+## rpm相关
+
+```
+#查看是否通过rpm安装了该软件
+rpm -qa | grep 软件名
+```
+
+## sshkey
+
+```
+#创建sshkey
+ssh-keygen -t rsa -C your_email@example.com
+
+#id_rsa.pub 的内容拷贝到要控制的服务器的 home/username/.ssh/authorized_keys 中,如果没有则新建(.ssh权限为700, authorized_keys权限为600)
+```
+
+## 命令重命名
+
+```
+#在各个用户的.bash_profile中添加重命名配置
+alias ll='ls -alF'
+```
+
+## 同步服务器时间
+
+```
+sudo ntpdate -u ntp.api.bz
+```
+
+## 后台运行命令
+
+```
+#后台运行,并且有nohup.out输出
+nohup xxx &
+
+#后台运行, 不输出任何日志
+nohup xxx > /dev/null &
+
+#后台运行, 并将错误信息做标准输出到日志中
+nohup xxx >out.log 2>&1 &
+```
+
+## 强制活动用户退出
+
+```
+#命令来完成强制活动用户退出.其中TTY表示终端名称
+pkill -kill -t [TTY]
+```
+
+## 查看命令路径
+
+```
+which <命令>
+```
+
+## 查看进程所有打开最大fd数
+
+```
+ulimit -n
+```
+
+## 配置dns
+
+```
+vim /etc/resolv.conf
+```
+
+## nslookup,查看域名路由表
+
+```
+nslookup google.com
+```
+
+## last, 最近登录信息列表
+
+```
+#最近登录的5个账号
+last -n 5
+```
+
+## 设置固定ip
+
+```
+ifconfig em1 192.168.5.177 netmask 255.255.255.0
+```
+
+## 查看进程内加载的环境变量
+
+```
+#也可以去 cd /proc 目录下, 查看进程内存中加载的东西
+ps eww -p XXXXX(进程号)
+```
+
+## 查看进程树找到服务器进程
+
+```
+ps auwxf
+```
+
+## 查看进程启动路径
+
+```
+cd /proc/xxx(进程号)
+ls -all
+#cwd对应的是启动路径
+```
+
+## 添加用户, 配置sudo权限
+
+```
+#新增用户
+useradd 用户名
+passwd 用户名
+
+#增加sudo权限
+vim /etc/sudoers
+#修改文件里面的
+#root ALL=(ALL) ALL
+#用户名 ALL=(ALL) ALL
+```
+
+## 强制关闭进程名包含xxx的所有进程
+
+```
+ps aux|grep xxx | grep -v grep | awk '{print $2}' | xargs kill -9
+```
+
+# 磁盘,文件,目录相关操作
+
+## vim操作
+
+```
+#normal模式下 g表示全局, x表示查找的内容, y表示替换后的内容
+:%s/x/y/g
+
+#normal模式下
+0 #光标移到行首(数字0)
+$ #光标移至行尾
+shift + g #跳到文件最后
+gg #跳到文件头
+
+#显示行号
+:set nu
+
+#去除行号
+:set nonu
+
+#检索
+/xxx(检索内容) #从头检索, 按n查找下一个
+?xxx(检索内容) #从尾部检索
+```
+
+## 打开只读文件,修改后需要保存时(不用切换用户即可保存的方式)
+
+```
+#在normal模式下
+:w !sudo tee %
+```
+
+## 查看磁盘, 文件目录基本信息
+
+```
+#查看磁盘挂载情况
+mount
+
+#查看磁盘分区信息
+df
+
+#查看目录及子目录大小
+du -H -h
+
+#查看当前目录下各个文件, 文件夹占了多少空间, 不会递归
+du -sh *
+```
+
+## wc命令
+
+```
+#查看文件里有多少行
+wc -l filename
+
+#看文件里有多少个word
+wc -w filename
+
+#文件里最长的那一行是多少个字
+wc -L filename
+
+#统计字节数
+wc -c
+```
+
+## 常用压缩, 解压缩命令
+
+### 压缩命令
+
+```
+tar czvf xxx.tar 压缩目录
+
+zip -r xxx.zip 压缩目录
+```
+
+### 解压缩命令
+
+```
+tar zxvf xxx.tar
+
+#解压到指定文件夹
+tar zxvf xxx.tar -C /xxx/yyy/
+
+unzip xxx.zip
+```
+
+## 变更文件所属用户, 用户组
+
+```
+chown eagleye.eagleye xxx.log
+```
+
+## cp, scp, mkdir
+
+```
+#复制
+cp xxx.log
+
+#复制并强制覆盖同名文件
+cp -f xxx.log
+
+#复制文件夹
+cp -r xxx(源文件夹) yyy(目标文件夹)
+
+#远程复制
+scp -P ssh端口 username@10.10.10.101:/home/username/xxx /home/xxx
+
+#级联创建目录
+mkdir -p /xxx/yyy/zzz
+
+#批量创建文件夹, 会在test,main下都创建java, resources文件夹
+mkdir -p src/{test,main}/{java,resources}
+```
+
+## 比较两个文件
+
+```
+diff -u 1.txt 2.txt
+```
+
+## 日志输出的字节数,可以用作性能测试
+
+```
+#如果做性能测试, 可以每执行一次, 往日志里面输出 “.” , 这样日志中的字节数就是实际的性能测试运行的次数, 还可以看见实时速率.
+tail -f xxx.log | pv -bt
+```
+
+## 查看, 去除特殊字符
+
+```
+#查看特殊字符
+cat -v xxx.sh
+
+#去除特殊字符
+sed -i 's/^M//g’ env.sh 去除文件的特殊字符, 比如^M: 需要这样输入: ctrl+v+enter
+```
+
+## 处理因系统原因引起的文件中特殊字符的问题
+
+```
+#可以转换为该系统下的文件格式
+cat file.sh > file.sh_bak
+
+#先将file.sh中文件内容复制下来然后运行, 然后粘贴内容, 最后ctrl + d 保存退出
+cat > file1.sh
+
+#在vim中通过如下设置文件编码和文件格式
+:set fileencodings=utf-8 ,然后 w (存盘)一下即可转化为 utf8 格式,
+:set fileformat=unix
+
+#在mac下使用dos2unix进行文件格式化
+find . -name "*.sh" | xargs dos2unix
+```
+
+## tee, 重定向的同时输出到屏幕
+
+```
+awk ‘{print $0}’ xxx.log | tee test.log
+```
+
+# 检索相关
+
+## grep
+
+```
+#反向匹配, 查找不包含xxx的内容
+grep -v xxx
+
+#排除所有空行
+grep -v '^/pre>
+
+#返回结果 2,则说明第二行是空行
+grep -n “^$” 111.txt
+
+#查询以abc开头的行
+grep -n “^abc” 111.txt
+
+#同时列出该词语出现在文章的第几行
+grep 'xxx' -n xxx.log
+
+#计算一下该字串出现的次数
+grep 'xxx' -c xxx.log
+
+#比对的时候,不计较大小写的不同
+grep 'xxx' -i xxx.log
+```
+
+## awk
+
+```
+#以':' 为分隔符,如果第五域有user则输出该行
+awk -F ':' '{if ($5 ~ /user/) print $0}' /etc/passwd
+
+#统计单个文件中某个字符(串)(中文无效)出现的次数
+awk -v RS='character' 'END {print --NR}' xxx.txt
+```
+
+## find检索命令
+
+```
+#在目录下找后缀是.mysql的文件
+find /home/eagleye -name '*.mysql' -print
+
+#会从 /usr 目录开始往下找,找最近3天之内存取过的文件。
+find /usr -atime 3 –print
+
+#会从 /usr 目录开始往下找,找最近5天之内修改过的文件。
+find /usr -ctime 5 –print
+
+#会从 /doc 目录开始往下找,找jacky 的、文件名开头是 j的文件。
+find /doc -user jacky -name 'j*' –print
+
+#会从 /doc 目录开始往下找,找寻文件名是 ja 开头或者 ma开头的文件。
+find /doc \( -name 'ja*' -o- -name 'ma*' \) –print
+
+#会从 /doc 目录开始往下找,找到凡是文件名结尾为 bak的文件,把它删除掉。-exec 选项是执行的意思,rm 是删除命令,{ } 表示文件名,“\;”是规定的命令结尾。
+find /doc -name '*bak' -exec rm {} \;
+```
+
+# 网络相关
+
+## 查看什么进程使用了该端口
+
+```
+lsof -i:port
+```
+
+## 获取本机ip地址
+
+```
+/sbin/ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6|awk '{print $2}'|tr -d "addr:"
+```
+
+## iptables
+
+```
+#查看iptables状态
+service iptables status
+
+#要封停一个ip
+iptables -I INPUT -s ***.***.***.*** -j DROP
+
+#要解封一个IP,使用下面这条命令:
+iptables -D INPUT -s ***.***.***.*** -j DROP
+
+备注: 参数-I是表示Insert(添加),-D表示Delete(删除)。后面跟的是规则,INPUT表示入站,***.***.***.***表示要封停的IP,DROP表示放弃连接。
+
+#开启9090端口的访问
+/sbin/iptables -I INPUT -p tcp --dport 9090 -j ACCEPT
+
+#防火墙开启、关闭、重启
+/etc/init.d/iptables status
+/etc/init.d/iptables start
+/etc/init.d/iptables stop
+/etc/init.d/iptables restart
+```
+
+## nc命令, tcp调试利器
+
+```
+#给某一个endpoint发送TCP请求,就将data的内容发送到对端
+nc 192.168.0.11 8000 < data.txt
+
+#nc可以当做服务器,监听某个端口号,把某一次请求的内容存储到received_data里
+nc -l 8000 > received_data
+
+#上边只监听一次,如果多次可以加上-k参数
+nc -lk 8000
+```
+
+## tcpdump
+
+```
+#dump出本机12301端口的tcp包
+tcpdump -i em1 tcp port 12301 -s 1500 -w abc.pcap
+```
+
+## 跟踪网络路由路径
+
+```
+#traceroute默认使用udp方式, 如果是-I则改成icmp方式
+traceroute -I www.163.com
+
+#从ttl第3跳跟踪
+traceroute -M 3 www.163.com
+
+#加上端口跟踪
+traceroute -p 8080 192.168.10.11
+```
+
+## ss
+
+```
+#显示本地打开的所有端口
+ss -l
+
+#显示每个进程具体打开的socket
+ss -pl
+
+#显示所有tcp socket
+ss -t -a
+
+#显示所有的UDP Socekt
+ss -u -a
+
+#显示所有已建立的SMTP连接
+ss -o state established '( dport = :smtp or sport = :smtp )'
+
+#显示所有已建立的HTTP连接
+ss -o state established '( dport = :http or sport = :http )'
+
+找出所有连接X服务器的进程
+ss -x src /tmp/.X11-unix/*
+
+列出当前socket统计信息
+ss -s
+
+解释:netstat是遍历/proc下面每个PID目录,ss直接读/proc/net下面的统计信息。所以ss执行的时候消耗资源以及消耗的时间都比netstat少很多
+```
+
+## netstat
+
+```
+#输出每个ip的连接数,以及总的各个状态的连接数
+netstat -n | awk '/^tcp/ {n=split($(NF-1),array,":");if(n<=2)++S[array[(1)]];else++S[array[(4)]];++s[$NF];++N} END {for(a in S){printf("%-20s %s\n", a, S[a]);++I}printf("%-20s %s\n","TOTAL_IP",I);for(a in s) printf("%-20s %s\n",a, s[a]);printf("%-20s %s\n","TOTAL_LINK",N);}'
+
+#统计所有连接状态,
+#CLOSED:无连接是活动的或正在进行
+#LISTEN:服务器在等待进入呼叫
+#SYN_RECV:一个连接请求已经到达,等待确认
+#SYN_SENT:应用已经开始,打开一个连接
+#ESTABLISHED:正常数据传输状态
+#FIN_WAIT1:应用说它已经完成
+#FIN_WAIT2:另一边已同意释放
+#ITMED_WAIT:等待所有分组死掉
+#CLOSING:两边同时尝试关闭
+#TIME_WAIT:主动关闭连接一端还没有等到另一端反馈期间的状态
+#LAST_ACK:等待所有分组死掉
+netstat -n | awk '/^tcp/ {++state[$NF]} END {for(key in state) print key,"\t",state[key]}'
+
+#查找较多time_wait连接
+netstat -n|grep TIME_WAIT|awk '{print $5}'|sort|uniq -c|sort -rn|head -n20
+```
+
+# 监控linux性能命令
+
+## top
+
+```
+按大写的 F 或 O 键,然后按 a-z 可以将进程按照相应的列进行排序, 然后回车。而大写的 R 键可以将当前的排序倒转
+```
+
+| 列名 | 含义 |
+| :------ | :----------------------------------------------------------- |
+| PID | 进程id |
+| PPID | 父进程id |
+| RUSER | Real user name |
+| UID | 进程所有者的用户id |
+| USER | 进程所有者的用户名 |
+| GROUP | 进程所有者的组名 |
+| TTY | 启动进程的终端名。不是从终端启动的进程则显示为 ? |
+| PR | 优先级 |
+| NI | nice值。负值表示高优先级,正值表示低优先级 |
+| P | 最后使用的CPU,仅在多CPU环境下有意义 |
+| %CPU | 上次更新到现在的CPU时间占用百分比 |
+| TIME | 进程使用的CPU时间总计,单位秒 |
+| TIME+ | 进程使用的CPU时间总计,单位1/100秒 |
+| %MEM | 进程使用的物理内存百分比 |
+| VIRT | 进程使用的虚拟内存总量,单位kb。VIRT=SWAP+RES |
+| SWAP | 进程使用的虚拟内存中,被换出的大小,单位kb。 |
+| RES | 进程使用的、未被换出的物理内存大小,单位kb。RES=CODE+DATA |
+| CODE | 可执行代码占用的物理内存大小,单位kb |
+| DATA | 可执行代码以外的部分(数据段+栈)占用的物理内存大小,单位kb |
+| SHR | 共享内存大小,单位kb |
+| nFLT | 页面错误次数 |
+| nDRT | 最后一次写入到现在,被修改过的页面数。 |
+| S | 进程状态。D=不可中断的睡眠状态,R=运行,S=睡眠,T=跟踪/停止,Z=僵尸进程 |
+| COMMAND | 命令名/命令行 |
+| WCHAN | 若该进程在睡眠,则显示睡眠中的系统函数名 |
+| Flags | 任务标志,参考 sched.h |
+
+## dmesg,查看系统日志
+
+```
+dmesg
+```
+
+## iostat,磁盘IO情况监控
+
+```
+iostat -xz 1
+
+#r/s, w/s, rkB/s, wkB/s:分别表示每秒读写次数和每秒读写数据量(千字节)。读写量过大,可能会引起性能问题。
+#await:IO操作的平均等待时间,单位是毫秒。这是应用程序在和磁盘交互时,需要消耗的时间,包括IO等待和实际操作的耗时。如果这个数值过大,可能是硬件设备遇到了瓶颈或者出现故障。
+#avgqu-sz:向设备发出的请求平均数量。如果这个数值大于1,可能是硬件设备已经饱和(部分前端硬件设备支持并行写入)。
+#%util:设备利用率。这个数值表示设备的繁忙程度,经验值是如果超过60,可能会影响IO性能(可以参照IO操作平均等待时间)。如果到达100%,说明硬件设备已经饱和。
+#如果显示的是逻辑设备的数据,那么设备利用率不代表后端实际的硬件设备已经饱和。值得注意的是,即使IO性能不理想,也不一定意味这应用程序性能会不好,可以利用诸如预读取、写缓存等策略提升应用性能。
+```
+
+## free,内存使用情况
+
+```
+free -m
+
+eg:
+
+ total used free shared buffers cached
+Mem: 1002 769 232 0 62 421
+-/+ buffers/cache: 286 715
+Swap: 1153 0 1153
+
+第一部分Mem行:
+total 内存总数: 1002M
+used 已经使用的内存数: 769M
+free 空闲的内存数: 232M
+shared 当前已经废弃不用,总是0
+buffers Buffer 缓存内存数: 62M
+cached Page 缓存内存数:421M
+
+关系:total(1002M) = used(769M) + free(232M)
+
+第二部分(-/+ buffers/cache):
+(-buffers/cache) used内存数:286M (指的第一部分Mem行中的used – buffers – cached)
+(+buffers/cache) free内存数: 715M (指的第一部分Mem行中的free + buffers + cached)
+
+可见-buffers/cache反映的是被程序实实在在吃掉的内存,而+buffers/cache反映的是可以挪用的内存总数.
+
+第三部分是指交换分区
+```
+
+## sar,查看网络吞吐状态
+
+```
+#sar命令在这里可以查看网络设备的吞吐率。在排查性能问题时,可以通过网络设备的吞吐量,判断网络设备是否已经饱和
+sar -n DEV 1
+
+#
+#sar命令在这里用于查看TCP连接状态,其中包括:
+#active/s:每秒本地发起的TCP连接数,既通过connect调用创建的TCP连接;
+#passive/s:每秒远程发起的TCP连接数,即通过accept调用创建的TCP连接;
+#retrans/s:每秒TCP重传数量;
+#TCP连接数可以用来判断性能问题是否由于建立了过多的连接,进一步可以判断是主动发起的连接,还是被动接受的连接。TCP重传可能是因为网络环境恶劣,或者服务器压力过大导致丢包
+sar -n TCP,ETCP 1
+```
+
+## vmstat, 给定时间监控CPU使用率, 内存使用, 虚拟内存交互, IO读写
+
+```
+#2表示每2秒采集一次状态信息, 1表示只采集一次(忽略既是一直采集)
+vmstat 2 1
+
+eg:
+r b swpd free buff cache si so bi bo in cs us sy id wa
+1 0 0 3499840 315836 3819660 0 0 0 1 2 0 0 0 100 0
+0 0 0 3499584 315836 3819660 0 0 0 0 88 158 0 0 100 0
+0 0 0 3499708 315836 3819660 0 0 0 2 86 162 0 0 100 0
+0 0 0 3499708 315836 3819660 0 0 0 10 81 151 0 0 100 0
+1 0 0 3499732 315836 3819660 0 0 0 2 83 154 0 0 100 0
+```
+
+- r 表示运行队列(就是说多少个进程真的分配到CPU),我测试的服务器目前CPU比较空闲,没什么程序在跑,当这个值超过了CPU数目,就会出现CPU瓶颈了。这个也和top的负载有关系,一般负载超过了3就比较高,超过了5就高,超过了10就不正常了,服务器的状态很危险。top的负载类似每秒的运行队列。如果运行队列过大,表示你的CPU很繁忙,一般会造成CPU使用率很高。
+- b 表示阻塞的进程,这个不多说,进程阻塞,大家懂的。
+- swpd 虚拟内存已使用的大小,如果大于0,表示你的机器物理内存不足了,如果不是程序内存泄露的原因,那么你该升级内存了或者把耗内存的任务迁移到其他机器。
+- free 空闲的物理内存的大小,我的机器内存总共8G,剩余3415M。
+- buff Linux/Unix系统是用来存储,目录里面有什么内容,权限等的缓存,我本机大概占用300多M
+- cache cache直接用来记忆我们打开的文件,给文件做缓冲,我本机大概占用300多M(这里是Linux/Unix的聪明之处,把空闲的物理内存的一部分拿来做文件和目录的缓存,是为了提高 程序执行的性能,当程序使用内存时,buffer/cached会很快地被使用。)
+- si 每秒从磁盘读入虚拟内存的大小,如果这个值大于0,表示物理内存不够用或者内存泄露了,要查找耗内存进程解决掉。我的机器内存充裕,一切正常。
+- so 每秒虚拟内存写入磁盘的大小,如果这个值大于0,同上。
+- bi 块设备每秒接收的块数量,这里的块设备是指系统上所有的磁盘和其他块设备,默认块大小是1024byte,我本机上没什么IO操作,所以一直是0,但是我曾在处理拷贝大量数据(2-3T)的机器上看过可以达到140000/s,磁盘写入速度差不多140M每秒
+- bo 块设备每秒发送的块数量,例如我们读取文件,bo就要大于0。bi和bo一般都要接近0,不然就是IO过于频繁,需要调整。
+- in 每秒CPU的中断次数,包括时间中断
+- cs 每秒上下文切换次数,例如我们调用系统函数,就要进行上下文切换,线程的切换,也要进程上下文切换,这个值要越小越好,太大了,要考虑调低线程或者进程的数目,例如在apache和nginx这种web服务器中,我们一般做性能测试时会进行几千并发甚至几万并发的测试,选择web服务器的进程可以由进程或者线程的峰值一直下调,压测,直到cs到一个比较小的值,这个进程和线程数就是比较合适的值了。系统调用也是,每次调用系统函数,我们的代码就会进入内核空间,导致上下文切换,这个是很耗资源,也要尽量避免频繁调用系统函数。上下文切换次数过多表示你的CPU大部分浪费在上下文切换,导致CPU干正经事的时间少了,CPU没有充分利用,是不可取的。
+- us 用户CPU时间,我曾经在一个做加密解密很频繁的服务器上,可以看到us接近100,r运行队列达到80(机器在做压力测试,性能表现不佳)。
+- sy 系统CPU时间,如果太高,表示系统调用时间长,例如是IO操作频繁。
+- id 空闲 CPU时间,一般来说,id + us + sy = 100,一般我认为id是空闲CPU使用率,us是用户CPU使用率,sy是系统CPU使用率。
+- wt 等待IO CPU时间。
\ No newline at end of file
diff --git "a/\345\267\245\345\205\267/Maven\345\256\236\346\210\230.md" "b/\345\267\245\345\205\267/maven-overview.md"
similarity index 100%
rename from "\345\267\245\345\205\267/Maven\345\256\236\346\210\230.md"
rename to "\345\267\245\345\205\267/maven-overview.md"
diff --git "a/\346\266\210\346\201\257\351\230\237\345\210\227/RabbitMQ.md" "b/\346\266\210\346\201\257\351\230\237\345\210\227/RabbitMQ.md"
index c89865f..6fcc798 100644
--- "a/\346\266\210\346\201\257\351\230\237\345\210\227/RabbitMQ.md"
+++ "b/\346\266\210\346\201\257\351\230\237\345\210\227/RabbitMQ.md"
@@ -1,41 +1,14 @@
-
-
-
-
-- [简介](#%E7%AE%80%E4%BB%8B)
- - [基本概念](#%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5)
- - [什么时候使用MQ](#%E4%BB%80%E4%B9%88%E6%97%B6%E5%80%99%E4%BD%BF%E7%94%A8mq)
- - [优缺点](#%E4%BC%98%E7%BC%BA%E7%82%B9)
-- [Exchange 类型](#exchange-%E7%B1%BB%E5%9E%8B)
- - [direct](#direct)
- - [fanout](#fanout)
- - [topic](#topic)
- - [headers](#headers)
-- [消息丢失](#%E6%B6%88%E6%81%AF%E4%B8%A2%E5%A4%B1)
- - [生产者确认机制](#%E7%94%9F%E4%BA%A7%E8%80%85%E7%A1%AE%E8%AE%A4%E6%9C%BA%E5%88%B6)
- - [路由不可达消息](#%E8%B7%AF%E7%94%B1%E4%B8%8D%E5%8F%AF%E8%BE%BE%E6%B6%88%E6%81%AF)
- - [Return消息机制](#return%E6%B6%88%E6%81%AF%E6%9C%BA%E5%88%B6)
- - [备份交换机](#%E5%A4%87%E4%BB%BD%E4%BA%A4%E6%8D%A2%E6%9C%BA)
- - [消费者手动消息确认](#%E6%B6%88%E8%B4%B9%E8%80%85%E6%89%8B%E5%8A%A8%E6%B6%88%E6%81%AF%E7%A1%AE%E8%AE%A4)
- - [持久化](#%E6%8C%81%E4%B9%85%E5%8C%96)
- - [镜像队列](#%E9%95%9C%E5%83%8F%E9%98%9F%E5%88%97)
-- [重复消费](#%E9%87%8D%E5%A4%8D%E6%B6%88%E8%B4%B9)
-- [消费端限流](#%E6%B6%88%E8%B4%B9%E7%AB%AF%E9%99%90%E6%B5%81)
-- [死信队列](#%E6%AD%BB%E4%BF%A1%E9%98%9F%E5%88%97)
-- [其他](#%E5%85%B6%E4%BB%96)
- - [pull模式](#pull%E6%A8%A1%E5%BC%8F)
- - [消息过期时间](#%E6%B6%88%E6%81%AF%E8%BF%87%E6%9C%9F%E6%97%B6%E9%97%B4)
-- [参考链接](#%E5%8F%82%E8%80%83%E9%93%BE%E6%8E%A5)
-
-
-
-# 简介
+---
+sidebar: heading
+---
+
+## 什么是RabbitMQ?
RabbitMQ是一个由erlang开发的消息队列。消息队列用于应用间的异步协作。

-## 基本概念
+## RabbitMQ的组件
Message:由消息头和消息体组成。消息体是不透明的,而消息头则由一系列的可选属性组成,这些属性包括routing-key、priority、delivery-mode(是否持久性存储)等。
@@ -57,15 +30,23 @@ Broker:消息队列服务器实体。
以常见的订单系统为例,用户点击下单按钮之后的业务逻辑可能包括:扣减库存、生成相应单据、发短信通知。这种场景下就可以用 MQ 。将短信通知放到 MQ 异步执行,在下单的主流程(比如扣减库存、生成相应单据)完成之后发送一条消息到 MQ, 让主流程快速完结,而由另外的线程消费MQ的消息。
-## 优缺点
+## RabbitMQ的优缺点
缺点:使用erlang实现,不利于二次开发和维护;性能较kafka差,持久化消息和ACK确认的情况下生产和消费消息单机吞吐量大约在1-2万左右,kafka单机吞吐量在十万级别。
优点:有管理界面,方便使用;可靠性高;功能丰富,支持消息持久化、消息确认机制、多种消息分发机制。
+## RabbitMQ 有哪些重要的角色?
+
+RabbitMQ 中重要的角色有:生产者、消费者和代理。
+
+1. 生产者:消息的创建者,负责创建和推送数据到消息服务器;
+2. 消费者:消息的接收方,用于处理数据和确认消息;
-# Exchange 类型
+3. 代理:就是 RabbitMQ 本身,用于扮演“快递”的角色,本身不生产消息,只是扮演“快递”的角色。
+
+## Exchange 类型
Exchange分发消息时根据类型的不同分发策略不同,目前共四种类型:direct、fanout、topic、headers 。headers 模式根据消息的headers进行路由,此外 headers 交换器和 direct 交换器完全一致,但性能差很多。
@@ -78,35 +59,35 @@ Exchange规则。
| topic | 模糊匹配 |
| headers | Exchange不依赖于routing key与binding key的匹配规则来路由消息,而是根据发送的消息内容中的header属性进行匹配。 |
-## direct
+**direct**
direct交换机会将消息路由到binding key 和 routing key完全匹配的队列中。它是完全匹配、单播的模式。

-## fanout
+**fanout**
所有发到 fanout 类型交换机的消息都会路由到所有与该交换机绑定的队列上去。fanout 类型转发消息是最快的。

-## topic
+**topic**
-topic交换机使用routing key和binding key进行模糊匹配,匹配成功则将消息发送到相应的队列。routing key和binding key都是句点号“. ”分隔的字符串,binding key中可以存在两种特殊字符“*”与“#”,用于做模糊匹配,其中“\*”用于匹配一个单词,“#”用于匹配多个单词。
+topic交换机使用routing key和binding key进行模糊匹配,匹配成功则将消息发送到相应的队列。routing key和binding key都是句点号“. ”分隔的字符串,binding key中可以存在两种特殊字符“*”与“##”,用于做模糊匹配,其中“\*”用于匹配一个单词,“##”用于匹配多个单词。

-## headers
+**headers**
headers交换机是根据发送的消息内容中的headers属性进行路由的。在绑定Queue与Exchange时指定一组键值对;当消息发送到Exchange时,RabbitMQ会取到该消息的headers(也是一个键值对的形式),对比其中的键值对是否完全匹配Queue与Exchange绑定时指定的键值对;如果完全匹配则消息会路由到该Queue,否则不会路由到该Queue。
-# 消息丢失
+## 消息丢失
消息丢失场景:生产者生产消息到RabbitMQ Server消息丢失、RabbitMQ Server存储的消息丢失和RabbitMQ Server到消费者消息丢失。
消息丢失从三个方面来解决:生产者确认机制、消费者手动确认消息和持久化。
-## 生产者确认机制
+### 生产者确认机制
生产者发送消息到队列,无法确保发送的消息成功的到达server。
@@ -120,7 +101,7 @@ headers交换机是根据发送的消息内容中的headers属性进行路由的
```yaml
spring:
rabbitmq:
- #开启 confirm 确认机制
+ ##开启 confirm 确认机制
publisher-confirms: true
```
@@ -139,20 +120,20 @@ final RabbitTemplate.ConfirmCallback confirmCallback = (CorrelationData correlat
rabbitTemplate.setConfirmCallback(confirmCallback);
```
-## 路由不可达消息
+### 路由不可达消息
生产者确认机制只确保消息正确到达交换机,对于从交换机路由到Queue失败的消息,会被丢弃掉,导致消息丢失。
对于不可路由的消息,有两种处理方式:Return消息机制和备份交换机。
-### Return消息机制
+**Return消息机制**
Return消息机制提供了回调函数 ReturnCallback,当消息从交换机路由到Queue失败才会回调这个方法。需要将`mandatory` 设置为 `true` ,才能监听到路由不可达的消息。
```yaml
spring:
rabbitmq:
- #触发ReturnCallback必须设置mandatory=true, 否则Exchange没有找到Queue就会丢弃掉消息, 而不会触发ReturnCallback
+ ##触发ReturnCallback必须设置mandatory=true, 否则Exchange没有找到Queue就会丢弃掉消息, 而不会触发ReturnCallback
template.mandatory: true
```
@@ -167,11 +148,11 @@ rabbitTemplate.setReturnCallback(returnCallback);
当消息从交换机路由到Queue失败时,会返回 `return exchange: , routingKey: MAIL, replyCode: 312, replyText: NO_ROUTE`。
-### 备份交换机
+**备份交换机**
备份交换机alternate-exchange 是一个普通的exchange,当你发送消息到对应的exchange时,没有匹配到queue,就会自动转移到备份交换机对应的queue,这样消息就不会丢失。
-## 消费者手动消息确认
+### 消费者手动消息确认
有可能消费者收到消息还没来得及处理MQ服务就宕机了,导致消息丢失。因为消息者默认采用自动ack,一旦消费者收到消息后会通知MQ Server这条消息已经处理好了,MQ 就会移除这条消息。
@@ -180,7 +161,7 @@ rabbitTemplate.setReturnCallback(returnCallback);
消费者设置手动ack:
```java
-#设置消费端手动 ack
+##设置消费端手动 ack
spring.rabbitmq.listener.simple.acknowledge-mode=manual
```
@@ -204,7 +185,7 @@ spring.rabbitmq.listener.simple.acknowledge-mode=manual
当消息消费失败时,消费端给broker回复nack,如果consumer设置了requeue为false,则nack后broker会删除消息或者进入死信队列,否则消息会重新入队。
-## 持久化
+### 持久化
如果RabbitMQ服务异常导致重启,将会导致消息丢失。RabbitMQ提供了持久化的机制,将内存中的消息持久化到硬盘上,即使重启RabbitMQ,消息也不会丢失。
@@ -216,15 +197,13 @@ spring.rabbitmq.listener.simple.acknowledge-mode=manual
当发布一条消息到交换机上时,Rabbit会先把消息写入持久化日志,然后才向生产者发送响应。一旦从队列中消费了一条消息的话并且做了确认,RabbitMQ会在持久化日志中移除这条消息。在消费消息前,如果RabbitMQ重启的话,服务器会自动重建交换机和队列,加载持久化日志中的消息到相应的队列或者交换机上,保证消息不会丢失。
-## 镜像队列
+### 镜像队列
当MQ发生故障时,会导致服务不可用。引入RabbitMQ的镜像队列机制,将queue镜像到集群中其他的节点之上。如果集群中的一个节点失效了,能自动地切换到镜像中的另一个节点以保证服务的可用性。
通常每一个镜像队列都包含一个master和多个slave,分别对应于不同的节点。发送到镜像队列的所有消息总是被直接发送到master和所有的slave之上。除了publish外所有动作都只会向master发送,然后由master将命令执行的结果广播给slave,从镜像队列中的消费操作实际上是在master上执行的。
-
-
-# 重复消费
+## 消息重复消费怎么处理?
消息重复的原因有两个:1.生产时消息重复,2.消费时消息重复。
@@ -238,9 +217,7 @@ spring.rabbitmq.listener.simple.acknowledge-mode=manual
2. 如果不存在,则正常消费,消费完毕后写入redis/db
3. 如果存在,则证明消息被消费过,直接丢弃
-
-
-# 消费端限流
+## 消费端怎么进行限流?
当 RabbitMQ 服务器积压大量消息时,队列里的消息会大量涌入消费端,可能导致消费端服务器奔溃。这种情况下需要对消费端限流。
@@ -249,7 +226,7 @@ Spring RabbitMQ 提供参数 prefetch 可以设置单个请求处理的消息个
开启消费端限流:
```properties
-#在单个请求中处理的消息个数,unack的最大数量
+##在单个请求中处理的消息个数,unack的最大数量
spring.rabbitmq.listener.simple.prefetch=2
```
@@ -261,7 +238,7 @@ spring.rabbitmq.listener.simple.prefetch=2
void basicQos(int prefetchSize, int prefetchCount, boolean global) throws IOException;
```
-# 死信队列
+## 什么是死信队列?
消费失败的消息存放的队列。
@@ -383,9 +360,7 @@ public class DeadListener {
当普通队列中有死信时,RabbitMQ 就会自动的将这个消息重新发布到设置的死信交换机去,然后被路由到死信队列。可以监听死信队列中的消息做相应的处理。
-# 其他
-
-## pull模式
+## 说说pull模式
pull模式主要是通过channel.basicGet方法来获取消息,示例代码如下:
@@ -395,7 +370,7 @@ System.out.println(new String(response.getBody()));
channel.basicAck(response.getEnvelope().getDeliveryTag(),false);
```
-## 消息过期时间
+## 怎么设置消息的过期时间?
在生产端发送消息的时候可以给消息设置过期时间,单位为毫秒(ms)
@@ -406,7 +381,7 @@ msg.getMessageProperties().setExpiration("3000");
也可以在创建队列的时候指定队列的ttl,从消息入队列开始计算,超过该时间的消息将会被移除。
-# 参考链接
+## 参考链接
[RabbitMQ基础](https://www.jianshu.com/p/79ca08116d57)
diff --git "a/\346\266\210\346\201\257\351\230\237\345\210\227/kafka.md" "b/\346\266\210\346\201\257\351\230\237\345\210\227/kafka.md"
new file mode 100644
index 0000000..5dd9255
--- /dev/null
+++ "b/\346\266\210\346\201\257\351\230\237\345\210\227/kafka.md"
@@ -0,0 +1,168 @@
+---
+sidebar: heading
+---
+
+
+
+## Kafka 都有哪些特点?
+
+- 高吞吐量、低延迟:kafka每秒可以处理几十万条消息,它的延迟最低只有几毫秒,每个topic可以分多个partition, consumer group 对partition进行consume操作。
+- 可扩展性:kafka集群支持热扩展
+- 持久性、可靠性:消息被持久化到本地磁盘,并且支持数据备份防止数据丢失
+- 容错性:允许集群中节点失败(若副本数量为n,则允许n-1个节点失败)
+- 高并发:支持数千个客户端同时读写
+
+## 请简述下你在哪些场景下会选择 Kafka?
+
+- 日志收集:一个公司可以用Kafka可以收集各种服务的log,通过kafka以统一接口服务的方式开放给各种consumer,例如hadoop、HBase、Solr等。
+- 消息系统:解耦和生产者和消费者、缓存消息等。
+- 用户活动跟踪:Kafka经常被用来记录web用户或者app用户的各种活动,如浏览网页、搜索、点击等活动,这些活动信息被各个服务器发布到kafka的topic中,然后订阅者通过订阅这些topic来做实时的监控分析,或者装载到hadoop、数据仓库中做离线分析和挖掘。
+- 运营指标:Kafka也经常用来记录运营监控数据。包括收集各种分布式应用的数据,生产各种操作的集中反馈,比如报警和报告。
+- 流式处理:比如spark streaming和 Flink
+
+## Kafka 的设计架构你知道吗?
+
+
+Kafka 架构分为以下几个部分:
+
+- Producer :消息生产者,就是向 kafka broker 发消息的客户端。
+- Consumer :消息消费者,向 kafka broker 取消息的客户端。
+- Topic :可以理解为一个队列,一个 Topic 又分为一个或多个分区,
+- Consumer Group:这是 kafka 用来实现一个 topic 消息的广播(发给所有的 consumer)和单播(发给任意一个 consumer)的手段。一个 topic 可以有多个 Consumer Group。
+- Broker :一台 kafka 服务器就是一个 broker。一个集群由多个 broker 组成。一个 broker 可以容纳多个 topic。
+- Partition:为了实现扩展性,一个非常大的 topic 可以分布到多个 broker上,每个 partition 是一个有序的队列。partition 中的每条消息都会被分配一个有序的id(offset)。将消息发给 consumer,kafka 只保证按一个 partition 中的消息的顺序,不保证一个 topic 的整体(多个 partition 间)的顺序。
+- Offset:kafka 的存储文件都是按照 offset.kafka 来命名,用 offset 做名字的好处是方便查找。例如你想找位于 2049 的位置,只要找到 2048.kafka 的文件即可。当然 the first offset 就是 00000000000.kafka。
+
+## Kafka 分区的目的?
+
+分区对于 Kafka 集群的好处是:实现负载均衡。分区对于消费者来说,可以提高并发度,提高效率。
+
+## 你知道 Kafka 是如何做到消息的有序性?
+
+kafka 中的每个 partition 中的消息在写入时都是有序的,而且单独一个 partition 只能由一个消费者去消费,可以在里面保证消息的顺序性。但是分区之间的消息是不保证有序的。
+
+## Kafka Producer 的执行过程?
+
+1,Producer生产消息 --> 2,从Zookeeper找到Partition的Leader --> 3,推送消息 --> 4,通过ISR列表通知给Follower --> 5, Follower从Leader拉取消息,并发送ack --> 6,Leader收到所有副本的ack,更新Offset,并向Producer发送ack,表示消息写入成功。
+
+## 讲一下你使用 Kafka Consumer 消费消息时的[线程模型](https://link.segmentfault.com/?enc=sniyt20KkIXAqIjQ4fraxQ%3D%3D.cmMRO%2FsnA0vXdDv1sttq1%2BSlDgGBRoAPWJw1zI%2Fslu10a6nBqqaCr9uYIZXuLMdYNK9%2B%2B17KBqxtMLvdyqlxYQ%3D%3D),为何如此设计?
+
+Thread-Per-Consumer Model,这种多线程模型是利用Kafka的topic分多个partition的机制来实现并行:每个线程都有自己的consumer实例,负责消费若干个partition。各个线程之间是完全独立的,不涉及任何线程同步和通信,所以实现起来非常简单。
+
+## 请谈一谈 Kafka 数据一致性原理
+
+一致性就是说不论是老的 Leader 还是新选举的 Leader,Consumer 都能读到一样的数据。
+
+假设分区的副本为3,其中副本0是 Leader,副本1和副本2是 follower,并且在 ISR 列表里面。虽然副本0已经写入了 Message4,但是 Consumer 只能读取到 Message2。因为所有的 ISR 都同步了 Message2,只有 High Water Mark 以上的消息才支持 Consumer 读取,而 High Water Mark 取决于 ISR 列表里面偏移量最小的分区,对应于上图的副本2,这个很类似于木桶原理。
+
+这样做的原因是还没有被足够多副本复制的消息被认为是“不安全”的,如果 Leader 发生崩溃,另一个副本成为新 Leader,那么这些消息很可能丢失了。如果我们允许消费者读取这些消息,可能就会破坏一致性。试想,一个消费者从当前 Leader(副本0) 读取并处理了 Message4,这个时候 Leader 挂掉了,选举了副本1为新的 Leader,这时候另一个消费者再去从新的 Leader 读取消息,发现这个消息其实并不存在,这就导致了数据不一致性问题。
+
+当然,引入了 High Water Mark 机制,会导致 Broker 间的消息复制因为某些原因变慢,那么消息到达消费者的时间也会随之变长(因为我们会先等待消息复制完毕)。延迟时间可以通过参数 replica.lag.time.max.ms 参数配置,它指定了副本在复制消息时可被允许的最大延迟时间。
+
+## ISR、OSR、AR 是什么?
+
+ISR:In-Sync Replicas 副本同步队列
+
+OSR:Out-of-Sync Replicas
+
+AR:Assigned Replicas 所有副本
+
+ISR是由leader维护,follower从leader同步数据有一些延迟(具体可以参见 图文了解 Kafka 的副本复制机制),超过相应的阈值会把 follower 剔除出 ISR, 存入OSR(Out-of-Sync Replicas )列表,新加入的follower也会先存放在OSR中。AR=ISR+OSR。
+
+## LEO、HW、LSO、LW等分别代表什么
+
+LEO:是 LogEndOffset 的简称,代表当前日志文件中下一条
+
+HW:水位或水印(watermark)一词,也可称为高水位(high watermark),通常被用在流式处理领域(比如Apache Flink、Apache Spark等),以表征元素或事件在基于时间层面上的进度。在Kafka中,水位的概念反而与时间无关,而是与位置信息相关。严格来说,它表示的就是位置信息,即位移(offset)。取 partition 对应的 ISR中 最小的 LEO 作为 HW,consumer 最多只能消费到 HW 所在的位置上一条信息。
+
+LSO:是 LastStableOffset 的简称,对未完成的事务而言,LSO 的值等于事务中第一条消息的位置(firstUnstableOffset),对已完成的事务而言,它的值同 HW 相同
+
+LW:Low Watermark 低水位, 代表 AR 集合中最小的 logStartOffset 值。
+
+## 数据传输的事务有几种?
+
+数据传输的事务定义通常有以下三种级别:
+
+(1)最多一次: 消息不会被重复发送,最多被传输一次,但也有可能一次不传输
+(2)最少一次: 消息不会被漏发送,最少被传输一次,但也有可能被重复传输.
+(3)精确的一次(Exactly once): 不会漏传输也不会重复传输,每个消息都传输被
+
+## Kafka 消费者是否可以消费指定分区消息?
+
+Kafa consumer消费消息时,向broker发出fetch请求去消费特定分区的消息,consumer指定消息在日志中的偏移量(offset),就可以消费从这个位置开始的消息,customer拥有了offset的控制权,可以向后回滚去重新消费之前的消息,这是很有意义的。
+
+## Kafka消息是采用Pull模式,还是Push模式?
+
+Kafka最初考虑的问题是,customer应该从brokes拉取消息还是brokers将消息推送到consumer,也就是pull还push。在这方面,Kafka遵循了一种大部分消息系统共同的传统的设计:producer将消息推送到broker,consumer从broker拉取消息。
+
+一些消息系统比如Scribe和Apache Flume采用了push模式,将消息推送到下游的consumer。这样做有好处也有坏处:由broker决定消息推送的速率,对于不同消费速率的consumer就不太好处理了。消息系统都致力于让consumer以最大的速率最快速的消费消息,但不幸的是,push模式下,当broker推送的速率远大于consumer消费的速率时,consumer恐怕就要崩溃了。最终Kafka还是选取了传统的pull模式。
+
+Pull模式的另外一个好处是consumer可以自主决定是否批量的从broker拉取数据。Push模式必须在不知道下游consumer消费能力和消费策略的情况下决定是立即推送每条消息还是缓存之后批量推送。如果为了避免consumer崩溃而采用较低的推送速率,将可能导致一次只推送较少的消息而造成浪费。Pull模式下,consumer就可以根据自己的消费能力去决定这些策略。
+
+Pull有个缺点是,如果broker没有可供消费的消息,将导致consumer不断在循环中轮询,直到新消息到t达。为了避免这点,Kafka有个参数可以让consumer阻塞知道新消息到达(当然也可以阻塞知道消息的数量达到某个特定的量这样就可以批量发
+
+## Kafka 高效文件存储设计特点
+
+Kafka把topic中一个parition大文件分成多个小文件段,通过多个小文件段,就容易定期清除或删除已经消费完文件,减少磁盘占用。
+
+通过索引信息可以快速定位message和确定response的最大大小。
+
+通过index元数据全部映射到memory,可以避免segment file的IO磁盘操作。
+
+通过索引文件稀疏存储,可以大幅降低index文件元数据占用空间大小
+
+## Kafka创建Topic时如何将分区放置到不同的Broker中
+
+副本因子不能大于 Broker 的个数;
+
+第一个分区(编号为0)的第一个副本放置位置是随机从 brokerList 选择的;
+
+其他分区的第一个副本放置位置相对于第0个分区依次往后移。也就是如果我们有5个 Broker,5个分区,假设第一个分区放在第四个 Broker 上,那么第二个分区将会放在第五个 Broker 上;第三个分区将会放在第一个 Broker 上;第四个分区将会放在第二个 Broker 上,依次类推;
+
+剩余的副本相对于第一个副本放置位置其实是由 nextReplicaShift 决定的,而这个数也是随机产生的
+
+## 谈一谈 Kafka 的再均衡
+
+在Kafka中,当有新消费者加入或者订阅的topic数发生变化时,会触发Rebalance(再均衡:在同一个消费者组当中,分区的所有权从一个消费者转移到另外一个消费者)机制,Rebalance顾名思义就是重新均衡消费者消费。Rebalance的过程如下:
+
+第一步:所有成员都向coordinator发送请求,请求入组。一旦所有成员都发送了请求,coordinator会从中选择一个consumer担任leader的角色,并把组成员信息以及订阅信息发给leader。
+
+第二步:leader开始分配消费方案,指明具体哪个consumer负责消费哪些topic的哪些partition。一旦完成分配,leader会将这个方案发给coordinator。coordinator接收到分配方案之后会把方案发给各个consumer,这样组内的所有成员就都知道自己应该消费哪些分区了。
+
+所以对于Rebalance来说,Coordinator起着至关重要的作用
+
+## Kafka 是如何实现高吞吐率的?
+
+Kafka是分布式消息系统,需要处理海量的消息,Kafka的设计是把所有的消息都写入速度低容量大的硬盘,以此来换取更强的存储能力,但实际上,使用硬盘并没有带来过多的性能损失。kafka主要使用了以下几个方式实现了超高的吞吐率:
+
+- 顺序读写;
+- 零拷贝
+- 文件分段
+- 批量发送
+- 数据压缩。
+
+## Kafka 缺点?
+
+- 由于是批量发送,数据并非真正的实时;
+- 对于mqtt协议不支持;
+- 不支持物联网传感数据直接接入;
+- 仅支持统一分区内消息有序,无法实现全局消息有序;
+- 监控不完善,需要安装插件;
+- 依赖zookeeper进行元数据管理;
+
+
+## Kafka 新旧消费者的区别
+
+旧的 Kafka 消费者 API 主要包括:SimpleConsumer(简单消费者) 和 ZookeeperConsumerConnectir(高级消费者)。SimpleConsumer 名字看起来是简单消费者,但是其实用起来很不简单,可以使用它从特定的分区和偏移量开始读取消息。高级消费者和现在新的消费者有点像,有消费者群组,有分区再均衡,不过它使用 ZK 来管理消费者群组,并不具备偏移量和再均衡的可操控性。
+
+现在的消费者同时支持以上两种行为,所以为啥还用旧消费者 API 呢?
+
+## Kafka 分区数可以增加或减少吗?为什么?
+
+我们可以使用 bin/kafka-topics.sh 命令对 Kafka 增加 Kafka 的分区数据,但是 Kafka 不支持减少分区数。
+
+Kafka 分区数据不支持减少是由很多原因的,比如减少的分区其数据放到哪里去?是删除,还是保留?删除的话,那么这些没消费的消息不就丢了。如果保留这些消息如何放到其他分区里面?追加到其他分区后面的话那么就破坏了 Kafka 单个分区的有序性。如果要保证删除分区数据插入到其他分区保证有序性,那么实现起来逻辑就会非常复杂。
+
+
+
+> 参考:https://blog.51cto.com/u_15127589/2679155
\ No newline at end of file
From 93cc798a596168b82c9297d0e1ed24afcc36c272 Mon Sep 17 00:00:00 2001
From: tyson
Date: Sun, 30 Oct 2022 17:32:31 +0800
Subject: [PATCH 02/75] modify url
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 3ea05f6..9fc5372 100644
--- a/README.md
+++ b/README.md
@@ -183,7 +183,7 @@
# 海量数据场景题
-1. [统计不同电话号码的个数](https://topjavaer.cn/system-design/1-scan-code-login.html)
+1. [统计不同电话号码的个数](https://topjavaer.cn/mass-data/1-count-phone-num.html)
2. [出现频率最高的100个词](https://topjavaer.cn/mass-data/2-find-hign-frequency-word.html)
3. [查找两个大文件共同的URL](https://topjavaer.cn/mass-data/3-find-same-url.html)
4. [如何在100亿的数据中找到中位数](https://topjavaer.cn/mass-data/4-find-mid-num.html)
From 9afe51d51d4648b9e70b520e6f243c8683967b66 Mon Sep 17 00:00:00 2001
From: tyson
Date: Sun, 6 Nov 2022 15:24:32 +0800
Subject: [PATCH 03/75] interview
---
README.md | 44 ++---
interview/1-byte-and-dance.md | 316 ++++++++++++++++++++++++++++++++++
interview/10-netease.md | 115 +++++++++++++
interview/2-tencent.md | 66 +++++++
interview/3-baidu.md | 107 ++++++++++++
interview/4-ali.md | 79 +++++++++
interview/5-kuaishou.md | 79 +++++++++
interview/6-meituan.md | 74 ++++++++
interview/7-shopee.md | 73 ++++++++
interview/8-jingdong.md | 58 +++++++
interview/9-huawei.md | 56 ++++++
interview/README.md | 13 ++
12 files changed, 1055 insertions(+), 25 deletions(-)
create mode 100644 interview/1-byte-and-dance.md
create mode 100644 interview/10-netease.md
create mode 100644 interview/2-tencent.md
create mode 100644 interview/3-baidu.md
create mode 100644 interview/4-ali.md
create mode 100644 interview/5-kuaishou.md
create mode 100644 interview/6-meituan.md
create mode 100644 interview/7-shopee.md
create mode 100644 interview/8-jingdong.md
create mode 100644 interview/9-huawei.md
create mode 100644 interview/README.md
diff --git a/README.md b/README.md
index 9fc5372..88004f5 100644
--- a/README.md
+++ b/README.md
@@ -190,6 +190,25 @@
5. [找出最热门的查询串](https://topjavaer.cn/mass-data/5-find-hot-string.html)
6. [如何找出排名前500的数字](https://topjavaer.cn/mass-data/6-top-500-num.html)
+# 大厂面经汇总
+
+- [字节跳动](./interview/1-byte-and-dance.md)
+- [腾讯](./interview/2-tencent.md)
+- [百度](./interview/3-baidu.md)
+- [阿里](./interview/4-ali.md)
+- [快手](./interview/5-kuaishou.md)
+- [美团](./interview/6-meituan.md)
+- [shopee](./interview/7-shopee.md)
+- [京东](./interview/8-jingdong.md)
+- [华为](./interview/9-huawei.md)
+- [网易](./interview/10-netease.md)
+
+# 其他优质文章
+
+[优质文章汇总,持续更新中~](https://topjavaer.cn/advance/excellent-article/)
+
+
+
# 工具
[Git 超详细总结!](工具/git-overview.md)(推荐 :+1:)
@@ -200,31 +219,6 @@
[Maven 基础总结!](tools/maven-overview.md)
-# 大厂面试系列
-
-1. [字节跳动一面面经](https://mp.weixin.qq.com/s/RH-SunzjqUTTx8HWaCmCcw)
-2. [别再问我Java List八股文了!](https://mp.weixin.qq.com/s/doyy_GYGWoH_YHgyMijStA)
-3. [腾讯面试,你真的懂HTTP吗?](https://mp.weixin.qq.com/s/kC7XRBfO7Z5hZcX6Dz2viw)
-4. [美团二面面经,最后竟然有惊喜?](https://mp.weixin.qq.com/s/3HvOtTU29HGALqmeeOZNWw)
-5. [Java多线程,被面试官难倒了!](https://mp.weixin.qq.com/s/tv8pOLaS6hpwgbKOB9w0Zw)
-6. [京东二面,Redis为什么这么快?](https://mp.weixin.qq.com/s/S3vN5T9HpziRd2s5ysLaSg)
-7. [MySQL索引,给我整不会了!](https://mp.weixin.qq.com/s/Q5CrDlNInpnckJaBQSrA7w)
-8. [别再问我Java虚拟机八股文了!](https://mp.weixin.qq.com/s/npo5-VqQt5sqZiSwPv6LVw)
-9. [计算机网络,问傻了!](https://mp.weixin.qq.com/s/WXcMLa_tdxpRLhO4U8LHIQ)
-10. [Spring这几道题你会吗?](https://mp.weixin.qq.com/s/DtgYRFfOQxQdtQosCU-6aw)
-11. [面向对象编程?](https://mp.weixin.qq.com/s/M8jDnLat61YAbM1-jIhJIA)
-12. [Java内功深厚?](https://mp.weixin.qq.com/s/v_kWSHX9GMS_aoqfwUMKsg)
-13. [面试初级Java开发,面试官问我MySQL架构?](https://mp.weixin.qq.com/s/JvDZCk4IecmaEYfFsRhpjQ)
-14. [手写红黑树?](https://mp.weixin.qq.com/s/yznh_IfMg4hWqU62U-t9GQ)
-15. [面完阿里,直接入职!](https://mp.weixin.qq.com/s/49QJ1FzaGTe-_54PT8_8jA)
-16. [华为面经](https://mp.weixin.qq.com/s/KmjwoG7pNvAHiX1UNnef6g)
-
-# 其他优质文章
-
-[优质文章汇总,持续更新中~](https://topjavaer.cn/advance/excellent-article/)
-
-
-
# 交流
如果想进**技术、面试交流群**,可以扫描下方二维码加我微信,**备注加群**,我拉你进群,群里有BAT大佬,互相学习~
diff --git a/interview/1-byte-and-dance.md b/interview/1-byte-and-dance.md
new file mode 100644
index 0000000..185ae8f
--- /dev/null
+++ b/interview/1-byte-and-dance.md
@@ -0,0 +1,316 @@
+# 字节跳动
+
+## 面经1
+
+一面:
+
+ 1.IM系统用户登录怎么实现的?
+
+ 2.登录状态是怎么保存的?session是怎么获取的?sessionid是怎么识别的?整个流程是什么样的?有没有考虑分布式session?
+
+ 3.Redis的数据类型
+
+ 4.Redis数据类型的底层数据结构
+
+ 5.三次握手、四次挥手
+
+ 6.Redis持久化机制
+
+ 7.MySQL的InnoDB索引数据结构
+
+ 8.哪些SQL的关键字会让索引失效
+
+ 9.队列、栈、数组、链表
+
+ 10.算法题:leetcode 92题
+
+ 二面:
+
+1.讲讲爬虫的构成
+
+2.爬虫抓到的数据不清洗吗?不去重吗?
+
+3.对爬虫有什么更多的了解吗?
+
+4.Linux进程间通信机制
+
+5.进程和线程的区别
+
+6.线程私有的数据有哪些?(不是Java线程)
+
+7.讲一下堆排序,每次调整的时间复杂度?堆排序是稳定的吗?(一开始说错了,应该是不稳定的,后面面试官问稳定的定义是什么)
+
+8.哈希表的原理,怎么减小哈希表的冲突,怎么保证哈希表的并行读写
+
+9.Kafka用过吗?说说Kafka的原理?怎么保证Kafka的高可用?Kafka怎么保证消息有序?
+
+10.项目里的set实现点赞,zset实现关注,为什么?
+
+11.zset底层实现?说一下跳表?节点的高度是多少?怎么决定节点的高度?
+
+12.https了解吗?
+
+13.中间人攻击知道吗?怎么做https的抓包?https怎么篡改?
+
+14.虚拟地址到物理地址的映射过程
+
+15.算法题:给一个数组,建一颗最小高度的二叉树(递归和非递归)
+
+ 三面:
+
+1.介绍一下做过的项目,哪些挑战性比较大,比较有难度的
+
+2.IM项目怎么用Netty的,为什么要用Netty,长连接交互是怎样的
+
+3.消息怎么存储,怎么发送,怎么知道消息已读和未读的
+
+4.读了5条消息、又来5条消息,你是怎么去更新的,你的消息是幂等的吗?
+
+5.项目里怎么用ES的,ES怎么支持搜索的
+
+6.技术论坛网站的评论是怎么存储的
+
+7.查询评论是在DB里扫表查询吗?怎么展示所有的评论?性能如何?想要查询更快可以做哪些优化?
+
+8.结合缓存和DB的时候会出现哪些问题?要怎么解决?
+
+9.快排了解吗?介绍一下快排?时间复杂度是多少?为什么会退化成O(n^2)?单链表可以做快排吗?快排最核心的逻辑是什么?写一下单链表快排
+
+## 面经2
+
+一年经验,Java开发
+
+1. 自我介绍;
+2. 项目介绍;(延申较浅)
+3. 手撕算法:NC95 最长连续子序列
+4. 手撕算法:NC91 最长递增子序列
+5. 线程和进程的区别?怎么创建线程?有哪些状态?有什么区别?
+6. Java1.8了解哪些新特性?重点讲讲HashMap和CurrentHashMap。
+7. OSI七层协议和TCP/IP五层协议有什么区别?TCP/UDP在哪一层?对TCP了解多少?
+8. 你还有什么要问的吗?
+
+总结:开幕雷击,字节对算法还是挺看重的。
+
+## 面经3
+
+字节电商后端
+
+- 进程和线程区别
+- 为什么进程切换开销比线程切换开销大?
+- 你知道逻辑地址和物理地址么?
+- sql题(表T (id,name,salary,city)用SQL实现找到同时符合城市平均工资大于5000,单人工资大于10000的人的名字)
+- wifi属于OSI哪一层
+- 三次握手四次挥手相关(细节,以及为什么三次,为什么四次)
+- hashmap的底层原理
+- 红黑树和二叉搜索树,二叉树之间的区别
+- 为什么hashmap不直接用红黑树
+
+ 算法题
+
+1. 求从一个树的左边的叶子节点到一个树右边的叶子节点的最短路径)
+
+2. 最长回文子串
+3. 给定一个数N,求(1-n)所有的可能子序列)比如给个1 返回1 给个2 返回1,2,1 2
+
+## 面经4
+
+1. 自我介绍
+2. 项目
+3. volatile原理和作用
+4. 什么是指令重排序,举个例子
+5. AQS原理
+6. JVM内存模型
+7. 类加载过程
+8. 双亲委派机制
+9. 介绍代理模式
+10. redis数据结构
+11. HTTP状态码,header
+12. MVCC原理
+13. 算法:
+
+ - 买股票无限次
+ - 买股票只买一次
+ - 买股票有手续费
+ - 买股票最多买两次
+
+## 面经5
+
+1. 自我介绍
+2. 业务部分:
+
+ - 2.1 堆和栈的区别
+ - 2.2 调用一个函数在堆中的过程
+ - 2.3 密码学 中间人攻击、RSA、DSA密码的差别、加密过程,私钥加密公钥加密,数字签名的过程(跟本人网络空间安全背景有关)
+ - 2.4 彩虹表、 kerberos、ECC(椭圆曲线加密)
+
+3. 算法题:
+
+ 4. 一个无序数组,求其topK
+
+ 5. 时段统计。动态规划。
+
+## 面经6
+
+1. 自我介绍
+2. 实习介绍:你在腾讯干了啥
+3. 实习项目介绍:这个项目(深挖)
+4. 项目为啥立项,你做了什么模块
+5. 你的日志模块是怎么设计的,怎么开发的
+6. 你的token怎么做的,怎么保证唯一
+7. 你开发的业务模块举个例子(给他举了),你是怎么设计数据表的,具体用了哪些中间件
+8. mysql中inoodb的索引有哪些种类
+9. B+树索引具体是怎么实现的
+10. B树与B+树的区别
+11. 为什么B+树的中间节点不储存数据?
+12. 给一个索引,在有的查询过程中他没有走索引查询,说说你能想到的原因
+13. 红黑树了解过么?
+14. 用过redis么?(妈耶能别聊数据库了么)
+15. 你提到了kafka是吧,来聊一哈
+16. 聊一下你对git的理解
+17. git rebase与git merge的区别
+18. 聊一下你对工作流的理解
+
+手撕算法:
+
+给四个整数,判断是否只通过四则运算加()优先级能否运算出24
+
+## 面经7
+
+一面:
+
+1. 自我介绍
+2. JAVA SDK起到的作用
+3. 项目
+4. 数据流(项目)
+5. 排序(介绍下你知道的排序和复杂度)
+6. Arrays.sort底层的排序算法(有三种策略)
+7. 堆排序基本思路
+8. linux,操作系统的开机流程(这题我不会。)
+9. 进程和线程的区别
+10. 进程切换会发生什么
+11. 进程调度算法有哪些
+12. TCP、udp区别
+13. java锁,关键字区别
+14. 公平锁、非公平锁解释一下
+
+二面:
+
+1. 算法题:由前序遍历中序遍历重建子树;
+2. 为什么静态类中不能使用非静态类(从类加载过程回答);
+3. java类加载过程;
+ 3.1. 加载阶段中,为什么要有自定义的类加载器;
+ 3.2. 双亲委派原则的机制;
+4. HashMap数据结构;
+ 4.1. 为什么小于6是链表,大于8变成红黑树;
+ 4.2. HashMap扩容机制;
+ 4.3. HashMap是否线程安全,例子;
+ 4.4. ConcurrentHashMap和HashTable的区别;
+ 4.5. ConcurrentHashMap如何保证高效,为什么是线程安全,为什么比HashTable优秀,分段锁机制;
+ 4.6. CAS能保证线程安全吗(我回答能,面试官说不能。。估计想考ABA问题),volatile关键字能保证线程安全吗;
+5. 随机数求根,比如根号二的值。(二分查询)
+6. 有n个筐,筐固定,每个筐内有不同数目的苹果,移动苹果,使每个筐苹果平均(移动的代价:1~2算1步,1~3算2步)使步数最小;
+
+三面:
+
+1. 自我介绍
+2. 解决什么问题,做了些什么?(项目)
+3. 多个接口,有失败怎么办(项目)
+4. redis分布式锁怎么实现
+5. 时间过期怎么办
+6. ArrayList怎么扩容,时间复杂度O(n)?插尾部O(1),平均是多少,答案O(2)需要考虑扩容,小伙伴们可以自己推一下。
+7. HashMap底层原理
+8. mysql索引什么原理、B+树
+9. mysql和redis区别(讲一下各自优缺点)
+10. 为什么不用redis存数据?
+11. 算法:LRU缓存(),先讲一下再写
+
+## 面经8
+
+抖音电商
+
+1. 自我介绍
+2. 讲一下HashMap的put方法
+3. 讲一下HashMap的扩容过程
+4. 讲一下你自定义协议怎么解决粘包问题的?
+5. 算法题:LeetCode 二叉树根节点到子节点的路径和
+6. mysql的索引结构
+7. 为什么用B+树呢?
+8. having的作用
+9. 聚簇索引、非聚簇索引
+10. 聚簇索引比非聚簇索引的优点
+11. mysql的四个隔离级别,应用场景
+12. 如何在可重复读隔离级别解决幻读问题
+13. dubbo的负载均衡策略
+14. java的动态代理
+15. Spring哪里用到了动态代理?
+16. CGlib动态代理说一下
+17. MQ如何保证消息不会丢失
+
+## 面经9
+
+一面
+
+- 怎么理解微服务
+- 微服务的缺点
+- 微服务之间怎么做负载均衡
+- Oauth2基本流程、原理
+- 登录模块是怎么做的
+- cookie和session的区别
+- 购物车为什么用Redis存,是永久存储吗
+- 为什么购物车多读多写
+- Redis怎样清除过期key,有哪些策略
+- lru是怎样的过程
+- Redis字典底层怎么实现的
+- hashtable是怎样实现的
+- ziplist怎样实现的
+- 普通的哈希表怎样实现的
+- 哈希表怎么扩容
+- 使用MQ的好处
+- MQ解耦和微服务解耦的区别
+- 算法:最长回文子串
+- https建立连接的过程(SSL/TLS协商的过程)
+- 对称加密和非对称加密的优缺点
+- 为什么要区分内核态和用户态
+- 什么时候从用户态切换到内核态
+- 你编程的情况下,系统调用什么时候会发生
+ 反问:业务,开发语言,表现,对应届生的要求(重点是基础和算法)
+ 面试体验不错,但是项目挖的有点深
+
+2面
+
+- 手写单例模式
+- volatile什么作用
+- 多线程的几种实现方式
+- 四种方式的区别
+- 锁用过哪些
+- 排它锁什么意思
+- 自旋锁什么意思
+- CAS相关
+- MySQL可以不指定主键建表吗,背后的逻辑是什么
+- 聚簇索引和其他索引有什么区别
+- 建唯一索引,插入数据时是怎么处理的
+- 重复插入会报错,是怎么处理的
+- 不同事物隔离级别的实现
+- 以前没有实习过吗
+- lc40 组合总和II
+ 反问:部门怎样培养新人,刚进来做什么(学基础,语言和中间件,做demo),大概多久做需求(1周到1个月不等,看学习情况),框架和中间件以开源的为主还是以自研的为主(自研的)
+
+3面
+
+- 有在实习吗
+- 面试通过后可以实习吗
+- 做项目的过程中遇到过什么问题
+- 内存泄露具体发生在哪
+- 什么情况下会出现多线程访问
+- 缓存穿透,怎么解决 (好像一紧张说成缓存击穿了,面试复盘的时候才发现。。。)
+- 缓存雪崩,怎么解决
+- 缓存与数据库数据一致性
+- 超卖问题怎么解决的
+- 集群环境下,Redis内存里的数据怎么保证一致
+- 算法:给定一个字符数组,和一个字符串,在字符串里找到任意一个完全由字符数组组成的子串,字符顺序无所谓(滑动窗口)
+
+
+
+**最后给大家分享一份精心整理的大厂高频面试题PDF,需要的小伙伴可以自行下载:**
+
+[大厂面试手册](http://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247485445&idx=1&sn=1c6e224b9bb3da457f5ee03894493dbc&chksm=ce98f543f9ef7c55325e3bf336607a370935a6c78dbb68cf86e59f5d68f4c51d175365a189f8#rd)
\ No newline at end of file
diff --git a/interview/10-netease.md b/interview/10-netease.md
new file mode 100644
index 0000000..7267aca
--- /dev/null
+++ b/interview/10-netease.md
@@ -0,0 +1,115 @@
+# 网易面经
+
+## 面经1
+
+- 自我介绍
+- 如何创建一个Java对象
+- 在哪检查类的合法性
+- 如果这个类不存在,会怎么样
+- 类的加载机制
+- Thread的几种状态,调用什么方法切换的
+- 对象锁,2种暂停
+- Wait()和sleep()的区别
+- 用的线程池叫什么,有什么特性
+- 对springboot的理解
+- springMVC的filter和xx的区别
+- ArrayList和LinkedList的区别
+
+
+
+## 面经2
+
+- 怼项目
+- A、B、C并发执行完,D、E、F并发执行实现方式
+- 线程池
+- CountDownLatch、LinkedHashMap、AQS实现原理
+- 部分J.U.C包底层实现
+- Sychronized和Lock区别
+- Condition用法
+- 用sychronized实现Condition功能
+- Condition和sychronized实现Condition两种方式有什么区别
+- JVM分区
+- 垃圾回收
+- 类加载机制
+- 双亲委派机制
+- Redis基本数据类型
+- Redis集群方式
+- Redis哨兵
+- Redis备份
+- Redis持久化方式及区别、优缺点、实现方式
+- Redis集群作用
+- MySQL索引方式
+- MySQL组合索引
+- MySQL实现组合索引方式
+- MySQL集群方式
+- MySQL日志文件
+- MySQL集群实现原理
+- MySQL事物隔离级别,作用
+- Spring IOC
+- 怎么解决循环引用
+- 聚簇索引和非聚簇索引
+- 怎么自定义String类型
+
+## 面经3
+
+- 自我介绍。
+- 你在xxx实习前后端都写?说说vue,angular,react的区别。
+- 如果给你个新项目,你用哪个框架,为什么。
+- 听说你用过缓存?用了什么缓存?几级缓存?怎么用的?
+- 缓存怎么保证和内存的数据一致性?
+- 如果让你自己设计一个缓存系统,你怎么设计?
+- 你了解哪些 hash 方法?一致性 hash 是干嘛的?
+- 对分布式有多少了解?
+- 为什么用 springboot,好在哪?
+- 给你一个sql语句,怎么判断有没有命中索引?
+- 如果让你现在设计xx项目,你会怎么做?
+- 你觉得java程序员写代码的时候应该关注哪些地方?
+- 在xxx实习中项目的难点?
+- pb和 json 你会怎么使用?
+
+## 面经4
+
+- 说一个源码改进业务的例子。
+- websocket心跳如何实现?
+- zk/db/redis锁怎么选型。
+- 8台物理机能撑住百万的长连接,一台能撑住13w???数据感觉有问题,至少有20台吧(lf+hl)
+- 多租户隔离,为什么不用两个进程?
+- 需要下沉容器层,有插件层消除重复。
+- 表现为同一个应用,然后只注册到注册中心一次。两个应用可以使用相同端口号。
+- 另外进程就更重了,要消耗更多的资源,而且不好管控。
+- 流程编排分支条件如何实现?
+- 业务身份,这个有问题,如果大量增加分支,会拷贝多份,会有很多重复的规则文件,但是只有一两个组件不一样。
+- 目前做法时在组件中可能有业务条件判断。后续会使用表达式语言增加分支条件支持,业务身份只支持大粒度的区分。
+- 流程编排如何避免大量调用下游服务,是否做了隔离?
+- 感觉调用下游要做熔断,例如hystrix。但是这个有点重了。sdk要做薄。
+- 客服IM也区分在线消息和离线消息,都会存起来,如果掉线了,下次登录成功了主动拉取就行。
+
+
+## 面经5
+
+- 介绍一下JVM内存模型。
+- MySQL索引优化原则
+- BufferPool原理聊聊。
+- 解释一下什么是负载均衡,Dubbo的负载均衡说一下?
+- 当MySQL单表记录数过大时,数据库如何优化?
+- 一个4库的怎么拆分成8库的在表数量不变的情况下?
+- 举举例子业务中DDD的设计。
+- 函数式编程的本质是什么,为什么需要了解过吗?
+- 知道流的原理吗 Foreach函数碰到报错后面的执行吗?为什么?
+- kafka消息怎么保证不丢失的?
+- 为什么跳槽?
+- 对不是互联网电商方向,其他方向感兴趣吗?
+- 今后有什么职场规划或者学习计划。
+- 你认为你的个人性格是怎样的呢。
+
+
+## 面经6
+
+- 自我介绍
+- 项目介绍
+- 浏览器输入 youdao.com 发生了什么有多详细说多详细。一直问到数据链路层 mac地址
+- TCP 有哪些状态
+- 归并排序 思想 及复杂度
+- Mysql MVCC
+- 算法题。剑指Offer26,树的子结构
+- 反问
\ No newline at end of file
diff --git a/interview/2-tencent.md b/interview/2-tencent.md
new file mode 100644
index 0000000..c37e35f
--- /dev/null
+++ b/interview/2-tencent.md
@@ -0,0 +1,66 @@
+# 腾讯
+
+## 一面
+1. mysql索引结构?
+2. redis持久化策略?
+3. zookeeper节点类型说一下;
+4. zookeeper选举机制?
+5. zookeeper主节点故障,如何重新选举?
+6. syn机制?
+7. 线程池的核心参数;
+8. threadlocal的实现,原理,业务用来做什么?
+9. spring DI的原理;
+10. 四次挥手;
+11. gc root选择;
+12. 标记清除算法的过程,标记清楚算法如何给对象分配内存空间?
+13. cms算法的缺点;
+## 二面
+1. CorruntHashmap理解
+2. ThreadLocal原理
+3. hashmap;
+4. Java数据类型,同步机制;
+5. 讲讲贪心算法;
+6. 如果线上用户出现502错误你怎么排查?
+7. 并发量很大,服务器宕机。你会怎么做?
+## 三面
+1. syn和lock的区别,哪个更好?怎么选择?
+2. hashmap源码,为什么8个节点变成红黑树又为什么到了6个节点才恢复为链表?
+3. 缓存穿透,怎么解决?
+4. 负载均衡算法,实现;
+5. 轮询和随机的缺点;
+6. 分布式服务治理;
+7. dns迭代和递归的区别;
+8. 算法题:最长回文串
+9. 为什么连接的时候是三次握手,关闭的时候却是四次握手?
+## 四面
+1. 自我介绍 就背景进行一些提问
+2. 简单说说计算机网络
+3. 简单描述一下从浏览器输入一个地址到服务端整个交互过程
+4. 说说数据结构
+5. 操作系统用过吗
+6. 用过 linux 的哪些命令
+7. 查看一个进程监听了哪些端口
+8. 详细介绍项目(简历上的域名访问不了)
+9. 讲解之前工作经历中做的东西
+ 10.做一道算法题(判断二叉树是否对称)
+10. java 如何从源代码转换成机器码执行的
+11. java 的击穿
+12. 网络的七层结构
+13. tcp\udp 详解 区别
+14. https 协议的交互过程
+15. linux 基础命令
+16. linux 开机过程
+17. 了解现在市面上主流的 cpu 架构
+18. fpga 概念了解吗
+19. 市面上的图数据库
+20. rdf 讲解
+21. 图数据库底层存储
+22. b 树,b+树的概念和区别
+23. 红黑树平衡二叉树优缺点和应用场景
+24. 有没有了解 docker 等云技术
+
+
+
+**最后给大家分享一份精心整理的大厂高频面试题PDF,需要的小伙伴可以自行下载:**
+
+[大厂面试手册](http://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247485445&idx=1&sn=1c6e224b9bb3da457f5ee03894493dbc&chksm=ce98f543f9ef7c55325e3bf336607a370935a6c78dbb68cf86e59f5d68f4c51d175365a189f8#rd)
\ No newline at end of file
diff --git a/interview/3-baidu.md b/interview/3-baidu.md
new file mode 100644
index 0000000..18caa36
--- /dev/null
+++ b/interview/3-baidu.md
@@ -0,0 +1,107 @@
+# 百度
+
+## 面经1
+
+shiro的组件
+分布式一致性算法
+zookeeper那些能参与投票,leader能投票吗?
+netty零拷贝实现
+volatile,如何感知到变量变化的
+redis高可用
+http如何跨域?
+tcp如何长链接。
+http如何操作浏览器缓存。
+用过消息队列吗?
+怎么自己扩展validator(参数校验)
+jwt组成 header payload 签名加密算法那些。
+rsa如何运用到jwt中
+synchronized和volatile的区别
+什么是上下文切换,URL解析过程
+http有那些方法,get那些
+进程和线程的区别。
+和别人协作出现冲突怎么办
+如何学一个新语言
+怎么自学的
+
+## 面经2
+
+说说IO多路复用
+你刚刚说的多路复用针对的是各个请求(比如set,get),那返回值Redis是怎么处理的(愣住)
+MySQL B+树一般几层,怎么算的
+数据库隔离级别
+脏读、不可重复读、幻读(结合具体场景来讲)
+MySQL隔离级别分别怎么实现的
+MVCC
+redo log、undo log
+刷脏页的流程
+算法题:平方根
+
+## 面经3
+
+自我介绍
+项目是自己练手的项目吗,怎么找的
+项目是从0开始搭建的,还是有用开源的脚手架
+秒杀大概用到哪些东西,怎么实现的
+MQ幂等性和消息积压问题
+缓存与数据库数据一致性
+唯一ID
+Java里怎么保证多个线程的互斥性
+一个线程有哪些状态
+AQS怎么理解的
+Spring IOC容器创建Bean的流程
+创建的Bean是单例还是多例的
+SpringCloud config是怎么在Bean创建后更新Bean的值的
+SpringBoot自动配置原理
+SpringMVC执行流程
+使用Spring和直接使用Java语言面向对象开发,有哪些好处
+怎么理解面向对象
+了解哪些设计模式
+策略模式描述一下
+JVM由哪些模块组成
+框架里打破双亲委派机制的SPI大概怎么实现的
+那说说双亲委派
+垃圾回收主要回收哪些区域
+怎么识别哪些是垃圾
+哪些是根节点
+什么时候会出现Full GC
+不同垃圾收集器的区别
+TCP为什么要握三次手,为什么要挥四次手,大概什么流程
+实现环形队列(数组,增加和删除功能)
+反转链表(迭代)
+
+## 面经4
+专业是偏向硬件吗
+对百度了解多少
+有什么兴趣爱好
+经常打球吗
+喜欢听什么音乐
+经常听音乐吗,什么时候开始喜欢听音乐的
+你说两个具体的歌名我听听
+平时是怎样的一个人,有什么特点
+有做过什么有成就感的事吗
+后面选择百度的概率有多少
+想过自己5年后、10年后是怎样的吗
+
+## 面经5
+
+1.面试官介绍自己,然后自我介绍
+2.java中的线程池有哪些?为什么使用线程池?你在哪里使用过或是见过?
+3.Mysql底层是怎么实现的?从内存布局,磁盘布局说起?
+4.Mysql有哪些索引?B树和B+树的区别,分别解决了什么问题?
+5.try catch finally机制讲解一下?
+6.为什么要使用SpringBoot做开发?与传统的开发有什么不一样的?
+7.什么是微服务?微服务是如何实现服务的注册与发现的?
+8.java中的集合分类有哪些?知道Queue吗?她下面有哪些实现类?重点说说HashMap?
+9.在集合中哪些集合类是线程安全的?
+10.什么是数字签名,作用是什么?使用的是什么算法?
+11.常见的网络攻击有哪些?
+12.在表单提交的时候,容易发起什么样的攻击?
+13.在进行服务调用的时候如何进行身份验证,如何防止网络攻击?
+14.你见过哪些安全框架?具体怎么使用的?(shiro)
+15.两道算法题:1)普通的二分查找,问了其中的一些细节,二分查找存在的问题? 2)判断S1中是不是有S2的排列,找到返回true,否则返回false
+16.Cookie和session 的使用场景,他们之间的关系?
+17.String,StringBuilder,StringBuffer的区别,String的两种初始化的区别?
+
+**最后给大家分享一份精心整理的大厂高频面试题PDF,需要的小伙伴可以自行下载:**
+
+[大厂面试手册](http://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247485445&idx=1&sn=1c6e224b9bb3da457f5ee03894493dbc&chksm=ce98f543f9ef7c55325e3bf336607a370935a6c78dbb68cf86e59f5d68f4c51d175365a189f8#rd)
\ No newline at end of file
diff --git a/interview/4-ali.md b/interview/4-ali.md
new file mode 100644
index 0000000..2c99b2e
--- /dev/null
+++ b/interview/4-ali.md
@@ -0,0 +1,79 @@
+# 阿里
+
+## 面经1
+
+1. 简单介绍项目
+2. 知道哪些数据结构以及他们的特点
+3. 链表增删快,那如何提高其查询效率,有没有什么想法?
+4. B+树了解吗?B+树如何范围查询?B+树退化的极端情况是什么?
+5. 跳表了解吗?
+6. 大顶堆、小顶堆了解吗?
+7. 实现长地址请求到服务端,然后服务端重定向短地址给客户端,如何实现长短地址的互相映射?
+8. 那我现在有10份数据,有1000个线程来争抢,你要怎么处理?
+9. 分布式是什么?为什么要分布式?分布式又会有哪些问题?分布式系统是如何实现事物的?
+10. Redis集群了解吗?如何处理宕机的情况?Redis的同步策略?
+11. LRU算法了解吗?你会如何实现它?这个算法可以应用在哪些场景下?
+12. TCP为什么是三次握手?两次行不行?多次行不行?
+13. TCP的安全性是如何实现的?两台服务器之间可以同时建立多条TCP链接吗?怎么实现的?
+14. 客服端输入一个网址后,是如何拿到客服想要的数据的,是怎样在网络中传输的?
+15. cookie和session
+16. java有哪些锁?共享锁是什么?CAS?乐观锁和悲观锁?synchronied的底层原理?锁升级?死锁怎么形成的?如何破解死锁?
+
+## 面经2
+
+
+1. Java容器:List,Set,Map
+2. Map的遍历方式
+3. HashMap扩容为什么是扩为两倍?
+4. Java线程同步机制(信号量,闭锁,栅栏)
+5. 对volatile的理解:常用于状态标记
+6. 八种基本数据类型的大小以及他们的封装类(顺带了解自动拆箱与装箱)
+7. 线程阻塞几种情况?如何自己实现阻塞队列?
+8. Java垃圾回收。可达性分析->引用级别->二次标记(finalize方法)->垃圾收集 算法(4个)->回收策略(3个)->垃圾收集器(GMS、G1)。
+9. java内存模型
+10. TCP/IP的理解
+11. 进程和线程的区别
+12. http状态码含义
+13. ThreadLocal(线程本地变量),如何实现一个本地缓存
+14. JVM内存区哪里会出现溢出?
+15. 双亲委派模型的理解,怎样将两个全路径相同的类加载到内存中?
+16. CMS收集器和G1收集器
+17. TCP流量控制和拥塞控制
+18. 服务器处理一个http请求的过程
+19. 例举几个Mysql优化手段
+20. 数据库死锁定义,怎样避免死锁
+21. spring的aop是什么?如何实现的
+22. 面向对象的设计原则
+23. 策略模式的实现
+24. 操作系统的内存管理的页面淘汰 算法 ,介绍下LRU(最近最少使用算法 )
+25. B+树的特点与优势
+
+## 面经3
+
+- 自我介绍,说简历里没有的东西
+- 说几个你最近在看的技术(MySQL,多线程)
+- 口述了一个统计数据的场景题
+- 如果这个统计数据场景不用MySQL,而是用Java来实现,怎么做
+- 如果数据量过大,内存放不下呢
+- 用面向对象的思想解决上面提出的问题,创建出父类,子类,方法,说一下思路
+- 下一个场景,口述了一个登录场景,同学用线程池做登录校验,会有什么问题
+- 如何解决这些问题
+- 你给出的方案弊端在哪里,还有哪些方案
+
+## 面经4
+
+
+
+- 谈谈类加载机制。
+- hashmap和concurenthashmap
+- 16g机器,让你分配jvm内存怎么分配。
+- 机器慢了怎么排查。
+- 谈谈consul和zookeeper,还有服务发现机制。
+- 详细说明raft协议。
+- 谈谈consul和zookeeper区别。
+- 服务注册的时候发现没有注册成功会是什么原因。
+- 讲讲你认为的rpc和service mesh之间的关系。
+
+**最后给大家分享一份精心整理的大厂高频面试题PDF,需要的小伙伴可以自行下载:**
+
+[大厂面试手册](http://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247485445&idx=1&sn=1c6e224b9bb3da457f5ee03894493dbc&chksm=ce98f543f9ef7c55325e3bf336607a370935a6c78dbb68cf86e59f5d68f4c51d175365a189f8#rd)
\ No newline at end of file
diff --git a/interview/5-kuaishou.md b/interview/5-kuaishou.md
new file mode 100644
index 0000000..a06a8c2
--- /dev/null
+++ b/interview/5-kuaishou.md
@@ -0,0 +1,79 @@
+# 快手
+
+## 一面
+
+1. 简单介绍项目
+2. 知道哪些数据结构以及他们的特点
+3. 链表增删快,那如何提高其查询效率,有没有什么想法?
+4. B+树了解吗?B+树如何范围查询?B+树退化的极端情况是什么?
+5. 跳表了解吗?
+6. 大顶堆、小顶堆了解吗?
+7. 实现长地址请求到服务端,然后服务端重定向短地址给客户端,如何实现长短地址的互相映射?
+8. 那我现在有10份数据,有1000个线程来争抢,你要怎么处理?
+9. 分布式是什么?为什么要分布式?分布式又会有哪些问题?分布式系统是如何实现事物的?
+10. Redis集群了解吗?如何处理宕机的情况?Redis的同步策略?
+11. LRU算法了解吗?你会如何实现它?这个算法可以应用在哪些场景下?
+12. TCP为什么是三次握手?两次行不行?多次行不行?
+13. TCP的安全性是如何实现的?两台服务器之间可以同时建立多条TCP链接吗?怎么实现的?
+14. 客服端输入一个网址后,是如何拿到客服想要的数据的,是怎样在网络中传输的?
+15. cookie和session
+16. java有哪些锁?共享锁是什么?CAS?乐观锁和悲观锁?synchronied的底层原理?锁升级?死锁怎么形成的?如何破解死锁?
+
+## 二面
+
+
+1. Java容器:List,Set,Map
+2. Map的遍历方式
+3. HashMap扩容为什么是扩为两倍?
+4. Java线程同步机制(信号量,闭锁,栅栏)
+5. 对volatile的理解:常用于状态标记
+6. 八种基本数据类型的大小以及他们的封装类(顺带了解自动拆箱与装箱)
+7. 线程阻塞几种情况?如何自己实现阻塞队列?
+8. Java垃圾回收。可达性分析->引用级别->二次标记(finalize方法)->垃圾收集 算法(4个)->回收策略(3个)->垃圾收集器(GMS、G1)。
+9. java内存模型
+10. TCP/IP的理解
+11. 进程和线程的区别
+12. http状态码含义
+13. ThreadLocal(线程本地变量),如何实现一个本地缓存
+14. JVM内存区哪里会出现溢出?
+15. 双亲委派模型的理解,怎样将两个全路径相同的类加载到内存中?
+16. CMS收集器和G1收集器
+17. TCP流量控制和拥塞控制
+18. 服务器处理一个http请求的过程
+19. 例举几个Mysql优化手段
+20. 数据库死锁定义,怎样避免死锁
+21. spring的aop是什么?如何实现的
+22. 面向对象的设计原则
+23. 策略模式的实现
+24. 操作系统的内存管理的页面淘汰 算法 ,介绍下LRU(最近最少使用算法 )
+25. B+树的特点与优势
+
+## 三面
+
+- 自我介绍,说简历里没有的东西
+- 说几个你最近在看的技术(MySQL,多线程)
+- 口述了一个统计数据的场景题
+- 如果这个统计数据场景不用MySQL,而是用Java来实现,怎么做
+- 如果数据量过大,内存放不下呢
+- 用面向对象的思想解决上面提出的问题,创建出父类,子类,方法,说一下思路
+- 下一个场景,口述了一个登录场景,同学用线程池做登录校验,会有什么问题
+- 如何解决这些问题
+- 你给出的方案弊端在哪里,还有哪些方案
+
+## 四面
+
+- 谈谈类加载机制。
+- hashmap和concurenthashmap
+- 16g机器,让你分配jvm内存怎么分配。
+- 机器慢了怎么排查。
+- 谈谈consul和zookeeper,还有服务发现机制。
+- 详细说明raft协议。
+- 谈谈consul和zookeeper区别。
+- 服务注册的时候发现没有注册成功会是什么原因。
+- 讲讲你认为的rpc和service mesh之间的关系。
+
+
+
+**最后给大家分享一份精心整理的大厂高频面试题PDF,需要的小伙伴可以自行下载:**
+
+[大厂面试手册](http://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247485445&idx=1&sn=1c6e224b9bb3da457f5ee03894493dbc&chksm=ce98f543f9ef7c55325e3bf336607a370935a6c78dbb68cf86e59f5d68f4c51d175365a189f8#rd)
\ No newline at end of file
diff --git a/interview/6-meituan.md b/interview/6-meituan.md
new file mode 100644
index 0000000..5b183c0
--- /dev/null
+++ b/interview/6-meituan.md
@@ -0,0 +1,74 @@
+# 美团
+
+## 一面
+
+- 消息队列如何保证可靠性
+- 消息队列如何保证消息幂等性
+- 消息队列的优缺点
+- 为什么用b+树
+- 聚集索引和主键区别,其他引擎怎么做的
+- 平时数据库编码
+- explain参数
+- http报文参数有哪些吗?
+- 做题,链表奇偶有序输出
+
+## 二面
+
+- 自我介绍
+- 有哪些排序算法?
+- 介绍下快排/堆排/归并排序。
+- 数据库中的索引应该如何设计?
+- 有哪些索引失效的情况?
+- 你们用到的HTTP接口用到了什么提交方式?
+- GET/POST的区别?
+- 除了GET/POST还有哪些?
+- 面向对象的基本原则?再详细说下依赖倒转。
+- 介绍下策略模式和观察者模式?
+- 如何保证用户请求的等幂性?等幂性指的是用户可能连点提交三次支付请求,返回同样的结果(支付成功),但实际后台只执行一次,保持一致性。
+- 介绍下TCP四次挥手?
+- 第四次挥手后客户端是立刻就关闭了吗?是什么状态?
+- 两个大文件,分别每行都存一个url,查找两个文件中重复的url。
+- 一个大文件中,每一行有一个整数,怎么找第100大的数?
+- 一个大文件中,每一行有一个整数,怎么找中位数?
+- redis的基本数据结构?
+- zset是怎么实现的?有哪些命令?
+- 算法题 力扣221. 最大正方形
+
+## 三面
+
+- 项目相关(模块划分,项目需求,技术方案,数据库设计,表的结构及关系,担任角色)
+- http协议的关键字段,比如request和response头部信息有哪些关键字段,有什么含义
+- http状态码:100,200,502,504
+- http和https的区别,https是为了解决什么问题
+- 三次握手、四次挥手(详细过程+状态变化)
+- 出现大量的close_wait可能是什么原因,解决方案,通过什么工具看出来网络有问题等等
+- Java中常见的集合有哪些,List、Set、Map初始容量加载因子了解吗
+- Java中线程通信的方式有哪些,大概的原理
+- MySQL如果遇到性能不好的问题,比如说慢查询,怎么做
+- 数据库优化方案(索引 | 分库分表)
+- 有哪些索引,数据结构,建立索引的原则
+- 分库分表的原则,说说场景(水平 | 垂直、热数据 | 冷数据 blabla)
+- 算法题:两数之和
+
+## 四面
+
+- 自我介绍、项目介绍,问了数据量
+- 了解微服务吗?(有没有自己在做项目时进行调研,了解企业目前常用的工具、方法)
+- 了解springcloud吗?
+- 一台机器无法满足运载需求,怎么办呢?答:多搞几台机器,问:多台机器如何协同工作?
+- 解释一下mapreduce
+- 如果有一个很大的文件,TB级别,文件里是乱序的数字,如何排序?mapreduce如何实现?
+- 排序过程中的归并排序,请描述一下其过程?时间复杂度
+- 进程、线程区别,问使用Java时,里面多线程的概念和os里的线程进程的区别是什么?真正使用时,Java里的线程和进程是如何调度?
+- 多线程的同步互斥的方法?答了信号量,问具体怎么实现,答pv操作,给了具体的场景,问变量如何初始化(等同于口述代码)
+- 有哪些索引?(mysql为例)
+- b树、b+树是什么样的树结构,查询复杂度?是平衡二叉树吗?
+- 使用过redis吗?具体做什么?
+- 手撕代码:LRU算法;正反序层序遍历二叉树
+
+
+
+**最后给大家分享一份精心整理的大厂高频面试题PDF,需要的小伙伴可以自行下载:**
+
+[大厂面试手册](http://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247485445&idx=1&sn=1c6e224b9bb3da457f5ee03894493dbc&chksm=ce98f543f9ef7c55325e3bf336607a370935a6c78dbb68cf86e59f5d68f4c51d175365a189f8#rd)
+
diff --git a/interview/7-shopee.md b/interview/7-shopee.md
new file mode 100644
index 0000000..f548f56
--- /dev/null
+++ b/interview/7-shopee.md
@@ -0,0 +1,73 @@
+# shopee
+
+## 面经1
+
+消息队列如何保证可靠性
+消息队列如何保证消息幂等性
+消息队列的优缺点
+为什么用b+树
+聚集索引和主键区别,其他引擎怎么做的
+平时数据库编码
+explain参数
+http报文参数有哪些吗?
+做题,链表奇偶有序输出
+
+## 面经2
+
+1. 自我介绍
+2. 有哪些排序算法?
+3. 介绍下快排/堆排/归并排序。
+4. 数据库中的索引应该如何设计?
+5. 有哪些索引失效的情况?
+6. 你们用到的HTTP接口用到了什么提交方式?
+7. GET/POST的区别?
+8. 除了GET/POST还有哪些?
+9. 面向对象的基本原则?再详细说下依赖倒转。
+10. 介绍下策略模式和观察者模式?
+11. 如何保证用户请求的等幂性?等幂性指的是用户可能连点提交三次支付请求,返回同样的结果(支付成功),但实际后台只执行一次,保持一致性。
+12. 介绍下TCP四次挥手?
+13. 第四次挥手后客户端是立刻就关闭了吗?是什么状态?
+14. 两个大文件,分别每行都存一个url,查找两个文件中重复的url。
+15. 一个大文件中,每一行有一个整数,怎么找第100大的数?
+16. 一个大文件中,每一行有一个整数,怎么找中位数?
+17. redis的基本数据结构?
+18. zset是怎么实现的?有哪些命令?
+19. 算法题 力扣221. 最大正方形
+
+## 面经3
+
+自我介绍、项目介绍,问了数据量
+
+了解微服务吗?(有没有自己在做项目时进行调研,了解企业目前常用的工具、方法)
+
+了解springcloud吗?
+
+一台机器无法满足运载需求,怎么办呢?答:多搞几台机器,问:多台机器如何协同工作?
+
+开始瞎答:mapreduce
+
+解释一下mapreduce
+
+如果有一个很大的文件,TB级别,文件里是乱序的数字,如何排序?mapreduce如何实现?
+
+排序过程中的归并排序,请描述一下其过程?时间复杂度
+
+进程、线程区别,问使用Java时,里面多线程的概念和os里的线程进程的区别是什么?真正使用时,Java里的线程和进程是如何调度?
+
+多线程的同步互斥的方法?答了信号量,问具体怎么实现,答pv操作,给了具体的场景,问变量如何初始化(等同于口述代码)
+
+有哪些索引?(mysql为例)
+
+b树、b+树是什么样的树结构,查询复杂度?是平衡二叉树吗?
+
+使用过redis吗?具体做什么?
+
+手撕代码:LRU算法;正反序层序遍历二叉树
+
+
+
+
+
+**最后给大家分享一份精心整理的大厂高频面试题PDF,需要的小伙伴可以自行下载:**
+
+[大厂面试手册](http://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247485445&idx=1&sn=1c6e224b9bb3da457f5ee03894493dbc&chksm=ce98f543f9ef7c55325e3bf336607a370935a6c78dbb68cf86e59f5d68f4c51d175365a189f8#rd)
\ No newline at end of file
diff --git a/interview/8-jingdong.md b/interview/8-jingdong.md
new file mode 100644
index 0000000..aa06fb9
--- /dev/null
+++ b/interview/8-jingdong.md
@@ -0,0 +1,58 @@
+# 京东
+
+## 一面
+
+- kafka在应用场景以及 项目 里的实现
+- bitmap底层
+- object里有哪些方法
+- hashmap相关
+- sychronized和reentrantlock相关问题以及锁升级
+- cas和volatile
+- 线程几种状态以及转化
+- jvm内存模型
+- mybatis相关问题
+- Redis数据结构,问了下跳表的底层
+- RDB和AOF
+- MySQL索引有哪些
+- b+树底层实现
+- 最左前缀原理
+
+## 二面
+
+- 线程的状态
+- cms
+- 增量更新法
+- GcRoots是哪些
+- java基础
+- mysql索引
+- 项目具体实现
+
+## 三面
+
+- 索引
+- 谈谈多线程
+- jvm如何调优
+- mq在项目中的用法
+- 遇到的多线程问题,如何解决
+- 最长无重复字串
+- 找到A^2+B^2 = C
+
+## 四面
+
+- 数据库乐观锁、悲观锁
+- 为啥用Redis
+- sql语句执行顺序
+- SpringMVC优点,原理
+- aop优点,原理
+- ioc优点,原理
+- 面向对象概念
+- 封装
+- 项目中封装如何体现
+- 高内聚,低耦合啥意思,如何去设计
+- 设计一个电梯场景,实现面向对象,高内聚,低耦合的情况
+- 统计学校内共享单车数量,你有啥想法(开放题)
+
+**最后分享一个BAT大佬总结的高频面试题PDF,需要的小伙伴可以自行下载(复制链接到浏览器打开):**
+
+链接:https://pan.baidu.com/s/16GnVoALA1r6BhumuUrXIRg
+提取码:6666
diff --git a/interview/9-huawei.md b/interview/9-huawei.md
new file mode 100644
index 0000000..ffb1c83
--- /dev/null
+++ b/interview/9-huawei.md
@@ -0,0 +1,56 @@
+# 华为
+
+最近越来越多公司校招进入面试流程了,为了帮助大家更好的应对面试,大彬整理了往年华为校招面试的题目,供大家参考~
+
+## 面经1
+
+### 技术一面
+
+1. 自我介绍
+2. 说下项目中的难点
+3. volatile和synchronized的区别, 问的比较细
+4. 大顶堆小顶堆怎么删除根节点
+5. CSRF攻击是什么,怎么预防
+6. 线程通信方式。
+7. Volitate关键字。
+8. Java 高效拷贝数组。
+9. 算法题 跳跃游戏 leetcode 55。
+
+
+### 技术二面
+1. 上来就手撕代码 ,奇偶链表,leetcode原题,先说思路,然后打开ide共享屏幕撕代码
+2. 手写单例模式,并说为什么这样写,会不会有什么问题,涉及到volatile原理
+3. mysql常用的数据类型
+4. Java集合框架的主类是什么,HashSet有没有继承Collection软件工程学过哪些课程
+5. 软件工程学过哪些课程
+6. 进程和线程的区别
+7. 知道哪些排序算法,快排的时间复杂度是多少,是稳定的排序算法吗
+8. 编程题/算法
+题目大概:请输出两个字符串a和b相减的结果(a>b,a和b的字符串长度介于1~50之间)。
+例:输入a:“99999”,b=“99998”
+输出:“1”
+
+
+## 面经2
+### 华为一面
+1. 项目、论文。
+2. String能否被继承。
+3. Java内存泄露和排查。
+4. Hash方式和Hash冲突解决。
+5. 静态代理和动态代理。
+6. spring boot常用的注解有哪些
+7. spring boot的配置文件
+8. redis集群的几种方式详细说一下
+9. redis缓存雪崩,缓存击穿,缓存穿透是什么,怎么解决
+10. mysql索引相关,为什么用B+树
+11. 手撕代码,链表求和,leetcode原题:https://leetcode-cn.com/problems/sum-lists-lcci/
+
+### 华为二面
+- 是否用过Java、Python做系统的项目
+- 平时熟练使用哪种语言
+- HashMap、HashSet、HashTable、StringBuffer、StringBuilder哪些是线程安全,哪些是线程不安全
+- HashSet数据结构,跟HashMap有什么区别
+- char和varchar的区别
+- mysql建索引的原则,索引是不是越多越好,为什么
+- spring boot用到了哪些设计模式,从源码层面说说你熟悉的以及实现
+- jvm调优你用什么工具,具体怎么做的,怎么调优
diff --git a/interview/README.md b/interview/README.md
new file mode 100644
index 0000000..4ebcf45
--- /dev/null
+++ b/interview/README.md
@@ -0,0 +1,13 @@
+
+## 大厂面经汇总
+
+- [字节跳动](./1-byte-and-dance.md)
+- [腾讯](./2-tencent.md)
+- [百度](./3-baidu.md)
+- [阿里](./4-ali.md)
+- [快手](./5-kuaishou.md)
+- [美团](./6-meituan.md)
+- [shopee](./7-shopee.md)
+- [京东](./8-jingdong.md)
+- [华为](./9-huawei.md)
+- [网易](./10-netease.md)
From f6221f6632d461880411d1996780f5a4511cb15a Mon Sep 17 00:00:00 2001
From: tyson
Date: Sun, 6 Nov 2022 15:33:16 +0800
Subject: [PATCH 04/75] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=B8=B8=E8=A7=81?=
=?UTF-8?q?=E7=9A=84Exception=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
...\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md" | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
index 04f0e86..0992793 100644
--- "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
@@ -921,7 +921,7 @@ class BMWCar extends Car implements Alarm {
5. `NumberFormatException` //数字格式化异常
6. `ArithmeticException` //数学运算异常
-unchecked Exception:
+checked Exception:
1. `NoSuchFieldException` //反射异常,没有对应的字段
2. `ClassNotFoundException` //类没有找到异常
From 24a1369a74ca68d2d8049afcaf1258452b3dc793 Mon Sep 17 00:00:00 2001
From: tyson
Date: Sun, 6 Nov 2022 15:36:46 +0800
Subject: [PATCH 05/75] =?UTF-8?q?Java=E5=9F=BA=E7=A1=80=E9=9D=A2=E8=AF=95?=
=?UTF-8?q?=E9=A2=98=E8=A1=A5=E5=85=85?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
...00\351\235\242\350\257\225\351\242\230.md" | 414 +++++++++++++++---
1 file changed, 361 insertions(+), 53 deletions(-)
diff --git "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
index 0992793..a3a25bb 100644
--- "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
@@ -1,12 +1,19 @@
+---
+sidebar: heading
+
+---
+
+
+
## Java的特点
-**Java是一门面向对象的编程语言。**面向对象和面向过程的区别参考下一个问题。
+**Java是一门面向对象的编程语言**。面向对象和面向过程的区别参考下一个问题。
-**Java具有平台独立性和移植性。**
+**Java具有平台独立性和移植性**。
- Java有一句口号:`Write once, run anywhere`,一次编写、到处运行。这也是Java的魅力所在。而实现这种特性的正是Java虚拟机JVM。已编译的Java程序可以在任何带有JVM的平台上运行。你可以在windows平台编写代码,然后拿到linux上运行。只要你在编写完代码后,将代码编译成.class文件,再把class文件打成Java包,这个jar包就可以在不同的平台上运行了。
-**Java具有稳健性。**
+**Java具有稳健性**。
- Java是一个强类型语言,它允许扩展编译时检查潜在类型不匹配问题的功能。Java要求显式的方法声明,它不支持C风格的隐式声明。这些严格的要求保证编译程序能捕捉调用错误,这就导致更可靠的程序。
- 异常处理是Java中使得程序更稳健的另一个特征。异常是某种类似于错误的异常条件出现的信号。使用`try/catch/finally`语句,程序员可以找到出错的处理代码,这就简化了出错处理和恢复的任务。
@@ -19,32 +26,11 @@
- Java 支持自动垃圾回收,而 C++ 需要手动回收。
- Java 不支持多重继承,只能通过实现多个接口来达到相同目的,而 C++ 支持多重继承。
-## 面向对象和面向过程的区别?
+## JDK/JRE/JVM三者的关系
-面向对象和面向过程是一种软件开发思想。
-
-- 面向过程就是分析出解决问题所需要的步骤,然后用函数按这些步骤实现,使用的时候依次调用就可以了。
+**JVM**
-- 面向对象是把构成问题事务分解成各个对象,分别设计这些对象,然后将他们组装成有完整功能的系统。面向过程只用函数实现,面向对象是用类实现各个功能模块。
-
-以五子棋为例,面向过程的设计思路就是首先分析问题的步骤:
-
-1、开始游戏,2、黑子先走,3、绘制画面,4、判断输赢,5、轮到白子,6、绘制画面,7、判断输赢,8、返回步骤2,9、输出最后结果。
-把上面每个步骤用分别的函数来实现,问题就解决了。
-
-而面向对象的设计则是从另外的思路来解决问题。整个五子棋可以分为:
-
-1. 黑白双方
-2. 棋盘系统,负责绘制画面
-3. 规则系统,负责判定诸如犯规、输赢等。
-
-黑白双方负责接受用户的输入,并告知棋盘系统棋子布局发生变化,棋盘系统接收到了棋子的变化的信息就负责在屏幕上面显示出这种变化,同时利用规则系统来对棋局进行判定。
-
-## JKD/JRE/JVM三者的关系
-
-### JVM
-
-**JVM** :英文名称(Java Virtual Machine),就是我们耳熟能详的 Java 虚拟机。Java 能够跨平台运行的核心在于 JVM 。
+英文名称(Java Virtual Machine),就是我们耳熟能详的 Java 虚拟机。Java 能够跨平台运行的核心在于 JVM 。

@@ -52,7 +38,7 @@
针对不同的系统有不同的 jvm 实现,有 Linux 版本的 jvm 实现,也有Windows 版本的 jvm 实现,但是同一段代码在编译后的字节码是一样的。这就是Java能够跨平台,实现一次编写,多处运行的原因所在。
-### JRE
+**JRE**
英文名称(Java Runtime Environment),就是Java 运行时环境。我们编写的Java程序必须要在JRE才能运行。它主要包含两个部分,JVM 和 Java 核心类库。
@@ -62,31 +48,81 @@ JRE是Java的运行环境,并不是一个开发环境,所以没有包含任
如果你只是想运行Java程序,而不是开发Java程序的话,那么你只需要安装JRE即可。
-### JDK
+**JDK**
英文名称(Java Development Kit),就是 Java 开发工具包
学过Java的同学,都应该安装过JDK。当我们安装完JDK之后,目录结构是这样的
-
+
可以看到,JDK目录下有个JRE,也就是JDK中已经集成了 JRE,不用单独安装JRE。
另外,JDK中还有一些好用的工具,如jinfo,jps,jstack等。
-
+
+最后,总结一下JDK/JRE/JVM,他们三者的关系
+**JRE = JVM + Java 核心类库**
-### 总结
+**JDK = JRE + Java工具 + 编译器 + 调试器**
-最后,总结一下JDK/JRE/JVM,他们三者的关系
+
-JRE = JVM + Java 核心类库
+## Java程序是编译执行还是解释执行?
-JDK = JRE + Java工具 + 编译器 + 调试器
+先看看什么是编译型语言和解释型语言。
-
+**编译型语言**
+
+在程序运行之前,通过编译器将源程序编译成机器码可运行的二进制,以后执行这个程序时,就不用再进行编译了。
+
+优点:编译器一般会有预编译的过程对代码进行优化。因为编译只做一次,运行时不需要编译,所以编译型语言的程序执行效率高,可以脱离语言环境独立运行。
+
+缺点:编译之后如果需要修改就需要整个模块重新编译。编译的时候根据对应的运行环境生成机器码,不同的操作系统之间移植就会有问题,需要根据运行的操作系统环境编译不同的可执行文件。
+
+**总结**:执行速度快、效率高;依靠编译器、跨平台性差些。
+
+**代表语言**:C、C++、Pascal、Object-C以及Swift。
+
+**解释型语言**
+
+定义:解释型语言的源代码不是直接翻译成机器码,而是先翻译成中间代码,再由解释器对中间代码进行解释运行。在运行的时候才将源程序翻译成机器码,翻译一句,然后执行一句,直至结束。
+
+优点:
+
+1. 有良好的平台兼容性,在任何环境中都可以运行,前提是安装了解释器(如虚拟机)。
+2. 灵活,修改代码的时候直接修改就可以,可以快速部署,不用停机维护。
+
+缺点:每次运行的时候都要解释一遍,性能上不如编译型语言。
+
+总结:解释型语言执行速度慢、效率低;依靠解释器、跨平台性好。
+
+代表语言:JavaScript、Python、Erlang、PHP、Perl、Ruby。
+
+对于Java这种语言,它的**源代码**会先通过javac编译成**字节码**,再通过jvm将字节码转换成**机器码**执行,即解释运行 和编译运行配合使用,所以可以称为混合型或者半编译型。
+
+## 面向对象和面向过程的区别?
+
+面向对象和面向过程是一种软件开发思想。
+
+- 面向过程就是分析出解决问题所需要的步骤,然后用函数按这些步骤实现,使用的时候依次调用就可以了。
+
+- 面向对象是把构成问题事务分解成各个对象,分别设计这些对象,然后将他们组装成有完整功能的系统。面向过程只用函数实现,面向对象是用类实现各个功能模块。
+
+以五子棋为例,面向过程的设计思路就是首先分析问题的步骤:
+
+1、开始游戏,2、黑子先走,3、绘制画面,4、判断输赢,5、轮到白子,6、绘制画面,7、判断输赢,8、返回步骤2,9、输出最后结果。
+把上面每个步骤用分别的函数来实现,问题就解决了。
+
+而面向对象的设计则是从另外的思路来解决问题。整个五子棋可以分为:
+
+1. 黑白双方
+2. 棋盘系统,负责绘制画面
+3. 规则系统,负责判定诸如犯规、输赢等。
+
+黑白双方负责接受用户的输入,并告知棋盘系统棋子布局发生变化,棋盘系统接收到了棋子的变化的信息就负责在屏幕上面显示出这种变化,同时利用规则系统来对棋局进行判定。
## 面向对象有哪些特性?
@@ -103,6 +139,27 @@ JDK = JRE + Java工具 + 编译器 + 调试器
4、抽象。把客观事物用代码抽象出来。
+## 数组到底是不是对象?
+
+先说说对象的概念。对象是根据某个类创建出来的一个实例,表示某类事物中一个具体的个体。
+
+对象具有各种属性,并且具有一些特定的行为。站在计算机的角度,对象就是内存中的一个内存块,在这个内存块封装了一些数据,也就是类中定义的各个属性。
+
+所以,对象是用来封装数据的。
+
+java中的数组具有java中其他对象的一些基本特点。比如封装了一些数据,可以访问属性,也可以调用方法。
+
+因此,可以说,数组是对象。
+
+也可以通过代码验证数组是对象的事实。比如以下的代码,输出结果为java.lang.Object。
+
+```java
+Class clz = int[].class;
+System.out.println(clz.getSuperclass().getName());
+```
+
+由此,可以看出,数组类的父类就是Object类,那么可以推断出数组就是对象。
+
## Java的基本数据类型有哪些?
- byte,8bit
@@ -119,6 +176,8 @@ JDK = JRE + Java工具 + 编译器 + 调试器
| 二进制位数 | 1 | 8 | 16 | 16 | 32 | 64 | 32 | 64 |
| 包装类 | Boolean | Byte | Character | Short | Integer | Long | Float | Double |
+在Java规范中,没有明确指出boolean的大小。在《Java虚拟机规范》给出了单个boolean占4个字节,和boolean数组1个字节的定义,具体 **还要看虚拟机实现是否按照规范来**,因此boolean占用1个字节或者4个字节都是有可能的。
+
## 为什么不能用浮点型表示金额?
由于计算机中保存的小数其实是十进制的小数的近似值,并不是准确值,所以,千万不要在代码中使用浮点数来表示金额等重要的指标。
@@ -127,8 +186,10 @@ JDK = JRE + Java工具 + 编译器 + 调试器
## 什么是值传递和引用传递?
-- 值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量。
-- 引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本, 并不是原对象本身 。所以对引用对象进行操作会同时改变原对象。
+- 值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量。
+- 引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本,并不是原对象本身,两者指向同一片内存空间。所以对引用对象进行操作会同时改变原对象。
+
+**java中不存在引用传递,只有值传递**。即不存在变量a指向变量b,变量b指向对象的这种情况。
## 了解Java的包装类型吗?为什么需要包装类?
@@ -228,7 +289,7 @@ private static class IntegerCache {
}
```
-这是IntegerCache静态代码块中的一段,默认Integer cache 的下限是-128,上限默认127。当赋值100给Integer时,刚好在这个范围内,所以从cache中取对应的Integer并返回,所以a和b返回的是同一个对象,所以==比较是相等的,当赋值200给Integer时,不在cache 的范围内,所以会new Integer并返回,当然==比较的结果是不相等的。
+这是IntegerCache静态代码块中的一段,默认Integer cache 的下限是-128,上限默认127。当赋值100给Integer时,刚好在这个范围内,所以从cache中取对应的Integer并返回,所以a和b返回的是同一个对象,所以`==`比较是相等的,当赋值200给Integer时,不在cache 的范围内,所以会new Integer并返回,当然`==`比较的结果是不相等的。
## String 为什么不可变?
@@ -262,14 +323,26 @@ String类内部所有的字段都是私有的,也就是被private修饰。而
主要有以下几点原因:
1. **线程安全**。同一个字符串实例可以被多个线程共享,因为字符串不可变,本身就是线程安全的。
-2. **支持hash映射和缓存。**因为String的hash值经常会使用到,比如作为 Map 的键,不可变的特性使得 hash 值也不会变,不需要重新计算。
+2. **支持hash映射和缓存**。因为String的hash值经常会使用到,比如作为 Map 的键,不可变的特性使得 hash 值也不会变,不需要重新计算。
3. **出于安全考虑**。网络地址URL、文件路径path、密码通常情况下都是以String类型保存,假若String不是固定不变的,将会引起各种安全隐患。比如将密码用String的类型保存,那么它将一直留在内存中,直到垃圾收集器把它清除。假如String类不是固定不变的,那么这个密码可能会被改变,导致出现安全隐患。
-3. **字符串常量池优化**。String对象创建之后,会缓存到字符串常量池中,下次需要创建同样的对象时,可以直接返回缓存的引用。
+4. **字符串常量池优化**。String对象创建之后,会缓存到字符串常量池中,下次需要创建同样的对象时,可以直接返回缓存的引用。
既然我们的String是不可变的,它内部还有很多substring, replace, replaceAll这些操作的方法。这些方法好像会改变String对象?怎么解释呢?
其实不是的,我们每次调用replace等方法,其实会在堆内存中创建了一个新的对象。然后其value数组引用指向不同的对象。
+## 为何JDK9要将String的底层实现由char[]改成byte[]?
+
+主要是为了**节约String占用的内存**。
+
+在大部分Java程序的堆内存中,String占用的空间最大,并且绝大多数String只有Latin-1字符,这些Latin-1字符只需要1个字节就够了。
+
+而在JDK9之前,JVM因为String使用char数组存储,每个char占2个字节,所以即使字符串只需要1字节,它也要按照2字节进行分配,浪费了一半的内存空间。
+
+到了JDK9之后,对于每个字符串,会先判断它是不是只有Latin-1字符,如果是,就按照1字节的规格进行分配内存,如果不是,就按照2字节的规格进行分配,这样便提高了内存使用率,同时GC次数也会减少,提升效率。
+
+不过Latin-1编码集支持的字符有限,比如不支持中文字符,因此对于中文字符串,用的是UTF16编码(两个字节),所以用byte[]和char[]实现没什么区别。
+
## String, StringBuffer 和 StringBuilder区别
**1. 可变性**
@@ -283,6 +356,62 @@ String类内部所有的字段都是私有的,也就是被private修饰。而
- StringBuilder 不是线程安全的
- StringBuffer 是线程安全的,内部使用 synchronized 进行同步
+## 什么是StringJoiner?
+
+StringJoiner是 Java 8 新增的一个 API,它基于 StringBuilder 实现,用于实现对字符串之间通过分隔符拼接的场景。
+
+StringJoiner 有两个构造方法,第一个构造要求依次传入分隔符、前缀和后缀。第二个构造则只要求传入分隔符即可(前缀和后缀默认为空字符串)。
+
+```java
+StringJoiner(CharSequence delimiter, CharSequence prefix, CharSequence suffix)
+StringJoiner(CharSequence delimiter)
+```
+
+有些字符串拼接场景,使用 StringBuffer 或 StringBuilder 则显得比较繁琐。
+
+比如下面的例子:
+
+```java
+List values = Arrays.asList(1, 3, 5);
+StringBuilder sb = new StringBuilder("(");
+
+for (int i = 0; i < values.size(); i++) {
+ sb.append(values.get(i));
+ if (i != values.size() -1) {
+ sb.append(",");
+ }
+}
+
+sb.append(")");
+```
+
+而通过StringJoiner来实现拼接List的各个元素,代码看起来更加简洁。
+
+```java
+List values = Arrays.asList(1, 3, 5);
+StringJoiner sj = new StringJoiner(",", "(", ")");
+
+for (Integer value : values) {
+ sj.add(value.toString());
+}
+```
+
+另外,像平时经常使用的Collectors.joining(","),底层就是通过StringJoiner实现的。
+
+源码如下:
+
+```java
+public static Collector joining(
+ CharSequence delimiter,CharSequence prefix,CharSequence suffix) {
+ return new CollectorImpl<>(
+ () -> new StringJoiner(delimiter, prefix, suffix),
+ StringJoiner::add, StringJoiner::merge,
+ StringJoiner::toString, CH_NOID);
+}
+```
+
+
+
## String 类的常用方法有哪些?
- indexOf():返回指定字符的索引。
@@ -308,6 +437,49 @@ String类内部所有的字段都是私有的,也就是被private修饰。而
字符串常量池(String Pool)保存着所有字符串字面量,这些字面量在编译时期就确定。字符串常量池位于堆内存中,专门用来存储字符串常量。在创建字符串时,JVM首先会检查字符串常量池,如果该字符串已经存在池中,则返回其引用,如果不存在,则创建此字符串并放入池中,并返回其引用。
+## String最大长度是多少?
+
+String类提供了一个length方法,返回值为int类型,而int的取值上限为2^31 -1。
+
+所以理论上String的最大长度为2^31 -1。
+
+**达到这个长度的话需要多大的内存吗**?
+
+String内部是使用一个char数组来维护字符序列的,一个char占用两个字节。如果说String最大长度是2^31 -1的话,那么最大的字符串占用内存空间约等于4GB。
+
+也就是说,我们需要有大于4GB的JVM运行内存才行。
+
+**那String一般都存储在JVM的哪块区域呢**?
+
+字符串在JVM中的存储分两种情况,一种是String对象,存储在JVM的堆栈中。一种是字符串常量,存储在常量池里面。
+
+**什么情况下字符串会存储在常量池呢**?
+
+当通过字面量进行字符串声明时,比如String s = "程序新大彬";,这个字符串在编译之后会以常量的形式进入到常量池。
+
+**那常量池中的字符串最大长度是2^31-1吗**?
+
+不是的,常量池对String的长度是有另外限制的。。Java中的UTF-8编码的Unicode字符串在常量池中以CONSTANT_Utf8类型表示。
+
+```java
+CONSTANT_Utf8_info {
+ u1 tag;
+ u2 length;
+ u1 bytes[length];
+}
+```
+
+length在这里就是代表字符串的长度,length的类型是u2,u2是无符号的16位整数,也就是说最大长度可以做到2^16-1 即 65535。
+
+不过javac编译器做了限制,需要length < 65535。所以字符串常量在常量池中的最大长度是65535 - 1 = 65534。
+
+最后总结一下:
+
+String在不同的状态下,具有不同的长度限制。
+
+- 字符串常量长度不能超过65534
+- 堆内字符串的长度不超过2^31-1
+
## Object常用方法有哪些?
Java面试经常会出现的一道题目,Object的常用方法。下面给大家整理一下。
@@ -844,7 +1016,7 @@ public class B extends A {
## 方法重载和重写的区别?
-**同个类中的多个方法可以有相同的方法名称,但是有不同的参数列表,这就称为方法重载。**参数列表又叫参数签名,包括参数的类型、参数的个数、参数的顺序,只要有一个不同就叫做参数列表不同。
+**同个类中的多个方法可以有相同的方法名称,但是有不同的参数列表,这就称为方法重载**。参数列表又叫参数签名,包括参数的类型、参数的个数、参数的顺序,只要有一个不同就叫做参数列表不同。
重载是面向对象的一个基本特性。
@@ -862,7 +1034,7 @@ public class OverrideTest {
}
```
-**方法的重写描述的是父类和子类之间的。当父类的功能无法满足子类的需求,可以在子类对方法进行重写。**方法重写时, 方法名与形参列表必须一致。
+**方法的重写描述的是父类和子类之间的。当父类的功能无法满足子类的需求,可以在子类对方法进行重写**。方法重写时, 方法名与形参列表必须一致。
如下代码,Person为父类,Student为子类,在Student中重写了dailyTask方法。
@@ -888,9 +1060,9 @@ public class Student extends Person {
1、**语法层面**上的区别
-- 抽象类可以有方法实现,而接口的方法中只能是抽象方法(Java 8 开始接口方法可以有默认实现);
+- 抽象类可以有方法实现,而接口的方法中只能是抽象方法(Java 8 之后接口方法可以有默认实现);
- 抽象类中的成员变量可以是各种类型的,接口中的成员变量只能是public static final类型;
-- 接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;
+- 接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法(Java 8之后接口可以有静态方法);
- 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
2、**设计层面**上的区别
@@ -1071,17 +1243,60 @@ Java不支持多继承的原因:
- Optional 类 :Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。
- Date Time API :加强对日期与时间的处理。
-> [Java8 新特性总结]([Java-learning/Java8.md at master · Tyson0314/Java-learning (github.com)](https://github.com/Tyson0314/Java-learning/blob/master/Java/Java8.md))
+> [Java8 新特性总结](https://github.com/Tyson0314/Java-learning/blob/master/Java/Java8新特性.md)
+
+## 序列化和反序列化
+
+- 序列化:把对象转换为字节序列的过程称为对象的序列化.
+- 反序列化:把字节序列恢复为对象的过程称为对象的反序列化.
+
+## 什么时候需要用到序列化和反序列化呢?
+
+当我们只在本地 JVM 里运行下 Java 实例,这个时候是不需要什么序列化和反序列化的,但当我们需要将内存中的对象持久化到磁盘,数据库中时,当我们需要与浏览器进行交互时,当我们需要实现 RPC 时,这个时候就需要序列化和反序列化了.
+
+前两个需要用到序列化和反序列化的场景,是不是让我们有一个很大的疑问? 我们在与浏览器交互时,还有将内存中的对象持久化到数据库中时,好像都没有去进行序列化和反序列化,因为我们都没有实现 Serializable 接口,但一直正常运行.
+
+下面先给出结论:
+
+**只要我们对内存中的对象进行持久化或网络传输,这个时候都需要序列化和反序列化.**
+
+理由:
+
+服务器与浏览器交互时真的没有用到 Serializable 接口吗? JSON 格式实际上就是将一个对象转化为字符串,所以服务器与浏览器交互时的数据格式其实是字符串,我们来看来 String 类型的源码:
+
+```
+public final class String
+ implements java.io.Serializable,Comparable,CharSequence {
+ /\*\* The value is used for character storage. \*/
+ private final char value\[\];
+
+ /\*\* Cache the hash code for the string \*/
+ private int hash; // Default to 0
+
+ /\*\* use serialVersionUID from JDK 1.0.2 for interoperability \*/
+ private static final long serialVersionUID = -6849794470754667710L;
+
+ ......
+}
+```
+
+String 类型实现了 Serializable 接口,并显示指定 serialVersionUID 的值.
-## 什么是序列化和反序列化?
+然后我们再来看对象持久化到数据库中时的情况,Mybatis 数据库映射文件里的 insert 代码:
+
+```
+
+ INSERT INTO t\_user(name,age) VALUES (#{name},#{age})
+
+```
-序列化:把内存中的对象转换为字节序列的过程。
+实际上我们并不是将整个对象持久化到数据库中,而是将对象中的属性持久化到数据库中,而这些属性(如Date/String)都实现了 Serializable 接口。
-反序列化:把字节序列恢复为Java对象的过程。
+## 实现序列化和反序列化为什么要实现 Serializable 接口?
-## 如何实现序列化
+在 Java 中实现了 Serializable 接口后, JVM 在类加载的时候就会发现我们实现了这个接口,然后在初始化实例对象的时候就会在底层帮我们实现序列化和反序列化。
-实现`Serializable`接口即可。序列化的时候(如`objectOutputStream.writeObject(user)`),会判断user是否实现了`Serializable`,如果对象没有实现`Serializable`接口,在序列化的时候会抛出`NotSerializableException`异常。源码如下:
+如果被写对象类型不是String、数组、Enum,并且没有实现Serializable接口,那么在进行序列化的时候,将抛出NotSerializableException。源码如下:
```java
// remaining cases
@@ -1103,6 +1318,20 @@ if (obj instanceof String) {
}
```
+## 实现 Serializable 接口之后,为什么还要显示指定 serialVersionUID 的值?
+
+如果不显示指定 serialVersionUID,JVM 在序列化时会根据属性自动生成一个 serialVersionUID,然后与属性一起序列化,再进行持久化或网络传输. 在反序列化时,JVM 会再根据属性自动生成一个新版 serialVersionUID,然后将这个新版 serialVersionUID 与序列化时生成的旧版 serialVersionUID 进行比较,如果相同则反序列化成功,否则报错.
+
+如果显示指定了 serialVersionUID,JVM 在序列化和反序列化时仍然都会生成一个 serialVersionUID,但值为我们显示指定的值,这样在反序列化时新旧版本的 serialVersionUID 就一致了.
+
+如果我们的类写完后不再修改,那么不指定serialVersionUID,不会有问题,但这在实际开发中是不可能的,我们的类会不断迭代,一旦类被修改了,那旧对象反序列化就会报错。 所以在实际开发中,我们都会显示指定一个 serialVersionUID。
+
+## static 属性为什么不会被序列化?
+
+因为序列化是针对对象而言的,而 static 属性优先于对象存在,随着类的加载而加载,所以不会被序列化.
+
+看到这个结论,是不是有人会问,serialVersionUID 也被 static 修饰,为什么 serialVersionUID 会被序列化? 其实 serialVersionUID 属性并没有被序列化,JVM 在序列化对象时会自动生成一个 serialVersionUID,然后将我们显示指定的 serialVersionUID 属性值赋给自动生成的 serialVersionUID.
+
## transient关键字的作用?
Java语言的关键字,变量修饰符,如果用transient声明一个实例变量,当对象存储时,它的值不需要维持。
@@ -1128,8 +1357,87 @@ Java泛型是JDK 5中引⼊的⼀个新特性, 允许在定义类和接口的
泛型最⼤的好处是可以提⾼代码的复⽤性。以List接口为例,我们可以将String、 Integer等类型放⼊List中, 如不⽤泛型, 存放String类型要写⼀个List接口, 存放Integer要写另外⼀个List接口, 泛型可以很好的解决这个问题。
-## String为什么不可变?
+## 如何停止一个正在运行的线程?
+
+有几种方式。
+
+1、**使用线程的stop方法**。
+
+使用stop()方法可以强制终止线程。不过stop是一个被废弃掉的方法,不推荐使用。
+
+使用Stop方法,会一直向上传播ThreadDeath异常,从而使得目标线程解锁所有锁住的监视器,即释放掉所有的对象锁。使得之前被锁住的对象得不到同步的处理,因此可能会造成数据不一致的问题。
+
+2、**使用interrupt方法中断线程**,该方法只是告诉线程要终止,但最终何时终止取决于计算机。调用interrupt方法仅仅是在当前线程中打了一个停止的标记,并不是真的停止线程。
+
+接着调用 Thread.currentThread().isInterrupted()方法,可以用来判断当前线程是否被终止,通过这个判断我们可以做一些业务逻辑处理,通常如果isInterrupted返回true的话,会抛一个中断异常,然后通过try-catch捕获。
+
+3、**设置标志位**
+
+设置标志位,当标识位为某个值时,使线程正常退出。设置标志位是用到了共享变量的方式,为了保证共享变量在内存中的可见性,可以使用volatile修饰它,这样的话,变量取值始终会从主存中获取最新值。
+
+但是这种volatile标记共享变量的方式,在线程发生阻塞时是无法完成响应的。比如调用Thread.sleep() 方法之后,线程处于不可运行状态,即便是主线程修改了共享变量的值,该线程此时根本无法检查循环标志,所以也就无法实现线程中断。
+
+因此,interrupt() 加上手动抛异常的方式是目前中断一个正在运行的线程**最为正确**的方式了。
+
+## 什么是跨域?
+
+简单来讲,跨域是指从一个域名的网页去请求另一个域名的资源。由于有**同源策略**的关系,一般是不允许这么直接访问的。但是,很多场景经常会有跨域访问的需求,比如,在前后端分离的模式下,前后端的域名是不一致的,此时就会发生跨域问题。
+
+**那什么是同源策略呢**?
+
+所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。
+
+同源策略限制以下几种行为:
+
+```mipsasm
+1. Cookie、LocalStorage 和 IndexDB 无法读取
+2. DOM 和 Js对象无法获得
+3. AJAX 请求不能发送
+```
+
+**为什么要有同源策略**?
+
+举个例子,假如你刚刚在网银输入账号密码,查看了自己的余额,然后再去访问其他带颜色的网站,这个网站可以访问刚刚的网银站点,并且获取账号密码,那后果可想而知。因此,从安全的角度来讲,同源策略是有利于保护网站信息的。
+
+## 跨域问题怎么解决呢?
+
+嗯,有以下几种方法:
+
+**CORS**,跨域资源共享
+
+CORS(Cross-origin resource sharing),跨域资源共享。CORS 其实是浏览器制定的一个规范,浏览器会自动进行 CORS 通信,它的实现主要在服务端,通过一些 HTTP Header 来限制可以访问的域,例如页面 A 需要访问 B 服务器上的数据,如果 B 服务器 上声明了允许 A 的域名访问,那么从 A 到 B 的跨域请求就可以完成。
+
+**@CrossOrigin注解**
+
+如果项目使用的是Springboot,可以在Controller类上添加一个 @CrossOrigin(origins ="*") 注解就可以实现对当前controller 的跨域访问了,当然这个标签也可以加到方法上,或者直接加到入口类上对所有接口进行跨域处理。注意SpringMVC的版本要在4.2或以上版本才支持@CrossOrigin。
+
+**nginx反向代理接口跨域**
+
+nginx反向代理跨域原理如下: 首先同源策略是浏览器的安全策略,不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就不存在跨越问题。
+
+nginx反向代理接口跨域实现思路如下:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。
+
+```js
+// proxy服务器
+server {
+ listen 81;
+ server_name www.domain1.com;
+ location / {
+ proxy_pass http://www.domain2.com:8080; #反向代理
+ proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
+ index index.html index.htm;
+
+ add_header Access-Control-Allow-Origin http://www.domain1.com;
+ }
+}
+```
+
+这样我们的前端代理只要访问 http:www.domain1.com:81/*就可以了。
+
+**通过jsonp跨域**
+
+通常为了减轻web服务器的负载,我们把js、css,img等静态资源分离到另一台独立域名的服务器上,在html页面中再通过相应的标签从不同域名下加载静态资源,这是浏览器允许的操作,基于此原理,我们可以通过动态创建script,再请求一个带参网址实现跨域通信。
-
+
\ No newline at end of file
From f3e1054917b4db4d607f8a32afb45931733b47bf Mon Sep 17 00:00:00 2001
From: tyson
Date: Sun, 6 Nov 2022 16:04:52 +0800
Subject: [PATCH 06/75] update
---
...7\272\347\241\200\351\235\242\350\257\225\351\242\230.md" | 5 -----
1 file changed, 5 deletions(-)
diff --git "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md" "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
index a3a25bb..bd5b11e 100644
--- "a/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
+++ "b/Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md"
@@ -1,8 +1,3 @@
----
-sidebar: heading
-
----
-

## Java的特点
From 72f3eb86e253ca44a5a1a13b360f66c3500eab66 Mon Sep 17 00:00:00 2001
From: tyson
Date: Fri, 23 Dec 2022 09:22:56 +0800
Subject: [PATCH 07/75] =?UTF-8?q?=E9=87=8D=E6=9E=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.gitignore | 23 +-
CNAME | 1 +
Java/JVM.md | 831 ---
Java/Java web.md | 83 -
...26\347\250\213\346\200\235\346\203\263.md" | 3916 -----------
...va\345\205\263\351\224\256\345\255\227.md" | 205 -
"Java/Java\345\237\272\347\241\200.md" | 1582 -----
...66\345\217\221\347\274\226\347\250\213.md" | 392 --
...70\347\224\250\346\226\271\346\263\225.md" | 241 -
"Java/\345\271\266\345\217\221.md" | 1166 ----
"Java/\351\233\206\345\220\210.md" | 479 --
README.md | 141 +-
...23\345\255\230\345\207\273\347\251\277.md" | 60 -
docs/.vuepress/config.ts | 86 +
docs/.vuepress/enhanceApp.js | 13 +
docs/.vuepress/navbar.ts | 276 +
docs/.vuepress/sidebar.ts | 176 +
docs/.vuepress/styles/index.scss | 0
docs/.vuepress/styles/palette.scss | 0
docs/.vuepress/theme.ts | 166 +
.../vuepress-sidebar-auto.js | 92 +
docs/README.md | 82 +
docs/about/contact.md | 27 +
docs/about/introduce.md | 66 +
docs/advance/concurrent/1-current-limiting.md | 139 +
docs/advance/concurrent/2-load-balance.md | 164 +
docs/advance/concurrent/README.md | 4 +
.../advance/design-pattern-all.md | 0
docs/advance/design-pattern/1-principle.md | 9 +
docs/advance/design-pattern/10-observer.md | 17 +
docs/advance/design-pattern/11-proxy.md | 77 +
docs/advance/design-pattern/12-builder.md | 79 +
docs/advance/design-pattern/2-singleton.md | 94 +
docs/advance/design-pattern/3-factory.md | 91 +
docs/advance/design-pattern/4-template.md | 82 +
docs/advance/design-pattern/5-strategy.md | 111 +
docs/advance/design-pattern/6-chain.md | 190 +
docs/advance/design-pattern/7-iterator.md | 30 +
docs/advance/design-pattern/8-decorator.md | 20 +
docs/advance/design-pattern/9-adapter.md | 18 +
docs/advance/design-pattern/README.md | 25 +
.../advance/distributed/1-global-unique-id.md | 30 +-
.../advance/distributed/2-distributed-lock.md | 4 +-
.../advance/distributed/3-rpc.md | 4 +-
docs/advance/distributed/4-micro-service.md | 183 +
docs/advance/distributed/5-distibuted-arch.md | 3 +
.../distributed/6-distributed-transaction.md | 197 +
docs/advance/distributed/README.md | 8 +
.../excellent-article/1-redis-stock-minus.md | 317 +
.../excellent-article/10-file-upload.md | 341 +
.../11-8-architect-pattern.md | 3 +
.../12-mysql-table-max-rows.md | 212 +
.../excellent-article/13-order-by-work.md | 263 +
.../excellent-article/14-architect-forward.md | 49 +
.../excellent-article/15-http-vs-rpc.md | 265 +
.../excellent-article/16-what-is-jwt.md | 167 +
.../excellent-article/2-spring-transaction.md | 111 +
.../3-springboot-auto-assembly.md | 544 ++
.../4-remove-duplicate-code.md | 595 ++
.../excellent-article/5-jvm-optimize.md | 94 +
.../excellent-article/6-spring-three-cache.md | 119 +
.../excellent-article/7-sql-optimize.md | 421 ++
.../8-interface-idempotent.md | 88 +
.../excellent-article/9-jvm-optimize-param.md | 242 +
docs/advance/excellent-article/README.md | 19 +
.../system-design/1-scan-code-login.md | 43 +-
.../2-order-timeout-auto-cancel.md | 616 ++
docs/advance/system-design/3-file-send.md | 17 +
.../advance/system-design/3-short-url.md | 60 +-
docs/advance/system-design/4-oversold.md | 30 +
docs/advance/system-design/5-second-kill.md | 45 +
.../6-wechat-redpacket-design.md | 134 +
docs/advance/system-design/7-file-send.md | 17 +
docs/advance/system-design/8-sso-design.md | 113 +
docs/advance/system-design/README.md | 11 +
...3\346\215\267\346\226\271\345\274\217.lnk" | Bin 0 -> 705 bytes
docs/campus-recruit/README.md | 3 +
docs/campus-recruit/career-plan.md | 37 +
.../interview}/1-byte-and-dance.md | 0
.../campus-recruit/interview}/10-netease.md | 0
.../campus-recruit/interview}/2-tencent.md | 2 +-
.../campus-recruit/interview}/3-baidu.md | 0
.../campus-recruit/interview}/4-ali.md | 0
.../campus-recruit/interview}/5-kuaishou.md | 0
.../campus-recruit/interview}/6-meituan.md | 0
.../campus-recruit/interview}/7-shopee.md | 0
.../campus-recruit/interview}/8-jingdong.md | 0
.../campus-recruit/interview}/9-huawei.md | 0
.../campus-recruit/interview}/README.md | 0
docs/campus-recruit/layoffs-solution.md | 24 +
.../campus-recruit/program-language/README.md | 5 +
.../program-language/java-or-c++.md | 34 +
.../program-language/java-or-golang.md | 73 +
docs/campus-recruit/project-experience.md | 124 +
docs/campus-recruit/resume.md | 136 +
docs/campus-recruit/share/1-23-backend.md | 108 +
docs/campus-recruit/share/2-no-offer.md | 56 +
docs/campus-recruit/share/README.md | 5 +
docs/computer-basic/algorithm.md | 1440 ++++
.../computer-basic/data-structure.md | 128 +-
docs/computer-basic/network.md | 601 ++
.../computer-basic/operate-system.md | 10 +-
.../\345\233\276\350\247\243HTTP.md" | 6 +-
docs/database/es/1-es-architect.md | 53 +
.../database/mysql-basic-all.md | 224 +-
docs/database/mysql-basic/1-data-type.md | 82 +
docs/database/mysql-basic/10-view.md | 50 +
docs/database/mysql-basic/11-procedure.md | 101 +
docs/database/mysql-basic/12-cursor.md | 58 +
docs/database/mysql-basic/13-trigger.md | 51 +
docs/database/mysql-basic/14-transaction.md | 32 +
docs/database/mysql-basic/15-permission.md | 35 +
.../mysql-basic/16-performace-optimization.md | 18 +
docs/database/mysql-basic/17-index.md | 36 +
docs/database/mysql-basic/2-basic-command.md | 230 +
docs/database/mysql-basic/3-function.md | 42 +
docs/database/mysql-basic/4-sum.md | 22 +
docs/database/mysql-basic/5-group.md | 56 +
docs/database/mysql-basic/6-join.md | 92 +
.../database/mysql-basic/7-full-text-query.md | 39 +
docs/database/mysql-basic/8-table-operate.md | 105 +
docs/database/mysql-basic/9-column-operate.md | 48 +
docs/database/mysql-basic/README.md | 31 +
.../database/mysql-execution-plan.md | 38 +-
.../database/mysql.md | 221 +-
docs/distributed/1-global-unique-id.md | 489 ++
docs/distributed/2-distributed-lock.md | 259 +
docs/distributed/2.1-rpc.md | 123 +
.../distributed/3-micro-service.md | 6 +-
docs/distributed/4-distibuted-arch.md | 3 +
docs/distributed/5-distributed-transaction.md | 199 +
...PC\347\232\204\345\216\237\347\220\206.md" | 0
...15\347\275\256\344\270\255\345\277\203.md" | 0
.../framework/mybatis.md | 15 +-
.../framework/netty-overview.md | 245 +-
docs/framework/netty/1-overview.md | 16 +
docs/framework/netty/10-encoder-decoder.md | 146 +
.../netty/11-preset-channel-handler.md | 409 ++
docs/framework/netty/2-make-your-app.md | 186 +
docs/framework/netty/3-component.md | 70 +
docs/framework/netty/4-transport.md | 66 +
docs/framework/netty/6-channel-handler.md | 88 +
docs/framework/netty/7-channel-pipeline.md | 79 +
.../netty/8-eventloop-thread-model.md | 46 +
docs/framework/netty/9-guide.md | 57 +
docs/framework/netty/README.md | 24 +
.../framework/spring.md | 64 +-
.../framework/springboot.md | 65 +-
docs/framework/springcloud-interview.md | 249 +
.../framework/springcloud-overview.md | 110 +-
docs/framework/springcloud/1-basic.md | 48 +
docs/framework/springcloud/10-bus.md | 6 +
docs/framework/springcloud/11-security.md | 28 +
.../2-springboot-springcloud-diff.md | 9 +
docs/framework/springcloud/3-eureka.md | 117 +
docs/framework/springcloud/4-ribbon.md | 132 +
docs/framework/springcloud/5-hystrix.md | 82 +
docs/framework/springcloud/6-feign.md | 120 +
docs/framework/springcloud/7-zuul.md | 278 +
docs/framework/springcloud/8-gateway.md | 407 ++
docs/framework/springcloud/9-config.md | 274 +
docs/framework/springcloud/README.md | 22 +
docs/framework/springmvc.md | 188 +
.../concurrent/1-forbid-default-executor.md | 43 +
docs/interview/java/1-create-object.md | 9 +
docs/interview/java/2-3rd-interface.md | 13 +
docs/interview/java/3-design-interface.md | 11 +
docs/interview/java/4-interface-slow.md | 19 +
.../java/5-comparable-vs-comparator.md | 14 +
docs/interview/java/README.md | 7 +
.../interview/javaweb/1-interceptor-filter.md | 27 +
docs/interview/mq/1-why-to-use-mq.md | 32 +
.../network/1-input-url-return-page.md | 25 +
docs/interview/network/2-http-status-code.md | 86 +
.../java/java-basic.md | 74 +
.../java/java-collection.md | 48 +-
.../java/java-concurrent.md | 148 +-
.../java/java8-all.md | 83 +-
docs/java/java8/1-functional-program.md | 16 +
docs/java/java8/2-lambda.md | 26 +
docs/java/java8/3-functional-interface.md | 25 +
.../java8/4-inner-functional-interface.md | 149 +
docs/java/java8/5-stream.md | 272 +
docs/java/java8/6-parallel-stream.md | 31 +
docs/java/java8/7-map.md | 118 +
docs/java/java8/README.md | 11 +
.../java/jvm.md | 80 +-
docs/learn/ghelper.md | 63 +
docs/learn/leetcode.md | 25 +
docs/learn/manual.md | 22 +
docs/learning-resources/chang-gou-mall.md | 15 +
docs/learning-resources/cs-learn-guide.md | 273 +
docs/learning-resources/java-learn-guide.md | 810 +++
docs/learning-resources/leetcode-note.md | 26 +
docs/learning-resources/mysql-top.md | 213 +
docs/learning-resources/mysql45-section.md | 19 +
docs/learning-resources/sgg-java-learn.md | 33 +
docs/learning-resources/shang-chou.md | 13 +
docs/learning-resources/springboot-guide.md | 21 +
docs/leetcode/README.md | 40 +
docs/leetcode/hot120/1-two-sum.md | 49 +
...inary-tree-zigzag-level-order-traversal.md | 69 +
.../104-maximum-depth-of-binary-tree.md | 52 +
.../hot120/11-container-with-most-water.md | 52 +
.../hot120/1143-longest-common-subquence.md | 102 +
docs/leetcode/hot120/120-triangle.md | 47 +
.../121-best-time-to-buy-and-sell-stock.md | 41 +
.../122-best-time-to-buy-and-sell-stock-ii.md | 43 +
.../128-longest-consecutive-sequence.md | 54 +
.../leetcode/hot120/131-palindrome-partion.md | 69 +
docs/leetcode/hot120/133-clone-graph.md | 89 +
docs/leetcode/hot120/134-gas-station.md | 50 +
docs/leetcode/hot120/141-linked-list-cycle.md | 96 +
docs/leetcode/hot120/148-sort-list.md | 112 +
docs/leetcode/hot120/15-3sum.md | 62 +
.../hot120/152-maximum-product-subarray.md | 45 +
.../160-intersection-of-two-linked-lists.md | 39 +
docs/leetcode/hot120/169-majority-element.md | 47 +
docs/leetcode/hot120/18-4sum .md | 76 +
.../19-remove-nth-node-from-end-of-list.md | 55 +
.../hot120/199-binary-tree-right-side-view.md | 51 +
docs/leetcode/hot120/20-valid-parentheses.md | 56 +
docs/leetcode/hot120/200-number-of-islands.md | 65 +
.../hot120/206-reverse-linked-list.md | 44 +
.../hot120/21-merge-two-sorted-lists.md | 41 +
.../215-kth-largest-element-in-an-array.md | 94 +
.../hot120/22-generate-parentheses.md | 105 +
.../hot120/234-palindrome-linked-list.md | 56 +
...lowest-common-ancestor-of-a-binary-tree.md | 41 +
.../leetcode/hot120/24-swap-nodes-in-pairs.md | 42 +
.../26-remove-duplicates-from-sorted-array.md | 56 +
docs/leetcode/hot120/27-remove-element.md | 44 +
docs/leetcode/hot120/28-mirror-binary-tree.md | 44 +
.../leetcode/hot120/29-divide-two-integers.md | 65 +
...-substring-without-repeating-characters.md | 59 +
docs/leetcode/hot120/31-next-permutation.md | 53 +
...ast-position-of-element-in-sorted-array.md | 56 +
docs/leetcode/hot120/36-valid-sudoku.md | 75 +
docs/leetcode/hot120/40-combination-sum-ii.md | 68 +
docs/leetcode/hot120/415-add-strings.md | 48 +
docs/leetcode/hot120/43-multiply-strings.md | 53 +
docs/leetcode/hot120/46-permutations.md | 49 +
docs/leetcode/hot120/47-permutations-ii.md | 63 +
docs/leetcode/hot120/48-rotate-image.md | 49 +
docs/leetcode/hot120/49-group-anagrams.md | 44 +
.../hot120/5-longest-palindromic-substring.md | 68 +
docs/leetcode/hot120/50-powx-n.md | 41 +
docs/leetcode/hot120/53-maximum-subarray.md | 37 +
docs/leetcode/hot120/54-spiral-matrix.md | 71 +
.../hot120/543-diameter-of-binary-tree.md | 47 +
docs/leetcode/hot120/55-jump-game.md | 47 +
docs/leetcode/hot120/56-merge-intervals.md | 43 +
docs/leetcode/hot120/59-spiral-matrix-ii.md | 60 +
docs/leetcode/hot120/62-unique-paths.md | 120 +
docs/leetcode/hot120/7-reverse-integer.md | 58 +
docs/leetcode/hot120/71-simplify-path.md | 73 +
docs/leetcode/hot120/73-set-matrix-zeroes.md | 76 +
docs/leetcode/hot120/74-search-a-2d-matrix.md | 54 +
docs/leetcode/hot120/75-sort-colors.md | 51 +
docs/leetcode/hot120/77-combinations.md | 67 +
.../83-remove-duplicates-from-sorted-list.md | 39 +
docs/leetcode/hot120/9-palindrome-number.md | 51 +
.../hot120/92-reverse-linked-list-ii.md | 41 +
...958-check-completeness-of-a-binary-tree.md | 57 +
.../hot120/98-validate-binary-search-tree.md | 56 +
docs/leetcode/hot120/README.md | 41 +
.../mass-data/1-count-phone-num.md | 8 +-
.../mass-data/2-find-hign-frequency-word.md | 17 +-
docs/mass-data/3-find-same-url.md | 30 +
docs/mass-data/4-find-mid-num.md | 36 +
docs/mass-data/5-find-hot-string.md | 54 +
docs/mass-data/6-top-500-num.md | 116 +
docs/mass-data/7-query-frequency-sort.md | 33 +
docs/mass-data/8-topk-template.md | 166 +
docs/mass-data/README.md | 12 +
.../kafka.md" => docs/message-queue/kafka.md | 0
docs/message-queue/mq.md | 181 +
.../message-queue/rabbitmq.md | 0
docs/other/leave-a-message.md | 4 +
docs/other/site-diary.md | 109 +
.../service-performance-optimization.md | 25 +
.../redis/redis-basic-all.md | 0
docs/redis/redis-basic/1-introduce.md | 36 +
docs/redis/redis-basic/10-lua.md | 70 +
docs/redis/redis-basic/11-deletion-policy.md | 26 +
docs/redis/redis-basic/12-others.md | 81 +
docs/redis/redis-basic/2-data-type.md | 318 +
docs/redis/redis-basic/3-data-structure.md | 100 +
docs/redis/redis-basic/4-implement.md | 68 +
docs/redis/redis-basic/5-sort.md | 36 +
docs/redis/redis-basic/6-transaction.md | 61 +
docs/redis/redis-basic/7-message-queue.md | 31 +
docs/redis/redis-basic/8-persistence.md | 67 +
docs/redis/redis-basic/9-cluster.md | 121 +
docs/redis/redis-basic/README.md | 26 +
.../redis/redis.md | 78 +-
docs/resource/1-cs-books.md | 329 +
.../tools/docker-overview.md | 152 +-
docs/tools/docker/1-introduce.md | 27 +
docs/tools/docker/2-image-command.md | 209 +
docs/tools/docker/3-container-command.md | 242 +
docs/tools/docker/4-docker-compose.md | 102 +
docs/tools/docker/5-maven-build.md | 136 +
docs/tools/docker/6-other.md | 53 +
docs/tools/docker/README.md | 17 +
.../tools/git-overview.md | 143 +-
docs/tools/git/1-introduce.md | 71 +
docs/tools/git/2-basic.md | 280 +
docs/tools/git/3-remote-repo.md | 110 +
docs/tools/git/4-label.md | 101 +
docs/tools/git/5-branch.md | 305 +
docs/tools/git/README.md | 17 +
.../tools/linux-overview.md | 0
docs/tools/linux/1-basic.md | 172 +
docs/tools/linux/2-disk-file.md | 159 +
docs/tools/linux/3-search.md | 59 +
docs/tools/linux/4-net.md | 125 +
docs/tools/linux/5-monitor.md | 131 +
docs/tools/linux/README.md | 12 +
.../tools/maven-overview.md | 127 +-
docs/tools/maven/1-introduce.md | 12 +
docs/tools/maven/2-basic.md | 104 +
docs/tools/maven/3-dependency.md | 53 +
docs/tools/maven/4-repo.md | 88 +
docs/tools/maven/5-lifecycle.md | 24 +
docs/tools/maven/6-plugin.md | 63 +
docs/tools/maven/7-aggregator.md | 17 +
docs/tools/maven/8-inherit.md | 169 +
docs/tools/maven/README.md | 20 +
docs/tools/typora-overview.md | 184 +
img/1588431199776.png | Bin 63521 -> 0 bytes
img/ConcurrentHashMap-segment.jpg | Bin 230179 -> 0 bytes
img/JDK1.8-ConcurrentHashMap-Structure.jpg | Bin 12869 -> 0 bytes
img/Java-Collections.jpeg | Bin 167969 -> 0 bytes
img/all-table-search.jpg | Bin 14178 -> 0 bytes
img/aqs.png | Bin 21797 -> 0 bytes
img/bean-life-cycle.jpg | Bin 29410 -> 0 bytes
img/bloom-filter.jpg | Bin 23578 -> 0 bytes
img/cms-collector.png | Bin 86817 -> 0 bytes
img/concurrent/executors-ali.png | Bin 114331 -> 0 bytes
img/concurrent/synchronized-block.png | Bin 188866 -> 0 bytes
img/concurrent/synchronized-method.png | Bin 127575 -> 0 bytes
img/condition-await.png | Bin 49132 -> 0 bytes
img/condition-signal.png | Bin 48475 -> 0 bytes
img/condition-wait-queue.png | Bin 46923 -> 0 bytes
img/cover-index.png | Bin 25311 -> 0 bytes
img/direct-pointer.png | Bin 94396 -> 0 bytes
img/docker/docker.jpg | Bin 88608 -> 0 bytes
img/explain-all.png | Bin 8446 -> 0 bytes
img/explain-const.png | Bin 4419 -> 0 bytes
img/explain-id.png | Bin 5014 -> 0 bytes
img/explain-range.png | Bin 4762 -> 0 bytes
img/explain-ref.png | Bin 10220 -> 0 bytes
img/g1-region.jpg | Bin 19644 -> 0 bytes
img/gc-root-refer.png | Bin 14443 -> 0 bytes
img/git-status.png | Bin 109277 -> 0 bytes
img/heap-structure.png | Bin 56484 -> 0 bytes
img/image-20200520234137916.png | Bin 13219 -> 0 bytes
img/image-20200520234200868.png | Bin 26483 -> 0 bytes
img/image-20200520234231001.png | Bin 25332 -> 0 bytes
img/image-20200608232749393.png | Bin 4492 -> 0 bytes
img/image-20200608232951873.png | Bin 2591 -> 0 bytes
img/image-20200608233400458.png | Bin 2946 -> 0 bytes
img/image-20200614165333479.png | Bin 208621 -> 0 bytes
img/image-20200614165432775.png | Bin 195674 -> 0 bytes
img/index-search-range.jpg | Bin 14490 -> 0 bytes
img/index-search.jpg | Bin 12920 -> 0 bytes
img/java-basic/exception.png | Bin 37810 -> 0 bytes
img/java-basic/field-method.png | Bin 118178 -> 0 bytes
img/java-basic/io.jpg | Bin 50725 -> 0 bytes
img/java-ram-region.png | Bin 110051 -> 0 bytes
img/jmeter/http-request-param.png | Bin 30984 -> 0 bytes
img/jmeter/jmeter-aggregate-report.png | Bin 49256 -> 0 bytes
img/jmeter/jmeter-default-request.png | Bin 79751 -> 0 bytes
img/jmeter/jmeter-request-header.png | Bin 75457 -> 0 bytes
img/jmeter/jmeter-thread-group.png | Bin 34358 -> 0 bytes
img/jvm/string-equal.png | Bin 33445 -> 0 bytes
img/jvm/string-intern.png | Bin 36462 -> 0 bytes
img/jvm/string-new.png | Bin 28718 -> 0 bytes
img/left-match.jpg | Bin 31737 -> 0 bytes
img/middleware/redis-transaction.png | Bin 7291 -> 0 bytes
img/middleware/redis-usage.png | Bin 31580 -> 0 bytes
img/mysql-architecture.png | Bin 1229182 -> 0 bytes
img/mysql-clustered-index.png | Bin 35301 -> 0 bytes
img/mysql/arch.png | Bin 69339 -> 0 bytes
img/mysql/current-read.png | Bin 10277 -> 0 bytes
img/mysql/mvcc-impl.png | Bin 38026 -> 0 bytes
img/mysql/myisam-innodb-index.png | Bin 82352 -> 0 bytes
img/mysql/mysql-archpng.png | Bin 223491 -> 0 bytes
img/mysql/nested-loop.png | Bin 129701 -> 0 bytes
img/mysql/orderby.png | Bin 6252 -> 0 bytes
img/net/bio.png | Bin 6774 -> 0 bytes
img/net/cdn.png | Bin 942422 -> 0 bytes
img/net/https-certificate-chain.jpg | Bin 33273 -> 0 bytes
img/net/https-certificate.png | Bin 34566 -> 0 bytes
img/net/https-client-hello.jpg | Bin 86157 -> 0 bytes
img/net/https-server-hello.jpg | Bin 37249 -> 0 bytes
img/net/nio.png | Bin 7276 -> 0 bytes
img/oauth2.png | Bin 10836 -> 0 bytes
img/object-dead.png | Bin 49726 -> 0 bytes
img/object-handle.png | Bin 110349 -> 0 bytes
img/optimistic-lock.jpg | Bin 39394 -> 0 bytes
img/others/json-web-token.png | Bin 61702 -> 0 bytes
img/others/seckill.jpg | Bin 32958 -> 0 bytes
img/parnew-collector.png | Bin 42532 -> 0 bytes
img/rabbitmq-direct.png | Bin 8762 -> 0 bytes
img/rabbitmq-fanout.png | Bin 9472 -> 0 bytes
img/rabbitmq-return-listener.png | Bin 48332 -> 0 bytes
img/rabbitmq-topic.png | Bin 12484 -> 0 bytes
img/rabbitmq.png | Bin 6474 -> 0 bytes
img/redis/cache-consist.png | Bin 23667 -> 0 bytes
img/redis/evalsha.png | Bin 184215 -> 0 bytes
img/redis/list-api.png | Bin 178712 -> 0 bytes
img/redis/redis-replication.png | Bin 178354 -> 0 bytes
img/redis/redis-skiplist.png | Bin 11736 -> 0 bytes
img/redis/time-complexity.png | Bin 421110 -> 0 bytes
...0\344\272\222\346\226\245\351\224\201.png" | Bin 232236 -> 0 bytes
img/scheduled-task.jpg | Bin 85304 -> 0 bytes
img/serial-collector.png | Bin 34121 -> 0 bytes
img/singleton-class-init.png | Bin 49462 -> 0 bytes
...7\345\210\253\346\263\250\350\247\243.png" | Bin 57651 -> 0 bytes
...5\347\275\256\345\216\237\347\220\206.jpg" | Bin 851247 -> 0 bytes
img/thread-pool.png | Bin 17381 -> 0 bytes
img/thread-status.jpeg | Bin 88664 -> 0 bytes
img/threadlocal-oom.png | Bin 23902 -> 0 bytes
img/threadlocal.png | Bin 17096 -> 0 bytes
img/tree-visit.png | Bin 25772 -> 0 bytes
img/two-index.jpg | Bin 15986 -> 0 bytes
img/web/servlet-container.jpg | Bin 22543 -> 0 bytes
"img/\345\271\273\350\257\2731.png" | Bin 22122 -> 0 bytes
...5\345\241\236\346\216\247\345\210\266.jpg" | Bin 48625 -> 0 bytes
...5\347\275\256\346\226\271\346\263\225.png" | Bin 49050 -> 0 bytes
package-lock.json | 6195 +++++++++++++++++
package.json | 26 +
.../Elasticsearch\345\205\245\351\227\250.md" | 2438 -------
"\345\205\266\344\273\226/code.md" | 77 -
"\345\205\266\344\273\226/linux.md" | 417 --
"\345\205\266\344\273\226/note.md" | 286 -
.../\345\256\236\346\210\230\347\257\207.md" | 93 -
...03\345\274\217\344\272\213\345\212\241.md" | 77 -
...03\345\274\217\347\274\223\345\255\230.md" | 98 -
"\345\211\215\347\253\257/vue.md" | 401 --
.../GitHub\346\214\207\345\215\227.md" | 91 -
"\345\267\245\345\205\267/NPM.md" | 48 -
"\345\267\245\345\205\267/jenkins.md" | 120 -
"\345\267\245\345\205\267/jmeter.md" | 39 -
...50\347\275\262\351\241\271\347\233\256.md" | 325 -
.../MySQL\350\277\233\351\230\266.md" | 811 ---
.../SpringBoot\345\256\236\346\210\230.md" | 1028 ---
"\346\241\206\346\236\266/SpringMVC.md" | 1602 -----
...VC\351\235\242\350\257\225\351\242\230.md" | 89 -
.../Spring\345\256\236\346\210\230.md" | 3228 ---------
.../Spring\346\200\273\347\273\223.md" | 471 --
...76\350\256\241\346\250\241\345\274\217.md" | 67 -
...52\345\212\250\350\243\205\351\205\215.md" | 187 -
...06\344\270\216\345\256\236\346\210\230.md" | 2435 -------
...73\344\277\241\351\230\237\345\210\227.md" | 117 -
...27\351\235\242\350\257\225\351\242\230.md" | 72 -
...347\273\237\350\256\276\350\256\241old.md" | 24 -
.../\347\256\227\346\263\225.md" | 374 -
.../TCP IP\345\215\217\350\256\256.md" | 349 -
.../session\345\222\214cookie.md" | 23 -
.../\347\275\221\347\273\234.md" | 362 -
...21\351\235\242\350\257\225\351\242\230.md" | 315 -
464 files changed, 34561 insertions(+), 26055 deletions(-)
create mode 100644 CNAME
delete mode 100644 Java/JVM.md
delete mode 100644 Java/Java web.md
delete mode 100644 "Java/Java \347\274\226\347\250\213\346\200\235\346\203\263.md"
delete mode 100644 "Java/Java\345\205\263\351\224\256\345\255\227.md"
delete mode 100644 "Java/Java\345\237\272\347\241\200.md"
delete mode 100644 "Java/Java\345\271\266\345\217\221\347\274\226\347\250\213.md"
delete mode 100644 "Java/Object\347\261\273\345\270\270\347\224\250\346\226\271\346\263\225.md"
delete mode 100644 "Java/\345\271\266\345\217\221.md"
delete mode 100644 "Java/\351\233\206\345\220\210.md"
delete mode 100644 "Redis/\347\274\223\345\255\230\347\251\277\351\200\217\343\200\201\347\274\223\345\255\230\351\233\252\345\264\251\343\200\201\347\274\223\345\255\230\345\207\273\347\251\277.md"
create mode 100644 docs/.vuepress/config.ts
create mode 100644 docs/.vuepress/enhanceApp.js
create mode 100644 docs/.vuepress/navbar.ts
create mode 100644 docs/.vuepress/sidebar.ts
create mode 100644 docs/.vuepress/styles/index.scss
create mode 100644 docs/.vuepress/styles/palette.scss
create mode 100644 docs/.vuepress/theme.ts
create mode 100644 docs/.vuepress/vuepress-sidebar-auto/vuepress-sidebar-auto.js
create mode 100644 docs/README.md
create mode 100644 docs/about/contact.md
create mode 100644 docs/about/introduce.md
create mode 100644 docs/advance/concurrent/1-current-limiting.md
create mode 100644 docs/advance/concurrent/2-load-balance.md
create mode 100644 docs/advance/concurrent/README.md
rename "\345\205\266\344\273\226/\350\256\276\350\256\241\346\250\241\345\274\217.md" => docs/advance/design-pattern-all.md (100%)
create mode 100644 docs/advance/design-pattern/1-principle.md
create mode 100644 docs/advance/design-pattern/10-observer.md
create mode 100644 docs/advance/design-pattern/11-proxy.md
create mode 100644 docs/advance/design-pattern/12-builder.md
create mode 100644 docs/advance/design-pattern/2-singleton.md
create mode 100644 docs/advance/design-pattern/3-factory.md
create mode 100644 docs/advance/design-pattern/4-template.md
create mode 100644 docs/advance/design-pattern/5-strategy.md
create mode 100644 docs/advance/design-pattern/6-chain.md
create mode 100644 docs/advance/design-pattern/7-iterator.md
create mode 100644 docs/advance/design-pattern/8-decorator.md
create mode 100644 docs/advance/design-pattern/9-adapter.md
create mode 100644 docs/advance/design-pattern/README.md
rename "\345\210\206\345\270\203\345\274\217/\345\205\250\345\261\200\345\224\257\344\270\200ID.md" => docs/advance/distributed/1-global-unique-id.md (97%)
rename "\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\351\224\201.md" => docs/advance/distributed/2-distributed-lock.md (99%)
rename "\345\210\206\345\270\203\345\274\217/\350\277\234\347\250\213\350\260\203\347\224\250.md" => docs/advance/distributed/3-rpc.md (99%)
create mode 100644 docs/advance/distributed/4-micro-service.md
create mode 100644 docs/advance/distributed/5-distibuted-arch.md
create mode 100644 docs/advance/distributed/6-distributed-transaction.md
create mode 100644 docs/advance/distributed/README.md
create mode 100644 docs/advance/excellent-article/1-redis-stock-minus.md
create mode 100644 docs/advance/excellent-article/10-file-upload.md
create mode 100644 docs/advance/excellent-article/11-8-architect-pattern.md
create mode 100644 docs/advance/excellent-article/12-mysql-table-max-rows.md
create mode 100644 docs/advance/excellent-article/13-order-by-work.md
create mode 100644 docs/advance/excellent-article/14-architect-forward.md
create mode 100644 docs/advance/excellent-article/15-http-vs-rpc.md
create mode 100644 docs/advance/excellent-article/16-what-is-jwt.md
create mode 100644 docs/advance/excellent-article/2-spring-transaction.md
create mode 100644 docs/advance/excellent-article/3-springboot-auto-assembly.md
create mode 100644 docs/advance/excellent-article/4-remove-duplicate-code.md
create mode 100644 docs/advance/excellent-article/5-jvm-optimize.md
create mode 100644 docs/advance/excellent-article/6-spring-three-cache.md
create mode 100644 docs/advance/excellent-article/7-sql-optimize.md
create mode 100644 docs/advance/excellent-article/8-interface-idempotent.md
create mode 100644 docs/advance/excellent-article/9-jvm-optimize-param.md
create mode 100644 docs/advance/excellent-article/README.md
rename "\347\263\273\347\273\237\350\256\276\350\256\241/\346\211\253\347\240\201\347\231\273\345\275\225\345\216\237\347\220\206.md" => docs/advance/system-design/1-scan-code-login.md (51%)
create mode 100644 docs/advance/system-design/2-order-timeout-auto-cancel.md
create mode 100644 docs/advance/system-design/3-file-send.md
rename "\347\263\273\347\273\237\350\256\276\350\256\241/\347\263\273\347\273\237\350\256\276\350\256\241.md" => docs/advance/system-design/3-short-url.md (61%)
create mode 100644 docs/advance/system-design/4-oversold.md
create mode 100644 docs/advance/system-design/5-second-kill.md
create mode 100644 docs/advance/system-design/6-wechat-redpacket-design.md
create mode 100644 docs/advance/system-design/7-file-send.md
create mode 100644 docs/advance/system-design/8-sso-design.md
create mode 100644 docs/advance/system-design/README.md
create mode 100644 "docs/advance/system-design/\345\233\276\347\211\207 - \345\277\253\346\215\267\346\226\271\345\274\217.lnk"
create mode 100644 docs/campus-recruit/README.md
create mode 100644 docs/campus-recruit/career-plan.md
rename {interview => docs/campus-recruit/interview}/1-byte-and-dance.md (100%)
rename {interview => docs/campus-recruit/interview}/10-netease.md (100%)
rename {interview => docs/campus-recruit/interview}/2-tencent.md (98%)
rename {interview => docs/campus-recruit/interview}/3-baidu.md (100%)
rename {interview => docs/campus-recruit/interview}/4-ali.md (100%)
rename {interview => docs/campus-recruit/interview}/5-kuaishou.md (100%)
rename {interview => docs/campus-recruit/interview}/6-meituan.md (100%)
rename {interview => docs/campus-recruit/interview}/7-shopee.md (100%)
rename {interview => docs/campus-recruit/interview}/8-jingdong.md (100%)
rename {interview => docs/campus-recruit/interview}/9-huawei.md (100%)
rename {interview => docs/campus-recruit/interview}/README.md (100%)
create mode 100644 docs/campus-recruit/layoffs-solution.md
create mode 100644 docs/campus-recruit/program-language/README.md
create mode 100644 docs/campus-recruit/program-language/java-or-c++.md
create mode 100644 docs/campus-recruit/program-language/java-or-golang.md
create mode 100644 docs/campus-recruit/project-experience.md
create mode 100644 docs/campus-recruit/resume.md
create mode 100644 docs/campus-recruit/share/1-23-backend.md
create mode 100644 docs/campus-recruit/share/2-no-offer.md
create mode 100644 docs/campus-recruit/share/README.md
create mode 100644 docs/computer-basic/algorithm.md
rename "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\346\225\260\346\215\256\347\273\223\346\236\204.md" => docs/computer-basic/data-structure.md (62%)
create mode 100644 docs/computer-basic/network.md
rename "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\223\215\344\275\234\347\263\273\347\273\237/\346\223\215\344\275\234\347\263\273\347\273\237\351\235\242\350\257\225\351\242\230.md" => docs/computer-basic/operate-system.md (98%)
rename "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/\345\233\276\350\247\243HTTP.md" => "docs/computer-basic/\345\233\276\350\247\243HTTP.md" (99%)
create mode 100644 docs/database/es/1-es-architect.md
rename "\346\225\260\346\215\256\345\272\223/MySQL\345\237\272\347\241\200.md" => docs/database/mysql-basic-all.md (94%)
create mode 100644 docs/database/mysql-basic/1-data-type.md
create mode 100644 docs/database/mysql-basic/10-view.md
create mode 100644 docs/database/mysql-basic/11-procedure.md
create mode 100644 docs/database/mysql-basic/12-cursor.md
create mode 100644 docs/database/mysql-basic/13-trigger.md
create mode 100644 docs/database/mysql-basic/14-transaction.md
create mode 100644 docs/database/mysql-basic/15-permission.md
create mode 100644 docs/database/mysql-basic/16-performace-optimization.md
create mode 100644 docs/database/mysql-basic/17-index.md
create mode 100644 docs/database/mysql-basic/2-basic-command.md
create mode 100644 docs/database/mysql-basic/3-function.md
create mode 100644 docs/database/mysql-basic/4-sum.md
create mode 100644 docs/database/mysql-basic/5-group.md
create mode 100644 docs/database/mysql-basic/6-join.md
create mode 100644 docs/database/mysql-basic/7-full-text-query.md
create mode 100644 docs/database/mysql-basic/8-table-operate.md
create mode 100644 docs/database/mysql-basic/9-column-operate.md
create mode 100644 docs/database/mysql-basic/README.md
rename "\346\225\260\346\215\256\345\272\223/MySQL\346\211\247\350\241\214\350\256\241\345\210\222.md" => docs/database/mysql-execution-plan.md (92%)
rename "\346\225\260\346\215\256\345\272\223/MySQL\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" => docs/database/mysql.md (77%)
create mode 100644 docs/distributed/1-global-unique-id.md
create mode 100644 docs/distributed/2-distributed-lock.md
create mode 100644 docs/distributed/2.1-rpc.md
rename "\345\210\206\345\270\203\345\274\217/\345\276\256\346\234\215\345\212\241.md" => docs/distributed/3-micro-service.md (99%)
create mode 100644 docs/distributed/4-distibuted-arch.md
create mode 100644 docs/distributed/5-distributed-transaction.md
rename "\345\210\206\345\270\203\345\274\217/RocketMQ\345\256\236\347\216\260RPC\347\232\204\345\216\237\347\220\206.md" => "docs/distributed/RocketMQ\345\256\236\347\216\260RPC\347\232\204\345\216\237\347\220\206.md" (100%)
rename "\346\241\206\346\236\266/Apollo\351\205\215\347\275\256\344\270\255\345\277\203.md" => "docs/framework/Apollo\351\205\215\347\275\256\344\270\255\345\277\203.md" (100%)
rename "\346\241\206\346\236\266/Mybatis\351\235\242\350\257\225\351\242\230.md" => docs/framework/mybatis.md (86%)
rename "\346\241\206\346\236\266/netty\345\256\236\346\210\230.md" => docs/framework/netty-overview.md (84%)
create mode 100644 docs/framework/netty/1-overview.md
create mode 100644 docs/framework/netty/10-encoder-decoder.md
create mode 100644 docs/framework/netty/11-preset-channel-handler.md
create mode 100644 docs/framework/netty/2-make-your-app.md
create mode 100644 docs/framework/netty/3-component.md
create mode 100644 docs/framework/netty/4-transport.md
create mode 100644 docs/framework/netty/6-channel-handler.md
create mode 100644 docs/framework/netty/7-channel-pipeline.md
create mode 100644 docs/framework/netty/8-eventloop-thread-model.md
create mode 100644 docs/framework/netty/9-guide.md
create mode 100644 docs/framework/netty/README.md
rename "\346\241\206\346\236\266/Spring\351\235\242\350\257\225\351\242\230.md" => docs/framework/spring.md (92%)
rename "\346\241\206\346\236\266/SpringBoot\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md" => docs/framework/springboot.md (74%)
create mode 100644 docs/framework/springcloud-interview.md
rename "\346\241\206\346\236\266/SpringCloud\345\276\256\346\234\215\345\212\241\345\256\236\346\210\230.md" => docs/framework/springcloud-overview.md (96%)
create mode 100644 docs/framework/springcloud/1-basic.md
create mode 100644 docs/framework/springcloud/10-bus.md
create mode 100644 docs/framework/springcloud/11-security.md
create mode 100644 docs/framework/springcloud/2-springboot-springcloud-diff.md
create mode 100644 docs/framework/springcloud/3-eureka.md
create mode 100644 docs/framework/springcloud/4-ribbon.md
create mode 100644 docs/framework/springcloud/5-hystrix.md
create mode 100644 docs/framework/springcloud/6-feign.md
create mode 100644 docs/framework/springcloud/7-zuul.md
create mode 100644 docs/framework/springcloud/8-gateway.md
create mode 100644 docs/framework/springcloud/9-config.md
create mode 100644 docs/framework/springcloud/README.md
create mode 100644 docs/framework/springmvc.md
create mode 100644 docs/interview/concurrent/1-forbid-default-executor.md
create mode 100644 docs/interview/java/1-create-object.md
create mode 100644 docs/interview/java/2-3rd-interface.md
create mode 100644 docs/interview/java/3-design-interface.md
create mode 100644 docs/interview/java/4-interface-slow.md
create mode 100644 docs/interview/java/5-comparable-vs-comparator.md
create mode 100644 docs/interview/java/README.md
create mode 100644 docs/interview/javaweb/1-interceptor-filter.md
create mode 100644 docs/interview/mq/1-why-to-use-mq.md
create mode 100644 docs/interview/network/1-input-url-return-page.md
create mode 100644 docs/interview/network/2-http-status-code.md
rename "Java/Java\345\237\272\347\241\200\351\235\242\350\257\225\351\242\230.md" => docs/java/java-basic.md (91%)
rename "Java/Java\351\233\206\345\220\210\351\235\242\350\257\225\351\242\230.md" => docs/java/java-collection.md (91%)
rename "Java/Java\345\271\266\345\217\221\351\235\242\350\257\225\351\242\230.md" => docs/java/java-concurrent.md (84%)
rename "Java/Java8\346\226\260\347\211\271\346\200\247.md" => docs/java/java8-all.md (87%)
create mode 100644 docs/java/java8/1-functional-program.md
create mode 100644 docs/java/java8/2-lambda.md
create mode 100644 docs/java/java8/3-functional-interface.md
create mode 100644 docs/java/java8/4-inner-functional-interface.md
create mode 100644 docs/java/java8/5-stream.md
create mode 100644 docs/java/java8/6-parallel-stream.md
create mode 100644 docs/java/java8/7-map.md
create mode 100644 docs/java/java8/README.md
rename "Java/JVM\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md" => docs/java/jvm.md (87%)
create mode 100644 docs/learn/ghelper.md
create mode 100644 docs/learn/leetcode.md
create mode 100644 docs/learn/manual.md
create mode 100644 docs/learning-resources/chang-gou-mall.md
create mode 100644 docs/learning-resources/cs-learn-guide.md
create mode 100644 docs/learning-resources/java-learn-guide.md
create mode 100644 docs/learning-resources/leetcode-note.md
create mode 100644 docs/learning-resources/mysql-top.md
create mode 100644 docs/learning-resources/mysql45-section.md
create mode 100644 docs/learning-resources/sgg-java-learn.md
create mode 100644 docs/learning-resources/shang-chou.md
create mode 100644 docs/learning-resources/springboot-guide.md
create mode 100644 docs/leetcode/README.md
create mode 100644 docs/leetcode/hot120/1-two-sum.md
create mode 100644 docs/leetcode/hot120/103-binary-tree-zigzag-level-order-traversal.md
create mode 100644 docs/leetcode/hot120/104-maximum-depth-of-binary-tree.md
create mode 100644 docs/leetcode/hot120/11-container-with-most-water.md
create mode 100644 docs/leetcode/hot120/1143-longest-common-subquence.md
create mode 100644 docs/leetcode/hot120/120-triangle.md
create mode 100644 docs/leetcode/hot120/121-best-time-to-buy-and-sell-stock.md
create mode 100644 docs/leetcode/hot120/122-best-time-to-buy-and-sell-stock-ii.md
create mode 100644 docs/leetcode/hot120/128-longest-consecutive-sequence.md
create mode 100644 docs/leetcode/hot120/131-palindrome-partion.md
create mode 100644 docs/leetcode/hot120/133-clone-graph.md
create mode 100644 docs/leetcode/hot120/134-gas-station.md
create mode 100644 docs/leetcode/hot120/141-linked-list-cycle.md
create mode 100644 docs/leetcode/hot120/148-sort-list.md
create mode 100644 docs/leetcode/hot120/15-3sum.md
create mode 100644 docs/leetcode/hot120/152-maximum-product-subarray.md
create mode 100644 docs/leetcode/hot120/160-intersection-of-two-linked-lists.md
create mode 100644 docs/leetcode/hot120/169-majority-element.md
create mode 100644 docs/leetcode/hot120/18-4sum .md
create mode 100644 docs/leetcode/hot120/19-remove-nth-node-from-end-of-list.md
create mode 100644 docs/leetcode/hot120/199-binary-tree-right-side-view.md
create mode 100644 docs/leetcode/hot120/20-valid-parentheses.md
create mode 100644 docs/leetcode/hot120/200-number-of-islands.md
create mode 100644 docs/leetcode/hot120/206-reverse-linked-list.md
create mode 100644 docs/leetcode/hot120/21-merge-two-sorted-lists.md
create mode 100644 docs/leetcode/hot120/215-kth-largest-element-in-an-array.md
create mode 100644 docs/leetcode/hot120/22-generate-parentheses.md
create mode 100644 docs/leetcode/hot120/234-palindrome-linked-list.md
create mode 100644 docs/leetcode/hot120/236-lowest-common-ancestor-of-a-binary-tree.md
create mode 100644 docs/leetcode/hot120/24-swap-nodes-in-pairs.md
create mode 100644 docs/leetcode/hot120/26-remove-duplicates-from-sorted-array.md
create mode 100644 docs/leetcode/hot120/27-remove-element.md
create mode 100644 docs/leetcode/hot120/28-mirror-binary-tree.md
create mode 100644 docs/leetcode/hot120/29-divide-two-integers.md
create mode 100644 docs/leetcode/hot120/3-longest-substring-without-repeating-characters.md
create mode 100644 docs/leetcode/hot120/31-next-permutation.md
create mode 100644 docs/leetcode/hot120/34-find-first-and-last-position-of-element-in-sorted-array.md
create mode 100644 docs/leetcode/hot120/36-valid-sudoku.md
create mode 100644 docs/leetcode/hot120/40-combination-sum-ii.md
create mode 100644 docs/leetcode/hot120/415-add-strings.md
create mode 100644 docs/leetcode/hot120/43-multiply-strings.md
create mode 100644 docs/leetcode/hot120/46-permutations.md
create mode 100644 docs/leetcode/hot120/47-permutations-ii.md
create mode 100644 docs/leetcode/hot120/48-rotate-image.md
create mode 100644 docs/leetcode/hot120/49-group-anagrams.md
create mode 100644 docs/leetcode/hot120/5-longest-palindromic-substring.md
create mode 100644 docs/leetcode/hot120/50-powx-n.md
create mode 100644 docs/leetcode/hot120/53-maximum-subarray.md
create mode 100644 docs/leetcode/hot120/54-spiral-matrix.md
create mode 100644 docs/leetcode/hot120/543-diameter-of-binary-tree.md
create mode 100644 docs/leetcode/hot120/55-jump-game.md
create mode 100644 docs/leetcode/hot120/56-merge-intervals.md
create mode 100644 docs/leetcode/hot120/59-spiral-matrix-ii.md
create mode 100644 docs/leetcode/hot120/62-unique-paths.md
create mode 100644 docs/leetcode/hot120/7-reverse-integer.md
create mode 100644 docs/leetcode/hot120/71-simplify-path.md
create mode 100644 docs/leetcode/hot120/73-set-matrix-zeroes.md
create mode 100644 docs/leetcode/hot120/74-search-a-2d-matrix.md
create mode 100644 docs/leetcode/hot120/75-sort-colors.md
create mode 100644 docs/leetcode/hot120/77-combinations.md
create mode 100644 docs/leetcode/hot120/83-remove-duplicates-from-sorted-list.md
create mode 100644 docs/leetcode/hot120/9-palindrome-number.md
create mode 100644 docs/leetcode/hot120/92-reverse-linked-list-ii.md
create mode 100644 docs/leetcode/hot120/958-check-completeness-of-a-binary-tree.md
create mode 100644 docs/leetcode/hot120/98-validate-binary-search-tree.md
create mode 100644 docs/leetcode/hot120/README.md
rename "\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\347\273\237\350\256\241\344\270\215\345\220\214\347\224\265\350\257\235\345\217\267\347\240\201\347\232\204\344\270\252\346\225\260.md" => docs/mass-data/1-count-phone-num.md (95%)
rename "\346\265\267\351\207\217\346\225\260\346\215\256\345\234\272\346\231\257\351\242\230/\345\246\202\344\275\225\344\273\216\346\265\267\351\207\217\346\225\260\346\215\256\346\211\276\345\207\272\351\253\230\351\242\221\350\257\215.md" => docs/mass-data/2-find-hign-frequency-word.md (87%)
create mode 100644 docs/mass-data/3-find-same-url.md
create mode 100644 docs/mass-data/4-find-mid-num.md
create mode 100644 docs/mass-data/5-find-hot-string.md
create mode 100644 docs/mass-data/6-top-500-num.md
create mode 100644 docs/mass-data/7-query-frequency-sort.md
create mode 100644 docs/mass-data/8-topk-template.md
create mode 100644 docs/mass-data/README.md
rename "\346\266\210\346\201\257\351\230\237\345\210\227/kafka.md" => docs/message-queue/kafka.md (100%)
create mode 100644 docs/message-queue/mq.md
rename "\346\266\210\346\201\257\351\230\237\345\210\227/RabbitMQ.md" => docs/message-queue/rabbitmq.md (100%)
create mode 100644 docs/other/leave-a-message.md
create mode 100644 docs/other/site-diary.md
create mode 100644 docs/practice/service-performance-optimization.md
rename "Redis/Redis\345\205\245\351\227\250\346\214\207\345\215\227\346\200\273\347\273\223.md" => docs/redis/redis-basic-all.md (100%)
create mode 100644 docs/redis/redis-basic/1-introduce.md
create mode 100644 docs/redis/redis-basic/10-lua.md
create mode 100644 docs/redis/redis-basic/11-deletion-policy.md
create mode 100644 docs/redis/redis-basic/12-others.md
create mode 100644 docs/redis/redis-basic/2-data-type.md
create mode 100644 docs/redis/redis-basic/3-data-structure.md
create mode 100644 docs/redis/redis-basic/4-implement.md
create mode 100644 docs/redis/redis-basic/5-sort.md
create mode 100644 docs/redis/redis-basic/6-transaction.md
create mode 100644 docs/redis/redis-basic/7-message-queue.md
create mode 100644 docs/redis/redis-basic/8-persistence.md
create mode 100644 docs/redis/redis-basic/9-cluster.md
create mode 100644 docs/redis/redis-basic/README.md
rename "Redis/Redis\351\235\242\350\257\225\351\242\230.md" => docs/redis/redis.md (85%)
create mode 100644 docs/resource/1-cs-books.md
rename "\345\267\245\345\205\267/docker-overview.md" => docs/tools/docker-overview.md (76%)
create mode 100644 docs/tools/docker/1-introduce.md
create mode 100644 docs/tools/docker/2-image-command.md
create mode 100644 docs/tools/docker/3-container-command.md
create mode 100644 docs/tools/docker/4-docker-compose.md
create mode 100644 docs/tools/docker/5-maven-build.md
create mode 100644 docs/tools/docker/6-other.md
create mode 100644 docs/tools/docker/README.md
rename "\345\267\245\345\205\267/git-overview.md" => docs/tools/git-overview.md (81%)
create mode 100644 docs/tools/git/1-introduce.md
create mode 100644 docs/tools/git/2-basic.md
create mode 100644 docs/tools/git/3-remote-repo.md
create mode 100644 docs/tools/git/4-label.md
create mode 100644 docs/tools/git/5-branch.md
create mode 100644 docs/tools/git/README.md
rename "\345\267\245\345\205\267/linux-overview.md" => docs/tools/linux-overview.md (100%)
create mode 100644 docs/tools/linux/1-basic.md
create mode 100644 docs/tools/linux/2-disk-file.md
create mode 100644 docs/tools/linux/3-search.md
create mode 100644 docs/tools/linux/4-net.md
create mode 100644 docs/tools/linux/5-monitor.md
create mode 100644 docs/tools/linux/README.md
rename "\345\267\245\345\205\267/maven-overview.md" => docs/tools/maven-overview.md (83%)
create mode 100644 docs/tools/maven/1-introduce.md
create mode 100644 docs/tools/maven/2-basic.md
create mode 100644 docs/tools/maven/3-dependency.md
create mode 100644 docs/tools/maven/4-repo.md
create mode 100644 docs/tools/maven/5-lifecycle.md
create mode 100644 docs/tools/maven/6-plugin.md
create mode 100644 docs/tools/maven/7-aggregator.md
create mode 100644 docs/tools/maven/8-inherit.md
create mode 100644 docs/tools/maven/README.md
create mode 100644 docs/tools/typora-overview.md
delete mode 100644 img/1588431199776.png
delete mode 100644 img/ConcurrentHashMap-segment.jpg
delete mode 100644 img/JDK1.8-ConcurrentHashMap-Structure.jpg
delete mode 100644 img/Java-Collections.jpeg
delete mode 100644 img/all-table-search.jpg
delete mode 100644 img/aqs.png
delete mode 100644 img/bean-life-cycle.jpg
delete mode 100644 img/bloom-filter.jpg
delete mode 100644 img/cms-collector.png
delete mode 100644 img/concurrent/executors-ali.png
delete mode 100644 img/concurrent/synchronized-block.png
delete mode 100644 img/concurrent/synchronized-method.png
delete mode 100644 img/condition-await.png
delete mode 100644 img/condition-signal.png
delete mode 100644 img/condition-wait-queue.png
delete mode 100644 img/cover-index.png
delete mode 100644 img/direct-pointer.png
delete mode 100644 img/docker/docker.jpg
delete mode 100644 img/explain-all.png
delete mode 100644 img/explain-const.png
delete mode 100644 img/explain-id.png
delete mode 100644 img/explain-range.png
delete mode 100644 img/explain-ref.png
delete mode 100644 img/g1-region.jpg
delete mode 100644 img/gc-root-refer.png
delete mode 100644 img/git-status.png
delete mode 100644 img/heap-structure.png
delete mode 100644 img/image-20200520234137916.png
delete mode 100644 img/image-20200520234200868.png
delete mode 100644 img/image-20200520234231001.png
delete mode 100644 img/image-20200608232749393.png
delete mode 100644 img/image-20200608232951873.png
delete mode 100644 img/image-20200608233400458.png
delete mode 100644 img/image-20200614165333479.png
delete mode 100644 img/image-20200614165432775.png
delete mode 100644 img/index-search-range.jpg
delete mode 100644 img/index-search.jpg
delete mode 100644 img/java-basic/exception.png
delete mode 100644 img/java-basic/field-method.png
delete mode 100644 img/java-basic/io.jpg
delete mode 100644 img/java-ram-region.png
delete mode 100644 img/jmeter/http-request-param.png
delete mode 100644 img/jmeter/jmeter-aggregate-report.png
delete mode 100644 img/jmeter/jmeter-default-request.png
delete mode 100644 img/jmeter/jmeter-request-header.png
delete mode 100644 img/jmeter/jmeter-thread-group.png
delete mode 100644 img/jvm/string-equal.png
delete mode 100644 img/jvm/string-intern.png
delete mode 100644 img/jvm/string-new.png
delete mode 100644 img/left-match.jpg
delete mode 100644 img/middleware/redis-transaction.png
delete mode 100644 img/middleware/redis-usage.png
delete mode 100644 img/mysql-architecture.png
delete mode 100644 img/mysql-clustered-index.png
delete mode 100644 img/mysql/arch.png
delete mode 100644 img/mysql/current-read.png
delete mode 100644 img/mysql/mvcc-impl.png
delete mode 100644 img/mysql/myisam-innodb-index.png
delete mode 100644 img/mysql/mysql-archpng.png
delete mode 100644 img/mysql/nested-loop.png
delete mode 100644 img/mysql/orderby.png
delete mode 100644 img/net/bio.png
delete mode 100644 img/net/cdn.png
delete mode 100644 img/net/https-certificate-chain.jpg
delete mode 100644 img/net/https-certificate.png
delete mode 100644 img/net/https-client-hello.jpg
delete mode 100644 img/net/https-server-hello.jpg
delete mode 100644 img/net/nio.png
delete mode 100644 img/oauth2.png
delete mode 100644 img/object-dead.png
delete mode 100644 img/object-handle.png
delete mode 100644 img/optimistic-lock.jpg
delete mode 100644 img/others/json-web-token.png
delete mode 100644 img/others/seckill.jpg
delete mode 100644 img/parnew-collector.png
delete mode 100644 img/rabbitmq-direct.png
delete mode 100644 img/rabbitmq-fanout.png
delete mode 100644 img/rabbitmq-return-listener.png
delete mode 100644 img/rabbitmq-topic.png
delete mode 100644 img/rabbitmq.png
delete mode 100644 img/redis/cache-consist.png
delete mode 100644 img/redis/evalsha.png
delete mode 100644 img/redis/list-api.png
delete mode 100644 img/redis/redis-replication.png
delete mode 100644 img/redis/redis-skiplist.png
delete mode 100644 img/redis/time-complexity.png
delete mode 100644 "img/redis/\347\274\223\345\255\230\345\207\273\347\251\277\345\212\240\344\272\222\346\226\245\351\224\201.png"
delete mode 100644 img/scheduled-task.jpg
delete mode 100644 img/serial-collector.png
delete mode 100644 img/singleton-class-init.png
delete mode 100644 "img/spring/\346\226\271\346\263\225\347\272\247\345\210\253\346\263\250\350\247\243.png"
delete mode 100644 "img/springboot/SpringBoot\347\232\204\350\207\252\345\212\250\351\205\215\347\275\256\345\216\237\347\220\206.jpg"
delete mode 100644 img/thread-pool.png
delete mode 100644 img/thread-status.jpeg
delete mode 100644 img/threadlocal-oom.png
delete mode 100644 img/threadlocal.png
delete mode 100644 img/tree-visit.png
delete mode 100644 img/two-index.jpg
delete mode 100644 img/web/servlet-container.jpg
delete mode 100644 "img/\345\271\273\350\257\2731.png"
delete mode 100644 "img/\346\213\245\345\241\236\346\216\247\345\210\266.jpg"
delete mode 100644 "img/\350\257\267\346\261\202\344\277\235\346\212\244\351\205\215\347\275\256\346\226\271\346\263\225.png"
create mode 100644 package-lock.json
create mode 100644 package.json
delete mode 100644 "\344\270\255\351\227\264\344\273\266/Elasticsearch\345\205\245\351\227\250.md"
delete mode 100644 "\345\205\266\344\273\226/code.md"
delete mode 100644 "\345\205\266\344\273\226/linux.md"
delete mode 100644 "\345\205\266\344\273\226/note.md"
delete mode 100644 "\345\205\266\344\273\226/\345\256\236\346\210\230\347\257\207.md"
delete mode 100644 "\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241.md"
delete mode 100644 "\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\347\274\223\345\255\230.md"
delete mode 100644 "\345\211\215\347\253\257/vue.md"
delete mode 100644 "\345\267\245\345\205\267/GitHub\346\214\207\345\215\227.md"
delete mode 100644 "\345\267\245\345\205\267/NPM.md"
delete mode 100644 "\345\267\245\345\205\267/jenkins.md"
delete mode 100644 "\345\267\245\345\205\267/jmeter.md"
delete mode 100644 "\345\267\245\345\205\267/nginx\351\203\250\347\275\262\351\241\271\347\233\256.md"
delete mode 100644 "\346\225\260\346\215\256\345\272\223/MySQL\350\277\233\351\230\266.md"
delete mode 100644 "\346\241\206\346\236\266/SpringBoot\345\256\236\346\210\230.md"
delete mode 100644 "\346\241\206\346\236\266/SpringMVC.md"
delete mode 100644 "\346\241\206\346\236\266/SpringMVC\351\235\242\350\257\225\351\242\230.md"
delete mode 100644 "\346\241\206\346\236\266/Spring\345\256\236\346\210\230.md"
delete mode 100644 "\346\241\206\346\236\266/Spring\346\200\273\347\273\223.md"
delete mode 100644 "\346\241\206\346\236\266/Spring\347\224\250\345\210\260\345\223\252\344\272\233\350\256\276\350\256\241\346\250\241\345\274\217.md"
delete mode 100644 "\346\241\206\346\236\266/Spring\350\207\252\345\212\250\350\243\205\351\205\215.md"
delete mode 100644 "\346\241\206\346\236\266/\346\267\261\345\205\245\346\265\205\345\207\272Mybatis\346\212\200\346\234\257\345\216\237\347\220\206\344\270\216\345\256\236\346\210\230.md"
delete mode 100644 "\346\266\210\346\201\257\351\230\237\345\210\227/\346\255\273\344\277\241\351\230\237\345\210\227.md"
delete mode 100644 "\346\266\210\346\201\257\351\230\237\345\210\227/\346\266\210\346\201\257\351\230\237\345\210\227\351\235\242\350\257\225\351\242\230.md"
delete mode 100644 "\347\263\273\347\273\237\350\256\276\350\256\241/\347\263\273\347\273\237\350\256\276\350\256\241old.md"
delete mode 100644 "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\347\256\227\346\263\225.md"
delete mode 100644 "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/TCP IP\345\215\217\350\256\256.md"
delete mode 100644 "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/session\345\222\214cookie.md"
delete mode 100644 "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/\347\275\221\347\273\234.md"
delete mode 100644 "\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/\347\275\221\347\273\234/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\351\253\230\351\242\221\351\235\242\350\257\225\351\242\230.md"
diff --git a/.gitignore b/.gitignore
index af0556b..0182294 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,15 +1,14 @@
.DS_Store
+*.log
node_modules/
-/dist/
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-
-# Editor directories and files
-.idea
-.vscode
-*.suo
-*.ntvs*
-*.njsproj
-*.sln
+*.bat
+*.sh
+web/
+.idea/
*.py
+public/
+docs/.vuepress/.temp/
+docs/.vuepress/.cache/
+ByteDanceVerify.html
+google053c90e5a7354c40.html
+sogousiteverification.txt
\ No newline at end of file
diff --git a/CNAME b/CNAME
new file mode 100644
index 0000000..59cab98
--- /dev/null
+++ b/CNAME
@@ -0,0 +1 @@
+topjavaer.cn
diff --git a/Java/JVM.md b/Java/JVM.md
deleted file mode 100644
index b19e730..0000000
--- a/Java/JVM.md
+++ /dev/null
@@ -1,831 +0,0 @@
-
-
-> 本文已经收录到github仓库,此仓库用于分享Java相关知识总结,包括Java基础、MySQL、Springboot、mybatis、Redis、rabbitMQ等等,欢迎大家提pr和star!
->
-> github地址:https://github.com/Tyson0314/Java-learning
->
-> gitee地址:https://gitee.com/tysondai/Java-learning
-
-
-
-
-
-- [内存结构](#%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84)
- - [程序计数器](#%E7%A8%8B%E5%BA%8F%E8%AE%A1%E6%95%B0%E5%99%A8)
- - [虚拟机栈](#%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%A0%88)
- - [本地方法栈](#%E6%9C%AC%E5%9C%B0%E6%96%B9%E6%B3%95%E6%A0%88)
- - [堆](#%E5%A0%86)
- - [方法区](#%E6%96%B9%E6%B3%95%E5%8C%BA)
- - [永久代](#%E6%B0%B8%E4%B9%85%E4%BB%A3)
- - [元空间](#%E5%85%83%E7%A9%BA%E9%97%B4)
- - [运行时常量池](#%E8%BF%90%E8%A1%8C%E6%97%B6%E5%B8%B8%E9%87%8F%E6%B1%A0)
- - [直接内存](#%E7%9B%B4%E6%8E%A5%E5%86%85%E5%AD%98)
- - [对象的访问定位](#%E5%AF%B9%E8%B1%A1%E7%9A%84%E8%AE%BF%E9%97%AE%E5%AE%9A%E4%BD%8D)
-- [类文件结构](#%E7%B1%BB%E6%96%87%E4%BB%B6%E7%BB%93%E6%9E%84)
-- [类的生命周期](#%E7%B1%BB%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F)
-- [类加载的过程](#%E7%B1%BB%E5%8A%A0%E8%BD%BD%E7%9A%84%E8%BF%87%E7%A8%8B)
- - [加载](#%E5%8A%A0%E8%BD%BD)
- - [验证](#%E9%AA%8C%E8%AF%81)
- - [准备](#%E5%87%86%E5%A4%87)
- - [解析](#%E8%A7%A3%E6%9E%90)
- - [初始化](#%E5%88%9D%E5%A7%8B%E5%8C%96)
-- [双亲委派模型](#%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE%E6%A8%A1%E5%9E%8B)
- - [实现](#%E5%AE%9E%E7%8E%B0)
-- [对象死亡](#%E5%AF%B9%E8%B1%A1%E6%AD%BB%E4%BA%A1)
- - [引用计数法](#%E5%BC%95%E7%94%A8%E8%AE%A1%E6%95%B0%E6%B3%95)
- - [可达性分析](#%E5%8F%AF%E8%BE%BE%E6%80%A7%E5%88%86%E6%9E%90)
- - [可作为GC Roots的对象](#%E5%8F%AF%E4%BD%9C%E4%B8%BAgc-roots%E7%9A%84%E5%AF%B9%E8%B1%A1)
- - [引用](#%E5%BC%95%E7%94%A8)
- - [强引用](#%E5%BC%BA%E5%BC%95%E7%94%A8)
- - [软引用](#%E8%BD%AF%E5%BC%95%E7%94%A8)
- - [弱引用](#%E5%BC%B1%E5%BC%95%E7%94%A8)
- - [虚引用](#%E8%99%9A%E5%BC%95%E7%94%A8)
- - [常量回收](#%E5%B8%B8%E9%87%8F%E5%9B%9E%E6%94%B6)
- - [类的卸载](#%E7%B1%BB%E7%9A%84%E5%8D%B8%E8%BD%BD)
-- [内存分配与回收策略](#%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D%E4%B8%8E%E5%9B%9E%E6%94%B6%E7%AD%96%E7%95%A5)
- - [Minor GC 和 Full GC](#minor-gc-%E5%92%8C-full-gc)
- - [内存分配策略](#%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D%E7%AD%96%E7%95%A5)
- - [对象优先在 Eden 分配](#%E5%AF%B9%E8%B1%A1%E4%BC%98%E5%85%88%E5%9C%A8-eden-%E5%88%86%E9%85%8D)
- - [大对象直接进入老年代](#%E5%A4%A7%E5%AF%B9%E8%B1%A1%E7%9B%B4%E6%8E%A5%E8%BF%9B%E5%85%A5%E8%80%81%E5%B9%B4%E4%BB%A3)
- - [长期存活的对象进入老年代](#%E9%95%BF%E6%9C%9F%E5%AD%98%E6%B4%BB%E7%9A%84%E5%AF%B9%E8%B1%A1%E8%BF%9B%E5%85%A5%E8%80%81%E5%B9%B4%E4%BB%A3)
- - [动态对象年龄判定](#%E5%8A%A8%E6%80%81%E5%AF%B9%E8%B1%A1%E5%B9%B4%E9%BE%84%E5%88%A4%E5%AE%9A)
- - [空间分配担保](#%E7%A9%BA%E9%97%B4%E5%88%86%E9%85%8D%E6%8B%85%E4%BF%9D)
- - [Full GC 的触发条件](#full-gc-%E7%9A%84%E8%A7%A6%E5%8F%91%E6%9D%A1%E4%BB%B6)
- - [调用 System.gc()](#%E8%B0%83%E7%94%A8-systemgc)
- - [老年代空间不足](#%E8%80%81%E5%B9%B4%E4%BB%A3%E7%A9%BA%E9%97%B4%E4%B8%8D%E8%B6%B3)
- - [空间分配担保失败](#%E7%A9%BA%E9%97%B4%E5%88%86%E9%85%8D%E6%8B%85%E4%BF%9D%E5%A4%B1%E8%B4%A5)
-- [垃圾回收算法](#%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95)
- - [标记清除算法](#%E6%A0%87%E8%AE%B0%E6%B8%85%E9%99%A4%E7%AE%97%E6%B3%95)
- - [复制清除算法](#%E5%A4%8D%E5%88%B6%E6%B8%85%E9%99%A4%E7%AE%97%E6%B3%95)
- - [标记整理算法](#%E6%A0%87%E8%AE%B0%E6%95%B4%E7%90%86%E7%AE%97%E6%B3%95)
- - [分类收集算法](#%E5%88%86%E7%B1%BB%E6%94%B6%E9%9B%86%E7%AE%97%E6%B3%95)
- - [记忆集](#%E8%AE%B0%E5%BF%86%E9%9B%86)
-- [垃圾收集器](#%E5%9E%83%E5%9C%BE%E6%94%B6%E9%9B%86%E5%99%A8)
- - [Serial 收集器](#serial-%E6%94%B6%E9%9B%86%E5%99%A8)
- - [ParNew 收集器](#parnew-%E6%94%B6%E9%9B%86%E5%99%A8)
- - [Parallel Scavenge 收集器](#parallel-scavenge-%E6%94%B6%E9%9B%86%E5%99%A8)
- - [Serial Old 收集器](#serial-old-%E6%94%B6%E9%9B%86%E5%99%A8)
- - [Parallel Old 收集器](#parallel-old-%E6%94%B6%E9%9B%86%E5%99%A8)
- - [CMS 收集器](#cms-%E6%94%B6%E9%9B%86%E5%99%A8)
- - [回收过程](#%E5%9B%9E%E6%94%B6%E8%BF%87%E7%A8%8B)
- - [CMS垃圾回收特点](#cms%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%89%B9%E7%82%B9)
- - [G1收集器](#g1%E6%94%B6%E9%9B%86%E5%99%A8)
- - [回收过程](#%E5%9B%9E%E6%94%B6%E8%BF%87%E7%A8%8B-1)
-- [JVM调优工具](#jvm%E8%B0%83%E4%BC%98%E5%B7%A5%E5%85%B7)
- - [jps](#jps)
- - [jstack](#jstack)
- - [jstat](#jstat)
- - [jmap](#jmap)
-- [补充](#%E8%A1%A5%E5%85%85)
- - [对象头](#%E5%AF%B9%E8%B1%A1%E5%A4%B4)
- - [main方法执行过程](#main%E6%96%B9%E6%B3%95%E6%89%A7%E8%A1%8C%E8%BF%87%E7%A8%8B)
- - [对象创建过程](#%E5%AF%B9%E8%B1%A1%E5%88%9B%E5%BB%BA%E8%BF%87%E7%A8%8B)
-- [参考资料](#%E5%8F%82%E8%80%83%E8%B5%84%E6%96%99)
-
-
-
-## 内存结构
-
-Java 内存模型(JMM)是基于共享内存的多线程通信机制。
-
-JVM内存结构 = 类加载器 + 执行引擎 + 运行时数据区域 。
-
-
-
-> 图片来源:深入理解Java虚拟机-周志明
-
-### 程序计数器
-
-程序计数器主要有两个作用:
-
-1. 当前线程所执行的字节码的行号指示器,通过改变它实现代码的流程控制,如:顺序执行、选择、循环、异常处理。
-2. 在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。
-
-程序计数器是唯一一个不会出现 `OutOfMemoryError` 的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而死亡。
-
-### 虚拟机栈
-
-Java 虚拟机栈是由一个个栈帧组成,而每个栈帧中都拥有:局部变量表、操作数栈、动态链接、方法出口信息。每一次函数调用都会有一个对应的栈帧被压入虚拟机栈,每一个函数调用结束后,都会有一个栈帧被弹出。
-
-局部变量表是用于存放方法参数和方法内的局部变量。
-
-每个栈帧都包含一个指向运行时常量池中该栈所属方法的符号引用,在方法调用过程中,会进行动态链接,将这个符号引用转化为直接引用。
-
-- 部分符号引用在类加载阶段的时候就转化为直接引用,这种转化就是静态链接
-- 部分符号引用在运行期间转化为直接引用,这种转化就是动态链接
-
-Java 虚拟机栈也是线程私有的,每个线程都有各自的 Java 虚拟机栈,而且随着线程的创建而创建,随着线程的死亡而死亡。Java 虚拟机栈会出现两种错误:`StackOverFlowError` 和 `OutOfMemoryError`。
-
-可以通过 -Xss 参数来指定每个线程的 Java 虚拟机栈内存大小,在 JDK 1.4 中默认为 256K,而在 JDK 1.5+ 默认为 1M:
-
-```java
-java -Xss2M
-```
-
-### 本地方法栈
-
-虚拟机栈为虚拟机执行 Java 方法服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。Native 方法一般是用其它语言(C、C++ 或汇编语言等)编写的,并且被编译为基于本机硬件和操作系统的程序,对待这些方法需要特别处理。
-
-本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。
-
-### 堆
-
-此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。Java 堆是垃圾收集器管理的主要区域,因此也被称作GC堆。
-
-Java 堆可以细分为:新生代(Eden 空间、From Survivor、To Survivor 空间)和老年代。进一步划分的目的是更好地回收内存,或者更快地分配内存。
-
-通过 -Xms设定程序启动时占用内存大小,通过 -Xmx 设定程序运行期间最大可占用的内存大小。如果程序运行需要占用更多的内存,超出了这个设置值,就会抛出OutOfMemory异常。
-
-```java
-java -Xms1M -Xmx2M
-```
-
-通过 -Xss 设定每个线程的堆栈大小。设置这个参数,需要评估一个线程大约需要占用多少内存,可能会有多少线程同时运行等。
-
-> 在这里也给大家分享一个github仓库,上面放了**200多本经典的计算机书籍**,包括C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生等,可以star一下,下次找书直接在上面搜索,仓库持续更新中~
->
-> github地址:https://github.com/Tyson0314/java-books
->
-> 如果github访问不了,可以访问gitee仓库。
->
-> gitee地址:https://gitee.com/tysondai/java-books
-
-### 方法区
-
-方法区与 Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。方法区逻辑上属于堆的一部分。
-
-对方法区进行垃圾回收的主要目标是对常量池的回收和对类的卸载。
-
-#### 永久代
-
-方法区是 JVM 的规范,而永久代(PermGen)是方法区的一种实现方式,并且只有 HotSpot 有永久代。而对于其他类型的虚拟机,如 JRockit(Oracle)、J9(IBM) 并没有永久代。由于方法区主要存储类的相关信息,所以对于动态生成类的情况比较容易出现永久代的内存溢出。最典型的场景就是,在 jsp 页面比较多的情况,容易出现永久代内存溢出。
-
-#### 元空间
-
-JDK 1.8 的时候,HotSpot 的永久代被彻底移除了,使用元空间替代。元空间的本质和永久代类似,都是对JVM规范中方法区的实现。两者最大的区别在于:元空间并不在虚拟机中,而是使用直接内存。
-
-为什么要将永久代替换为元空间呢?
-
-永久代内存受限于 JVM 可用内存,而元空间使用的是直接内存,受本机可用内存的限制,虽然元空间仍旧可能溢出,但是相比永久代内存溢出的概率更小。
-
-### 运行时常量池
-
-运行时常量池是方法区的一部分,在类加载之后,会将编译器生成的各种字面量和符号引号放到运行时常量池。在运行期间动态生成的常量,如 String 类的 intern()方法,也会被放入运行时常量池。
-
-
-
-
-
-
-
-> 图片来源:https://blog.csdn.net/soonfly
-
-### 直接内存
-
-直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用。而且也可能导致 OutOfMemoryError 错误出现。
-
-NIO的Buffer提供了DirectBuffer,可以直接访问系统物理内存,避免堆内内存到堆外内存的数据拷贝操作,提高效率。DirectBuffer直接分配在物理内存中,并不占用堆空间,其可申请的最大内存受操作系统限制,不受最大堆内存的限制。
-
-直接内存的读写操作比堆内存快,可以提升程序I/O操作的性能。通常在I/O通信过程中,会存在堆内内存到堆外内存的数据拷贝操作,对于需要频繁进行内存间数据拷贝且生命周期较短的暂存数据,都建议存储到直接内存。
-
-### 对象的访问定位
-
-Java 程序通过栈上的 reference 数据来操作堆上的具体对象。对象的访问方式由虚拟机实现而定,目前主流的访问方式有使用句柄和直接指针两种:
-
-- 如果使用句柄的话,那么 Java 堆中将会划分出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息。使用句柄来访问的最大好处是 reference 中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而 reference 本身不需要修改。
-
-
-
-- 直接指针。reference 中存储的直接就是对象的地址。对象包含到对象类型数据的指针,通过这个指针可以访问对象类型数据。使用直接指针访问方式最大的好处就是访问对象速度快,它节省了一次指针定位的时间开销,虚拟机hotspot主要是使用直接指针来访问对象。
-
-
-
-
-
-## 类文件结构
-
-Class 文件结构:
-
-```java
-ClassFile {
- u4 magic; //Class 文件的标志
- u2 minor_version;//Class 的小版本号
- u2 major_version;//Class 的大版本号
- u2 constant_pool_count;//常量池的数量
- cp_info constant_pool[constant_pool_count-1];//常量池
- u2 access_flags;//Class 的访问标记
- u2 this_class;//当前类
- u2 super_class;//父类
- u2 interfaces_count;//接口
- u2 interfaces[interfaces_count];//一个类可以实现多个接口
- u2 fields_count;//Class 文件的字段属性
- field_info fields[fields_count];//一个类会可以有个字段
- u2 methods_count;//Class 文件的方法数量
- method_info methods[methods_count];//一个类可以有个多个方法
- u2 attributes_count;//此类的属性表中的属性数
- attribute_info attributes[attributes_count];//属性表集合
-}
-```
-
-魔数:class 文件标志。
-
-文件版本:高版本的 Java 虚拟机可以执行低版本编译器生成的 Class 文件,但是低版本的 Java 虚拟机不能执行高版本编译器生成的 Class 文件。
-
-常量池:存放字面量和符号引用。字面量类似于Java的常量,如字符串,声明为final的常量值等。符号引用包含三类:类和接口的全限定名,方法的名称和描述符,字段的名称和描述符。
-
-访问标志:识别一些类或者接口层次的访问信息,包括:这个 Class 是类还是接口,是否为 public 或者 abstract 类型,如果是类的话是否声明为 final 等等。
-
-当前类索引this_class:类索引用于确定这个类的全限定名。
-
-属性表集合:在 Class 文件,字段表,方法表中都可以携带自己的属性表集合,以用于描述某些场景专有的信息。与 Class 文件中其它的数据项目要求的顺序、长度和内容不同,属性表集合的限制稍微宽松一些,不再要求各个属性表具有严格的顺序,并且只要不与已有的属性名重复,任何人实现的编译器都可以向属性表中写 入自己定义的属性信息,Java 虚拟机运行时会忽略掉它不认识的属性。
-
-
-
-## 类的生命周期
-
-加载、验证、准备、解析、初始化、使用和卸载。
-
-
-
-## 类加载的过程
-
-类的加载指的是将类的class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个对象,这个对象封装了类在方法区内的数据结构,并且提供了访问方法区内的类信息的接口。
-
-### 加载
-
-类加载过程的一个阶段:通过一个类的完全限定查找此类字节码文件,并利用字节码文件创建一个Class对象。
-
-1. 通过全类名获取定义此类的二进制字节流
-2. 将字节流所代表的静态存储结构转换为方法区的运行时数据结构
-3. 在内存中生成一个代表该类的 Class 对象,作为方法区这些数据的访问入口
-
-### 验证
-
-确保Class文件的字节流中包含的信息符合虚拟机规范,保证这些信息被当作代码运行后不会危害虚拟机自身的安全。主要包括四种验证:文件格式验证,元数据验证,字节码验证,符号引用验证。
-
-### 准备
-
-为类变量分配内存并设置类变量初始值的阶段。此阶段进行内存分配的仅包括类变量,不包括实例变量和final修饰的static变量(因为final在编译的时候就会分配了),实例变量会在对象实例化时随着对象一块分配在 Java 堆中。
-
-### 解析
-
-虚拟机将常量池内的符号引用替换为直接引用的过程。符号引用用于描述目标,直接引用直接指向目标的地址。
-
-### 初始化
-
-初始化阶段就是执行类构造器\()方法的过程。\()并不是程序员在Java代码中直接编写的方法,它是Javac编译器的自动生成的,由编译器自动收集类中的所有类变量的赋值动作和静态语句块中的语句合并产生的。
-
-
-
-## 双亲委派模型
-
-一个类加载器收到一个类的加载请求时,它首先不会自己尝试去加载它,而是把这个请求委派给父类加载器去完成,这样层层委派,因此所有的加载请求最终都会传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。
-
-
-
-双亲委派模型的好处:可以防止内存中出现多份同样的字节码。如果没有双亲委派模型而是由各个类加载器自行加载的话,如果用户编写了一个java.lang.Object的同名类并放在ClassPath中,多个类加载器都去加载这个类到内存中,系统中将会出现多个不同的Object类,那么类之间的比较结果及类的唯一性将无法保证。
-
-### 实现
-
-双亲委派模型的具体实现代码在抽象类 java.lang.ClassLoader 中,此类的 loadClass() 方法运行过程如下:先检查类是否已经加载过,如果没有则让父类加载器去加载。当父类加载器加载失败时抛出 ClassNotFoundException,此时尝试自己去加载。
-
-```java
-public abstract class ClassLoader {
- // The parent class loader for delegation
- private final ClassLoader parent;
-
- public Class> loadClass(String name) throws ClassNotFoundException {
- return loadClass(name, false);
- }
-
- protected Class> loadClass(String name, boolean resolve) throws ClassNotFoundException {
- synchronized (getClassLoadingLock(name)) {
- // First, check if the class has already been loaded
- Class> c = findLoadedClass(name);
- if (c == null) {
- try {
- if (parent != null) {
- c = parent.loadClass(name, false);
- } else {
- c = findBootstrapClassOrNull(name);
- }
- } catch (ClassNotFoundException e) {
- // ClassNotFoundException thrown if class not found
- // from the non-null parent class loader
- }
-
- if (c == null) {
- // If still not found, then invoke findClass in order
- // to find the class.
- c = findClass(name);
- }
- }
- if (resolve) {
- resolveClass(c);
- }
- return c;
- }
- }
-
- protected Class> findClass(String name) throws ClassNotFoundException {
- throw new ClassNotFoundException(name);
- }
-}
-```
-
-
-
-## 对象死亡
-
-堆中几乎放着所有的对象实例,对堆垃圾回收前的第一步就是要判断那些对象已经死亡(即不能再被任何途径使用的对象)。
-
-
-
-### 引用计数法
-
-给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加 1;当引用失效,计数器就减 1;任何时候计数器为 0 的对象就是不可能再被使用的。
-
-这种方法很难解决对象之间相互循环引用的问题。
-
-```java
-public class ReferenceCountingGc {
- Object instance = null;
- public static void main(String[] args) {
- ReferenceCountingGc objA = new ReferenceCountingGc();
- ReferenceCountingGc objB = new ReferenceCountingGc();
- objA.instance = objB;
- objB.instance = objA;
- objA = null;
- objB = null;
- }
-}
-```
-
-### 可达性分析
-
-通过GC Root对象为起点,从这些节点向下搜索,搜索所走过的路径叫引用链,当一个对象到GC Root没有任何的引用链相连时,说明这个对象是不可用的。
-
-
-
-#### 可作为GC Roots的对象
-
-1. 虚拟机栈(栈帧中的本地变量表)中引用的对象
-2. 本地方法栈中JNI(Native方法)引用的对象
-3. 方法区中类静态属性引用的对象
-4. 方法区中常量引用的对象
-5. 所有被同步锁(synchronized关键字)持有的对象。
-
-### 引用
-
-引用分为强引用、软引用、弱引用、虚引用四种。
-
-在程序设计中一般很少使用弱引用与虚引用,使用软引用的情况较多,这是因为软引用可以加速 JVM 对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出(OutOfMemory)等问题的产生。
-
-#### 强引用
-
-垃圾回收器绝不会回收它。当内存空间不足,Java 虚拟机宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。
-
-#### 软引用
-
-如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。
-
-#### 弱引用
-
-在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。
-
-#### 虚引用
-
-虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。**虚引用主要用来跟踪对象被垃圾回收的活动**。
-
-### 常量回收
-
-运行时常量池主要回收的是废弃的常量。假如在常量池中存在字符串 "abc",如果当前没有任何 String 对象引用该字符串常量的话,就说明常量 "abc" 就是废弃常量,如果这时发生内存回收的话而且有必要的话,"abc" 就会被系统清理出常量池。
-
-### 类的卸载
-
-需要同时满足下面 3 个条件才能算是 “无用的类” :
-
-- 该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。
-- 加载该类的 ClassLoader 已经被回收。
-- 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
-
-虚拟机可以对满足上述 3 个条件的无用类进行回收,但不一定被回收。
-
-
-
-## 内存分配与回收策略
-
-### Minor GC 和 Full GC
-
-- Minor GC:回收新生代,因为新生代对象存活时间很短,因此 Minor GC 会频繁执行,执行的速度一般也会比较快。
-
-- Full GC:回收老年代和新生代,老年代对象其存活时间长,因此 Full GC 很少执行,执行速度会比 Minor GC 慢很多。
-
-### 内存分配策略
-
-#### 对象优先在 Eden 分配
-
-大多数情况下,对象在新生代 Eden 上分配,当 Eden 空间不够时,发起 Minor GC。
-
-#### 大对象直接进入老年代
-
-大对象是指需要连续内存空间的对象,最典型的大对象是那种很长的字符串以及数组。经常出现大对象会提前触发垃圾收集以获取足够的连续空间分配给大对象。
-
-可以设置JVM参数 -XX:PretenureSizeThreshold,大于此值的对象直接在老年代分配,避免在 Eden 和 Survivor 之间的大量内存复制。
-
-#### 长期存活的对象进入老年代
-
-通过参数 `-XX:MaxTenuringThreshold` 可以设置对象进入老年代的年龄阈值。对象在 Survivor 中每经过一次 MinorGC,年龄就增加 1 岁,当它的年龄增加到一定程度,就会被晋升到老年代中。
-
-#### 动态对象年龄判定
-
-虚拟机并不是永远要求对象的年龄必须达到 MaxTenuringThreshold 才能晋升老年代,如果在 Survivor 中相同年龄所有对象大小的总和大于 Survivor 空间的一半,则年龄大于或等于该年龄的对象可以直接进入老年代,无需等到 MaxTenuringThreshold 中要求的年龄。
-
-#### 空间分配担保
-
-在发生 Minor GC 之前,虚拟机先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果条件成立的话,那么 Minor GC 可以确认是安全的。如果不成立的话虚拟机会查看 HandlePromotionFailure 的值是否允许担保失败。如果允许,那么就会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次 Minor GC;如果小于,或者 HandlePromotionFailure 的值不允许冒险,那么就要进行一次 Full GC。
-
-### Full GC 的触发条件
-
-对于 Minor GC,其触发条件比较简单,当 Eden 空间满时,就将触发一次 Minor GC。而 Full GC 触发条件相对复杂,有以下条件:
-
-#### 调用 System.gc()
-
-只是建议虚拟机执行 Full GC,但是虚拟机不一定真正去执行。不建议使用这种方式,而是让虚拟机管理内存。
-
-#### 老年代空间不足
-
-老年代空间不足的常见场景为前文所讲的大对象直接进入老年代、长期存活的对象进入老年代等。为了避免以上原因引起的 Full GC,应当尽量不要创建过大的对象以及数组。除此之外,可以通过 -Xmn 参数调大新生代的大小,让对象尽量在新生代被回收掉,不进入老年代。还可以通过 -XX:MaxTenuringThreshold 调大对象进入老年代的年龄,让对象在新生代多存活一段时间。
-
-#### 空间分配担保失败
-
-使用复制算法的 Minor GC 需要老年代的内存空间作担保,如果担保失败会执行一次 Full GC。
-
-
-
-## 垃圾回收算法
-
-### 标记清除算法
-
-标记清除算法就是分为“标记”和“清除”两个阶段。标记出所有需要回收的对象,标记结束后统一回收所有被标记的对象。这种垃圾回收算法效率较低,并且会产生大量不连续的空间碎片。
-
-
-
-### 复制清除算法
-
-半区复制,用于新生代垃圾回收。将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。
-
-
-
-特点:实现简单,运行高效,但可用内存缩小为了原来的一半,浪费空间。
-
-### 标记整理算法
-
-根据老年代的特点提出的一种标记算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存。
-
-### 分类收集算法
-
-根据各个年代的特点采用最适当的收集算法。
-
-一般将堆分为新生代和老年代。
-
-- 新生代使用:复制算法
-- 老年代使用:标记清除算法或者标记整理算法
-
-在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清理”或者“标记-整理”算法来进行回收。
-
-由于对象之间会存在跨代引用,如果要进行一次新生代垃圾收集,除了需要遍历新生代对象,还要额外遍历整个老年代的所有对象,这会给内存回收带来很大的性能负担。
-
-跨代引用相对于同代引用来说仅占极少数。存在互相引用关系的两个对象,是应该倾向于同时生存或者同时消亡的。举个例子,如果某个新生代对象存在跨代引用,由于老年代对象难以消亡,该引用会使得新生代对象在收集时同样得以存活,进而在年龄增长之后晋升到老年代中,这时跨代引用也随即被消除了。
-
-所以没必要为了少量的跨代引用去扫描整个老年代,只需在新生代建立一个全局的数据结构 Remembered Set,这个结构把老年代划分成若干小块,标识出老年代的哪一块内存会存在跨代引用。此后当发生Minor GC时,只有包含了跨代引用的小块内存里的对象才会被加入到GC Roots进行扫描。
-
-#### 记忆集
-
-记忆集是一种用于记录从非收集区域指向收集区域的指针集合的抽象数据结构。Card Table 是最常用的一种记忆集实现形式。字节数组CARD_TABLE的每一个元素都对应着其标识的内存区域中一块特定大小的内存块,这个内存块被称作“卡页”(Card Page)。
-
-一个卡页的内存中通常包含不止一个对象,只要卡页内有一个(或更多)对象的字段存在着跨代指针,那就将对应卡表的数组元素的值标识为1,称为这个元素变脏(Dirty),没有则标识为0。在垃圾收集发生时,只要筛选出卡表中变脏的元素,就能轻易得出哪些卡页内存块中包含跨代指针,把它们加入GC Roots中一并扫描。
-
-
-
-## 垃圾收集器
-
-经典的垃圾收集器主要有三种类型:串行收集器、并行收集器和并发标记清除收集器CMS,这三种收集器分别可以是满足Java应用三种不同的需求:内存占用及并发开销最小化、应用吞吐量最大化和应用GC暂停时间最小化。
-
-JDK1.7和1.8中默认使用的是Parallel Scavenge和Parallel Old收集器组合。jdk1.9 默认垃圾收集器是G1。
-
-```java
-java -XX:+PrintCommandLineFlags -version
-```
-
-7个垃圾收集器的特点:
-
-| 收集器 | 串行、并行or并发 | 新生代/老年代 | 算法 | 目标 | 适用场景 |
-| :-------------------: | :--------------: | :-----------: | :----------------: | :----------: | :---------------------------------------: |
-| **Serial** | 串行 | 新生代 | 复制算法 | 响应速度优先 | 单CPU环境下的Client模式 |
-| **ParNew** | 并行 | 新生代 | 复制算法 | 响应速度优先 | 多CPU环境时在Server模式下与CMS配合 |
-| **Parallel Scavenge** | 并行 | 新生代 | 复制算法 | 吞吐量优先 | 在后台运算而不需要太多交互的任务 |
-| **Serial Old** | 串行 | 老年代 | 标记-整理 | 响应速度优先 | 单CPU环境下的Client模式、CMS的后备预案 |
-| **Parallel Old** | 并行 | 老年代 | 标记-整理 | 吞吐量优先 | 在后台运算而不需要太多交互的任务 |
-| **CMS** | 并发 | 老年代 | 标记-清除 | 响应速度优先 | 集中在互联网站或B/S系统服务端上的Java应用 |
-| **G1** | 并发 | both | 标记-整理+复制算法 | 响应速度优先 | 面向服务端应用,将来替换CMS |
-
-### Serial 收集器
-
-单线程收集器,使用一条垃圾收集线程去完成垃圾收集工作,在进行垃圾收集工作的时候必须暂停其他所有的工作线程( "Stop The World" ),直到它收集结束。
-
-
-
-特点:简单高效;内存消耗最小;没有线程交互的开销,单线程收集效率高;需暂停所有的工作线程,用户体验不好。
-
-### ParNew 收集器
-
-Serial 收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为(控制参数、收集算法、回收策略等等)和 Serial 收集器完全一样。
-
-
-
-除了 Serial 收集器外,只有它能与 CMS 收集器配合工作。
-
-### Parallel Scavenge 收集器
-
-新生代收集器,基于复制清除算法实现的收集器。吞吐量优先收集器,也是能够并行收集的多线程收集器,允许多个垃圾回收线程同时运行,降低垃圾收集时间,提高吞吐量。所谓吞吐量就是 CPU 中用于运行用户代码的时间与 CPU 总消耗时间的比值(吞吐量 = 运行用户代码时间 /(运行用户代码时间 + 垃圾收集时间))。Parallel Scavenge 收集器关注点是吞吐量,高效率的利用 CPU 资源。CMS 垃圾收集器关注点更多的是用户线程的停顿时间。
-
-Parallel Scavenge收集器提供了两个参数用于**精确控制吞吐量**,分别是控制最大垃圾收集停顿时间的-XX:MaxGCPauseMillis参数以及直接设置吞吐量大小的-XX:GCTimeRatio参数。
-
--XX:MaxGCPauseMillis参数允许的值是一个大于0的毫秒数,收集器将尽力保证内存回收花费的时间不超过用户设定值。
-
--XX:GCTimeRatio参数的值则应当是一个大于0小于100的整数,也就是垃圾收集时间占总时间的比率,相当于吞吐量的倒数。
-
-相比ParNew收集器的优点:
-
-1. 精确控制吞吐量;
-2. 垃圾收集的自适应的调节策略。通过参数-XX:+UseAdaptiveSizePolicy 打开自适应调节策略,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整参数以提供最合适的停顿时间或者最大的吞吐量。调整的参数包括新生代的大小(-Xmn)、Eden与Survivor区的比例(-XX:SurvivorRatio)、晋升老年代对象大小(-XX:PretenureSizeThreshold)等。
-
-### Serial Old 收集器
-
-Serial 收集器的老年代版本,它同样是一个单线程收集器,使用标记整理算法。它主要有两大用途:一种用途是在 JDK1.5 以及以前的版本中与 Parallel Scavenge 收集器搭配使用,另一种用途是作为 CMS 收集器的后备方案。
-
-### Parallel Old 收集器
-
-Parallel Scavenge 收集器的老年代版本。多线程垃圾收集,使用标记-整理算法。在注重吞吐量以及 CPU 资源的场合,都可以优先考虑 Parallel Scavenge 收集器和 Parallel Old 收集器。
-
-### CMS 收集器
-
-Concurrent Mark Sweep 并发标记清除,目的是获取最短应用停顿时间。第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程基本上同时工作。在并发标记和并发清除阶段,虽然用户线程没有被暂停,但是由于垃圾收集器线程占用了一部分系统资源,应用程序的吞吐量会降低。
-
-#### 回收过程
-
-基于标记清除算法实现,垃圾收集整个过程分为四个步骤:
-
-- 初始标记: stw暂停所有的其他线程,记录直接与 gc root 直接相连的对象,速度很快 。
-- 并发标记:从GC Roots开始对堆中对象进行可达性分析,找出存活对象,耗时较长,但是不需要停顿用户线程。
-- 重新标记: 在并发标记期间对象的引用关系可能会变化,需要重新进行标记。此阶段也会stw,停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短。
-- 并发清除:清除死亡对象,由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发的。
-
-
-
-由于在整个过程中耗时最长的并发标记和并发清除阶段中,垃圾收集器线程都可以与用户线程一起工作,所以从总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。
-
-优点:并发收集,低停顿。
-
-缺点:
-
-- 标记清除算法导致收集结束有大量空间碎片,往往出现老年代空间剩余,但无法找到足够大连续空间来分配当前对象,不得不提前触发一次 Full GC。
-- 会产生浮动垃圾,由于CMS并发清理阶段用户线程还在运行着,会不断有新的垃圾产生,这一部分垃圾出现在标记过程之后,CMS无法在当次收集中处理掉它们,只好等到下一次GC去处理;
-- 对处理器资源非常敏感。在并发阶段,收集器占用了一部分线程资源,导致应用程序变慢,降低总吞吐量。
-
-#### CMS垃圾回收特点
-
-1. cms只会回收老年代和永久代(1.8开始为元数据区,需要设置CMSClassUnloadingEnabled),不会收集年轻代;
-2. cms垃圾回收器开始执行回收操作,有一个触发阈值,默认是老年代或永久带达到92%,不能等到old内存用尽时回收,否则会导致并发回收失败。因为需要预留空间给用户线程运行。
-
-### G1收集器
-
-G1垃圾收集器的目标是用在多核、大内存的机器上,在不同应用场景中追求高吞吐量和低停顿之间的最佳平衡。
-
-在G1收集器出现之前的所有其他收集器,包括CMS在内,垃圾收集的目标范围要么是整个新生代(Minor GC),要么就是整个老年代(Major GC),再要么就是整个Java堆(Full GC)。而G1可以面向堆内存任何部分来组成回收集(Collection Set,一般简称CSet)进行回收,衡量标准不再是它属于哪个分代,而是哪块内存中存放的垃圾数量最多,回收收益最大,这就是G1收集器的Mixed GC模式。
-
-G1将整个堆分成相同大小的分区(Region),有四种不同类型的分区:Eden、Survivor、Old和Humongous(大对象)。分区的大小取值范围为1M到32M,都是2的幂次方。Region大小可以通过`-XX:G1HeapRegionSize`参数指定。Humongous区域用于存储大对象。G1认为只要大小超过了一个Region容量一半的对象即可判定为大对象。
-
-
-
-G1 收集器对各个Region回收所获得的空间大小和回收所需时间的经验值进行排序,得到一个优先级列表,每次根据用户设置的最大的回收停顿时间(使用参数-XX:MaxGCPauseMillis指定,默认值是200毫秒),优先处理回收价值最大的 Region。
-
-Java堆分成多个独立Region,Region里面会存在跨Region引用对象,在垃圾回收寻找GC Roots需要扫描整个堆。G1采用了Rset(Remembered Set)来避免扫描整个堆。每个Region会有一个RSet,记录了哪些Region引用本Region中对象,即谁引用了我的对象,这样的话,在做可达性分析的时候就可以避免全堆扫描。
-
-特点:可以由用户指定期望的垃圾收集停顿时间。
-
-#### 回收过程
-
-G1 收集器的运作大致分为以下几个步骤:
-
-- 初始标记:。stw暂停所有的其他线程,记录直接与 gc root 直接相连的对象,速度很快 。
-- 并发标记。从GC Root开始对堆中对象进行可达性分析,递归扫描整个堆里的对象图,找出要回收的对象,这阶段耗时较长,但可与用户程序并发执行。
-- 最终标记。对用户线程做另一个短暂的暂停,用于处理并发阶段对象引用出现变动的区域。
-- 筛选回收。对各个Region的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,然后把决定回收的那一部分Region的存活对象复制到空的Region中,再清理掉整个旧的Region的全部空间。这里的操作涉及存活对象的移动,是必须暂停用户线程,由多条收集器线程并行完成的。
-
-
-
-## JVM调优工具
-
-### jps
-
-列出本机所有java进程的pid。
-
-选项
-
-- -q 仅输出VM标识符,不包括class name,jar name,arguments in main method
-- -m 输出main method的参数
-- -l 输出完全的包名,应用主类名,jar的完全路径名
-- -v 输出jvm参数
-- -V 输出通过flag文件传递到JVM中的参数(.hotspotrc文件或-XX:Flags=所指定的文件
-- -Joption 传递参数到vm,例如:-J-Xms48m
-
-```bash
-jps -lvm
-//output
-//4124 com.zzx.Application -javaagent:E:\IDEA2019\lib\idea_rt.jar=10291:E:\IDEA2019\bin -Dfile.encoding=UTF-8
-```
-
-### jstack
-
-查看某个Java进程内的线程堆栈信息。-l,long listings,打印额外的锁信息,发生死锁时可以使用`jstack -l pid`观察锁持有情况。
-
-```java
-jstack -l 4124 | more
-```
-
-output:
-
-```java
-"http-nio-8001-exec-10" #40 daemon prio=5 os_prio=0 tid=0x000000002542f000 nid=0x4028 waiting on condition [0x000000002cc9e000]
- java.lang.Thread.State: WAITING (parking)
- at sun.misc.Unsafe.park(Native Method)
- - parking to wait for <0x000000077420d7e8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
- at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
- at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
- at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
- at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:103)
- at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:31)
- at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
- at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
- at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
- at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
- at java.lang.Thread.run(Thread.java:748)
-
- Locked ownable synchronizers:
- - None
-```
-
-### jstat
-
-虚拟机各种运行状态信息(类装载、内存、垃圾收集、jit编译等运行数据)。gcuitl 查看新生代、老年代及持久代GC的情况。
-
-```java
-jstat -gcutil 4124
- S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
- 0.00 0.00 67.21 19.20 96.36 94.96 10 0.084 3 0.191 0.275
-```
-
-### jmap
-
-查看堆内存快照。查看进程中新生代、老年代、永久代的使用情况。
-
-查询进程4124的堆内存快照:
-
-```java
->jmap -heap 4124
-Attaching to process ID 4124, please wait...
-Debugger attached successfully.
-Server compiler detected.
-JVM version is 25.221-b11
-
-using thread-local object allocation.
-Parallel GC with 6 thread(s)
-
-Heap Configuration:
- MinHeapFreeRatio = 0
- MaxHeapFreeRatio = 100
- MaxHeapSize = 4238344192 (4042.0MB)
- NewSize = 88604672 (84.5MB)
- MaxNewSize = 1412431872 (1347.0MB)
- OldSize = 177733632 (169.5MB)
- NewRatio = 2
- SurvivorRatio = 8
- MetaspaceSize = 21807104 (20.796875MB)
- CompressedClassSpaceSize = 1073741824 (1024.0MB)
- MaxMetaspaceSize = 17592186044415 MB
- G1HeapRegionSize = 0 (0.0MB)
-
-Heap Usage:
-PS Young Generation
-Eden Space:
- capacity = 327155712 (312.0MB)
- used = 223702392 (213.33922576904297MB)
- free = 103453320 (98.66077423095703MB)
- 68.37795697725736% used
-From Space:
- capacity = 21495808 (20.5MB)
- used = 0 (0.0MB)
- free = 21495808 (20.5MB)
- 0.0% used
-To Space:
- capacity = 23068672 (22.0MB)
- used = 0 (0.0MB)
- free = 23068672 (22.0MB)
- 0.0% used
-PS Old Generation
- capacity = 217579520 (207.5MB)
- used = 41781472 (39.845916748046875MB)
- free = 175798048 (167.65408325195312MB)
- 19.20285144484187% used
-
-27776 interned Strings occupying 3262336 bytes.
-```
-
-查询进程pid = 41843 存活的对象占用内存前100排序: `jmap -histo:live 41843 | head -n 100`
-
-
-
-## 补充
-
-### 对象头
-
-Java对象保存在内存中时,由以下三部分组成:对象头、实例数据和对齐填充字节。
-
-java的对象头由以下三部分组成:mark word、指向类信息的指针和数组长度(数组对象才有)。
-
-mark word包含:对象的hashcode、分代年龄和锁标志位。
-
-对象的实例数据就是在java代码中对象的属性和值。
-
-对齐填充字节:因为JVM要求java的对象占的内存大小应该是8bit的倍数,所以后面有几个字节用于把对象的大小补齐至8bit的倍数。
-
-**内存对齐的主要作用是:**
-
-1. 平台原因:不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
-2. 性能原因:经过内存对齐后,CPU的内存访问速度大大提升。
-
-### main方法执行过程
-
-以下是示例代码:
-
-```java
-public class App {
- public static void main(String[] args) {
- Student s = new Student("dabin");
- s.getName();
- }
-}
-
-class Student {
- public String name;
-
- public Student(String name) {
- this.name = name;
- }
-
- public String getName() {
- return this.name;
- }
-}
-```
-
-执行main方法的步骤如下:
-
-1. 编译好 App.java 后得到 App.class 后,执行 App.class,系统会启动一个 JVM 进程,从 classpath 路径中找到一个名为 App.class 的二进制文件,将 App 的类信息加载到运行时数据区的方法区内,这个过程叫做 App 类的加载
-2. JVM 找到 App 的主程序入口,执行main方法
-3. 这个main中的第一条语句为 `Student student = new Student("dabin") `,就是让 JVM 创建一个Student对象,但是这个时候方法区中是没有 Student 类的信息的,所以 JVM 马上加载 Student 类,把 Student 类的信息放到方法区中
-4. 加载完 Student 类后,JVM 在堆中为一个新的 Student 实例分配内存,然后调用构造函数初始化 Student 实例,这个 Student 实例持有 **指向方法区中的 Student 类的类型信息** 的引用
-5. 执行student.getName()时,JVM 根据 student 的引用找到 student 对象,然后根据 student 对象持有的引用定位到方法区中 student 类的类型信息的方法表,获得 getName() 的字节码地址。
-6. 执行getName()
-
-
-
-### 对象创建过程
-
-1. 类加载检查
-
- 虚拟机遇到一条 new 指令时,首先将去检查这个指令的参数是否能在常量池中定位到这个类的符号引用,并且检查这个符号引用代表的类是否已被加载过、解析和初始化过。如果没有,那必须先执行相应的类加载过程。
-
-2. 分配内存
-
- 在类加载检查通过后,接下来虚拟机将为新生对象分配内存。对象所需的内存大小在类加载完成后便可确定,为对象分配空间的任务等同于把一块确定大小的内存从 Java 堆中划分出来。
-
-3. 初始化零值
-
- 分配到的内存空间都初始化为零值(不包括对象头),这一步操作保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。
-
-4. 设置对象头
-
- Hotspot 虚拟机的对象头包括两部分信息,第一部分用于存储对象自身的运行时数据(哈希码、GC 分代年龄、锁状态标志等等),另一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是那个类的实例。
-
-5. 执行init方法
-
- 按照Java代码进行初始化。
-
-
-
-## 参考资料
-
-- 周志明. 深入理解 Java 虚拟机 [M]. 机械工业出版社
diff --git a/Java/Java web.md b/Java/Java web.md
deleted file mode 100644
index b92935b..0000000
--- a/Java/Java web.md
+++ /dev/null
@@ -1,83 +0,0 @@
-
-
-
-
-- [servlet](#servlet)
-- [jsp](#jsp)
-- [Tomcat](#tomcat)
- - [tomcat和netty区别](#tomcat%E5%92%8Cnetty%E5%8C%BA%E5%88%AB)
-- [跨域](#%E8%B7%A8%E5%9F%9F)
- - [同源策略](#%E5%90%8C%E6%BA%90%E7%AD%96%E7%95%A5)
- - [CSRF攻击](#csrf%E6%94%BB%E5%87%BB)
-- [statement和prepareStatement](#statement%E5%92%8Cpreparestatement)
-
-
-
-## servlet
-
-servlet接口定义的是一套处理网络请求的规范。servlet运行在服务端,由servlet容器管理,用于生成动态的内容(早期的web技术主要用来浏览静态页面)。
-
-Servlet是什么?
-
-- 运行在Servlet容器(如Tomcat)中的Java类
-- 没有main方法,不能独立运行,必须被部署到Servlet容器中,由容器来实例化和调用Servlet的方法
-
-servlet生命周期指它从被web服务器加载到它被销毁的整个过程,分三个阶段:
-1. 初始化阶段,调用init()方法
-2. 响应客户请求阶段,调用service()方法
-3. 终止阶段,调用destroy()方法
-
-servlet容器:负责接收请求,生成servlet实例用于处理请求(调用service方法),然后将servlet生成的响应数据返回给客户端。
-
-
-
-
-
-## jsp
-
-Java server pages。当有人请求JSP时,服务器会自动帮我们把JSP中的HTML片段和java代码拼接成静态资源响应给浏览器。也就是说JSP运行在服务器端,但最终发给客户端的都已经是转换好的HTML静态页面(在响应体里)。
-
-即:**JSP = HTML + Java片段**(各种标签本质上还是Java片段)
-
-
-
-## Tomcat
-
-Tomcat 是由 Apache 开发的一个 Servlet 容器,实现了对 Servlet 和 JSP 的支持。
-
-### tomcat和netty区别
-
-Netty和Tomcat最大的区别就在于通信协议,Tomcat是基于Http协议的,他的实质是一个基于http协议的web容器,但是Netty不一样,他能通过编程自定义各种协议,因为netty能够通过codec自己来编码/解码字节流,完成类似redis访问的功能,这就是netty和tomcat最大的不同。
-
-
-
-## 跨域
-
-当发送请求时,如果浏览器发现是跨源AJAX请求,就自动在头信息之中,添加一个Origin字段。
-
-`Origin: http://api.bob.com`
-
-对于服务端,如果请求头Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP响应。浏览器收到响应后,发现响应头信息没有包含Access-Control-Allow-Origin字段,就知道出错了,从而抛出一个错误,被XMLHttpRequest的onerror回调函数捕获。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200。
-
-如果请求头Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。
-
-```java
-Access-Control-Allow-Origin: http://api.bob.com
-Access-Control-Allow-Credentials: true
-Access-Control-Expose-Headers: FooBar
-```
-
-### 同源策略
-
-同domain(或ip),同端口,同协议视为同一个域,一个域内的脚本仅仅具有本域内的权限,也就是本域脚本只能读写本域内的资源,而无法访问其它域的资源。这种安全限制称为同源策略。
-
-### CSRF攻击
-
-跨域请求有可能被黑客利用来发动 CSRF攻击。CSRF攻击(Cross-site request forgery),跨站请求伪造。攻击者盗用了你的身份,以你的名义发送请求,比如发送邮件,发消息,盗取你的账号,甚至购买商品。
-
-
-
-## statement和prepareStatement
-Statement对象每次执行sql,相关数据库都会执行sql语句的编译,prepareStatement是预编译的,支持批处理。
-PreparedStatement是预编译的,对于批量处理可以大大提高效率,也叫JDBC存储过程。
-prepareStatement对象的开销比statement对象开销大,对于一次性操作使用statement更佳。
\ No newline at end of file
diff --git "a/Java/Java \347\274\226\347\250\213\346\200\235\346\203\263.md" "b/Java/Java \347\274\226\347\250\213\346\200\235\346\203\263.md"
deleted file mode 100644
index 960b2ac..0000000
--- "a/Java/Java \347\274\226\347\250\213\346\200\235\346\203\263.md"
+++ /dev/null
@@ -1,3916 +0,0 @@
-
-
-
-- [基础知识](#%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86)
- - [数据类型](#%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B)
- - [整型](#%E6%95%B4%E5%9E%8B)
- - [浮点类型](#%E6%B5%AE%E7%82%B9%E7%B1%BB%E5%9E%8B)
- - [char 类型](#char-%E7%B1%BB%E5%9E%8B)
- - [boolean 类型](#boolean-%E7%B1%BB%E5%9E%8B)
- - [大数值](#%E5%A4%A7%E6%95%B0%E5%80%BC)
- - [操作符](#%E6%93%8D%E4%BD%9C%E7%AC%A6)
- - [注释文档](#%E6%B3%A8%E9%87%8A%E6%96%87%E6%A1%A3)
- - [代码规范](#%E4%BB%A3%E7%A0%81%E8%A7%84%E8%8C%83)
-- [控制执行流程](#%E6%8E%A7%E5%88%B6%E6%89%A7%E8%A1%8C%E6%B5%81%E7%A8%8B)
- - [switch](#switch)
- - [break 和 continue 实现 goto](#break-%E5%92%8C-continue-%E5%AE%9E%E7%8E%B0-goto)
-- [初始化和清理](#%E5%88%9D%E5%A7%8B%E5%8C%96%E5%92%8C%E6%B8%85%E7%90%86)
- - [成员初始化](#%E6%88%90%E5%91%98%E5%88%9D%E5%A7%8B%E5%8C%96)
- - [可变参数列表](#%E5%8F%AF%E5%8F%98%E5%8F%82%E6%95%B0%E5%88%97%E8%A1%A8)
-- [访问权限控制](#%E8%AE%BF%E9%97%AE%E6%9D%83%E9%99%90%E6%8E%A7%E5%88%B6)
- - [访问权限修饰词](#%E8%AE%BF%E9%97%AE%E6%9D%83%E9%99%90%E4%BF%AE%E9%A5%B0%E8%AF%8D)
-- [复用类](#%E5%A4%8D%E7%94%A8%E7%B1%BB)
- - [继承语法](#%E7%BB%A7%E6%89%BF%E8%AF%AD%E6%B3%95)
- - [final 关键字](#final-%E5%85%B3%E9%94%AE%E5%AD%97)
- - [初始化及类的加载](#%E5%88%9D%E5%A7%8B%E5%8C%96%E5%8F%8A%E7%B1%BB%E7%9A%84%E5%8A%A0%E8%BD%BD)
- - [继承与初始化](#%E7%BB%A7%E6%89%BF%E4%B8%8E%E5%88%9D%E5%A7%8B%E5%8C%96)
-- [多态](#%E5%A4%9A%E6%80%81)
- - [缺陷:“覆盖”私有方法](#%E7%BC%BA%E9%99%B7%E8%A6%86%E7%9B%96%E7%A7%81%E6%9C%89%E6%96%B9%E6%B3%95)
- - [域和静态方法](#%E5%9F%9F%E5%92%8C%E9%9D%99%E6%80%81%E6%96%B9%E6%B3%95)
- - [构造器和多态](#%E6%9E%84%E9%80%A0%E5%99%A8%E5%92%8C%E5%A4%9A%E6%80%81)
- - [构造器的调用顺序](#%E6%9E%84%E9%80%A0%E5%99%A8%E7%9A%84%E8%B0%83%E7%94%A8%E9%A1%BA%E5%BA%8F)
-- [接口](#%E6%8E%A5%E5%8F%A3)
- - [抽象类](#%E6%8A%BD%E8%B1%A1%E7%B1%BB)
- - [接口的域](#%E6%8E%A5%E5%8F%A3%E7%9A%84%E5%9F%9F)
-- [内部类](#%E5%86%85%E9%83%A8%E7%B1%BB)
- - [.this 和 .new](#this-%E5%92%8C-new)
- - [匿名内部类](#%E5%8C%BF%E5%90%8D%E5%86%85%E9%83%A8%E7%B1%BB)
- - [工厂方法](#%E5%B7%A5%E5%8E%82%E6%96%B9%E6%B3%95)
- - [嵌套类](#%E5%B5%8C%E5%A5%97%E7%B1%BB)
- - [为什么需要内部类](#%E4%B8%BA%E4%BB%80%E4%B9%88%E9%9C%80%E8%A6%81%E5%86%85%E9%83%A8%E7%B1%BB)
- - [局部内部类](#%E5%B1%80%E9%83%A8%E5%86%85%E9%83%A8%E7%B1%BB)
- - [内部类标识符](#%E5%86%85%E9%83%A8%E7%B1%BB%E6%A0%87%E8%AF%86%E7%AC%A6)
-- [容器](#%E5%AE%B9%E5%99%A8)
- - [添加一组元素](#%E6%B7%BB%E5%8A%A0%E4%B8%80%E7%BB%84%E5%85%83%E7%B4%A0)
- - [迭代器](#%E8%BF%AD%E4%BB%A3%E5%99%A8)
- - [LinkedList](#linkedlist)
- - [Set](#set)
- - [Map](#map)
- - [Queue](#queue)
- - [PriorityQueue](#priorityqueue)
- - [foreach 和 迭代器](#foreach-%E5%92%8C-%E8%BF%AD%E4%BB%A3%E5%99%A8)
- - [适配器方法](#%E9%80%82%E9%85%8D%E5%99%A8%E6%96%B9%E6%B3%95)
-- [异常、断言和日志](#%E5%BC%82%E5%B8%B8%E6%96%AD%E8%A8%80%E5%92%8C%E6%97%A5%E5%BF%97)
- - [异常分类](#%E5%BC%82%E5%B8%B8%E5%88%86%E7%B1%BB)
- - [声明异常](#%E5%A3%B0%E6%98%8E%E5%BC%82%E5%B8%B8)
- - [捕获异常](#%E6%8D%95%E8%8E%B7%E5%BC%82%E5%B8%B8)
- - [带资源的 try 语句](#%E5%B8%A6%E8%B5%84%E6%BA%90%E7%9A%84-try-%E8%AF%AD%E5%8F%A5)
- - [断言](#%E6%96%AD%E8%A8%80)
- - [启用和禁用断言](#%E5%90%AF%E7%94%A8%E5%92%8C%E7%A6%81%E7%94%A8%E6%96%AD%E8%A8%80)
- - [日志](#%E6%97%A5%E5%BF%97)
- - [logback](#logback)
-- [字符串](#%E5%AD%97%E7%AC%A6%E4%B8%B2)
- - [格式化输出](#%E6%A0%BC%E5%BC%8F%E5%8C%96%E8%BE%93%E5%87%BA)
- - [printf() 和 format()](#printf-%E5%92%8C-format)
- - [Formatter](#formatter)
- - [正则表达式](#%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F)
- - [基础](#%E5%9F%BA%E7%A1%80)
- - [创建正则表达式](#%E5%88%9B%E5%BB%BA%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F)
- - [Pattern 和 Matcher](#pattern-%E5%92%8C-matcher)
- - [扫描输入](#%E6%89%AB%E6%8F%8F%E8%BE%93%E5%85%A5)
- - [Scanner 定界符](#scanner-%E5%AE%9A%E7%95%8C%E7%AC%A6)
- - [用正则表达式扫描](#%E7%94%A8%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%E6%89%AB%E6%8F%8F)
-- [类型信息](#%E7%B1%BB%E5%9E%8B%E4%BF%A1%E6%81%AF)
- - [反射](#%E5%8F%8D%E5%B0%84)
-- [泛型](#%E6%B3%9B%E5%9E%8B)
- - [类型参数的好处](#%E7%B1%BB%E5%9E%8B%E5%8F%82%E6%95%B0%E7%9A%84%E5%A5%BD%E5%A4%84)
- - [泛型类](#%E6%B3%9B%E5%9E%8B%E7%B1%BB)
- - [泛型接口](#%E6%B3%9B%E5%9E%8B%E6%8E%A5%E5%8F%A3)
- - [泛型方法](#%E6%B3%9B%E5%9E%8B%E6%96%B9%E6%B3%95)
- - [可变参数和泛型方法](#%E5%8F%AF%E5%8F%98%E5%8F%82%E6%95%B0%E5%92%8C%E6%B3%9B%E5%9E%8B%E6%96%B9%E6%B3%95)
- - [匿名内部类](#%E5%8C%BF%E5%90%8D%E5%86%85%E9%83%A8%E7%B1%BB-1)
- - [泛型擦除](#%E6%B3%9B%E5%9E%8B%E6%93%A6%E9%99%A4)
- - [类型变量的限定](#%E7%B1%BB%E5%9E%8B%E5%8F%98%E9%87%8F%E7%9A%84%E9%99%90%E5%AE%9A)
- - [擦除的问题](#%E6%93%A6%E9%99%A4%E7%9A%84%E9%97%AE%E9%A2%98)
- - [边界](#%E8%BE%B9%E7%95%8C)
- - [通配符](#%E9%80%9A%E9%85%8D%E7%AC%A6)
- - [上界通配符](#%E4%B8%8A%E7%95%8C%E9%80%9A%E9%85%8D%E7%AC%A6)
- - [下界通配符](#%E4%B8%8B%E7%95%8C%E9%80%9A%E9%85%8D%E7%AC%A6)
-- [数组](#%E6%95%B0%E7%BB%84)
- - [复制数组](#%E5%A4%8D%E5%88%B6%E6%95%B0%E7%BB%84)
- - [Arrays 工具](#arrays-%E5%B7%A5%E5%85%B7)
- - [数组拷贝](#%E6%95%B0%E7%BB%84%E6%8B%B7%E8%B4%9D)
- - [数组的比较](#%E6%95%B0%E7%BB%84%E7%9A%84%E6%AF%94%E8%BE%83)
- - [数组元素的比较](#%E6%95%B0%E7%BB%84%E5%85%83%E7%B4%A0%E7%9A%84%E6%AF%94%E8%BE%83)
- - [数组排序](#%E6%95%B0%E7%BB%84%E6%8E%92%E5%BA%8F)
- - [排序数组查找](#%E6%8E%92%E5%BA%8F%E6%95%B0%E7%BB%84%E6%9F%A5%E6%89%BE)
-- [容器深入研究](#%E5%AE%B9%E5%99%A8%E6%B7%B1%E5%85%A5%E7%A0%94%E7%A9%B6)
- - [填充容器](#%E5%A1%AB%E5%85%85%E5%AE%B9%E5%99%A8)
- - [SortedSet](#sortedset)
- - [队列](#%E9%98%9F%E5%88%97)
- - [优先级队列](#%E4%BC%98%E5%85%88%E7%BA%A7%E9%98%9F%E5%88%97)
- - [双向队列](#%E5%8F%8C%E5%90%91%E9%98%9F%E5%88%97)
- - [LinkedHashMap](#linkedhashmap)
- - [Colletions 工具类](#colletions-%E5%B7%A5%E5%85%B7%E7%B1%BB)
- - [排序和查询](#%E6%8E%92%E5%BA%8F%E5%92%8C%E6%9F%A5%E8%AF%A2)
- - [只读容器](#%E5%8F%AA%E8%AF%BB%E5%AE%B9%E5%99%A8)
- - [Collection 和 Map 的同步控制](#collection-%E5%92%8C-map-%E7%9A%84%E5%90%8C%E6%AD%A5%E6%8E%A7%E5%88%B6)
- - [快速报错机制](#%E5%BF%AB%E9%80%9F%E6%8A%A5%E9%94%99%E6%9C%BA%E5%88%B6)
- - [Java 1.0/1.1 的容器](#java-1011-%E7%9A%84%E5%AE%B9%E5%99%A8)
- - [BitSet](#bitset)
-- [Java I/O 系统](#java-io-%E7%B3%BB%E7%BB%9F)
- - [输入和输出](#%E8%BE%93%E5%85%A5%E5%92%8C%E8%BE%93%E5%87%BA)
- - [InputStream 和 OutputStream](#inputstream-%E5%92%8C-outputstream)
- - [Reader 和 Writer](#reader-%E5%92%8C-writer)
- - [组合输入输出流过滤器](#%E7%BB%84%E5%90%88%E8%BE%93%E5%85%A5%E8%BE%93%E5%87%BA%E6%B5%81%E8%BF%87%E6%BB%A4%E5%99%A8)
- - [文本输入和输出](#%E6%96%87%E6%9C%AC%E8%BE%93%E5%85%A5%E5%92%8C%E8%BE%93%E5%87%BA)
- - [以文本格式存储对象](#%E4%BB%A5%E6%96%87%E6%9C%AC%E6%A0%BC%E5%BC%8F%E5%AD%98%E5%82%A8%E5%AF%B9%E8%B1%A1)
- - [字符编码方式](#%E5%AD%97%E7%AC%A6%E7%BC%96%E7%A0%81%E6%96%B9%E5%BC%8F)
- - [读写二进制数据](#%E8%AF%BB%E5%86%99%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%95%B0%E6%8D%AE)
- - [DataInput 和 DataOutput](#datainput-%E5%92%8C-dataoutput)
- - [随机访问文件](#%E9%9A%8F%E6%9C%BA%E8%AE%BF%E9%97%AE%E6%96%87%E4%BB%B6)
- - [序列化](#%E5%BA%8F%E5%88%97%E5%8C%96)
- - [操作文件](#%E6%93%8D%E4%BD%9C%E6%96%87%E4%BB%B6)
- - [目录列表器](#%E7%9B%AE%E5%BD%95%E5%88%97%E8%A1%A8%E5%99%A8)
- - [](#)
-- [枚举类型](#%E6%9E%9A%E4%B8%BE%E7%B1%BB%E5%9E%8B)
- - [基本 enum 特性](#%E5%9F%BA%E6%9C%AC-enum-%E7%89%B9%E6%80%A7)
- - [向 enum 添加新方法](#%E5%90%91-enum-%E6%B7%BB%E5%8A%A0%E6%96%B0%E6%96%B9%E6%B3%95)
- - [覆盖 enum 的方法](#%E8%A6%86%E7%9B%96-enum-%E7%9A%84%E6%96%B9%E6%B3%95)
- - [Switch 语句中的 enum](#switch-%E8%AF%AD%E5%8F%A5%E4%B8%AD%E7%9A%84-enum)
- - [EnumSet](#enumset)
- - [源码解析](#%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90)
- - [EnumMap](#enummap)
-- [注解](#%E6%B3%A8%E8%A7%A3)
- - [基本语法](#%E5%9F%BA%E6%9C%AC%E8%AF%AD%E6%B3%95)
- - [自定义注解](#%E8%87%AA%E5%AE%9A%E4%B9%89%E6%B3%A8%E8%A7%A3)
- - [元注解](#%E5%85%83%E6%B3%A8%E8%A7%A3)
- - [编写注解处理器](#%E7%BC%96%E5%86%99%E6%B3%A8%E8%A7%A3%E5%A4%84%E7%90%86%E5%99%A8)
- - [注解综合](#%E6%B3%A8%E8%A7%A3%E7%BB%BC%E5%90%88)
-
-
-
-## 基础知识
-
-### 数据类型
-
-#### 整型
-
-| 类型 | 存储空间 |
-| ----- | -------- |
-| int | 4字节 |
-| short | 2字节 |
-| long | 8字节 |
-| byte | 1字节 |
-
-byte 的取值范围是-128~127。整型的取值范围和运行 Java 代码的机器无关。Java 没有无符号的整型。
-
-**数据类型表示:**
-
-长整型:400L
-
-十六进制:0xCA
-
-八进制:020(有前缀0)
-
-二进制:0b1001(Java 7及以上版本)
-
-从 Java 7开始,可以为数据字面量添加下划线,如1_000_000,方便易读。Java 编译器会去除这些下划线。
-
-#### 浮点类型
-
-| 类型 | 存储空间 |
-| ------ | -------- |
-| float | 4字节 |
-| double | 8字节 |
-
-float 类型的数值会有后缀 F 或者 f (2.01F),没有后缀 F 的浮点数默认为 double 类型(也可以加后缀 D)。浮点数采用二进制系统表示,有些数值会有精度损失,如`System.out.println(2.0 - 1.1)`会输出0.8999999999999999。如果数值计算不允许有任何精度误差,应该使用 BigDecimal。
-
-**特殊值**
-
-正无穷大:Double.POSITIVE_INFINITY
-
-负无穷大:Double.NEGATIVE_INFINITY
-
-NaN(不是一个数字):Double.NaN
-
-#### char 类型
-
-Unicode 字符用一个或者两个 char 值描述。Unicode 字符可以表示为十六进制,其范围从/u0000到/uffff。
-
-#### boolean 类型
-
-布尔类型有两个值:true 和 false。布尔型和整型不能相互转化。Java 语言表达式所操作的 boolean 值,在编译之后都使用Java虚拟机中的int数据类型来代替,而 boolean 数组将会被编码成 Java 虚拟机的 byte 数组,每个元素 boolean 元素占8位”。这样我们可以得出 boolean 类型占了单独使用是4个字节,在数组中又是1个字节。
-
-#### 大数值
-
-BigInteger 实现了任意精度的整数运算,BigDecimal 实现了任意精度的浮点数运算。
-
-```java
-BigInteger a = BigInteger.valueOf(100);
-BigInteger b = BigInteger.valueOf(100);
-BigInteger c = a.add(b);
-BigInteger d = c.multiply(b.add(BigInteger.valueOf(2)));
-```
-
-### 操作符
-
-**截尾和舍入**
-
-```java
-float num = 0.7f;
-//类型转化对数字进行截尾
-System.out.println((int)num); //0
-//四舍五入操作
-System.out.println(Math.round(num)); //1
-```
-
-### 注释文档
-
-javadoc 用来提取注释的工具。javadoc 只能为 public 和 protected 成员进行文档注释。
-
-### 代码规范
-
-可以采用将 public 成员放在开头,后面跟着 protect、包访问权限和 private 成员的创建类的形式,方便类的使用者阅读最为重要的部分(public成员)。
-
-```java
-public class OrganizedByAccess {
- public void method1() {}
- public void method2() {}
- protected void method3() {}
- void method4() {}
- private method5() {}
- private int i;
- //...
-}
-```
-
-
-
-## 控制执行流程
-
-### switch
-
-break 会跳出 switch 语句,没有 break 语句则会一直往下执行。
-
-```java
-int i = 1;
-switch (i) {
- case 0:
- System.out.println("i = 0");
- break;
- case 1:
- System.out.println("i = 1");
- break;
- case 2:
- System.out.println("i = 2");
- break;
- default:
- System.out.println("i >= 3");
-}
-//输出i = 1
-
-switch (i) {
- case 0:
- System.out.println("i = 0");
- //break;
- case 1:
- System.out.println("i = 1");
- //break;
- case 2:
- System.out.println("i = 2");
- //break;
- default:
- System.out.println("i >= 3");
-}
-//输出
-//i = 1
-//i = 2
-//i >= 3
-```
-
-### break 和 continue 实现 goto
-
-```java
-public class LabeledFor {
- public static void main(String[] args) {
- int i = 0;
- outer: // Can't have statements here
- for(; true ;) { // infinite loop
- inner: // Can't have statements here
- for(; i < 10; i++) {
- print("i = " + i);
- if(i == 2) {
- print("continue");
- continue;
- }
- if(i == 3) {
- print("break");
- i++;
- break;//break跳出循环,不会执行递增操作
- }
- if(i == 7) {
- print("continue outer");
- i++;
- continue outer;//continue跳到循环顶部,不会执行递增操作
- }
- if(i == 8) {
- print("break outer");
- break outer;
- }
- for(int k = 0; k < 5; k++) {
- if(k == 3) {
- print("continue inner");
- continue inner;
- }
- }
- }
- }
- // Can't break or continue to labels here
- }
-}
-```
-
-
-
-## 初始化和清理
-
-### 成员初始化
-
-局部变量未初始化就使用,会产生编译时错误。类的数据成员是基本类型,它们会有初值(如 int 类型的数据成员初值为0)。
-
-### 可变参数列表
-
-Java SE5 开始支持可变参数列表。当指定参数时,编译器会将元素列表转换为数组。
-
-```java
-public class VarArgs {
- public static void printArray(Object... args) {
- for(Object obj : args) {
- System.out.print(obj + " ");
- }
- System.out.println();
- }
-
- public static void main(String[] args) {
- printArray(new Integer(1), new Integer(3));
- printArray("tyson", "sophia", "tom");
- printArray(new Integer[]{2, 4, 6});
- printArray();//空列表
- }
-}
-```
-
-## 访问权限控制
-
-package 语句必须是文件中第一行非注释代码。
-
-### 访问权限修饰词
-
-1. 包访问权限
-
- 默认的访问权限没有任何关键字,通常是指包访问权限,即当前包的所有其他类对当前成员具有访问权限。
-
-2. protected
-
- 继承访问权限,派生类可以访问基类的 protected 元素。同时,protected 也提供包访问权限,即相同包内的其他类也可以访问 protected 元素。
-
-3. private
-
- 通过 private 隐藏了代码细节,类库设计者可以更改类内部工作方式,而不会影响客户端。
-
-4. public
-
-
-
-示例代码:
-
-```java
-package com.tyson.chapter6_access;
-//Cookie.java
-public class Cookie {
- public Cookie() {
- System.out.println("Cookie constructor");
- }
- protected void bite() {
- System.out.println("bite");
- }
-}
-
-//ChocolateChip.java
-public class ChocolateChip extends Cookie {
- //私有构造器
- private ChocolateChip() {
- System.out.println("chocolate chip constructor");
- }
- //通过静态方法创建类
- public static ChocolateChip createChocolateChip() {
- return new ChocolateChip();
- }
-
- public void chomp() {
- //访问父类的protected成员
- bite();
- }
-}
-
-class Application {
- public static void main(String[] args) {
- ChocolateChip chocolateChip = ChocolateChip.createChocolateChip();
- chocolateChip.chomp();
- }
-}
-```
-
-
-
-## 复用类
-
-### 继承语法
-
-派生类如果没有指定某个基类的构造器,则会调用基类的默认构造器,若没有默认的构造器(不带参数的构造器),编译器会报错。
-
-### final 关键字
-
-1. final 数据
-
- 基本类型变量用 final 修饰,则该变量是常量,数值不能改变;对象引用用 final 修饰,则一旦该引用被初始化指向一个对象,就不能再把它改为指向另一个对象,不过对象自身可以修改,只是引用不能修改。
-
-2. final 参数
-
- 将参数指明为 final,则无法在方法中更改参数。
-
-```java
-public class FinalArguments {
- void add(final Num num) {
- //num = new Num(1); //不能更改final参数
- num.i++;
- }
- void add(final int i) {
- //i++; //不能更改final参数的值
- }
-}
-
-class Num {
- int i;
- public Num(int i) {
- this.i = i;
- }
-}
-```
-
-3. final 方法
-
- final 方法不能被重写,使用 final 方法主要是为了防止继承类修改它的含义。过去使用 final 方法效率会高点,现在的 JVM 相比之前有了很大的优化,没必要通过设置 final 来提高效率。
-
-4. final 类
-
- final 类不能被继承。final 类的所有方法都被隐式指定为 final。JDK 的 String 类就是 final 类。
-
-### 初始化及类的加载
-
-#### 继承与初始化
-
-```java
-class Insect {
- private int i = printInit("Insect.i initialized");
- protected int j;
- Insect() {
- System.out.println("i = " + i + ", j = " + j);
- j = 39;
- }
- private static int x1 =
- printInit("static Insect.x1 initialized");
- static int printInit(String s) {
- System.out.println(s);
- return 47;
- }
-}
-
-public class Beetle extends Insect {
- private int k = printInit("Beetle.k initialized");
- public Beetle() {
- System.out.println("k = " + k + ", j = " + j);
- }
- private static int x2 =
- printInit("static Beetle.x2 initialized");
- public static void main(String[] args) {
- System.out.println("Beetle constructor");
- Beetle b = new Beetle();
- }
-}
-/*output
-static Insect.x1 initialized
-static Beetle.x2 initialized
-Beetle constructor
-Insect.i initialized
-i = 47, j = 0
-Beetle.k initialized
-k = 47, j = 39
- */
-```
-
-
-
-## 多态
-
-封装把接口和实现分离开来,多态消除类型之间的耦合关系。
-
-向上转型:将子类看作是它的基类的过程。子类转型为基类就是在继承图上向上移动。
-
-
-
-### 缺陷:“覆盖”私有方法
-
-```java
-public class PrivateOverride {
- private void f() {
- System.out.println("private f()");
- }
-
- public static void main(String[] args) {
- PrivateOverride po = new Derived();
- po.f();
- }
-}
-
-class Derived extends PrivateOverride {
- public void f() {
- System.out.println("public f()");
- }
-}
-/*
-private f()
- */
-```
-
-Derived 类的 f() 实际上是一个全新的方法。基类的 f() 方法在子类 Derived 中不可见,因此不能被重载。
-
-### 域和静态方法
-
-只有普通的方法调用具备多态性,域和静态方法没有多态性。
-
-### 构造器和多态
-
-构造器实际上是 static 方法,只不过 static 声明是隐式的,不具备多态性。
-
-#### 构造器的调用顺序
-
-```java
-class Meal {
- private int weight = get();
- Meal() {
- print("Meal()");
- }
- public int get() {
- System.out.println("Meal.get()");
- return 1;
- }
-}
-
-class Bread {
- Bread() {
- print("Bread()");
- }
-}
-
-class Cheese {
- Cheese() {
- print("Cheese()");
- }
-}
-
-class Lettuce {
- Lettuce() {
- print("Lettuce()");
- }
-}
-
-class Lunch extends Meal {
- private int price = food();
- Lunch() {
- print("Lunch()");
- }
- public int food() {
- System.out.println("Lunch.food()");
- return 1;
- }
-}
-
-class PortableLunch extends Lunch {
- PortableLunch() {
- print("PortableLunch()");
- }
-}
-
-public class Sandwich extends PortableLunch {
- private Bread b = new Bread();
- private Cheese c = new Cheese();
- private Lettuce l = new Lettuce();
-
- public Sandwich() {
- print("Sandwich()");
- }
-
- public static void main(String[] args) {
- new Sandwich();
- }
-} /* Output:
-Meal.get()
-Meal()
-Lunch.food()
-Lunch()
-PortableLunch()
-Bread()
-Cheese()
-Lettuce()
-Sandwich()
-*/
-
-```
-
-调用顺序:
-
-1. 按照声明顺序调用基类成员的初始化方法
-2. 基类构造器。从最顶层的基类到最底层的派生类
-3. 按照声明顺序调用成员的初始化方法
-4. 派生类构造器
-
-
-
-## 接口
-
-接口和内部类为我们提供了一种将接口与实现分离的方法。
-
-### 抽象类
-
-从一个抽象类继承,需要为基类中的所有方法提供方法定义。如果没有提供,则派生类也是抽象类。
-
-如果有一个类,让其包含任何 abstract 方法都没有实际意义,而我们又想阻止产生这个类的任何对象,这时候可以把它设置成一个没有任何抽象方法的抽象类。
-
-### 接口的域
-
-接口中的域都自动声明为`public static final`,故接口可以用来创建常量组。Java SE5之前没有提供 enum 实现,可以通过接口实现 enum 的功能。
-
-## 内部类
-
-内部类允许把一些逻辑相关的类组织在一起,并控制内部类的可视性。内部类可以访问外围类,有些通过编写内部类可以让代码结构更清晰。
-
-### .this 和 .new
-
-使用外部类的名字后面紧跟 .this,可以生成对外部类对象的引用。
-
-```java
-public class DotThis {
- void f() {
- System.out.println("DotThis.f()");
- }
- public class Inner {
- public DotThis outer() {
- return DotThis.this;
- }
- }
- public Inner inner() {
- return new Inner();
- }
-
- public static void main(String[] args) {
- DotThis dt = new DotThis();
- DotThis.Inner dti = dt.inner();
- dti.outer().f();
- }
-}
-//output DotThis.f()
-```
-
-使用 .new 创建内部类的对象。
-
-```java
-public class DotNew {
- public class Inner {}
-
- public static void main(String[] args) {
- DotNew dn = new DotNew();
- DotNew.Inner dni = dn.new Inner();
- }
-}
-```
-
-创建内部类的对象,必须通过外部类的对象来创建,在拥有外部类对象之前是不可能创建内部类对象的。这是因为内部类对象会隐式连接到创建它的外部类对象上。但是如果创建的是嵌套类(静态内部类),则不需要创建外部类对象。
-
-### 匿名内部类
-
-```java
-//Wrapping.java
-public class Wrapping {
- private int i;
-
- public Wrapping(int x) {
- i = x;
- }
- public int value() {
- return i;
- }
-}
-
-//Parcel.java
-public class Parcel {
- public Wrapping wrapping(int x) {
- return new Wrapping(x) {//传递构造器参数
- @Override
- public int value() {
- return super.value() * 47;
- }
- };
- }
-
- public static void main(String[] args) {
- Parcel p = new Parcel();
- Wrapping w = p.wrapping(8);
- System.out.println(w.value());
- }
-}
-//output 376
-```
-
-在匿名类定义字段时,还能够对其执行初始化操作。
-
-```java
-//Destination.java
-public interface Destination {
- String readLabel();
-}
-
-//Parcel1.java
-public class Parcel1 {
- public Destination destination(final String dest) {
- return new Destination() {
- private String label = dest;
- @Override
- public String readLabel() {
- return label;
- }
- };
- }
-
- public static void main(String[] args) {
- Parcel1 p1 = new Parcel1();
- Destination d = p1.destination("Puning");
- System.out.println(d.readLabel());
- }
-}
-```
-
-匿名内部类没有命名构造器,通过实例初始化给匿名内部类创建构造器的效果。
-
-```java
-abstract class Base {
- public Base(int i) {
- System.out.println("Base constructor, i = " + i);
- }
-
- public abstract void f();
-}
-
-public class AnonymousConstructor {
- public static Base getBase(int i) {
- return new Base(i) {
- //构造器的效果
- {
- System.out.println("Inside instance initializer");
- }
- @Override
- public void f() {
- System.out.println("In anonymous f()");
- }
- };
- }
-
- public static void main(String[] args) {
- Base base = getBase(47);
- base.f();
- }
-} /* Output:
-Base constructor, i = 47
-Inside instance initializer
-In anonymous f()
-*/
-```
-
-#### 工厂方法
-
-使用匿名内部类改进工厂方法。
-
-```java
-interface Game { boolean move();}
-interface GameFactory { Game getGame();}
-
-public class Games {
- public static void playGame(GameFactory gameFactory) {
- Game game = gameFactory.getGame();
- while(game.move()) {
- ;
- }
- }
-
- public static void main(String[] args) {
- playGame(Checkers.factory);
- playGame(Chess.factory);
- }
-}
-
-class Chess implements Game {
-
- private Chess() {}
- private int moves = 0;
- private static final int MOVES = 4;
-
- @Override
- public boolean move() {
- System.out.println("chess move " + moves);
- return ++moves != MOVES;
- }
-
- public static GameFactory factory = new GameFactory() {
- @Override
- public Game getGame() {
- return new Chess();
- }
- };
-}
-
-class Checkers implements Game {
- private Checkers() {}
- private int moves = 0;
- private static final int MOVES = 3;
- @Override
- public boolean move() {
- System.out.println("checkers move" + moves);
- return ++moves != MOVES;
- }
-
- public static GameFactory factory = new GameFactory() {
- @Override
- public Game getGame() {
- return new Checkers();
- }
- };
-}
-/*
-checkers move0
-checkers move1
-checkers move2
-chess move 0
-chess move 1
-chess move 2
-chess move 3
- */
-```
-
-### 嵌套类
-
-将内部类声明为 static,即为嵌套类。普通的内部类隐式保存了一个指向外围类对象的引用,而嵌套类没有。所以创建嵌套类的对象,不需要先创建外围类对象。并且嵌套类不能访问非静态的外围类的成员。
-
-普通的内部类不能包含 static 字段和 static 方法,也不能包含嵌套类,但嵌套类可以包含这些。
-
-```java
-public class NestingClass {
- public static class StaticInner {
- public void dynamicFuc() {
- System.out.println("StaticInner动态方法");
- }
- public static void staticFuc() {
- System.out.println("StaticInner静态方法");
- }
- }
-
- public static void main(String[] args) {
- StaticInner si = new StaticInner();
- si.dynamicFuc();
- StaticInner.staticFuc();//直接通过类名调用静态方法
- }
-}
-/*
-StaticInner动态方法
-StaticInner静态方法
- */
-```
-
-嵌套类没有.this引用。
-
-### 为什么需要内部类
-
-使用内部类可以实现多重继承。
-
-```java
-interface Selector {
- boolean end();
- Object current();
- void next();
-}
-
-public class Sequence {
- private Object[] items;
- private int next = 0;
-
- public Sequence(int size) {
- items = new Object[size];
- }
-
- public void add(Object x) {
- if (next < items.length)
- items[next++] = x;
- }
-
- private class SequenceSelector implements Selector {
- private int i = 0;
-
- public boolean end() {
- return i == items.length;
- }
- public Object current() {
- return items[i];
- }
- public void next() {
- if (i < items.length) i++;
- }
- }
-
- public Selector selector() {
- return new SequenceSelector();
- }
-
- public static void main(String[] args) {
- Sequence sequence = new Sequence(10);
- for (int i = 0; i < 10; i++)
- sequence.add(Integer.toString(i));
- Selector selector = sequence.selector();
- while (!selector.end()) {
- System.out.print(selector.current() + " ");
- selector.next();
- }
- }
-} /* Output:
-0 1 2 3 4 5 6 7 8 9
-*/
-```
-
-如果 Sequence.java 不使用内部类,就必须声明 Sequence 是一个 Selector,对于某个特定的 Sequence 只能有一个 Selector。使用内部类的话很容易就可以拥有另一个方法 reverseSelector(),用来生成一个反向遍历序列的 Selector。
-
-### 局部内部类
-
-```java
-interface Counter {int next();}
-
-public class LocalInnerClass {
- private int count = 0;
-
- Counter getCounter(final String name) {
- // A local inner class:
- class LocalCounter implements Counter {
- public LocalCounter() {
- // Local inner class can have a constructor
- print("LocalCounter()");
- }
-
- public int next() {
- printnb(name); // Access local final
- return count++;
- }
- }
- return new LocalCounter();
- }
-
- // The same thing with an anonymous inner class:
- Counter getCounter2(final String name) {
- return new Counter() {
- // Anonymous inner class cannot have a named
- // constructor, only an instance initializer:
- {
- print("Counter()");
- }
-
- public int next() {
- printnb(name); // Access local final
- return count++;
- }
- };
- }
-
- public static void main(String[] args) {
- LocalInnerClass lic = new LocalInnerClass();
- Counter c1 = lic.getCounter("Local inner "),
- c2 = lic.getCounter2("Anonymous inner ");
- print(c1.next());
- print(c2.next());
- }
-} /* Output:
-LocalCounter()
-Counter()
-Local inner 0
-Anonymous inner 1
-*/
-```
-
-局部内部类可以拥有命名的构造器或者重载构造器,而匿名内部类只能使用实例初始化。如果需要不止一个该内部类对象,那么只能使用局部内部类。
-
-### 内部类标识符
-
-内部类文件的命名格式:外围类名字加上"$",再加上内部类的名字(如果是匿名类,则是一个数字)。
-
-`LocalInnerClass$1LocalCounter.class`
-
-`LocalInnerClass$1.class`
-
-
-
-## 容器
-
-
-
-### 添加一组元素
-
-```java
-public class AddingGroups {
- public static void main(String[] args) {
- Collection nums = new ArrayList<>(Arrays.asList(1, 2, 3, 4));
- Integer[] ints = {5, 6, 7};
- nums.addAll(Arrays.asList(ints));
- Collections.addAll(nums, 8, 9);
- Collections.addAll(nums, ints);
-
- List list = Arrays.asList(1, 2, 3);
- list.set(1, 90);
- System.out.println(list.getClass());
- //底层是数组java.util.Arrays$ArrayList,不能修改结构,java.lang.UnsupportedOperationException
- //list.add(7);
- }
-}
-```
-
-`Arrays.asList()`返回类型是`java.util.Arrays$ArrayList`,底层是数组,试图删除或增加元素会抛异常 UnsupportedOperationException。
-
-### 迭代器
-
-```java
-import typeinfo.pets.Pet;
-import typeinfo.pets.Pets;
-import java.util.Iterator;
-import java.util.List;
-
-public class SimpleIteration {
- public static void main(String[] args) {
- List pets = Pets.arrayList(8);
- Iterator it = pets.iterator();
- while(it.hasNext()) {
- Pet p = it.next();
- System.out.print(p.id() + ":" + p + " ");
- }
- System.out.println();
- for(Pet p : pets) {
- System.out.print(p.id() + ":" + p + " ");
- }
- System.out.println();
- it = pets.iterator();
- for(int i = 0; i < 4; i++) {
- it.next();
- it.remove();//删除最近遍历的元素
- }
- System.out.println(pets);
- }
-}
-/*output
-0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx
-0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx
-[Pug, Cymric, Pug, Manx]
- */
-```
-
-ListIterator 是一个更为强大的 Iterator 子类型,它只能用于各种 List 类的遍历。ListIterator 可以双向移动。它还可以产生迭代器当前位置前一个和后一个元素的索引,并且可以使用 set() 方法修改最近遍历的元素。通过 listIterator(index) 可以创建指向索引为index的元素的ListIterator。
-
-```java
-public class ListIteration {
- public static void main(String[] args) {
- List pets = Pets.arrayList(8);
- ListIterator it = pets.listIterator();
- while(it.hasNext()) {
- System.out.print(it.next() + ", " + it.nextIndex() +
- ", " + it.previousIndex() + "; ");
- }
- System.out.println();
- while(it.hasPrevious()) {
- System.out.print(it.previous().id() + " ");
- }
- System.out.println();
- System.out.println(pets);
- //创建指向索引为index元素处的ListIterator
- it = pets.listIterator(3);
- while(it.hasNext()) {
- it.next();
- it.set(Pets.randomPet());
- }
- System.out.println(pets);
- }
-}/*output
-Rat, 1, 0; Manx, 2, 1; Cymric, 3, 2; Mutt, 4, 3; Pug, 5, 4; Cymric, 6, 5; Pug, 7, 6; Manx, 8, 7;
-7 6 5 4 3 2 1 0
-[Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug, Manx]
-[Rat, Manx, Cymric, Cymric, Rat, EgyptianMau, Hamster, EgyptianMau]
- */
-```
-
-### LinkedList
-
-```java
-public class LinkedListFeatures {
- public static void main(String[] args) {
- LinkedList pets =
- new LinkedList(Pets.arrayList(5));
- print(pets);
- // Identical:
- print("pets.getFirst(): " + pets.getFirst());
- print("pets.element(): " + pets.element());
- // Only differs in empty-list behavior:
- print("pets.peek(): " + pets.peek());
- // Identical; remove and return the first element:
- print("pets.remove(): " + pets.remove());
- print("pets.removeFirst(): " + pets.removeFirst());
- // Only differs in empty-list behavior:
- print("pets.poll(): " + pets.poll());
- print(pets);
- pets.addFirst(new Rat());
- print("After addFirst(): " + pets);
- pets.offer(Pets.randomPet());
- print("After offer(): " + pets);
- pets.add(Pets.randomPet());
- print("After add(): " + pets);
- pets.addLast(new Hamster());
- print("After addLast(): " + pets);
- print("pets.removeLast(): " + pets.removeLast());
- }
-} /* Output:
-[Rat, Manx, Cymric, Mutt, Pug]
-pets.getFirst(): Rat
-pets.element(): Rat
-pets.peek(): Rat
-pets.remove(): Rat
-pets.removeFirst(): Manx
-pets.poll(): Cymric
-[Mutt, Pug]
-After addFirst(): [Rat, Mutt, Pug]
-After offer(): [Rat, Mutt, Pug, Cymric]
-After add(): [Rat, Mutt, Pug, Cymric, Pug]
-After addLast(): [Rat, Mutt, Pug, Cymric, Pug, Hamster]
-pets.removeLast(): Hamster
-*/
-```
-
-LinkedList 具有实现栈所有功能的所有方法,可以将 LinkedList 作为栈使用:
-
-```java
-public class MyStack {
- private LinkedList storage = new LinkedList<>();
- public void push(T t) {
- storage.addFirst(t);
- }
- public T pop() {
- return storage.removeFirst();
- }
- public T peek() {
- return storage.getFirst();
- }
- public boolean isEmpty() {
- return storage.isEmpty();
- }
- @Override
- public String toString() {
- return storage.toString();
- }
-}
-```
-
-### Set
-
-Set 不保存重复的元素,插入相同的元素会被忽略。HashSet 存储元素没有顺序;TreeSet 按照升序的方式存储元素;LinkedHashList 使用链表维护元素的插入顺序,并通过散列提供了快速访问能力。
-
-```java
-public class SortedSetOfInteger {
- public static void main(String[] args) {
- Random rand = new Random(47);
- SortedSet set = new TreeSet<>();
- for (int i = 0; i < 1000; i++) {
- set.add(rand.nextInt(30));
- }
- System.out.println(set);
- }
-}
-/*output
-[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
- 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
- */
-```
-
-TreeSet 对字母排序是按照字典序,大小写字母会被分到不同的组,如果想按照字母序排序,可以向 TreeSet 构造器传入 String.CASE_INSENTIVE_ORDER 比较器。
-
-```java
-public class SortedWords {
- public static void main(String[] args) {
- //往构造器传入String.CASE_INSENTIVE_ORDER 比较器
- Set words = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
- String[] strs = {"hello", "world", "tyson", "a"};
- words.addAll(Arrays.asList(strs));
- System.out.println(words);
- }
-}
-/*
-[a, hello, tyson, world]
- */
-```
-
-### Map
-
-HashMap 设计用来快速访问;TreeMap保持键始终处于升序状态;LinkedHashMap 使用链表维护元素的插入顺序,并通过散列提供了快速访问能力。
-
-```java
-public class PetMap {
- public static void main(String[] args) {
- Map petMap = new HashMap<>();
- petMap.put("Tyson", new Cat("cat"));
- petMap.put("Sophia", new Dog("dog"));
- petMap.put("Tom", new Hamster("hamster"));
-
- System.out.println(petMap.keySet());
- System.out.println(petMap.values());
- for(String person : petMap.keySet()) {
- System.out.println(person + " : " + petMap.get(person));
- }
-
- Set> entries = petMap.entrySet();
- for(Map.Entry entry : entries) {
- System.out.println(entry.getKey() + ": " + entry.getValue());
- entry.setValue(new Cat("cat"));
- System.out.println(entry.getKey() + ": " + entry.getValue());
- }
-
- }
-}
-/*output
-[Tom, Tyson, Sophia]
-[Hamster hamster, Cat cat, Dog dog]
-Tom : Hamster hamster
-Tyson : Cat cat
-Sophia : Dog dog
-Tom: Hamster hamster
-Tom: Cat cat
-Tyson: Cat cat
-Tyson: Cat cat
-Sophia: Dog dog
-Sophia: Cat cat
- */
-```
-
-### Queue
-
-LinkedList 实现了 Queue 接口,可以作为队列使用。
-
-```java
-public class QueueDemo {
- public static void printQ(Queue queue) {
- while(queue.peek() != null) {
- System.out.print(queue.remove() + " ");
- }
- System.out.println();
- }
- public static void main(String[] args) {
- Queue queue = new LinkedList<>();
- Random rand = new Random(47);
- for(int i = 0; i < 10; i++) {
- queue.offer(rand.nextInt(i + 10));
- }
- printQ(queue);
- Queue qc = new LinkedList<>();
- for(char c : "tyson".toCharArray()) {
- qc.offer(c);
- }
- printQ(qc);
- }
-}
-```
-
-peek()和 element()都将在不移除的情况下返回对头,peek()方法在队列为空时返回 null,而element()会跑 NoSuchElementExeception 异常。
-
-poll()和remove()方法将移除并返回对头,poll()在队列为空时返回 null,而remove()会跑 NoSuchElementExeception 异常。
-
-#### PriorityQueue
-
-Integer、String 和 Character 内建了自然排序,可以与 PriorityQueue 一起工作。要想在 PriorityQueue 中使用自己的类,就必须提供自己的 Comparator。
-
-```java
-public class PriorityQueueDemo {
- public static void main(String[] args) {
- PriorityQueue priorityQueue =
- new PriorityQueue();
- Random rand = new Random(47);
- for(int i = 0; i < 10; i++)
- priorityQueue.offer(rand.nextInt(i + 10));
- QueueDemo.printQ(priorityQueue);
-
- List ints = Arrays.asList(25, 22, 20,
- 18, 14, 9, 3, 1, 1, 2, 3, 9, 14, 18, 21, 23, 25);
- priorityQueue = new PriorityQueue(ints);
- QueueDemo.printQ(priorityQueue);
- priorityQueue = new PriorityQueue(
- ints.size(), Collections.reverseOrder());
- priorityQueue.addAll(ints);
- QueueDemo.printQ(priorityQueue);
-
- String fact = "EDUCATION SHOULD ESCHEW OBFUSCATION";
- List strings = Arrays.asList(fact.split(""));
- PriorityQueue stringPQ =
- new PriorityQueue(strings);
- QueueDemo.printQ(stringPQ);
- stringPQ = new PriorityQueue(
- strings.size(), Collections.reverseOrder());
- stringPQ.addAll(strings);
- QueueDemo.printQ(stringPQ);
-
- Set charSet = new HashSet();
- for(char c : fact.toCharArray())
- charSet.add(c); // Autoboxing
- PriorityQueue characterPQ =
- new PriorityQueue(charSet);
- QueueDemo.printQ(characterPQ);
- }
-} /* Output:
-0 1 1 1 1 1 3 5 8 14
-1 1 2 3 3 9 9 14 14 18 18 20 21 22 23 25 25
-25 25 23 22 21 20 18 18 14 14 9 9 3 3 2 1 1
- A A B C C C D D E E E F H H I I L N N O O O O S S S T T U U U W
-W U U U T T S S S O O O O N N L I I H H F E E E D D C C C B A A
- A B C D E F H I L N O S T U W
-*///:~
-```
-
-### foreach 和 迭代器
-
-Java SE5引入了 Iterable 接口,该接口包含了一个能够产生 Iterator 的 iterator()方法。任何实现了 Iterable 的类(如Collection)都可以应用于 foreach 语句中。
-
-```java
-public class IterableClass implements Iterable {
- protected String[] words = {"hello", "world", "program"};
- @Override
- public Iterator iterator() {
- return new Iterator() {
- private int index = 0;
- @Override
- public boolean hasNext() {
- return index < words.length;
- }
- @Override
- public String next() {
- return words[index++];
- }
- @Override
- public void remove() {
- throw new UnsupportedOperationException();
- }
- };
- }
-
- public static void main(String[] args) {
- for(String s : new IterableClass()) {
- System.out.print(s + " ");
- }
- }
-}
-```
-
-### 适配器方法
-
-添加反向迭代器功能。
-
-```java
-public class ReversibleArrayList extends ArrayList {
- public ReversibleArrayList(Collection c) { super(c); }
- public Iterable reversed() {
- return new Iterable() {
- @Override
- public Iterator iterator() {
- return new Iterator() {
- int current = size() - 1;
- @Override
- public boolean hasNext() { return current > -1; }
- @Override
- public T next() { return get(current--); }
- };
- }
- };
- }
- public static void main(String[] args) {
- ReversibleArrayList ral =
- new ReversibleArrayList<>(Arrays.asList(new String[]{"hello", "world", "yes"}));
- for(String s : ral.reversed()) {
- System.out.print(s + " ");
- }
- }
-}
-```
-
-
-
-## 异常、断言和日志
-
-Java 语言给出了三种处理系统错误的机制:抛出异常、日志和使用断言。
-
-### 异常分类
-
-所有异常都有 Throwable 继承而来。
-
-
-
-
-
-Error 类描述 Java 运行时系统的内部错误和资源耗尽错误,应用程序不应该抛出这种类型的对象。Error是程序无法处理的错误。
-
-RuntimeException由程序错误导致,如果发生了 Runtime Exception,那一定是你的问题,应该修正程序避免这类异常发生。常见的运行时异常有:
-
-```java
-ClassCastException
-IndexOutOfBoundsException
-NullPointerException
-ArrayStoreException
-NumberFormatException
-ArithmeticException
-```
-
-其他 Exception 由具体的环境,如读取的文件不存在、文件为空、sql异常、寻找不存在的 Class 对象等导致的异常。
-
-Java 语言规范将派生于 Error 类或 Runtime Exception 类的异常称为非检查(unchecked)异常;其他所有异常称为检查(checked)异常,编译器将会核查是否为所有的检查异常提供异常处理器。
-
-### 声明异常
-
-一个方法必须声明所有可能抛出的检查异常。任何抛出异常的方法都可能是一个死亡陷阱,如果没有异常处理器捕获这个异常,当前线程就会结束。非检查异常要么不可控,要么应当避免发生。
-
-如果在子类覆盖了父类的方法,在子类方法中可以抛出更特定的异常,或者不抛异常。如果父类方法没有抛出异常,则子类只能在方法内捕获所有检查异常,不允许在子类的 throws 说明符中出现超过超类方法所列出的异常类范围。
-
-### 捕获异常
-
-捕获多个异常可以合并 catch 子句(异常类型不存在子类关系)。
-
-```java
-try {
- //
-} catch (FileNotFindException | UnknowHostException e) {
- e.printStackTrace();
-}
-```
-
-解耦合 try/catch 和 try/finally 语句块,这种设计方式不仅代码清晰,而且还会报告 finally 子句出现的错误。
-
-```java
-InputStream in = ...;
-try {
- try {
- //
- } finally {
- in.close();
- }
-} catch (IOException ex) {
- ex.printStackTrace();
-}
-```
-
-### 带资源的 try 语句
-
-```java
-try (Scanner in = new Scanner(new FileInputStream("e:\data"), "UTF-8");
- PrintWriter out = new PrinterWriter("out.txt")) {
- while (in.hasNext()) {
- System.out.println(in.next().toUpperCase());
- }
-}
-```
-
-Java SE7 引入了带资源的 try 语句,当语句正常退出或者有异常,都会调用`in.close()`方法。如果`in.close()`方法也抛出异常,则原来的异常会重新抛出,而close方法抛出的异常会被抑制,通过调用 getSuppressed 方法可以得到从close 方法抛出并被抑制的异常列表。
-
-### 断言
-
-```java
-int x = -1;
-if(x < 0) {
- throw new IllegalArgumentException("x < 0");
-}
-double y = Math.sqrt(x);
-```
-
-这段检查代码会一直保留在程序,如果程序中含有大量这样的检查,程序运行起来将会很慢。
-
-断言允许在测试期间向代码插入一些检查语句。当代码发布时,这些检查语句将会被自动的移走。
-
-Java 语言引入了关键字 assert,assert 有两种形式:
-
-```java
-assert condition;
-assert condition: expression;//expression用于产生消息字符串
-```
-
-这两种形式都会对条件进行检测,如果结果是 false,则会抛出 AssertError 异常。第二种形式中,表达式将会被传入 AssertError 的构造器,并转化成一个消息字符串。
-
-断言 x 是一个非负数值:
-
-```java
-assert x >= 0;
-assert x >= 0 : x;//将x的值传递给AssertError异常
-```
-
-断言只应该用在测试阶段确定程序内部的错误位置。
-
-#### 启用和禁用断言
-
-默认情况下,断言被禁用,可以在运行程序时用`-eableassertions`或`-ea`选项启用。idea --> run configuration --> vm options 添加 `-ea` 开启断言。
-
-`java -enableassertions MyApp`
-
-启用和禁用断言是类加载器的功能,在启用和禁用断言时不必重新编译程序。当断言被禁用时,类加载器将跳过断言代码,因此不会降低程序的运行速度。
-
-在某个类和包使用断言:`java -ea:MyClass -ea:com.tyson.chapter12 MyApp` MyClass 类和 com.tyson.chapter12 包以及子包的所有类都会启用断言。
-
-使用选项`-dableassertions`或`-da`禁用某个类和包的断言。
-
-### 日志
-
-#### logback
-
-``的三个属性
-
-- scan:默认值为true,配置文件发生更改时会重新加载。
-- scanPeriod:设置监控配置文件的时间间隔,默认单位是毫秒。
-- debug:默认值是false,打印logback内部日志信息,实时查看logback运行状态。
-
-``的子标签
-root是根``,只有level属性,默认为DEBUG。
-logger关联包或者具体的类到appender,可以定义日志类型、级别。
-Appender主要用于指定日志输出的目的地,如控制台、文件、数据库。
-
-```
-
-
-
-
-
-
- %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
-
-
-
-
-
-
-
-
-
-
-
-
-```
-日志级别:ERROR>WARN>INFO>DEBUG>TRACE
-
-Layout用于自定义日志输出格式。
-```
-
-
-
-```
-
-
-
-## 字符串
-
-String 对象是不可变的。String 类中会修改 String 的方法都会创建一个全新的 String 对象。
-
-### 格式化输出
-
-#### printf() 和 format()
-
-```java
-public class SimpleFormat {
- public static void main(String[] args) {
- int x = 5;
- double y = 5.26;
- System.out.format("Row 1: [%d %f]\n", x, y);
- System.out.printf("Row 1: [%d %f]\n", x, y);
- }
-}
-/*output
-Row 1: [5 5.260000]
-Row 1: [5 5.260000]
- */
-```
-
-#### Formatter
-
-```java
-public class FormatterDemo {
- public static void main(String[] args) {
- Formatter f = new Formatter(System.out);
- String item = "peas";
- int quatity = 3;
- float price = 5.2f;
- //-表示右对齐
- f.format("%-10s %5s %10s\n", "item", "quatity", "price");
- //-表示右对齐,%10.10s指宽度为10,最大输出字符为10
- f.format("%-10.10s %5d %10.2f\n", item, quatity, price);
- //String.format()
- System.out.println("------string.format()------");
- System.out.println(String.format("%-10.10s %5d %10.2f", item, quatity, price));
- }
-}
-/*
-item quatity price
-peas 3 5.20
-------string.format()------
-peas 3 5.20
- */
-```
-
-String.format()接受与Formatter.format()一样的参数,它是通过创建一个 Formatter 对象实现的。
-
-### 正则表达式
-
-#### 基础
-
-可能有一个负号在最前面:`-?`
-
-`\\`在 Java 表示插入一个正则表达式的反斜线
-
-表示一个数字:`\\d`
-
-插入普通的反斜线:`\\\\`
-
-`+`表示一个或多个之前的表达式
-
-可能有一个负号,后面跟着一个或多个数字:`-?\\d+`
-
-```java
-public class IntegerMatch {
- public static void main(String[] args) {
- System.out.println("-89".matches("-?\\d+"));
- System.out.println("+98".matches("-?\\d+"));
- //竖直线|代表或操作;+是特殊符号,需转义;括号可以将表达式分组
- System.out.println("+789".matches("(-|\\+)?\\d+"));
- }
-}
-/*output
-true
-false
-true
- */
-```
-
-String 自带一个正则表达式工具 split()方法,它可以将字符串从正则表达式匹配的地方切开。特殊字符如`.`和`|`需要用反斜杠转义,即`\\.`和`\\|`。
-
-```java
-public class Splitting {
- public static String motto = "All in all, we all have a dream.";
- public static void split(String regex) {
- System.out.println(Arrays.toString(motto.split(regex)));
- }
- public static void main(String[] args) {
- //.需要转义
- split("\\.");
- split(" ");
- //\\w+表示一个或多个非单词字符
- split("\\W+");
- //表示字母n后面跟着一个或多个非单词字符
- split("n\\W+");
- }
-}
-/*output
-[All in all, we all have a dream, Thanks]
-[All, in, all,, we, all, have, a, dream., Thanks.]
-[All, in, all, we, all, have, a, dream, Thanks]
-[All i, all, we all have a dream. Thanks.]
- */
-```
-
-替换功能。
-
-```java
-public class Replacing {
- public static void main(String[] args) {
- //小写的w表示字母
- System.out.println(Splitting.motto.replaceFirst("w\\w+", "they"));
- System.out.println(Splitting.motto.replaceAll("all|dream", "apple"));
- }
-}
-/*output
-All in all, they all have a dream.
-All in apple, we apple have a apple.
- */
-```
-
-#### 创建正则表达式
-
-字符类表达式,JDK 文档中 java.util.regex.Pattern 页面有完整的表达式。
-
-[jdk8文档](https://docs.oracle.com/javase/8/docs/api/index.html)
-
-[菜鸟教程正则表达式语法](http://www.runoob.com/java/java-regular-expressions.html)
-
-**常用的字符类表达式**
-
-| 字符 | 描述 |
-| -------- | ------------------------------------------------------------ |
-| . | 匹配除"\r\n"之外的任何单个字符 |
-| + | 表示一个或多个之前的表达式,如\\\d+表示一个或多个数字 |
-| * | 零次或多次匹配前面的字符或子表达式。例如,zo* 匹配"z"和"zoo"。* 等效于 {0,} |
-| ? | 零次或一次匹配前面的字符或子表达式。如"do(es)?"匹配"do"或"does"中的"do"。? 等效于 {0,1} |
-| [abc] | 包含a/b/c的任何字符(和a\|b\|c作用相同) |
-| [^abc] | 除了a/b/c外的任何字符 |
-| [a-zA-z] | a-z/A-Z的任何字符 |
-| \s | 空白符(空格、tab、换行、换页和回车) |
-| \S | 非空白符(\[^\s]) |
-| \d | 数字0-9 |
-| \D | 非数字(\[^0-9]) |
-| \w | 字符[a-zA-Z0-9] |
-| \W | 非字符 |
-
-**边界匹配符**
-
-| 符号 | 描述 |
-| ---- | ---------- |
-| ^ | 一行的起始 |
-| $ | 一行的结束 |
-| \b | 词的边界 |
-| \B | 非词的边界 |
-
-**量词**
-
-
-
-Rudolph.java
-
-```java
-public class Rudolph {
- public static void main(String[] args) {
- for(String pattern : new String[]{ "Rudolph",
- "[rR]udolph", "[rR][aeiou][a-z]ol.*", "R.*" })
- System.out.println("Rudolph".matches(pattern));
- }
-} /* Output:
-true
-true
-true
-true
-*///:~
-```
-
-#### Pattern 和 Matcher
-
-Pattern 和 Matcher 功能较 String 更为强大。Pattern.compile(String regex) 用来编译正则表达式,生成一个。
-
-**find()**
-
-`Matcher.find()`可用来在 CharSequence 中查找多个匹配。find()可以像迭代器一样前向遍历输入的字符串。重载的 find(int index)能够接收一个整数参数,作为搜索起点。
-
-```java
-public class Finding {
- //划分为单词
- public static Pattern WORD_PATTERN = Pattern.compile("\\w+");
- public static void main(String[] args) {
- Matcher m = WORD_PATTERN.matcher("Everyone is born equally");
- //find()可用来查找多个匹配
- while(m.find()) {
- System.out.print(m.group() + " ");
- }
- System.out.println();
- int i = 0;
- //指定搜索起点
- while(m.find(i++)) {
- System.out.print(m.group() + " ");
- }
- }
-}
-/*output
-Everyone is born equally
-Everyone veryone eryone ryone yone one ne e is is s born born orn rn n equally equally qually ually ally lly ly y
- */
-```
-
-**组**
-
-组使用括号划分的正则表达式,如`A(B(C))D`,组0是ABCD,组1是BC,组2是C。Matcher 对象提供了一系列方法,用以获取与组相关的信息:
-
-`public int groupCount()`返回该匹配器的分组数目(**不包含第0组**),如上面例子分组数目为2组(不包含第0组);
-
-`public String group()`返回前一次匹配操作(如find())的第0组(整个匹配);
-
-`public String group(int i)`返回前一次匹配操作指定的组号,如果没有匹配到输入字符串的任何部分,将会返回null。
-
-```java
-public class Groups {
- public static final String POEM =
- "Twas brillig, and the slithy toves\n" +
- "Did gyre and gimble in the wabe.\n" +
- "All mimsy were the borogoves,\n" +
- "And the mome raths outgrabe.\n\n" +
- "Beware the Jabberwock, my son,\n" +
- "The jaws that bite, the claws that catch.\n" +
- "Beware the Jubjub bird, and shun\n" +
- "The frumious Bandersnatch.\n" +
- "end\n"; //匹配失败
- //捕获每行最后的三个词
- public static Pattern LAST_THREE_WORD_PATTERN = Pattern.compile("(?m)(\\S+)\\s+((\\S+)\\s+(\\S+))$");
-
- public static void main(String[] args) {
- Matcher m = LAST_THREE_WORD_PATTERN.matcher(POEM);
- while(m.find()) {
- //有4个分组,不包括分组0,分组0是整个匹配
- for(int i = 0; i <= m.groupCount(); i++) {
- System.out.print("[" + m.group(i) + "]");
- }
- System.out.println();
- }
- }
-}
-/* Output:
-[the slithy toves][the][slithy toves][slithy][toves]
-[in the wabe.][in][the wabe.][the][wabe.]
-[were the borogoves,][were][the borogoves,][the][borogoves,]
-[mome raths outgrabe.][mome][raths outgrabe.][raths][outgrabe.]
-[Jabberwock, my son,][Jabberwock,][my son,][my][son,]
-[claws that catch.][claws][that catch.][that][catch.]
-[bird, and shun][bird,][and shun][and][shun]
-[The frumious Bandersnatch.][The][frumious Bandersnatch.][frumious][Bandersnatch.]
-*/
-```
-
-默认情况下,^和$仅匹配输入的完整字符串的开始和结束。?m表示多行模式下,^和$分别匹配一行的开始和结束。
-
-**start() 与 end()**
-
-在匹配操作成功之后,start()返回匹配的起始位置的索引,end()返回所匹配字符的最后字符的索引加一的值。
-
-**Pattern 标记**
-
-Pattern 类的 compile()方法还有一个版本,可以接收一个标记参数,以调整匹配的行为:
-
-`Pattern Pattern.compile(String regex, int flag)`
-
-flag 来自以下 Pattern 类中的常量:
-
-
-
-可以通过'|'(或)操作符组合多个标记的功能:
-
-```java
-public class ReFlags {
- public static Pattern WORD_PATTERN = Pattern.compile("^java",
- Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
- public static void main(String[] args) {
- Matcher m = WORD_PATTERN.matcher("java has regex\nJava has regex\n" +
- "JAVA has pretty good regular expressions\n" +
- "Regular expressions are in Java");
- while(m.find()) {
- System.out.println(m.group());
- }
- }
-}
-/* Output:
-java
-Java
-JAVA
-*/
-```
-
-**split()**
-
-```java
-public class SplitDemo {
- public static void main(String[] args) {
- String input = "This!!unusual use!!of exclamation!!points";
- print(Arrays.toString(Pattern.compile("!!").split(input)));
- // 限制分割字符串的数量
- print(Arrays.toString(Pattern.compile("!!").split(input, 3)));
- }
-} /* Output:
-[This, unusual use, of exclamation, points]
-[This, unusual use, of exclamation!!points]
-*/
-```
-
-**替换操作**
-
-```java
-public class TheReplacements {
- public static void main(String[] args) throws Exception {
- String s = TextFile.read("src/com/tyson/chapter13/string/regex/TheReplacements.java");
- // 匹配上面的注释
- Matcher mInput = Pattern.compile("/\\*!(.*)!\\*/", Pattern.DOTALL).matcher(s);
- if (mInput.find()) {
- s = mInput.group(1); // 捕捉正则表达式括号,组是括号划分的正则表达式
- }
-
- // Replace two or more spaces with a single space:
- s = s.replaceAll(" {2,}", " ");
- // Replace one or more spaces at the beginning of each
- // line with no spaces. Must enable MULTILINE mode:
- s = s.replaceAll("(?m)^ +", "");
- print(s);
- s = s.replaceFirst("[aeiou]", "(VOWEL1)");
- StringBuffer sbuf = new StringBuffer();
- Pattern p = Pattern.compile("[aeiou]");
- Matcher m = p.matcher(s);
- // Process the find information as you
- // perform the replacements:
- while (m.find())
- //将找到的元音字母转化为大写字母
- m.appendReplacement(sbuf, m.group().toUpperCase());
- // Put in the remainder of the text:
- m.appendTail(sbuf);
- print(sbuf);
- }
-} /* Output:
-Here's a block of text to use as input to
-the regular expression matcher. Note that we'll
-first extract the block of text by looking for
-the special delimiters, then process the
-extracted block.
-H(VOWEL1)rE's A blOck Of tExt tO UsE As InpUt tO
-thE rEgUlAr ExprEssIOn mAtchEr. NOtE thAt wE'll
-fIrst ExtrAct thE blOck Of tExt by lOOkIng fOr
-thE spEcIAl dElImItErs, thEn prOcEss thE
-ExtrActEd blOck.
-*/
-```
-
-**reset()**
-
-通过 reset() 方法,可以将 Matcher 对象应用于一个新的字符序列。
-
-```java
-public class Resetting {
- public static void main(String[] args) throws Exception {
- Matcher m = Pattern.compile("[frb][aiu][gx]").matcher("fix the rug with bags");
- while (m.find())
- System.out.print(m.group() + " ");
- System.out.println();
- m.reset("fix the rig with rags");
- while (m.find())
- System.out.print(m.group() + " ");
- }
-} /* Output:
-fix rug bag
-fix rig rag
-*/
-```
-
-### 扫描输入
-
-Java SE5新增了 Scanner 类,Scanner 的构造器可以接受任何类型的输入对象,包括 File 对象、InputStream、String 或者 Readable 对象。Scanner 所有的输入、分词以及翻译都隐藏在不用类型的 next 方法中。普通的 next()方法返回下一个 String 。
-
-#### Scanner 定界符
-
-默认情况下,Scanner 根据空白字符对输入进行分词,我们可以用正则表达式指定自己所需的定界符。
-
-```java
-public class ScannerDelimiter {
- public static void main(String[] args) {
- Scanner sc = new Scanner("12, 42, 85, 23");
- //使用逗号包括逗号前后的空白字符作为定界符
- sc.useDelimiter("\\s*,\\s*");
- while(sc.hasNextInt()) {
- System.out.println(sc.nextInt());
- }
- }
-}
-```
-
-#### 用正则表达式扫描
-
-```java
-public class ThreatAnalyzer {
- static String threatData =
- "58.27.82.161@02/10/2005\n" +
- "204.45.234.40@02/11/2005\n" +
- "58.27.82.161@02/11/2005\n" +
- "58.27.82.161@02/12/2005\n" +
- "58.27.82.161@02/12/2005\n" +
- "[Next log section with different data format]";
-
- public static void main(String[] args) {
- Scanner sc = new Scanner(threatData);
- String pattern = "(\\d+[.]\\d+[.]\\d+[.]\\d+)@(\\d{2}/\\d{2}/\\d{4})";
- while(sc.hasNext(pattern)) {
- sc.next(pattern);
- MatchResult match = sc.match();
- String ip = match.group(1);
- String date = match.group(2);
- System.out.format("Thread on %s from %s\n", ip, date);
- }
- }
-}
-/*output
-Thread on 58.27.82.161 from 02/10/2005
-Thread on 204.45.234.40 from 02/11/2005
-Thread on 58.27.82.161 from 02/11/2005
-Thread on 58.27.82.161 from 02/12/2005
-Thread on 58.27.82.161 from 02/12/2005
- */
-```
-
-注意:配合正则表达式进行扫描时,正则表达式不要含有定界符(Scanner 默认定界符是空白符,根据空白符对输入进行分词)。
-
-
-
-## 类型信息
-
-通过运行时类型信息可以在程序运行时发现和使用类型信息。
-
-`Class.forName("Gum")`类 Gum 没有被加载就加载它。
-
-### 反射
-
-类方法提取:
-
-```java
-public class typeinfo {
- //匹配类似包名的字符串 com.tyson.chapter
- public static Pattern p = Pattern.compile("\\w+\\.");
- public static void main(String[] args) {
- try {
- Class> c = Class.forName("com.tyson.chapter10.innerclass.LocalInnerClass");
- Method[] methods = c.getMethods();
- for(Method m : methods) {
- //去掉包名
- System.out.println(p.matcher(m.toString()).replaceAll(""));
- }
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- }
- }
-}
-/*output
-public static void main(String[])
-public final void wait() throws InterruptedException
-public final void wait(long,int) throws InterruptedException
-public final native void wait(long) throws InterruptedException
-public boolean equals(Object)
-public String toString()
-public native int hashCode()
-public final native Class getClass()
-public final native void notify()
-public final native void notifyAll()
- */
-```
-
-
-
-## 泛型
-
-泛型实现了参数化类型的概念,使代码可以应用于多种类型。使用泛型编写的程序比使用 Object 变量再进行强制类型转化的程序具有更好的安全性和可读性。
-
-### 类型参数的好处
-
-Java SE5泛型出现之前,ArrayList 是通过维护一个 Object 数组实现的。
-
-```java
-public class MyArrayList {
- private Object[] elementData;
- private int index = 0;
- public MyArrayList() { elementData = new Object[8]; }
- public Object get(int i) { return elementData[i]; }
- public void add(Object o) { elementData[index++] = o; }
-
- public static void main(String[] args) {
- MyArrayList list = new MyArrayList();
- list.add("tyson");
- System.out.println((String)list.get(0));
- list.add(new Cat());
- System.out.println((String)list.get(1));
- }
-}
-/*output
-tyson
-Exception in thread "main" java.lang.ClassCastException: typeinfo.pets.Cat cannot be cast to java.lang.String
- at com.tyson.chapter15.generics.MyArrayList.main(MyArrayList.java:29)
- */
-```
-
-当获取值的时候需要进行强制类型转化,并且 add 操作可以放进任何类型的对象,编译和运行都不报错,当获取值进行强制类型转换时,可能会抛异常。
-
-### 泛型类
-
-```java
-public class Holder {
- private T a;
- public Holder(T a) { this.a = a; }
- public void set(T a) { this.a = a; }
- public T get() { return a; }
-
- public static void main(String[] args) {
- Holder h = new Holder<>("Tyson");
- System.out.println(h.get());
- }
-}
-//output Tyson
-```
-
-### 泛型接口
-
-泛型也可以用于接口。
-
-```java
-//public interface Generator { T next(); }
-
-public class Fibonacci implements Generator {
- private int count = 0;
- @Override
- public Integer next() {
- return fib(count++);
- }
- public int fib(int n) {
- if(n < 2) { return 1; }
- return fib(n - 2) + fib(n - 1);
- }
-
- public static void main(String[] args) {
- Fibonacci fib = new Fibonacci();
- for(int i = 0; i < 10; i++) {
- System.out.print(fib.next() + " ");
- }
- }
-}
-//output:1 1 2 3 5 8 13 21 34 55
-```
-
-### 泛型方法
-
-如果使用泛型方法可以取代将整个类泛型化,那么就应该只使用泛型方法。对于一个 static 方法而言,无法访问泛型类的类型参数,所以如果 static 方法需要使用泛化能力,就必须使其成为泛型方法。
-
-定义泛型方法,只需将泛型参数列表放在返回值之前。
-
-```java
-public class GenericMethod {
- public void f(T t) { System.out.println(t.getClass().getName()); }
-
- public static void main(String[] args) {
- GenericMethod gm = new GenericMethod();
- //可省略,编译器自己可推断出
- gm.f("");
- gm.f(1);
- gm.f(1.0);
- gm.f(1.0f);
- gm.f(gm);
- }
-}
-/*output
-java.lang.String
-java.lang.Integer
-java.lang.Double
-java.lang.Float
-com.tyson.chapter15.generics.GenericMethod
- */
-```
-
-GenericMethod 并不是参数化的,只有 f() 拥有参数类型。
-
-#### 可变参数和泛型方法
-
-向参数个数可变的方法传递一个泛型类型的实例。
-
-```java
-public class GenericVarArgs {
- public static List makeList(T... args) {
- List result = new ArrayList<>();
- for(T item : args) {
- result.add(item);
- }
- return result;
- }
-
- public static void main(String[] args) {
- List l = makeList("a", "b");
- System.out.println(l);
- }
-}
-//output:[a, b]
-```
-
-### 匿名内部类
-
-```java
-public class Customer {
- public Customer() {}
- public static Generator generator() {
- return new Generator() {
- @Override
- public Customer next() {
- return new Customer();
- }
- };
- }
-
- public static void main(String[] args) {
- System.out.println(Customer.generator().next());
- }
-}
-```
-
-### 泛型擦除
-
-```java
-public class ErasedTypeEquvalence {
- public static void main(String[] args) {
- Class c1 = new ArrayList().getClass();
- Class c2 = new ArrayList().getClass();
- System.out.println(c1 == c2);
- }
-}
-//output:true
-```
-
-`List`的类型变量没有显式的限定,擦除类型后变成原始的 List,故`List`和`List`是相同的类型。
-
-```java
-class Manipulator {
- private T obj;
- public Manipulator(T t) { obj = t; }
- public void manipulate() { obj.f(); }
-}
-```
-
-Manipulator 类有显式限定 HasF,故擦除类型后变成:
-
-```java
-class Manipulato {
- private HasF obj;
- public Manipulator(HasF t) { obj = HasF; }
- public void manipulate() { obj.f(); }
-}
-```
-
-### 类型变量的限定
-
-给定泛型类的边界,以此告知编译器只能接受遵循这个边界的类型。
-
-```java
-//HasF.java
-public class HasF {
- public void f() {
- System.out.println("Has.f()");
- }
-}
-
-//Manipulation.java
-class Manipulator {
- private T obj;
- public Manipulator(T t) { obj = t; }
- public void manipulate() { obj.f(); }
-}
-
-public class Manipulation {
- public static void main(String[] args) {
- HasF hf = new HasF();
- Manipulator manipulator = new Manipulator<>(hf);
- manipulator.manipulate();
- }
-}
-//output: Has.f()
-```
-
-### 擦除的问题
-
-```java
-class GenericBase {
- private T element;
- public void set(T arg) { element = arg; }
- public T get() { return element; }
-
- public static void main(String[] args) {
-
- }
-}
-
-class Derived extends GenericBase {}
-
-public class ErasureAndInheritance {
- public static void main(String[] args) {
- Derived d = new Derived();
- d.set(new Object());
- System.out.println(d.get());
- }
-}
-//output: java.lang.Object@6d6f6e28
-```
-
-Derived 继承自 GenericBase,但是没有任何泛型参数,而编译器不会发出任何警告。
-
-### 边界
-
-在泛型的参数类型上设置限制条件,可以强制规定泛型可以应用的类型,从而可以按照自己的边界类型去调用方法。如果将泛型参数限制为某个类型子集,那么就可以用这些类型子集来调用方法。
-
-```java
-interface HasColor { Color getColor(); }
-
-class Colored {
- T item;
- Colored(T item) { this.item = item; }
- T getItem() { return item; }
- Color color() { return item.getColor(); }
-}
-
-class Dimension { public int x; }
-
-//编译错误,应该类在前,接口在后
-//class ColoredDimension {}
-
-class ColoredDimension {
- T item;
- ColoredDimension(T item) { this.item = item; }
- T getItem() { return item; }
- Color color() { return item.getColor(); }
- int getX() { return item.x; }
-}
-
-interface Weight { int weight(); }
-
-//边界继承只能有一个类,可以有多个接口
-class Solid {
- T item;
- Solid(T item) { this.item = item; }
- T getItem() { return item; }
- Color color() { return item.getColor(); }
- int getX() { return item.x; }
- int weight() { return item.weight(); }
-}
-
-class Bounded extends Dimension implements HasColor, Weight {
-
- @Override
- public Color getColor() { return null; }
-
- @Override
- public int weight() { return 0; }
-}
-
-public class BasicBounds {
- public static void main(String[] args) {
- Solid solid = new Solid<>(new Bounded());
- solid.color();
- solid.getX();
- solid.weight();
- }
-}
-```
-
-通过在继承的每个层次上添加边界限制,消除 BasicBounds.java 代码冗余。
-
-```java
-class HoldItem {
- T item;
- HoldItem(T item) { this.item = item; }
- T getItem() { return item; }
-}
-
-class Colored2 extends HoldItem {
- Colored2(T item) { super(item); }
- Color color() { return item.getColor(); }
-}
-
-class ColorDimension2 extends Colored2 {
- ColorDimension2(T item) { super(item); }
- int getX() { return item.x; }
-}
-
-class Solid2 extends ColorDimension2 {
- Solid2(T item) { super(item); }
- int weight() { return item.weight(); }
-}
-
-public class InheritBounds {
- public static void main(String[] args) {
- Solid2 solid2 = new Solid2<>(new Bounded());
- solid2.color();
- solid2.getX();
- solid2.weight();
- }
-}
-```
-
-### 通配符
-
-参考自:[泛型超详细解读](https://blog.csdn.net/jeffleo/article/details/52250948) | [通配符的上界通配符和下界通配符](https://blog.csdn.net/hello_worldee/article/details/77934244)
-
-#### 上界通配符
-
-上界< ? extends Class>
-
-```java
-class Fruit { }
-class Apple extends Fruit { }
-class Orange extends Fruit { }
-
-public class UpperBound {
- public static void main(String[] args) {
- List extends Fruit> list = new ArrayList<>();
- //编译错误
- //list.add(new Apple());
- //list.add(new Orange());
- //list.add(new Fruit());
- list.add(null);
-
- list.contains(new Apple());
- list.indexOf(new Apple());
-
- System.out.println(list.get(0));
- }
-}
-```
-
-可见,指定了下边界,却不能add任何类型,甚至Object都不行,除了null,因为null代表任何类型。List< ? extends Fruit>可以解读为,“具有任何从Fruit继承的类型”,但实际上,它意味着,它没有指定具体类型。对于编译器来说,当你指定了一个List< ? extends Fruit>,add的参数也变成了“? extends Fruit”。因此编译器并不能了解这里到底需要哪种Fruit的子类型,因此他不会接受任何类型的Fruit。
-
-然而,contain和indexof却能执行,这是因为,这两个方法的参数是Object,不涉及任何的通配符,所以编译器允许它调用。
-
-list.get(0)能够执行是因为,当item在此list存在时,编译器能够确定他是Apple的子类,所以能够安全获得。
-
-#### 下界通配符
-
-< ? super Class>表示,指定类的基类。
-
-```java
-class Apple1 extends Apple {}
-
-public class LowerBound{
-
- public static void main(String[] args) {
- List super Apple> list = new ArrayList<>();
- list.add(new Apple());
- list.add(new Apple1());
- //编译错误
- //list.add(new Fruit());
-
- Object apple = list.get(0);
- System.out.println(apple);
- }
-}
-//output: com.tyson.chapter15.generics.Apple@6d6f6e28
-```
-
-对于super,get返回的是Object,因为编译器不能确定列表中的是Apple的哪个子类,所以只能返回Object。
-
-List < ? super Apple>, 代表容器内存放的是Apple的所有父类,所以有多态和上转型,这个容器时可以接受所有Apple父类的子类的(多态的定义:父类可以接受子类型对象)。Apple和Apple1都直接或间接继承了Fruit,所以Apple和Apple1是能够加入List < ? super Apple>这个容器的。
-
-list.add(new Fruit())不能添加,是因为容器内存放的是Apple的**所有**父类,正是因为能存放所有,Apple的父类可能有Fruit1, Fruit2, Fruit3 , 所以编译器根本不能识别你要存放哪个Apple的父类,所以不能添加Fruit,因为这不能保证类型安全的原则。这从最后的Object apple = list.get(0)可以看出。
-
-
-
-## 数组
-
-数组的 length 是数组的大小,不是实际保存的元素个数。
-
-如果不显式初始化,基本类型数组会被自动初始化成初值,对象数组则会被初始化成 null。
-
-### 复制数组
-
-Java 标准类库提供有 static 方法 System.arraycopy(),用来复制数组比用 for 循环复制要快的多。System.arraycopy() 针对所有类型做了重载。
-
-```java
-public class CopyArrays {
- public static void main(String[] args) {
- int[] src = new int[7];
- int[] dest = new int[10];
- Arrays.fill(src, 47);
- Arrays.fill(dest, 99);
- System.out.println("src = " + Arrays.toString(src));
- System.out.println("dest = " + Arrays.toString(dest));
- System.arraycopy(src, 0, dest, 0, src.length);
- System.out.println("dest = " + Arrays.toString(dest));
- }
-}
-/*output
-src = [47, 47, 47, 47, 47, 47, 47]
-dest = [99, 99, 99, 99, 99, 99, 99, 99, 99, 99]
-dest = [47, 47, 47, 47, 47, 47, 47, 99, 99, 99]
- */
-```
-
-基本类型数组和对象数组都可以复制。复制对象只是复制对象引用,即浅复制。System.arraycopy() 不会执行自动包装和自动拆包,两个数组需要具有相同的数据类型。
-
-### Arrays 工具
-
-#### 数组拷贝
-
-```java
-public class ArrayCopy {
- //Arrays 类方法的使用
- public static void main(String[] args) {
- int[] arr = {12, 45, 2, 56, 20};
- int[] arrCopy = Arrays.copyOf(arr, arr.length);
- System.out.println("arr: " + Arrays.toString(arr));
- System.out.println("arrCopy: " + Arrays.toString(arrCopy));
- //增加数组大小
- arrCopy = Arrays.copyOf(arr, 2 * arr.length);
- System.out.println("arr: " + Arrays.toString(arr));
- System.out.println("arrCopy: " + Arrays.toString(arrCopy));
- }
-}
-/*output
-arr: [12, 45, 2, 56, 20]
-arrCopy: [12, 45, 2, 56, 20]
-arr: [12, 45, 2, 56, 20]
-arrCopy: [12, 45, 2, 56, 20, 0, 0, 0, 0, 0]
- */
-```
-
-`Array.copyOf()`底层是通过`System.arraycopy()`使用的。
-
-```java
-public static T[] copyOf(U[] original, int newLength, Class extends T[]> newType) {
- @SuppressWarnings("unchecked")
- T[] copy = ((Object)newType == (Object)Object[].class)
- ? (T[]) new Object[newLength]
- : (T[]) Array.newInstance(newType.getComponentType(), newLength);
- System.arraycopy(original, 0, copy, 0,
- Math.min(original.length, newLength));
- return copy;
-}
-```
-
-#### 数组的比较
-
-Arrays 类提供了重载后的 equals() 方法,用来比较整个数组。
-
-```java
-public class ComparingArrays {
- public static void main(String[] args) {
- int[] a1 = new int[10];
- int[] a2 = new int[10];
- Arrays.fill(a1, 47);
- Arrays.fill(a2, 47);
- System.out.println(Arrays.equals(a1, a2));
- a2[1] = 0;
- System.out.println(Arrays.equals(a1, a2));
- String[] s1 = new String[2];
- Arrays.fill(s1, "hello");
- String[] s2 = { new String("hello"), new String("hello")};
- System.out.println(Arrays.equals(s1, s2));
- }
-}
-/*output
-true
-false
-true
- */
-```
-
-#### 数组元素的比较
-
-1. 实现`java.lang.Comparable`接口,使得类本身具有比较比较能力。
-
- ```java
- public class CompType implements Comparable {
- int i;
- int j;
- public CompType(int i, int j) {
- this.i = i;
- this.j = j;
- }
-
- @Override
- public String toString() {
- return "CompType{" +
- "i=" + i +
- ", j=" + j +
- '}';
- }
-
- @Override
- public int compareTo(CompType ct) {
- return i < ct.i ? -1 : (i == ct.i ? 0 : 1);
- }
-
- private static Random r = new Random(47);
- public static Generator generator() {
- return new Generator() {
- @Override
- public CompType next() {
- return new CompType(r.nextInt(100), r.nextInt(100));
- }
- };
- }
-
- public static void main(String[] args) {
- CompType[] arr = Generated.array(new CompType[4], generator());
- System.out.println("before sorting: " + Arrays.toString(arr));
- Arrays.sort(arr);
- System.out.println("after sorting: " + Arrays.toString(arr));
- //反转自然排列顺序
- Arrays.sort(arr, Collections.reverseOrder());
- System.out.println("reverse order: " + Arrays.toString(arr));
- }
- }
- /*output
- before sorting: [CompType{i=58, j=55}, CompType{i=93, j=61}, CompType{i=61, j=29}, CompType{i=68, j=0}]
- after sorting: [CompType{i=58, j=55}, CompType{i=61, j=29}, CompType{i=68, j=0}, CompType{i=93, j=61}]
- reverse order: [CompType{i=93, j=61}, CompType{i=68, j=0}, CompType{i=61, j=29}, CompType{i=58, j=55}]
- */
- ```
-
-2. 编写自己的比较器Comparator。
-
- ```java
- class CompTypeComparator implements Comparator {
-
- @Override
- public int compare(CompType o1, CompType o2) {
- return o1.j > o2.j ? 1 : (o1.j == o2.j ? 0 : -1);
- }
- }
-
- public class ComparatorTest {
- public static void main(String[] args) {
- CompType[] arr = Generated.array(new CompType[4], CompType.generator());
- System.out.println("before sorting: " + Arrays.toString(arr));
- Arrays.sort(arr, new CompTypeComparator());
- System.out.println("after sorting: " + Arrays.toString(arr));
- }
- }
- /*output
- before sorting: [CompType{i=58, j=55}, CompType{i=93, j=61}, CompType{i=61, j=29}, CompType{i=68, j=0}]
- after sorting: [CompType{i=68, j=0}, CompType{i=61, j=29}, CompType{i=58, j=55}, CompType{i=93, j=61}]
- */
- ```
-
-#### 数组排序
-
-使用内置的排序方法,就可以对基本类型数组进行排序。也可以对对象数组进行排序,只要该对象实现了 Comparable 接口或者具有相关联的 Comparator。
-
-```java
-public class StringSorting {
- public static void main(String[] args) {
- String[] strs = Generated.array(new String[5], new RandomGenerator.String(5));
- System.out.println("before sorting: " + Arrays.toString(strs));
- Arrays.sort(strs);
- System.out.println("after sorting: " + Arrays.toString(strs));
- Arrays.sort(strs, Collections.reverseOrder());
- System.out.println("reverse order: " + Arrays.toString(strs));
- Arrays.sort(strs, String.CASE_INSENSITIVE_ORDER);
- System.out.println("case insensive: " + Arrays.toString(strs));
- }
-}
-/*output
-before sorting: [YNzbr, nyGcF, OWZnT, cQrGs, eGZMm]
-after sorting: [OWZnT, YNzbr, cQrGs, eGZMm, nyGcF]
-reverse order: [nyGcF, eGZMm, cQrGs, YNzbr, OWZnT]
-case insensive: [cQrGs, eGZMm, nyGcF, OWZnT, YNzbr]
- */
-```
-
-String 排序算法依据词典编排顺序排序。大写字母在前,小写字母在后。Java 标准类库中的排序算法针对正排序做了优化。针对基础类型设计了快速排序,针对对象设计了稳定归并排序。
-
-#### 排序数组查找
-
-若数组已经排序,既可以使用`Arrays.binarySearch()`执行快速查找。
-
-```java
-public class ArraySearching {
- public static void main(String[] args) {
- Generator gen = new RandomGenerator.Integer(1000);
- int[] arr = ConvertTo.primitive(Generated.array(new Integer[5], gen));
- Arrays.sort(arr);
- System.out.println("after sorting: " + Arrays.toString(arr));
- while(true) {
- int r = gen.next();
- int location = Arrays.binarySearch(arr, r);
- if(location >= 0) {
- System.out.println("location: " + location + "-->" + arr[location]);
- break;
- }
- }
- }
-}
-/*output
-after sorting: [258, 555, 693, 861, 961]
-location: 3-->861
- */
-```
-
-找到目标,`Arrays.binarySearch()`返回值大于或等于0。否则返回负值:`-(插入点)- 1`;
-
-对象数组使用`Arrays.binarySearch()`需要提供Comparator。
-
-```java
-public class StringSearch {
- public static void main(String[] args) {
- String[] strs = Generated.array(new String[6], new RandomGenerator.String(5));
- Arrays.sort(strs, String.CASE_INSENSITIVE_ORDER);
- System.out.println(Arrays.toString(strs));
- int index = Arrays.binarySearch(strs, strs[3], String.CASE_INSENSITIVE_ORDER);
- System.out.println("index: " + index + "-->" + strs[index]);
- }
-}
-/*output
-[cQrGs, eGZMm, JMRoE, nyGcF, OWZnT, YNzbr]
-index: 3-->nyGcF
- */
-```
-
-
-
-## 容器深入研究
-
-### 填充容器
-
-```java
-public class FillingList {
- public static void main(String[] args) {
- List list = new ArrayList<>(Collections.nCopies(3, "Tyson"));
- System.out.println(list);
- Collections.fill(list, "Sophia");
- System.out.println(list);
- }
-}
-/*output
-[Tyson, Tyson, Tyson]
-[Sophia, Sophia, Sophia]
- */
-```
-
-### SortedSet
-
-按对象的比较函数对元素进行排序。用 TreeSet 迭代通常比用 HashSet 要快。
-
-```java
-public class SortedSetDemo {
- public static void main(String[] args) {
- SortedSet sortedSet = new TreeSet<>();
- Collections.addAll(sortedSet, "one two three four".split(" "));
- System.out.println(sortedSet);
- System.out.println("first: " + sortedSet.first());
- System.out.println("last: " + sortedSet.last());
- Iterator itr = sortedSet.iterator();
- while (itr.hasNext()) {
- System.out.print(itr.next() + " ");
- }
- System.out.println();
- //from包含-to不包含
- System.out.println("subSet: " + sortedSet.subSet("one", "three"));
- //小于three的元素
- System.out.println(sortedSet.headSet("three"));
- //大于等于one的元素
- System.out.println(sortedSet.tailSet("one"));
- }
-}
-/*output
-[four, one, three, two]
-first: four
-last: two
-four one three two
-subSet: [one]
-[four, one]
-[one, three, two]
- */
-```
-
-### 队列
-
-除了并发应用,Queue Java SE5中仅有两个实现 LinkedList 和 PriorityQueue,它们的差异在于排序行为不在性能。
-
-#### 优先级队列
-
-```java
-public class ToDoList extends PriorityQueue {
- static class ToDoItem implements Comparable {
- private char primary;
- private int secondary;
- private String item;
-
- public ToDoItem(String item, char pri, int sec) {
- this.item = item;
- primary = pri;
- secondary = sec;
- }
- @Override
- public int compareTo(ToDoItem o) {
- if(primary > o.primary) {
- return 1;
- }
- if(primary == o.primary) {
- if(secondary > o.secondary) {
- return 1;
- } else if (secondary == o.secondary) {
- return 0;
- } else {
- return -1;
- }
- }
- return -1;
- }
- @Override
- public String toString() {
- return "ToDoItem{" +
- "primary=" + primary +
- ", secondary=" + secondary +
- ", item='" + item + '\'' +
- '}';
- }
- }
- public void add(String item, char pri, int sec) {
- super.add(new ToDoItem(item, pri, sec));
- }
-
- public static void main(String[] args) {
- ToDoList toDoList = new ToDoList();
- toDoList.add("homework", 'b', 3);
- toDoList.add("feed dog", 'a', 4);
- toDoList.add("lunch", 'a', 1);
- while (!toDoList.isEmpty()) {
- System.out.println(toDoList.remove());
- }
- }
-}
-/*output
-ToDoItem{primary=a, secondary=1, item='lunch'}
-ToDoItem{primary=a, secondary=4, item='feed dog'}
-ToDoItem{primary=b, secondary=3, item='homework'}
- */
-```
-
-#### 双向队列
-
-可以在队列任何一端添加或移除元素。Java 标准库中没有任何显式的用于双向队列的接口。
-
-```java
-public class Deque {
- private LinkedList deque = new LinkedList<>();
- public void addFirst(T t) { deque.add(t); }
- public void addLast(T t) { deque.add(t); }
- public T getFirst() { return deque.getFirst(); }
- public T getLast() { return deque.getLast(); }
- public T removeFirst() { return deque.removeFirst(); }
- public T removeLast() { return deque.removeLast(); }
- public int size() { return deque.size(); }
- @Override
- public String toString() { return deque.toString(); }
-
- public static void main(String[] args) {
- Deque deque = new Deque<>();
- for(int i = 20; i < 27; i++) {
- deque.addFirst(i);
- }
- for(int j = 50; j < 55; j++) {
- deque.addLast(j);
- }
- System.out.println(deque);
- while(deque.size() != 0) {
- System.out.print(deque.removeFirst() + " ");
- }
- }
-}
-/*output
-[20, 21, 22, 23, 24, 25, 26, 50, 51, 52, 53, 54]
-20 21 22 23 24 25 26 50 51 52 53 54
- */
-```
-
-### LinkedHashMap
-
-可以在构造器中设定 LinkedHashMap,使之采用基于访问的最近最少使用(LRU)算法,没有访问过的元素会出现在队列的前面。可用于定期清理元素以节省空间。
-
-```java
-public class LinkedHashMapDemo {
- public static void main(String[] args) {
- LinkedHashMap linkedHashMap = new LinkedHashMap<>(new CountingMapData(5));
- System.out.println(linkedHashMap);
- linkedHashMap = new LinkedHashMap<>(16, 0.75f, true);
- linkedHashMap.putAll(new CountingMapData(5));
- for(int i = 0; i < 3; i++) {
- linkedHashMap.get(i);
- }
- System.out.println(linkedHashMap);
- linkedHashMap.get(3);
- System.out.println(linkedHashMap);
- }
-}
-/*output
-{0=A0, 1=B0, 2=C0, 3=D0, 4=E0}
-{3=D0, 4=E0, 0=A0, 1=B0, 2=C0}
-{4=E0, 0=A0, 1=B0, 2=C0, 3=D0}
- */
-```
-
-### Colletions 工具类
-
-```java
-public class Utilities {
- static List list = Arrays.asList("one two three four".split(" "));
-
- public static void main(String[] args) {
- System.out.println(list);
- System.out.println("list disjoint(four)?" +
- Collections.disjoint(list, Collections.singletonList("four"))); //不相交
- System.out.println("max: " + Collections.max(list));
- System.out.println("min: " + Collections.min(list));
- System.out.println("max with comparator: " + Collections.max(list, String.CASE_INSENSITIVE_ORDER));
- List subList = Arrays.asList("one two".split(" "));
- System.out.println("indexOfSubList: " + Collections.indexOfSubList(list, subList));
- System.out.println("lastIndexOfSubList: " + Collections.lastIndexOfSubList(list, subList));
- Collections.replaceAll(list, "one", "emm");
- System.out.println("replace all: " + list);
- Collections.reverse(list);
- System.out.println("reverse: " + list);
- Collections.rotate(list, 1);
- System.out.println("rotate: " + list);
- List source = Arrays.asList("int the matrix".split(" "));
- Collections.copy(list, source);
- System.out.println("copy: " + list);
- Collections.swap(list, 0, list.size() - 1);
- System.out.println("swap: " + list);
- Collections.shuffle(list, new Random((47)));
- System.out.println("shuffle: " + list); //打乱
- Collections.fill(list, "frequency");
- System.out.println("fill: " + list);
- System.out.println("frequency of 'frequency': " + Collections.frequency(list, "frequency"));
- List nCopies = Collections.nCopies(3, "nCopies;");
- System.out.println("nCopies: " + nCopies);
- System.out.println("list disjoint nCopies? " + Collections.disjoint(list, nCopies));
- Enumeration e = Collections.enumeration(nCopies);
- Vector v = new Vector<>();
- while(e.hasMoreElements()) {
- v.add(e.nextElement());
- }
- ArrayList arrayList = Collections.list(v.elements());
- System.out.println("arrayList: " + arrayList);
- }
-}
-/*output
-[one, two, three, four]
-list disjoint(four)?false
-max: two
-min: four
-max with comparator: two
-indexOfSubList: 0
-lastIndexOfSubList: 0
-replace all: [emm, two, three, four]
-reverse: [four, three, two, emm]
-rotate: [emm, four, three, two]
-copy: [int, the, matrix, two]
-swap: [two, the, matrix, int]
-shuffle: [two, the, int, matrix]
-fill: [frequency, frequency, frequency, frequency]
-frequency of 'frequency': 4
-nCopies: [nCopies;, nCopies;, nCopies;]
-list disjoint nCopies? true
-arrayList: [nCopies;, nCopies;, nCopies;]
- */
-```
-
-#### 排序和查询
-
-```java
-public class ListSortSearch {
- public static void main(String[] args) {
- List list =
- new ArrayList(Utilities.list);
- list.addAll(Utilities.list);
- print(list);
- Collections.shuffle(list, new Random(47));
- print("Shuffled: " + list);
- // Use a ListIterator to trim off the last elements:
- ListIterator it = list.listIterator(4);
- while (it.hasNext()) {
- it.next();
- it.remove();
- }
- print("Trimmed: " + list);
- Collections.sort(list);
- print("Sorted: " + list);
- String key = list.get(3);
- int index = Collections.binarySearch(list, key);
- print("Location of " + key + " is " + index +
- ", list.get(" + index + ") = " + list.get(index));
- Collections.sort(list, String.CASE_INSENSITIVE_ORDER);
- print("Case-insensitive sorted: " + list);
- key = list.get(3);
- index = Collections.binarySearch(list, key,
- String.CASE_INSENSITIVE_ORDER);
- print("Location of " + key + " is " + index +
- ", list.get(" + index + ") = " + list.get(index));
- }
-} /* Output:
-[one, Two, three, Four, five, six, one, one, Two, three, Four, five, six, one]
-Shuffled: [Four, five, one, one, Two, six, six, three, three, five, Four, Two, one, one]
-Trimmed: [Four, five, one, one, Two, six, six, three, three, five]
-Sorted: [Four, Two, five, five, one, one, six, six, three, three]
-Location of six is 7, list.get(7) = six
-Case-insensitive sorted: [five, five, Four, one, one, six, six, three, three, Two]
-Location of three is 7, list.get(7) = three
-*/
-```
-
-#### 只读容器
-
-```java
-public class ReadOnly {
- static Collection data =
- new ArrayList<>(Countries.names(6));
-
- public static void main(String[] args) {
- Collection c = Collections.unmodifiableCollection(
- new ArrayList<>(data));
- print(c); // Reading is OK
- //! c.add("one"); // Can't change it
-
- List a = Collections.unmodifiableList(new ArrayList<>(data));
- ListIterator lit = a.listIterator();
- print(lit.next()); // Reading is OK
- //! lit.add("one"); // Can't change it
-
- Set s = Collections.unmodifiableSet(new HashSet<>(data));
- print(s); // Reading is OK
- //! s.add("one"); // Can't change it
-
- // For a SortedSet:
- Set ss = Collections.unmodifiableSortedSet(new TreeSet<>(data));
-
- Map m = Collections.unmodifiableMap(
- new HashMap<>(Countries.capitals(6)));
- print(m); // Reading is OK
- //! m.put("Ralph", "Howdy!");
-
- // For a SortedMap:
- Map sm = Collections.unmodifiableSortedMap(
- new TreeMap<>(Countries.capitals(6)));
- }
-}
-```
-
-#### Collection 和 Map 的同步控制
-
-```java
-public class Synchronization {
- public static void main(String[] args) {
- Collection c = Collections.synchronizedCollection(new ArrayList<>());
- List list = Collections.synchronizedList(new ArrayList<>());
- Set set = Collections.synchronizedSet(new HashSet<>());
- Set ss = Collections.synchronizedSortedSet(new TreeSet<>());
- Map map = Collections.synchronizedMap(new HashMap<>());
- Map sm = Collections.synchronizedSortedMap(new TreeMap<>());
- }
-}
-```
-
-#### 快速报错机制
-
- fast-fail 是 Java 容器的一种保护机制。当多个线程对同一个集合进行操作时,就有可能会产生 fast-fail 事件。例如:当线程a正通过 iterator 遍历集合时,另一个线程b修改了集合的内容(modCount 不等于expectedModCount),那么线程a在遍历的时候会抛出 ConcurrentModificationException,产生 fast-fail 事件。
-
-```java
-public class FastFail {
- public static void main(String[] args) {
- Collection c = new ArrayList<>();
- Iterator itr = c.iterator();
- c.add("haha");
- try {
- String s = itr.next();
- } catch (ConcurrentModificationException ex) {
- ex.printStackTrace();
- }
- }
-}
-```
-
-在此例中,应该添加完所有元素后,再获取迭代器。
-
-**多线程并发修改容器的方法:**
-
-- 使用`Colletions.synchronizedList()`方法或在修改集合内容的地方加上 synchronized。这样的话,增删集合内容的同步锁会阻塞遍历操作,影响性能。
-- 使用 CopyOnWriteArrayList 来替换 ArrayList。在对 CopyOnWriteArrayList 进行修改操作的时候,会拷贝一个新的集合,对新的集合进行操作,操作完成后再把引用指向新的集合。
-
-### Java 1.0/1.1 的容器
-
-#### BitSet
-
-参考自:[JAVA中BitSet使用](https://blog.csdn.net/xv1356027897/article/details/79518647)
-
-位图,数据的存在性可以使用bit位上的1或0来表示,一个long型数字占用64位空间,那么一个long型数字就可以保存64个数字的“存在性”状态(true or false)。BitSet内部是一个long[]数组,数组的大小由 BitSet 接收的最大数字决定,这个数组将数字分段表示[0,63],[64,127],[128,191]...。即long[0]用来存储[0,63]这个范围的数字的“存在性”,long[1]用来存储[64,127],依次递推。
-
-```java
-public class BitSetDemo {
- public static void printBitSet(BitSet b) {
- System.out.println("BitSet: " + b);
- StringBuilder sb = new StringBuilder();
- for(int i = 0; i < b.size(); i++) {
- sb.append(b.get(i) ? "1" : "0");
- }
- System.out.println("bitset pattern: " + sb);
- }
- public static void main(String[] args) {
- Random rand = new Random(47);
- int num = rand.nextInt(20);
- BitSet bs = new BitSet();
- bs.set(num);
- System.out.println(bs);
- System.out.println("num exist? " + bs.get(num));
- System.out.println("size of bitset: " + bs.size());
- System.out.println("the number of bits set: " + bs.cardinality());
- bs.set(num);//重复设置
- System.out.println("set again! num exist? " + bs.get(num));
- bs.clear(num);//清除
- System.out.println("after clearing bit: " + bs);
-
- bs.set(num + 100);//自动扩充容量
- System.out.println("num+100 exist? " + bs.get(num + 100));
- System.out.println("num-1 exist? " + bs.get(num - 1));
- System.out.println("size of bitset: " + bs.size());
- }
-}
-/*output
-{18}
-num exist? true
-size of bitset: 64
-the number of bits set: 1
-set again! num exist? true
-after clearing bit: {}
-num+100 exist? true
-num-1 exist? false
-size of bitset: 128
- */
-```
-
-
-
-## Java I/O 系统
-
-### 输入和输出
-
-继承自 InputStream 或 Reader 的类都具有 read() 方法,用于读取单个字节或者字节数组;继承自 OutputStream 或 Writer 的类都含有 write() 方法,用于写单个字节或字节数组。
-
-#### InputStream 和 OutputStream
-
-InputStream 用来表示那些从不同数据源产生输入的类。这些数据源包括:1.字节数组;2.String 对象;3.文件;4.管道;5.一个由其他种类的流组成的序列。
-
-InputStream 类有一个抽象方法:`abstract int read()`,这个方法将读入并返回一个字节,或者在遇到输入源结尾时返回-1。
-
-OutputStream 决定了输出所要去的目标:字节数组、文件或管道。OutputStream 的 `abstract void write(int b)` 可以向某个输出位置写出一个字节。
-
-read() 和 write() 方法在执行时都将阻塞,等待数据被读入或者写出。
-
-#### Reader 和 Writer
-
-字符流是由通过字节流转换得到的,转化过程耗时,而且容易出现乱码问题。I/O 流提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。如果音频文件、图片等媒体文件用字节流比较好,如果涉及到字符的话使用字符流比较好。
-
-```java
-abstract int read();
-abstract void write(char c);
-```
-
-### 组合输入输出流过滤器
-
-FileInputStream 和 FileOutputStream 可以提供附在磁盘文件上的输入流和输出流。
-
-`FileInputStream fin = new FileInputStream("E:/data.txt") //E:\\data.txt`
-
-通过 java.io.File.separator 可以获得与平台相关的 文件分隔符。
-
-FileInputStream 只支持在文件的读写字节,而DataInputStream 只支持数值的读写。
-
-实现从文件读取数字,需要将 FileInputStream 和 DataInputStream 组合。
-
-```java
-FileInputStream fin = new FileInputStream("e:/data.txt");
-DataInputStream din = new DataInputStream(fin);
-double x = din.readDouble();
-```
-
-使用缓冲机制:
-
-```java
-DataInputStream din = new DataInputStream(
- new BufferedInputStream(
- new FileInputStream("E:/data.txt")
- )
-);
-```
-
-可回推的输入流:
-
-```java
-PushbackInputStream pin = new PushbackInputStream(
- new BufferedInputStream(
- new FileInputStream("E:/data.txt")
- )
-);
-
-int b = pin.read();
-if(b != '<') {
- pin.unread(b);
-}
-```
-
-从 zip 文件读入数字:
-
-```java
-DataInputStream din = new DataInputStream(
- new ZipInputStream(
- new FileInputStream("E:/data.zip")
- )
-);
-```
-
-### 文本输入和输出
-
-将字节流转化为 Unicode 字符的读入器:
-
-```java
-//Reader in = new InputStreamReader(System.in);
-Reader in = new InputStreamReader(new FileInputStream("E:/data.txt"), StandardCharsets.UTF_8);
-```
-
-将 Unicode 字符转化为字节流的写出器:
-
-```java
-PrintWriter out = new PrintWriter("H:/data.txt", "UTF-8");
-out.print("haha");
-out.flush();
-```
-
-默认情况下,自动冲刷机制是禁用的,可以使用`PrintWriter(Writer out, Boolean autoFlush)`启用或禁用自动冲刷机制:
-
-```java
-PrintWriter out = new PrintWriter(
- new OutputStreamWriter(
- new FileOutputStream("H:/data.txt"), "UTF-8"),
- true);
-out.print("tyson");
-out.flush();
-```
-
-### 以文本格式存储对象
-
-```java
-public class TextFile {
- public static void main(String[] args) throws IOException {
- Employee[] staff = new Employee[2];
- DateTimeFormatter ymd = DateTimeFormatter.ofPattern("yyyy-MM-dd");
- //字符串转换成LocalDate类型
- LocalDate ld = LocalDate.parse("2015-11-23", ymd);
- staff[0] = new Employee("tyson", 1200.00, ld);
- staff[1] = new Employee("tom", 1200.00, ld);
-
- try (PrintWriter out = new PrintWriter("H:/data.txt", "UTF-8")) {
- writeData(staff, out);
- }
- try (Scanner in = new Scanner(
- new FileInputStream("H:/data.txt"), "UTF-8")) {
- Employee[] newStaff = readData(in);
-
- for(Employee e : newStaff) {
- System.out.println(e);
- }
- }
- }
-
- private static void writeData(Employee[] employees, PrintWriter out) throws IOException {
- out.println(employees.length);
- for(Employee e : employees) {
- writeEmployee(out, e);
- }
- }
-
- private static Employee[] readData(Scanner in) {
- int n = in.nextInt();
- in.nextLine();
-
- Employee[] employees = new Employee[n];
- for(int i = 0; i < n ; i++) {
- employees[i] = readEmployee(in);
- }
-
- return employees;
- }
-
- public static void writeEmployee(PrintWriter out, Employee e) throws IOException {
- out.println(e.getName() + "|" + e.getSalary() + "|" + e.getHireDate());
- }
-
- public static Employee readEmployee(Scanner in) {
- String line = in.nextLine();
- String[] tokens = line.split("\\|");
- String name = tokens[0];
- double salary = Double.parseDouble(tokens[1]);
- LocalDate hireDate = LocalDate.parse(tokens[2]);
- return new Employee(name, salary, hireDate);
- }
-}
-/*output
-Employee{name='tyson', salary=1200.0, hireDate=2015-11-23}
-Employee{name='tom', salary=1200.0, hireDate=2015-11-23}
- */
-```
-
-### 字符编码方式
-
-输入和输出流都是用于字节序列,很多情况下,我们希望操作的是字符序列。
-
-平台使用的编码方法可以由静态方法`Charset.defaultCharset`返回。
-
-将字节数组转化成字符串:
-
-```java
-String str = new String(bytes, StandardCharsets.UTF_8);
-```
-
-### 读写二进制数据
-
-#### DataInput 和 DataOutput
-
-DataOutput 接口定义了以二进制格式写数组、字符、字符串等的方法,如 writeChars、writeByte、writeInt 和 writeDouble。
-
-writeInt 总是将一个整数写出为4字节的二进制数量值,writeDouble 总将一个 double 值写出为8字节的二进制数量值。Java 会将所有的值都按照高位在前的模式写出,这使得 Java 数据文件可以独立于平台。
-
-DataInput 接口用于二进制格式读数据,接口定义了如下方法:readInt、readShort、readChar等。
-
-DataInputStream 实现了 DataInput 接口,DataOutputStream 实现了 DataOutput 接口:
-
-```java
-DataInputStream in = new DataInputStream(new FileInputStream("e:/data.txt"));
-DataOutputStream out = new DataOutputStream(new FileOutputStream("e:/data.txt"));
-```
-
-#### 随机访问文件
-
-RandomAccessFile 类可以在文件中的任何位置查找或写入。RandomAccessFile 类同时实现了 DataInput 和 DataOutput 接口。
-
-#### 序列化
-
-**保存和加载序列化对象**
-
-保存对象:
-
-```java
-ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("e:/data.txt"));
-Employee tyson = new Employee();
-out.writeObject(tyson);
-```
-
-读回对象:
-
-```java
-ObjectInputStream in = new ObjectInputStream(new FileInputStream("e:/data.txt"));
-Employee e = (Employee)in.readObject();
-```
-
-其中,Employee 需要实现 Serializable 接口,接口没有任何方法:
-
-```java
-Employee implements Serializable {...}
-```
-
-ObjectStreamTest.java
-
-```java
-/**
- * Copyright (C), 2018-2019
- * FileName: ObjectStream
- * Author: Tyson
- * Date: 2019/4/25/0025 14:58
- * Description: 对象序列化
- */
-package com.tyson.chapter18.io;
-
-import java.io.*;
-
-/**
- * @author Tyson
- * @ClassName: ObjectStream
- * @create 2019/4/25/0025 14:58
- */
-public class ObjectStream {
- public static void main(String[] args) throws IOException, ClassNotFoundException {
- Employee tyson = new Employee("tyson", 8888.0, "2015-02-15");
- Manager sophia = new Manager("sophia", 6666.0, "2012-03-15");
- sophia.setSecretary(tyson);
- Manager tom = new Manager("tom", 5555.0, "2014-03-16");
- tom.setSecretary(tyson);
- Employee[] staff = new Employee[3];
- staff[0] = tyson;
- staff[1] = sophia;
- staff[2] = tom;
-
- try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("e:/data.txt"))) {
- out.writeObject(staff);
- out.flush();
- }
-
- try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("e:/data.txt"))) {
- Employee[] newStaff = (Employee[]) in.readObject();
- newStaff[1].setSalary(23333.0);
- for(Employee e : newStaff) {
- System.out.println(e);
- }
- //原对象sophia不变
- System.out.println(staff[1]);
- }
- }
-}
-
-class Manager extends Employee {
- private Employee secretary;
-
- public Manager(String name, Double salary, String hireDate) {
- super(name, salary, hireDate);
- }
-
- @Override
- public String toString() {
- return "Manager{" +
- "name=" +super.getName() +
- ", salary=" +super.getSalary() +
- ", hireday=" +super.getHireDate() +
- ", secretary=" + secretary +
- '}';
- }
-
- public Employee getSecretary() {
- return secretary;
- }
-
- public void setSecretary(Employee secretary) {
- this.secretary = secretary;
- }
-}
-```
-
-通过关键字 transient 可以将某些不可序列化的域设置成瞬时的域,这些域在对象被序列化时总是被跳过。
-
-**使用序列化实现克隆**
-
-将对象序列化到输出流中,然后将其读回,产生的新对象是原对象的深拷贝。
-
-### 操作文件
-
-File 类既能代表特定文件的名称,又能代表一个目录下的一组文件的名称。
-
-#### 目录列表器
-
-获取某个目录下以".java"文件后缀结尾的文件名。
-
-```java
-class DirFilter implements FilenameFilter {
- private Pattern pattern;
- public DirFilter(String regex) {
- pattern = Pattern.compile(regex);
- }
- @Override
- public boolean accept(File dir, String name) {
- return pattern.matcher(name).matches();
- }
-}
-
-public class DirList {
- public static void main(String[] args) {
- File path = new File("H:" + File.separator +
- "java-data" + File.separator + "TIJ4-code" + File.separator + "containers");
- //Java文件后缀".+\\.java$"
- String[] list = path.list(new DirFilter("A.+\\.java$"));
- Arrays.sort(list);
- System.out.println(Arrays.toString(list));
- }
-}
-/*output
-[AssociativeArray.java]
- */
-```
-
-用匿名内部类实现。
-
-```java
-public class DirList {
- public static void main(String[] args) {
- File f = new File("H:" + File.separator +
- "java-data" + File.separator + "TIJ4-code" + File.separator + "containers");
- String[] fileNames = f.list(new FilenameFilter() {
- @Override
- public boolean accept(File dir, String name) {
- return name.endsWith(".java");
- }
- });
- System.out.println(Arrays.toString(fileNames));
- }
-}
-```
-
-###
-
-## 枚举类型
-
-### 基本 enum 特性
-
-除了不能继承自一个 enum 之外,我们基本上可以将 enum 看做一个常规的类。所有的 enum 都继承自 java.lang.Enum 类,所以 enum 不能再继承其他类。在创建一个新的 enum 时,可以同时实现一个或多个接口。
-
-```java
-enum Color { RED, GREEN, BLUE }
-
-public class EnumClass {
- public static void main(String[] args) {
- for(Color c : Color.values()) {
- System.out.println(c + " ordinal: " + c.ordinal());
- System.out.print(c.compareTo(Color.RED) + " ");
- System.out.print(c.equals(Color.RED) + " ");
- System.out.println(c == Color.RED);
- System.out.println(c.getDeclaringClass());
- System.out.println(c.name());
- System.out.println("-------------------------");
- }
- for(String s : "RED GREEN BLUE".split(" ")) {
- Color c = Enum.valueOf(Color.class, s);
- System.out.println(c);
- }
- }
-}
-/*output
-RED ordinal: 0
-0 true true
-class com.tyson.chapter19.enumerated.Color
-RED
--------------------------
-GREEN ordinal: 1
-1 false false
-class com.tyson.chapter19.enumerated.Color
-GREEN
--------------------------
-BLUE ordinal: 2
-2 false false
-class com.tyson.chapter19.enumerated.Color
-BLUE
--------------------------
-RED
-GREEN
-BLUE
- */
-```
-
-values() 是编译器添加的 static 方法,Enum 类中没有 values() 方法。ordinal() 方法返回一个 int 值,这是每个 enum 实例声明时的次序,从0开始。valueOf() 根据给定的名字返回相应的 enum实例,如果不存在给定名字的实例,将会抛出异常。
-
-### 向 enum 添加新方法
-
-```java
-public enum Direction {
- WEST("Xizang"),
- NORTH("Beijing"),
- EAST("Shanghai"),
- SOUTH("Hainan");//定义自己的方法,enum实例序列的最后需添加分号
- //属性和方法需要在enum实例之后定义
- private String discription;
- private Direction(String discription) {
- this.discription = discription;
- }
- public String getDiscription() {
- return discription;
- }
-
- public static void main(String[] args) {
- for(Direction dir : Direction.values()) {
- System.out.println(dir + ": " + dir.getDiscription());
- }
- }
-}
-/*output
-WEST: Xizang
-NORTH: Beijing
-EAST: Shanghai
-SOUTH: Hainan
- */
-```
-
-### 覆盖 enum 的方法
-
-```java
-public enum Animal {
- CAT, DOG, BIRD, PIG;
- @Override
- public String toString() {
- String id = name();
- String lower = id.substring(1).toLowerCase();
- return id.charAt(0) + lower;
- }
-
- public static void main(String[] args) {
- for(Animal a : values()) {
- System.out.print(a + " ");
- }
- }
-}
-//output: Cat Dog Bird Pig
-```
-
-### Switch 语句中的 enum
-
-```java
-enum Signal {
- GREEN, YELLOW, RED,
-}
-
-public class TrafficLight {
- Signal color = Signal.RED;
-
- public void change() {
- switch (color) {
- case RED:
- color = Signal.GREEN;
- break;
- case GREEN:
- color = Signal.YELLOW;
- break;
- case YELLOW:
- color = Signal.RED;
- break;
- }
- }
- @Override
- public String toString() {
- return "the traffic light is: " + color;
- }
-
- public static void main(String[] args) {
- TrafficLight tl = new TrafficLight();
- for (int i = 0; i < 4; i++) {
- System.out.println(tl);
- tl.change();
- }
- }
-}
-/*output
-the traffic light is: RED
-the traffic light is: GREEN
-the traffic light is: YELLOW
-the traffic light is: RED
- */
-```
-
-### EnumSet
-
- 如果你想用一个数表示多种状态,那么位运算是一种很好的选择。EnumSet 是通过位运算实现的。它是一个与枚举类型一起使用的专用 Set 实现。枚举set中所有元素都必须来自单个枚举类型(即必须是同类型,且该类型是 Enum 的子类)。
-
-```java
-public enum Season {
- SPRING, SUMMER, AUTUMN, WINTER;
-
- public static void main(String[] args) {
- Set emptyEnumSet = EnumSet.noneOf(Season.class);
- System.out.println("EnumSet.noneOf(): " + emptyEnumSet);
- emptyEnumSet.add(SPRING);
- System.out.println("emptyEnumSet.add(SPRING): " + emptyEnumSet);
- Set enumSet = EnumSet.allOf(Season.class);
- System.out.println("EnumSet.allOf(): " + enumSet);
- emptyEnumSet.addAll(enumSet);
- System.out.println("emptyEnumSet.addAll(): " + emptyEnumSet);
- Season[] seasons = new Season[emptyEnumSet.size()];
- enumSet.toArray(seasons);
- System.out.println("seasons: " + Arrays.toString(seasons));
- }
-}
-/*output
-EnumSet.noneOf(): []
-emptyEnumSet.add(SPRING): [SPRING]
-EnumSet.allOf(): [SPRING, SUMMER, AUTUMN, WINTER]
-emptyEnumSet.addAll(): [SPRING, SUMMER, AUTUMN, WINTER]
-seasons: [SPRING, SUMMER, AUTUMN, WINTER]
- */
-```
-
-#### 源码解析
-
-参考自:[EnumSet源码解析](https://www.jianshu.com/p/f7035c5816b1)
-
-```java
-//EnumSet的容量小于64,创建的是RegularEnumSet,大于64,创建的是JumboEnumSet
-public boolean add(E e) {
- // 校验枚举类型
- typeCheck(e);
-
- long oldElements = elements;
- elements |= (1L << ((Enum>)e).ordinal());
- return elements != oldElements;
-}
-
-/**
- * 用于校验枚举类型,位于EnumSet中
- */
-final void typeCheck(E e) {
- Class> eClass = e.getClass();
- if (eClass != elementType && eClass.getSuperclass() != elementType)
- throw new ClassCastException(eClass + " != " + elementType);
-}
-```
-
-每一个枚举元素都有一个属性ordinal,用来表示该元素在枚举类型中的次序或者说下标。
-
-
-
-addAll 方法就是将 elements 上,从低位到枚举长度上的下标值置为1。比如某一个枚举类型共5个元素,而addAll 就是将 elements 的二进制的低5位置为1。
-
-```java
-void addAll() {
- if (universe.length != 0)
- elements = -1L >>> -universe.length;
-}
-```
-
-### EnumMap
-
-EnumMap 是一种特殊的 Map,它要求其中的键必须来自一个 enum。由于 enum 本身的限制,所以 EnumMap 在内部可由数组实现。因此 EnumMap 的速度很快。
-
-```java
-interface Command { void action(); }
-public class EnumMaps {
- public static void main(String[] args) {
- EnumMap em = new EnumMap<>(AlarmPoints.class);
- em.put(KITCHEN, new Command() {
- @Override
- public void action() {
- System.out.println("Kitchen fire!");
- }
- });
- em.put(BATHROOM, new Command() {
- @Override
- public void action() {
- System.out.println("Bathroom alert");
- }
- });
- for(Map.Entry e : em.entrySet()) {
- System.out.print(e.getKey() + ": ");
- e.getValue().action();
- }
- //获取不存在的值
- try {
- em.get(UTILITY).action();
- } catch (Exception ex) {
- System.out.println(ex);
- }
- }
-}
-/*output
-BATHROOM: Bathroom alert
-KITCHEN: Kitchen fire!
-java.lang.NullPointerException
- */
-```
-
-与 EnumSet 一样,enum 实例定义时的次序决定了其在 EnumMap 中的顺序。每个 enum 实例都会作为键存放到 EnumMap 中,如果没有为这个键调用 put() 方法来存入相应的值的话,其对应的值为 null。
-
-
-
-## 注解
-
-Java SE5内置了三种定义在 java.lang 中的注解:@Override,@Deprecated 和 @SuppressWarnings(关闭不当的编译器警告信息)。
-
-### 基本语法
-
-```java
-@Target(ElementType.METHOD)//应用于什么地方,方法、域等
-@Retention(RetentionPolicy.SOURCE)//注解在哪一个级别可用,SOURCE/CLASS/RUNTIME
-public @interface Override {
-}
-```
-
-#### 自定义注解
-
-```java
-//UserCase.java
-@Target(ElementType.METHOD)
-@Retention(RetentionPolicy.RUNTIME)
-public @interface UserCase {
- public int id();
- public String description() default "no description";
-}
-
-//PasswordUtils.java
-public class PasswordUtils {
- @UserCase(id = 47, description = "Passwords must contain at least one numeric")
- public boolean validatePassword(String password) {
- return password.matches("\\w*\\d\\w*");
- }
-}
-```
-
-注解元素(本例的是 id和description)只能是基本类型、String、Class、enum、Annotation 和这些类型的数组。
-
-@Retention注解三种取值:**RetentionPolicy.SOURCE**、**RetentionPolicy.CLASS**、**RetentionPolicy.RUNTIME**分别对应:Java源文件(.java文件)---->.class文件---->内存中的字节码。
-
-@Target元注解决定了一个注解可以标识到哪些成分上,如标识在在类身上,或者属性身上,或者方法身上等成分,@Target默认值为任何元素(成分)。
-
-**Retention注解说明**
-
-参考自:[注解](https://www.cnblogs.com/xdp-gacl/p/3622275.html)
-
-当在Java源程序上加了一个注解,这个Java源程序要由javac去编译,javac把java源文件编译成.class文件,在编译成class时可能会把Java源程序上的一些注解给去掉,java编译器在处理java源程序时,可能会认为这个注解没有用了,于是就把这个注解去掉了,那么此时在编译好的class中就找不到注解了, 这是编译器编译java源程序时对注解进行处理的第一种可能情况,假设java编译器在把java源程序编译成class时,没有把java源程序中的注解去掉,那么此时在编译好的class中就可以找到注解,当程序使用编译好的class文件时,需要用类加载器把class文件加载到内存中,class文件中的东西不是字节码,class文件里面的东西由类加载器加载到内存中去,类加载器在加载class文件时,会对class文件里面的东西进行处理,如安全检查,处理完以后得到的最终在内存中的二进制的东西才是字节码,类加载器在把class文件加载到内存中时也有转换,转换时是否把class文件中的注解保留下来,这也有说法,所以说**一个注解的生命周期有三个阶段:java源文件是一个阶段,class文件是一个阶段,内存中的字节码是一个阶段**,javac把java源文件编译成.class文件时,有可能去掉里面的注解,类加载器把.class文件加载到内存时也有可能去掉里面的注解,因此**在自定义注解时就可以使用Retention注解指明自定义注解的生命周期,自定义注解的生命周期是在RetentionPolicy.SOURCE阶段(java源文件阶段),还是在RetentionPolicy.CLASS阶段(class文件阶段),或者是在RetentionPolicy.RUNTIME阶段(内存中的字节码运行时阶段)**,根据**JDK提供的API可以知道默认是在RetentionPolicy.CLASS阶段 (JDK的API写到:the retention policy defaults to RetentionPolicy.CLASS)。**
-
-### 元注解
-
-元、注解负责注解其他的注解。
-
-| 注解 | 作用 |
-| ----------- | -------------------------------- |
-| @Target | 表示该注解可以用于什么地方 |
-| @Retention | 表示需要在什么级别保存该注解信息 |
-| @Documented | 将此注解包含在 Javadoc 中 |
-| @Inherited | 允许子类继承父类的注解 |
-
-### 编写注解处理器
-
-使用注解时,很重要的一部分就是创建和使用注解处理器。
-
-```java
-public class UseCaseTracker {
- public static void trackUseCases(List useCases, Class> c) {
- for(Method m : c.getDeclaredMethods()) {
- UseCase uc = m.getAnnotation(UseCase.class);
- if(uc != null) {
- System.out.println("found use case: " + uc.id() + " " + uc.description());
- useCases.remove(new Integer(uc.id()));
- }
- }
- for(int i : useCases) {
- System.out.println("warning: missing use case-" + i);
- }
- }
-
- public static void main(String[] args) {
- List useCases = new ArrayList<>();
- Collections.addAll(useCases, 47, 48);
- trackUseCases(useCases, PasswordUtils.class);
- }
-}
-/*output
-found use case: 47 Passwords must contain at least one numeric
-warning: missing use case-48
- */
-```
-
-### 注解综合
-
-```java
-//TrafficLight.java
-public enum TrafficLight {
- RED, GREEN, YELLOW;
-}
-
-//MetaAnnotation.java
-public @interface MetaAnnotation {
- String value();
-}
-
-//MyAnnotation.java
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.METHOD, ElementType.TYPE})
-public @interface MyAnnotation {
- String color() default "blue";
- //当只有value属性要设置时,可以省略"value="
- String value();
- int[] arrayAttr() default {1, 2, 3};
- TrafficLight lamp() default TrafficLight.GREEN;
- MetaAnnotation annotationAttr() default @MetaAnnotation("tyson");
-}
-
-//MyAnnotationTracker.java
-@MyAnnotation(
- color = "red",
- value = "MyAnnotation注解用于MyAnnotationTracker类",
- arrayAttr = {6, 6, 6},
- lamp = TrafficLight.GREEN,
- annotationAttr = @MetaAnnotation("sophia")
-)
-public class MyAnnotationTracker {
- @MyAnnotation("MyAnnotation注解用于main方法")
- public static void main(String[] args) {
- //如果指定类型的注解存在于此元素上则返回true
- if(MyAnnotationTracker.class.isAnnotationPresent(MyAnnotation.class)) {
- //获取类的注解
- MyAnnotation myAnnotation = (MyAnnotation)MyAnnotationTracker.class.getAnnotation(MyAnnotation.class);
- System.out.println(myAnnotation.color());
- System.out.println(myAnnotation.value());
- System.out.println(Arrays.toString(myAnnotation.arrayAttr()));
- System.out.println(myAnnotation.lamp());
- MetaAnnotation ma = myAnnotation.annotationAttr();
- System.out.println(ma.value());
- }
- //获取方法的注解
- for(Method m : MyAnnotationTracker.class.getDeclaredMethods()) {
- MyAnnotation myAnnotation1 = m.getAnnotation(MyAnnotation.class);
- if(myAnnotation1 != null) {
- System.out.println(myAnnotation1.value());
- }
- }
- }
-}
-/*output
-red
-MyAnnotation注解用于MyAnnotationTracker类
-[6, 6, 6]
-GREEN
-sophia
-MyAnnotation注解用于main方法
- */
-```
-
-
-
-
-
-
-
diff --git "a/Java/Java\345\205\263\351\224\256\345\255\227.md" "b/Java/Java\345\205\263\351\224\256\345\255\227.md"
deleted file mode 100644
index 3dc54e5..0000000
--- "a/Java/Java\345\205\263\351\224\256\345\255\227.md"
+++ /dev/null
@@ -1,205 +0,0 @@
-
-
-
-
-- [static](#static)
- - [静态变量](#%E9%9D%99%E6%80%81%E5%8F%98%E9%87%8F)
- - [静态方法](#%E9%9D%99%E6%80%81%E6%96%B9%E6%B3%95)
- - [静态代码块](#%E9%9D%99%E6%80%81%E4%BB%A3%E7%A0%81%E5%9D%97)
- - [静态内部类](#%E9%9D%99%E6%80%81%E5%86%85%E9%83%A8%E7%B1%BB)
-- [final](#final)
-- [this](#this)
-- [super](#super)
-
-
-
-## static
-
-static可以用来修饰类的成员方法、类的成员变量。
-
-### 静态变量
-
-static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。
-
-以下例子,age为非静态变量,则p1打印结果是:`Name:zhangsan, Age:10`;若age使用static修饰,则p1打印结果是:`Name:zhangsan, Age:12`,因为static变量在内存只有一个副本。
-
-```java
-public class Person {
- String name;
- int age;
-
- public String toString() {
- return "Name:" + name + ", Age:" + age;
- }
-
- public static void main(String[] args) {
- Person p1 = new Person();
- p1.name = "zhangsan";
- p1.age = 10;
- Person p2 = new Person();
- p2.name = "lisi";
- p2.age = 12;
- System.out.println(p1);
- System.out.println(p2);
- }
- /**Output
- * Name:zhangsan, Age:10
- * Name:lisi, Age:12
- *///~
-}
-```
-
-### 静态方法
-
-static方法一般称作静态方法。静态方法不依赖于任何对象就可以进行访问,通过类名即可调用静态方法。
-
-```java
-public class Utils {
- public static void print(String s) {
- System.out.println("hello world: " + s);
- }
-
- public static void main(String[] args) {
- Utils.print("程序员大彬");
- }
-}
-```
-
-### 静态代码块
-
-静态代码块只会在类加载的时候执行一次。以下例子,startDate和endDate在类加载的时候进行赋值。
-
-```java
-class Person {
- private Date birthDate;
- private static Date startDate, endDate;
- static{
- startDate = Date.valueOf("2008");
- endDate = Date.valueOf("2021");
- }
-
- public Person(Date birthDate) {
- this.birthDate = birthDate;
- }
-}
-```
-
-### 静态内部类
-
-**在静态方法里**,使用⾮静态内部类依赖于外部类的实例,也就是说需要先创建外部类实例,才能用这个实例去创建非静态内部类。⽽静态内部类不需要。
-
-```java
-public class OuterClass {
- class InnerClass {
- }
- static class StaticInnerClass {
- }
- public static void main(String[] args) {
- // 在静态方法里,不能直接使用OuterClass.this去创建InnerClass的实例
- // 需要先创建OuterClass的实例o,然后通过o创建InnerClass的实例
- // InnerClass innerClass = new InnerClass();
- OuterClass outerClass = new OuterClass();
- InnerClass innerClass = outerClass.new InnerClass();
- StaticInnerClass staticInnerClass = new StaticInnerClass();
-
- outerClass.test();
- }
-
- public void nonStaticMethod() {
- InnerClass innerClass = new InnerClass();
- System.out.println("nonStaticMethod...");
- }
-}
-```
-
-
-
-## final
-
-1. **基本数据**类型用final修饰,则不能修改,是常量;**对象引用**用final修饰,则引用只能指向该对象,不能指向别的对象,但是对象本身可以修改。
-
-2. final修饰的方法不能被子类重写
-
-3. final修饰的类不能被继承。
-
-
-
-## this
-
- `this.属性名称`指访问类中的成员变量,可以用来区分成员变量和局部变量。如下代码所示,`this.name`访问类Person当前实例的变量。
-
-```java
-/**
- * @description:
- * @author: 程序员大彬
- * @time: 2021-08-17 00:29
- */
-public class Person {
- String name;
- int age;
-
- public Person(String name, int age) {
- this.name = name;
- this.age = age;
- }
-}
-```
-
-`this.方法名称`用来访问本类的方法。以下代码中,`this.born()`调用类 Person 的当前实例的方法。
-
-```java
-/**
- * @description:
- * @author: 程序员大彬
- * @time: 2021-08-17 00:29
- */
-public class Person {
- String name;
- int age;
-
- public Person(String name, int age) {
- this.born();
- this.name = name;
- this.age = age;
- }
-
- void born() {
- }
-}
-```
-
-
-
-## super
-
-super 关键字用于在子类中访问父类的变量和方法。
-
-```java
-class A {
- protected String name = "大彬";
-
- public void getName() {
- System.out.println("父类:" + name);
- }
-}
-
-public class B extends A {
- @Override
- public void getName() {
- System.out.println(super.name);
- super.getName();
- }
-
- public static void main(String[] args) {
- B b = new B();
- b.getName();
- }
- /**
- * 大彬
- * 父类:大彬
- */
-}
-```
-
-在子类B中,我们重写了父类的getName()方法,如果在重写的getName()方法中我们要调用父类的相同方法,必须要通过super关键字显式指出。
-
diff --git "a/Java/Java\345\237\272\347\241\200.md" "b/Java/Java\345\237\272\347\241\200.md"
deleted file mode 100644
index 3a08dc1..0000000
--- "a/Java/Java\345\237\272\347\241\200.md"
+++ /dev/null
@@ -1,1582 +0,0 @@
-
-
-
-
-- [Java概述](#java%E6%A6%82%E8%BF%B0)
- - [Java的特点](#java%E7%9A%84%E7%89%B9%E7%82%B9)
- - [JKD和JRE](#jkd%E5%92%8Cjre)
- - [JDK](#jdk)
- - [JRE](#jre)
-- [Java基础语法](#java%E5%9F%BA%E7%A1%80%E8%AF%AD%E6%B3%95)
- - [基本数据类型](#%E5%9F%BA%E6%9C%AC%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B)
- - [包装类型](#%E5%8C%85%E8%A3%85%E7%B1%BB%E5%9E%8B)
- - [包装类缓存](#%E5%8C%85%E8%A3%85%E7%B1%BB%E7%BC%93%E5%AD%98)
- - [String](#string)
- - [String拼接](#string%E6%8B%BC%E6%8E%A5)
- - [关键字](#%E5%85%B3%E9%94%AE%E5%AD%97)
- - [static](#static)
- - [静态变量](#%E9%9D%99%E6%80%81%E5%8F%98%E9%87%8F)
- - [静态方法](#%E9%9D%99%E6%80%81%E6%96%B9%E6%B3%95)
- - [静态代码块](#%E9%9D%99%E6%80%81%E4%BB%A3%E7%A0%81%E5%9D%97)
- - [静态内部类](#%E9%9D%99%E6%80%81%E5%86%85%E9%83%A8%E7%B1%BB)
- - [final](#final)
- - [this](#this)
- - [super](#super)
- - [object常用方法](#object%E5%B8%B8%E7%94%A8%E6%96%B9%E6%B3%95)
- - [toString](#tostring)
- - [equals](#equals)
- - [hashCode](#hashcode)
- - [clone](#clone)
- - [浅拷贝](#%E6%B5%85%E6%8B%B7%E8%B4%9D)
- - [深拷贝](#%E6%B7%B1%E6%8B%B7%E8%B4%9D)
- - [getClass](#getclass)
- - [wait](#wait)
- - [notity](#notity)
- - [equals()和hashcode()的关系](#equals%E5%92%8Chashcode%E7%9A%84%E5%85%B3%E7%B3%BB)
- - [==和equals区别](#%E5%92%8Cequals%E5%8C%BA%E5%88%AB)
-- [面向对象](#%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1)
- - [面向对象特性](#%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%89%B9%E6%80%A7)
- - [多态怎么实现](#%E5%A4%9A%E6%80%81%E6%80%8E%E4%B9%88%E5%AE%9E%E7%8E%B0)
- - [类与对象](#%E7%B1%BB%E4%B8%8E%E5%AF%B9%E8%B1%A1)
- - [属性](#%E5%B1%9E%E6%80%A7)
- - [方法](#%E6%96%B9%E6%B3%95)
- - [普通方法](#%E6%99%AE%E9%80%9A%E6%96%B9%E6%B3%95)
- - [构造方法](#%E6%9E%84%E9%80%A0%E6%96%B9%E6%B3%95)
- - [方法重载](#%E6%96%B9%E6%B3%95%E9%87%8D%E8%BD%BD)
- - [方法重写](#%E6%96%B9%E6%B3%95%E9%87%8D%E5%86%99)
- - [初始化顺序](#%E5%88%9D%E5%A7%8B%E5%8C%96%E9%A1%BA%E5%BA%8F)
-- [接口和抽象类](#%E6%8E%A5%E5%8F%A3%E5%92%8C%E6%8A%BD%E8%B1%A1%E7%B1%BB)
- - [抽象类](#%E6%8A%BD%E8%B1%A1%E7%B1%BB)
- - [接口](#%E6%8E%A5%E5%8F%A3)
- - [接口与抽象类区别](#%E6%8E%A5%E5%8F%A3%E4%B8%8E%E6%8A%BD%E8%B1%A1%E7%B1%BB%E5%8C%BA%E5%88%AB)
-- [反射](#%E5%8F%8D%E5%B0%84)
- - [Class类](#class%E7%B1%BB)
- - [Field类](#field%E7%B1%BB)
- - [Method类](#method%E7%B1%BB)
-- [泛型](#%E6%B3%9B%E5%9E%8B)
- - [泛型类](#%E6%B3%9B%E5%9E%8B%E7%B1%BB)
- - [泛型接口](#%E6%B3%9B%E5%9E%8B%E6%8E%A5%E5%8F%A3)
- - [泛型方法](#%E6%B3%9B%E5%9E%8B%E6%96%B9%E6%B3%95)
-- [Exception](#exception)
- - [Throwable](#throwable)
- - [常见的Exception](#%E5%B8%B8%E8%A7%81%E7%9A%84exception)
- - [关键字](#%E5%85%B3%E9%94%AE%E5%AD%97-1)
-- [IO流](#io%E6%B5%81)
- - [InputStream 和 OutputStream](#inputstream-%E5%92%8C-outputstream)
- - [Reader 和 Writer](#reader-%E5%92%8C-writer)
- - [字符流和字节流的转换](#%E5%AD%97%E7%AC%A6%E6%B5%81%E5%92%8C%E5%AD%97%E8%8A%82%E6%B5%81%E7%9A%84%E8%BD%AC%E6%8D%A2)
- - [同步异步](#%E5%90%8C%E6%AD%A5%E5%BC%82%E6%AD%A5)
- - [阻塞非阻塞](#%E9%98%BB%E5%A1%9E%E9%9D%9E%E9%98%BB%E5%A1%9E)
- - [BIO](#bio)
- - [NIO](#nio)
- - [AIO](#aio)
- - [BIO/NIO/AIO区别](#bionioaio%E5%8C%BA%E5%88%AB)
-- [ThreadLocal](#threadlocal)
- - [原理](#%E5%8E%9F%E7%90%86)
- - [内存泄漏](#%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F)
- - [使用场景](#%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF)
-- [线程安全类](#%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E7%B1%BB)
-- [常见操作](#%E5%B8%B8%E8%A7%81%E6%93%8D%E4%BD%9C)
- - [排序](#%E6%8E%92%E5%BA%8F)
- - [数组操作](#%E6%95%B0%E7%BB%84%E6%93%8D%E4%BD%9C)
- - [拷贝](#%E6%8B%B7%E8%B4%9D)
- - [数组拷贝](#%E6%95%B0%E7%BB%84%E6%8B%B7%E8%B4%9D)
- - [对象拷贝](#%E5%AF%B9%E8%B1%A1%E6%8B%B7%E8%B4%9D)
- - [序列化](#%E5%BA%8F%E5%88%97%E5%8C%96)
- - [什么情况下需要序列化?](#%E4%BB%80%E4%B9%88%E6%83%85%E5%86%B5%E4%B8%8B%E9%9C%80%E8%A6%81%E5%BA%8F%E5%88%97%E5%8C%96)
- - [如何实现序列化](#%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E5%BA%8F%E5%88%97%E5%8C%96)
- - [serialVersionUID](#serialversionuid)
- - [遍历](#%E9%81%8D%E5%8E%86)
- - [fast-fail](#fast-fail)
- - [fail-safe](#fail-safe)
- - [移除集合元素](#%E7%A7%BB%E9%99%A4%E9%9B%86%E5%90%88%E5%85%83%E7%B4%A0)
-
-
-
-# Java概述
-
-## Java的特点
-
-**Java是一门面向对象的编程语言。**面向对象和面向过程是一种软件开发思想。
-
-- 面向过程就是分析出解决问题所需要的步骤,然后用函数按这些步骤实现,使用的时候依次调用就可以了。面向对象是把构成问题事务分解成各个对象,分别设计这些对象,然后将他们组装成有完整功能的系统。面向过程只用函数实现,面向对象是用类实现各个功能模块。
-
- 例如五子棋,面向过程的设计思路就是首先分析问题的步骤:
- 1、开始游戏,2、黑子先走,3、绘制画面,4、判断输赢,5、轮到白子,6、绘制画面,7、判断输赢,8、返回步骤2,9、输出最后结果。
- 把上面每个步骤用分别的函数来实现,问题就解决了。
-
-- 而面向对象的设计则是从另外的思路来解决问题。整个五子棋可以分为:
- 1、黑白双方
- 2、棋盘系统,负责绘制画面
- 3、规则系统,负责判定诸如犯规、输赢等。
- 黑白双方负责接受用户的输入,并告知棋盘系统棋子布局发生变化,棋盘系统接收到了棋子的变化的信息就负责在屏幕上面显示出这种变化,同时利用规则系统来对棋局进行判定。
-
-**Java具有平台独立性和移植性。**
-
-- Java有一句口号:Write once, run anywhere,一次编写、到处运行。这也是Java的魅力所在。而实现这种特性的正是Java虚拟机JVM。已编译的Java程序可以在任何带有JVM的平台上运行。你可以在windows平台编写代码,然后拿到linux上运行。只要你在编写完代码后,将代码编译成.class文件,再把class文件打成Java包,这个jar包就可以在不同的平台上运行了。
-
-**Java具有稳健性。**
-
-- Java是一个强类型语言,它允许扩展编译时检查潜在类型不匹配问题的功能。Java要求显式的方法声明,它不支持C风格的隐式声明。这些严格的要求保证编译程序能捕捉调用错误,这就导致更可靠的程序。
-- 异常处理是Java中使得程序更稳健的另一个特征。异常是某种类似于错误的异常条件出现的信号。使用try/catch/finally语句,程序员可以找到出错的处理代码,这就简化了出错处理和恢复的任务。
-
-## JKD和JRE
-
-JDK和JRE是Java开发和运行工具,其中JDK包含了JRE,而JRE是可以独立安装的。
-
-### JDK
-
-Java Development Kit,JAVA语言的软件工具开发包,是整个JAVA开发的核心,它包含了JAVA的运行(JVM+JAVA类库)环境和JAVA工具。
-
-### JRE
-
-JRE(Java Runtime Environment,Java运行环境):包含JVM标准实现及Java核心类库。JRE是Java运行环境,并不是一个开发环境,所以没有包含任何开发工具(如编译器和调试器)。
-
-JRE是运行基于Java语言编写的程序所不可缺少的运行环境。也是通过它,Java的开发者才得以将自己开发的程序发布到用户手中,让用户使用。
-
-# Java基础语法
-
-## 基本数据类型
-
-- byte,8bit
-- char,16bit
-- short,16bit
-- int,32bit
-- float,32bit
-- long,64bit
-- double,64bit
-- boolean,只有两个值:true、false,可以使⽤用 1 bit 来存储,但是具体⼤小没有明确规定。
-
-## 包装类型
-
-基本类型都有对应的包装类型,基本类型与其对应的包装类型之间的赋值通过自动装箱与拆箱完成。
-
-```java
-Integer x = 1; // 装箱 调⽤ Integer.valueOf(1)
-int y = x; // 拆箱 调⽤了 X.intValue()
-```
-
-### 包装类缓存
-
-使用Integer.valueOf(i)生成Integer,如果 -128 <= i <= 127,则直接从cache取对象返回,不会创建新的对象,避免频繁创建包装类对象。
-
-```java
- public static Integer valueOf(int i) {
- if (i >= IntegerCache.low && i <= IntegerCache.high)
- return IntegerCache.cache[i + (-IntegerCache.low)];
- return new Integer(i);
- }
-```
-
-## String
-
-String是final类,不可被继承。
-
-### String拼接
-
-字符串拼接可以使用String用+做拼接,也可以使用StringBuilder和StringBuffer实现,三种方式对比:
-
-- 底层都是char数组实现的
-
-- 字符串拼接性能:StringBuilder > StringBuffer > String
-- String 是字符串常量,一旦创建之后该对象是不可更改的,用+对String做拼接操作,实际上是先通过建立StringBuilder,然后调用append()做拼接操作,所以在大量字符串拼接的时候,会频繁创建StringBuilder,性能较差。
-- StringBuilder和StringBuffer的对象是字符串变量,对变量进行操作就是直接对该对象进行修改(修改char[]数组),所以速度要比String快很多。
-- 在线程安全上,StringBuilder是线程不安全的,而StringBuffer是线程安全的,StringBuffer中很多方法带有synchronized关键字,可以保证线程安全。
-
-## 关键字
-
-### static
-
-static可以用来修饰类的成员方法、类的成员变量。
-
-#### 静态变量
-
-static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。
-
-以下例子,age为非静态变量,则p1打印结果是:`Name:zhangsan, Age:10`;若age使用static修饰,则p1打印结果是:`Name:zhangsan, Age:12`,因为static变量在内存只有一个副本。
-
-```java
-public class Person {
- String name;
- int age;
-
- public String toString() {
- return "Name:" + name + ", Age:" + age;
- }
-
- public static void main(String[] args) {
- Person p1 = new Person();
- p1.name = "zhangsan";
- p1.age = 10;
- Person p2 = new Person();
- p2.name = "lisi";
- p2.age = 12;
- System.out.println(p1);
- System.out.println(p2);
- }
- /**Output
- * Name:zhangsan, Age:10
- * Name:lisi, Age:12
- *///~
-}
-```
-
-#### 静态方法
-
-static方法一般称作静态方法。静态方法不依赖于任何对象就可以进行访问,通过类名即可调用静态方法。
-
-```java
-public class Utils {
- public static void print(String s) {
- System.out.println("hello world: " + s);
- }
-
- public static void main(String[] args) {
- Utils.print("程序员大彬");
- }
-}
-```
-
-#### 静态代码块
-
-静态代码块只会在类加载的时候执行一次。以下例子,startDate和endDate在类加载的时候进行赋值。
-
-```java
-class Person {
- private Date birthDate;
- private static Date startDate, endDate;
- static{
- startDate = Date.valueOf("2008");
- endDate = Date.valueOf("2021");
- }
-
- public Person(Date birthDate) {
- this.birthDate = birthDate;
- }
-}
-```
-
-#### 静态内部类
-
-**在静态方法里**,使用⾮静态内部类依赖于外部类的实例,也就是说需要先创建外部类实例,才能用这个实例去创建非静态内部类。⽽静态内部类不需要。
-
-```java
-public class OuterClass {
- class InnerClass {
- }
- static class StaticInnerClass {
- }
- public static void main(String[] args) {
- // 在静态方法里,不能直接使用OuterClass.this去创建InnerClass的实例
- // 需要先创建OuterClass的实例o,然后通过o创建InnerClass的实例
- // InnerClass innerClass = new InnerClass();
- OuterClass outerClass = new OuterClass();
- InnerClass innerClass = outerClass.new InnerClass();
- StaticInnerClass staticInnerClass = new StaticInnerClass();
-
- outerClass.test();
- }
-
- public void nonStaticMethod() {
- InnerClass innerClass = new InnerClass();
- System.out.println("nonStaticMethod...");
- }
-}
-```
-
-
-
-### final
-
-1. **基本数据**类型用final修饰,则不能修改,是常量;**对象引用**用final修饰,则引用只能指向该对象,不能指向别的对象,但是对象本身可以修改。
-
-2. final修饰的方法不能被子类重写
-
-3. final修饰的类不能被继承。
-
-
-
-### this
-
- `this.属性名称`指访问类中的成员变量,可以用来区分成员变量和局部变量。如下代码所示,`this.name`访问类Person当前实例的变量。
-
-```java
-/**
- * @description:
- * @author: 程序员大彬
- * @time: 2021-08-17 00:29
- */
-public class Person {
- String name;
- int age;
-
- public Person(String name, int age) {
- this.name = name;
- this.age = age;
- }
-}
-```
-
-`this.方法名称`用来访问本类的方法。以下代码中,`this.born()`调用类 Person 的当前实例的方法。
-
-```java
-/**
- * @description:
- * @author: 程序员大彬
- * @time: 2021-08-17 00:29
- */
-public class Person {
- String name;
- int age;
-
- public Person(String name, int age) {
- this.born();
- this.name = name;
- this.age = age;
- }
-
- void born() {
- }
-}
-```
-
-
-
-### super
-
-super 关键字用于在子类中访问父类的变量和方法。
-
-```java
-class A {
- protected String name = "大彬";
-
- public void getName() {
- System.out.println("父类:" + name);
- }
-}
-
-public class B extends A {
- @Override
- public void getName() {
- System.out.println(super.name);
- super.getName();
- }
-
- public static void main(String[] args) {
- B b = new B();
- b.getName();
- }
- /**
- * 大彬
- * 父类:大彬
- */
-}
-```
-
-在子类B中,我们重写了父类的getName()方法,如果在重写的getName()方法中我们要调用父类的相同方法,必须要通过super关键字显式指出。
-
-## object常用方法
-
-Java面试经常会出现的一道题目,Object的常用方法。下面给大家整理一下。
-
-object常用方法有:toString()、equals()、hashCode()、clone()等。
-
-### toString
-
-默认输出对象地址。
-
-```java
-public class Person {
- private int age;
- private String name;
-
- public Person(int age, String name) {
- this.age = age;
- this.name = name;
- }
-
- public static void main(String[] args) {
- System.out.println(new Person(18, "程序员大彬").toString());
- }
- //output
- //me.tyson.java.core.Person@4554617c
-}
-```
-
-可以重写toString方法,按照重写逻辑输出对象值。
-
-```java
-public class Person {
- private int age;
- private String name;
-
- public Person(int age, String name) {
- this.age = age;
- this.name = name;
- }
-
- @Override
- public String toString() {
- return name + ":" + age;
- }
-
- public static void main(String[] args) {
- System.out.println(new Person(18, "程序员大彬").toString());
- }
- //output
- //程序员大彬:18
-}
-```
-
-### equals
-
-默认比较两个引用变量是否指向同一个对象(内存地址)。
-
-```java
-public class Person {
- private int age;
- private String name;
-
- public Person(int age, String name) {
- this.age = age;
- this.name = name;
- }
-
- public static void main(String[] args) {
- String name = "程序员大彬";
- Person p1 = new Person(18, name);
- Person p2 = new Person(18, name);
-
- System.out.println(p1.equals(p2));
- }
- //output
- //false
-}
-```
-
-可以重写equals方法,按照age和name是否相等来判断:
-
-```java
-public class Person {
- private int age;
- private String name;
-
- public Person(int age, String name) {
- this.age = age;
- this.name = name;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof Person) {
- Person p = (Person) o;
- return age == p.age && name.equals(p.name);
- }
- return false;
- }
-
- public static void main(String[] args) {
- String name = "程序员大彬";
- Person p1 = new Person(18, name);
- Person p2 = new Person(18, name);
-
- System.out.println(p1.equals(p2));
- }
- //output
- //true
-}
-```
-
-### hashCode
-
-将与对象相关的信息映射成一个哈希值,默认的实现hashCode值是根据内存地址换算出来。
-
-```java
-public class Cat {
- public static void main(String[] args) {
- System.out.println(new Cat().hashCode());
- }
- //out
- //1349277854
-}
-```
-
-### clone
-
-java赋值是复制对象引用,如果我们想要得到一个对象的副本,使用赋值操作是无法达到目的的。Object对象有个clone()方法,实现了对
-
-象中各个属性的复制,但它的可见范围是protected的。
-
-```java
-protected native Object clone() throws CloneNotSupportedException;
-```
-
-所以实体类使用克隆的前提是:
-
-- 实现Cloneable接口,这是一个标记接口,自身没有方法,这应该是一种约定。调用clone方法时,会判断有没有实现Cloneable接口,没有实现Cloneable的话会抛异常CloneNotSupportedException。
-- 覆盖clone()方法,可见性提升为public。
-
-```java
-public class Cat implements Cloneable {
- private String name;
-
- @Override
- protected Object clone() throws CloneNotSupportedException {
- return super.clone();
- }
-
- public static void main(String[] args) throws CloneNotSupportedException {
- Cat c = new Cat();
- c.name = "程序员大彬";
- Cat cloneCat = (Cat) c.clone();
- c.name = "大彬";
- System.out.println(cloneCat.name);
- }
- //output
- //程序员大彬
-}
-```
-
-#### 浅拷贝
-
-拷⻉对象和原始对象的引⽤类型引用同⼀个对象。
-
-以下例子,Cat对象里面有个Person对象,调用clone之后,克隆对象和原对象的Person引用的是同一个对象,这就是浅拷贝。
-
-```java
-public class Cat implements Cloneable {
- private String name;
- private Person owner;
-
- @Override
- protected Object clone() throws CloneNotSupportedException {
- return super.clone();
- }
-
- public static void main(String[] args) throws CloneNotSupportedException {
- Cat c = new Cat();
- Person p = new Person(18, "程序员大彬");
- c.owner = p;
-
- Cat cloneCat = (Cat) c.clone();
- p.setName("大彬");
- System.out.println(cloneCat.owner.getName());
- }
- //output
- //大彬
-}
-```
-
-#### 深拷贝
-
-拷贝对象和原始对象的引用类型引用不同的对象。
-
-以下例子,在clone函数中不仅调用了super.clone,而且调用Person对象的clone方法(Person也要实现Cloneable接口并重写clone方法),从而实现了深拷贝。可以看到,拷贝对象的值不会受到原对象的影响。
-
-```java
-public class Cat implements Cloneable {
- private String name;
- private Person owner;
-
- @Override
- protected Object clone() throws CloneNotSupportedException {
- Cat c = null;
- c = (Cat) super.clone();
- c.owner = (Person) owner.clone();//拷贝Person对象
- return c;
- }
-
- public static void main(String[] args) throws CloneNotSupportedException {
- Cat c = new Cat();
- Person p = new Person(18, "程序员大彬");
- c.owner = p;
-
- Cat cloneCat = (Cat) c.clone();
- p.setName("大彬");
- System.out.println(cloneCat.owner.getName());
- }
- //output
- //程序员大彬
-}
-```
-
-### getClass
-
-返回此 Object 的运行时类,常用于java反射机制。
-
-```java
-public class Person {
- private String name;
-
- public Person(String name) {
- this.name = name;
- }
-
- public static void main(String[] args) {
- Person p = new Person("程序员大彬");
- Class clz = p.getClass();
- System.out.println(clz);
- //获取类名
- System.out.println(clz.getName());
- }
- /**
- * class com.tyson.basic.Person
- * com.tyson.basic.Person
- */
-}
-```
-
-### wait
-
-当前线程调用对象的wait()方法之后,当前线程会释放对象锁,进入等待状态。等待其他线程调用此对象的notify()/notifyAll()唤醒或者等待超时时间wait(long timeout)自动唤醒。线程需要获取obj对象锁之后才能调用 obj.wait()。
-
-### notity
-
-obj.notify()唤醒在此对象上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象上等待的所有线程。
-
-> LockSupport.park()/LockSupport.parkNanos(long nanos)/LockSupport.parkUntil(long deadlines),用于阻塞当前线程。对比obj.wait()方法,不需要获得锁就可以让线程进入等待状态,需要通过LockSupport.unpark(Thread thread)唤醒。
-
-### equals()和hashcode()的关系
-
-equals与hashcode的关系:
-1、如果两个对象调用equals比较返回true,那么它们的hashCode值一定要相同;
-2、如果两个对象的hashCode相同,它们并不一定相同。
-
-hashcode方法主要是用来提升对象比较的效率,先进行hashcode()的比较,如果不相同,那就不必在进行equals的比较,这样就大大减少了equals比较的次数,当比较对象的数量很大的时候能提升效率。
-
-之所以重写equals()要重写hashcode(),是为了保证equals()方法返回true的情况下hashcode值也要一致,如果重写了equals()没有重写hashcode(),就会出现两个对象相等但hashcode()不相等的情况。这样,当用其中的一个对象作为键保存到hashMap、hashTable或hashSet中,再以另一个对象作为键值去查找他们的时候,则会查找不到。
-
-### ==和equals区别
-
-- 对于基本数据类型,==比较的是他们的值。基本数据类型没有equal方法;
-
-- 对于复合数据类型,==比较的是它们的存放地址(是否是同一个对象)。equals()默认比较地址值,重写的话按照重写逻辑去比较。
-
-# 面向对象
-
-## 面向对象特性
-
-面向对象四大特性:封装,继承,多态,抽象
-
-- 封装就是将类的信息隐藏在类内部,不允许外部程序直接访问,而是通过该类的方法实现对隐藏信息的操作和访问。 良好的封装能够减少耦合。
-- 继承是从已有的类中派生出新的类,新的类继承父类的属性和行为,并能扩展新的能力,大大增加程序的重用性和易维护性。在Java中是单继承的,也就是说一个子类只有一个父类。
-- 多态是同一个行为具有多个不同表现形式的能力。在不修改程序代码的情况下改变程序运行时绑定的代码。
- 实现多态的三要素:继承、重写、父类引用指向子类对象。
- 静态多态性:通过重载实现,相同的方法有不同的參数列表,可以根据参数的不同,做出不同的处理。
- 动态多态性:在子类中重写父类的方法。运行期间判断所引用对象的实际类型,根据其实际类型调用相应的方法。
-- 抽象。把客观事物用代码抽象出来。
-
-### 多态怎么实现
-
-Java提供了编译时多态和运行时多态两种多态机制。编译时多态通过重载实现,根据传入参数不同调用不同的方法。运行时多态通过重写来实现,在子类中重写父类的方法,运行期间判断所引用对象的实际类型,根据其实际类型调用相应的方法。
-
-## 类与对象
-
-**类class**对一类事物的描述,是抽象的、概念上的定义。类的结构包括属性和方法。
-
-```java
-public class Person {
- //属性
- private int age;
- private String name;
-
- //构造方法
- public Person(int age, String name) {
- this.age = age;
- this.name = name;
- }
-}
-```
-
-对象是实际存在的该类事物的个体,也称为实例。
-
-```java
-public class Person {
- private int age;
- private String name;
-
- public Person(int age, String name) {
- this.age = age;
- this.name = name;
- }
-
- public static void main(String[] args) {
- //p为对象
- Person p = new Person(18, "程序员大彬");
- }
-}
-```
-
-在Java中,万物皆对象,一切事物都可以看做对象。上述代码通过new关键字,创建了Person对象,p为对象的引用,p指向所创建的Person对象的内存地址。
-
-## 属性
-
-属性用来描述对象的状态信息,通常以变量的形式进行定义。变量分为成员变量和局部变量。
-
-在类中,方法体之外定义的变量称为成员变量:
-
-- 成员变量定义在类中,在整个类中都可以被访问
-- 成员变量分为类变量和实例变量,实例变量存在于每个对象所在的堆内存中。实例变量要创建对象后才能访问。
-- 成员变量有默认初始化值
-- 成员变量的权限修饰符可以指定
-
-定义在方法内,代码块内的变量称为局部变量:
-
-- 局部变量存在于栈内存中
-- 局部变量作用范围结束,变量的内存空间回自动释放
-- 局部变量没有默认值,每次必须显示初始化
-- 局部变量声明时不指定权限修饰符
-
-## 方法
-
-描述的是对象的动作信息,方法也称为函数。
-
-### 普通方法
-
-普通通方法的语法结构:
-
-```java
-[修饰符列表] 返回值类型 方法名(形参列表){
- //方法体
-}
-```
-
-定义方法可以将功能代码进行封装。便于该功能进行复用。方法只有被调用才会被执行。
-
-### 构造方法
-
-构造方法是一种比较特殊的方法,通过构造方法可以创建对象以及初始化实例变量。实例变量没有手动赋值的时候,系统会赋默认值。
-
-```java
-public class Person {
- //属性
- private int age;
- private String name;
-
- //构造方法
- public Person(int age, String name) {
- this.age = age;
- this.name = name;
- }
-}
-```
-
-## 方法重载
-
-同个类中的多个方法可以有相同的方法名称,但是有不同的参数列表,这就称为方法重载。参数列表又叫参数签名,包括参数的类型、参数的个数、参数的顺序,只要有一个不同就叫做参数列表不同。
-
-重载是面向对象的一个基本特性。
-
-```java
-public class OverrideTest {
- void setPerson() { }
-
- void setPerson(String name) {
- //set name
- }
-
- void setPerson(String name, int age) {
- //set name and age
- }
-}
-```
-
-## 方法重写
-
-方法的重写描述的是父类和子类之间的。当父类的功能无法满足子类的需求,可以在子类对方法进行重写。
-
-方法重写时, 方法名与形参列表必须一致。
-
-如下代码,Person为父类,Student为子类,在Student中重写了dailyTask方法。
-
-```java
-public class Person {
- private String name;
-
- public void dailyTask() {
- System.out.println("work eat sleep");
- }
-}
-
-
-public class Student extends Person {
- @Override
- public void dailyTask() {
- System.out.println("study eat sleep");
- }
-}
-```
-
-## 初始化顺序
-
-Java中类初始化顺序:
-
-1. 静态属性,静态代码块。
-2. 普通属性,普通代码块。
-3. 构造方法。
-
-```java
-public class LifeCycle {
- // 静态属性
- private static String staticField = getStaticField();
-
- // 静态代码块
- static {
- System.out.println(staticField);
- System.out.println("静态代码块初始化");
- }
-
- // 普通属性
- private String field = getField();
-
- // 普通代码块
- {
- System.out.println(field);
- System.out.println("普通代码块初始化");
- }
-
- // 构造方法
- public LifeCycle() {
- System.out.println("构造方法初始化");
- }
-
- // 静态方法
- public static String getStaticField() {
- String statiFiled = "静态属性初始化";
- return statiFiled;
- }
-
- // 普通方法
- public String getField() {
- String filed = "普通属性初始化";
- return filed;
- }
-
- public static void main(String[] argc) {
- new LifeCycle();
- }
-
- /**
- * 静态属性初始化
- * 静态代码块初始化
- * 普通属性初始化
- * 普通代码块初始化
- * 构造方法初始化
- */
-}
-```
-
-# 接口和抽象类
-
-Java中接口和抽象类的定义语法分别为interface与abstract关键字。
-
-## 抽象类
-
-首先了解一下抽象方法。抽象方法是一种特殊的方法:它只有声明,而没有具体的实现。抽象方法的格式为:
-
-```java
-abstract void method();
-```
-
-抽象方法必须用abstract关键字进行修饰。如果一个类含有抽象方法,则称这个类为抽象类,抽象类必须在类前用abstract关键字修饰。因为抽象类中含有未实现的方法,所以不能用抽象类创建对象。
-
-抽象类的特点:
-
-- 抽象类不能被实例化只能被继承;
-- 包含抽象方法的一定是抽象类,但是抽象类不一定含有抽象方法;
-- 抽象类中的抽象方法的修饰符只能为public或者protected,默认为public。
-
-## 接口
-
-接口对行为的抽象。在Java中,定一个接口的形式如下:
-
-```java
-public interface InterfaceName {
-}
-```
-
-接口中可以含有变量和方法。
-
-**接口的特点:**
-
-- 接口中的变量会被隐式指定为public static final类型,而方法会被隐式地指定为public abstract类型;
-- 接口中的方法必须是抽象方法,所有的方法不能有具体的实现;
-- 一个类可以实现多个接口。
-
-**接口的用处**
-
-接口是对行为的抽象,通过接口可以实现不相关类的相同行为。通过接口可以知道一个类具有哪些行为特性。
-
-## 接口与抽象类区别
-
-- 语法层面上
- 1)抽象类可以有方法实现,而接口的方法中只能是抽象方法;
- 2)抽象类中的成员变量可以是各种类型的,接口中的成员变量只能是public static final类型;
- 3)接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;
- 4)一个类只能继承一个抽象类,而一个类却可以实现多个接口。
-
-- 设计层面上的区别
- 1)抽象层次不同。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口只是对类行为进行抽象。继承抽象类是一种"是不是"的关系,而接口实现则是 "有没有"的关系。如果一个类继承了某个抽象类,则子类必定是抽象类的种类,而接口实现则是具备不具备的关系,比如鸟是否能飞。
- 2) 继承抽象类的是具有相似特点的类,而实现接口的却可以不同的类。
-
- 门和警报的例子:
-
- ```java
- class AlarmDoor extends Door implements Alarm {
- //code
- }
-
- class BMWCar extends Car implements Alarm {
- //code
- }
- ```
-
-# 反射
-
-Java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,并且能改变它的属性。
-
-作用:可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;还有动态代理的实现等。需要注意的是反射使用不当会造成很高的资源消耗!
-
-## Class类
-
-在Java中,每定义一个Java class实体都会产生一个Class对象。这个Class对象用于表示这个类的类型信息。
-
-获取Class对象的三种方式:
-
-```java
-//1、通过对象调用 getClass() 方法来获取,通常应用在:比如你传过来一个 Object
-// 类型的对象,而我不知道你具体是什么类,用这种方法
- Person p1 = new Person();
- Class c1 = p1.getClass();
-
-//2、直接通过 类名.class 的方式得到,该方法最为安全可靠,程序性能更高
-// 这说明任何一个类都有一个隐含的静态成员变量 class
- Class c2 = Person.class;
-
-//3、通过 Class 对象的 forName() 静态方法来获取,用的最多,
-// 但可能抛出 ClassNotFoundException 异常
- Class c3 = Class.forName("com.ys.reflex.Person");
-```
-
-Class 类提供了一些方法,可以获取成员变量、成员方法、接口、超类、构造方法:
-
-```java
- getName():获得类的完整名字。
- getFields():获得类的public类型的属性。
- getDeclaredFields():获得类的所有属性。包括private 声明的和继承类
- getMethods():获得类的public类型的方法。
- getDeclaredMethods():获得类的所有方法。包括private 声明的和继承类
- getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。
- getConstructors():获得类的public类型的构造方法。
- getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。
- newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
-```
-
-## Field类
-
-Field提供了类和接口中字段的信息,通过Field类可以动态访问这些字段。下图是Field类提供的一些方法。
-
-
-
-## Method类
-
-Method类位于 java.lang.reflect 包中,主要用于在程序运行状态中,动态地获取方法信息。
-
-Method类常用的方法:
-
-1. getAnnotation(Class annotationClass):如果该方法对象存在指定类型的注解,则返回该注解,否则返回null。
-2. getName():返回方法对象名称。
-3. isAnnotationPresent(Class extends Annotation> annotationClass):如果该方法对象上有指定类型的注解,则返回true,否则为false。
-4. getDeclaringClass ():返回该方法对象表示的方法所在类的Class对象。
-5. getParameters():返回一个参数对象数组,该数组表示该方法对象的所有参数。
-6. getReturnType():返回一个Class对象,该Class对象表示该方法对象的返回对象,会擦除泛型。
-
-# 泛型
-
-泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。编译时会进行类型擦除。在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
-
-## 泛型类
-
-泛型类型用于类的定义中,被称为泛型类。通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种容器类,如:List、Set、Map。
-
-泛型类示例:
-
-```java
-//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
-//在实例化泛型类时,必须指定T的具体类型
-public class Generic{
- //key这个成员变量的类型为T,T的类型由外部指定
- private T key;
-
- public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定
- this.key = key;
- }
-
- public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定
- return key;
- }
-}
-```
-
-泛型类的使用:
-
-```java
-@Slf4j
-public class GenericTest {
- public static void main(String[] args) {
- //泛型的类型参数只能是类类型(包括自定义类),不能是简单类型
- //传入的实参类型需与泛型的类型参数类型相同,即为Integer.
- Generic genericInteger = new Generic(666);
-
- //传入的实参类型需与泛型的类型参数类型相同,即为String.
- Generic genericString = new Generic("程序员大彬");
- log.info("泛型测试: key is {}", genericInteger.getKey());
- log.info("泛型测试: key is {}", genericString.getKey());
- }
-
- /**
- * output
- * 23:51:55.519 [main] INFO com.tyson.generic.GenericTest - 泛型测试: key is 666
- * 23:51:55.526 [main] INFO com.tyson.generic.GenericTest - 泛型测试: key is 程序员大彬
- */
-}
-```
-
-## 泛型接口
-
-泛型接口与泛型类的定义及使用基本相同。
-
-```java
-//定义一个泛型接口
-public interface Generator {
- public T next();
-}
-```
-
-## 泛型方法
-
-泛型类,是在实例化类的时候指明泛型的具体类型;泛型方法,是在调用方法的时候指明泛型的具体类型 。
-
-```java
-@Slf4j
-public class GenericMethod {
-
- /**
- * 泛型方法的基本介绍
- * @param t 传入的泛型实参
- * @return T 返回值为T类型
- * 说明:
- * 1)public 与 返回值中间非常重要,可以理解为声明此方法为泛型方法。
- * 2)只有声明了的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
- * 3)表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
- * 4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。
- */
- public void genericMethod(T t) {
- log.info(t.toString());
- }
-
- public static void main(String[] args) {
- GenericMethod genericMethod = new GenericMethod();
- genericMethod.genericMethod("程序员大彬");
- genericMethod.genericMethod(666);
- }
-
- /**
- * output
- * 23:59:11.906 [main] INFO com.tyson.generic.GenericMethod - 程序员大彬
- * 23:59:11.912 [main] INFO com.tyson.generic.GenericMethod - 666
- */
-}
-```
-
-
-
-# Exception
-
-在 JAVA 语言中,将程序执行中发生的不正常情况称为异常。异常是程序经常会出现的情况,发现错误的最佳时机是在编译阶段,也就是运行程序之前。
-
-所有异常都继承了 Throwable。
-
-## Throwable
-
-Throwable类是Error和Exception的父类,只有继承于Throwable的类或者其子类才能被抛出。Throwable分为两类:
-
-
-
-- Error:JVM 无法解决的严重问题,如栈溢出(StackOverflowError)、内存溢出(OOM)等。程序无法处理的错误。
-
- 栈溢出:如下代码递归调用main,最终会抛出StackOverflowError。
-
- ```java
- public class Test {
- public static void main(String[] args) {
- main(args);
- }
- /**
- * Exception in thread "main" java.lang.StackOverflowError
- */
- }
- ```
-
- 内存溢出OOM:下面的代码中,新建了一个数组,数组长度是 1G,又因为是 Integer 包装类型,一个元素占 4 个字节,所以,这个数组占用内存 4GB。最后,堆内存空间不足,抛出了 OOM。
-
- ```java
- public class Test {
- public static void main(String[] args) {
- Integer[] arr = new Integer[1024 * 1024 * 1024];
- }
- /**
- * Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
- */
- }
- ```
-
-- Exception:其它因编程错误或偶然的外在因素导致的一般性问题。可以在代码中进行处理。如:空指针异常、数组下标越界等。
-
-unchecked exception包括RuntimeException和Error类,其他所有异常称为检查(checked)异常。
-
-运行时异常和非运行时异常(checked)的区别:
-
-1. RuntimeException由程序错误导致,应该修正程序避免这类异常发生。
-2. checked Exception由具体的环境(读取的文件不存在或文件为空或sql异常)导致的异常。必须进行处理,不然编译不通过,可以catch或者throws。
-
-## 常见的Exception
-
-常见的RuntimeException:
-
-```java
-ClassCastException //类型转换异常
-IndexOutOfBoundsException //数组越界异常
-NullPointerException //空指针
-ArrayStoreException //数组存储异常
-NumberFormatException //数字格式化异常
-ArithmeticException //数学运算异常
-```
-
-unchecked Exception:
-
-```
-NoSuchFieldException //反射异常,没有对应的字段
-ClassNotFoundException //类没有找到异常
-IllegalAccessException //安全权限异常,可能是反射时调用了private方法
-```
-
-## 关键字
-
-- **throw**:用于抛出一个具体的异常对象。
-
-- **throws**:用在方法签名中,用于声明该方法可能抛出的异常。子类方法抛出的异常范围更加小,或者根本不抛异常。
-
-- **try**:用于监听。将可能抛出异常的代码放在try语句块之中,当try语句块内发生异常时,异常就被抛出。
-- **catch**:用于捕获异常。
-- **finally**:finally语句块总是会被执行。它主要用于回收在try块里打开的资源(如数据库连接、网络连接和磁盘文件)。
-
-如下代码示例,除以0抛出异常,发生异常之后的代码不会执行,直接跳到catch语句块执行,最后执行finally语句块。
-
-```java
-public class ExceptionTest {
- public static void main(String[] args) {
- try {
- int i = 1 / 0;
- System.out.println(i + 1);
- } catch (Exception e) {
- System.out.println(e.getMessage());
- } finally {
- System.out.println("run finally...");
- }
- }
- /**
- * / by zero
- * run finally...
- */
-}
-```
-
-
-
-# IO流
-
-Java IO流的核心就是对文件的操作,对于字节 、字符类型的输入和输出流。IO流主要分为两大类,字节流和字符流。字节流可以处理任何类型的数据,如图片,视频等,字符流只能处理字符类型的数据。
-
-
-
-> 图片参考:[Java io学习整理](https://zhuanlan.zhihu.com/p/25418336)
-
-## InputStream 和 OutputStream
-
-InputStream 用来表示那些从不同数据源产生输入的类。这些数据源包括:1.字节数组;2.String 对象;3.文件;4.管道;5.一个由其他种类的流组成的序列。
-
-InputStream 类有一个抽象方法:`abstract int read()`,这个方法将读入并返回一个字节,或者在遇到输入源结尾时返回-1。
-
-OutputStream 决定了输出所要去的目标:字节数组、文件或管道。OutputStream 的 `abstract void write(int b)` 可以向某个输出位置写出一个字节。
-
-read() 和 write() 方法在执行时都将阻塞,等待数据被读入或者写出。
-
-常用的字节流有FileInputStream、FileOutputStream、ObjectInputStream、ObjectOutputStream。
-
-## Reader 和 Writer
-
-字符流是由通过字节流转换得到的,转化过程耗时,而且容易出现乱码问题。I/O 流提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。如果音频文件、图片等媒体文件用字节流比较好,如果涉及到字符的话使用字符流比较好。
-
-```java
-abstract int read();
-abstract void write(char c);
-```
-
-## 字符流和字节流的转换
-
-InputStreamReader:字节到字符的转换,可对读取到的字节数据经过指定编码转换成字符。
-
-OutputStreamWriter:字符到字节的转换,可对读取到的字符数据经过指定编码转换成字节。
-
-## 同步异步
-
-同步:发出一个调用时,在没有得到结果之前,该调用就不返回。
-
-异步:在调用发出后,被调用者返回结果之后会通知调用者,或通过回调函数处理这个调用。
-
-## 阻塞非阻塞
-
-阻塞和非阻塞关注的是线程的状态。
-
-阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会恢复运行。
-
-非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。
-
-> 举个例子,理解下同步、阻塞、异步、非阻塞的区别:
->
-> 同步就是烧开水,要自己来看开没开;异步就是水开了,然后水壶响了通知你水开了(回调通知)。阻塞是烧开水的过程中,你不能干其他事情,必须在旁边等着;非阻塞是烧开水的过程里可以干其他事情。
-
-## BIO
-
-同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。
-
-
-
-## NIO
-
-NIO是一种同步非阻塞的I/O模型,在Java 1.4 中引入了 NIO 框架,对应 java.nio 包,提供了 Channel , Selector,Buffer等抽象。
-
-
-
-NIO与IO区别:
-
-- IO是面向流的,NIO是面向缓冲区的;
-- IO流是阻塞的,NIO流是不阻塞的;
-- NIO有选择器,而IO没有。
-
-Buffer:Buffer用于和Channel交互。从Channel中读取数据到Buffer里,从Buffer把数据写入到Channel。
-
-Channel:NIO 通过Channel(通道) 进行读写。通道是双向的,可读也可写,而流的读写是单向的。无论读写,通道只能和Buffer交互。
-
-Selector:使用更少的线程来就可以来处理通道了,相比使用多个线程,避免了线程上下文切换带来的开销。
-
-## AIO
-
-异步非阻塞 IO。异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。
-
-## BIO/NIO/AIO区别
-
-同步阻塞IO : 用户进程发起一个IO操作以后,必须等待IO操作的真正完成后,才能继续运行。
-
-同步非阻塞IO: 客户端与服务器通过Channel连接,采用多路复用器轮询注册的Channel。提高吞吐量和可靠性。用户进程发起一个IO操作以后,可做其它事情,但用户进程需要轮询IO操作是否完成,这样造成不必要的CPU资源浪费。
-
-异步非阻塞IO: 非阻塞异步通信模式,NIO的升级版,采用异步通道实现异步通信,其read和write方法均是异步方法。用户进程发起一个IO操作,然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知。类比Future模式。
-
-
-
-# ThreadLocal
-线程本地变量。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程。
-
-## 原理
-
-每个线程都有一个ThreadLocalMap(ThreadLocal内部类),Map中元素的键为ThreadLocal,而值对应线程的变量副本。
-
-
-
-调用threadLocal.set()-->调用getMap(Thread)-->返回当前线程的ThreadLocalMap-->map.set(this, value),this是ThreadLocal
-
-```java
-public void set(T value) {
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null)
- map.set(this, value);
- else
- createMap(t, value);
-}
-
-ThreadLocalMap getMap(Thread t) {
- return t.threadLocals;
-}
-
-void createMap(Thread t, T firstValue) {
- t.threadLocals = new ThreadLocalMap(this, firstValue);
-}
-```
-调用get()-->调用getMap(Thread)-->返回当前线程的ThreadLocalMap-->map.getEntry(this),返回value
-
-```java
- public T get() {
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null) {
- ThreadLocalMap.Entry e = map.getEntry(this);
- if (e != null) {
- @SuppressWarnings("unchecked")
- T result = (T)e.value;
- return result;
- }
- }
- return setInitialValue();
- }
-```
-
-threadLocals的类型ThreadLocalMap的键为ThreadLocal对象,因为每个线程中可有多个threadLocal变量,如longLocal和stringLocal。
-
-```
-public class ThreadLocalDemo {
- ThreadLocal longLocal = new ThreadLocal<>();
-
- public void set() {
- longLocal.set(Thread.currentThread().getId());
- }
- public Long get() {
- return longLocal.get();
- }
-
- public static void main(String[] args) throws InterruptedException {
- ThreadLocalDemo threadLocalDemo = new ThreadLocalDemo();
- threadLocalDemo.set();
- System.out.println(threadLocalDemo.get());
-
- Thread thread = new Thread(() -> {
- threadLocalDemo.set();
- System.out.println(threadLocalDemo.get());
- }
- );
-
- thread.start();
- thread.join();
-
- System.out.println(threadLocalDemo.get());
- }
-}
-```
-ThreadLocal 并不是用来解决共享资源的多线程访问的问题,因为每个线程中的资源只是副本,并不共享。因此ThreadLocal适合作为线程上下文变量,简化线程内传参。
-
-## 内存泄漏
-
-每个Thread都有⼀个ThreadLocalMap的内部属性,map的key是ThreaLocal,定义为弱引用,value是强引用类型。GC的时候会⾃动回收key,而value的回收取决于Thread对象的生命周期。一般会通过线程池的方式复用Thread对象节省资源,这也就导致了Thread对象的生命周期比较长,这样便一直存在一条强引用链的关系:Thread --> ThreadLocalMap-->Entry-->Value,随着任务的执行,value就有可能越来越多且无法释放,最终导致内存泄漏。
-
-
-
-解决⽅法:每次使⽤完ThreadLocal就调⽤它的remove()⽅法,手动将对应的键值对删除,从⽽避免内存泄漏。
-
-```java
-currentTime.set(System.currentTimeMillis());
-result = joinPoint.proceed();
-Log log = new Log("INFO",System.currentTimeMillis() - currentTime.get());
-currentTime.remove();
-```
-
-## 使用场景
-
-ThreadLocal 适用场景:每个线程需要有自己单独的实例,且需要在多个方法中共享实例,即同时满足实例在线程间的隔离与方法间的共享。比如Java web应用中,每个线程有自己单独的 Session 实例,就可以使用ThreadLocal来实现。
-
-
-
-# 线程安全类
-
-线程安全:代码段在多线程下执行和在单线程下执行能获得一样的结果。
-线程安全类:线程安全的类其方法是同步的,每次只能有一个线程访问,效率较低。
-- vector:比arraylist多了个同步化机制,效率较低
-- stack:堆栈类,继承自vector
-- hashtable:hashtable不允许插入空值,hashmap允许
-- enumeration:枚举,相当于迭代器
-- StringBuffer
-
-Iterator和Enumeration的重要区别:
-- Enumeration为vector/hashtable等类提供遍历接口,Iterator为ArrayList/HashMap提供遍历接口。
-- Enumeration只能读集合中的数据,不能删除。
-- Enumeration是先进后出,而Iterator是先进先出。
-- Enumeration不支持fast-fail机制,不会抛ConcurrentModificationException。
-
-
-
-# 常见操作
-
-## 排序
-
-数组
-
-```java
-Arrays.sort(jdArray, (int[] jd1, int[] jd2) -> {return jd1[0] - jd2[0];});
-```
-
-
-
-## 数组操作
-
-数组遍历
-
-```java
-Arrays.asList(array).stream().forEach(System.out::println);
-```
-
-数组排序
-
-```java
-Arrays.sort(players,(String s1,String s2)->(s1.compareTo(s2)));
-```
-
-集合转数组:
-
-```java
-//List --> Array
-List list = new ArrayList<>();
-list.add(1);
-Integer[] arr = list.toArray(new Integer[list.size()]);
-```
-
-数组转集合:
-
-```java
-//Array --> List
-String[] array = {"java", "c"};
-List list = Arrays.asList(array);
-```
-
-该方法存在一定的弊端,返回的list是Arrays里面的一个静态内部类(数组的视图),对list的操作会反映在原数组上,而且list是定长的,不支持add、remove操作。
-
-该ArrayList并非java.util.ArrayList,而是 java.util.Arrays.ArrayList.ArrayList(T[]),该类并未实现add、remove方法,因此在使用时存在局限性。
-
-代替方案:
-
-```java
-List list = new ArrayList(Arrays.asList(array));
-```
-
-
-
-## 拷贝
-
-### 数组拷贝
-
-```java
-System.arraycopy(Object src, int srcPos, Object dest, int desPos, int length)
-Arrays.copyOf(originalArr, length) //length为拷贝的长度
-Arrays.copyOfRange(originalArr, from, to); //from包含,to不包含
-```
-
-二维数组拷贝:
-
-```java
-int[][] arr = {{1, 2},{3, 4}};
-int[][] newArr = new int[2][2];
-for(int i = 0; i < arr.length; i++) {
- newArr[i] = arr[i].clone();
-}
-```
-
-### 对象拷贝
-
-实现对象克隆有两种方式:
-
-1. 实现Cloneable接口并重写Object类中的clone()方法;
-
-2. 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。
-
-实现cloneable接口,重写clone方法。
-
-```java
-public class Dog implements Cloneable {
- private String id;
- private String name;
-
- public Dog(String id, String name) {
- this.id = id;
- this.name = name;
- }
-
- // 省略 getter 、 setter 以及 toString 方法
-
- @Override
- public Dog clone() throws CloneNotSupportedException {
- Dog dog = (Dog) super.clone();
-
- return dog;
- }
-}
-```
-
-使用:
-
-```java
-Dog dog1 = new Dog("1", "Dog1");
-Dog dog2 = dog1.clone();
-
-dog2.setName("Dog1 changed");
-
-System.out.println(dog1); // Dog{id='1', name='Dog1'}
-System.out.println(dog2); // Dog{id='1', name='Dog1 changed'}
-```
-
-如果一个类引用了其他类,引用的类也需要实现cloneable接口,比较麻烦。可以将所有的类都实现Serializable接口,通过序列化反序列化实现对象的深度拷贝。
-
-## 序列化
-
-序列化:把内存中的对象转换为字节序列的过程称为对象的序列化。
-
-### 什么情况下需要序列化?
-
-当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;
-当你想在网络上传送对象的时候;
-
-### 如何实现序列化
-
-实现Serializable接口即可。序列化的时候(如objectOutputStream.writeObject(user)),会判断user是否实现了Serializable(obj instanceof Serializable),如果对象没有实现Serializable接口,在序列化的时候会抛出NotSerializableException异常。
-
-### serialVersionUID
-
-serialVersionUID 是 Java 为每个序列化类产生的版本标识,可用来保证在反序列时,发送方发送的和接受方接收的是可兼容的对象。类的serialVersionUID的默认值完全依赖于Java编译器的实现。当完成序列化之后,此时对对象进行修改,由编译器生成的serialVersionUID会改变,这样反序列化的时候会报错。可以在序列化对象中添加 serialVersionUID,固定版本号,这样即便序列化对象做了修改,版本都是一致的,就能进行反序列化了。
-
-## 遍历
-
-### fast-fail
-
-fast-fail是Java集合的一种错误机制。当多个线程对同一个集合进行操作时,就有可能会产生fast-fail事件。
-例如:当线程a正通过iterator遍历集合时,另一个线程b修改了集合的内容,此时modCount(记录集合操作过程的修改次数)会加1,不等于expectedModCount,那么线程a访问集合的时候,就会抛出ConcurrentModificationException,产生fast-fail事件。边遍历边修改集合也会产生fast-fail事件。
-
-解决方法:
-
-- 使用Colletions.synchronizedList方法或在修改集合内容的地方加上synchronized。这样的话,增删集合内容的同步锁会阻塞遍历操作,影响性能。
-- 使用CopyOnWriteArrayList来替换ArrayList。在对CopyOnWriteArrayList进行修改操作的时候,会拷贝一个新的数组,对新的数组进行操作,操作完成后再把引用移到新的数组。
-
-### fail-safe
-
-fail-safe允许在遍历的过程中对容器中的数据进行修改。因为采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先copy原有集合内容,在拷贝的集合上进行遍历。
-
-由于迭代时是对原集合的拷贝的值进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发`ConcurrentModificationException`。
-
-java.util.concurrent包下的容器都是安全失败的,可以在多线程下并发使用。常见的的使用fail-safe方式遍历的容器有`ConcerrentHashMap`和`CopyOnWriteArrayList`等。
-
-fail-safe机制有两个问题:(1)需要复制集合,产生大量的无效对象,内存开销大;(2)不能访问到修改后的内容 。
-
-### 移除集合元素
-
-遍历时安全的移除集合中的元素,要使用遍历器Iterator和iterator.remove()方法。next()必须在remove()之前调用。
-
-```
-ArrayList list = new ArrayList(Arrays.asList("a","b","c","d"));
-Iterator iter = list.iterator();
-while(iter.hasNext()){
- String s = iter.next();
- if(s.equals("a")){
- iter.remove();
- }
-}
-```
-
-
-
diff --git "a/Java/Java\345\271\266\345\217\221\347\274\226\347\250\213.md" "b/Java/Java\345\271\266\345\217\221\347\274\226\347\250\213.md"
deleted file mode 100644
index 5381c9e..0000000
--- "a/Java/Java\345\271\266\345\217\221\347\274\226\347\250\213.md"
+++ /dev/null
@@ -1,392 +0,0 @@
-
-
-
-
-- [共享对象](#%E5%85%B1%E4%BA%AB%E5%AF%B9%E8%B1%A1)
- - [非原子的64位操作](#%E9%9D%9E%E5%8E%9F%E5%AD%90%E7%9A%8464%E4%BD%8D%E6%93%8D%E4%BD%9C)
- - [this 引用逸出](#this-%E5%BC%95%E7%94%A8%E9%80%B8%E5%87%BA)
- - [安全的对象构造过程](#%E5%AE%89%E5%85%A8%E7%9A%84%E5%AF%B9%E8%B1%A1%E6%9E%84%E9%80%A0%E8%BF%87%E7%A8%8B)
- - [ThreadLocal](#threadlocal)
-- [容器](#%E5%AE%B9%E5%99%A8)
- - [间接的迭代操作](#%E9%97%B4%E6%8E%A5%E7%9A%84%E8%BF%AD%E4%BB%A3%E6%93%8D%E4%BD%9C)
- - [ConcurrentHashMap](#concurrenthashmap)
- - [同步工具类](#%E5%90%8C%E6%AD%A5%E5%B7%A5%E5%85%B7%E7%B1%BB)
- - [信号量](#%E4%BF%A1%E5%8F%B7%E9%87%8F)
- - [缓存系统](#%E7%BC%93%E5%AD%98%E7%B3%BB%E7%BB%9F)
-- [任务执行](#%E4%BB%BB%E5%8A%A1%E6%89%A7%E8%A1%8C)
- - [Executor 框架](#executor-%E6%A1%86%E6%9E%B6)
- - [延迟任务](#%E5%BB%B6%E8%BF%9F%E4%BB%BB%E5%8A%A1)
- - [携带结果的 Callable 和 Future](#%E6%90%BA%E5%B8%A6%E7%BB%93%E6%9E%9C%E7%9A%84-callable-%E5%92%8C-future)
- - [为任务设置时限](#%E4%B8%BA%E4%BB%BB%E5%8A%A1%E8%AE%BE%E7%BD%AE%E6%97%B6%E9%99%90)
- - [取消与关闭](#%E5%8F%96%E6%B6%88%E4%B8%8E%E5%85%B3%E9%97%AD)
- - [任务取消](#%E4%BB%BB%E5%8A%A1%E5%8F%96%E6%B6%88)
- - [阻塞和中断](#%E9%98%BB%E5%A1%9E%E5%92%8C%E4%B8%AD%E6%96%AD)
-
-
-
-## 共享对象
-
-### 非原子的64位操作
-
-在多线程程序使用共享且可变的64位数据类型的变量是不安全的。
-
-
-
-### this 引用逸出
-
-参考自:[this 引用逸出](https://www.cnblogs.com/whatisjava/archive/2013/05/29/3106336.html)
-
-实例化 ThisEscape 对象时,会调用 source 的 registerListener 方法,这时便启动了一个线程,而且这个线程持有了 ThisEscape 对象(调用了对象的 doSomething 方法),但此时 ThisEscape 对象却没有实例化完成(还没有返回一个引用),所以我们说,此时造成了一个 this 引用逸出,即还没有完成的实例化 ThisEscape 对象的动作,却已经暴露了对象的引用。其他线程访问还没有构造好的对象,可能会造成意料不到的问题。
-
-```java
-public class ThisEscape {
- public ThisEscape(EventSource source) {
- source.registerListener(new EventListener() {
- public void onEvent(Event e) {
- doSomething(e);
- }
- });
- }
-
- void doSomething(Event e) {
- }
-
- interface EventSource {
- void registerListener(EventListener e);
- }
-
- interface EventListener {
- void onEvent(Event e);
- }
-
- interface Event {
- }
-}
-```
-
-### 安全的对象构造过程
-
-使用工厂方法来防止 this 引用在构造过程中逸出。
-
-```java
-public class SafeListener {
- private final EventListener listener;
-
- private SafeListener() {
- listener = new EventListener() {
- public void onEvent(Event e) {
- doSomething(e);
- }
- };
- }
-
- public static SafeListener newInstance(EventSource source) {
- SafeListener safe = new SafeListener();
- source.registerListener(safe.listener);
- return safe;
- }
-
- void doSomething(Event e) {
- }
-
- interface EventSource {
- void registerListener(EventListener e);
- }
-
- interface EventListener {
- void onEvent(Event e);
- }
-
- interface Event {
- }
- }
-```
-
-构造好了 SafeListener 对象(通过构造器构造)之后,才启动了监听线程,也就确保了构造完成之后再使用SafeListener对象。
-
-### ThreadLocal
-
-当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
-每个线程都有一个ThreadLocalMap(ThreadLocal内部类),Map中元素的键为ThreadLocal,而值对应线程的变量副本。
-调用set()-->调用getMap(Thread)-->返回当前线程的ThreadLocalMap-->map.set(this, value),this是ThreadLocal。
-调用get()-->调用getMap(Thread)-->返回当前线程的ThreadLocalMap-->map.getEntry(this),返回value。
-
-```java
-public class ThreadLocalDemo {
- ThreadLocal longLocal = new ThreadLocal<>();
-
- public void set() {
- longLocal.set(Thread.currentThread().getId());
- }
-
- public String get() {
- return Thread.currentThread().getName() + ": " + longLocal.get();
- }
-
- public static void main(String[] args) throws InterruptedException {
- ThreadLocalDemo threadLocalDemo = new ThreadLocalDemo();
- threadLocalDemo.set();
- System.out.println(threadLocalDemo.get());
-
- Thread thread = new Thread(() -> {
- threadLocalDemo.set();
- System.out.println(threadLocalDemo.get());
- }
- );
-
- thread.start();
- thread.join();
-
- System.out.println(threadLocalDemo.get());
- }
-}
-/*output
-main: 1
-Thread-0: 11
-main: 1
- */
-```
-
-## 容器
-
-### 间接的迭代操作
-
-调用容器的 toString 方法会迭代容器。容器的 hashcode 和 equals 方法也会间接的进行迭代操作。
-
-```java
-public class HiddenIterator {
- private final Set set = new HashSet<>();
- //...
- public void print() {
- System.out.println("set: " + set);
- }
-}
-```
-
-### ConcurrentHashMap
-多线程环境下,使用Hashmap进行put操作会引起死循环。
-CocurrentHashMap利用锁分段技术增加了锁的数目,从而使争夺同一把锁的线程的数目得到控制。
-锁分段技术就是将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。
-ConcurrentHashMap调用get的时候不加锁,原因是node数组成员val和指针next是用volatile修饰的,更改后的值会立刻刷新到主存中,保证了可见性,node数组table也用volatile修饰,保证在运行过程对其他线程具有可见性。
-
-JDK1.7中的ConcurrentHashmap主要使用Segment来实现减小锁粒度,把HashMap分割成若干个Segment,在put的时候需要锁住Segment,get时候不加锁,使用volatile来保证可见性,当要统计size时,比较统计前后modCount是否发生变化。如果没有变化,则直接返回size。否则,需要依次锁住所有的Segment来计算。jdk1.7中ConcurrentHashmap中,当长度过长碰撞会很频繁,链表的增改删查操作都会消耗很长的时间,影响性能。
-
-jdk1.8不采用segment而采用Node,锁住Node来实现减小锁粒度。当链表长度过长时,Node会转换成TreeNode。
-
-put 操作会对当前的table进行无条件自循环直到put成功,可以分成以下流程来概述:
-1)如果没有初始化就先调用 initTable 方法来进行初始化过程
-2)如果没有hash冲突就直接CAS插入
-3)如果还在进行扩容操作就先进行扩容
-4)如果存在hash冲突,就加锁来保证线程安全,这里有两种情况,一种是链表形式就直接遍历到尾端插入,一种是红黑树就按照红黑树结构插入
-
-5)如果该链表的数量大于阈值8,就要先转换成黑红树的结构
-6)如果添加成功就调用 addCount 方法统计size,并且检查是否需要扩容
-
-### 同步工具类
-
-#### 信号量
-
-信号量 Semaphore 用来控制同时访问某个特定资源的操作数量,或者同时执行某个指定操作的数量。
-
-通过信号量实现有界的HashSet:
-
-```java
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.concurrent.Semaphore;
-
-public class BoundedHashSet {
- private final Set set;
- private final Semaphore sem;
-
- public BoundedHashSet(int bound) {
- set = Collections.synchronizedSet(new HashSet<>());
- sem = new Semaphore(bound);
- }
-
- public boolean add(T t) throws InterruptedException {
- sem.acquire();
- boolean wasAdded = false;
- try {
- wasAdded = set.add(t);
- return wasAdded;
- } finally {
- if (!wasAdded) {
- sem.release();
- }
- }
- }
-
- public boolean remove(Object o) {
- boolean wasRemoved = set.remove(o);
- if (wasRemoved) {
- sem.release();
- }
- return wasRemoved;
- }
-}
-```
-
-### 缓存系统
-
-假设有一个高计算开销的 compute 函数,我们可以将计算结果保存在 Map 中,调用 compute 时先检查 Map 是否存在需要的结果。使用 ConturrentHashMap 可以提高系统的并发能力。假如两个线程同时调用 compute ,则相同的数据会被计算多次,使用 FutureTask 可以避免这个问题。
-
-```java
-interface Computable {
- V compute(A arg) throws InterruptedException;
-}
-
-public class Memorizer implements Computable {
- private final ConcurrentMap> cache = new ConcurrentHashMap<>();
- private final Computable c;
- public Memorizer(Computable c) {
- this.c = c;
- }
- @Override
- public V compute(A arg) throws InterruptedException {
- while (true) {
- Future f = cache.get(arg);
- if (f == null) {
- Callable eval = new Callable() {
- @Override
- public V call() throws InterruptedException {
- return c.compute(arg);
- }
- };
- FutureTask ft = new FutureTask<>(eval);
- //先放进缓存,再进行计算;若线程x正在计算某个值,而线程y刚好正在查找这个值,则线程y会等待x的计算结果
- f = cache.putIfAbsent(arg, ft);
- //避免两个线程同一时间调用compute计算相同的值
- if (f == null) {
- f = ft;
- ft.run();
- }
- }
- try {
- return f.get();
- } catch (CancellationException ex) {
- cache.remove(arg, f);
- } catch (ExecutionException ex) {
- ex.printStackTrace();
- }
- }
- }
-}
-```
-
-## 任务执行
-
-### Executor 框架
-
-1.5后引入的 Executor 框架的最大优点是把任务的提交和执行解耦。Executor 是任务执行的抽象,,使用 Runnable 或 Callable 来表示任务。
-
-```java
-public class TaskExecutionWebServer {
- private static final int NTHREADS = 100;
- private static final Executor exec = Executors.newFixedThreadPool(NTHREADS);
-
- public static void main(String[] args) throws IOException {
- ServerSocket socket = new ServerSocket(80);
- while (true) {
- final Socket connection = socket.accept();
- Runnable task = new Runnable() {
- @Override
- public void run() {
- //handleRequest(connection);
- }
- };
- //将任务提交到工作队列
- exec.execute(task);
- }
- }
-}
-```
-
-
-
-#### 延迟任务
-
-Timer 类负责延迟任务和周期任务,然后 Timer 存在一些缺陷。现在一般使用 ScheduledThreadPoolExecutor 来代替它,通过 ScheduledThreadPoolExecutor 的构造函数或者 Executors.newScheduledThreadPool 工厂方法来创建该类的对象(不推荐,见阿里编码规范)。
-
-#### 携带结果的 Callable 和 Future
-
-当提交一个Callable对象给ExecutorService,将得到一个Future对象,调用Future对象的get方法等待执行结果就好了。而 get 方法的行为取决于任务的状态(尚未开始、正在运行、已完成)。任务已完成,那么 get 会立即返回或者抛出异常;任务没有完成,get 将一直阻塞直到任务完成;任务抛出异常,那么 get 会将异常封装成 ExecutionException 重新抛出;任务被取消,那么 get 将抛出 CancellationExeception,此时通过 getCause 可以获取被封装的初始异常。
-
-
-#### 为任务设置时限
-
-Future.get() 支持时间限制,当结果可用时,它将直接返回,如果在指定时间内没有计算出结果,那么将抛出 TimeoutException。
-
-```java
-Page renderPageWithAd() throws InterruptedException {
- long endNanos = System.nanoTime() + TIME_BUDGET;
- Future f = exec.submit(new FetchAdTask());
- //等待广告同时显示页面
- Page page = renderPageBody();
- Ad ad;
- try {
- //只等待指定的时间
- long timeLeft = endNanos - System.nanoTime();
- ad = f.get(timeLeft, NANOSECONDES);
- } catch (ExecutionException e) {
- ad = DEFAULT_AD;
- } catch (TimeoutException e) {
- ad = DEFAULT_AD;
- //超时则取消任务
- f.cancel(true);
- }
- page.setAd(ad);
- return page;
-}
-```
-
-### 取消与关闭
-
-Java 没有提供任何机制来安全的终止线程,但它提供了中断机制,这是一种协作机制,能够使一个线程终止另一个线程的当前工作。
-
-
-#### 任务取消
-
-给任务设置某个“请求取消”的标志,而任务将定期查看该标志,如果设置了该标志,那么任务将提前结束。
-
-#### 阻塞和中断
-
-如果任务中调用了一个阻塞方法,那么任务有可能永远不会检查取消标志,因此永远不会结束。通过中断机制可以避免这个问题。一些特殊的阻塞库的方法支持中断。
-
-线程可能受到阻塞的原因:等待 IO 操作,等待获得锁,等待从 Thread.sleep 方法醒来,或是等待另一个线程的计算结果。阻塞方法可能会抛出 InterruptedException。当在代码中调用了一个会抛出 InterruptedException 的方法时,这个方法也就变成了阻塞方法,需要处理中断异常。阻塞方法必须等待某个不受它控制的事件发生后才能继续执行。
-
-每个线程都有一个 boolean 类型的中断状态,当中断线程时,这个线程的中断状态会被设置为 true。在 Thread 中包含了中断线程以及查询线程中断状态的方法。
-
-```java
-public class Thread {
- public void interrupt() {} //中断目标线程
- public boolean isInterrupted() {} //返回目标线程的中断状态
- public static boolean interrupted() {}//清除当前线程中断状态的唯一方法,并返回它之前的值
-}
-```
-
-阻塞方法如 Thread.sleep 和 Object.wait 等,都会检查线程何时中断,并且在发生中断时提前返回。它们在响应中断时执行的操作包含:清除中断状态,抛出 InterruptedException,表示阻塞操作由于中断而提前结束。**调用interrupt并不意味着立即停止目标线程正在进行的工作,而只是传递了请求中断的消息**。对中断操作的正确理解是:它并不会真正的中断一个正在运行的线程,而只是发出中断请求,然后由线程在下一个合适的时刻自己中断。
-
-阻塞方法必须处理对中断的响应,有两种处理方法:
-
-1. 传递 InterruptedException 给方法的调用者。
-2. 恢复中断。有时不能抛出 InterruptedException,如代码是 Runnable 一部分时,必须捕获 InterruptedException,并通过调用当前线程上的 interrupt 方法恢复中断状态,这样在高层代码就看到引发了一个中断。
-
-```java
-public class TaskRunnable implements Runnable {
- BlockingQueue queue;
- ...
- public void run() {
- try {
- processTask(queue.take());
- } catch (InterruptedException e) {
- //恢复被中断的状态,不然会丢失线程被中断的证据
- Thread.currentThread().interrupt();
- }
- }
-}
-```
-
-
-
diff --git "a/Java/Object\347\261\273\345\270\270\347\224\250\346\226\271\346\263\225.md" "b/Java/Object\347\261\273\345\270\270\347\224\250\346\226\271\346\263\225.md"
deleted file mode 100644
index e78bb55..0000000
--- "a/Java/Object\347\261\273\345\270\270\347\224\250\346\226\271\346\263\225.md"
+++ /dev/null
@@ -1,241 +0,0 @@
-
-
-**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
-
-- [Object常用方法](#object%E5%B8%B8%E7%94%A8%E6%96%B9%E6%B3%95)
- - [toString](#tostring)
- - [equals](#equals)
- - [hashCode](#hashcode)
- - [clone](#clone)
- - [浅拷贝](#%E6%B5%85%E6%8B%B7%E8%B4%9D)
- - [深拷贝](#%E6%B7%B1%E6%8B%B7%E8%B4%9D)
-
-
-
-## Object常用方法
-
-Java面试经常会出现的一道题目,Object的常用方法。下面给大家整理一下。
-
-object常用方法有:toString()、equals()、hashCode()、clone()等。
-
-### toString
-
-默认输出对象地址。
-
-```java
-public class Person {
- private int age;
- private String name;
-
- public Person(int age, String name) {
- this.age = age;
- this.name = name;
- }
-
- public static void main(String[] args) {
- System.out.println(new Person(18, "程序员大彬").toString());
- }
- //output
- //me.tyson.java.core.Person@4554617c
-}
-```
-
-可以重写toString方法,按照重写逻辑输出对象值。
-
-```java
-public class Person {
- private int age;
- private String name;
-
- public Person(int age, String name) {
- this.age = age;
- this.name = name;
- }
-
- @Override
- public String toString() {
- return name + ":" + age;
- }
-
- public static void main(String[] args) {
- System.out.println(new Person(18, "程序员大彬").toString());
- }
- //output
- //程序员大彬:18
-}
-```
-
-### equals
-
-默认比较两个引用变量是否指向同一个对象(内存地址)。
-
-```java
-public class Person {
- private int age;
- private String name;
-
- public Person(int age, String name) {
- this.age = age;
- this.name = name;
- }
-
- public static void main(String[] args) {
- String name = "程序员大彬";
- Person p1 = new Person(18, name);
- Person p2 = new Person(18, name);
-
- System.out.println(p1.equals(p2));
- }
- //output
- //false
-}
-```
-
-可以重写equals方法,按照age和name是否相等来判断:
-
-```java
-public class Person {
- private int age;
- private String name;
-
- public Person(int age, String name) {
- this.age = age;
- this.name = name;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof Person) {
- Person p = (Person) o;
- return age == p.age && name.equals(p.name);
- }
- return false;
- }
-
- public static void main(String[] args) {
- String name = "程序员大彬";
- Person p1 = new Person(18, name);
- Person p2 = new Person(18, name);
-
- System.out.println(p1.equals(p2));
- }
- //output
- //true
-}
-```
-
-### hashCode
-
-将与对象相关的信息映射成一个哈希值,默认的实现hashCode值是根据内存地址换算出来。
-
-```java
-public class Cat {
- public static void main(String[] args) {
- System.out.println(new Cat().hashCode());
- }
- //out
- //1349277854
-}
-```
-
-### clone
-
-java赋值是复制对象引用,如果我们想要得到一个对象的副本,使用赋值操作是无法达到目的的。Object对象有个clone()方法,实现了对
-
-象中各个属性的复制,但它的可见范围是protected的。
-
-```java
-protected native Object clone() throws CloneNotSupportedException;
-```
-
-所以实体类使用克隆的前提是:
-
-- 实现Cloneable接口,这是一个标记接口,自身没有方法,这应该是一种约定。调用clone方法时,会判断有没有实现Cloneable接口,没有实现Cloneable的话会抛异常CloneNotSupportedException。
-- 覆盖clone()方法,可见性提升为public。
-
-```java
-public class Cat implements Cloneable {
- private String name;
-
- @Override
- protected Object clone() throws CloneNotSupportedException {
- return super.clone();
- }
-
- public static void main(String[] args) throws CloneNotSupportedException {
- Cat c = new Cat();
- c.name = "程序员大彬";
- Cat cloneCat = (Cat) c.clone();
- c.name = "大彬";
- System.out.println(cloneCat.name);
- }
- //output
- //程序员大彬
-}
-```
-
-下面介绍下浅拷贝和深拷贝。
-
-#### 浅拷贝
-
-拷⻉对象和原始对象的引⽤类型引用同⼀个对象。
-
-以下例子,Cat对象里面有个Person对象,调用clone之后,克隆对象和原对象的Person引用的是同一个对象,这就是浅拷贝。
-
-```java
-public class Cat implements Cloneable {
- private String name;
- private Person owner;
-
- @Override
- protected Object clone() throws CloneNotSupportedException {
- return super.clone();
- }
-
- public static void main(String[] args) throws CloneNotSupportedException {
- Cat c = new Cat();
- Person p = new Person(18, "程序员大彬");
- c.owner = p;
-
- Cat cloneCat = (Cat) c.clone();
- p.setName("大彬");
- System.out.println(cloneCat.owner.getName());
- }
- //output
- //大彬
-}
-```
-
-#### 深拷贝
-
-拷贝对象和原始对象的引用类型引用不同的对象。
-
-以下例子,在clone函数中不仅调用了super.clone,而且调用Person对象的clone方法(Person也要实现Cloneable接口并重写clone方法),从而实现了深拷贝。可以看到,拷贝对象的值不会受到原对象的影响。
-
-```java
-public class Cat implements Cloneable {
- private String name;
- private Person owner;
-
- @Override
- protected Object clone() throws CloneNotSupportedException {
- Cat c = null;
- c = (Cat) super.clone();
- c.owner = (Person) owner.clone();//拷贝Person对象
- return c;
- }
-
- public static void main(String[] args) throws CloneNotSupportedException {
- Cat c = new Cat();
- Person p = new Person(18, "程序员大彬");
- c.owner = p;
-
- Cat cloneCat = (Cat) c.clone();
- p.setName("大彬");
- System.out.println(cloneCat.owner.getName());
- }
- //output
- //程序员大彬
-}
-```
-
diff --git "a/Java/\345\271\266\345\217\221.md" "b/Java/\345\271\266\345\217\221.md"
deleted file mode 100644
index 65ea9c9..0000000
--- "a/Java/\345\271\266\345\217\221.md"
+++ /dev/null
@@ -1,1166 +0,0 @@
-
-
-
-
-- [线程池](#%E7%BA%BF%E7%A8%8B%E6%B1%A0)
- - [线程池原理](#%E7%BA%BF%E7%A8%8B%E6%B1%A0%E5%8E%9F%E7%90%86)
- - [线程池大小](#%E7%BA%BF%E7%A8%8B%E6%B1%A0%E5%A4%A7%E5%B0%8F)
- - [关闭线程池](#%E5%85%B3%E9%97%AD%E7%BA%BF%E7%A8%8B%E6%B1%A0)
-- [executor框架](#executor%E6%A1%86%E6%9E%B6)
- - [简介](#%E7%AE%80%E4%BB%8B)
- - [ThreadPoolExecutor实例](#threadpoolexecutor%E5%AE%9E%E4%BE%8B)
- - [Runnable和Callable的区别](#runnable%E5%92%8Ccallable%E7%9A%84%E5%8C%BA%E5%88%AB)
- - [Future和FutureTask](#future%E5%92%8Cfuturetask)
- - [execute()和submit()](#execute%E5%92%8Csubmit)
- - [常用的线程池](#%E5%B8%B8%E7%94%A8%E7%9A%84%E7%BA%BF%E7%A8%8B%E6%B1%A0)
- - [FixedThreadPool](#fixedthreadpool)
- - [SingleThreadExecutor](#singlethreadexecutor)
- - [CachedThreadPool](#cachedthreadpool)
- - [ScheduledThreadPoolExecutor](#scheduledthreadpoolexecutor)
- - [编码规范](#%E7%BC%96%E7%A0%81%E8%A7%84%E8%8C%83)
-- [JMM](#jmm)
-- [进程线程](#%E8%BF%9B%E7%A8%8B%E7%BA%BF%E7%A8%8B)
- - [线程状态](#%E7%BA%BF%E7%A8%8B%E7%8A%B6%E6%80%81)
- - [中断](#%E4%B8%AD%E6%96%AD)
- - [常见方法](#%E5%B8%B8%E8%A7%81%E6%96%B9%E6%B3%95)
- - [join](#join)
- - [yield](#yield)
- - [sleep](#sleep)
- - [wait()和sleep()的区别](#wait%E5%92%8Csleep%E7%9A%84%E5%8C%BA%E5%88%AB)
- - [创建线程的方法](#%E5%88%9B%E5%BB%BA%E7%BA%BF%E7%A8%8B%E7%9A%84%E6%96%B9%E6%B3%95)
-- [线程间通信](#%E7%BA%BF%E7%A8%8B%E9%97%B4%E9%80%9A%E4%BF%A1)
- - [volatile](#volatile)
- - [synchronized](#synchronized)
- - [等待通知机制](#%E7%AD%89%E5%BE%85%E9%80%9A%E7%9F%A5%E6%9C%BA%E5%88%B6)
-- [锁](#%E9%94%81)
- - [synchronized](#synchronized-1)
- - [释放锁](#%E9%87%8A%E6%94%BE%E9%94%81)
- - [实现原理](#%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86)
- - [锁的状态](#%E9%94%81%E7%9A%84%E7%8A%B6%E6%80%81)
- - [ReentrantLock](#reentrantlock)
- - [原理](#%E5%8E%9F%E7%90%86)
- - [ReentrantLock和synchronized区别](#reentrantlock%E5%92%8Csynchronized%E5%8C%BA%E5%88%AB)
- - [锁的分类](#%E9%94%81%E7%9A%84%E5%88%86%E7%B1%BB)
- - [公平锁与非公平锁](#%E5%85%AC%E5%B9%B3%E9%94%81%E4%B8%8E%E9%9D%9E%E5%85%AC%E5%B9%B3%E9%94%81)
- - [共享式与独占式锁](#%E5%85%B1%E4%BA%AB%E5%BC%8F%E4%B8%8E%E7%8B%AC%E5%8D%A0%E5%BC%8F%E9%94%81)
- - [悲观锁与乐观锁](#%E6%82%B2%E8%A7%82%E9%94%81%E4%B8%8E%E4%B9%90%E8%A7%82%E9%94%81)
- - [CAS](#cas)
-- [并发工具](#%E5%B9%B6%E5%8F%91%E5%B7%A5%E5%85%B7)
- - [CountDownLatch](#countdownlatch)
- - [CyclicBarrier](#cyclicbarrier)
- - [CyclicBarrier和CountDownLatch区别](#cyclicbarrier%E5%92%8Ccountdownlatch%E5%8C%BA%E5%88%AB)
- - [Semaphore](#semaphore)
-- [原子类](#%E5%8E%9F%E5%AD%90%E7%B1%BB)
- - [基本类型原子类](#%E5%9F%BA%E6%9C%AC%E7%B1%BB%E5%9E%8B%E5%8E%9F%E5%AD%90%E7%B1%BB)
- - [数组类型原子类](#%E6%95%B0%E7%BB%84%E7%B1%BB%E5%9E%8B%E5%8E%9F%E5%AD%90%E7%B1%BB)
- - [引用类型原子类](#%E5%BC%95%E7%94%A8%E7%B1%BB%E5%9E%8B%E5%8E%9F%E5%AD%90%E7%B1%BB)
-- [AQS](#aqs)
- - [原理](#%E5%8E%9F%E7%90%86-1)
-- [Condition](#condition)
- - [实现原理](#%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86-1)
-- [其他](#%E5%85%B6%E4%BB%96)
- - [Daemon Thread](#daemon-thread)
-- [参考资料](#%E5%8F%82%E8%80%83%E8%B5%84%E6%96%99)
-
-
-
-> 首先给大家分享一个github仓库,上面放了**200多本经典的计算机书籍**,包括C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生等,可以star一下,下次找书直接在上面搜索,仓库持续更新中~
->
-> github地址:https://github.com/Tyson0314/java-books
->
-> 如果github访问不了,可以访问gitee仓库。
->
-> gitee地址:https://gitee.com/tysondai/java-books
-
-## 线程池
-
-**使用线程池的好处**:
-
-- **降低资源消耗**。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
-- **提高响应速度**。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
-- **提高线程的可管理性**。统一管理线程,避免系统创建大量同类线程而导致消耗完内存。
-
-```
-public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler);
-```
-### 线程池原理
-
-创建新的线程需要获取全局锁,通过这种设计可以尽量避免获取全局锁,当 ThreadPoolExecutor 完成预热之后(当前运行的线程数大于等于 corePoolSize),提交的大部分任务都会被放到 BlockingQueue。
-
-
-
-ThreadPoolExecutor 的通用构造函数:
-
-```
-public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler);
-```
-
-- corePoolSize:当有新任务时,如果线程池中线程数没有达到线程池的基本大小,则会创建新的线程执行任务,否则将任务放入阻塞队列。当线程池中存活的线程数总是大于 corePoolSize 时,应该考虑调大 corePoolSize。
-
-- maximumPoolSize:当阻塞队列填满时,如果线程池中线程数没有超过最大线程数,则会创建新的线程运行任务。否则根据拒绝策略处理新任务。非核心线程类似于临时借来的资源,这些线程在空闲时间超过 keepAliveTime 之后,就应该退出,避免资源浪费。
-
-- BlockingQueue:存储等待运行的任务。
-
-- keepAliveTime:**非核心线程**空闲后,保持存活的时间,此参数只对非核心线程有效。设置为0,表示多余的空闲线程会被立即终止。
-
-- TimeUnit:时间单位
-
- ```java
- TimeUnit.DAYS
- TimeUnit.HOURS
- TimeUnit.MINUTES
- TimeUnit.SECONDS
- TimeUnit.MILLISECONDS
- TimeUnit.MICROSECONDS
- TimeUnit.NANOSECONDS
- ```
-- ThreadFactory:每当线程池创建一个新的线程时,都是通过线程工厂方法来完成的。在 ThreadFactory 中只定义了一个方法 newThread,每当线程池需要创建新线程就会调用它。
-
- ```java
- public class MyThreadFactory implements ThreadFactory {
- private final String poolName;
-
- public MyThreadFactory(String poolName) {
- this.poolName = poolName;
- }
-
- public Thread newThread(Runnable runnable) {
- return new MyAppThread(runnable, poolName);//将线程池名字传递给构造函数,用于区分不同线程池的线程
- }
- }
- ```
-
-- RejectedExecutionHandler:当队列和线程池都满了时,根据拒绝策略处理新任务。
-
- ```java
- AbortPolicy:默认的策略,直接抛出RejectedExecutionException
- DiscardPolicy:不处理,直接丢弃
- DiscardOldestPolicy:将等待队列队首的任务丢弃,并执行当前任务
- CallerRunsPolicy:由调用线程处理该任务
- ```
-
-### 线程池大小
-
-如果线程池线程数量太小,当有大量请求需要处理,系统响应比较慢影响体验,甚至会出现任务队列大量堆积任务导致OOM。
-
-如果线程池线程数量过大,大量线程可能会同时在争取 CPU 资源,这样会导致大量的上下文切换(cpu给线程分配时间片,当线程的cpu时间片用完后保存状态,以便下次继续运行),从而增加线程的执行时间,影响了整体执行效率。
-
-**CPU 密集型任务(N+1)**: 这种任务消耗的主要是 CPU 资源,可以将线程数设置为 N(CPU 核心数)+1,比 CPU 核心数多出来的一个线程是为了防止某些原因导致的任务暂停(线程阻塞,如io操作,等待锁,线程sleep)而带来的影响。一旦某个线程被阻塞,释放了cpu资源,而在这种情况下多出来的一个线程就可以充分利用 CPU 的空闲时间。
-
-**I/O 密集型任务(2N)**: 系统会用大部分的时间来处理 I/O 操作,而线程等待 I/O 操作会被阻塞,释放 cpu资源,这时就可以将 CPU 交出给其它线程使用。因此在 I/O 密集型任务的应用中,我们可以多配置一些线程,具体的计算方法:最佳线程数 = CPU核心数 * (1/CPU利用率) = CPU核心数 * (1 + (I/O耗时/CPU耗时)),一般可设置为2N。
-
-### 关闭线程池
-
-shutdown():
-
-将线程池状态置为`SHUTDOWN`,并不会立即停止:
-
-- 停止接收外部提交的任务
-- 内部正在跑的任务和队列里等待的任务,会执行完
-- 等到第二步完成后,才真正停止
-
-shutdownNow():
-
-将线程池状态置为`STOP`。企图立即停止,事实上不一定:
-
-- 跟shutdown()一样,先停止接收外部提交的任务
-- 忽略队列里等待的任务
-- 尝试将正在跑的任务中断(不一定中断成功,取决于任务响应中断的逻辑)
-- 返回未执行的任务列表
-
-
-
-## executor框架
-
-1.5后引入的Executor框架的最大优点是把任务的提交和执行解耦。当提交一个Callable对象给ExecutorService,将得到一个Future对象,调用Future对象的get方法等待执行结果。Executor框架的内部使用了线程池机制,它在java.util.cocurrent 包下,通过该框架来控制线程的启动、执行和关闭,可以简化并发编程的操作。
-
-### 简介
-
-executor框架由3部分组成:任务、任务的执行、异步计算的结果
-
-- 任务。需要实现的接口:Runnable和Callable接口。
-- 任务的执行。ExecutorService 是一个接口,用于定义线程池,调用它的 execute(Runnable)或者 submit(Runnable/Callable)执行任务。ExecutorService接口继承于Executor,有两个实现类`ThreadPoolExecutor`和`ScheduledThreadPoolExecutor`。
-- 异步计算的结果。包括future接口和实现future接口的FutureTask,调用future.get()会阻塞当前线程直到任务完成,future.cancel()可以取消执行任务。
-
-### ThreadPoolExecutor实例
-
-使用 `ThreadPoolExecutor` 构造函数自定义参数的方式来创建线程池。
-
-```java
-public class ThreadPoolExecutorDemo {
- private static final int CORE_POOL_SIZE = 5;
- private static final int MAX_POOL_SIZE = 10;
- private static final int QUEUE_CAPACITY = 100;
- private static final long KEEP_ALIVE_TIME = 1L;
-
- public static void main(String[] args) throws ExecutionException, InterruptedException {
- ThreadPoolExecutor executor = new ThreadPoolExecutor(
- CORE_POOL_SIZE,
- MAX_POOL_SIZE,
- KEEP_ALIVE_TIME,
- TimeUnit.SECONDS,
- new ArrayBlockingQueue<>(QUEUE_CAPACITY),
- new ThreadPoolExecutor.CallerRunsPolicy()
- );
-
- for (int i = 0; i < 10; i++) {
- Callable worker = () -> {
- System.out.println(Thread.currentThread().getName());
- return "ok";
- };
- Future f = executor.submit(worker);
- f.get();
- }
- executor.shutdown();
- while (!executor.isTerminated()) {
- }
- System.out.println("Finished all threads");
- }
-}
-```
-
-### Runnable和Callable的区别
-
-Runnable 任务执行后不能返回值或者抛出异常。Callable 任务执行后可以返回值或抛出异常。
-
-```
-Executors.callable(Runnable task);//runnable转化为callable
-ExecutorService.execute(Runnable);
-ExecutorService.submit(Runnable/Callable);//submit callable任务有返回值
-
-//返回值是泛型参数V
-public interface Callable {
- V call() throws Exception;
-}
-```
-
-### Future和FutureTask
-
-Future 可以获取任务执行的结果、取消任务。调用 future.get()会阻塞当前线程直到任务返回结果。
-
-```java
-public interface Future {
- boolean cancel(boolean mayInterruptIfRunning);
-
- boolean isCancelled();
-
- boolean isDone();
-
- V get() throws InterruptedException, ExecutionException;
-
- V get(long timeout, TimeUnit unit)
- throws InterruptedException, ExecutionException, TimeoutException;
-}
-```
-
-FutureTask 实现了 RunnableFuture 接口,而 RunnableFuture 实现了 Runnable 和 Future\ 接口。
-
-### execute()和submit()
-
-`execute()`方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否。
-
-`submit()`方法用于提交需要返回值的任务。线程池会返回一个 `Future` 类型的对象,通过这个 `Future` 对象可以判断任务是否执行成功,并且可以通过 `Future` 的 `get()`方法来获取返回值,`get()`方法会阻塞当前线程直到任务完成,而使用 `get(long timeout, TimeUnit unit)`方法则会阻塞当前线程一段时间后立即返回,无论任务是否执行完。
-
-### 常用的线程池
-
-常见的线程池有 FixedThreadPool、SingleThreadExecutor、CachedThreadPool 和 ScheduledThreadPool。这几个都是 ExecutorService (线程池)实例。
-
-#### FixedThreadPool
-
-固定线程数的线程池。任何时间点,最多只有 nThreads 个线程处于活动状态执行任务。
-
-```
-public static ExecutorService newFixedThreadPool(int nThreads) {
- return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
-}
-```
-
-使用无界队列 LinkedBlockingQueue(队列容量为 Integer.MAX_VALUE),运行中的线程池不会拒绝任务,即不会调用RejectedExecutionHandler.rejectedExecution()方法。
-
-maxThreadPoolSize 是无效参数,故将它的值设置为与 coreThreadPoolSize 一致。
-
-keepAliveTime 也是无效参数,设置为0L,因为此线程池里所有线程都是核心线程,核心线程不会被回收(除非设置了executor.allowCoreThreadTimeOut(true))。
-
-适用场景:适用于处理CPU密集型的任务,确保CPU在长期被工作线程使用的情况下,尽可能的少的分配线程,即适用执行长期的任务。需要注意的是,FixedThreadPool 不会拒绝任务,**在任务比较多的时候会导致 OOM。**
-
-#### SingleThreadExecutor
-
-只有一个线程的线程池。
-
-```
-public static ExecutionService newSingleThreadExecutor() {
- return new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
-}
-```
-
-使用无界队列 LinkedBlockingQueue。线程池只有一个运行的线程,新来的任务放入工作队列,线程处理完任务就循环从队列里获取任务执行。保证顺序的执行各个任务。
-
-适用场景:适用于串行执行任务的场景,一个任务一个任务地执行。**在任务比较多的时候也是会导致 OOM。**
-
-#### CachedThreadPool
-
-根据需要创建新线程的线程池。
-
-```
-public static ExecutorService newCachedThreadPool() {
- return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue());
-}
-```
-
-如果主线程提交任务的速度高于线程处理任务的速度时,`CachedThreadPool` 会不断创建新的线程。极端情况下,这样会导致耗尽 cpu 和内存资源。
-
-使用没有容量的SynchronousQueue作为线程池工作队列,当线程池有空闲线程时,`SynchronousQueue.offer(Runnable task)`提交的任务会被空闲线程处理,否则会创建新的线程处理任务。
-
-适用场景:用于并发执行大量短期的小任务。`CachedThreadPool`允许创建的线程数量为 Integer.MAX_VALUE ,**可能会创建大量线程,从而导致 OOM。**
-
-#### ScheduledThreadPoolExecutor
-
-在给定的延迟后运行任务,或者定期执行任务。在实际项目中基本不会被用到,因为有其他方案选择比如`quartz`。
-
-使用的任务队列 `DelayQueue` 封装了一个 `PriorityQueue`,`PriorityQueue` 会对队列中的任务进行排序,时间早的任务先被执行(即`ScheduledFutureTask` 的 `time` 变量小的先执行),如果time相同则先提交的任务会被先执行(`ScheduledFutureTask` 的 `squenceNumber` 变量小的先执行)。
-
-执行周期任务步骤:
-
-1. 线程从 `DelayQueue` 中获取已到期的 `ScheduledFutureTask(DelayQueue.take())`。到期任务是指 `ScheduledFutureTask`的 time 大于等于当前系统的时间;
-2. 执行这个 `ScheduledFutureTask`;
-3. 修改 `ScheduledFutureTask` 的 time 变量为下次将要被执行的时间;
-4. 把这个修改 time 之后的 `ScheduledFutureTask` 放回 `DelayQueue` 中(`DelayQueue.add()`)。
-
-
-
-适用场景:周期性执行任务的场景,需要限制线程数量的场景。
-
-#### 编码规范
-
-阿里巴巴编码规约不允许使用Executors去创建线程池,而是通过ThreadPoolExecutor的方式手动创建线程池,这样子使用者会更加明确线程池的运行机制,避免资源耗尽的风险。
-
-Executors 创建线程池对象的弊端:
-
-FixedThreadPool和SingleThreadPool。允许请求队列长度为 Integer.MAX_VALUE,可能堆积大量请求,从而导致OOM。
-
-CachedThreadPool。创建的线程池允许的最大线程数是Integer.MAX_VALUE,当添加任务的速度大于线程池处理任务的速度,可能会创建大量的线程,消耗资源,甚至导致OOM。
-
-
-
-正确示例(阿里巴巴编码规范):
-
-```java
-//正例1
-ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("demo-pool-%d").build();
-//Common Thread Pool
-ExecutorService pool = new ThreadPoolExecutor(5, 200,
-0L, TimeUnit.MILLISECONDS, //0L keepAliveTime
-new LinkedBlockingQueue(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
-
-pool.execute(()-> System.out.println(Thread.currentThread().getName()));
-pool.shutdown();//gracefully shutdown
-
-//正例2
-ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1, //corePoolSize threadFactory
- new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build());
-```
-
-## JMM
-
-Java内存模型:线程之间的共享变量存储在主内存里,每个线程都有自己私有的本地内存,本地内存保存了共享变量的副本,线程对变量的操作都在本地内存中进行,不能直接读写主内存中的变量。不同的线程之间也无法直接访问对方本地内存中的变量,线程之间值的传递都需要通过主内存来完成。
-
-线程a和线程b要想进行数据交换一般要经过下面的步骤:
-
-1. 线程a把本地内存a中的更新过的共享变量刷新到主内存中去。
-2. 线程b到主内存中去读取线程a刷新过的共享变量,然后复制一份到本地内存b中去。
-
-
-
-本地内存是JMM的一个抽象概念,并不真实存在,它包括缓存、写缓冲区、寄存器以及其他硬件和编译器优化。
-
-## 进程线程
-
-进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程。
-线程是比进程更小的执行单位,它是在一个进程中独立的控制流,一个进程可以启动多个线程,每条线程并行执行不同的任务。
-
-### 线程状态
-
-初始(NEW):线程被构建,还没有调用 start()。
-
-运行(RUNNABLE):包括操作系统的就绪和运行两种状态。
-
-阻塞(BLOCKED):一般是被动的,在抢占资源中得不到资源,被动的挂起在内存,等待资源释放将其唤醒。线程被阻塞会释放CPU,不释放内存。
-
-等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
-
-超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
-
-终止(TERMINATED):表示该线程已经执行完毕。
-
-
-
-> 图片来源:Java并发编程的艺术
-
-### 中断
-
-线程中断即线程运行过程中被其他线程给打断了,它与 stop 最大的区别是:stop 是由系统强制终止线程,而线程中断则是给目标线程发送一个中断信号,如果目标线程没有接收线程中断的信号并结束线程,线程则不会终止,具体是否退出或者执行其他逻辑取决于目标线程。
-
-线程中断三个重要的方法:
-
-**1、java.lang.Thread#interrupt**
-
-调用目标线程的interrupt()方法,给目标线程发一个中断信号,线程被打上中断标记。
-
-**2、java.lang.Thread#isInterrupted()**
-
-判断目标线程是否被中断,不会清除中断标记。
-
-**3、java.lang.Thread#interrupted**
-
-判断目标线程是否被中断,会清除中断标记。
-
-```java
-private static void test2() {
- Thread thread = new Thread(() -> {
- while (true) {
- Thread.yield();
-
- // 响应中断
- if (Thread.currentThread().isInterrupted()) {
- System.out.println("Java技术栈线程被中断,程序退出。");
- return;
- }
- }
- });
- thread.start();
- thread.interrupt();
-}
-```
-
-### 常见方法
-
-#### join
-
-Thread.join(),在main中创建了thread线程,在main中调用了thread.join()/thread.join(long millis),main线程放弃cpu控制权,线程进入WAITING/TIMED_WAITING状态,等到thread线程执行完才继续执行main线程。
-
-```java
-public final void join() throws InterruptedException {
- join(0);
-}
-```
-
-#### yield
-
-Thread.yield(),一定是当前线程调用此方法,当前线程放弃获取的CPU时间片,但不释放锁资源,由运行状态变为就绪状态,让OS再次选择线程。作用:让相同优先级的线程轮流执行,但并不保证一定会轮流执行。实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会导致阻塞。该方法与sleep()类似,只是不能由用户指定暂停多长时间。
-
-```java
-public static native void yield(); //static方法
-```
-
-#### sleep
-
-Thread.sleep(long millis),一定是当前线程调用此方法,当前线程进入TIMED_WAITING状态,让出cpu资源,但不释放对象锁,指定时间到后又恢复运行。作用:给其它线程执行机会的最佳方式。
-
-```java
-public static native void sleep(long millis) throws InterruptedException;//static方法
-```
-
-#### wait()和sleep()的区别
-
-相同点:
-
-1. 使当前线程暂停运行,把机会交给其他线程
-2. 任何线程在等待期间被中断都会抛出InterruptedException
-
-不同点:
-
-1. wait() 是Object超类中的方法;而sleep()是线程Thread类中的方法
-2. 对锁的持有不同,wait()会释放锁,而sleep()并不释放锁
-3. 唤醒方法不完全相同,wait() 依靠notify或者notifyAll 、中断、达到指定时间来唤醒;而sleep()到达指定时间被唤醒
-4. 调用obj.wait()需要先获取对象的锁,而 Thread.sleep()不用
-
-### 创建线程的方法
-
-- 通过扩展Thread类来创建多线程
-- 通过实现Runnable接口来创建多线程,可实现线程间的资源共享
-- 实现Callable接口,通过FutureTask接口创建线程。
-- 使用Executor框架来创建线程池。
-
-**继承 Thread 创建线程**代码如下。run()方法是由jvm创建完操作系统级线程后回调的方法,不可以手动调用,手动调用相当于调用普通方法。
-
-```java
-/**
- * @author: 程序员大彬
- * @time: 2021-09-11 10:15
- */
-public class MyThread extends Thread {
- public MyThread() {
- }
-
- @Override
- public void run() {
- for (int i = 0; i < 10; i++) {
- System.out.println(Thread.currentThread() + ":" + i);
- }
- }
-
- public static void main(String[] args) {
- MyThread mThread1 = new MyThread();
- MyThread mThread2 = new MyThread();
- MyThread myThread3 = new MyThread();
- mThread1.start();
- mThread2.start();
- myThread3.start();
- }
-}
-```
-
-**Runnable 创建线程代码**:
-
-```java
-/**
- * @author: 程序员大彬
- * @time: 2021-09-11 10:04
- */
-public class RunnableTest {
- public static void main(String[] args){
- Runnable1 r = new Runnable1();
- Thread thread = new Thread(r);
- thread.start();
- System.out.println("主线程:["+Thread.currentThread().getName()+"]");
- }
-}
-
-class Runnable1 implements Runnable{
- @Override
- public void run() {
- System.out.println("当前线程:"+Thread.currentThread().getName());
- }
-}
-```
-
-实现Runnable接口比继承Thread类所具有的优势:
-
-1. 资源共享,适合多个相同的程序代码的线程去处理同一个资源
-2. 可以避免java中的单继承的限制
-3. 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类
-
-**Callable 创建线程代码**:
-
-```java
-/**
- * @author: 程序员大彬
- * @time: 2021-09-11 10:21
- */
-public class CallableTest {
- public static void main(String[] args) {
- Callable1 c = new Callable1();
-
- //异步计算的结果
- FutureTask result = new FutureTask<>(c);
-
- new Thread(result).start();
-
- try {
- //等待任务完成,返回结果
- int sum = result.get();
- System.out.println(sum);
- } catch (InterruptedException | ExecutionException e) {
- e.printStackTrace();
- }
- }
-
-}
-
-class Callable1 implements Callable {
-
- @Override
- public Integer call() throws Exception {
- int sum = 0;
-
- for (int i = 0; i <= 100; i++) {
- sum += i;
- }
- return sum;
- }
-}
-```
-
-**使用 Executor 创建线程代码**:
-
-```java
-/**
- * @author: 程序员大彬
- * @time: 2021-09-11 10:44
- */
-public class ExecutorsTest {
- public static void main(String[] args) {
- //获取ExecutorService实例,生产禁用,需要手动创建线程池
- ExecutorService executorService = Executors.newCachedThreadPool();
- //提交任务
- executorService.submit(new RunnableDemo());
- }
-}
-
-class RunnableDemo implements Runnable {
- @Override
- public void run() {
- System.out.println("大彬");
- }
-}
-```
-
-
-
-## 线程间通信
-
-### volatile
-
-volatile是轻量级的同步机制,volatile保证变量对所有线程的可见性,不保证原子性。
-
-1. 当对volatile变量进行写操作的时候,JVM会向处理器发送一条LOCK前缀的指令,将该变量所在缓存行的数据写回系统内存。
-2. 由于缓存一致性协议,每个处理器通过嗅探在总线上传播的数据来检查自己的缓存是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行置为无效状态,当处理器对这个数据进行修改操作的时候,会重新从系统内存中把数据读到处理器缓存中。
-
-MESI(缓存一致性协议):当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时,就会从内存重新读取。
-
-volatile关键字的两个作用:
-
-1. 保证了不同线程对共享变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
-2. 禁止进行指令重排序。
-
-指令重排序是JVM为了优化指令,提高程序运行效率,在不影响单线程程序执行结果的前提下,尽可能地提高并行度。Java编译器会在生成指令系列时在适当的位置会插入`内存屏障`指令来禁止处理器重排序。插入一个内存屏障,相当于告诉CPU和编译器先于这个命令的必须先执行,后于这个命令的必须后执行。对一个volatile字段进行写操作,Java内存模型将在写操作后插入一个写屏障指令,这个指令会把之前的写入值都刷新到内存。
-
-### synchronized
-
-保证线程对变量访问的可见性和排他性。synchronized 详细内容见下文锁部分。
-
-### 等待通知机制
-
-wait/notify为 Object 对象的方法,调用wait/notify需要先获得对象的锁。对象调用wait之后线程释放锁,将线程放到对象的等待队列,当通知线程调用此对象的notify()方法后,等待线程并不会立即从wait返回,需要等待通知线程释放锁(通知线程执行完同步代码块),等待队列里的线程获取锁,获取锁成功才能从wait()方法返回,即从wait方法返回前提是线程获得锁。
-
-等待通知机制依托于同步机制,目的是确保等待线程从wait方法返回时能感知到通知线程对对象的变量值的修改。
-
-
-
-## 锁
-
-### synchronized
-
-较常用的用于保证线程安全的方式。当一个线程获取到锁时,其他线程都会被阻塞住,当持有锁的线程释放锁之后会唤醒这些线程,被唤醒的线程才有机会获取到锁。
-
-- 修饰实例方法,作用于当前对象实例加锁,进入同步代码前要获得当前对象实例的锁
-- 修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁(类的字节码文件)
-- 修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码块前要获得给定对象的锁
-
-获取了类锁的线程和获取了对象锁的线程是不冲突的。
-
-#### 释放锁
-
-当方法或者代码块执行完毕后会自动释放锁,不需要做任何的操作。
-当一个线程执行的代码出现异常时,其所持有的锁会自动释放。
-
-#### 实现原理
-
-synchronized通过对象内部的监视器锁(monitor)实现。每个对象都有一个monitor,当对象的monitor被持有时,则它处于锁定的状态。
-
-**代码块的同步**是使用monitorenter和monitorexit指令实现的,monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处或异常处。
-
-```java
-public class SynchronizedDemo {
- public void method() {
- synchronized (this) {
- System.out.println("method start");
- }
- }
-}
-```
-
-线程访问同步块时,先执行monitorenter指令时尝试获取monitor,过程如下:
-
-1. 如果monitor的进入数entry count为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。
-2. 如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1。
-3. 如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor。
-
-线程退出同步块时会执行monitorexit指令,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor。
-
-Synchronized底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。
-
-
-
-**方法的同步**不是通过添加monitorenter和monitorexit指令来完成,而是在其常量池中添加了ACC_SYNCHRONIZED标识符。JVM就是根据该标识符来实现方法的同步的:当线程调用方法时,会先检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,说明此方法是同步方法,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他线程无法再获得同一个monitor对象。
-
-```java
-public class SynchronizedMethod {
- public synchronized void method() {
- System.out.println("Hello World!");
- }
-}
-```
-
-
-
-#### 锁的状态
-
-Synchronized是通过对象内部的监视器来实现的。但是监视器锁本质又是依赖于底层的操作系统的Mutex Lock来实现的。而操作系统实现线程之间的切换这就需要从用户态转换到核心态,这个成本非常高,状态之间的转换需要相对比较长的时间。这种依赖于操作系统Mutex Lock所实现的锁我们称之为重量级锁。
-
-JDK1.6中为了减少获得锁和释放锁带来的性能消耗,引入了偏向锁、轻量级锁、自旋锁、适应性自旋锁、锁消除、锁粗化等技术来减少锁操作的开销。
-
-synchronized锁主要存在四种状态,依次是:偏向锁状态、轻量级锁状态、重量级锁状态,他们会随着竞争的激烈而逐渐升级。锁可以升级不可降级,这种策略是为了提高获得锁和释放锁的效率。
-
-- 偏向锁:当线程访问同步块并获取锁时,会在对象头和锁记录中存储锁偏向的线程id,以后该线程进入和退出同步块时,只需简单测试一下对象头的mark word中是否存储着指向当前线程的偏向锁,如果测试成功,则线程获取锁成功,否则,需再测试一下mark word中偏向锁标识是否是1,是的话则使用CAS操作竞争锁。如果竞争成功,则将Mark Word中线程ID设置为当前线程ID,如果CAS获取偏向锁失败,则表示有竞争。当到达全局安全点时获得偏向锁的线程被挂起,偏向锁升级为轻量级锁,然后被阻塞在安全点的线程继续往下执行同步代码。
- **偏向锁偏向于第一个获得它的线程,如果程序运行过程,该锁没有被其他线程获取,那么持有偏向锁的线程就不需要进行同步。引入偏向锁是为了在无多线程竞争的情况下尽量减少不必要的轻量级锁执行的开销,因为轻量级锁的获取及释放使用了多次CAS原子指令,而偏向锁只在置换ThreadID的时候使用一次CAS原子指令。当存在锁竞争的时候,偏向锁会升级为轻量级锁。**
- 适用场景:在锁无竞争的情况下使用,在线程没有执行完同步代码之前,没有其它线程去竞争锁,一旦有了竞争就升级为轻量级锁,升级为轻量级锁的时候需要撤销偏向锁,会做很多额外操作,导致性能下降。
-
-- 轻量级锁
- 加锁过程:线程执行同步块之前,JVM会先在当前线程的栈帧中创建用于存储锁记录的空间,并将对象头的mark word复制到锁记录(displaced mark word)中,然后线程尝试使用cas将对象头的mark word替换为指向锁记录的指针。如果成功,则当前线程获得锁,否则表示有其他线程竞争锁,当前线程便尝试使用自旋来获得锁。当自旋超过一定的次数,或者一个线程在持有锁,一个在自旋,又有第三个来访时,轻量级锁升级为重量级锁。
- 解锁过程:使用原子的cas操作将displaced mark word替换回到对象头,如果成功则解锁成功,否则表明有锁竞争,锁会膨胀成重量级锁。
-
- **在没有多线程竞争的前提下,使用轻量级锁可以减少传统的重量级锁使用操作系统互斥量(申请互斥锁)产生的性能消耗,因为使用轻量级锁时,不需要申请互斥量。另外,轻量级锁的加锁和解锁都用到了CAS操作。如果没有竞争,轻量级锁使用 CAS 操作避免了使用互斥操作的开销。但如果存在锁竞争,除了互斥量开销外,还会额外发生CAS操作,因此在有锁竞争的情况下,轻量级锁比传统的重量级锁更慢!如果锁竞争激烈,那么轻量级锁将很快膨胀为重量级锁!**
-
-- 重量级锁:当一个线程获取到锁时,其他线程都会被阻塞住,当持有锁的线程释放锁之后会唤醒这些线程,被唤醒的线程才有机会获取到锁。
-
- synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中,保证了可见性。
-
-- 自旋锁:一般线程持有锁的时间都不是太长,所以仅仅为了这一点时间去挂起线程/恢复线程比较浪费资源。自旋锁就是让该线程等待一段时间,执行一段无意义的循环,不会被立即挂起,看持有锁的线程是否会很快释放锁。如果持有锁的线程很快就释放了锁,那么自旋的效率就非常好,反之,自旋的线程就会白白消耗掉处理的资源,这样反而会带来性能上的浪费。所以自旋的次数必须要有一个限度,如果自旋超过了限定次数仍然没有获取到锁,则应该被挂起。
-
-- 自适应自旋锁:JDK 1.6引入了更加聪明的自旋锁,即自适应自旋锁。所谓自适应就意味着自旋的次数不再是固定的,它是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。
-
-- 锁消除:虚拟机即使编译器在运行时,如果检测到那些共享数据不可能存在竞争,那么就执行锁消除。
-
-- 锁粗化:如果一系列的连续操作都对同一个对象反复加锁和解锁,那么会带来很多不必要的性能消耗,使用锁粗化减少锁操作的开销。
-
-### ReentrantLock
-
-重入锁,支持一个线程对资源的重复加锁。该锁的还支持设置获取锁时的公平和非公平性。
-
-使用lock时需要在try finally块进行解锁:
-
-```java
-public static final Object get(String key) {
- r.lock();
- try {
- return map.get(key);
- } finally {
- r.unlock();
- }
-}
-```
-
-#### 原理
-
-ReentrantLock是通过组合自定义同步器来实现锁的获取与释放。当线程尝试获取同步状态时,首先判断当前线程是否为获取锁的线程来决定获取操作是否成功,如果是获取锁的线程再次请求,则将同步状态值进行增加并返回true,表示获取同步状态成功。获取同步状态失败,则该线程会被构造成node节点放到AQS同步队列中。
-
-如果锁被获取了n次,那么前n-1次 tryRelease(int releases)方法必须返回false,第n次调用tryRelease()之后,同步状态完全释放(值为0),才会返回true。
-
-### ReentrantLock和synchronized区别
-
-1. 使用synchronized关键字实现同步,线程执行完同步代码块会自动释放锁,而ReentrantLock需要手动释放锁。
-2. synchronized是非公平锁,ReentrantLock可以设置为公平锁。
-3. ReentrantLock上等待获取锁的线程是可中断的,线程可以放弃等待锁。而synchonized会无限期等待下去。
-4. ReentrantLock 可以设置超时获取锁。在指定的截止时间之前获取锁,如果截止时间到了还没有获取到锁,则返回。
-5. ReentrantLock 的 tryLock() 方法可以尝试非阻塞的获取锁,调用该方法后立刻返回,如果能够获取则返回true,否则返回false。
-
-### 锁的分类
-
-#### 公平锁与非公平锁
-
-按照线程访问顺序获取对象锁。synchronized 是非公平锁, Lock 默认是非公平锁,可以设置为公平锁,公平锁会影响性能。
-
-```java
-public ReentrantLock() {
- sync = new NonfairSync();
-}
-
-public ReentrantLock(boolean fair) {
- sync = fair ? new FairSync() : new NonfairSync();
-}
-```
-
-#### 共享式与独占式锁
-
-共享式与独占式的最主要区别在于:同一时刻独占式只能有一个线程获取同步状态,而共享式在同一时刻可以有多个线程获取同步状态。例如读操作可以有多个线程同时进行,而写操作同一时刻只能有一个线程进行写操作,其他操作都会被阻塞。
-
-#### 悲观锁与乐观锁
-
-悲观锁,每次访问资源都会加锁,执行完同步代码释放锁,synchronized 和 ReentrantLock 属于悲观锁。
-
-乐观锁,不会锁定资源,所有的线程都能访问并修改同一个资源,如果没有冲突就修改成功并退出,否则就会继续循环尝试。乐观锁最常见的实现就是CAS。
-
-乐观锁一般来说有以下2种方式:
-
-1. 使用数据版本记录机制实现,这是乐观锁最常用的一种实现方式。给数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的version字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据。
-2. 使用时间戳。数据库表增加一个字段,字段类型使用时间戳(timestamp),和上面的version类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突。
-
-适用场景:
-
-- 悲观锁适合写操作多的场景。
-- 乐观锁适合读操作多的场景,不加锁可以提升读操作的性能。
-
-##### CAS
-
-CAS全称 Compare And Swap,比较与交换,是乐观锁的主要实现方式。CAS 在不使用锁的情况下实现多线程之间的变量同步。ReentrantLock 内部的 AQS 和原子类内部都使用了 CAS。
-
-CAS算法涉及到三个操作数:
-
-- 需要读写的内存值 V。
-- 进行比较的值 A。
-- 要写入的新值 B。
-
-只有当 V 的值等于 A 时,才会使用原子方式用新值B来更新V的值,否则会继续重试直到成功更新值。
-
-以 AtomicInteger 为例,AtomicInteger 的 getAndIncrement()方法底层就是CAS实现,关键代码是 `compareAndSwapInt(obj, offset, expect, update)`,其含义就是,如果`obj`内的`value`和`expect`相等,就证明没有其他线程改变过这个变量,那么就更新它为`update`,如果不相等,那就会继续重试直到成功更新值。
-
-CAS 三大问题:
-
-1. **ABA问题**。CAS需要在操作值的时候检查内存值是否发生变化,没有发生变化才会更新内存值。但是如果内存值原来是A,后来变成了B,然后又变成了A,那么CAS进行检查时会发现值没有发生变化,但是实际上是有变化的。ABA问题的解决思路就是在变量前面添加版本号,每次变量更新的时候都把版本号加一,这样变化过程就从`A-B-A`变成了`1A-2B-3A`。
-
- JDK从1.5开始提供了AtomicStampedReference类来解决ABA问题,原子更新带有版本号的引用类型。
-
-2. **循环时间长开销大**。CAS操作如果长时间不成功,会导致其一直自旋,给CPU带来非常大的开销。
-
-3. **只能保证一个共享变量的原子操作**。对一个共享变量执行操作时,CAS能够保证原子操作,但是对多个共享变量操作时,CAS是无法保证操作的原子性的。
-
- Java从1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性,可以把多个变量放在一个对象里来进行CAS操作。
-
-
-
-## 并发工具
-
-在JDK的并发包里提供了几个非常有用的并发工具类。CountDownLatch、CyclicBarrier和Semaphore工具类提供了一种并发流程控制的手段。
-
-### CountDownLatch
-
-CountDownLatch用于某个线程等待其他线程**执行完任务**再执行,与thread.join()功能类似。常见的应用场景是开启多个线程同时执行某个任务,等到所有任务执行完再执行特定操作,如汇总统计结果。
-
-```
-public class CountDownLatchDemo {
- static final int N = 4;
- static CountDownLatch latch = new CountDownLatch(N);
-
- public static void main(String[] args) throws InterruptedException {
-
- for(int i = 0; i < N; i++) {
- new Thread(new Thread1()).start();
- }
-
- latch.await(1000, TimeUnit.MILLISECONDS); //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行;等待timeout时间后count值还没变为0的话就会继续执行
- System.out.println("task finished");
- }
-
- static class Thread1 implements Runnable {
-
- @Override
- public void run() {
- try {
- System.out.println(Thread.currentThread().getName() + "starts working");
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- } finally {
- latch.countDown();
- }
- }
- }
-}
-```
-
-运行结果:
-
-```java
-Thread-0starts working
-Thread-1starts working
-Thread-2starts working
-Thread-3starts working
-task finished
-```
-
-### CyclicBarrier
-
-CyclicBarrier(同步屏障),用于一组线程互相等待到某个状态,然后这组线程再**同时**执行。
-
-```java
-public CyclicBarrier(int parties, Runnable barrierAction) {
-}
-public CyclicBarrier(int parties) {
-}
-```
-
-参数parties指让多少个线程或者任务等待至某个状态;参数barrierAction为当这些线程都达到某个状态时会执行的内容。
-
-```java
-public class CyclicBarrierTest {
- // 请求的数量
- private static final int threadCount = 10;
- // 需要同步的线程数量
- private static final CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
-
- public static void main(String[] args) throws InterruptedException {
- // 创建线程池
- ExecutorService threadPool = Executors.newFixedThreadPool(10);
-
- for (int i = 0; i < threadCount; i++) {
- final int threadNum = i;
- Thread.sleep(1000);
- threadPool.execute(() -> {
- try {
- test(threadNum);
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (BrokenBarrierException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- });
- }
- threadPool.shutdown();
- }
-
- public static void test(int threadnum) throws InterruptedException, BrokenBarrierException {
- System.out.println("threadnum:" + threadnum + "is ready");
- try {
- /**等待60秒,保证子线程完全执行结束*/
- cyclicBarrier.await(60, TimeUnit.SECONDS);
- } catch (Exception e) {
- System.out.println("-----CyclicBarrierException------");
- }
- System.out.println("threadnum:" + threadnum + "is finish");
- }
-
-}
-```
-
-运行结果如下,可以看出CyclicBarrier是可以重用的:
-
-```java
-threadnum:0is ready
-threadnum:1is ready
-threadnum:2is ready
-threadnum:3is ready
-threadnum:4is ready
-threadnum:4is finish
-threadnum:3is finish
-threadnum:2is finish
-threadnum:1is finish
-threadnum:0is finish
-threadnum:5is ready
-threadnum:6is ready
-...
-```
-
-当四个线程都到达barrier状态后,会从四个线程中选择一个线程去执行Runnable。
-
-### CyclicBarrier和CountDownLatch区别
-
-CyclicBarrier 和 CountDownLatch 都能够实现线程之间的等待。
-
-CountDownLatch用于某个线程等待其他线程**执行完任务**再执行。CyclicBarrier用于一组线程互相等待到某个状态,然后这组线程再**同时**执行。
-CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset()方法重置,可用于处理更为复杂的业务场景。
-
-### Semaphore
-
-Semaphore类似于锁,它用于控制同时访问特定资源的线程数量,控制并发线程数。
-
-```
-public class SemaphoreDemo {
- public static void main(String[] args) {
- final int N = 7;
- Semaphore s = new Semaphore(3);
- for(int i = 0; i < N; i++) {
- new Worker(s, i).start();
- }
- }
-
- static class Worker extends Thread {
- private Semaphore s;
- private int num;
- public Worker(Semaphore s, int num) {
- this.s = s;
- this.num = num;
- }
-
- @Override
- public void run() {
- try {
- s.acquire();
- System.out.println("worker" + num + " using the machine");
- Thread.sleep(1000);
- System.out.println("worker" + num + " finished the task");
- s.release();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
-}
-```
-
-运行结果如下,可以看出并非按照线程访问顺序获取资源的锁,即
-
-```java
-worker0 using the machine
-worker1 using the machine
-worker2 using the machine
-worker2 finished the task
-worker0 finished the task
-worker3 using the machine
-worker4 using the machine
-worker1 finished the task
-worker6 using the machine
-worker4 finished the task
-worker3 finished the task
-worker6 finished the task
-worker5 using the machine
-worker5 finished the task
-```
-
-
-
-## 原子类
-
-### 基本类型原子类
-
-使用原子的方式更新基本类型
-
-- AtomicInteger:整型原子类
-- AtomicLong:长整型原子类
-- AtomicBoolean :布尔型原子类
-
-AtomicInteger 类常用的方法:
-
-```java
-public final int get() //获取当前的值
-public final int getAndSet(int newValue)//获取当前的值,并设置新的值
-public final int getAndIncrement()//获取当前的值,并自增
-public final int getAndDecrement() //获取当前的值,并自减
-public final int getAndAdd(int delta) //获取当前的值,并加上预期的值
-boolean compareAndSet(int expect, int update) //如果输入的数值等于预期值,则以原子方式将该值设置为输入值(update)
-public final void lazySet(int newValue)//最终设置为newValue,使用 lazySet 设置之后可能导致其他线程在之后的一小段时间内还是可以读到旧的值。
-```
-
-AtomicInteger 类主要利用 CAS (compare and swap) 保证原子操作,从而避免加锁的高开销。
-
-### 数组类型原子类
-
-使用原子的方式更新数组里的某个元素
-
-- AtomicIntegerArray:整形数组原子类
-- AtomicLongArray:长整形数组原子类
-- AtomicReferenceArray :引用类型数组原子类
-
-AtomicIntegerArray 类常用方法:
-
-```java
-public final int get(int i) //获取 index=i 位置元素的值
-public final int getAndSet(int i, int newValue)//返回 index=i 位置的当前的值,并将其设置为新值:newValue
-public final int getAndIncrement(int i)//获取 index=i 位置元素的值,并让该位置的元素自增
-public final int getAndDecrement(int i) //获取 index=i 位置元素的值,并让该位置的元素自减
-public final int getAndAdd(int i, int delta) //获取 index=i 位置元素的值,并加上预期的值
-boolean compareAndSet(int i, int expect, int update) //如果输入的数值等于预期值,则以原子方式将 index=i 位置的元素值设置为输入值(update)
-public final void lazySet(int i, int newValue)//最终 将index=i 位置的元素设置为newValue,使用 lazySet 设置之后可能导致其他线程在之后的一小段时间内还是可以读到旧的值。
-```
-
-### 引用类型原子类
-
-- AtomicReference:引用类型原子类
-- AtomicStampedReference:带有版本号的引用类型原子类。该类将整数值与引用关联起来,可用于解决原子的更新数据和数据的版本号,可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题。
-- AtomicMarkableReference :原子更新带有标记的引用类型。该类将 boolean 标记与引用关联起来
-
-
-
-## AQS
-
-AQS定义了一套多线程访问共享资源的同步器框架,许多并发工具的实现都依赖于它,如常用的ReentrantLock/Semaphore/CountDownLatch。
-
-### 原理
-
-AQS使用一个volatile的int类型的成员变量state来表示同步状态,通过CAS修改同步状态的值。
-
-```java
-private volatile int state;//共享变量,使用volatile修饰保证线程可见性
-```
-
-同步器依赖内部的同步队列(一个FIFO双向队列)来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态(独占或共享 )构造成为一个节点(Node)并将其加入同步队列并进行自旋,当同步状态释放时,会把首节中的后继节点对应的线程唤醒,使其再次尝试获取同步状态。
-
-
-
-
-
-## Condition
-
-任意一个Java对象,都拥有一组监视器方法(定义在java.lang.Object上),主要包括wait()、wait(long timeout)、notify()以及notifyAll()方法,使用这些方法的前提是已经获取对象的锁,和 synchronized 配合使用。Condition接口也提供了类似Object的监视器方法,与Lock配合可以实现等待/通知模式。Condition是依赖Lock对象。
-
-```java
-Lock lock = new ReentrantLock();
-Condition condition = lock.newCondition();
-public void conditionWait() throws InterruptedException {
- lock.lock();
- try {
- condition.await();
- } finally {
- lock.unlock();
- }
-}
-public void conditionSignal() throws InterruptedException {
- lock.lock();
- try {
- condition.signal();
- } finally {
- lock.unlock();
- }
-}
-```
-
-一般将Condition对象作为成员变量。当调用await()方法后,当前线程会释放锁进入等待队列。其他线程调用Condition对象的signal()方法,唤醒等待队列首节点的线程。
-
-### 实现原理
-
-每个Condition对象都包含着一个等待队列,如果一个线程成功获取了锁之后调用了Condition.await()方法,那么该线程将会释放同步状态、唤醒同步队列中的后继节点,然后构造成节点加入等待队列。只有当线程再次获取Condition相关联的锁之后,才能从await()方法返回。
-
-
-
-> 图片来源:Java并发编程的艺术
-
-在Object的监视器模型上,一个对象拥有一个同步队列和等待队列。Lock通过AQS实现,AQS可以有多个Condition,所以Lock拥有一个同步队列和多个等待队列。
-
-
-
-> 图片来源:Java并发编程的艺术
-
-线程获取了锁之后,调用Condition的signal()方法,会将等待队列的队首节点移到同步队列中,然后该节点的线程会尝试去获取同步状态。成功获取同步状态之后,线程将await()方法返回。
-
-
-
-> 图片来源:Java并发编程的艺术
-
-
-
-## 其他
-
-### Daemon Thread
-
-在Java中有两类线程:
-
-- User Thread(用户线程)
-- Daemon Thread(守护线程)
-
-只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。
-
-Daemon的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是垃圾收集。
-
-将线程转换为守护线程可以通过调用Thread对象的setDaemon(true)方法来实现。
-
-
-
-## 参考资料
-
-[线程中断](https://zhuanlan.zhihu.com/p/45667127)
-
-[synchronized实现原理](https://www.cnblogs.com/paddix/p/5367116.html)
-
-[指令重排导致单例模式失效](https://blog.csdn.net/jiyiqinlovexx/article/details/50989328)
-
-
-
-> 本文已经收录到github仓库,此仓库用于分享Java相关知识总结,包括Java基础、MySQL、Spring Boot、MyBatis、Redis、RabbitMQ、计算机网络、数据结构与算法等等,欢迎大家提pr和star!
->
-> github地址:https://github.com/Tyson0314/Java-learning
->
-> 如果github访问不了,可以访问gitee仓库。
->
-> gitee地址:https://gitee.com/tysondai/Java-learning
diff --git "a/Java/\351\233\206\345\220\210.md" "b/Java/\351\233\206\345\220\210.md"
deleted file mode 100644
index d626f12..0000000
--- "a/Java/\351\233\206\345\220\210.md"
+++ /dev/null
@@ -1,479 +0,0 @@
-
-
-
-
-- [ArrayList 简介](#arraylist-%E7%AE%80%E4%BB%8B)
- - [Arraylist 和 Vector 的区别](#arraylist-%E5%92%8C-vector-%E7%9A%84%E5%8C%BA%E5%88%AB)
- - [Arraylist 与 LinkedList 区别](#arraylist-%E4%B8%8E-linkedlist-%E5%8C%BA%E5%88%AB)
-- [Map](#map)
- - [HashMap](#hashmap)
- - [hash算法](#hash%E7%AE%97%E6%B3%95)
- - [resize](#resize)
- - [put](#put)
- - [红黑树](#%E7%BA%A2%E9%BB%91%E6%A0%91)
- - [HashMap和HashTable](#hashmap%E5%92%8Chashtable)
- - [LinkedHashMap](#linkedhashmap)
- - [TreeMap](#treemap)
-- [Set](#set)
-- [Queue](#queue)
-- [Iterator](#iterator)
- - [ListIterator](#listiterator)
-- [并发容器](#%E5%B9%B6%E5%8F%91%E5%AE%B9%E5%99%A8)
- - [ConcurrentHashMap](#concurrenthashmap)
- - [put](#put-1)
- - [扩容](#%E6%89%A9%E5%AE%B9)
- - [ConcurrentHashMap 和 Hashtable](#concurrenthashmap-%E5%92%8C-hashtable)
- - [CopyOnWrite](#copyonwrite)
- - [ConcurrentLinkedQueue](#concurrentlinkedqueue)
- - [阻塞队列](#%E9%98%BB%E5%A1%9E%E9%98%9F%E5%88%97)
- - [方法](#%E6%96%B9%E6%B3%95)
- - [JDK提供的阻塞队列](#jdk%E6%8F%90%E4%BE%9B%E7%9A%84%E9%98%BB%E5%A1%9E%E9%98%9F%E5%88%97)
- - [原理](#%E5%8E%9F%E7%90%86)
-
-
-
-## ArrayList 简介
-
-`ArrayList` 的底层是动态数组,它的容量能动态增长。在添加大量元素前,应用可以使用`ensureCapacity`操作增加 `ArrayList` 实例的容量。ArrayList 继承了 AbstractList ,并实现了 List 接口。
-
-### Arraylist 和 Vector 的区别
-
-1. ArrayList在内存不够时默认是扩展50% + 1个,Vector是默认扩展1倍。
-2. Vector属于线程安全级别的,但是大多数情况下不使用Vector,因为操作Vector效率比较低。
-
-### Arraylist 与 LinkedList 区别
-
-1. ArrayList基于动态数组实现;LinkedList基于链表实现。
-2. 对于随机index访问的get和set方法,ArrayList的速度要优于LinkedList。因为ArrayList直接通过数组下标直接找到元素;LinkedList要移动指针遍历每个元素直到找到为止。
-3. 新增和删除元素,LinkedList的速度要优于ArrayList。因为ArrayList在新增和删除元素时,可能扩容和复制数组;LinkedList实例化对象需要时间外,只需要修改指针即可。
-
-## Map
-
-以 Map 结尾的类都实现了 Map 接口,其他所有的类都实现了 Collection 接口。
-
-
-
-### HashMap
-
-HashMap 使用数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的, 链表长度大于8(TREEIFY_THRESHOLD)时,会把链表转换为红黑树,红黑树节点个数小于6(UNTREEIFY_THRESHOLD)时才转化为链表,防止频繁的转化。
-
-#### hash算法
-
-Hash算法:取key的hashCode值、高位运算、取模运算。
-
-```
-h=key.hashCode() //第一步 取hashCode值
-h^(h>>>16) //第二步 高位参与运算,减少冲突
-return h&(length-1); //第三步 取模运算
-```
-
-在JDK1.8的实现中,优化了高位运算的算法,通过hashCode()的高16位异或低16位实现的:这么做可以在数组比较小的时候,也能保证考虑到高低位都参与到Hash的计算中,可以减少冲突,同时不会有太大的开销。
-
-#### resize
-
-1.8扩容机制:当元素个数大于threshold时,会进行扩容,使用2倍容量的数组代替原有数组。采用尾插入的方式将原数组元素拷贝到新数组。1.8扩容之后链表元素相对位置没有变化,而1.7扩容之后链表元素会倒置。
-
-1.7链表新节点采用的是头插法,这样在线程一扩容迁移元素时,会将元素顺序改变,导致两个线程中出现元素的相互指向而形成循环链表,1.8采用了尾插法,避免了这种情况的发生。
-
-原数组的元素在重新计算hash之后,因为数组容量n变为2倍,那么n-1的mask范围在高位多1bit。在元素拷贝过程不需要重新计算元素在数组中的位置,只需要看看原来的hash值新增的那个bit是1还是0,是0的话索引没变,是1的话索引变成“原索引+oldCap”(根据`e.hash & (oldCap - 1) == 0`判断) 。这样可以省去重新计算hash值的时间,而且由于新增的1bit是0还是1可以认为是随机的,因此resize的过程会均匀的把之前的冲突的节点分散到新的bucket。
-
-#### put
-
-put方法流程:
-
-1. 如果table没有初始化就先进行初始化过程
-2. 使用hash算法计算key的索引
-3. 判断索引处有没有存在元素,没有就直接插入
-4. 如果索引处存在元素,则遍历插入,有两种情况,一种是链表形式就直接遍历到尾端插入,一种是红黑树就按照红黑树结构插入
-5. 链表的数量大于阈值8,就要转换成红黑树的结构
-6. 添加成功后会检查是否需要扩容
-
-
-
-> 参考链接:
->
-> http://www.importnew.com/20386.html
->
-> https://www.cnblogs.com/yangming1996/p/7997468.html
->
-> https://coolshell.cn/articles/9606.htm(HashMap 死循环)
-
-
-
-#### 红黑树
-
-为什么使用红黑树而不使用AVL树?
-
-ConcurrentHashMap 在put的时候会加锁,使用红黑树插入速度更快,可以减少等待锁释放的时间。红黑树是对AVL树的优化,只要求部分平衡,用非严格的平衡来换取增删节点时候旋转次数的降低,提高了插入和删除的性能。
-
-### HashMap和HashTable
-
-HashMap和Hashtable都实现了Map接口
-
-1. HashMap可以接受为null的键值(key)和值(value),key为null的键值对放在下标为0的头结点的链表中,而Hashtable则不行。
-2. HashMap是非线程安全的,HashTable是线程安全的。Jdk1.5提供了ConcurrentHashMap,它是HashTable的替代。
-3. Hashtable很多方法是同步方法,在单线程环境下它比HashMap要慢。
-4. 哈希值的使用不同,HashTable直接使用对象的hashCode。而HashMap重新计算hash值。
-
-### LinkedHashMap
-
-HashMap是无序的,迭代HashMap所得到元素的顺序并不是它们最初放到HashMap的顺序,即不能保持它们的插入顺序。
-
-LinkedHashMap继承于HashMap,是HashMap和LinkedList的融合体,具备两者的特性。每次put操作都会将entry插入到双向链表的尾部。
-
-
-
-### TreeMap
-
-TreeMap是一个能比较元素大小的Map集合,会对传入的key进行了大小排序。可以使用元素的自然顺序,也可以使用集合中自定义的比较器来进行排序。
-
-```java
-public class TreeMap
- extends AbstractMap
- implements NavigableMap, Cloneable, java.io.Serializable {
-}
-```
-
-TreeMap 的继承结构:
-
-
-
-**TreeMap的特点:**
-
-1. TreeMap是有序的key-value集合,通过红黑树实现。根据键的自然顺序进行排序或根据提供的Comparator进行排序。
-2. TreeMap继承了AbstractMap,实现了NavigableMap接口,支持一系列的导航方法,给定具体搜索目标,可以返回最接近的匹配项。如floorEntry()、ceilingEntry()分别返回小于等于、大于等于给定键关联的Map.Entry()对象,不存在则返回null。lowerKey()、floorKey、ceilingKey、higherKey()只返回关联的key。
-
-## Set
-
-HashSet 基于 HashMap 实现。放入HashSet中的元素实际上由HashMap的key来保存,而HashMap的value则存储了一个静态的Object对象。
-
-```java
-public class HashSet
- extends AbstractSet
- implements Set, Cloneable, java.io.Serializable {
- static final long serialVersionUID = -5024744406713321676L;
-
- private transient HashMap map; //基于HashMap实现
- //...
-}
-```
-
-
-
-## Queue
-
-ArrayDeque实现了双端队列,内部使用循环数组实现,默认大小为16。
-
-特点:
-
-1. 在两端添加、删除元素的效率较高
-
-2. 根据元素内容查找和删除的效率比较低。
-
-3. 没有索引位置的概念,不能根据索引位置进行操作。
-
-ArrayDeque和LinkedList都实现了Deque接口,如果只需要从两端进行操作,ArrayDeque效率更高一些。如果同时需要根据索引位置进行操作,或者经常需要在中间进行插入和删除(LinkedList有相应的 api,如add(int index, E e)),则应该选LinkedList。
-
-ArrayDeque和LinkedList都是线程不安全的,可以使用Collections工具类中synchronizedXxx()转换成线程同步。
-
-## Iterator
-
-Iterator模式用同一种逻辑来遍历集合。它可以把访问逻辑从不同类型的集合类中抽象出来,不需要了解集合内部实现便可以遍历集合元素,集合是数组还是链表实现的无所谓,统一使用 Iterator 提供的接口去遍历。
-
-主要有三个方法:hasNext()、next()和remove()。
-
-```java
- for(Iterator it = c.iterater(); it.hasNext(); ) { ... }
-```
-
-### ListIterator
-
-Iterator的增强版,增加了以下功能:
-
-1. ListIterator有add()方法,可以向List中添加对象。
-
-2. hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历。
-3. nextIndex()和previousIndex()定位索引位置。
-4. set() 实现对象的修改。
-
-```java
-public interface ListIterator extends Iterator {
- boolean hasNext();
-
- E next();
-
- boolean hasPrevious();
-
- E previous();
-
- int nextIndex();
-
- int previousIndex();
-
- void remove();
-
- void set(E var1);
-
- void add(E var1);
-}
-```
-
-
-
-## 并发容器
-
-JDK 提供的这些容器大部分在 `java.util.concurrent` 包中。
-
-- **ConcurrentHashMap:** 线程安全的 HashMap
-- **CopyOnWriteArrayList:** 线程安全的 List,在读多写少的场合性能非常好,远远好于 Vector.
-- **ConcurrentLinkedQueue:** 高效的并发队列,使用链表实现。可以看做一个线程安全的 LinkedList,这是一个非阻塞队列。
-- **BlockingQueue:** 阻塞队列接口,JDK 内部通过链表、数组等方式实现了这个接口。非常适合用于作为数据共享的通道。
-- **ConcurrentSkipListMap:** 跳表的实现。使用跳表的数据结构进行快速查找。
-
-### ConcurrentHashMap
-
-多线程环境下,使用Hashmap进行put操作会引起死循环,应该使用支持多线程的 ConcurrentHashMap。
-
-```java
-private static final int MAXIMUM_CAPACITY = 1 << 30;
-private static final int DEFAULT_CAPACITY = 16;
-static final int TREEIFY_THRESHOLD = 8;
-static final int UNTREEIFY_THRESHOLD = 6;
-static final int MIN_TREEIFY_CAPACITY = 64;
-static final int MOVED = -1; // 表示正在转移
-static final int TREEBIN = -2; // 表示已经转换成树
-static final int RESERVED = -3; // hash for transient reservations
-static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hash
-transient volatile Node[] table;//默认没初始化的数组,用来保存元素
-private transient volatile Node[] nextTable;//转移的时候用的数组
-/**
- * 用来控制表初始化和扩容的,默认值为0,当在初始化的时候指定了大小,这会将这个大小保存在sizeCtl中,大小为数组的0.75
- * 当为负的时候,说明表正在初始化或扩张,
- * -1表示初始化
- * -(1+n) n:表示活动的扩张线程
- */
-private transient volatile int sizeCtl;
-```JDK1.7中的ConcurrentHashmap主要使用Segment来实现减小锁粒度。Segment继承了ReentrantLock,所以它就是一种可重入锁。默认分配16个segment,允许16个线程并发执行。Segment维护了一个HashEntry**数组**,对于同一个Segment的操作才需考虑线程同步,不同的Segment则无需考虑。当要统计size时,比较统计前后modCount是否发生变化。如果没有变化,则直接返回size。否则,需要依次锁住所有的Segment来计算。当长度过长碰撞会很频繁,链表的增改删查操作都会消耗很长的时间,影响性能。
-```
-
-JDK1.8 ConcurrentHashMap取消了segment分段锁,而采用CAS和synchronized来保证并发安全。数据结构采用数组+链表/红黑二叉树。synchronized只锁定当前链表或红黑二叉树的首节点,相比1.7锁定HashEntry数组,锁粒度更小,支持更高的并发量。当链表长度过长时,Node会转换成TreeNode,提高查找速度。
-
-#### put
-
-在put的时候需要锁住Segment,保证并发安全。调用get的时候不加锁,因为node数组成员val和指针next是用volatile修饰的,更改后的值会立刻刷新到主存中,保证了可见性,node数组table也用volatile修饰,保证在运行过程对其他线程具有可见性。
-
-```java
-transient volatile Node[] table;
-
-static class Node implements Map.Entry {
- volatile V val;
- volatile Node next;
-}
-```
-
-put 操作流程:
-
-1. 如果table没有初始化就先进行初始化过程
-2. 使用hash算法计算key的位置
-3. 如果这个位置为空则直接CAS插入,如果不为空的话,则取出这个节点来
-4. 如果取出来的节点的hash值是MOVED(-1)的话,则表示当前正在对这个数组进行扩容,复制到新的数组,则当前线程也去帮助复制
-5. 如果这个节点,不为空,也不在扩容,则通过synchronized来加锁,进行添加操作,这里有两种情况,一种是链表形式就直接遍历到尾端插入或者覆盖掉相同的key,一种是红黑树就按照红黑树结构插入
-6. 链表的数量大于阈值8,就会转换成红黑树的结构或者进行扩容(table长度小于64)
-7. 添加成功后会检查是否需要扩容
-
-#### 扩容
-
-数组扩容transfer方法中会设置一个步长,表示一个线程处理的数组长度,最小值是16。在一个步长范围内只有一个线程会对其进行复制移动操作。
-
-#### ConcurrentHashMap 和 Hashtable
-
-区别:
-
-1. Hashtable通过使用synchronized修饰方法的方式来实现多线程同步,因此,Hashtable的同步会锁住整个数组。在高并发的情况下,性能会非常差。ConcurrentHashMap采用了更细粒度的锁来提高在并发情况下的效率。注:Synchronized容器(同步容器)也是通过synchronized关键字来实现线程安全,在使用的时候会对所有的数据加锁。
-2. Hashtable默认的大小为11,当达到阈值后,每次按照下面的公式对容量进行扩充:newCapacity = oldCapacity * 2 + 1。ConcurrentHashMap默认大小是16,扩容时容量扩大为原来的2倍。
-
-ConcurrentHashMap 和 Hashtable 的key和value不能为null。
-
-HashMap.java 部分源码:
-
-```java
- static final int hash(Object key) {
- int h;
- return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);//key为null时,hash值为0
- }
-```
-
-ConcurrentHashMap.java 部分源码:
-
-```java
- /** Implementation for put and putIfAbsent */
- final V putVal(K key, V value, boolean onlyIfAbsent) {
- if (key == null || value == null) throw new NullPointerException();
- int hash = spread(key.hashCode());
- int binCount = 0;
- ......
- }
-```
-
-ConcurrentHashmap和Hashtable都支持并发,当你通过get(k)获取对应的value时,如果获取到的是null时,无法判断是key 对应的 value 为 null,还是这个 key 从来没有做过映射,在多线程里面是模糊不清的,所以不让put null。HashMap用于非并发场景,可以通过contains(key)来判断是否存在key。而支持并发的Map在调用m.get(key)之后,再调用m.contains(key),两个调用之间可能有其他线程删除了key,得到的结果不准确,产生多线程安全问题。因此ConcurrentHashMap 和 Hashtable 的key和value不能为null。
-
-### CopyOnWrite
-
-写时复制。当我们往容器添加元素时,不直接往容器添加,而是先将当前容器进行复制,复制出一个新的容器,然后往新的容器添加元素,添加完元素之后,再将原容器的引用指向新容器。这样做的好处就是可以对CopyOnWrite容器进行并发的读而不需要加锁,因为当前容器不会被修改。
-
-```java
- public boolean add(E e) {
- final ReentrantLock lock = this.lock;
- lock.lock(); //add方法需要加锁
- try {
- Object[] elements = getArray();
- int len = elements.length;
- Object[] newElements = Arrays.copyOf(elements, len + 1); //复制新数组
- newElements[len] = e;
- setArray(newElements); //原容器的引用指向新容器
- return true;
- } finally {
- lock.unlock();
- }
- }
-```
-
-从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发容器,它们是CopyOnWriteArrayList和CopyOnWriteArraySet。
-
-CopyOnWriteArrayList中add方法添加的时候是需要加锁的,保证同步,避免了多线程写的时候复制出多个副本。读的时候不需要加锁,如果读的时候有其他线程正在向CopyOnWriteArrayList添加数据,还是可以读到旧的数据。
-
-**缺点:**
-
-- 内存占用问题。由于CopyOnWrite的写时复制机制,在进行写操作的时候,内存里会同时驻扎两个对象的内存。
-- CopyOnWrite容器不能保证数据的实时一致性,可能读取到旧数据。
-
-### ConcurrentLinkedQueue
-
-非阻塞队列。高效的并发队列,使用链表实现。可以看做一个线程安全的 LinkedList,通过 CAS 操作实现。
-
-如果对队列加锁的成本较高则适合使用无锁的 ConcurrentLinkedQueue 来替代。适合在对性能要求相对较高,同时有多个线程对队列进行读写的场景。
-
-**非阻塞队列中的几种主要方法:**
-add(E e) : 将元素e插入到队列末尾,如果插入成功,则返回true;如果插入失败(即队列已满),则会抛出异常;
-remove() :移除队首元素,若移除成功,则返回true;如果移除失败(队列为空),则会抛出异常;
-offer(E e) :将元素e插入到队列末尾,如果插入成功,则返回true;如果插入失败(即队列已满),则返回false;
-poll() :移除并获取队首元素,若成功,则返回队首元素;否则返回null;
-peek() :获取队首元素,若成功,则返回队首元素;否则返回null
-
-对于非阻塞队列,一般情况下建议使用offer、poll和peek三个方法,不建议使用add和remove方法。因为使用offer、poll和peek三个方法可以通过返回值判断操作成功与否,而使用add和remove方法却不能达到这样的效果。
-
-### 阻塞队列
-
-阻塞队列是java.util.concurrent包下重要的数据结构,BlockingQueue提供了线程安全的队列访问方式:当阻塞队列进行插入数据时,如果队列已满,线程将会阻塞等待直到队列非满;从阻塞队列取数据时,如果队列已空,线程将会阻塞等待直到队列非空。并发包下很多高级同步类的实现都是基于BlockingQueue实现的。BlockingQueue 适合用于作为数据共享的通道。
-
-使用阻塞算法的队列可以用一个锁(入队和出队用同一把锁)或两个锁(入队和出队用不同的锁)等方式来实现。
-
-阻塞队列和一般的队列的区别就在于:
-
-1. 多线程支持,多个线程可以安全的访问队列
-2. 阻塞操作,当队列为空的时候,消费线程会阻塞等待队列不为空;当队列满了的时候,生产线程就会阻塞直到队列不满。
-
-#### 方法
-
-| 方法\处理方式 | 抛出异常 | 返回特殊值 | 一直阻塞 | 超时退出 |
-| ------------- | --------- | ---------- | -------- | ------------------ |
-| 插入方法 | add(e) | offer(e) | put(e) | offer(e,time,unit) |
-| 移除方法 | remove() | poll() | take() | poll(time,unit) |
-| 检查方法 | element() | peek() | 不可用 | 不可用 |
-
-#### JDK提供的阻塞队列
-
-JDK 7 提供了7个阻塞队列,如下
-
-1、**ArrayBlockingQueue**
-
-有界阻塞队列,底层采用数组实现。ArrayBlockingQueue 一旦创建,容量不能改变。其并发控制采用可重入锁来控制,不管是插入操作还是读取操作,都需要获取到锁才能进行操作。此队列按照先进先出(FIFO)的原则对元素进行排序。默认情况下不能保证线程访问队列的公平性,参数`fair`可用于设置线程是否公平访问队列。为了保证公平性,通常会降低吞吐量。
-
-```java
-private static ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue(10,true);//fair
-```
-
-2、**LinkedBlockingQueue**
-
-LinkedBlockingQueue是一个用单向链表实现的有界阻塞队列,可以当做无界队列也可以当做有界队列来使用。通常在创建 LinkedBlockingQueue 对象时,会指定队列最大的容量。此队列的默认和最大长度为`Integer.MAX_VALUE`。此队列按照先进先出的原则对元素进行排序。与 ArrayBlockingQueue 相比起来具有更高的吞吐量。
-
-3、**PriorityBlockingQueue**
-
-支持优先级的**无界**阻塞队列。默认情况下元素采取自然顺序升序排列。也可以自定义类实现`compareTo()`方法来指定元素排序规则,或者初始化PriorityBlockingQueue时,指定构造参数`Comparator`来进行排序。
-
-PriorityBlockingQueue 只能指定初始的队列大小,后面插入元素的时候,如果空间不够的话会**自动扩容**。
-
-PriorityQueue 的线程安全版本。不可以插入 null 值,同时,插入队列的对象必须是可比较大小的(comparable),否则报 ClassCastException 异常。它的插入操作 put 方法不会 block,因为它是无界队列(take 方法在队列为空的时候会阻塞)。
-
-4、**DelayQueue**
-
-支持延时获取元素的无界阻塞队列。队列使用PriorityBlockingQueue来实现。队列中的元素必须实现Delayed接口,在创建元素时可以指定多久才能从队列中获取当前元素。只有在延迟期满时才能从队列中提取元素。
-
-5、**SynchronousQueue**
-
-不存储元素的阻塞队列,每一个put必须等待一个take操作,否则不能继续添加元素。支持公平访问队列。
-
-SynchronousQueue可以看成是一个传球手,负责把生产者线程处理的数据直接传递给消费者线程。队列本身不存储任何元素,非常适合传递性场景。SynchronousQueue的吞吐量高于LinkedBlockingQueue和ArrayBlockingQueue。
-
-6、**LinkedTransferQueue**
-
-由链表结构组成的无界阻塞TransferQueue队列。相对于其他阻塞队列,多了tryTransfer和transfer方法。
-
-transfer方法:如果当前有消费者正在等待接收元素(take或者待时间限制的poll方法),transfer可以把生产者传入的元素立刻传给消费者。如果没有消费者等待接收元素,则将元素放在队列的tail节点,并等到该元素被消费者消费了才返回。
-
-tryTransfer方法:用来试探生产者传入的元素能否直接传给消费者。如果没有消费者在等待,则返回false。和上述方法的区别是该方法无论消费者是否接收,方法立即返回。而transfer方法是必须等到消费者消费了才返回。
-
-#### 原理
-
-JDK使用通知模式实现阻塞队列。所谓通知模式,就是当生产者往满的队列里添加元素时会阻塞生产者,当消费者消费了一个队列中的元素后,会通知生产者当前队列可用。
-
-ArrayBlockingQueue使用Condition来实现:
-
-```java
-private final Condition notEmpty;
-private final Condition notFull;
-
-public ArrayBlockingQueue(int capacity, boolean fair) {
- if (capacity <= 0)
- throw new IllegalArgumentException();
- this.items = new Object[capacity];
- lock = new ReentrantLock(fair);
- notEmpty = lock.newCondition();
- notFull = lock.newCondition();
-}
-
-public E take() throws InterruptedException {
- final ReentrantLock lock = this.lock;
- lock.lockInterruptibly();
- try {
- while (count == 0) // 队列为空时,阻塞当前消费者
- notEmpty.await();
- return dequeue();
- } finally {
- lock.unlock();
- }
-}
-
-public void put(E e) throws InterruptedException {
- checkNotNull(e);
- final ReentrantLock lock = this.lock;
- lock.lockInterruptibly();
- try {
- while (count == items.length)
- notFull.await();
- enqueue(e);
- } finally {
- lock.unlock();
- }
-}
-
-private void enqueue(E x) {
- final Object[] items = this.items;
- items[putIndex] = x;
- if (++putIndex == items.length)
- putIndex = 0;
- count++;
- notEmpty.signal(); // 队列不为空时,通知消费者获取元素
-}
-```
-
diff --git a/README.md b/README.md
index 88004f5..7826a37 100644
--- a/README.md
+++ b/README.md
@@ -55,33 +55,38 @@
## 基础
-1. [**Java面试题精选**](Java/Java基础面试题.md) **精心整理的大厂Java面试题目,附有详细答案** (推荐 :+1:)
-2. [Java8 新特性总结](Java/Java8.md)
+1. [**Java面试题精选**](docs/java/java-basic.md) **精心整理的大厂Java面试题目,附有详细答案** (推荐 :+1:)
+2. [Java8 新特性总结](docs/java/java8-all.md)
## 集合
-1. [Java集合高频面试题](Java/Java集合面试题.md)(**牛客点赞200+!推荐** :+1:)
+1. [Java集合高频面试题](docs/java/java-collection.md)(**牛客点赞200+!推荐** :+1:)
## 并发
-1. [Java并发高频面试题(精华版)](Java/Java并发面试题.md) **精心整理的大厂Java并发编程面试题目,附有详细答案** (推荐 :+1:)
+1. [Java并发高频面试题(精华版)](docs/java/java-concurrent.md) **精心整理的大厂Java并发编程面试题目,附有详细答案** (推荐 :+1:)
## JVM
-1. [【大厂面试】——JVM高频面试题](Java/JVM高频面试题.md)(推荐 :+1:)
+1. [【大厂面试】——JVM高频面试题](/docs/java/jvm.md)(推荐 :+1:)
# 数据库
## MySQL
-1. [【大厂面试】—— MySQL高频面试题50道](数据库/MySQL高频面试题.md)(**知乎1k+收藏,推荐** :+1:)
-2. [图解索引下推](https://mp.weixin.qq.com/s/W1XQYmihtSdbLWQKeNwZvQ)(推荐 :+1:)
-3. [MySQL执行计划详解](数据库/MySQL执行计划.md)(推荐 :+1:)
+- [MySQL高频面试题50道](docs/database/mysql.md)(**知乎1k+收藏,推荐** :+1:)
+
+**重要知识点**:
+
+- [8种最坑SQL语法](docs/advance/excellent-article/7-sql-optimize.md)
+
+- [MySQL执行计划详解](docs/database/mysql-execution-plan.md)(推荐 :+1:)
+
+- [图解索引下推](https://mp.weixin.qq.com/s/W1XQYmihtSdbLWQKeNwZvQ)(推荐 :+1:)
## Redis
-1. [【大厂面试】——Redis30问](Redis/Redis面试题.md)(牛客高赞,推荐 :+1:)
-2. [缓存穿透、缓存雪崩、缓存击穿](Redis/缓存穿透、缓存雪崩、缓存击穿.md)
+1. [Redis高频面试题总结](docs/redis/redis.md)(牛客高赞,推荐 :+1:)
## ElasticSearch
@@ -91,54 +96,52 @@
## Spring
-1. [Spring高频面试题30道](框架/Spring面试题.md)(推荐 :+1:)
-2. [Spring用到哪些设计模式?](框架/Spring用到哪些设计模式.md)
+1. [Spring高频面试题30道](docs/framework/spring.md)(推荐 :+1:)
+2. [Spring用到哪些设计模式?]() todo
## Spring Boot
[Spring Boot总结](框架/SpringBoot实战.md)
-[SpringBoot面试题总结](框架/SpringBoot面试题总结.md)
+[SpringBoot面试题总结](docs/framework/springboot.md)
## Spring MVC
-[Spring MVC入门知识点](框架/SpringMVC.md)
-
-[Spring MVC面试题总结](框架/SpringMVC面试题.md)
+[Spring MVC面试题总结](docs/framework/springmvc.md)
## Mybatis
[Mybatis入门知识点](框架/深入浅出Mybatis技术原理与实战.md)
-[Mybatis面试题总结](框架/Mybatis面试题.md)
+[Mybatis面试题总结](docs/framework/mybatis.md)
## SpringCloud
-[[SpringCloud面试题](https://topjavaer.cn/framework/springcloud-interview.html)(推荐 :+1:)
+[[SpringCloud面试题](docs/framework/springcloud-interview.md)(推荐 :+1:)
-[SpringCloud总结](框架/SpringCloud微服务实战.md)
+[SpringCloud总结](docs/framework/springcloud-overview.md)
## Netty
-[Netty实战笔记](框架/netty实战.md)
+[Netty实战笔记](docs/framework/netty-overview.md)
# 分布式
## 微服务
-[微服务面试题](https://topjavaer.cn/distributed/micro-service.html)
+[微服务面试题](docs/advance/distributed/4-micro-service.md)
## RPC
-[RPC面试题](https://topjavaer.cn/distributed/rpc.html)
+[RPC面试题](docs/advance/distributed/3-rpc.md)
## 全局唯一ID
-[全局唯一ID](https://topjavaer.cn/distributed/global-unique-id.html)
+[全局唯一ID](docs/advance/distributed/1-global-unique-id.md)
## 分布式事务
-[分布式事务总结](https://topjavaer.cn/distributed/distributed-transaction.html)
+[分布式事务总结](docs/advance/distributed/6-distributed-transaction.md)
## 分布式架构
@@ -148,38 +151,30 @@
## 限流
-[限流算法总结](https://topjavaer.cn/advance/concurrent/1-current-limiting.html)
+[限流算法总结](docs/advance/concurrent/1-current-limiting.md)
## 负载均衡
-[负载均衡](https://topjavaer.cn/advance/concurrent/2-load-balance.html)
+[负载均衡](docs/advance/concurrent/2-load-balance.md)
# 消息队列
## RabbitMQ
-1. [消息队列面试题](消息队列/消息队列面试题.md)
-2. [RabbitMQ面试题总结](消息队列/rabbitmq.md) (推荐 :+1:)
-3. [Kafka面试题总结](消息队列/kafka.md) (推荐 :+1:)
-4. [死信队列](消息队列/死信队列.md)
+1. [消息队列面试题](docs/message-queue/mq.md)
+2. [RabbitMQ面试题总结](docs/message-queue/rabbitmq.md) (推荐 :+1:)
+3. [Kafka面试题总结](docs/message-queue/kafka.md) (推荐 :+1:)
+4. [死信队列]() todo
# 计算机网络
-[【大厂面试】—— 计算机网络常见面试题总结](计算机基础/网络/计算机网络高频面试题.md) (**知乎1k+收藏!推荐 :+1:**)
+[计算机网络常见面试题总结](docs/computer-basic/network.md) (**知乎1k+收藏!推荐 :+1:**)
# 数据结构与算法
-[如何高效的刷LeetCode?](https://www.zhihu.com/question/280279208/answer/2377906738)
-
-[120道Leetcode题解(高频)](https://topjavaer.cn/leetcode/hot120/)
-
-[常见数据结构总结](https://topjavaer.cn/computer-basic/data-structure.html)
-
-# 设计模式
-
-[字节跳动大佬总结的设计模式PDF](https://t.1yb.co/y96J)
-
-[设计模式总结](https://topjavaer.cn/advance/design-pattern/)
+- [常见数据结构总结](docs/computer-basic/data-structure.md)
+- [如何高效的刷LeetCode?](https://www.zhihu.com/question/280279208/answer/2377906738)
+- [120道Leetcode题解(高频)](https://topjavaer.cn/leetcode/hot120/)
# 海量数据场景题
@@ -189,21 +184,49 @@
4. [如何在100亿的数据中找到中位数](https://topjavaer.cn/mass-data/4-find-mid-num.html)
5. [找出最热门的查询串](https://topjavaer.cn/mass-data/5-find-hot-string.html)
6. [如何找出排名前500的数字](https://topjavaer.cn/mass-data/6-top-500-num.html)
+7. [如何按照 query 的频度排序?](https://topjavaer.cn/mass-data/7-query-frequency-sort.html)
+8. [大数据中 TopK 问题的常用套路](https://topjavaer.cn/mass-data/8-topk-template.html)
+
+# 系统设计
+
+- [扫码登录](docs/advance/system-design/1-scan-code-login.md)
+- [订单超时未支付自动取消](docs/advance/system-design/2-order-timeout-auto-cancel.md)
+- [短链系统设计](docs/advance/system-design/3-short-url.md)
+- [超卖问题](docs/advance/system-design/4-oversold.md)
+- [秒杀系统设计](docs/advance/system-design/5-second-kill.md)
+- [微信红包系统如何设计?](docs/advance/system-design/6-wechat-redpacket-design.md)
+- [如何把一个文件较快的发送到100w个服务器?](docs/advance/system-design/7-file-send.md)
+
+# 安全
+
+- [什么是JWT?](docs/advance/excellent-article/16-what-is-jwt.md)
+- [单点登录(SSO)设计与实现](docs/advance/system-design/8-sso-design.md)
+
+# 编程实践
+
+1. [线上接口很慢怎么办?](docs/practice/service-performance-optimization.md)
+2. [干掉 “重复代码” 的技巧有哪些](docs/advance/excellent-article/4-remove-duplicate-code.md)
# 大厂面经汇总
-- [字节跳动](./interview/1-byte-and-dance.md)
-- [腾讯](./interview/2-tencent.md)
-- [百度](./interview/3-baidu.md)
-- [阿里](./interview/4-ali.md)
-- [快手](./interview/5-kuaishou.md)
-- [美团](./interview/6-meituan.md)
-- [shopee](./interview/7-shopee.md)
-- [京东](./interview/8-jingdong.md)
-- [华为](./interview/9-huawei.md)
-- [网易](./interview/10-netease.md)
+- [字节跳动](docs/campus-recruit/interview/1-byte-and-dance.md)
+- [腾讯](docs/campus-recruit/interview/2-tencent.md)
+- [百度](docs/campus-recruit/interview/3-baidu.md)
+- [阿里](docs/campus-recruit/interview/4-ali.md)
+- [快手](docs/campus-recruit/interview/5-kuaishou.md)
+- [美团](docs/campus-recruit/interview/6-meituan.md)
+- [shopee](docs/campus-recruit/interview/7-shopee.md)
+- [京东](docs/campus-recruit/interview/8-jingdong.md)
+- [华为](docs/campus-recruit/interview/9-huawei.md)
+- [网易](docs/campus-recruit/interview/10-netease.md)
-# 其他优质文章
+# 设计模式
+
+[字节跳动大佬总结的设计模式PDF](https://t.1yb.co/y96J)
+
+[设计模式总结](https://topjavaer.cn/advance/design-pattern/)
+
+# 优质文章
[优质文章汇总,持续更新中~](https://topjavaer.cn/advance/excellent-article/)
@@ -211,13 +234,13 @@
# 工具
-[Git 超详细总结!](工具/git-overview.md)(推荐 :+1:)
+[Git 超详细总结!](docs/tools/git-overview.md)(推荐 :+1:)
-[Linux 常用命令总结!](工具/linux-overview.md)
+[Linux 常用命令总结!](docs/tools/linux-overview.md)
-[Docker 基础总结!](工具/docker-overview.md)
+[Docker 基础总结!](docs/tools/docker-overview.md)
-[Maven 基础总结!](tools/maven-overview.md)
+[Maven 基础总结!](docs/tools/maven-overview.md)
# 交流
@@ -249,6 +272,6 @@
| 2022.05.03 | 微信收款码 | *哈 | 2元 | |
| 2022.06.12 | 微信收款码 | *可 | 8.8元 | |
| 2022.10.19 | 微信收款码 | *斌 | 10元 | 支持一下,希望能持续更新 |
-
-
+| 2022.11.16 | 支付宝收款码 | *雄 | 2元 | |
+| 2022.12.02 | 微信收款码 | *军 | 5元 | |
diff --git "a/Redis/\347\274\223\345\255\230\347\251\277\351\200\217\343\200\201\347\274\223\345\255\230\351\233\252\345\264\251\343\200\201\347\274\223\345\255\230\345\207\273\347\251\277.md" "b/Redis/\347\274\223\345\255\230\347\251\277\351\200\217\343\200\201\347\274\223\345\255\230\351\233\252\345\264\251\343\200\201\347\274\223\345\255\230\345\207\273\347\251\277.md"
deleted file mode 100644
index ca08607..0000000
--- "a/Redis/\347\274\223\345\255\230\347\251\277\351\200\217\343\200\201\347\274\223\345\255\230\351\233\252\345\264\251\343\200\201\347\274\223\345\255\230\345\207\273\347\251\277.md"
+++ /dev/null
@@ -1,60 +0,0 @@
-
-
-**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
-
-- [缓存穿透](#%E7%BC%93%E5%AD%98%E7%A9%BF%E9%80%8F)
-- [缓存雪崩](#%E7%BC%93%E5%AD%98%E9%9B%AA%E5%B4%A9)
-- [缓存击穿](#%E7%BC%93%E5%AD%98%E5%87%BB%E7%A9%BF)
-
-
-
-# 缓存穿透
-
-缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,如果从DB查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到DB去查询,失去了缓存的意义。在流量大时,可能DB就挂掉了。
-
-1. 缓存空值,不会查数据库
-2. 采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,查询不存在的数据会被这个bitmap拦截掉,从而避免了对DB的查询压力。
-
-布隆可以看成数据库的缩略版,用来判定是否存在值。启动的时候过滤器是要全表扫描的,数据库数据发生变化的时候会更新布隆过滤器。
-
-布隆过滤器的原理:当一个元素被加入集合时,通过K个散列函数将这个元素映射成一个位数组中的K个点,把它们置为1。查询时,将元素通过散列函数映射之后会得到k个点,如果这些点有任何一个0,则被检元素一定不在,直接返回;如果都是1,则查询元素很可能存在,就会去查询redis和数据库。
-
-
-
-> 图片来源网络
-
-
-
-# 缓存雪崩
-
-缓存雪崩是指在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重挂掉。
-
-解决方法:在原有的失效时间基础上增加一个随机值,使得过期时间分散一些。
-
-# 缓存击穿
-
-缓存击穿:大量的请求同时查询一个 key 时,此时这个key正好失效了,就会导致大量的请求都落到数据库。缓存击穿是查询缓存中失效的 key,而缓存穿透是查询不存在的 key。
-
-解决方法:加互斥锁(redis分布式锁或者使用ReentrantLock),第一个请求的线程可以拿到锁,拿到锁的线程查询到了数据之后设置缓存,其他的线程获取锁失败会等待50ms然后重新到缓存取数据,这样便可以避免大量的请求落到数据库。
-
-```java
-public String get(key) {
- String value = redis.get(key);
- if (value == null) { //代表缓存值过期
- String key_mutex = "mutext:key:" + key;
- //设置30s的超时,防止del操作失败的时候,下次缓存过期一直不能load db
- if (redis.set(key_mutex, 1, 'NX', 'PX', 30000) == 1) { //代表设置成功
- value = db.get(key);
- redis.set(key, value, expire_secs);
- redis.del(key_mutex);
- } else { //这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可
- sleep(50);
- get(key); //重试
- }
- } else {
- return value;
- }
- }
-```
-
-SETNX:只有不存在的时候才设置,可以利用它来实现锁的效果。
\ No newline at end of file
diff --git a/docs/.vuepress/config.ts b/docs/.vuepress/config.ts
new file mode 100644
index 0000000..be5e7ac
--- /dev/null
+++ b/docs/.vuepress/config.ts
@@ -0,0 +1,86 @@
+import {defineUserConfig} from "vuepress";
+import theme from "./theme";
+
+const {searchPlugin} = require('@vuepress/plugin-search')
+
+import { gitPlugin } from '@vuepress/plugin-git'
+
+
+export default defineUserConfig({
+ lang: "zh-CN",
+ title: "程序员大彬",
+ description: "自学转码之路",
+ base: "/",
+ dest: './public',
+ theme,
+ // 是否开启默认预加载 js
+ shouldPrefetch: (file, type) => false,
+
+ head: [
+ //meta
+ ["meta", { name: "robots", content: "all" }],
+ ["meta", {name: "author", content: "大彬"}],
+ [
+ "meta",
+ {
+ "http-equiv": "Cache-Control",
+ content: "no-cache, no-store, must-revalidate",
+ },
+ ],
+ ["meta", { "http-equiv": "Pragma", content: "no-cache" }],
+ ["meta", { "http-equiv": "Expires", content: "0" }],
+ ['meta', {name: 'baidu-site-verification', content: 'code-mtJaPDeFwy'}],
+ // ['meta', { name: 'google-site-verification', content: 'eGgkbT6uJR-WQeSkhhcB6RbnZ2RtF5poPf1ai-Fgmy8' }],
+ ['meta', {name: 'keywords', content: 'Java, Spring, Mybatis, SpringMVC, Springboot, 编程, 程序员, MySQL, Redis, 系统设计, 分布式, RPC, 高可用, 高并发'}],
+ [
+ 'script', {}, `
+ var _hmt = _hmt || [];
+ (function() {
+ var hm = document.createElement("script");
+ hm.src = "https://hm.baidu.com/hm.js?f9b36644dd9e756e508a77f272a63e07";
+ var s = document.getElementsByTagName("script")[0];
+ s.parentNode.insertBefore(hm, s);
+ })();
+ `
+ ],
+ ],
+
+ plugins: [
+ searchPlugin({
+ // 配置项
+ }),
+ gitPlugin({
+ createdTime: false,
+ updatedTime: false,
+ contributors: false,
+ }),
+ ],
+ //plugins: [
+ // copyright({
+ // disableCopy: true,
+ // global: true,
+ // disableSelection: true,
+ // author: "大彬",
+ // license: "MIT",
+ // hostname: "https://www.topjavaer.cn",
+ // }),
+ // [
+ // 'vuepress-plugin-baidu-autopush'
+ // ],
+ // sitemapPlugin({
+ // // 配置选项
+ // hostname: "https:www.topjavaer.cn"
+ // }),
+ // photoSwipePlugin({
+ // // 你的选项
+ // }),
+ // readingTimePlugin({
+ // // 你的选项
+ // }),
+ // [
+ // nprogressPlugin(),
+ // ],
+ // //['@vuepress/nprogress'],
+ //],
+})
+;
diff --git a/docs/.vuepress/enhanceApp.js b/docs/.vuepress/enhanceApp.js
new file mode 100644
index 0000000..42e09d7
--- /dev/null
+++ b/docs/.vuepress/enhanceApp.js
@@ -0,0 +1,13 @@
+export default ({router}) => {
+ router.beforeEach((to, from, next) => {
+ //对每个页面点击添加百度统计
+ if(typeof _hmt!='undefined'){
+ if (to.path) {
+ _hmt.push(['_trackPageview', to.fullPath]);
+ }
+ }
+
+ // continue
+ next();
+ })
+};
diff --git a/docs/.vuepress/navbar.ts b/docs/.vuepress/navbar.ts
new file mode 100644
index 0000000..16989b2
--- /dev/null
+++ b/docs/.vuepress/navbar.ts
@@ -0,0 +1,276 @@
+import {navbar} from "vuepress-theme-hope";
+
+export default navbar([
+
+ //"/",
+ //"/home",
+ {
+ text: "主页",
+ link: "/",
+ icon: "home",
+ },
+ {
+ text: "校招",
+ icon: "campus",
+ children: [
+ {text: "校招分享", link: "/campus-recruit/share"},
+ {text: "简历应该这么写", link: "/campus-recruit/resume.md"},
+ {text: "项目经验介绍", link: "/campus-recruit/project-experience.md"},
+ {text: "编程语言", link: "/campus-recruit/program-language"},
+ {text: "面经总结", link: "/campus-recruit/interview/"},
+ {text: "秋招内推", link: "https://docs.qq.com/sheet/DYW9ObnpobXNRTXpq"},
+ ],
+ },
+ {
+ text: "Java",
+ icon: "java",
+ children: [
+ {text: "基础", link: "/java/java-basic.md"},
+ {text: "集合", link: "/java/java-collection.md"},
+ {text: "并发", link: "/java/java-concurrent.md"},
+ {text: "JVM", link: "/java/jvm.md"},
+ {text: "Java8", link: "/java/java8"},
+ ]
+ },
+ {
+ text: "框架",
+ icon: "framework",
+ children: [
+ {
+ text: "框架",
+ children: [
+ {text: "Spring面试题", link: "/framework/spring.md"},
+ {text: "SpringMVC面试题", link: "/framework/springmvc.md"},
+ {text: "Mybatis面试题", link: "/framework/mybatis.md"},
+ {text: "SpringBoot面试题", link: "/framework/springboot.md"},
+ {text: "SpringCloud详解", link: "/framework/springcloud/"},
+ {text: "SpringCloud面试题", link: "/framework/springcloud-interview.md"},
+ {text: "Netty详解", link: "/framework/netty/"},
+ ]
+ },
+ {
+ text: "消息队列",
+ children: [
+ {text: "消息队列面试题", link: "/message-queue/mq.md"},
+ {text: "RabbitMQ面试题", link: "/message-queue/rabbitmq.md"},
+ {text: "Kafka面试题", link: "/message-queue/kafka.md"},
+ ]
+ }
+ ]
+ },
+ {
+ text: "计算机基础",
+ icon: "computer",
+ children: [
+ {text: "网络", link: "/computer-basic/network.md"},
+ {text: "操作系统", link: "/computer-basic/operate-system.md"},
+ {text: "算法", link: "/computer-basic/algorithm.md"},
+ {text: "LeetCode题解", link: "/leetcode/hot120"},
+ {text: "数据结构", link: "/computer-basic/data-structure.md"},
+ ]
+ },
+ {
+ text: "数据库",
+ icon: "db",
+ children: [
+ {
+ text: "关系型数据库",
+ children: [
+ //{text: "MySQL基础", children: ["/database/mysql-basic/"],},
+ {text: "MySQL基础", link: "/database/mysql-basic/"},
+ {text: "MySQL面试题", link: "/database/mysql.md"},
+ {text: "MySQL执行计划详解", link: "/database/mysql-execution-plan.md"},
+ ]
+ },
+ {
+ text: "非关系型数据库",
+ children: [
+ {text: "Redis基础", link: "/redis/redis-basic/"},
+ {text: "Redis面试题", link: "/redis/redis.md"},
+ {text: "ElasticSearch面试题", link: "https://mp.weixin.qq.com/s/Ffb8NDgavf9QAWYBm0qAVg"},
+ ]
+ },
+ ]
+ },
+ {
+ text: "进阶之路",
+ icon: "win",
+ children: [
+ {
+ text: "分布式",
+ icon: "distribute",
+ children: [
+ {text: "全局唯一ID", link: "/advance/distributed/1-global-unique-id.md"},
+ {text: "分布式锁", link: "/advance/distributed/2-distributed-lock.md"},
+ {text: "RPC", link: "/advance/distributed/3-rpc.md"},
+ {text: "微服务", link: "/advance/distributed/4-micro-service.md"},
+ {text: "分布式架构", link: "/advance/distributed/5-distibuted-arch.md"},
+ {text: "分布式事务", link: "/advance/distributed/6-distributed-transaction.md"},
+ ]
+ },
+ {
+ text: "高并发",
+ children: [
+ {text: "限流", link: "/advance/concurrent/1-current-limiting.md"},
+ {text: "负载均衡", link: "/advance/concurrent/2-load-balance.md"},
+ ],
+ },
+ {
+ text: "设计模式",
+ icon: "win",
+ children: [
+ {text: "设计模式详解", link: "/advance/design-pattern/"},
+ ],
+ },
+ {
+ text: "系统设计",
+ children: [
+ {text: "扫码登录设计", link: "/advance/system-design/1-scan-code-login.md"},
+ {text: "超时订单自动取消", link: "/advance/system-design/2-order-timeout-auto-cancel.md"},
+ {text: "短链系统设计", link: "/advance/system-design/3-short-url.md"},
+ {text: "微信红包系统如何设计?", link: "/advance/system-design/6-wechat-redpacket-design.md"},
+ {text: "单点登录设计与实现", link: "/advance/system-design/8-sso-design.md"},
+ ]
+ },
+ {
+ text: "海量数据",
+ children: [
+ {text: "统计不同号码的个数", link: "/mass-data/1-count-phone-num.md"},
+ {text: "出现频率最高的100个词", link: "/mass-data/2-find-hign-frequency-word.md"},
+ {text: "查找两个大文件共同的URL", link: "/mass-data/3-find-same-url.md"},
+ {text: "如何在100亿数据中找到中位数?", link: "/mass-data/4-find-mid-num.md"},
+ {text: "如何查询最热门的查询串?", link: "/mass-data/5-find-hot-string.md"},
+ {text: "如何找出排名前 500 的数?", link: "/mass-data/6-top-500-num.md"},
+ {text: "如何按照 query 的频度排序?", link: "/mass-data/7-query-frequency-sort.md"},
+ {text: "大数据中 TopK 问题的常用套路", link: "/mass-data/8-topk-template.md"},
+ ]
+ },
+ {
+ text: "优质文章",
+ children: [
+ {text: "优质文章汇总", link: "/advance/excellent-article"},
+ ]
+ },
+ ]
+ },
+ //{
+ // text: "场景题",
+ // icon: "design",
+ // children: [
+ // {
+ // text: "海量数据",
+ // children: [
+ // {text: "统计不同号码的个数", link: "/mass-data/count-phone-num.md"},
+ // {text: "出现频率最高的100个词", link: "/mass-data/find-hign-frequency-word.md"},
+ // ]
+ // },
+ // {
+ // text: "系统设计",
+ // children: [
+ // {text: "扫码登录设计", link: "/system-design/scan-code-login.md"},
+ // ]
+ // },
+ // ]
+ //},
+ {
+ text: "工具",
+ icon: "tool",
+ children: [
+ {
+ text: "开发工具",
+ children: [
+ {text: "Git详解", link: "/tools/git/"},
+ {text: "Maven详解", link: "/tools/maven/"},
+ {text: "Docker详解", link: "/tools/docker/"},
+ {text: "Linux常用命令", link: "/tools/linux"},
+ {text: "Nginx面试题", link: "https://mp.weixin.qq.com/s/SKKEeYxif0wWJo6n57rd6A"},
+ ]
+ },
+ {
+ text: "编程利器",
+ children: [
+ {text: "markdown编辑器", link: "/tools/typora-overview.md"},
+ ]
+ },
+ ]
+ },
+ {
+ text: "珍藏资源",
+ icon: "collection",
+ children: [
+ {
+ text: "学习资源",
+ children: [
+ {text: "计算机经典电子书PDF", link: "https://github.com/Tyson0314/java-books"},
+ {text: "Leetcode刷题笔记", link: "/learning-resources/leetcode-note.md"},
+ ]
+ },
+ {
+ text: "学习路线",
+ children: [
+ {text: "Java学习路线", link: "/learning-resources/java-learn-guide.md"},
+ {text: "CS学习路线", link: "/learning-resources/cs-learn-guide.md"},
+ ]
+ },
+
+ ]
+ },
+ {
+ text: "关于",
+ icon: "about",
+ children: [
+ {text: "关于我", link: "/about/introduce.md"},
+ {text: "网站日记", link: "/other/site-diary.md"},
+ {text: "联系我", link: "/about/contact.md"},
+ {text: "留言区", link: "/other/leave-a-message.md"},
+ ]
+ },
+
+
+ //{ text: "Guide", icon: "creative", link: "/guide/" },
+ //{
+ // text: "Posts",
+ // icon: "edit",
+ // prefix: "/posts/",
+ // children: [
+ // {
+ // text: "Articles 1-4",
+ // icon: "edit",
+ // prefix: "article/",
+ // children: [
+ // { text: "Article 1", icon: "edit", link: "article1" },
+ // { text: "Article 2", icon: "edit", link: "article2" },
+ // "article3",
+ // "article4",
+ // ],
+ // },
+ // {
+ // text: "Articles 5-12",
+ // icon: "edit",
+ // children: [
+ // {
+ // text: "Article 5",
+ // icon: "edit",
+ // link: "article/article5",
+ // },
+ // {
+ // text: "Article 6",
+ // icon: "edit",
+ // link: "article/article6",
+ // },
+ // "article/article7",
+ // "article/article8",
+ // ],
+ // },
+ // { text: "Article 9", icon: "edit", link: "article9" },
+ // { text: "Article 10", icon: "edit", link: "article10" },
+ // "article11",
+ // "article12",
+ // ],
+ //},
+ //{
+ // text: "Theme Docs",
+ // icon: "note",
+ // link: "https://vuepress-theme-hope.github.io/v2/",
+ //},
+]);
diff --git a/docs/.vuepress/sidebar.ts b/docs/.vuepress/sidebar.ts
new file mode 100644
index 0000000..d23d12d
--- /dev/null
+++ b/docs/.vuepress/sidebar.ts
@@ -0,0 +1,176 @@
+import {sidebar} from "vuepress-theme-hope";
+
+const {getChildren} = require("./vuepress-sidebar-auto/vuepress-sidebar-auto");
+
+export default sidebar({
+ "/database/mysql-basic/": [{
+ text: "MySQL基础",
+ collapsable: false,
+ children: getChildren('./docs/database', 'mysql-basic'),
+ // children: ["1-data-type"],
+ },
+ ],
+ "/redis/redis-basic/": [
+ {
+ text: "Redis基础",
+ collapsable: false,
+ children: getChildren('./docs/redis', 'redis-basic'),
+ },
+ ],
+ "/advance/design-pattern/": [
+ {
+ text: "设计模式",
+ collapsable: false,
+ children: getChildren('./docs/advance', 'design-pattern'),
+ },
+ ],
+ "/tools/docker/": [
+ {
+ text: "Docker基础",
+ collapsable: false,
+ children: getChildren('./docs/tools', 'docker'),
+ },
+ ],
+ "/tools/git/": [
+ {
+ text: "Git基础",
+ collapsable: false,
+ children: getChildren('./docs/tools', 'git'),
+ },
+ ],
+ "/leetcode/hot120": [
+ {
+ text: "LeetCode题解",
+ collapsable: false,
+ children: getChildren('./docs/leetcode', 'hot120'),
+ },
+ ],
+ "/tools/maven/": [
+ {
+ text: "Maven基础",
+ collapsable: false,
+ children: getChildren('./docs/tools', 'maven'),
+ },
+ ],
+ "/framework/netty/": [
+ {
+ text: "Netty基础",
+ collapsable: false,
+ children: getChildren('./docs/framework', 'netty'),
+ },
+ ],
+ "/framework/springcloud/": [
+ {
+ text: "SpringCloud基础",
+ collapsable: false,
+ children: getChildren('./docs/framework', 'springcloud'),
+ },
+ ],
+ "/java/java8/": [
+ {
+ text: "java8新特性",
+ collapsable: false,
+ children: getChildren('./docs/java', 'java8'),
+ },
+ ],
+ "/campus-recruit/interview/": [
+ {
+ text: "面经合集",
+ collapsable: true,
+ children: getChildren('./docs/campus-recruit', 'interview'),
+ },
+ ],
+ "/advance/excellent-article": [
+ {
+ text: "优质文章汇总",
+ collapsable: false,
+ children: getChildren('./docs/advance', 'excellent-article'),
+ },
+ ],
+ "/advance/concurrent": [
+ {
+ text: "高并发",
+ collapsable: false,
+ children: getChildren('./docs/advance', 'concurrent'),
+ },
+ ],
+ "/tools/linux/": [
+ {
+ text: "linux常用命令",
+ collapsable: false,
+ children: getChildren('./docs/tools', 'linux'),
+ },
+ ],
+ "/campus-recruit/program-language/": [
+ {
+ text: "编程语言",
+ collapsable: true,
+ children: getChildren('./docs/campus-recruit', 'program-language'),
+ },
+ ],
+ "/advance/system-design": [
+ {
+ text: "系统设计",
+ collapsable: false,
+ children: getChildren('./docs/advance', 'system-design'),
+ },
+ ],
+ "/campus-recruit/share": [
+ {
+ text: "校招分享",
+ collapsable: false,
+ children: getChildren('./docs/campus-recruit', 'share'),
+ },
+ ],
+ "/mass-data": [
+ {
+ text: "海量数据",
+ collapsable: false,
+ children: getChildren('./docs', '/mass-data'),
+ },
+ ],
+
+ //'/': "auto", //不能放在数组第一个,否则会导致右侧栏无法使用
+ //"/",
+ //"/home",
+ //"/slide",
+ //{
+ // icon: "creative",
+ // text: "Guide",
+ // prefix: "/guide/",
+ // link: "/guide/",
+ // children: "structure",
+ //},
+ //{
+ // text: "Articles",
+ // icon: "note",
+ // prefix: "/posts/",
+ // children: [
+ // {
+ // text: "Articles 1-4",
+ // icon: "note",
+ // collapsable: true,
+ // prefix: "article/",
+ // children: ["article1", "article2", "article3", "article4"],
+ // },
+ // {
+ // text: "Articles 5-12",
+ // icon: "note",
+ // children: [
+ // {
+ // text: "Articles 5-8",
+ // icon: "note",
+ // collapsable: true,
+ // prefix: "article/",
+ // children: ["article5", "article6", "article7", "article8"],
+ // },
+ // {
+ // text: "Articles 9-12",
+ // icon: "note",
+ // children: ["article9", "article10", "article11", "article12"],
+ // },
+ // ],
+ // },
+ // ],
+ //},
+});
diff --git a/docs/.vuepress/styles/index.scss b/docs/.vuepress/styles/index.scss
new file mode 100644
index 0000000..e69de29
diff --git a/docs/.vuepress/styles/palette.scss b/docs/.vuepress/styles/palette.scss
new file mode 100644
index 0000000..e69de29
diff --git a/docs/.vuepress/theme.ts b/docs/.vuepress/theme.ts
new file mode 100644
index 0000000..a4c25a3
--- /dev/null
+++ b/docs/.vuepress/theme.ts
@@ -0,0 +1,166 @@
+import {hopeTheme} from "vuepress-theme-hope";
+import navbar from "./navbar";
+import sidebar from "./sidebar";
+
+export default hopeTheme({
+ hostname: "https://www.topjavaer.cn",
+
+ author: {
+ name: "大彬",
+ url: "https://www.topjavaer.cn",
+ },
+
+ iconAssets: "//at.alicdn.com/t/c/font_3573089_m0vn9di8q9.css",
+ iconPrefix: "iconfont icon-",
+ //iconAssets: "iconfont",
+
+ darkmode: "toggle",
+
+ fullscreen: false,
+
+ logo: "/logo.svg",
+
+ repo: "Tyson0314/Java-learning",
+
+ docsDir: "docs",
+
+ // navbar
+ navbar: navbar,
+
+ // sidebar
+ sidebar: sidebar,
+ headerDepth: 5,
+ collapsable: true,
+
+ displayFooter: true,
+
+ // footer: '粤ICP备2022005190号-2 |' +
+ // '关于网站',
+
+ footer: '粤ICP备2022005190号-2',
+
+ pageInfo: ["Author", "Original", "Date", "Category", "Tag", "ReadingTime"],
+
+ blog: {
+ description: "非科班自学转码选手,校招拿了多家互联网大厂offer",
+ intro: "https://mp.weixin.qq.com/s/84ZDT5d9TIbnyg-jeRKIIA",
+ medias: {
+ Github: "https://github.com/Tyson0314",
+ Gitee: "https://gitee.com/tysondai",
+ ZhiHu: "https://www.zhihu.com/people/dai-shu-bin-13",
+ },
+ },
+
+ // encrypt: {
+ // config: {
+ // "/guide/encrypt.html": ["1234"],
+ // },
+ // },
+
+ plugins: {
+ blog: {
+ autoExcerpt: true,
+ },
+
+ // If you don't need comment feature, you can remove following option
+ // The following config is for demo ONLY, if you need comment feature, please generate and use your own config, see comment plugin documentation for details.
+ // To avoid disturbing the theme developer and consuming his resources, please DO NOT use the following config directly in your production environment!!!!!
+ comment: {
+ /**
+ * Using Giscus
+ */
+ provider: "Giscus",
+ repo: "Tyson0314/topjavaer",
+ repoId: "R_kgDOHxs_3g",
+ category: "Announcements",
+ categoryId: "DIC_kwDOHxs_3s4CQpxA",
+//
+ /**
+ * Using Twikoo
+ */
+ // provider: "Twikoo",
+ // envId: "https://twikoo.ccknbc.vercel.app",
+//
+ /**
+ * Using Waline
+ */
+ // provider: "Waline",
+ // serverURL: "https://vuepress-theme-hope-comment.vercel.app",
+ },
+
+ mdEnhance: {
+ enableAll: true,
+ presentation: {
+ plugins: ["highlight", "math", "search", "notes", "zoom"],
+ },
+ },
+
+ //myplugin
+ copyright: {
+ disableCopy: true,
+ global: true,
+ author: "大彬",
+ license: "MIT",
+ hostname: "https://www.topjavaer.cn",
+ },
+ baiduAutoPush: {},
+ sitemapPlugin: {
+ // 配置选项
+ hostname: "https:www.topjavaer.cn"
+ },
+ photoSwipePlugin: {
+ // 你的选项
+ },
+ readingTimePlugin: {},
+
+ nprogressPlugin: {},
+
+ //searchPlugin: {
+ // appId: "xxx",
+ // apiKey: "xxxx",
+ // indexName: "topjavaer.cn",
+ // locales: {
+ // "/": {
+ // placeholder: "搜索文档",
+ // translations: {
+ // button: {
+ // buttonText: "搜索文档",
+ // buttonAriaLabel: "搜索文档",
+ // },
+ // modal: {
+ // searchBox: {
+ // resetButtonTitle: "清除查询条件",
+ // resetButtonAriaLabel: "清除查询条件",
+ // cancelButtonText: "取消",
+ // cancelButtonAriaLabel: "取消",
+ // },
+ // startScreen: {
+ // recentSearchesTitle: "搜索历史",
+ // noRecentSearchesText: "没有搜索历史",
+ // saveRecentSearchButtonTitle: "保存至搜索历史",
+ // removeRecentSearchButtonTitle: "从搜索历史中移除",
+ // favoriteSearchesTitle: "收藏",
+ // removeFavoriteSearchButtonTitle: "从收藏中移除",
+ // },
+ // errorScreen: {
+ // titleText: "无法获取结果",
+ // helpText: "你可能需要检查你的网络连接",
+ // },
+ // footer: {
+ // selectText: "选择",
+ // navigateText: "切换",
+ // closeText: "关闭",
+ // searchByText: "搜索提供者",
+ // },
+ // noResultsScreen: {
+ // noResultsText: "无法找到相关结果",
+ // suggestedQueryText: "你可以尝试查询",
+ // },
+ // },
+ // },
+ // },
+ // },
+ // },
+ },
+})
+;
diff --git a/docs/.vuepress/vuepress-sidebar-auto/vuepress-sidebar-auto.js b/docs/.vuepress/vuepress-sidebar-auto/vuepress-sidebar-auto.js
new file mode 100644
index 0000000..29d0ca4
--- /dev/null
+++ b/docs/.vuepress/vuepress-sidebar-auto/vuepress-sidebar-auto.js
@@ -0,0 +1,92 @@
+//侧边栏
+// const autosidebar = require('vuepress-auto-sidebar-doumjun')
+const fs = require('fs')
+const path = require('path')
+
+/**
+ * 过滤所要导航的文件
+ * 文件名 包含.md 但 不包含 README */
+function checkFileType(path) {
+ return path.includes(".md")&&(!path.includes("README"));
+}
+
+/**
+ * 格式化文件路径*/
+function prefixPath(basePath, dirPath) {
+ let index = basePath.indexOf("/")
+// 去除一级目录地址
+ basePath = basePath.slice(index, path.length)
+// replace用于处理windows电脑的路径用\表示的问题
+ return path.join(basePath, dirPath).replace(/\\/g, "/")
+}
+
+/**
+ * 截取文档路径*/
+function getPath(path,ele) {
+ let item=prefixPath(path,ele);
+ //if (item.split('/')[6]) {
+ // return item.split('/')[3] + '/' + item.split('/')[4]+ '/' + item.split('/')[5]+ '/' + item.split('/')[6]
+ //}else if (item.split('/')[5]) {
+ // return item.split('/')[3] + '/' + item.split('/')[4]+ '/' + item.split('/')[5]
+ //}else if (item.split('/')[4]) {
+ // return item.split('/')[3] + '/' + item.split('/')[4]
+ //} else {
+ // return item.split('/')[3]
+ //}
+
+ if (item.split('/')[6]) {
+ return item.split('/')[4]+ '/' + item.split('/')[5]+ '/' + item.split('/')[6]
+ }else if (item.split('/')[5]) {
+ return item.split('/')[4]+ '/' + item.split('/')[5]
+ }else {
+ return item.split('/')[4]
+ }
+}
+
+/**
+ * 递归获取分组信息并排序*/
+function getGroupChildren(path,ele,root) {
+ let pa = fs.readdirSync(path + "/" + ele + "/");
+ let palist=pa;
+ // console.log("pathlist", pa)
+ pa = palist.sort(function (a, b) {
+ //console.log("a " + a, a.replace(".md", "").match(/^[^-]/))
+ //console.log("b " + b, b.replace(".md", "").match(/^[^-]/))
+ let num1 = a.split("-")[0]
+ let num2 = b.split("-")[0]
+ //console.log("num1", num1, "num2", num2)
+ //console.log(num1 - num2)
+ return num1 - num2
+ //return a.replace(".md", "").match(/^[^-]/) - b.replace(".md", "").match(/^[^-]/)
+ //return a.replace(".md", "").match(/[^-]*$/) - b.replace(".md", "").match(/[^-]*$/)
+ });
+ // console.log("after sort ", pa)
+ pa.forEach(function (item, index) {
+ let info = fs.statSync(path + "/" + ele + "/" + item);
+ if (info.isDirectory()) {
+ let children = [];
+ let group = {};
+ group.title = item.split('-')[1];
+ group.collapsable = true;
+ group.sidebarDepth = 4;
+ getGroupChildren(path + "/" + ele, item, children);
+ group.children=children;
+ root.push(group);
+ } else {
+ if (checkFileType(item)) {
+ root.push(getPath(path + "/" + ele, item));
+ }
+ }
+ })
+}
+/**
+ * 初始化*/
+function getChildren(path,ele){
+ var root=[]
+ getGroupChildren(path,ele,root);
+ return root;
+}
+
+// console.log("demo", getChildren('./docs','database/mysql-basic'))
+
+module.exports = {getChildren: getChildren};
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..c611e07
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,82 @@
+---
+home: true
+icon: home
+title: 主页
+heroImage: /logo.png
+heroText: 程序员大彬
+tagline: 非科班自学转码之路
+actions:
+ - text: 来不及了,快上车 💡
+ link: /java/java-basic.md
+ type: primary
+features:
+ - title: 经典计算机书籍
+ icon: repo
+ details: 大彬精心整理200本经典计算机书籍
+ link: https://github.com/Tyson0314/java-books
+ - title: Leetcode算法笔记
+ icon: note
+ details: 谷歌师兄整理的Leetcode算法笔记
+ link: learning-resources/leetcode-note.html
+projects:
+ - name: java-books
+ desc: 经典计算机书籍电子版
+ link: https://github.com/Tyson0314/java-books
+ icon: /assets/img/vuepress.png
+
+ - name: Leetcode算法手册
+ desc: Leetcode算法手册
+ link: learning-resources/leetcode-note.html
+ icon: /assets/img/vuepress-hope-logo.svg
+
+
+---
+
+
+
+大彬是**非科班**出身,大三开始自学Java,校招斩获**京东、携程、华为**等offer。作为一名转码选手,深感这一路的不易。
+
+**希望我的分享可以帮助更多的小伙伴,我踩过的坑你们不要再踩**!
+
+[
](http://img.dabin-coder.cn/image/微信群.png)
+[
](https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247488751&idx=1&sn=507e27534b6ea5f4b3771b30e1fcf57e&chksm=ce98e9a9f9ef60bfbf1370899b49181bae5247e5935714f7ad9e3d06c0028a25c0bfc34d4441#rd)
+[
](https://space.bilibili.com/1729916794)
+[
](https://www.zhihu.com/people/dai-shu-bin-13)
+[
](https://github.com/Tyson0314/java-books)
+
+## 秋招提前批信息汇总
+
+[秋招提前批及正式批信息汇总(含内推)](https://docs.qq.com/sheet/DYW9ObnpobXNRTXpq?tab=BB08J2)
+
+## 面试手册电子版
+
+本网站所有内容已经汇总成**PDF电子版**,需要电子版的小伙伴,可以在公众号【**程序员大彬**】后台回复【**面试**】,即可获取~
+
+## 学习路线
+
+
+
+> 微信搜索【程序员大彬】,回复【学习路线】获取**高清图**
+
+## 交流群
+
+学习路上,难免遇到很多坑,为方便大家交流求职和技术问题,我建了**求职&技术交流群**,在群里可以讨论技术、面试相关问题,也可以获得阿里、字节等大厂的内推机会!
+
+交流群学习氛围很浓厚,截个图给大家感受下。
+
+
+
+
+
+
+
+感兴趣的小伙伴可以扫描下方的二维码**加我微信**,**备注加群**,我拉你进群,一起学习成长!
+
+
+
+## 参与贡献
+
+1. 如果您对本项目有任何建议或发现文中内容有误的,欢迎提交 issues 进行指正。
+2. 对于文中我没有涉及到知识点,欢迎提交 PR。
+3. 如果您有文章推荐请以 markdown 格式到邮箱 `1713476357@qq.com`,
+[中文技术文档的写作规范指南](https://github.com/ruanyf/document-style-guide)。
diff --git a/docs/about/contact.md b/docs/about/contact.md
new file mode 100644
index 0000000..01e0eaa
--- /dev/null
+++ b/docs/about/contact.md
@@ -0,0 +1,27 @@
+---
+sidebar: heading
+---
+
+## 联系我
+
+如果有什么疑问或者建议,欢迎添加大彬微信进行交流~
+
+
+
+
+
+## 交流群
+
+学习路上,难免遇到很多坑,为方便大家交流求职和技术问题,我建了**求职&技术交流群**,在群里可以讨论技术、面试相关问题,也可以获得阿里、字节等大厂的内推机会!
+
+交流群学习氛围很浓厚,截个图给大家感受下。
+
+
+
+
+
+
+
+感兴趣的小伙伴可以扫描下方的二维码**加我微信**,**备注加群**,我拉你进群,一起学习成长!
+
+
diff --git a/docs/about/introduce.md b/docs/about/introduce.md
new file mode 100644
index 0000000..6e8c6f1
--- /dev/null
+++ b/docs/about/introduce.md
@@ -0,0 +1,66 @@
+---
+sidebar: heading
+
+---
+
+大家好,我是大彬~
+
+我是非科班出身,本科学的不是计算机,大四开始自学Java,并且找到了中大厂的offer。自学路上遇到不少问题,每天晚上都是坚持到一两点才睡觉,**最终也拿到了30w的offer**。
+
+
+
+下面来说说自己的经历吧(附自学路线)。
+
+## 接触编程
+
+大学以前基本没碰过电脑,家里没电脑,也没去过网吧。高中的计算机课程,期末作业要完成一个自我介绍的PPT,也不会做,最后直接抄同桌的作业(复制粘贴都不会。。还得同桌教,捂脸)。
+
+高考完一个月后,买了电脑,真正开始使用上了电脑。
+
+大一上学期的时候,系里开了一门C语言的课程,这也是我第一次接触编程。教材是英文的,刚开始学还是挺头大的。每次课程作业,周围的同学都是一顿复制粘贴,我也一样嘿嘿。
+
+记得在讲指针那一章的时候,听的一头雾水。稍微走神,回过头来,已经不知道讲的是啥了。
+
+后面系里开设了兴趣小组,因为平时比较闲,也想着去捣鼓点东西,就去参加了。刚开始的时候,什么都不懂,老师推荐我学一下51单片机,拿了一本厚厚的51单片机的书籍,跟着书里的demo敲了一遍,发现了新天地!原来编程这么有意思!
+
+
+
+记得第一次跑出流水灯的时候,那叫一个激动啊,满满的都是成就感!后面也写了一些电机、红外遥控等demo。从那以后,激发了我学习编程的兴趣。
+
+到了大二,辅导员在群里发布全国电子设计大赛的信息,参赛题跟四轴飞行器相关,那段时间对四轴飞行器比较感兴趣,于是约了两个小伙伴一块参加。距离比赛时间只有一个月,在那一个月的时间里,每天都是早出晚归,吃饭的时候还在想着哪一块代码出了bug。虽然最后没能获奖,但是在这个过程中,学到很多知识,编程能力也有了很大的提升。
+
+## 决定转码
+
+转眼间,大三开学,开始纠结考研还是工作,思考了一周时间,也进了系里的实验室体验了一把研究生生活,最后还是听从内心的想法,决定直接找工作。
+
+我咨询了本专业的师兄师姐们往年的就业情况,他们大部分人还是找了互联网方向的工作。有一个在传统行业的师兄,也劝我投互联网公司的岗位,因为在传统行业加班也不少,但是工资贼低。。最后决定转行程序员,找后端相关的工作。
+
+那么学习哪一种语言呢?当时有三个选择:c++,Java,python。
+
+那段时间python比较火,但是经过一番深思熟虑之后,还是选择了Java。为什么选择Java呢?
+
+很简单,市场需求大,学习难度适中。相比科班同学来说,我缺乏系统的计算机基础知识,而距离秋招也只有不到一年时间,所以还是选择学习难度低一点的Java。
+
+## 闭关自学
+
+确定方向后,便开始制定学习路线。不得不说,Java要学的东西是真的多。。
+
+自学期间遇到挺多问题,比如一些环境配置问题,有时候搞上好几天,很打击积极性。中途也有很多次怀疑自己的水平,是不是不适合干编程,差点就放弃了。幸好最后还是坚持了下来。
+
+半年多的时间,除了平时上课,其他时间就是在图书馆。周末或者节假日,每天都是7点起床,八点到图书馆开始学习,到了晚上十点,图书馆闭馆,才回宿舍,每天都是图书馆最后走的一批。回到宿舍,洗完澡,继续肝到十二点多(卷王!)。
+
+
+
+> 很多人在问,大三才开始自学Java,来的及吗? 我觉得,还是看个人的投入程度和学习能力。有些人自学能力强一点,每天可以投入10小时及以上的时间去学习,那完全没问题。
+
+自学过程还是挺辛苦的,要耐得住寂寞,最最重要的还是得坚持!
+
+我根据自己的自学经历,整理了一些学习过程中踩坑总结的经验,希望自学的小伙伴可以少走弯路:
+
+- **注重实践**,不要只是埋头看书,一定要多动手写代码。
+- 刚开始自学的时候,可以不用太深究细节,不然可能会怀疑自己的学习能力。等到后面有了一定的基础,回过头来重新回顾,可能会恍然大悟,没有当初想的那么难。
+- 可以适当加一些[交流群](https://topjavaer.cn/about/contact.html#%E4%BA%A4%E6%B5%81%E7%BE%A4),遇到不懂的知识点,多与其他人交流。
+
+## 自学路线
+
+具体学习路线[点这里](https://topjavaer.cn/learning-resources/java-learn-guide.html#%E8%87%AA%E5%AD%A6%E8%B7%AF%E7%BA%BF)
\ No newline at end of file
diff --git a/docs/advance/concurrent/1-current-limiting.md b/docs/advance/concurrent/1-current-limiting.md
new file mode 100644
index 0000000..52003ff
--- /dev/null
+++ b/docs/advance/concurrent/1-current-limiting.md
@@ -0,0 +1,139 @@
+# 限流算法
+
+大多数情况下,我们不需要自己实现一个限流系统,但限流在实际应用中是一个非常微妙、有很多细节的系统保护手段,尤其是在高流量时,了解你所使用的限流系统的限流算法,将能很好地帮助你充分利用该限流系统达到自己的商业需求和目的,并规避一些使用限流系统可能带来的大大小小的问题。
+
+## 令牌桶算法
+
+令牌桶(token bucket)算法,指的是设计一个容器(即“桶”),由某个组件持续运行往该容器中添加令牌(token),令牌可以是简单的数字、字符或组合,也可以仅仅是一个计数,然后每个请求进入系统时,需要从桶中领取一个令牌,所有请求都必须有令牌才能进入后端系统。当令牌桶空时,拒绝请求;当令牌桶满时,不再往其中添加新的令牌。
+令牌桶算法的架构如图1所示:
+
+
+
+令牌桶算法的实现逻辑如下:
+
+首先会有一个定义的时间窗口的访问次数阈值,例如每天1000人,每秒5个请求之类,限流系统一般最小粒度是秒,再小就会因为实现和性能的原因而变得不准确或不稳定,假设是T秒内允许N个请求,那么令牌桶算法则会使令牌添加组件每T秒往令牌桶中添加N个令牌。
+
+其次,令牌桶需要有一个最大值M,当令牌添加组件检测到令牌桶中已经有M个令牌时,剩余的令牌会被丢弃。反映到限流系统中,可以认为是当前系统允许的瞬时最大流量,但不是持续最大流量。例如令牌桶中的令牌最大数量是100个,每秒钟会往其中添加10个新令牌,当令牌满的时候,突然出现100 TPS的流量,这时候是可以承受的,但是假如连续两秒的100 TPS流量就不行,因为令牌添加速度是一秒10个,添加速度跟不上使用速度。
+
+因此,凡是使用令牌桶算法的限流系统,我们都会注意到它在配置时要求两个参数:
+
+- 平均阈值(rate或average)
+- 高峰阈值(burst或peak)
+
+
+通过笔者的介绍,读者应该意识到,令牌桶算法的高峰阈值是有特指的,并不是指当前限流系统允许的最高流量。因为这一描述可能会使人认为只要低于该阈值的流量都可以,但事实上不是这样,因为只要高于添加速度的流量持续一段时间都会出现问题。
+
+反过来说,令牌桶算法的限流系统不容易计算出它支持的最高流量,因为它能实时支持的最高流量取决于那整个时间段内的流量变化情况即令牌存量,而不是仅仅取决于一个瞬时的令牌量。
+
+最后,当有组件请求令牌的时候,令牌桶会随机挑选一个令牌分发出去,然后将该令牌从桶中移除。注意,此时令牌桶不再做别的操作,令牌桶永远不会主动要求令牌添加组件补充新的令牌。
+
+令牌桶算法有一个同一思想、方向相反的变种,被称为漏桶(leaky bucket)算法,它是令牌桶的一种改进,在商业应用中非常广泛。
+
+漏桶算法的基本思想,是将请求看作水流,用一个底下有洞的桶盛装,底下的洞漏出水的速率是恒定的,所有请求进入系统的时候都会先进入这个桶,并慢慢由桶流出交给后台服务。桶有一个固定大小,当水流量超过这个大小的时候,多余的请求都会被丢弃。
+
+漏桶算法的架构如图所示:
+
+
+
+漏桶算法的实现逻辑如下:
+
+- 首先会有一个容器存放请求,该容器有一个固定大小M,所有请求都会被先存放到该容器中。
+- 该容器会有一个转发逻辑,该转发以每T秒N个请求的速率循环发生。
+- 当容器中请求数已经达到M个时,拒绝所有新的请求。
+
+
+因此同样地,漏桶算法的配置也需要两个值:平均值(rate)和峰值(burst)。只是平均值这时候是用来表示漏出的请求数量,峰值则是表示桶中可以存放的请求数量。
+
+注意:漏桶算法和缓冲的限流思想不是一回事!
+
+同样是将请求放在一个容器中,漏桶算法和缓冲不是一个用途,切不可搞混,它们的区别如下:
+
+- 漏桶算法中,存在桶中的请求会以恒定的速率被漏给后端业务服务器,而缓冲思想中,放在缓冲区域的请求只有等到后端服务器空闲下来了,才会被发出去。
+- 漏桶算法中,存在桶中的请求是原本就应该被系统处理的,是系统对外界宣称的预期,不应该被丢失,而缓冲思想中放在缓冲区域的请求仅仅是对意外状况的尽量优化,并没有任何强制要求这些请求可以被处理。
+
+
+漏桶算法和令牌桶算法在思想上非常接近,而实现方向恰好相反,它们有如下的相同和不同之处:
+
+- 令牌桶算法以固定速率补充可以转发的请求数量(令牌),而漏桶算法以固定速率转发请求;
+- 令牌桶算法限制数量的是预算数,漏桶算法限制数量的是实际请求数;
+- 令牌桶算法在有爆发式增长的流量时可以一定程度上接受,漏桶算法也是,但当流量爆发时,令牌桶算法会使业务服务器直接承担这种流量,而漏桶算法的业务服务器感受到的是一样的速率变化。
+
+
+因此,通过以上比较,我们会发现漏桶算法略优于令牌桶算法,因为漏桶算法对流量控制更平滑,而令牌桶算法在设置的数值范围内,会将流量波动忠实地转嫁到业务服务器头上。
+
+漏桶算法在Nginx和分布式的限流系统例如Redis的限流功能中都有广泛应用,是目前业界最流行的算法之一。
+
+## 时间窗口算法
+
+时间窗口算法是比较简单、基础的限流算法,由于它比较粗略,不适合大型、流量波动大或者有更精细的流量控制需求的网站。
+
+时间窗口算法根据确定时间窗口的方式,可以分为两种:
+
+- 固定时间窗口算法
+- 滑动时间窗口算法
+
+
+固定时间窗口算法最简单,相信如果让初次接触限流理念的读者去快速设计实现一个限流系统的话,也可以很快想到这种算法。这种算法即固定一个时间段内限定一个请求阈值,没有达到则让请求通过,达到数量阈值了就拒绝请求。步骤如下:
+
+- 先确定一个起始时间点,一般就是系统启动的时间。
+- 从起始时间点开始,根据我们的需求,设置一个最大值M,开始接受请求并从0开始为请求计数。
+- 在时间段T内,请求计数超过M时,拒绝所有剩下的请求。
+- 超过时间段T后,重置计数。
+
+
+固定时间窗口算法的思路固然简单,但是它的逻辑是有问题的,它不适合流量波动大和有精细控制流量需求的服务。让我们看以下例子:
+
+假设我们的时间段T是1秒,请求最大值是10,在第一秒内,请求数量分布是第500毫秒时有1个请求,第800毫秒时有9个请求,如图3所示:
+
+
+
+这是对于第一秒而言,这个请求分布是合理的。
+
+此时第二秒的第200毫秒(即两秒中的第1200毫秒)内,又来了10个请求,如图4所示:
+
+
+
+单独看第二秒依然是合理的,但是两个时间段连在一起的时候,就出现了问题,如图5所示:
+
+
+
+从500毫秒到1200毫秒,短短700毫秒的时间内后端服务器就接收了20个请求,这显然违背了一开始我们希望1秒最多10个的初衷。这种远远大于预期流量的流量加到后端服务器头上,是会造成不可预料的后果的。因此,人们改进了固定窗口的算法,将其改为检查任何一个时间段都不超过请求数量阈值的时间窗口算法:滑动时间窗口算法。
+
+滑动时间窗口算法要求当请求进入系统时,回溯过去的时间段T,找到其中的请求数量,然后决定是否接受当前请求,因此,滑动时间窗口算法需要记录时间段T内请求到达的时间点,逻辑如图6所示:
+
+
+
+解释如下:
+
+1、确定一个起始时间点,一般就是系统启动的时间,并记录该点为时间窗口的开始点。然后创建一个空的列表作为时间窗口内请求进入的时间戳记录。
+
+2、当请求到来时,使用当前时间戳比较它是否在时间窗口起始点加上T时间段(从开始点到开始点+T就是时间窗口)内。
+
+- 如果在,则查看当前时间窗口内记录的所有请求的数量:
+ - 如果超过,则拒绝请求。
+ - 如果没有,则将该请求加入到时间戳记录中,并将请求交给后端业务服务器。
+- 如果不在,则查看时间戳记录,将时间戳最久远的记录删除,然后将时间窗口的开始点更新为第二久远的记录时间,然后回到步骤2,再次检查时间戳是否在时间窗口内。
+
+
+滑动时间窗口尽管有所改进,但依然不能很好应对某个时间段内突发大量请求,而令牌桶和漏桶算法就由于允许指定平均请求率和最大瞬时请求率,它比时间窗口算法控制更精确。
+
+时间窗口算法可以通过多时间窗口来改进。例如,可以设置一个1秒10 TPS的时间窗口限流和一个500毫秒5 TPS的时间窗口限流,二者同时运行,如此就可以保证更精确的限流控制。
+
+## 队列法
+
+队列法与漏桶算法很类似,都是将请求放入到一个区域,然后业务服务器从中提取请求,但是队列法采用的是完全独立的外部系统,而不是依附于限流系统。队列法的架构如图7所示:
+
+
+
+与漏桶算法相比,队列法的优势如下:
+
+- 由业务逻辑层决定请求收取的速度。限流系统即队列不需要再关注流量的设置(例如T是多少,N是多少,M又是多少等等),只需要专注保留发送的请求,而业务服务器由于完全掌控消息的拉取,可以根据自身条件决定请求获取的速度,更加自由。
+- 完全将业务逻辑层保护起来,并且可以增加服务去消费这些请求。这一手段将业务服务器完全隐藏在了客户端后面,由队列去承担所有流量,也可以更好地保护自身不受到恶意流量的攻击。
+- 队列可以使用更健壮、更成熟的服务,这些服务比限流系统复杂,但能够承受大得多的流量。例如,业务服务器使用的是像阿里云或者AWS这样的消息队列的话,业务服务器就不用担心扩容的问题了,只要请求对实时性的要求不高。业务服务器由于使用了云服务,队列一端的扩容不用担心,而由于消息是自由决定拉取频率和处理速度,自身的扩容压力也就不那么大了。
+
+
+但队列法最大的缺陷,就是服务器不能直接与客户端沟通,因此只适用于客户端令业务服务器执行任务且不要求响应的用例,所有客户端需要有实质响应的服务都不能使用。例如,业务服务器提供的服务是消息发送服务,那么这种模式就可以的,但如果客户端是请求某些用户信息,那这种方式就完全不可行了。
+
+
+
+> 本文摘录自《深入浅出大型网站架构设计》
\ No newline at end of file
diff --git a/docs/advance/concurrent/2-load-balance.md b/docs/advance/concurrent/2-load-balance.md
new file mode 100644
index 0000000..968d6d0
--- /dev/null
+++ b/docs/advance/concurrent/2-load-balance.md
@@ -0,0 +1,164 @@
+# 高可用——负载均衡
+
+## **一、 什么是负载均衡?**
+
+**什么是负载均衡?**
+
+记得第一次接触 Nginx 是在实验室,那时候在服务器部署网站需要用 Nginx 。Nginx 是一个服务组件,用来反向代理、负载平衡和 HTTP 缓存等。那么这里的 负载均衡 是什么?
+
+负载均衡(LB,Load Balance),是一种技术解决方案。用来在多个资源(一般是服务器)中分配负载,达到最优化资源使用,避免过载。
+
+
+
+
+
+
+
+资源,相当于每个服务实例的执行操作单元,负载均衡就是将大量的数据处理操作分摊到多个操作单元进行执行,用来解决互联网分布式系统的大流量、高并发和高可用的问题。那什么是高可用呢?
+
+## **二、什么是高可用?**
+
+**首先了解什么是高可用?**
+
+这是 CAP 定理是分布式系统的基础,也是分布式系统的 3 个指标:
+
+1. Consistency(一致性)
+2. Availability(可用性)
+3. Partition tolerance(分区容错性)
+
+那高可用(High Availability)是什么?高可用,简称 HA,是系统一种特征或者指标,通常是指,提供一定性能上的服务运行时间,高于平均正常时间段。反之,消除系统服务不可用的时间。
+
+衡量系统是否满足高可用,就是当一台或者多台服务器宕机的时候,系统整体和服务依然正常可用。
+
+举个例子,一些知名的网站保证 4 个 9 以上的可用性,也就是可用性超过 99.99%。那 0.01% 就是所谓故障时间的百分比。比如电商网站有赞,服务不可用会造成商家损失金钱和用户。那么在提高可用性基础上同时,对系统宕机和服务不可用会有补偿。
+
+
+
+
+
+
+
+比如下单服务,可以使用带有负载均衡的多个下单服务实例,代替单一的下单服务实例,即使用冗余的方式来提高可靠性。
+
+总而言之,负载均衡(Load Balance)是分布式系统架构设计中必须考虑的因素之一。一般通过负载均衡,冗余同一个服务实例的方式,解决分布式系统的大流量、高并发和高可用的问题。负载均衡核心关键:在于是否分配均匀。
+
+## **三、常见的负载均衡案例**
+
+
+
+
+
+
+
+场景1:微服务架构中,网关路由到具体的服务实例 hello:
+
+- 两个相同的服务实例 hello service ,一个端口 8000 ,另一个端口 8082
+- 通过 Kong 的负载均衡 LB 功能,让请求均匀的分发到两个 hello 服务实例
+- Kong 的负载均衡策略算法很多:默认 weighted-round-robin 算法,还有 consumer: consumer id 作为 hash 算法输入值等
+
+
+
+
+
+
+
+场景2:微服务架构中,A 服务调用 B 服务的集群。通过了 Ribbon 客户端负载均衡组件:
+
+- 负载均衡策略算法并不高级,最简单的是随机选择和轮循
+
+## **四、互联网分布式系统解决方案**
+
+
+
+
+
+
+
+常见的互联网分布式系统架构分为几层,一般如下:
+
+- 客户端层:比如用户浏览器、APP 端
+- 反向代理层:技术选型 Nignx 或者 F5 等
+- Web 层:前后端分离场景下, Web 端可以用 NodeJS 、 RN 、Vue
+- 业务服务层:用 Java 、Go,一般互联网公司,技术方案选型就是 SC 或者 Spring Boot + Dubbo 服务化
+- 数据存储层:DB 选型 MySQL ,Cache 选型 Redis ,搜索选型 ES 等
+
+一个请求从第 1 层到第 4 层,层层访问都需要负载均衡。即每个上游调用下游多个业务方的时候,需要均匀调用。这样整体系统来看,就比较负载均衡
+
+**第 1 层:客户端层 -> 反向代理层 的负载均衡**
+
+客户端层 -> 反向代理层的负载均衡如何实现呢?
+
+答案是:DNS 的轮询。 DNS 可以通过 A (Address,返回域名指向的 IP 地址)设置多个 IP 地址。比如这里访问 [http://bysocket.com](https://link.zhihu.com/?target=http%3A//bysocket.com) 的 DNS 配置了 ip1 和 ip2 。为了反向代理层的高可用,至少会有两条 A 记录。这样冗余的两个 ip 对应的 nginx 服务实例,防止单点故障。
+
+每次请求 [http://bysocket.com](https://link.zhihu.com/?target=http%3A//bysocket.com) 域名的时候,通过 DNS 轮询,返回对应的 ip 地址,每个 ip 对应的反向代理层的服务实例,也就是 nginx 的外网ip。这样可以做到每一个反向代理层实例得到的请求分配是均衡的。
+
+**第 2 层:反向代理层 -> Web 层 的负载均衡**
+
+反向代理层 -> Web 层 的负载均衡如何实现呢?
+
+是通过反向代理层的负载均衡模块处理。比如 nginx 有多种均衡方法:
+
+1. 请求轮询。请求按时间顺序,逐一分配到 web 层服务,然后周而复始。如果 web 层服务 down 掉,自动剔除
+
+```text
+upstream web-server {
+server ip3;
+server ip4;
+}
+```
+
+ip 哈希。按照 ip 的哈希值,确定路由到对应的 web 层。只要是用户的 ip 是均匀的,那么请求到 Web 层也是均匀的。
+
+1. 还有个好处就是同一个 ip 的请求会分发到相同的 web 层服务。这样每个用户固定访问一个 web 层服务,可以解决 session 的问题。
+
+```text
+upstream web-server {
+ip_hash;
+server ip3;
+server ip4;
+}
+```
+
+1. weight 权重 、 fair、url_hash 等
+
+**第 3 层:Web 层 -> 业务服务层 的负载均衡**
+
+Web 层 -> 业务服务层 的负载均衡如何实现呢?
+
+比如 Dubbo 是一个服务治理方案,包括服务注册、服务降级、访问控制、动态配置路由规则、权重调节、负载均衡。其中一个特性就是智能负载均衡:内置多种负载均衡策略,智能感知下游节点健康状况,显著减少调用延迟,提高系统吞吐量。
+
+为了避免避免单点故障和支持服务的横向扩容,一个服务通常会部署多个实例,即 Dubbo 集群部署。会将多个服务实例成为一个服务提供方,然后根据配置的随机负载均衡策略,在20个 Provider 中随机选择了一个来调用,假设随机到了第7个 Provider。LoadBalance 组件从提供者地址列表中,使用均衡策略,选择选一个提供者进行调用,如果调用失败,再选另一台调用。
+
+Dubbo内置了4种负载均衡策略:
+
+- RandomLoadBalance:随机负载均衡。随机的选择一个。是Dubbo的默认负载均衡策略。
+- RoundRobinLoadBalance:轮询负载均衡。轮询选择一个。
+- LeastActiveLoadBalance:最少活跃调用数,相同活跃数的随机。活跃数指调用前后计数差。使慢的 Provider 收到更少请求,因为越慢的 Provider 的调用前后计数差会越大。
+- ConsistentHashLoadBalance:一致性哈希负载均衡。相同参数的请求总是落在同一台机器上。
+
+同样,因为业务的需要,也可以实现自己的负载均衡策略
+
+**第 4 层:业务服务层 -> 数据存储层 的负载均衡**
+
+数据存储层的负载均衡,一般通过 DBProxy 实现。比如 MySQL 分库分表。
+
+当单库或者单表访问太大,数据量太大的情况下,需要进行垂直拆分和水平拆分两个维度。比如水平切分规则:
+
+- Range 、 时间
+- hash 取模,订单根据店铺ID 等
+
+但伴随着这块的负载会出现下面的问题,需要解决:
+
+- 分布式事务
+- 跨库 join 等
+
+现状分库分表的产品方案很多:当当 sharding-jdbc、阿里的 Cobar 等
+
+## **五、小结**
+
+对外看来,负载均衡是一个系统或软件的整体。对内看来,层层上下游调用。只要存在调用,就需要考虑负载均衡这个因素。所以负载均衡(Load Balance)是分布式系统架构设计中必须考虑的因素之一。考虑主要是如何让下游接收到的请求是均匀分布的:
+
+- 第 1 层:客户端层 -> 反向代理层 的负载均衡。通过 DNS 轮询
+- 第 2 层:反向代理层 -> Web 层 的负载均衡。通过 Nginx 的负载均衡模块
+- 第 3 层:Web 层 -> 业务服务层 的负载均衡。通过服务治理框架的负载均衡模块
+- 第 4 层:业务服务层 -> 数据存储层 的负载均衡。通过数据的水平分布,数据均匀了,理论上请求也会均匀。比如通过买家ID分片类似
\ No newline at end of file
diff --git a/docs/advance/concurrent/README.md b/docs/advance/concurrent/README.md
new file mode 100644
index 0000000..b495d0c
--- /dev/null
+++ b/docs/advance/concurrent/README.md
@@ -0,0 +1,4 @@
+## 高并发专题(更新中)
+
+- [限流算法](./1-current-limiting.md)
+- [负载均衡](./2-load-balance.md)
diff --git "a/\345\205\266\344\273\226/\350\256\276\350\256\241\346\250\241\345\274\217.md" b/docs/advance/design-pattern-all.md
similarity index 100%
rename from "\345\205\266\344\273\226/\350\256\276\350\256\241\346\250\241\345\274\217.md"
rename to docs/advance/design-pattern-all.md
diff --git a/docs/advance/design-pattern/1-principle.md b/docs/advance/design-pattern/1-principle.md
new file mode 100644
index 0000000..cec9d65
--- /dev/null
+++ b/docs/advance/design-pattern/1-principle.md
@@ -0,0 +1,9 @@
+# 设计模式的六大原则
+
+- 开闭原则:对扩展开放,对修改关闭,多使用抽象类和接口。
+- 里氏替换原则:基类可以被子类替换,使用抽象类继承,不使用具体类继承。
+- 依赖倒转原则:要依赖于抽象,不要依赖于具体,针对接口编程,不针对实现编程。
+- 接口隔离原则:使用多个隔离的接口,比使用单个接口好,建立最小的接口。
+- 迪米特法则:一个软件实体应当尽可能少地与其他实体发生相互作用,通过中间类建立联系。
+- 合成复用原则:尽量使用合成/聚合,而不是使用继承。
+
diff --git a/docs/advance/design-pattern/10-observer.md b/docs/advance/design-pattern/10-observer.md
new file mode 100644
index 0000000..0ad3819
--- /dev/null
+++ b/docs/advance/design-pattern/10-observer.md
@@ -0,0 +1,17 @@
+# 观察者模式
+
+定义对象之间的一对多依赖,当一个对象状态改变时,它的所有依赖都会收到通知并且自动更新状态。
+
+主题(Subject)是被观察的对象,而其所有依赖者(Observer)称为观察者。
+
+多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。其作用是让主题对象和观察者松耦合。
+
+观察者模式所涉及的角色有:
+
+- 抽象主题(Subject)角色:抽象主题角色把所有对观察者对象的引用保存在一个聚集(比如ArrayList对象)里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,抽象主题角色又叫做抽象被观察者(Observable)角色。
+- 具体主题(ConcreteSubject)角色:将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者(Concrete Observable)角色。
+- 抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在得到主题的通知时更新自己,这个接口叫做更新接口。
+- 具体观察者(ConcreteObserver)角色:存储与主题的状态自恰的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。如果需要,具体观察者角色可以保持一个指向具体主题对象的引用。
+
+
+
diff --git a/docs/advance/design-pattern/11-proxy.md b/docs/advance/design-pattern/11-proxy.md
new file mode 100644
index 0000000..33e71a8
--- /dev/null
+++ b/docs/advance/design-pattern/11-proxy.md
@@ -0,0 +1,77 @@
+# 代理模式
+
+代理模式使用代理对象完成用户请求,屏蔽用户对真实对象的访问。
+
+## 静态代理
+
+静态代理:代理类在编译阶段生成,程序运行前就已经存在,在编译阶段将通知织入Java字节码中。
+
+缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类。同时,一旦接口增加方法,目标对象与代理对象都要维护。
+
+## 动态代理
+
+动态代理:代理类在程序运行时创建,在内存中临时生成一个代理对象,在运行期间对业务方法进行增强。
+
+**JDK动态代理**
+
+JDK实现代理只需要使用newProxyInstance方法:
+
+```java
+static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h )
+```
+
+三个入参:
+
+- ClassLoader loader:指定当前目标对象使用的类加载器
+- Class>[] interfaces:目标对象实现的接口的类型
+- InvocationHandler:当代理对象调用目标对象的方法时,会触发事件处理器的invoke方法()
+
+示例代码:
+
+```
+public class DynamicProxyDemo {
+
+ public static void main(String[] args) {
+ //被代理的对象
+ MySubject realSubject = new RealSubject();
+
+ //调用处理器
+ MyInvacationHandler handler = new MyInvacationHandler(realSubject);
+
+ MySubject subject = (MySubject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(),
+ realSubject.getClass().getInterfaces(), handler);
+
+ System.out.println(subject.getClass().getName());
+ subject.rent();
+ }
+}
+
+interface MySubject {
+ public void rent();
+}
+class RealSubject implements MySubject {
+
+ @Override
+ public void rent() {
+ System.out.println("rent my house");
+ }
+}
+class MyInvacationHandler implements InvocationHandler {
+
+ private Object subject;
+
+ public MyInvacationHandler(Object subject) {
+ this.subject = subject;
+ }
+
+ @Override
+ public Object invoke(Object object, Method method, Object[] args) throws Throwable {
+ System.out.println("before renting house");
+ //invoke方法会拦截代理对象的方法调用
+ Object o = method.invoke(subject, args);
+ System.out.println("after rentint house");
+ return o;
+ }
+}
+```
+
diff --git a/docs/advance/design-pattern/12-builder.md b/docs/advance/design-pattern/12-builder.md
new file mode 100644
index 0000000..201ee16
--- /dev/null
+++ b/docs/advance/design-pattern/12-builder.md
@@ -0,0 +1,79 @@
+# 建造者模式
+
+建造者模式:封装一个对象的构造过程,并允许按步骤构造。
+
+有两种形式:传统建造者模式和传统建造者模式变种。
+
+传统建造者模式:
+
+```java
+public class Computer {
+ private final String cpu;//必须
+ private final String ram;//必须
+ private final int usbCount;//可选
+ private final String keyboard;//可选
+ private final String display;//可选
+
+ private Computer(Builder builder){
+ this.cpu=builder.cpu;
+ this.ram=builder.ram;
+ this.usbCount=builder.usbCount;
+ this.keyboard=builder.keyboard;
+ this.display=builder.display;
+ }
+ public static class Builder{
+ private String cpu;//必须
+ private String ram;//必须
+ private int usbCount;//可选
+ private String keyboard;//可选
+ private String display;//可选
+
+ public Builder(String cup,String ram){
+ this.cpu=cup;
+ this.ram=ram;
+ }
+ public Builder setDisplay(String display) {
+ this.display = display;
+ return this;
+ }
+ //set...
+ public Computer build(){
+ return new Computer(this);
+ }
+ }
+}
+
+public class ComputerDirector {
+ public void makeComputer(ComputerBuilder builder){
+ builder.setUsbCount();
+ builder.setDisplay();
+ builder.setKeyboard();
+ }
+}
+```
+
+传统建造者模式变种,链式调用:
+
+```java
+public class LenovoComputerBuilder extends ComputerBuilder {
+ private Computer computer;
+ public LenovoComputerBuilder(String cpu, String ram) {
+ computer=new Computer(cpu,ram);
+ }
+ @Override
+ public void setUsbCount() {
+ computer.setUsbCount(4);
+ }
+ //...
+ @Override
+ public Computer getComputer() {
+ return computer;
+ }
+}
+
+Computer computer=new Computer.Builder("因特尔","三星")
+ .setDisplay("三星24寸")
+ .setKeyboard("罗技")
+ .setUsbCount(2)
+ .build();
+```
diff --git a/docs/advance/design-pattern/2-singleton.md b/docs/advance/design-pattern/2-singleton.md
new file mode 100644
index 0000000..3af2c2b
--- /dev/null
+++ b/docs/advance/design-pattern/2-singleton.md
@@ -0,0 +1,94 @@
+# 单例模式
+
+需要对实例字段使用线程安全的延迟初始化,使用双重检查锁定的方案;需要对静态字段使用线程安全的延迟初始化,使用静态内部类的方案。
+
+## 饿汉模式
+
+JVM在类的初始化阶段,会执行类的静态方法。在执行类的初始化期间,JVM会去获取Class对象的锁。这个锁可以同步多个线程对同一个类的初始化。
+
+饿汉模式只在类加载的时候创建一次实例,没有多线程同步的问题。单例没有用到也会被创建,而且在类加载之后就被创建,内存就被浪费了。
+
+```
+public class Singleton {
+ private static Singleton instance = new Singleton();
+ private Singleton() {}
+ public static Singleton newInstance() {
+ return instance;
+ }
+}
+```
+## 双重检查锁定
+
+双重校验锁先判断 instance 是否已经被实例化,如果没有被实例化,那么才对实例化语句进行加锁。
+
+instance使用static修饰的原因:getInstance为静态方法,因为静态方法的内部不能直接使用非静态变量,只有静态成员才能在没有创建对象时进行初始化,所以返回的这个实例必须是静态的。
+
+```
+public class Singleton {
+ private static volatile Singleton instance = null; //volatile
+ private Singleton(){}
+ public static Singleton getInstance() {
+ if (instance == null) {
+ synchronized (Singleton.class) {
+ if (instance == null) {
+ instance = new Singleton();
+ }
+ }
+ }
+ return instance;
+ }
+}
+```
+为什么两次判断`instance == null`:
+
+| Time | Thread A | Thread B |
+| ---- | -------------------- | -------------------- |
+| T1 | 检查到`instance`为空 | |
+| T2 | | 检查到`instance`为空 |
+| T3 | | 初始化对象`A` |
+| T4 | | 返回对象`A` |
+| T5 | 初始化对象`B` | |
+| T6 | 返回对象`B` | |
+
+`new Singleton()`会执行三个动作:分配内存空间、初始化对象和对象引用指向内存地址。
+
+```java
+memory = allocate(); // 1:分配对象的内存空间
+ctorInstance(memory); // 2:初始化对象
+instance = memory; // 3:设置instance指向刚分配的内存地址
+```
+
+由于指令重排优化的存在,导致初始化对象和将对象引用指向内存地址的顺序是不确定的。在某个线程创建单例对象时,会为该对象分配了内存空间并将对象的字段设置为默认值。此时就可以将分配的内存地址赋值给instance字段了,然而该对象可能还没有初始化。若紧接着另外一个线程来调用getInstance,取到的是未初始化的对象,程序就会出错。volatile 可以禁止指令重排序,保证了先初始化对象再赋值给instance变量。
+
+| Time | Thread A | Thread B |
+| :--- | :----------------------- | :--------------------------------------- |
+| T1 | 检查到`instance`为空 | |
+| T2 | 获取锁 | |
+| T3 | 再次检查到`instance`为空 | |
+| T4 | 为`instance`分配内存空间 | |
+| T5 | 将`instance`指向内存空间 | |
+| T6 | | 检查到`instance`不为空 |
+| T7 | | 访问`instance`(此时对象还未完成初始化) |
+| T8 | 初始化`instance` | |
+
+## 静态内部类
+
+它与饿汉模式一样,也是利用了类初始化机制,因此不存在多线程并发的问题。不一样的是,它是在内部类里面去创建对象实例。这样的话,只要应用中不使用内部类,JVM就不会去加载这个单例类,也就不会创建单例对象,从而实现懒汉式的延迟加载。也就是说这种方式可以同时保证延迟加载和线程安全。
+
+
+
+基于类初始化的方案的实现代码更简洁。
+
+```
+public class Instance {
+ private static class InstanceHolder {
+ public static Instance instance = new Instance();
+ }
+ private Instance() {}
+ public static Instance getInstance() {
+ return InstanceHolder.instance ; // 这里将导致InstanceHolder类被初始化
+ }
+}
+```
+但基于volatile的双重检查锁定的方案有一个额外的优势:除了可以对静态字段实现延迟初始化外,还可以对实例字段实现延迟初始化。字段延迟初始化降低了初始化类或创建实例的开销,但增加了访问被延迟初始化的字段的开销。在大多数时候,正常的初始化要优于延迟初始化。
+
diff --git a/docs/advance/design-pattern/3-factory.md b/docs/advance/design-pattern/3-factory.md
new file mode 100644
index 0000000..42cadbd
--- /dev/null
+++ b/docs/advance/design-pattern/3-factory.md
@@ -0,0 +1,91 @@
+# 工厂模式
+
+工厂模式是用来封装对象的创建。
+
+## 简单工厂模式
+
+把实例化的操作单独放到一个类中,这个类就成为简单工厂类,让简单工厂类来决定应该用哪个具体子类来实例化,这样做能把客户类和具体子类的实现解耦,客户类不再需要知道有哪些子类以及应当实例化哪个子类。
+
+```java
+ public class ShapeFactory {
+ public static final String TAG = "ShapeFactory";
+ public static Shape getShape(String type) {
+ Shape shape = null;
+ if (type.equalsIgnoreCase("circle")) {
+ shape = new CircleShape();
+ } else if (type.equalsIgnoreCase("rect")) {
+ shape = new RectShape();
+ } else if (type.equalsIgnoreCase("triangle")) {
+ shape = new TriangleShape();
+ }
+ return shape;
+ }
+ }
+```
+
+优点:只需要一个工厂创建对象,代码量少。
+
+缺点:系统扩展困难,新增产品需要修改工厂逻辑,当产品较多时,会造成工厂逻辑过于复杂,不利于系统扩展和维护。
+
+## 工厂方法模式
+
+针对不同的对象提供不同的工厂。每个对象都有一个与之对应的工厂。
+
+```java
+public interface Reader {
+ void read();
+}
+
+public class JpgReader implements Reader {
+ @Override
+ public void read() {
+ System.out.print("read jpg");
+ }
+}
+
+public class PngReader implements Reader {
+ @Override
+ public void read() {
+ System.out.print("read png");
+ }
+}
+
+public interface ReaderFactory {
+ Reader getReader();
+}
+
+public class JpgReaderFactory implements ReaderFactory {
+ @Override
+ public Reader getReader() {
+ return new JpgReader();
+ }
+}
+
+public class PngReaderFactory implements ReaderFactory {
+ @Override
+ public Reader getReader() {
+ return new PngReader();
+ }
+}
+```
+
+客户端通过子类来指定创建对应的对象。
+
+```java
+ReaderFactory factory=new JpgReaderFactory();
+Reader reader=factory.getReader();
+reader.read();
+```
+
+优点:增加新的产品类时无须修改现有系统,只需增加新产品和对应的工厂类即可。
+
+## 抽象工厂模式
+
+抽象工厂模式创建的是对象家族,也就是很多对象而不是一个对象,并且这些对象是相关的,也就是说必须一起创建出来。而工厂方法模式只是用于创建一个对象,这和抽象工厂模式有很大不同。
+
+多了一层抽象,减少了工厂的数量(HpMouseFactory和HpKeyboFactory合并为HpFactory)。
+
+
+
+
+
diff --git a/docs/advance/design-pattern/4-template.md b/docs/advance/design-pattern/4-template.md
new file mode 100644
index 0000000..c5a00dc
--- /dev/null
+++ b/docs/advance/design-pattern/4-template.md
@@ -0,0 +1,82 @@
+# 模板模式
+
+模板模式:一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。 这种类型的设计模式属于行为型模式。定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
+
+**模板模式**主要由抽象模板(Abstract Template)角色和具体模板(Concrete Template)角色组成。
+
+- 抽象模板(Abstract Template): 定义了一个或多个抽象操作,以便让子类实现。这些抽象操作叫做基本操作,它们是一个顶级逻辑的组成步骤;定义并实现了一个模板方法。这个模板方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。顶级逻辑也有可能调用一些具体方法。
+- 具体模板(Concrete Template): 实现父类所定义的一个或多个抽象方法,它们是一个顶级逻辑的组成步骤;每一个抽象模板角色都可以有任意多个具体模板角色与之对应,而每一个具体模板角色都可以给出这些抽象方法(也就是顶级逻辑的组成步骤)的不同实现,从而使得顶级逻辑的实现各不相同。
+
+示例图如下:
+
+
+
+以游戏为例。创建一个抽象类,它的模板方法被设置为 final,这样它就不会被重写。
+
+```java
+public abstract class Game {
+ abstract void initialize();
+ abstract void startPlay();
+ abstract void endPlay();
+
+ //模板
+ public final void play(){
+ //初始化游戏
+ initialize();
+
+ //开始游戏
+ startPlay();
+
+ //结束游戏
+ endPlay();
+ }
+}
+```
+
+Football类:
+
+```java
+public class Football extends Game {
+
+ @Override
+ void endPlay() {
+ System.out.println("Football Game Finished!");
+ }
+
+ @Override
+ void initialize() {
+ System.out.println("Football Game Initialized! Start playing.");
+ }
+
+ @Override
+ void startPlay() {
+ System.out.println("Football Game Started. Enjoy the game!");
+ }
+}
+```
+
+使用Game的模板方法 play() 来演示游戏的定义方式。
+
+```java
+public class TemplatePatternDemo {
+ public static void main(String[] args) {
+
+ Game game = new Cricket();
+ game.play();
+ System.out.println();
+ game = new Football();
+ game.play();
+ }
+}
+```
+
+**模板模式优点** :
+
+1. 封装不变部分,扩展可变部分。
+2. 提取公共代码,便于维护。
+3. 行为由父类控制,子类实现。
+
+**模板模式缺点**:
+
+- 每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
+
diff --git a/docs/advance/design-pattern/5-strategy.md b/docs/advance/design-pattern/5-strategy.md
new file mode 100644
index 0000000..197681b
--- /dev/null
+++ b/docs/advance/design-pattern/5-strategy.md
@@ -0,0 +1,111 @@
+# 策略模式
+
+策略模式(Strategy Pattern)属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。
+
+其主要目的是通过定义相似的算法,替换if else 语句写法,并且可以随时相互替换。
+
+**策略模式**主要由这三个角色组成,环境角色(Context)、抽象策略角色(Strategy)和具体策略角色(ConcreteStrategy)。
+
+- 环境角色(Context):持有一个策略类的引用,提供给客户端使用。
+- 抽象策略角色(Strategy):这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
+- 具体策略角色(ConcreteStrategy):包装了相关的算法或行为。
+
+示例图如下:
+
+
+
+以计算器为例,如果我们想得到两个数字相加的和,我们需要用到“+”符号,得到相减的差,需要用到“-”符号等等。虽然我们可以通过字符串比较使用if/else写成通用方法,但是计算的符号每次增加,我们就不得不加在原先的方法中进行增加相应的代码,如果后续计算方法增加、修改或删除,那么会使后续的维护变得困难。
+
+但是在这些方法中,我们发现其基本方法是固定的,这时我们就可以通过策略模式来进行开发,可以有效避免通过if/else来进行判断,即使后续增加其他的计算规则也可灵活进行调整。
+
+首先定义一个抽象策略角色,并拥有一个计算的方法。
+
+```java
+interface CalculateStrategy {
+ int doOperation(int num1, int num2);
+}
+```
+
+然后再定义加减乘除这些具体策略角色并实现方法。代码如下:
+
+```java
+class OperationAdd implements CalculateStrategy {
+ @Override
+ public int doOperation(int num1, int num2) {
+ return num1 + num2;
+ }
+}
+
+class OperationSub implements CalculateStrategy {
+ @Override
+ public int doOperation(int num1, int num2) {
+ return num1 - num2;
+ }
+}
+
+class OperationMul implements CalculateStrategy {
+ @Override
+ public int doOperation(int num1, int num2) {
+ return num1 * num2;
+ }
+}
+
+class OperationDiv implements CalculateStrategy {
+ @Override
+ public int doOperation(int num1, int num2) {
+ return num1 / num2;
+ }
+}
+```
+
+最后在定义一个环境角色,提供一个计算的接口供客户端使用。代码如下:
+
+```java
+class CalculatorContext {
+ private CalculateStrategy strategy;
+
+ public CalculatorContext(CalculateStrategy strategy) {
+ this.strategy = strategy;
+ }
+
+ public int executeStrategy(int num1, int num2) {
+ return strategy.doOperation(num1, num2);
+ }
+}
+```
+
+测试代码如下:
+
+```java
+public static void main(String[] args) {
+ int a=4,b=2;
+ CalculatorContext context = new CalculatorContext(new OperationAdd());
+ System.out.println("a + b = "+context.executeStrategy(a, b));
+
+ CalculatorContext context2 = new CalculatorContext(new OperationSub());
+ System.out.println("a - b = "+context2.executeStrategy(a, b));
+
+ CalculatorContext context3 = new CalculatorContext(new OperationMul());
+ System.out.println("a * b = "+context3.executeStrategy(a, b));
+
+ CalculatorContext context4 = new CalculatorContext(new OperationDiv());
+ System.out.println("a / b = "+context4.executeStrategy(a, b));
+}
+```
+
+**策略模式优点:**
+
+- 扩展性好,可以在不修改对象结构的情况下,为新的算法进行添加新的类进行实现;
+- 灵活性好,可以对算法进行自由切换;
+
+**策略模式缺点:**
+
+- 使用策略类变多,会增加系统的复杂度。;
+- 客户端必须知道所有的策略类才能进行调用;
+
+**使用场景:**
+
+- 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为;
+ 一个系统需要动态地在几种算法中选择一种;
+- 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现;
+
diff --git a/docs/advance/design-pattern/6-chain.md b/docs/advance/design-pattern/6-chain.md
new file mode 100644
index 0000000..512b777
--- /dev/null
+++ b/docs/advance/design-pattern/6-chain.md
@@ -0,0 +1,190 @@
+# 责任链模式
+
+为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
+
+在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,请求会自动进行传递。所以责任链将请求的发送者和请求的处理者解耦了。
+
+**责任链模式是一种对象行为型模式,其主要优点如下。**
+
+1. 降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
+2. 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
+3. 增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
+4. 责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
+5. 责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
+
+代码实现如下:
+
+请假条类
+
+```java
+public class LeaveRequest {
+ //姓名
+ private String name;
+ // 请假天数
+ private int num;
+ // 请假内容
+ private String content;
+
+ public LeaveRequest(String name, int num, String content) {
+ this.name = name;
+ this.num = num;
+ this.content = content;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getNum() {
+ return num;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+}
+```
+
+处理类
+
+```java
+public abstract class Handler {
+ protected final static int NUM_ONE = 1;
+ protected final static int NUM_THREE = 3;
+ protected final static int NUM_SEVEN = 7;
+
+ //该领导处理的请假天数区间
+ private int numStart;
+ private int numEnd;
+
+
+ //领导上还有领导
+ private Handler nextHandler;
+
+ //设置请假天数范围
+ public Handler(int numStart) {
+ this.numStart = numStart;
+ }
+
+ //设置请假天数范围
+ public Handler(int numStart, int numEnd) {
+ this.numStart = numStart;
+ this.numEnd = numEnd;
+ }
+
+ //设置上级领导
+ public void setNextHandler(Handler nextHandler) {
+ this.nextHandler = nextHandler;
+ }
+
+ //提交请假条
+ public final void submit(LeaveRequest leaveRequest) {
+ if (this.numStart == 0) {
+ return;
+ }
+ //请假天数达到领导处理要求
+ if (leaveRequest.getNum() >= this.numStart) {
+ this.handleLeave(leaveRequest);
+
+ //如果还有上级 并且请假天数超过当前领导的处理范围
+ if (this.nextHandler != null && leaveRequest.getNum() > numEnd) {
+ //继续提交
+ this.nextHandler.submit(leaveRequest);
+ } else {
+ System.out.println("流程结束!!!");
+ }
+ }
+ }
+
+ //各级领导处理请假条方法
+ protected abstract void handleLeave(LeaveRequest leave);
+
+}
+```
+
+小组长类
+
+```java
+public class GroupLeader extends Handler {
+ //1-3天的假
+ public GroupLeader() {
+ super(Handler.NUM_ONE, Handler.NUM_THREE);
+ }
+
+ @Override
+ protected void handleLeave(LeaveRequest leave) {
+ System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "!");
+ System.out.println("小组长审批通过:同意!");
+ }
+}
+```
+
+部门经理类
+
+```java
+public class Manager extends Handler {
+ //3-7天的假
+ public Manager() {
+ super(Handler.NUM_THREE, Handler.NUM_SEVEN);
+ }
+
+ @Override
+ protected void handleLeave(LeaveRequest leave) {
+ System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "!");
+ System.out.println("部门经理审批通过:同意!");
+ }
+}
+```
+
+总经理类
+
+```java
+public class GeneralManager extends Handler{
+ //7天以上的假
+ public GeneralManager() {
+ super(Handler.NUM_THREE, Handler.NUM_SEVEN);
+ }
+
+ @Override
+ protected void handleLeave(LeaveRequest leave) {
+ System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "!");
+ System.out.println("总经理审批通过:同意!");
+ }
+}
+```
+
+测试类
+
+```java
+public class Client {
+ public static void main(String[] args) {
+ //请假条
+ LeaveRequest leave = new LeaveRequest("小庄", 3, "出去旅游");
+
+ //各位领导
+ Manager manager = new Manager();
+ GroupLeader groupLeader = new GroupLeader();
+ GeneralManager generalManager = new GeneralManager();
+
+ /*
+ * 小组长上司是经理 经理上司是总经理
+ */
+ groupLeader.setNextHandler(manager);
+ manager.setNextHandler(generalManager);
+
+ //提交
+ groupLeader.submit(leave);
+
+ }
+}
+```
+
+应用场景:
+
+1. 多个对象可以处理一个请求,但具体由哪个对象处理该请求在运行时自动确定。
+2. 可动态指定一组对象处理请求,或添加新的处理者。
+3. 需要在不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。
+
+[参考链接](https://segmentfault.com/a/1190000040450513)
+
diff --git a/docs/advance/design-pattern/7-iterator.md b/docs/advance/design-pattern/7-iterator.md
new file mode 100644
index 0000000..a54bdf7
--- /dev/null
+++ b/docs/advance/design-pattern/7-iterator.md
@@ -0,0 +1,30 @@
+# 迭代器模式
+
+提供一种方法顺序访问一个聚合对象中的各个元素, 而又不暴露其内部的表示。
+
+把在元素之间游走的责任交给迭代器,而不是聚合对象。
+
+**应用实例:**JAVA 中的 iterator。
+
+**优点:** 1、它支持以不同的方式遍历一个聚合对象。 2、迭代器简化了聚合类。 3、在同一个聚合上可以有多个遍历。 4、在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。
+
+**缺点:**由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
+
+**使用场景:** 1、访问一个聚合对象的内容而无须暴露它的内部表示。 2、需要为聚合对象提供多种遍历方式。 3、为遍历不同的聚合结构提供一个统一的接口。
+
+**迭代器模式在JDK中的应用**
+
+ArrayList的遍历:
+
+```java
+Iterator iter = null;
+
+System.out.println("ArrayList:");
+iter = arrayList.iterator();
+while (iter.hasNext()) {
+ System.out.print(iter.next() + "\t");
+}
+```
+
+
+
diff --git a/docs/advance/design-pattern/8-decorator.md b/docs/advance/design-pattern/8-decorator.md
new file mode 100644
index 0000000..55b4d1c
--- /dev/null
+++ b/docs/advance/design-pattern/8-decorator.md
@@ -0,0 +1,20 @@
+# 装饰模式
+
+装饰者模式(decorator pattern):动态地将责任附加到对象上, 若要扩展功能, 装饰者提供了比继承更有弹性的替代方案。
+
+装饰模式以对客户端透明的方式拓展对象的功能,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不创造更多子类的情况下,将对象的功能加以扩展。
+
+比如设置FileInputStream,先用BufferedInputStream装饰它,再用自己写的LowerCaseInputStream过滤器去装饰它。
+
+```
+InputStream in = new LowerCaseInputStream(
+ new BufferedInputStream(
+ new FileInputStream("test.txt")));
+```
+在装饰模式中的角色有:
+
+- 抽象组件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
+- 具体组件(ConcreteComponent)角色:定义一个将要接收附加责任的类。
+- 装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
+- 具体装饰(ConcreteDecorator)角色:负责给构件对象“贴上”附加的责任。
+
diff --git a/docs/advance/design-pattern/9-adapter.md b/docs/advance/design-pattern/9-adapter.md
new file mode 100644
index 0000000..bc2624d
--- /dev/null
+++ b/docs/advance/design-pattern/9-adapter.md
@@ -0,0 +1,18 @@
+# 适配器模式
+适配器模式将现成的对象通过适配变成我们需要的接口。 适配器让原本接口不兼容的类可以合作。
+
+适配器模式有类的适配器模式和对象的适配器模式两种不同的形式。
+
+对象适配器模式通过组合对象进行适配。
+
+
+
+类适配器通过继承来完成适配。
+
+
+
+适配器模式的**优点**:
+
+1. 更好的复用性。系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。
+2. 更好的扩展性。在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。
+
diff --git a/docs/advance/design-pattern/README.md b/docs/advance/design-pattern/README.md
new file mode 100644
index 0000000..a1e777f
--- /dev/null
+++ b/docs/advance/design-pattern/README.md
@@ -0,0 +1,25 @@
+---
+title: 设计模式
+icon: design
+date: 2022-08-07
+category: 设计模式
+star: true
+---
+
+**本专栏是大彬学习设计模式基础知识的学习笔记,如有错误,可以在评论区指出**~
+
+
+## 设计模式详解
+
+- [设计模式的六大原则](./1-principle.md)
+- [单例模式](./2-singleton.md)
+- [工厂模式](./3-factory.md)
+- [模板模式](./4-template.md)
+- [策略模式](./5-strategy.md)
+- [责任链模式](./6-chain.md)
+- [迭代器模式](./7-iterator.md)
+- [装饰模式](./8-decorator.md)
+- [适配器模式](./9-adapter.md)
+- [观察者模式](./10-observer.md)
+- [代理模式](./11-proxy.md)
+- [建造者模式](./12-builder.md)
diff --git "a/\345\210\206\345\270\203\345\274\217/\345\205\250\345\261\200\345\224\257\344\270\200ID.md" b/docs/advance/distributed/1-global-unique-id.md
similarity index 97%
rename from "\345\210\206\345\270\203\345\274\217/\345\205\250\345\261\200\345\224\257\344\270\200ID.md"
rename to docs/advance/distributed/1-global-unique-id.md
index 0871efa..a28fe0d 100644
--- "a/\345\210\206\345\270\203\345\274\217/\345\205\250\345\261\200\345\224\257\344\270\200ID.md"
+++ b/docs/advance/distributed/1-global-unique-id.md
@@ -1,10 +1,12 @@
+# 全局唯一ID生成方案
+
传统的单体架构的时候,我们基本是单库然后业务单表的结构。每个业务表的ID一般我们都是从1增,通过`AUTO_INCREMENT=1`设置自增起始值,但是在分布式服务架构模式下分库分表的设计,使得多个库或多个表存储相同的业务数据。这种情况根据数据库的自增ID就会产生相同ID的情况,不能保证主键的唯一性。

如上图,如果第一个订单存储在 DB1 上则订单 ID 为1,当一个新订单又入库了存储在 DB2 上订单 ID 也为1。我们系统的架构虽然是分布式的,但是在用户层应是无感知的,重复的订单主键显而易见是不被允许的。那么针对分布式系统如何做到主键唯一性呢?
-# UUID
+## UUID
UUID (Universally Unique Identifier),通用唯一识别码的缩写。UUID是由一组32位数的16进制数字所构成,所以UUID理论上的总数为 1632=2128,约等于 3.4 x 10^38。也就是说若每纳秒产生1兆个UUID,要花100亿年才会将所有UUID用完。
@@ -52,7 +54,7 @@ public static void main(String[] args) {
- 信息不安全:基于MAC地址生成UUID的算法可能会造成MAC地址泄露,暴露使用者的位置。
- 对MySQL索引不利:如果作为数据库主键,在InnoDB引擎下,UUID的无序性可能会引起数据位置频繁变动,严重影响性能,可以查阅 Mysql 索引原理 B+树的知识。
-# 数据库生成
+## 数据库生成
是不是一定要基于外界的条件才能满足分布式唯一ID的需求呢,我们能不能在我们分布式数据库的基础上获取我们需要的ID?
@@ -71,7 +73,7 @@ public static void main(String[] args) {
但是缺点也很明显,首先它强依赖DB,当DB异常时整个系统不可用。虽然配置主从复制可以尽可能的增加可用性,但是数据一致性在特殊情况下难以保证。主从切换时的不一致可能会导致重复发号。还有就是ID发号性能瓶颈限制在单台MySQL的读写性能。
-# 使用redis实现
+## 使用redis实现
Redis实现分布式唯一ID主要是通过提供像 *INCR* 和 *INCRBY* 这样的自增原子命令,由于Redis自身的单线程的特点所以能保证生成的 ID 肯定是唯一有序的。
@@ -83,7 +85,7 @@ Redis 实现分布式全局唯一ID,它的性能比较高,生成的数据是
当然现在Redis的使用性很普遍,所以如果其他业务已经引进了Redis集群,则可以资源利用考虑使用Redis来实现。
-# 雪花算法-Snowflake
+## 雪花算法-Snowflake
Snowflake,雪花算法是由Twitter开源的分布式ID生成算法,以划分命名空间的方式将 64-bit位分割成多个部分,每个部分代表不同的含义。而 Java中64bit的整数是Long类型,所以在 Java 中 SnowFlake 算法生成的 ID 就是 long 来存储的。
@@ -290,7 +292,7 @@ public static void main(String[] args) {
很多其他类雪花算法也是在此思想上的设计然后改进规避它的缺陷,后面介绍的百度 UidGenerator 和 美团分布式ID生成系统 Leaf 中snowflake模式都是在 snowflake 的基础上演进出来的。
-# 百度-UidGenerator
+## 百度-UidGenerator
[百度的 UidGenerator](https://github.com/baidu/uid-generator) 是百度开源基于Java语言实现的唯一ID生成器,是在雪花算法 snowflake 的基础上做了一些改进。UidGenerator以组件形式工作在应用项目中, 支持自定义workerId位数和初始化策略,适用于docker等虚拟化环境下实例自动重启、漂移等场景。
@@ -323,7 +325,7 @@ PRIMARY KEY(ID)
COMMENT='DB WorkerID Assigner for UID Generator',ENGINE = INNODB;
```
-## DefaultUidGenerator 实现
+### DefaultUidGenerator 实现
DefaultUidGenerator 就是正常的根据时间戳和机器位还有序列号的生成方式,和雪花算法很相似,对于时钟回拨也只是抛异常处理。仅有一些不同,如以秒为为单位而不再是毫秒和支持Docker等虚拟化环境。
@@ -371,7 +373,7 @@ protected synchronized long nextId() {
```
-## CachedUidGenerator 实现
+### CachedUidGenerator 实现
而官方建议的性能较高的 CachedUidGenerator 生成方式,是使用 RingBuffer 缓存生成的id。数组每个元素成为一个slot。RingBuffer容量,默认为Snowflake算法中sequence最大值(2^13 = 8192)。可通过 boostPower 配置进行扩容,以提高 RingBuffer 读写吞吐量。
@@ -382,13 +384,13 @@ Tail指针、Cursor指针用于环形数组上读写slot:
- Cursor指针
表示Consumer消费到的最小序号(序号序列与Producer序列相同)。Cursor不能超过Tail,即不能消费未生产的slot。当Cursor已赶上tail,此时可通过rejectedTakeBufferHandler指定TakeRejectPolicy
-[](https://img2018.cnblogs.com/blog/1162587/201907/1162587-20190707142503899-1530677221.png)
+
CachedUidGenerator采用了双RingBuffer,Uid-RingBuffer用于存储Uid、Flag-RingBuffer用于存储Uid状态(是否可填充、是否可消费)。
由于数组元素在内存中是连续分配的,可最大程度利用CPU cache以提升性能。但同时会带来「伪共享」FalseSharing问题,为此在Tail、Cursor指针、Flag-RingBuffer中采用了CacheLine 补齐方式。
-[](https://img2018.cnblogs.com/blog/1162587/201907/1162587-20190707142450492-1894906450.png)
+
RingBuffer填充时机
@@ -399,13 +401,13 @@ RingBuffer填充时机
- 周期填充
通过Schedule线程,定时补全空闲slots。可通过scheduleInterval配置,以应用定时填充功能,并指定Schedule时间间隔。
-# 美团Leaf
+## 美团Leaf
Leaf是美团基础研发平台推出的一个分布式ID生成服务,名字取自德国哲学家、数学家莱布尼茨的著名的一句话:“There are no two identical leaves in the world”,世间不可能存在两片相同的叶子。
Leaf 也提供了两种ID生成的方式,分别是 Leaf-segment 数据库方案和 Leaf-snowflake 方案。
-## Leaf-segment 数据库方案
+### Leaf-segment 数据库方案
Leaf-segment 数据库方案,是在上文描述的在使用数据库的方案上,做了如下改变:
@@ -444,7 +446,7 @@ CREATE TABLE `leaf_alloc` (
对于这种方案依然存在一些问题,它仍然依赖 DB的稳定性,需要采用主从备份的方式提高 DB的可用性,还有 Leaf-segment方案生成的ID是趋势递增的,这样ID号是可被计算的,例如订单ID生成场景,通过订单id号相减就能大致计算出公司一天的订单量,这个是不能忍受的。
-## Leaf-snowflake方案
+### Leaf-snowflake方案
Leaf-snowflake方案完全沿用 snowflake 方案的bit位设计,对于workerID的分配引入了Zookeeper持久顺序节点的特性自动对snowflake节点配置 wokerID。避免了服务规模较大时,动手配置成本太高的问题。
@@ -466,7 +468,7 @@ Leaf-snowflake是按照下面几个步骤启动的:
在性能上官方提供的数据目前 Leaf 的性能在4C8G 的机器上QPS能压测到近5w/s,TP999 1ms。
-# 总结
+## 总结
以上基本列出了所有常用的分布式ID生成方式,其实大致分类的话可以分为两类:
@@ -482,4 +484,4 @@ Leaf-snowflake是按照下面几个步骤启动的:
-[参考链接](https://www.cnblogs.com/jajian/p/11101213.html)
\ No newline at end of file
+[参考链接](https://www.cnblogs.com/jajian/p/11101213.html)
diff --git "a/\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\351\224\201.md" b/docs/advance/distributed/2-distributed-lock.md
similarity index 99%
rename from "\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\351\224\201.md"
rename to docs/advance/distributed/2-distributed-lock.md
index 8a65dd1..07c5445 100644
--- "a/\345\210\206\345\270\203\345\274\217/\345\210\206\345\270\203\345\274\217\351\224\201.md"
+++ b/docs/advance/distributed/2-distributed-lock.md
@@ -1,3 +1,5 @@
+# 分布式锁
+
## 为什么要使用分布式锁
在单机环境下,当存在多个线程可以同时改变某个变量(可变共享变量)时,就会出现线程安全问题。这个问题可以通过 JAVA 提供的 volatile、ReentrantLock、synchronized 以及 concurrent 并发包下一些线程安全的类等来避免。
@@ -252,4 +254,4 @@ ZooKeeper是一个为分布式应用提供一致性服务的开源组件,它
## 参考链接
-https://mp.weixin.qq.com/s/xQknd6xsVDPBr4TbETTk2A
\ No newline at end of file
+https://mp.weixin.qq.com/s/xQknd6xsVDPBr4TbETTk2A
diff --git "a/\345\210\206\345\270\203\345\274\217/\350\277\234\347\250\213\350\260\203\347\224\250.md" b/docs/advance/distributed/3-rpc.md
similarity index 99%
rename from "\345\210\206\345\270\203\345\274\217/\350\277\234\347\250\213\350\260\203\347\224\250.md"
rename to docs/advance/distributed/3-rpc.md
index beade88..30ec852 100644
--- "a/\345\210\206\345\270\203\345\274\217/\350\277\234\347\250\213\350\260\203\347\224\250.md"
+++ b/docs/advance/distributed/3-rpc.md
@@ -1,3 +1,5 @@
+# RPC
+
## RPC简介
RPC,英文全名remote procedure call,即远程过程调用。就是说一个应用部署在A服务器上,想要调用B服务器上应用提供的方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。
@@ -116,4 +118,4 @@ Protobuf有个缺点就是要传输每一个类的结构都要生成对应的pro
1. 对性能要求不高的场景,可以采用基于XML的SOAP协议
2. 性能和间接性有比较高要求的场景,那么Hessian、Protobuf、Thrift、Avro都可以。
3. 基于前后端分离,或者独立的对外API服务,选用JSON是比较好的,对于调试、可读性都很不错。
-4. Avro设计理念偏于动态类型语言,那么这类的场景使用Avro是可以的。
\ No newline at end of file
+4. Avro设计理念偏于动态类型语言,那么这类的场景使用Avro是可以的。
diff --git a/docs/advance/distributed/4-micro-service.md b/docs/advance/distributed/4-micro-service.md
new file mode 100644
index 0000000..0803aaa
--- /dev/null
+++ b/docs/advance/distributed/4-micro-service.md
@@ -0,0 +1,183 @@
+# 微服务
+
+## 什么是微服务?
+
+微服务是将一个原本独立的系统拆分成多个小型服务,这些小型服务都在各自独立的进程中运行,服务和服务之间采用轻量级的通信机制进行协作。每个服务可以被独立的部署到生产环境。
+
+[从单体应用到微服务](https://www.zhihu.com/question/65502802/answer/802678798)
+
+单体系统的缺点:
+
+1. 修改一个小功能,就需要将整个系统重新部署上线,影响其他功能的运行;
+2. 功能模块互相依赖,强耦合,扩展困难。如果出现性能瓶颈,需要对整体应用进行升级,虽然影响性能的可能只是其中一个小模块;
+
+单体系统的优点:
+
+1. 容易部署,程序单一,不存在分布式集群的复杂部署环境;
+2. 容易测试,没有复杂的服务调用关系。
+
+微服务的**优点**:
+
+1. 不同的服务可以使用不同的技术;
+2. 隔离性。一个服务不可用不会导致其他服务不可用;
+3. 可扩展性。某个服务出现性能瓶颈,只需对此服务进行升级即可;
+4. 简化部署。服务的部署是独立的,哪个服务出现问题,只需对此服务进行修改重新部署;
+
+微服务的**缺点**:
+
+1. 网络调用频繁。性能相对函数调用较差。
+2. 运维成本增加。系统由多个独立运行的微服务构成,需要设计一个良好的监控系统对各个微服务的运行状态进行监控。
+
+
+
+## 分布式和微服务的区别
+
+从概念理解,分布式服务架构强调的是服务化以及服务的**分散化**,微服务则更强调服务的**专业化和精细分工**;
+
+从实践的角度来看,**微服务架构通常是分布式服务架构**,反之则未必成立。
+
+一句话概括:分布式:分散部署;微服务:分散能力。
+
+
+
+## 服务怎么划分?
+
+横向拆分:按照不同的业务域进行拆分,例如订单、营销、风控、积分资源等。形成独立的业务领域微服务集群。
+
+纵向拆分:把一个业务功能里的不同模块或者组件进行拆分。例如把公共组件拆分成独立的原子服务,下沉到底层,形成相对独立的原子服务层。这样一纵一横,就可以实现业务的服务化拆分。
+
+要做好微服务的分层:梳理和抽取核心应用、公共应用,作为独立的服务下沉到核心和公共能力层,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求
+
+总之,微服务的设计一定要 **渐进式** 的,总的原则是 **服务内部高内聚,服务之间低耦合。**
+
+ ## 微服务设计原则
+**单一职责原则**
+
+意思是每个微服务只需要实现自己的业务逻辑就可以了,比如订单管理模块,它只需要处理订单的业务逻辑就可以了,其它的不必考虑。
+
+**服务自治原则**
+
+意思是每个微服务从开发、测试、运维等都是独立的,包括存储的数据库也都是独立的,自己就有一套完整的流程,我们完全可以把它当成一个项目来对待。不必依赖于其它模块。
+
+**轻量级通信原则**
+
+首先是通信的语言非常的轻量,第二,该通信方式需要是跨语言、跨平台的,之所以要跨平台、跨语言就是为了让每个微服务都有足够的独立性,可以不受技术的钳制。
+
+**接口明确原则**
+
+由于微服务之间可能存在着调用关系,为了尽量避免以后由于某个微服务的接口变化而导致其它微服务都做调整,在设计之初就要考虑到所有情况,让接口尽量做的更通用,更灵活,从而尽量避免其它模块也做调整。
+
+
+
+## 微服务之间是如何通讯的?
+
+**1、RPC**
+
+优点:简单,常见。因为没有中间件代理,系统更简单
+
+缺点:
+
+1. 只支持请求/响应的模式,不支持别的,比如通知、发布/订阅
+2. 降低了可用性,因为客户端和服务端在请求过程中必须都是可用的
+
+**2、消息队列**
+
+除了标准的基于RPC通信的微服务架构,还有基于消息队列通信的微服务架构,这种架构下的微服务采用发送消息(Publish Message)与监听消息(Subscribe Message)的方式来实现彼此之间的交互。
+
+网易的蜂巢平台就采用了基于消息队列的微服务架构设计思路,如下图所示,微服务之间通过RabbitMQ传递消息,实现通信。
+
+与上面几种微服务架构相比,基于消息队列的微服务架构并不多,案例也相对较少,更多地体现为一种与业务相关的设计经验,各家有各家的实现方式,缺乏公认的设计思路与参考架构,也没有形成一个知名的开源平台。因此,如果需要实施这种微服务架构,则基本上需要项目组自己从零开始去设计实现一个微服务架构基础平台,其代价是成本高、风险大。
+
+**优点**:
+
+- 把客户端和服务端解耦,更松耦合提高可用性,因为消息中间件缓存了消息,直到消费者可以消费
+- 支持很多通信机制比如通知、发布/订阅等
+
+**缺点**:
+
+- 缺乏公认的设计思路与参考架构,也没有形成一个知名的开源平台
+- 成本高、风险大
+
+## 熔断器
+
+雪崩效应:假如存在这样的调用链路,a服务->b服务->c服务,当c服务挂了的时候,b服务调用c服务会等待超时,a服务调用b服务也会等待超时,调用方长时间等不到相应而占用线程,如果有大量的请求过来,就会造成线程池打满,导致整个链路的服务奔溃。
+
+为了解决分布式系统的雪崩效应,分布式系统引进了**熔断器机制** 。
+
+当一个服务的处理用户请求的失败次数在一定时间内小于设定的阀值时,熔断器出于关闭状态,服务正常。
+
+当服务处理用户请求失败次数在一定时间内大于设定的阀值时,说明服务出现故障,打开熔断器,这时所有的请求会快速返回失败的错误信息,不执行业务逻辑,从而防止故障的蔓延。
+
+当处于打开状态的熔断器时,一段时间后出于半打开状态,并执行一定数量的请求,剩余的请求会执行快速失败,若执行请求失败了,则继续打开熔断器,若成功了,则将熔断器关闭。
+
+## 服务网关
+
+### 何为网关?
+
+通俗一点的讲:网关就是要去别的网络的时候,把报文首先发送到的那台设备。稍微专业一点的术语,网关就是当前主机的默认路由。
+
+网关一般就是一台路由器,有点像“一个小区中的一个邮局”,小区里面的住户互相是知道怎么走,但是要向外地投递东西就不知道了,怎么办?把地址写好送到本小区的邮局就好了。
+
+那么,怎么区分是“本小区”和“外地小区”的呢?根据IP地址 + 掩码。如果是在一个范围内的,就是本小区(局域网内部),如果掩不住的,就是外地的。
+
+例如,你的机器的IP地址是:192.168.0.2/24,网关是192.168.0.1
+
+如果机器访问的IP地址范围是:192.168.0.1~192.168.0.254的,说明是一个小区的邻居,你的机器就直接发送了(和网关没任何关系)。如果你访问的IP地址不是这个范围的,则就投递到192.168.0.1上,让这台设备来转发。
+
+参考:https://www.zhihu.com/question/362842680/answer/951412213
+
+### 何为API网关
+
+假设你正在开发一个电商网站,那么这里会涉及到很多后端的微服务,比如会员、商品、推荐服务等等。
+
+那么这里就会遇到一个问题,APP/Browser怎么去访问这些后端的服务? 如果业务比较简单的话,可以给每个业务都分配一个独立的域名(`https://service.api.company.com`),但这种方式会有几个问题:
+
+- 每个业务都会需要鉴权、限流、权限校验等逻辑,如果每个业务都各自为战,自己造轮子实现一遍,会很蛋疼,完全可以抽出来,放到一个统一的地方去做。
+- 如果业务量比较简单的话,这种方式前期不会有什么问题,但随着业务越来越复杂,比如淘宝、亚马逊打开一个页面可能会涉及到数百个微服务协同工作,如果每一个微服务都分配一个域名的话,一方面客户端代码会很难维护,涉及到数百个域名
+- 每上线一个新的服务,都需要运维参与,申请域名、配置Nginx等,当上线、下线服务器时,同样也需要运维参与,另外采用域名这种方式,对于环境的隔离也不太友好,调用者需要自己根据域名自己进行判断。
+- 另外还有一个问题,后端每个微服务可能采用了不同的协议,比如HTTP、AMQP、自定义TCP协议等,但是你不可能要求客户端去适配这么多种协议,这是一项非常有挑战的工作,项目会变的非常复杂且很难维护。
+
+更好的方式是采用API网关(也叫做服务网关),实现一个API网关**接管所有的入口流量**,类似Nginx的作用,将所有用户的请求转发给后端的服务器,但网关做的不仅仅只是简单的转发,也会针对流量做一些扩展,比如鉴权、限流、权限、熔断、协议转换、错误码统一、缓存、日志、监控、告警等,这样将通用的逻辑抽出来,由网关统一去做,业务方也能够更专注于业务逻辑,提升迭代的效率。
+
+
+
+通过引入API网关,客户端只需要与API网关交互,而不用与各个业务方的接口分别通讯,但多引入一个组件就多引入了一个潜在的故障点,因此要实现一个高性能、稳定的网关,也会涉及到很多点。
+
+网关层通常以集群的形式存在。并在服务网关层前通常会加上Nginx 用来负载均衡。
+
+**服务网关基本功能**:
+
+
+
+- 智能路由:接收**外部**一切请求,并转发到后端的对外服务。注意:我们只转发外部请求,服务之间的请求不走网关,这就表示全链路追踪、内部服务API监控、内部服务之间调用的容错、智能路由不能在网关完成;当然,也可以将所有的服务调用都走网关,那么几乎所有的功能都可以集成到网关中,但是这样的话,网关的压力会很大,不堪重负。
+- 权限校验:网关可以做一些用户身份认证,权限认证,防止非法请求操作API 接口,对内部服务起到保护作用
+- API监控:监控经过网关的请求,以及网关本身的一些性能指标(gc等)
+- 限流:与监控配合,进行限流操作
+- API日志统一收集:类似于一个aspect切面,记录接口的进入和出去时的相关日志
+
+当然,网关实现这些功能,需要做高可用,否则网关很可能成为架构的瓶颈,最常用的网关组件Zuul、Nginx
+
+## 服务配置统一管理
+
+在微服务架构中,需要有统一管理配置文件的组件,例如:SpringCloud Config组件、阿里的Diamond、百度的Disconf、携程的Apollo等
+
+## 服务链路追踪
+
+在微服务架构中,必须实现分布式链路追踪,去跟进一个请求到底有哪些服务参与、参与顺序,是每个请求链路清晰可见,便于问题快速定位。
+
+常用链路追踪组件有Google的Dapper、Twitter 的Zipkin,以及阿里Eagleeye。
+
+
+
+## 微服务框架
+
+市面常用微服务框架有:Spring Cloud 、Dubbo 、kubernetes
+
+- 从功能模块上考虑,Dubbo缺少很多功能模块,例如网关、链路追踪等
+- 从学习成本上考虑,Dubbo 版本趋于稳定,稳定完善、可以即学即用,难度简单,Spring cloud 基于Spring Boot,需要先掌握Spring Boot ,例外Spring cloud 大多为英文文档,要求学习者有一定的英文阅读能力
+- 从开发风格考虑,Dubbo倾向于xml的配置方式,Spring cloud 基于Spring Boot ,采用基于注解和JavaBean配置方式的敏捷开发
+- 从开发速度上考虑,Spring cloud 具有更高的开发和部署速度
+- 从通信方式上考虑,Spring cloud 基于HTTP Restful 风格,服务于服务之间完全无关、无耦合。Dubbo 基于远程调用,对接口、平台和语言有强依赖性,如果需要实现跨平台,需要有额外的中间件。
+
+所以Dubbo专注于服务治理;Spring Cloud关注于微服务架构生态。
+
diff --git a/docs/advance/distributed/5-distibuted-arch.md b/docs/advance/distributed/5-distibuted-arch.md
new file mode 100644
index 0000000..30b0b32
--- /dev/null
+++ b/docs/advance/distributed/5-distibuted-arch.md
@@ -0,0 +1,3 @@
+# 分布式架构,微服务、限流、熔断....
+
+[分布式架构,微服务、限流、熔断....](https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247490543&idx=1&sn=ee34bee96511d5e548381e0576f8b484&chksm=ce98e6a9f9ef6fbf7db9c2b6d2fed26853a3bc13a50c3228ab57bea55afe0772008cdb1f957b&token=1594696656&lang=zh_CN#rd)
\ No newline at end of file
diff --git a/docs/advance/distributed/6-distributed-transaction.md b/docs/advance/distributed/6-distributed-transaction.md
new file mode 100644
index 0000000..7b3e256
--- /dev/null
+++ b/docs/advance/distributed/6-distributed-transaction.md
@@ -0,0 +1,197 @@
+# 分布式事务
+
+## 简介
+
+### 事务
+
+事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消。也就是事务具有原子性,一个事务中的一系列的操作要么全部成功,要么一个都不做。事务应该具有 4 个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为 ACID 特性。
+
+### 分布式事务
+
+分布式事务是指事务的参与者,支持事务的服务器,资源服务器以及事务管理器分别位于分布式系统的不同节点之上。通常一个分布式事务中会涉及对多个数据源或业务系统的操作。分布式事务也可以被定义为一种嵌套型的事务,同时也就具有了ACID事务的特性。
+
+### 强一致性、弱一致性、最终一致性
+
+**强一致性**
+
+任何一次读都能读到某个数据的最近一次写的数据。系统中的所有进程,看到的操作顺序,都和全局时钟下的顺序一致。简言之,在任意时刻,所有节点中的数据是一样的。
+
+**弱一致性**
+
+数据更新后,如果能容忍后续的访问只能访问到部分或者全部访问不到,则是弱一致性。
+
+**最终一致性**
+
+不保证在任意时刻任意节点上的同一份数据都是相同的,但是随着时间的迁移,不同节点上的同一份数据总是在向趋同的方向变化。简单说,就是在一段时间后,节点间的数据会最终达到一致状态。
+
+由于分布式事务方案,无法做到完全的ACID的保证,没有一种完美的方案,能够解决掉所有业务问题。因此在实际应用中,会根据业务的不同特性,选择最适合的分布式事务方案。
+
+## 分布式事务的基础
+
+### CAP理论
+
+**Consistency**(一致性):数据一致更新,所有数据变动都是同步的(强一致性)。
+
+**Availability**(可用性):好的响应性能。
+
+**Partition tolerance**(分区容错性) :可靠性。
+
+定理:任何分布式系统**只可同时满足二点**,没法三者兼顾。
+
+CA系统(放弃P):指将所有数据(或者仅仅是那些与事务相关的数据)都放在一个分布式节点上,就不会存在网络分区。所以强一致性以及可用性得到满足。
+
+CP系统(放弃A):如果要求数据在各个服务器上是强一致的,然而网络分区会导致同步时间无限延长,那么如此一来可用性就得不到保障了。坚持事务ACID(原子性、一致性、隔离性和持久性)的传统数据库以及对结果一致性非常敏感的应用通常会做出这样的选择。
+
+AP系统(放弃C):这里所说的放弃一致性,并不是完全放弃数据一致性,而是放弃数据的强一致性,而保留数据的最终一致性。如果即要求系统高可用又要求分区容错,那么就要放弃一致性了。因为一旦发生网络分区,节点之间将无法通信,为了满足高可用,每个节点只能用本地数据提供服务,这样就会导致数据不一致。一些遵守BASE原则数据库,(如:Cassandra、CouchDB等)往往会放宽对一致性的要求(满足最终一致性即可),一次来获取基本的可用性。
+
+### BASE理论
+
+BASE 是 Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent (最终一致性)三个短语的缩写。是对CAP中AP的一个扩展。
+
+1. 基本可用:分布式系统在出现故障时,允许损失部分可用功能,保证核心功能可用。
+2. 软状态:允许系统中存在中间状态,这个状态不影响系统可用性,这里指的是CAP中的不一致。
+3. 最终一致:最终一致是指经过一段时间后,所有节点数据都将会达到一致。
+
+BASE解决了CAP中理论没有网络延迟,在BASE中用软状态和最终一致,保证了延迟后的一致性。BASE和 ACID 是相反的,它完全不同于ACID的强一致性模型,而是通过牺牲强一致性来获得可用性,并允许数据在一段时间内是不一致的,但最终达到一致状态。
+
+## 分布式事务解决方案
+
+分布式事务的实现主要有以下 6 种方案:
+
+- 2PC 方案
+- TCC 方案
+- 本地消息表
+- MQ事务
+- Saga事务
+- 最大努力通知方案
+
+### 2PC方案
+
+2PC方案分为两阶段:
+
+第一阶段:事务管理器要求每个涉及到事务的数据库预提交(precommit)此操作,并反映是否可以提交.
+
+第二阶段:事务协调器要求每个数据库提交数据,或者回滚数据。
+
+优点: 尽量保证了数据的强一致,实现成本较低,在各大主流数据库都有自己实现,对于MySQL是从5.5开始支持。
+
+缺点:
+
+- 单点问题:事务管理器在整个流程中扮演的角色很关键,如果其宕机,比如在第一阶段已经完成,在第二阶段正准备提交的时候事务管理器宕机,资源管理器就会一直阻塞,导致数据库无法使用。
+- 同步阻塞:在准备就绪之后,资源管理器中的资源一直处于阻塞,直到提交完成,释放资源。
+- 数据不一致:两阶段提交协议虽然为分布式数据强一致性所设计,但仍然存在数据不一致性的可能,比如在第二阶段中,假设协调者发出了事务commit的通知,但是因为网络问题该通知仅被一部分参与者所收到并执行了commit操作,其余的参与者则因为没有收到通知一直处于阻塞状态,这时候就产生了数据的不一致性。
+
+总的来说,2PC方案比较简单,成本较低,但是其单点问题,以及不能支持高并发(由于同步阻塞)依然是其最大的弱点。
+
+### TCC
+
+TCC 的全称是:`Try`、`Confirm`、`Cancel`。
+
+- **Try 阶段**:这个阶段说的是对各个服务的资源做检测以及对资源进行 **锁定或者预留**。
+- **Confirm 阶段**:这个阶段说的是在各个服务中执行实际的操作。
+- **Cancel 阶段**:如果任何一个服务的业务方法执行出错,那么这里就需要 **进行补偿**,就是执行已经执行成功的业务逻辑的回滚操作。(把那些执行成功的回滚)
+-
+
+举个简单的例子如果你用100元买了一瓶水, Try阶段:你需要向你的钱包检查是否够100元并锁住这100元,水也是一样的。
+
+如果有一个失败,则进行cancel(释放这100元和这一瓶水),如果cancel失败不论什么失败都进行重试cancel,所以需要保持幂等。
+
+如果都成功,则进行confirm,确认这100元扣,和这一瓶水被卖,如果confirm失败无论什么失败则重试(会依靠活动日志进行重试)。
+
+这种方案说实话几乎很少人使用,但是也有使用的场景。因为这个**事务回滚实际上是严重依赖于你自己写代码来回滚和补偿**了,会造成补偿代码巨大。
+
+### 本地消息表
+
+本地消息表的核心是将需要分布式处理的任务通过消息日志的方式来异步执行。消息日志可以存储到本地文本、数据库或消息队列,再通过业务规则自动或人工发起重试。人工重试更多的是应用于支付场景,通过对账系统对事后问题的处理。
+
+
+
+对于本地消息队列来说核心是把大事务转变为小事务。还是举上面用100元去买一瓶水的例子。
+
+1.当你扣钱的时候,你需要在你扣钱的服务器上新增加一个本地消息表,你需要把你扣钱和写入减去水的库存到本地消息表放入同一个事务(依靠数据库本地事务保证一致性。
+
+2.这个时候有个定时任务去轮询这个本地事务表,把没有发送的消息,扔给商品库存服务器,叫他减去水的库存,到达商品服务器之后这个时候得先写入这个服务器的事务表,然后进行扣减,扣减成功后,更新事务表中的状态。
+
+3.商品服务器通过定时任务扫描消息表或者直接通知扣钱服务器,扣钱服务器本地消息表进行状态更新。
+
+4.针对一些异常情况,定时扫描未成功处理的消息,进行重新发送,在商品服务器接到消息之后,首先判断是否是重复的,如果已经接收,在判断是否执行,如果执行在马上又进行通知事务,如果未执行,需要重新执行需要由业务保证幂等,也就是不会多扣一瓶水。
+
+本地消息队列是BASE理论,是最终一致模型,适用于对一致性要求不高的。实现这个模型时需要注意重试的幂等。
+
+### MQ事务
+
+基于 MQ 的分布式事务方案其实是对本地消息表的封装,将本地消息表基于 MQ 内部,其他方面的协议基本与本地消息表一致。
+
+MQ事务方案整体流程和本地消息表的流程很相似,如下图:
+
+
+
+从上图可以看出和本地消息表方案唯一不同就是将本地消息表存在了MQ内部,而不是业务数据库中。
+
+那么MQ内部的处理尤为重要,下面主要基于 RocketMQ 4.3 之后的版本介绍 MQ 的分布式事务方案。
+
+在本地消息表方案中,保证事务主动方发写业务表数据和写消息表数据的一致性是基于数据库事务,RocketMQ 的事务消息相对于普通 MQ提供了 2PC 的提交接口,方案如下:
+
+**正常情况:事务主动方发消息**
+
+
+
+这种情况下,事务主动方服务正常,没有发生故障,发消息流程如下:
+
+- 发送方向 MQ 服务端(MQ Server)发送 half 消息。
+- MQ Server 将消息持久化成功之后,向发送方 ack 确认消息已经发送成功。
+- 发送方开始执行本地事务逻辑。
+- 发送方根据本地事务执行结果向 MQ Server 提交二次确认(commit 或是 rollback)。
+- MQ Server 收到 commit 状态则将半消息标记为可投递,订阅方最终将收到该消息;MQ Server 收到 rollback 状态则删除半消息,订阅方将不会接受该消息。
+
+**异常情况:事务主动方消息恢复**
+
+
+
+在断网或者应用重启等异常情况下,图中 4 提交的二次确认超时未到达 MQ Server,此时处理逻辑如下:
+
+- MQ Server 对该消息发起消息回查。
+- 发送方收到消息回查后,需要检查对应消息的本地事务执行的最终结果。
+- 发送方根据检查得到的本地事务的最终状态再次提交二次确认。
+- MQ Server基于 commit/rollback 对消息进行投递或者删除。
+
+**优点**
+
+相比本地消息表方案,MQ 事务方案优点是:
+
+- 消息数据独立存储 ,降低业务系统与消息系统之间的耦合。
+- 吞吐量大于使用本地消息表方案。
+
+**缺点**
+
+- 一次消息发送需要两次网络请求(half 消息 + commit/rollback 消息) 。
+- 业务处理服务需要实现消息状态回查接口。
+
+### Saga事务
+
+Saga是由一系列的本地事务构成。每一个本地事务在更新完数据库之后,会发布一条消息或者一个事件来触发Saga中的下一个本地事务的执行。如果一个本地事务因为某些业务规则无法满足而失败,Saga会执行在这个失败的事务之前成功提交的所有事务的补偿操作。
+
+Saga的实现有很多种方式,其中最流行的两种方式是:
+
+- **基于事件的方式**。这种方式没有协调中心,整个模式的工作方式就像舞蹈一样,各个舞蹈演员按照预先编排的动作和走位各自表演,最终形成一只舞蹈。处于当前Saga下的各个服务,会产生某类事件,或者监听其它服务产生的事件并决定是否需要针对监听到的事件做出响应。
+- **基于命令的方式**。这种方式的工作形式就像一只乐队,由一个指挥家(协调中心)来协调大家的工作。协调中心来告诉Saga的参与方应该执行哪一个本地事务。
+
+### 最大努力通知方案
+
+最大努力通知也称为定期校对,是对MQ事务方案的进一步优化。它在事务主动方增加了消息校对的接口,如果事务被动方没有接收到消息,此时可以调用事务主动方提供的消息校对的接口主动获取。
+
+最大努力通知的整体流程如下图:
+
+
+
+在可靠消息事务中,事务主动方需要将消息发送出去,并且消息接收方成功接收,这种可靠性发送是由事务主动方保证的;
+
+但是最大努力通知,事务主动方尽最大努力(重试,轮询....)将事务发送给事务接收方,但是仍然存在消息接收不到,此时需要事务被动方主动调用事务主动方的消息校对接口查询业务消息并消费,这种通知的可靠性是由事务被动方保证的。
+
+最大努力通知适用于业务通知类型,例如微信交易的结果,就是通过最大努力通知方式通知各个商户,既有回调通知,也有交易查询接口。
+
+## 参考文章
+
+https://www.pdai.tech/md/arch/arch-z-transection.html
+
+https://juejin.cn/post/6844903647197806605#heading-15
diff --git a/docs/advance/distributed/README.md b/docs/advance/distributed/README.md
new file mode 100644
index 0000000..b2e29b3
--- /dev/null
+++ b/docs/advance/distributed/README.md
@@ -0,0 +1,8 @@
+## 目录
+
+- [全局唯一ID生成方案](./1-global-unique-id.md)
+- [分布式锁](./2-distributed-lock.md)
+- [RPC](./3-rpc.md)
+- [微服务](./4-micro-service.md)
+- [分布式架构,微服务、限流、熔断....](./6-distibuted-arch.md)
+- [分布式事务](./7-distributed-transaction.md)
diff --git a/docs/advance/excellent-article/1-redis-stock-minus.md b/docs/advance/excellent-article/1-redis-stock-minus.md
new file mode 100644
index 0000000..7fa632c
--- /dev/null
+++ b/docs/advance/excellent-article/1-redis-stock-minus.md
@@ -0,0 +1,317 @@
+# Redis 如何实现库存扣减操作和防止被超卖?
+
+电商当项目经验已经非常普遍了,不管你是包装的还是真实的,起码要能讲清楚电商中常见的问题,比如库存的操作怎么防止商品被超卖
+
+解决方案:
+- 基于数据库单库存
+- 基于数据库多库存
+- 基于redis
+
+基于redis实现扣减库存的具体实现
+- 初始化库存回调函数(IStockCallback)
+- 扣减库存服务(StockService)
+- 调用
+
+
+
+------
+
+在日常开发中有很多地方都有类似扣减库存的操作,比如电商系统中的商品库存,抽奖系统中的奖品库存等。
+
+## **解决方案**
+
+1. 使用mysql数据库,使用一个字段来存储库存,每次扣减库存去更新这个字段。
+2. 还是使用数据库,但是将库存分层多份存到多条记录里面,扣减库存的时候路由一下,这样子增大了并发量,但是还是避免不了大量的去访问数据库来更新库存。
+3. 将库存放到redis使用redis的incrby特性来扣减库存。
+
+## **分析**
+
+在上面的第一种和第二种方式都是基于数据来扣减库存。
+
+### 基于数据库单库存
+
+第一种方式在所有请求都会在这里等待锁,获取锁有去扣减库存。在并发量不高的情况下可以使用,但是一旦并发量大了就会有大量请求阻塞在这里,导致请求超时,进而整个系统雪崩;而且会频繁的去访问数据库,大量占用数据库资源,所以在并发高的情况下这种方式不适用。
+
+### 基于数据库多库存
+
+第二种方式其实是第一种方式的优化版本,在一定程度上提高了并发量,但是在还是会大量的对数据库做更新操作大量占用数据库资源。
+
+**基于数据库来实现扣减库存还存在的一些问题:**
+
+- 用数据库扣减库存的方式,扣减库存的操作必须在一条语句中执行,不能先selec在update,这样在并发下会出现超扣的情况。如:
+
+```
+update number set x=x-1 where x > 0
+```
+
+- MySQL自身对于高并发的处理性能就会出现问题,一般来说,MySQL的处理性能会随着并发thread上升而上升,但是到了一定的并发度之后会出现明显的拐点,之后一路下降,最终甚至会比单thread的性能还要差。
+- 当减库存和高并发碰到一起的时候,由于操作的库存数目在同一行,就会出现争抢InnoDB行锁的问题,导致出现互相等待甚至死锁,从而大大降低MySQL的处理性能,最终导致前端页面出现超时异常。
+
+### 基于redis
+
+针对上述问题的问题我们就有了第三种方案,将库存放到缓存,利用redis的incrby特性来扣减库存,解决了超扣和性能问题。但是一旦缓存丢失需要考虑恢复方案。比如抽奖系统扣奖品库存的时候,初始库存=总的库存数-已经发放的奖励数,但是如果是异步发奖,需要等到MQ消息消费完了才能重启redis初始化库存,否则也存在库存不一致的问题。
+
+## **基于redis实现扣减库存的具体实现**
+
+- 我们使用redis的lua脚本来实现扣减库存
+- 由于是分布式环境下所以还需要一个分布式锁来控制只能有一个服务去初始化库存
+- 需要提供一个回调函数,在初始化库存的时候去调用这个函数获取初始化库存
+
+### 初始化库存回调函数(IStockCallback )
+
+```
+/**
+ * 获取库存回调
+ */
+public interface IStockCallback {
+
+ /**
+ * 获取库存
+ * @return
+ */
+ int getStock();
+}
+```
+
+### 扣减库存服务(StockService)
+
+```
+/**
+ * 扣库存
+ *
+ */
+@Service
+public class StockService {
+ Logger logger = LoggerFactory.getLogger(StockService.class);
+
+ /**
+ * 不限库存
+ */
+ public static final long UNINITIALIZED_STOCK = -3L;
+
+ /**
+ * Redis 客户端
+ */
+ @Autowired
+ private RedisTemplate redisTemplate;
+
+ /**
+ * 执行扣库存的脚本
+ */
+ public static final String STOCK_LUA;
+
+ static {
+ /**
+ *
+ * @desc 扣减库存Lua脚本
+ * 库存(stock)-1:表示不限库存
+ * 库存(stock)0:表示没有库存
+ * 库存(stock)大于0:表示剩余库存
+ *
+ * @params 库存key
+ * @return
+ * -3:库存未初始化
+ * -2:库存不足
+ * -1:不限库存
+ * 大于等于0:剩余库存(扣减之后剩余的库存)
+ * redis缓存的库存(value)是-1表示不限库存,直接返回1
+ */
+ StringBuilder sb = new StringBuilder();
+ sb.append("if (redis.call('exists', KEYS[1]) == 1) then");
+ sb.append(" local stock = tonumber(redis.call('get', KEYS[1]));");
+ sb.append(" local num = tonumber(ARGV[1]);");
+ sb.append(" if (stock == -1) then");
+ sb.append(" return -1;");
+ sb.append(" end;");
+ sb.append(" if (stock >= num) then");
+ sb.append(" return redis.call('incrby', KEYS[1], 0 - num);");
+ sb.append(" end;");
+ sb.append(" return -2;");
+ sb.append("end;");
+ sb.append("return -3;");
+ STOCK_LUA = sb.toString();
+ }
+
+ /**
+ * @param key 库存key
+ * @param expire 库存有效时间,单位秒
+ * @param num 扣减数量
+ * @param stockCallback 初始化库存回调函数
+ * @return -2:库存不足; -1:不限库存; 大于等于0:扣减库存之后的剩余库存
+ */
+ public long stock(String key, long expire, int num, IStockCallback stockCallback) {
+ long stock = stock(key, num);
+ // 初始化库存
+ if (stock == UNINITIALIZED_STOCK) {
+ RedisLock redisLock = new RedisLock(redisTemplate, key);
+ try {
+ // 获取锁
+ if (redisLock.tryLock()) {
+ // 双重验证,避免并发时重复回源到数据库
+ stock = stock(key, num);
+ if (stock == UNINITIALIZED_STOCK) {
+ // 获取初始化库存
+ final int initStock = stockCallback.getStock();
+ // 将库存设置到redis
+ redisTemplate.opsForValue().set(key, initStock, expire, TimeUnit.SECONDS);
+ // 调一次扣库存的操作
+ stock = stock(key, num);
+ }
+ }
+ } catch (Exception e) {
+ logger.error(e.getMessage(), e);
+ } finally {
+ redisLock.unlock();
+ }
+
+ }
+ return stock;
+ }
+
+ /**
+ * 加库存(还原库存)
+ *
+ * @param key 库存key
+ * @param num 库存数量
+ * @return
+ */
+ public long addStock(String key, int num) {
+
+ return addStock(key, null, num);
+ }
+
+ /**
+ * 加库存
+ *
+ * @param key 库存key
+ * @param expire 过期时间(秒)
+ * @param num 库存数量
+ * @return
+ */
+ public long addStock(String key, Long expire, int num) {
+ boolean hasKey = redisTemplate.hasKey(key);
+ // 判断key是否存在,存在就直接更新
+ if (hasKey) {
+ return redisTemplate.opsForValue().increment(key, num);
+ }
+
+ Assert.notNull(expire,"初始化库存失败,库存过期时间不能为null");
+ RedisLock redisLock = new RedisLock(redisTemplate, key);
+ try {
+ if (redisLock.tryLock()) {
+ // 获取到锁后再次判断一下是否有key
+ hasKey = redisTemplate.hasKey(key);
+ if (!hasKey) {
+ // 初始化库存
+ redisTemplate.opsForValue().set(key, num, expire, TimeUnit.SECONDS);
+ }
+ }
+ } catch (Exception e) {
+ logger.error(e.getMessage(), e);
+ } finally {
+ redisLock.unlock();
+ }
+
+ return num;
+ }
+
+ /**
+ * 获取库存
+ *
+ * @param key 库存key
+ * @return -1:不限库存; 大于等于0:剩余库存
+ */
+ public int getStock(String key) {
+ Integer stock = (Integer) redisTemplate.opsForValue().get(key);
+ return stock == null ? -1 : stock;
+ }
+
+ /**
+ * 扣库存
+ *
+ * @param key 库存key
+ * @param num 扣减库存数量
+ * @return 扣减之后剩余的库存【-3:库存未初始化; -2:库存不足; -1:不限库存; 大于等于0:扣减库存之后的剩余库存】
+ */
+ private Long stock(String key, int num) {
+ // 脚本里的KEYS参数
+ List keys = new ArrayList<>();
+ keys.add(key);
+ // 脚本里的ARGV参数
+ List args = new ArrayList<>();
+ args.add(Integer.toString(num));
+
+ long result = redisTemplate.execute(new RedisCallback() {
+ @Override
+ public Long doInRedis(RedisConnection connection) throws DataAccessException {
+ Object nativeConnection = connection.getNativeConnection();
+ // 集群模式和单机模式虽然执行脚本的方法一样,但是没有共同的接口,所以只能分开执行
+ // 集群模式
+ if (nativeConnection instanceof JedisCluster) {
+ return (Long) ((JedisCluster) nativeConnection).eval(STOCK_LUA, keys, args);
+ }
+
+ // 单机模式
+ else if (nativeConnection instanceof Jedis) {
+ return (Long) ((Jedis) nativeConnection).eval(STOCK_LUA, keys, args);
+ }
+ return UNINITIALIZED_STOCK;
+ }
+ });
+ return result;
+ }
+
+}
+```
+
+### 调用
+
+```
+@RestController
+public class StockController {
+
+ @Autowired
+ private StockService stockService;
+
+ @RequestMapping(value = "stock", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+ public Object stock() {
+ // 商品ID
+ long commodityId = 1;
+ // 库存ID
+ String redisKey = "redis_key:stock:" + commodityId;
+ long stock = stockService.stock(redisKey, 60 * 60, 2, () -> initStock(commodityId));
+ return stock >= 0;
+ }
+
+ /**
+ * 获取初始的库存
+ *
+ * @return
+ */
+ private int initStock(long commodityId) {
+ // TODO 这里做一些初始化库存的操作
+ return 1000;
+ }
+
+ @RequestMapping(value = "getStock", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+ public Object getStock() {
+ // 商品ID
+ long commodityId = 1;
+ // 库存ID
+ String redisKey = "redis_key:stock:" + commodityId;
+
+ return stockService.getStock(redisKey);
+ }
+
+ @RequestMapping(value = "addStock", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
+ public Object addStock() {
+ // 商品ID
+ long commodityId = 2;
+ // 库存ID
+ String redisKey = "redis_key:stock:" + commodityId;
+
+ return stockService.addStock(redisKey, 2);
+ }
+}
+```
+
diff --git a/docs/advance/excellent-article/10-file-upload.md b/docs/advance/excellent-article/10-file-upload.md
new file mode 100644
index 0000000..35655eb
--- /dev/null
+++ b/docs/advance/excellent-article/10-file-upload.md
@@ -0,0 +1,341 @@
+# 大文件上传时如何做到秒传?
+
+大家好,我是大彬~
+
+文件上传是一个老生常谈的话题了,在文件相对比较小的情况下,可以直接把文件转化为字节流上传到服务器,但在文件比较大的情况下,用普通的方式进行上传,这可不是一个好的办法,毕竟很少有人会忍受,当文件上传到一半中断后,继续上传却只能重头开始上传,这种让人不爽的体验。
+
+那有没有比较好的上传体验呢,答案有的,就是下边要介绍的几种上传方式
+
+## 秒传
+
+### 1、什么是秒传
+
+通俗的说,你把要上传的东西上传,服务器会先做**MD5**校验,如果服务器上有一样的东西,它就直接给你个新地址,其实你下载的都是服务器上的同一个文件,想要不秒传,其实只要让**MD5**改变,就是对文件本身做一下修改(改名字不行),例如一个文本文件,你多加几个字,MD5就变了,就不会秒传了.
+
+### 2、本文实现的秒传核心逻辑
+
+**a**、利用redis的set方法存放文件上传状态,其中key为文件上传的md5,value为是否上传完成的标志位,
+
+**b**、当标志位true为上传已经完成,此时如果有相同文件上传,则进入秒传逻辑。如果标志位为false,则说明还没上传完成,此时需要在调用set的方法,保存块号文件记录的路径,其中key为上传文件md5加一个固定前缀,value为块号文件记录路径
+
+## 分片上传
+
+### 1、什么是分片上传
+
+分片上传,就是将所要上传的文件,按照一定的大小,将整个文件分隔成多个数据块(我们称之为Part)来进行分别上传,上传完之后再由服务端对所有上传的文件进行汇总整合成原始的文件。
+
+### 2、分片上传的场景
+
+1.大文件上传
+
+2.网络环境环境不好,存在需要重传风险的场景
+
+## 断点续传
+
+### 1、什么是断点续传
+
+断点续传是在下载或上传时,将下载或上传任务(一个文件或一个压缩包)人为的划分为几个部分,每一个部分采用一个线程进行上传或下载,如果碰到网络故障,可以从已经上传或下载的部分开始继续上传或者下载未完成的部分,而没有必要从头开始上传或者下载。本文的断点续传主要是针对断点上传场景。
+
+#### 2、应用场景
+
+断点续传可以看成是分片上传的一个衍生,因此可以使用分片上传的场景,都可以使用断点续传。
+
+#### 3、实现断点续传的核心逻辑
+
+在分片上传的过程中,如果因为系统崩溃或者网络中断等异常因素导致上传中断,这时候客户端需要记录上传的进度。在之后支持再次上传时,可以继续从上次上传中断的地方进行继续上传。
+
+为了避免客户端在上传之后的进度数据被删除而导致重新开始从头上传的问题,服务端也可以提供相应的接口便于客户端对已经上传的分片数据进行查询,从而使客户端知道已经上传的分片数据,从而从下一个分片数据开始继续上传。
+
+#### 4、实现流程步骤
+
+a、方案一,常规步骤
+
+- 将需要上传的文件按照一定的分割规则,分割成相同大小的数据块;
+- 初始化一个分片上传任务,返回本次分片上传唯一标识;
+- 按照一定的策略(串行或并行)发送各个分片数据块;
+- 发送完成后,服务端根据判断数据上传是否完整,如果完整,则进行数据块合成得到原始文件。
+
+b、方案二、本文实现的步骤
+
+- 前端(客户端)需要根据固定大小对文件进行分片,请求后端(服务端)时要带上分片序号和大小
+- 服务端创建conf文件用来记录分块位置,conf文件长度为总分片数,每上传一个分块即向conf文件中写入一个127,那么没上传的位置就是默认的0,已上传的就是Byte.MAX_VALUE 127(这步是实现断点续传和秒传的核心步骤)
+- 服务器按照请求数据中给的分片序号和每片分块大小(分片大小是固定且一样的)算出开始位置,与读取到的文件片段数据,写入文件。
+
+#### 5、分片上传/断点上传代码实现
+
+a、前端采用百度提供的webuploader的插件,进行分片。因本文主要介绍服务端代码实现,webuploader如何进行分片,具体实现可以查看如下链接:
+
+> http://fex.baidu.com/webuploader/getting-started.html
+
+b、后端用两种方式实现文件写入,一种是用**RandomAccessFile**,如果对**RandomAccessFile**不熟悉的朋友,可以查看如下链接:
+
+> https://blog.csdn.net/dimudan2015/article/details/81910690
+
+另一种是使用**MappedByteBuffer**,对**MappedByteBuffer**不熟悉的朋友,可以查看如下链接进行了解:
+
+> https://www.jianshu.com/p/f90866dcbffc
+
+## 后端进行写入操作的核心代码
+
+### 1、RandomAccessFile实现方式
+
+```
+@UploadMode(mode = UploadModeEnum.RANDOM_ACCESS)
+@Slf4j
+public class RandomAccessUploadStrategy extends SliceUploadTemplate {
+
+ @Autowired
+ private FilePathUtil filePathUtil;
+
+ @Value("${upload.chunkSize}")
+ private long defaultChunkSize;
+
+ @Override
+ public boolean upload(FileUploadRequestDTO param) {
+ RandomAccessFile accessTmpFile = null;
+ try {
+ String uploadDirPath = filePathUtil.getPath(param);
+ File tmpFile = super.createTmpFile(param);
+ accessTmpFile = new RandomAccessFile(tmpFile, "rw");
+ //这个必须与前端设定的值一致
+ long chunkSize = Objects.isNull(param.getChunkSize()) ? defaultChunkSize * 1024 * 1024
+ : param.getChunkSize();
+ long offset = chunkSize * param.getChunk();
+ //定位到该分片的偏移量
+ accessTmpFile.seek(offset);
+ //写入该分片数据
+ accessTmpFile.write(param.getFile().getBytes());
+ boolean isOk = super.checkAndSetUploadProgress(param, uploadDirPath);
+ return isOk;
+ } catch (IOException e) {
+ log.error(e.getMessage(), e);
+ } finally {
+ FileUtil.close(accessTmpFile);
+ }
+ return false;
+ }
+
+}
+```
+
+### 2、MappedByteBuffer实现方式
+
+```
+@UploadMode(mode = UploadModeEnum.MAPPED_BYTEBUFFER)
+@Slf4j
+public class MappedByteBufferUploadStrategy extends SliceUploadTemplate {
+
+ @Autowired
+ private FilePathUtil filePathUtil;
+
+ @Value("${upload.chunkSize}")
+ private long defaultChunkSize;
+
+ @Override
+ public boolean upload(FileUploadRequestDTO param) {
+
+ RandomAccessFile tempRaf = null;
+ FileChannel fileChannel = null;
+ MappedByteBuffer mappedByteBuffer = null;
+ try {
+ String uploadDirPath = filePathUtil.getPath(param);
+ File tmpFile = super.createTmpFile(param);
+ tempRaf = new RandomAccessFile(tmpFile, "rw");
+ fileChannel = tempRaf.getChannel();
+
+ long chunkSize = Objects.isNull(param.getChunkSize()) ? defaultChunkSize * 1024 * 1024
+ : param.getChunkSize();
+ //写入该分片数据
+ long offset = chunkSize * param.getChunk();
+ byte[] fileData = param.getFile().getBytes();
+ mappedByteBuffer = fileChannel
+.map(FileChannel.MapMode.READ_WRITE, offset, fileData.length);
+ mappedByteBuffer.put(fileData);
+ boolean isOk = super.checkAndSetUploadProgress(param, uploadDirPath);
+ return isOk;
+
+ } catch (IOException e) {
+ log.error(e.getMessage(), e);
+ } finally {
+
+ FileUtil.freedMappedByteBuffer(mappedByteBuffer);
+ FileUtil.close(fileChannel);
+ FileUtil.close(tempRaf);
+
+ }
+
+ return false;
+ }
+
+}
+```
+
+### 3、文件操作核心模板类代码
+
+```
+@Slf4j
+public abstract class SliceUploadTemplate implements SliceUploadStrategy {
+
+ public abstract boolean upload(FileUploadRequestDTO param);
+
+ protected File createTmpFile(FileUploadRequestDTO param) {
+
+ FilePathUtil filePathUtil = SpringContextHolder.getBean(FilePathUtil.class);
+ param.setPath(FileUtil.withoutHeadAndTailDiagonal(param.getPath()));
+ String fileName = param.getFile().getOriginalFilename();
+ String uploadDirPath = filePathUtil.getPath(param);
+ String tempFileName = fileName + "_tmp";
+ File tmpDir = new File(uploadDirPath);
+ File tmpFile = new File(uploadDirPath, tempFileName);
+ if (!tmpDir.exists()) {
+ tmpDir.mkdirs();
+ }
+ return tmpFile;
+ }
+
+ @Override
+ public FileUploadDTO sliceUpload(FileUploadRequestDTO param) {
+
+ boolean isOk = this.upload(param);
+ if (isOk) {
+ File tmpFile = this.createTmpFile(param);
+ FileUploadDTO fileUploadDTO = this.saveAndFileUploadDTO(param.getFile().getOriginalFilename(), tmpFile);
+ return fileUploadDTO;
+ }
+ String md5 = FileMD5Util.getFileMD5(param.getFile());
+
+ Map map = new HashMap<>();
+ map.put(param.getChunk(), md5);
+ return FileUploadDTO.builder().chunkMd5Info(map).build();
+ }
+
+ /**
+ * 检查并修改文件上传进度
+ */
+ public boolean checkAndSetUploadProgress(FileUploadRequestDTO param, String uploadDirPath) {
+
+ String fileName = param.getFile().getOriginalFilename();
+ File confFile = new File(uploadDirPath, fileName + ".conf");
+ byte isComplete = 0;
+ RandomAccessFile accessConfFile = null;
+ try {
+ accessConfFile = new RandomAccessFile(confFile, "rw");
+ //把该分段标记为 true 表示完成
+ System.out.println("set part " + param.getChunk() + " complete");
+ //创建conf文件文件长度为总分片数,每上传一个分块即向conf文件中写入一个127,那么没上传的位置就是默认0,已上传的就是Byte.MAX_VALUE 127
+ accessConfFile.setLength(param.getChunks());
+ accessConfFile.seek(param.getChunk());
+ accessConfFile.write(Byte.MAX_VALUE);
+
+ //completeList 检查是否全部完成,如果数组里是否全部都是127(全部分片都成功上传)
+ byte[] completeList = FileUtils.readFileToByteArray(confFile);
+ isComplete = Byte.MAX_VALUE;
+ for (int i = 0; i < completeList.length && isComplete == Byte.MAX_VALUE; i++) {
+ //与运算, 如果有部分没有完成则 isComplete 不是 Byte.MAX_VALUE
+ isComplete = (byte) (isComplete & completeList[i]);
+ System.out.println("check part " + i + " complete?:" + completeList[i]);
+ }
+
+ } catch (IOException e) {
+ log.error(e.getMessage(), e);
+ } finally {
+ FileUtil.close(accessConfFile);
+ }
+ boolean isOk = setUploadProgress2Redis(param, uploadDirPath, fileName, confFile, isComplete);
+ return isOk;
+ }
+
+ /**
+ * 把上传进度信息存进redis
+ */
+ private boolean setUploadProgress2Redis(FileUploadRequestDTO param, String uploadDirPath,
+ String fileName, File confFile, byte isComplete) {
+
+ RedisUtil redisUtil = SpringContextHolder.getBean(RedisUtil.class);
+ if (isComplete == Byte.MAX_VALUE) {
+ redisUtil.hset(FileConstant.FILE_UPLOAD_STATUS, param.getMd5(), "true");
+ redisUtil.del(FileConstant.FILE_MD5_KEY + param.getMd5());
+ confFile.delete();
+ return true;
+ } else {
+ if (!redisUtil.hHasKey(FileConstant.FILE_UPLOAD_STATUS, param.getMd5())) {
+ redisUtil.hset(FileConstant.FILE_UPLOAD_STATUS, param.getMd5(), "false");
+ redisUtil.set(FileConstant.FILE_MD5_KEY + param.getMd5(),
+ uploadDirPath + FileConstant.FILE_SEPARATORCHAR + fileName + ".conf");
+ }
+
+ return false;
+ }
+ }
+/**
+ * 保存文件操作
+ */
+ public FileUploadDTO saveAndFileUploadDTO(String fileName, File tmpFile) {
+
+ FileUploadDTO fileUploadDTO = null;
+
+ try {
+
+ fileUploadDTO = renameFile(tmpFile, fileName);
+ if (fileUploadDTO.isUploadComplete()) {
+ System.out
+ .println("upload complete !!" + fileUploadDTO.isUploadComplete() + " name=" + fileName);
+ //TODO 保存文件信息到数据库
+
+ }
+
+ } catch (Exception e) {
+ log.error(e.getMessage(), e);
+ } finally {
+
+ }
+ return fileUploadDTO;
+ }
+/**
+ * 文件重命名
+ *
+ * @param toBeRenamed 将要修改名字的文件
+ * @param toFileNewName 新的名字
+ */
+ private FileUploadDTO renameFile(File toBeRenamed, String toFileNewName) {
+ //检查要重命名的文件是否存在,是否是文件
+ FileUploadDTO fileUploadDTO = new FileUploadDTO();
+ if (!toBeRenamed.exists() || toBeRenamed.isDirectory()) {
+ log.info("File does not exist: {}", toBeRenamed.getName());
+ fileUploadDTO.setUploadComplete(false);
+ return fileUploadDTO;
+ }
+ String ext = FileUtil.getExtension(toFileNewName);
+ String p = toBeRenamed.getParent();
+ String filePath = p + FileConstant.FILE_SEPARATORCHAR + toFileNewName;
+ File newFile = new File(filePath);
+ //修改文件名
+ boolean uploadFlag = toBeRenamed.renameTo(newFile);
+
+ fileUploadDTO.setMtime(DateUtil.getCurrentTimeStamp());
+ fileUploadDTO.setUploadComplete(uploadFlag);
+ fileUploadDTO.setPath(filePath);
+ fileUploadDTO.setSize(newFile.length());
+ fileUploadDTO.setFileExt(ext);
+ fileUploadDTO.setFileId(toFileNewName);
+
+ return fileUploadDTO;
+ }
+}
+```
+
+## 总结
+
+在实现分片上传的过程,需要前端和后端配合,比如前后端的上传块号的文件大小,前后端必须得要一致,否则上传就会有问题。其次文件相关操作正常都是要搭建一个文件服务器的,比如使用**fastdfs**、**hdfs**等。
+
+本示例代码在电脑配置为4核内存8G情况下,上传24G大小的文件,上传时间需要30多分钟,主要时间耗费在前端的**md5**值计算,后端写入的速度还是比较快。
+
+如果项目组觉得自建文件服务器太花费时间,且项目的需求仅仅只是上传下载,那么推荐使用阿里的oss服务器,其介绍可以查看官网:
+
+> https://help.aliyun.com/product/31815.html
+
+阿里的oss它本质是一个对象存储服务器,而非文件服务器,因此如果有涉及到大量删除或者修改文件的需求,oss可能就不是一个好的选择。
+
+文末提供一个oss表单上传的链接demo,通过oss表单上传,可以直接从前端把文件上传到oss服务器,把上传的压力都推给oss服务器:
+
+> https://www.cnblogs.com/ossteam/p/4942227.html
\ No newline at end of file
diff --git a/docs/advance/excellent-article/11-8-architect-pattern.md b/docs/advance/excellent-article/11-8-architect-pattern.md
new file mode 100644
index 0000000..7ec3181
--- /dev/null
+++ b/docs/advance/excellent-article/11-8-architect-pattern.md
@@ -0,0 +1,3 @@
+# 8种架构模式
+
+[8种架构模式](https://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247490779&idx=2&sn=eff9e8cf9b15c29630514a137f102701&chksm=ce98e19df9ef688bd9c7b775658c704a51b7961347a7aabf70e6c555cb57560aa5e8b1e497a1&token=1170645384&lang=zh_CN#rd)
\ No newline at end of file
diff --git a/docs/advance/excellent-article/12-mysql-table-max-rows.md b/docs/advance/excellent-article/12-mysql-table-max-rows.md
new file mode 100644
index 0000000..d8f5381
--- /dev/null
+++ b/docs/advance/excellent-article/12-mysql-table-max-rows.md
@@ -0,0 +1,212 @@
+# MySQL最大建议行数 2000w,靠谱吗?
+
+## **1 背景**
+
+作为在后端圈开车的多年老司机,是不是经常听到过,“mysql 单表最好不要超过 2000w”,“单表超过 2000w 就要考虑数据迁移了”,“你这个表数据都马上要到 2000w 了,难怪查询速度慢”
+
+这些名言民语就和 “群里只讨论技术,不开车,开车速度不要超过 120 码,否则自动踢群”,只听过,没试过,哈哈。
+
+下面我们就把车速踩到底,干到 180 码试试…….
+
+## **2 实验**
+
+实验一把看看…
+
+建一张表
+
+```
+CREATE TABLE person(
+id int NOT NULL AUTO_INCREMENT PRIMARY KEY comment '主键',
+person_id tinyint not null comment '用户id',
+person_name VARCHAR(200) comment '用户名称',
+gmt_create datetime comment '创建时间',
+gmt_modified datetime comment '修改时间'
+) comment '人员信息表';
+```
+
+插入一条数据
+
+```
+insert into person values(1,1,'user_1', NOW(), now());
+```
+
+利用 mysql 伪列 rownum 设置伪列起始点为 1
+
+```
+select (@i:=@i+1) as rownum, person_name from person, (select @i:=100) as init;
+set @i=1;
+```
+
+运行下面的 sql,连续执行 20 次,就是 2 的 20 次方约等于 100w 的数据;执行 23 次就是 2 的 23 次方约等于 800w , 如此下去即可实现千万测试数据的插入,如果不想翻倍翻倍的增加数据,而是想少量,少量的增加,有个技巧,就是在 SQL 的后面增加 where 条件,如 id > 某一个值去控制增加的数据量即可。
+
+```
+insert into person(id, person_id, person_name, gmt_create, gmt_modified)
+
+select @i:=@i+1,
+
+left(rand()*10,10) as person_id,
+
+concat('user_',@i%2048),
+
+date_add(gmt_create,interval + @i*cast(rand()*100 as signed) SECOND),
+
+date_add(date_add(gmt_modified,interval +@i*cast(rand()*100 as signed) SECOND), interval + cast(rand()*1000000 as signed) SECOND)
+
+from person;
+```
+
+此处需要注意的是,也许你在执行到近 800w 或者 1000w 数据的时候,会报错:`The total number of locks exceeds the lock table size`,这是由于你的临时表内存设置的不够大,只需要扩大一下设置参数即可。
+
+```
+SET GLOBAL tmp_table_size =512*1024*1024; (512M)
+SET global innodb_buffer_pool_size= 1*1024*1024*1024 (1G);
+```
+
+先来看一组测试数据,这组数据是在 mysql8.0 的版本,并且是在我本机上,由于本机还跑着 idea , 浏览器等各种工具,所以并不是机器配置就是用于数据库配置,所以测试数据只限于参考。
+
+
+
+
+
+看到这组数据似乎好像真的和标题对应,当数据达到 2000w 以后,查询时长急剧上升;难道这就是铁律吗?
+
+那下面我们就来看看这个建议值 2kw 是怎么来的?
+
+## **3 单表数量限制**
+
+首先我们先想想数据库单表行数最大多大?
+
+```
+CREATE TABLE person(
+id int(10) NOT NULL AUTO_INCREMENT PRIMARY KEY comment '主键',
+person_id tinyint not null comment '用户id',
+person_name VARCHAR(200) comment '用户名称',
+gmt_create datetime comment '创建时间',
+gmt_modified datetime comment '修改时间'
+) comment '人员信息表';
+```
+
+看看上面的建表 sql,id 是主键,本身就是唯一的,也就是说主键的大小可以限制表的上限,如果主键声明 int 大小,也就是 32 位,那么支持 `2^32-1 ~~21` 亿;如果是 bigint,那就是 `2^62-1` ?(`36893488147419103232`),难以想象这个的多大了,一般还没有到这个限制之前,可能数据库已经爆满了!!
+
+有人统计过,如果建表的时候,自增字段选择无符号的 bigint , 那么自增长最大值是 `18446744073709551615`,按照一秒新增一条记录的速度,大约什么时候能用完?
+
+
+
+## **4 表空间**
+
+下面我们再来看看索引的结构,对了,我们下面讲内容都是基于 Innodb 引擎的,大家都知道 Innodb 的索引内部用的是 B+ 树
+
+
+
+这张表数据,在硬盘上存储也是类似如此的,它实际是放在一个叫 `person.ibd` (innodb data)的文件中,也叫做表空间;虽然数据表中,他们看起来是一条连着一条,但是实际上在文件中它被分成很多小份的数据页,而且每一份都是 16K。大概就像下面这样,当然这只是我们抽象出来的,在表空间中还有段、区、组等很多概念,但是我们需要跳出来看。
+
+
+
+## **5 页的数据结构**
+
+因为每个页只有 16K 的大小,但是如果数据很多,那一页肯定就放不下这些数据,那数据肯定就会被分到其他的页中,所以为了把这些页关联起来,肯定就会有记录前后页地址,方便找到对应页;同时每页都是唯一的,那就会需要有一个唯一标志来标记页,就是页号;
+
+页中会记录数据所以会存在读写操作,读写操作会存在中断或者其他异常导致数据不全等,那就会需要有校验机制,所以里面还有会校验码,而读操作最重要的就是效率问题,如果按照记录一个个进行遍历,那肯定是很费劲的,所以这里面还会为数据生成对应的页目录(`Page Directory`); 所以实际页的内部结构像是下面这样的。
+
+
+
+从图中可以看出,一个 InnoDB 数据页的存储空间大致被划分成了 7 个部分,有的部分占用的字节数是确定的,有的部分占用的字节数是不确定的。
+
+在页的 7 个组成部分中,我们自己存储的记录会按照我们指定的行格式存储到 `User Records` 部分。
+
+但是在一开始生成页的时候,其实并没有 `User Records` 这个部分,每当我们插入一条记录,都会从 `Free Space` 部分,也就是尚未使用的存储空间中申请一个记录大小的空间划分到 `User Records` 部分,当 `Free Space` 部分的空间全部被 `User Records` 部分替代掉之后,也就意味着这个页使用完了,如果还有新的记录插入的话,就需要去申请新的页了。
+
+这个过程的图示如下:
+
+
+
+刚刚上面说到了数据的新增的过程。
+
+那下面就来说说,数据的查找过程,假如我们需要查找一条记录,我们可以把表空间中的每一页都加载到内存中,然后对记录挨个判断是不是我们想要的,在数据量小的时候,没啥问题,内存也可以撑;但是现实就是这么残酷,不会给你这个局面;为了解决这问题,mysql 中就有了索引的概念;大家都知道索引能够加快数据的查询,那到底是怎么个回事呢?下面我就来看看。
+
+## **6 索引的数据结构**
+
+在 mysql 中索引的数据结构和刚刚描述的页几乎是一模一样的,而且大小也是 16K, 但是在索引页中记录的是页 (数据页,索引页) 的最小主键 id 和页号,以及在索引页中增加了层级的信息,从 0 开始往上算,所以页与页之间就有了上下层级的概念。
+
+看到这个图之后,是不是有点似曾相似的感觉,是不是像一棵二叉树啊,对,没错!它就是一棵树,只不过我们在这里只是简单画了三个节点,2 层结构的而已,如果数据多了,可能就会扩展到 3 层的树,这个就是我们常说的 B+ 树,最下面那一层的 page level =0, 也就是叶子节点,其余都是非叶子节点。
+
+
+
+看上图中,我们是单拿一个节点来看,首先它是一个非叶子节点(索引页),在它的内容区中有 id 和 页号地址两部分,这个 id 是对应页中记录的最小记录 id 值,页号地址是指向对应页的指针;而数据页与此几乎大同小异,区别在于数据页记录的是真实的行数据而不是页地址,而且 id 的也是顺序的。
+
+## **7 单表建议值**
+
+下面我们就以 3 层,2 分叉(实际中是 M 分叉)的图例来说明一下查找一个行数据的过程。
+
+比如说我们需要查找一个 id=6 的行数据,因为在非叶子节点中存放的是页号和该页最小的 id,所以我们从顶层开始对比,首先看页号 10 中的目录,有 `[id=1, 页号 = 20]`,`[id=5, 页号 = 30]`, 说明左侧节点最小 id 为 1,右侧节点最小 id 是 5;`6>5`, 那按照二分法查找的规则,肯定就往右侧节点继续查找,找到页号 30 的节点后,发现这个节点还有子节点(非叶子节点),那就继续比对,同理,`6>5&&6<7`, 所以找到了页号 60,找到页号 60 之后,发现此节点为叶子节点(数据节点),于是将此页数据加载至内存进行一一对比,结果找到了 `id=6 `的数据行。
+
+从上述的过程中发现,我们为了查找 id=6 的数据,总共查询了三个页,如果三个页都在磁盘中(未提前加载至内存),那么最多需要经历三次的磁盘 IO。
+
+需要注意的是,图中的页号只是个示例,实际情况下并不是连续的,在磁盘中存储也不一定是顺序的。
+
+至此,
+
+我们大概已经了解了表的数据是怎么个结构了,也大概知道查询数据是个怎么的过程了,这样我们也就能大概估算这样的结构能存放多少数据了。
+
+从上面的图解我们知道 B+ 数的叶子节点才是存在数据的,而非叶子节点是用来存放索引数据的。
+
+所以,同样一个 16K 的页,非叶子节点里的每条数据都指向新的页,而新的页有两种可能
+
+- 如果是叶子节点,那么里面就是一行行的数据
+- 如果是非叶子节点的话,那么就会继续指向新的页
+
+假设
+
+- 非叶子节点内指向其他页的数量为 x
+- 叶子节点内能容纳的数据行数为 y
+- B+ 数的层数为 z
+
+如下图中所示
+
+`Total =x^(z-1) *y `也就是说总数会等于 x 的 `z-1` 次方 与 Y 的乘积。
+
+
+
+> X =?
+
+在文章的开头已经介绍了页的结构,索引也也不例外,都会有 `File Header (38 byte)`、`Page Header (56 Byte)`、`Infimum + Supermum(26 byte)`、`File Trailer(8byte)`, 再加上页目录,大概 1k 左右,我们就当做它就是 1K, 那整个页的大小是 16K, 剩下 15k 用于存数据,在索引页中主要记录的是主键与页号,主键我们假设是 Bigint (8 byte), 而页号也是固定的(4Byte), 那么索引页中的一条数据也就是 12byte; 所以 `x=15*1024/12≈1280` 行。
+
+> Y=?
+
+叶子节点和非叶子节点的结构是一样的,同理,能放数据的空间也是 15k;但是叶子节点中存放的是真正的行数据,这个影响的因素就会多很多,比如,字段的类型,字段的数量;每行数据占用空间越大,页中所放的行数量就会越少;这边我们暂时按一条行数据 1k 来算,那一页就能存下 15 条,`Y≈15`。
+
+算到这边了,是不是心里已经有谱了啊
+
+- 根据上述的公式,`Total =x^(z-1) y`,已知 `x=1280,y=15`
+- 假设 B+ 树是两层,那就是 Z =2, `Total = (1280 ^1 )15 = 19200`
+- 假设 B+ 树是三层,那就是 Z =3, `Total = (1280 ^2) *15 = 24576000 (约 2.45kw)`
+
+哎呀,妈呀!这不是正好就是文章开头说的最大行数建议值 2000w 嘛!对的,一般 B+ 数的层级最多也就是 3 层,你试想一下,如果是 4 层,除了查询的时候磁盘 IO 次数会增加,而且这个 Total 值会是多少,大概应该是 3 百多亿吧,也不太合理,所以,3 层应该是比较合理的一个值。
+
+到这里难道就完了?
+
+no
+
+我们刚刚在说 Y 的值时候假设的是 1K ,那比如我实际当行的数据占用空间不是 1K , 而是 5K, 那么单个数据页最多只能放下 3 条数据
+
+同样,还是按照 Z=3 的值来计算,那 `Total = (1280 ^2) *3 = 4915200 (近 500w)`
+
+所以,在保持相同的层级(相似查询性能)的情况下,在行数据大小不同的情况下,其实这个最大建议值也是不同的,而且影响查询性能的还有很多其他因素,比如,数据库版本,服务器配置,sql 的编写等等,MySQL 为了提高性能,会将表的索引装载到内存中。在 `InnoDB buffer size` 足够的情况下,其能完成全加载进内存,查询不会有问题。
+
+但是,当单表数据库到达某个量级的上限时,导致内存无法存储其索引,使得之后的 SQL 查询会产生磁盘 IO,从而导致性能下降,所以增加硬件配置(比如把内存当磁盘使),可能会带来立竿见影的性能提升哈。
+
+## **8 总结**
+
+Mysql 的表数据是以页的形式存放的,页在磁盘中不一定是连续的。
+
+页的空间是 16K, 并不是所有的空间都是用来存放数据的,会有一些固定的信息,如,页头,页尾,页码,校验码等等。在 B+ 树中,叶子节点和非叶子节点的数据结构是一样的,区别在于,叶子节点存放的是实际的行数据,而非叶子节点存放的是主键和页号。
+
+索引结构不会影响单表最大行数,2kw 也只是推荐值,超过了这个值可能会导致 B + 树层级更高,影响查询性能。
+
+## **9 参考**
+
+- *https://www.jianshu.com/p/cf5d381ef637*
+- *https://www.modb.pro/db/139052*
+- *《MYSQL 内核:INNODB 存储引擎 卷 1》*
+
+*来源:my.oschina.net/u/4090830/blog/5559454*
\ No newline at end of file
diff --git a/docs/advance/excellent-article/13-order-by-work.md b/docs/advance/excellent-article/13-order-by-work.md
new file mode 100644
index 0000000..5c7b0ee
--- /dev/null
+++ b/docs/advance/excellent-article/13-order-by-work.md
@@ -0,0 +1,263 @@
+# order by是怎么工作的?
+
+在你开发应用的时候,一定会经常碰到需要根据指定的字段排序来显示结果的需求。还是以我们前面举例用过的市民表为例,假设你要查询城市是“杭州”的所有人名字,并且按照姓名排序返回前 1000 个人的姓名、年龄。
+
+假设这个表的部分定义是这样的:
+
+```r
+CREATE TABLE `t` (
+ `id` int(11) NOT NULL,
+ `city` varchar(16) NOT NULL,
+ `name` varchar(16) NOT NULL,
+ `age` int(11) NOT NULL,
+ `addr` varchar(128) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `city` (`city`)
+) ENGINE=InnoDB;
+```
+
+这时,你的 SQL 语句可以这么写:
+
+```csharp
+select city,name,age from t where city='杭州' order by name limit 1000 ;
+```
+
+这个语句看上去逻辑很清晰,但是你了解它的执行流程吗?今天,我就和你聊聊这个语句是怎么执行的,以及有什么参数会影响执行的行为。
+
+# 全字段排序
+
+前面我们介绍过索引,所以你现在就很清楚了,为避免全表扫描,我们需要在 city 字段加上索引。
+
+在 city 字段上创建索引之后,我们用 explain 命令来看看这个语句的执行情况。
+
+
+
+图 1 使用 explain 命令查看语句的执行情况
+
+Extra 这个字段中的“Using filesort”表示的就是需要排序,MySQL 会给每个线程分配一块内存用于排序,称为 sort_buffer。
+
+为了说明这个 SQL 查询语句的执行过程,我们先来看一下 city 这个索引的示意图。
+
+
+
+图 2 city 字段的索引示意图
+
+从图中可以看到,满足 city='杭州’条件的行,是从 ID_X 到 ID_(X+N) 的这些记录。
+
+通常情况下,这个语句执行流程如下所示 :
+
+1. 初始化 sort_buffer,确定放入 name、city、age 这三个字段;
+2. 从索引 city 找到第一个满足 city='杭州’条件的主键 id,也就是图中的 ID_X;
+3. 到主键 id 索引取出整行,取 name、city、age 三个字段的值,存入 sort_buffer 中;
+4. 从索引 city 取下一个记录的主键 id;
+5. 重复步骤 3、4 直到 city 的值不满足查询条件为止,对应的主键 id 也就是图中的 ID_Y;
+6. 对 sort_buffer 中的数据按照字段 name 做快速排序;
+7. 按照排序结果取前 1000 行返回给客户端。
+
+我们暂且把这个排序过程,称为全字段排序,执行流程的示意图如下所示,下一篇文章中我们还会用到这个排序。
+
+
+
+图 3 全字段排序
+
+图中“按 name 排序”这个动作,可能在内存中完成,也可能需要使用外部排序,这取决于排序所需的内存和参数 sort_buffer_size。
+
+sort_buffer_size,就是 MySQL 为排序开辟的内存(sort_buffer)的大小。如果要排序的数据量小于 sort_buffer_size,排序就在内存中完成。但如果排序数据量太大,内存放不下,则不得不利用磁盘临时文件辅助排序。
+
+你可以用下面介绍的方法,来确定一个排序语句是否使用了临时文件。
+
+```sql
+/* 打开 optimizer_trace,只对本线程有效 */
+SET optimizer_trace='enabled=on';
+
+/* @a 保存 Innodb_rows_read 的初始值 */
+select VARIABLE_VALUE into @a from performance_schema.session_status where variable_name = 'Innodb_rows_read';
+
+/* 执行语句 */
+select city, name,age from t where city='杭州' order by name limit 1000;
+
+/* 查看 OPTIMIZER_TRACE 输出 */
+SELECT * FROM `information_schema`.`OPTIMIZER_TRACE`\G
+
+/* @b 保存 Innodb_rows_read 的当前值 */
+select VARIABLE_VALUE into @b from performance_schema.session_status where variable_name = 'Innodb_rows_read';
+
+/* 计算 Innodb_rows_read 差值 */
+select @b-@a;
+```
+
+这个方法是通过查看 OPTIMIZER_TRACE 的结果来确认的,你可以从 number_of_tmp_files 中看到是否使用了临时文件。
+
+
+
+图 4 全排序的 OPTIMIZER_TRACE 部分结果
+
+number_of_tmp_files 表示的是,排序过程中使用的临时文件数。你一定奇怪,为什么需要 12 个文件?内存放不下时,就需要使用外部排序,外部排序一般使用归并排序算法。可以这么简单理解,**MySQL 将需要排序的数据分成 12 份,每一份单独排序后存在这些临时文件中。然后把这 12 个有序文件再合并成一个有序的大文件。**
+
+如果 sort_buffer_size 超过了需要排序的数据量的大小,number_of_tmp_files 就是 0,表示排序可以直接在内存中完成。
+
+否则就需要放在临时文件中排序。sort_buffer_size 越小,需要分成的份数越多,number_of_tmp_files 的值就越大。
+
+接下来,我再和你解释一下图 4 中其他两个值的意思。
+
+我们的示例表中有 4000 条满足 city='杭州’的记录,所以你可以看到 examined_rows=4000,表示参与排序的行数是 4000 行。
+
+sort_mode 里面的 packed_additional_fields 的意思是,排序过程对字符串做了“紧凑”处理。即使 name 字段的定义是 varchar(16),在排序过程中还是要按照实际长度来分配空间的。
+
+同时,最后一个查询语句 select @b-@a 的返回结果是 4000,表示整个执行过程只扫描了 4000 行。
+
+这里需要注意的是,为了避免对结论造成干扰,我把 internal_tmp_disk_storage_engine 设置成 MyISAM。否则,select @b-@a 的结果会显示为 4001。
+
+这是因为查询 OPTIMIZER_TRACE 这个表时,需要用到临时表,而 internal_tmp_disk_storage_engine 的默认值是 InnoDB。如果使用的是 InnoDB 引擎的话,把数据从临时表取出来的时候,会让 Innodb_rows_read 的值加 1。
+
+# rowid 排序
+
+在上面这个算法过程里面,只对原表的数据读了一遍,剩下的操作都是在 sort_buffer 和临时文件中执行的。但这个算法有一个问题,就是如果查询要返回的字段很多的话,那么 sort_buffer 里面要放的字段数太多,这样内存里能够同时放下的行数很少,要分成很多个临时文件,排序的性能会很差。
+
+所以如果单行很大,这个方法效率不够好。
+
+那么,**如果 MySQL 认为排序的单行长度太大会怎么做呢?**
+
+接下来,我来修改一个参数,让 MySQL 采用另外一种算法。
+
+```java
+SET max_length_for_sort_data = 16;
+```
+
+max_length_for_sort_data,是 MySQL 中专门控制用于排序的行数据的长度的一个参数。它的意思是,如果单行的长度超过这个值,MySQL 就认为单行太大,要换一个算法。
+
+city、name、age 这三个字段的定义总长度是 36,我把 max_length_for_sort_data 设置为 16,我们再来看看计算过程有什么改变。
+
+新的算法放入 sort_buffer 的字段,只有要排序的列(即 name 字段)和主键 id。
+
+但这时,排序的结果就因为少了 city 和 age 字段的值,不能直接返回了,整个执行流程就变成如下所示的样子:
+
+1. 初始化 sort_buffer,确定放入两个字段,即 name 和 id;
+2. 从索引 city 找到第一个满足 city='杭州’条件的主键 id,也就是图中的 ID_X;
+3. 到主键 id 索引取出整行,取 name、id 这两个字段,存入 sort_buffer 中;
+4. 从索引 city 取下一个记录的主键 id;
+5. 重复步骤 3、4 直到不满足 city='杭州’条件为止,也就是图中的 ID_Y;
+6. 对 sort_buffer 中的数据按照字段 name 进行排序;
+7. 遍历排序结果,取前 1000 行,并按照 id 的值回到原表中取出 city、name 和 age 三个字段返回给客户端。
+
+这个执行流程的示意图如下,我把它称为 rowid 排序。
+
+
+
+图 5 rowid 排序
+
+对比图 3 的全字段排序流程图你会发现,rowid 排序多访问了一次表 t 的主键索引,就是步骤 7。
+
+需要说明的是,最后的“结果集”是一个逻辑概念,实际上 MySQL 服务端从排序后的 sort_buffer 中依次取出 id,然后到原表查到 city、name 和 age 这三个字段的结果,不需要在服务端再耗费内存存储结果,是直接返回给客户端的。
+
+根据这个说明过程和图示,你可以想一下,这个时候执行 select @b-@a,结果会是多少呢?
+
+现在,我们就来看看结果有什么不同。
+
+首先,图中的 examined_rows 的值还是 4000,表示用于排序的数据是 4000 行。但是 select @b-@a 这个语句的值变成 5000 了。
+
+因为这时候除了排序过程外,在排序完成后,还要根据 id 去原表取值。由于语句是 limit 1000,因此会多读 1000 行。
+
+
+
+图 6 rowid 排序的 OPTIMIZER_TRACE 部分输出
+
+从 OPTIMIZER_TRACE 的结果中,你还能看到另外两个信息也变了。
+
+- sort_mode 变成了 ,表示参与排序的只有 name 和 id 这两个字段。
+- number_of_tmp_files 变成 10 了,是因为这时候参与排序的行数虽然仍然是 4000 行,但是每一行都变小了,因此需要排序的总数据量就变小了,需要的临时文件也相应地变少了。
+
+# 全字段排序 VS rowid 排序
+
+我们来分析一下,从这两个执行流程里,还能得出什么结论。
+
+如果 MySQL 实在是担心排序内存太小,会影响排序效率,才会采用 rowid 排序算法,这样排序过程中一次可以排序更多行,但是需要再回到原表去取数据。
+
+如果 MySQL 认为内存足够大,会优先选择全字段排序,把需要的字段都放到 sort_buffer 中,这样排序后就会直接从内存里面返回查询结果了,不用再回到原表去取数据。
+
+这也就体现了 MySQL 的一个设计思想:**如果内存够,就要多利用内存,尽量减少磁盘访问。**
+
+对于 InnoDB 表来说,rowid 排序会要求回表多造成磁盘读,因此不会被优先选择。
+
+这个结论看上去有点废话的感觉,但是你要记住它,下一篇文章我们就会用到。
+
+看到这里,你就了解了,MySQL 做排序是一个成本比较高的操作。那么你会问,是不是所有的 order by 都需要排序操作呢?如果不排序就能得到正确的结果,那对系统的消耗会小很多,语句的执行时间也会变得更短。
+
+其实,并不是所有的 order by 语句,都需要排序操作的。从上面分析的执行过程,我们可以看到,MySQL 之所以需要生成临时表,并且在临时表上做排序操作,**其原因是原来的数据都是无序的。**
+
+你可以设想下,如果能够保证从 city 这个索引上取出来的行,天然就是按照 name 递增排序的话,是不是就可以不用再排序了呢?
+
+确实是这样的。
+
+所以,我们可以在这个市民表上创建一个 city 和 name 的联合索引,对应的 SQL 语句是:
+
+```sql
+alter table t add index city_user(city, name);
+```
+
+作为与 city 索引的对比,我们来看看这个索引的示意图。
+
+
+
+图 7 city 和 name 联合索引示意图
+
+在这个索引里面,我们依然可以用树搜索的方式定位到第一个满足 city='杭州’的记录,并且额外确保了,接下来按顺序取“下一条记录”的遍历过程中,只要 city 的值是杭州,name 的值就一定是有序的。
+
+这样整个查询过程的流程就变成了:
+
+1. 从索引 (city,name) 找到第一个满足 city='杭州’条件的主键 id;
+2. 到主键 id 索引取出整行,取 name、city、age 三个字段的值,作为结果集的一部分直接返回;
+3. 从索引 (city,name) 取下一个记录主键 id;
+4. 重复步骤 2、3,直到查到第 1000 条记录,或者是不满足 city='杭州’条件时循环结束。
+
+
+
+图 8 引入 (city,name) 联合索引后,查询语句的执行计划
+
+可以看到,这个查询过程不需要临时表,也不需要排序。接下来,我们用 explain 的结果来印证一下。
+
+
+
+图 9 引入 (city,name) 联合索引后,查询语句的执行计划
+
+从图中可以看到,Extra 字段中没有 Using filesort 了,也就是不需要排序了。而且由于 (city,name) 这个联合索引本身有序,所以这个查询也不用把 4000 行全都读一遍,只要找到满足条件的前 1000 条记录就可以退出了。也就是说,在我们这个例子里,只需要扫描 1000 次。
+
+既然说到这里了,我们再往前讨论,**这个语句的执行流程有没有可能进一步简化呢?**不知道你还记不记得,我在第 5 篇文章[《 深入浅出索引(下)》]中,和你介绍的覆盖索引。
+
+这里我们可以再稍微复习一下。**覆盖索引是指,索引上的信息足够满足查询请求,不需要再回到主键索引上去取数据。**
+
+按照覆盖索引的概念,我们可以再优化一下这个查询语句的执行流程。
+
+针对这个查询,我们可以创建一个 city、name 和 age 的联合索引,对应的 SQL 语句就是:
+
+```sql
+alter table t add index city_user_age(city, name, age);
+```
+
+这时,对于 city 字段的值相同的行来说,还是按照 name 字段的值递增排序的,此时的查询语句也就不再需要排序了。这样整个查询语句的执行流程就变成了:
+
+1. 从索引 (city,name,age) 找到第一个满足 city='杭州’条件的记录,取出其中的 city、name 和 age 这三个字段的值,作为结果集的一部分直接返回;
+2. 从索引 (city,name,age) 取下一个记录,同样取出这三个字段的值,作为结果集的一部分直接返回;
+3. 重复执行步骤 2,直到查到第 1000 条记录,或者是不满足 city='杭州’条件时循环结束。
+
+
+
+图 10 引入 (city,name,age) 联合索引后,查询语句的执行流程
+
+然后,我们再来看看 explain 的结果。
+
+
+
+图 11 引入 (city,name,age) 联合索引后,查询语句的执行计划
+
+可以看到,Extra 字段里面多了“Using index”,表示的就是使用了覆盖索引,性能上会快很多。
+
+当然,这里并不是说要为了每个查询能用上覆盖索引,就要把语句中涉及的字段都建上联合索引,毕竟索引还是有维护代价的。这是一个需要权衡的决定。
+
+# 小结
+
+今天这篇文章,我和你介绍了 MySQL 里面 order by 语句的几种算法流程。
+
+在开发系统的时候,你总是不可避免地会使用到 order by 语句。你心里要清楚每个语句的排序逻辑是怎么实现的,还要能够分析出在最坏情况下,每个语句的执行对系统资源的消耗,这样才能做到下笔如有神,不犯低级错误。
+
+> 内容摘录自丁奇的《MySQL45讲》
\ No newline at end of file
diff --git a/docs/advance/excellent-article/14-architect-forward.md b/docs/advance/excellent-article/14-architect-forward.md
new file mode 100644
index 0000000..f4ce67c
--- /dev/null
+++ b/docs/advance/excellent-article/14-architect-forward.md
@@ -0,0 +1,49 @@
+### 传统单体应用架构
+
+十多年前主流的应用架构都是单体应用,部署形式就是一台服务器加一个数据库,在这种架构下,运维人员会小心翼翼地维护这台服务器,以保证服务的可用性。
+
+
+
+#### 单体应用架构面临的问题
+
+随着业务的增长,这种最简单的单体应用架构很快就面临两个问题。首先,这里只有一台服务器,如果这台服务器出现故障,例如硬件损坏,那么整个服务就会不可用;其次,业务量变大之后,一台服务器的资源很快会无法承载所有流量。
+
+解决这两个问题最直接的方法就是在流量入口加一个负载均衡器,使单体应用同时部署到多台服务器上,这样服务器的单点问题就解决了,与此同时,这个单体应用也具备了水平伸缩的能力。
+
+
+
+### 微服务架构
+
+#### 1. 微服务架构演进出通用服务
+
+随着业务的进一步增长,更多的研发人员加入到团队中,共同在单体应用上开发特性。由于单体应用内的代码没有明确的物理边界,大家很快就会遇到各种冲突,需要人工协调,以及大量的 conflict merge 操作,研发效率直线下降。
+
+因此大家开始把单体应用拆分成一个个可以独立开发、独立测试、独立部署的微服务应用,服务和服务之间通过 API 通讯,如 HTTP、GRPC 或者 DUBBO。基于领域驱动设计中 Bounded Context 拆分的微服务架构能够大幅提升中大型团队的研发效率。
+
+#### 2. 微服务架构给运维带来挑战
+
+应用从单体架构演进到微服务架构,从物理的角度看,分布式就成了默认选项,这时应用架构师就不得不面对分布式带来的新挑战。在这个过程中,大家都会开始使用一些分布式服务和框架,例如缓存服务 Redis,配置服务 ACM,状态协调服务 ZooKeeper,消息服务 Kafka,还有通讯框架如 GRPC 或者 DUBBO,以及分布式追踪系统等。
+
+除分布式环境带来的挑战之外,微服务架构给运维也带来新挑战。研发人员原来只需要运维一个应用,现在可能需要运维十个甚至更多的应用,这意味着安全 patch 升级、容量评估、故障诊断等事务的工作量呈现成倍增长,这时,应用分发标准、生命周期标准、观测标准、自动化弹性等能力的重要性也更加凸显。
+
+
+
+### 云原生
+
+#### 1. 基于云产品架构
+
+一个架构是否是云原生,就看这个架构是否是长在云上的,这是对“云原生”的简单理解。这个“长在云上”不是简单地说用云的 IaaS 层服务,比如简单的 ECS、OSS 这些基本的计算存储;而是应该理解成有没有使用云上的分布式服务,比如 Redis、Kafka 等,这些才是直接影响到业务架构的服务。微服务架构下,分布式服务是必要的,原来大家都是自己研发这样的服务,或者基于开源版本自己运维这样的服务。而到了云原生时代,业务则可以直接使用云服务。
+
+另外两个不得不提的技术就是 Docker 和 Kubenetes,其中,前者标准化了应用分发的标准,不论是 Spring Boot 写的应用,还是 NodeJS 写的应用,都以镜像的方式分发;而后者在前者的技术上又定义了应用生命周期的标准,一个应用从启动到上线,到健康检查,再到下线,都有了统一的标准。
+
+#### 2. 应用生命周期托管
+
+有了应用分发的标准和生命周期的标准,云就能提供标准化的应用托管服务。包括应用的版本管理、发布、上线后的观测、自愈等。例如对于无状态的应用来说,一个底层物理节点的故障根本不会影响到研发,因为应用托管服务基于标准化应用生命周期可以自动完成腾挪工作,在故障物理节点上将应用的容器下线,在新的物理节点上启动同等数量的应用容器。可以看出,云原生进一步释放了价值红利。
+
+在此基础上,由于应用托管服务能够感知到应用运行期的数据,例如业务流量的并发、cpu load、内存占用等,业务就可以配置基于这些指标的伸缩规则,再由平台执行这些规则,根据业务流量的实际情况增加或者减少容器数量,这就是最基本的 auto scaling——自动伸缩。这能够帮助用户避免在业务低峰期限制资源,节省成本,提升运维效率。
+
+### 本文总结
+
+在架构的演进过程中,研发运维人员逐渐把关注点从机器上移走,希望更多地由平台系统管理机器,而不是由人去管理,这就是一个对 Serverless 的朴素理解。
+
+> 本文部分内容摘抄自网络
\ No newline at end of file
diff --git a/docs/advance/excellent-article/15-http-vs-rpc.md b/docs/advance/excellent-article/15-http-vs-rpc.md
new file mode 100644
index 0000000..4c1647b
--- /dev/null
+++ b/docs/advance/excellent-article/15-http-vs-rpc.md
@@ -0,0 +1,265 @@
+> 原文链接:https://www.jianshu.com/p/9d42b926d40d
+
+## 既然有 HTTP 请求,为什么还要用 RPC 调用?
+
+一直以来都没有深究过RPC和HTTP的区别,不都是写一个服务然后在客户端调用么?
+
+HTTP和RPC最本质的区别,就是 **RPC 主要是基于 TCP/IP 协议的**,而 **HTTP 服务主要是基于 HTTP 协议的**。
+
+我们都知道 HTTP 协议是在传输层协议 TCP 之上的,所以效率来看的话,RPC 当然是要更胜一筹啦!
+
+HTTP和RPC的相同点是,底层通讯都是基于socket,都可以实现远程调用,都可以实现服务调用服务
+
+### HTTP 的本质
+
+首先你要明确 HTTP 是一个协议,是一个超文本传输协议。
+
+HTTP 它是协议,不是运输通道。
+
+它基于 TCP/IP 来传输文本、图片、视频、音频等。
+
+重点来了。
+
+HTTP 不提供数据包的传输功能,也就是数据包从浏览器到服务端再来回的传输和它没关系。
+
+这是 TCP/IP 干的。
+
+那 HTTP 有啥用?我们来分析一波。
+
+我们上网要么就是获取一些信息来看,要么就是修改一些信息。
+
+比如你用浏览器刷微博就是获取信息,发微博就是修改信息。
+
+所以说浏览器需要告知服务器它需要什么,这次的请求是要获取哪些信息?发怎么样的微博。
+
+这就涉及到浏览器和服务器之间的通信交互。
+
+而交互就需要一种格式。
+
+像你我之间的谈话就用中文,你要突然换成俄语我听不懂那不就 GG 了。
+
+所以说 HTTP 它规定了一种格式,一种通信格式,大家都用这个格式来交谈。
+
+这样不论你是什么服务器、什么浏览器都能顺利的交流,减少交互的成本。
+
+就像全世界如果都讲中文,那我们不就不需要学英文了,那不就较少交互的成本了。
+
+不像现在我们还得学英文,不然就看不懂文档等等。
+
+万一之后俄语又起来了,咱还得对接俄文,这交互成本是不是就上来了。
+
+而网络世界还好,咱们现在的 Web 交互基本上就是 HTTP 了。
+
+其实 HTTP 协议的格式很像我们信封,有个固定的格式。
+
+
+
+左上角写邮编,右上角贴邮票,然后地址姓名啥的依次来。
+
+因为计算机是很死板的,不像我们人一样有一种立体扫描感,所以要规定先写头、再写尾。
+
+你要是先写尾,再写头计算机就认不出来了。
+
+所以 HTTP 就规定了请求先搞请求行、再搞请求报头、再搞请求体。
+
+响应就状态行、响应报头、响应体。
+
+
+
+所以 HTTP 的本质是什么?
+
+**就是客户端和服务端约定好的一种通信格式。**
+
+### HTTP 和 RPC 的关系
+
+HTTP 和 RPC 其实是两个维度的东西, HTTP 指的是通信协议。
+
+而 RPC 则是远程调用,其对应的是本地调用。
+
+RPC 的通信可以用 HTTP 协议,也可以自定义协议,是不做约束的。
+
+像之前的单体时代,我们的 service 调用就是自己实现的方法,是本地进程内的调用。
+
+```
+public User getUserById(Long id) {
+ return userDao.getUserById(id); // 这叫本地调用
+ }
+```
+
+现在都是微服务了,根据业务模块做了不同的拆分,像用户的服务不用我这个小组负责,我这小组只要写订单服务就行了。
+
+但是我们服务需要用到用户的信息,于是我们需要调用用户小组的服务,于是代码变成了以下这种
+
+```
+public User getUserById(Long id) {
+ return userConsumer.getUserById(id); // 这是远程调用,逻辑是用户小组的服务实现的。
+ }
+```
+
+可能还有些小伙伴不太清楚,再来看个图。
+
+
+
+把之前的用户实现拆分出来弄了一个用户服务,订单相关的也拆成了订单服务,都单独部署。
+
+这样订单相关的服务要获取用户的信息就需要远程调用了。
+
+可以看到 RPC 就是通过网络进行远程调用,订单服务其实就是客户端,而用户服务是服务端。
+
+这又涉及到交互了,所以也需要约定一个格式,至于要不要用 HTTP 这个格式,就是大家自己看着办。
+
+至此相信你对 HTTP 是啥也清楚了。
+
+RPC 和 HTTP 的之间的关系也清楚了。
+
+### 那为什么要有 RPC?
+
+可能你常听到什么什么之间是 RPC 调用的,那你有没有想过为什么要 RPC, 我们直接 WebClient HTTP 调用不行么?
+
+其实 RPC 调用是因为服务的拆分,或者本身公司内部的多个服务之间的通信。
+
+服务的拆分独立部署,那服务间的调用就必然需要网络通信,用 WebClient 调用当然可行,但是比较麻烦。
+
+我们想即使服务被拆分了但是使用起来还是和之前本地调用一样方便。
+
+所以就出现了 RPC 框架,来屏蔽这些底层调用细节,使得我们编码上还是和之前本地调用相差不多。
+
+并且 HTTP 协议比较的冗余,RPC 都是内部调用所以不需要太考虑通用性,只要公司内部保持格式统一即可。
+
+所以可以做各种定制化的协议来使得通信更高效。
+
+比如规定 yes 代表 yes的练级攻略,你看是不是更高效了,少传输的 5 个字。
+
+就像特殊行动的暗号,高效简洁!
+
+所以公司内部服务的调用一般都用 RPC,而 HTTP 的优势在于通用,大家都认可这个协议。
+
+所以三方平台提供的接口都是通过 HTTP 协议调用的。
+
+所以现在知道为什么我们调用第三方都是 HTTP ,公司内部用 RPC 了吧?
+
+上面这段话看起来仿佛 HTTP 和 RPC 是对等关系,不过相信大家看了之前的解析心里应该都有数了。
+
+下面来具体说一说 RPC 服务和 HTTP 服务的区别。
+
+#### OSI 网络七层模型
+
+在说 RPC 和 HTTP 的区别之前,我觉的有必要了解一下 OSI 的七层网络结构模型(
+
+
+
+它可以分为以下几层:(从上到下)
+
+- **第一层:应用层。**定义了用于在网络中进行通信和传输数据的接口。
+- **第二层:表示层。**定义不同的系统中数据的传输格式,编码和解码规范等。
+- **第三层:会话层。**管理用户的会话,控制用户间逻辑连接的建立和中断。
+- **第四层:传输层。**管理着网络中的端到端的数据传输。
+- **第五层:网络层。**定义网络设备间如何传输数据。
+- **第六层:链路层。**将上面的网络层的数据包封装成数据帧,便于物理层传输。
+- **第七层:物理层。**这一层主要就是传输这些二进制数据。
+
+
+
+> **实际应用过程中,五层协议结构里面是没有表示层和会话层的。应该说它们和应用层合并了。**
+
+我们应该将重点放在应用层和传输层这两个层面。因为 HTTP 是应用层协议,而 TCP 是传输层协议。
+
+好,知道了网络的分层模型以后我们可以更好地理解为什么 RPC 服务相比 HTTP 服务要 Nice 一些!
+
+### RPC 服务
+
+从三个角度来介绍 RPC 服务,分别是:
+
+- **RPC 架构**
+- **同步异步调用**
+- **流行的 RPC 框架**
+
+**RPC 架构**
+
+先说说 RPC 服务的基本架构吧。我们可以很清楚地看到,一个完整的 RPC 架构里面包含了四个核心的组件。
+
+分别是:
+
+- **Client**
+- **Server**
+- **Client Stub**
+- **Server Stub(这个Stub大家可以理解为存根)**
+
+
+
+分别说说这几个组件:
+
+- **客户端(Client),**服务的调用方。
+- **服务端(Server),**真正的服务提供者。
+- **客户端存根,**存放服务端的地址消息,再将客户端的请求参数打包成网络消息,然后通过网络远程发送给服务方。微信搜索公众号:Linux技术迷,回复:linux 领取资料 。
+- 服务端存根,接收客户端发送过来的消息,将消息解包,并调用本地的方法。
+
+RPC 主要是用在大型企业里面,因为大型企业里面系统繁多,业务线复杂,而且效率优势非常重要的一块,这个时候 RPC 的优势就比较明显了。
+
+
+
+比如我们有一个处理订单的系统服务,先声明它的所有的接口,然后将整个项目打包,服务端这边引入,然后实现相应的功能,客户端这边也只需要引入就可以调用了。
+
+为什么这么做?
+
+主要是为了减少客户端这边的包大小,因为每一次打包发布的时候,包太多总是会影响效率。
+
+另外也是将客户端和服务端解耦,提高代码的可移植性。
+
+**同步调用与异步调用**
+
+什么是同步调用?什么是异步调用?
+
+同步调用就是客户端等待调用执行完成并返回结果。
+
+异步调用就是客户端不等待调用执行完成返回结果,不过依然可以通过回调函数等接收到返回结果的通知。如果客户端并不关心结果,则可以变成一个单向的调用。
+
+#### 流行的 RPC 框架
+
+目前流行的开源 RPC 框架还是比较多的。下面重点介绍三种:
+
+**①gRPC** 是 Google 最近公布的开源软件,基于最新的 HTTP2.0 协议,并支持常见的众多编程语言。
+
+我们知道 HTTP2.0 是基于二进制的 HTTP 协议升级版本,目前各大浏览器都在快马加鞭的加以支持。
+
+这个 RPC 框架是基于 HTTP 协议实现的,底层使用到了 Netty 框架的支持。
+
+**②Thrift** 是 Facebook 的一个开源项目,主要是一个跨语言的服务开发框架。它有一个代码生成器来对它所定义的 IDL 定义文件自动生成服务代码框架。
+
+用户只要在其之前进行二次开发就行,对于底层的 RPC 通讯等都是透明的。不过这个对于用户来说的话需要学习特定领域语言这个特性,还是有一定成本的。
+
+**③Dubbo** 是阿里集团开源的一个极为出名的 RPC 框架,在很多互联网公司和企业应用中广泛使用。协议和序列化框架都可以插拔是及其鲜明的特色。
+
+### HTTP 服务
+
+通常,我们的开发模式一直定性为 HTTP 接口开发,也就是我们常说的 RESTful 风格的服务接口。
+
+的确,对于在接口不多、系统与系统交互较少的情况下,解决信息孤岛初期常使用的一种通信手段;优点就是简单、直接、开发方便。
+
+利用现成的 HTTP 协议进行传输。
+
+平时的工作主要就是进行接口的开发,还要写一大份接口文档,严格地标明输入输出是什么?说清楚每一个接口的请求方法,以及请求参数需要注意的事项等。
+
+
+
+比如下面这个例子:
+
+```
+POST http://www.httpexample.com/restful/buyer/info/shar
+```
+
+接口可能返回一个 JSON 字符串或者是 XML 文档。然后客户端再去处理这个返回的信息,从而可以比较快速地进行开发。
+
+但是对于大型企业来说,内部子系统较多、接口非常多的情况下,RPC 框架的好处就显示出来了,首先就是长链接,不必每次通信都要像 HTTP 一样去 3 次握手什么的,减少了网络开销。
+
+其次就是 RPC 框架一般都有注册中心,有丰富的监控管理;发布、下线接口、动态扩展等,对调用方来说是无感知、统一化的操作。
+
+### 小结
+
+RPC 服务和 HTTP 服务还是存在很多的不同点的,一般来说,RPC 服务主要是针对大型企业的,而 HTTP 服务主要是针对小企业的,因为 RPC 效率更高,而 HTTP 服务开发迭代会更快。
+
+很多RPC框架包含了重试机制,路由策略,负载均衡策略,高可用策略,流量控制策略等等。如果应用进程之间只使用HTTP协议通信,显然是无法完成上述功能的。
+
+总之,选用什么样的框架不是按照市场上流行什么而决定的,而是要对整个项目进行完整地评估,从而在仔细比较两种开发框架对于整个项目的影响,最后再决定什么才是最适合这个项目的。
+
+一定不要为了使用 RPC 而每个项目都用 RPC,而是要因地制宜,具体情况具体分析。
\ No newline at end of file
diff --git a/docs/advance/excellent-article/16-what-is-jwt.md b/docs/advance/excellent-article/16-what-is-jwt.md
new file mode 100644
index 0000000..2f317c0
--- /dev/null
+++ b/docs/advance/excellent-article/16-what-is-jwt.md
@@ -0,0 +1,167 @@
+JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案。
+
+## 传统的session认证
+
+http协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再一次进行用户认证才行,因为根据http协议,我们并不能知道是哪个用户发出的请求,所以为了让我们的应用能识别是哪个用户发出的请求,我们只能在服务器存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为cookie,以便下次请求时发送给我们的应用,这样我们的应用就能识别请求来自哪个用户了,这就是传统的基于session认证。
+
+这种基于session的认证使应用本身很难得到扩展,随着不同客户端用户的增加,独立的服务器已无法承载更多的用户,而这时候基于session认证应用的问题就会暴露出来.
+
+#### 基于session认证所显露的问题
+
+**Session**: 每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。
+
+**扩展性**: 用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。
+
+**CSRF**: 因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。
+
+## 基于token的鉴权机制
+
+基于token的鉴权机制类似于http协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于token认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利。
+
+流程上是这样的:
+
+- 用户使用用户名密码来请求服务器
+- 服务器进行验证用户的信息
+- 服务器通过验证发送给用户一个token
+- 客户端存储token,并在每次请求时附送上这个token值
+- 服务端验证token值,并返回数据
+
+这个token必须要在每次请求时传递给服务端,它应该保存在请求头里, 另外,服务端要支持`CORS(跨来源资源共享)`策略,一般我们在服务端这么做就可以了`Access-Control-Allow-Origin: *`。
+
+## JWT长什么样?
+
+JWT是由三段信息构成的,将这三段信息文本用`.`链接一起就构成了Jwt字符串。就像这样:
+
+```css
+eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW
+```
+
+## JWT的构成
+
+第一部分我们称它为头部(header),第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),第三部分是签证(signature).
+
+### header
+
+jwt的头部承载两部分信息:
+
+- 声明类型,这里是jwt
+- 声明加密的算法 通常直接使用 HMAC SHA256
+
+完整的头部就像下面这样的JSON:
+
+```bash
+{
+ 'typ': 'JWT',
+ 'alg': 'HS256'
+}
+```
+
+然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分.
+
+```undefined
+eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
+```
+
+### playload
+
+载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分
+
+- 标准中注册的声明
+- 公共的声明
+- 私有的声明
+
+**标准中注册的声明** (建议但不强制使用) :
+
+- **iss**: jwt签发者
+- **sub**: jwt所面向的用户
+- **aud**: 接收jwt的一方
+- **exp**: jwt的过期时间,这个过期时间必须要大于签发时间
+- **nbf**: 定义在什么时间之前,该jwt都是不可用的.
+- **iat**: jwt的签发时间
+- **jti**: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
+
+**公共的声明** :
+ 公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.
+
+**私有的声明** :
+ 私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
+
+定义一个payload:
+
+```json
+{
+ "sub": "1234567890",
+ "name": "John Doe",
+ "admin": true
+}
+```
+
+然后将其进行base64加密,得到Jwt的第二部分。
+
+```undefined
+eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
+```
+
+### signature
+
+jwt的第三部分是一个签证信息,这个签证信息由三部分组成:
+
+- header (base64后的)
+- payload (base64后的)
+- secret
+
+这个部分需要base64加密后的header和base64加密后的payload使用`.`连接组成的字符串,然后通过header中声明的加密方式进行加盐`secret`组合加密,然后就构成了jwt的第三部分。
+
+```csharp
+// javascript
+var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
+
+var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
+```
+
+将这三部分用`.`连接成一个完整的字符串,构成了最终的jwt:
+
+```css
+ eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
+```
+
+**注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。**
+
+### 如何应用
+
+一般是在请求头里加入`Authorization`,并加上`Bearer`标注:
+
+```bash
+fetch('api/user/1', {
+ headers: {
+ 'Authorization': 'Bearer ' + token
+ }
+})
+```
+
+服务端会验证token,如果验证通过就会返回相应的资源。整个流程就是这样的:
+
+
+
+## 总结
+
+### 优点
+
+- 因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。
+- 因为有了payload部分,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。
+- 便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。
+- 它不需要在服务端保存会话信息, 所以它易于应用的扩展
+
+### 安全相关
+
+- 不应该在jwt的payload部分存放敏感信息,因为该部分是客户端可解密的部分。
+- 保护好secret私钥,该私钥非常重要。
+- 如果可以,请使用https协议
+
+
+
+## 参考链接
+
+- [什么是 JWT](https://www.jianshu.com/p/576dbf44b2ae)
+
+- [JSON Web Token 入门教程](https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html)
\ No newline at end of file
diff --git a/docs/advance/excellent-article/2-spring-transaction.md b/docs/advance/excellent-article/2-spring-transaction.md
new file mode 100644
index 0000000..d9fcf4f
--- /dev/null
+++ b/docs/advance/excellent-article/2-spring-transaction.md
@@ -0,0 +1,111 @@
+# @Transactional 事务注解详解
+
+## Spring事务的传播行为
+
+**先简单介绍一下Spring事务的传播行为:**
+
+所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在`TransactionDefinition`定义中括了如下几个表示传播行为的常量:
+
+- `TransactionDefinition.PROPAGATION_REQUIRED`:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
+- `TransactionDefinition.PROPAGATION_REQUIRES_NEW`:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
+- `TransactionDefinition.PROPAGATION_SUPPORTS`:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
+- `TransactionDefinition.PROPAGATION_NOT_SUPPORTED`:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
+- `TransactionDefinition.PROPAGATION_NEVER`:以非事务方式运行,如果当前存在事务,则抛出异常。
+- `TransactionDefinition.PROPAGATION_MANDATORY`:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
+- `TransactionDefinition.PROPAGATION_NESTED`:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于`TransactionDefinition.PROPAGATION_REQUIRED`。
+
+## Spring事务的回滚机制
+
+**然后说一下Spring事务的回滚机制:**
+
+Spring的AOP即声明式事务管理默认是针对`unchecked exception`回滚。Spring的事务边界是在调用业务方法之前开始的,业务方法执行完毕之后来执行`commit or rollback`(Spring默认取决于是否抛出`runtimeException`)。
+
+如果你在方法中有`try{}catch(Exception e){}`处理,那么try里面的代码块就脱离了事务的管理,若要事务生效需要在catch中`throw new RuntimeException ("xxxxxx");`这一点也是面试中会问到的事务失效的场景。
+
+## @Transactional注解实现原理
+
+再简单介绍一下`@Transactional`注解底层实现方式吧,毫无疑问,是通过动态代理,那么动态代理又分为JDK自身和CGLIB,这个也不多赘述了,毕竟今天的主题是如何将`@Transactional`对于事物的控制应用到炉火纯青。哈哈~
+
+第一点要注意的就是在`@Transactional`注解的方法中,再调用本类中的其他方法method2时,那么method2方法上的`@Transactional`注解是不!会!生!效!的!但是加上也并不会报错,拿图片简单帮助理解一下吧。这一点也是面试中会问到的事务失效的场景。
+
+
+
+通过代理对象在目标对象前后进行方法增强,也就是事务的开启提交和回滚。那么继续调用本类中其他方法是怎样呢,如下图:
+
+
+
+可见目标对象内部的自我调用,也就是通过this.指向的目标对象将不会执行方法的增强。
+
+先说第二点需要注意的地方,等下说如何解决上面第一点的问题。第二点就是`@Transactional`注解的方法必须是公共方法,就是必须是public修饰符!!!
+
+至于这个的原因,发表下个人的理解吧,因为JVM的动态代理是基于接口实现的,通过代理类将目标方法进行增强,想一下也是啦,没有权限访问那么你让我怎么进行,,,好吧,这个我也没有深入研究底层,个人理解个人理解。
+
+在这里我也放个问题吧,希望有高手可以回复指点指点我,因为JVM动态代理是基于接口实现的,那么是不是service层都要按照接口和实现类的开发模式,注解才会生效呢,就是说`controller`层直接调用没有接口的service层,加了注解也一样不起作用吧,这个懒了,没有测试,其一是因为没有人会这么开发吧,其二是我就认为是不起作用的,哈哈。
+
+**下面来解决一下第一点的问题,如何在方法中调用本类中其他方法呢。**
+
+通过`AopContext.currentProxy ()`获取到本类的代理对象,再去调用就好啦。因为这个是CGLIB实现,所以要开启AOP,当然也很简单,在springboot启动类上加上注解`@EnableAspectJAutoProxy(exposeProxy = true)`就可以啦,这个依赖大家自行搜一下就好啦。要注意,注意,代理对象调用的方法也要是public修饰符,否则方法中获取不到注入的bean,会报空指针错误。
+
+emmmm,我先把调用的方式和结果说下吧。自己简单写了代码,有点粗糙,就不要介意啦,嘿嘿。。。
+
+Controller中调用Service
+
+```
+@RestController
+public class TransactionalController {
+
+ @Autowired
+ private TransactionalService transactionalService;
+
+ @PostMapping("transactionalTest")
+ public void transacionalTest(){
+ transactionalService.transactionalMethod();
+ }
+}
+```
+
+Service中实现对事务的控制:接口
+
+```
+public interface TransactionalService {
+ void transactionalMethod();
+}
+```
+
+Service中实现对事务的控制:实现类(各种情况的说明都写在图片里了,这样方便阅读,有助于快速理解吧)
+
+
+
+
+
+上面两种情况不管使不使用代理调用方法1和方法2,方法`transactionalMethod`都处在一个事务中,四条更新操作全部失败。
+
+那么有人可能会有疑问了,在方法1和方法2上都加`@Transactional`注解呢?答案是结果和上面是一致的。
+
+小结只要方法`transactionalMethod`上有注解,并且方法1和方法2都处于当前事务中(不使用代理调用,方法1和方法2上的`@Transactional`注解是不生效的;使用代理,需要方法1和方法2都处在`transactionalMethod`方法的事务中,默认或者嵌套事务均可,当然也可以不加`@Transactional`注解),那么整体保持事务一致性。
+
+如果想要方法1和方法2均单独保持事务一致性怎么办呢,刚说过了,如果不是用代理调用`@Transactional`注解是不生效的,所以一定要使用代理调用实现,然后让方法1和方法2分别单独开启新的事务,便OK啦。下面摆上图片。
+
+
+
+
+
+这两种情况都是方法1和方法2均处在单独的事务中,各自保持事务的一致性。
+
+接下来进行进一步的优化,可以在`transactionalMethod`方法中分别对方法1和方法2进行控制。要将代码的艺术发挥到极致嘛,下面装逼开始。
+
+
+
+代码太长了,超过屏幕了,粘贴出来截的图,红框注释需要仔细看,希望不要影响你的阅读体验,至此,本篇关于`@Transactioinal`注解的使用就到此为止啦,
+
+简单总结一下吧:
+
+1、就是`@Transactional`注解保证的是每个方法处在一个事务,如果有try一定在catch中抛出运行时异常。
+
+2、方法必须是public修饰符。否则注解不会生效,但是加了注解也没啥毛病,不会报错,只是没卵用而已。
+
+3、this.本方法的调用,被调用方法上注解是不生效的,因为无法再次进行切面增强。
+
+
+
+> 原文:blog.csdn.net/fanxb92/article/details/81296005
\ No newline at end of file
diff --git a/docs/advance/excellent-article/3-springboot-auto-assembly.md b/docs/advance/excellent-article/3-springboot-auto-assembly.md
new file mode 100644
index 0000000..c8eb437
--- /dev/null
+++ b/docs/advance/excellent-article/3-springboot-auto-assembly.md
@@ -0,0 +1,544 @@
+# Spring Boot 自动装配原理
+
+首先,先看SpringBoot的主配置类:
+
+```
+@SpringBootApplication
+public class StartEurekaApplication
+{
+ public static void main(String[] args)
+ {
+ SpringApplication.run(StartEurekaApplication.class, args);
+ }
+}
+```
+
+点进@SpringBootApplication来看,发现@SpringBootApplication是一个组合注解。
+
+```
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+@SpringBootConfiguration
+@EnableAutoConfiguration
+@ComponentScan(excludeFilters = {
+ @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
+ @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
+public @interface SpringBootApplication {
+
+}
+```
+
+首先我们先来看 @SpringBootConfiguration:
+
+```
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Configuration
+public @interface SpringBootConfiguration {
+}
+```
+
+可以看到这个注解除了元注解以外,就只有一个@Configuration,那也就是说这个注解相当于@Configuration,所以这两个注解作用是一样的,它让我们能够去注册一些额外的Bean,并且导入一些额外的配置。
+
+那@Configuration还有一个作用就是把该类变成一个配置类,不需要额外的XML进行配置。所以@SpringBootConfiguration就相当于@Configuration。进入@Configuration,发现@Configuration核心是@Component,说明Spring的配置类也是Spring的一个组件。
+
+```
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Component
+public @interface Configuration {
+ @AliasFor(
+ annotation = Component.class
+ )
+ String value() default "";
+}
+```
+
+继续来看下一个@EnableAutoConfiguration,这个注解是开启自动配置的功能。
+
+```
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+@AutoConfigurationPackage
+@Import({AutoConfigurationImportSelector.class})
+public @interface EnableAutoConfiguration {
+ String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
+
+ Class>[] exclude() default {};
+
+ String[] excludeName() default {};
+}
+```
+
+可以看到它是由 @AutoConfigurationPackage,@Import(EnableAutoConfigurationImportSelector.class)这两个而组成的,我们先说@AutoConfigurationPackage,他是说:让包中的类以及子包中的类能够被自动扫描到spring容器中。
+
+```
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+@Import({Registrar.class})
+public @interface AutoConfigurationPackage {
+}
+```
+
+使用@Import来给Spring容器中导入一个组件 ,这里导入的是Registrar.class。来看下这个Registrar:
+
+```
+static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
+ Registrar() {
+ }
+
+ public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
+ AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
+ }
+
+ public Set