diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..6560a98
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 9467402..860f932 100644
--- a/README.md
+++ b/README.md
@@ -1,146 +1,16 @@
# 🔥 微信公众号 : 码上Java
-
-
-> 关注微信订阅号: 码上Java 回复关键字 : 1 领取后端面试大礼包(简历模板、面试题、Java、数据结构、算法、数据库、python、操作系统、网络等)
-
[](https://jq.qq.com/?_wv=1027&k=5HPYvQk)

[](https://msjavacoder.github.io/msJava)
[](https://msjavacoder.gitee.io/msjava)
-
+

-| ♨ | ⭕ | 🔐 | 💈 | 💻 | 🚏 | 🔭 | 🏖 | 📰 | 📮 | 🔍 | 🗽 | 🚀 | 🌈 | ☎ |
-| :-----------------------------: | :-------------: | :----------------------------: | :---------: | :-----------: | :---------------------: | --------------- | --------------- | :---------------------------------: | :-----------------: | :-----------------------: | :---------------: | ----------------- | :-----------------: | :---------------------: |
-| [Java核心基础](#♨-java核心基础) | [集合](#⭕-集合) | [多线程](#%f0%9f%94%90-多线程) | [IO](#💈-io) | [JVM](#💻-jvm) | [设计模式](#🚏-设计模式) | [网络](#🔭-网络) | [框架](#🏖-框架) | [数据结构与算法](#📰-数据结构与算法) | [数据库](#📰-数据库) | [算法](#📰-数据结构与算法) | [Redis](#🗽-redis) | [Linux](#🚀-linux) | [面试题](#🌈-面试题) | [联系作者](#☎-联系作者) |
-
----
-
-### ♨ Java核心基础
-
-- [理解基本数据类型与包装类](https://github.com/msJavaCoder/msJava/blob/master/docs/java/理解基本数据类型与包装类.md)
-- [理解类与Object](https://github.com/msJavaCoder/msJava/blob/master/docs/java/理解类与Object.md)
-- [理解泛型与迭代器](https://github.com/msJavaCoder/msJava/blob/master/docs/java/理解泛型与迭代器.md)
-- [理解Java关键字](https://github.com/msJavaCoder/msJava/blob/master/docs/java/Java关键字理解.md)
-- [理解字符串](https://github.com/msJavaCoder/msJava/blob/master/docs/java/深入理解字符串.md)
-- [浅克隆和深克隆](https://github.com/msJavaCoder/msJava/blob/master/docs/java/浅克隆和深克隆.md)
-- [理解动态代理](https://github.com/msJavaCoder/msJava/blob/master/docs/java//理解动态代理.md)
-- [理解抽象类与接口](https://github.com/msJavaCoder/msJava/blob/master/docs/java//理解抽象类与接口.md)
-- [理解异常处理](https://github.com/msJavaCoder/msJava/blob/master/docs/java//理解异常处理.md)
-- [理解IO](https://github.com/msJavaCoder/msJava/blob/master/docs/java//IO.md)
-- [理解数据结构队列](https://github.com/msJavaCoder/msJava/blob/master/docs/java//理解数据结构队列.md)
-- [理解内部类与枚举类](https://github.com/msJavaCoder/msJava/blob/master/docs/java//各种内部类和枚举类.md)
-- [理解克隆与序列化应用](https://github.com/msJavaCoder/msJava/blob/master/docs/java//理解克隆与序列化应用.md)
-
-
----
-
-### ⭕ 集合
-
-- [理解集合Collection](https://github.com/msJavaCoder/msJava/blob/master/docs/java/理解集合Collection.md)
-- [理解集合Map](https://github.com/msJavaCoder/msJava/blob/master/docs/java/理解集合Map.md)
-- [HashMap原理分析](https://github.com/msJavaCoder/msJava/blob/master/docs/java/HashMap原理分析.md)
-- [HashMap为什么是线程不安全的](https://github.com/msJavaCoder/msJava/blob/master/docs/java/HashMap为什么是线程不安全的.md)
-- [ConcurrentHashMap实现原理](https://github.com/msJavaCoder/msJava/blob/master/docs/java/ConcurrentHashMap实现原理.md)
-
---
-
-
-### 🔐 多线程
-- [理解线程与死锁](https://github.com/msJavaCoder/msJava/blob/master/docs/java/理解线程与死锁.md)
-- [理解Java中的各种锁](https://github.com/msJavaCoder/msJava/blob/master/docs/java/理解Java中的各种锁.md)
-- [理解ThreadLocal](https://github.com/msJavaCoder/msJava/blob/master/docs/java/理解ThreadLocal.md)
-- [理解synchronized关键字](https://github.com/msJavaCoder/msJava/blob/master/docs/java/理解synchronized关键字.md)
-- [理解线程安全synchronized与ReentrantLock](https://github.com/msJavaCoder/msJava/blob/master/docs/java/理解线程安全synchronized与ReentrantLock.md)
-- [理解线程池](https://github.com/msJavaCoder/msJava/blob/master/docs/java/理解线程池.md)
+
---
-
-### 💈 IO
-- [IO基础知识总结](https://github.com/msJavaCoder/msJava/blob/master/docs/java/IO基础知识总结.md)
----
-
-### 💻 JVM
-- [Java运行时内存划分](https://github.com/msJavaCoder/msJava/blob/master/docs/jvm/Java运行时内存划分.md)
-- [类加载机制](https://github.com/msJavaCoder/msJava/blob/master/docs/jvm/类加载机制.md)
-- [垃圾回收器](https://github.com/msJavaCoder/msJava/blob/master/docs/jvm/垃圾回收器.md)
-- [垃圾回收算法](https://github.com/msJavaCoder/msJava/blob/master/docs/jvm/垃圾回收算法.md)
-- [JVM确认可回收对象的方式](https://github.com/msJavaCoder/msJava/blob/master/docs/jvm/JVM确认可回收对象的方式.md)
-- [Java内存模型](https://github.com/msJavaCoder/msJava/blob/master/docs/jvm/Java内存模型.md)
-
----
-
-
-### 🚏 设计模式
-- [设计模式总结](https://github.com/msJavaCoder/msJava/blob/master/设计模式/设计模式总结.md)
-- [单例模式及Java实现](https://github.com/msJavaCoder/msJava/blob/master/设计模式/设计模式总结.md)
-- [工厂模式及Java实现](https://github.com/msJavaCoder/msJava/blob/master/设计模式/设计模式总结.md)
-- [抽象工长及Java实现](https://github.com/msJavaCoder/msJava/blob/master/设计模式/设计模式总结.md)
-- [代理模式及Java实现](https://github.com/msJavaCoder/msJava/blob/master/设计模式/设计模式总结.md)
-- [适配器模式及Java实现](https://github.com/msJavaCoder/msJava/blob/master/设计模式/设计模式总结.md)
-
----
-
-### 🔭 网络
-
-- [理解网络协议分层](https://github.com/msJavaCoder/msJava/blob/master/docs/network/理解网络协议分层.md)
-- [理解TCP和UDP](https://github.com/msJavaCoder/msJava/blob/master/docs/network/理解TCP和UDP.md)
-- [理解HTTP与HTTPS](https://github.com/msJavaCoder/msJava/blob/master/docs/network/理解HTTP与HTTPS.md)
-
----
-
-### 🏖 框架
-- [Spring](https://github.com/msJavaCoder/msJava/blob/master/docs/框架/Spring.md)
-- [SpringMVC](https://github.com/msJavaCoder/msJava/blob/master/docs/框架/SpringMVC.md)
-- [MyBatis](https://github.com/msJavaCoder/msJava/blob/master/docs/框架/MyBatis.md)
-- [SpringBoot](https://github.com/msJavaCoder/msJava/blob/master/docs/框架/SpringBoot.md)
----
-
-### 📰 数据库
-- [SQL进阶](https://github.com/msJavaCoder/msJava/blob/master/docs/network/SQL进阶.md)
-- [常见SQL优化方式](https://github.com/msJavaCoder/msJava/blob/master/docs/network/常见SQL优化方式.md)
-- [MySQL基础概念](https://github.com/msJavaCoder/msJava/blob/master/docs/network/MySQL.md)
-- [如何使用索引](https://github.com/msJavaCoder/msJava/blob/master/docs/network/如何使用索引.md)
-- [什么情况下索引会失效](https://github.com/msJavaCoder/msJava/blob/master/docs/network/什么情况下索引会失效.md)
-- [什么时候不需要创建索引](https://github.com/msJavaCoder/msJava/blob/master/docs/network/什么时候不需要创建索引.md)
-- [B树与B+树详谈](https://github.com/msJavaCoder/msJava/blob/master/docs/network/B树与B+树详谈.md)
-- [Hash索引与B+树索引的区别](https://github.com/msJavaCoder/msJava/blob/master/docs/network/Hash索引与B+树索引的区别.md)
-- [如何使用EXPLAIN查看执行计划](https://github.com/msJavaCoder/msJava/blob/master/docs/network/如何使用EXPLAIN查看执行计划.md)
----
-
-### 🗽 Redis
-
-- [Redis为什么会这么快](https://github.com/msJavaCoder/msJava/blob/master/docs/Redis/Redis为什么会这么快.md)
-
----
-
-### 🚀 Linux
-- [后端开发必备Linux基础命令总结](https://github.com/msJavaCoder/msJava/blob/master/Linux/后端开发必备Linux基础命令总结.md)
-
----
-
-### 📰 数据结构与算法
-- [排序算法汇总](https://github.com/msJavaCoder/msJava/blob/master/docs/算法/排序算法汇总.md)
-
----
-
-### 🌈 面试题
-- [Java核心面试题](https://github.com/msJavaCoder/msJava/blob/master/docs/面试题/Java核心面试题汇总.md)
-- [MySQL面试题汇总](https://github.com/msJavaCoder/msJava/blob/master/docs/面试题/MySQL面试题汇总.md)
-- [算法常用面试题汇总](https://github.com/msJavaCoder/msJava/blob/master/docs/面试题/算法常用面试题汇总.md)
-- [JVM面试题汇总](https://github.com/msJavaCoder/msJava/blob/master/docs/面试题/JVM面试题汇总.md)
-- [设计模式常见面试题汇总](https://github.com/msJavaCoder/msJava/blob/master/docs/面试题/设计模式常见面试题汇总.md)
-- [消息队列面试题汇总](https://github.com/msJavaCoder/msJava/blob/master/docs/面试题/消息队列面试题汇总.md)
-- [分布式框架面试题汇总](https://github.com/msJavaCoder/msJava/blob/master/docs/面试题/分布式框架面试题汇总.md)
-
----
-
-
### ☎ 联系作者
-
-> 关注微信订阅号: 码上Java 🔥🔥🔥
-
----
-
+> 关注微信订阅号: 码上Java 🔥🔥🔥
\ No newline at end of file
diff --git a/_coverpage.md b/_coverpage.md
index 9321db5..c1e5be2 100644
--- a/_coverpage.md
+++ b/_coverpage.md
@@ -1,13 +1,18 @@
-
Java后端面试核心知识体系
-祝您早日收获心仪offer😀
+
+
+JAVA后端核心知识体系
+风儿哪儿吹,不要问跟风的人。💯


-
+

-
[GitHub](https://github.com/msJavaCoder/msJava)
[开始阅读](#🔥-微信公众号-:-码上java)
+
+
+
+
\ No newline at end of file
diff --git a/_navbar.md b/_navbar.md
index e69de29..795d8fa 100644
--- a/_navbar.md
+++ b/_navbar.md
@@ -0,0 +1,39 @@
+ * Java
+ * [Java核心基础](docs/Java核心基础/README.md)
+ * [Java源码分析](docs/Java源码分析/README.md)
+ * [Java性能优化](docs/Java性能优化/README.md)
+ * [Java并发编程](docs/Java并发编程/README.md)
+ * [Java虚拟机](docs/Java虚拟机/README.md)
+
+ * 主流框架
+ * Spring
+ * SpringMVC
+ * MyBatis
+ * [SpringBoot](docs/SpringBoot/README.md)
+ * SpringCloud
+
+ * 数据库
+ * [MySQL](docs/MySQL/README.md)
+ * [Redis](docs/Redis/README.md)
+
+
+
+ * 数据结构与算法
+ * [排序算法](docs/算法与数据结构/排序算法.md)
+ * [数据结构](docs/算法与数据结构/README.md)
+ * [剑指offer](docs/算法与数据结构/剑指offer题解.md)
+
+ * 计算机基础
+ * 操作系统
+ * 计算机组成原理
+ * 计算机网络
+
+ * 项目实战
+ * 个人博客
+ * wiki知识库
+ * ···
+
+ * [面试题](docs/面试题/README.md)
+
+ * [首页](/)
+
diff --git a/_sidebar.md b/_sidebar.md
index ac55fff..63bbb3b 100644
--- a/_sidebar.md
+++ b/_sidebar.md
@@ -1,83 +1,47 @@
-* 网络
- * [理解网络协议分层](./docs/network/网络协议分层.md)
- * [理解TCP和UDP](./docs/network/理解TCP和UDP.md)
- * [理解HTTP和HTTPS](./docs/network/理解HTTP与HTTPS.md)
-
-* Java核心基础
- * [理解基本数据类型与包装类](./docs/java/理解基本数据类型与包装类.md)
- * [理解类与Object](./docs/java/理解类与Object.md)
- * [理解泛型与迭代器](./docs/java/理解泛型与迭代器.md)
- * [理解Java关键字](./docs/java/Java关键字理解.md)
- * [理解字符串](./docs/java/深入理解字符串.md)
- * [理解动态代理](./docs/java/理解动态代理.md)
- * [理解抽象类与接口](./docs/java/理解抽象类与接口.md)
- * [理解异常处理](./docs/java/理解异常处理.md)
- * [理解IO](./docs/java/IO.md)
- * [理解数据结构队列](./docs/java/理解数据结构队列.md)
- * [理解内部类与枚举类](./docs/java/各种内部类和枚举类.md)
- * [理解克隆与序列化应用](./docs/java/理解克隆与序列化应用.md)
-
-* 集合
- * [理解集合Collection](./docs/java/理解集合Collection.md)
- * [理解集合Map](./docs/java/理解集合Map.md)
- * [HashMap原理分析](./docs/java/HashMap原理分析.md)
- * [HashMap为什么是线程不安全的](./docs/java/HashMap为什么是线程不安全的.md)
- * [ConcurrentHashMap实现原理](./docs/java/ConcurrentHashMap实现原理.md)
-
-* Java源码分析
- * [ArrayList源码分析](./docs/java/ArrayList源码分析.md)
- * [LinkedList源码分析](./docs/java/LinkedList源码分析.md)
- * [LinkedHashMap源码分析](./docs/java/LinkedHashMap源码分析.md)
- * [HashMap源码分析](./docs/java/HashMap源码分析.md)
- * [TreeMap源码分析](./docs/java/TreeMap源码分析.md)
- * [ConcurrentHashMap源码分析](./docs/java/ConcurrentHashMap源码分析.md)
-
-
-* 并发编程
- * [理解线程与死锁](./docs/java/理解线程与死锁.md)
- * [理解ThreadLocal](./docs/java/理解ThreadLocal.md)
- * [理解synchronized关键字](./docs/java/理解synchronized关键字.md)
- * [理解线程安全synchronized与ReentrantLock](./docs/java/理解线程安全synchronized与ReentrantLock.md)
- * [理解Java中的各种锁](./docs/java/理解Java中的各种锁.md)
- * [理解线程池](./docs/java/理解线程池.md)
-
-* JVM
- * [垃圾回收器](./docs/jvm/垃圾回收器.md)
- * [垃圾回收算法](./docs/jvm/垃圾回收算法.md)
- * [类加载机制](./docs/jvm/类加载机制.md)
- * [Java内存模型](./docs/jvm/Java内存模型.md)
- * [Java运行时内存划分](./docs/jvm/Java运行时内存划分.md)
- * [JVM确认可回收对象的方式](./docs/jvm/JVM确认可回收对象的方式.md)
-
-* 框架
- * [Spring框架](./docs/框架/Spring.md)
- * [SpringMVC框架](./docs/框架/SpringMVC.md)
- * [MyBatis框架](./docs/框架/MyBatis.md)
- * [SpringBoot框架](./docs/框架/SpringBoot.md)
-
-* 数据库
- * [MySQL基础概念](./docs/database/MySQL.md)
- * [如何使用索引](./docs/database/如何使用索引.md)
- * [什么情况下索引会失效](./docs/database/什么情况下索引失效.md)
- * [什么时候不需要创建索引](./docs/database/什么时候不需要创建索引.md)
- * [B树与B+树详谈](./docs/database/B树与B+树详谈.md)
- * [Hash索引与B+树索引的区别](./docs/database/Hash索引与B+树索引的区别.md)
- * [如何使用EXPLAIN查看执行计划](./docs/database/如何使用EXPLAIN查看执行计划.md)
-
-* SQL
- * [SQL进阶](./docs/database/SQL进阶.md)
- * [常见SQL优化方式](./docs/database/常见SQL优化方式.md)
- * [SQL经典笔试题目](./docs/database/SQL经典笔试题目.md)
-
-* Redis
- * [Redis为什么会这么快](./docs/Redis/Redis为什么会这么快.md)
+
+* [**📕 数据结构与算法**](docs/算法与数据结构/README.md)
+
-* 面试题
- * [Java核心面试题汇总](./docs/Interviewquestions/Java核心面试题汇总.md)
- * [MySQL面试题汇总](./docs/Interviewquestions/MySQL面试题汇总.md)
- * [算法常用面试题汇总](./docs/Interviewquestions/算法常用面试题汇总.md)
- * [JVM面试题汇总](./docs/Interviewquestions/JVM面试题汇总.md)
- * [设计模式常见面试题汇总](./docs/Interviewquestions/设计模式常见面试题汇总.md)
- * [消息队列面试题汇总](./docs/Interviewquestions/消息队列面试题汇总.md)
- * [分布式框架面试题汇总](./docs/Interviewquestions/分布式框架面试题合集.md)
+---
+
+* [**💦 Java核心基础**](docs/Java核心基础/README.md)
+
+---
+
+* [**🙈 Java源码分析**](docs/Java源码分析/README.md)
+
+---
+
+* [**🐯 Java并发编程**](docs/Java并发编程/README.md)
+
+---
+
+* [**🐷 Java虚拟机**](docs/Java虚拟机/README.md)
+
+---
+
+* [**🌱 SpringBoot**](docs/SpringBoot/README.md)
+
+
+---
+
+* [**📚 MySQL**](docs/MySQL/README.md)
+
+---
+
+* **🌀 Redis**
+
+
+---
+
+* [**📑 设计模式**](docs/设计模式/README.md)
+
+---
+
+* [**👀 面试题**](docs/面试题/README.md)
+
+---
+
+* **🔨 杂谈**
+---
diff --git a/docs/J2EE.md b/docs/J2EE.md
deleted file mode 100644
index 55bbab9..0000000
--- a/docs/J2EE.md
+++ /dev/null
@@ -1,192 +0,0 @@
-## Servlet总结
-
-在Java Web程序中,**Servlet**主要负责接收用户请求 `HttpServletRequest`,在`doGet()`,`doPost()`中做相应的处理,并将回应`HttpServletResponse`反馈给用户。**Servlet** 可以设置初始化参数,供Servlet内部使用。一个Servlet类只会有一个实例,在它初始化时调用`init()`方法,销毁时调用`destroy()`方法**。**Servlet需要在web.xml中配置(MyEclipse中创建Servlet会自动配置),**一个Servlet可以设置多个URL访问**。**Servlet不是线程安全**,因此要谨慎使用类变量。
-
-## Servlet接口中有哪些方法及Servlet生命周期
-
-Servlet接口定义了5个方法,其中**前三个方法与Servlet生命周期相关**:
-
-- `void init(ServletConfig config) throws ServletException`
-- `void service(ServletRequest req, ServletResponse resp) throws ServletException, java.io.IOException`
-- `void destroy()`
-- `java.lang.String getServletInfo()`
-- `ServletConfig getServletConfig()`
-
-**生命周期:** **Web容器加载Servlet并将其实例化后,Servlet生命周期开始**,容器运行其**init()方法**进行Servlet的初始化;请求到达时调用Servlet的**service()方法**,service()方法会根据需要调用与请求对应的**doGet或doPost**等方法;当服务器关闭或项目被卸载时服务器会将Servlet实例销毁,此时会调用Servlet的**destroy()方法**。**init方法和destroy方法只会执行一次,service方法客户端每次请求Servlet都会执行**。Servlet中有时会用到一些需要初始化与销毁的资源,因此可以把初始化资源的代码放入init方法中,销毁资源的代码放入destroy方法中,这样就不需要每次处理客户端的请求都要初始化与销毁资源。
-
-参考:《javaweb整合开发王者归来》P81
-
-## get和post请求的区别
-
-可以把 get 和 post 当作两个不同的行为,两者并没有什么本质区别,底层都是 TCP 连接。 get请求用来从服务器上获得资源,而post是用来向服务器提交数据。比如你要获取人员列表可以用 get 请求,你需要创建一个人员可以用 post 。这也是 Restful API 最基本的一个要求。
-
-## 转发(Forward)和重定向(Redirect)的区别
-
-**转发是服务器行为,重定向是客户端行为。**
-
-**转发(Forward)** 通过RequestDispatcher对象的forward(HttpServletRequest request,HttpServletResponse response)方法实现的。RequestDispatcher可以通过HttpServletRequest 的getRequestDispatcher()方法获得。例如下面的代码就是跳转到login_success.jsp页面。
-
-```java
- request.getRequestDispatcher("login_success.jsp").forward(request, response);Copy to clipboardErrorCopied
-```
-
-**重定向(Redirect)** 是利用服务器返回的状态码来实现的。客户端浏览器请求服务器的时候,服务器会返回一个状态码。服务器通过 `HttpServletResponse` 的 `setStatus(int status)` 方法设置状态码。如果服务器返回301或者302,则浏览器会到新的网址重新请求该资源。
-
-1. **从地址栏显示来说**
-
-forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器.浏览器根本不知道服务器发送的内容从哪里来的,所以它的地址栏还是原来的地址. redirect是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的是新的URL.
-
-1. **从数据共享来说**
-
-forward:转发页面和转发到的页面可以共享request里面的数据. redirect:不能共享数据.
-
-1. **从运用地方来说**
-
-forward:一般用于用户登陆的时候,根据角色转发到相应的模块. redirect:一般用于用户注销登陆时返回主页面和跳转到其它的网站等
-
-1. 从效率来说
-
-forward:高. redirect:低.
-
-## Servlet与线程安全
-
-**Servlet不是线程安全的,多线程并发的读写会导致数据不同步的问题。** 解决的办法是尽量不要定义name属性,而是要把name变量分别定义在doGet()和doPost()方法内。虽然使用synchronized(name){}语句块可以解决问题,但是会造成线程的等待,不是很科学的办法。 注意:多线程的并发的读写Servlet类属性会导致数据不同步。但是如果只是并发地读取属性而不写入,则不存在数据不同步的问题。因此Servlet里的只读属性最好定义为final类型的。
-
-参考:《javaweb整合开发王者归来》P92
-
-## JSP有哪些内置对象、作用分别是什么
-
-JSP有9个内置对象:
-
-- request:封装客户端的请求,其中包含来自GET或POST请求的参数;
-- response:封装服务器对客户端的响应;
-- pageContext:通过该对象可以获取其他对象;
-- session:封装用户会话的对象;
-- application:封装服务器运行环境的对象;
-- out:输出服务器响应的输出流对象;
-- config:Web应用的配置对象;
-- page:JSP页面本身(相当于Java程序中的this);
-- exception:封装页面抛出异常的对象。
-
-## Request对象的主要方法有哪些
-
-- setAttribute(String name,Object):设置名字为name的request 的参数值
-- getAttribute(String name):返回由name指定的属性值
-- getAttributeNames():返回request 对象所有属性的名字集合,结果是一个枚举的实例
-- getCookies():返回客户端的所有 Cookie 对象,结果是一个Cookie 数组
-- getCharacterEncoding() :返回请求中的字符编码方式 = getContentLength() :返回请求的 Body的长度
-- getHeader(String name) :获得HTTP协议定义的文件头信息
-- getHeaders(String name) :返回指定名字的request Header 的所有值,结果是一个枚举的实例
-- getHeaderNames() :返回所以request Header 的名字,结果是一个枚举的实例
-- getInputStream() :返回请求的输入流,用于获得请求中的数据
-- getMethod() :获得客户端向服务器端传送数据的方法
-- getParameter(String name) :获得客户端传送给服务器端的有 name指定的参数值
-- getParameterNames() :获得客户端传送给服务器端的所有参数的名字,结果是一个枚举的实例
-- getParameterValues(String name):获得有name指定的参数的所有值
-- getProtocol():获取客户端向服务器端传送数据所依据的协议名称
-- getQueryString() :获得查询字符串
-- getRequestURI() :获取发出请求字符串的客户端地址
-- getRemoteAddr():获取客户端的 IP 地址
-- getRemoteHost() :获取客户端的名字
-- getSession([Boolean create]) :返回和请求相关 Session
-- getServerName() :获取服务器的名字
-- getServletPath():获取客户端所请求的脚本文件的路径
-- getServerPort():获取服务器的端口号
-- removeAttribute(String name):删除请求中的一个属性
-
-## request.getAttribute()和 request.getParameter()有何区别
-
-**从获取方向来看:**
-
-`getParameter()`是获取 POST/GET 传递的参数值;
-
-`getAttribute()`是获取对象容器中的数据值;
-
-**从用途来看:**
-
-`getParameter()`用于客户端重定向时,即点击了链接或提交按扭时传值用,即用于在用表单或url重定向传值时接收数据用。
-
-`getAttribute()` 用于服务器端重定向时,即在 sevlet 中使用了 forward 函数,或 struts 中使用了 mapping.findForward。 getAttribute 只能收到程序用 setAttribute 传过来的值。
-
-另外,可以用 `setAttribute()`,`getAttribute()` 发送接收对象.而 `getParameter()` 显然只能传字符串。 `setAttribute()` 是应用服务器把这个对象放在该页面所对应的一块内存中去,当你的页面服务器重定向到另一个页面时,应用服务器会把这块内存拷贝另一个页面所对应的内存中。这样`getAttribute()`就能取得你所设下的值,当然这种方法可以传对象。session也一样,只是对象在内存中的生命周期不一样而已。`getParameter()`只是应用服务器在分析你送上来的 request页面的文本时,取得你设在表单或 url 重定向时的值。
-
-**总结:**
-
-`getParameter()`返回的是String,用于读取提交的表单中的值;(获取之后会根据实际需要转换为自己需要的相应类型,比如整型,日期类型啊等等)
-
-`getAttribute()`返回的是Object,需进行转换,可用`setAttribute()`设置成任意对象,使用很灵活,可随时用
-
-## 讲解JSP中的四种作用域
-
-JSP中的四种作用域包括page、request、session和application,具体来说:
-
-- **page**代表与一个页面相关的对象和属性。
-- **request**代表与Web客户机发出的一个请求相关的对象和属性。一个请求可能跨越多个页面,涉及多个Web组件;需要在页面显示的临时数据可以置于此作用域。
-- **session**代表与某个用户与服务器建立的一次会话相关的对象和属性。跟某个用户相关的数据应该放在用户自己的session中。
-- **application**代表与整个Web应用程序相关的对象和属性,它实质上是跨越整个Web应用程序,包括多个页面、请求和会话的一个全局作用域。
-
-## 实现会话跟踪的技术有哪些
-
-1. **使用Cookie**
-
-向客户端发送Cookie
-
-```java
-Cookie c =new Cookie("name","value"); //创建Cookie
-c.setMaxAge(60*60*24); //设置最大时效,此处设置的最大时效为一天
-response.addCookie(c); //把Cookie放入到HTTP响应中Copy to clipboardErrorCopied
-```
-
-从客户端读取Cookie
-
-```java
-String name ="name";
-Cookie[]cookies =request.getCookies();
-if(cookies !=null){
- for(int i= 0;iCopy to clipboardErrorCopied
-```
-
-**优点:** Cookie被禁时可以使用
-
-**缺点:** 所有页面必须是表单提交之后的结果。
-
-1. HttpSession
-
-在所有会话跟踪技术中,HttpSession对象是最强大也是功能最多的。当一个用户第一次访问某个网站时会自动创建 HttpSession,每个用户可以访问他自己的HttpSession。可以通过HttpServletRequest对象的getSession方 法获得HttpSession,通过HttpSession的setAttribute方法可以将一个值放在HttpSession中,通过调用 HttpSession对象的getAttribute方法,同时传入属性名就可以获取保存在HttpSession中的对象。与上面三种方式不同的 是,HttpSession放在服务器的内存中,因此不要将过大的对象放在里面,即使目前的Servlet容器可以在内存将满时将HttpSession 中的对象移到其他存储设备中,但是这样势必影响性能。添加到HttpSession中的值可以是任意Java对象,这个对象最好实现了 Serializable接口,这样Servlet容器在必要的时候可以将其序列化到文件中,否则在序列化时就会出现异常。
-
-## Cookie和Session的的区别
-
-Cookie 和 Session都是用来跟踪浏览器用户身份的会话方式,但是两者的应用场景不太一样。
-
-**Cookie 一般用来保存用户信息** 比如1我们在 Cookie 中保存已经登录过得用户信息,下次访问网站的时候页面可以自动帮你登录的一些基本信息给填了;2一般的网站都会有保持登录也就是说下次你再访问网站的时候就不需要重新登录了,这是因为用户登录的时候我们可以存放了一个 Token 在 Cookie 中,下次登录的时候只需要根据 Token 值来查找用户即可(为了安全考虑,重新登录一般要将 Token 重写);3登录一次网站后访问网站其他页面不需要重新登录。**Session 的主要作用就是通过服务端记录用户的状态。** 典型的场景是购物车,当你要添加商品到购物车的时候,系统不知道是哪个用户操作的,因为 HTTP 协议是无状态的。服务端给特定的用户创建特定的 Session 之后就可以标识这个用户并且跟踪这个用户了。
-
-Cookie 数据保存在客户端(浏览器端),Session 数据保存在服务器端。
-
-Cookie 存储在客户端中,而Session存储在服务器上,相对来说 Session 安全性更高。如果使用 Cookie 的一些敏感信息不要写入 Cookie 中,最好能将 Cookie 信息加密然后使用到的时候再去服务器端解密。
\ No newline at end of file
diff --git "a/docs/Java345円267円245円347円250円213円345円270円210円345円277円205円351円241円273円346円216円214円346円217円241円347円232円204円347円237円245円350円257円206円.md" "b/docs/Java345円267円245円347円250円213円345円270円210円345円277円205円351円241円273円346円216円214円346円217円241円347円232円204円347円237円245円350円257円206円.md"
deleted file mode 100644
index dcee072..0000000
--- "a/docs/Java345円267円245円347円250円213円345円270円210円345円277円205円351円241円273円346円216円214円346円217円241円347円232円204円347円237円245円350円257円206円.md"
+++ /dev/null
@@ -1,33 +0,0 @@
-# Java 工程师必须掌握的知识呢
-
-## 1. Java 基础中的核心内容
-
-字符串和字符串常量池的深入理解、Array 的操作和排序算法、深克隆和浅克隆、各种 IO 操作、反射和动态代理(JDK 自身动态代理和 CGLIB)等。
-
-## 2. 集合
-
-集合和 String 是编程中最常用的数据类型,关于集合的知识也是面试备考的内容,它包含:链表(LinkedList)、TreeSet、栈(Stack)、队列(双端、阻塞、非阻塞队列、延迟队列)、HashMap、TreeMap 等,它们的使用和底层存储数据结构都是热门的面试内容。
-
-## 3. 多线程
-
-多线程使用和线程安全的知识也是必考的面试题目,它包括:死锁、6 种线程池的使用与差异、ThreadLocal、synchronized、Lock、JUC(java.util.concurrent包)、CAS(Compare and Swap)、ABA 问题等。
-
-## 4. 主流框架
-
-Spring、Spring MVC、MyBatis、SpringBoot
-
-## 5. 分布式编程
-
-消息队列(RabbitMQ、Kafka)、Dubbo、Zookeeper、SpringCloud 等。
-
-## 6. 数据库
-
-MySQL 常用引擎的掌握、MySQL 前缀索引、回表查询、数据存储结构、最左匹配原则、MySQL 的问题分析和排除方案、MySQL 读写分离的实现原理以及 MySQL 的常见优化方案等。 Redis 的使用场景、缓存雪崩和缓存穿透的解决方案、Redis 过期淘汰策略和主从复制的实现方案等。
-
-## 7.JVM虚拟机
-
-虚拟机的组成、垃圾回收算法、各种垃圾回收器的区别、Java 虚拟机分析工具的掌握、垃圾回收器的常用调优参数等。
-
-## 8. 其他
-
-常用算法的掌握、设计模式的理解、网络知识和常见 Linux 命令的掌握等。
\ No newline at end of file
diff --git "a/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/README.md" "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/README.md"
new file mode 100644
index 0000000..94e0a88
--- /dev/null
+++ "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/README.md"
@@ -0,0 +1,17 @@
+# Java并发编程
+ * [为什么说本质上实现线程的方法只有一种](docs/Java并发编程/为什么说本质上实现线程的方法只有一种.md)
+ * [可能会遇到的三类线程安全问题](docs/Java并发编程/可能会遇到的三类线程安全问题.md)
+ * [哪些场景需要额外注意线程安全问题](docs/Java并发编程/哪些场景需要额外注意线程安全问题.md)
+ * [理解Callable和Runnable的不同](docs/Java并发编程/理解Callable和Runnable的不同.md)
+ * [理解CAS优缺点](docs/Java并发编程/理解CAS优缺点.md)
+ * [理解Java中的各种锁](docs/Java并发编程/理解Java中的各种锁.md)
+ * [理解Java中的锁及其特点](docs/Java并发编程/理解Java中的锁及其特点.md)
+ * [理解JVM内存结构与Java内存模型](docs/Java并发编程/理解JVM内存结构与Java内存模型.md)
+ * [理解synchronized关键字](docs/Java并发编程/理解synchronized关键字.md)
+ * [理解ThreadLocal](docs/Java并发编程/理解ThreadLocal.md)
+ * [理解线程与死锁](docs/Java并发编程/理解线程与死锁.md)
+ * [理解线程安全synchronized与ReentrantLock](docs/Java并发编程/理解线程安全synchronized与ReentrantLock.md)
+ * [理解线程池](docs/Java并发编程/理解线程池.md)
+ * [理解线程池4种拒绝策略](docs/Java并发编程/理解线程池4种拒绝策略.md)
+ * [理解线程的状态及如何进行转换的](docs/Java并发编程/理解线程的状态及如何进行转换的.md)
+ * [第2章Java并发机制的底层原理](docs/Java并发编程/为什么说本质上实现线程的方法只有一种.md)
diff --git "a/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/_sidebar.md" "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/_sidebar.md"
new file mode 100644
index 0000000..5b75b0c
--- /dev/null
+++ "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/_sidebar.md"
@@ -0,0 +1,17 @@
+* **👉 Java并发编程** [↩](/README)
+ * [为什么说本质上实现线程的方法只有一种](docs/Java并发编程/为什么说本质上实现线程的方法只有一种.md)
+ * [可能会遇到的三类线程安全问题](docs/Java并发编程/可能会遇到的三类线程安全问题.md)
+ * [哪些场景需要额外注意线程安全问题](docs/Java并发编程/哪些场景需要额外注意线程安全问题.md)
+ * [理解Callable和Runnable的不同](docs/Java并发编程/理解Callable和Runnable的不同.md)
+ * [理解CAS优缺点](docs/Java并发编程/理解CAS优缺点.md)
+ * [理解Java中的各种锁](docs/Java并发编程/理解Java中的各种锁.md)
+ * [理解Java中的锁及其特点](docs/Java并发编程/理解Java中的锁及其特点.md)
+ * [理解JVM内存结构与Java内存模型](docs/Java并发编程/理解JVM内存结构与Java内存模型.md)
+ * [理解synchronized关键字](docs/Java并发编程/理解synchronized关键字.md)
+ * [理解ThreadLocal](docs/Java并发编程/理解ThreadLocal.md)
+ * [理解线程与死锁](docs/Java并发编程/理解线程与死锁.md)
+ * [理解线程安全synchronized与ReentrantLock](docs/Java并发编程/理解线程安全synchronized与ReentrantLock.md)
+ * [理解线程池](docs/Java并发编程/理解线程池.md)
+ * [理解线程池4种拒绝策略](docs/Java并发编程/理解线程池4种拒绝策略.md)
+ * [理解线程的状态及如何进行转换的](docs/Java并发编程/理解线程的状态及如何进行转换的.md)
+ * [第2章Java并发机制的底层原理](docs/Java并发编程/为什么说本质上实现线程的方法只有一种.md)
\ No newline at end of file
diff --git "a/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/344円270円272円344円273円200円344円271円210円350円257円264円346円234円254円350円264円250円344円270円212円345円256円236円347円216円260円347円272円277円347円250円213円347円232円204円346円226円271円346円263円225円345円217円252円346円234円211円344円270円200円347円247円215円.md" "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/344円270円272円344円273円200円344円271円210円350円257円264円346円234円254円350円264円250円344円270円212円345円256円236円347円216円260円347円272円277円347円250円213円347円232円204円346円226円271円346円263円225円345円217円252円346円234円211円344円270円200円347円247円215円.md"
new file mode 100644
index 0000000..aaa6efc
--- /dev/null
+++ "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/344円270円272円344円273円200円344円271円210円350円257円264円346円234円254円350円264円250円344円270円212円345円256円236円347円216円260円347円272円277円347円250円213円347円232円204円346円226円271円346円263円225円345円217円252円346円234円211円344円270円200円347円247円215円.md"
@@ -0,0 +1,172 @@
+# 👉 为什么说本质上实现线程的方法只有一种?
+
+> 在本课时我们主要学习为什么说本质上只有一种实现线程的方式?实现 Runnable 接口究竟比继承 Thread 类实现线程好在哪里?
+
+实现线程是并发编程中基础中的基础,因为我们必须要先实现多线程,才可以继续后续的一系列操作。所以本课时就先从并发编程的基础如何实现线程开始讲起,希望你能够夯实基础,虽然实现线程看似简单、基础,但实际上却暗藏玄机。首先,我们来看下为什么说本质上实现线程只有一种方式?
+
+实现线程的方式到底有几种?大部分人会说有 2 种、3 种或是 4 种,很少有人会说有 1 种。我们接下来看看它们具体指什么?2 种实现方式的描述是最基本的,也是最为大家熟知的,我们就先来看看 2 种线程实现方式的源码。
+
+## 实现 Runnable 接口
+
+```java
+public class RunnableThread implements Runnable {
+ @Override
+ public void run() {
+ System.out.println('用实现Runnable接口实现线程');
+ }
+}
+
+```
+
+第 1 种方式是通过实现 Runnable 接口实现多线程,如代码所示,首先通过 RunnableThread 类实现 Runnable 接口,然后重写 run() 方法,之后只需要把这个实现了 run() 方法的实例传到 Thread 类中就可以实现多线程。
+
+## 继承 Thread 类
+
+```java
+public class ExtendsThread extends Thread {
+ @Override
+ public void run() {
+ System.out.println('用Thread类实现线程');
+ }
+}
+
+```
+
+第 2 种方式是继承 Thread 类,如代码所示,与第 1 种方式不同的是它没有实现接口,而是继承 Thread 类,并重写了其中的 run() 方法。相信上面这两种方式你一定非常熟悉,并且经常在工作中使用它们。
+
+## 线程池创建线程
+
+那么为什么说还有第 3 种或第 4 种方式呢?我们先来看看第 3 种方式:通过线程池创建线程。线程池确实实现了多线程,比如我们给线程池的线程数量设置成 10,那么就会有 10 个子线程来为我们工作,接下来,我们深入解析线程池中的源码,来看看线程池是怎么实现线程的?
+
+```java
+static class DefaultThreadFactory implements ThreadFactory {
+
+ DefaultThreadFactory() {
+ SecurityManager s = System.getSecurityManager();
+ group = (s != null) ? s.getThreadGroup() :
+ Thread.currentThread().getThreadGroup();
+ namePrefix = "pool-" +
+ poolNumber.getAndIncrement() +
+ "-thread-";
+ }
+
+
+ public Thread newThread(Runnable r) {
+ Thread t = new Thread(group, r,
+ namePrefix + threadNumber.getAndIncrement(),
+0);
+
+ if (t.isDaemon())
+ t.setDaemon(false);
+ if (t.getPriority() != Thread.NORM_PRIORITY)
+ t.setPriority(Thread.NORM_PRIORITY);
+ return t;
+ }
+}
+
+```
+
+对于线程池而言,本质上是通过线程工厂创建线程的,默认采用 DefaultThreadFactory ,它会给线程池创建的线程设置一些默认值,比如:线程的名字、是否是守护线程,以及线程的优先级等。但是无论怎么设置这些属性,最终它还是通过 new Thread() 创建线程的 ,只不过这里的构造函数传入的参数要多一些,由此可以看出通过线程池创建线程并没有脱离最开始的那两种基本的创建方式,因为本质上还是通过 new Thread() 实现的。
+
+在面试中,如果你只是知道这种方式可以创建线程但不了解其背后的实现原理,就会在面试的过程中举步维艰,想更好的表现自己却给自己挖了"坑"。
+
+所以我们在回答线程实现的问题时,描述完前两种方式,可以进一步引申说"我还知道线程池和Callable 也是可以创建线程的,但是它们本质上也是通过前两种基本方式实现的线程创建。"这样的回答会成为面试中的加分项。然后面试官大概率会追问线程池的构成及原理,这部分内容会在后面的课时中详细分析。
+
+## 有返回值的 Callable 创建线程
+
+```java
+class CallableTask implements Callable {
+
+ @Override
+ public Integer call() throws Exception {
+ return new Random().nextInt();
+ }
+}
+
+//创建线程池
+ExecutorService service = Executors.newFixedThreadPool(10);
+//提交任务,并用 Future提交返回结果
+Future future = service.submit(new CallableTask());
+
+```
+
+第 4 种线程创建方式是通过有返回值的 Callable 创建线程,Runnable 创建线程是无返回值的,而 Callable 和与之相关的 Future、FutureTask,它们可以把线程执行的结果作为返回值返回,如代码所示,实现了 Callable 接口,并且给它的泛型设置成 Integer,然后它会返回一个随机数。
+
+但是,无论是 Callable 还是 FutureTask,它们首先和 Runnable 一样,都是一个任务,是需要被执行的,而不是说它们本身就是线程。它们可以放到线程池中执行,如代码所示, submit() 方法把任务放到线程池中,并由线程池创建线程,不管用什么方法,最终都是靠线程来执行的,而子线程的创建方式仍脱离不了最开始讲的两种基本方式,也就是实现 Runnable 接口和继承 Thread 类。
+
+## 其他创建方式
+
+#### 定时器 Timer
+
+```java
+class TimerThread extends Thread {
+//具体实现
+}
+```
+
+讲到这里你可能会说,我还知道一些其他的实现线程的方式。比如,定时器也可以实现线程,如果新建一个 Timer,令其每隔 10 秒或设置两个小时之后,执行一些任务,那么这时它确实也创建了线程并执行了任务,但如果我们深入分析定时器的源码会发现,本质上它还是会有一个继承自 Thread 类的 TimerThread,所以定时器创建线程最后又绕回到最开始说的两种方式。
+
+#### 其他方法
+
+```java
+/**
+ *描述:匿名内部类创建线程
+ */
+new Thread(new Runnable() {
+ @Override
+ public void run() {
+ System.out.println(Thread.currentThread().getName());
+ }
+}).start();
+
+}
+}
+```
+
+或许你还会说,我还知道一些其他方式,比如匿名内部类或 lambda 表达式方式,实际上,匿名内部类或 lambda 表达式创建线程,它们仅仅是在语法层面上实现了线程,并不能把它归结于实现多线程的方式,如匿名内部类实现线程的代码所示,它仅仅是用一个匿名内部类把需要传入的 Runnable 给实例出来。
+
+```java
+new Thread(() -> System.out.println(Thread.currentThread().getName())).start();
+
+}
+```
+
+我们再来看下 lambda 表达式方式。如代码所示,最终它们依然符合最开始所说的那两种实现线程的方式。
+
+## 实现线程只有一种方式
+
+关于这个问题,我们先不聚焦为什么说创建线程只有一种方式,先认为有两种创建线程的方式,而其他的创建方式,比如线程池或是定时器,它们仅仅是在 new Thread() 外做了一层封装,如果我们把这些都叫作一种新的方式,那么创建线程的方式便会千变万化、层出不穷,比如 JDK 更新了,它可能会多出几个类,会把 new Thread() 重新封装,表面上看又会是一种新的实现线程的方式,透过现象看本质,打开封装后,会发现它们最终都是基于 Runnable 接口或继承 Thread 类实现的。
+
+接下来,我们进行更深层次的探讨,为什么说这两种方式本质上是一种呢?
+
+```java
+@Override
+public void run() {
+ if (target != null) {
+ target.run();
+ }
+}
+
+```
+
+首先,启动线程需要调用 start() 方法,而 start() 方法最终还会调用 run() 方法,我们先来看看第一种方式中 run() 方法究竟是怎么实现的,可以看出 run() 方法的代码非常短小精悍,第 1 行代码 **if (target != null)** ,判断 target 是否等于 null,如果不等于 null,就执行第 2 行代码 target.run(),而 target 实际上就是一个 Runnable,即使用 Runnable 接口实现线程时传给Thread类的对象。
+
+然后,我们来看第二种方式,也就是继承 Thread 方式,实际上,继承 Thread 类之后,会把上述的 run() 方法重写,重写后 run() 方法里直接就是所需要执行的任务,但它最终还是需要调用 thread.start() 方法来启动线程,而 start() 方法最终也会调用这个已经被重写的 run() 方法来执行它的任务,这时我们就可以彻底明白了,事实上创建线程只有一种方式,就是构造一个 Thread 类,这是创建线程的唯一方式。
+
+我们上面已经了解了两种创建线程方式本质上是一样的,它们的不同点仅仅在于**实现线程运行内容的不同**,那么运行内容来自于哪里呢?
+
+运行内容主要来自于两个地方,要么来自于 target,要么来自于重写的 run() 方法,在此基础上我们进行拓展,可以这样描述:本质上,实现线程只有一种方式,而要想实现线程执行的内容,却有两种方式,也就是可以通过 实现 Runnable 接口的方式,或是继承 Thread 类重写 run() 方法的方式,把我们想要执行的代码传入,让线程去执行,在此基础上,如果我们还想有更多实现线程的方式,比如线程池和 Timer 定时器,只需要在此基础上进行封装即可。
+
+## 实现 Runnable 接口比继承 Thread 类实现线程要好
+
+下面我们来对刚才说的两种实现线程内容的方式进行对比,也就是为什么说实现 Runnable 接口比继承 Thread 类实现线程要好?好在哪里呢?
+
+首先,我们从代码的架构考虑,实际上,Runnable 里只有一个 run() 方法,它定义了需要执行的内容,在这种情况下,实现了 Runnable 与 Thread 类的解耦,Thread 类负责线程启动和属性设置等内容,权责分明。
+
+第二点就是在某些情况下可以提高性能,使用继承 Thread 类方式,每次执行一次任务,都需要新建一个独立的线程,执行完任务后线程走到生命周期的尽头被销毁,如果还想执行这个任务,就必须再新建一个继承了 Thread 类的类,如果此时执行的内容比较少,比如只是在 run() 方法里简单打印一行文字,那么它所带来的开销并不大,相比于整个线程从开始创建到执行完毕被销毁,这一系列的操作比 run() 方法打印文字本身带来的开销要大得多,相当于捡了芝麻丢了西瓜,得不偿失。如果我们使用实现 Runnable 接口的方式,就可以把任务直接传入线程池,使用一些固定的线程来完成任务,不需要每次新建销毁线程,大大降低了性能开销。
+
+第三点好处在于 Java 语言不支持双继承,如果我们的类一旦继承了 Thread 类,那么它后续就没有办法再继承其他的类,这样一来,如果未来这个类需要继承其他类实现一些功能上的拓展,它就没有办法做到了,相当于限制了代码未来的可拓展性。
+
+综上所述,我们应该优先选择通过实现 Runnable 接口的方式来创建线程。
+
+好啦,本课时的全部内容就讲完了,在这一课时我们主要学习了 通过 Runnable 接口和继承 Thread 类等几种方式创建线程,又详细分析了为什么说本质上只有一种实现线程的方式,以及实现 Runnable 接口究竟比继承 Thread 类实现线程好在哪里?学习完本课时相信你一定对创建线程有了更深入的理解。
\ No newline at end of file
diff --git "a/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/345円217円257円350円203円275円344円274円232円351円201円207円345円210円260円347円232円204円344円270円211円347円261円273円347円272円277円347円250円213円345円256円211円345円205円250円351円227円256円351円242円230円.md" "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/345円217円257円350円203円275円344円274円232円351円201円207円345円210円260円347円232円204円344円270円211円347円261円273円347円272円277円347円250円213円345円256円211円345円205円250円351円227円256円351円242円230円.md"
new file mode 100644
index 0000000..7a27b33
--- /dev/null
+++ "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/345円217円257円350円203円275円344円274円232円351円201円207円345円210円260円347円232円204円344円270円211円347円261円273円347円272円277円347円250円213円345円256円211円345円205円250円351円227円256円351円242円230円.md"
@@ -0,0 +1,172 @@
+# 👉 可能会遇到的三类线程安全问题
+
+>本文我们一起学习在实际工作中可能会遇到的三类线程安全问题。
+## 什么是线程安全
+要想弄清楚有哪 3 类线程安全问题,首先需要了解什么是线程安全,线程安全经常在工作中被提到,比如:你的对象不是线程安全的,你的线程发生了安全错误,虽然线程安全经常被提到,但我们可能对线程安全并没有一个明确的定义。
+
+《Java Concurrency In Practice》的作者 Brian Goetz 对线程安全是这样理解的,当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行问题,也不需要进行额外的同步,而调用这个对象的行为都可以获得正确的结果,那这个对象便是线程安全的。
+
+事实上,Brian Goetz 想表达的意思是,如果某个对象是线程安全的,那么对于使用者而言,在使用时就不需要考虑方法间的协调问题,比如不需要考虑不能同时写入或读写不能并行的问题,也不需要考虑任何额外的同步问题,比如不需要额外自己加 synchronized 锁,那么它才是线程安全的,可以看出对线程安全的定义还是非常苛刻的。
+
+而我们在实际开发中经常会遇到线程不安全的情况,那么一共有哪 3 种典型的线程安全问题呢?
+1. 运行结果错误;
+2. 发布和初始化导致线程安全问题;
+3. 活跃性问题
+### 运行结果错误
+首先,来看多线程同时操作一个变量导致的运行结果错误。
+```java
+public class WrongResult {
+
+ volatile static int i;
+
+ public static void main(String[] args) throws InterruptedException {
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ for (int j = 0; j < 10000; j++) { + i++; + } + } + }; + Thread thread1 = new Thread(r); + thread1.start(); + Thread thread2 = new Thread(r); + thread2.start(); + thread1.join(); + thread2.join(); + System.out.println(i); + } +} + + +``` +如代码所示,首先定义了一个 int 类型的静态变量 i,然后启动两个线程,分别对变量 i 进行 10000 次 i++ 操作。理论上得到的结果应该是 20000,但实际结果却远小于理论结果,比如可能是12996,也可能是13323,每次的结果都还不一样,这是为什么呢? + +是因为在多线程下,CPU 的调度是以时间片为单位进行分配的,每个线程都可以得到一定量的时间片。但如果线程拥有的时间片耗尽,它将会被暂停执行并让出 CPU 资源给其他线程,这样就有可能发生线程安全问题。比如 i++ 操作,表面上看只是一行代码,但实际上它并不是一个原子操作,它的执行步骤主要分为三步,而且在每步操作之间都有可能被打断。 + +- 第一个步骤是读取; +- 第二个步骤是增加; +- 第三个步骤是保存。那么我们接下来看如何发生的线程不安全问题。 + + + + +我们根据箭头指向依次看,线程 1 首先拿到 i=1 的结果,然后进行 i+1 操作,但此时 i+1 的结果并没有保存下来,线程 1 就被切换走了,于是 CPU 开始执行线程 2,它所做的事情和线程 1 是一样的 i++ 操作,但此时我们想一下,它拿到的 i 是多少?实际上和线程 1 拿到的 i 的结果一样都是 1,为什么呢?因为线程 1 虽然对 i 进行了 +1 操作,但结果没有保存,所以线程 2 看不到修改后的结果。 + +然后假设等线程 2 对 i 进行 +1 操作后,又切换到线程 1,让线程 1 完成未完成的操作,即将 i+1 的结果 2 保存下来,然后又切换到线程 2 完成 i=2 的保存操作,虽然两个线程都执行了对 i 进行 +1 的操作,但结果却最终保存了 i=2 的结果,而不是我们期望的 i=3,这样就发生了线程安全问题,导致了数据结果错误,这也是最典型的线程安全问题。 +### 发布和初始化导致线程安全问题 + +第二种是对象发布和初始化时导致的线程安全问题,我们创建对象并进行发布和初始化供其他类或对象使用是常见的操作,但如果我们操作的时间或地点不对,就可能导致线程安全问题。如代码所示: +```java +public class WrongInit { + + private Map students;
+
+ public WrongInit() {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ students = new HashMap();
+ students.put(1, "王小美");
+ students.put(2, "钱二宝");
+ students.put(3, "周三");
+ students.put(4, "赵四");
+ }
+ }).start();
+ }
+
+ public Map getStudents() {
+ return students;
+ }
+
+ public static void main(String[] args) throws InterruptedException {
+ WrongInit multiThreadsError6 = new WrongInit();
+ System.out.println(multiThreadsError6.getStudents().get(1));
+
+ }
+}
+
+```
+在类中,定义一个类型为 Map 的成员变量 students,Integer 是学号,String 是姓名。然后在构造函数中启动一个新线程,并在线程中为 students 赋值。
+
+- 学号:1,姓名:王小美;
+- 学号:2,姓名:钱二宝;
+- 学号:3,姓名:周三;
+- 学号:4,姓名:赵四。
+
+只有当线程运行完 run() 方法中的全部赋值操作后,4 名同学的全部信息才算是初始化完毕,可是我们看在主函数 mian() 中,初始化 WrongInit 类之后并没有进行任何休息就直接打印 1 号同学的信息,试想这个时候程序会出现什么情况?实际上会发生空指针异常。
+```java
+
+Exception in thread "main" java.lang.NullPointerException
+at lesson6.WrongInit.main(WrongInit.java:32)
+```
+## 活跃性问题
+第三种线程安全问题统称为活跃性问题,最典型的有三种,分别为死锁、活锁和饥饿。
+
+什么是活跃性问题呢,活跃性问题就是程序始终得不到运行的最终结果,相比于前面两种线程安全问题带来的数据错误或报错,活跃性问题带来的后果可能更严重,比如发生死锁会导致程序完全卡死,无法向下运行。
+### 死锁
+最常见的活跃性问题是死锁,死锁是指两个线程之间相互等待对方资源,但同时又互不相让,都想自己先执行,如代码所示。
+```java
+public class MayDeadLock {
+
+ Object o1 = new Object();
+ Object o2 = new Object();
+
+ public void thread1() throws InterruptedException {
+ synchronized (o1) {
+ Thread.sleep(500);
+ synchronized (o2) {
+ System.out.println("线程1成功拿到两把锁");
+ }
+ }
+ }
+
+ public void thread2() throws InterruptedException {
+ synchronized (o2) {
+ Thread.sleep(500);
+ synchronized (o1) {
+ System.out.println("线程2成功拿到两把锁");
+ }
+ }
+ }
+
+ public static void main(String[] args) {
+ MayDeadLock mayDeadLock = new MayDeadLock();
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mayDeadLock.thread1();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }).start();
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mayDeadLock.thread2();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }).start();
+ }
+}
+
+```
+首先,代码中创建了两个 Object 作为 synchronized 锁的对象,线程 1 先获取 o1 锁,sleep(500) 之后,获取 o2 锁;线程 2 与线程 1 执行顺序相反,先获取 o2 锁,sleep(500) 之后,获取 o1 锁。 假设两个线程几乎同时进入休息,休息完后,线程 1 想获取 o2 锁,线程 2 想获取 o1 锁,这时便发生了死锁,两个线程不主动调和,也不主动退出,就这样死死地等待对方先释放资源,导致程序得不到任何结果也不能停止运行。
+### 活锁
+第二种活跃性问题是活锁,活锁与死锁非常相似,也是程序一直等不到结果,但对比于死锁,活锁是活的,什么意思呢?因为正在运行的线程并没有阻塞,它始终在运行中,却一直得不到结果。
+
+举一个例子,假设有一个消息队列,队列里放着各种各样需要被处理的消息,而某个消息由于自身被写错了导致不能被正确处理,执行时会报错,可是队列的重试机制会重新把它放在队列头进行优先重试处理,但这个消息本身无论被执行多少次,都无法被正确处理,每次报错后又会被放到队列头进行重试,周而复始,最终导致线程一直处于忙碌状态,但程序始终得不到结果,便发生了活锁问题。
+### 饥饿
+第三个典型的活跃性问题是饥饿,饥饿是指线程需要某些资源时始终得不到,尤其是CPU 资源,就会导致线程一直不能运行而产生的问题。在 Java 中有线程优先级的概念,Java 中优先级分为 1 到 10,1 最低,10 最高。如果我们把某个线程的优先级设置为 1,这是最低的优先级,在这种情况下,这个线程就有可能始终分配不到 CPU 资源,而导致长时间无法运行。或者是某个线程始终持有某个文件的锁,而其他线程想要修改文件就必须先获取锁,这样想要修改文件的线程就会陷入饥饿,长时间不能运行。
+## 总结
+通过本文的学习我们知道了线程安全问题主要有 3 种,i++ 等情况导致的运行结果错误,通常是因为并发读写导致的,第二种是对象没有在正确的时间、地点被发布或初始化,而第三种线程安全问题就是活跃性问题,包括死锁、活锁和饥饿。
+
+
+
+
+
diff --git "a/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/345円223円252円344円272円233円345円234円272円346円231円257円351円234円200円350円246円201円351円242円235円345円244円226円346円263円250円346円204円217円347円272円277円347円250円213円345円256円211円345円205円250円351円227円256円351円242円230円.md" "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/345円223円252円344円272円233円345円234円272円346円231円257円351円234円200円350円246円201円351円242円235円345円244円226円346円263円250円346円204円217円347円272円277円347円250円213円345円256円211円345円205円250円351円227円256円351円242円230円.md"
new file mode 100644
index 0000000..a30ac4f
--- /dev/null
+++ "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/345円223円252円344円272円233円345円234円272円346円231円257円351円234円200円350円246円201円351円242円235円345円244円226円346円263円250円346円204円217円347円272円277円347円250円213円345円256円211円345円205円250円351円227円256円351円242円230円.md"
@@ -0,0 +1,73 @@
+# 👉 哪些场景需要额外注意线程安全问题
+
+> 本文我们一起学习在实际开发中哪些场景需要额外注意线程安全问题?
+## 1.访问共享变量或资源
+
+第一种场景是访问共享变量或共享资源的时候,典型的场景有访问共享对象的属性,访问 static 静态变量,访问共享的缓存,等等。因为这些信息不仅会被一个线程访问到,还有可能被多个线程同时访问,那么就有可能在并发读写的情况下发生线程安全问题。比如我们上一课时讲过的多线程同时 i++ 的例子:
+
+```java
+/**
+ * 描述: 共享的变量或资源带来的线程安全问题
+ */
+public class ThreadNotSafe1 {
+ static int i;
+ public static void main(String[] args) throws InterruptedException {
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ for (int j = 0; j < 10000; j++) { + i++; + } + } + }; + Thread thread1 = new Thread(r); + Thread thread2 = new Thread(r); + thread1.start(); + thread2.start(); + thread1.join(); + thread2.join(); + System.out.println(i); + } +} +``` +如上述代码所示,两个线程同时对 i 进行 i++ 操作,最后的输出可能是 15875 等小于20000的数,而不是我们期待的20000,这便是非常典型的共享变量带来的线程安全问题。 + +## 2.依赖时序的操作 + +第二个需要我们注意的场景是依赖时序的操作,如果我们操作的正确性是依赖时序的,而在多线程的情况下又不能保障执行的顺序和我们预想的一致,这个时候就会发生线程安全问题,如下面的代码所示: + +```java +if (map.containsKey(key)) { + map.remove(obj) +} +``` +代码中首先检查 map 中有没有 key 对应的元素,如果有则继续执行 remove 操作。此时,这个组合操作就是危险的,因为它是先检查后操作,而执行过程中可能会被打断。如果此时有两个线程同时进入 if() 语句,然后它们都检查到存在 key 对应的元素,于是都希望执行下面的 remove 操作,随后一个线程率先把 obj 给删除了,而另外一个线程它刚已经检查过存在 key 对应的元素,if 条件成立,所以它也会继续执行删除 obj 的操作,但实际上,集合中的 obj 已经被前面的线程删除了,这种情况下就可能导致线程安全问题。 + +类似的情况还有很多,比如我们先检查 x=1,如果 x=1 就修改 x 的值,代码如下所示: + +``` +if (x == 1) { + x = 7 * x; +} + +``` +这样类似的场景都是同样的道理,"检查与执行"并非原子性操作,在中间可能被打断,而检查之后的结果也可能在执行时已经过期、无效,换句话说,获得正确结果取决于幸运的时序。这种情况下,我们就需要对它进行加锁等保护措施来保障操作的原子性。 + +## 3.不同数据之间存在绑定关系 + +第三种需要我们注意的线程安全场景是不同数据之间存在相互绑定关系的情况。有时候,我们的不同数据之间是成组出现的,存在着相互对应或绑定的关系,最典型的就是 IP 和端口号。有时候我们更换了 IP,往往需要同时更换端口号,如果没有把这两个操作绑定在一起,就有可能出现单独更换了 IP 或端口号的情况,而此时信息如果已经对外发布,信息获取方就有可能获取一个错误的 IP 与端口绑定情况,这时就发生了线程安全问题。在这种情况下,我们也同样需要保障操作的原子性。 + +## 4.对方没有声明自己是线程安全的 + +第四种值得注意的场景是在我们使用其他类时,如果对方没有声明自己是线程安全的,那么这种情况下对其他类进行多线程的并发操作,就有可能会发生线程安全问题。举个例子,比如说我们定义了 ArrayList,它本身并不是线程安全的,如果此时多个线程同时对 ArrayList 进行并发读/写,那么就有可能会产生线程安全问题,造成数据出错,而这个责任并不在 ArrayList,因为它本身并不是并发安全的。 + +## 总结 + +本文中我们总结了在在存在并发读写的业务场景下,需要注意线程安全的四种常见场景。分别是访问共享变量或资源,依赖时序的操作,不同数据之间存在绑定关系,以及对方没有声明自己是线程安全的。 + + + + + + + diff --git "a/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円CAS344円274円230円347円274円272円347円202円271円.md" "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円CAS344円274円230円347円274円272円347円202円271円.md" new file mode 100644 index 0000000..1c3a819 --- /dev/null +++ "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円CAS344円274円230円347円274円272円347円202円271円.md" @@ -0,0 +1,47 @@ +# 👉 CAS有很多优点,但它的缺点呢? + +> 本文我们一起学习探讨CAS的缺点。
+
+在面试中,面试官常问是了解CAS吗?它有什么优点?你可能一听,这多简单,我会!于是你就开始噼里啪啦说可以避免加互斥锁,可以提高程序的运行效率等。
+
+但是CAS 的缺点,你知道吗?因为对于开发人员来说,对于任何一门技术或者某个知识点,我们都应该熟悉它的优缺点及适合的应用场景,这样才能写出更优雅且高效的代码了。
+
+首先,我们就来看一下 CAS 有哪几个主要的缺点。
+
+## ABA 问题
+**首先,CAS 最大的缺点就是 ABA 问题。**
+
+决定 CAS 是否进行 swap 的判断标准是"当前的值和预期的值是否一致",如果一致,就认为在此期间这个数值没有发生过变动,这在大多数情况下是没有问题的。
+
+但是在有的业务场景下,我们想确切知道从上一次看到这个值以来到现在,这个值是否发生过变化。例如,**这个值假设从 A 变成了 B,再由 B 变回了 A,此时,我们不仅认为它发生了变化,并且会认为它变化了两次。**
+
+在这种场景下,我们使用 CAS,就看不到这两次的变化,因为仅判断"当前的值和预期的值是否一致"就是不够的了。CAS 检查的并不是值有没有发生过变化,而是去比较这当前的值和预期值是不是相等,如果变量的值从旧值 A 变成了新值 B 再变回旧值 A,由于最开始的值 A 和现在的值 A 是相等的,所以 CAS 会认为变量的值在此期间没有发生过变化。所以,CAS 并不能检测出在此期间值是不是被修改过,它只能检查出现在的值和最初的值是不是一样。
+
+**那么如何解决这个问题呢?添加一个版本号就可以解决。**
+
+我们在变量值自身之外,再添加一个版本号,那么这个值的变化路径就从 A→B→A 变成了 1A→2B→3A,这样一来,就可以通过对比版本号来判断值是否变化过,这比我们直接去对比两个值是否一致要更靠谱,所以通过这样的思路就可以解决 ABA 的问题了。
+
+## 自旋时间过长
+
+**CAS 的第二个缺点就是自旋时间过长。**
+
+由于单次 CAS 不一定能执行成功,所以 CAS 往往是配合着循环来实现的,有的时候甚至是死循环,不停地进行重试,直到线程竞争不激烈的时候,才能修改成功。
+
+可是如果我们的应用场景本身就是高并发的场景,就有可能导致 CAS 一直都操作不成功,这样的话,循环时间就会越来越长。而且在此期间,CPU 资源也是一直在被消耗的,这会对性能产生很大的影响。所以这就要求我们,要根据实际情况来选择是否使用 CAS,在高并发的场景下,通常 CAS 的效率是不高的。
+
+## 范围不能灵活控制
+**CAS 的第三个缺点就是不能灵活控制线程安全的范围。**
+
+通常我们去执行 CAS 的时候,是针对某一个,而不是多个共享变量的,这个变量可能是 Integer 类型,也有可能是 Long 类型、对象类型等等,但是我们不能针对多个共享变量同时进行 CAS 操作,因为这多个变量之间是独立的,简单的把原子操作组合到一起,并不具备原子性。因此如果我们想对多个对象同时进行 CAS 操作并想保证线程安全的话,是比较困难的。
+
+有一个解决方案,那就是利用一个新的类,来整合刚才这一组共享变量,这个新的类中的多个成员变量就是刚才的那多个共享变量,然后再利用 atomic 包中的 AtomicReference 来把这个新对象整体进行 CAS 操作,这样就可以保证线程安全。
+
+相比之下,如果我们使用其他的线程安全技术,那么调整线程安全的范围就可能变得非常容易,比如我们用 synchronized 关键字时,如果想把更多的代码加锁,那么只需要把更多的代码放到同步代码块里面就可以了。
+
+## 总结
+本文中我们学习了 CAS 的三个缺点,分别是**ABA 问题、自旋时间过长以及线程安全的范围不能灵活控制**。
+
+## 干货分享
+**小伙伴们**关注【**码上Java**】微信公众号,回复关键字"**面试宝典**",领取一份**嘟嘟**平时收集的一些优质资源,包含我们代码侠必备的优质简历模板、面试题库、电子书等资源大礼包一份,助力**小伙伴们**早日收获**心仪offer**,遇见更好的自己~
+
+
diff --git "a/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円Callable345円222円214円Runnable347円232円204円344円270円215円345円220円214円.md" "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円Callable345円222円214円Runnable347円232円204円344円270円215円345円220円214円.md"
new file mode 100644
index 0000000..5d0f976
--- /dev/null
+++ "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円Callable345円222円214円Runnable347円232円204円344円270円215円345円220円214円.md"
@@ -0,0 +1,69 @@
+
+# 👉 Callable和Runnable的不同?
+
+> 本文我们一起学习 Callable 和 Runnable 的不同。
+
+## Runnable接口
+首先,我们先来看看Runnable有什么缺点?
+### 1.不能返回一个返回值
+第一个缺陷,对于 Runnable 而言,它不能返回一个返回值,虽然可以利用其他的一些办法,比如在 Runnable 方法中写入日志文件或者修改某个共享的对象的办法,来达到保存线程执行结果的目的,但这种解决问题的行为千曲百折,属于曲线救国,效率着实不高。
+
+实际上,在很多情况下执行一个子线程时,我们都希望能得到执行的任务的结果,也就是说,我们是需要得到返回值的,比如请求网络、查询数据库等。可是 Runnable 不能返回一个返回值,这是它第一个非常严重的缺陷。
+
+### 2. 不能抛出 checked Exception
+第二个缺陷就是不能抛出 checked Exception,如下面这段代码所示:
+
+```java
+public class RunThrowException {
+ /**
+ * 普通方法内可以 throw 异常,并在方法签名上声明 throws
+ */
+ public void normalMethod() throws Exception {
+ throw new IOException();
+ }
+ Runnable runnable = new Runnable() {
+ /**
+ * run方法上无法声明 throws 异常,且run方法内无法 throw 出 checked Exception,除非使用try catch进行处理
+ */
+ @Override
+ public void run() {
+ try {
+ throw new IOException();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+}
+```
+在这段代码中,有两个方法,第一个方法是一个普通的方法,叫作 normalMethod,可以看到,在它的方法签名中有 throws Exception,并且在它的方法内也 throw 了一个 new IOException()。
+
+然后在下面的的代码中,我们新建了一个 Runnable 对象,同时重写了它的 run 方法,我们没有办法在这个 run 方法的方法签名上声明 throws 一个异常出来。同时,在这个 run 方法里面也没办法 throw 一个 checked Exception,除非如代码所示,用 try catch 包裹起来,但是如果不用 try catch 是做不到的。
+
+这就是对于 Runnable 而言的两个重大缺陷。
+
+## Callable 接口
+Callable 是一个类似于 Runnable 的接口,实现 Callable 接口的类和实现 Runnable 接口的类都是可以被其他线程执行的任务。 我们看一下 Callable 的源码:
+
+```java
+public interface Callable {
+ V call() throws Exception;
+}
+```
+
+可以看出它也是一个 interface,并且它的 call 方法中已经声明了 throws Exception,前面还有一个 V 泛型的返回值,这就和之前的 Runnable 有很大的区别。实现 Callable 接口,就要实现 call 方法,这个方法的返回值是泛型 V,如果把 call 中计算得到的结果放到这个对象中,就可以利用 call 方法的返回值来获得子线程的执行结果了。
+
+## Callable 和 Runnable 的区别
+1. 方法名,Callable 规定的执行方法是 call(),而 Runnable 规定的执行方法是 run();
+2. 返回值,Callable 的任务执行后有返回值,而 Runnable 的任务执行后是没有返回值的;
+3. 抛出异常,call() 方法可抛出异常,而 run() 方法是不能抛出受检查异常的;
+和 Callable 配合的有一个 Future 类,通过 Future 可以了解任务执行情况,或者取消任务的执行,还可获取任务执行的结果,这些功能都是 Runnable 做不到的,Callable 的功能要比 Runnable 强大。
+
+## 总结
+本文中我们学习了 Runnable 的两个缺陷,第一个是没有返回值,第二个是不能抛出受检查异常;接下来分析了 Callable 接口,并且把 Callable 接口和 Runnable 接口的区别进行了对比和总结。
+
+
+
+
+
+
diff --git "a/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円JVM345円206円205円345円255円230円347円273円223円346円236円204円344円270円216円Java345円206円205円345円255円230円346円250円241円345円236円213円.md" "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円JVM345円206円205円345円255円230円347円273円223円346円236円204円344円270円216円Java345円206円205円345円255円230円346円250円241円345円236円213円.md"
new file mode 100644
index 0000000..7c8fcff
--- /dev/null
+++ "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円JVM345円206円205円345円255円230円347円273円223円346円236円204円344円270円216円Java345円206円205円345円255円230円346円250円241円345円236円213円.md"
@@ -0,0 +1,35 @@
+# 👉 不要在混淆JVM内存结构与Java内存模型了!
+
+> 本文我们一起学习什么是Java内存模型。
+
+在学习Java内存模型之前,我们简单了解一下什么JVM内存结构。
+
+## JVM内存结构
+
+Java 代码是要运行在虚拟机上的,而虚拟机在执行 Java 程序的过程中会把所管理的内存划分为若干个不同的数据区域,这些区域都有各自的用途。 JVM 运行时内存区域结构可分为以下 6 个区。
+
+1. 堆区(Heap):堆是存储类实例和数组的,通常是内存中最大的一块。实例很好理解,比如 new Object() 就会生成一个实例;而数组也是保存在堆上面的,因为在 Java 中,数组也是对象。
+
+2. 虚拟机栈(Java Virtual Machine Stacks):它保存局部变量和部分结果,并在方法调用和返回中起作用。
+
+3. 方法区(Method Area):它存储每个类的结构,例如运行时的常量池、字段和方法数据,以及方法和构造函数的代码,包括用于类初始化以及接口初始化的特殊方法。
+
+4. 本地方法栈(Native Method Stacks):与虚拟机栈基本类似,区别在于虚拟机栈为虚拟机执行的 Java 方法服务,而本地方法栈则是为 Native 方法服务。
+
+5. 程序计数器(The PC Register):是最小的一块内存区域,它的作用通常是保存当前正在执行的 JVM 指令地址。
+
+6. 运行时常量池(Run-Time Constant Pool):是方法区的一部分,包含多种常量,范围从编译时已知的数字到必须在运行时解析的方法和字段引用。
+
+## JMM内存模型是什么
+
+JMM其实是一种规范,是一种和多线程相关的一组规范,需要各个 JVM 的实现来遵守 JMM 规范,以便于开发者可以利用这些规范,更方便地开发多线程程序。这样一来,即便同一个程序在不同的虚拟机上运行,得到的程序结果也是一致的。
+
+如果没有 JMM 内存模型来规范,那么很可能在经过了不同 JVM 的"翻译"之后,导致在不同的虚拟机上运行的结果不一样,那是很大的问题。这也是为什么需要JMM的一个很重要的原因。
+
+因此,JMM 与处理器、缓存、并发、编译器有关。它解决了 CPU 多级缓存、处理器优化、指令重排等导致的结果不可预期的问题。
+## 总结
+本文我们学习了JVM内存结构与Java内存模型这两个容易混淆的概念,理解了JMM内存模型。其实之前我们使用了各种同步工具和关键字,包括 volatile、synchronized、Lock 等,其实它们的原理都涉及 JMM。正是 JMM 的参与和帮忙,才让各个同步工具和关键字能够发挥作用,帮我们开发出并发安全的程序。
+
+
+
+
diff --git "a/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円Java344円270円255円347円232円204円345円220円204円347円247円215円351円224円201円.md" "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円Java344円270円255円347円232円204円345円220円204円347円247円215円351円224円201円.md"
new file mode 100644
index 0000000..2a28a65
--- /dev/null
+++ "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円Java344円270円255円347円232円204円345円220円204円347円247円215円351円224円201円.md"
@@ -0,0 +1,247 @@
+# 👉 理解Java中的各种锁
+
+如果说快速理解多线程有什么捷径的话,那本文介绍的各种锁无疑是其中之一,它不但为我们开发多线程程序提供理论支持,还是面试中经常被问到的核心面试题之一。因此下面就让我们一起深入地学习一下这些锁吧。
+
+### 乐观锁和悲观锁
+
+悲观锁和乐观锁并不是某个具体的"锁"而是一种并发编程的基本概念。乐观锁和悲观锁最早出现在数据库的设计当中,后来逐渐被 Java 的并发包所引入。
+
+#### 悲观锁
+
+悲观锁认为对于同一个数据的并发操作,一定是会发生修改的,哪怕没有修改,也会认为修改。因此对于同一个数据的并发操作,悲观锁采取加锁的形式。悲观地认为,不加锁的并发操作一定会出问题。
+
+#### 乐观锁
+
+乐观锁正好和悲观锁相反,它获取数据的时候,并不担心数据被修改,每次获取数据的时候也不会加锁,只是在更新数据的时候,通过判断现有的数据是否和原数据一致来判断数据是否被其他线程操作,如果没被其他线程修改则进行数据更新,如果被其他线程修改则不进行数据更新。
+
+### 公平锁和非公平锁
+
+根据线程获取锁的抢占机制,锁又可以分为公平锁和非公平锁。
+
+#### 公平锁
+
+公平锁是指多个线程按照申请锁的顺序来获取锁。
+
+#### 非公平锁
+
+非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。
+ReentrantLock 提供了公平锁和非公平锁的实现。
+
+- 公平锁:new ReentrantLock(true)
+- 非公平锁:new ReentrantLock(false)
+
+如果构造函数不传任何参数的时候,默认提供的是非公平锁。
+
+### 独占锁和共享锁
+
+根据锁能否被多个线程持有,可以把锁分为独占锁和共享锁。
+
+#### 独占锁
+
+独占锁是指任何时候都只有一个线程能执行资源操作。
+
+#### 共享锁
+
+共享锁指定是可以同时被多个线程读取,但只能被一个线程修改。比如 Java 中的 ReentrantReadWriteLock 就是共享锁的实现方式,它允许一个线程进行写操作,允许多个线程读操作。
+
+ReentrantReadWriteLock 共享锁演示代码如下:
+
+```java
+public class ReadWriteLockTest {
+ public static void main(String[] args) throws InterruptedException {
+ final MyReadWriteLock rwLock = new MyReadWriteLock();
+ // 创建读锁 r1 和 r2
+ Thread r1 = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ rwLock.read();
+ }
+ }, "r1");
+ Thread r2 = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ rwLock.read();
+ }
+ }, "r2");
+ r1.start();
+ r2.start();
+ // 等待同时读取线程执行完成
+ r1.join();
+ r2.join();
+ // 开启写锁的操作
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ rwLock.write();
+ }
+ }, "w1").start();
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ rwLock.write();
+ }
+ }, "w2").start();
+ }
+ static class MyReadWriteLock {
+ ReadWriteLock lock = new ReentrantReadWriteLock();
+ public void read() {
+ try {
+ lock.readLock().lock();
+ System.out.println("读操作,进入 | 线程:" + Thread.currentThread().getName());
+ Thread.sleep(3000);
+ System.out.println("读操作,退出 | 线程:" + Thread.currentThread().getName());
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ } finally {
+ lock.readLock().unlock();
+ }
+ }
+ public void write() {
+ try {
+ lock.writeLock().lock();
+ System.out.println("写操作,进入 | 线程:" + Thread.currentThread().getName());
+ Thread.sleep(3000);
+ System.out.println("写操作,退出 | 线程:" + Thread.currentThread().getName());
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+ }
+}
+```
+
+以上程序执行结果如下:
+
+> 读操作,进入 | 线程:r1
+>
+> 读操作,进入 | 线程:r2
+>
+> 读操作,退出 | 线程:r1
+>
+> 读操作,退出 | 线程:r2
+>
+> 写操作,进入 | 线程:w1
+>
+> 写操作,退出 | 线程:w1
+>
+> 写操作,进入 | 线程:w2
+>
+> 写操作,退出 | 线程:w2
+
+### 可重入锁
+
+可重入锁指的是该线程获取了该锁之后,可以无限次的进入该锁锁住的代码。
+
+### 自旋锁
+
+自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗 CPU。
+
+### CAS 与 ABA
+
+CAS(Compare and Swap)比较并交换,是一种乐观锁的实现,是用非阻塞算法来代替锁定,其中 java.util.concurrent 包下的 AtomicInteger 就是借助 CAS 来实现的。
+但 CAS 也不是没有任何副作用,比如著名的 ABA 问题就是 CAS 引起的。
+
+#### ABA 问题描述
+
+老王去银行取钱,余额有 200 元,老王取 100 元,但因为程序的问题,启动了两个线程,线程一和线程二进行比对扣款,线程一获取原本有 200 元,扣除 100 元,余额等于 100 元,此时阿里给老王转账 100 元,于是启动了线程三抢先在线程二之前执行了转账操作,把 100 元又变成了 200 元,而此时线程二对比自己事先拿到的 200 元和此时经过改动的 200 元值一样,就进行了减法操作,把余额又变成了 100 元。这显然不是我们要的正确结果,我们想要的结果是余额减少了 100 元,又增加了 100 元,余额还是 200 元,而此时余额变成了 100 元,显然有悖常理,这就是著名的 ABA 的问题。
+
+执行流程如下。
+
+- 线程一:取款,获取原值 200 元,与 200 元比对成功,减去 100 元,修改结果为 100 元。
+- 线程二:取款,获取原值 200 元,阻塞等待修改。
+- 线程三:转账,获取原值 100 元,与 100 元比对成功,加上 100 元,修改结果为 200 元。
+- 线程二:取款,恢复执行,原值为 200 元,与 200 元对比成功,减去 100 元,修改结果为 100 元。
+
+最终的结果是 100 元。
+
+#### ABA 问题的解决
+
+常见解决 ABA 问题的方案加版本号,来区分值是否有变动。以老王取钱的例子为例,如果加上版本号,执行流程如下。
+
+- 线程一:取款,获取原值 200_V1,与 200_V1 比对成功,减去 100 元,修改结果为 100_V2。
+- 线程二:取款,获取原值 200_V1 阻塞等待修改。
+- 线程三:转账,获取原值 100_V2,与 100_V2 对比成功,加 100 元,修改结果为 200_V3。
+- 线程二:取款,恢复执行,原值 200_V1 与现值 200_V3 对比不相等,退出修改。
+
+最终的结果为 200 元,这显然是我们需要的结果。
+在程序中,要怎么解决 ABA 的问题呢?
+在 JDK 1.5 的时候,Java 提供了一个 AtomicStampedReference 原子引用变量,通过添加版本号来解决 ABA 的问题,具体使用示例如下:
+
+```java
+String name = "老王";
+String newName = "Java";
+AtomicStampedReference as = new AtomicStampedReference(name, 1);
+System.out.println("值:" + as.getReference() + " | Stamp:" + as.getStamp());
+as.compareAndSet(name, newName, as.getStamp(), as.getStamp() + 1);
+System.out.println("值:" + as.getReference() + " | Stamp:" + as.getStamp());
+```
+
+以上程序执行结果如下:
+
+> 值:老王 | Stamp:1
+>
+> 值:Java | Stamp:2
+
+### 相关面试题
+
+#### 1.synchronized 是哪种锁的实现?为什么?
+
+答:synchronized 是悲观锁的实现,因为 synchronized 修饰的代码,每次执行时会进行加锁操作,同时只允许一个线程进行操作,所以它是悲观锁的实现。
+
+#### 2.new ReentrantLock() 创建的是公平锁还是非公平锁?
+
+答:非公平锁,查看 ReentrantLock 的实现源码可知。
+
+```java
+/**
+ * Creates an instance of {@code ReentrantLock}.
+ * This is equivalent to using {@code ReentrantLock(false)}.
+ */
+public ReentrantLock() {
+ sync = new NonfairSync();
+}
+```
+
+#### 3.synchronized 使用的是公平锁还是非公平锁?
+
+答:synchronized 使用的是非公平锁,并且是不可设置的。这是因为非公平锁的吞吐量大于公平锁,并且是主流操作系统线程调度的基本选择,所以这也是 synchronized 使用非公平锁原由。
+
+#### 4.为什么非公平锁吞吐量大于公平锁?
+
+答:比如 A 占用锁的时候,B 请求获取锁,发现被 A 占用之后,堵塞等待被唤醒,这个时候 C 同时来获取 A 占用的锁,如果是公平锁 C 后来者发现不可用之后一定排在 B 之后等待被唤醒,而非公平锁则可以让 C 先用,在 B 被唤醒之前 C 已经使用完成,从而节省了 C 等待和唤醒之间的性能消耗,这就是非公平锁比公平锁吞吐量大的原因。
+
+#### 5.volatile 的作用是什么?
+
+答:volatile 是 Java 虚拟机提供的最轻量级的同步机制。
+当变量被定义成 volatile 之后,具备两种特性:
+
+- 保证此变量对所有线程的可见性,当一条线程修改了这个变量的值,修改的新值对于其他线程是可见的(可以立即得知的);
+- 禁止指令重排序优化,普通变量仅仅能保证在该方法执行过程中,得到正确结果,但是不保证程序代码的执行顺序。
+
+#### 6.volatile 对比 synchronized 有什么区别?
+
+答:synchronized 既能保证可见性,又能保证原子性,而 volatile 只能保证可见性,无法保证原子性。比如,i++ 如果使用 synchronized 修饰是线程安全的,而 volatile 会有线程安全的问题。
+
+#### 7.CAS 是如何实现的?
+
+答: CAS(Compare and Swap)比较并交换,CAS 是通过调用 JNI(Java Native Interface)的代码实现的,比如,在 Windows 系统 CAS 就是借助 C 语言来调用 CPU 底层指令实现的。
+
+#### 8.CAS 会产生什么问题?应该怎么解决?
+
+答:CAS 是标准的乐观锁的实现,会产生 ABA 的问题(详见正文)。
+ABA 通常的解决办法是添加版本号,每次修改操作时版本号加一,这样数据对比的时候就不会出现 ABA 的问题了。
+
+#### 9.以下说法错误的是?
+
+A:独占锁是指任何时候都只有一个线程能执行资源操作
+B:共享锁指定是可以同时被多个线程读取和修改
+C:公平锁是指多个线程按照申请锁的顺序来获取锁
+D:非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁
+答:B
+题目解析:共享锁指定是可以同时被多个线程读取,但只能被一个线程修改。
+
+### 总结
+
+本文介绍了 Java 中各种锁,明白了 Java 程序中比较常用的为非公平锁而非公平锁,原因在于非公平锁的吞吐量要更大,并且发生线程"饥饿"的情况很少,是风险远小于收益的事所以可以广而用之。又重点介绍了 CAS 和著名的 ABA 的问题,以及解决 ABA 的常见手段:添加版本号,可以通过 Java 自身提供的 AtomicStampedReference(原子引用变量)来解决 ABA 的问题,至此我们对 Java 多线程的了解又向前迈了一大步。
\ No newline at end of file
diff --git "a/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円Java344円270円255円347円232円204円351円224円201円345円217円212円345円205円266円347円211円271円347円202円271円.md" "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円Java344円270円255円347円232円204円351円224円201円345円217円212円345円205円266円347円211円271円347円202円271円.md"
new file mode 100644
index 0000000..657c578
--- /dev/null
+++ "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円Java344円270円255円347円232円204円351円224円201円345円217円212円345円205円266円347円211円271円347円202円271円.md"
@@ -0,0 +1,71 @@
+# 👉 Java中哪几种锁?分别有什么特点?
+> 本文中我们一起学习Java中锁的分类及其特点。
+
+锁是用来控制多线程访问共享资源的方式,一般的讲,一个锁能够防止多个线程同时访问共享资源。
+
+对于 Java 中的锁而言,根据分类标准我们把锁分为以下 7 大类别,分别是:
+
+1. 偏向锁/轻量级锁/重量级锁;
+
+2. 可重入锁/非可重入锁;
+
+3. 共享锁/独占锁;
+
+4. 公平锁/非公平锁;
+
+5. 悲观锁/乐观锁;
+
+6. 自旋锁/非自旋锁;
+
+7. 可中断锁/不可中断锁。
+
+## 1. 偏向锁/轻量级锁/重量级锁
+
+第一种分类是偏向锁/轻量级锁/重量级锁,这三种锁特指 synchronized 锁的状态,通过在对象头中的 mark word 来表明锁的状态。
+
+- 偏向锁
+
+如果自始至终,对于这把锁都不存在竞争,那么其实就没必要上锁,只需要打个标记就行了,这就是偏向锁的思想。一个对象被初始化后,还没有任何线程来获取它的锁时,那么它就是可偏向的,当有第一个线程来访问它并尝试获取锁的时候,它就将这个线程记录下来,以后如果尝试获取锁的线程正是偏向锁的拥有者,就可以直接获得锁,开销很小,性能最好。
+
+- 轻量级锁
+
+JVM 开发者发现在很多情况下,synchronized 中的代码是被多个线程交替执行的,而不是同时执行的,也就是说并不存在实际的竞争,或者是只有短时间的锁竞争,用 CAS 就可以解决,这种情况下,用完全互斥的重量级锁是没必要的。轻量级锁是指当锁原来是偏向锁的时候,被另一个线程访问,说明存在竞争,那么偏向锁就会升级为轻量级锁,线程会通过自旋的形式尝试获取锁,而不会陷入阻塞。
+
+- 重量级锁
+
+重量级锁是互斥锁,它是利用操作系统的同步机制实现的,所以开销相对比较大。当多个线程直接有实际竞争,且锁竞争时间长的时候,轻量级锁不能满足需求,锁就会膨胀为重量级锁。重量级锁会让其他申请却拿不到锁的线程进入阻塞状态。
+
+
+
+你可以发现锁升级的路径:无锁→偏向锁→轻量级锁→重量级锁。
+
+综上所述,偏向锁性能最好,可以避免执行 CAS 操作。而轻量级锁利用自旋和 CAS 避免了重量级锁带来的线程阻塞和唤醒,性能中等。重量级锁则会把获取不到锁的线程阻塞,性能最差。
+
+## 2. 可重入锁/非可重入锁
+
+第 2 个分类是可重入锁和非可重入锁。可重入锁指的是线程当前已经持有这把锁了,能在不释放这把锁的情况下,再次获取这把锁。同理,不可重入锁指的是虽然线程当前持有了这把锁,但是如果想再次获取这把锁,也必须要先释放锁后才能再次尝试获取。
+
+对于可重入锁而言,最典型的就是 ReentrantLock 了,正如它的名字一样,reentrant 的意思就是可重入,它也是 Lock 接口最主要的一个实现类。
+
+## 3.共享锁/独占锁
+
+第 3 种分类标准是共享锁和独占锁。共享锁指的是我们同一把锁可以被多个线程同时获得,而独占锁指的就是,这把锁只能同时被一个线程获得。我们的读写锁,就最好地诠释了共享锁和独占锁的理念。读写锁中的读锁,是共享锁,而写锁是独占锁。读锁可以被同时读,可以同时被多个线程持有,而写锁最多只能同时被一个线程持有。
+
+## 4.公平锁/非公平锁
+
+第 4 种分类是公平锁和非公平锁。公平锁的公平的含义在于如果线程现在拿不到这把锁,那么线程就都会进入等待,开始排队,在等待队列里等待时间长的线程会优先拿到这把锁,有先来先得的意思。而非公平锁就不那么"完美"了,它会在一定情况下,忽略掉已经在排队的线程,发生插队现象。
+
+## 5.悲观锁/乐观锁
+
+第 5 种分类是悲观锁,以及与它对应的乐观锁。悲观锁的概念是在获取资源之前,必须先拿到锁,以便达到"独占"的状态,当前线程在操作资源的时候,其他线程由于不能拿到锁,所以其他线程不能来影响我。而乐观锁恰恰相反,它并不要求在获取资源前拿到锁,也不会锁住资源;相反,乐观锁利用 CAS 理念,在不独占资源的情况下,完成了对资源的修改。
+
+## 6.自旋锁/非自旋锁
+
+第 6 种分类是自旋锁与非自旋锁。自旋锁的理念是如果线程现在拿不到锁,并不直接陷入阻塞或者释放 CPU 资源,而是开始利用循环,不停地尝试获取锁,这个循环过程被形象地比喻为"自旋",就像是线程在"自我旋转"。相反,非自旋锁的理念就是没有自旋的过程,如果拿不到锁就直接放弃,或者进行其他的处理逻辑,例如去排队、陷入阻塞等。
+
+## 7.可中断锁/不可中断锁
+
+第 7 种分类是可中断锁和不可中断锁。在 Java 中,synchronized 关键字修饰的锁代表的是不可中断锁,一旦线程申请了锁,就没有回头路了,只能等到拿到锁以后才能进行其他的逻辑处理。而我们的 ReentrantLock 是一种典型的可中断锁,例如使用 lockInterruptibly 方法在获取锁的过程中,突然不想获取了,那么也可以在中断之后去做其他的事情,不需要一直傻等到获取到锁才离开。
+
+## 总结
+本文中我们首先会对锁的分类有一个整体的概念,了解锁究竟有哪些分类标准。然后在后续的课程中,会对其中重要的锁进行详细讲解。
diff --git "a/docs/java/347円220円206円350円247円243円ThreadLocal.md" "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円ThreadLocal.md"
similarity index 99%
rename from "docs/java/347円220円206円350円247円243円ThreadLocal.md"
rename to "docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円ThreadLocal.md"
index b78f9b6..fc69799 100644
--- "a/docs/java/347円220円206円350円247円243円ThreadLocal.md"
+++ "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円ThreadLocal.md"
@@ -1,4 +1,4 @@
-## 理解ThreadLocal
+# 👉 理解ThreadLocal
**什么是 ThreadLocal?**
ThreadLocal 诞生于 JDK 1.2,用于解决多线程间的数据隔离问题。也就是说 ThreadLocal 会为每一个线程创建一个单独的变量副本。
diff --git "a/docs/java/347円220円206円350円247円243円synchronized345円205円263円351円224円256円345円255円227円.md" "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円synchronized345円205円263円351円224円256円345円255円227円.md"
similarity index 99%
rename from "docs/java/347円220円206円350円247円243円synchronized345円205円263円351円224円256円345円255円227円.md"
rename to "docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円synchronized345円205円263円351円224円256円345円255円227円.md"
index 779eda2..8b0626b 100644
--- "a/docs/java/347円220円206円350円247円243円synchronized345円205円263円351円224円256円345円255円227円.md"
+++ "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円synchronized345円205円263円351円224円256円345円255円227円.md"
@@ -1,4 +1,4 @@
-# synchronized关键字原理
+# 👉 synchronized关键字原理
众所周知 `synchronized` 关键字是解决并发问题常用解决方案,有以下三种使用方式:
diff --git "a/docs/java/347円220円206円350円247円243円347円272円277円347円250円213円344円270円216円346円255円273円351円224円201円.md" "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円347円272円277円347円250円213円344円270円216円346円255円273円351円224円201円.md"
similarity index 99%
rename from "docs/java/347円220円206円350円247円243円347円272円277円347円250円213円344円270円216円346円255円273円351円224円201円.md"
rename to "docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円347円272円277円347円250円213円344円270円216円346円255円273円351円224円201円.md"
index 8637caa..68ce72c 100644
--- "a/docs/java/347円220円206円350円247円243円347円272円277円347円250円213円344円270円216円346円255円273円351円224円201円.md"
+++ "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円347円272円277円347円250円213円344円270円216円346円255円273円351円224円201円.md"
@@ -1,4 +1,4 @@
-## 理解线程与死锁
+# 👉 理解线程与死锁
### 线程介绍
diff --git "a/docs/java/347円220円206円350円247円243円347円272円277円347円250円213円345円256円211円345円205円250円synchronized344円270円216円ReentrantLock.md" "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円347円272円277円347円250円213円345円256円211円345円205円250円synchronized344円270円216円ReentrantLock.md"
similarity index 99%
rename from "docs/java/347円220円206円350円247円243円347円272円277円347円250円213円345円256円211円345円205円250円synchronized344円270円216円ReentrantLock.md"
rename to "docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円347円272円277円347円250円213円345円256円211円345円205円250円synchronized344円270円216円ReentrantLock.md"
index 393737a..92af496 100644
--- "a/docs/java/347円220円206円350円247円243円347円272円277円347円250円213円345円256円211円345円205円250円synchronized344円270円216円ReentrantLock.md"
+++ "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円347円272円277円347円250円213円345円256円211円345円205円250円synchronized344円270円216円ReentrantLock.md"
@@ -1,4 +1,4 @@
-## 理解线程安全synchronized与ReentrantLock
+# 👉 理解线程安全synchronized与ReentrantLock
前面我们介绍了很多关于多线程的内容,在多线程中有一个很重要的课题需要我们攻克,那就是线程安全问题。线程安全问题指的是在多线程中,各线程之间因为同时操作所产生的数据污染或其他非预期的程序运行结果。
diff --git "a/docs/java/347円220円206円350円247円243円347円272円277円347円250円213円346円261円240円.md" "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円347円272円277円347円250円213円346円261円240円.md"
similarity index 83%
rename from "docs/java/347円220円206円350円247円243円347円272円277円347円250円213円346円261円240円.md"
rename to "docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円347円272円277円347円250円213円346円261円240円.md"
index 10407d1..39eb519 100644
--- "a/docs/java/347円220円206円350円247円243円347円272円277円347円250円213円346円261円240円.md"
+++ "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円347円272円277円347円250円213円346円261円240円.md"
@@ -71,7 +71,7 @@
| 4 | DiscardPolicy | 丢弃当前的线程任务而不做任何处理。如果系统允许在资源不足的情况相下丢弃部分任务,则这将是保障系统安全、稳定的一种很好的方案。 |
| 5 | 自定义拒绝策略 | 以上四种拒绝策略无法满足实际需要,用户可以扩展RejectedExecutionHandler接口实现拒绝策略。 |
-## 5.5种常用的线程池
+## 5.五种常用的线程池
| 序号 | 名称 | 说明 |
| :--: | :---------------------: | :--------------------------: |
@@ -81,8 +81,27 @@
| 4 | newSingleThreadExecutor | 单个线程的线程池 |
| 5 | newWorkStealingPool | 足够大小的线程池,JDK1.8新增 |
-## 6.线程池的好处
+## 6. 向线程池提交任务
+
+- execute()方法:当提交不需要返回值的任务,缺点无法判断任务是否被线程池执行成功;
+- submit()方法:当提交需要返回值的任务,此时线程池会返回一个future类型的对象,通过该对象可以判断线程池是否执行成功,并且可以通过调用该对象的get()方法获取返回值;
+
+## 7. 关闭线程池
+
+ 关闭线程池可以调用shutdown或者shutdownNow方法。区别如下:
+
+- 共同点:
+ - 它们的原理都是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程( 无法响应中断的任务可能永远无法终止)。
+- 不同点:
+ - shutdownNow首先将线程池的状态设置为STOP,然后尝试停止所有的正在执行或暂停任务的线程。
+ - shutdown只是将线程池的状态设置成SHOTDOWN状态,然后中断所有没有正在执行任务的线程。
+
+**总结:通常调用shutdown方法来关闭线程池,如果任务不一定要执行完,则可以调用shutdownNow方法。**
+
+## 总结
+
+**线程池的好处**
+ 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁的消耗。
+ 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
-+ 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
\ No newline at end of file
++ 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
\ No newline at end of file
diff --git "a/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円347円272円277円347円250円213円346円261円2404円347円247円215円346円213円222円347円273円235円347円255円226円347円225円245円.md" "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円347円272円277円347円250円213円346円261円2404円347円247円215円346円213円222円347円273円235円347円255円226円347円225円245円.md"
new file mode 100644
index 0000000..90500ee
--- /dev/null
+++ "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円347円272円277円347円250円213円346円261円2404円347円247円215円346円213円222円347円273円235円347円255円226円347円225円245円.md"
@@ -0,0 +1,34 @@
+# 👉 线程池的拒绝策略
+
+> 本文我们一起学习线程池有哪 4 种默认的拒绝策略。
+
+若线程池中的核心线程数被用完且阻塞队列已排满,则此时线程池的资源已耗尽,线程池将没有足够的线程资源执行新的任务。为了保证操作系统的安全,线程池将通过拒绝策略处理新添加的线程任务。
+
+## 1. AbortPolicy
+第一种拒绝策略是 `AbortPolicy`,这种拒绝策略在拒绝任务时,会直接抛出一个类型为 RejectedExecutionException的RuntimeException,让你感知到任务被拒绝了,于是你便可以根据业务逻辑选择重试或者放弃提交等策略。
+
+
+## 2.DiscardPolicy
+第2种拒绝策略是 `DiscardPolicy`,这种拒绝策略正如它的名字所描述的一样,当新任务被提交后直接被丢弃掉,也不会给你任何的通知,相对而言存在一定的风险,因为我们提交的时候根本不知道这个任务会被丢弃,可能造成数据丢失。
+
+## 3.DiscardOldestPolicy
+第3种拒绝策略是 `DiscardOldestPolicy`,如果线程池没被关闭且没有能力执行,则会丢弃任务队列中的头结点,通常是存活时间最长的任务,这种策略与第二种不同之处在于它丢弃的不是最新提交的,而是队列中存活时间最长的,这样就可以腾出空间给新提交的任务,但同理它也存在一定的数据丢失风险。
+
+## 4.CallerRunsPolicy
+第4种拒绝策略是 CallerRunsPolicy,相对而言它就比较完善了,当有新任务提交后,如果线程池没被关闭且没有能力执行,则把这个任务交于提交任务的线程执行,也就是谁提交任务,谁就负责执行任务。这样做主要有两点好处。
+
+1. 第一点新提交的任务不会被丢弃,这样也就不会造成业务损失。
+2. 第二点好处是,由于谁提交任务谁就要负责执行任务,这样提交任务的线程就得负责执行任务,而执行任务又是比较耗时的,在这段期间,提交任务的线程被占用,也就不会再提交新的任务,减缓了任务提交的速度,相当于是一个负反馈。在此期间,线程池中的线程也可以充分利用这段时间来执行掉一部分任务,腾出一定的空间,相当于是给了线程池一定的缓冲期。
+## 总结
+
+本文中我们学习线程池中的4 种默认的拒绝策略。线程池会在以下两种情况下会拒绝新提交的任务。
+- 第一种情况是当我们调用 shutdown 等方法关闭线程池后,即便此时可能线程池内部依然有没执行完的任务正在执行,但是由于线程池已经关闭,此时如果再向线程池内提交任务,就会遭到拒绝。
+- 第二种情况是线程池没有能力继续处理新提交的任务,也就是工作已经非常饱和的时候。
+
+
+
+
+
+
+
+
diff --git "a/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円347円272円277円347円250円213円347円232円204円347円212円266円346円200円201円345円217円212円345円246円202円344円275円225円350円277円233円350円241円214円350円275円254円346円215円242円347円232円204円.md" "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円347円272円277円347円250円213円347円232円204円347円212円266円346円200円201円345円217円212円345円246円202円344円275円225円350円277円233円350円241円214円350円275円254円346円215円242円347円232円204円.md"
new file mode 100644
index 0000000..1612d62
--- /dev/null
+++ "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円220円206円350円247円243円347円272円277円347円250円213円347円232円204円347円212円266円346円200円201円345円217円212円345円246円202円344円275円225円350円277円233円350円241円214円350円275円254円346円215円242円347円232円204円.md"
@@ -0,0 +1,75 @@
+# 👉 理解线程的状态及如何进行转换的
+> 本文我们一起学习线程的状态有哪些以及它们之间是如何进行转换的?
+
+线程(Thread)是并发编程的基础,也是程序执行的最小单元,它依托进程而存在。一个进程中可以包含多个线程,多线程可以共享一块内存空间和一组系统资源,因此线程之间的切换更加节省资源、更加轻量化,也因此被称为轻量级的进程。
+
+## 线程状态六君子
+
+线程的状态在 JDK 1.5 之后以枚举的方式被定义在 Thread 的源码中,它总共包含以下 6 个状态:
+1. NEW,新建状态,线程被创建出来,但尚未启动时的线程状态;
+2. RUNNABLE,就绪状态,表示可以运行的线程状态,它可能正在运行,或者是在排队等待操作系统给它分配 CPU 资源;
+3. BLOCKED,阻塞等待锁的线程状态,表示处于阻塞状态的线程正在等待监视器锁,比如等待执行 synchronized 代码块或者使用 synchronized 标记的方法;
+4. WAITING,等待状态,一个处于等待状态的线程正在等待另一个线程执行某个特定的动作,比如,一个线程调用了 Object.wait() 方法,那它就在等待另一个线程调用 Object.notify() 或 Object.notifyAll() 方法;
+5. TIMED_WAITING,计时等待状态,和等待状态(WAITING)类似,它只是多了超时时间,比如调用了有超时时间设置的方法 Object.wait(long timeout) 和 Thread.join(long timeout) 等这些方法时,它才会进入此状态;
+6. TERMINATED,终止状态,表示线程已经执行完成。
+
+## 揭露源码
+
+```java
+public enum State {
+ /**
+ * 新建状态,线程被创建出来,但尚未启动时的线程状态
+ */
+ NEW,
+
+ /**
+ * 就绪状态,表示可以运行的线程状态,但它在排队等待来自操作系统的 CPU 资源
+ */
+ RUNNABLE,
+
+ /**
+ * 阻塞等待锁的线程状态,表示正在处于阻塞状态的线程
+ * 正在等待监视器锁,比如等待执行 synchronized 代码块或者
+ * 使用 synchronized 标记的方法
+ */
+ BLOCKED,
+
+ /**
+ * 等待状态,一个处于等待状态的线程正在等待另一个线程执行某个特定的动作。
+ * 例如,一个线程调用了 Object.wait() 它在等待另一个线程调用
+ * Object.notify() 或 Object.notifyAll()
+ */
+ WAITING,
+
+ /**
+ * 计时等待状态,和等待状态 (WAITING) 类似,只是多了超时时间,比如
+ * 调用了有超时时间设置的方法 Object.wait(long timeout) 和
+ * Thread.join(long timeout) 就会进入此状态
+ */
+ TIMED_WAITING,
+
+ /**
+ * 终止状态,表示线程已经执行完成
+ */
+}
+
+```
+线程的工作模式是,首先先要创建线程并指定线程需要执行的业务方法,然后再调用线程的 start() 方法,此时线程就从 NEW(新建)状态变成了 RUNNABLE(就绪)状态,此时线程会判断要执行的方法中有没有 synchronized 同步代码块,如果有并且其他线程也在使用此锁,那么线程就会变为 BLOCKED(阻塞等待)状态,当其他线程使用完此锁之后,线程会继续执行剩余的方法。
+
+当遇到 Object.wait() 或 Thread.join() 方法时,线程会变为 WAITING(等待状态)状态,如果是带了超时时间的等待方法,那么线程会进入 TIMED_WAITING(计时等待)状态,当有其他线程执行了 notify() 或 notifyAll() 方法之后,线程被唤醒继续执行剩余的业务方法,直到方法执行完成为止,此时整个线程的流程就执行完了,执行流程如下图所示:
+
+
+
+
+
+## 总结
+
+本文中我们学习了线程中的六种状态,这个问题一般出现在面试的起始问题上,由此逐渐延伸更多的并发编程的面试问题,考验面试者的并发编程掌握程度。
+
+
+
+
+
+
+
+
diff --git "a/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円254円2542円347円253円240円Java345円271円266円345円217円221円346円234円272円345円210円266円347円232円204円345円272円225円345円261円202円345円216円237円347円220円206円.md" "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円254円2542円347円253円240円Java345円271円266円345円217円221円346円234円272円345円210円266円347円232円204円345円272円225円345円261円202円345円216円237円347円220円206円.md"
new file mode 100644
index 0000000..07f6efa
--- /dev/null
+++ "b/docs/Java345円271円266円345円217円221円347円274円226円347円250円213円/347円254円2542円347円253円240円Java345円271円266円345円217円221円346円234円272円345円210円266円347円232円204円345円272円225円345円261円202円345円216円237円347円220円206円.md"
@@ -0,0 +1,34 @@
+# 第2章 Java并发机制的底层原理
+
+## 1. valatile关键字
+
+`valatile`是轻量级的`synchronized`,在多处理器开发中保证了共享变量的`可见性`。
+
+> 可见性是指当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。
+
+如果`valatile`关键字使用恰到,它的使用和执行成本会比`synchronized`底,因为它不会引起线程上下文的切换和调度。 如果一个字段被声明成`valatile`,Java线程内存模型确保所有线程看到这个变量的值是一致的。
+
+## 2. synchronized关键字
+
+Java中的每一个对象都可以作为锁。
+
+- 对于普通同步方法,锁的是当前实例对象。
+- 对于静态同步方法,锁的是当前类的Class对象。
+- 对于同步方法块,锁的是`synchronized`括号里面配置的对象。
+
+当一个线程试图访问同步代码块时候,首先线程必须得到锁,退出或抛出异常时必须释放锁。
+
+## 3. Java对象头
+
+`synchronized`用的锁是存在Java对象头里面的,如果对象是数组类型,则虚拟机用3个字宽存储对象头,如果对象是非数组类型,则用2字宽存储对象头。
+
+## 4. Java中的锁
+
+**锁的优缺点总结**
+
+| 锁 | 优点 | 缺点 | 适用场景 |
+| :------: | :----------------------------------------------------------: | :----------------------------------------------: | :----------------------------------: |
+| 偏向锁 | 加锁和解锁不需要额外的开销,和执行非同步方法相比仅存在纳秒级的差距 | 如何线程之间存在锁竞争,会带来额外的锁撤销的消耗 | 适用于只有一个线程访问同步块场景 |
+| 轻量级锁 | 竞争的线程不会阻塞,提高了程序的响应速度 | 如果始终得不到锁竞争的线程,使用自旋会消耗CPU | 追求响应时间、头同步块执行速度非常快 |
+| 重量级锁 | 线程竞争不使用自旋,不会消耗CPU | 线程阻塞,响应时间缓慢 | 追求吞吐量,同步块执行速度较长 |
+
diff --git "a/docs/Java345円274円200円345円217円221円350円247円204円350円214円203円/Java345円274円200円345円217円221円346円211円213円345円206円214円345円265円251円345円261円261円347円211円210円.md" "b/docs/Java345円274円200円345円217円221円350円247円204円350円214円203円/Java345円274円200円345円217円221円346円211円213円345円206円214円345円265円251円345円261円261円347円211円210円.md"
new file mode 100644
index 0000000..f67b914
--- /dev/null
+++ "b/docs/Java345円274円200円345円217円221円350円247円204円350円214円203円/Java345円274円200円345円217円221円346円211円213円345円206円214円345円265円251円345円261円261円347円211円210円.md"
@@ -0,0 +1,255 @@
+# 👉 Java开发手册 嵩山版
+
+| 版本 | 制定 | 更新日期 | 备注 |
+| :---: | :----------------------------: | :--------: | :------------------------: |
+| 1.7.0 | 阿里巴巴与全球 Java 社区开发者 | 2020年08月03日 | 嵩山版,首次发布前后端规约
+
+ ## 一、 编程规约
+
+### (一) 命名风格
+
+1. **【强制】代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。**
+
+> **反例:_name / __name / $name / name_ / name$ / name__**
+
+2. **【强制】所有编程相关的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。**
+
+ **说明:正确的英文拼写和语法可以让阅读者易于理解,避免歧义。注意,纯拼音命名方式更要避免采用。**
+
+> **正例:ali / alibaba / taobao / cainiao/ aliyun/ youku / hangzhou 等国际通用的名称,可视同英文。**
+>
+> **反例:DaZhePromotion [打折] / getPingfenByName() [评分] / String fw[福娃] / int 某变量 = 3**
+
+3. **【强制】代码和注释中都要避免使用任何语言的种族歧视性词语。**
+
+> **正例:日本人 / 印度人 / blockList / allowList / secondary**
+>
+> **反例:RIBENGUIZI / Asan / blackList / whiteList / slave**
+
+4. **【强制】类名使用 UpperCamelCase 风格,但以下情形例外:DO / BO / DTO / VO / AO / PO / UID 等。**
+
+> **正例:ForceCode / UserDO / HtmlDTO / XmlService / TcpUdpDeal / TaPromotion**
+>
+> **反例:forcecode / UserDo / HTMLDto / XMLService / TCPUDPDeal / TAPromotion**
+
+5. **【强制】方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格。**
+
+> **正例: localValue / getHttpMessage() / inputUserId**
+
+6. **【强制】常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。**
+
+> **正例:MAX_STOCK_COUNT / CACHE_EXPIRED_TIME 反例:MAX_COUNT / EXPIRED_TIME**
+
+7. **【强制】抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类 命名以它要测试的类的名称开始,以 Test 结尾。**
+
+8. **【强制】类型与中括号紧挨相连来表示数组。 正例:定义整形数组 int[] arrayDemo。**
+
+> **反例:在 main 参数中,使用 String args[]来定义。**
+
+9. **【强制】POJO 类中的任何布尔类型的变量,都不要加 is 前缀,否则部分框架解析会引起序列 化错误。**
+
+ **说明:在本文 MySQL 规约中的建表约定第一条,表达是与否的变量采用 is_xxx 的命名方式,所以,需要 在设置从 is_xxx 到 xxx 的映射关系。**
+
+> **反例:定义为基本数据类型 Boolean isDeleted 的属性,它的方法也是 isDeleted(),框架在反向解析的时 候,"误以为"对应的属性名称是 deleted,导致属性获取不到,进而抛出异常。**
+
+10. **【强制】包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用 单数形式,但是类名如果有复数含义,类名可以使用复数形式。**
+
+> **正例:应用工具类包名为 com.alibaba.ei.kunlun.aap.util、类名为 MessageUtils(此规则参考 spring 的 框架结构)**
+
+11. **【强制】避免在子父类的成员变量之间、或者不同代码块的局部变量之间采用完全相同的命名, 使可理解性降低。**
+
+ **说明:子类、父类成员变量名相同,即使是 public 类型的变量也能够通过编译,另外,局部变量在同一方 法内的不同代码块中同名也是合法的,这些情况都要避免。对于非 setter/getter 的参数名称也要避免与成 员变量名称相同。**
+
+ ```java
+ 反例:
+ public class ConfusingName {
+ public int stock;
+ // 非 setter/getter 的参数名称,不允许与本类成员变量同名
+ public void get(String alibaba) {
+ if (condition) {
+ final int money = 666;
+ // ...
+ }
+ for (int i = 0; i < 10; i++) { + // 在同一方法体中,不允许与其它代码块中的 money 命名相同 + final int money = 15978; + // ... + } + } + } + class Son extends ConfusingName { + // 不允许与父类的成员变量名称相同 + public int stock; + } + ``` + +12. **【强制】杜绝完全不规范的缩写,避免望文不知义。** + +> **反例:AbstractClass"缩写"成 AbsClass;condition"缩写"成 condi;Function 缩写"成 Fu,此类 随意缩写严重降低了代码的可阅读性。**
+
+13. **【推荐】为了达到代码自解释的目标,任何自定义编程元素在命名时,使用尽量完整的单词组 合来表达。**
+
+> **正例:对某个对象引用的 volatile 字段进行原子更新的类名为 AtomicReferenceFieldUpdater。**
+>
+> **反例:常见的方法内变量为 int a;的定义方式**。
+
+14. **【推荐】在常量与变量的命名时,表示类型的名词放在词尾,以提升辨识度。**
+
+> **正例:startTime / workQueue / nameList / TERMINATED_THREAD_COUNT**
+>
+> **反例:startedAt / QueueOfWork / listName / COUNT_TERMINATED_THREAD**
+
+15. **【推荐】如果模块、接口、类、方法使用了设计模式,在命名时需体现出具体模式。**
+
+ **说明:将设计模式体现在名字中,有利于阅读者快速理解架构设计理念。**
+
+> 正例: public class OrderFactory;
+>
+> public class LoginProxy;
+>
+> public class ResourceObserver;
+
+16. **【推荐】接口类中的方法和属性不要加任何修饰符号(public 也不要加),保持代码的简洁 性,并加上有效的 Javadoc 注释。尽量不要在接口里定义变量,如果一定要定义变量,确定 与接口方法相关,并且是整个应用的基础常量。**
+
+> 正例:接口方法签名 void commit(); 接口基础常量 String COMPANY = "alibaba";
+>
+> 反例:接口方法定义 public abstract void f();
+
+ **说明:JDK8 中接口允许有默认实现,那么这个 default 方法,是对所有实现类都有价值的默认实现。**
+
+17. **接口和实现类的命名有两套规则:**
+
+ **1)【强制】对于 Service 和 DAO 类,基于 SOA 的理念,暴露出来的服务一定是接口,内部的实现类用 Impl 的后缀与接口区别。**
+
+> **正例:CacheServiceImpl 实现 CacheService 接口。**
+
+ **2)【推荐】如果是形容能力的接口名称,取对应的形容词为接口名(通常是–able 的形容词)。**
+
+> **正例:AbstractTranslator 实现 Translatable 接口。**
+
+18. **【参考】枚举类名带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。**
+
+ **说明:枚举其实就是特殊的常量类,且构造方法被默认强制是私有。**
+
+> 正例:枚举名字为 ProcessStatusEnum 的成员名称:SUCCESS / UNKNOWN_REASON。
+
+19. **【参考】各层命名规约:**
+
+ **A) Service/DAO 层方法命名规约**
+
+ **1) 获取单个对象的方法用 get 做前缀。**
+
+ **2) 获取多个对象的方法用 list 做前缀,复数结尾,如:listObjects。**
+
+ **3) 获取统计值的方法用 count 做前缀。**
+
+ **4) 插入的方法用 save/insert 做前缀。**
+
+ **5) 删除的方法用 remove/delete 做前缀。**
+
+ **6) 修改的方法用 update 做前缀。**
+
+ **B) 领域模型命名规约 Java 开发手册 4/59**
+
+ **1) 数据对象:xxxDO,xxx 即为数据表名。**
+
+ **2) 数据传输对象:xxxDTO,xxx 为业务领域相关的名称。**
+
+ **3) 展示对象:xxxVO,xxx 一般为网页名称。**
+
+ **4) POJO 是 DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO。**
+
+### (二) 常量定义
+
+1. **【强制】不允许任何魔法值(即未经预先定义的常量)直接出现在代码中。**
+
+> **反例: // 本例中,开发者 A 定义了缓存的 key,然后开发者 B 使用缓存时少了下划线,即 key 是"Id#taobao"+tradeId,导致 出现故障**
+>
+> **String key = "Id#taobao_" + tradeId; cache.put(key, value);**
+
+2. **【强制】在 long 或者 Long 赋值时,数值后使用大写字母 L,不能是小写字母 l,小写容易跟 数字混淆,造成误解。**
+
+> **说明:Long a = 2l; 写的是数字的 21,还是 Long 型的 2?**
+
+3. 【**推荐】不要使用一个常量类维护所有常量,要按常量功能进行归类,分开维护。**
+
+ **说明:大而全的常量类,杂乱无章,使用查找功能才能定位到修改的常量,不利于理解,也不利于维护。**
+
+> **正例:缓存相关常量放在类 CacheConsts 下;系统配置相关常量放在类 SystemConfigConsts 下。**
+
+4. **【推荐】常量的复用层次有五层:跨应用共享常量、应用内共享常量、子工程内共享常量、包 内共享常量、类内共享常量。**
+
+ **1) 跨应用共享常量:放置在二方库中,通常是 client.jar 中的 constant 目录下。**
+
+ **2) 应用内共享常量:放置在一方库中,通常是子模块中的 constant 目录下。**
+
+> **反例:易懂变量也要统一定义成应用内共享常量,两位工程师在两个类中分别定义了"YES"的变量:**
+>
+> **类 A 中:public static final String YES = "yes";**
+>
+> **类 B 中:public static final String YES = "y"; A.YES.equals(B.YES),预期是 true,但实际返回为 false,导致线上问题。**
+
+ **3) 子工程内部共享常量:即在当前子工程的 constant 目录下。**
+
+ **4) 包内共享常量:即在当前包下单独的 constant 目录下。**
+
+ **5) 类内共享常量:直接在类内部 private static final 定义。**
+
+5.
+
+### (三) 代码格式
+
+### (四) OOP 规约
+
+### (五) 日期时间
+
+### (六) 集合处理
+
+### (七) 并发处理
+
+### (八) 控制语句
+
+### (九) 注释规约
+
+### (十) 前后端规约
+
+### (十一) 其他
+
+## 二、异常日志
+
+### (一) 错误码
+
+### (二) 异常处理
+
+### (三) 日志规约
+
+## 三、单元测试
+
+## 四、安全规约
+
+## 五、MySQL 数据库
+
+### (一) 建表规约
+
+### (二) 索引规约
+
+### (三) SQL 语句
+
+### (四) ORM 映射
+
+## 六、工程结构
+
+### (一) 应用分层
+
+### (二) 二方库依赖
+
+### (三) 服务器
+
+## 七、设计规约
+
+# 附 1:专有名词解释
+
+# 附 2:错误码列表
+
+
+
diff --git "a/docs/Java346円200円247円350円203円275円344円274円230円345円214円226円/README.md" "b/docs/Java346円200円247円350円203円275円344円274円230円345円214円226円/README.md"
new file mode 100644
index 0000000..47f4eec
--- /dev/null
+++ "b/docs/Java346円200円247円350円203円275円344円274円230円345円214円226円/README.md"
@@ -0,0 +1,3 @@
+# Java性能优化
+
+* [常见Java代码优化法则](docs/Java并发编程/常见Java代码优化法则.md)
diff --git "a/docs/Java346円200円247円350円203円275円344円274円230円345円214円226円/_sidebar.md" "b/docs/Java346円200円247円350円203円275円344円274円230円345円214円226円/_sidebar.md"
new file mode 100644
index 0000000..828be2b
--- /dev/null
+++ "b/docs/Java346円200円247円350円203円275円344円274円230円345円214円226円/_sidebar.md"
@@ -0,0 +1,3 @@
+* **👉 Java性能优化** [↩](/README)
+ * [常见Java代码优化法则](docs/Java性能优化/常见Java代码优化法则.md)
+
\ No newline at end of file
diff --git "a/docs/Java346円200円247円350円203円275円344円274円230円345円214円226円/345円270円270円350円247円201円Java344円273円243円347円240円201円344円274円230円345円214円226円346円263円225円345円210円231円.md" "b/docs/Java346円200円247円350円203円275円344円274円230円345円214円226円/345円270円270円350円247円201円Java344円273円243円347円240円201円344円274円230円345円214円226円346円263円225円345円210円231円.md"
new file mode 100644
index 0000000..2eb7417
--- /dev/null
+++ "b/docs/Java346円200円247円350円203円275円344円274円230円345円214円226円/345円270円270円350円247円201円Java344円273円243円347円240円201円344円274円230円345円214円226円346円263円225円345円210円231円.md"
@@ -0,0 +1,89 @@
+# 👉 常见Java代码优化法则
+
+## 1.使用局部变量可避免在堆上分配
+由于堆资源是多线程共享的,是垃圾回收器工作的主要区域,过多的对象会造成 GC 压力。可以通过局部变量的方式,将变量在栈上分配。这种方式变量会随着方法执行的完毕而销毁,能够减轻 GC 的压力。
+
+## 2.削弱变量的作用范围
+注意变量的作用范围,尽量减少对象的创建。如下面的代码,变量 a 每次进入方法都会创建,可以将它移动到 if 语句内部。
+
+## 3.使用类名方式访问静态变量
+有的同学习惯使用对象访问静态变量,这种方式多了一步寻址操作,需要先找到变量对应的类,再找到类对应的变量。
+
+## 4.字符串拼接不要使用 "+"
+字符串拼接,使用 `StringBuilder `或者` StringBuffer`,不要使用 + 号。
+
+## 5.重写对象的`HashCode`,不要简单地返回固定值
+开发时重写 `HashCode` 和`Equals` 方法时,会把 `HashCode `的值返回固定的 0,而这样做是不恰当的。
+
+当这些对象存入 `HashMap` 时,性能就会非常低,因为 `HashMap `是通过 `HashCode` 定位到 Hash 槽,有冲突的时候,才会使用链表或者红黑树组织节点。固定地返回 0,相当于把 Hash 寻址功能给废除了。
+
+## 6.`HashMap `等集合初始化的时候,尽量指定初始值大小
+通过指定初始值大小可减少扩容造成的性能损耗。
+
+## 7.遍历`Map` 的时候,使用 `EntrySet` 方法
+使用 EntrySet 方法,可以直接返回 set 对象,直接拿来用即可;而使用 KeySet 方法,获得的是key 的集合,需要再进行一次 get 操作,多了一个操作步骤。所以更推荐使用 EntrySet 方式遍历 Map。
+
+## 8.不要在多线程下使用同一个 Random
+Random 类的 seed 会在并发访问的情况下发生竞争,造成性能降低,建议在多线程环境下使用 `ThreadLocalRandom` 类。
+
+> 在 Linux 上,通过加入 JVM 配置 -Djava.security.egd=file:/dev/./urandom,使用 urandom 随机生成器,在进行随机数获取时,速度会更快。
+
+## 9.自增推荐使用 LongAddr
+自增运算可以通过 synchronized 和 volatile 的组合,或者也可以使用原子类(比如 AtomicLong)。
+
+后者的速度比前者要高一些,AtomicLong 使用 CAS 进行比较替换,在线程多的情况下会造成过多无效自旋,所以可以使用 LongAdder 替换 AtomicLong 进行进一步的性能提升。
+
+## 10.不要使用异常控制程序流程
+异常,是用来了解并解决程序中遇到的各种不正常的情况,它的实现方式比较昂贵,比平常的条件判断语句效率要低很多。
+
+这是因为异常在字节码层面,需要生成一个如下所示的异常表(Exception table),多了很多判断步骤。
+
+## 11.不要在循环中使用 try catch
+道理与上面类似,很多文章介绍,不要把异常处理放在循环里,而应该把它放在最外层,但实际测试情况表明这两种方式性能相差并不大。
+
+既然性能没什么差别,那么就推荐根据业务的需求进行编码。比如,循环遇到异常时,不允许中断,也就是允许在发生异常的时候能够继续运行下去,那么异常就只能在 for 循环里进行处理。
+
+## 12.不要捕捉 RuntimeException
+Java 异常分为两种,一种是可以通过预检查机制避免的 RuntimeException;另外一种就是普通异常。
+
+其中,RuntimeException 不应该通过 catch 语句去捕捉,而应该使用编码手段进行规避。
+
+## 13.合理使用 PreparedStatement
+PreparedStatement 使用预编译对 SQL 的执行进行提速,大多数数据库都会努力对这些能够复用的查询语句进行预编译优化,并能够将这些编译结果缓存起来。
+
+这样等到下次用到的时候,就可以很快进行执行,也就少了一步对 SQL 的解析动作。
+
+PreparedStatement 还能提高程序的安全性,能够有效防止 SQL 注入。
+
+但如果你的程序每次 SQL 都会变化,不得不手工拼接一些数据,那么 PreparedStatement 就失去了它的作用,反而使用普通的 Statement 速度会更快一些。
+
+## 14.日志打印的注意事项
+ debug 输出一些调试信息,然后在线上关掉它。
+
+## 15.减少事务的作用范围
+如果的程序使用了事务,那一定要注意事务的作用范围,尽量以最快的速度完成事务操作。这是因为,事务的隔离性是使用锁实现的。
+
+## 16.使用位移操作替代乘除法
+计算机是使用二进制表示的,位移操作会极大地提高性能。
+
+- `<<` 左移相当于乘以 2; +- `<<` 右移相当于除以 2; +- `>>>`无符号右移相当于除以 2,但它会忽略符号位,空位都以 0 补齐。
+
+## 17.不要打印大集合或者使用大集合的 toString 方法
+有的开发喜欢将集合作为字符串输出到日志文件中,这个习惯是非常不好的。
+
+拿 ArrayList 来说,它需要遍历所有的元素来迭代生成字符串。在集合中元素非常多的情况下,这不仅会占用大量的内存空间,执行效率也非常慢
+
+## 18.尽量少在程序中使用反射
+反射的功能很强大,但它是通过解析字节码实现的,性能就不是很理想。
+
+现实中有很多对反射的优化方法,比如把反射执行的过程(比如 Method)缓存起来,使用复用来加快反射速度。
+
+Java 7.0 之后,加入了新的包 java.lang.invoke,同时加入了新的 JVM 字节码指令 invokedynamic,用来支持从 JVM 层面,直接通过字符串对目标方法进行调用。
+
+如果你对性能有非常苛刻的要求,则使用 invoke 包下的 MethodHandle 对代码进行着重优化,但它的编程不如反射方便,在平常的编码中,反射依然是首选。
+
+## 19.正则表达式可以预先编译,加快速度
+Java 的正则表达式需要先编译再使用。
+
diff --git "a/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/README.md" "b/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/README.md"
new file mode 100644
index 0000000..a816816
--- /dev/null
+++ "b/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/README.md"
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git "a/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/_sidebar.md" "b/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/_sidebar.md"
new file mode 100644
index 0000000..fae09b2
--- /dev/null
+++ "b/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/_sidebar.md"
@@ -0,0 +1,20 @@
+* **👉 Java核心基础** [↩](/README)
+ * [理解Java关键字](docs/Java核心基础/理解Java关键字.md)
+ * [理解String字符串](docs/Java核心基础/理解String字符串.md)
+ * [理解基本数据类型与包装类](docs/Java核心基础/理解基本数据类型与包装类.md)
+ * [理解各种内部类和枚举类](docs/Java核心基础/理解各种内部类和枚举类.md)
+ * [理解动态代理](docs/Java核心基础/理解动态代理.md)
+ * [理解克隆与序列化应用](docs/Java核心基础/理解克隆与序列化应用.md)
+ * [理解异常处理](docs/Java核心基础/理解异常处理.md)
+ * [理解抽象类与接口](docs/Java核心基础/理解抽象类与接口.md)
+ * [理解泛型与迭代器](docs/Java核心基础/理解泛型与迭代器.md)
+ * [理解浅克隆和深克隆](docs/Java核心基础/理解浅克隆和深克隆.md)
+ * [理解类与Object](docs/Java核心基础/理解类与Object.md)
+ * [理解集合Collection](docs/Java核心基础/理解集合Collection.md)
+ * [理解集合Map](docs/Java核心基础/理解集合Map.md)
+ * [理解Java中的各种锁](docs/Java核心基础/理解Java中的各种锁.md)
+ * [理解HashMap底层实现原理](docs/Java核心基础/理解HashMap底层实现原理.md)
+ * [理解HashMap为什么是线程不安全的](docs/Java核心基础/理解HashMap为什么是线程不安全的.md)
+ * [理解ConcurrentHashMap底层实现原理](docs/Java核心基础/理解ConcurrentHashMap底层实现原理.md)
+ * [理解数据结构队列](docs/Java核心基础/理解数据结构队列.md)
+
\ No newline at end of file
diff --git "a/docs/java/ConcurrentHashMap345円256円236円347円216円260円345円216円237円347円220円206円.md" "b/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円ConcurrentHashMap345円272円225円345円261円202円345円256円236円347円216円260円345円216円237円347円220円206円.md"
similarity index 100%
rename from "docs/java/ConcurrentHashMap345円256円236円347円216円260円345円216円237円347円220円206円.md"
rename to "docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円ConcurrentHashMap345円272円225円345円261円202円345円256円236円347円216円260円345円216円237円347円220円206円.md"
diff --git "a/docs/java/HashMap344円270円272円344円273円200円344円271円210円346円230円257円347円272円277円347円250円213円344円270円215円345円256円211円345円205円250円347円232円204円.md" "b/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円HashMap344円270円272円344円273円200円344円271円210円346円230円257円347円272円277円347円250円213円344円270円215円345円256円211円345円205円250円347円232円204円.md"
similarity index 100%
rename from "docs/java/HashMap344円270円272円344円273円200円344円271円210円346円230円257円347円272円277円347円250円213円344円270円215円345円256円211円345円205円250円347円232円204円.md"
rename to "docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円HashMap344円270円272円344円273円200円344円271円210円346円230円257円347円272円277円347円250円213円344円270円215円345円256円211円345円205円250円347円232円204円.md"
diff --git "a/docs/java/HashMap345円216円237円347円220円206円345円210円206円346円236円220円.md" "b/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円HashMap345円272円225円345円261円202円345円256円236円347円216円260円345円216円237円347円220円206円.md"
similarity index 100%
rename from "docs/java/HashMap345円216円237円347円220円206円345円210円206円346円236円220円.md"
rename to "docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円HashMap345円272円225円345円261円202円345円256円236円347円216円260円345円216円237円347円220円206円.md"
diff --git "a/docs/java/347円220円206円350円247円243円Java344円270円255円347円232円204円345円220円204円347円247円215円351円224円201円.md" "b/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円Java344円270円255円347円232円204円345円220円204円347円247円215円351円224円201円.md"
similarity index 100%
rename from "docs/java/347円220円206円350円247円243円Java344円270円255円347円232円204円345円220円204円347円247円215円351円224円201円.md"
rename to "docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円Java344円270円255円347円232円204円345円220円204円347円247円215円351円224円201円.md"
diff --git "a/docs/java/Java345円205円263円351円224円256円345円255円227円347円220円206円350円247円243円.md" "b/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円Java345円205円263円351円224円256円345円255円227円.md"
similarity index 90%
rename from "docs/java/Java345円205円263円351円224円256円345円255円227円347円220円206円350円247円243円.md"
rename to "docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円Java345円205円263円351円224円256円345円255円227円.md"
index 44d3a06..389dd88 100644
--- "a/docs/java/Java345円205円263円351円224円256円345円255円227円347円220円206円350円247円243円.md"
+++ "b/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円Java345円205円263円351円224円256円345円255円227円.md"
@@ -42,7 +42,7 @@ static {
### 1.2 初始化时机
对于被 static 修饰的类变量、方法块和静态方法的初始化时机,我们写了一个测试 demo,如下图:
- 打印出来的结果是:
+ 打印出来的结果是:
父类静态变量初始化
父类静态块初始化
@@ -92,7 +92,7 @@ public void testCatchFinally() {
```
这个代码演示了在 try、catch 中都遇到了异常,代码的执行顺序为:try -> catch -> finally,输出的结果如下:
- 可以看到两点:
+ 可以看到两点:
1. finally 先执行后,再抛出 catch 的异常;
2. 最终捕获的异常是 catch 的异常,try 抛出来的异常已经被 catch 吃掉了,所以当我们遇见 catch 也有可能会抛出异常时,我们可以先打印出 try 的异常,这样 try 的异常在日志中就会有所体现。
@@ -110,7 +110,7 @@ volatile 的意思是可见的,常用来修饰某个共享变量,意思是
volatile 关键字就会触发这种机制,加了 volatile 关键字的变量,就会被识别成共享变量,内存中值被修改后,会通知到各个 CPU 缓存,使 CPU 缓存中的值也对应被修改,从而保证线程从 CPU 缓存中拿取出来的值是最新的。
我们画了一个图来说明一下:
- 从图中我们可以看到,线程 1 和线程 2 一开始都读取了 C 值,CPU 1 和 CPU 2 缓存中也都有了 C 值,然后线程 1 把 C 值修改了,这时候内存的值和 CPU 2 缓存中的 C 值就不等了,内存这时发现 C 值被 volatile 关键字修饰,发现其是共享变量,就会使 CPU 2 缓存中的 C 值状态置为无效,CPU 2 会从内存中重新拉取最新的值,这时候线程 2 再来读取 C 值时,读取的已经是内存中最新的值了。
+ 从图中我们可以看到,线程 1 和线程 2 一开始都读取了 C 值,CPU 1 和 CPU 2 缓存中也都有了 C 值,然后线程 1 把 C 值修改了,这时候内存的值和 CPU 2 缓存中的 C 值就不等了,内存这时发现 C 值被 volatile 关键字修饰,发现其是共享变量,就会使 CPU 2 缓存中的 C 值状态置为无效,CPU 2 会从内存中重新拉取最新的值,这时候线程 2 再来读取 C 值时,读取的已经是内存中最新的值了。
## 5 transient
@@ -119,7 +119,7 @@ transient 关键字我们常用来修饰类变量,意思是当前变量是无
## 6 default
default 关键字一般会用在接口的方法上,意思是对于该接口,子类是无需强制实现的,但自己必须有默认实现,我们举个例子如下:
-
+
## 7 面试题
diff --git "a/docs/java/346円267円261円345円205円245円347円220円206円350円247円243円345円255円227円347円254円246円344円270円262円.md" "b/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円String345円255円227円347円254円246円344円270円262円.md"
similarity index 100%
rename from "docs/java/346円267円261円345円205円245円347円220円206円350円247円243円345円255円227円347円254円246円344円270円262円.md"
rename to "docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円String345円255円227円347円254円246円344円270円262円.md"
diff --git "a/docs/java/347円220円206円350円247円243円345円205円213円351円232円206円344円270円216円345円272円217円345円210円227円345円214円226円345円272円224円347円224円250円.md" "b/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円345円205円213円351円232円206円344円270円216円345円272円217円345円210円227円345円214円226円345円272円224円347円224円250円.md"
similarity index 100%
rename from "docs/java/347円220円206円350円247円243円345円205円213円351円232円206円344円270円216円345円272円217円345円210円227円345円214円226円345円272円224円347円224円250円.md"
rename to "docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円345円205円213円351円232円206円344円270円216円345円272円217円345円210円227円345円214円226円345円272円224円347円224円250円.md"
diff --git "a/docs/java/347円220円206円350円247円243円345円212円250円346円200円201円344円273円243円347円220円206円.md" "b/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円345円212円250円346円200円201円344円273円243円347円220円206円.md"
similarity index 100%
rename from "docs/java/347円220円206円350円247円243円345円212円250円346円200円201円344円273円243円347円220円206円.md"
rename to "docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円345円212円250円346円200円201円344円273円243円347円220円206円.md"
diff --git "a/docs/java/345円220円204円347円247円215円345円206円205円351円203円250円347円261円273円345円222円214円346円236円232円344円270円276円347円261円273円.md" "b/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円345円220円204円347円247円215円345円206円205円351円203円250円347円261円273円345円222円214円346円236円232円344円270円276円347円261円273円.md"
similarity index 100%
rename from "docs/java/345円220円204円347円247円215円345円206円205円351円203円250円347円261円273円345円222円214円346円236円232円344円270円276円347円261円273円.md"
rename to "docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円345円220円204円347円247円215円345円206円205円351円203円250円347円261円273円345円222円214円346円236円232円344円270円276円347円261円273円.md"
diff --git "a/docs/java/347円220円206円350円247円243円345円237円272円346円234円254円346円225円260円346円215円256円347円261円273円345円236円213円344円270円216円345円214円205円350円243円205円347円261円273円.md" "b/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円345円237円272円346円234円254円346円225円260円346円215円256円347円261円273円345円236円213円344円270円216円345円214円205円350円243円205円347円261円273円.md"
similarity index 90%
rename from "docs/java/347円220円206円350円247円243円345円237円272円346円234円254円346円225円260円346円215円256円347円261円273円345円236円213円344円270円216円345円214円205円350円243円205円347円261円273円.md"
rename to "docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円345円237円272円346円234円254円346円225円260円346円215円256円347円261円273円345円236円213円344円270円216円345円214円205円350円243円205円347円261円273円.md"
index 8362af0..6ca3096 100644
--- "a/docs/java/347円220円206円350円247円243円345円237円272円346円234円254円346円225円260円346円215円256円347円261円273円345円236円213円344円270円216円345円214円205円350円243円205円347円261円273円.md"
+++ "b/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円345円237円272円346円234円254円346円225円260円346円215円256円347円261円273円345円236円213円344円270円216円345円214円205円350円243円205円347円261円273円.md"
@@ -1,6 +1,6 @@
-## 理解基本数据类型与包装类
+# 理解基本数据类型与包装类
-### 基本数据类型
+## 基本数据类型
Java 基本数据按类型可以分为四大类:布尔型、整数型、浮点型、字符型,这四大类包含 8 种基本数据类型。
@@ -35,7 +35,7 @@ public static void main(String[] args) {
}
```
-### 包装类型
+## 包装类型
我们知道 8 种基本数据类型都有其对应的包装类,因为 Java 的设计思想是万物既对象,有很多时候我们需要以对象的形式操作某项功能,比如说获取哈希值(hashCode)或获取类(getClass)等。
@@ -65,7 +65,7 @@ List list = new ArrayList(); // 编译器代码报错
因为包装类都实现了 Serializable 接口,所以包装类天然支持序列化和反序列化。比如 Integer 的类图如下:
-
+
**4. 类型转换**
@@ -121,7 +121,7 @@ public static Integer valueOf(int i) {
- Long:缓存区 -128~127
- Integer:缓存区 -128~127
-### 包装类的注意事项
+## 包装类的注意事项
- int 的默认值是 0,而 Integer 的默认值是 null。
- 推荐所有包装类对象之间的值比较使用 `equals()` 方法,因为包装类的非高频区数据会在堆上产生,而高频区又会复用已有对象,这样会导致同样的代码,因为取值的不同,而产生两种截然不同的结果。代码示例:
@@ -173,9 +173,9 @@ public static void main(String[] args) {
由此可见将 Integer 最大缓存修改为 666 之后,667 不会被缓存,而 -128~666 之间的数都被缓存了。
-### 相关面试题
+## 相关面试题
-#### 1. 以下 Integer 代码输出的结果是?
+### 1. 以下 Integer 代码输出的结果是?
```java
Integer age = 10;
@@ -187,7 +187,7 @@ System.out.println((age == age2) + "," + (age3 == age4));
答:`true,false`
-#### 2. 以下 Double 代码输出的结果是?
+### 2. 以下 Double 代码输出的结果是?
```
Double num = 10d;
@@ -199,7 +199,7 @@ System.out.println((num == num2) + "," + (num3 == num4));
答:`false,false`
-#### 3. 以下程序输出结果是?
+### 3. 以下程序输出结果是?
```java
int i = 100;
@@ -217,7 +217,7 @@ D:false,false
题目分析:有人认为这和 Integer 高速缓存有关系,但你发现把值改为 10000 结果也是 `true,true`,这是因为 Integer 和 int 比较时,会自动拆箱为 int 相当于两个 int 比较,值一定是 `true,true`。
-#### 4. 以下程序执行的结果是?
+### 4. 以下程序执行的结果是?
```java
final int iMax = Integer.MAX_VALUE;
@@ -233,7 +233,7 @@ D:以上都不是
题目解析:这是因为整数在内存中使用的是补码的形式表示,最高位是符号位 0 表示正数,1 表示负数,当执行 +1 时,最高位就变成了 1,结果就成了 -2147483648。
-#### 5. 以下程序执行的结果是?
+### 5. 以下程序执行的结果是?
```java
Set set = new HashSet();
@@ -253,36 +253,36 @@ D:以上都不是
题目解析:Short 类型 -1 之后转换成了 Int 类型,remove() 的时候在集合中找不到 Int 类型的数据,所以就没有删除任何元素,执行的结果就是 5。
-#### 6. `short s=2;s=s+1;` 会报错吗?`short s=2;s+=1;` 会报错吗?
+### 6. `short s=2;s=s+1;` 会报错吗?`short s=2;s+=1;` 会报错吗?
答:s=s+1 会报错,s+=1 不会报错,因为 s=s+1 会导致 short 类型升级为 int 类型,所以会报错,而 s+=1 还是原来的 short 类型,所以不会报错。
-#### 7. `float f=3.4;` 会报错吗?为什么?
+### 7. `float f=3.4;` 会报错吗?为什么?
答:会报错,因为值 3.4 是 double 类型,float 类型级别小于 double 类型,所以会报错。如下图所示:
-
+
-#### 8. 为什么需要包装类?
+### 8. 为什么需要包装类?
答:需要包装类的原因有两个。
1 Java 的设计思想是万物既对象,包装类体现了面向对象的设计理念;
2 包装类包含了很多属性和方法,比基本数据类型功能多,比如提供的获取哈希值(hashCode)或获取类(getClass)的方法等。
-#### 9. 基本类 int 和包装类 Integer,在 -128~127 之间都会复用已有的缓存对象,这种说法正确吗?
+### 9. 基本类 int 和包装类 Integer,在 -128~127 之间都会复用已有的缓存对象,这种说法正确吗?
答:不正确,只有包装类高频区域数据才有缓存。
-#### 10. 包装类 Double 和 Integer 一样都有高频区域数据缓存,这种说法正确吗?
+### 10. 包装类 Double 和 Integer 一样都有高频区域数据缓存,这种说法正确吗?
答:不正确,基本数据类型的包装类只有 Double 和 Float 没有高频区域的缓存。
-#### 11. 包装类的值比较要使用什么方法?
+### 11. 包装类的值比较要使用什么方法?
答:包装类因为有高频区域数据缓存,所以推荐使用 equals() 方法进行值比较。
-#### 12. 包装类有哪些功能?
+### 12. 包装类有哪些功能?
答:包装类提供的功能有以下几个。
@@ -294,11 +294,11 @@ D:以上都不是
详见正文"包装类型"部分内容。
-#### 13. 泛型可以为基本类型吗?为什么?
+### 13. 泛型可以为基本类型吗?为什么?
答:泛型不能使用基本数据类型。泛型在 JVM(Java虚拟机)编译的时候会类型檫除,比如代码 `List list` 在 JVM 编译的时候会转换为 `List list` ,因为泛型是在 JDK 5 时提供的,而 JVM 的类型檫除是为了兼容以前代码的一个折中方案,类型檫除之后就变成了 Object,而 Object 不能存储基本数据类型,但可以使用基本数据类型对应的包装类,所以像 `List list` 这样的代码是不被允许的,编译器阶段会检查报错,而 `List list` 是被允许的。
-#### 14. 选择包装类还是基本类的原则有哪些?
+### 14. 选择包装类还是基本类的原则有哪些?
答:我们知道正确的使用包装类,可以提供程序的执行效率,可以使用已有的缓存,一般情况下选择基本数据类型还是包装类原则有以下几个。
@@ -306,14 +306,14 @@ D:以上都不是
2 RPC 方法返回值和参数必须使用包装类;
3 所有局部变量推荐使用基本数据类型。
-#### 15. 基本数据类型在 JVM 中一定存储在栈中吗?为什么?
+### 15. 基本数据类型在 JVM 中一定存储在栈中吗?为什么?
答:基本数据类型不一定存储在栈中,因为基本类型的存储位置取决于声明的作用域,来看具体的解释。
- 当基本数据类型为局部变量的时候,比如在方法中声明的变量,则存放在方法栈中的,当方法结束系统会释放方法栈,在该方法中的变量也会随着栈的销毁而结束,这也是局部变量只能在方法中使用的原因;
- 当基本数据类型为全局变量的时候,比如类中的声明的变量,则存储在堆上,因为全局变量不会随着某个方法的执行结束而销毁。
-#### 16. 以下程序执行的结果是?
+### 16. 以下程序执行的结果是?
```java
Integer i1 = new Integer(10);
@@ -334,7 +334,7 @@ D:true,false,false
题目解析:new Integer(10) 每次都会创建一个新对象,Integer.valueOf(10) 则会使用缓存池中的对象。
-#### 17. 3*0.1==0.3 返回值是多少?
+### 17. 3*0.1==0.3 返回值是多少?
答:返回值为:false。
diff --git "a/docs/java/347円220円206円350円247円243円345円274円202円345円270円270円345円244円204円347円220円206円.md" "b/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円345円274円202円345円270円270円345円244円204円347円220円206円.md"
similarity index 89%
rename from "docs/java/347円220円206円350円247円243円345円274円202円345円270円270円345円244円204円347円220円206円.md"
rename to "docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円345円274円202円345円270円270円345円244円204円347円220円206円.md"
index bb7147c..7a0e7ad 100644
--- "a/docs/java/347円220円206円350円247円243円345円274円202円345円270円270円345円244円204円347円220円206円.md"
+++ "b/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円345円274円202円345円270円270円345円244円204円347円220円206円.md"
@@ -1,8 +1,8 @@
-## 理解异常处理
+# 理解异常处理
在程序开发中,异常处理也是我们经常使用到的模块,只是平常很少去深究异常模块的一些知识点。比如,try-catch 处理要遵循的原则是什么,finally 为什么总是能执行,try-catch 为什么比较消耗程序的执行性能等问题,我们本讲内容都会给出相应的答案,当然还有面试中经常被问到的异常模块的一些面试题,也是我们本篇要讲解的重点内容。
-### 异常处理基础介绍
+## 异常处理基础介绍
先来看看**异常处理的语法格式**:
@@ -42,9 +42,9 @@ try {
需要注意的是 Java 虚拟机会从上往下匹配错误类型,因此前面的 catch 异常类型不能包含后面的异常类型。比如上面的代码如果把 Exception 放在最前面编译器就会报错,具体可以参考下面的图片。
-
+
-### 异常处理的发展
+## 异常处理的发展
随着 Java 语言的发展,JDK 7 的时候引入了一些更加便利的特性,用来更方便的处理异常信息,如 try-with-resources 和 multiple catch,具体可以参考下面的代码段:
@@ -57,7 +57,7 @@ try (FileReader fileReader = new FileReader("");
}
```
-### 异常处理的基本原则
+## 异常处理的基本原则
先来看下面这段代码,有没有发现一些问题?
@@ -74,7 +74,7 @@ try {
- 第一,尽量不要捕获通用异常,也就是像 Exception 这样的异常,而是应该捕获特定异常,这样更有助于你发现问题;
- 第二,不要忽略异常,像上面的这段代码只是加了 catch,但没有进行如何的错误处理,信息就已经输出了,这样在程序出现问题的时候,根本找不到问题出现的原因,因此要切记不要直接忽略异常。
-### 异常处理对程序性能的影响
+## 异常处理对程序性能的影响
异常处理固然好用,但一定不要滥用,比如下面的代码片段:
@@ -112,13 +112,13 @@ if (null != jsonStr && !jsonStr.equals("")) {
System.out.println(array.size());
```
-### 相关面试题
+## 相关面试题
-#### 1. try 可以单独使用吗?
+### 1. try 可以单独使用吗?
答:try 不能单独使用,否则就失去了 try 的意义和价值。
-#### 2. 以下 try-catch 可以正常运行吗?
+### 2. 以下 try-catch 可以正常运行吗?
```
try {
@@ -130,7 +130,7 @@ try {
答:不能正常运行,catch 后必须包含异常信息,如 catch (Exception e)。
-#### 3. 以下 try-finally 可以正常运行吗?
+### 3. 以下 try-finally 可以正常运行吗?
```
try {
@@ -142,7 +142,7 @@ try {
答:可以正常运行。
-#### 4. 以下代码 catch 里也发生了异常,程序会怎么执行?
+### 4. 以下代码 catch 里也发生了异常,程序会怎么执行?
```java
try {
@@ -159,7 +159,7 @@ System.out.println("main");
答:程序会打印出 finally 之后抛出异常并终止运行。
-#### 5. 以下代码 finally 里也发生了异常,程序会怎么运行?
+### 5. 以下代码 finally 里也发生了异常,程序会怎么运行?
```
try {
@@ -175,7 +175,7 @@ System.out.println("main");
答:程序在输出 try 之后抛出异常并终止运行,不会再执行 finally 异常之后的代码。
-#### 6. 常见的运行时异常都有哪些?
+### 6. 常见的运行时异常都有哪些?
答:常见的运行时异常如下:
@@ -191,21 +191,21 @@ System.out.println("main");
- java.lang.NoSuchMethodException 方法不存在异常;
- java.lang.IllegalArgumentException 方法传递参数错误。
-#### 7. Exception 和 Error 有什么区别?
+### 7. Exception 和 Error 有什么区别?
答:Exception 和 Error 都属于 Throwable 的子类,在 Java 中只有 Throwable 及其之类才能被捕获或抛出,它们的区别如下:
- Exception(异常)是程序正常运行中,可以预期的意外情况,并且可以使用 try/catch 进行捕获处理的。Exception 又分为运行时异常(Runtime Exception)和受检查的异常(Checked Exception),运行时异常编译能通过,但如果运行过程中出现这类未处理的异常,程序会终止运行;而受检查的异常,要么用 try/catch 捕获,要么用 throws 字句声明抛出,否则编译不会通过。
- Error(错误)是指突发的非正常情况,通常是不可以恢复的,比如 Java 虚拟机内存溢出,诸如此类的问题叫做 Error。
-#### 8. throw 和 throws 的区别是什么?
+### 8. throw 和 throws 的区别是什么?
答:它们的区别如下:
- throw 语句用在方法体内,表示抛出异常由方法体内的语句处理,执行 throw 一定是抛出了某种异常;
- throws 语句用在方法声明的后面,该方法的调用者要对异常进行处理,throws 代表可能会出现某种异常,并不一定会发生这种异常。
-#### 9. Integer.parseInt(null) 和 Double.parseDouble(null) 抛出的异常一样吗?为什么?
+### 9. Integer.parseInt(null) 和 Double.parseDouble(null) 抛出的异常一样吗?为什么?
答:Integer.parseInt(null) 和 Double.parseDouble(null) 抛出的异常类型不一样,如下所示:
@@ -214,17 +214,17 @@ System.out.println("main");
至于为什么会产生不同的异常,其实没有特殊的原因,主要是由于这两个功能是不同人开发的,因而就产生了两种不同的异常信息。
-#### 10. NoClassDefFoundError 和 ClassNoFoundException 有什么区别?
+### 10. NoClassDefFoundError 和 ClassNoFoundException 有什么区别?
- NoClassDefFoundError 是 Error(错误)类型,而 ClassNoFoundExcept 是 Exception(异常)类型;
- ClassNoFoundExcept 是 Java 使用 Class.forName 方法动态加载类,没有加载到,就会抛出 ClassNoFoundExcept 异常;
- NoClassDefFoundError 是 Java 虚拟机或者 ClassLoader 尝试加载类的时候却找不到类订阅导致的,也就是说要查找的类在编译的时候是存在的,运行的时候却找不到,这个时候就会出现 NoClassDefFoundError 的错误。
-#### 11. 使用 try-catch 为什么比较耗费性能?
+### 11. 使用 try-catch 为什么比较耗费性能?
答:这个问题要从 JVM(Java 虚拟机)层面找答案了。首先 Java 虚拟机在构造异常实例的时候需要生成该异常的栈轨迹,这个操作会逐一访问当前线程的栈帧,并且记录下各种调试信息,包括栈帧所指向方法的名字,方法所在的类名、文件名,以及在代码中的第几行触发该异常等信息,这就是使用异常捕获耗时的主要原因了。
-#### 12. 常见的 OOM 原因有哪些?
+### 12. 常见的 OOM 原因有哪些?
答:常见的 OOM 原因有以下几个:
@@ -232,7 +232,7 @@ System.out.println("main");
- 加载特别大的图片;
- 递归次数过多,并一直操作未释放的变量。
-#### 13. 以下程序的返回结果是?
+### 13. 以下程序的返回结果是?
```java
public static int getNumber() {
@@ -257,13 +257,13 @@ D:1
题目解析:程序最后一定会执行 finally 里的代码,会把之前的结果覆盖为 3。
-#### 14. finally、finalize 的区别是什么?
+### 14. finally、finalize 的区别是什么?
答:finally、finalize 的区别如下:
- finally 是异常处理语句的一部分,表示总是执行;
- finalize 是 Object 类的一个方法,子类可以覆盖该方法以实现资源清理工作,垃圾回收之前会调用此方法。
-#### 15. 为什么 finally 总能被执行?
+### 15. 为什么 finally 总能被执行?
答:finally 总会被执行,都是编译器的作用,因为编译器在编译 Java 代码时,会复制 finally 代码块的内容,然后分别放在 try-catch 代码块所有的正常执行路径及异常执行路径的出口中,这样 finally 才会不管发生什么情况都会执行。
\ No newline at end of file
diff --git "a/docs/java/347円220円206円350円247円243円346円212円275円350円261円241円347円261円273円344円270円216円346円216円245円345円217円243円.md" "b/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円346円212円275円350円261円241円347円261円273円344円270円216円346円216円245円345円217円243円.md"
similarity index 100%
rename from "docs/java/347円220円206円350円247円243円346円212円275円350円261円241円347円261円273円344円270円216円346円216円245円345円217円243円.md"
rename to "docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円346円212円275円350円261円241円347円261円273円344円270円216円346円216円245円345円217円243円.md"
diff --git "a/docs/java/347円220円206円350円247円243円346円225円260円346円215円256円347円273円223円346円236円204円351円230円237円345円210円227円.md" "b/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円346円225円260円346円215円256円347円273円223円346円236円204円351円230円237円345円210円227円.md"
similarity index 100%
rename from "docs/java/347円220円206円350円247円243円346円225円260円346円215円256円347円273円223円346円236円204円351円230円237円345円210円227円.md"
rename to "docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円346円225円260円346円215円256円347円273円223円346円236円204円351円230円237円345円210円227円.md"
diff --git "a/docs/java/347円220円206円350円247円243円346円263円233円345円236円213円344円270円216円350円277円255円344円273円243円345円231円250円.md" "b/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円346円263円233円345円236円213円344円270円216円350円277円255円344円273円243円345円231円250円.md"
similarity index 95%
rename from "docs/java/347円220円206円350円247円243円346円263円233円345円236円213円344円270円216円350円277円255円344円273円243円345円231円250円.md"
rename to "docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円346円263円233円345円236円213円344円270円216円350円277円255円344円273円243円345円231円250円.md"
index eb26a23..d588004 100644
--- "a/docs/java/347円220円206円350円247円243円346円263円233円345円236円213円344円270円216円350円277円255円344円273円243円345円231円250円.md"
+++ "b/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円346円263円233円345円236円213円344円270円216円350円277円255円344円273円243円345円231円250円.md"
@@ -38,7 +38,7 @@ arrayList.add("Java");
报错信息如下图所示:
-
+
这样就可以避免开头示例中,类型不一致导致程序运行过程中报错的问题了。
@@ -216,19 +216,19 @@ System.out.println(list.getClass() == list2.getClass());
答:`List>` 可以容纳任意类型,只不过 `List>` 被赋值之后,就不允许添加和修改操作了;而 `List` 和 `List>` 不同的是它在赋值之后,可以进行添加和修改操作,如下图所示:
-
+
#### 6.可以把 `List` 赋值给 `List` 吗?
答:不可以,编译器会报错,如下图所示:
-
+
#### 7. `List` 和 `List` 的区别是什么?
答: `List` 和 `List` 都能存储任意类型的数据,但 `List` 和 `List` 的唯一区别就是,`List` 不会触发编译器的类型安全检查,比如把 `List` 赋值给 `List` 是没有任何问题的,但赋值给 `List` 就不行,如下图所示:
-
+
#### 8.以下程序执行的结果是?
diff --git "a/docs/java/346円265円205円345円205円213円351円232円206円345円222円214円346円267円261円345円205円213円351円232円206円.md" "b/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円346円265円205円345円205円213円351円232円206円345円222円214円346円267円261円345円205円213円351円232円206円.md"
similarity index 100%
rename from "docs/java/346円265円205円345円205円213円351円232円206円345円222円214円346円267円261円345円205円213円351円232円206円.md"
rename to "docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円346円265円205円345円205円213円351円232円206円345円222円214円346円267円261円345円205円213円351円232円206円.md"
diff --git "a/docs/java/347円220円206円350円247円243円347円261円273円344円270円216円Object.md" "b/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円347円261円273円344円270円216円Object.md"
similarity index 100%
rename from "docs/java/347円220円206円350円247円243円347円261円273円344円270円216円Object.md"
rename to "docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円347円261円273円344円270円216円Object.md"
diff --git "a/docs/java/347円220円206円350円247円243円351円233円206円345円220円210円Collection.md" "b/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円351円233円206円345円220円210円Collection.md"
similarity index 57%
rename from "docs/java/347円220円206円350円247円243円351円233円206円345円220円210円Collection.md"
rename to "docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円351円233円206円345円220円210円Collection.md"
index ed2ba8e..82fd8e8 100644
--- "a/docs/java/347円220円206円350円247円243円351円233円206円345円220円210円Collection.md"
+++ "b/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円351円233円206円345円220円210円Collection.md"
@@ -1,6 +1,6 @@
## 理解集合Collection
-先来看看集合的继承关系图,如下图所示:
+ 先来看看集合的继承关系图,如下图所示:

@@ -18,250 +18,66 @@
下面我们分别对集合类进行详细地介绍。
-### 集合使用
+### List :可重复
-#### 1)Vector
+> List是一个非常常用的数据类型,一共有以下三种实现类,分别为:**Vector 、ArrayList、LinkedList** 。
-Vector 是 Java 早期提供的线程安全的有序集合,如果不需要线程安全,不建议使用此集合,毕竟同步是有线程开销的。
+#### Vector
-使用示例代码:
+ Vector内部是基于数组实现,**线程安全**(synchronized关键字),也就是说在同一个时刻只能允许一个线程对Vector进行写操作,以保证在多线程环境下数据的一致性,但是频繁的进行加锁和释放锁操作,会导致Vector的**读写效率比较底** 。
-```
-Vector vector = new Vector();
-vector.add("dog");
-vector.add("cat");
-vector.remove("cat");
-System.out.println(vector);
-```
-
-程序执行结果:`[dog]`
-
-#### 2)ArrayList
-
-ArrayList 是最常见的非线程安全的有序集合,因为内部是数组存储的,所以随机访问效率很高,但非尾部的插入和删除性能较低,如果在中间插入元素,之后的所有元素都要后移。ArrayList 的使用与 Vector 类似。
+#### ArrayList
-#### 3)LinkedList
+ ArrayList使用非常广泛,内部也是基于**数组**实现,**线程不安全**,ArrayList**不适合随机插入和删除的操作**,更**适合随机查找和遍历的操作**。
-LinkedList 是使用双向链表数据结构实现的,因此增加和删除效率比较高,而随机访问效率较差。
+#### LinkedList
-LinkedList 除了包含以上两个类的操作方法之外,还新增了几个操作方法,如 offer() 、peek() 等,具体详情,请参考以下代码:
+ LinkedList采用**双向链表**结构存储元素,**随机插入和删除效率高**,**随机访问的效率低**。
-```
-LinkedList linkedList = new LinkedList();
-// 添加元素
-linkedList.offer("bird");
-linkedList.push("cat");
-linkedList.push("dog");
-// 获取第一个元素
-System.out.println(linkedList.peek());
-// 获取第一个元素,并删除此元素
-System.out.println(linkedList.poll());
-System.out.println(linkedList);
-```
-
-程序的执行结果:
+### Set : 不可重复
-```
-dog
-dog
-[cat, bird]
-```
+> Set 的核心价值观就是独一无二,适合存储无序且值不相等的元素。对象的相等性本质上就是对象的HashCode值相等,在Java中根据对象的内存地址计算的对象的HashCode值。如果想要比较两个对象是否相等,则必然同时覆盖对象的hashCode方法和equals方法,并且hashCode方法和equals方法的返回值也必须一样。
-#### 4)HashSet
+#### HashSet
-HashSet 是一个没有重复元素的集合。虽然它是 Set 集合的子类,实际却为 HashMap 的实例,相关源码如下:
+ HashSet 是一个**没有重复元素**的集合,存放的是散列值,它按照元素的散列值来存取元素的。元素的散列值是通过元素的hashCode方法计算得到的,HashSet 首先判断两个元素的散列值是否相等,如果散列值相等,在用equals方法比较,如果equals也返回true,则是同一个元素,否则就不是同一个元素。虽然它是 Set 集合的子类,**基于 HashMap 的实现,**相关源码如下:
-```
+```java
public HashSet() {
map = new HashMap();
}
```
-因此 HashSet 是无序集合,没有办法保证元素的顺序性。
+因此 HashSet 是**无序**集合,没有办法保证元素的顺序性。
-HashSet 默认容量为 16,每次扩充 0.75 倍,相关源码如下:
+**HashSet 默认容量为 16,每次扩充 0.75 倍**,相关源码如下:
-```
+```java
public HashSet(Collection extends E> c) {
map = new HashMap(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
```
-HashSet 的使用与 Vector 类似。
-
-#### 5)TreeSet
-
-TreeSet 集合实现了自动排序,也就是说 TreeSet 会把你插入数据进行自动排序。
-
-示例代码如下:
-
-```
-TreeSet treeSet = new TreeSet();
-treeSet.add("dog");
-treeSet.add("camel");
-treeSet.add("cat");
-treeSet.add("ant");
-System.out.println(treeSet);
-```
-
-程序执行结果:`[ant, camel, cat, dog]`
-
-可以看出,TreeSet 的使用与 Vector 类似,只是实现了自动排序。
-
-#### 6)LinkedHashSet
-
-LinkedHashSet 是按照元素的 hashCode 值来决定元素的存储位置,但同时又使用链表来维护元素的次序,这样使得它看起来像是按照插入顺序保存的。
+#### TreeSet
-LinkedHashSet 的使用与 Vector 类似。
-
-### 集合与数组
-
-集合和数组的转换可使用 toArray() 和 Arrays.asList() 来实现,请参考以下代码示例:
-
-```java
-List list = new ArrayList();
-list.add("cat");
-list.add("dog");
-// 集合转数组
-String[] arr = list.toArray(new String[list.size()]);
-// 数组转集合
-List list2 = Arrays.asList(arr);
-```
+ TreeSet 基于二叉树的原理对新添加的对象按照指定的顺序排序,每添加一个对象都会进行排序 ,并将对象插入二叉树的指定位置。
-集合与数组的区别,可以参考[「数组和排序算法的应用 + 面试题」](https://gitbook.cn/gitchat/column/5d493b4dcb702a087ef935d9/topic/5d4d7ea069004b174ccfffef)的内容。
+#### LinkedHashSet
-### 集合排序
+ LinkedHashSet 继承HashSet,HashMap实现数据存储,双向链表记录顺序。LinkedHashSet 底层使用的LinkedHashMap存储元素,它继承了HashMap。
-在 Java 语言中排序提供了两种方式:Comparable 和 Comparator,它们的区别也是常见的面试题之一。下面我们彻底地来了解一下 Comparable 和 Comparator 的使用与区别。
+### Quenue
-#### 1)Comparable
+ Quenue是队列结构,Java中的常用队列如下:
-Comparable 位于 java.lang 包下,是一个排序接口,也就是说如果一个类实现了 Comparable 接口,就意味着该类有了排序功能。
-
-Comparable 接口只包含了一个函数,定义如下:
-
-```
-package java.lang;
-import java.util.*;
-public interface Comparable {
- public int compareTo(T o);
-}
-```
-
-**Comparable 使用示例**,请参考以下代码:
-
-```xml
-class ComparableTest {
- public static void main(String[] args) {
- Dog[] dogs = new Dog[]{
- new Dog("老旺财", 10),
- new Dog("小旺财", 3),
- new Dog("二旺财", 5),
- };
- // Comparable 排序
- Arrays.sort(dogs);
- for (Dog d : dogs) {
- System.out.println(d.getName() + ":" + d.getAge());
- }
- }
-}
-class Dog implements Comparable {
- private String name;
- private int age;
- @Override
- public int compareTo(Dog o) {
- return age - o.age;
- }
- public Dog(String name, int age) {
- this.name = name;
- this.age = age;
- }
- public String getName() {
- return name;
- }
- public int getAge() {
- return age;
- }
-}
-```
-
-程序执行结果:
-
-```
-小旺财:3
-二旺财:5
-老旺财:10
-```
-
-如果 Dog 类未实现 Comparable 执行代码会报程序异常的信息,错误信息为:
-
-> Exception in thread "main" java.lang.ClassCastException: xxx cannot be cast to java.lang.Comparable
->
-> compareTo() 返回值有三种:
-
-- e1.compareTo(e2)> 0 即 e1> e2;
-- e1.compareTo(e2) = 0 即 e1 = e2;
-- e1.compareTo(e2) < 0 即 e1 < e2。 - -#### 2)Comparator - -Comparator 是一个外部比较器,位于 java.util 包下,之所以说 Comparator 是一个外部比较器,是因为它无需在比较类中实现 Comparator 接口,而是要新创建一个比较器类来进行比较和排序。 - -Comparator 接口包含的主要方法为 compare(),定义如下: - -``` -public interface Comparator {
- int compare(T o1, T o2);
-}
-```
-
-**Comparator 使用示例**,请参考以下代码:
-
-```xml
-class ComparatorTest {
- public static void main(String[] args) {
- Dog[] dogs = new Dog[]{
- new Dog("老旺财", 10),
- new Dog("小旺财", 3),
- new Dog("二旺财", 5),
- };
- // Comparator 排序
- Arrays.sort(dogs,new DogComparator());
- for (Dog d : dogs) {
- System.out.println(d.getName() + ":" + d.getAge());
- }
- }
-}
-class DogComparator implements Comparator {
- @Override
- public int compare(Dog o1, Dog o2) {
- return o1.getAge() - o2.getAge();
- }
-}
-class Dog {
- private String name;
- private int age;
- public Dog(String name, int age) {
- this.name = name;
- this.age = age;
- }
- public String getName() {
- return name;
- }
- public int getAge() {
- return age;
- }
-}
-```
-
-程序执行结果:
-
-```
-小旺财:3
-二旺财:5
-老旺财:10
-```
+- ArrayBlockingQueue : 基于数组数据结构实现的有界阻塞队列。
+- LinkedBlockingQueue : 基于链表数据结构实现的有界阻塞队列。
+- PriorityBlockingQueue : 支持优先级排序的无界阻塞队列。
+- DelayQueue : 支持延迟操作的无界阻塞队列。
+- SynchronousQueue : 用于线程同步的阻塞队列。
+- LinkedTransferQueue : 基于链表数据结构实现的无界阻塞队列。
+- LinkedBlockingDeque : 基于链表数据结构实现双向阻塞队列。 吧
### 相关面试题
@@ -283,7 +99,7 @@ class Dog {
Vector 默认容量源码:
-```
+```java
public Vector() {
this(10);
}
@@ -291,13 +107,13 @@ public Vector() {
ArrayList 默认容量源码:
-```
+```java
private static final int DEFAULT_CAPACITY = 10;
```
Vector 容量扩充默认增加 1 倍,源码如下:
-```
+```java
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
@@ -315,7 +131,7 @@ private void grow(int minCapacity) {
ArrayList 容量扩充默认增加大概 0.5 倍(oldCapacity + (oldCapacity>> 1)),源码如下(JDK 8):
-```
+```java
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
@@ -377,7 +193,7 @@ public boolean add(E e) {
#### 10.执行以下程序会输出什么结果?为什么?
-```
+```java
Integer num = 10;
Integer num2 = 5;
System.out.println(num.compareTo(num2));
@@ -385,7 +201,7 @@ System.out.println(num.compareTo(num2));
答:程序输出的结果是 `1`,因为 Integer 默认实现了 compareTo 方法,定义了自然排序规则,所以当 num 比 num2 大时会返回 1,Integer 相关源码如下:
-```
+```java
public int compareTo(Integer anotherInteger) {
return compare(this.value, anotherInteger.value);
}
@@ -398,7 +214,7 @@ public static int compare(int x, int y) {
答:可以使用集合中的 Stack 实现,Stack 是标准的后进先出的栈结构,使用 Stack 中的 pop() 方法返回栈顶元素并删除该元素,示例代码如下。
-```
+```java
Stack stack = new Stack();
stack.push("a");
stack.push("b");
diff --git "a/docs/java/347円220円206円350円247円243円351円233円206円345円220円210円Map.md" "b/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円351円233円206円345円220円210円Map.md"
similarity index 99%
rename from "docs/java/347円220円206円350円247円243円351円233円206円345円220円210円Map.md"
rename to "docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円351円233円206円345円220円210円Map.md"
index 8870052..d6f2b9f 100644
--- "a/docs/java/347円220円206円350円247円243円351円233円206円345円220円210円Map.md"
+++ "b/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円351円233円206円345円220円210円Map.md"
@@ -4,7 +4,7 @@
以下是 Map 的继承关系图:
-
+avatar
### Map 简介
diff --git "a/docs/jvm/Java345円206円205円345円255円230円346円250円241円345円236円213円.md" "b/docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円351円235円242円345円220円221円345円257円271円350円261円241円.md"
similarity index 100%
rename from "docs/jvm/Java345円206円205円345円255円230円346円250円241円345円236円213円.md"
rename to "docs/Java346円240円270円345円277円203円345円237円272円347円241円200円/347円220円206円350円247円243円351円235円242円345円220円221円345円257円271円350円261円241円.md"
diff --git "a/docs/java/ArrayList346円272円220円347円240円201円345円210円206円346円236円220円.md" "b/docs/Java346円272円220円347円240円201円345円210円206円346円236円220円/ArrayList346円272円220円347円240円201円345円210円206円346円236円220円.md"
similarity index 99%
rename from "docs/java/ArrayList346円272円220円347円240円201円345円210円206円346円236円220円.md"
rename to "docs/Java346円272円220円347円240円201円345円210円206円346円236円220円/ArrayList346円272円220円347円240円201円345円210円206円346円236円220円.md"
index 1926745..36851e0 100644
--- "a/docs/java/ArrayList346円272円220円347円240円201円345円210円206円346円236円220円.md"
+++ "b/docs/Java346円272円220円347円240円201円345円210円206円346円236円220円/ArrayList346円272円220円347円240円201円345円210円206円346円236円220円.md"
@@ -1,8 +1,4 @@
-
-
-
-
-# ArrayList源码分析
+# 👉 ArrayList源码分析
## 1 底层结构
diff --git "a/docs/java/ConcurrentHashMap346円272円220円347円240円201円345円210円206円346円236円220円.md" "b/docs/Java346円272円220円347円240円201円345円210円206円346円236円220円/ConcurrentHashMap346円272円220円347円240円201円345円210円206円346円236円220円.md"
similarity index 99%
rename from "docs/java/ConcurrentHashMap346円272円220円347円240円201円345円210円206円346円236円220円.md"
rename to "docs/Java346円272円220円347円240円201円345円210円206円346円236円220円/ConcurrentHashMap346円272円220円347円240円201円345円210円206円346円236円220円.md"
index e32c62d..0c646d3 100644
--- "a/docs/java/ConcurrentHashMap346円272円220円347円240円201円345円210円206円346円236円220円.md"
+++ "b/docs/Java346円272円220円347円240円201円345円210円206円346円236円220円/ConcurrentHashMap346円272円220円347円240円201円345円210円206円346円236円220円.md"
@@ -1,4 +1,4 @@
-## ConcurrentHashMap源码分析
+# 👉 ConcurrentHashMap源码分析
当我们碰到线程不安全场景下,需要使用 Map 的时候,我们第一个想到的 API 估计就是 ConcurrentHashMap,ConcurrentHashMap 内部封装了锁和各种数据结构来保证访问 Map 是线程安全的,接下来我们一一来看下,和 HashMap 相比,多了哪些数据结构,又是如何保证线程安全的。
diff --git "a/docs/java/HashMap346円272円220円347円240円201円345円210円206円346円236円220円.md" "b/docs/Java346円272円220円347円240円201円345円210円206円346円236円220円/HashMap346円272円220円347円240円201円345円210円206円346円236円220円.md"
similarity index 99%
rename from "docs/java/HashMap346円272円220円347円240円201円345円210円206円346円236円220円.md"
rename to "docs/Java346円272円220円347円240円201円345円210円206円346円236円220円/HashMap346円272円220円347円240円201円345円210円206円346円236円220円.md"
index f578794..a648254 100644
--- "a/docs/java/HashMap346円272円220円347円240円201円345円210円206円346円236円220円.md"
+++ "b/docs/Java346円272円220円347円240円201円345円210円206円346円236円220円/HashMap346円272円220円347円240円201円345円210円206円346円236円220円.md"
@@ -1,4 +1,4 @@
-## HashMap源码分析
+# 👉 HashMap源码分析
## 1 整体架构
diff --git "a/docs/java/HashSet344円270円216円TreeSet.md" "b/docs/Java346円272円220円347円240円201円345円210円206円346円236円220円/HashSet344円270円216円TreeSet346円272円220円347円240円201円345円210円206円346円236円220円.md"
similarity index 99%
rename from "docs/java/HashSet344円270円216円TreeSet.md"
rename to "docs/Java346円272円220円347円240円201円345円210円206円346円236円220円/HashSet344円270円216円TreeSet346円272円220円347円240円201円345円210円206円346円236円220円.md"
index c83671a..cc56b9d 100644
--- "a/docs/java/HashSet344円270円216円TreeSet.md"
+++ "b/docs/Java346円272円220円347円240円201円345円210円206円346円236円220円/HashSet344円270円216円TreeSet346円272円220円347円240円201円345円210円206円346円236円220円.md"
@@ -1,4 +1,4 @@
-## HashSet与TreeSet
+# 👉 HashSet与TreeSet
HashSet、TreeSet 两个类是在 Map 的基础上组装起来的类,我们学习的侧重点,主要在于 Set 是如何利用 Map 现有的功能,来达成自己的目标的,也就是说如何基于现有的功能进行创新,然后再看看一些改变的小细节是否值得我们学习。
diff --git "a/docs/java/LinkedHashMap346円272円220円347円240円201円345円210円206円346円236円220円.md" "b/docs/Java346円272円220円347円240円201円345円210円206円346円236円220円/LinkedHashMap346円272円220円347円240円201円345円210円206円346円236円220円.md"
similarity index 99%
rename from "docs/java/LinkedHashMap346円272円220円347円240円201円345円210円206円346円236円220円.md"
rename to "docs/Java346円272円220円347円240円201円345円210円206円346円236円220円/LinkedHashMap346円272円220円347円240円201円345円210円206円346円236円220円.md"
index ecd3e53..897ddbb 100644
--- "a/docs/java/LinkedHashMap346円272円220円347円240円201円345円210円206円346円236円220円.md"
+++ "b/docs/Java346円272円220円347円240円201円345円210円206円346円236円220円/LinkedHashMap346円272円220円347円240円201円345円210円206円346円236円220円.md"
@@ -1,4 +1,4 @@
-# LinkedHashMap源码分析
+# 👉 LinkedHashMap源码分析
HashMap 是无序的,TreeMap 可以按照 key 进行排序,那有木有 Map 是可以维护插入的顺序的呢?接下来我们一起来看下 LinkedHashMap。
diff --git "a/docs/Java346円272円220円347円240円201円345円210円206円346円236円220円/README.md" "b/docs/Java346円272円220円347円240円201円345円210円206円346円236円220円/README.md"
new file mode 100644
index 0000000..a788b3f
--- /dev/null
+++ "b/docs/Java346円272円220円347円240円201円345円210円206円346円236円220円/README.md"
@@ -0,0 +1,8 @@
+# Java并发编程
+
+* [ArrayList源码分析](docs/Java源码分析/ArrayList源码分析.md)
+* [HashMap源码分析](docs/Java源码分析/HashMap源码分析.md)
+* [HashSet与TreeSet源码分析](docs/Java源码分析/HashSet与TreeSet源码分析.md)
+* [LinkedHashMap源码分析](docs/Java源码分析/LinkedHashMap源码分析.md)
+* [TreeMap源码分析](docs/Java源码分析/TreeMap源码分析.md)
+
diff --git "a/docs/java/TreeMap346円272円220円347円240円201円345円210円206円346円236円220円.md" "b/docs/Java346円272円220円347円240円201円345円210円206円346円236円220円/TreeMap346円272円220円347円240円201円345円210円206円346円236円220円.md"
similarity index 99%
rename from "docs/java/TreeMap346円272円220円347円240円201円345円210円206円346円236円220円.md"
rename to "docs/Java346円272円220円347円240円201円345円210円206円346円236円220円/TreeMap346円272円220円347円240円201円345円210円206円346円236円220円.md"
index 58715aa..5db0820 100644
--- "a/docs/java/TreeMap346円272円220円347円240円201円345円210円206円346円236円220円.md"
+++ "b/docs/Java346円272円220円347円240円201円345円210円206円346円236円220円/TreeMap346円272円220円347円240円201円345円210円206円346円236円220円.md"
@@ -1,4 +1,4 @@
-# TreeMap源码分析
+# 👉 TreeMap源码分析
## 1 知识储备
diff --git "a/docs/Java346円272円220円347円240円201円345円210円206円346円236円220円/_sidebar.md" "b/docs/Java346円272円220円347円240円201円345円210円206円346円236円220円/_sidebar.md"
new file mode 100644
index 0000000..bf4f932
--- /dev/null
+++ "b/docs/Java346円272円220円347円240円201円345円210円206円346円236円220円/_sidebar.md"
@@ -0,0 +1,6 @@
+* **👉 Java源码分析** [↩](/README)
+ * [ArrayList源码分析](docs/Java源码分析/ArrayList源码分析.md)
+ * [HashMap源码分析](docs/Java源码分析/HashMap源码分析.md)
+ * [HashSet与TreeSet源码分析](docs/Java源码分析/HashSet与TreeSet源码分析.md)
+ * [LinkedHashMap源码分析](docs/Java源码分析/LinkedHashMap源码分析.md)
+ * [TreeMap源码分析](docs/Java源码分析/TreeMap源码分析.md)
\ No newline at end of file
diff --git "a/docs/Java350円231円232円346円213円237円346円234円272円/JVM347円241円256円350円256円244円345円217円257円345円233円236円346円224円266円345円257円271円350円261円241円347円232円204円346円226円271円345円274円217円.md" "b/docs/Java350円231円232円346円213円237円346円234円272円/JVM347円241円256円350円256円244円345円217円257円345円233円236円346円224円266円345円257円271円350円261円241円347円232円204円346円226円271円345円274円217円.md"
new file mode 100644
index 0000000..596b465
--- /dev/null
+++ "b/docs/Java350円231円232円346円213円237円346円234円272円/JVM347円241円256円350円256円244円345円217円257円345円233円236円346円224円266円345円257円271円350円261円241円347円232円204円346円226円271円345円274円217円.md"
@@ -0,0 +1,110 @@
+# 👉 JVM确认可回收对象的方式
+
+## 前言
+
+在开始之前,我们先回顾一下`堆`是个什么玩意,大家可能都知道,我们每天创建的Java对象几乎都存放在堆上面,所以说堆是一个巨大的对象池一点都不过分,在这个对象池里面管理者数据巨大的对象实例。
+
+在对象池中对象的引用层次,有的是很深的。比如一个调用非常频繁的接口,生产对象的速度是非常可观的。对象之间的关系,可以形容成一张网。虽然Java总是给人一种有使不完的内存的感觉,但是对象也不能一直增加不减少啊,所以就必须有**垃圾回收**这个操作。
+
+## JVM是怎么认识`垃圾`的呢?
+
+**"垃圾回收"本文中简称 GC**
+
+你还记得电视剧中的"诛九族""?
+
+img
+
+比如小憨批打了皇帝老儿一巴掌,把皇帝老儿打的鼻青脸肿滴,皇帝老儿非常生气,他要下令诛小憨批的九族,以平心头只恨。
+
+哈哈哈嗝~ 小憨批完了~
+
+img
+
+那么我们看看在古代这个诛九族是具体操作的呢?首先需要追溯到共同的祖先(也就是小憨批家族的大哥大),再往下逐一细数和小憨批有关系的(小憨批真坑啊)。
+
+img
+
+
+
+其实发生在堆上的垃圾回收和这个"诛九族"的是相同思路,那么我们下面具体分析一下JVM是如何进行GC的呢?
+
+**关于JVM的GC是不受程序控制的,当满足一定条件的时候就会主动触发。**
+
+当发生GC的时候,对于一个对象来说,JVM总能够找到引用它的祖先,当找到最后的时候,JVM发现这家伙的有些祖先已经玩完了,那么它们就会被JVM给干掉。
+
+为什么还有没有被干掉的祖先呢?因为这些躲过GC的祖先们,它们是GC Roots ,长得比较特殊嘛。
+
+当从GC Roots 向下追溯、搜索,就会产生一个引用链。当碰到有对象没有任何一个GC Roots 产生关系的话,这个对象就会被无情的干掉。(一根绳上的蚂蚱嘛)
+
+来,我们画个图瞅瞅咋回事,如下图所示,Obj5、Obj6、Obj7,由于不能和 GC Root 产生关联,发生 GC 时,就会被摧毁。
+
+img
+
+
+
+其实所谓的垃圾回收就是围绕着GC Roots 来的,但是同时,GC Roots 也存在着很多内存泄漏的根源,因为其他引用小弟压根没有这个权利。
+
+那么什么样的对象才会是GC Roots 呢? 这个不在于它是什么样的对象,关键是它所处的位置(仔细品~)。
+
+## GC Roots 是什么
+
+首先,GC Roots必须是一组必须活跃的引用。简单的讲,就是程序接下来通过直接引用或间接引用,能够被访问到的潜在被使用的对象(咋感觉还是有点绕呢)。
+
+**GC Roots:**
+
+1. Java线程中,当前所有正在被调用的方法的引用类型参数、局部变量、临时值等等。也就是与我们栈帧相关的各种引用。
+2. 所有当前被加载的Java类。
+3. Java类的引用类型静态变量。
+4. 运行时常量池里的引用类型常量。
+5. JVM内部数据结构的一些引用,比如sun.jvm.hotspot.memory.Univers类。
+6. 用于同步的监控对象。比如调用了对象的wait()方法。
+7. JNI handles,包括global handles 和 local handles。
+
+以上GC Roots大致可以分为一下三大类。
+
+1. 活动线程相关的各种引用。
+2. 类的静态变量的引用。
+3. JNI引用。
+
+最后我们需要注意的是,我们这里说的是活跃的引用,而不是对象,对象是不能作为GC Roots的。
+
+整个GC过程中是找到那些活对象,并把剩余的空间都认得为"无用"。而不是找到所有死掉的对象,并回收它们占用的空间。所有说,哪怕JVM的堆非常大,基于tracing的GC方式,回收速度也是跟快的。
+
+## 总结
+
+ GC Roots 就是可达性分析法。还有一种叫作引用计数法的方式。下面我们简单介绍一下。
+
+引用计数法:在Java中如果要操作对象,就必须先获取该对象的引用,因此可以通过引用计数法来判断一个对象是否可以被回收。在为一个对象添加一个引用时,引用计数器就加1;为对象删除一个引用时,引用计数器就减1;如果一个对象的引用计数为0,则说明该对象没有被引用,可以回收。**优点是垃圾回收比较及时,实时性比较高,只要对象计数器为 0,则可以直接进行回收操作;而缺点是无法解决循环引用的问题。**
+
+因为存在循环引用这个致命的硬伤,没有一个是采用引用计数法来实现 GC 的,所有你现在完全忘记引用计数这种方式了。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/docs/Java350円231円232円346円213円237円346円234円272円/Java350円277円220円350円241円214円346円227円266円345円206円205円345円255円230円345円210円222円345円210円206円.md" "b/docs/Java350円231円232円346円213円237円346円234円272円/Java350円277円220円350円241円214円346円227円266円345円206円205円345円255円230円345円210円222円345円210円206円.md"
new file mode 100644
index 0000000..d5931c9
--- /dev/null
+++ "b/docs/Java350円231円232円346円213円237円346円234円272円/Java350円277円220円350円241円214円346円227円266円345円206円205円345円255円230円345円210円222円345円210円206円.md"
@@ -0,0 +1,36 @@
+# 👉 JVM的内存区域
+
+ **线程私有区域**的生命周期与线程相同,随线程的启动而创建,随线程的启动而创建,随线程的结束而销毁。在JVM内部,每个线程都与操作系统的本地线程直接映射,因此线程私有内存区域的存在与否和本地线程的启动和销毁对应。
+
+ **线程共享区域**随虚拟机的启动而创建,随虚拟机的关闭而销毁。
+
+## 程序计数器 : 线程私有,无内存溢出问题
+
+ 程序计数器是一块很小的内存空间,用于存储当前运行的线程所执行的字节码的行号指令,每个运行中的线程都有一个独立的程序计数器,在方法正在执行时,该方法的程序计数器记录的是实时虚拟机字节码指令的地址;如果该方法执行的是Native方法,则程序计数器的值为空。
+
+ 程序计数器是唯一没有内存溢出的区域。
+
+## 虚拟机栈 : 线程私有,描述Java方法的执行过程
+
+ 虚拟机栈是描述Java方法的执行过程的内存模型,它在当前栈帧存储了局部变量表、操作数栈、动态链接、方法出口等信息。同时,栈帧用来存储部分运行时数据及其数据结构,处理动态链接方法的返回值和异常分派。
+
+ 栈帧用来记录方法的执行过程,在方法被执行时虚拟机会为其创建一个与之对应的栈帧,方法的执行和返回对应栈帧在虚拟机中的入栈和出栈。无法方法是正常运行完成还是异常完成,都可以视为方法运行结束。
+
+## 本地方法区 : 线程私有
+
+ 本地方法区和虚拟机栈作用类似,区别就是本地方法栈是为Native方法服务,而虚拟机栈是为了Java方法服务。
+
+## 堆内存 : 线程共享,运行时数据区
+
+ 在JVM运行过程中创建的对象和产生的数据都被存储在堆中,堆是被线程共享的内存区域,也是垃圾收集器进行垃圾回首的最主要的内存区域。
+
+## 方法区 :线程共享
+
+ 方法区用于存储常量、静态变量、类信息、即时编译器编译后的机器码、运行时常量池等数据。
+
+ JVM把GC分代收集扩展至方法区,即使用Java堆的永久代来实现方法区,这样JVM的垃圾收集器就可以像管理Java堆一样管理这部分内存。永久带的内存回收主要针对常量池的回收和类的卸载,因此可回收的对象很少。
+
+ 常量被存储在运行时常量池中,是方法区的一部分。静态也属于方法区的一部分。在类信息中不但保存了类的版本/字段/方法/接口等描述信息,还保存了常量信息。
+
+ 在即使编译后,代码的内容将在执行阶段被保存在方法区的运行时常量池中。Java虚拟机堆Class文件每一部分的格式都明确的规定,只有符合规范的Class文件才能通过虚拟机的检查,然后被装载和执行。
+
diff --git "a/docs/Java350円231円232円346円213円237円346円234円272円/README.md" "b/docs/Java350円231円232円346円213円237円346円234円272円/README.md"
new file mode 100644
index 0000000..bac0e28
--- /dev/null
+++ "b/docs/Java350円231円232円346円213円237円346円234円272円/README.md"
@@ -0,0 +1,9 @@
+# JVM虚拟机
+
+ * [Java运行时内存划分](docs/Java虚拟机/Java运行时内存划分.md)
+ * [JVM确认可回收对象的方式](docs/Java虚拟机/JVM确认可回收对象的方式.md)
+ * [类加载机制](docs/Java虚拟机/类加载机制.md)
+ * [双亲委派机制](docs/Java虚拟机/双亲委派机制.md)
+ * [四种引用类型](docs/Java虚拟机/四种引用类型.md)
+ * [垃圾回收器](docs/Java虚拟机/垃圾回收器.md)
+ * [垃圾回收算法](docs/Java虚拟机/Jav垃圾回收算法a运行时内存划分.md)
diff --git "a/docs/Java350円231円232円346円213円237円346円234円272円/_sidebar.md" "b/docs/Java350円231円232円346円213円237円346円234円272円/_sidebar.md"
new file mode 100644
index 0000000..890d76e
--- /dev/null
+++ "b/docs/Java350円231円232円346円213円237円346円234円272円/_sidebar.md"
@@ -0,0 +1,8 @@
+* **👉 Java虚拟机** [↩](/README)
+ * [Java运行时内存划分](docs/Java虚拟机/Java运行时内存划分.md)
+ * [JVM确认可回收对象的方式](docs/Java虚拟机/JVM确认可回收对象的方式.md)
+ * [类加载机制](docs/Java虚拟机/类加载机制.md)
+ * [双亲委派机制](docs/Java虚拟机/双亲委派机制.md)
+ * [四种引用类型](docs/Java虚拟机/四种引用类型.md)
+ * [垃圾回收器](docs/Java虚拟机/垃圾回收器.md)
+ * [垃圾回收算法](docs/Java虚拟机/Jav垃圾回收算法a运行时内存划分.md)
diff --git "a/docs/Java350円231円232円346円213円237円346円234円272円/345円217円214円344円272円262円345円247円224円346円264円276円346円234円272円345円210円266円.md" "b/docs/Java350円231円232円346円213円237円346円234円272円/345円217円214円344円272円262円345円247円224円346円264円276円346円234円272円345円210円266円.md"
new file mode 100644
index 0000000..82b9862
--- /dev/null
+++ "b/docs/Java350円231円232円346円213円237円346円234円272円/345円217円214円344円272円262円345円247円224円346円264円276円346円234円272円345円210円266円.md"
@@ -0,0 +1,17 @@
+# 👉 双亲委派机制
+
+ 双亲委派机制是指一个类在收到类加载请求之后不会尝试自己加载这个类,而是把该类加载请求向上委派给其父类,其父类在接受到该类的加载请求之后又会将其委派给自己的父类,以此类推,这样所有的类加载请求都被向上委派到启动类加载器中。
+
+ 但是若父类加载器在接收到类加载请求后发现自己也无法加载该类,则父类会将该信息反馈给子类并向下委派子类加载器,直到该类被成功加载,若找不到该类,则JVM会抛出ClassNotFind异常。
+
+ **双亲委派加载机制的类加载流程。**
+
+1. 将自定义加载器挂载到应用程序类加载器。
+2. 应用程序类加载器将类加载请求委托给扩展类加载器。
+3. 扩展器加载器将类加载请求委托给启动类加载器。
+4. 启动类加载器在加载路径下查找并加载Class文件,如果未找到目标Class文件,则交给应用程序类加载器加载。
+5. 扩张类加载器在加载路径下查找并加载Class文件,如果未找到目标Class文件,则交由应用程序类加载器加载。
+6. 应用程序类加载器在加载路径下查找并加载Class文件,如果未找到目标Class文件,则交由自定义类加载器加载。
+7. 在自定义加载器下查找并加载用户指定目录下的Class文件,如果在自定义加载路径下为找到目标Class文件,则抛出ClassNotFind异常。
+
+ **双亲委派加载机制的核心就是保证类的唯一性和安全性。**
\ No newline at end of file
diff --git "a/docs/Java350円231円232円346円213円237円346円234円272円/345円233円233円347円247円215円345円274円225円347円224円250円347円261円273円345円236円213円.md" "b/docs/Java350円231円232円346円213円237円346円234円272円/345円233円233円347円247円215円345円274円225円347円224円250円347円261円273円345円236円213円.md"
new file mode 100644
index 0000000..1bfca77
--- /dev/null
+++ "b/docs/Java350円231円232円346円213円237円346円234円272円/345円233円233円347円247円215円345円274円225円347円224円250円347円261円273円345円236円213円.md"
@@ -0,0 +1,114 @@
+# 👉 四种引用类型
+Java中一切皆为对象,Java中的引用类型有四种,分别为强引用/软引用/弱引用/虚引用等。
+
+## 强引用—trong references
+
+当内存空间不足,系统撑不住了,JVM 就会抛出 OutOfMemoryError 错误。即使程序会异常终止,这种对象也不会被回收。这种引用属于最普通最强硬的一种存在,只有在和 GC Roots 断绝关系时,才会被消灭掉。
+
+这种引用,你每天的编码都在用。例如:new 一个普通的对象。
+
+```java
+Object obj = new Object()
+```
+
+这种方式可能是有问题的。假如你的系统被大量用户(User)访问,你需要记录这个 User 访问的时间。可惜的是,User 对象里并没有这个字段,所以我们决定将这些信息额外开辟一个空间进行存放。
+
+```java
+static Map userVisitMap = new HashMap();
+
+...
+
+userVisitMap.put(user, time);
+```
+
+当你用完了 User 对象,其实你是期望它被回收掉的。但是,由于它被 userVisitMap 引用,我们没有其他手段 remove 掉它。这个时候,就发生了内存泄漏(memory leak)。
+
+这种情况还通常发生在一个没有设定上限的 Cache 系统,由于设置了不正确的引用方式,加上不正确的容量,很容易造成 OOM。
+
+## 软引用—Soft references
+
+软引用用于维护一些可有可无的对象。在内存足够的时候,软引用对象不会被回收,只有在内存不足时,系统则会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,才会抛出内存溢出异常。
+
+可以看到,这种特性非常适合用在缓存技术上。比如网页缓存、图片缓存等。
+
+Guava 的 CacheBuilder,就提供了软引用和弱引用的设置方式。在这种场景中,软引用比强引用安全的多。
+
+软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,Java 虚拟机就会把这个软引用加入到与之关联的引用队列中。
+
+我们可以看一下它的代码。软引用需要显式的声明,使用泛型来实现。
+
+```java
+// 伪代码
+
+Object object = new Object();
+
+SoftReference softRef = new SoftReference(object);
+```
+
+这里有一个相关的 JVM 参数。它的意思是:每 MB 堆空闲空间中 SoftReference 的存活时间。这个值的默认时间是1秒(1000)。
+
+```java
+-XX:SoftRefLRUPolicyMSPerMB=
+```
+
+这里要特别说明的是,网络上一些流传的优化方法,即把这个值设置成 0,其实是错误的,这样容易引发故障,感兴趣的话你可以自行搜索一下。
+
+这种比较偏门的优化手段,除非在你对其原理相当了解的情况下,才能设置一些比较特殊的值。比如 0 值,无限大等,这种值在 JVM 的设置中,最好不要发生。
+
+## 弱引用—Weak references
+
+弱引用对象相比较软引用,要更加无用一些,它拥有更短的生命周期。
+
+当 JVM 进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。弱引用拥有更短的生命周期,在 Java 中,用 java.lang.ref.WeakReference 类来表示。
+
+它的应用场景和软引用类似,可以在一些对内存更加敏感的系统里采用。它的使用方式类似于这段的代码:
+
+```java
+// 伪代码
+
+Object object = new Object();
+
+WeakReference softRef = new WeakReference(object);
+```
+
+## 虚引用—Phantom References
+
+这是一种形同虚设的引用,在现实场景中用的不是很多。虚引用必须和引用队列(ReferenceQueue)联合使用。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。
+
+实际上,虚引用的 get,总是返回 null。
+
+```java
+Object object = new Object();
+
+ReferenceQueue queue = new ReferenceQueue();
+
+// 虚引用,必须与一个引用队列关联
+
+PhantomReference pr = new PhantomReference(object, queue);
+```
+
+虚引用主要用来跟踪对象被垃圾回收的活动。
+
+当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象之前,把这个虚引用加入到与之关联的引用队列中。
+
+程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。
+
+下面的方法,就是一个用于监控 GC 发生的例子。
+
+```java
+private static void startMonitoring(ReferenceQueue referenceQueue, Reference ref) {
+ ExecutorService ex = Executors.newSingleThreadExecutor();
+ ex.execute(() -> {
+ while (referenceQueue.poll()!=ref) {
+ //don't hang forever
+ if(finishFlag){
+ break;
+ }
+ }
+ System.out.println("-- ref gc'ed --");
+ });
+ ex.shutdown();
+}
+```
+
+基于虚引用,有一个更加优雅的实现方式,那就是 Java 9 以后新加入的 Cleaner,用来替代 Object 类的 finalizer 方法。
\ No newline at end of file
diff --git "a/docs/jvm/345円236円203円345円234円276円345円233円236円346円224円266円345円231円250円.md" "b/docs/Java350円231円232円346円213円237円346円234円272円/345円236円203円345円234円276円345円233円236円346円224円266円345円231円250円.md"
similarity index 95%
rename from "docs/jvm/345円236円203円345円234円276円345円233円236円346円224円266円345円231円250円.md"
rename to "docs/Java350円231円232円346円213円237円346円234円272円/345円236円203円345円234円276円345円233円236円346円224円266円345円231円250円.md"
index 876f8ce..5699fe4 100644
--- "a/docs/jvm/345円236円203円345円234円276円345円233円236円346円224円266円345円231円250円.md"
+++ "b/docs/Java350円231円232円346円213円237円346円234円272円/345円236円203円345円234円276円345円233円236円346円224円266円345円231円250円.md"
@@ -1,4 +1,4 @@
-# 垃圾回收器
+# 👉 垃圾回收器

diff --git "a/docs/Java350円231円232円346円213円237円346円234円272円/345円236円203円345円234円276円345円233円236円346円224円266円347円256円227円346円263円225円.md" "b/docs/Java350円231円232円346円213円237円346円234円272円/345円236円203円345円234円276円345円233円236円346円224円266円347円256円227円346円263円225円.md"
new file mode 100644
index 0000000..b937626
--- /dev/null
+++ "b/docs/Java350円231円232円346円213円237円346円234円272円/345円236円203円345円234円276円345円233円236円346円224円266円347円256円227円346円263円225円.md"
@@ -0,0 +1,113 @@
+# 👉 垃圾回收算法
+
+## 复制算法
+
+- 将可用内存按容量划分为相等的两部分,每次只使用其中的一块,当一块内存用完时,就将还存活的对象复制到第二块内存上,然后一次性清除第一块内存,再将第二块上的对象复制到第一块。
+- 实现方便,运行高效,不用考虑内存碎片,但是内存利用率只有一半。
+
+## 标记清除算法
+
+- 分为**标记**和**清除**两个阶段。首先标记出所有需要回收的对象,在标记完成后统一回收被标记的对象。
+- 算法简单,但是有两个缺点:
+ - 1、效率不高,标记和清除的效率都很低;
+ - 2、空间问题,会产生大量不连续的内存碎片,导致以后程序在分配较大的对象时,由于没有充足的连续内存而提前触发一次 GC 动作。
+
+## 标记整理算法
+
+- 标记过程仍然与 "标记 - 清除" 算法一样,但不是直接对可回收对象进行清理,而是让所有存活的对象向一端移动,然后直接清理掉边界以外的内存,形成一版连续的内存区域。
+- 解决标记 - 清除算法产生的大量内存碎片问题;当对象存活率较高时,也解决了复制算法的空间效率问题,不过它本身也存在时间效率方面的问题。
+
+## 分代收集算法
+
+- 根据对象的生存周期,将堆分为新生代和老年代,然后根据各个年代的特点采用最适当的收集算法。在新生代中,由于对象生存期短,每次回收都会有大量对象死去,那么这时就采用**复制**算法。老年代里的对象存活率较高,没有额外的空间进行分配担保,所以可以使用**标记 - 整理** 或者 **标记 - 清除**。
+- 严格地说,这并非是一种算法,而是一种思想,或者说是一种复合算法。
+
+Java 的堆内存被分代管理,为什么要分代管理呢?分代管理主要是为了方便垃圾回收,这样做基于2个事实,第一,大部分对象很快就不再使用;第二,还有一部分不会立即无用,但也不会持续很长时间。
+
+ 虚拟机划分为年轻代、老年代、和永久代,如下图所示。
+
+img
+
+- 年轻代主要用来存放新创建的对象,年轻代分为 Eden 区和两个 Survivor 区。大部分对象在 Eden 区中生成。当 Eden 区满时,还存活的对象会在两个 Survivor 区交替保存,达到一定次数的对象会晋升到老年代。
+
+- 老年代用来存放从年轻代晋升而来的,存活时间较长的对象。
+
+- 永久代,主要保存类信息等内容,这里的永久代是指对象划分方式,不是专指 1.7 的 PermGen,或者 1.8 之后的 Metaspace。
+
+
+ 根据年轻代与老年代的特点,JVM 提供了不同的垃圾回收算法。垃圾回收算法按类型可以分为引用计数法、复制法和标记清除法。
+
+- 引用计数法是通过对象被引用的次数来确定对象是否被使用,缺点是无法解决循环引用的问题。
+
+- 复制算法需要 from 和 to 两块相同大小的内存空间,对象分配时只在 from 块中进行,回收时把存活对象复制到 to 块中,并清空 from 块,然后交换两块的分工,即把 from 块作为 to 块,把 to 块作为 from 块。缺点是内存使用率较低。
+
+- 标记清除算法分为标记对象和清除不在使用的对象两个阶段,标记清除算法的缺点是会产生内存碎片。
+
+
+ JVM 中提供的年轻代回收算法 Serial、ParNew、Parallel Scavenge 都是复制算法,而 CMS、G1、ZGC 都属于标记清除算法。
+
+## CMS 算法
+
+基于分代回收理论,详细介绍几个典型的垃圾回收算法,先来看 CMS 回收算法。CMS 在 JDK1.7 之前可以说是最主流的垃圾回收算法。CMS 使用标记清除算法,优点是并发收集,停顿小。
+
+CMS 算法如下图所示。
+
+img
+
+1. 第一个阶段是初始标记,这个阶段会 stop the world,标记的对象只是从 root 集最直接可达的对象;
+
+2. 第二个阶段是并发标记,这时 GC 线程和应用线程并发执行。主要是标记可达的对象;
+
+3. 第三个阶段是重新标记阶段,这个阶段是第二个 stop the world 的阶段,停顿时间比并发标记要小很多,但比初始标记稍长,主要对对象进行重新扫描并标记;
+
+4. 第四个阶段是并发清理阶段,进行并发的垃圾清理;
+
+5. 最后一个阶段是并发重置阶段,为下一次 GC 重置相关数据结构。
+
+## G1 算法
+
+G1 在 1.9 版本后成为 JVM 的默认垃圾回收算法,G1 的特点是保持高回收率的同时,减少停顿。
+
+G1 算法取消了堆中年轻代与老年代的物理划分,但它仍然属于分代收集器。G1 算法将堆划分为若干个区域,称作 Region,如下图中的小方格所示。一部分区域用作年轻代,一部分用作老年代,另外还有一种专门用来存储巨型对象的分区。
+
+img
+
+G1 也和 CMS 一样会遍历全部的对象,然后标记对象引用情况,在清除对象后会对区域进行复制移动整合碎片空间。
+
+
+
+G1 回收过程如下。
+
+- G1 的年轻代回收,采用复制算法,并行进行收集,收集过程会 STW。
+
+- G1 的老年代回收时也同时会对年轻代进行回收。主要分为四个阶段:
+
+ - 依然是初始标记阶段完成对根对象的标记,这个过程是STW的;
+
+ - 并发标记阶段,这个阶段是和用户线程并行执行的;
+ - 最终标记阶段,完成三色标记周期;
+ - 复制/清除阶段,这个阶段会优先对可回收空间较大的 Region 进行回收,即 garbage first,这也是 G1 名称的由来。
+
+G1 采用每次只清理一部分而不是全部的 Region 的增量式清理,由此来保证每次 GC 停顿时间不会过长。
+
+总结如下,G1 是逻辑分代不是物理划分,需要知道回收的过程和停顿的阶段。此外还需要知道,G1 算法允许通过 JVM 参数设置 Region 的大小,范围是 1〜32MB,可以设置期望的最大 GC 停顿时间等。有兴趣读者也可以对 CMS 和 G1 使用的三色标记算法做简单了解。
+
+## 考察点
+
+总结 JVM 相关的面试考察点如下:
+
+1. 深入了解 JVM 的内存模型和 Java 的内存模型;
+
+2. 要了解类的加载过程,了解双亲委派机制;
+
+3. 要理解内存的可见性与 Java 内存模型对原子性、可见性、有序性的保证机制;
+
+4. 要了解常用的 GC 算法的特点、执行过程,和适用场景,例如 G1 适合对最大延迟有要求的场合,ZGC 适用于 64 位系统的大内存服务中;
+
+5. 要了解常用的 JVM 参数,明白对不同参数的调整会有怎样的影响,适用什么样的场景,例如垃圾回收的并发数、偏向锁设置等。
+
+附录:JVM 相关的面试真题
+
+img
+
+img
\ No newline at end of file
diff --git "a/docs/jvm/347円261円273円345円212円240円350円275円275円346円234円272円345円210円266円.md" "b/docs/Java350円231円232円346円213円237円346円234円272円/347円261円273円345円212円240円350円275円275円346円234円272円345円210円266円.md"
similarity index 74%
rename from "docs/jvm/347円261円273円345円212円240円350円275円275円346円234円272円345円210円266円.md"
rename to "docs/Java350円231円232円346円213円237円346円234円272円/347円261円273円345円212円240円350円275円275円346円234円272円345円210円266円.md"
index 7275006..05cc850 100644
--- "a/docs/jvm/347円261円273円345円212円240円350円275円275円346円234円272円345円210円266円.md"
+++ "b/docs/Java350円231円232円346円213円237円346円234円272円/347円261円273円345円212円240円350円275円275円346円234円272円345円210円266円.md"
@@ -1,12 +1,14 @@
-# 类加载机制
+# 👉 类加载机制
JVM 通过双亲委派模型进行类的加载,即当某个类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。
+
+
**类加载器:**
-1. **启动类加载器 (Bootstrap ClassLoader)**:负责加载 JAVA_HOME\lib 目录中的,或通过 - Xbootclasspath 参数指定路径中的,且被虚拟机认可(按文件名识别,如 rt.jar,名字不符合的类库即使放在 lib 目录也不会被加载)的类。启动类加载器无法被 Java 程序直接引用;
-2. **扩展类加载器 (Extension ClassLoader)**:负责加载 JAVA_HOME\jre\lib\ext 目录中的,或通过 java.ext.dirs 系统变量指定路径中的类库;
-3. **应用程序类加载器 (Application ClassLoader)**:负责加载用户路径(classpath)上的类库。
+1. **启动类加载器 (Bootstrap ClassLoader)** :负责加载 JAVA_HOME\lib 目录中的,或通过 - Xbootclasspath 参数指定路径中的,且被虚拟机认可(按文件名识别,如 rt.jar,名字不符合的类库即使放在 lib 目录也不会被加载)的类。启动类加载器无法被 Java 程序直接引用;
+2. **扩展类加载器 (Extension ClassLoader)** :负责加载 JAVA_HOME\jre\lib\ext 目录中的,或通过 java.ext.dirs 系统变量指定路径中的类库;
+3. **应用程序类加载器 (Application ClassLoader)** :负责加载用户路径(classpath)上的类库。
4. 通过继承 java.lang.ClassLoader 类实现**自定义类加载器**(主要是重写 findClass 方法)。
**总结:** 类加载器和字节码是Java平台无关性的基石,对于任意一个类,都需要由它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性。
@@ -15,9 +17,14 @@ JVM 通过双亲委派模型进行类的加载,即当某个类加载器在接
1. 基础类的统一加载问题(越基础的类由越上层的加载器进行加载)。如类 java.lang.String,无论哪一个类加载器要加载这个类,最终都是委派给启动类加载器进行加载,所以在程序的各种类加载器环境中都是同一个类。
2. 提高 java 代码的安全性。比如说用户自定义了一个与系统库里同名的 java.lang.String 类,那么这个类就不会被加载,因为最顶层的类加载器会首先加载系统的 java.lang.String 类,而不会加载自定义的 String 类,防止了恶意代码的注入。
+3. 可以避免类的重复加载,另外也避免了 Java 的核心 API 被篡改。
+
+
# 类加载流程
+
+
类的生命周期会经历以下 7 个阶段:
- **加载阶段**
@@ -53,3 +60,5 @@ HotSpot 虚拟机在 JDK 1.7 之前都在方法区,而 JDK 1.8 之后此变量
初始化阶段 JVM 就正式开始执行类中编写的 Java 业务代码了。到这一步骤之后,类的加载过程就算正式完成了。
+如上图所示,浅绿的两个部分表示类的生命周期,就是从类的加载到类实例的创建与使用,再到类对象不再被使用时可以被 GC 卸载回收。这里要注意一点,由 Java 虚拟机自带的三种类加载器加载的类在虚拟机的整个生命周期中是不会被卸载的,只有用户自定义的类加载器所加载的类才可以被卸载。
+
diff --git "a/docs/MyBatis/346円267円261円345円205円245円345円211円226円346円236円220円 MyBatis 346円240円270円345円277円203円345円216円237円347円220円206円/MyBatis 344円270円211円345円261円202円346円236円266円346円236円204円345円233円276円.png" "b/docs/MyBatis/346円267円261円345円205円245円345円211円226円346円236円220円 MyBatis 346円240円270円345円277円203円345円216円237円347円220円206円/MyBatis 344円270円211円345円261円202円346円236円266円346円236円204円345円233円276円.png"
new file mode 100644
index 0000000..da76a46
Binary files /dev/null and "b/docs/MyBatis/346円267円261円345円205円245円345円211円226円346円236円220円 MyBatis 346円240円270円345円277円203円345円216円237円347円220円206円/MyBatis 344円270円211円345円261円202円346円236円266円346円236円204円345円233円276円.png" differ
diff --git "a/docs/MyBatis/346円267円261円345円205円245円345円211円226円346円236円220円 MyBatis 346円240円270円345円277円203円345円216円237円347円220円206円/MyBatis 346円211円247円350円241円214円344円270円200円346円235円241円 SQL 350円257円255円345円217円245円347円232円204円346円240円270円345円277円203円350円277円207円347円250円213円.png" "b/docs/MyBatis/346円267円261円345円205円245円345円211円226円346円236円220円 MyBatis 346円240円270円345円277円203円345円216円237円347円220円206円/MyBatis 346円211円247円350円241円214円344円270円200円346円235円241円 SQL 350円257円255円345円217円245円347円232円204円346円240円270円345円277円203円350円277円207円347円250円213円.png"
new file mode 100644
index 0000000..f7472fb
Binary files /dev/null and "b/docs/MyBatis/346円267円261円345円205円245円345円211円226円346円236円220円 MyBatis 346円240円270円345円277円203円345円216円237円347円220円206円/MyBatis 346円211円247円350円241円214円344円270円200円346円235円241円 SQL 350円257円255円345円217円245円347円232円204円346円240円270円345円277円203円350円277円207円347円250円213円.png" differ
diff --git "a/docs/database/B346円240円221円344円270円216円B+346円240円221円350円257円246円350円260円210円.md" "b/docs/MySQL/B346円240円221円344円270円216円B+346円240円221円350円257円246円350円260円210円.md"
similarity index 100%
rename from "docs/database/B346円240円221円344円270円216円B+346円240円221円350円257円246円350円260円210円.md"
rename to "docs/MySQL/B346円240円221円344円270円216円B+346円240円221円350円257円246円350円260円210円.md"
diff --git "a/docs/database/Hash347円264円242円345円274円225円344円270円216円B+346円240円221円347円264円242円345円274円225円347円232円204円345円214円272円345円210円253円.md" "b/docs/MySQL/Hash347円264円242円345円274円225円344円270円216円B+346円240円221円347円264円242円345円274円225円347円232円204円345円214円272円345円210円253円.md"
similarity index 100%
rename from "docs/database/Hash347円264円242円345円274円225円344円270円216円B+346円240円221円347円264円242円345円274円225円347円232円204円345円214円272円345円210円253円.md"
rename to "docs/MySQL/Hash347円264円242円345円274円225円344円270円216円B+346円240円221円347円264円242円345円274円225円347円232円204円345円214円272円345円210円253円.md"
diff --git a/docs/database/MySQL.md "b/docs/MySQL/MySQL345円237円272円347円241円200円346円246円202円345円277円265円.md"
similarity index 100%
rename from docs/database/MySQL.md
rename to "docs/MySQL/MySQL345円237円272円347円241円200円346円246円202円345円277円265円.md"
diff --git "a/docs/MySQL/MySQL345円256円236円346円210円230円345円256円235円345円205円270円/08 347円264円242円345円274円225円357円274円232円346円216円222円345円272円217円347円232円204円350円211円272円346円234円257円.md" "b/docs/MySQL/MySQL345円256円236円346円210円230円345円256円235円345円205円270円/08 347円264円242円345円274円225円357円274円232円346円216円222円345円272円217円347円232円204円350円211円272円346円234円257円.md"
new file mode 100644
index 0000000..966e24f
--- /dev/null
+++ "b/docs/MySQL/MySQL345円256円236円346円210円230円345円256円235円345円205円270円/08 347円264円242円345円274円225円357円274円232円346円216円222円345円272円217円347円232円204円350円211円272円346円234円257円.md"
@@ -0,0 +1,25 @@
+# 08 | 索引:排序的艺术
+
+## 前言
+
+索引是关系型数据库中最核心的概念之一,只有正确设计索引,业务才能达到上线的初步标准。本文我们一起探索——索引,目的是认识索引、用好索引。
+
+## 索引是什么?
+
+索引是提升查询速度的一种数据结构。
+
+> 思考:索引为什么会提升查询速度呢?
+
+索引之所以能够提升查询速度,那是因为在插入时候对数据进行了排序操作,当然什么事情都要两面性,这个排序操作虽然提升了查询速度,但是会导致插入或者更新的效率下降。
+
+所以说索引是一门排序的艺术,只有用好索引,才能提升整个数据库系统的性能。在MySQL 8.0版本中,InnoDB 存储引擎支持的索引有 B+ 树索引、全文索引、R 树索引。当然,使用最为广泛的是B+树索引了。
+
+### B+树索引结构
+
+B+ 树索引是数据库系统中最为常见的一种索引数据结构,几乎所有的关系型数据库都支持它。
+
+> 思考:为什么呢?它有什么"魔力"?
+
+因为它是**目前为止**排序最有效率的数据结构。像二叉树,哈希索引、红黑树、SkipList,在海量数据基于磁盘存储效率方面远不如 B+ 树索引高效。
+
+所以,上述的数据结构一般仅用于内存对象,基于磁盘的数据排序与存储,最有效的依然是 B+ 树索引了。
\ No newline at end of file
diff --git a/docs/MySQL/README.md b/docs/MySQL/README.md
new file mode 100644
index 0000000..a9faa85
--- /dev/null
+++ b/docs/MySQL/README.md
@@ -0,0 +1,13 @@
+# MySQL
+
+* [MySQL基础概念](docs/MySQL/MySQL基础概念.md)
+* [常见SQL优化方式](docs/MySQL/常见SQL优化方式.md)
+* [浅谈MySQL的优化方案](docs/MySQL/浅谈MySQL的优化方案.md)
+* [如何使用索引](docs/MySQL/如何使用索引.md)
+* [如何使用EXPLAIN查看执行计划](docs/MySQL/如何使用EXPLAIN查看执行计划.md)
+* [什么时候不需要创建索引](docs/MySQL/什么时候不需要创建索引.md)
+* [什么情况下索引失效](docs/MySQL/什么情况下索引失效.md)
+* [Hash索引与B+树索引的区别](docs/MySQL/Hash索引与B+树索引的区别.md)
+* [B树与B+树详谈](docs/MySQL/树详谈.md)
+* [SQL经典笔试题目](docs/MySQL/SQL经典笔试题目.md)
+* [SQL进阶](docs/MySQL/SQL进阶.md)
diff --git "a/docs/database/SQL347円273円217円345円205円270円347円254円224円350円257円225円351円242円230円347円233円256円.md" "b/docs/MySQL/SQL347円273円217円345円205円270円347円254円224円350円257円225円351円242円230円347円233円256円.md"
similarity index 97%
rename from "docs/database/SQL347円273円217円345円205円270円347円254円224円350円257円225円351円242円230円347円233円256円.md"
rename to "docs/MySQL/SQL347円273円217円345円205円270円347円254円224円350円257円225円351円242円230円347円233円256円.md"
index d8dc74a..fbe7ab3 100644
--- "a/docs/database/SQL347円273円217円345円205円270円347円254円224円350円257円225円351円242円230円347円233円256円.md"
+++ "b/docs/MySQL/SQL347円273円217円345円205円270円347円254円224円350円257円225円351円242円230円347円233円256円.md"
@@ -237,13 +237,24 @@ having count(sc.cid ) < 3> 查询至少有一门课与学号为"01"的同学所学相同的同学的学号和姓名;
```sql
-
+select distinct sc.sid,sname
+from sc
+join student as s
+on sc.sid=s.sid
+where cid in
+ (select distinct cid from sc where sid='01')
```
### 3.2 第十二道
> 查询和"01"号的同学学习的课程完全相同的其他同学的学号和姓名;
+```sql
+
+```
+
+
+
### 3.3 第十三道
### 3.4 第十四道
diff --git "a/docs/database/SQL350円277円233円351円230円266円.md" "b/docs/MySQL/SQL350円277円233円351円230円266円.md"
similarity index 100%
rename from "docs/database/SQL350円277円233円351円230円266円.md"
rename to "docs/MySQL/SQL350円277円233円351円230円266円.md"
diff --git a/docs/MySQL/_sidebar.md b/docs/MySQL/_sidebar.md
new file mode 100644
index 0000000..6e1c640
--- /dev/null
+++ b/docs/MySQL/_sidebar.md
@@ -0,0 +1,12 @@
+* **👉 MySQL** [↩](/README)
+ * [MySQL基础概念](docs/MySQL/MySQL基础概念.md)
+ * [常见SQL优化方式](docs/MySQL/常见SQL优化方式.md)
+ * [浅谈MySQL的优化方案](docs/MySQL/浅谈MySQL的优化方案.md)
+ * [如何使用索引](docs/MySQL/如何使用索引.md)
+ * [如何使用EXPLAIN查看执行计划](docs/MySQL/如何使用EXPLAIN查看执行计划.md)
+ * [什么时候不需要创建索引](docs/MySQL/什么时候不需要创建索引.md)
+ * [什么情况下索引失效](docs/MySQL/什么情况下索引失效.md)
+ * [Hash索引与B+树索引的区别](docs/MySQL/Hash索引与B+树索引的区别.md)
+ * [B树与B+树详谈](docs/MySQL/B树与B+树详谈.md)
+ * [SQL经典笔试题目](docs/MySQL/SQL经典笔试题目.md)
+ * [SQL进阶](docs/MySQL/SQL进阶.md)
diff --git "a/docs/MySQL/343円200円212円MySQL345円277円205円347円237円245円345円277円205円344円274円232円343円200円213円.md" "b/docs/MySQL/343円200円212円MySQL345円277円205円347円237円245円345円277円205円344円274円232円343円200円213円.md"
new file mode 100644
index 0000000..dc0948b
--- /dev/null
+++ "b/docs/MySQL/343円200円212円MySQL345円277円205円347円237円245円345円277円205円344円274円232円343円200円213円.md"
@@ -0,0 +1,377 @@
+# 《MySQL必知必会》
+
+## 1. 基础概念
+
+### 1.1 什么是数据库
+
+ 数据库(DataBase,DB)是一个长期存储在计算机内的、有组织的、有共享的、统一管理的数据集合。它是一个按数据结构来存储和管理数据的计算机软件系统。
+
+### 1.2 表
+
+ 在关系数据库中,表是一系列二维数组的集合,用于存储数据和操作数据的逻辑结构。表是右纵向的列和横向的行组成。行叫做记录,是组织数据的单位。列叫做字段,是记录的一个属性,所用表都是由一个或多个列组成的。
+
+> 从技术上来说,行才是正确的术语。但是记录也不错~
+
+我们举个例子解释,如表1-1所示:
+
+| 编号 | 姓名 | 性别 | 年龄 | 专业 |
+| :--: | :----: | :--: | :--: | :----------: |
+| 1 | 张无忌 | 男 | 18 | 软件工程 |
+| 2 | 赵敏 | 女 | 16 | 车辆工程 |
+| 3 | 张三丰 | 男 | 99 | 文化产业管理 |
+
+上表中,我们可以看到编码、姓名、性别、年龄、专业等就是这个表的字段(熟悉、列),编号1、2、3等从左到右一行就是这个表的记录(行)。
+
+### 1.3 主键
+
+主键(Primary Key),用于唯一地标识表中的每一条记录。主键列上既不能有两行相同的值,也不能为空值。
+
+> 简单的说,主键的作用就是唯一性区分表中的每一行。
+
+主键通常是定义在表的一列上,但是也不一定,也可以一起使用多个列最为主键,但是这多个列的组合必须满足唯一性。
+
+### 1.4 了解SQL
+
+SQL是对数据库进行查询和修改操作的语言,其含义是结构化查询语言(Structured Query Language)。SQL语言包含以下4部分。
+
+1. 数据定义语言(DDL):DROP、CREATE、ALTER等语句。
+2. 数据操作语言(DML):INSERT(插入)、UPDATE(修改)、DELETE(删除)语句。
+3. 数据查询语言(DQL):SELECT语句。
+4. 数据控制语言(DCL):GRANT、REVOKE、COMMIT、ROLLBACK等语句。
+
+### 1.5 了解MySQL
+
+MySQL是一个小型关系数据库管理系统。与其他大型数据库管理系统(例如Oracle、DB2、SQL Server等)相比,MySQL规模小、功能有限,但是它体积小、速度快、成本低,并且提供的功能对稍微复杂的应用来说已经够用,这些特性使得MySQL成为世界上最受欢迎的开放源代码数据库。
+
+MySQL的优点:
+
+1. 性能:MySQL执行很快(非常快)。
+2. 成本:MySQL是开发源代码的,社区版本是免费的,企业版本是收费的。
+3. 简单:MySQL安装非常简单且容易上手。
+4. 移植性高:能够工作在众多不同的系统平台上,例如Windows、Linux、UNIX、Mac OS等。
+
+MySQL常用图形管理工具推荐:
+
+1. Navicat 下载地址:http://www.navicat.com/
+2. SQLyog 下载地址:http://www.webyog.com/
+3. MySQL Workbench 下载地址:http://dev.MySQL.com/downloads/workbench/
+
+## 2. 表的基本操作
+
+实际开发中,大多都是直接使用第三方管理软件进行数据库操作了,很少会使用命令直接操作表。但是这些基础知识也是应该了解一下的。
+
+### 2.1 创建数据表
+
+创建数据表的语句为`CREATE TABLE`,语法规则如下:
+
+```sql
+CREATE TABLE 表名 (
+ 字段名1,数据类型[列级别约束条件] [默认值] ,
+ 字段名2数据类型[列级别约束条件] [默认值] ,
+ 字段名3数据类型[列级别约束条件] [默认值] ,
+ ···
+
+ [表级别约束条件]
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+```
+
+具体创建表的SQL如下列:
+
+```sql
+CREATE TABLE `t_student` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `name` varchar(32) DEFAULT NULL COMMENT '姓名',
+ `sex` char(8) DEFAULT NULL COMMENT '性别',
+ `age` int(11) DEFAULT NULL COMMENT '年龄',
+ `major` varchar(64) DEFAULT NULL COMMENT '专业',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+```
+
+注意事项:
+
++ 要创建的表的名称,不区分大小写,不能使用SQL语言中的关键字,如DROP、ALTER、INSERT等。
++ 数据表中每一列(字段)的名称和数据类型,如果创建多列,就要用逗号隔开。
+
+### 2.2 查看数据表结构
+
+查看表的结构语句,SQL命令是:SHOW COLUMNS FROM 表名 或者 DESCRIBE 表名 或者 DESC 表名。具体操作如下:
+
+```sql
+mysql> SHOW COLUMNS FROM t_student;
++-------+-------------+------+-----+---------+----------------+
+| Field | Type | Null | Key | Default | Extra |
++-------+-------------+------+-----+---------+----------------+
+| id | int(11) | NO | PRI | NULL | auto_increment |
+| name | varchar(32) | YES | | NULL | |
+| sex | char(8) | YES | | NULL | |
+| age | int(11) | YES | | NULL | |
+| major | varchar(64) | YES | | NULL | |
++-------+-------------+------+-----+---------+----------------+
+5 rows in set (0.00 sec)
+
+mysql> DESCRIBE t_student;
++-------+-------------+------+-----+---------+----------------+
+| Field | Type | Null | Key | Default | Extra |
++-------+-------------+------+-----+---------+----------------+
+| id | int(11) | NO | PRI | NULL | auto_increment |
+| name | varchar(32) | YES | | NULL | |
+| sex | char(8) | YES | | NULL | |
+| age | int(11) | YES | | NULL | |
+| major | varchar(64) | YES | | NULL | |
++-------+-------------+------+-----+---------+----------------+
+5 rows in set (0.00 sec)
+
+
+mysql> DESC t_student;
++-------+-------------+------+-----+---------+----------------+
+| Field | Type | Null | Key | Default | Extra |
++-------+-------------+------+-----+---------+----------------+
+| id | int(11) | NO | PRI | NULL | auto_increment |
+| name | varchar(32) | YES | | NULL | |
+| sex | char(8) | YES | | NULL | |
+| age | int(11) | YES | | NULL | |
+| major | varchar(64) | YES | | NULL | |
++-------+-------------+------+-----+---------+----------------+
+5 rows in set (0.00 sec)
+
+```
+
+其中,表结构中的各个字段的含义分别解释如下:
+
++ NULL:表示该列是否可以存储NULL值。
++ Key:表示该列是否已编制索引。PRI表示该列是表主键的一部分;
++ UNI表示该列是UNIQUE索引的一部分;
++ MUL表示在列中某个给定值允许出现多次。
++ Default:表示该列是否有默认值,有的话指定值是多少。
++ Extra:表示可以获取的与给定列有关的附加信息,例如AUTO_INCREMENT等。
+
+查看表创建时候的CREATE TABLE 语句,SQL命令是:`SHOW CREATE TABLE 表名`,具体操作如下:
+
+```sql
+mysql> SHOW CREATE TABLE t_student;
++-----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Table | Create Table |
++-----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| t_student | CREATE TABLE `t_student` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `name` varchar(32) DEFAULT NULL COMMENT '姓名',
+ `sex` char(8) DEFAULT NULL COMMENT '性别',
+ `age` int(11) DEFAULT NULL COMMENT '年龄',
+ `major` varchar(64) DEFAULT NULL COMMENT '专业',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
++-----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+1 row in set (0.00 sec)
+
+```
+
+> 使用SHOW CREATE TABLE语句,不仅可以查看表创建时候的详细语句,还可以查看存储引擎和字符编码。
+
+如果不加‘\G’参数,显示的结果可能非常混乱,加上参数‘\G’之后,可使显示结果更加直观,易于查看。
+
+使用参数‘\G’之后的结果如下:
+
+```sql
+mysql> SHOW CREATE TABLE t_student\G;
+*************************** 1. row ***************************
+ Table: t_student
+Create Table: CREATE TABLE `t_student` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `name` varchar(32) DEFAULT NULL COMMENT '姓名',
+ `sex` char(8) DEFAULT NULL COMMENT '性别',
+ `age` int(11) DEFAULT NULL COMMENT '年龄',
+ `major` varchar(64) DEFAULT NULL COMMENT '专业',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8
+1 row in set (0.00 sec)
+```
+
+是不是变得整洁了许多呢~
+
+### 2.3 修改数据表
+
+修改表指的是修改数据库中已经存在的数据表的结构。MySQL使用`ALTER TABLE`语句修改表。常用的修改表的操作有修改表名、修改字段数据类型或字段名、增加和删除字段、修改字段的排列位置、更改表的存储引擎、删除表的外键约束等。
+
+修改数据表名,MySQL是通过`ALTER TABLE`语句来实现表名的修改的,具体的语法规则如下:
+
+```sql
+ALTER TABLE <旧表名称> RENAME [TO] <新表名称>;
+```
+
+其中,TO为可选参数,使用与否均不影响结果。
+
+我们这里将上面创建的`t_student`表改为`t_students`,加个s。具体操作如下:
+
+```sql
+mysql> ALTER TABLE t_student RENAME t_students;
+Query OK, 0 rows affected (0.01 sec)
+```
+
+使用`SHOW TABLES`命令,查看当前库中的所有表。
+
+```sql
+mysql> SHOW TABLES;
++-----------------+
+| Tables_in_study |
++-----------------+
+| t_students |
++-----------------+
+1 row in set (0.00 sec)
+```
+
+现在表名已经修改为了`t_students`。
+
+### 2.4 删除数据库表
+
+删除数据表是一个危险的操作,最好在删除之前对表有个备份的心里准备。当然了,在开发中,大多数开发者不会有权限直接操作生产库中的表,所谓的"删库跑路"听听得了,真是删了库,你也跑不掉。
+
+删除一张表的SQL命令是这样写的:`DROP TABLE [IF EXISTS] 表名`。
+
+```sql
+mysql> DROP TABLE IF EXISTS t_students;
+Query OK, 0 rows affected (0.00 sec)
+```
+
+可选参数"`IF EXISTS`"用于在删除前判断删除的表是否存在,加上该参数后,再删除表的时候,如果表不存在,SQL语句可以顺利执行,但是会发出警告(warning)。
+
+```sql
+mysql> DROP TABLE IF EXISTS t_students;
+Query OK, 0 rows affected, 1 warning (0.00 sec)
+```
+
+## 3. 数据类型与运算符
+
+一张数据库表是由多个列字段组成的,每一个字段都会指定一个数据类型,数据类型也就决定了字段存储的数据内容。不同的数据类型也决定了MySQL在存储它们的时候使用的方式,以及在使用它们的时候选择什么运算符号进行运算。
+
+MySQL数据类型:
+
+- 数值类型:包括整数类型`TINYINT`、`SMALLINT`、`MEDIUMINT`、`INT`、`BIGINT`、浮点小数数据类型`FLOAT`和`DOUBLE`,定点小数类型`DECIMAL`。
+- 日期、时间类型:包括`YEAR`、`TIME`、`DATE`、`DATETIME`和`TIMESTAMP`。
+- 字符串类型:包括`CHAR`、`VARCHAR`、`BINARY`、`VARBINARY`、`BLOB`、`TEXT`、`ENUM`和`SET`等。字符串类型又分为文本字符串和二进制字符串。
+
+### 3.1 整数类型
+
+数值型数据类型主要用来存储数字,MySQL提供了多种数值数据类型,不同的数据类型提供不同的取值范围,可以存储的值范围越大,其所需要的存储空间也会越大。
+
+MySQL主要提供的整数类型有`TINYINT`、`SMALLINT`、`MEDIUMINT`、`INT(INTEGER)`、`BIGINT`。整数类型的属性字段可以添加`AUTO_INCREMENT`自增约束条件。如下表MySQL中的数值类型及取值范围:
+
+| 类型名称 | 用途 | 大小(单位:字节) | 有符号 | 无符号 |
+| :----------: | :--------: | :----------------: | :-----------------------------------------------------: | :----------------------------: |
+| TINYINT | 小整数值 | 1 | (-128,127) | (0,255) |
+| SMALLINT | 大整数值 | 2 | (-32 768,32 767) | (0,65 535) |
+| MEDIUMINT | 大整数值 | 3 | (-8 388 608,8 388 607) | (0,16 777 215) |
+| INT(INTEGER) | 大整数值 | 4 | -2 147 483 648,2 147 483 647) | (0,4 294 967 295) |
+| BIGINT | 极大整数值 | 8 | (-9,223,372,036,854,775,808,9 223 372 036 854 775 807) | 0,18 446 744 073 709 551 615) |
+
+我们还以上文中创建的`t_student`表为例:
+
+```sql
+CREATE TABLE `t_student` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `name` varchar(32) DEFAULT NULL COMMENT '姓名',
+ `sex` char(8) DEFAULT NULL COMMENT '性别',
+ `age` int(11) DEFAULT NULL COMMENT '年龄',
+ `major` varchar(64) DEFAULT NULL COMMENT '专业',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+```
+
+id 字段的数据类型为INT(11),注意后面的数字11,它表示的是该数据类型指定的显示宽度,即能够显示的数值中数字的个数。
+
+显示宽度和数据类型的取值范围是无关的。显示宽度只是指明MySQL最大可能显示的数字个数,数值的位数小于指定的宽度时会由空格填充;
+
+如果插入了大于显示宽度的值,只要该值不超过该类型整数的取值范围,数值依然可以插入,而且能够显示出来。
+
+> 注意:显示宽度只用于显示,并不能限制取值范围和占用空间。例如:INT(3)会占用4字节的存储空间,并且允许的最大值不会是999,而是INT整型所允许的最大值。
+
+### 3.2浮点数类型和定点数类型
+
+| 类型名称 | 大小(单位:字节) | 有符号 | 无符号 |
+| -------- | ---------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
+| FLOAT | 4 bytes | (-3.402 823 466 E+38,-1.175 494 351 E-38),0,(1.175 494 351 E-38,3.402 823 466 351 E+38) | 0,(1.175 494 351 E-38,3.402 823 466 E+38) |
+| DOUBLE | 8 bytes | (-1.797 693 134 862 315 7 E+308,-2.225 073 858 507 201 4 E-308),0,(2.225 073 858 507 201 4 E-308,1.797 693 134 862 315 7 E+308) | 0,(2.225 073 858 507 201 4 E-308,1.797 693 134 862 315 7 E+308) |
+| DECIMAL | 对DECIMAL(M,D) ,如果M>D,为M+2否则为D+2 | 依赖于M和D的值 | 依赖于M和D的值 |
+
+### 3.3 日期与时间类型
+
+MySQL中有多种表示日期的数据类型,主要有DATETIME、DATE、TIMESTAMP、TIME和YEAR。
+
+| 类名称 | 大小(单位:字节) | 范围 | 格式 | 用途 |
+| :-------: | :----------------: | :----------------------------------------------------------: | :------------------ | :----------------------: |
+| DATE | 3 | 1000年01月01日/9999-12-31 | YYYY-MM-DD | 日期值 |
+| TIME | 3 | '-838:59:59'/'838:59:59' | HH:MM:SS | 时间值或持续时间 |
+| YEAR | 1 | 1901/2155 | YYYY | 年份值 |
+| DATETIME | 8 | 1000年01月01日 00:00:00/9999-12-31 23:59:59 | YYYY-MM-DD HH:MM:SS | 混合日期和时间值 |
+| TIMESTAMP | 4 | 1970年01月01日 00:00:00/2038结束时间是第 2147483647 秒,北京时间 2038年1月19日 11:14:07 | YYYYMMDD HHMMSS | 混合日期和时间值,时间戳 |
+
+### 3.4 如何选择数据类型
+
+MySQL中提供了很多的数据类型供选择,选择最合适的数据类型,有利于优化存储、提供数据库性能。在上文中我们对MySQL中的数据类型逐一做了介绍,在此基础上我们进一步分析,在实际使用过程中,我们该如何选择数据类型才能尽可能最优、最合适。
+
+#### 3.4.1 整数和浮点数
+
+整数类型使用非常频繁,如果你确定你不需要小数部分的话,可以选择整数类型来保存数据。如果你需要小数部分,可以选择浮点数类型,浮点数类型存入的数据会对该列定义的小数位进行四舍五入。
+
+浮点类型包括`FLOAT`和`DOUBLE`类型。`DOUBLE`类型精度比`FLOAT`类型高,因此要求存储精度较高时应选择`DOUBLE`类型。
+
+还有一点,如果你定义货币等这类对精确度要求较高的数据时候,可以选择`DECIMAL`。
+
+#### 3.4.2 日期与时间类型
+
+MySQL中有不同种类的日期和时间的数据类型,比如`YEAR`和`TIME`。如果仅需要记录年份,则使用`YEAR`类型即可;如果仅记录时间,则使用`TIME`类型。
+
+如果同时需要记录日期和时间的话,则可以使用`TIMESTAMP`或者`DATETIME`类型。由于`TIMESTAMP`列的取值范围小于`DATETIME`的取值范围,因此存储范围较大的日期最好使用`DATETIME`。
+
+`TIMESTAMP`也有一个`DATETIME`不具备的属性。默认的情况下,当插入一条记录但并没有指定`TIMESTAMP`这个列值时,MySQL会把`TIMESTAMP`列设为当前的时间,这一点非常方便。
+
+因此当需要插入记录的同时插入当前时间时,使用`IMESTAMP`是方便的。另外,`TIMESTAMP`在空间上比`DATETIME`更有效。
+
+#### 3.4.3 CHAR与VARCHAR之间的特点与选择
+
+我们先看看`CHAR`与`VARCHAR`的区别:
+
+- `CHAR`是固定长度字符,`VARCHAR`是可变长度字符;
+- `CHAR`会自动删除插入数据的尾部空格,`VARCHAR`不会删除尾部空格;
+
+因为`CHAR`是固定长度,所以它的处理速度会比`VARCHAR`的速度快,但是缺点也很明显,那就是造成一定程度上的存储空间浪费,对于那些存储不大但是速度有要求的可以选择使用`CHAR`类型,反之可以使用`VARCHAR`类型。
+
+我们来看看存储引擎对于选择`CHAR`和`VARCHAR`会产生什么影响:
+
++ `MyISAM`:最好使用固定长度的数据列代替可变长度的数据列。这样可以使整个表静态化,从而使数据检索更快,用空间换时间。
++ `InnoDB`:使用可变长度的数据列,因为`InnoDB`数据表的存储格式不分固定长度和可变长度,因此使用`CHAR`不一定比使用`VARCHAR`更好,但由于`VARCHAR`是按照实际的长度存储的,比较节省空间,所以对磁盘I/O和数据存储总量比较好。
+
+#### 3.4.4 ENUM和SET
+
+`ENUM`只能取单值,它的数据列表是一个枚举集合。它的合法取值列表最多允许有65535个成员。因此,在需要从多个值中选取一个时,可以使用`ENUM`。比如:性别字段适合定义为`ENUM`类型,每次只能从‘男’或‘女’中取一个值。
+
+`SET`可取多值。它的合法取值列表最多允许有64个成员。空字符串也是一个合法的`SET`值。在需要取多个值的时候,适合使用`SET`类型,比如要存储一个人的兴趣爱好,最好使用SET类型。
+
+`ENUM`和`SET`的值是以字符串形式出现的,但在内部,MySQL是以数值的形式存储它们的。
+
+#### 3.4.5 BLOB和TEXT
+
+`BLOB`是二进制字符串,`TEXT`是非二进制字符串,两者均可存放大容量的信息。`BLOB`主要存储图片、音频信息等,而`TEXT`只能存储纯文本文件。
+
+## 4. 查询数据
+
+### 4.1 单表查询
+
+#### 4.1.1 查询所有字段
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git "a/docs/database/344円273円200円344円271円210円346円203円205円345円206円265円344円270円213円347円264円242円345円274円225円345円244円261円346円225円210円.md" "b/docs/MySQL/344円273円200円344円271円210円346円203円205円345円206円265円344円270円213円347円264円242円345円274円225円345円244円261円346円225円210円.md"
similarity index 100%
rename from "docs/database/344円273円200円344円271円210円346円203円205円345円206円265円344円270円213円347円264円242円345円274円225円345円244円261円346円225円210円.md"
rename to "docs/MySQL/344円273円200円344円271円210円346円203円205円345円206円265円344円270円213円347円264円242円345円274円225円345円244円261円346円225円210円.md"
diff --git "a/docs/database/344円273円200円344円271円210円346円227円266円345円200円231円344円270円215円351円234円200円350円246円201円345円210円233円345円273円272円347円264円242円345円274円225円.md" "b/docs/MySQL/344円273円200円344円271円210円346円227円266円345円200円231円344円270円215円351円234円200円350円246円201円345円210円233円345円273円272円347円264円242円345円274円225円.md"
similarity index 100%
rename from "docs/database/344円273円200円344円271円210円346円227円266円345円200円231円344円270円215円351円234円200円350円246円201円345円210円233円345円273円272円347円264円242円345円274円225円.md"
rename to "docs/MySQL/344円273円200円344円271円210円346円227円266円345円200円231円344円270円215円351円234円200円350円246円201円345円210円233円345円273円272円347円264円242円345円274円225円.md"
diff --git "a/docs/database/345円246円202円344円275円225円344円275円277円347円224円250円EXPLAIN346円237円245円347円234円213円346円211円247円350円241円214円350円256円241円345円210円222円.md" "b/docs/MySQL/345円246円202円344円275円225円344円275円277円347円224円250円EXPLAIN346円237円245円347円234円213円346円211円247円350円241円214円350円256円241円345円210円222円.md"
similarity index 100%
rename from "docs/database/345円246円202円344円275円225円344円275円277円347円224円250円EXPLAIN346円237円245円347円234円213円346円211円247円350円241円214円350円256円241円345円210円222円.md"
rename to "docs/MySQL/345円246円202円344円275円225円344円275円277円347円224円250円EXPLAIN346円237円245円347円234円213円346円211円247円350円241円214円350円256円241円345円210円222円.md"
diff --git "a/docs/database/345円246円202円344円275円225円344円275円277円347円224円250円347円264円242円345円274円225円.md" "b/docs/MySQL/345円246円202円344円275円225円344円275円277円347円224円250円347円264円242円345円274円225円.md"
similarity index 100%
rename from "docs/database/345円246円202円344円275円225円344円275円277円347円224円250円347円264円242円345円274円225円.md"
rename to "docs/MySQL/345円246円202円344円275円225円344円275円277円347円224円250円347円264円242円345円274円225円.md"
diff --git "a/docs/database/345円270円270円350円247円201円SQL344円274円230円345円214円226円346円226円271円345円274円217円.md" "b/docs/MySQL/345円270円270円350円247円201円SQL344円274円230円345円214円226円346円226円271円345円274円217円.md"
similarity index 100%
rename from "docs/database/345円270円270円350円247円201円SQL344円274円230円345円214円226円346円226円271円345円274円217円.md"
rename to "docs/MySQL/345円270円270円350円247円201円SQL344円274円230円345円214円226円346円226円271円345円274円217円.md"
diff --git "a/docs/database/346円265円205円350円260円210円MySQL347円232円204円344円274円230円345円214円226円346円226円271円346円241円210円.md" "b/docs/MySQL/346円265円205円350円260円210円MySQL347円232円204円344円274円230円345円214円226円346円226円271円346円241円210円.md"
similarity index 100%
rename from "docs/database/346円265円205円350円260円210円MySQL347円232円204円344円274円230円345円214円226円346円226円271円346円241円210円.md"
rename to "docs/MySQL/346円265円205円350円260円210円MySQL347円232円204円344円274円230円345円214円226円346円226円271円346円241円210円.md"
diff --git "a/docs/Nginx347円256円200円345円215円225円345円205円245円351円227円250円.md" "b/docs/Nginx347円256円200円345円215円225円345円205円245円351円227円250円.md"
deleted file mode 100644
index b402c2e..0000000
--- "a/docs/Nginx347円256円200円345円215円225円345円205円245円351円227円250円.md"
+++ /dev/null
@@ -1,19 +0,0 @@
-# Nginx简单入门
-
-## Nginx 的使用场景
-
-### HTTP 服务器
-
-Nginx 作为 Web 服务器能独立提供 Http 服务。另外,我们常常通过 Nginx 作为静态资源服务器来访问服务器上的静态资源,比如对于最新热门的前后端分离架构,前端打好包后直接放到某个地址,在 Nginx 配置后可以通过 Nginx 来访问主机上的前端页面。
-
-### 反向代理
-
-反向代理(Reverse Proxy)方式是指以代理服务器来接受 Internet 上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给 Internet 上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。这样的好处是,将不暴露内部的服务地址,只统一使用一个公共出口,通过 URI 匹配转发到不同的内部服务处理请求。
-
-### 负载均衡
-
-负载均衡也是 Nginx 的一个高频使用场景,对于下游存在的多个相同服务,可以将请求采用某种策略(随机、轮询、权重)发到相应的服务处理。这样由于多个相同服务的存在,可以实现高可用功能,在一个服务不可用时,Nginx 会自动发现并将其剔出服务集群,将请求转发给正常的服务进行处理。
-
-### 第三方插件
-
-基于第三方插件,Nginx 可以完成各种各样复杂的功能,全方位满足程序员的想法。比如在 Nginx 中引入 lua 模块,可以实现对 Http 请求更细粒度的限制,包括限速、限流、校验认证等等。后续,在 Nginx 上发展出来的 OpenResty 已经应用到了微服务网关方向。
\ No newline at end of file
diff --git a/docs/Redis/README.md b/docs/Redis/README.md
new file mode 100644
index 0000000..8f51c35
--- /dev/null
+++ b/docs/Redis/README.md
@@ -0,0 +1,2 @@
+# Redis TODO
+
diff --git "a/docs/Redis/Redis 346円230円257円345円246円202円344円275円225円345円244円204円347円220円206円350円277円207円346円234円237円346円225円260円346円215円256円347円232円204円357円274円237円345円275円223円345円206円205円345円255円230円344円270円215円345円244円237円347円224円250円346円227円266円 Redis 345円217円210円346円230円257円345円246円202円344円275円225円345円244円204円347円220円206円347円232円204円357円274円237円.md" "b/docs/Redis/Redis 346円230円257円345円246円202円344円275円225円345円244円204円347円220円206円350円277円207円346円234円237円346円225円260円346円215円256円347円232円204円357円274円237円345円275円223円345円206円205円345円255円230円344円270円215円345円244円237円347円224円250円346円227円266円 Redis 345円217円210円346円230円257円345円246円202円344円275円225円345円244円204円347円220円206円347円232円204円357円274円237円.md"
deleted file mode 100644
index 6be3074..0000000
--- "a/docs/Redis/Redis 346円230円257円345円246円202円344円275円225円345円244円204円347円220円206円350円277円207円346円234円237円346円225円260円346円215円256円347円232円204円357円274円237円345円275円223円345円206円205円345円255円230円344円270円215円345円244円237円347円224円250円346円227円266円 Redis 345円217円210円346円230円257円345円246円202円344円275円225円345円244円204円347円220円206円347円232円204円357円274円237円.md"
+++ /dev/null
@@ -1,212 +0,0 @@
-
-
-# Redis 是如何处理过期数据的?当内存不够用时 Redis 又是如何处理的?
-
-**典型回答**
-我们在新增 Redis 缓存时可以设置缓存的过期时间,该时间保证了数据在规定的时间内失效,可以借助这个特性来实现很多功能。比如,存储一定天数的用户(登录)会话信息,这样在一定范围内用户不用重复登录了,但为了安全性,需要在一定时间之后重新验证用户的信息。因此,我们可以使用 Redis 设置过期时间来存储用户的会话信息。
-
-对于已经过期的数据,Redis 将使用两种策略来删除这些过期键,它们分别是惰性删除和定期删除。
-
-惰性删除是指 Redis 服务器不主动删除过期的键值,而是当访问键值时,再检查当前的键值是否过期,如果过期则执行删除并返回 null 给客户端;如果没过期则正常返回值信息给客户端。
-
-它的优点是不会浪费太多的系统资源,只是在每次访问时才检查键值是否过期。缺点是删除过期键不及时,造成了一定的空间浪费。
-
-惰性删除的源码位于 src/db.c 文件的 expireIfNeeded 方法中,如下所示:
-
-```c
-int expireIfNeeded(redisDb *db, robj *key) {
- // 判断键是否过期
- if (!keyIsExpired(db,key)) return 0;
- if (server.masterhost != NULL) return 1;
- /* 删除过期键 */
- // 增加过期键个数
- server.stat_expiredkeys++;
- // 传播键过期的消息
- propagateExpire(db,key,server.lazyfree_lazy_expire);
- notifyKeyspaceEvent(NOTIFY_EXPIRED,
- "expired",key,db->id);
- // server.lazyfree_lazy_expire 为 1 表示异步删除,否则则为同步删除
- return server.lazyfree_lazy_expire ? dbAsyncDelete(db,key) :
- dbSyncDelete(db,key);
-}
-// 判断键是否过期
-int keyIsExpired(redisDb *db, robj *key) {
- mstime_t when = getExpire(db,key);
- if (when < 0) return 0; - if (server.loading) return 0; - mstime_t now = server.lua_caller ? server.lua_time_start : mstime(); - return now> when;
-}
-// 获取键的过期时间
-long long getExpire(redisDb *db, robj *key) {
- dictEntry *de;
- if (dictSize(db->expires) == 0 ||
- (de = dictFind(db->expires,key->ptr)) == NULL) return -1;
- serverAssertWithInfo(NULL,key,dictFind(db->dict,key->ptr) != NULL);
- return dictGetSignedIntegerVal(de);
-}
-```
-
-惰性删除的执行流程如下图所示:
-
-图片1.png
-
-除了惰性删除之外,Redis 还提供了定期删除功能以弥补惰性删除的不足。
-
-定期删除是指 Redis 服务器每隔一段时间会检查一下数据库,看看是否有过期键可以被清除。
-
-默认情况下 Redis 定期检查的频率是每秒扫描 10 次,用于定期清除过期键。当然此值还可以通过配置文件进行设置,在 redis.conf 中修改配置"hz"即可,默认的值为"hz 10"。
-
-小贴士:定期删除的扫描并不是遍历所有的键值对,这样的话比较费时且太消耗系统资源。Redis 服务器采用的是随机抽取形式,每次从过期字典中,取出 20 个键进行过期检测,过期字典中存储的是所有设置了过期时间的键值对。如果这批随机检查的数据中有 25% 的比例过期,那么会再抽取 20 个随机键值进行检测和删除,并且会循环执行这个流程,直到抽取的这批数据中过期键值小于 25%,此次检测才算完成。
-
-定期删除的源码在 expire.c 文件的 activeExpireCycle 方法中,如下所示:
-
-```c
-void activeExpireCycle(int type) {
- static unsigned int current_db = 0; /* 上次定期删除遍历到的数据库ID */
- static int timelimit_exit = 0;
- static long long last_fast_cycle = 0; /* 上次执行定期删除的时间点 */
- int j, iteration = 0;
- int dbs_per_call = CRON_DBS_PER_CALL; // 需要遍历数据库的数量
- long long start = ustime(), timelimit, elapsed;
- if (clientsArePaused()) return;
- if (type == ACTIVE_EXPIRE_CYCLE_FAST) {
- if (!timelimit_exit) return;
- // ACTIVE_EXPIRE_CYCLE_FAST_DURATION 快速定期删除的执行时长
- if (start < last_fast_cycle + ACTIVE_EXPIRE_CYCLE_FAST_DURATION*2) return; - last_fast_cycle = start; - } - if (dbs_per_call> server.dbnum || timelimit_exit)
- dbs_per_call = server.dbnum;
- // 慢速定期删除的执行时长
- timelimit = 1000000*ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC/server.hz/100;
- timelimit_exit = 0;
- if (timelimit <= 0) timelimit = 1; - if (type == ACTIVE_EXPIRE_CYCLE_FAST) - timelimit = ACTIVE_EXPIRE_CYCLE_FAST_DURATION; /* 删除操作花费的时间 */ - long total_sampled = 0; - long total_expired = 0; - for (j = 0; j < dbs_per_call && timelimit_exit == 0; j++) { - int expired; - redisDb *db = server.db+(current_db % server.dbnum); - current_db++; - do { - // ....... - expired = 0; - ttl_sum = 0; - ttl_samples = 0; - // 每个数据库中检查的键的数量 - if (num> ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP)
- num = ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP;
- // 从数据库中随机选取 num 个键进行检查
- while (num--) {
- dictEntry *de;
- long long ttl;
- if ((de = dictGetRandomKey(db->expires)) == NULL) break;
- ttl = dictGetSignedInteger
- // 过期检查,并对过期键进行删除
- if (activeExpireCycleTryExpire(db,de,now)) expired++;
- if (ttl> 0) {
- ttl_sum += ttl;
- ttl_samples++;
- }
- total_sampled++;
- }
- total_expired += expired;
- if (ttl_samples) {
- long long avg_ttl = ttl_sum/ttl_samples;
- if (db->avg_ttl == 0) db->avg_ttl = avg_ttl;
- db->avg_ttl = (db->avg_ttl/50)*49 + (avg_ttl/50);
- }
- if ((iteration & 0xf) == 0) { /* check once every 16 iterations. */
- elapsed = ustime()-start;
- if (elapsed> timelimit) {
- timelimit_exit = 1;
- server.stat_expired_time_cap_reached_count++;
- break;
- }
- }
- /* 判断过期键删除数量是否超过 25% */
- } while (expired> ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP/4);
- }
- // .......
-}
-```
-
-定期删除的执行流程,如下图所示:
-
-图片2.png
-
-> 小贴士:Redis 服务器为了保证过期删除策略不会导致线程卡死,会给过期扫描增加了最大执行时间为 25ms。
-
-以上是 Redis 服务器对待过期键的处理方案,当 Redis 的内存超过最大允许的内存之后,Redis 会触发内存淘汰策略,这和过期策略是完全不同的两个概念,经常有人把二者搞混,这两者一个是在正常情况下清除过期键,一个是在非正常情况下为了保证 Redis 顺利运行的保护策略。
-
-当 Redis 内存不够用时,Redis 服务器会根据服务器设置的淘汰策略,删除一些不常用的数据,以保证 Redis 服务器的顺利运行。
-
-考点分析
-本课时的面试题并非 Redis 的入门级面试题,需要面试者对 Redis 有一定的了解才能对答如流,并且 Redis 的过期淘汰策略和内存淘汰策略的概念比较类似,都是用于淘汰数据的。因此很多人会把二者当成一回事,但其实并不是,这个面试者特别注意一下,和此知识点相关的面试题还有以下这些:
-
-Redis 内存淘汰策略有哪些?
-Redis 有哪些内存淘汰算法?
-知识扩展
-Redis 内存淘汰策略
-我们可以使用 config get maxmemory-policy 命令,来查看当前 Redis 的内存淘汰策略,示例代码如下:
-
-```shell
-127.0.0.1:6379> config get maxmemory-policy
-1) "maxmemory-policy"
-2) "noeviction"
-```
-
-从上面的结果可以看出,当前 Redis 服务器设置的是"noeviction"类型的内存淘汰策略,那么这表示什么含义呢?Redis 又有几种内存淘汰策略呢?
-
-在 4.0 版本之前 Redis 的内存淘汰策略有以下 6 种。
-
-1. noeviction:不淘汰任何数据,当内存不足时,执行缓存新增操作会报错,它是 Redis 默认内存淘汰策略。
-
-2. allkeys-lru:淘汰整个键值中最久未使用的键值。
-
-3. allkeys-random:随机淘汰任意键值。
-
-4. volatile-lru:淘汰所有设置了过期时间的键值中最久未使用的键值。
-
-5. volatile-random:随机淘汰设置了过期时间的任意键值。
-
-6. volatile-ttl:优先淘汰更早过期的键值。
-
- 可以看出我们上面示例使用的是 Redis 默认的内存淘汰策略"noeviction"。
-
-而在 Redis 4.0 版本中又新增了 2 种淘汰策略:
-
-1. volatile-lfu,淘汰所有设置了过期时间的键值中最少使用的键值;
-
-2. allkeys-lfu,淘汰整个键值中最少使用的键值。
-
-> 小贴士:从以上内存淘汰策略中可以看出,allkeys-xxx 表示从所有的键值中淘汰数据,而 volatile-xxx 表示从设置了过期键的键值中淘汰数据。
-
-这个内存淘汰策略我们可以通过配置文件来修改,redis.conf 对应的配置项是"maxmemory-policy noeviction",只需要把它修改成我们需要设置的类型即可。
-
-需要注意的是,如果使用修改 redis.conf 的方式,当设置完成之后需要重启 Redis 服务器才能生效。
-
-还有另一种简单的修改内存淘汰策略的方式,我们可以使用命令行工具输入"config set maxmemory-policy noeviction"来修改内存淘汰的策略,这种修改方式的好处是执行成功之后就会生效,无需重启 Redis 服务器。但它的坏处是不能持久化内存淘汰策略,每次重启 Redis 服务器之后设置的内存淘汰策略就会丢失。
-
-**Redis 内存淘汰算法**
-内存淘汰算法主要包含两种:**LRU 淘汰算法和 LFU 淘汰算法。**
-
-**LRU**( Least Recently Used,最近最少使用)淘汰算法:是一种常用的页面置换算法,也就是说最久没有使用的缓存将会被淘汰。
-
-LRU 是基于链表结构实现的,链表中的元素按照操作顺序从前往后排列,最新操作的键会被移动到表头,当需要进行内存淘汰时,只需要删除链表尾部的元素即可。
-
-Redis 使用的是一种近似 LRU 算法,目的是为了更好的节约内存,它的实现方式是给现有的数据结构添加一个额外的字段,用于记录此键值的最后一次访问时间。Redis 内存淘汰时,会使用随机采样的方式来淘汰数据,它是随机取 5 个值 (此值可配置) ,然后淘汰最久没有使用的数据。
-
-**LFU**(Least Frequently Used,最不常用的)淘汰算法:最不常用的算法是根据总访问次数来淘汰数据的,它的核心思想是"如果数据过去被访问多次,那么将来被访问的频率也更高"。
-
-LFU 相对来说比 LRU 更"智能",因为它解决了使用频率很低的缓存,只是最近被访问了一次就不会被删除的问题。如果是使用 LRU 类似这种情况数据是不会被删除的,而使用 LFU 的话,这个数据就会被删除。
-
-Redis 内存淘汰策略使用了 LFU 和近 LRU 的淘汰算法,具体使用哪种淘汰算法,要看服务器是如何设置内存淘汰策略的,也就是要看"maxmemory-policy"的值是如何设置的。
-
-小结
-本课时我们讲了 Redis 的过期删除策略:惰性删除 + 定期删除;还讲了 Redis 的内存淘汰策略,它和过期策略是完全不同的两个概念,内存淘汰策略是当内存不够用时才会触发的一种机制,它在 Redis 4.0 之后提供了 8 种内存淘汰策略,这些淘汰策略主要使用了近 LRU 淘汰算法和 LFU 淘汰算法。
-
-
-
diff --git "a/docs/Redis/Redis344円270円272円344円273円200円344円271円210円344円274円232円350円277円231円344円271円210円345円277円253円.md" "b/docs/Redis/Redis344円270円272円344円273円200円344円271円210円344円274円232円350円277円231円344円271円210円345円277円253円.md"
deleted file mode 100644
index 93d82af..0000000
--- "a/docs/Redis/Redis344円270円272円344円273円200円344円271円210円344円274円232円350円277円231円344円271円210円345円277円253円.md"
+++ /dev/null
@@ -1,32 +0,0 @@
-# Redis为什么会这么快?
-
-## 关于Redis
-
-**Redis 全称是 REmote DIctionary Server,从名字中你也能看出来它用字典结构存储数据,也就是 key-value 类型的数据。Redis 的查询效率非常高,根据官方提供的数据,Redis 每秒最多处理的请求可以达到 10 万次。**
-
-## Redis为什么这么快
-
-Redis 采用 ANSI C 语言编写,它和 SQLite 一样。采用 **C 语言**进行编写的好处是底层代码执行效率高,依赖性低,因为使用 C 语言开发的库没有太多运行时(Runtime)依赖,而且系统的兼容性好,稳定性高。
-
-此外,**Redis 是基于内存的数据库**,我们之前讲到过,这样可以**避免磁盘 I/O**,因此 Redis 也被称为缓存工具。
-
-其次,**数据结构结构简单**,Redis 采用 Key-Value 方式进行存储,也就是使用 Hash 结构进行操作,数据的操作复杂度为 O(1)。
-
-但 Redis 快的原因还不止这些,它采用**单进程单线程模型**,这样做的好处就是避免了上下文切换和不必要的线程之间引起的资源竞争。
-
-在技术上 Redis 还采用了**多路 I/O 复用技术**。这里的多路指的是多个 socket 网络连接,复用指的是复用同一个线程。采用多路 I/O 复用技术的好处是可以在同一个线程中处理多个 I/O 请求,尽量减少网络 I/O 的消耗,提升使用效率。
-
-总结以上所述,Redis优点如下:
-
-1. 采用C语言编写,底层执行效率高。
-2. 基于内存的数据库,避免磁盘I/O。
-3. 采用单进程单线程,避免了上下文切换和不必要的线程引起的资源竞争。
-4. 采用多路 I/O 复用技术。
-
-## Redis中的数据类型
-
-Redis 支持的数据类型包括字符串、哈希、列表、集合、有序集合等。
-
-字符串类型是 Redis 提供的最基本的数据类型,对应的结构是 key-value。
-## 思考
-为什么 Redis 采用了单线程工作模式?有哪些机制可以保证 Redis 即使采用单线程模式效率也很高呢?
diff --git a/docs/Redis/_sidebar.md b/docs/Redis/_sidebar.md
new file mode 100644
index 0000000..d2a93c0
--- /dev/null
+++ b/docs/Redis/_sidebar.md
@@ -0,0 +1,2 @@
+* **👉 Redis** [↩](/README)
+ * [**Reids**]()
diff --git a/docs/SpringBoot/README.md b/docs/SpringBoot/README.md
new file mode 100644
index 0000000..20c3c4e
--- /dev/null
+++ b/docs/SpringBoot/README.md
@@ -0,0 +1,5 @@
+# SpringBoot
+
+* [SpringBoot的常用注解](docs/SpringBoot/SpringBoot的常用注解.md)
+* [基于SpringBoot集成Mybatis-Plus实现代码生成器](docs/SpringBoot/基于SpringBoot集成Mybatis-Plus实现代码生成器.md)
+
\ No newline at end of file
diff --git "a/docs/SpringBoot/SpringBoot347円232円204円345円270円270円347円224円250円346円263円250円350円247円243円.md" "b/docs/SpringBoot/SpringBoot347円232円204円345円270円270円347円224円250円346円263円250円350円247円243円.md"
new file mode 100644
index 0000000..204a8bd
--- /dev/null
+++ "b/docs/SpringBoot/SpringBoot347円232円204円345円270円270円347円224円250円346円263円250円350円247円243円.md"
@@ -0,0 +1,50 @@
+# 👉 SpringBoot的常用注解
+
+>**注解用来定义一个类、属性或方法,方便程序能够被编译处理。它也相当于一个说明文件。告诉程序被某个注解标注的类或属性是什么,要怎么处理。注解可以用在标注包、类、方法和变量。**
+
+## 1. 类上常使用的注解
+
+| 注解 | 使用位置 | 说明 |
+| :-------------------: | :----------------------------: | :----------------------------------------------------------: |
+| @RestController | 类名上 | @RestController=@Controller + @RequestBody 前后端分离情况下,用于返回JSON、XML等格式数据 |
+| @Controller | 类名上 | 声明当前类是一个控制器层,相当于MVC开发模式中的 C(控制器) |
+| @Service | 类名上 | 声明当前类是一个业务处理类,用于标注服务层,处理业务逻辑类 |
+| @Reporitory | 类名上 | 用于标注数据访问层 |
+| @Component | 类名上 | 通用的注解,可标注任意类为 `Spring` 组件。如果一个 Bean 不知道属于哪个层,可以使用`@Component` 注解标注。 |
+| @Configuration | 类名上 | 一般用来声明配置类,可以使用 `@Component`注解替代,不过使用`Configuration`注解声明配置类更加语义化 |
+| @Resource | 类名上、属性上或构造函数参数上 | 和@Autowired都可以用来装配Bean,默认是byType自动注入 |
+| @Autowired | 类名上、属性上或构造函数参数上 | 自动导入对象到类中,被注入进的类同样要被 Spring 容器管理,默认是byName自动注入 |
+| @RequestMapping | 类名或者方法上 | 用来处理请求地址映射 |
+| @Transactional | 类名或者方法上 | 在要开启事务的方法上使用`@Transactional`注解即可 |
+| @Qualifier | 类名或属性上 | 常与@Autowired一起使用,用于标注哪一个实现类才是需要注入的 |
+| @JsonIgnoreProperties | 类名上 | 用于过滤掉特定字段不返回或者不解析 |
+| @Transient | 属性上 | 声明不需要与数据库映射的字段,在保存的时候不需要保存进数据库,虚拟字段 |
+
+## 2. 方法上常使用的注解
+
+| 注解 | 使用位置 | 说明 |
+| :-----------: | :--------: | :----------------------------------------------------------: |
+| @Bean | 方法上 | 声明一个Bean并交给Spring管理。 |
+| @ReponseBody | 方法上 | 将方法返回的对象转换为JSON或XML格式后,写入Response对象的body数据区 |
+| @RequestBody | 方法参数前 | 简而言之,就是将JSON 字符串转换为 Java对象 |
+| @PathVariable | 方法参数前 | 将URL获取的参数映射到方法上 |
+| ··· | | |
+
+## 3. 其他常使用的注解
+
+| 注解 | 使用位置 | 说明 |
+| :----------------------: | :------------: | :----------------------------------------------------------: |
+| @EnableAutoConfiguration | 入口类、类名上 | 开启自动配置 |
+| @SpringBootApplication | 入口类、类名上 | 启动入口类Application(@SpringBootApplication看作是 @Configuration、@EnableAutoConfiguration、@ComponentScan 注解的集合。) |
+| @EnableScheduling | 入口类、类名上 | 用来开启计划任务,如定时执行的任务 |
+| @EnableAsync | 入口类、类名上 | 开启异步注解功能 |
+| @ComponentScan | 入口类、类名上 | 用来扫描组件,看自动发现和装配一些Bean |
+| @Aspec | 入口类、类名上 | 标注切面,可以用来配置事务、日志、权限验证等 |
+| @ControllerAdvice | 类名上 | 包含@Component,可以被扫描到,统一处理异常 |
+| @ExceptionHandler | 方法上 | 表示遇到这个异常就执行该方法 |
+| @Value | 属性上 | 用于获取配置文件中的值 |
+| ··· | | |
+
+## 4. 总结
+
+ 当然SpringBoot提供给我们开发人员的注解非常之多,上文中只是总结了SpringBoot开发过程中常用的一些注解,以及简述了其注解的作用或用途。
\ No newline at end of file
diff --git a/docs/SpringBoot/_sidebar.md b/docs/SpringBoot/_sidebar.md
new file mode 100644
index 0000000..f5a1b95
--- /dev/null
+++ b/docs/SpringBoot/_sidebar.md
@@ -0,0 +1,4 @@
+* **👉 SpringBoot** [↩](/README)
+ * [SpringBoot的常用注解](docs/SpringBoot/SpringBoot的常用注解.md)
+ * [基于SpringBoot集成Mybatis-Plus实现代码生成器](docs/SpringBoot/基于SpringBoot集成Mybatis-Plus实现代码生成器.md)
+
\ No newline at end of file
diff --git "a/docs/SpringBoot/345円237円272円344円272円216円SpringBoot351円233円206円346円210円220円Mybatis-Plus345円256円236円347円216円260円344円273円243円347円240円201円347円224円237円346円210円220円345円231円250円.md" "b/docs/SpringBoot/345円237円272円344円272円216円SpringBoot351円233円206円346円210円220円Mybatis-Plus345円256円236円347円216円260円344円273円243円347円240円201円347円224円237円346円210円220円345円231円250円.md"
new file mode 100644
index 0000000..4c0f8d9
--- /dev/null
+++ "b/docs/SpringBoot/345円237円272円344円272円216円SpringBoot351円233円206円346円210円220円Mybatis-Plus345円256円236円347円216円260円344円273円243円347円240円201円347円224円237円346円210円220円345円231円250円.md"
@@ -0,0 +1,236 @@
+# 👉 基于`SpringBoot`集成`Mybatis-Plus`实现代码生成器
+
+## 1. 引入所需依赖
+
+```xml
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+
+
+ com.baomidou
+ mybatis-plus-boot-starter
+ 3.3.2
+
+
+
+
+ com.baomidou
+ mybatis-plus-generator
+ 3.3.2
+
+
+
+
+ org.freemarker
+ freemarker
+ 2.3.30
+
+
+
+
+ mysql
+ mysql-connector-java
+ runtime
+
+
+
+
+ com.alibaba
+ druid
+ 1.1.20
+
+
+```
+
+## 2. application配置文件信息
+
+```yml
+server:
+ port: 8081
+spring:
+ datasource:
+ username: root
+ password: root
+ driver-class-name: com.mysql.cj.jdbc.Driver
+ url: jdbc:mysql://localhost:3306/sb?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai
+ type: com.alibaba.druid.pool.DruidDataSource
+
+mybatis-plus:
+ global-config:
+ db-config:
+ table-prefix: tb_ # 数据库表名的前缀
+ type-aliases-package: vim.msjava.examples.pojo # 实体类所在包
+ mapper-locations: classpath*:/mapper/**Mapper.xml # **Mapper.xml 文件所在位置
+```
+
+## 3. 启动类的扫描配置
+
+```java
+package vip.msjava.examples;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+@MapperScan(basePackages = "vip.msjava.example.mapper")
+public class SpringbootApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SpringbootApplication.class, args);
+ }
+
+}
+```
+
+## 4. 代码生成工具类
+
+```java
+package vip.msjava.examples.generator;
+
+
+import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
+import com.baomidou.mybatisplus.core.toolkit.StringPool;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.baomidou.mybatisplus.generator.AutoGenerator;
+import com.baomidou.mybatisplus.generator.InjectionConfig;
+import com.baomidou.mybatisplus.generator.config.*;
+import com.baomidou.mybatisplus.generator.config.po.TableInfo;
+import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
+import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Scanner;
+/**
+ * @author msJava
+ * @Description: 基于MyBatis-Plus 自动生成 代码
+ */
+public class MyBatisPlusGenerator {
+ /**
+ * 读取控制台内容
+ */
+ public static String scanner(String tip) {
+ Scanner scanner = new Scanner(System.in);
+ StringBuilder help = new StringBuilder();
+ help.append("请输入" + tip + ":");
+ System.out.println(help.toString());
+ if (scanner.hasNext()) {
+ String ipt = scanner.next();
+ if (StringUtils.isNotEmpty(ipt)) {
+ return ipt;
+ }
+ }
+ throw new MybatisPlusException("请输入正确的" + tip + "!");
+ }
+
+ public static void main(String[] args) {
+ // 代码生成器
+ AutoGenerator mpg = new AutoGenerator();
+
+ // 全局配置
+ GlobalConfig gc = new GlobalConfig();
+ String projectPath = System.getProperty("user.dir");
+ gc.setOutputDir(projectPath + "/src/main/java");
+ // gc.setOutputDir("D:\\test");
+ // 注释中的作者信息
+ gc.setAuthor("微信公众号: msJava , 一起实战学Java! ");
+ gc.setOpen(false);
+ // gc.setSwagger2(true); // 实体属性 Swagger2 注解
+ gc.setServiceName("%sService");
+ mpg.setGlobalConfig(gc);
+
+ // 数据源配置
+ DataSourceConfig dsc = new DataSourceConfig();
+ dsc.setUrl("jdbc:mysql://localhost:3306/sb?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=UTC");
+ // dsc.setSchemaName("public");
+ dsc.setDriverName("com.mysql.cj.jdbc.Driver");
+ dsc.setUsername("root");
+ dsc.setPassword("root");
+ mpg.setDataSource(dsc);
+
+ // 包配置
+ PackageConfig pc = new PackageConfig();
+ pc.setModuleName(null);
+ // 这个需要根据你项目的包 修改
+ pc.setParent("vip.msjava.examples");
+ mpg.setPackageInfo(pc);
+
+ // 自定义配置
+ InjectionConfig cfg = new InjectionConfig() {
+ @Override
+ public void initMap() {
+ // to do nothing
+ }
+ };
+
+ // 如果模板引擎是 freemarker
+ String templatePath = "/templates/mapper.xml.ftl";
+ // 如果模板引擎是 velocity
+ // String templatePath = "/templates/mapper.xml.vm";
+
+ // 自定义输出配置
+ List focList = new ArrayList();
+ // 自定义配置会被优先输出
+ focList.add(new FileOutConfig(templatePath) {
+ @Override
+ public String outputFile(TableInfo tableInfo) {
+ // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
+ return projectPath + "/src/main/resources/mapper/"
+ + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
+ }
+ });
+
+ cfg.setFileOutConfigList(focList);
+ mpg.setCfg(cfg);
+
+ // 配置模板
+ TemplateConfig templateConfig = new TemplateConfig();
+
+ templateConfig.setXml(null);
+ mpg.setTemplate(templateConfig);
+
+ // 策略配置
+ StrategyConfig strategy = new StrategyConfig();
+ strategy.setNaming(NamingStrategy.underline_to_camel);
+ strategy.setColumnNaming(NamingStrategy.underline_to_camel);
+ strategy.setEntityLombokModel(true);
+ strategy.setRestControllerStyle(true);
+ strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
+ strategy.setControllerMappingHyphenStyle(true);
+ // 数据库 表明前缀 也可以不配置
+ strategy.setTablePrefix("tb_"); // 设置表前缀
+ mpg.setStrategy(strategy);
+ mpg.setTemplateEngine(new FreemarkerTemplateEngine());
+ mpg.execute();
+ }
+
+
+}
+
+```
+
+## 5. 启动`MyBatisPlusGenerator`的Main方法
+
+**输入表名即可,如果一次生成多个表信息,按照提示即可;**
+
+**最终的项目结果如下图所示:**
+
+
\ No newline at end of file
diff --git a/docs/java/IO.md b/docs/java/IO.md
deleted file mode 100644
index 50b97e4..0000000
--- a/docs/java/IO.md
+++ /dev/null
@@ -1,310 +0,0 @@
-### IO
-
-IO 是 Input/Output 的缩写,它是基于流模型实现的,比如操作文件时使用输入流和输出流来写入和读取文件等。
-
-#### IO 分类
-
-传统的 IO,按照流类型我们可以分为:
-
-- 字符流
-- 字节流
-
-其中,字符流包括 Reader、Writer;字节流包括 InputStream、OutputStream。
-传统 IO 的类关系图,如下图所示:
-
-
-
-#### IO 使用
-
-了解了 IO 之间的关系,下面我们正式进入实战环节,分别来看字符流(Reader、Writer)和字节流(InputStream、OutputStream)的使用。
-
-##### 1 Writer 使用
-
-Writer 可用来写入文件,请参考以下代码:
-
-```java
-// 给指定目录下的文件追加信息
-Writer writer = new FileWriter("d:\\io.txt",true);
-writer.append("老王");
-writer.close();
-```
-
-这几行简单的代码就可以实现把信息 `老王` 追加到 `d:\\io.txt` 的文件下,参数二表示的是覆盖文字还是追加文字。
-
-##### 2 Reader 使用
-
-Reader 可用来读取文件,请参考以下代码:
-
-```java
-Reader reader = new FileReader("d:\\io.txt");
-BufferedReader bufferedReader = new BufferedReader(reader);
-String str = null;
-// 逐行读取信息
-while (null != (str = bufferedReader.readLine())) {
- System.out.println(str);
-}
-bufferedReader.close();
-reader.close();
-```
-
-##### 3 InputStream 使用
-
-InputStream 可用来读取文件,请参考以下代码:
-
-```java
-InputStream inputStream = new FileInputStream(new File("d:\\io.txt"));
-byte[] bytes = new byte[inputStream.available()];
-// 读取到 byte 数组
-inputStream.read(bytes);
-// 内容转换为字符串
-String content = new String(bytes, "UTF-8");
-inputStream.close();
-```
-
-##### 4 OutputStream 使用
-
-OutputStream 可用来写入文件,请参考以下代码:
-
-```java
-OutputStream outputStream = new FileOutputStream(new File("d:\\io.txt"),true);
-outputStream.write("老王".getBytes());
-outputStream.close();
-```
-
-### NIO 介绍
-
-上面讲的内容都是 java.io 包下的知识点,但随着 Java 的不断发展,在 Java 1.4 时新的 IO 包出现了 java.nio,NIO(Non-Blocking IO)的出现解决了传统 IO,也就是我们经常说的 BIO(Blocking IO)同步阻塞的问题,NIO 提供了 Channel、Selector 和 Buffer 等概念,可以实现多路复用和同步非阻塞 IO 操作,从而大大提升了 IO 操作的性能。
-前面提到同步和阻塞的问题,那下面来看看同步和阻塞结合都有哪些含义。
-
-| 组合方式 | 性能分析 |
-| :--------- | :----------------------------------------------------------- |
-| 同步阻塞 | 最常用的一种用法,使用也是最简单的,但是 I/O 性能一般很差,CPU 大部分在空闲状态 |
-| 同步非阻塞 | 提升 I/O 性能的常用手段,就是将 I/O 的阻塞改成非阻塞方式,尤其在网络 I/O 是长连接,同时传输数据也不是很多的情况下,提升性能非常有效。 这种方式通常能提升 I/O 性能,但是会增加 CPU 消耗,要考虑增加的 I/O 性能能不能补偿 CPU 的消耗,也就是系统的瓶颈是在 I/O 还是在 CPU 上 |
-| 异步阻塞 | 这种方式在分布式数据库中经常用到。例如,在往一个分布式数据库中写一条记录,通常会有一份是同步阻塞的记录,而还有两至三份是备份记录会写到其他机器上,这些备份记录通常都是采用异步阻塞的方式写 I/O;异步阻塞对网络 I/O 能够提升效率,尤其像上面这种同时写多份相同数据的情况 |
-| 异步非阻塞 | 这种组合方式用起来比较复杂,只有在一些非常复杂的分布式情况下使用,像集群之间的消息同步机制一般用这种 I/O 组合方式。例如,Cassandra 的 Gossip 通信机制就是采用异步非阻塞的方式。它适合同时要传多份相同的数据到集群中不同的机器,同时数据的传输量虽然不大,但是却非常频繁。这种网络 I/O 用这个方式性能能达到最高 |
-
-了解了同步和阻塞的含义,下面来看 **NIO 的具体使用**,请参考以下代码:
-
-```java
-int port = 6666;
-new Thread(new Runnable() {
- @Override
- public void run() {
- try (Selector selector = Selector.open();
- ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();) {
- serverSocketChannel.bind(new InetSocketAddress(InetAddress.getLocalHost(), port));
- serverSocketChannel.configureBlocking(false);
- serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
- while (true) {
- selector.select(); // 阻塞等待就绪的 Channel
- Set selectionKeys = selector.selectedKeys();
- Iterator iterator = selectionKeys.iterator();
- while (iterator.hasNext()) {
- SelectionKey key = iterator.next();
- try (SocketChannel channel = ((ServerSocketChannel) key.channel()).accept()) {
- channel.write(Charset.defaultCharset().encode("老王,你好~"));
- }
- iterator.remove();
- }
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-}).start();
-
-new Thread(new Runnable() {
- @Override
- public void run() {
- // Socket 客户端 1(接收信息并打印)
- try (Socket cSocket = new Socket(InetAddress.getLocalHost(), port)) {
- BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(cSocket.getInputStream()));
- bufferedReader.lines().forEach(s -> System.out.println("客户端 1 打印:" + s));
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-}).start();
-
-new Thread(new Runnable() {
- @Override
- public void run() {
- // Socket 客户端 2(接收信息并打印)
- try (Socket cSocket = new Socket(InetAddress.getLocalHost(), port)) {
- BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(cSocket.getInputStream()));
- bufferedReader.lines().forEach(s -> System.out.println("客户端 2 打印:" + s));
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-}).start();
-```
-
-以上代码创建了两个 Socket 客户端,用于收取和打印服务器端的消息。
-其中,服务器端通过 SelectionKey(选择键)获取到 SocketChannel(通道),而通道都注册到 Selector(选择器)上,所有的客户端都可以获得对应的通道,而不是所有客户端都排队堵塞等待一个服务器连接,这样就实现多路复用的效果了。多路指的是多个通道(SocketChannel),而复用指的是一个服务器端连接重复被不同的客户端使用。
-
-### AIO 介绍
-
-AIO(Asynchronous IO)是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。
-AIO 实现简单的 Socket 服务器,代码如下:
-
-```java
-int port = 8888;
-new Thread(new Runnable() {
- @Override
- public void run() {
- AsynchronousChannelGroup group = null;
- try {
- group = AsynchronousChannelGroup.withThreadPool(Executors.newFixedThreadPool(4));
- AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open(group).bind(new InetSocketAddress(InetAddress.getLocalHost(), port));
- server.accept(null, new CompletionHandler() {
- @Override
- public void completed(AsynchronousSocketChannel result, AsynchronousServerSocketChannel attachment) {
- server.accept(null, this); // 接收下一个请求
- try {
- Future f = result.write(Charset.defaultCharset().encode("Hi, 老王"));
- f.get();
- System.out.println("服务端发送时间:" + DateFormat.getDateTimeInstance().format(new Date()));
- result.close();
- } catch (InterruptedException | ExecutionException | IOException e) {
- e.printStackTrace();
- }
- }
- @Override
- public void failed(Throwable exc, AsynchronousServerSocketChannel attachment) {
- }
- });
- group.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
- } catch (IOException | InterruptedException e) {
- e.printStackTrace();
- }
- }
-}).start();
-
-// Socket 客户端
-AsynchronousSocketChannel client = AsynchronousSocketChannel.open();
-Future future = client.connect(new InetSocketAddress(InetAddress.getLocalHost(), port));
-future.get();
-ByteBuffer buffer = ByteBuffer.allocate(100);
-client.read(buffer, null, new CompletionHandler() {
- @Override
- public void completed(Integer result, Void attachment) {
- System.out.println("客户端打印:" + new String(buffer.array()));
- }
-
- @Override
- public void failed(Throwable exc, Void attachment) {
- exc.printStackTrace();
- try {
- client.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-});
-Thread.sleep(10 * 1000);
-```
-
-### 相关面试题
-
-#### 1.使用以下哪个方法来判断一个文件是否存在?
-
-A:createFile
-B:exists
-C:read
-D:exist
-
-答:B
-
-#### 2.以下说法错误的是?
-
-A:同步操作不一定会阻塞
-B:异步操作不一定会阻塞
-C:阻塞一定是同步操作
-D:同步或异步都可能会阻塞
-
-答:C
-
-题目解析:异步操作也可能会阻塞,比如分布式集群消息同步,采用的就是异步阻塞的方式。
-
-#### 3.BIO、NIO、AIO 的区别是什么?
-
-答:它们三者的区别如下。
-
-- BIO 就是传统的 java.io 包,它是基于流模型实现的,交互的方式是同步、阻塞方式,也就是说在读入输入流或者输出流时,在读写动作完成之前,线程会一直阻塞在那里,它们之间的调用是可靠的线性顺序。它的优点就是代码比较简单、直观;缺点就是 IO 的效率和扩展性很低,容易成为应用性能瓶颈。
-- NIO 是 Java 1.4 引入的 java.nio 包,提供了 Channel、Selector、Buffer 等新的抽象,可以构建多路复用的、同步非阻塞 IO 程序,同时提供了更接近操作系统底层高性能的数据操作方式。
-- AIO 是 Java 1.7 之后引入的包,是 NIO 的升级版本,提供了异步非堵塞的 IO 操作方式,因此人们叫它 AIO(Asynchronous IO),异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。
-
-简单来说 BIO 就是传统 IO 包,产生的最早;NIO 是对 BIO 的改进提供了多路复用的同步非阻塞 IO,而 AIO 是 NIO 的升级,提供了异步非阻塞 IO。
-
-#### 4.读取和写入文件最简洁的方式是什么?
-
-答:使用 Java 7 提供的 Files 读取和写入文件是最简洁,请参考以下代码:
-
-```java
-// 读取文件
-byte[] bytes = Files.readAllBytes(Paths.get("d:\\io.txt"));
-// 写入文件
-Files.write(Paths.get("d:\\io.txt"), "追加内容".getBytes(), StandardOpenOption.APPEND);
-```
-
-读取和写入都是一行代码搞定,可以说很简洁了。
-
-#### 5.Files 常用方法都有哪些?
-
-答:Files 是 Java 1.7 提供的,使得文件和文件夹的操作更加方便,它的常用方法有以下几个:
-
-- Files. exists():检测文件路径是否存在
-- Files. createFile():创建文件
-- Files. createDirectory():创建文件夹
-- Files. delete():删除一个文件或目录
-- Files. copy():复制文件
-- Files. move():移动文件
-- Files. size():查看文件个数
-- Files. read():读取文件
-- Files. write():写入文件
-
-#### 6.FileInputStream 可以实现什么功能?
-
-答:FileInputStream 可以实现文件的读取。
-
-题目解析:因为 FileInputStream 和 FileOutputStream 很容易被记反,FileOutputStream 才是用来写入文件的,所以也经常被面试官问到。
-
-#### 7.不定项选择:为了提高读写性能,可以采用什么流?
-
- A:InputStream
- B:DataInputStream
- C:BufferedReader
- D:BufferedInputStream
- E:OutputStream
- F:BufferedOutputStream
-
-答:D、F
-
-题目解析:BufferedInputStream 是一种带缓存区的输入流,在读取字节数据时可以从底层流中一次性读取多个字节到缓存区,而不必每次都调用系统底层;同理,BufferedOutputStream 也是一种带缓冲区的输出流,通过缓冲区输出流,应用程序先把字节写入缓冲区,缓存区满后再调用操作系统底层,从而提高系统性能,而不必每次都去调用系统底层方法。
-
-#### 8.FileInputStream 和 BufferedInputStream 的区别是什么?
-
-答:FileInputStream 在小文件读写时性能较好,而在大文件操作时使用 BufferedInputStream 更有优势。
-
-#### 9.以下这段代码运行在 Windwos 平台,执行的结果是?
-
-```java
-Files.createFile(Paths.get("c:\\pf.txt"), PosixFilePermissions.asFileAttribute(
- EnumSet.of(PosixFilePermission.OWNER_READ)));
-```
-
-A:在指定的盘符产生了对应的文件,文件只读
-B:在指定的盘符产生了对应的文件,文件只写
-C:在指定的盘符产生了对应的文件,文件可读写
-D:程序报错
-
-答:D
-
-题目解析:本题目考察的是 Files.createFile 参数传递的问题,PosixFilePermissions 不支持 Windows,因此在 Windows 执行会报错 java.lang.UnsupportedOperationException: 'posix:permissions' not supported as initial attribute。
-
-### 总结
-
-在 Java 1.4 之前只有 BIO(Blocking IO)可供使用,也就是 java.io 包下的那些类,它的缺点是同步阻塞式运行的。随后在 Java 1.4 时,提供了 NIO(Non-Blocking IO)属于 BIO 的升级,提供了同步非阻塞的 IO 操作方式,它的重要组件是 Selector(选择器)、Channel(通道)、Buffer(高效数据容器)实现了多路复用的高效 IO 操作。而 AIO(Asynchronous IO)也叫 NIO 2.0,属于 NIO 的补充和升级,提供了异步非阻塞的 IO 操作。
-
-还有另一个重要的知识点,是 Java 7.0 时新增的 Files 类,极大地提升了文件操作的便利性,比如读、写文件 Files.write()、Files.readAllBytes() 等,都是非常简便和实用的方法。
\ No newline at end of file
diff --git "a/docs/java/LinkedList346円272円220円347円240円201円345円210円206円346円236円220円.md" "b/docs/java/LinkedList346円272円220円347円240円201円345円210円206円346円236円220円.md"
deleted file mode 100644
index b9e91bd..0000000
--- "a/docs/java/LinkedList346円272円220円347円240円201円345円210円206円346円236円220円.md"
+++ /dev/null
@@ -1,268 +0,0 @@
-# LinkedList源码分析
-
-## 1 整体架构
-
-LinkedList 底层数据结构是一个双向链表,整体结构如下图所示:
-图片描述上图代表了一个双向链表结构,链表中的每个节点都可以向前或者向后追溯,我们有几个概念如下:
-
-- 链表每个节点我们叫做 Node,Node 有 prev 属性,代表前一个节点的位置,next 属性,代表后一个节点的位置;
-- first 是双向链表的头节点,它的前一个节点是 null。
-- last 是双向链表的尾节点,它的后一个节点是 null;
-- 当链表中没有数据时,first 和 last 是同一个节点,前后指向都是 null;
-- 因为是个双向链表,只要机器内存足够强大,是没有大小限制的。
-
-链表中的元素叫做 Node,我们看下 Node 的组成部分:
-
-```java
-private static class Node {
- E item;// 节点值
- Node next; // 指向的下一个节点
- Node prev; // 指向的前一个节点
-
- // 初始化参数顺序分别是:前一个节点、本身节点值、后一个节点
- Node(Node prev, E element, Node next) {
- this.item = element;
- this.next = next;
- this.prev = prev;
- }
-}
-```
-
-
-
-## 2 源码解析
-
-
-
-### 2.1 追加(新增)
-
-追加节点时,我们可以选择追加到链表头部,还是追加到链表尾部,add 方法默认是从尾部开始追加,addFirst 方法是从头部开始追加,我们分别来看下两种不同的追加方式:
-
-**从尾部追加(add)**
-
-```java
-// 从尾部开始追加节点
-void linkLast(E e) {
- // 把尾节点数据暂存
- final Node l = last;
- // 新建新的节点,初始化入参含义:
- // l 是新节点的前一个节点,当前值是尾节点值
- // e 表示当前新增节点,当前新增节点后一个节点是 null
- final Node newNode = new Node(l, e, null);
- // 新建节点追加到尾部
- last = newNode;
- //如果链表为空(l 是尾节点,尾节点为空,链表即空),头部和尾部是同一个节点,都是新建的节点
- if (l == null)
- first = newNode;
- //否则把前尾节点的下一个节点,指向当前尾节点。
- else
- l.next = newNode;
- //大小和版本更改
- size++;
- modCount++;
-}
-```
-
-从源码上来看,尾部追加节点比较简单,只需要简单地把指向位置修改下即可,我们做个动图来描述下整个过程:
-
-**从头部追加(addFirst)**
-
-```java
-// 从头部追加
-private void linkFirst(E e) {
- // 头节点赋值给临时变量
- final Node f = first;
- // 新建节点,前一个节点指向null,e 是新建节点,f 是新建节点的下一个节点,目前值是头节点的值
- final Node newNode = new Node(null, e, f);
- // 新建节点成为头节点
- first = newNode;
- // 头节点为空,就是链表为空,头尾节点是一个节点
- if (f == null)
- last = newNode;
- //上一个头节点的前一个节点指向当前节点
- else
- f.prev = newNode;
- size++;
- modCount++;
-}
-```
-
-头部追加节点和尾部追加节点非常类似,只是前者是移动头节点的 prev 指向,后者是移动尾节点的 next 指向。
-
-
-### 2.2 节点删除
-
-节点删除的方式和追加类似,我们可以选择从头部删除,也可以选择从尾部删除,删除操作会把节点的值,前后指向节点都置为 null,帮助 GC 进行回收。
-
-**从头部删除**
-
-```java
-//从头删除节点 f 是链表头节点
-private E unlinkFirst(Node f) {
- // 拿出头节点的值,作为方法的返回值
- final E element = f.item;
- // 拿出头节点的下一个节点
- final Node next = f.next;
- //帮助 GC 回收头节点
- f.item = null;
- f.next = null;
- // 头节点的下一个节点成为头节点
- first = next;
- //如果 next 为空,表明链表为空
- if (next == null)
- last = null;
- //链表不为空,头节点的前一个节点指向 null
- else
- next.prev = null;
- //修改链表大小和版本
- size--;
- modCount++;
- return element;
-}
-```
-
-从尾部删除节点代码也是类似的,就不贴了。
-
-**从源码中我们可以了解到,链表结构的节点新增、删除都非常简单,仅仅把前后节点的指向修改下就好了,所以 LinkedList 新增和删除速度很快。**
-
-
-
-### 2.3 节点查询
-
-链表查询某一个节点是比较慢的,需要挨个循环查找才行,我们看看 LinkedList 的源码是如何寻找节点的:
-
-```java
-// 根据链表索引位置查询节点
-Node node(int index) {
- // 如果 index 处于队列的前半部分,从头开始找,size>> 1 是 size 除以 2 的意思。
- if (index < (size>> 1)) {
- Node x = first;
- // 直到 for 循环到 index 的前一个 node 停止
- for (int i = 0; i < index; i++) - x = x.next; - return x; - } else {// 如果 index 处于队列的后半部分,从尾开始找 - Node x = last;
- // 直到 for 循环到 index 的后一个 node 停止
- for (int i = size - 1; i> index; i--)
- x = x.prev;
- return x;
- }
-}
-```
-
-从源码中我们可以发现,LinkedList 并没有采用从头循环到尾的做法,而是采取了简单二分法,首先看看 index 是在链表的前半部分,还是后半部分。如果是前半部分,就从头开始寻找,反之亦然。通过这种方式,使循环的次数至少降低了一半,提高了查找的性能,这种思想值得我们借鉴。
-
-### 2.4 方法对比
-
-LinkedList 实现了 Queue 接口,在新增、删除、查询等方面增加了很多新的方法,这些方法在平时特别容易混淆,在链表为空的情况下,返回值也不太一样,我们列一个表格,方便大家记录:
-
-| 方法含义 | 返回异常 | 返回特殊值 | 底层实现 |
-| :------- | :-------- | :--------- | :----------------------------------------------- |
-| 新增 | add(e) | offer(e) | 底层实现相同 |
-| 删除 | remove() | poll(e) | 链表为空时,remove 会抛出异常,poll 返回 null。 |
-| 查找 | element() | peek() | 链表为空时,element 会抛出异常,peek 返回 null。 |
-
-PS:Queue 接口注释建议 add 方法操作失败时抛出异常,但 LinkedList 实现的 add 方法一直返回 true。
-LinkedList 也实现了 Deque 接口,对新增、删除和查找都提供从头开始,还是从尾开始两种方向的方法,比如 remove 方法,Deque 提供了 removeFirst 和 removeLast 两种方向的使用方式,但当链表为空时的表现都和 remove 方法一样,都会抛出异常。
-
-### 2.5 迭代器
-
-因为 LinkedList 要实现双向的迭代访问,所以我们使用 Iterator 接口肯定不行了,因为 Iterator 只支持从头到尾的访问。Java 新增了一个迭代接口,叫做:ListIterator,这个接口提供了向前和向后的迭代方法,如下所示:
-
-| 迭代顺序 | 方法 |
-| :--------------- | :----------------------------------- |
-| 从尾到头迭代方法 | hasPrevious、previous、previousIndex |
-| 从头到尾迭代方法 | hasNext、next、nextIndex |
-
-LinkedList 实现了 ListIterator 接口,如下图所示:
-
-```java
-// 双向迭代器
-private class ListItr implements ListIterator {
- private Node lastReturned;//上一次执行 next() 或者 previos() 方法时的节点位置
- private Node next;//下一个节点
- private int nextIndex;//下一个节点的位置
- //expectedModCount:期望版本号;modCount:目前最新版本号
- private int expectedModCount = modCount;
- ............
-}
-```
-
-我们先来看下从头到尾方向的迭代:
-
-```java
-// 判断还有没有下一个元素
-public boolean hasNext() {
- return nextIndex < size;// 下一个节点的索引小于链表的大小,就有 -} - -// 取下一个元素 -public E next() { - //检查期望版本号有无发生变化 - checkForComodification(); - if (!hasNext())//再次检查 - throw new NoSuchElementException(); - // next 是当前节点,在上一次执行 next() 方法时被赋值的。 - // 第一次执行时,是在初始化迭代器的时候,next 被赋值的 - lastReturned = next; - // next 是下一个节点了,为下次迭代做准备 - next = next.next; - nextIndex++; - return lastReturned.item; -} -``` - -上述源码的思路就是直接取当前节点的下一个节点,而从尾到头迭代稍微复杂一点,如下: - -```java -// 如果上次节点索引位置大于 0,就还有节点可以迭代 -public boolean hasPrevious() { - return nextIndex> 0;
-}
-// 取前一个节点
-public E previous() {
- checkForComodification();
- if (!hasPrevious())
- throw new NoSuchElementException();
- // next 为空场景:1:说明是第一次迭代,取尾节点(last);2:上一次操作把尾节点删除掉了
- // next 不为空场景:说明已经发生过迭代了,直接取前一个节点即可(next.prev)
- lastReturned = next = (next == null) ? last : next.prev;
- // 索引位置变化
- nextIndex--;
- return lastReturned.item;
-}
-```
-
-这里复杂点体现在需要判断 next 不为空和为空的场景,代码注释中有详细的描述。
-
-**迭代器删除**
-
-LinkedList 在删除元素时,也推荐通过迭代器进行删除,删除过程如下:
-
-```java
-public void remove() {
- checkForComodification();
- // lastReturned 是本次迭代需要删除的值,分以下空和非空两种情况:
- // lastReturned 为空,说明调用者没有主动执行过 next() 或者 previos(),直接报错
- // lastReturned 不为空,是在上次执行 next() 或者 previos()方法时赋的值
- if (lastReturned == null)
- throw new IllegalStateException();
- Node lastNext = lastReturned.next;
- //删除当前节点
- unlink(lastReturned);
- // next == lastReturned 的场景分析:从尾到头递归顺序,并且是第一次迭代,并且要删除最后一个元素的情况下
- // 这种情况下,previous() 方法里面设置了 lastReturned = next = last,所以 next 和 lastReturned会相等
- if (next == lastReturned)
- // 这时候 lastReturned 是尾节点,lastNext 是 null,所以 next 也是 null,这样在 previous() 执行时,发现 next 是 null,就会把尾节点赋值给 next
- next = lastNext;
- else
- nextIndex--;
- lastReturned = null;
- expectedModCount++;
-}
-```
-
-## 总结
-
-LinkedList 适用于要求有顺序、并且会按照顺序进行迭代的场景,主要是依赖于底层的链表结构,在面试中的频率还是蛮高的,相信理清楚上面的源码后,应对面试应该没有问题。
\ No newline at end of file
diff --git "a/docs/jvm/JVM347円241円256円350円256円244円345円217円257円345円233円236円346円224円266円345円257円271円350円261円241円347円232円204円346円226円271円345円274円217円.md" "b/docs/jvm/JVM347円241円256円350円256円244円345円217円257円345円233円236円346円224円266円345円257円271円350円261円241円347円232円204円346円226円271円345円274円217円.md"
deleted file mode 100644
index 7d209ae..0000000
--- "a/docs/jvm/JVM347円241円256円350円256円244円345円217円257円345円233円236円346円224円266円345円257円271円350円261円241円347円232円204円346円226円271円345円274円217円.md"
+++ /dev/null
@@ -1,17 +0,0 @@
-# 如何确定垃圾
-
-+ 引用计数法
-
-在Java中如果要操作对象,就必须先获取该对象的引用,因此可以通过引用计数法来判断一个对象是否可以被回收。在为一个对象添加一个引用时,引用计数器就加1;为对象删除一个引用时,引用计数器就减1;如果一个对象的引用计数为0,则说明该对象没有被引用,可以回收。**优点是垃圾回收比较及时,实时性比较高,只要对象计数器为 0,则可以直接进行回收操作;而缺点是无法解决循环引用的问题。**
-
-+ 可达性分析
-
-以一系列GC ROOTS的点作为起点向下搜索,当一个对象到任何GC ROOTS都没有引用链相连时,说明该对象可以回收。
-
-在 Java 中可以作为 **CG Roots 的对象**,主要包含以下几个:
-
-1. 所有被同步锁持有的对象,比如被 synchronize 持有的对象;
-2. 字符串常量池里的引用(String Table);
-3. 类型为引用类型的静态变量;
-4. 虚拟机栈中引用对象;
-5. 本地方法栈中的引用对象。
\ No newline at end of file
diff --git "a/docs/jvm/Java350円277円220円350円241円214円346円227円266円345円206円205円345円255円230円345円210円222円345円210円206円.md" "b/docs/jvm/Java350円277円220円350円241円214円346円227円266円345円206円205円345円255円230円345円210円222円345円210円206円.md"
deleted file mode 100644
index 197855e..0000000
--- "a/docs/jvm/Java350円277円220円350円241円214円346円227円266円345円206円205円345円255円230円345円210円222円345円210円206円.md"
+++ /dev/null
@@ -1,26 +0,0 @@
-# JVM运行内存区域
-
-> JVM内存区域分为线程私有区域(程序计数器、虚拟机栈、本地方法区),线程共享区域(堆内存、方法区)和直接内存。
-
-+ 程序计数器
-
-程序计数器是一块很小的内存空间,用于存储当前运行的线程所执行的字节码的行号指令,每个运行中的线程都有一个独立的程序计数器,在方法正在执行时,该方法的程序计数器记录的是实时虚拟机字节码指令的地址;如果该方法执行的是Native方法,则程序计数器的值为空。
-
-+ 虚拟机栈
-
-虚拟机栈是描述Java方法的执行过程的内存模型,它在当前栈帧存储了局部变量表、操作数栈、动态链接、方法出口等信息。同时,栈帧用来存储部分运行时数据及其数据结构,处理动态链接方法的返回值和异常分派。
-
-+ 本地方法区
-
-本地方法区和虚拟机栈作用类似,区别就是本地方法栈是为Native方法服务,而虚拟机栈是为了Java方法服务。
-
-+ 堆内存
-
-在JVM运行过程中创建的对象和产生的数据都被存储在堆中,堆是被线程共享的内存区域,也是垃圾收集器进行垃圾回首的最主要的内存区域。
-
-+ 方法区
-
-方法区用于存储常量、静态变量、类信息、即时编译器编译后的机器码、运行时常量等数据。
-
-
-
diff --git "a/docs/jvm/345円236円203円345円234円276円345円233円236円346円224円266円347円256円227円346円263円225円.md" "b/docs/jvm/345円236円203円345円234円276円345円233円236円346円224円266円347円256円227円346円263円225円.md"
deleted file mode 100644
index bcdab76..0000000
--- "a/docs/jvm/345円236円203円345円234円276円345円233円236円346円224円266円347円256円227円346円263円225円.md"
+++ /dev/null
@@ -1,24 +0,0 @@
-# 垃圾回收算法
-
-## 复制算法
-
-- 将可用内存按容量划分为相等的两部分,每次只使用其中的一块,当一块内存用完时,就将还存活的对象复制到第二块内存上,然后一次性清除第一块内存,再将第二块上的对象复制到第一块。
-- 实现方便,运行高效,不用考虑内存碎片,但是内存利用率只有一半。
-
-## 标记清除算法
-
-- 分为**标记**和**清除**两个阶段。首先标记出所有需要回收的对象,在标记完成后统一回收被标记的对象。
-- 算法简单,但是有两个缺点:
- - 1、效率不高,标记和清除的效率都很低;
- - 2、空间问题,会产生大量不连续的内存碎片,导致以后程序在分配较大的对象时,由于没有充足的连续内存而提前触发一次 GC 动作。
-
-## 标记整理算法
-
-- 标记过程仍然与 "标记 - 清除" 算法一样,但不是直接对可回收对象进行清理,而是让所有存活的对象向一端移动,然后直接清理掉边界以外的内存,形成一版连续的内存区域。
-- 解决标记 - 清除算法产生的大量内存碎片问题;当对象存活率较高时,也解决了复制算法的空间效率问题,不过它本身也存在时间效率方面的问题。
-
-## 分代收集算法
-
-- 根据对象的生存周期,将堆分为新生代和老年代,然后根据各个年代的特点采用最适当的收集算法。在新生代中,由于对象生存期短,每次回收都会有大量对象死去,那么这时就采用**复制**算法。老年代里的对象存活率较高,没有额外的空间进行分配担保,所以可以使用**标记 - 整理** 或者 **标记 - 清除**。
-- 严格地说,这并非是一种算法,而是一种思想,或者说是一种复合算法。
-
diff --git "a/docs/network/OSI344円270円203円345円261円202円347円275円221円347円273円234円346円250円241円345円236円213円.md" "b/docs/network/OSI344円270円203円345円261円202円347円275円221円347円273円234円346円250円241円345円236円213円.md"
deleted file mode 100644
index b1bc885..0000000
--- "a/docs/network/OSI344円270円203円345円261円202円347円275円221円347円273円234円346円250円241円345円236円213円.md"
+++ /dev/null
@@ -1,17 +0,0 @@
-# OSI 七层网络模型
-
-+ 应用层
-> 基于网络构建具体应用。如ftp文件上传下载服务、http服务、dns服务、snmp邮件服务、telnet服务等;
-+ 表示层
-> 主要是对接受的数据进行解释、加密、解密、压缩、解压缩等,转化为人能识别的内容(图片、声音、文字)等;
-+ 会话层
-> 建立连接并访问验证和会话管理,如登录验证、断点续传、数据沾包分包等;
-+ 传输层
-> 定义了传输数据的协议和端口号,如tcp、udp等;
-+ 网络层
-> ip地址的封装和解析,如路由器、交换机、防火墙等;
-+ 数据链路层
-> mac地址解析和封装,如数据帧、网卡、网桥、交换机等;
-+ 物理层
-> 定义物理设备标准,如比特流;
-
diff --git "a/docs/tool/347円233円256円345円275円225円345円257円274円350円210円252円.md" "b/docs/tool/347円233円256円345円275円225円345円257円274円350円210円252円.md"
deleted file mode 100644
index 78c67a2..0000000
--- "a/docs/tool/347円233円256円345円275円225円345円257円274円350円210円252円.md"
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-| ♨ | ⭕ | 🔐 | 💈 | 💻 | 🚏 | 🏖 | 📰 | 📮 | 🔍 | 🗽 | 🚀 | 🌈 | ☎ |
-| ------------------------------------------------------------ | -------------------------------------------------- | ------------------------------------------------------ | ---------------------------------------------- | ------------------------------------------------ | ---------------------------------------------------------- | -------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------ | ------------------------------------------------------------ | ---------------------------------------------------- | ---------------------------------------------------- | ------------------------------------------------------ | ---------------------------------------------------------- |
-| [Java核心基础](https://github.com/msJavaCoder/msJava#java核心基础) | [集合](https://github.com/msJavaCoder/msJava#集合) | [多线程](https://github.com/msJavaCoder/msJava#多线程) | [IO](https://github.com/msJavaCoder/msJava#IO) | [JVM](https://github.com/msJavaCoder/msJava#JVM) | [设计模式](https://github.com/msJavaCoder/msJava#设计模式) | [框架](https://github.com/msJavaCoder/msJava#框架) | [数据结构与算法](https://github.com/msJavaCoder/msJava#数据结构与算法) | [数据库](https://github.com/msJavaCoder/msJava#数据库) | [算法](https://github.com/msJavaCoder/msJava#数据结构与算法) | [Redis](https://github.com/msJavaCoder/msJava#Redis) | [Linux](https://github.com/msJavaCoder/msJava#Linux) | [面试题](https://github.com/msJavaCoder/msJava#面试题) | [联系作者](https://github.com/msJavaCoder/msJava#联系作者) |
\ No newline at end of file
diff --git "a/docs/tool/360円237円224円245円msJava.md" "b/docs/tool/360円237円224円245円msJava.md"
deleted file mode 100644
index e190657..0000000
--- "a/docs/tool/360円237円224円245円msJava.md"
+++ /dev/null
@@ -1,69 +0,0 @@
-# 🔥msJava
-
-> 关注微信订阅号: 码上Java 回复关键字 : 1 领取后端面试大礼包(简历模板、Java、数据结构、算法、数据库、python、操作系统、网络等)
-
-| ♨ | ⭕ | 🔐 | 💈 | 💻 | 🚏 | 🏖 | 📰 | 📮 | 🔍 | 🗽 | 🚀 | 🌈 | ☎ |
-| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----: | :-------------------------------------------------: | :----------: | ------------------------------------------------------------ | :----------------: | :--------------------------------------------------------: | :----------------------------------------------------------: | :-------: | :-------: | :--------: | :----------------------------------------------------------: |
-| [Java核心基础]([https://github.com/msJavaCoder/msJava/blob/master/README.md#java%E6%A0%B8%E5%BF%83%E5%9F%BA%E7%A1%80](https://github.com/msJavaCoder/msJava/blob/master/README.md#java核心基础)) | [集合]([https://github.com/msJavaCoder/msJava/blob/master/README.md#%E9%9B%86%E5%90%88](https://github.com/msJavaCoder/msJava/blob/master/README.md#集合)) | [多线程]([https://github.com/msJavaCoder/msJava/blob/master/README.md#%E5%A4%9A%E7%BA%BF%E7%A8%8B](https://github.com/msJavaCoder/msJava/blob/master/README.md#多线程)) | [IO]() | [JVM](https://github.com/crossoverJie/JCSprout#jvm) | [设计模式]() | [框架](https://github.com/crossoverJie/JCSprout#常用框架第三方组件) | [数据结构与算法]() | [数据库](https://github.com/crossoverJie/JCSprout#db-相关) | [算法](https://github.com/crossoverJie/JCSprout#数据结构与算法) | [Redis]() | [Linux]() | [面试题]() | [联系作者](https://github.com/crossoverJie/JCSprout#联系作者) |
-
-### Java核心基础
-
-- [Java关键字理解](https://github.com/msJavaCoder/msJava/blob/master/Java核心基础/Java关键字理解.md)
-
-### 集合
-
-- [Java集合容器有哪些?](https://github.com/msJavaCoder/msJava/blob/master/集合/Java集合容器.md)
-- [HashMap原理分析](https://github.com/msJavaCoder/msJava/blob/master/集合/HashMap原理分析.md)
-- [HashMap 为什么是线程不安全的?](https://github.com/msJavaCoder/msJava/blob/master/集合/HashMap%20为什么是线程不安全的?.md)
-- ........
-
-### 多线程
-
-- [Java中的锁](https://github.com/msJavaCoder/msJava/blob/master/多线程/Java中的锁.md)
-- [理解synchronized关键字](https://github.com/msJavaCoder/msJava/blob/master/多线程/理解synchronized关键字.md)
-- [Java线程池](https://github.com/msJavaCoder/msJava/blob/master/多线程/Java线程池.md)
-- .....
-
-### IO
-
-- [IO基础知识总结](https://github.com/msJavaCoder/msJava/blob/master/IO/IO基础知识总结.md)
-
-### JVM
-
-- [Java运行时内存划分](https://github.com/msJavaCoder/msJava/blob/master/JVM/Java运行时内存划分.md)
-- [类加载机制](https://github.com/msJavaCoder/msJava/blob/master/JVM/类加载机制.md)
-- [垃圾回收器](https://github.com/msJavaCoder/msJava/blob/master/JVM/垃圾回收器.md)
-- [垃圾回收算法](https://github.com/msJavaCoder/msJava/blob/master/JVM/垃圾回收器算法.md)
-- [JVM确认可回收对象的方式](https://github.com/msJavaCoder/msJava/blob/master/JVM/JVM确认可回收对象的方式.md)
-- [Java内存模型](https://github.com/msJavaCoder/msJava/blob/master/JVM/JVM内存模型.md)
-- .....
-
-### 设计模式
-
-- [设计模式总结](https://github.com/msJavaCoder/msJava/blob/master/设计模式/设计模式总结.md)
-
-### 框架
-
-### 数据库
-
-### Redis
-
-### Linux
-
-- [后端开发必备Linux基础命令总结](https://github.com/msJavaCoder/msJava/blob/master/Linux/后端开发必备Linux基础命令总结.md)
-
-### 数据结构与算法
-
-### 面试题
-
-- [Java基础面试题](https://github.com/msJavaCoder/msJava/blob/master/面试题/Java基础面试题.md)
-- [Java进阶面试题](https://github.com/msJavaCoder/msJava/blob/master/面试题/Java进阶面试题.md)
-- [框架基础面试题](https://github.com/msJavaCoder/msJava/blob/master/面试题/框架基础面试题.md)
-- [框架进阶面试题](https://github.com/msJavaCoder/msJava/blob/master/面试题/框架进阶面试题.md)
-- [数据库基础面试题](https://github.com/msJavaCoder/msJava/blob/master/面试题/数据库基础面试题.md)
-- [数据库进阶面试题](https://github.com/msJavaCoder/msJava/blob/master/面试题/数据库进阶面试题.md)
-- .....
-
-### 联系作者
-
-> 关注微信订阅号: 码上Java 🔥🔥🔥
\ No newline at end of file
diff --git "a/docs/tool/Emoji347円254円246円345円217円267円345円244円247円345円205円250円.md" "b/docs/345円267円245円345円205円267円/Emoji347円254円246円345円217円267円345円244円247円345円205円250円.md"
similarity index 97%
rename from "docs/tool/Emoji347円254円246円345円217円267円345円244円247円345円205円250円.md"
rename to "docs/345円267円245円345円205円267円/Emoji347円254円246円345円217円267円345円244円247円345円205円250円.md"
index 3bf294d..10a8f33 100644
--- "a/docs/tool/Emoji347円254円246円345円217円267円345円244円247円345円205円250円.md"
+++ "b/docs/345円267円245円345円205円267円/Emoji347円254円246円345円217円267円345円244円247円345円205円250円.md"
@@ -1,4 +1,4 @@
-## Emoji符号大全
+# 👉 Emoji符号大全
> 如有符号显示不正常,请更换浏览器或操作系统浏览
@@ -7,7 +7,7 @@
| 常见 | 🌹🍀🍎💰📱🌙🍁🍂🍃🌷💎🔪🔫🏀⚽⚡👄👍🔥 |
| 表情 | 😀😁😂😃😄😅😆😉😊😋😎😍😘😗😙😚☺😇😐😑😶😏😣😥😮😯😪😫😴😌😛😜😝😒😓😔😕😲😷😖😞😟😤😢😭😦😧😨😬😰😱😳😵😡😠 |
| 人物 | 👦👧👨👩👴👵👶👱👮👲👳👷👸💂🎅👰👼💆💇🙍🙎🙅🙆💁🙋🙇🙌🙏👤👥🚶🏃👯💃👫👬👭💏💑👪 |
-| 手势 | 💪👈👉☝👆👇✌✋👌👍👎✊👊👋👏👐✍ |
+| 手势 | ☕️💪👈👉☝👆👇✌✋👌👍👎✊👊👋👏👐✍ |
| 日常 | 👣👀👂👃👅👄💋👓👔👕👖👗👘👙👚👛👜👝🎒💼👞👟👠👡👢👑👒🎩🎓💄💅💍🌂 |
| 手机 | 📱📲📶📳📴☎📞📟📠 |
| 公共 | ♻🏧🚮🚰♿🚹🚺🚻🚼🚾⚠🚸⛔🚫🚳🚭🚯🚱🚷🔞💈 |
diff --git "a/docs/346円241円206円346円236円266円/MyBatis.md" "b/docs/346円241円206円346円236円266円/MyBatis.md"
deleted file mode 100644
index aa7cbb4..0000000
--- "a/docs/346円241円206円346円236円266円/MyBatis.md"
+++ /dev/null
@@ -1,287 +0,0 @@
-## MyBatis
-
-### MyBatis 介绍
-
-MyBatis 是一款优秀的 ORM(Object Relational Mapping,对象关系映射)框架,它可以通过对象和数据库之间的映射,将程序中的对象自动存储到数据库中。它是 Apache 提供的一个开源项目,之前的名字叫做 iBatis,2010 年迁移到了 Google Code,并且将名字改为我们现在所熟知的 MyBatis,又于 2013 年 11 月迁移到了 Github。
-
-MyBatis 提供了普通 SQL 查询、事务、存储过程等功能,它的优缺点如下。
-
-**优点**:
-
-- 相比于 JDBC 需要编写的代码更少
-- 使用灵活,支持动态 SQL
-- 提供映射标签,支持对象与数据库的字段关系映射
-
-**缺点**:
-
-- SQL 语句依赖于数据库,数据库移植性差
-- SQL 语句编写工作量大,尤其在表、字段比较多的情况下
-
-总体来说,MyBatis 是一个非常优秀和灵活的数据持久化框架,适用于需求多变的互联网项目,也是当前主流的 ORM 框架。
-
-#### MyBatis 重要组件
-
-MyBatis 中的重要组件如下:
-
-- Mapper 配置:用于组织具体的查询业务和映射数据库的字段关系,可以使用 XML 格式或 Java 注解格式来实现;
-- Mapper 接口:数据操作接口也就是通常说的 DAO 接口,要和 Mapper 配置文件中的方法一一对应;
-- Executor:MyBatis 中所有的 Mapper 语句的执行都是通过 Executor 执行的;
-- SqlSession:类似于 JDBC 中的 Connection,可以用 SqlSession 实例来直接执行被映射的 SQL 语句;
-- SqlSessionFactory:SqlSessionFactory 是创建 SqlSession 的工厂,可以通过 SqlSession openSession() 方法创建 SqlSession 对象。
-
-#### MyBatis 执行流程
-
-MyBatis 完整执行流程如下图所示:
-
-
-
-MyBatis 执行流程说明:
-
-1. 首先加载 Mapper 配置的 SQL 映射文件,或者是注解的相关 SQL 内容。
-2. 创建会话工厂,MyBatis 通过读取配置文件的信息来构造出会话工厂(SqlSessionFactory)。
-3. 创建会话,根据会话工厂,MyBatis 就可以通过它来创建会话对象(SqlSession),会话对象是一个接口,该接口中包含了对数据库操作的增、删、改、查方法。
-4. 创建执行器,因为会话对象本身不能直接操作数据库,所以它使用了一个叫做数据库执行器(Executor)的接口来帮它执行操作。
-5. 封装 SQL 对象,在这一步,执行器将待处理的 SQL 信息封装到一个对象中(MappedStatement),该对象包括 SQL 语句、输入参数映射信息(Java 简单类型、HashMap 或 POJO)和输出结果映射信息(Java 简单类型、HashMap 或 POJO)。
-6. 操作数据库,拥有了执行器和 SQL 信息封装对象就使用它们访问数据库了,最后再返回操作结果,结束流程。
-
-### MyBatis XML 版
-
-MyBatis 使用分为两个版本:XML 版和 Java 注解版。接下来我们使用 Spring Boot 结合 MyBatis 的 XML 版,来实现对数据库的基本操作,步骤如下。
-
-#### 1)创建数据表
-
-```sql
-drop table if exists `t_user`;
-create table `t_user` (
- `id` bigint(20) not null auto_increment comment '主键id',
- `username` varchar(32) default null comment '用户名',
- `password` varchar(32) default null comment '密码',
- `nick_name` varchar(32) default null,
- primary key (`id`)
-) engine=innodb auto_increment=1 default charset=utf8;
-```
-
-#### 2)添加依赖
-
-在项目添加对 MyBatis 和 MySQL 支持的依赖包,在 pom.xml 文件中添加如下代码:
-
-```xml
-
-
- org.mybatis.spring.boot
- mybatis-spring-boot-starter
- 2.1.0
-
-
-
- mysql
- mysql-connector-java
- 8.0.16
-
-```
-
-mybatis-spring-boot-starter 是 MyBatis 官方帮助我们快速集成 Spring Boot 提供的一个组件包,mybatis-spring-boot-starter 2.1.0 对应 MyBatis 的版本是 3.5.2。
-
-#### 3)增加配置文件
-
-在 application.yml 文件中添加以下内容:
-
-```xml
-spring:
- datasource:
- url: jdbc:mysql://localhost:3306/learndb?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
- username: root
- password: root
- driver-class-name: com.mysql.cj.jdbc.Driver
-mybatis:
- config-location: classpath:mybatis/mybatis-config.xml
- mapper-locations: classpath:mybatis/mapper/*.xml
- type-aliases-package: com.interview.mybatislearning.model
-```
-
-其中:
-
-- mybatis.config-location:配置 MyBatis 基础属性;
-- mybatis.mapper-locations:配置 Mapper 对应的 XML 文件路径;
-- mybatis.type-aliases-package:配置项目中实体类包路径。
-
-注:如果配置文件使用的是 application.properties,配置内容是相同的,只是内容格式不同。
-
-#### 4)创建实体类
-
-```java
-public class UserEntity implements Serializable {
- private static final long serialVersionUID = -5980266333958177104L;
- private Integer id;
- private String userName;
- private String passWord;
- private String nickName;
- public UserEntity(String userName, String passWord, String nickName) {
- this.userName = userName;
- this.passWord = passWord;
- this.nickName = nickName;
- }
- public Integer getId() {
- return id;
- }
- public void setId(Integer id) {
- this.id = id;
- }
- public String getUserName() {
- return userName;
- }
- public void setUserName(String userName) {
- this.userName = userName;
- }
- public String getPassWord() {
- return passWord;
- }
- public void setPassWord(String passWord) {
- this.passWord = passWord;
- }
- public String getNickName() {
- return nickName;
- }
- public void setNickName(String nickName) {
- this.nickName = nickName;
- }
-}
-```
-
-#### 5)创建 XML 文件
-
-**mybatis-config.xml**(基础配置文件):
-
-```xml
-
-
-
-
-
-
-
-
-
-
-
-
-```
-
-mybatis-config.xml 主要是为常用的数据类型设置别名,用于减少类完全限定名的长度,比如:`resultType="Integer"` 完整示例代码如下:
-
-```xml
-
-```
-
-**UserMapper.xml**(业务配置文件):
-
-```xml
-
-
-
-
-
-
-
-
-
-
- id, username, password, nick_name
-
-
-
- and userName = #{userName}
-
-
-
-
-
- INSERT INTO
- t_user
- (username,password,nick_name)
- VALUES
- (#{userName}, #{passWord}, #{nickName})
-
-
- UPDATE
- t_user
- SET
- username = #{userName},
- password = #{passWord},
- nick_name = #{nickName}
- WHERE
- id = #{id}
-
-
- DELETE FROM
- t_user
- WHERE
- id =#{id}
-
-
-```
-
-以上配置我们增加了增删改查等基础方法。
-
-#### 6)增加 Mapper 文件
-
-此步骤我们需要创建一个与 XML 对应的业务 Mapper 接口,代码如下:
-
-```java
-public interface UserMapper {
- List getAll();
- UserEntity getOne(Long id);
- void insert(UserEntity user);
- void update(UserEntity user);
- void delete(Long id);
-}
-```
-
-#### 7)添加 Mapper 包扫描
-
-在启动类中添加 @MapperScan,设置 Spring Boot 启动的时候会自动加载包路径下的 Mapper。
-
-```java
-@SpringBootApplication
-@MapperScan("com.interview.mybatislearning.mapper")
-public class MyBatisLearningApplication {
- public static void main(String[] args) {
- SpringApplication.run(MyBatisLearningApplication.class, args);
- }
-}
-```
-
-#### 8)编写测试代码
-
-经过以上步骤之后,整个 MyBatis 的集成就算完成了。接下来我们写一个单元测试,验证一下。
-
-```java
-@RunWith(SpringRunner.class)
-@SpringBootTest
-public class MybatislearningApplicationTests {
- @Resource
- private UserMapper userMapper;
- @Test
- public void testInsert() {
- userMapper.insert(new UserEntity("laowang", "123456", "老王"));
- Assert.assertEquals(1, userMapper.getAll().size());
- }
-}
-```
-
-### 总结
-
-通过本文我们知道 MyBatis 是一个优秀和灵活的数据持久化框架,MyBatis 包含 Mapper 配置、Mapper 接口、Executor、SqlSession、SqlSessionFactory 等几个重要的组件,知道了 MyBatis 基本流程:MyBatis 首先加载 Mapper 配置和 SQL 映射文件,通过创建会话工厂得到 SqlSession 对象,再执行 SQL 语句并返回操作信息。我们也使用 XML 的方式,实现了 MyBatis 对数据库的基础操作。
\ No newline at end of file
diff --git "a/docs/346円241円206円346円236円266円/SpringBoot.md" "b/docs/346円241円206円346円236円266円/SpringBoot.md"
deleted file mode 100644
index b15a07c..0000000
--- "a/docs/346円241円206円346円236円266円/SpringBoot.md"
+++ /dev/null
@@ -1,513 +0,0 @@
-## SpringBoot
-
-### 为什么要用 Spring Boot?
-
-Spring Boot 来自于 Spring 大家族,是 Spring 官方团队(Pivotal 团队)提供的全新框架,它的诞生解决了 Spring 框架使用较为繁琐的问题。Spring Boot 的核心思想是约定优于配置,让开发人员不需要配置任何 XML 文件,就可以像 Maven 整合 Jar 包一样,整合并使用所有框架。
-
-**Spring Boot 特性**
-
-- 秒级构建一个项目;
-- 便捷的对外输出格式,如 REST API、WebSocket、Web 等;
-- 简洁的安全集成策略;
-- 内嵌容器运行,如 Tomcat、Jetty;
-- 强大的开发包,支持热启动;
-- 自动管理依赖;
-- 自带应用监控。
-
-**Spring Boot 2 对系统环境的要求**
-
-- Java 8+
-- Gradle 4+ or Maven 3.2+
-- Tomcat 8+
-
-### Spring Boot 使用
-
-在开始之前,我们先来创建一个Spring Boot 项目。
-
-Spring Boot 有两种快速创建的方式:Spring 官网在线网站创建和 IntelliJ IDEA 的 Spring Initializr 创建,下面分别来看。
-
-#### 创建 Spring Boot 项目
-
-##### 1)在线网站创建
-
-在浏览器输入 [https://start.spring.io](https://start.spring.io/),页面打开如下图所示:
-
-
-
-填写相应的项目信息,选择对应的 Spring Boot 和 Java 版本点击 "Generate the project"按钮下载项目压缩文件,解压后用 IDEA 打开即可。
-
-其中 Group 和 Artifact 是 Maven 项目用来确认依赖项目的标识,比如:
-
-```xml
-
- org.springframework
- spring-core
- 4.1.6.RELEASE
-
-```
-
-Group 对应的是配置文件的 groupId,相当于项目的包名;而 Artifact 对应的是配置文件的 artifactId,相当于项目名。
-
-##### 2)使用 IntelliJ IDEA 创建
-
-1 新建项目 → 选择 Spring Initialzr,如下图所示:
-
-
-
-2 点击 Next 按钮,填写对应的项目信息(和在线网站创建的字段基本相同),如下图所示:
-
-
-
-3 点击 Next 按钮,选择相应的依赖信息,如下图所示:
-
-
-
-4 点击 Next 按钮,选择项目保存的路径,点击 Finish 创建项目完成,如下图所示:
-
-
-
-#### 创建一个 Web 应用
-
-1)pom.xml 中添加 Web 模块的依赖,如下所示:
-
-```xml
-
- org.springframework.boot
- spring-boot-starter-web
-
-```
-
-2)创建后台代码
-
-```java
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-@RestController
-public class HelloController {
- @RequestMapping("/index")
- public String index(String name) {
- return "Hello, " + name;
- }
-}
-```
-
-3)启动并访问项目
-
-项目的启动类是标识了 @Spring BootApplication 的类,代码如下所示:
-
-```java
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-@SpringBootApplication
-public class SpringbootlearningApplication {
- public static void main(String[] args) {
- SpringApplication.run(SpringbootlearningApplication.class, args);
- }
-}
-```
-
-启动并访问 http://localhost:8080/index?name=laowang 效果如下:
-
-
-
-到目前为止 Spring Boot 的项目就创建并正常运行了。
-
-#### 设置配置文件
-
-Spring Boot 的配置文件,是 resources 目录下 application.properties 文件,如下图所示:
-
-
-
-可以在配置文件中设置很多关于 Spring 框架的配置,格式如下配置所示:
-
-```xml
-# 项目运行端口
-server.port=8086
-# 请求编码格式
-server.tomcat.uri-encoding=UTF-8
-```
-
-Spring Boot 的其他功能开发和 Spring 相同(Spring Boot 2 是基于 Spring Framework 5 构建的),本文就不过多的介绍了,[感兴趣的朋友可以点击这里查看](https://docs.spring.io/spring-boot/docs/current/reference/html/)
-
-### Spring Boot 发布
-
-Spring Boot 项目的发布方式有两种:
-
-- 内置容器运行
-- 外置容器(Tomcat)运行
-
-#### 内置容器运行
-
-##### 1)打包应用
-
-使用窗口命令,在 pom.xml 同级目录下:
-
-> mvn clean package -Dmaven.test.skip=true
-
-Dmaven.test.skip=true 表示不执行测试用例,也不编译测试用例类。
-
-##### 2)启动应用
-
-后台启动 Java 程序, 命令如下:
-
-> nohup java -jar springbootlearning-0.0.1-SNAPSHOT.jar &
-
-**停止程序**
-
-首先查询 Java 程序的 pid
-
-> ps -ef|grep java
-
-再停止程序
-
-> kill -9 pid
-
-操作如下图所示:
-
-
-
-**扩展内容**
-
-指定程序运行日志文件
-
-> nohup java -jar springbootlearning-0.0.1-SNAPSHOT.jar 1>>logs 2>>errlog &
-
-其中:
-
-- 1:表示普通日志
-- 2:表示错误日志
-
-#### 外置容器(Tomcat)运行
-
-##### 1)排除内置 Tomcat
-
-```xml
-
- org.springframework.boot
- spring-boot-starter-tomcat
- provided
-
-```
-
-将 scope 属性设置为 provided,表示打包不会包含此依赖。
-
-##### 2)配置启动类
-
-在项目的启动类中继承 Spring BootServletInitializer 并重写 configure() 方法:
-
-```java
-@SpringBootApplication
-public class PackageApplication extends SpringBootServletInitializer {
- @Override
- protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
- return application.sources(PackageApplication.class);
- }
- public static void main(String[] args) {
- SpringApplication.run(PackageApplication.class, args);
- }
-}
-```
-
-##### 3)打包应用
-
-使用窗口命令,在 pom.xml 同级目录下:
-
-> mvn clean package -Dmaven.test.skip=true
-
-##### 4)部署应用
-
-打包完成会在 target 目录下生成:项目名 + 版本号.war 文件,复制到 Tomcat 的 webapps 目录下,运行 Tomcat 即可。
-
-### 相关面试题
-
-#### 1.Spring Boot 2.0 支持最低的 JDK 版本是?
-
-A:JDK 6
-B:JDK 7
-C:JDK 8
-D:JDK 9
-
-答:C
-
-#### 2.Spring、Spring Boot、Spring Cloud 是什么关系?
-
-答:它们都是来自于 Spring 大家庭,Spring Boot 是在 Spring 框架的基础上开发而来,让更加方便使用 Spring;Spring Cloud 是依赖于 Spring Boot 而构建的一套微服务治理框架。
-
-#### 3.Spring Boot 项目有哪些优势?
-
-答:Spring Boot 项目优势如下:
-
-- 开发变得简单,提供了丰富的解决方案,快速集成各种解决方案提升开发效率;
-- 配置变得简单,提供了丰富的 Starters,集成主流开源产品往往只需要简单的配置即可;
-- 部署变得简单,其本身内嵌启动容器,仅仅需要一个命令即可启动项目,结合 Jenkins、Docker 自动化运维非常容易实现;
-- 监控变得简单,自带监控组件,使用 Actuator 轻松监控服务各项状态。
-
-#### 4.如何将 Spring Boot 项目打包成 war 包?
-
-答:在 pom.xml 里设置 `war` 。
-
-#### 5.在 Maven 项目中如何修改打包名称?
-
-答:在 pom.xml 文件的 build 节点中,添加 finalName 节点并设置为要的名称即可,配置如下:
-
-```xml
-
- warName
-
-```
-
-#### 6.Ant、Maven、Gradle 有什么区别?
-
-答:Ant、Maven、Gradle 是 Java 领域中主要有三大构建工具,它们的区别如下:
-
-- Ant(AnotherNeatTool)诞生于 2000 年,是由 Java 编写,采用 XML 作为构建脚本,这样就允许你在任何环境下运行构建。Ant 是 Java 领域最早的构建工具,不过因为操作复杂,慢慢的已经被淘汰了;
-- Maven 诞生于 2004 年,目的是解决程序员使用 Ant 所带来的一些问题,它的好处在于可以将项目过程规范化、自动化、高效化以及强大的可扩展性;
-- Gradle 诞生于 2009 年,是一个基于 Apache Ant 和 Apache Maven 概念的项目自动化建构工具。它使用一种基于 Groovy 的特定领域语言来声明项目设置,而不是传统的 XML。结合了前两者的优点,在此基础之上做了很多改进,它具有 Ant 的强大和灵活,又有 Maven 的生命周期管理且易于使用。
-
-Spring Boot 官方支持 Maven 和 Gradle 作为项目构建工具。Gradle 虽然有更好的理念,但是相比 Maven 来讲其行业使用率偏低,并且 Spring Boot 官方默认使用 Maven。
-
-#### 9.Spring Boot 热部署有几种方式?
-
-答:Spring Boot 热部署主要有两种方式:Spring Loaded、Spring-boot-devtools。
-
-方式 1:Spring Loaded
-
-在 pom.xml 文件中添加如下依赖:
-
-```xml
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
- org.springframework
- springloaded
- 1.2.6.RELEASE
-
-
-
- 此处为入口类
-
-
-```
-
-方式 2:Spring-boot-devtools
-
-在 pom.xml 文件中添加如下依赖:
-
-```xml
-
- org.springframework.boot
- spring-boot-devtools
- provided
- true
-
-```
-
-#### 10.Spring Boot 2.0 可以在 Tomcat 7 运行吗?为什么?
-
-答:Spring Boot 2.0 无法在 Tomcat 7 上运行。因为 Spring Boot 2.0 使用的是 Spring Framework 5,Spring Framework 5 使用的是 Servlet 3.1,而 Tomcat 7 最高支持到 Servlet 3.0,所以 Spring Boot 2.0 无法在 Tomcat 7 上运行。
-
-#### 11.如何使用 Jetty 代替 Tomcat?
-
-答:在 spring-boot-starter-web 移除现有的依赖项,添加 Jetty 依赖,配置如下:
-
-```xml
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- org.springframework.boot
- spring-boot-starter-tomcat
-
-
-
-
- org.springframework.boot
- spring-boot-starter-jetty
-
-```
-
-#### 12.Spring Boot 不支持以下哪个内嵌容器?
-
-A:Tomcat
-B:Jetty
-C:Undertow
-D:Nginx
-
-答:D
-
-题目解析:Jetty 容器支持如下:
-
-```xml
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- org.springframework.boot
- spring-boot-starter-tomcat
-
-
-
-
- org.springframework.boot
- spring-boot-starter-jetty
-
-```
-
-Undertow 容器支持如下:
-
-```xml
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- org.springframework.boot
- spring-boot-starter-tomcat
-
-
-
-
- org.springframework.boot
- spring-boot-starter-undertow
-
-```
-
-#### 13.Spring Boot 中配置文件有几种格式?
-
-答:Spring Boot 中有 .properties 和 .yml 两种配置文件格式,它们主要的区别是书写格式不同。
-
-.properties 配置文件格式如下:
-
-```xml
-app.user.name = hellojava
-```
-
-.yml 配置文件格式如下:
-
-```xml
-app:
- user:
- name: hellojava
-```
-
-#### 14.项目中有两个配置 application.properties 和 application.yml,以下说法正确的是?
-
-A:application.properties 的内容会被忽略,只会识别 application.yml 的内容。
-B:两个配置文件同时有效,有相同配置时,以 application.properties 文件为主。
-C:application.yml 的内容会被忽略,只会识别 application.properties 的内容。
-D:两个配置文件同时有效,有相同配置时,以 application.yml 文件为主。
-
-答:B
-
-#### 15.RequestMapping 和 GetMapping 有什么不同?
-
-答:RequestMapping 和 GetMapping 区别如下:
-
-- RequestMapping 可以支持 GET、POST、PUT 请求;
-- GetMapping 是一个组合注解,相当于 @RequestMapping(method = RequestMethod.GET)。
-
-#### 16.以下关于 @RestController 和 @Controller 说法正确的?
-
-A:@Controller 返回 JSON 数据
-B:@RestController 返回 JSON 数据
-C:@APIController 返回 JSON 数据
-D:以上都对
-
-答:B
-
-#### 17.Spring Cache 常用的缓存注解有哪些?
-
-答:Spring Cache 常用注解如下:
-
-- @Cacheable:用来声明方法是可缓存,将结果存储到缓存中以便后续使用相同参数调用时不需执行实际的方法,直接从缓存中取值;
-- @CachePut:使用它标注的方法在执行前,不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中;
-- CacheEvict:是用来标注在需要清除缓存元素的方法或类上的,当标记在一个类上时表示其中所有方法的执行都会触发缓存的清除操作。
-
-#### 18.Spring Boot Admin 和 Spring Boot Actuator 的关系是什么?
-
-答:Spring Boot Admin 使用了 Spring Boot Actuator 接口进行 UI 美化封装的监控工具,它以图形化的方式查询单个应用的详细状态,也可以使用 Spring Boot Admin 来监控整个集群的状态。
-
-#### 19.如何理解 Spring Boot 中的 Stater?
-
-答:Stater 可以理解为启动器,它是方便开发者快速集成其他框架到 Spring 中的一种技术。比如,spring-boot-starter-data-jpa 就是把 JPA 快速集成到 Spring 中。
-
-#### 20.常见的 starter 有哪些?
-
-答:常见的 starter 如下:
-
-- spring-boot-starter-web:Web 开发支持
-- spring-boot-starter-data-jpa:JPA 操作数据库支持
-- spring-boot-starter-data-redis:Redis 操作支持
-- spring-boot-starter-data-solr:Solr 权限支持
-- mybatis-spring-boot-starter:MyBatis 框架支持
-
-#### 21.Spring Boot Starter JDBC 和 Spring JDBC 有什么关系?
-
-答:spring-boot-starter-jdbc 是 Spring Boot 针对 JDBC 的使用提供了对应的 Starter 包,在 Spring JDBC 上做了进一步的封装,方便在 Spring Boot 生态中更好的使用 JDBC。
-
-#### 22.Spring Boot 有哪几种读取配置的方式?
-
-答:Spring Boot 可以通过 @Value、@Environment、@ConfigurationProperties 这三种方式来读取。
-
-例如,配置文件内容如下:
-
-```xml
-app.name=中文
-```
-
-**1 Value 方式**
-
-```java
-@Value("${app.name}")
-private String appName;
-```
-
-**2 Environment 方式**
-
-```java
-public class HelloController {
- @Autowired
- private Environment environment;
- @RequestMapping("/index")
- public String index(String hiName) {
- // 读取配置文件
- String appName = environment.getProperty("app.name");
- return "Hello, " + hiName + " |@" + appName;
- }
-}
-```
-
-**3 ConfigurationProperties 方式**
-
-```java
-@ConfigurationProperties(prefix = "app")
-public class HelloController {
- // 读取配置文件,必须有 setter 方法
- private String name;
- public void setName(String name) {
- this.name = name;
- }
- @RequestMapping("/index")
- public String index(String hiName) {
- System.out.println("appname:" + name);
- return "Hello, " + hiName + " |@" + appName;
- }
-}
-```
-
-#### 23.使用 @Value 读取中文乱码是什么原因?如何处理?
-
-答:这是因为配置文件的编码格式导致的,需要把编码格式设置为 UTF-8,如下图所示:
-
-
-
-设置完成之后,重新启动 IDEA 就可以正常显示中文了。
-
-### 总结
-
-通过本文我们学习了 Spring Boot 的两种创建方式:在线网站创建和 IntelliJ IDEA 方式创建。知道了 Spring Boot 发布的两种方式:内置容器和外置 Tomcat,知道了 Spring Boot 项目特性,以及配置文件 .properties 和 .yml 的差异,掌握了读取配置文件的三种方式:@Value、@Environment、@ConfigurationProperties。
\ No newline at end of file
diff --git "a/docs/347円256円227円346円263円225円/346円216円222円345円272円217円347円256円227円346円263円225円346円261円207円346円200円273円.md" "b/docs/347円256円227円346円263円225円/346円216円222円345円272円217円347円256円227円346円263円225円346円261円207円346円200円273円.md"
deleted file mode 100644
index 5afc02a..0000000
--- "a/docs/347円256円227円346円263円225円/346円216円222円345円272円217円347円256円227円346円263円225円346円261円207円346円200円273円.md"
+++ /dev/null
@@ -1,37 +0,0 @@
-# 排序算法比较
-| 算法 | 稳定性 | 时间复杂度 | 空间复杂度 | 备注 |
-| :--------------: | :----: | :--------------------------: | :--------: | :----------------------: |
-| 选择排序 | ×ばつ | N2 | 1 | |
-| 冒泡排序 | √ | N2 | 1 | |
-| 插入排序 | √ | N ~ N2 | 1 | 时间复杂度和初始顺序有关 |
-| 希尔排序 | ×ばつ | N 的若干倍乘于递增序列的长度 | 1 | 改进版插入排序 |
-| 快速排序 | ×ばつ | NlogN | logN | |
-| 三向切分快速排序 | ×ばつ | N ~ NlogN | logN | 适用于有大量重复主键 |
-| 归并排序 | √ | NlogN | N | |
-| 堆排序 | ×ばつ | NlogN | 1 | 无法利用局部性原理 |
-
-**稳定排序算法:**
-
-+ 冒泡排序
-
-3.gif
-
-+ 插入排序
-
-6.gif
-
-+ 归并排序
-
-**不稳定排序算法**
-
-+ 选择排序
-
-7.gif
-
-+ 希尔排序
-+ 快速排序
-
-+ 堆排序
-+ 三向切分快速排序
-
-
\ No newline at end of file
diff --git "a/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/README.md" "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/README.md"
new file mode 100644
index 0000000..eac34dc
--- /dev/null
+++ "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/README.md"
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git "a/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/_sidebar.md" "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/_sidebar.md"
new file mode 100644
index 0000000..948a9e9
--- /dev/null
+++ "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/_sidebar.md"
@@ -0,0 +1,14 @@
+* **👉 算法与数据结构** [↩](/README)
+ * [算法概述](docs/算法与数据结构/算法概述.md)
+ * [数组](docs/算法与数据结构/数组.md)
+ * [链表](docs/算法与数据结构/链表.md)
+ * [队列](docs/算法与数据结构/队列.md)
+ * [栈](docs/算法与数据结构/栈.md)
+ * [散列表](docs/算法与数据结构/散列表.md)
+ * [树](docs/算法与数据结构/树.md)
+ * [图](docs/算法与数据结构/图.md)
+ * [排序算法](docs/算法与数据结构/排序算法.md)
+ * [动态规划](docs/算法与数据结构/动态规划.md)
+ * [递归算法](docs/算法与数据结构/递归算法.md)
+ * [贪心算法](docs/算法与数据结构/贪心算法.md)
+ * [剑指offer](docs/算法与数据结构/剑指offer题解.md)
diff --git "a/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/345円211円221円346円214円207円offer351円242円230円350円247円243円.md" "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/345円211円221円346円214円207円offer351円242円230円350円247円243円.md"
new file mode 100644
index 0000000..10ab10a
--- /dev/null
+++ "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/345円211円221円346円214円207円offer351円242円230円350円247円243円.md"
@@ -0,0 +1,9 @@
+# 👉 剑指offer题解
+
+- [数组中重复的数字](http://mp.weixin.qq.com/s?__biz=MzUzMzM2NTQ0Ng==&mid=100000753&idx=1&sn=cccd8da3a96253839533b7cf41f21edc&chksm=7aa454584dd3dd4ed4644ab3a69372121f1fd8ca42fdb3bd5ee92e6285794e10f786a8c5694e#rd)
+- [二维数组中的查找](http://mp.weixin.qq.com/s?__biz=MzUzMzM2NTQ0Ng==&mid=100000773&idx=1&sn=8dc1150518955041da93dd76571f2837&chksm=7aa453ac4dd3daba8b8ff766e54368906cb7028df912fb51833b99d4ad1572129c73cbc9d416#rd)
+- [替换空格](http://mp.weixin.qq.com/s?__biz=MzUzMzM2NTQ0Ng==&mid=100000801&idx=1&sn=dcbca06ed25ca77b76de426d3c7910b3&chksm=7aa453884dd3da9e80d22652af33afb411e526fa1fc47538b41e10fa8b7b85a9a048ebe45726#rd)
+- [用两个栈实现队列](http://mp.weixin.qq.com/s?__biz=MzUzMzM2NTQ0Ng==&mid=100000846&idx=1&sn=a1e9f3c2ccc75717018b6797b44b19b7&chksm=7aa453e74dd3daf10f930f28065cb62c30b5fa2dffbd38761ae7683690305d801ec00da5ddd1#rd)
+- [删除链表节点](http://mp.weixin.qq.com/s?__biz=MzUzMzM2NTQ0Ng==&mid=100000881&idx=1&sn=30ff81c0cec276e8b9fd2c55e6a05468&chksm=7aa453d84dd3dace71e61de78c4404646f7877da39718568396677e25d34778a41044142b300#rd)
+- [调整数组顺序使奇数位于偶数前面](http://mp.weixin.qq.com/s?__biz=MzUzMzM2NTQ0Ng==&mid=100000881&idx=1&sn=30ff81c0cec276e8b9fd2c55e6a05468&chksm=7aa453d84dd3dace71e61de78c4404646f7877da39718568396677e25d34778a41044142b300#rd)
+- ···
diff --git "a/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/345円212円250円346円200円201円350円247円204円345円210円222円.md" "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/345円212円250円346円200円201円350円247円204円345円210円222円.md"
new file mode 100644
index 0000000..9789e18
--- /dev/null
+++ "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/345円212円250円346円200円201円350円247円204円345円210円222円.md"
@@ -0,0 +1,3 @@
+# 👉 动态规划:
+
+todo~
\ No newline at end of file
diff --git "a/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/345円233円276円.md" "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/345円233円276円.md"
new file mode 100644
index 0000000..ab27f35
--- /dev/null
+++ "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/345円233円276円.md"
@@ -0,0 +1,3 @@
+# 👉 图:
+
+todo~
\ No newline at end of file
diff --git "a/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円216円222円345円272円217円347円256円227円346円263円225円.md" "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円216円222円345円272円217円347円256円227円346円263円225円.md"
new file mode 100644
index 0000000..9ad34a1
--- /dev/null
+++ "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円216円222円345円272円217円347円256円227円346円263円225円.md"
@@ -0,0 +1,3 @@
+# 👉 排序算法:
+
+todo~
\ No newline at end of file
diff --git "a/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円243円345円210円227円350円241円250円.md" "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円243円345円210円227円350円241円250円.md"
new file mode 100644
index 0000000..c259b8c
--- /dev/null
+++ "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円243円345円210円227円350円241円250円.md"
@@ -0,0 +1,3 @@
+# 👉 哈希表:
+
+todo~
\ No newline at end of file
diff --git "a/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円344円270円223円346円240円217円/01 344円270円272円344円273円200円344円271円210円350円246円201円345円255円246円344円271円240円346円225円260円346円215円256円347円273円223円346円236円204円345円222円214円347円256円227円346円263円225円357円274円237円.md" "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円344円270円223円346円240円217円/01 344円270円272円344円273円200円344円271円210円350円246円201円345円255円246円344円271円240円346円225円260円346円215円256円347円273円223円346円236円204円345円222円214円347円256円227円346円263円225円357円274円237円.md"
new file mode 100644
index 0000000..54c5c4f
--- /dev/null
+++ "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円344円270円223円346円240円217円/01 344円270円272円344円273円200円344円271円210円350円246円201円345円255円246円344円271円240円346円225円260円346円215円256円347円273円223円346円236円204円345円222円214円347円256円227円346円263円225円357円274円237円.md"
@@ -0,0 +1,52 @@
+# 01 | 为什么要学习数据结构和算法?
+
+img
+
+## 前言
+
+很多人认为数据结构和算法、计算机网络、操作系统以及计算机组成原理,实际工作中用的非常少,是脱离实际工作的,也就面试的时候会有被问到的机会吧~
+
+这样的声音很多很多,就像我们上学的时候,总会人经常说买菜也不需要勾股定理啊,来抵制应试教育~
+
+其实这样的说法很真实,但是看的还是不够本质吧~ 买菜的时候是不需要勾股定理来计算一下,但是会决定你在哪里买菜~ 仔细品!
+
+同样的道理,为什么大厂面试的时候总会被问到数据结构和算法、计算机网络、操作系统等等基础知识呢?
+
+有人会说,我在实际工作中很少或者几乎都用不到这数据结构那算法的,不照样把代码写的很"好"?但是事实真的是这样?
+
+
+
+问题来了:到底为什么建议要去学习数据结构与算法呢?
+
+
+
+## 大厂面试必备
+
+目前很多的大厂面试,算法与数据结构的考察几乎是必备的。为什么这些大厂都偏向于这方面的考察呢?
+
++ 如果你是参加校招,从公司考察角度来看,你没有什么实际的项目经验,只能从基础知识考察,比如计算机网络、操作系统、数据库原理、算法与数据结构等等。
+
++ 如果你是参加社招,公司考察算法与数据结构,其其目的更看重你的长期潜力,而非短期能力。
+
+## 提升个人竞争力
+
+作为业务开发,我们会用到各种框架、中间件和底层系统,比如 Spring、RPC 框架、消息中间件、Redis 等等。在这些基础框架中,一般都揉和了很多基础数据结构和算法的设计思想。
+
+比如,我们常用的 Key-Value 数据库 Redis 中,里面的有序集合是用什么数据结构来实现的呢?为什么要用跳表来实现呢?为什么不用二叉树呢?
+
+如果你能弄明白这些底层原理,你就能更好地使用它们。即便出现问题,也很容易就能定位。因此,掌握数据结构和算法,不管对于阅读框架源码,还是理解其背后的设计思想,都是非常有用的。
+
+在平时的工作中,数据结构和算法的应用到处可见。我来举一个你非常熟悉的例子:如何实时地统计业务接口的 99% 响应时间?
+
+你可能最先想到,每次查询时,从小到大排序所有的响应时间,如果总共有 1200 个数据,那第 1188 个数据就是 99% 的响应时间。很显然,每次用这个方法查询的话都要排序,效率是非常低的。但是,如果你知道"堆"这个数据结构,用两个堆可以非常高效地解决这个问题。
+
+
+
+## 总结
+
+最重要的是掌握了数据结构与算法,你看待问题的深度,解决问题的角度就会完全不一样
+
+因为这样的你,就像是站在巨人的肩膀上,拿着生存利器行走世界。数据结构与算法,会为你的编程之路,甚至人生之路打开一扇通往新世界的大门。
+
+
+
diff --git "a/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円344円270円223円346円240円217円/02 345円246円202円344円275円225円346円212円223円344円275円217円351円207円215円347円202円271円357円274円214円347円263円273円347円273円237円351円253円230円346円225円210円345円234円260円345円255円246円344円271円240円346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円357円274円237円.md" "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円344円270円223円346円240円217円/02 345円246円202円344円275円225円346円212円223円344円275円217円351円207円215円347円202円271円357円274円214円347円263円273円347円273円237円351円253円230円346円225円210円345円234円260円345円255円246円344円271円240円346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円357円274円237円.md"
new file mode 100644
index 0000000..a88af0f
--- /dev/null
+++ "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円344円270円223円346円240円217円/02 345円246円202円344円275円225円346円212円223円344円275円217円351円207円215円347円202円271円357円274円214円347円263円273円347円273円237円351円253円230円346円225円210円345円234円260円345円255円246円344円271円240円346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円357円274円237円.md"
@@ -0,0 +1,84 @@
+# 02 | 如何抓住重点,系统高效地学习数据结构与算法?
+
+img
+
+## 理解数据结构与算法
+
+1. 数据结构就是指一组数据的存储结构。算法就是操作数据的一组方法。
+2. 数据结构和算法是相辅相成的,数据结构是为算法服务的,算法要作用在特定的数据结构之上。
+3. 数据结构是静态的吗,它只是组织数据的一种方式。如果不在它的基础上操作、构建算法、孤立存在的数据结构就是没用的。
+
+img
+
+## 学习重点
+
+### 复杂度分析
+
+数据结构和算法解决的是如何更省、更快地存储和处理数据的问题,因此,我们就需要一个考量效率和资源消耗的方法,这就是复杂度分析方法。
+
+> 思考:在实际开发中,如何选取合适的算法与数据结构,都取决于复杂度分析。
+
+### 20个最常用的、最基础的数据结构与算法
+
+#### 数据结构
+
+1. 数组
+2. 链表
+3. 栈
+4. 队列
+5. 散列表
+6. 二叉树
+7. 堆
+8. 跳表
+9. 图
+10. Trie树
+
+#### 算法
+
+1. 递归
+2. 排序
+3. 二分查找
+4. 搜索
+5. 哈希算法
+6. 贪心算法
+7. 分治算法
+8. 回溯算法
+9. 动态规划
+10. 字符串匹配算法
+
+> 思考:在学习数据结构和算法的过程中,你也要注意,不要只是死记硬背,不要为了学习而学习,而是要学习它的"来历""自身的特点""适合解决的问题"以及"实际的应用场景"。对于每一种数据结构或算法,我都会从这几个方面进行详细讲解。只要你掌握了我每节课里讲的内容,就能在开发中灵活应用。
+
+## 如何学习才能事半功倍
+
+### 1. 边学边练,适度刷题
+
+"边学边练"这一招非常有用。建议你每周花 1〜2 个小时的时间,集中把这周的三节内容涉及的数据结构和算法,全都自己写出来,用代码实现一遍。这样一定会比单纯地看或者听的效果要好很多!
+
+### 2. 多问、多思考、多互动
+
+学习最好的方法是,找到几个人一起学习,一块儿讨论切磋,有问题及时寻求老师答疑。
+
+> 思考:比如:创建一个交流群,多交流,多思考。
+
+### 3. 打怪升级学习法
+
+学习的过程中,我们碰到最大的问题就是,坚持不下来。
+
+> 思考:每次学习一个知识点之后,要学会总结与思考,记下自己的学习心得,针对性的设立阶段目标 。
+
+### 4. 知识需要沉淀,不要想试图一下子掌握所有
+
+在学习的过程中,一定会碰到"拦路虎"。如果哪个知识点没有怎么学懂,不要着急,这是正常的。因为,想听一遍、看一遍就把所有知识掌握,这肯定是不可能的。
+
+学习知识的过程是反复迭代、不断沉淀的过程。如果碰到"拦路虎",你可以尽情地在留言区问我,也可以先沉淀一下,过几天再重新学一遍。所谓,书读百遍其义自见,我觉得是很有道理的!
+
+> 思考:说的非常对,学习就是一个反反复复的过程。
+
+
+
+
+
+
+
+
+
diff --git "a/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円344円270円223円346円240円217円/03 345円244円215円346円235円202円345円272円246円345円210円206円346円236円220円357円274円210円344円270円212円357円274円211円357円274円232円345円246円202円344円275円225円345円210円206円346円236円220円343円200円201円347円273円237円350円256円241円347円256円227円346円263円225円347円232円204円346円211円247円350円241円214円346円225円210円347円216円207円345円222円214円350円265円204円346円272円220円346円266円210円350円200円227円357円274円237円.md" "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円344円270円223円346円240円217円/03 345円244円215円346円235円202円345円272円246円345円210円206円346円236円220円357円274円210円344円270円212円357円274円211円357円274円232円345円246円202円344円275円225円345円210円206円346円236円220円343円200円201円347円273円237円350円256円241円347円256円227円346円263円225円347円232円204円346円211円247円350円241円214円346円225円210円347円216円207円345円222円214円350円265円204円346円272円220円346円266円210円350円200円227円357円274円237円.md"
new file mode 100644
index 0000000..165ee55
--- /dev/null
+++ "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円344円270円223円346円240円217円/03 345円244円215円346円235円202円345円272円246円345円210円206円346円236円220円357円274円210円344円270円212円357円274円211円357円274円232円345円246円202円344円275円225円345円210円206円346円236円220円343円200円201円347円273円237円350円256円241円347円256円227円346円263円225円347円232円204円346円211円247円350円241円214円346円225円210円347円216円207円345円222円214円350円265円204円346円272円220円346円266円210円350円200円227円357円274円237円.md"
@@ -0,0 +1,2 @@
+# 03 | 复杂度分析(上):如何分析、统计算法的执行效率和资源消耗?
+
diff --git "a/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円344円270円223円346円240円217円/04 345円244円215円346円235円202円345円272円246円345円210円206円346円236円220円357円274円210円344円270円213円357円274円211円357円274円232円346円265円205円346円236円220円346円234円200円345円245円275円343円200円201円346円234円200円345円235円217円343円200円201円345円271円263円345円235円207円343円200円201円345円235円207円346円221円212円346円227円266円351円227円264円345円244円215円346円235円202円345円272円246円.md" "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円344円270円223円346円240円217円/04 345円244円215円346円235円202円345円272円246円345円210円206円346円236円220円357円274円210円344円270円213円357円274円211円357円274円232円346円265円205円346円236円220円346円234円200円345円245円275円343円200円201円346円234円200円345円235円217円343円200円201円345円271円263円345円235円207円343円200円201円345円235円207円346円221円212円346円227円266円351円227円264円345円244円215円346円235円202円345円272円246円.md"
new file mode 100644
index 0000000..fe2817e
--- /dev/null
+++ "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円344円270円223円346円240円217円/04 345円244円215円346円235円202円345円272円246円345円210円206円346円236円220円357円274円210円344円270円213円357円274円211円357円274円232円346円265円205円346円236円220円346円234円200円345円245円275円343円200円201円346円234円200円345円235円217円343円200円201円345円271円263円345円235円207円343円200円201円345円235円207円346円221円212円346円227円266円351円227円264円345円244円215円346円235円202円345円272円246円.md"
@@ -0,0 +1,2 @@
+# 04 | 复杂度分析(下):浅析最好、最坏、平均、均摊时间复杂度
+
diff --git "a/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円344円270円223円346円240円217円/05 346円225円260円347円273円204円357円274円232円344円270円272円344円273円200円344円271円210円345円276円210円345円244円232円347円274円226円347円250円213円350円257円255円350円250円200円344円270円255円346円225円260円347円273円204円351円203円275円344円273円2160円345円274円200円345円247円213円347円274円226円345円217円267円357円274円237円.md" "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円344円270円223円346円240円217円/05 346円225円260円347円273円204円357円274円232円344円270円272円344円273円200円344円271円210円345円276円210円345円244円232円347円274円226円347円250円213円350円257円255円350円250円200円344円270円255円346円225円260円347円273円204円351円203円275円344円273円2160円345円274円200円345円247円213円347円274円226円345円217円267円357円274円237円.md"
new file mode 100644
index 0000000..de46008
--- /dev/null
+++ "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円344円270円223円346円240円217円/05 346円225円260円347円273円204円357円274円232円344円270円272円344円273円200円344円271円210円345円276円210円345円244円232円347円274円226円347円250円213円350円257円255円350円250円200円344円270円255円346円225円260円347円273円204円351円203円275円344円273円2160円345円274円200円345円247円213円347円274円226円345円217円267円357円274円237円.md"
@@ -0,0 +1,30 @@
+# 05 | 数组:为什么很多编程语言中数组都从0开始编号?
+
+## 数组的定义
+
+> 数组是一种线性表数据结构,它是一种线性表数据结构。数组使用一组连续的内存空间,存储一组具有**相同类型**的数据。
+
+## 什么是线性表?
+
+线性表是数据按照线性排列的结构,每一个线性表上的数据最多只有前后两个方向。像数组、链表、队列、栈等都是线性表结构。
+
+如下图所示:
+
+img
+
+## 什么是非线性表?
+
+非线性表的概念与线性表相对立,像图、二叉树、堆等。不像线性表一样,在非线性表中的数据并不是简单的前后关系。
+
+如下图所示:
+
+img
+
+## 数组的特征:连续的内存空间、相同的数据类型
+
+连续的内存空间,使得数据的**随机访问**非常高效。但是也使得在数组中删除、插入一个数据变得非常低效。
+
+这是因为数据的连续性带来的限制,在删除或插入一个数据之后,数组需要保持其连续性就必须会涉及到大量的数据搬移操作。
+
+## 数组是如何实现随机访问?
+
diff --git "a/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円344円270円223円346円240円217円/06 351円223円276円350円241円250円357円274円210円344円270円212円357円274円211円357円274円232円345円246円202円344円275円225円345円256円236円347円216円260円LRU347円274円223円345円255円230円346円267円230円346円261円260円347円256円227円346円263円225円.md" "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円344円270円223円346円240円217円/06 351円223円276円350円241円250円357円274円210円344円270円212円357円274円211円357円274円232円345円246円202円344円275円225円345円256円236円347円216円260円LRU347円274円223円345円255円230円346円267円230円346円261円260円347円256円227円346円263円225円.md"
new file mode 100644
index 0000000..1e51137
--- /dev/null
+++ "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円344円270円223円346円240円217円/06 351円223円276円350円241円250円357円274円210円344円270円212円357円274円211円357円274円232円345円246円202円344円275円225円345円256円236円347円216円260円LRU347円274円223円345円255円230円346円267円230円346円261円260円347円256円227円346円263円225円.md"
@@ -0,0 +1,2 @@
+# 06 | 链表(上):如何实现LRU缓存淘汰算法?
+
diff --git "a/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円344円270円223円346円240円217円/README.md" "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円344円270円223円346円240円217円/README.md"
new file mode 100644
index 0000000..eac34dc
--- /dev/null
+++ "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円344円270円223円346円240円217円/README.md"
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git "a/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円344円270円223円346円240円217円/_sidebar.md" "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円344円270円223円346円240円217円/_sidebar.md"
new file mode 100644
index 0000000..0d20f5a
--- /dev/null
+++ "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円344円270円223円346円240円217円/_sidebar.md"
@@ -0,0 +1,6 @@
+* **👉 数据结构与算法专栏** [↩](/README)
+ * **01.为什么要学习数据结构和算法?.md**
+ * **02.如何抓住重点,系统高效地学习数据结构与算法?.md**
+ * **03.复杂度分析(上):如何分析、统计算法的执行效率和资源消耗?.md**
+ * **04.复杂度分析(下):浅析最好、最坏、平均、均摊时间复杂度.md**
+
diff --git "a/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円351円235円242円350円257円225円345円256円235円345円205円270円344円270円223円346円240円217円/01 346円240円210円357円274円232円344円273円216円347円256円200円345円215円225円346円240円210円345円210円260円345円215円225円350円260円203円346円240円210円357円274円214円350円247円243円345円206円263円347円273円217円345円205円270円346円240円210円351円227円256円351円242円230円.md" "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円351円235円242円350円257円225円345円256円235円345円205円270円344円270円223円346円240円217円/01 346円240円210円357円274円232円344円273円216円347円256円200円345円215円225円346円240円210円345円210円260円345円215円225円350円260円203円346円240円210円357円274円214円350円247円243円345円206円263円347円273円217円345円205円270円346円240円210円351円227円256円351円242円230円.md"
new file mode 100644
index 0000000..d5d3484
--- /dev/null
+++ "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円351円235円242円350円257円225円345円256円235円345円205円270円344円270円223円346円240円217円/01 346円240円210円357円274円232円344円273円216円347円256円200円345円215円225円346円240円210円345円210円260円345円215円225円350円260円203円346円240円210円357円274円214円350円247円243円345円206円263円347円273円217円345円205円270円346円240円210円351円227円256円351円242円230円.md"
@@ -0,0 +1,256 @@
+# 01 | 栈:从简单栈到单调栈,解决经典栈问题
+
+## 前言
+
+栈这种数据结构,使用是非常广泛的,比如我们Java中函数的调用、浏览器中的前进与后退功能、操作系统中从用户态到内核态寄存器的保存、网络消息的处理等都会用到栈。
+
+## 如何理解栈?
+
+如何去理解栈呢?
+
+
+
+子弹上膛的这个过程,后进的子弹最先射出,最上面的子弹就相当于栈顶。一句话可以概况栈的特性:**先进后出**,俗称"**出多了吐**"。
+
+
+
+在Java中如何使用栈,代码如下:
+
+```java
+// 创建一个栈
+Stack stack = new Stack();
+// 想栈中放入4个元素
+stack.push(1);
+stack.push(2);
+stack.push(3);
+stack.push(4);
+
+// 查看此时栈顶的元素
+System.out.println(stack.peek()); // 输出结果:4
+// 将栈顶元素出站
+System.out.println(stack.pop()); // 输出结果:4
+// 再次查看此时栈顶元素
+System.out.println(stack.peek()); // 输出结果:3
+// 再次将栈顶元素出站
+System.out.println(stack.pop()); // 输出结果:3
+```
+
+## 如何实现栈?
+
+实现栈的方式既可以选择数组,也可以链表。那么选择数组来实现的栈就叫做顺序栈,选择链表来实现的栈就叫做链式栈。
+
+接下来,我们用Java代码分别实现**顺序栈**、**链式栈**。
+
+### 顺序栈
+
+```java
+/**
+ * @author 微信公众号:码上Java
+ * @Description: 用数组实现站结构
+ */
+
+public class MyArratStack {
+
+ private Object[] data = null; // 泛型数组
+ private int maxSize = 0; //栈容量
+ private int top = -1; //栈顶指针
+
+ /**
+ * 初始化构造方法
+ * @param initialSize 初始栈的容量
+ */
+ MyArratStack(int initialSize) {
+ if (initialSize>= 0) {
+ this.maxSize = initialSize;
+ data = new Object[initialSize];
+ top = -1;
+ } else {
+ throw new RuntimeException("初始化大小不能小于0: " + initialSize);
+ }
+ }
+
+ /**
+ * 初始化构造方法 默认栈容量为10
+ */
+ public MyArratStack() {
+ this(10);
+ }
+
+ /**
+ * 入栈操作
+ * @param e
+ * @return
+ */
+ public boolean push(E e) {
+ //首先判断一下栈是否已经满了
+ if (top == maxSize - 1) {
+ //可优化 TODO 扩容操作
+ throw new RuntimeException("栈已满,元素无法入栈");
+ } else {
+ data[top] = e;
+ top++;
+ return true;
+ }
+ }
+
+ /**
+ * 出栈操作
+ * @return
+ */
+ public E pop() {
+ //首先查看一下栈是否为空
+ if (top == -1) {
+ throw new RuntimeException("栈为空 ");
+ } else {
+ //将栈顶元素返回后维护一下栈顶指针
+ return (E) data[top--];
+ }
+ }
+
+ /**
+ * 查看栈顶元素
+ * @return
+ */
+ public E peek() {
+ if (top == -1) {
+ throw new RuntimeException("栈为空");
+ } else {
+ // 查看栈顶元素并不移除所以说不需要维护栈顶指针
+ return (E) data[top];
+ }
+ }
+
+ /**
+ * 判断栈是否为空
+ * @return
+ */
+ public boolean isEmpty() {
+ return maxSize == 0;
+ }
+
+}
+```
+
+### 链式栈
+
+```java
+public class StackBasedOnLinkedList {
+ private Node top = null;
+
+ public void push(int value) {
+ Node newNode = new Node(value, null);
+ // 判断是否栈空
+ if (top == null) {
+ top = newNode;
+ } else {
+ newNode.next = top;
+ top = newNode;
+ }
+ }
+
+ /**
+ * 我用-1表示栈中没有数据。
+ */
+ public int pop() {
+ if (top == null) return -1;
+ int value = top.data;
+ top = top.next;
+ return value;
+ }
+
+ public void printAll() {
+ Node p = top;
+ while (p != null) {
+ System.out.print(p.data + " ");
+ p = p.next;
+ }
+ System.out.println();
+ }
+
+ private static class Node {
+ private int data;
+ private Node next;
+
+ public Node(int data, Node next) {
+ this.data = data;
+ this.next = next;
+ }
+
+ public int getData() {
+ return data;
+ }
+ }
+}
+```
+
+### 小结
+
+不管是顺序栈还是链式栈,因为栈本身的局限性,也就是一个口子,只能涉及到个别元素的操作,所以说时间复杂度都是O(1)。
+
+还有一点你发现没有,顺序栈是存在一个扩容的问题,因为数组不像链表,链表只要你内存够大,是可以支持无限扩展的。但是数组就不一样了,因为数组本身大小是已经初始化好了的,如果数组满了的话,你是无法继续往栈中添加元素的。如果想要添加元素,就必须解决这个扩容的问题。
+
+那么如何解决呢?无非就是创建一个更大点的新数组,将就旧数组的元素通过遍历放到新数组里面。此时的时间复杂度会变成O(n)了。
+
+那么我们可以总结下,最好的情况是栈不满,时间复杂度是O(1)。最坏的情况是栈满了,时间复杂度是O(1)。
+
+## 实践演练
+
+### 判断字符串括号是否合法
+
+题目描述:给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
+
+有效字符串需满足:
+
++ 左括号必须用相同类型的右括号闭合。
++ 左括号必须以正确的顺序闭合。
+
+**解题思路:**
+
+1. 从左到右遍历字符串,当遇到左括号的时候,将其压入栈中,当遇到有括号的时候,将栈顶的元素与其匹配,如果能够匹配说明合法字符,继续以上操作。如果遇到不能匹配或者栈为空,则说明非法字符。
+2. 如果遍历结束,栈为空,则说明字符串为合法字符。否则,说明有未能匹配的左括号,则为非法字符。
+
+**代码实现:**
+
+```java
+ /**
+ * 有效括号校验
+ *
+ * @param str
+ * @return
+ */
+ public static boolean isValid(String str) {
+ // 如果字符串为空,直接返回false
+ if (str.length() == 0 || str == null) {
+ return false;
+ }
+ // 如果字符串的长度为奇数,直接返回false
+ if (str.length() % 2 != 0) {
+ return false;
+ }
+ // 创建一个栈
+ Stack stack = new Stack();
+
+ for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c == '(') { + stack.push(')'); + } else if (c == '{') { + stack.push('}'); + } else if (c == '[') { + stack.push(']'); + } else if (stack.isEmpty() || c != stack.pop()) { + return false; + } + } + return stack.isEmpty(); + } +``` + + + + + + + + + diff --git "a/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円351円235円242円350円257円225円345円256円235円345円205円270円344円270円223円346円240円217円/_sidebar.md" "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円351円235円242円350円257円225円345円256円235円345円205円270円344円270円223円346円240円217円/_sidebar.md" new file mode 100644 index 0000000..89a9d32 --- /dev/null +++ "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円351円235円242円350円257円225円345円256円235円345円205円270円344円270円223円346円240円217円/_sidebar.md" @@ -0,0 +1,12 @@ +* **👉 数据结构与算法面试宝典专栏** [↩](/README) + * **01.栈:从简单栈到单调栈,解决经典栈问题.md** + * **链表** + * **队列** + * **栈** + * **散列表** + * **树** + * **图** + * **排序算法** + * **动态规划** + * **递归算法** + * **贪心算法** diff --git "a/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円351円235円242円350円257円225円345円256円235円345円205円270円344円270円223円346円240円217円/346円240円221円.md" "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円351円235円242円350円257円225円345円256円235円345円205円270円344円270円223円346円240円217円/346円240円221円.md" new file mode 100644 index 0000000..3253d16 --- /dev/null +++ "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円351円235円242円350円257円225円345円256円235円345円205円270円344円270円223円346円240円217円/346円240円221円.md" @@ -0,0 +1 @@ +~ todo \ No newline at end of file diff --git "a/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円347円273円204円.md" "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円347円273円204円.md" new file mode 100644 index 0000000..3d77a40 --- /dev/null +++ "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円225円260円347円273円204円.md" @@ -0,0 +1,169 @@ +# 👉 数组: 一种非常基础且重要的数据结构 + + + +## 前言 + +数组是一种非常基础且重要的数据结构,很多复杂的数据结构都是基于数组实现的。深入理解数据的存储原理和特点,有利于我们在实际开发工作中,充分发挥数据的优势。 + +## 数据是什么 + + 数组的定义:数组(Array)是一种**线性表**数据结构。它用一组**连续的内存空间**,存储一组具有**相同类型的数据**。 + +在上面的定义中加黑的描述,我们可以发现数组的几个特点,分别是:线性表、连续的内存空间、相同类型的数据。如下图所示: + + + +数组因具有连续的内存空间的特点,让数据拥有非常高效率的"随机访问",但也是因为要保持这个连续的内存空间,导致数组在删除或插入操作的时非常低效。因为数组为了保持连续性,必然会涉及大量数据的搬移,这个是非常消耗时间的。 + +> 思考:这里你可能会有疑问:什么是连续的内存空间?
+
+首先,我们来说说内存,内存是由一个个连续的内存单元组成的,每一个内存单元都有自己的地址。在这些内存单元中,有些被其他数据占用了,有些是空闲的。
+
+然而数据中的每个元素,都存储在小小的内存单元中,并且元素之间紧密排列,既不能打乱元素的存储顺序,也不能跳过某个存储单元进行存储。
+
+## 数组的随机访问
+
+数组的随机访问是有个寻址公式的,上问中我们提到过数组是用一组**连续的内存空间**存储数据元素的,然而每个内存单元都有自己的地址(在计算机里面就是通过这个地址访问数据的),又加上每个内存单元的大小都是一样的,这样就很容易得到一个公式了,如下所示:
+
+```
+a[i]_address=base_address+i*data_type_size
+```
+
+我们来简单解释一下上述公式,其中data_type_size表示数组中每个元素的大小、base_address表示内存块的首地址、i 表示数组下标。
+
+## 数组的基本操作
+
+在开始之前我们先创建一个数组类,来模拟数组操作时候的相关操作。代码如下:
+
+```java
+public class MyArray {
+
+ private int[] array;
+ // 数组大小
+ private int size;
+
+ public MyArray(int capacity) {
+ this.size = 0;
+ this.array = new int[capacity];
+ }
+
+}
+```
+
+### 1. 读取元素
+
+我们知道数组在内存中是连续存储的,所以根据上文的寻址公式可以知道,我们可以根据数组下标 i 快速定位到对应的元素。
+
+简单举例,代码如下:
+
+```java
+int[] array={1,2,3,4,5,6};
+System.out.println(array[1]); // 输出的是2 因为数组的下标是从0开始的。
+```
+
+### 2. 更新元素
+
+我们可以根据数组下标快速查找到对应元素。那么同样道理,我们可以根据数组下标 i 快速更新元素,这中间涉及两个过程,首先就是找到数组下标 i 对应的数据元素A,然后将新的数据元素B赋值给A即完成更新。
+
+简单举例,代码如下:
+
+```java
+int[] array={1,2,3,4,5,6};
+System.out.println(array[1]); // 输出的是2
+
+//更新数组下标为 1 的数组元素
+array[1]=22;
+System.out.println(array[1]); // 输出的是22
+```
+
+### 3. 插入元素
+
+相比读取、更新操作,插入元素稍微复杂一些,分为以下两种情况:
+
+尾部插入:首先,我们看看尾部插入,这种情况很简单,在数组的最后新增一个新的元素,此时对于原数组来说没有任何影响,时间复杂度为0(1)。如下图所示:
+
+
+
+中间插入:如果在数组的中间位置插入元素的话,此时会对插入元素位置之后的元素产生影响,也就是这些数据需要向后依次挪动一个位置。如下图所示:
+
+
+
+中间插入的代码如下:
+
+```java
+/**
+ * 插入元素
+ * @param index 待插入的位置
+ * @param element 待插入的元素
+*/
+public void insert(int index,int element){
+ if(index<0 || index>size){
+ throw new IndexOutOfBoundsException("超过数组容量 ! 插入失败!");
+ }
+ // 从左到右,将元素向右移动一位
+ for (int i=size-1 ; i>index ; i--){
+ array[i+1]=array[i];
+ }
+ // 此时index这个位置已经腾空了,可以放进入element
+ array[index]=element;
+ //数组中元素个数+1
+ size++;
+}
+```
+
+#### 3.1 数组扩容
+
+因为数组的长度在创建的时候已经确定了,当插入元素的时候如果数组已经满了,是没办法插入成功的。这个时候就要考虑数组扩容的问题了,那么该如何实现扩容呢?
+
+其实我们可以这样,比如此时的数组是A, A已经满了,我们再创建一个数组B且数组长度是A的2倍,然后我们将数组A的元素全部放到数组B中,这样就完成了数组扩容了。
+
+数组扩容的代码如下:
+
+```java
+/**
+ * 数组扩容为原数组的二倍
+ */
+public void resize(){
+ int[] newArray=new int[array.length*2];
+ System.arraycopy(array,0,newArray,0,array.length);
+ array=newArray;
+}
+```
+
+### 4. 删除元素
+
+删除元素和插入元素类似,如果我们删除第k个位置的数据,为了内存的连续性,同样会涉及数据的挪动。如下图所示:
+
+
+
+删除元素的代码如下:
+
+```java
+ /**
+ * 根据数组下标删除元素
+ *
+ * @param index 数组下标
+ * @return
+ */
+ public int delete(int index) {
+ if (index < 0 || index> size) {
+ throw new IndexOutOfBoundsException("已经超过数组容量 ! 插入失败!");
+ }
+ int deleteElement = array[index];
+ // 从左到右,将元素向左移动一位
+ for (int i = index; i < size - 1; i++) { + array[i] = array[i + 1]; + } + size--; + return deleteElement; + } +``` + +## 总结 + +数组是使用一块连续的内存空间,存储相同类型的一组数据,其最大的优点是数组支持随机访问,是因为数组可以通过数组下标(寻址公式)快速访问对应元素,时间复杂度为O(1)。 + +数组在删除元素和插入元素这两个操作比较低效,是因为数组为了保持数据的连续性,会涉及到数据的挪动,平均时间复杂度为O(N)。 + +故数组适合"读多写少" 的场景。 \ No newline at end of file diff --git "a/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円240円210円.md" "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円240円210円.md" new file mode 100644 index 0000000..7e4a3ab --- /dev/null +++ "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円240円210円.md" @@ -0,0 +1,173 @@ +# 栈:一吃多就会吐的家伙~ + +## 前言 + +前两篇文章中我们学习了线性表中的数组和链表,数组和链表是最基础的数据结构,很多数据结构的实现都是基于数据或链表的。那么今天我们一起学习一个非常简单的数据结构—**栈**。栈使用是非常广泛的,比如我们Java中函数的调用、浏览器中的前进与后退功能等都会用到栈。 + +## 什么是栈 + +先画张图,看看栈长什么样。如下图所示: + + + +从图中看到栈是有些特殊,对于栈的操作被限制只能在栈的一端(栈顶)进行,也就是不允许在栈的中间进行数据操作,只能在栈顶进行数据操作(也就是插入和删除数据)。 + +> 思考:"受限制"的栈有什么用呢?
+
+特定的数据结构肯定有其特定的使用场景,相比于数组或者链表而言,栈虽然没有怎么灵活(只能在栈的一端进行数据操作),但是对于新增或者删除数据的时候,因为栈只涉及到一端,效率肯定不低。
+
+如何去理解栈呢?其实也非常简单,一句话可以概况栈的特性:**先进后出**,俗称"**吃多了吐**"。哈哈~
+
+## 栈的基本操作
+
+栈有不同的实现方式,基于数组实现的栈,被叫做**顺序栈**。基于链表实现的栈,被叫做**链式栈**。不管用什么方式实现的栈,其原理都是一样的,不用担心!
+
+栈的操作主要就两个:入栈(push)和出栈(pop)。
+
+- 顺序栈
+
+下面我们先基于数组来实现一个顺序栈,代码如下:
+
+```java
+public class MyStack {
+ private Object[] data = null; // 数组
+ private int maxSize = 0; //栈容量
+ private int top = -1; //栈顶指针
+
+ // 初始化构造方法
+ MyStack(int initialSize) {
+ if (initialSize>= 0) {
+ this.maxSize = initialSize;
+ data = new Object[initialSize];
+ top = -1;
+ } else {
+ throw new RuntimeException("初始化大小不能小于0: " + initialSize);
+ }
+ }
+
+ // 初始化构造方法 默认栈容量为10
+ public MyStack() {
+ this(10);
+ }
+
+ //入栈操作
+ public boolean push(E e) {
+ //首先判断一下栈是否已经满了
+ if (top == maxSize - 1) {
+ // 扩容
+ resize();
+ }
+ data[top] = e;
+ top++;
+ return true;
+ }
+
+ //出栈操作
+ public E pop() {
+ //首先查看一下栈是否为空
+ if (top == -1) {
+ throw new RuntimeException("栈为空");
+ } else {
+ //将栈顶元素返回后维护一下栈顶指针
+ return (E) data[top--];
+ }
+ }
+
+ //查看栈顶元素
+ public E peek() {
+ if (top == -1) {
+ throw new RuntimeException("栈为空");
+ } else {
+ // 查看栈顶元素并不移除所以说不需要维护栈顶指针
+ return (E) data[top];
+ }
+ }
+
+ // 查看栈是否为空
+ public boolean isEmpty() {
+ return maxSize == 0;
+ }
+
+ // 扩容操作
+ public void resize() {
+ // 创建一个新数组
+ Object[] newArray = new Object[data.length * 2];
+ System.arraycopy(data, 0, newArray, 0, data.length);
+ data = newArray;
+ }
+
+
+}
+
+```
+
+在顺序栈中,数组的第一个元素最为栈底,最后一个元素最为栈顶。当top=-1的时候,此时栈为空。
+
+每当新增数据入栈push的时候,maxSize加一,同理删除元素出栈pop的时候,maxSize减一。因为是基础数组的实现,所以顺序栈会涉及一个扩容的情况。
+
+- 链式栈
+
+我们再来看看基于链表来实现一个链式栈,代码如下:
+
+```java
+public class MyStack {
+ StackNode top = null; //栈顶
+
+ private class StackNode{
+ E data;
+ StackNode next;
+ StackNode(E data) {
+ this.data=data;
+ }
+ }
+
+ /**
+ * 入栈
+ * 首先将要push的数据的next赋值为栈顶top
+ * 然后将栈顶指针指向新push进来的节点
+ * @param data
+ */
+ public void push(E data) {
+ StackNode newNode = new StackNode(data);
+ newNode.next = top;
+ top = newNode;
+ }
+
+ /**
+ * 出栈
+ * @return
+ */
+ public E pop() {
+ if(this.isEmpty()) {
+ throw new RuntimeException("栈为空");
+ }
+ E data = top.data;
+ top = top.next;
+ return data;
+ }
+
+ /**
+ * 查看栈顶元素
+ * @return
+ */
+ public E peek() {
+ if(isEmpty()) {
+ throw new RuntimeException("栈为空");
+ }
+ return top.data;
+ }
+
+ // 判断栈是否为空
+ public boolean isEmpty() {
+ return top == null;
+ }
+}
+```
+
+在链式栈中,单链表的头部最为栈顶,因为栈的特性是先进后出,所以不需要头节点的。每当新增数据入栈push的时候,需要让新的结点指向原栈顶,然后再让top指向新增的这个结点。同理删除元素出栈pop的时候,只需要栈顶的 top 指向栈顶元素的 next 指针即可完成删除。
+
+## 总结
+
+栈作为一个受限制的线性表,只允许对栈顶的数据进行操作,也就是所谓的:先进后出,后进先出。不管是顺序栈还是链式栈,新增或者删除数据时都只能在栈顶进行,故时间复杂度都是O(1),查找数据的时候都需要进行全局遍历,故时间复杂度都是O(n)。顺序栈基于数组实现,初始化时大小便已经固定,后续需要考虑扩容的情况,而链式栈基于链表实现,不需要考虑扩容。
+
+~ todo
\ No newline at end of file
diff --git "a/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円240円221円.md" "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円240円221円.md"
new file mode 100644
index 0000000..3a5f8ce
--- /dev/null
+++ "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/346円240円221円.md"
@@ -0,0 +1,3 @@
+# 👉 树:
+
+todo~
\ No newline at end of file
diff --git "a/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/347円256円227円346円263円225円346円246円202円350円277円260円.md" "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/347円256円227円346円263円225円346円246円202円350円277円260円.md"
new file mode 100644
index 0000000..5c708ff
--- /dev/null
+++ "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/347円256円227円346円263円225円346円246円202円350円277円260円.md"
@@ -0,0 +1,3 @@
+# 👉 算法概述:
+
+todo~
\ No newline at end of file
diff --git "a/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/350円264円252円345円277円203円347円256円227円346円263円225円.md" "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/350円264円252円345円277円203円347円256円227円346円263円225円.md"
new file mode 100644
index 0000000..d2a13de
--- /dev/null
+++ "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/350円264円252円345円277円203円347円256円227円346円263円225円.md"
@@ -0,0 +1,3 @@
+# 👉 贪心算法:
+
+todo~
\ No newline at end of file
diff --git "a/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/351円200円222円345円275円222円347円256円227円346円263円225円.md" "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/351円200円222円345円275円222円347円256円227円346円263円225円.md"
new file mode 100644
index 0000000..1ca7f82
--- /dev/null
+++ "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/351円200円222円345円275円222円347円256円227円346円263円225円.md"
@@ -0,0 +1,3 @@
+# 👉 递归算法:
+
+todo~
\ No newline at end of file
diff --git "a/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/351円223円276円350円241円250円.md" "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/351円223円276円350円241円250円.md"
new file mode 100644
index 0000000..9126d23
--- /dev/null
+++ "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/351円223円276円350円241円250円.md"
@@ -0,0 +1,232 @@
+# 👉 链表:想写好链表代码可真要下点功夫
+
+
+
+
+## 前言
+
+上一篇文章我们探讨了数组这个非常基础的数据结构。对于数组,我们知道了数组在内存中是按照顺序存储并线性排列,所以具有"随机访问"的能力,但是对于删除和插入等操作却十分低效。
+
+今天我们一起探讨一个新的数据结构—**链表**,看看链表是什么?学习链表有什么用?
+
+## 链表是什么
+
+链表是一种非常重要的数据结构,应用的非常广泛,在写链表代码非常容易出错,所以面试中链表经常会被用来考察面试者的逻辑是否严谨。
+
+链表它不像数组,数组需要的是一块连续的内存空间来存储,而链表并不需要一块连续的内存你空间(也就是可连续也可不连续),它可以利用"**指针**"(`next`域)将一组零散的内存块串联起来,所有链表的存储方式是随机存储。我们看看链表中的单个节点长什么样,如下如所示:
+
+
+
+图中你可以看到,`data`和`next`。 解释一下:
+
+1. **data**: 存放结点值的数据域 ;
+2. **next**: 记录下个结点地址的指针,也叫做后继指针域;
+
+链表之所以能够将零散的内存块串联起来,主要就是依靠这个`next`指针。
+
+那么接下来,今天我们一起了解三种最常见的链表结构,分别是**单链表**、**双向链表**、**循环链表**等。
+
+### 1. 单链表
+
+我们先来看看单链表的结构,如下图所示:
+
+
+
+图中我们可以发现,在单链表中每个单节点都包含两部分,也就是上面我们说的`data`和`next`。这里就不再解释了。除此之外,还有一个`head`,这个是什么呢? 这个其实是**头结点**,也就是链表的第一个节点。同样道理,链表最后一个结点我们称为**尾结点**,尾结点比较特殊,它的next指针是指向null的,也就是表示链表的最后一个结点。
+
+### 2. 双向链表
+
+我们先再来看看双向链表的结构,如下图所示:
+
+
+
+图中我们可以发现,双向链表是比单链表稍微复杂一些的,在单链表中只有一个方向,每个结点只有一个后继指针`next` , 而双向链表支持两个方向,每个结点中不仅有一个后继指针`next`,还有一个前继指针`pre`,而且第一个结点的前继指针`pre`是指向`null`的。
+
+> 思考:双向链表每个结点使用两个指针有什么优缺点呢?
+
+由图可知,单链表只支持一个方向的遍历,而双向链表是支持两个方向的遍历的。优点就是双向链表要比单链表灵活的多,但是这种灵活是要付出代价的。缺点就是如果存储相同数量的元素,相比单链表而言,双向链表的两个指针是比较浪费空间的。
+
+### 3. 循环链表
+
+提到循环链表,可分为单向循环链表和双向循环链表,其实都是由上述的两种链表演化而来。如下图所示:
+
+
+
+
+
+单链表的尾结点后继指针是指向`null`,而循环链表的尾结点后继指针是指向链表的头结点的,图中我们可以发现,循环链表就像一个环一样首尾连接。
+
+## 链表的基本操作
+
+上文中我们一起简单聊了几种常见的链表结构,下面我们以单链表为例,用图解的方式看看链表是怎么进行增删改查的,在开始之前我们先创建一个类。代码如下:
+
+```java
+public class MyLinked {
+
+ private Node head;
+
+ private Node last;
+
+ private int size;
+
+ private static class Node{
+
+ public int data;
+
+ public Node next;
+
+ public Node(int data){
+ this.data=data;
+ }
+ }
+
+}
+```
+
+### 1. 查找结点
+
+当数组在查找元素的时候,可以通过下标快速定位到对应元素。但是链表可没这个能力,在链表中查找某个元素,只能从头结点开始一个个向后查找,直到找到要查找的元素或者找不到。由于从头开始遍历,故时间复杂度为O(N)。链表查找结点过程如下图所示:
+
+
+
+ 查找指定结点的代码如下:
+
+```java
+/**
+ * 获取指定位置的元素
+ *
+ * @param index 指定位置
+ * @return
+ * @throws Exception
+ */
+public Node find(int index) throws Exception {
+ if (index < 0 || index> size) {
+ throw new IndexOutOfBoundsException("超出链表实际节点范围!");
+ }
+ Node temp = head;
+ for (int i = 0; i < index; i++) { + temp = temp.next; + } + return temp; +} +``` + +### 2. 更新结点 + +链表中更新结点如查找过程类似,也是从头开始遍历,找到要更新的结点那个位置,然后直接赋值就可以了。链表更新结点过程如下图所示: + + + +### 3. 新增结点 + +链表中新增结点需要考虑三种情况,分别是:头部新增、中间新增、尾部新增。 + +我们先来看看最简单的尾部新增的情况,只需要遍历链表,如果当前结点的`next`指向`null `的话,就直接该结点的`next`指针指向新增的这个结点就可以了。如下图所示: + +接下来我们在看看头部插入情况,因为在链表头部插入,所有我们不需要遍历链表。我们先将新增的这个结点的`next`指针指向原链表的头结点`head`,然后修改一下头结点的位置为新增的这个结点即可。如下图所示: + + + +最后我们再看看中间新增结点的情况,此时我们需要遍历链表,第一步:将新增的结点的`next`指针指向新增的位置的结点,第二步:将新增的这个位置的前置结点的`next`指针指向新结点即可。这个过程一点要注意,一点不能颠倒顺序,否则容易链表的断开。如下图所示: + + + +指定位置新增结点的代码如下: + +```java + /** + * 指定位置新增元素 + * + * @param data + * @param index + * @throws Exception + */ +public void insert(int data, int index) throws Exception { + if (index < 0 || index> size) {
+ throw new IndexOutOfBoundsException("超出链表实际节点范围!");
+ }
+ Node insertNode = new Node(data);
+ if (size == 0) {
+ // 空链表 新增
+ head = insertNode;
+ last = insertNode;
+ } else if (index == 0) {
+ // 头部新增
+ insertNode.next = head;
+ head = insertNode;
+ } else if (size == index) {
+ // 尾部新增
+ last.next = insertNode;
+ last = insertNode;
+ } else {
+ // 获得 新增的位置前面一个元素
+ Node preNode = find(index - 1);
+ insertNode.next = preNode.next;
+ preNode.next = insertNode;
+ }
+ // 链表实际长度+1
+ size++;
+}
+```
+
+
+
+### 4. 删除结点
+
+链表中删除结点同样需要考虑三种情况,分别是:头部删除、中间删除、尾部删除。
+
+我们先来看看最简单的尾部删除的情况,当遍历到链表倒数第二个结点的结点,直接将该结点的`next`结点指向`null`即可。如下图所示:
+
+
+
+接下来我们在看看头部删除情况,当删除链表头部结点的时候,只需要将头结点变更为原头结点的下一个结点为新的头结点即可。如下图所示:
+
+
+
+最后我们再看看中间删除结点的情况,这个情况的关键是找到待删除结点的前置结点。修改这个前置结点的下一个结点为待删除结点的下一个结点接口。如下图所示:
+
+
+
+指定位置新增结点的代码如下:
+
+```java
+/**
+ * 删除指定位置的链表元素
+ *
+ * @param index 指定位置
+ * @return 删除的元素
+ * @throws Exception
+*/
+public Node delete(int index) throws Exception {
+ if (index < 0 || index> size) {
+ throw new IndexOutOfBoundsException("超出链表实际节点范围!");
+ }
+ Node removeNode = null;
+ if (size == 0) {
+ // 头部删除
+ removeNode = head;
+ head = head.next;
+ } else if (size - 1 == index) {
+ // 尾部删除
+ // 获得 删除的位置前面一个元素
+ Node preNode = find(index - 1);
+ removeNode = preNode.next;
+ preNode.next = null;
+ last = preNode;
+ } else {
+ // 中间删除
+ // 获得 删除的位置前面一个元素
+ Node preNode = find(index - 1);
+ removeNode = preNode.next;
+ preNode.next = preNode.next.next;
+ }
+ // 链表实际长度-1
+ size--;
+ return removeNode;
+}
+```
+
+## 总结
+
+本文简单介绍了链表这个数据结构,我们知道了链表的优点是大小可变,插入和删除的效率很都非常高。缺点就是如果查找一个元素,你只能从头开始遍历,所以说查询的效率很低。
+
diff --git "a/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/351円230円237円345円210円227円.md" "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/351円230円237円345円210円227円.md"
new file mode 100644
index 0000000..cd80a2f
--- /dev/null
+++ "b/docs/347円256円227円346円263円225円344円270円216円346円225円260円346円215円256円347円273円223円346円236円204円/351円230円237円345円210円227円.md"
@@ -0,0 +1,123 @@
+# 👉 队列: 排队买包子,还不允许插队的那种
+
+## 前言
+
+上一篇文章我们简单阐述了**栈**这个基本数据结构,我们知道了,栈最大的特点就是`后进先出`,以及**入栈**和**出栈**这两个基本的操作。今天我们再来学习与**栈**非常相似的另一个数据结构—**队列**,那么接下来我们看看队列到底是什么吧。
+
+## 队列是什么
+
+首先,当你看到**队列**这两个字的时候,你脑袋里面会不会联想到每天在早餐店排队买包子的场景呢?(什么?你不吃早餐),这个时候不考虑插队情况(拒绝插队,从你我做起)的话,那就是站在队列前面的人先买到包子,后来的人只能站在队尾等待,故先来的先买包子,也就是队列的**先进先出**。
+
+上面我们提到`不能插队`,这个其实就是队列的限制,只能按照`先进先出`的规则,所以说队列同栈一样,也是一个操作受限的数据结构。画张图看看队列,如下图所示:
+
+
+
+## 队列的基本操作
+
+队列与栈相似,数组和链表均可以实现队列。其中基于数组实现的队列被称为顺序队列,基于链表实现的队列被称为链式队列。队列支持两种基本操作,分别是**入队**和**出队**,数据入队操作是在队列的队尾,数据的出队是在队列的队头。下面我们以基于数组实现的顺序队列为例,看看队列是如何进行入队和出队操作的。
+
+我们先创建一个属于我们的队列,代码就不做解释了,该注释的都注释了,如下代码所示:
+
+```java
+/**
+ * msJava
+ *
+ * @Description 基于数组实现顺序队列
+ * @Date 2021年08月01日
+ */
+public class MyArrayQueue {
+
+ private Object[] array;
+ //队列容量
+ private int n=0;
+ // 队头
+ private int head=0;
+ // 队尾
+ private int tail=0;
+
+ /**
+ * 队列构造
+ * @param capacity 队列容量
+ */
+ public MyArrayQueue(int capacity){
+ array=new Object[capacity];
+ n=capacity;
+ }
+
+ /**
+ * 查看当前队列是否为空
+ * @return
+ */
+ public boolean isEmpty() {
+ return n == 0;
+ }
+
+ /**
+ * 遍历当前队列
+ */
+ public void ergodic(){
+ for (int i = head; i < tail; i++) { + System.out.print(array[i]+" "); + } + System.out.println(); + } + +} +``` + +### 入队 + +我们先来画张图,再唠两毛钱的入队操作,如下图所示: + +**** + +如上图中,当6、3、2、1、7、8依次入队之后,此时队头第一个红色块6,队尾是图中黄色块。当有新的数据9入队的时候是直接放到黄色块中的,当数据入队的后,队尾需要向后移动一个位置。 + +我们已经学习过数组了,知道了数组在创建的时候容量已经确定,那么我们基于数组实现其他的数据结构,比如栈和队列,都必然会涉及到数组已满的情况,那么当实现队列的时候,队列已满的情况,你该怎么办呢?类比我们前面文章谈到了,这个你可以好好想想哦。 + +队列入队的代码如下: + +```java + /** + * 入队 + * @param e + * @return + */ + public boolean enqueue(E e){ + if(n==tail){ + throw new RuntimeException("队列已满~" ); + } + array[tail]=e; + tail++; + return true; + } +``` + +### 出队 + +然后再画张图,我们看看出队操作,如下图所示: + + + +如上图中,当6、3、2等依次出队,每次出队一个数据之后,队头都要向后移动一个位置。队列出队的代码如下: + +```java + /** + * 出队 + * @return + */ + public E dequeue(){ + if(head==tail){ + throw new RuntimeException("队列为空~" ); + } + E ref=(E)array[head]; + ++head; + return ref; + } +``` + +图中可以看到,已经出队的块变成了灰色,随着不断的出队,队列的容量逐渐减少,但是队头左侧的数组空间已经无法利用了(因为队列的只能队头出、队尾进),这样不是造成了空间浪费嘛。的确是这样,其实这样的问题可以采用**循环队列**,我们下一篇文章再来唠唠其他类型的队列。 + +## 总结 + +队列是一种操作受限的数据结构,只能先进先出,队列支持两种基本操作:入队和出队。基于数组实现的队列被称为顺序队列,基于链表实现的队列被称为链式队列。 \ No newline at end of file diff --git "a/docs/350円256円241円347円256円227円346円234円272円347円275円221円347円273円234円/OSI344円270円203円345円261円202円346円250円241円345円236円213円.md" "b/docs/350円256円241円347円256円227円346円234円272円347円275円221円347円273円234円/OSI344円270円203円345円261円202円346円250円241円345円236円213円.md" new file mode 100644 index 0000000..fd1409f --- /dev/null +++ "b/docs/350円256円241円347円256円227円346円234円272円347円275円221円347円273円234円/OSI344円270円203円345円261円202円346円250円241円345円236円213円.md" @@ -0,0 +1,33 @@ +# 👉 OSI 七层模型 + + image.png
+
+OSI 模型全称为开放式通信系统互连参考模型,是国际标准化组织 ( ISO ) 提出的一个试图使各种计算机在世界范围内互连为网络的标准框架。 OSI 将计算机网络体系结构划分为七层,每一层实现各自的功能和协议,并完成与相邻层的接口通信。OSI 的服务定义详细说明了各层所提供的服务。某一层的服务就是该层及其下各层的一种能力,它通过接口提供给更高一层。各层所提供的服务与这些服务是怎么实现的无关
+
+## 1 应用层
+
+应用层位于 OSI 参考模型的第七层,其作用是通过应用程序间的交互来完成特定的网络应用。该层协议定义了应用进程之间的交互规则,通过不同的应用层协议为不同的网络应用提供服务。例如域名系统 DNS,支持万维网应用的 HTTP 协议,电子邮件系统采用的 SMTP 协议等。在应用层交互的数据单元我们称之为报文。
+
+## 2 表示层
+
+表示层的作用是使通信的应用程序能够解释交换数据的含义,其位于 OSI 参考模型的第六层,向上为应用层提供服务,向下接收来自会话层的服务。该层提供的服务主要包括数据压缩,数据加密以及数据描述。这使得应用程序不必担心在各台计算机中表示和存储的内部格式差异。
+
+## 3 会话层
+
+会话层就是负责建立、管理和终止表示层实体之间的通信会话。该层提供了数据交换的定界和同步功能,包括了建立检查点和恢复方案的方法。
+
+## 4 传输层
+
+传输层的主要任务是为两台主机进程之间的通信提供服务。应用程序利用该服务传送应用层报文。该服务并不针对某一特定的应用,多种应用可以使用同一个运输层服务。由于一台主机可同时运行多个线程,因此运输层有复用和分用的功能。所谓复用就是指多个应用层进程可同时使用下面运输层的服务,分用和复用相反,是运输层把收到的信息分别交付上面应用层中的相应进程。
+
+## 5 网络层
+
+两台计算机之间传送数据时其通信链路往往不止一条,所传输的信息甚至可能经过很多通信子网。网络层的主要任务就是选择合适的网间路由和交换节点,确保数据按时成功传送。在发送数据时,网络层把运输层产生的报文或用户数据报封装成分组和包向下传输到数据链路层。在网络层使用的协议是无连接的网际协议(Internet Protocol)和许多路由协议,因此我们通常把该层简单地成为 IP 层。
+
+## 6 数据链路层
+
+数据链路层通常也叫做链路层,在物理层和网络层之间。两台主机之间的数据传输,总是在一段一段的链路上传送的,这就需要使用专门的链路层协议。在两个相邻节点之间传送数据时,数据链路层将网络层交下来的 IP 数据报组装成帧,在两个相邻节点间的链路上传送帧。每一帧包括数据和必要的控制信息。通过控制信息我们可以知道一个帧的起止比特位置,此外,也能使接收端检测出所收到的帧有无差错,如果发现差错,数据链路层能够简单的丢弃掉这个帧,以避免继续占用网络资源。
+
+## 7 物理层
+
+作为 OSI 参考模型中最低的一层,物理层的作用是实现计算机节点之间比特流的透明传送,尽可能屏蔽掉具体传输介质和物理设备的差异。使其上面的数据链路层不必考虑网络的具体传输介质是什么。该层的主要任务是确定与传输媒体的接口的一些特性(机械特性、电气特性、功能特性,过程特性)。
diff --git "a/docs/350円256円241円347円256円227円346円234円272円347円275円221円347円273円234円/README.md" "b/docs/350円256円241円347円256円227円346円234円272円347円275円221円347円273円234円/README.md"
new file mode 100644
index 0000000..4bc39e8
--- /dev/null
+++ "b/docs/350円256円241円347円256円227円346円234円272円347円275円221円347円273円234円/README.md"
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git "a/docs/350円256円241円347円256円227円346円234円272円347円275円221円347円273円234円/_sidebar.md" "b/docs/350円256円241円347円256円227円346円234円272円347円275円221円347円273円234円/_sidebar.md"
new file mode 100644
index 0000000..17b58c0
--- /dev/null
+++ "b/docs/350円256円241円347円256円227円346円234円272円347円275円221円347円273円234円/_sidebar.md"
@@ -0,0 +1,5 @@
+* **👉 计算机网络** [↩](/README)
+ * [**OSI七层模型**](docs/计算机网络/OSI七层模型.md)
+ * [**网络协议分层**](docs/计算机网络/网络协议分层.md)
+ * [**TCP和UDP**](docs/计算机网络/理解TCP和UDP.md)
+ * [**HTTP与HTTPS**](docs/计算机网络/理解HTTP与HTTPS.md)
diff --git "a/docs/network/347円220円206円350円247円243円HTTP344円270円216円HTTPS.md" "b/docs/350円256円241円347円256円227円346円234円272円347円275円221円347円273円234円/347円220円206円350円247円243円HTTP344円270円216円HTTPS.md"
similarity index 99%
rename from "docs/network/347円220円206円350円247円243円HTTP344円270円216円HTTPS.md"
rename to "docs/350円256円241円347円256円227円346円234円272円347円275円221円347円273円234円/347円220円206円350円247円243円HTTP344円270円216円HTTPS.md"
index ae65a6a..ce77ce2 100644
--- "a/docs/network/347円220円206円350円247円243円HTTP344円270円216円HTTPS.md"
+++ "b/docs/350円256円241円347円256円227円346円234円272円347円275円221円347円273円234円/347円220円206円350円247円243円HTTP344円270円216円HTTPS.md"
@@ -1,4 +1,4 @@
-# 理解HTTP与HTTPS
+# 👉 理解HTTP与HTTPS
## HTTP 概况
diff --git "a/docs/network/347円220円206円350円247円243円TCP345円222円214円UDP.md" "b/docs/350円256円241円347円256円227円346円234円272円347円275円221円347円273円234円/347円220円206円350円247円243円TCP345円222円214円UDP.md"
similarity index 69%
rename from "docs/network/347円220円206円350円247円243円TCP345円222円214円UDP.md"
rename to "docs/350円256円241円347円256円227円346円234円272円347円275円221円347円273円234円/347円220円206円350円247円243円TCP345円222円214円UDP.md"
index 280cc12..a86583a 100644
--- "a/docs/network/347円220円206円350円247円243円TCP345円222円214円UDP.md"
+++ "b/docs/350円256円241円347円256円227円346円234円272円347円275円221円347円273円234円/347円220円206円350円247円243円TCP345円222円214円UDP.md"
@@ -1,4 +1,4 @@
-# 理解TCP和UDP
+# 👉 理解TCP和UDP
## 1. TCP是什么
@@ -14,11 +14,23 @@ TCP 之所以可以保证可靠性主要得益于两个方面,一个是"状
**面向字节流**:是指 TCP 是以字节流的方式进行数据传输的。
-## 2. TCP三次握手
+**TCP 是传输层协议,对应 OSI 网络模型的第四层传输层,特点如下。**
+
+- TCP 协议是基于链接的,也就是传输数据前需要先建立好链接,然后再进行传输。
+
+- TCP 链接一旦建立,就可以在链接上进行双向的通信。
+
+- TCP 的传输是基于字节流而不是报文,将数据按字节大小进行编号,接收端通过 ACK 来确认收到的数据编号,通过这种机制,TCP 协议能够保证接收数据的有序性和完整性,因此 TCP 能够提供可靠性传输。
+
+- TCP 还能提供流量控制能力,通过滑动窗口来控制数据的发送速率。滑动窗口的本质是动态缓冲区,接收端根据自己的处理能力,在 TCP 的 Header 中动态调整窗口大小,通过 ACK 应答包通知给发送端,发送端根据窗口大小调整发送的的速度。
+
+- 仅仅有了流量控制能力还不够,TCP 协议还考虑到了网络问题可能会导致大量重传,进而导致网络情况进一步恶化,因此 TCP 协议还提供拥塞控制。TCP 处理拥塞控制主要用到了慢启动、拥塞避免、拥塞发生、快速恢复四个算法,感兴趣的同学可以进一步了解。
+
+## 2. TCP三次握手与四次挥手
TCP 三次握手的执行流程,如下图所示:
-img
+img
TCP 三次握手的执行流程图
@@ -30,15 +42,27 @@ TCP 三次握手的执行流程图
**SEQ(Sequence Number),序列号。**
-TCP 的执行流程如下:
+TCP 的三次握手执行流程如下:
- 最开始时客户端和服务端都处于 CLOSED 状态,然后服务端先主动监听某个端口,此时服务器端就变成了 LISTEN(监听)状态;
-
- 然后客户端主动发起连接,发送 SYN(同步序列编号),此时客户端就变成了 SYN-SENT 状态;
-
- 服务端接收到信息之后返回 SYN 和 ACK 至客户端,此时服务器端就变成了 SYN-REVD 状态;
+- 客户端接收到消息之后,再发送 ACK 至服务器端,此时客户端就变成了 ESTABLISHED(已确认)状态,服务端收到 ACK 之后,也变成了 ESTABLISHED 状态,此时连接工作就执行完了。
+
+ TCP 四次挥手的执行流程,如下图所示:
+
+
+
+TCP 的四次挥手执行流程如下:
+
+- TCP 链接的关闭,通信双方都可以先发起,我们暂且把先发起的一方看作 Client,从图中看出,通信中 Client 和 Server 两端的链接都是 ESTABLISHED 状态,然后 Client 先主动发起了关闭链接请求,Client 向 Server 发送了一个 FIN 包,表示 Client 端已经没有数据要发送了,然后 Client 进入了 FIN_WAIT_1 状态。
+
+- Server 端收到 FIN 后,返回 ACK,然后进入 CLOSE_WAIT 状态。此时 Server 属于半关闭状态,因为此时 Client 向 Server 方向已经不会发送数据了,可是 Server 向 Client 端可能还有数据要发送。
+
+- 当 Server 端数据发送完毕后,Server 端会向 Client 端发送 FIN,表示 Server 端也没有数据要发送了,此时 Server 进入 LAST_ACK 状态,就等待 Client 的应答就可以关闭链接了。
+
+- Client 端收到 Server 端的 FIN 后,回复 ACK,然后进入 TIME_WAIT 状态。TIME_WAIT 状态下需要等待 2 倍的最大报文段生存时间,来保证链接的可靠关闭,之后才会进入 CLOSED 关闭状态。而 Server 端收到 ACK 后直接就进入 CLOSED 状态。
-- 客户端接收到消息之后,再发送 ACK 至服务器端,此时客户端就变成了 ESTABLISHED(已确认)状态,服务端收到 ACK 之后,也变成了 ESTABLISHED 状态,此时连接工作就执行完了。\
## 3.为什么TCP需要三次握手呢?
@@ -57,16 +81,18 @@ TCP 的执行流程如下:
那么在**建立 TCP 连接时就需要同步初始化一个序列号来保证 TCP 的稳定性**,因此它需要执行以下过程:
1. 首先客户端发送一个携带了初始序列号的 SYN 报文给服务器端;
-
2. 服务端接收到消息之后会回复一个 ACK 的应答报文,表示客户端的 SYN 报文已被服务端成功接收了;
-
3. 而客户端收到消息之后也会发送一个 ACK 给服务端,服务器端拿到这个消息之后,我们就可以得到一个可靠的初始化序列号了。
-
4. 而如果是两次握手的话,就无法进行序列号的确认工作了,因此也就无法得到一个可靠的序列号了,所以 TCP 连接至少需要三次握手。
-
5. 以上两种原因就是 TCP 连接为什么需要三次握手的主要原因,当然 TCP 连接还可以四次握手,甚至是五次握手,也能实现 TCP 连接的稳定性,但三次握手是最节省资源的连接方式,因此 TCP 连接应该为三次握手。
-## 4. UDP是什么?
+## 4. 为什么需要等待 2 倍最大报文段生存时间之后再关闭链接?
+
+1. 保证 TCP 协议的全双工连接能够可靠关闭;
+
+2. 保证这次连接的重复数据段从网络中消失,防止端口被重用时可能产生数据混淆。
+
+## 5. UDP是什么?
> UDP(User Data Protocol,用户数据报协议)是无连接的、简单的、面向数据报的传输层协议。也就是 UDP 在发送数据之前,无须建立客户端与服务端的连接,直接发送消息即可。
diff --git "a/docs/network/347円275円221円347円273円234円345円215円217円350円256円256円345円210円206円345円261円202円.md" "b/docs/350円256円241円347円256円227円346円234円272円347円275円221円347円273円234円/347円275円221円347円273円234円345円215円217円350円256円256円345円210円206円345円261円202円.md"
similarity index 99%
rename from "docs/network/347円275円221円347円273円234円345円215円217円350円256円256円345円210円206円345円261円202円.md"
rename to "docs/350円256円241円347円256円227円346円234円272円347円275円221円347円273円234円/347円275円221円347円273円234円345円215円217円350円256円256円345円210円206円345円261円202円.md"
index ef41df1..a27a568 100644
--- "a/docs/network/347円275円221円347円273円234円345円215円217円350円256円256円345円210円206円345円261円202円.md"
+++ "b/docs/350円256円241円347円256円227円346円234円272円347円275円221円347円273円234円/347円275円221円347円273円234円345円215円217円350円256円256円345円210円206円345円261円202円.md"
@@ -1,4 +1,4 @@
-# 网络协议分层
+# 👉 网络协议分层
> 国际标准化组织 ISO 提出了 OSI 开放互连的七层计算机网络模型,从上到下分别是应用层、表示层、会
> 话层、运输层、网络层、链路层和物理层。OSI 模型的概念清楚,理论也比较完善,但是既复杂又不实
diff --git "a/docs/350円256円276円350円256円241円346円250円241円345円274円217円/README.md" "b/docs/350円256円276円350円256円241円346円250円241円345円274円217円/README.md"
new file mode 100644
index 0000000..2aab32c
--- /dev/null
+++ "b/docs/350円256円276円350円256円241円346円250円241円345円274円217円/README.md"
@@ -0,0 +1,2 @@
+
+
diff --git "a/docs/350円256円276円350円256円241円346円250円241円345円274円217円/_sidebar.md" "b/docs/350円256円276円350円256円241円346円250円241円345円274円217円/_sidebar.md"
new file mode 100644
index 0000000..3ebaf78
--- /dev/null
+++ "b/docs/350円256円276円350円256円241円346円250円241円345円274円217円/_sidebar.md"
@@ -0,0 +1,4 @@
+* **👉 设计模式** [↩](/README)
+ * [单例模式](docs/设计模式/单例模式.md)
+ * [原型模式](docs/设计模式/原型模式.md)
+
\ No newline at end of file
diff --git "a/docs/350円256円276円350円256円241円346円250円241円345円274円217円/345円215円225円344円276円213円346円250円241円345円274円217円.md" "b/docs/350円256円276円350円256円241円346円250円241円345円274円217円/345円215円225円344円276円213円346円250円241円345円274円217円.md"
new file mode 100644
index 0000000..b36e4b3
--- /dev/null
+++ "b/docs/350円256円276円350円256円241円346円250円241円345円274円217円/345円215円225円344円276円213円346円250円241円345円274円217円.md"
@@ -0,0 +1,249 @@
+# 👉 单例模式
+
+## 1. 单例模式的定义
+
+ **单例模式指的是一个类中在任何情况下都绝对只有一个实例,并且提供一个全局访问点。**
+
+## 2. 单例模式的应用场景
+
+> 单例模式的应用非常广泛,如数据库中的连接池、J2EE中的ServletContext和ServletContextConfig、Spring框架中的ApplicationContext等等。然而在Java中,单例模式还可以保证一个JVM中只存在一个唯一的实例。
+
+单例模式的应用场景主要有以下几个方面:
+
+- 当需要频繁创建一些类的时候,使用单例可以降低系统的内存压力,减少GC(垃圾回收) ;
+- 当某些类创建实例时候需要占用的资源较多,或者实例化过程耗时比较长,且经常使用的情况;
+- 当存在频繁访问数据库或者文件的对象;
+- 当对于一些控制硬件级别的操作,或者从系统上来讲应当是单一控制逻辑的操作,是不允许存在多个实例的,否则玩完;
+
+## 3. 单例模式的优缺点
+
+### 3.1 单例模式的优点
+
+- **单例模式可以保证内存中只有一个实例对象,从而会减少内存的开销;**
+- **单例模式可以避免对资源的多重占用;**
+- **单例模式设置全局访问点,可以起到优化和共享资源的访问的作用;**
+
+### 3.2 单例模式的缺点
+
+- **扩展难**, 因为单例模式通常是没有接口的啊,如果想要扩展,那么你唯一途径就是修改之前的代码,所以说单例模式违背了开闭原则;
+- **调试难**,因为在并发测试中,单例模式是不利于代码的调试的,单例中的代码没有执行完,也不能模拟生成一个新对象;
+- **违背单一职责原则**,因为单例模式的业务代码通常写在一个类中,如果功能设计不合理,就很容易违背单一职责原则;
+
+## 4. 单例模式的实现方式及其优缺点
+
+### 4.1 单例模式的饿汉式实现
+
+#### 4.1.1 饿汉式标准写法
+
+Singleton类称为单例类,通过内部初始化一次 , 隐藏构造方法, 并提供一个全局访问点的方式实现。**
+
+```java
+/**
+ * msJava
+ *
+ * @Description 单例模式的通用写法
+ * @Date 2021年01月23日
+ */
+public class Singleton {
+ /**
+ * 内部初始化一次
+ */
+ private static final Singleton instance = new Singleton();
+
+ /**
+ * 隐藏构造方法
+ */
+ private Singleton() {
+ }
+
+ /**
+ * 提供一个全局访问点
+ *
+ * @return Singleton
+ */
+ public static Singleton getInstance() {
+ return instance;
+ }
+
+}
+
+```
+
+ **以上饿汉式单例写法在类的初始化的时候就会进行初始化操作,并且创建对象,绝对的线程安全,因为此时线程还没有出现就已经实例化了,故不会存在访问安全的问题。**
+
+#### 4.1.2 饿汉式静态块机制写法
+
+ **饿汉式还有一种实现,那就是静态块机制,如下代码所示:**
+
+```java
+/**
+ * msJava
+ *
+ * @Description 单例模式 饿汉式静态机制 实现
+ * @Date 2021年01月23日
+ */
+public class HungryStaticSingleton {
+
+ private static final HungryStaticSingleton hungrySingleton;
+ //静态代码块 类加载的时候就初始化
+ static {
+ hungrySingleton=new HungryStaticSingleton();
+ }
+ /**
+ * 私有化构造函数
+ */
+ private HungryStaticSingleton(){}
+
+ /**
+ * 提供一个全局访问点
+ * @return
+ */
+ public static HungryStaticSingleton getInstance() {
+ return hungrySingleton;
+ }
+}
+```
+
+ **我们分析一下这种是写法 ,可以明显的看到所以对象是类在加载的时候就进行实例化了,那么这样一来,会导致单例对象的数量不确定,从而会导致系统初始化的时候就造成大量内存浪费,况且你用不用还不一定,还一直占着空间,俗称"占着茅坑不拉屎"。**
+
+### 4.2 单例模式的懒汉式实现
+
+ **为了解决饿汉式单例写法可能带来的内存浪费问题,这里分析一下懒汉式单例的写法。如下代码所示:**
+
+```java
+/**
+ * msJava
+ *
+ * @Description 单例模式 懒汉式单例实现
+ * @Date 2021年01月23日
+ */
+public class LazySimpleSingleton {
+
+ private static LazySimpleSingleton lazySingleton = null;
+
+ /**
+ * 私有化构造函数
+ */
+ private LazySimpleSingleton() {
+
+ }
+ /**
+ * 提供一个全局访问点
+ *
+ * @return
+ */
+ public static LazySimpleSingleton getInstance() {
+ if (lazySingleton == null) {
+ lazySingleton = new LazySimpleSingleton();
+ }
+ return lazySingleton;
+ }
+}
+
+```
+
+ **这样实现的好处就是只有对象被使用的时候才会进行初始化,不会存在内存浪费的问题,但是它会在多线程环境下,存在线程安全问题。我们可以利用synchronized关键字将全局访问点方法变成一个同步方法,这样就可以解决线程安全的问题,代码如下所示:**
+
+```java
+/**
+ * msJava
+ *
+ * @Description 单例模式 懒汉式单例实现 synchronized修饰
+ * @Date 2021年01月23日
+ */
+public class LazySimpleSingleton {
+ private static LazySimpleSingleton lazySingleton = null;
+ /**
+ * 私有化构造函数
+ */
+ private LazySimpleSingleton() {}
+ /**
+ * 提供一个全局访问点
+ *
+ * @return
+ */
+ public synchronized static LazySimpleSingleton getInstance() {
+ if (lazySingleton == null) {
+ lazySingleton = new LazySimpleSingleton();
+ }
+ return lazySingleton;
+ }
+}
+```
+
+ **但是,这样虽然解决了线程安全的问题,可是如果在线程数量剧增的情况下,用synchronized加锁,则会导致大批线程阻塞,从而骤减系统性能。**
+
+### 4.3 单例模式的双重检测实现
+
+ 在上述代码上进一步优化,代码如下所示:
+
+```java
+
+/**
+ * msJava
+ *
+ * @Description 单例模式 懒汉式-双重检测单例实现
+ * @Date 2021年01月23日
+ */
+public class LazyDoubleCheckSingleton {
+ // volatile 关键字修饰
+ private volatile static LazyDoubleCheckSingleton lazySingleton ;
+ /**
+ * 私有化构造函数
+ */
+ private LazyDoubleCheckSingleton() {}
+ /**
+ * 提供一个全局访问点
+ *
+ * @return
+ */
+ public static LazyDoubleCheckSingleton getInstance() {
+ // 这里先判断一下是否阻塞
+ if (lazySingleton == null) {
+ synchronized (LazyDoubleCheckSingleton.class){
+ // 判断是否需要重新创建实例
+ if (lazySingleton == null) {
+ lazySingleton = new LazyDoubleCheckSingleton();
+ }
+ }
+ }
+ return lazySingleton;
+ }
+}
+```
+
+ **当第一个线程调用getInstance()方法时,第二个线程也可以调用,但是第一个线程执行synchronized时候,第二个线程就会发现阻塞,但是此时的阻塞是getInstance()内部的阻塞。**
+
+#### 4.4 单例模式的静态内部类实现
+
+ **虽然双重检测锁的单例模式解决了线程安全和性能问题,但是毕竟涉及加锁的操作,多多少少就会到了性能的影响,下面我们分享一下更加优雅的单例模式实现,如下代码所示:**
+
+```java
+/**
+ * msJava
+ *
+ * @Description 单例模式 静态内部类单例实现
+ * @Date 2021年01月23日
+ */
+public class LazyStaticInnerClassSingleton {
+ // 在构造方法里面抛出异常真的合适?
+ private LazyStaticInnerClassSingleton(){
+ if(LazyHolder.INSTANCE != null){
+ throw new RuntimeException("不允许创建多个实例");
+ }
+ }
+ // static 保证这个方法不会被重写 覆盖
+ private static LazyStaticInnerClassSingleton getInstance(){
+ return LazyHolder.INSTANCE;
+ }
+ // Java 默认不会加载内部类
+ private static class LazyHolder{
+ private static final LazyStaticInnerClassSingleton INSTANCE=new LazyStaticInnerClassSingleton();
+ }
+}
+
+```
+
+## 5. 总结
+
+ 单例模式面试几乎必备!
\ No newline at end of file
diff --git "a/docs/350円256円276円350円256円241円346円250円241円345円274円217円/345円216円237円345円236円213円346円250円241円345円274円217円.md" "b/docs/350円256円276円350円256円241円346円250円241円345円274円217円/345円216円237円345円236円213円346円250円241円345円274円217円.md"
new file mode 100644
index 0000000..2502767
--- /dev/null
+++ "b/docs/350円256円276円350円256円241円346円250円241円345円274円217円/345円216円237円345円236円213円346円250円241円345円274円217円.md"
@@ -0,0 +1,3 @@
+# 👉 原型模式
+
+todo~
\ No newline at end of file
diff --git "a/docs/350円256円276円350円256円241円346円250円241円345円274円217円/345円267円245円345円216円202円346円250円241円345円274円217円.md" "b/docs/350円256円276円350円256円241円346円250円241円345円274円217円/345円267円245円345円216円202円346円250円241円345円274円217円.md"
new file mode 100644
index 0000000..b2c4c8f
--- /dev/null
+++ "b/docs/350円256円276円350円256円241円346円250円241円345円274円217円/345円267円245円345円216円202円346円250円241円345円274円217円.md"
@@ -0,0 +1 @@
+todo~
\ No newline at end of file
diff --git "a/docs/350円256円276円350円256円241円346円250円241円345円274円217円/346円212円275円350円261円241円345円267円245円345円216円202円346円250円241円345円274円217円.md" "b/docs/350円256円276円350円256円241円346円250円241円345円274円217円/346円212円275円350円261円241円345円267円245円345円216円202円346円250円241円345円274円217円.md"
new file mode 100644
index 0000000..b2c4c8f
--- /dev/null
+++ "b/docs/350円256円276円350円256円241円346円250円241円345円274円217円/346円212円275円350円261円241円345円267円245円345円216円202円346円250円241円345円274円217円.md"
@@ -0,0 +1 @@
+todo~
\ No newline at end of file
diff --git "a/docs/350円256円276円350円256円241円346円250円241円345円274円217円/350円256円276円350円256円241円346円250円241円345円274円217円345円205円255円345円244円247円350円256円276円350円256円241円345円216円237円345円210円231円.md" "b/docs/350円256円276円350円256円241円346円250円241円345円274円217円/350円256円276円350円256円241円346円250円241円345円274円217円345円205円255円345円244円247円350円256円276円350円256円241円345円216円237円345円210円231円.md"
new file mode 100644
index 0000000..97ef7f4
--- /dev/null
+++ "b/docs/350円256円276円350円256円241円346円250円241円345円274円217円/350円256円276円350円256円241円346円250円241円345円274円217円345円205円255円345円244円247円350円256円276円350円256円241円345円216円237円345円210円231円.md"
@@ -0,0 +1,50 @@
+# 👉 六大设计原则
+> 本文我们一起学习设计模式中的六大设计原则
+
+六大设计原则包括:单一职责原则、里氏替换原则、依赖倒置原则、接口隔离原则、迪米特法则、开闭原则,接下来我们一一来看看它们分别是什么。
+
+
+## 1. 单一职责原则
+
+单一职责是指一个类只负责一个职责。比如现在比较流行的微服务,就是将之前很复杂耦合性很高的业务,分成多个独立的功能单一的简单接口,然后通过服务编排组装的方式实现不同的业务需求,而这种细粒度的独立接口就是符合单一职责原则的具体实践。
+
+## 2. 开闭原则
+
+开闭原则指的是对拓展开放、对修改关闭。它是说我们在实现一个新功能时,首先应该想到的是扩展原来的功能,而不是修改之前的功能。
+
+这个设计思想非常重要,也是一名优秀工程师所必备的设计思想。至于为什么要这样做?其实非常简单,我们团队在开发后端接口时遵循的也是这个理念。
+
+
+## 3. 里氏替换原则
+
+里氏替换原则是面向对象(OOP)编程的实现基础,它指的是所有引用了父类的地方都能被子类所替代,并且使用子类替代不会引发任何异常或者是错误的出现。
+
+## 4. 依赖倒置原则
+
+依赖倒置原则指的是要针对接口编程,而不是面向具体的实现编程。也就说高层模块不应该依赖底层模块,因为底层模块的职责通常更单一,不足以应对高层模块的变动,因此我们在实现时,应该依赖高层模块而非底层模块。
+
+## 5. 接口隔离原则
+
+接口隔离原则是指使用多个专门的接口比使用单一的总接口要好,即接口应该是相互隔离的小接口,而不是一个臃肿且庞杂的大接口。
+
+使用接口隔离原则的好处是避免接口的污染,提高了程序的灵活性。
+
+可以看出,接口隔离原则和单一职责原则的概念很像,单一职责原则要求接口的职责要单一,而接口隔离原则要求接口要尽量细化,二者虽然有异曲同工之妙,但可以看出单一职责原则要求的粒度更细。
+
+## 6. 迪米特法则
+
+迪米特法则又叫最少知识原则,它是指一个类对于其他类知道的越少越好。
+
+迪米特法则设计的初衷是降低类之间的耦合,让每个类对其他类都不了解,因此每个类都在做自己的事情,这样就能降低类之间的耦合性。
+
+这就好比我们在一些电视中看到的有些人在遇到强盗时,会选择闭着眼睛不看强盗,因为知道的信息越少反而对自己就越安全,这就是迪米特法则的基本思想。
+
+
+## 总结
+
+本文中我们总结回顾了设计模式中的六大设计原则,分别为单一职责原则、开闭原则、里氏替换原则、依赖倒置原则、接口隔离原则和迪米特法则,在真是开发中我们应该结合这些概念对照日常项目中的代码,看看还有哪些代码可以进行优化和改进。
+
+
+
+
+
diff --git "a/docs/designpatterns/350円256円276円350円256円241円346円250円241円345円274円217円346円200円273円347円273円223円.md" "b/docs/350円256円276円350円256円241円346円250円241円345円274円217円/350円256円276円350円256円241円346円250円241円345円274円217円346円200円273円347円273円223円.md"
similarity index 100%
rename from "docs/designpatterns/350円256円276円350円256円241円346円250円241円345円274円217円346円200円273円347円273円223円.md"
rename to "docs/350円256円276円350円256円241円346円250円241円345円274円217円/350円256円276円350円256円241円346円250円241円345円274円217円346円200円273円347円273円223円.md"
diff --git "a/docs/350円265円204円346円272円220円345円210円206円344円272円253円/347円274円226円347円250円213円344円272円272円347円224円237円.md" "b/docs/350円265円204円346円272円220円345円210円206円344円272円253円/347円274円226円347円250円213円344円272円272円347円224円237円.md"
new file mode 100644
index 0000000..52b79d7
--- /dev/null
+++ "b/docs/350円265円204円346円272円220円345円210円206円344円272円253円/347円274円226円347円250円213円344円272円272円347円224円237円.md"
@@ -0,0 +1,56 @@
+# 计算机基础
++ 《图解HTTP》
++ 《图解TCP/IP》
+
+# 数据结构与算法
++ 《算法 》
++ 《图解算法》
++ 《大话数据结构》
++ 《剑指offer 第二版》
++ 《程序员代码面试指南 第二版》
++ 《程序员面试金典 第六版》
+
+# Java
++ 《Java编程的逻辑》
++ 《Effective Java 第三版》
++ 《深入理解Java虚拟机 第三版》
++ 《Java并发编程实战》
++ 《Java并发编程的艺术》
++ 《Java并发编程之美》
+
+# 数据库
++ 《SQL必知必会》
++ 《MySQL必知必会》
++ 《高性能 MySQL》
++ 《Redis设计与实现》
+
+
+# 主流开源框架
++ 《SpringBoot实战派》
++ 《SpringBoot编程思想》
++ 《SpringCloud微服务架构实战派》
++ 《通用源码阅读指导书 MyBatis源码详解》
++ 《Spring5 核心原理与30个类手写实战》
+
+
+# 阅读人生
++ 《认识世界,认识自己》
++ 《中国通史》
++ 《毛泽东传》
++ 《如何学习》
++ 《围城》
++ 《平凡的世界》
++ 《人生》
++ 《人生海海》
++ 《理解人性》
++ 《乌合之众》
++ 《思考,快与慢》
++ 《金钱不能买什么》
++ 《人类简史:从动物到上帝》
++ 《未来简史:从智人到智神》
++ 《思维简史:从丛林到宇宙》
++ 《今日简史:人类命运大议题》
+
+
+
+
diff --git "a/docs/350円270円251円345円235円221円350円256円260円345円275円225円/IDEAMaven344円276円235円350円265円226円346円210円220円345円212円237円345円257円274円345円205円245円344円275円206円344円273円215円347円204円266円346円212円245円351円224円231円346円211円276円344円270円215円345円210円260円345円214円205円350円247円243円345円206円263円346円226円271円346円241円210円.md" "b/docs/350円270円251円345円235円221円350円256円260円345円275円225円/IDEAMaven344円276円235円350円265円226円346円210円220円345円212円237円345円257円274円345円205円245円344円275円206円344円273円215円347円204円266円346円212円245円351円224円231円346円211円276円344円270円215円345円210円260円345円214円205円350円247円243円345円206円263円346円226円271円346円241円210円.md"
new file mode 100644
index 0000000..2e70972
--- /dev/null
+++ "b/docs/350円270円251円345円235円221円350円256円260円345円275円225円/IDEAMaven344円276円235円350円265円226円346円210円220円345円212円237円345円257円274円345円205円245円344円275円206円344円273円215円347円204円266円346円212円245円351円224円231円346円211円276円344円270円215円345円210円260円345円214円205円350円247円243円345円206円263円346円226円271円346円241円210円.md"
@@ -0,0 +1,15 @@
+# IDEA 2020 Maven依赖成功导入但仍然报错找不到包解决方案
+
+## 问题描述
+
+项目中引入新的依赖,本地仓库和项目中都可以定位到依赖,但是点击运行,IDEA仍然提示如法加载该依赖。
+
+## 报错原因
+
+ IDEA启动程序按钮和maven的build使用的jar包环境不一样 。
+
+## 解决方案
+
+设置idea构建/运行操作委托给maven就行了。
+
+具体设置:Settings搜索Runner,勾选`Delegate IDE build/run actions to Maven`
\ No newline at end of file
diff --git "a/docs/350円270円251円345円235円221円350円256円260円345円275円225円/README.md" "b/docs/350円270円251円345円235円221円350円256円260円345円275円225円/README.md"
new file mode 100644
index 0000000..6b6e941
--- /dev/null
+++ "b/docs/350円270円251円345円235円221円350円256円260円345円275円225円/README.md"
@@ -0,0 +1,3 @@
+# 踩坑记录
+ * [IDEAMaven依赖成功导入但仍然报错找不到包解决方案](docs/踩坑记录/IDEAMaven依赖成功导入但仍然报错找不到包解决方案.md)
+
\ No newline at end of file
diff --git "a/docs/350円270円251円345円235円221円350円256円260円345円275円225円/_sidebar.md" "b/docs/350円270円251円345円235円221円350円256円260円345円275円225円/_sidebar.md"
new file mode 100644
index 0000000..2bf76a6
--- /dev/null
+++ "b/docs/350円270円251円345円235円221円350円256円260円345円275円225円/_sidebar.md"
@@ -0,0 +1,3 @@
+* **踩坑记录**
+ * [IDEAMaven依赖成功导入但仍然报错找不到包解决方案](docs/踩坑记录/IDEAMaven依赖成功导入但仍然报错找不到包解决方案.md)
+
\ No newline at end of file
diff --git "a/docs/Interviewquestions/Java346円240円270円345円277円203円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md" "b/docs/351円235円242円350円257円225円351円242円230円/Java346円240円270円345円277円203円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
similarity index 99%
rename from "docs/Interviewquestions/Java346円240円270円345円277円203円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
rename to "docs/351円235円242円350円257円225円351円242円230円/Java346円240円270円345円277円203円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
index 81029a2..de9737d 100644
--- "a/docs/Interviewquestions/Java346円240円270円345円277円203円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
+++ "b/docs/351円235円242円350円257円225円351円242円230円/Java346円240円270円345円277円203円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
@@ -1,4 +1,4 @@
-# Java后端开发核心面试题
+# 👉 Java后端开发核心面试题
## Java 基础部分
diff --git "a/docs/Interviewquestions/JVM351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md" "b/docs/351円235円242円350円257円225円351円242円230円/Java350円231円232円346円213円237円346円234円272円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
similarity index 99%
rename from "docs/Interviewquestions/JVM351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
rename to "docs/351円235円242円350円257円225円351円242円230円/Java350円231円232円346円213円237円346円234円272円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
index 9fab676..43a63c4 100644
--- "a/docs/Interviewquestions/JVM351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
+++ "b/docs/351円235円242円350円257円225円351円242円230円/Java350円231円232円346円213円237円346円234円272円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
@@ -1,4 +1,4 @@
-## JVM 面试题汇总
+# 👉 JVM 面试题汇总
#### 1.什么是 JVM?它有什么作用?
diff --git "a/docs/351円235円242円350円257円225円351円242円230円/MyBatis351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md" "b/docs/351円235円242円350円257円225円351円242円230円/MyBatis351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
new file mode 100644
index 0000000..247beb9
--- /dev/null
+++ "b/docs/351円235円242円350円257円225円351円242円230円/MyBatis351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
@@ -0,0 +1,25 @@
+# 👉 MyBatis
+
+#### MyBatis 重要组件
+
+- Mapper 配置:用于组织具体的查询业务和映射数据库的字段关系,可以使用 XML 格式或 Java 注解格式来实现;
+- Mapper 接口:数据操作接口也就是通常说的 DAO 接口,要和 Mapper 配置文件中的方法一一对应;
+- Executor:MyBatis 中所有的 Mapper 语句的执行都是通过 Executor 执行的;
+- SqlSession:类似于 JDBC 中的 Connection,可以用 SqlSession 实例来直接执行被映射的 SQL 语句;
+- SqlSessionFactory:SqlSessionFactory 是创建 SqlSession 的工厂,可以通过 SqlSession openSession() 方法创建 SqlSession 对象。
+
+#### MyBatis 执行流程
+
+MyBatis 完整执行流程如下图所示:
+
+
+
+MyBatis 执行流程说明:
+
+1. 首先加载 Mapper 配置的 SQL 映射文件,或者是注解的相关 SQL 内容。
+2. 创建会话工厂,MyBatis 通过读取配置文件的信息来构造出会话工厂(SqlSessionFactory)。
+3. 创建会话,根据会话工厂,MyBatis 就可以通过它来创建会话对象(SqlSession),会话对象是一个接口,该接口中包含了对数据库操作的增、删、改、查方法。
+4. 创建执行器,因为会话对象本身不能直接操作数据库,所以它使用了一个叫做数据库执行器(Executor)的接口来帮它执行操作。
+5. 封装 SQL 对象,在这一步,执行器将待处理的 SQL 信息封装到一个对象中(MappedStatement),该对象包括 SQL 语句、输入参数映射信息(Java 简单类型、HashMap 或 POJO)和输出结果映射信息(Java 简单类型、HashMap 或 POJO)。
+6. 操作数据库,拥有了执行器和 SQL 信息封装对象就使用它们访问数据库了,最后再返回操作结果,结束流程。
+
diff --git "a/docs/Interviewquestions/MySQL351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md" "b/docs/351円235円242円350円257円225円351円242円230円/MySQL351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
similarity index 99%
rename from "docs/Interviewquestions/MySQL351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
rename to "docs/351円235円242円350円257円225円351円242円230円/MySQL351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
index 1839010..501ec60 100644
--- "a/docs/Interviewquestions/MySQL351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
+++ "b/docs/351円235円242円350円257円225円351円242円230円/MySQL351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
@@ -1,4 +1,4 @@
-## MySQL 面试题汇总
+# 👉 MySQL 面试题汇总
#### 1.说一下 MySQL 执行一条查询语句的内部执行过程?
@@ -60,7 +60,7 @@ D:truncate table t
答:D
-题目解析:truncate 清除表数据不会写日志,delete 要写日志,因此 truncate 的效率要高于 delete。
+> 题目解析:truncate 清除表数据不会写日志,delete 要写日志,因此 truncate 的效率要高于 delete。
#### 10.唯一索引和普通索引哪个性能更好?
diff --git "a/docs/351円235円242円350円257円225円351円242円230円/README.md" "b/docs/351円235円242円350円257円225円351円242円230円/README.md"
new file mode 100644
index 0000000..3c53c50
--- /dev/null
+++ "b/docs/351円235円242円350円257円225円351円242円230円/README.md"
@@ -0,0 +1,14 @@
+# 面试题
+
+* [Java核心面试题汇总](docs/面试题/Java核心面试题汇总.md)
+* [算法常用面试题汇总](docs/面试题/算法常用面试题汇总.md)
+* [设计模式常见面试题汇总](docs/面试题/设计模式常见面试题汇总.md)
+* [MySQL面试题汇总](docs/面试题/MySQL面试题汇总.md)
+* [Java虚拟机面试题汇总](docs/面试题/Java虚拟机面试题汇总.md)
+* [Spring面试题汇总](docs/面试题/Spring面试题汇总.md)
+* [SpringMVC面试题汇总](docs/面试题/SpringMVC面试题汇总.md)
+* [MyBatis面试题汇总](docs/面试题/MyBatis面试题汇总.md)
+* [SpringBoot面试题汇总](docs/面试题/SpringBoot面试题汇总.md)
+* [分布式框架面试题汇总](docs/面试题/分布式框架面试题汇总.md)
+* [消息队列面试题汇总](docs/面试题/消息队列面试题汇总.md)
+
diff --git "a/docs/351円235円242円350円257円225円351円242円230円/SpringBoot351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md" "b/docs/351円235円242円350円257円225円351円242円230円/SpringBoot351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
new file mode 100644
index 0000000..d216e4d
--- /dev/null
+++ "b/docs/351円235円242円350円257円225円351円242円230円/SpringBoot351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
@@ -0,0 +1,3 @@
+# 👉 SpringBoot
+
+ ···· 高质量的面试题 整理中
\ No newline at end of file
diff --git "a/docs/346円241円206円346円236円266円/SpringMVC.md" "b/docs/351円235円242円350円257円225円351円242円230円/SpringMVC351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
similarity index 85%
rename from "docs/346円241円206円346円236円266円/SpringMVC.md"
rename to "docs/351円235円242円350円257円225円351円242円230円/SpringMVC351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
index c11fe9e..eee0232 100644
--- "a/docs/346円241円206円346円236円266円/SpringMVC.md"
+++ "b/docs/351円235円242円350円257円225円351円242円230円/SpringMVC351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
@@ -1,12 +1,6 @@
-## SpringMVC
+# 👉 SpringMVC
-### Spring MVC 介绍
-
-Spring MVC(Spring Web MVC)是 Spring Framework 提供的 Web 组件,它的实现基于 MVC 的设计模式:Controller(控制层)、Model(模型层)、View(视图层),提供了前端路由映射、视图解析等功能,让 Java Web 开发变得更加简单,也属于 Java 开发中必须要掌握的热门框架。
-
-### 执行流程
-
-Spring MVC 的执行流程如下:
+### Spring MVC 的执行流程
1. 客户端发送请求至前端控制器(DispatcherServlet)
2. 前端控制器根据请求路径,进入对应的处理器
@@ -34,93 +28,6 @@ Spring MVC 的核心组件如下列表所示:
7. **ModelAndView**:装载了模型数据和视图信息,作为 Handler 的处理结果,返回给 DispatcherServlet。
8. **ViewResolver**:视图解析器,DispatcherServlet 通过它将逻辑视图解析成物理视图,最终将渲染结果响应给客户端。
-### 自动类型转换
-
-自动类型转换指的是,Spring MVC 可以将表单中的字段,自动映射到实体类的对应属性上,请参考以下示例。
-
-#### 1. JSP 页面代码
-
-```html
-<%@ page contentType="text/html;charset=UTF-8" language="java" %>
-
-
-
-
-
-```
-
-#### 2. 编写实体类
-
-```java
-public class PersonDTO {
- private String name;
- private int age;
-
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
-}
-```
-
-#### 3. 编写控制器
-
-```java
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-@RestController
-public class PersonController {
- @RequestMapping(value = "/add", produces = "text/plain;charset=utf-8")
- public String add(PersonVO person) {
- return person.getName() + ":" + person.getAge();
- }
-}
-```
-
-#### 4. 执行结果
-
-执行结果如下图所示:
-
-
-
-#### 中文乱码处理
-
-业务的操作过程中可能会出现中文乱码的情况,以下是处理中文乱码的解决方案。
-第一步,在 web.xml 添加编码过滤器,配置如下:
-
-```xml
-
- encodingFilter
- org.springframework.web.filter.CharacterEncodingFilter
-
- encoding
- UTF-8
-
-
-
- encodingFilter
- /*
-
-```
-
-第二步,设置 RequestMapping 的 produces 属性,指定返回值类型和编码,如下所示:
-
-```java
-@RequestMapping(value = "/add", produces = "text/plain;charset=utf-8")
-```
-
### 拦截器
在 Spring MVC 中可以通过配置和实现 HandlerInterceptor 接口,来实现自己的拦截器。
@@ -511,6 +418,3 @@ public class HomeController{
}
```
-### 总结
-
-本文我们了解了 Spring MVC 运行的 8 个步骤和它的 8 大核心组件,也尝试了 Spring MVC 方面的类型转换,可将表单自动转换为实体对象,也使用 Hibernate 的验证功能优雅地实现了参数的验证,还可以通过配置和实现 HandlerInterceptor 接口来自定义拦截器,相信有了这些知识,可以帮助我们更高效地开发 Web 和接口项目。
\ No newline at end of file
diff --git "a/docs/346円241円206円346円236円266円/Spring.md" "b/docs/351円235円242円350円257円225円351円242円230円/Spring351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
similarity index 91%
rename from "docs/346円241円206円346円236円266円/Spring.md"
rename to "docs/351円235円242円350円257円225円351円242円230円/Spring351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
index 76160fc..f30f2ff 100644
--- "a/docs/346円241円206円346円236円266円/Spring.md"
+++ "b/docs/351円235円242円350円257円225円351円242円230円/Spring351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
@@ -1,38 +1,14 @@
-## Spring
-
-Spring Framework 简称 Spring,是 Java 开发中最常用的框架,地位仅次于 Java API,就连近几年比较流行的微服务框架 SpringBoot,也是基于 Spring 实现的,SpringBoot 的诞生是为了让开发者更方便地使用 Spring,因此 Spring 在 Java 体系中的地位可谓首屈一指。
-当然,如果想要把 Spring 所有功能都讲的一清二楚,远远不是一两篇文章能够做到的,但幸运的是,Spring 的基础资料可以很轻易的搜索到,那么我们本讲主要的目的就是把 Spring 中的核心知识点和常见面试题分享给大家,希望对大家能有所帮助。
+# 👉 Spring
### Spring 介绍
-Spring 是一个开源框架,为了解决企业应用程序开发复杂性而创建的,Spring 的概念诞生于 2002 年,于 2003 年正式发布第一个版本 Spring Framework 0.9。下面一起来看 Spring 各个版本的更新特性和它的发展变化吧。Spring 特性如下图所示:
-
-image-20200720165026696
-
-Spring 模块如下图所示:
-
-image-20200720165200616
-
-#### Spring 1.x
-
-此版本主要是为了解决企业应用程序开发复杂性而创建的,当时 J2EE 应用的经典架构是分层架构:表现层、业务层、持久层,最流行的组合就是 SSH(Struts、Spring、Hibernate)。
-Spring 1.x 仅支持基于 XML 的配置,确保用户代码不依赖 Spring,它主要包含了以下功能模块:aop、beans、ejb、jdbc、jndi、orm、transation、validation、web 等。
-
-#### Spring 2.x
-
-Spring 2.x 的改动并不是很大,主要是在 Spring 1.x 的基础上增加了几个新模块,如 ehcache、jms、jmx、scripting、stereotype 等。
-
-#### Spring 3.x
+Spring 是一个开源框架,为了解决企业应用程序开发复杂性而创建的,Spring 的概念诞生于 2002 年,于 2003 年正式发布第一个版本 Spring Framework 0.9。下面一起来看 Spring 各个版本的更新特性和它的发展变化吧。
-Spring 3.x 开始不止支持 XML 的配置,还扩展了基于 Java 类的配置,还增加了 Expression、Instructment、Tomcat、oxm 等组件,同时将原来的 Web 细分为:Portlet、Servlet。
+
-#### Spring 4.x
+图中红框框住的是比较重要的组件,Core 组件是 Spring 所有组件的核心;Bean 组件和 Context 组件我刚才提到了,是实现 IoC 和依赖注入的基础;AOP 组件用来实现面向切面编程;Web 组件包括 SpringMVC,是 Web 服务的控制层实现。
-Spring 4.x 扩充了 Groovy、Messaging、WebMvc、Tiles2、WebSocket 等功能组件,同时 Spring 还适配了 Java 版本,全面支持 Java 8.0、Lambda 表达式等。随着 RESTful 架构风格被越来越多的用户所采用,Spring 4.x 也提供了 RestController 等注解新特性。
-
-#### Spring 5.x
-
-Spring 5.x 紧跟 Java 相关技术的更新迭代,不断适配 Java 的新版本,同时不断重构优化自身核心框架代码,支持函数式、响应式编程模型等。
+img
### Spring 核心
@@ -46,7 +22,17 @@ Spring 核心包括以下三个方面:
#### 控制反转(IoC)
-控制反转(Inversion of Control,IoC),Spring的控制反转指一个对象依赖的其他对象将会在容器的初始化完成后主动将其依赖的对象传递给它,而不需要这个对象自己创建或者查找其依赖的对象。Spring基于控制反转技术实现系统对象之间依赖的解耦。
+控制反转(Inversion of Control,IoC),Spring的控制反转指一个对象依赖的其他对象将会在容器的初始化完成后主动将其依赖的对象传递给它,而不需要这个对象自己创建或者查找其依赖的对象。Spring基于控制反转技术实现系统对象之间依赖的解耦。举个例子如下图,拿公司招聘岗位来举例。假设一个公司有产品、研发、测试等岗位。如果是公司根据岗位要求,逐个安排人选,如图中向下的箭头,这是正向流程。如果反过来,不用公司来安排候选人,而是由第三方猎头来匹配岗位和候选人,然后进行推荐,如图中向上的箭头,这就是控制反转。
+
+
+
+在 Spring 中,对象的属性是由对象自己创建的,就是正向流程;如果属性不是对象创建,而是由 Spring 来自动进行装配,就是控制反转。这里的 DI 也就是依赖注入,就是实现控制反转的方式。正向流程导致了对象于对象之间的高耦合,IoC 可以解决对象耦合的问题,有利于功能的复用,能够使程序的结构变得非常灵活。
+
+#### Context 和 Bean
+
+Spring 进行 IoC 实现时使用的两个概念:Context 上下文和 Bean。如下图所示,所有被 Spring 管理的、由 Spring 创建的、用于依赖注入的对象,就叫作一个 Bean。Spring 创建并完成依赖注入后,所有 Bean 统一放在一个叫作 Context 的上下文中进行管理。
+
+
#### 依赖注入(DI)
@@ -470,6 +456,8 @@ public void save() {
答:Spring 中 Bean 的生命周期如下:
+
+
- 1 实例化 Bean:对于 BeanFactory 容器,当客户向容器请求一个尚未初始化的 Bean 时,或初始化 Bean 的时候需要注入另一个尚未初始化的依赖时,容器就会调用 createBean 进行实例化。对于 ApplicationContext 容器,当容器启动结束后,通过获取 BeanDefinition 对象中的信息,实例化所有的 Bean;
- 2 设置对象属性(依赖注入):实例化后的对象被封装在 BeanWrapper 对象中,紧接着 Spring 根据 BeanDefinition 中的信息以及通过 BeanWrapper 提供的设置属性的接口完成依赖注入;
- 3 处理 Aware 接口:Spring 会检测该对象是否实现了 xxxAware 接口,并将相关的 xxxAware 实例注入给 Bean:
diff --git "a/docs/351円235円242円350円257円225円351円242円230円/_sidebar.md" "b/docs/351円235円242円350円257円225円351円242円230円/_sidebar.md"
new file mode 100644
index 0000000..3679d2f
--- /dev/null
+++ "b/docs/351円235円242円350円257円225円351円242円230円/_sidebar.md"
@@ -0,0 +1,12 @@
+* **👉 面试题** [↩](/README)
+ * [Java核心面试题汇总](docs/面试题/Java核心面试题汇总.md)
+ * [算法常用面试题汇总](docs/面试题/算法常用面试题汇总.md)
+ * [设计模式常见面试题汇总](docs/面试题/设计模式常见面试题汇总.md)
+ * [MySQL面试题汇总](docs/面试题/MySQL面试题汇总.md)
+ * [Java虚拟机面试题汇总](docs/面试题/Java虚拟机面试题汇总.md)
+ * [Spring面试题汇总](docs/面试题/Spring面试题汇总.md)
+ * [SpringMVC面试题汇总](docs/面试题/SpringMVC面试题汇总.md)
+ * [MyBatis面试题汇总](docs/面试题/MyBatis面试题汇总.md)
+ * [SpringBoot面试题汇总](docs/面试题/SpringBoot面试题汇总.md)
+ * [分布式框架面试题汇总](docs/面试题/分布式框架面试题汇总.md)
+ * [消息队列面试题汇总](docs/面试题/消息队列面试题汇总.md)
diff --git "a/docs/Interviewquestions/345円210円206円345円270円203円345円274円217円346円241円206円346円236円266円351円235円242円350円257円225円351円242円230円345円220円210円351円233円206円.md" "b/docs/351円235円242円350円257円225円351円242円230円/345円210円206円345円270円203円345円274円217円346円241円206円346円236円266円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
similarity index 99%
rename from "docs/Interviewquestions/345円210円206円345円270円203円345円274円217円346円241円206円346円236円266円351円235円242円350円257円225円351円242円230円345円220円210円351円233円206円.md"
rename to "docs/351円235円242円350円257円225円351円242円230円/345円210円206円345円270円203円345円274円217円346円241円206円346円236円266円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
index 5b2957e..800950a 100644
--- "a/docs/Interviewquestions/345円210円206円345円270円203円345円274円217円346円241円206円346円236円266円351円235円242円350円257円225円351円242円230円345円220円210円351円233円206円.md"
+++ "b/docs/351円235円242円350円257円225円351円242円230円/345円210円206円345円270円203円345円274円217円346円241円206円346円236円266円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
@@ -1,4 +1,4 @@
-## Java 分布式框架面试题合集
+# 👉 Java 分布式框架面试题合集
#### 1.什么是 ZooKeeper?
diff --git "a/docs/Interviewquestions/346円241円206円346円236円266円345円237円272円347円241円200円351円235円242円350円257円225円351円242円230円.md" "b/docs/351円235円242円350円257円225円351円242円230円/346円241円206円346円236円266円345円237円272円347円241円200円351円235円242円350円257円225円351円242円230円.md"
similarity index 98%
rename from "docs/Interviewquestions/346円241円206円346円236円266円345円237円272円347円241円200円351円235円242円350円257円225円351円242円230円.md"
rename to "docs/351円235円242円350円257円225円351円242円230円/346円241円206円346円236円266円345円237円272円347円241円200円351円235円242円350円257円225円351円242円230円.md"
index 7777310..b0b9d3c 100644
--- "a/docs/Interviewquestions/346円241円206円346円236円266円345円237円272円347円241円200円351円235円242円350円257円225円351円242円230円.md"
+++ "b/docs/351円235円242円350円257円225円351円242円230円/346円241円206円346円236円266円345円237円272円347円241円200円351円235円242円350円257円225円351円242円230円.md"
@@ -1,4 +1,4 @@
-# Spring经典面试题汇总
+# 👉 Spring经典面试题汇总
## 1. Spring的两大特性是什么?
diff --git "a/docs/Interviewquestions/346円266円210円346円201円257円351円230円237円345円210円227円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md" "b/docs/351円235円242円350円257円225円351円242円230円/346円266円210円346円201円257円351円230円237円345円210円227円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
similarity index 99%
rename from "docs/Interviewquestions/346円266円210円346円201円257円351円230円237円345円210円227円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
rename to "docs/351円235円242円350円257円225円351円242円230円/346円266円210円346円201円257円351円230円237円345円210円227円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
index 195c166..3c4256a 100644
--- "a/docs/Interviewquestions/346円266円210円346円201円257円351円230円237円345円210円227円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
+++ "b/docs/351円235円242円350円257円225円351円242円230円/346円266円210円346円201円257円351円230円237円345円210円227円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
@@ -1,4 +1,4 @@
-## 消息队列面试题汇总
+# 👉 消息队列面试题汇总
#### 1.消息队列的应用场景有哪些?
diff --git "a/docs/Interviewquestions/347円256円227円346円263円225円345円270円270円347円224円250円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md" "b/docs/351円235円242円350円257円225円351円242円230円/347円256円227円346円263円225円345円270円270円347円224円250円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
similarity index 99%
rename from "docs/Interviewquestions/347円256円227円346円263円225円345円270円270円347円224円250円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
rename to "docs/351円235円242円350円257円225円351円242円230円/347円256円227円346円263円225円345円270円270円347円224円250円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
index ad1c989..9617287 100644
--- "a/docs/Interviewquestions/347円256円227円346円263円225円345円270円270円347円224円250円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
+++ "b/docs/351円235円242円350円257円225円351円242円230円/347円256円227円346円263円225円345円270円270円347円224円250円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
@@ -1,4 +1,4 @@
-## 算法常用面试题汇总
+# 👉 算法常用面试题汇总
#### 1.说一下什么是二分法?使用二分法时需要注意什么?如何用代码实现?
diff --git "a/docs/Interviewquestions/350円256円276円350円256円241円346円250円241円345円274円217円345円270円270円350円247円201円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md" "b/docs/351円235円242円350円257円225円351円242円230円/350円256円276円350円256円241円346円250円241円345円274円217円345円270円270円350円247円201円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
similarity index 99%
rename from "docs/Interviewquestions/350円256円276円350円256円241円346円250円241円345円274円217円345円270円270円350円247円201円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
rename to "docs/351円235円242円350円257円225円351円242円230円/350円256円276円350円256円241円346円250円241円345円274円217円345円270円270円350円247円201円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
index ef43dc1..3eca0b4 100644
--- "a/docs/Interviewquestions/350円256円276円350円256円241円346円250円241円345円274円217円345円270円270円350円247円201円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
+++ "b/docs/351円235円242円350円257円225円351円242円230円/350円256円276円350円256円241円346円250円241円345円274円217円345円270円270円350円247円201円351円235円242円350円257円225円351円242円230円346円261円207円346円200円273円.md"
@@ -1,4 +1,4 @@
-## 设计模式常见面试题汇总
+# 👉 设计模式常见面试题汇总
#### 1.说一下设计模式?你都知道哪些?
diff --git "a/image/345円205円254円344円274円227円345円217円267円344円272円214円347円273円264円347円240円201円.jpg" "b/image/345円205円254円344円274円227円345円217円267円344円272円214円347円273円264円347円240円201円.jpg"
new file mode 100644
index 0000000..bd36374
Binary files /dev/null and "b/image/345円205円254円344円274円227円345円217円267円344円272円214円347円273円264円347円240円201円.jpg" differ
diff --git "a/image/345円215円225円345円220円221円345円276円252円347円216円257円351円223円276円350円241円250円.png" "b/image/345円215円225円345円220円221円345円276円252円347円216円257円351円223円276円350円241円250円.png"
new file mode 100644
index 0000000..0b67f1e
Binary files /dev/null and "b/image/345円215円225円345円220円221円345円276円252円347円216円257円351円223円276円350円241円250円.png" differ
diff --git "a/image/345円215円225円351円223円276円350円241円250円.png" "b/image/345円215円225円351円223円276円350円241円250円.png"
new file mode 100644
index 0000000..9248d1d
Binary files /dev/null and "b/image/345円215円225円351円223円276円350円241円250円.png" differ
diff --git "a/image/345円215円225円351円223円276円350円241円250円344円270円255円351円227円264円345円210円240円351円231円244円345円205円203円347円264円240円.png" "b/image/345円215円225円351円223円276円350円241円250円344円270円255円351円227円264円345円210円240円351円231円244円345円205円203円347円264円240円.png"
new file mode 100644
index 0000000..12967fc
Binary files /dev/null and "b/image/345円215円225円351円223円276円350円241円250円344円270円255円351円227円264円345円210円240円351円231円244円345円205円203円347円264円240円.png" differ
diff --git "a/image/345円215円225円351円223円276円350円241円250円344円270円255円351円227円264円346円226円260円345円242円236円345円205円203円347円264円240円.png" "b/image/345円215円225円351円223円276円350円241円250円344円270円255円351円227円264円346円226円260円345円242円236円345円205円203円347円264円240円.png"
new file mode 100644
index 0000000..96f42b3
Binary files /dev/null and "b/image/345円215円225円351円223円276円350円241円250円344円270円255円351円227円264円346円226円260円345円242円236円345円205円203円347円264円240円.png" differ
diff --git "a/image/345円215円225円351円223円276円350円241円250円345円244円264円351円203円250円345円210円240円351円231円244円345円205円203円347円264円240円.png" "b/image/345円215円225円351円223円276円350円241円250円345円244円264円351円203円250円345円210円240円351円231円244円345円205円203円347円264円240円.png"
new file mode 100644
index 0000000..db03bb3
Binary files /dev/null and "b/image/345円215円225円351円223円276円350円241円250円345円244円264円351円203円250円345円210円240円351円231円244円345円205円203円347円264円240円.png" differ
diff --git "a/image/345円215円225円351円223円276円350円241円250円345円244円264円351円203円250円346円226円260円345円242円236円345円205円203円347円264円240円.png" "b/image/345円215円225円351円223円276円350円241円250円345円244円264円351円203円250円346円226円260円345円242円236円345円205円203円347円264円240円.png"
new file mode 100644
index 0000000..e094008
Binary files /dev/null and "b/image/345円215円225円351円223円276円350円241円250円345円244円264円351円203円250円346円226円260円345円242円236円345円205円203円347円264円240円.png" differ
diff --git "a/image/345円215円225円351円223円276円350円241円250円345円260円276円351円203円250円345円210円240円351円231円244円345円205円203円347円264円240円.png" "b/image/345円215円225円351円223円276円350円241円250円345円260円276円351円203円250円345円210円240円351円231円244円345円205円203円347円264円240円.png"
new file mode 100644
index 0000000..8211ba0
Binary files /dev/null and "b/image/345円215円225円351円223円276円350円241円250円345円260円276円351円203円250円345円210円240円351円231円244円345円205円203円347円264円240円.png" differ
diff --git "a/image/345円215円225円351円223円276円350円241円250円345円260円276円351円203円250円346円226円260円345円242円236円345円205円203円347円264円240円.png" "b/image/345円215円225円351円223円276円350円241円250円345円260円276円351円203円250円346円226円260円345円242円236円345円205円203円347円264円240円.png"
new file mode 100644
index 0000000..09468e9
Binary files /dev/null and "b/image/345円215円225円351円223円276円350円241円250円345円260円276円351円203円250円346円226円260円345円242円236円345円205円203円347円264円240円.png" differ
diff --git "a/image/345円215円225円351円223円276円350円241円250円346円233円264円346円226円260円345円205円203円347円264円240円.png" "b/image/345円215円225円351円223円276円350円241円250円346円233円264円346円226円260円345円205円203円347円264円240円.png"
new file mode 100644
index 0000000..f8bbc60
Binary files /dev/null and "b/image/345円215円225円351円223円276円350円241円250円346円233円264円346円226円260円345円205円203円347円264円240円.png" differ
diff --git "a/image/345円215円225円351円223円276円350円241円250円346円237円245円346円211円276円345円205円203円347円264円240円.png" "b/image/345円215円225円351円223円276円350円241円250円346円237円245円346円211円276円345円205円203円347円264円240円.png"
new file mode 100644
index 0000000..90a6fa2
Binary files /dev/null and "b/image/345円215円225円351円223円276円350円241円250円346円237円245円346円211円276円345円205円203円347円264円240円.png" differ
diff --git "a/image/345円217円214円345円220円221円345円276円252円347円216円257円351円223円276円350円241円250円.png" "b/image/345円217円214円345円220円221円345円276円252円347円216円257円351円223円276円350円241円250円.png"
new file mode 100644
index 0000000..13d1296
Binary files /dev/null and "b/image/345円217円214円345円220円221円345円276円252円347円216円257円351円223円276円350円241円250円.png" differ
diff --git "a/image/345円217円214円345円220円221円351円223円276円350円241円250円.png" "b/image/345円217円214円345円220円221円351円223円276円350円241円250円.png"
new file mode 100644
index 0000000..329f31f
Binary files /dev/null and "b/image/345円217円214円345円220円221円351円223円276円350円241円250円.png" differ
diff --git "a/image/345円276円252円347円216円257円351円223円276円350円241円250円.png" "b/image/345円276円252円347円216円257円351円223円276円350円241円250円.png"
new file mode 100644
index 0000000..01b2513
Binary files /dev/null and "b/image/345円276円252円347円216円257円351円223円276円350円241円250円.png" differ
diff --git "a/image/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円344円270円223円346円240円217円.png" "b/image/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円344円270円223円346円240円217円.png"
new file mode 100644
index 0000000..05e4256
Binary files /dev/null and "b/image/346円225円260円346円215円256円347円273円223円346円236円204円344円270円216円347円256円227円346円263円225円344円270円223円346円240円217円.png" differ
diff --git "a/image/346円225円260円347円273円204円.png" "b/image/346円225円260円347円273円204円.png"
new file mode 100644
index 0000000..66169af
Binary files /dev/null and "b/image/346円225円260円347円273円204円.png" differ
diff --git "a/image/346円225円260円347円273円2041円.png" "b/image/346円225円260円347円273円2041円.png"
new file mode 100644
index 0000000..2128d60
Binary files /dev/null and "b/image/346円225円260円347円273円2041円.png" differ
diff --git "a/image/346円225円260円347円273円2042円.png" "b/image/346円225円260円347円273円2042円.png"
new file mode 100644
index 0000000..9a5b7e0
Binary files /dev/null and "b/image/346円225円260円347円273円2042円.png" differ
diff --git "a/image/346円225円260円347円273円2043円.png" "b/image/346円225円260円347円273円2043円.png"
new file mode 100644
index 0000000..357579b
Binary files /dev/null and "b/image/346円225円260円347円273円2043円.png" differ
diff --git "a/image/346円225円260円347円273円2044円.png" "b/image/346円225円260円347円273円2044円.png"
new file mode 100644
index 0000000..5b1c892
Binary files /dev/null and "b/image/346円225円260円347円273円2044円.png" differ
diff --git "a/image/346円240円210円.276ioja9ty4.png" "b/image/346円240円210円.276ioja9ty4.png"
new file mode 100644
index 0000000..824bd54
Binary files /dev/null and "b/image/346円240円210円.276ioja9ty4.png" differ
diff --git "a/image/347円240円201円344円270円212円Java.webp" "b/image/347円240円201円344円270円212円Java.webp"
new file mode 100644
index 0000000..a66df05
Binary files /dev/null and "b/image/347円240円201円344円270円212円Java.webp" differ
diff --git "a/image/350円256円276円350円256円241円346円250円241円345円274円217円346円200円235円347円273円264円345円257円274円345円233円276円.png" "b/image/350円256円276円350円256円241円346円250円241円345円274円217円346円200円235円347円273円264円345円257円274円345円233円276円.png"
new file mode 100644
index 0000000..4e22b4b
Binary files /dev/null and "b/image/350円256円276円350円256円241円346円250円241円345円274円217円346円200円235円347円273円264円345円257円274円345円233円276円.png" differ
diff --git "a/image/351円223円276円350円241円250円.png" "b/image/351円223円276円350円241円250円.png"
new file mode 100644
index 0000000..dce6b8b
Binary files /dev/null and "b/image/351円223円276円350円241円250円.png" differ
diff --git "a/image/351円223円276円350円241円250円345円215円225円344円270円252円350円212円202円347円202円271円.png" "b/image/351円223円276円350円241円250円345円215円225円344円270円252円350円212円202円347円202円271円.png"
new file mode 100644
index 0000000..c5b5edd
Binary files /dev/null and "b/image/351円223円276円350円241円250円345円215円225円344円270円252円350円212円202円347円202円271円.png" differ
diff --git "a/image/351円241円272円345円272円217円346円240円210円344円270円216円351円223円276円345円274円217円346円240円210円.png" "b/image/351円241円272円345円272円217円346円240円210円344円270円216円351円223円276円345円274円217円346円240円210円.png"
new file mode 100644
index 0000000..284e1d5
Binary files /dev/null and "b/image/351円241円272円345円272円217円346円240円210円344円270円216円351円223円276円345円274円217円346円240円210円.png" differ
diff --git "a/image/351円241円272円345円272円217円351円230円237円345円210円227円342円200円224円345円205円245円351円230円237円.5zboyf0cmqc0.png" "b/image/351円241円272円345円272円217円351円230円237円345円210円227円342円200円224円345円205円245円351円230円237円.5zboyf0cmqc0.png"
new file mode 100644
index 0000000..8bbdf81
Binary files /dev/null and "b/image/351円241円272円345円272円217円351円230円237円345円210円227円342円200円224円345円205円245円351円230円237円.5zboyf0cmqc0.png" differ
diff --git "a/image/351円241円272円345円272円217円351円230円237円345円210円227円342円200円224円345円207円272円351円230円237円.5kqhi5ajn0w0.png" "b/image/351円241円272円345円272円217円351円230円237円345円210円227円342円200円224円345円207円272円351円230円237円.5kqhi5ajn0w0.png"
new file mode 100644
index 0000000..e53e448
Binary files /dev/null and "b/image/351円241円272円345円272円217円351円230円237円345円210円227円342円200円224円345円207円272円351円230円237円.5kqhi5ajn0w0.png" differ
diff --git "a/image/351円241円272円345円272円217円351円230円237円345円210円227円344円270円216円351円223円276円345円274円217円351円230円237円345円210円227円.1y4xfbj2zun4.png" "b/image/351円241円272円345円272円217円351円230円237円345円210円227円344円270円216円351円223円276円345円274円217円351円230円237円345円210円227円.1y4xfbj2zun4.png"
new file mode 100644
index 0000000..fe0593a
Binary files /dev/null and "b/image/351円241円272円345円272円217円351円230円237円345円210円227円344円270円216円351円223円276円345円274円217円351円230円237円345円210円227円.1y4xfbj2zun4.png" differ
diff --git a/images/msjava.jpg b/images/msjava.jpg
deleted file mode 100644
index 1c97b3a..0000000
Binary files a/images/msjava.jpg and /dev/null differ
diff --git a/index.html b/index.html
index 81c2930..1ce2702 100644
--- a/index.html
+++ b/index.html
@@ -3,61 +3,440 @@
码上Java
-
+
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+🏃 💨 嘟嘟嘟 疯狂 加载中...
+
+
+
+
关注微信公众号:码上Java
+
一起进步,一起成为更好的自己~
+ EnjoyToShare
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+