<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh_CN">
  <title>仙鹤的学习小窝</title>
  <subtitle>坚持学习，无限进步</subtitle>
  <link href="https://xianhe51920.github.io/" rel="alternate" type="text/html"/>
  <link href="https://xianhe51920.github.io/atom.xml" rel="self" type="application/atom+xml"/>
  <id>https://xianhe51920.github.io/</id>
  <updated>2026-09-08T00:00:00.000Z</updated>
  <entry>
    <title>数据结构（综述）</title>
    <link href="https://xianhe51920.github.io/posts/data-structure/" rel="alternate" type="text/html"/>
    <id>https://xianhe51920.github.io/posts/data-structure/</id>
    <published>2026-09-08T00:00:00.000Z</published>
    <updated>2026-09-08T00:00:00.000Z</updated>
    <summary>数据结构：基本概念、顺序表、链表</summary>
    <content type="html"><![CDATA[<h1>数据结构</h1>
<blockquote>
<p>依据 王道考研《数据结构》课程整理（本文件为全部章节的总笔记，随阅读逐章追加）。</p>
</blockquote>
<h2>第一章 绪论</h2>
<h3>1.0 开篇——数据结构在学什么？</h3>
<p><strong>数据结构在学什么？</strong></p>
<ul>
<li>如何用程序代码把现实世界的问题<strong>信息化</strong>；</li>
<li>如何用计算机<strong>高效地处理</strong>这些信息从而创造价值。</li>
</ul>
<p><strong>人类文明三次浪潮</strong>（阿尔文·托夫勒《第三次浪潮》）：</p>
<table>
<thead>
<tr>
<th>浪潮</th>
<th>阶段</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>第一次</td>
<td>农业（约 1 万年前）</td>
<td>学会农耕，从"动物"到"人类"</td>
</tr>
<tr>
<td>第二次</td>
<td>工业（17 世纪末）</td>
<td>出现枪炮与机械，导致古文明灭亡</td>
</tr>
<tr>
<td>第三次</td>
<td><strong>信息化</strong>（正在到来）</td>
<td>高度信息化的世界，导致 ? ? ?</td>
</tr>
</tbody>
</table>
<blockquote>
<p>托夫勒："唯一可以确定的是，明天会使我们所有人大吃一惊。"</p>
</blockquote>
<p><strong>"信息化"</strong>：把现实事物用计算机表示。</p>
<table>
<thead>
<tr>
<th>现实事物</th>
<th>信息化后的表示</th>
</tr>
</thead>
<tbody>
<tr>
<td>财富（存钱罐）</td>
<td>电子零钱（浮点型变量 <code>float</code>）</td>
</tr>
<tr>
<td>排队（海底捞取号）</td>
<td>号单/用户号（数组？还是…）</td>
</tr>
<tr>
<td>友谊（朋友圈/粉丝）</td>
<td>社交网络（粉丝列表、二维码）</td>
</tr>
</tbody>
</table>
<blockquote>
<p>学完 C 语言后，如何用计算机表示这些信息、并高效处理 → 这就是<strong>数据结构</strong>要解决的问题。</p>
</blockquote>
<p><strong>计算机系统分层</strong>（信息化世界）：一台计算机/手机 = <strong>计组（硬件）</strong> → <strong>操作系统</strong> → <strong>C、数据结构（软件）</strong>；多台计算机之间通过<strong>计算机网络</strong>互联，构成"信息化世界"。</p>
<hr />
<h3>1.1 数据结构的基本概念</h3>
<blockquote>
<p>学习建议：概念多但较基础，抓大放小、形成框架。讨论一种数据结构时，关注"<strong>三要素</strong>"。</p>
</blockquote>
<p><strong>知识总览</strong>：</p>
<pre><code>graph LR
    V[数据结构] --&gt; A[基本概念]
    V --&gt; B[三要素]
    A --&gt; A1[数据]
    A --&gt; A2[数据元素、数据项]
    A --&gt; A3[数据对象、数据结构]
    A --&gt; A4[数据类型、抽象数据类型 ADT]
    B --&gt; B1[逻辑结构]
    B --&gt; B2[物理结构（存储结构）]
    B --&gt; B3[数据的运算]
</code></pre>
<h4>1. 什么是数据</h4>
<p><strong>数据</strong>：信息的载体，是描述客观事物属性的<strong>数、字符</strong>及所有能输入到计算机中并被计算机程序识别和处理的<strong>符号的集合</strong>。数据是计算机程序加工的原料。（计算机内以二进制 0/1 表示。）</p>
<h4>2. 数据元素、数据项</h4>
<ul>
<li><strong>数据元素</strong>：数据的<strong>基本单位</strong>，通常作为一个整体来考虑和处理。</li>
<li><strong>数据项</strong>：构成数据元素的<strong>不可分割的最小单位</strong>。</li>
<li>一个数据元素可由若干数据项组成；根据实际业务需求确定"什么是数据元素、什么是数据项"。
例：海底捞取号记录 = {号数、取号时间、就餐人数}；微博账号 = {昵称、性别、生日（年/月/日可为"组合项"）}。</li>
</ul>
<h4>3. 数据结构、数据对象</h4>
<ul>
<li><strong>结构</strong>：各个元素之间的关系（如汉字"沙 / 粥 / 曼"的不同结构）。</li>
<li><strong>数据结构</strong>：相互之间存在一种或多种<strong>特定关系</strong>的数据元素的集合。</li>
<li><strong>数据对象</strong>：具有<strong>相同性质</strong>的数据元素的集合，是数据的一个<strong>子集</strong>。
例：数据结构 = 某个特定门店的排队顾客信息及其关系；数据对象 = 全国所有门店的排队顾客信息。</li>
</ul>
<h4>4. 数据结构的三要素</h4>
<p>讨论一种数据结构要关注：<strong>逻辑结构、物理结构（存储结构）、数据的运算</strong>。</p>
<p><strong>① 逻辑结构</strong>（数据元素之间的逻辑关系是什么？）：</p>
<pre><code>graph LR
    L[逻辑结构] --&gt; A[集合]
    L --&gt; B[线性结构]
    L --&gt; C[树形结构]
    L --&gt; D[图状结构]
</code></pre>
<table>
<thead>
<tr>
<th>逻辑结构</th>
<th>关系</th>
<th>例子</th>
</tr>
</thead>
<tbody>
<tr>
<td>集合</td>
<td>各元素同属一个集合，别无其他关系</td>
<td>火锅配料</td>
</tr>
<tr>
<td>线性结构</td>
<td>一对一；除第一个外均有唯一前驱，除最后一个外均有唯一后继</td>
<td>排队取号、烤串</td>
</tr>
<tr>
<td>树形结构</td>
<td>一对多</td>
<td>文件系统目录</td>
</tr>
<tr>
<td>图状结构（网状）</td>
<td>多对多</td>
<td>微信好友</td>
</tr>
</tbody>
</table>
<blockquote>
<p>线性结构 = 一对一；集合、树形、图状结构 = <strong>非线性结构</strong>。</p>
</blockquote>
<p><strong>② 物理结构（存储结构）</strong>（如何用计算机表示数据元素的逻辑关系？）：</p>
<table>
<thead>
<tr>
<th>存储结构</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td>顺序存储</td>
<td>逻辑上相邻的元素在物理位置上也相邻，关系由存储单元的邻接关系体现</td>
</tr>
<tr>
<td>链式存储</td>
<td>逻辑上相邻的元素物理位置可不相邻，借助指针表示逻辑关系</td>
</tr>
<tr>
<td>索引存储</td>
<td>存储元素信息的同时建立附加的索引表，索引项形如（关键字，地址）</td>
</tr>
<tr>
<td>散列存储</td>
<td>根据元素关键字直接算出存储地址（又称 Hash 存储）</td>
</tr>
</tbody>
</table>
<blockquote>
<p>链式、索引、散列属于<strong>非顺序存储</strong>。
<strong>绪论只需理解两点</strong>：①顺序存储元素物理上必须连续，非顺序存储可以离散；②存储结构会影响存储空间分配的方便程度（如"有人插队"）；③存储结构会影响对数据运算的速度（如"找第三个人"）。</p>
</blockquote>
<p><strong>③ 数据的运算</strong>：运算的<strong>定义</strong>针对<strong>逻辑结构</strong>（指出功能），运算的<strong>实现</strong>针对<strong>存储结构</strong>（指出操作步骤）。
例：队列逻辑结构（海底捞排队）定义运算：①队头元素出队；②新元素入队；③输出队列长度。</p>
<h4>5. 数据类型、抽象数据类型（ADT）</h4>
<p><strong>数据类型</strong>：一个值的集合和定义在此集合上的一组操作的总称。</p>
<ol>
<li><strong>原子类型</strong>：其值不可再分的数据类型（如 bool、int）。</li>
<li><strong>结构类型</strong>：其值可再分解为若干成分（分量）的数据类型（如 <code>struct Customer{ int num; int people; }</code>）。</li>
</ol>
<table>
<thead>
<tr>
<th>类型</th>
<th>值的范围</th>
<th>可进行的操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>bool</td>
<td>true / false</td>
<td>与、或、非</td>
</tr>
<tr>
<td>int</td>
<td>-2147483648 ~ 2147483647</td>
<td>加、减、乘、除、模…</td>
</tr>
<tr>
<td>struct Customer</td>
<td>num∈1~9999，people∈1~12</td>
<td>如"拼桌"运算</td>
</tr>
</tbody>
</table>
<p><strong>抽象数据类型（ADT, Abstract Data Type）</strong>：抽象数据组织及与之相关的操作。用数学化的语言定义数据的逻辑结构、定义运算，<strong>与具体的实现无关</strong>。</p>
<blockquote>
<p><strong>重要结论</strong>：</p>
<ul>
<li>定义了一个 ADT，就是定义了数据的逻辑结构、数据的运算，即定义了一个<strong>数据结构</strong>。</li>
<li>确定一种存储结构，就表示出了逻辑结构；存储结构不同，运算的具体实现也不同；确定了存储结构才能<strong>实现</strong>数据结构。</li>
<li>数据结构这门课看重的是<strong>数据元素之间的关系</strong>和<strong>对这些数据元素的操作</strong>，而不关心具体的数据项内容。</li>
</ul>
</blockquote>
<hr />
<h3>1.2.1 算法的基本概念</h3>
<p><strong>什么是算法？</strong></p>
<ul>
<li><strong>程序 = 数据结构 + 算法</strong>（数据结构是要处理的信息；算法是处理信息的步骤）。</li>
<li><strong>算法（Algorithm）</strong>：对特定问题求解步骤的一种描述，是指令的<strong>有限序列</strong>，其中的每条指令表示一个或多个操作。</li>
<li>例：做番茄炒蛋（食材+步骤）；对线性表按年龄递增排序（选择排序 Step1~Step4）。</li>
</ul>
<blockquote>
<p><strong>程序设计</strong> = 设计一个好的数据结构 + 设计一个好的算法。</p>
</blockquote>
<p><strong>算法的五个特性</strong>：</p>
<table>
<thead>
<tr>
<th>特性</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td>有穷性</td>
<td>算法总在执行<strong>有穷步</strong>后结束，且每一步都能在<strong>有穷时间</strong>内完成</td>
</tr>
<tr>
<td>确定性</td>
<td>每条指令必须有确切的含义，相同输入只能得出相同的输出</td>
</tr>
<tr>
<td>可行性</td>
<td>描述的每一步都能通过已实现的基本运算执行有限次来实现</td>
</tr>
<tr>
<td>输入</td>
<td>有零个或多个输入，取自某个特定对象的集合</td>
</tr>
<tr>
<td>输出</td>
<td>有一个或多个输出，是与输入有某种特定关系的量</td>
</tr>
</tbody>
</table>
<blockquote>
<p>注：<strong>算法必须是有穷的，而程序可以是无穷的</strong>（如微信是程序，不是算法）。</p>
</blockquote>
<p><strong>"好"算法的特质</strong>（设计时要尽量追求的目标）：</p>
<ol>
<li><strong>正确性</strong>：能正确地解决求解问题。</li>
<li><strong>可读性</strong>：描述要让别人看得懂（可由代码/伪代码/文字描述，重要的是<strong>无歧义</strong>）。</li>
<li><strong>健壮性</strong>：输入非法数据时能恰当地反应或处理，不产生莫名其妙的输出。</li>
<li><strong>高效率与低存储量需求</strong>：省时、省内存，即<strong>时间复杂度低、空间复杂度低</strong>。</li>
</ol>
<hr />
<h3>1.2.2 算法的时间复杂度</h3>
<p><strong>如何评估算法时间开销？</strong></p>
<ul>
<li>让算法先运行、事后统计运行时间？存在问题：和机器性能、编程语言、编译程序指令质量有关；有些算法不能事后统计（如导弹控制算法）。</li>
<li>应<strong>事前预估</strong>：算法<strong>时间复杂度</strong> = 事前预估算法时间开销 $T(n)$ 与问题规模 $n$ 的关系（T 表示 time）。</li>
</ul>
<p><strong>大 O 表示法</strong>：$T(n) = O(f(n))$，表示二者<strong>同阶/同等数量级</strong>（当 $n \to \infty$ 时二者之比为常数）。只考虑阶数高的部分，系数和低次幂可忽略。</p>
<p><strong>计算规则</strong>：</p>
<ul>
<li><strong>加法规则</strong>：$T(n) = O(f(n)) + O(g(n)) = O(\max(f(n), g(n)))$ —— 多项相加，只保留最高阶项，系数变为 1。</li>
<li><strong>乘法规则</strong>：$T(n) = O(f(n)) \times O(g(n)) = O(f(n)\times g(n))$ —— 多项相乘，都保留。</li>
</ul>
<p><strong>常用技巧</strong>：顺序执行的代码只影响常数项（忽略）；只需挑循环中的一个基本操作、分析其执行次数与 n 的关系；多层嵌套循环只需关注<strong>最深层循环</strong>循环了几次。</p>
<p><strong>三种时间复杂度</strong>：</p>
<table>
<thead>
<tr>
<th>类型</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td>最坏时间复杂度</td>
<td>考虑输入数据"最坏"的情况</td>
</tr>
<tr>
<td>平均时间复杂度</td>
<td>所有输入示例等概率出现时算法的期望运行时间</td>
</tr>
<tr>
<td>最好时间复杂度</td>
<td>考虑输入数据"最好"的情况</td>
</tr>
</tbody>
</table>
<blockquote>
<p>很多算法执行时间与输入数据有关（如顺序查找）；算法性能问题在 n 很大时才会暴露。</p>
</blockquote>
<p><strong>如何计算</strong>：①找一个基本操作（最深层循环）→ ②分析其执行次数 $x$ 与问题规模 $n$ 的关系 $x=f(n)$ → ③取 $O(x)$ 即 $T(n)$。</p>
<p><strong>例题</strong>：</p>
<ul>
<li><strong>逐步递增</strong>：<code>int i=1; while(i&lt;=n){ i++; printf(...); }</code> → $T(n)=3n+3=O(n)$。</li>
<li><strong>指数递增</strong>：<code>i=i*2</code> 每次翻倍 → 最深层循环语句频度 $x$，结束时 $2^x&gt;n$，$x=\log_2 n+1$ → $T(n)=O(\log_2 n)$。</li>
<li><strong>顺序查找</strong>（数组中找元素 n）：最好 $O(1)$、最坏 $O(n)$、平均 $\frac{1+2+\dots+n}{n}=\frac{1+n}{2}$ → $O(n)$。</li>
</ul>
<p><strong>常见复杂度排序</strong>（口诀"常对幂指阶"）：</p>
<p>$$O(1) &lt; O(\log_2 n) &lt; O(n) &lt; O(n\log_2 n) &lt; O(n^2) &lt; O(n^3) &lt; O(2^n) &lt; O(n!) &lt; O(n^n)$$</p>
<hr />
<h3>1.2.3 算法的空间复杂度</h3>
<p><strong>空间复杂度</strong>：空间开销（内存开销）与问题规模 $n$ 的关系，记作 $S(n) = O(f(n))$（S 表示 Space）。</p>
<p><strong>程序运行时的内存需求</strong>：</p>
<ul>
<li><strong>程序代码</strong>：大小固定，与问题规模无关；</li>
<li><strong>数据</strong>：局部变量、参数等，其中<strong>与 $n$ 相关</strong>的变量才影响空间复杂度。</li>
</ul>
<p><strong>原地工作</strong>：算法所需内存空间为常量，即 $S(n) = O(1)$。（如算法1 逐步递增型爱你，无论 $n$ 怎么变，内存都是固定常量。）</p>
<p><strong>例题</strong>：</p>
<table>
<thead>
<tr>
<th>程序</th>
<th>所需空间</th>
<th>$S(n)$</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>int flag[n]; int i;</code></td>
<td>$\approx 4n+8$</td>
<td>$O(n)$</td>
</tr>
<tr>
<td><code>int flag[n][n];</code></td>
<td>$n\times n$</td>
<td>$O(n^2)$</td>
</tr>
<tr>
<td><code>flag[n][n] + other[n] + i</code></td>
<td>$O(n^2)+O(n)+O(1)$</td>
<td>$O(n^2)$（加法规则）</td>
</tr>
<tr>
<td>递归 <code>loveYou(n)</code>（各层存参数 n）</td>
<td>递归深度 $n$</td>
<td>$O(n)$</td>
</tr>
<tr>
<td>递归 <code>loveYou(n)</code>（各层存 <code>flag[n]</code> 数组）</td>
<td>$1+2+\dots+n=\frac{n(n+1)}{2}$</td>
<td>$O(n^2)$</td>
</tr>
</tbody>
</table>
<p><strong>递归的空间复杂度 = 递归调用的深度</strong>（若各层所需存储空间不同，分析方法略有区别）。</p>
<p><strong>如何计算空间复杂度</strong>：</p>
<ul>
<li><strong>普通程序</strong>：①找到所占空间大小与问题规模相关的变量 → ②分析其所占空间 $x$ 与 $n$ 的关系 $x=f(n)$ → ③取 $O(x)$ 即 $S(n)$；</li>
<li><strong>递归程序</strong>：找到<strong>递归调用的深度</strong> $x$ 与 $n$ 的关系，取 $O(x)$ 即 $S(n)$。</li>
</ul>
<p><strong>常用技巧</strong>：同时间复杂度，加法/乘法规则、"常对幂指阶"（$O(1)&lt;O(\log_2 n)&lt;O(n)&lt;O(n\log_2 n)&lt;O(n^2)&lt;O(n^3)&lt;O(2^n)&lt;O(n!)$）。</p>
<hr />
<h3>小结</h3>
<ol>
<li><strong>基本概念</strong>：数据、数据元素/数据项、数据对象/数据结构、数据类型/抽象数据类型 ADT。</li>
<li><strong>数据结构的三要素</strong>：逻辑结构（集合/线性/树形/图状，即线性与非线性）、物理结构/存储结构（顺序/链式/索引/散列，后三者非顺序）、数据的运算（定义针对逻辑结构、实现针对存储结构）。</li>
<li><strong>算法</strong>：程序 = 数据结构 + 算法；算法的五个特性（有穷性、确定性、可行性、输入、输出），"好"算法特质（正确、可读、健壮、高效低存储）。</li>
<li><strong>时间复杂度</strong>：$T(n)=O(f(n))$；加法/乘法规则；"常对幂指阶"；最好/最坏/平均。</li>
<li><strong>空间复杂度</strong>：$S(n)=O(f(n))$；原地工作 $O(1)$；递归空间 = 递归深度。</li>
<li><strong>主考点</strong>：定义了一个 ADT 就是定义了一个数据结构；存储结构影响运算实现；复杂度用大 O 只留最高阶。</li>
</ol>
<hr />
<h2>第二章 线性表</h2>
<h3>2.1 线性表的定义和基本操作</h3>
<p><strong>线性表（Linear List）定义</strong>：是具有<strong>相同数据类型</strong>的 $n$（$n\ge0$）个数据元素的<strong>有限序列</strong>。$n$ 为表长，$n=0$ 时是空表。用 $L$ 命名线性表，一般表示为：</p>
<p>$$L = (a_1,\ a_2,\ \dots,\ a_i,\ a_{i+1},\ \dots,\ a_n)$$</p>
<p><strong>特性</strong>：①元素个数有限；②元素类型相同（每个元素所占空间一样大）；③元素有次序。</p>
<p><strong>重要术语</strong>：</p>
<ul>
<li>$a_i$ 是线性表中的"第 $i$ 个"元素——线性表中的<strong>位序</strong>（<strong>从 1 开始</strong>，注意数组下标从 0 开始，用数组实现线性表时要审题）。</li>
<li>$a_1$ 是<strong>表头元素</strong>，$a_n$ 是<strong>表尾元素</strong>。</li>
<li>除第一个元素外，每个元素有且仅有一个<strong>直接前驱</strong>；除最后一个元素外，每个元素有且仅有一个<strong>直接后继</strong>。</li>
</ul>
<blockquote>
<p>Eg：所有整数按递增次序排列，是线性表吗？——<strong>否</strong>（无限序列，不是线性表）。</p>
</blockquote>
<pre><code>graph LR
    A1[a1] --&gt; A2[a2] --&gt; A3[a3] --&gt; A4[a4] --&gt; A5[a5]
</code></pre>
<p><strong>基本操作</strong>（记忆思路：<strong>创销、增删改查</strong>）：</p>
<table>
<thead>
<tr>
<th>操作</th>
<th>功能</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>InitList(&amp;L)</td>
<td>初始化表</td>
<td>构造空表，分配内存空间（从无到有）</td>
</tr>
<tr>
<td>DestroyList(&amp;L)</td>
<td>销毁表</td>
<td>释放线性表占用的内存空间（从有到无）</td>
</tr>
<tr>
<td>ListInsert(&amp;L,i,e)</td>
<td>插入</td>
<td>在表 L 第 i 个位置插入元素 e（增）</td>
</tr>
<tr>
<td>ListDelete(&amp;L,i,&amp;e)</td>
<td>删除</td>
<td>删除表 L 第 i 个位置的元素，用 e 返回其值（删）</td>
</tr>
<tr>
<td>LocateElem(L,e)</td>
<td>按值查找</td>
<td>在表 L 中查找具有给定关键字值的元素</td>
</tr>
<tr>
<td>GetElem(L,i)</td>
<td>按位查找</td>
<td>获取表 L 第 i 个位置的元素的值</td>
</tr>
<tr>
<td>Length(L)</td>
<td>求表长</td>
<td>返回 L 中数据元素的个数</td>
</tr>
<tr>
<td>PrintList(L)</td>
<td>输出</td>
<td>按前后顺序输出 L 的所有元素</td>
</tr>
<tr>
<td>Empty(L)</td>
<td>判空</td>
<td>若 L 为空表返回 true，否则 false</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>为什么要实现基本操作</strong>：①团队合作编程，定义的 ADT 要让人方便使用（封装）；②将常用操作封装成函数，避免重复工作、降低出错风险。
<strong>改、查（本质是"定位"）</strong>：改之前也要先"查"（LocateElem / GetElem）。</p>
</blockquote>
<p><strong>什么时候要传入参数的引用 <code>&amp;</code></strong>：对参数的修改结果需要"<strong>带回来</strong>"时。如 <code>InitList(&amp;L)</code>、<code>DestroyList(&amp;L)</code>、<code>ListInsert(&amp;L,i,e)</code>、<code>ListDelete(&amp;L,i,&amp;e)</code>；而 <code>LocateElem(L,e)</code>、<code>GetElem(L,i)</code> 只读，不加 <code>&amp;</code>。</p>
<pre><code>void test(int &amp;x){ x = 1024; ... }   // 引用传递，x 的修改"带回来了"
int x = 1;  test(x);                 // 调用后 x=1024
</code></pre>
<blockquote>
<p>Tips：比起学会"How"，更重要的是想明白"Why"；函数命名要有可读性。</p>
</blockquote>
<h3>2.2.1 顺序表的定义</h3>
<p><strong>顺序表</strong>：用<strong>顺序存储</strong>的方式实现线性表。顺序存储把逻辑上相邻的元素存储在物理位置上也相邻的存储单元中，元素之间的关系由存储单元的邻接关系来体现。（即用"数组"存放数据元素。）</p>
<p><strong>元素地址</strong>：设线性表第一个元素存放位置为 LOC(L)（location 的缩写），则第 $i$ 个元素地址 = LOC(L) + $(i-1)\times$ sizeof(ElemType)。用 C 语言 <code>sizeof(ElemType)</code> 得到元素大小（如 <code>sizeof(int)=4B</code>，<code>sizeof(Customer)=8B</code>）。</p>
<p><strong>顺序表的实现——静态分配</strong>：</p>
<pre><code>#define MaxSize 10                 // 定义最大长度
typedef struct {
    ElemType data[MaxSize];        // 用静态的"数组"存放数据元素
    int length;                    // 顺序表的当前长度
} SqList;                          // 顺序表类型定义（静态分配）
</code></pre>
<p>Sq = sequence（顺序、序列）。给数据元素分配连续存储空间，大小为 MaxSize × sizeof(ElemType)。</p>
<ul>
<li>表长一开始就确定、<strong>无法更改</strong>（静态）。存满就"放弃治疗"；若一开始就声明很大的空间，又会浪费。</li>
</ul>
<p><strong>顺序表的实现——动态分配</strong>（静态数组存满无法扩容，用动态数组）：</p>
<pre><code>#define InitSize 10
typedef struct {
    ElemType *data;    // 指示动态分配数组的指针
    int MaxSize;       // 顺序表的最大容量
    int length;        // 顺序表的当前长度
} SeqList;             // 动态分配方式
</code></pre>
<pre><code>L.data = (ElemType *) malloc(sizeof(ElemType) * InitSize);   // 申请一片连续空间
</code></pre>
<ul>
<li>C 用 malloc / free（头文件 <code>&lt;stdlib.h&gt;</code>），C++ 用 new / delete。</li>
<li><strong>动态扩容</strong>（<code>IncreaseSize(&amp;L, len)</code>）：<code>int *p = L.data;  L.data = (int *) malloc((L.MaxSize + len) * sizeof(int));  for(...) L.data[i]=p[i];  L.MaxSize += len;  free(p);</code></li>
<li>注：可用 <code>realloc</code> 实现，但建议初学者用 malloc / free 以理解过程；<strong>扩容要把旧数据复制到新区域，时间开销大（O(n)）</strong>。</li>
</ul>
<p><strong>顺序表的特点</strong>：</p>
<table>
<thead>
<tr>
<th>特点</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>随机访问</td>
<td>能在 $O(1)$ 时间内找到第 $i$ 个元素（<code>data[i-1]</code>，位序从 1、下标从 0）</td>
</tr>
<tr>
<td>存储密度高</td>
<td>每个节点只存储数据元素，无指针</td>
</tr>
<tr>
<td>拓展容量不方便</td>
<td>静态分配无法改变；动态分配扩容也要搬移数据（$O(n)$）</td>
</tr>
<tr>
<td>插入、删除不方便</td>
<td>需要移动大量元素</td>
</tr>
</tbody>
</table>
<h3>2.2.2_1 顺序表的插入和删除</h3>
<p><strong>插入</strong>：<code>ListInsert(&amp;L, i, e)</code> —— 在表 L 的第 i 个位置插入元素 e。</p>
<pre><code>bool ListInsert(SqList &amp;L, int i, int e){
    if(i &lt; 1 || i &gt; L.length + 1) return false;   // 判断 i 的范围是否有效
    if(L.length &gt;= MaxSize) return false;          // 当前存储空间已满，不能插入
    for(int j = L.length; j &gt;= i; j--)             // 将第 i 个元素及之后的元素后移
        L.data[j] = L.data[j-1];
    L.data[i-1] = e;                               // 在位置 i 处放入 e
    L.length++;                                     // 长度 +1
    return true;
}
</code></pre>
<ul>
<li>注意位序 i 与数组下标关系：元素后移时<strong>从表尾元素开始往前</strong>移动。</li>
<li>健壮性：<code>if</code> 判断 i 是否合法、空间是否已满，返回 <code>bool</code> 给使用者反馈。</li>
</ul>
<p><strong>插入时间复杂度</strong>（问题规模 n = L.length）：</p>
<table>
<thead>
<tr>
<th>情况</th>
<th>说明</th>
<th>复杂度</th>
</tr>
</thead>
<tbody>
<tr>
<td>最好</td>
<td>插到表尾（i=n+1），0 次移动</td>
<td>$O(1)$</td>
</tr>
<tr>
<td>最坏</td>
<td>插到表头（i=1），n 次移动</td>
<td>$O(n)$</td>
</tr>
<tr>
<td>平均</td>
<td>各位置概率 $\frac{1}{n+1}$，平均移动 $\frac{n}{2}$</td>
<td>$O(n)$</td>
</tr>
</tbody>
</table>
<p><strong>删除</strong>：<code>ListDelete(&amp;L, i, &amp;e)</code> —— 删除表 L 第 i 个位置的元素，并用 e 返回其值。</p>
<pre><code>bool ListDelete(SqList &amp;L, int i, int &amp;e){
    if(i &lt; 1 || i &gt; L.length) return false;   // 判断 i 的范围是否有效
    e = L.data[i-1];                           // 将被删除的元素赋值给 e
    for(int j = i; j &lt; L.length; j++)          // 将第 i 个位置后的元素前移
        L.data[j-1] = L.data[j];
    L.length--;                                 // 长度 -1
    return true;
}
</code></pre>
<ul>
<li>元素前移时<strong>从靠前的元素开始</strong>（与插入的后移相反，注意别弄反）。</li>
<li><code>e</code> 加了 <code>&amp;</code>，因为要把删除的值"带回来"；不加 &amp; 传的是复制品，改不回去。</li>
</ul>
<p><strong>删除时间复杂度</strong>（问题规模 n = L.length）：</p>
<table>
<thead>
<tr>
<th>情况</th>
<th>说明</th>
<th>复杂度</th>
</tr>
</thead>
<tbody>
<tr>
<td>最好</td>
<td>删除表尾（i=n），0 次移动</td>
<td>$O(1)$</td>
</tr>
<tr>
<td>最坏</td>
<td>删除表头（i=1），n-1 次移动</td>
<td>$O(n)$</td>
</tr>
<tr>
<td>平均</td>
<td>各位置概率 $\frac{1}{n}$，平均移动 $\frac{n-1}{2}$</td>
<td>$O(n)$</td>
</tr>
</tbody>
</table>
<h3>2.2.2_2 顺序表的查找</h3>
<p><strong>按位查找</strong>：<code>GetElem(L, i)</code> —— 获取表 L 第 i 个位置的元素的值。</p>
<pre><code>ElemType GetElem(SeqList L, int i){
    return L.data[i-1];      // 位序 i 对应数组下标 i-1
}
</code></pre>
<ul>
<li><strong>时间复杂度 $O(1)$</strong>：顺序表数据元素在内存中<strong>连续存放</strong>，根据起始地址 + 元素大小可立即定位第 i 个元素（<strong>随机存取</strong>特性）。</li>
<li>指针类型决定"步长"：<code>data[i]</code> = 起始地址 + i×sizeof(ElemType)；<code>malloc</code> 返回 <code>void*</code>，需<strong>强制转换成</strong> <code>ElemType*</code>。</li>
</ul>
<p><strong>按值查找</strong>：<code>LocateElem(L, e)</code> —— 在表 L 中查找第一个元素值等于 e 的元素，返回其<strong>位序</strong>（找不到返回 0）。</p>
<pre><code>int LocateElem(SeqList L, ElemType e){
    for(int i = 0; i &lt; L.length; i++)
        if(L.data[i] == e)  return i + 1;   // 数组下标 i 对应位序 i+1
    return 0;                               // 退出循环，查找失败
}
</code></pre>
<ul>
<li><strong>基本数据类型</strong>（int、char、double、float…）可直接用 <code>==</code> 比较；</li>
<li><strong>结构类型</strong>不能直接用 <code>==</code>（C 语言会报错），需逐分量比较，或封装成 <code>isCustomerEqual(a, b)</code> 函数；考研初试手写代码可直接用 <code>==</code>（主要考算法思想）。</li>
</ul>
<p><strong>按值查找时间复杂度</strong>（问题规模 n = L.length）：</p>
<table>
<thead>
<tr>
<th>情况</th>
<th>说明</th>
<th>复杂度</th>
</tr>
</thead>
<tbody>
<tr>
<td>最好</td>
<td>目标元素在表头，循环 1 次</td>
<td>$O(1)$</td>
</tr>
<tr>
<td>最坏</td>
<td>目标元素在表尾，循环 n 次</td>
<td>$O(n)$</td>
</tr>
<tr>
<td>平均</td>
<td>各位置概率 $\frac{1}{n}$，平均 $\frac{n+1}{2}$ 次</td>
<td>$O(n)$</td>
</tr>
</tbody>
</table>
<h3>2.3.1 单链表的定义</h3>
<p><strong>单链表</strong>：用<strong>链式存储</strong>（存储结构）实现了<strong>线性结构</strong>（逻辑结构）。每个结点存储一个数据元素，各结点间的先后关系用一个<strong>指针</strong>表示。</p>
<table>
<thead>
<tr>
<th></th>
<th>顺序表（顺序存储）</th>
<th>单链表（链式存储）</th>
</tr>
</thead>
<tbody>
<tr>
<td>优点</td>
<td>可随机存取，存储密度高</td>
<td>不要求大片连续空间，改变容量方便</td>
</tr>
<tr>
<td>缺点</td>
<td>要求大片连续空间，改变容量不方便</td>
<td>不可随机存取，要耗费空间存放指针</td>
</tr>
</tbody>
</table>
<pre><code>graph LR
    H[头指针 L] --&gt; N1[a1] --&gt; N2[a2] --&gt; N3[a3] --&gt; N4[a4] --&gt; N5[NULL]
</code></pre>
<p><strong>用代码定义一个单链表</strong>：</p>
<pre><code>typedef struct LNode{      // 定义单链表结点类型
    ElemType data;         // 数据域，存放一个数据元素
    struct LNode *next;    // 指针域，指向下一个结点
}LNode, *LinkList;
</code></pre>
<ul>
<li><code>typedef &lt;数据类型&gt; &lt;别名&gt;</code>：给类型重命名。<code>LNode</code> 是结点类型，<code>LinkList</code> 等价于 <code>LNode *</code>。<strong>前者强调"是一个结点"，后者强调"这是一个单链表"，合适的地方用合适的名字可读性更高</strong>。</li>
<li>新建结点：<code>LNode * p = (LNode *) malloc(sizeof(LNode));</code>（malloc 返回 void*，需强制转换）。</li>
</ul>
<p><strong>两种实现方式：带头结点 / 不带头结点</strong>（头结点不存放数据，只是为了操作方便）：</p>
<table>
<thead>
<tr>
<th></th>
<th>不带头结点</th>
<th>带头结点</th>
</tr>
</thead>
<tbody>
<tr>
<td>初始化</td>
<td><code>InitList(&amp;L){ L = NULL; }</code></td>
<td><code>InitList(&amp;L){ L = (LNode*)malloc(sizeof(LNode)); L-&gt;next = NULL; }</code></td>
</tr>
<tr>
<td>空表判断</td>
<td><code>L == NULL</code></td>
<td><code>L-&gt;next == NULL</code></td>
</tr>
<tr>
<td>写代码</td>
<td>麻烦（首个/后续结点、空表/非空表逻辑不同）</td>
<td>更方便（统一处理）</td>
</tr>
</tbody>
</table>
<blockquote>
<p>头指针 L 就代表整个单链表；因为要修改头指针，<code>InitList</code> 的参数要加 <code>&amp;</code> 引用。</p>
</blockquote>
<h3>2.3.2_1 单链表的插入和删除</h3>
<p><strong>插入</strong>：</p>
<p><strong>① 按位序插入</strong> <code>ListInsert(&amp;L, i, e)</code>：在第 i 个位置插入元素 e，核心是找到第 i-1 个结点，把新结点插到它后面。</p>
<pre><code>bool ListInsert(LinkList &amp;L, int i, ElemType e){
    if(i &lt; 1) return false;
    LNode *p; int j = 0;
    p = L;                                // 头结点看作第 0 个结点
    while(p != NULL &amp;&amp; j &lt; i-1){ p = p-&gt;next; j++; }   // 找到第 i-1 个结点
    if(p == NULL) return false;
    LNode *s = (LNode *) malloc(sizeof(LNode));
    s-&gt;data = e;
    s-&gt;next = p-&gt;next;                    // 先连（顺序不能颠倒）
    p-&gt;next = s;                          // 再断
    return true;
}
</code></pre>
<ul>
<li>时间复杂度：最好 $O(1)$（插表头 i=1），最坏 $O(n)$（插表尾），平均 $O(n)$。</li>
<li><strong>不带头结点</strong>：不存在"第 0 个"结点，i=1 需特殊处理（改头指针 L）。</li>
</ul>
<p><strong>② 指定结点的后插</strong> <code>InsertNextNode(p, e)</code>：<code>s-&gt;next = p-&gt;next; p-&gt;next = s;</code> —— $O(1)$。</p>
<p><strong>③ 指定结点的前插</strong> <code>InsertPriorNode(p, e)</code>：<strong>偷天换日</strong>——把新结点 s 插到 p 后面，再交换数据，$O(1)$：</p>
<pre><code>s-&gt;next = p-&gt;next; p-&gt;next = s;
s-&gt;data = p-&gt;data;      // 把 p 中元素复制到 s
p-&gt;data = e;            // p 覆盖为 e
</code></pre>
<p><strong>删除</strong>：</p>
<p><strong>① 按位序删除</strong> <code>ListDelete(&amp;L, i, &amp;e)</code>：找到第 i-1 个结点，令其指向第 i+1 个结点，释放第 i 个结点。</p>
<pre><code>bool ListDelete(LinkList &amp;L, int i, ElemType &amp;e){
    if(i &lt; 1) return false;
    LNode *p; int j = 0; p = L;
    while(p != NULL &amp;&amp; j &lt; i-1){ p = p-&gt;next; j++; }
    if(p == NULL) return false;
    if(p-&gt;next == NULL) return false;      // 第 i-1 个结点之后已无其余结点
    LNode *q = p-&gt;next;
    e = q-&gt;data;
    p-&gt;next = q-&gt;next;                     // 将 *q 从链中"断开"
    free(q);
    return true;
}
</code></pre>
<ul>
<li>时间复杂度：最好 $O(1)$（删表头），最坏/平均 $O(n)$。</li>
</ul>
<p><strong>② 指定结点的删除</strong> <code>DeleteNode(p)</code>：</p>
<ul>
<li>方法 1：传入头指针，循环找到 p 的前驱，改前驱的 next。</li>
<li>方法 2：<strong>偷天换日</strong>——把 p-&gt;next 的数据复制到 p，再删除 p-&gt;next，$O(1)$。
<blockquote>
<p><strong>有坑</strong>：p 是最后一个结点时（p-&gt;next == NULL），方法 2 要特殊处理。</p>
</blockquote>
</li>
</ul>
<blockquote>
<p>Tips：这些代码都要会写；注意审题（<strong>带头否？</strong>）；体会"封装"的好处。</p>
</blockquote>
<h3>2.3.2_2 单链表的查找</h3>
<p><strong>按位查找</strong> <code>GetElem(L, i)</code>：返回第 i 个结点（头结点看作第 0 个结点）。</p>
<pre><code>LNode * GetElem(LinkList L, int i){
    if(i &lt; 0) return NULL;
    LNode *p; int j = 0;
    p = L;                                       // 头结点为第 0 个结点
    while(p != NULL &amp;&amp; j &lt; i){ p = p-&gt;next; j++; }
    return p;                                     // i 超过表长时返回 NULL
}
</code></pre>
<ul>
<li>时间复杂度 $O(n)$：[<strong>单链表不具备"随机访问"特性，只能依次扫描</strong>]（对比顺序表是 $O(1)$）。</li>
<li>王道书版本用 <code>j=1; p=L-&gt;next</code>；<code>if(i==0) return L; if(i&lt;1) return NULL;</code>。</li>
</ul>
<p><strong>按值查找</strong> <code>LocateElem(L, e)</code>：返回第一个数据域等于 e 的结点指针，找不到返回 NULL。</p>
<pre><code>LNode * LocateElem(LinkList L, ElemType e){
    LNode *p = L-&gt;next;                          // 从第 1 个结点开始查找
    while(p != NULL &amp;&amp; p-&gt;data != e) p = p-&gt;next;
    return p;
}
</code></pre>
<ul>
<li>平均时间复杂度 $O(n)$。（ElemType 是复杂结构类型时需逐分量比较，同顺序表。）</li>
</ul>
<p><strong>求单链表长度</strong> <code>Length(L)</code>：</p>
<pre><code>int Length(LinkList L){
    int len = 0; LNode *p = L;
    while(p-&gt;next != NULL){ p = p-&gt;next; len++; }
    return len;
}
</code></pre>
<ul>
<li>时间复杂度 $O(n)$。</li>
</ul>
<blockquote>
<p>对比：顺序表可"随机存取"（$O(1)$ 按位访问）；单链表只能<strong>依次扫描</strong>，三种基本操作（按位插入/删除/查找）平均都是 $O(n)$。写循环扫描时要<strong>注意边界条件</strong>。</p>
</blockquote>
]]></content>
    <author><name>仙鹤</name></author>
    <category term="编程"/>
  </entry>
  <entry>
    <title>Java 核心与 AI 开发进阶</title>
    <link href="https://xianhe51920.github.io/posts/java-core-ai-advanced/" rel="alternate" type="text/html"/>
    <id>https://xianhe51920.github.io/posts/java-core-ai-advanced/</id>
    <published>2026-09-06T00:00:00.000Z</published>
    <updated>2026-09-06T00:00:00.000Z</updated>
    <summary>Java 核心与 AI 开发进阶</summary>
    <content type="html"><![CDATA[<h1>Java核心与AI开发进阶</h1>
<h2>第十二章：集合高级</h2>
<blockquote>
<p>集合是长度可变的容器。Java 集合按"单列/双列"分为两大体系：<strong>单列</strong>（一次添加一个元素，对应 <code>Collection</code> 接口）和<strong>双列</strong>（一次添加两个元素即键值对，对应 <code>Map</code> 接口）。本篇覆盖 <code>Collection</code>、<code>List</code>、常用数据结构、<code>ArrayList</code> / <code>LinkedList</code> 以及泛型基础。</p>
</blockquote>
<h3>一、集合体系结构</h3>
<p><strong>整体划分</strong>：</p>
<table>
<thead>
<tr>
<th>分类</th>
<th>顶层接口</th>
<th>子接口 / 实现类</th>
<th>一次添加</th>
<th>特点</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:blue"&gt;单列集合&lt;/span&gt;</td>
<td><code>Collection</code></td>
<td><code>List</code>：ArrayList、LinkedList</td>
<td>1 个元素</td>
<td>有序</td>
</tr>
<tr>
<td>单列集合</td>
<td><code>Collection</code></td>
<td><code>Set</code>：TreeSet、HashSet、LinkedHashSet</td>
<td>1 个元素</td>
<td>无序</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;双列集合&lt;/span&gt;</td>
<td><code>Map</code></td>
<td>TreeMap、HashMap、LinkedHashMap</td>
<td>2 个元素（键值对）</td>
<td>键不可重复</td>
</tr>
</tbody>
</table>
<p><strong>List 与 Set 的核心区别</strong>：</p>
<table>
<thead>
<tr>
<th>接口</th>
<th>存取</th>
<th>索引</th>
<th>重复元素</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:blue"&gt;List 接口&lt;/span&gt;</td>
<td>存取有序</td>
<td>有</td>
<td>&lt;span style="color:orange"&gt;可以&lt;/span&gt;存储重复</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;Set 接口&lt;/span&gt;</td>
<td>存取无序</td>
<td>&lt;span style="color:red"&gt;没有&lt;/span&gt;</td>
<td>&lt;span style="color:red"&gt;不可以&lt;/span&gt;存储重复</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意&lt;/span&gt;：单列集合调用 <code>add(元素)</code>，双列集合调用 <code>put(键, 值)</code>。</p>
</blockquote>
<h3>二、Collection 接口的使用</h3>
<blockquote>
<p><code>Collection</code> 是所有单列集合的<strong>父接口</strong>，其内部方法（<code>add</code> / <code>remove</code> / <code>contains</code> / …）所有单列集合都可以使用。</p>
</blockquote>
<table>
<thead>
<tr>
<th>方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>public boolean add(E e)</code></td>
<td>把给定的对象添加到当前集合中</td>
</tr>
<tr>
<td><code>public void clear()</code></td>
<td>清空集合中所有的元素</td>
</tr>
<tr>
<td><code>public boolean remove(E e)</code></td>
<td>把给定的对象在当前集合中删除</td>
</tr>
<tr>
<td><code>public boolean contains(Object obj)</code></td>
<td>判断当前集合中是否包含给定的对象</td>
</tr>
<tr>
<td><code>public boolean isEmpty()</code></td>
<td>判断当前集合是否为空</td>
</tr>
<tr>
<td><code>public int size()</code></td>
<td>返回集合中元素的个数（集合的长度）</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:red"&gt;⚠ 关键点&lt;/span&gt;：<code>remove()</code> 与 <code>contains()</code> 方法<strong>底层依赖对象的 <code>equals</code> 方法</strong>判断是否相等。若集合中存的是自定义对象（如 <code>Student</code>），未重写 <code>equals</code> 时会调用 <code>Object.equals</code>（比地址），需重写才能按内容比较。</p>
</blockquote>
<h3>三、集合的通用遍历方式</h3>
<blockquote>
<p>通用遍历方式有三种，<strong><code>Set</code> 没有索引不能用普通 for 循环</strong>，所以后两种才真正"通用"。</p>
</blockquote>
<table>
<thead>
<tr>
<th>方式</th>
<th>适用场景</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:blue"&gt;普通 for 循环&lt;/span&gt;</td>
<td>仅 <code>List</code>（需索引）</td>
<td>写法 <code>list.get(i)</code>，<strong>Set 不可用</strong></td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;迭代器&lt;/span&gt; Iterator</td>
<td>所有 Collection</td>
<td><code>hasNext()</code> 判断 + <code>next()</code> 取值并后移</td>
</tr>
<tr>
<td>&lt;span style="color:orange"&gt;增强 for 循环&lt;/span&gt;</td>
<td>所有 Collection 与数组</td>
<td>JDK5 引入，<strong>内部原理就是 Iterator</strong></td>
</tr>
</tbody>
</table>
<p><strong>普通 for（仅 List）</strong>：</p>
<pre><code>ArrayList&lt;String&gt; list = new ArrayList&lt;&gt;();
for (int i = 0; i &lt; list.size(); i++) {
    String s = list.get(i);
}
</code></pre>
<p><strong>迭代器</strong>：</p>
<pre><code>Iterator&lt;String&gt; it = list.iterator();
while (it.hasNext()) {
    String s = it.next();
    System.out.println(s);
}
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意&lt;/span&gt;：<code>next()</code> 方法会<strong>取出当前元素并将指针向后移动一位</strong>。调用次数不能超过 <code>hasNext()</code> 的次数，否则抛 <code>NoSuchElementException</code>。</p>
</blockquote>
<p><strong>增强 for（语法糖）</strong>：</p>
<pre><code>for (String s : list) {          // 内部就是 Iterator
    System.out.println(s);
}
</code></pre>
<h3>四、List 接口</h3>
<blockquote>
<p><code>List</code> 因为<strong>支持索引</strong>，所以在 <code>Collection</code> 基础上多了一组"索引操作"的独特 API。</p>
</blockquote>
<table>
<thead>
<tr>
<th>方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>void add(int index, E element)</code></td>
<td>在集合中的<strong>指定位置</strong>插入指定元素</td>
</tr>
<tr>
<td><code>E remove(int index)</code></td>
<td>删除指定索引处的元素，返回被删除的元素</td>
</tr>
<tr>
<td><code>E set(int index, E element)</code></td>
<td>修改指定索引处的元素，返回被修改的元素</td>
</tr>
<tr>
<td><code>E get(int index)</code></td>
<td>返回指定索引处的元素</td>
</tr>
</tbody>
</table>
<p><strong>List 接口的 5 种遍历方式</strong>：迭代器 <code>Iterator</code>、增强 for、foreach 方法、普通 for 循环、<strong>列表迭代器 <code>ListIterator</code></strong>。</p>
<h4>4.1 并发修改异常</h4>
<blockquote>
<p>使用迭代器遍历集合的过程中，<strong>调用了集合对象的 <code>add</code> / <code>remove</code> 方法</strong>，就会出现 <code>ConcurrentModificationException</code>（并发修改异常）。</p>
</blockquote>
<table>
<thead>
<tr>
<th>想要的操作</th>
<th>正确做法</th>
</tr>
</thead>
<tbody>
<tr>
<td>迭代过程中做&lt;span style="color:red"&gt;删除&lt;/span&gt;</td>
<td>使用 <code>Iterator</code> 自带的 <code>remove</code> 方法</td>
</tr>
<tr>
<td>迭代过程中做&lt;span style="color:green"&gt;添加&lt;/span&gt;</td>
<td>使用 <code>ListIterator</code> 自带的 <code>add</code> 方法</td>
</tr>
</tbody>
</table>
<pre><code>// 错误写法：会抛 ConcurrentModificationException
Iterator&lt;String&gt; it = list.iterator();
while (it.hasNext()) {
    String s = it.next();
    if ("xxx".equals(s)) {
        list.remove(s);          // ❌
    }
}

// 正确写法：用迭代器自己的 remove()
Iterator&lt;String&gt; it = list.iterator();
while (it.hasNext()) {
    String s = it.next();
    if ("xxx".equals(s)) {
        it.remove();             // ✅
    }
}
</code></pre>
<h3>五、数据结构</h3>
<blockquote>
<p>集合的"底层"就是数据结构：<code>ArrayList</code> 用数组，<code>LinkedList</code> 用链表，<code>Stack</code> / <code>Queue</code> 用栈 / 队列。理解数据结构才能选对集合。</p>
</blockquote>
<h4>5.1 栈（Stack）</h4>
<blockquote>
<p>栈是一种 <strong>&lt;span style="color:blue"&gt;先进后出&lt;/span&gt;（后进先出）</strong> 的线性表，像弹匣——<strong>只有一端开口</strong>（栈顶），另一端封闭（栈底）。</p>
</blockquote>
<table>
<thead>
<tr>
<th>操作</th>
<th>别名</th>
<th>方向</th>
</tr>
</thead>
<tbody>
<tr>
<td>入栈</td>
<td>压栈 / push</td>
<td>从栈顶进入</td>
</tr>
<tr>
<td>出栈</td>
<td>弹栈 / pop</td>
<td>从栈顶出去</td>
</tr>
</tbody>
</table>
<pre><code>flowchart TB
  A["数据A"] --&gt;|压栈| B["数据A 数据B"]
  B --&gt;|压栈| C["数据A 数据B 数据C"]
  C --&gt;|弹栈| B
  B --&gt;|弹栈| A
</code></pre>
<h4>5.2 队列（Queue）</h4>
<blockquote>
<p>队列是 <strong>&lt;span style="color:blue"&gt;先进先出&lt;/span&gt;</strong> 的线性表，像排队——<strong>两端都开口</strong>。</p>
</blockquote>
<table>
<thead>
<tr>
<th>位置</th>
<th>操作</th>
<th>方向</th>
</tr>
</thead>
<tbody>
<tr>
<td>后端（rear）</td>
<td>入队 / offer</td>
<td>元素<strong>从后端进入</strong></td>
</tr>
<tr>
<td>前端（front）</td>
<td>出队 / poll</td>
<td>元素<strong>从前端出去</strong></td>
</tr>
</tbody>
</table>
<h4>5.3 数组（Array）</h4>
<blockquote>
<p>数组在内存中<strong>连续存储</strong>，每个元素有固定下标（索引）。</p>
</blockquote>
<table>
<thead>
<tr>
<th>优点</th>
<th>缺点</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:green"&gt;查询速度快&lt;/span&gt;：通过地址值 + 索引定位，查询任意数据耗时相同</td>
<td>&lt;span style="color:red"&gt;增删效率低&lt;/span&gt;：新增 / 删除数据时，可能大批量移动数组中其他元素</td>
</tr>
</tbody>
</table>
<h4>5.4 链表（Linked List）</h4>
<blockquote>
<p>链表中的结点是<strong>独立的内存块</strong>，结点之间通过"下一个结点地址"串联，<strong>内存不连续</strong>。</p>
</blockquote>
<table>
<thead>
<tr>
<th>优点</th>
<th>缺点</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:green"&gt;增删相对快&lt;/span&gt;：只需要改变相邻结点的地址指向</td>
<td>&lt;span style="color:red"&gt;查询慢&lt;/span&gt;：无论查哪个数据都要从头开始找</td>
</tr>
</tbody>
</table>
<p><strong>单向链表 vs 双向链表</strong>：</p>
<table>
<thead>
<tr>
<th>类型</th>
<th>存储内容</th>
<th>遍历方向</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:blue"&gt;单向链表&lt;/span&gt;</td>
<td>数据 + 下一个结点地址</td>
<td>只能从头到尾</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;双向链表&lt;/span&gt;</td>
<td>前一个结点地址 + 数据 + 下一个结点地址</td>
<td>可前后双向，<strong>查找第 N 个元素</strong>更灵活</td>
</tr>
</tbody>
</table>
<p><strong>链表的插入过程</strong>（在数据 A、C 之间插入 B）：</p>
<ol>
<li>数据 B 对应的下一个数据地址指向数据 C</li>
<li>数据 A 对应的下一个数据地址指向数据 B</li>
</ol>
<h3>六、ArrayList 类</h3>
<blockquote>
<p><code>ArrayList</code> 底层是<strong>数组</strong>实现的，因此<strong>根据索引查询元素快，增删相对慢</strong>。</p>
</blockquote>
<h4>6.1 长度可变原理</h4>
<pre><code>flowchart LR
  S1["1. 底层创建 长度 10 的空数组"] --&gt; S2["2. 存满后扩容 原数组 × 1.5"]
  S2 --&gt; S3["3. 拷贝原数据 到新数组"]
  S3 --&gt; S4["4. 添加新元素 到新数组"]
</code></pre>
<p><strong>四步概括</strong>：① 初始长度 10 的空数组 → ② 存满后扩容 1.5 倍 → ③ 旧数据拷贝到新数组 → ④ 新元素添加到新数组。</p>
<h4>6.2 源码解析</h4>
<table>
<thead>
<tr>
<th>步骤</th>
<th>行为</th>
</tr>
</thead>
<tbody>
<tr>
<td>①</td>
<td>使用<strong>空参构造器</strong>创建的集合，底层会创建一个<strong>默认长度为 0</strong> 的数组</td>
</tr>
<tr>
<td>②</td>
<td><strong>添加第一个元素</strong>时，底层会创建一个<strong>新的长度为 10</strong> 的数组</td>
</tr>
<tr>
<td>③</td>
<td>数组<strong>存满</strong>时，会<strong>扩容 1.5 倍</strong></td>
</tr>
</tbody>
</table>
<h4>6.3 LinkedList 类</h4>
<blockquote>
<p><code>LinkedList</code> 底层基于<strong>双链表</strong>实现：<strong>查询元素慢，增删首尾元素非常快</strong>。</p>
</blockquote>
<table>
<thead>
<tr>
<th>特有方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>public void addFirst(E e)</code></td>
<td>在该列表<strong>开头</strong>插入指定的元素</td>
</tr>
<tr>
<td><code>public void addLast(E e)</code></td>
<td>将指定的元素<strong>追加到此列表的末尾</strong></td>
</tr>
<tr>
<td><code>public E getFirst()</code></td>
<td>返回此列表中的<strong>第一个</strong>元素</td>
</tr>
<tr>
<td><code>public E getLast()</code></td>
<td>返回此列表中的<strong>最后一个</strong>元素</td>
</tr>
<tr>
<td><code>public E removeFirst()</code></td>
<td>从此列表中<strong>删除并返回第一个</strong>元素</td>
</tr>
<tr>
<td><code>public E removeLast()</code></td>
<td>从此列表中<strong>删除并返回最后一个</strong>元素</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:orange"&gt;思考&lt;/span&gt;：<code>LinkedList</code> 也有 <code>get(int index)</code> 方法，<strong>表面看起来是根据索引获取元素，实际上要从头 / 尾开始逐个查找</strong>，所以查询性能差；如果大量查询请用 <code>ArrayList</code>，大量首尾增删请用 <code>LinkedList</code>。</p>
</blockquote>
<h3>七、泛型（Generics）</h3>
<blockquote>
<p><strong>JDK5 引入</strong>的特性，可以在<strong>编译阶段</strong>约束操作的数据类型，并进行检查。</p>
</blockquote>
<h4>7.1 泛型的好处</h4>
<ol>
<li><strong>统一数据类型</strong></li>
<li><strong>将运行期的错误提升到编译期</strong>（编译时就能发现类型不匹配）</li>
</ol>
<blockquote>
<p>&lt;span style="color:red"&gt;⚠ 注意事项&lt;/span&gt;：<strong>泛型中只能编写引用数据类型</strong>，不能写基本类型（要用包装类：<code>int</code> → <code>Integer</code>、<code>double</code> → <code>Double</code>）。</p>
</blockquote>
<pre><code>// ❌ 错误
ArrayList&lt;int&gt; list = new ArrayList&lt;&gt;();

// ✅ 正确
ArrayList&lt;Integer&gt; list = new ArrayList&lt;&gt;();
</code></pre>
<h4>7.2 泛型类</h4>
<blockquote>
<p>在类名后用 <code>&lt;E&gt;</code> 声明类型占位符，类中即可用 <code>E</code> 表示"某种类型"。</p>
</blockquote>
<pre><code>public class ArrayList&lt;E&gt; {
    public boolean add(E e) { ... }
}
</code></pre>
<h4>7.3 泛型方法</h4>
<table>
<thead>
<tr>
<th>场景</th>
<th>泛型声明方式</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:blue"&gt;非静态方法&lt;/span&gt;</td>
<td>泛型<strong>根据类的泛型</strong>去匹配</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;静态方法&lt;/span&gt;</td>
<td>需要<strong>自己声明独立的泛型</strong>（静态先于对象存在，无法用类的泛型）</td>
</tr>
</tbody>
</table>
<pre><code>// 非静态：使用类上声明的 E
public class ArrayList&lt;E&gt; {
    public boolean add(E e) { ... }
}

// 静态：必须自己声明 &lt;T&gt;
public static &lt;T&gt; void printArray(T[] array) { ... }
</code></pre>
<h4>7.4 泛型接口</h4>
<blockquote>
<p>类实现带泛型的接口时，有两种操作方式：</p>
</blockquote>
<ol>
<li><strong>实现接口时直接确定类型</strong>：<code>public class ArrayList&lt;E&gt; implements List&lt;E&gt;</code></li>
<li><strong>保持接口的泛型，等创建对象时再确定</strong>：<code>public class ArrayList&lt;E&gt; implements List&lt;E&gt;</code></li>
</ol>
<h4>7.5 泛型通配符 <code>?</code></h4>
<blockquote>
<p>用于<strong>方法形参</strong>接收"任意类型"的集合，常配合上下限 <code>? extends T</code> / <code>? super T</code> 使用（进阶内容，留到后续章节）。</p>
</blockquote>
<h3>集合高级（2）</h3>
<blockquote>
<p>本篇聚焦 <code>Set</code> 家族（<code>TreeSet</code> / <code>HashSet</code> / <code>LinkedHashSet</code>）和 <code>Map</code> 家族（<code>HashMap</code> / <code>LinkedHashMap</code> / <code>TreeMap</code>）的实现原理、API 与遍历方式，以及 <code>Collections</code> 工具类的常用方法（含<strong>可变参数</strong>）。</p>
</blockquote>
<h3>一、Set 集合介绍</h3>
<blockquote>
<p><code>Set</code> 接口是 <code>Collection</code> 的子接口。<code>Set</code> 实现类有 3 个：<code>TreeSet</code> / <code>HashSet</code> / <code>LinkedHashSet</code>，整体特点是：<strong>存取顺序不一致，没有跟索引有关的方法，不能存储重复元素</strong>。</p>
</blockquote>
<table>
<thead>
<tr>
<th>实现类</th>
<th>特点</th>
<th>底层</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:blue"&gt;TreeSet&lt;/span&gt;</td>
<td>&lt;span style="color:green"&gt;元素排序&lt;/span&gt;</td>
<td>红黑树</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;HashSet&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;元素唯一&lt;/span&gt;（不保证顺序）</td>
<td>哈希表</td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;LinkedHashSet&lt;/span&gt;</td>
<td>元素唯一，&lt;span style="color:green"&gt;保证存取顺序&lt;/span&gt;</td>
<td>哈希表 + 双向链表</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意&lt;/span&gt;：<code>Set</code> 没有索引，三种通用遍历方式（迭代器 / 增强 for / foreach）都可用，<strong>普通 for 循环不可用</strong>。</p>
</blockquote>
<h3>二、TreeSet 集合</h3>
<blockquote>
<p><code>TreeSet</code> 特点是元素<strong>按规则排序</strong>（默认自然排序 / 自定义比较器），本质底层是<strong>红黑树</strong>（一种自平衡的二叉查找树）。</p>
</blockquote>
<h4>2.1 底层数据结构演进</h4>
<table>
<thead>
<tr>
<th>数据结构</th>
<th>规则</th>
<th>缺点</th>
</tr>
</thead>
<tbody>
<tr>
<td>普通的二叉树</td>
<td>父结点只跟子结点相连</td>
<td>退化成链表时查询效率 O(n)</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;二叉查找树&lt;/span&gt;（二叉排序树）</td>
<td>小的存左边，大的存右边，一样的不存</td>
<td>数据倾斜时仍可能退化成链表</td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;平衡二叉树&lt;/span&gt;</td>
<td>在满足查找二叉树规则下，让树尽可能矮小，提高查询性能</td>
<td>频繁旋转开销大</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;红黑树&lt;/span&gt;（TreeSet 实际用）</td>
<td>近似平衡的二叉查找树，规则更灵活，旋转更少</td>
<td>—</td>
</tr>
</tbody>
</table>
<h4>2.2 两种排序方式</h4>
<blockquote>
<p><code>TreeSet</code> 支持两种排序方式：<strong>自然排序</strong>（元素自身实现 <code>Comparable</code>）和<strong>比较器排序</strong>（构造时传入 <code>Comparator</code>）。</p>
</blockquote>
<p><strong>自然排序三步骤</strong>：</p>
<ol>
<li>类实现 <code>Comparable</code> 接口</li>
<li>重写 <code>compareTo</code> 方法</li>
<li>根据 <code>compareTo</code> 返回值组织排序规则：&lt;span style="color:red"&gt;负数：左边走&lt;/span&gt;、&lt;span style="color:green"&gt;正数：右边走&lt;/span&gt;、<strong>0：不存</strong></li>
</ol>
<pre><code>public class Student implements Comparable&lt;Student&gt; {
    @Override
    public int compareTo(Student o) {
        return this.age - o.age;   // 按年龄升序
    }
}
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意&lt;/span&gt;：<code>TreeSet</code> 取出的顺序是<strong>左中右</strong>（中序遍历），<code>张三, 23 李四, 24 王五, 25 赵六, 26</code>。</p>
</blockquote>
<p><strong>比较器排序</strong>：当<strong>同具备自然排序和比较器排序</strong>时，<strong>会优先按照比较器进行排序操作</strong>。当 <code>String</code> / <code>Integer</code> / <code>Double</code> 等内置自然排序规则<strong>不符合需求</strong>时（如 <code>String</code> 是字典序，需要按长度排），使用比较器排序。</p>
<h3>三、HashSet 集合</h3>
<blockquote>
<p><code>HashSet</code> 特点：&lt;span style="color:green"&gt;元素唯一&lt;/span&gt;、不保证顺序。底层是<strong>哈希表</strong>，因此<strong>存取速度快</strong>。</p>
</blockquote>
<h4>3.1 <code>hashCode</code> 与 <code>equals</code> 配合流程</h4>
<blockquote>
<p>哈希值是一个 <code>int</code> 类型的随机值，Java 中每个对象都有一个哈希值（由 <code>Object.hashCode()</code> 底层 C++ 计算）。<strong>自定义对象</strong>必须按"内容"实现 <code>hashCode</code> + <code>equals</code>，否则重复元素识别不出来。</p>
</blockquote>
<p><strong><code>hashCode</code> 的作用</strong>：定位元素应存入哪个桶（数组下标）；<strong><code>equals</code> 的作用</strong>：同一桶内的元素<strong>去重判断</strong>。</p>
<pre><code>@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Student student = (Student) o;
    return age == student.age &amp;&amp; Objects.equals(name, student.name);
}

@Override
public int hashCode() {
    return Objects.hash(name, age);
}
</code></pre>
<blockquote>
<p>&lt;span style="color:red"&gt;⚠ 关键点&lt;/span&gt;：根据对象内容生成 hash 值，<strong>不同的对象 hash 值也可能相等</strong>（如 <code>"重地"</code> 和 <code>"通话"</code>），这种现象叫 &lt;span style="color:red"&gt;<strong>哈希碰撞</strong>&lt;/span&gt;。</p>
</blockquote>
<h4>3.2 HashSet 底层原理</h4>
<blockquote>
<p><code>HashSet</code> 底层就是 <code>HashMap</code>，存储流程基于<strong>哈希表</strong>（JDK 8 前后结构不同）。</p>
</blockquote>
<table>
<thead>
<tr>
<th>版本</th>
<th>底层结构</th>
</tr>
</thead>
<tbody>
<tr>
<td>JDK 8 之前</td>
<td>&lt;span style="color:blue"&gt;数组 + 链表&lt;/span&gt;</td>
</tr>
<tr>
<td>JDK 8 之后</td>
<td>&lt;span style="color:green"&gt;数组 + 链表 + 红黑树&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<p><strong>哈希表存储数据的详细流程</strong>：</p>
<pre><code>flowchart TD
  S1["① 创建默认长度16的数组（加载因子 0.75）"] --&gt; S2["② 用元素的哈希值跟数组长度 计算应存入的位置"]
  S2 --&gt; S3{"③ 该位置是否为 null?"}
  S3 --&gt;|是 null| S4["直接存入"]
  S3 --&gt;|不为 null| S5["④ 调用 equals 比较"]
  S5 --&gt;|相等| S6["不存"]
  S5 --&gt;|不相等| S7["存入（挂到链表中）"]
  S4 --&gt; S8["⑤ 存满到 16×0.75=12 时 自动扩容为原长度的 2 倍"]
</code></pre>
<table>
<thead>
<tr>
<th>关键参数</th>
<th>默认值</th>
</tr>
</thead>
<tbody>
<tr>
<td>初始数组长度</td>
<td>16</td>
</tr>
<tr>
<td>加载因子</td>
<td>0.75</td>
</tr>
<tr>
<td>扩容触发点</td>
<td>元素数 = 16 × 0.75 = 12</td>
</tr>
<tr>
<td>扩容规则</td>
<td>每次扩为原来的 2 倍</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意&lt;/span&gt;：JDK 8 之前是<strong>新元素占老元素位置、老元素挂下面</strong>；JDK 8 之后是<strong>新元素直接挂在老元素下面</strong>。当<strong>链表长度超过 8 且数组长度 ≥ 64</strong>时，链表自动转成<strong>红黑树</strong>提升查询性能。</p>
</blockquote>
<h3>四、LinkedHashSet 集合</h3>
<blockquote>
<p><code>LinkedHashSet</code> <strong>依然是基于哈希表</strong>（数组 + 链表 + 红黑树）实现的，<strong>但每个元素额外多了一个双向链表</strong>机制记录前后元素位置——所以<strong>保证存取顺序</strong>。</p>
</blockquote>
<ul>
<li>元素唯一 ✔（继承自 <code>HashSet</code>）</li>
<li>保证存取顺序 ✔（多了一条双向链表）</li>
<li>效率&lt;span style="color:red"&gt;低于&lt;/span&gt;<code>HashSet</code>（多维护一条链表）</li>
</ul>
<h3>五、Collections 工具类</h3>
<blockquote>
<p><code>java.util.Collections</code> 是<strong>集合工具类</strong>，提供操作集合的静态方法。</p>
</blockquote>
<h4>5.1 可变参数（Varargs）</h4>
<blockquote>
<p>可变参数用在形参中可以接收多个数据，本质在方法内部就是一个<strong>数组</strong>。</p>
</blockquote>
<table>
<thead>
<tr>
<th>格式</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>数据类型...参数名称</code></td>
<td>只能写在形参列表中</td>
</tr>
</tbody>
</table>
<p><strong>调用方式灵活</strong>：可以不传参数、传 1 个或多个、传一个数组。</p>
<blockquote>
<p>&lt;span style="color:red"&gt;⚠ 注意事项&lt;/span&gt;：</p>
<ol>
<li><strong>一个形参列表中可变参数只能有一个</strong></li>
<li><strong>可变参数必须放在形参列表的最&lt;span style="color:red"&gt;后面&lt;/span&gt;</strong></li>
</ol>
</blockquote>
<pre><code>public static int sum(int... nums) {     // 本质 int[] nums
    int total = 0;
    for (int n : nums) total += n;
    return total;
}

sum();              // 0
sum(1, 2, 3);       // 6
sum(new int[]{1,2});// 3
</code></pre>
<h3>六、Map 集合介绍</h3>
<blockquote>
<p><code>Map</code> 是&lt;span style="color:green"&gt;键值对集合&lt;/span&gt;，<strong>一次添加两个元素</strong>。需要存储<strong>一对应</strong>的数据时，优先考虑 <code>Map</code>。</p>
</blockquote>
<table>
<thead>
<tr>
<th>实现类</th>
<th>键的特点</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:blue"&gt;HashMap&lt;/span&gt;</td>
<td>按键&lt;span style="color:blue"&gt;无序&lt;/span&gt;，不重复，无索引</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;LinkedHashMap&lt;/span&gt;</td>
<td>按键&lt;span style="color:green"&gt;有序&lt;/span&gt;，不重复，无索引</td>
</tr>
<tr>
<td>&lt;span style="color:orange"&gt;TreeMap&lt;/span&gt;</td>
<td>按键&lt;span style="color:orange"&gt;排序&lt;/span&gt;，不重复，无索引</td>
</tr>
</tbody>
</table>
<blockquote>
<p>三个实现类的值<strong>都不做要求</strong>（可重复、可为 null）。</p>
</blockquote>
<h4>6.1 常见 API</h4>
<blockquote>
<p><code>Map</code> 是双列集合的<strong>顶层接口</strong>，它的功能是<strong>全部双列集合</strong>都可以使用的。</p>
</blockquote>
<table>
<thead>
<tr>
<th>方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>V put(K key, V value)</code></td>
<td>添加元素</td>
</tr>
<tr>
<td><code>V remove(Object key)</code></td>
<td>根据键删除键值对元素</td>
</tr>
<tr>
<td><code>void clear()</code></td>
<td>移除所有的键值对元素</td>
</tr>
<tr>
<td><code>boolean containsKey(Object key)</code></td>
<td>判断集合是否包含指定的<strong>键</strong></td>
</tr>
<tr>
<td><code>boolean containsValue(Object value)</code></td>
<td>判断集合是否包含指定的<strong>值</strong></td>
</tr>
<tr>
<td><code>boolean isEmpty()</code></td>
<td>判断集合是否为空</td>
</tr>
<tr>
<td><code>int size()</code></td>
<td>集合的长度（键值对的个数）</td>
</tr>
</tbody>
</table>
<h4>6.2 三种遍历方式</h4>
<table>
<thead>
<tr>
<th>方式</th>
<th>核心方法</th>
<th>适用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:blue"&gt;通过键找值&lt;/span&gt;</td>
<td><code>keySet()</code> + <code>get(key)</code></td>
<td>只想用键的遍历</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;通过键值对对象&lt;/span&gt;</td>
<td><code>entrySet()</code> + <code>Map.Entry</code></td>
<td>同时拿键和值（推荐）</td>
</tr>
<tr>
<td>&lt;span style="color:orange"&gt;通过 <code>forEach</code>&lt;/span&gt;</td>
<td><code>forEach(BiConsumer)</code></td>
<td>Lambda 风格</td>
</tr>
</tbody>
</table>
<p><strong>方式 1：通过键找值</strong>（分 3 步）</p>
<pre><code>Set&lt;String&gt; keys = map.keySet();          // ① 获取所有键
for (String key : keys) {                 // ② 遍历 Set 集合
    String value = map.get(key);          // ③ 根据键找值
    System.out.println(key + "=" + value);
}
</code></pre>
<p><strong>方式 2：通过键值对对象</strong>（核心是 <code>Map.Entry</code>）</p>
<pre><code>// Map.Entry 内部接口的方法
K getKey();      // 获取键
V getValue();    // 获取值

Set&lt;Map.Entry&lt;String, String&gt;&gt; entries = map.entrySet();
for (Map.Entry&lt;String, String&gt; entry : entries) {
    System.out.println(entry.getKey() + "=" + entry.getValue());
}
</code></pre>
<p><strong>方式 3：通过 <code>forEach</code></strong>（Lambda）</p>
<pre><code>map.forEach((key, value) -&gt; System.out.println(key + "=" + value));
</code></pre>
<h3>七、Map 集合的实现类</h3>
<table>
<thead>
<tr>
<th>实现类</th>
<th>键的底层</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:blue"&gt;TreeMap&lt;/span&gt;</td>
<td>红黑树</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;HashMap&lt;/span&gt;</td>
<td>哈希表</td>
</tr>
<tr>
<td>&lt;span style="color:orange"&gt;LinkedHashMap&lt;/span&gt;</td>
<td>哈希表 + 双向链表</td>
</tr>
</tbody>
</table>
<h4>7.1 HashMap 底层原理</h4>
<blockquote>
<p><code>HashMap</code> 与 <code>HashSet</code> 底层结构<strong>完全相同</strong>：哈希表。</p>
</blockquote>
<table>
<thead>
<tr>
<th>版本</th>
<th>结构</th>
<th>重要升级</th>
</tr>
</thead>
<tbody>
<tr>
<td>JDK 8 之前</td>
<td>数组 + 链表</td>
<td>—</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;JDK 8 开始&lt;/span&gt;</td>
<td>数组 + 链表 + &lt;span style="color:green"&gt;红黑树&lt;/span&gt;</td>
<td>链表长度 &gt; 8 且数组长度 ≥ 64 时自动转红黑树</td>
</tr>
</tbody>
</table>
<p><strong><code>map.put("键", "值")</code> 的存储过程</strong>：</p>
<pre><code>flowchart LR
  A["利用键计算哈希值 跟值无关"] --&gt; B["根据哈希值与数组长度 计算应存入位置"]
  B --&gt; C{"该位置是否 为 null?"}
  C --&gt;|是| D["Entry 对象直接存入"]
  C --&gt;|否| E["调用 equals 比较"]
  E --&gt;|相等| F["值覆盖"]
  E --&gt;|不相等| G["挂到链表中"]
</code></pre>
<h4>7.2 LinkedHashMap</h4>
<p>底层是<strong>哈希表 + 双向链表</strong>，与 <code>LinkedHashSet</code> 同理——哈希表保证键唯一，双向链表保证<strong>存取顺序</strong>。</p>
<h4>7.3 TreeMap</h4>
<p>底层是<strong>红黑树</strong>，键按<strong>排序</strong>组织。<code>Comparable</code> / <code>Comparator</code> 两种排序方式与 <code>TreeSet</code> 一致。</p>
<h3>八、Set / List / Map 选择速查</h3>
<table>
<thead>
<tr>
<th>需求</th>
<th>推荐集合</th>
<th>底层</th>
</tr>
</thead>
<tbody>
<tr>
<td>元素可重复</td>
<td>&lt;span style="color:green"&gt;ArrayList&lt;/span&gt;（用的最多）</td>
<td>数组</td>
</tr>
<tr>
<td>元素可重复，&lt;span style="color:red"&gt;增删明显多于查询&lt;/span&gt;</td>
<td>LinkedList</td>
<td>链表</td>
</tr>
<tr>
<td>元素去重</td>
<td>&lt;span style="color:green"&gt;HashSet&lt;/span&gt;（用的最多）</td>
<td>哈希表</td>
</tr>
<tr>
<td>元素去重，且&lt;span style="color:green"&gt;保证存取顺序&lt;/span&gt;</td>
<td>LinkedHashSet</td>
<td>哈希表 + 双向链表</td>
</tr>
<tr>
<td>元素&lt;span style="color:orange"&gt;排序&lt;/span&gt;</td>
<td>TreeSet</td>
<td>红黑树</td>
</tr>
</tbody>
</table>
<h3>九、练习：统计字符出现次数</h3>
<blockquote>
<p><strong>需求</strong>：字符串 <code>aababcabcdabcde</code>，统计每个字符出现次数，按 <code>a(5)b(4)c(3)d(2)e(1)</code> 格式输出。</p>
</blockquote>
<pre><code>public class CharCount {
    public static void main(String[] args) {
        String s = "aababcabcdabcde";
        Map&lt;Character, Integer&gt; map = new LinkedHashMap&lt;&gt;(); // 保证输出顺序
        for (int i = 0; i &lt; s.length(); i++) {
            char c = s.charAt(i);
            map.merge(c, 1, Integer::sum);     // 简化 put 逻辑
        }

        StringBuilder sb = new StringBuilder();
        map.forEach((k, v) -&gt; sb.append(k).append('(').append(v).append(')'));
        System.out.println(sb);                 // a(5)b(4)c(3)d(2)e(1)
    }
}
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意&lt;/span&gt;：用 <code>LinkedHashMap</code> 而不是 <code>HashMap</code>，是为了<strong>保证输出顺序与首次出现顺序一致</strong>。</p>
</blockquote>
<h2>第十三章：Stream 流</h2>
<blockquote>
<p>&lt;span style="color:blue"&gt;Stream 流&lt;/span&gt; 配合 <strong>Lambda 表达式</strong>使用，可以&lt;span style="color:green"&gt;简化集合和数组操作&lt;/span&gt;。其思想类似"工厂流水线"：<strong>原料（数据）→ 加工（中间方法）→ 出厂（终结方法）</strong>，每一步都可以灵活组合，写出<strong>链式编程</strong>风格的代码。</p>
</blockquote>
<h3>一、Stream 流思想三步骤</h3>
<pre><code>flowchart LR
  A["① 将数据到流中 （获取流对象）"] --&gt; B["② 中间方法 过滤/转换/去重..."]
  B --&gt; C["③ 终结方法 遍历/统计/收集"]
</code></pre>
<table>
<thead>
<tr>
<th>步骤</th>
<th>关键点</th>
</tr>
</thead>
<tbody>
<tr>
<td>① 获取流对象</td>
<td>把集合/数组/零散数据"上"到流水线</td>
</tr>
<tr>
<td>② 中间方法</td>
<td>调用后<strong>返回新的 Stream</strong>，可继续链式调用</td>
</tr>
<tr>
<td>③ 终结方法</td>
<td>调用后&lt;span style="color:red"&gt;流被消费&lt;/span&gt;，链式调用结束</td>
</tr>
</tbody>
</table>
<h3>二、获取 Stream 流对象</h3>
<blockquote>
<p>共有 3 种获取方式，根据数据来源选择。</p>
</blockquote>
<table>
<thead>
<tr>
<th>数据来源</th>
<th>调用方式</th>
<th>方法签名</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:blue"&gt;集合&lt;/span&gt;</td>
<td><code>Collection</code> 接口中的<strong>默认方法</strong></td>
<td><code>default Stream&lt;E&gt; stream()</code></td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;数组&lt;/span&gt;</td>
<td><code>Arrays</code> 工具类的<strong>静态方法</strong></td>
<td><code>static &lt;T&gt; Stream&lt;T&gt; stream(T[] array)</code></td>
</tr>
<tr>
<td>&lt;span style="color:orange"&gt;零散数据&lt;/span&gt;</td>
<td><code>Stream</code> 接口中的<strong>静态方法</strong></td>
<td><code>static &lt;T&gt; Stream&lt;T&gt; of(T... values)</code></td>
</tr>
</tbody>
</table>
<pre><code>// ① 集合
List&lt;String&gt; list = ...;
Stream&lt;String&gt; s1 = list.stream();

// ② 数组
Integer[] arr = {1, 2, 3};
Stream&lt;Integer&gt; s2 = Arrays.stream(arr);

// ③ 零散数据
Stream&lt;String&gt; s3 = Stream.of("a", "b", "c");
</code></pre>
<h3>三、Stream 中间操作方法</h3>
<blockquote>
<p>中间方法调用后返回<strong>新的 Stream</strong>，支持链式编程。</p>
</blockquote>
<table>
<thead>
<tr>
<th>方法签名</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Stream&lt;T&gt; filter(Predicate&lt;? super T&gt; predicate)</code></td>
<td>用于对流中的数据进行&lt;span style="color:green"&gt;过滤&lt;/span&gt;</td>
</tr>
<tr>
<td><code>Stream&lt;T&gt; limit(long maxSize)</code></td>
<td>获取前几个元素</td>
</tr>
<tr>
<td><code>Stream&lt;T&gt; skip(long n)</code></td>
<td>跳过前几个元素</td>
</tr>
<tr>
<td><code>Stream&lt;T&gt; distinct()</code></td>
<td>去除流中重复的元素（依赖 <code>hashCode</code> 和 <code>equals</code>）</td>
</tr>
<tr>
<td><code>static &lt;T&gt; Stream&lt;T&gt; concat(Stream a, Stream b)</code></td>
<td>&lt;span style="color:orange"&gt;合并&lt;/span&gt; a 和 b 两个流为一个流</td>
</tr>
<tr>
<td><code>Stream&lt;R&gt; map(Function&lt;? super T, ? extends R&gt; mapper)</code></td>
<td>对流中的每一个元素进行<strong>转换</strong></td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:red"&gt;⚠ 注意事项&lt;/span&gt;：<strong>如果流对象已经被消费过，就不允许再次使用</strong>。每个流对象<strong>只能终结一次</strong>，多次调用终结方法会抛 <code>IllegalStateException</code>。</p>
</blockquote>
<pre><code>// 链式编程：用 limit + concat + distinct 一气呵成
Stream&lt;String&gt; s1 = list.stream().limit(4);
Stream&lt;String&gt; s2 = list.stream().skip(2);
Stream&lt;String&gt; s3 = Stream.concat(s1, s2);
s3.distinct().forEach(System.out::println);   // 消费 s3 后流就关闭
</code></pre>
<h3>四、Stream 终结操作方法</h3>
<blockquote>
<p>终结方法调用后<strong>流被关闭</strong>，链式结束。</p>
</blockquote>
<table>
<thead>
<tr>
<th>方法签名</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>void forEach(Consumer action)</code></td>
<td>对此流的每个元素执行<strong>遍历</strong>操作</td>
</tr>
<tr>
<td><code>long count()</code></td>
<td>返回此流中的<strong>元素数</strong></td>
</tr>
</tbody>
</table>
<h3>五、Stream 收集操作</h3>
<blockquote>
<p>Stream 流操作<strong>不会修改数据源</strong>，操作完成后若需要重新得到集合/数组，需要用 <code>collect</code> 方法。</p>
</blockquote>
<pre><code>// 把流中的数据转回集合
R collect(Collector collector);
</code></pre>
<p><code>Collectors</code> 工具类提供 3 种常用收集方式：</p>
<table>
<thead>
<tr>
<th>静态方法</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>public static &lt;T&gt; Collector toList()</code></td>
<td>把元素收集到 &lt;span style="color:blue"&gt;List&lt;/span&gt; 集合中</td>
</tr>
<tr>
<td><code>public static &lt;T&gt; Collector toSet()</code></td>
<td>把元素收集到 &lt;span style="color:green"&gt;Set&lt;/span&gt; 集合中</td>
</tr>
<tr>
<td><code>public static Collector toMap(Function keyMapper, Function valueMapper)</code></td>
<td>把元素收集到 &lt;span style="color:orange"&gt;Map&lt;/span&gt; 集合中</td>
</tr>
</tbody>
</table>
<h4>5.1 收集到 Map 示例</h4>
<blockquote>
<p><strong>需求</strong>：将 <code>ArrayList</code> 中的 <code>"zhangsan,23"</code> / <code>"lisi,24"</code> / <code>"wangwu,25"</code> 收集到 <code>Map&lt;姓名, 年龄&gt;</code>。</p>
</blockquote>
<pre><code>ArrayList&lt;String&gt; list = new ArrayList&lt;&gt;();
Collections.addAll(list, "zhangsan,23", "lisi,24", "wangwu,25");

Map&lt;String, Integer&gt; map = list.stream()
        .collect(Collectors.toMap(
                s -&gt; s.split(",")[0],   // 键：姓名
                s -&gt; Integer.parseInt(s.split(",")[1])  // 值：年龄
        ));
// {zhangsan=23, lisi=24, wangwu=25}
</code></pre>
<h4>5.2 综合练习：演员信息处理</h4>
<blockquote>
<p><strong>需求</strong>：两个 <code>ArrayList</code> 集合分别存储 6 名男演员和 6 名女演员。</p>
<ol>
<li>&lt;span style="color:green"&gt;男演员只要名字为 3 个字的前两人&lt;/span&gt;</li>
<li>&lt;span style="color:green"&gt;女演员只要姓林的，并且不要第一个&lt;/span&gt;</li>
<li>把过滤后的男演员姓名和女演员姓名&lt;span style="color:orange"&gt;合并&lt;/span&gt;到一起</li>
<li>把合并后的元素作为构造方法的参数创建 <code>Actor</code> 对象，遍历数据</li>
<li>演员类 <code>Actor</code> 含一个成员变量、一个带参构造方法、对应 get / set 方法</li>
</ol>
</blockquote>
<pre><code>// 演员类
public class Actor {
    private String name;
    public Actor(String name) { this.name = name; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
}

// 处理
ArrayList&lt;String&gt; males = new ArrayList&lt;&gt;(Arrays.asList(
        "蔡徐坤", "李易峰", "王俊凯", "易烊千玺", "陈伟霆", "邓伦"));
ArrayList&lt;String&gt; females = new ArrayList&lt;&gt;(Arrays.asList(
        "林允儿", "林志玲", "杨幂", "赵丽颖", "刘亦菲", "林心如"));

Stream&lt;String&gt; m = males.stream().filter(s -&gt; s.length() == 3).limit(2);
Stream&lt;String&gt; f = females.stream().filter(s -&gt; s.startsWith("林")).skip(1);
Stream.concat(m, f).map(Actor::new).forEach(a -&gt; System.out.println(a.getName()));
</code></pre>
<h2>第十四章：异常</h2>
<blockquote>
<p>&lt;span style="color:blue"&gt;异常&lt;/span&gt; 指的是程序在<strong>编译或执行过程</strong>中，出现的非正常的情况（错误）。常见如 <code>ArrayIndexOutOfBoundsException</code> / <code>ClassCastException</code> / <code>NullPointerException</code>。<strong>语法错误不是异常</strong>。</p>
</blockquote>
<h3>一、异常体系</h3>
<pre><code>flowchart TD
  T["Throwable"] --&gt; E["Error"]
  T --&gt; X["Exception"]
  X --&gt; R["RuntimeException 及其子类"]
  X --&gt; C["除 RuntimeException 之外 所有的异常"]
</code></pre>
<table>
<thead>
<tr>
<th>父类</th>
<th>子类</th>
<th>性质</th>
<th>举例</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;Error&lt;/span&gt;</td>
<td>严重级别问题</td>
<td>&lt;span style="color:red"&gt;无法处理&lt;/span&gt;</td>
<td><code>StackOverflowError</code>（栈内存溢出）、<code>OutOfMemoryError</code>（堆内存溢出）</td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;Exception&lt;/span&gt;</td>
<td>异常类，程序常见错误</td>
<td>可处理</td>
<td>见下分类</td>
</tr>
</tbody>
</table>
<h4>1.1 Exception 两大类</h4>
<table>
<thead>
<tr>
<th>分类</th>
<th>触发时机</th>
<th>常见异常</th>
<th>原因</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:blue"&gt;运行时异常&lt;/span&gt;（<code>RuntimeException</code> 及其子类）</td>
<td>编译不报错，&lt;span style="color:orange"&gt;运行时可能&lt;/span&gt;出错</td>
<td>数组索引越界 <code>ArrayIndexOutOfBoundsException</code>、空指针 <code>NullPointerException</code>、类型转换 <code>ClassCastException</code></td>
<td>程序员代码&lt;span style="color:red"&gt;不严谨&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;编译时异常&lt;/span&gt;（除 <code>RuntimeException</code> 外）</td>
<td>&lt;span style="color:red"&gt;编译阶段就报错&lt;/span&gt;</td>
<td><code>new FileReader("D:\\A.txt")</code></td>
<td>起到<strong>提醒</strong>作用，需提前处理</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意&lt;/span&gt;：<strong>编译时异常不报错，运行时可能报错</strong>；<strong>编译时异常没继承 RuntimeException，编译阶段就会出错</strong>。</p>
</blockquote>
<h3>二、异常的默认处理流程</h3>
<blockquote>
<p>不做任何处理时，JVM 的默认行为：</p>
</blockquote>
<pre><code>flowchart TD
  A["① 虚拟机在出现异常的代码处 自动创建异常对象"] --&gt; B["② 异常从方法出现的点抛出给调用者 调用者最终抛出给 JVM"]
  B --&gt; C["③ JVM 收到异常后 在控制台输出异常信息"]
  C --&gt; D["④ 终止 Java 程序运行"]
</code></pre>
<h3>三、异常处理方式</h3>
<blockquote>
<p>Java 提供 &lt;span style="color:blue"&gt;2 种&lt;/span&gt; 主动处理方式：<strong>捕获（try...catch）<strong>和</strong>抛出（throws / throw）</strong>。</p>
</blockquote>
<h4>3.1 <code>try...catch</code>：捕获异常</h4>
<blockquote>
<p>优点：<strong>程序可以继续往下执行</strong>。</p>
</blockquote>
<pre><code>try {
    // 可能会出现异常的代码
} catch (异常类型1 变量) {
    // 处理异常的方案
} catch (异常类型2 变量) {
    // 处理异常的方案
}
</code></pre>
<blockquote>
<p>&lt;span style="color:red"&gt;⚠ 关键点&lt;/span&gt;：<strong>如果使用多个 catch，最大的异常需要放在最后</strong>（父类在后）。</p>
</blockquote>
<p><strong><code>Throwable</code> 常见方法</strong>：</p>
<table>
<thead>
<tr>
<th>方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>public String getMessage()</code></td>
<td>获取异常的<strong>错误原因</strong>（简短）</td>
</tr>
<tr>
<td><code>public void printStackTrace()</code></td>
<td>展示<strong>完整的</strong>异常错误信息（最常用）</td>
</tr>
</tbody>
</table>
<h4>3.2 <code>throws</code>：抛出异常</h4>
<blockquote>
<p>用在<strong>方法名后面</strong>，声明"这个方法存在异常，但不做处理，交给调用方处理"。</p>
</blockquote>
<pre><code>public void method() throws 异常1, 异常2, 异常3... { }
</code></pre>
<blockquote>
<p>&lt;span style="color:red"&gt;⚠ 关键点&lt;/span&gt;：<strong>子类重写父类方法时，不能抛出父类没有的异常，或者比父类更大的异常</strong>。</p>
</blockquote>
<h4>3.3 两种方式怎么选</h4>
<table>
<thead>
<tr>
<th>方式</th>
<th>出现问题时</th>
<th>选择依据</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:blue"&gt;try...catch&lt;/span&gt;</td>
<td>程序可以<strong>继续</strong>执行</td>
<td>问题<strong>不需要暴露</strong>给调用者</td>
</tr>
<tr>
<td>&lt;span style="color:orange"&gt;throws 抛出&lt;/span&gt;</td>
<td>程序在<strong>错误点停止</strong></td>
<td>问题<strong>需要暴露</strong>出去</td>
</tr>
</tbody>
</table>
<blockquote>
<p>思路：看这个问题是否需要暴露出来 → &lt;span style="color:green"&gt;需要：抛出&lt;/span&gt;，&lt;span style="color:green"&gt;不需要：try...catch&lt;/span&gt;。</p>
</blockquote>
<h4>3.4 <code>throw</code> vs <code>throws</code></h4>
<table>
<thead>
<tr>
<th>关键字</th>
<th>位置</th>
<th>后面跟</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:blue"&gt;<code>throw</code>&lt;/span&gt;</td>
<td><strong>方法体中</strong></td>
<td>异常对象</td>
<td>抛出异常对象</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;<code>throws</code>&lt;/span&gt;</td>
<td><strong>方法名后面</strong></td>
<td>异常类型</td>
<td>声明方法存在异常</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:orange"&gt;细节&lt;/span&gt;：抛出的异常对象如果是&lt;span style="color:red"&gt;编译时异常&lt;/span&gt;，<strong>必须使用 <code>throws</code> 声明</strong>；如果是&lt;span style="color:green"&gt;运行时异常&lt;/span&gt;，则不需要写 <code>throws</code>。</p>
</blockquote>
<h3>四、自定义异常</h3>
<blockquote>
<p>Java 无法为这个世界上<strong>全部的问题</strong>提供异常类。当企业想用<strong>异常管理某个业务问题</strong>时，需要自定义异常类。</p>
</blockquote>
<h4>4.1 两类自定义异常</h4>
<table>
<thead>
<tr>
<th>类型</th>
<th>父类</th>
<th>步骤</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:blue"&gt;自定义编译时异常&lt;/span&gt;</td>
<td>继承 <code>Exception</code></td>
<td>① 定义类 ② 重写构造器</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;自定义运行时异常&lt;/span&gt;</td>
<td>继承 <code>RuntimeException</code></td>
<td>① 定义类 ② 重写构造器</td>
</tr>
</tbody>
</table>
<h4>4.2 示例：自定义业务异常</h4>
<pre><code>// 1. 自定义运行时异常
public class AgeOutOfRangeException extends RuntimeException {
    public AgeOutOfRangeException() { super(); }
    public AgeOutOfRangeException(String message) { super(message); }
}

// 2. 业务方法中抛出
public void setAge(int age) {
    if (age &lt; 0 || age &gt; 150) {
        throw new AgeOutOfRangeException("年龄超出范围: " + age);
    }
    this.age = age;
}

// 3. 调用方捕获
try {
    setAge(200);
} catch (AgeOutOfRangeException e) {
    System.out.println(e.getMessage());   // 年龄超出范围: 200
}
</code></pre>
<blockquote>
<p>&lt;span style="color:green"&gt;好处&lt;/span&gt;：通过自定义异常可以<strong>精准表达特定错误</strong>（比通用 <code>Exception</code> 更有语义）。</p>
</blockquote>
<h2>第十五章：File 类、递归</h2>
<blockquote>
<p><code>File</code> 类是<strong>操作系统文件对象</strong>（文件、文件夹）的 Java 封装，本章先掌握 <code>File</code> 的 API，再学习<strong>递归</strong>——用 <code>File</code> 遍历多层目录的关键。</p>
</blockquote>
<h3>一、File 类创建对象</h3>
<blockquote>
<p><code>File</code> 封装的对象<strong>仅仅是一个路径名</strong>，这个路径可以存在，也可以不存在。</p>
</blockquote>
<table>
<thead>
<tr>
<th>构造方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>public File(String pathname)</code></td>
<td>根据<strong>文件路径</strong>创建文件对象</td>
</tr>
<tr>
<td><code>public File(String parent, String child)</code></td>
<td>根据<strong>父路径 + 子路径</strong>字符串创建文件对象</td>
</tr>
<tr>
<td><code>public File(File parent, String child)</code></td>
<td>根据<strong>父路径 File 对象 + 子路径</strong>字符串创建文件对象</td>
</tr>
</tbody>
</table>
<h4>1.1 绝对路径 vs 相对路径</h4>
<table>
<thead>
<tr>
<th>路径类型</th>
<th>含义</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:blue"&gt;绝对路径&lt;/span&gt;</td>
<td>从&lt;span style="color:green"&gt;盘符根目录&lt;/span&gt;开始，到具体的文件或文件夹</td>
<td><code>new File("D:\\A.txt")</code></td>
</tr>
<tr>
<td>&lt;span style="color:orange"&gt;相对路径&lt;/span&gt;</td>
<td>相对于&lt;span style="color:orange"&gt;当前项目&lt;/span&gt;的所在目录</td>
<td><code>new File("A.txt")</code>（实际路径是"项目根\A.txt"）</td>
</tr>
</tbody>
</table>
<h3>二、File 类常用 API</h3>
<h4>2.1 判断方法</h4>
<table>
<thead>
<tr>
<th>方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>public boolean isDirectory()</code></td>
<td>判断是否为&lt;span style="color:blue"&gt;文件夹&lt;/span&gt;</td>
</tr>
<tr>
<td><code>public boolean isFile()</code></td>
<td>判断是否为&lt;span style="color:green"&gt;文件&lt;/span&gt;</td>
</tr>
<tr>
<td><code>public boolean exists()</code></td>
<td>判断路径是否&lt;span style="color:orange"&gt;存在&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<h4>2.2 获取信息方法</h4>
<table>
<thead>
<tr>
<th>方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>public long length()</code></td>
<td>返回文件的<strong>大小</strong>（字节数）</td>
</tr>
<tr>
<td><code>public String getAbsolutePath()</code></td>
<td>返回文件的&lt;span style="color:blue"&gt;绝对路径&lt;/span&gt;</td>
</tr>
<tr>
<td><code>public String getPath()</code></td>
<td>返回<strong>定义文件时</strong>使用的路径</td>
</tr>
<tr>
<td><code>public String getName()</code></td>
<td>返回文件的<strong>名称</strong>，带后缀</td>
</tr>
<tr>
<td><code>public long lastModified()</code></td>
<td>返回文件的<strong>最后修改时间</strong>（时间毫秒值）</td>
</tr>
</tbody>
</table>
<h4>2.3 创建和删除方法</h4>
<table>
<thead>
<tr>
<th>方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>public boolean createNewFile()</code></td>
<td>创建一个<strong>新的空文件</strong></td>
</tr>
<tr>
<td><code>public boolean mkdir()</code></td>
<td>只能创建&lt;span style="color:orange"&gt;一级文件夹&lt;/span&gt;</td>
</tr>
<tr>
<td><code>public boolean mkdirs()</code></td>
<td>可以创建<strong>多级文件夹</strong>（推荐）</td>
</tr>
<tr>
<td><code>public boolean delete()</code></td>
<td>删除文件或&lt;span style="color:red"&gt;空文件夹&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:red"&gt;⚠ 关键点&lt;/span&gt;：<code>delete()</code> 方法<strong>只能删除空文件夹</strong>，且<strong>不走回收站</strong>（不可恢复）。</p>
</blockquote>
<h4>2.4 遍历方法</h4>
<table>
<thead>
<tr>
<th>方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>public File[] listFiles()</code></td>
<td>获取当前目录下所有的<strong>一级文件对象</strong>（不包含子目录内容）</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意&lt;/span&gt;：<code>listFiles()</code> 返回值的 4 种情况：</p>
<ul>
<li>路径&lt;span style="color:red"&gt;不存在&lt;/span&gt; → 返回 <code>null</code></li>
<li>路径是&lt;span style="color:red"&gt;文件&lt;/span&gt; → 返回 <code>null</code></li>
<li>路径是&lt;span style="color:green"&gt;空文件夹&lt;/span&gt; → 返回长度为 <code>0</code> 的数组</li>
<li>路径是&lt;span style="color:red"&gt;需权限&lt;/span&gt;才能访问的文件夹 → 返回 <code>null</code></li>
</ul>
</blockquote>
<h3>三、递归</h3>
<blockquote>
<p>&lt;span style="color:blue"&gt;递归&lt;/span&gt; 是指<strong>方法直接或者间接调用本身</strong>。它的核心思路是：<strong>将大问题层层转化为与原问题相似、规模更小的问题</strong>来解决。</p>
</blockquote>
<blockquote>
<p>&lt;span style="color:red"&gt;⚠ 关键点&lt;/span&gt;：递归如果<strong>没有控制好终止</strong>，会出现<strong>递归死循环</strong>，导致&lt;span style="color:red"&gt;栈内存溢出&lt;/span&gt;（<code>StackOverflowError</code>）。</p>
</blockquote>
<h4>3.1 案例：求 5 的阶乘</h4>
<blockquote>
<p>推导：5! = 5 × 4!、4! = 4 × 3!、…、1! = 1。</p>
</blockquote>
<pre><code>public class RecursionDemo1 {
    public static void main(String[] args) {
        int result = jc(5);
        System.out.println(result);   // 120
    }

    public static int jc(int num) {
        if (num == 1) {
            return 1;                    // 终止条件
        }
        return num * jc(num - 1);        // 自己调自己，参数规模更小
    }
}
</code></pre>
<p><strong>递归栈帧</strong>：</p>
<pre><code>flowchart LR
  A["jc(5) return 5*24"] --&gt; B["jc(4) return 4*6"]
  B --&gt; C["jc(3) return 3*2"]
  C --&gt; D["jc(2) return 2*1"]
  D --&gt; E["jc(1) return 1"]
</code></pre>
<h4>3.2 案例：不死神兔（斐波那契数列）</h4>
<blockquote>
<p><strong>题目</strong>：一对兔子，从出生后<strong>第 3 个月起</strong>每个月都生一对兔子，小兔子长到第 3 个月后又生一对。假设兔子都不死，问<strong>第 20 个月</strong>的兔子对数为多少？</p>
</blockquote>
<blockquote>
<p><strong>规律</strong>：从第 3 个月开始，兔子对数 = 前两个月之和。</p>
</blockquote>
<table>
<thead>
<tr>
<th>月</th>
<th>1</th>
<th>2</th>
<th>3</th>
<th>4</th>
<th>5</th>
<th>6</th>
<th>7</th>
<th>8</th>
<th>...</th>
</tr>
</thead>
<tbody>
<tr>
<td>对数</td>
<td>1</td>
<td>1</td>
<td>2</td>
<td>3</td>
<td>5</td>
<td>8</td>
<td>13</td>
<td>21</td>
<td>...</td>
</tr>
</tbody>
</table>
<pre><code>public static int rabbit(int month) {
    if (month == 1 || month == 2) return 1;
    return rabbit(month - 1) + rabbit(month - 2);
}
</code></pre>
<h4>3.3 递归使用场景</h4>
<blockquote>
<p>常用于<strong>逻辑类似的业务</strong>：文件遍历、目录树、阶乘、斐波那契等。</p>
</blockquote>
<h3>四、递归案例实战</h3>
<h4>4.1 找出所有 <code>.java</code> 文件（含子目录）</h4>
<blockquote>
<p><strong>需求</strong>：键盘录入一个文件夹路径，找出这个文件夹下<strong>所有</strong>的 <code>.java</code> 文件（包括子文件夹）。
<strong>思路</strong>：单层用 <code>listFiles()</code> 即可，子文件夹需要&lt;span style="color:green"&gt;递归&lt;/span&gt;进入查找。</p>
</blockquote>
<pre><code>public static void findJava(File dir, ArrayList&lt;String&gt; list) {
    File[] files = dir.listFiles();
    if (files == null) return;

    for (File f : files) {
        if (f.isFile() &amp;&amp; f.getName().endsWith(".java")) {
            list.add(f.getAbsolutePath());
        } else if (f.isDirectory()) {
            findJava(f, list);                 // 递归
        }
    }
}
</code></pre>
<h4>4.2 递归删除文件夹</h4>
<blockquote>
<p><strong>注意</strong>：<code>delete()</code> 只能删除<strong>空文件夹</strong>，所以要<strong>先删子再删自己</strong>（后序遍历）。</p>
</blockquote>
<pre><code>public static void deleteDir(File dir) {
    File[] files = dir.listFiles();
    if (files == null) return;

    for (File f : files) {
        if (f.isFile()) {
            f.delete();                       // 文件直接删
        } else if (f.isDirectory()) {
            deleteDir(f);                     // 递归
        }
    }
    dir.delete();                             // 最后删自己
}
</code></pre>
<h4>4.3 递归统计文件夹大小</h4>
<blockquote>
<p><strong>思路</strong>：文件夹 = 所有文件大小之和；遇到子文件夹继续递归。</p>
</blockquote>
<pre><code>public static long sizeOf(File dir) {
    long total = 0;
    File[] files = dir.listFiles();
    if (files == null) return 0;

    for (File f : files) {
        if (f.isFile()) {
            total += f.length();
        } else if (f.isDirectory()) {
            total += sizeOf(f);
        }
    }
    return total;
}
</code></pre>
<h3>五、键盘录入路径案例</h3>
<blockquote>
<p><strong>需求</strong>：键盘录入一个文件夹路径，如果输入错误就给出提示，<strong>并继续录入，直到正确为止</strong>。</p>
</blockquote>
<p><strong>分析</strong>：输入的路径<strong>可能不存在</strong>或<strong>是文件路径</strong>，所以需用 <code>exists()</code> + <code>isDirectory()</code> 双重判断。</p>
<pre><code>Scanner sc = new Scanner(System.in);
File dir;
while (true) {
    System.out.print("请输入文件夹路径: ");
    String path = sc.nextLine();
    dir = new File(path);
    if (!dir.exists()) {
        System.out.println("路径不存在，请重新输入");
    } else if (!dir.isDirectory()) {
        System.out.println("不是文件夹，请重新输入");
    } else {
        break;
    }
}
System.out.println("已找到文件夹: " + dir.getAbsolutePath());
</code></pre>
<h2>第十六章：常用 API</h2>
<blockquote>
<p>Java 提供了一系列"工具类"来完成数学计算、系统操作、日期处理等通用任务。本章覆盖 <code>Math</code>、<code>System</code>、包装类、<code>BigDecimal</code>、<code>Arrays</code>、<code>Date</code>、<code>SimpleDateFormat</code>、<code>LocalDateTime</code> / <code>LocalDate</code> / <code>LocalTime</code>、<code>DateTimeFormatter</code> 以及时间间隔工具 <code>ChronoUnit</code> / <code>Duration</code> / <code>Period</code>。</p>
</blockquote>
<h3>一、Math 类</h3>
<blockquote>
<p><code>Math</code> 类是用于<strong>执行基本数字运算</strong>的工具类，所有方法都是 <code>static</code> 修饰，可直接通过类名调用。</p>
</blockquote>
<table>
<thead>
<tr>
<th>方法名</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>public static int abs(int a)</code></td>
<td>获取参数绝对值</td>
</tr>
<tr>
<td><code>public static double ceil(double a)</code></td>
<td>向上取整</td>
</tr>
<tr>
<td><code>public static double floor(double a)</code></td>
<td>向下取整</td>
</tr>
<tr>
<td><code>public static int round(float a)</code></td>
<td>四舍五入</td>
</tr>
<tr>
<td><code>public static int max(int a, int b)</code></td>
<td>获取两个 int 值中的较大值</td>
</tr>
<tr>
<td><code>public static double pow(double a, double b)</code></td>
<td>返回 a 的 b 次幂的值</td>
</tr>
<tr>
<td><code>public static double random()</code></td>
<td>返回值为 double 的随机值，范围 <code>[0.0, 1.0)</code></td>
</tr>
</tbody>
</table>
<h3>二、System 类</h3>
<blockquote>
<p><code>System</code> 的功能是静态的，<strong>都是直接用类名调用即可</strong>。</p>
</blockquote>
<table>
<thead>
<tr>
<th>方法名</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>public static void exit(int status)</code></td>
<td>终止当前运行的 Java 虚拟机，非零表示异常终止</td>
</tr>
<tr>
<td><code>public static long currentTimeMillis()</code></td>
<td>返回当前系统的时间毫秒值形式</td>
</tr>
<tr>
<td><code>public static void arraycopy(数据源数组, 起始索引, 目的地数组, 起始索引, 拷贝个数)</code></td>
<td>数组拷贝</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:orange"&gt;时间原点小知识&lt;/span&gt;：计算机中的时间原点是 <strong>1970 年 1 月 1 日 08:00:00</strong>。1969 年 8 月贝尔实验室的肯·汤普逊利用妻子离开一个月的机会开发了 Unix，使用 B 编译语言在 PDP-7 上开发，随后丹尼斯·里奇改进了 B 语言，开发出 C 语言重写了 Unix，因此 <strong>1970 年 1 月 1 日 也算 C 语言的生日</strong>。</p>
</blockquote>
<h3>三、包装类</h3>
<blockquote>
<p>Java 是面向对象的语言，但基本类型（如 <code>int</code>、<code>double</code>）不是对象，无法调用方法或参与泛型。<strong>包装类的作用就是将基本数据类型包装成类</strong>（变成引用数据类型），从而可以创建对象、调用方法解决问题。</p>
</blockquote>
<table>
<thead>
<tr>
<th>基本数据类型</th>
<th>引用数据类型</th>
<th>基本数据类型</th>
<th>引用数据类型</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:green"&gt;byte&lt;/span&gt;</td>
<td>&lt;span style="color:green"&gt;Byte&lt;/span&gt;</td>
<td>&lt;span style="color:green"&gt;float&lt;/span&gt;</td>
<td>&lt;span style="color:green"&gt;Float&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;short&lt;/span&gt;</td>
<td>&lt;span style="color:green"&gt;Short&lt;/span&gt;</td>
<td>&lt;span style="color:green"&gt;double&lt;/span&gt;</td>
<td>&lt;span style="color:green"&gt;Double&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;int&lt;/span&gt;</td>
<td>&lt;span style="color:green"&gt;Integer&lt;/span&gt;</td>
<td>&lt;span style="color:green"&gt;boolean&lt;/span&gt;</td>
<td>&lt;span style="color:green"&gt;Boolean&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;long&lt;/span&gt;</td>
<td>&lt;span style="color:green"&gt;Long&lt;/span&gt;</td>
<td>&lt;span style="color:red"&gt;char&lt;/span&gt;</td>
<td>&lt;span style="color:red"&gt;Character&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:red"&gt;⚠ 注意事项&lt;/span&gt;：除了 <code>int → Integer</code> 和 <code>char → Character</code> 是"特殊拼写"外，其他都是基本类型首字母大写。</p>
</blockquote>
<h4>3.1 Integer 类</h4>
<blockquote>
<p>用于将基本数据类型<strong>手动包装为类</strong>。</p>
</blockquote>
<table>
<thead>
<tr>
<th>构造 / 方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;<code>public Integer(int value)</code>&lt;/span&gt;</td>
<td><s>已过时，不推荐使用</s></td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;<code>public static Integer valueOf(int i)</code>&lt;/span&gt;</td>
<td><strong>推荐</strong>通过静态方法装箱</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:orange"&gt;说明&lt;/span&gt;：从 JDK9 开始 <code>new Integer(int)</code> 构造方法已被标记为过时，建议使用 <code>valueOf</code> 或自动装箱。</p>
</blockquote>
<h4>3.2 自动装箱与自动拆箱</h4>
<table>
<thead>
<tr>
<th>名称</th>
<th>解释</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:blue"&gt;自动装箱&lt;/span&gt;</td>
<td>基本类型的数据和变量<strong>可以直接赋值给包装类型的变量</strong></td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;自动拆箱&lt;/span&gt;</td>
<td>包装类型的变量<strong>可以直接赋值给基本数据类型的变量</strong></td>
</tr>
</tbody>
</table>
<pre><code>Integer i = 10;        // 自动装箱
int n = i;             // 自动拆箱
int result = i + 100;  // 拆箱后参与运算
</code></pre>
<blockquote>
<p>&lt;span style="color:green"&gt;简单记&lt;/span&gt;：有了自动拆装箱，<strong>基本数据类型和对应的包装类，可以直接运算</strong>，操作起来非常便捷。</p>
</blockquote>
<h4>3.3 包装类练习</h4>
<blockquote>
<p><strong>需求</strong>：已知字符串 <code>String s = "10,50,30,20,40"</code>，请将该字符串转换为整数并存入数组，随后求出最大值打印在控制台。</p>
</blockquote>
<pre><code>String s = "10,50,30,20,40";
String[] arr = s.split(",");
int[] nums = new int[arr.length];
int max = nums[0];
for (int i = 0; i &lt; arr.length; i++) {
    nums[i] = Integer.parseInt(arr[i]);   // 字符串 → int
    if (nums[i] &gt; max) {
        max = nums[i];
    }
}
System.out.println("最大值: " + max);
</code></pre>
<h3>四、BigDecimal 类</h3>
<blockquote>
<p>用于<strong>解决小数运算中出现的不精确问题</strong>。例如：</p>
</blockquote>
<pre><code>double num1 = 0.1;
double num2 = 0.2;
System.out.println(num1 + num2);   // 0.30000000000000004
</code></pre>
<h4>4.1 创建对象</h4>
<table>
<thead>
<tr>
<th>构造 / 方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>public BigDecimal(double val)</code></td>
<td>通过 double 创建（<strong>不推荐</strong>，仍有精度问题）</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;<code>public BigDecimal(String val)</code>&lt;/span&gt;</td>
<td>通过字符串创建，&lt;span style="color:green"&gt;推荐&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;<code>public static BigDecimal valueOf(double val)</code>&lt;/span&gt;</td>
<td>静态方法，&lt;span style="color:green"&gt;推荐&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<h4>4.2 常用方法</h4>
<table>
<thead>
<tr>
<th>方法名</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>public BigDecimal add(BigDecimal b)</code></td>
<td>加法</td>
</tr>
<tr>
<td><code>public BigDecimal subtract(BigDecimal b)</code></td>
<td>减法</td>
</tr>
<tr>
<td><code>public BigDecimal multiply(BigDecimal b)</code></td>
<td>乘法</td>
</tr>
<tr>
<td><code>public BigDecimal divide(BigDecimal b)</code></td>
<td>除法</td>
</tr>
<tr>
<td><code>public BigDecimal divide(另一个 BigDecimal 对象, 精确几位, 舍入模式)</code></td>
<td>除法（精确控制）</td>
</tr>
</tbody>
</table>
<h4>4.3 divide 除法细节</h4>
<blockquote>
<p>除法运算需要注意<strong>除不尽</strong>的情况，必须指定保留位数与舍入模式。</p>
</blockquote>
<pre><code>BigDecimal divide = bd1.divide(参与运算的对象, 小数点后精确到多少位, 舍入模式);
</code></pre>
<table>
<thead>
<tr>
<th>舍入模式</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>RoundingMode.UP</code></td>
<td>&lt;span style="color:orange"&gt;进一法&lt;/span&gt;</td>
</tr>
<tr>
<td><code>RoundingMode.DOWN</code></td>
<td>&lt;span style="color:orange"&gt;去尾法&lt;/span&gt;</td>
</tr>
<tr>
<td><code>RoundingMode.HALF_UP</code></td>
<td>&lt;span style="color:orange"&gt;四舍五入&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<pre><code>BigDecimal a = new BigDecimal("10");
BigDecimal b = new BigDecimal("3");
BigDecimal r = a.divide(b, 2, RoundingMode.HALF_UP);  // 3.33
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;小结&lt;/span&gt;：BigDecimal 用于解决小数运算的精度损失问题；创建对象推荐 <code>BigDecimal(String)</code> 或 <code>static valueOf(double)</code>；除法运算要注意除不尽的情况。</p>
</blockquote>
<h3>五、Arrays 类</h3>
<blockquote>
<p>数组操作工具类，<strong>专门用于操作数组元素</strong>。</p>
</blockquote>
<table>
<thead>
<tr>
<th>方法名</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>public static String toString(类型[] a)</code></td>
<td>将数组元素拼接为带格式的字符串</td>
</tr>
<tr>
<td><code>public static boolean equals(类型[] a, 类型[] b)</code></td>
<td>比较两个数组内容是否相同</td>
</tr>
<tr>
<td><code>public static int binarySearch(int[] a, int key)</code></td>
<td>查找元素在数组中的索引（&lt;span style="color:red"&gt;二分查找法&lt;/span&gt;）</td>
</tr>
<tr>
<td><code>public static void sort(类型[] a)</code></td>
<td>对数组进行默认升序排序</td>
</tr>
</tbody>
</table>
<h4>5.1 二分查找原理</h4>
<blockquote>
<p>二分查找的<strong>前提：必须是排好序的数据</strong>。每次取中间位置元素与目标值比较：</p>
<ul>
<li>中间元素 &lt; 目标值 → 往右半区查找</li>
<li>中间元素 &gt; 目标值 → 往左半区查找</li>
<li>中间元素 = 目标值 → 找到</li>
</ul>
</blockquote>
<pre><code>flowchart LR
  A["原始数组 11 22 33 44 55 66 77 88 99 100"] --&gt;|"查找 88 中间=55 比 55 大，往右"| B["右半区 66 77 88 99 100"]
  B --&gt;|"中间=88 正好相等"| C["找到索引 7"]
</code></pre>
<blockquote>
<p>&lt;span style="color:red"&gt;⚠ 注意&lt;/span&gt;：如果查找的元素不在数组中，<code>binarySearch</code> 会返回<strong>负数</strong>（即 - (插入点 + 1)）。</p>
</blockquote>
<pre><code>int[] arr = {11, 22, 33, 44, 55, 66, 77, 88, 99, 100};
int idx = Arrays.binarySearch(arr, 88);   // 7
</code></pre>
<h3>六、Date 类</h3>
<blockquote>
<p><code>Date</code> 代表<strong>日期和时间</strong>，位于 <code>java.util</code> 包下。</p>
</blockquote>
<table>
<thead>
<tr>
<th>构造器</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>public Date()</code></td>
<td>创建一个 <code>Date</code> 对象，代表系统当前此刻的日期时间</td>
</tr>
<tr>
<td><code>public Date(long time)</code></td>
<td>把时间毫秒值转换成 <code>Date</code> 日期对象</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th>常见方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>public long getTime()</code></td>
<td>返回从 1970 年 1 月 1 日 00:00:00 走到此列的总毫秒数</td>
</tr>
<tr>
<td><code>public void setTime(long time)</code></td>
<td>设置日期对象的时间为当前时间毫秒值对应的时间</td>
</tr>
</tbody>
</table>
<pre><code>Date d1 = new Date();
System.out.println(d1);                    // 当前时间
System.out.println(d1.getTime());          // 当前时间毫秒值
Date d2 = new Date(0L);                    // 时间原点
System.out.println(d2);                    // Thu Jan 01 08:00:00 CST 1970
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;小结&lt;/span&gt;：① <code>Date</code> 对象创建 → <code>new Date()</code>；② 获取时间毫秒值 → <code>getTime()</code>；③ 时间毫秒值转 <code>Date</code> 对象 → <code>new Date(long time)</code> 或 <code>setTime(long time)</code>。</p>
</blockquote>
<h3>七、SimpleDateFormat 类</h3>
<blockquote>
<p>用于<strong>日期格式化</strong>（<code>Date</code> → 字符串）和<strong>解析</strong>（字符串 → <code>Date</code>）。</p>
</blockquote>
<h4>7.1 构造器与格式化方法</h4>
<table>
<thead>
<tr>
<th>分类</th>
<th>方法名</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:blue"&gt;构造器&lt;/span&gt;</td>
<td><code>public SimpleDateFormat()</code></td>
<td>构造一个 SimpleDateFormat，使用<strong>默认格式</strong></td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;构造器&lt;/span&gt;</td>
<td><code>public SimpleDateFormat(String pattern)</code></td>
<td>构造一个 SimpleDateFormat，使用<strong>指定的格式</strong></td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;格式化方法&lt;/span&gt;</td>
<td><code>public final String format(Date date)</code></td>
<td>将日期格式化成日期/时间字符串</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;格式化方法&lt;/span&gt;</td>
<td><code>public final Date parse(String source)</code></td>
<td>将字符串解析为日期类型</td>
</tr>
</tbody>
</table>
<pre><code>SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = new Date();
String s = sdf.format(d);                  // Date → 字符串
Date d2 = sdf.parse("2025-01-20 15:30:00"); // 字符串 → Date
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;常用格式符号&lt;/span&gt;：<code>yyyy</code>（年）<code>MM</code>（月）<code>dd</code>（日）<code>HH</code>（时）<code>mm</code>（分）<code>ss</code>（秒）。</p>
</blockquote>
<h3>八、JDK8 新增的时间 API</h3>
<blockquote>
<p>JDK8 之后新增的时间 API 设计更合理、<strong>功能丰富、使用方便</strong>，主要特点：</p>
<ol>
<li>都是<strong>不可变对象</strong>，修改后会返回新的时间对象，不会丢失最开始的时间</li>
<li>&lt;span style="color:green"&gt;线程安全&lt;/span&gt;</li>
<li>&lt;span style="color:green"&gt;能精确到毫秒、纳秒&lt;/span&gt;</li>
</ol>
</blockquote>
<h4>8.1 LocalDateTime / LocalDate / LocalTime</h4>
<table>
<thead>
<tr>
<th>类</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:blue"&gt;LocalDate&lt;/span&gt;</td>
<td>代表本地日期（年、月、日、星期）</td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;LocalTime&lt;/span&gt;</td>
<td>代表本地时间（时、分、秒、纳秒）</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;LocalDateTime&lt;/span&gt;</td>
<td>代表本地日期 + 时间（年、月、日、星期、时、分、秒、纳秒）</td>
</tr>
</tbody>
</table>
<p><strong>获取对象</strong>：</p>
<table>
<thead>
<tr>
<th>方法</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>public static Xxxx now()</code>：获取系统当前时间对应的该对象</td>
<td><code>LocalDate ld = LocalDate.now();</code>&lt;br&gt;<code>LocalTime lt = LocalTime.now();</code>&lt;br&gt;<code>LocalDateTime ldt = LocalDateTime.now();</code></td>
</tr>
<tr>
<td><code>public static Xxxx of(...)</code>：获取指定时间的对象</td>
<td><code>LocalDate.of(2099, 11, 11)</code>&lt;br&gt;<code>LocalTime.of(9, 8, 59)</code>&lt;br&gt;<code>LocalDateTime.of(2025, 11, 16, 14, 30, 1)</code></td>
</tr>
</tbody>
</table>
<p><strong>获取年月日时分秒相关的方法</strong>：</p>
<table>
<thead>
<tr>
<th>方法名</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>int getYear()</code></td>
<td>获取年份字段</td>
</tr>
<tr>
<td><code>Month getMonth()</code></td>
<td>使用 Month 枚举获取年份字段</td>
</tr>
<tr>
<td><code>int getMonthValue()</code></td>
<td>获取 1 到 12 之间的月份字段</td>
</tr>
<tr>
<td><code>int getDayOfMonth()</code></td>
<td>获取日期字段</td>
</tr>
<tr>
<td><code>DayOfWeek getDayOfWeek()</code></td>
<td>获取星期几字段，即枚举 DayOfWeek</td>
</tr>
<tr>
<td><code>int getHour()</code></td>
<td>获取当日时间字段</td>
</tr>
<tr>
<td><code>int getMinute()</code></td>
<td>获取分钟字段</td>
</tr>
<tr>
<td><code>int getSecond()</code></td>
<td>获取秒字段</td>
</tr>
</tbody>
</table>
<p><strong>修改年月日时分秒相关的方法</strong>：</p>
<blockquote>
<p><code>LocalDateTime</code> / <code>LocalDate</code> / <code>LocalTime</code> 都是<strong>不可变</strong>的，下列方法返回的是<strong>一个新的对象</strong>。</p>
</blockquote>
<table>
<thead>
<tr>
<th>方法名</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>withHour</code>、<code>withMinute</code>、<code>withSecond</code>、<code>withNano</code></td>
<td>&lt;span style="color:blue"&gt;修改时间，返回新时间对象&lt;/span&gt;</td>
</tr>
<tr>
<td><code>plusHours</code>、<code>plusMinutes</code>、<code>plusSeconds</code>、<code>plusNanos</code></td>
<td>把某个信息&lt;span style="color:green"&gt;加&lt;/span&gt;多少，返回新时间对象</td>
</tr>
<tr>
<td><code>minusHours</code>、<code>minusMinutes</code>、<code>minusSeconds</code>、<code>minusNanos</code></td>
<td>把某个信息&lt;span style="color:red"&gt;减&lt;/span&gt;多少，返回新时间对象</td>
</tr>
<tr>
<td><code>equals</code> / <code>isBefore</code> / <code>isAfter</code></td>
<td>判断 2 个时间对象是否相等，在前还是在后</td>
</tr>
</tbody>
</table>
<pre><code>LocalDateTime ldt = LocalDateTime.now();
LocalDateTime newLdt = ldt
        .withHour(10)               // 修改小时
        .plusDays(7)                // 加 7 天
        .minusHours(2);             // 减 2 小时
</code></pre>
<h4>8.2 DateTimeFormatter</h4>
<blockquote>
<p>用于<strong>时间的格式化和解析</strong>，是 JDK8 新增时间 API 的"格式器"。</p>
</blockquote>
<table>
<thead>
<tr>
<th>方法名</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>static DateTimeFormatter ofPattern(格式)</code></td>
<td>&lt;span style="color:green"&gt;获取格式对象&lt;/span&gt;</td>
</tr>
<tr>
<td><code>String format(时间对象)</code></td>
<td>按照指定方式格式化</td>
</tr>
</tbody>
</table>
<pre><code>DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime ldt = LocalDateTime.now();
String s = dtf.format(ldt);                     // 格式化
LocalDateTime ldt2 = LocalDateTime.parse(s, dtf); // 解析
</code></pre>
<h3>九、时间间隔工具类</h3>
<blockquote>
<p>用于<strong>计算时间间隔</strong>，JDK8 提供了三个工具：</p>
</blockquote>
<table>
<thead>
<tr>
<th>类</th>
<th>用途</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:blue"&gt;Duration&lt;/span&gt;</td>
<td>用于计算两个"<strong>时间</strong>"间隔（秒、纳秒）</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;Period&lt;/span&gt;</td>
<td>用于计算两个"<strong>日期</strong>"间隔（年、月、日）</td>
</tr>
<tr>
<td>&lt;span style="color:orange"&gt;ChronoUnit&lt;/span&gt;</td>
<td>用于计算两个"<strong>日期</strong>"间隔</td>
</tr>
</tbody>
</table>
<pre><code>// 计算两个 LocalDateTime 之间相差的天、时、分
LocalDateTime start = LocalDateTime.of(2025, 1, 1, 0, 0, 0);
LocalDateTime end   = LocalDateTime.now();

long days  = ChronoUnit.DAYS.between(start, end);
long hours = ChronoUnit.HOURS.between(start, end);
long mins  = ChronoUnit.MINUTES.between(start, end);
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;案例&lt;/span&gt;：根据用户填写的生日计算年龄——使用 <code>Period.between(birth, today)</code> 取得"年、月、日"三段间隔，进而精确计算周岁。</p>
</blockquote>
<pre><code>LocalDate birth = LocalDate.of(1990, 12, 1);
LocalDate today = LocalDate.now();
Period p = Period.between(birth, today);
System.out.println("年龄：" + p.getYears() + " 岁 "
        + p.getMonths() + " 月 " + p.getDays() + " 天");
</code></pre>
<h2>第十七章：IO 流</h2>
<blockquote>
<p>IO 是 "Input / Output" 的缩写，意为<strong>输入 / 输出</strong>，本质是<strong>数据传输</strong>。常见场景：读写配置文件 / 日志文件、客户端与服务端通讯、文件上传下载等。</p>
</blockquote>
<h3>一、IO 介绍和分类</h3>
<h4>1.1 IO 流向</h4>
<blockquote>
<p>把外存中的数据<strong>读取</strong>到内存中称为"输入"（读），把内存中的数据<strong>写出</strong>到外存中称为"输出"（写）。</p>
</blockquote>
<pre><code>flowchart LR
  subgraph 输入流管道
    direction LR
    FILE["D:\user.txt"] --&gt;|"读取数据"| JVM["Java 程序"]
  end
  subgraph 输出流管道
    direction RL
    FILE2["D:\user.txt"] &lt;==|"写出数据"| JVM2["Java 程序"]
  end
</code></pre>
<h4>1.2 IO 流体系结构</h4>
<blockquote>
<p>IO 流按"数据单位"分为两大体系：<strong>字节流</strong>（万能流，所有文件都能操作）和<strong>字符流</strong>（纯文本文件专用，能解决中文乱码）。</p>
</blockquote>
<table>
<thead>
<tr>
<th>分类</th>
<th>抽象类</th>
<th>说明</th>
<th>抽象类的子类</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:orange"&gt;字节流&lt;/span&gt;（万能流）</td>
<td><code>InputStream</code></td>
<td>字节输入流</td>
<td><code>FileInputStream</code></td>
</tr>
<tr>
<td>字节流</td>
<td><code>OutputStream</code></td>
<td>字节输出流</td>
<td><code>FileOutputStream</code></td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;字符流&lt;/span&gt;（纯文本）</td>
<td><code>Reader</code></td>
<td>字符输入流</td>
<td><code>FileReader</code></td>
</tr>
<tr>
<td>字符流</td>
<td><code>Writer</code></td>
<td>字符输出流</td>
<td><code>FileWriter</code></td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:orange"&gt;小结&lt;/span&gt;：<strong>字符流使用场景：读写纯文本文件；字节流使用场景：不是纯文本文件都用字节流</strong>。</p>
</blockquote>
<h3>二、FileOutputStream 字节输出流</h3>
<blockquote>
<p><code>FileOutputStream</code> 是<strong>字节输出流</strong>的子类，用于把 Java 程序的数据写出到文件（如 <code>D:\A.txt</code>）。</p>
</blockquote>
<h4>2.1 构造方法</h4>
<table>
<thead>
<tr>
<th>构造方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>FileOutputStream(String name)</code></td>
<td>输出流关联文件，文件路径以<strong>字符串</strong>形式给出</td>
</tr>
<tr>
<td><code>FileOutputStream(String name, boolean append)</code></td>
<td>第二个参数是&lt;span style="color:green"&gt;追加写入&lt;/span&gt;的开关</td>
</tr>
<tr>
<td><code>FileOutputStream(File file)</code></td>
<td>输出流关联文件，文件路径以 <code>File</code> 对象形式给出</td>
</tr>
<tr>
<td><code>FileOutputStream(File file, boolean append)</code></td>
<td>第二个参数是追加写入的开关</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:red"&gt;⚠ 注意事项&lt;/span&gt;：① 关联的文件如果<strong>不存在会自动创建</strong>；② 如果文件已存在，默认会&lt;span style="color:red"&gt;清空原有的内容&lt;/span&gt;再重新写入（除非开启追加模式）。</p>
</blockquote>
<h4>2.2 成员方法</h4>
<table>
<thead>
<tr>
<th>方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>void write(int b)</code></td>
<td>写出&lt;span style="color:orange"&gt;单个字节&lt;/span&gt;</td>
</tr>
<tr>
<td><code>void write(byte[] b)</code></td>
<td>写出一个&lt;span style="color:orange"&gt;字节数组&lt;/span&gt;</td>
</tr>
<tr>
<td><code>void write(byte[] b, int off, int len)</code></td>
<td>写出字节数组的&lt;span style="color:orange"&gt;一部分&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<pre><code>// 示例：写出数据
FileOutputStream fos = new FileOutputStream("D:\\A.txt", true);  // 追加写入
fos.write(97);                      // 写出单个字节 'a'
fos.write("Hello".getBytes());      // 写出整个字符串
fos.close();                        // 关闭流，释放资源
</code></pre>
<h3>三、关流与异常处理</h3>
<blockquote>
<p>流对象使用完毕后，&lt;span style="color:red"&gt;记得调用 <code>close</code> 方法关闭，不然会占用资源&lt;/span&gt;。</p>
</blockquote>
<h4>3.1 JDK7 之后的 try-with-resources 写法</h4>
<blockquote>
<p><code>try</code> 后面的<strong>小括号</strong>里可以直接写需要关闭的对象，<strong>前提是该对象实现了 <code>AutoCloseable</code> 接口</strong>。</p>
</blockquote>
<pre><code>try (FileOutputStream fos = new FileOutputStream("D:\\A.txt")) {
    fos.write("Hello".getBytes());
} catch (IOException e) {
    e.printStackTrace();
}
</code></pre>
<blockquote>
<p>&lt;span style="color:green"&gt;优势&lt;/span&gt;：① 自动调用 <code>close()</code> 释放资源；② 代码更简洁，<strong>避免忘记关流</strong>。</p>
</blockquote>
<h3>四、FileInputStream 字节输入流</h3>
<blockquote>
<p><code>FileInputStream</code> 是字节输入流，用于<strong>读取</strong>文件中的数据到 Java 程序（如把 <code>D:\A.txt</code> 的内容读进来）。</p>
</blockquote>
<h4>4.1 构造方法</h4>
<table>
<thead>
<tr>
<th>构造方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>FileInputStream(String name)</code></td>
<td>输入流关联文件，文件路径以字符串形式给出</td>
</tr>
<tr>
<td><code>FileInputStream(File file)</code></td>
<td>输入流关联文件，文件路径以 <code>File</code> 对象形式给出</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:red"&gt;⚠ 注意事项&lt;/span&gt;：① 关联的<strong>文件不存在</strong>会抛出 <code>FileNotFoundException</code> 异常；② 文件夹的话会拒绝访问。</p>
</blockquote>
<h4>4.2 成员方法</h4>
<table>
<thead>
<tr>
<th>方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>int read()</code></td>
<td>读取一个字节并返回，如果到达文件结尾则返回 <strong>-1</strong></td>
</tr>
<tr>
<td><code>int read(byte[] b)</code></td>
<td>将读取到字节放到传入的数组，&lt;span style="color:green"&gt;返回读取到的有效字节个数&lt;/span&gt;，到达文件结尾则返回 -1</td>
</tr>
</tbody>
</table>
<pre><code>// 方式一：单字节读取（效率低）
try (FileInputStream fis = new FileInputStream("D:\\A.txt")) {
    int b;
    while ((b = fis.read()) != -1) {
        System.out.print((char) b);
    }
} catch (IOException e) {
    e.printStackTrace();
}

// 方式二：字节数组读取（推荐，效率高）
try (FileInputStream fis = new FileInputStream("D:\\A.txt")) {
    byte[] bys = new byte[2];
    int len;
    while ((len = fis.read(bys)) != -1) {
        System.out.print(new String(bys, 0, len));
    }
} catch (IOException e) {
    e.printStackTrace();
}
</code></pre>
<h4>4.3 String 构造方法（配合使用）</h4>
<table>
<thead>
<tr>
<th>构造方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>public String(byte[] bytes, int offset, int length)</code></td>
<td>将字节数组转换为字符串，参数 1：字节数组；参数 2：起始索引；参数 3：转换的个数</td>
</tr>
</tbody>
</table>
<h3>五、文件拷贝案例</h3>
<blockquote>
<p><strong>需求</strong>：将 <code>D:\嘿嘿.jpg</code> 拷贝到 <code>E:\</code> 根目录下。</p>
</blockquote>
<pre><code>flowchart LR
  A["1. 创建输入流对象 读取文件"] --&gt; B["2. 创建输出流对象 关联数据目的"]
  B --&gt; C["3. 读写操作"]
  C --&gt; D["4. 关流释放资源"]
</code></pre>
<pre><code>try (
    FileInputStream fis = new FileInputStream("D:\\嘿嘿.jpg");
    FileOutputStream fos = new FileOutputStream("E:\\嘿嘿.jpg")
) {
    byte[] bys = new byte[1024];
    int len;
    while ((len = fis.read(bys)) != -1) {
        fos.write(bys, 0, len);
    }
} catch (IOException e) {
    e.printStackTrace();
}
</code></pre>
<h3>六、FileReader 字符输入流</h3>
<blockquote>
<p>用于<strong>读取纯文本文件</strong>，能解决<strong>中文乱码问题</strong>。</p>
</blockquote>
<table>
<thead>
<tr>
<th>分类</th>
<th>方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:blue"&gt;构造&lt;/span&gt;</td>
<td><code>FileReader(String fileName)</code></td>
<td>字符输入流关联文件，路径以字符串形式给出</td>
</tr>
<tr>
<td>构造</td>
<td><code>FileReader(File file)</code></td>
<td>字符输入流关联文件，路径以 <code>File</code> 对象形式给出</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;成员方法&lt;/span&gt;</td>
<td><code>public int read()</code></td>
<td>读取&lt;span style="color:green"&gt;单个字符&lt;/span&gt;</td>
</tr>
<tr>
<td>成员方法</td>
<td><code>public int read(char[] cbuf)</code></td>
<td>读取一个字符数组，&lt;span style="color:green"&gt;返回读取到的有效字符个数&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<h3>七、FileWriter 字符输出流</h3>
<h4>7.1 构造方法</h4>
<table>
<thead>
<tr>
<th>构造方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>FileWriter(String fileName)</code></td>
<td>字符输出流关联文件，路径以字符串形式给出</td>
</tr>
<tr>
<td><code>FileWriter(String fileName, boolean append)</code></td>
<td>参数 2：&lt;span style="color:green"&gt;追加写入&lt;/span&gt;的开关</td>
</tr>
<tr>
<td><code>FileWriter(File file)</code></td>
<td>字符输出流关联文件，路径以 <code>File</code> 对象形式给出</td>
</tr>
<tr>
<td><code>FileWriter(File file, boolean append)</code></td>
<td>参数 2：追加写入的开关</td>
</tr>
</tbody>
</table>
<h4>7.2 成员方法</h4>
<table>
<thead>
<tr>
<th>方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>public void write(int c)</code></td>
<td>写出&lt;span style="color:blue"&gt;单个字符&lt;/span&gt;</td>
</tr>
<tr>
<td><code>public void write(char[] cbuf)</code></td>
<td>写出一个&lt;span style="color:blue"&gt;字符数组&lt;/span&gt;</td>
</tr>
<tr>
<td><code>public void write(char[] cbuf, int off, int len)</code></td>
<td>写出字符数组的&lt;span style="color:blue"&gt;一部分&lt;/span&gt;</td>
</tr>
<tr>
<td><code>public void write(String str)</code></td>
<td>写出&lt;span style="color:blue"&gt;字符串&lt;/span&gt;</td>
</tr>
<tr>
<td><code>public void write(String str, int off, int len)</code></td>
<td>写出字符串的&lt;span style="color:blue"&gt;一部分&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:red"&gt;⚠ 注意事项&lt;/span&gt;：字符输出流写出数据，<strong>需要调用 <code>flush</code> 或 <code>close</code> 方法，数据才会写出</strong>。</p>
<ul>
<li><code>flush()</code> 后可以继续写出</li>
<li><code>close()</code> 后不能继续写出（流已关闭）</li>
</ul>
</blockquote>
<h3>八、Properties 集合</h3>
<blockquote>
<p><code>Properties</code> 是 <code>java.util</code> 包下的类，&lt;span style="color:blue"&gt;继承自 <code>Hashtable&lt;Object, Object&gt;</code>，本质是一个 Map 集合&lt;/span&gt;，但<strong>键和值都必须是 String</strong>。</p>
</blockquote>
<h4>8.1 作为集合使用</h4>
<table>
<thead>
<tr>
<th>分类</th>
<th>方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:blue"&gt;构造&lt;/span&gt;</td>
<td><code>Properties()</code></td>
<td>创建一个没有默认值的空属性列表</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;成员方法&lt;/span&gt;</td>
<td><code>Object setProperty(String key, String value)</code></td>
<td>添加 / 修改一个键值对</td>
</tr>
<tr>
<td>成员方法</td>
<td><code>String getProperty(String key)</code></td>
<td>根据键获取值</td>
</tr>
<tr>
<td>成员方法</td>
<td><code>Set&lt;String&gt; stringPropertyNames()</code></td>
<td>获取集合中所有的键</td>
</tr>
</tbody>
</table>
<pre><code>Properties prop = new Properties();
prop.setProperty("username", "admin");
prop.setProperty("password", "123456");
System.out.println(prop.getProperty("username"));  // admin
</code></pre>
<h4>8.2 和 IO 有关的方法</h4>
<table>
<thead>
<tr>
<th>方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>void load(InputStream inStream)</code></td>
<td>从输入<strong>字节流</strong>读取属性列表（键和元素对）</td>
</tr>
<tr>
<td><code>void load(Reader reader)</code></td>
<td>从输入<strong>字符流</strong>读取属性列表（键和元素对）</td>
</tr>
<tr>
<td><code>void store(OutputStream out, String comments)</code></td>
<td>将此属性列表写入输出<strong>字节流</strong>，以适合 <code>load(InputStream)</code> 方法的格式</td>
</tr>
<tr>
<td><code>void store(Writer writer, String comments)</code></td>
<td>将此属性列表写入输出<strong>字符流</strong>，以适合 <code>load(Reader)</code> 方法的格式</td>
</tr>
</tbody>
</table>
<pre><code>// 把集合中的键值对写到配置文件
Properties prop = new Properties();
prop.setProperty("username", "admin");
prop.setProperty("password", "123456");
try (FileWriter fw = new FileWriter("config.properties")) {
    prop.store(fw, "user config");
}

// 从配置文件中读取键值对到集合
Properties prop2 = new Properties();
try (FileReader fr = new FileReader("config.properties")) {
    prop2.load(fr);
    System.out.println(prop2.getProperty("username"));
}
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;小结&lt;/span&gt;：① <code>Properties</code> 本质就是 Map 集合，键值都是 String；② <code>setProperty(键,值)</code> 添加、<code>getProperty(键)</code> 取值；③ <code>load</code> 从流加载、<code>store</code> 写入流——<strong>将来加载配置文件时很方便</strong>。</p>
</blockquote>
<h3>九、HuTool 工具</h3>
<h4>9.1 Hutool 介绍</h4>
<blockquote>
<p><strong>Hutool</strong> 是一个小而全的 Java 工具类库，<strong>通过静态方法封装</strong>，降低相关 API 的学习成本，提高工作效率。</p>
<ul>
<li>名称由来：<code>Hutool = Hu + tool</code>，是原公司项目底层代码剥离后的开源库，<strong>"Hu" 是公司名称的表示，tool 表示工具</strong></li>
<li>谐音"糊涂"，一方面简洁易用，一方面寓意"难得糊涂"</li>
</ul>
</blockquote>
<h4>9.2 Hutool 常用 API</h4>
<blockquote>
<p>Hutool 不是 JDK 自带的，<strong>使用时需要导入 <code>jar</code> 包</strong>（后续学习 Maven 可简化此操作）。</p>
</blockquote>
<p><strong><code>IoUtil</code> 类提供的部分方法</strong>：</p>
<table>
<thead>
<tr>
<th>方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>copy(InputStream in, OutputStream out, int bufferSize)</code></td>
<td>字节流拷贝</td>
</tr>
<tr>
<td><code>copy(Reader reader, Writer writer)</code></td>
<td>字符流拷贝</td>
</tr>
<tr>
<td><code>readLines(Reader reader, Collection&lt;String&gt; collection)</code></td>
<td>按行读取内容到集合</td>
</tr>
<tr>
<td><code>close(Closeable closeables)</code></td>
<td>&lt;span style="color:green"&gt;安全关闭流&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<p><strong><code>FileUtil</code> 类提供的部分方法</strong>：</p>
<table>
<thead>
<tr>
<th>方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>touch(filePath)</code></td>
<td>&lt;span style="color:green"&gt;创建文件&lt;/span&gt;（自动创建父目录）</td>
</tr>
<tr>
<td><code>mkdir(dirPath)</code></td>
<td>&lt;span style="color:green"&gt;创建目录&lt;/span&gt;（支持多级目录）</td>
</tr>
<tr>
<td><code>copy(srcPath, destPath, isOverride)</code></td>
<td>复制文件或目录（可选覆盖）</td>
</tr>
<tr>
<td><code>move(srcFile, destDir, isOverride)</code></td>
<td>&lt;span style="color:green"&gt;移动&lt;/span&gt;文件或目录</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:orange"&gt;Tips&lt;/span&gt;：Hutool 导入 jar 包的方式较为繁琐，&lt;span style="color:green"&gt;后面学到 Maven 的时候可以简化&lt;/span&gt;。</p>
</blockquote>
<h2>第十八章：多线程</h2>
<blockquote>
<p>多线程是 Java 编程中非常重要的特性，用于<strong>提高程序执行效率</strong>和<strong>同时处理多个任务</strong>。本章从进程和线程的概念讲起，逐步深入到 Java 线程的创建、线程安全、线程同步以及线程池的使用。</p>
</blockquote>
<h3>一、进程和线程</h3>
<h4>1.1 进程介绍</h4>
<blockquote>
<p><strong>进程（Process）</strong> 是计算机中的程序关于某数据集合上的一次运行活动，是系统进行<strong>资源分配的基本单位</strong>。简单理解为<strong>程序的执行过程</strong>。</p>
</blockquote>
<p><strong>进程的特点</strong>：</p>
<table>
<thead>
<tr>
<th>特点</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:green"&gt;独立性&lt;/span&gt;</td>
<td>每一个进程都有自己的空间，在没有经过进程本身允许的情况下，一个进程不可以直接访问其它的进程空间</td>
</tr>
<tr>
<td>&lt;span style="color:orange"&gt;动态性&lt;/span&gt;</td>
<td>进程是动态产生、动态消亡的</td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;并发性&lt;/span&gt;</td>
<td>任何进程都可以同其它进程一起并发执行</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:orange"&gt;小结&lt;/span&gt;：进程是程序的执行过程，具有独立性、动态性、并发性。对于一个 CPU 而言，它是在<strong>多个进程间轮换执行</strong>的。</p>
</blockquote>
<h4>1.2 并行和并发</h4>
<blockquote>
<p><strong>并行</strong>：在同一时刻，有多个指令在多个 CPU 上&lt;span style="color:green"&gt;同时&lt;/span&gt;执行。
<strong>并发</strong>：在同一时刻，有多个指令在单个 CPU 上&lt;span style="color:orange"&gt;交替&lt;/span&gt;执行。</p>
</blockquote>
<pre><code>flowchart LR
  subgraph 并行
    direction LR
    A["CPU 1"] --&gt; A1["任务 1"]
    B["CPU 2"] --&gt; B1["任务 2"]
  end
  subgraph 并发
    direction LR
    C["单个 CPU"] --&gt; C1["任务 1"]
    C1 -.-&gt; C2["任务 2"]
    C2 -.-&gt; C1
  end
</code></pre>
<h4>1.3 线程介绍</h4>
<blockquote>
<p><strong>线程（Thread）</strong>：进程可以同时执行多个任务，&lt;span style="color:green"&gt;每个任务就是一个线程&lt;/span&gt;。例如：360 安全卫士中的"电脑清理"和"电脑杀毒"是同一个进程中的不同线程。</p>
</blockquote>
<pre><code>flowchart TB
  P["进程"] --&gt; T1["子任务 1（单线程）"]
  P --&gt; T2["子任务 2（多线程）"]
  T1 --&gt; S1["子任务 1"]
  S1 --&gt; S2["子任务 2"]
  T2 --&gt; S3["子任务 1"]
  T2 --&gt; S4["子任务 2"]
</code></pre>
<h4>1.4 多线程的意义</h4>
<blockquote>
<p>随着处理器上的<strong>核心数量越来越多</strong>，现在大多数计算机都比以往更加擅长<strong>并行计算</strong>。一个线程，在一个时刻，<strong>只能运行在一个处理器核心上</strong>。</p>
</blockquote>
<p><strong>多线程的两个核心作用</strong>：</p>
<table>
<thead>
<tr>
<th>作用</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:green"&gt;提高执行效率&lt;/span&gt;</td>
<td>多个任务并行执行，节省时间</td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;同时处理多个任务&lt;/span&gt;</td>
<td>一个应用可以同时处理多种业务（如下载 + 上传）</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:orange"&gt;小结&lt;/span&gt;：线程是进程中的任务，&lt;span style="color:green"&gt;多线程就是多个任务&lt;/span&gt;。多线程的意义在于<strong>提高效率</strong>、<strong>可以同时处理多个任务</strong>。</p>
</blockquote>
<h3>二、Java 开启线程的方式</h3>
<blockquote>
<p>Java 中有三种方式可以开启线程：&lt;span style="color:green"&gt;继承 Thread 类&lt;/span&gt;、&lt;span style="color:green"&gt;实现 Runnable 接口&lt;/span&gt;、&lt;span style="color:green"&gt;实现 Callable 接口&lt;/span&gt;。&lt;span style="color:red"&gt;推荐后两种方式&lt;/span&gt;，因为它们扩展性更好（避免了单继承的局限性）。</p>
</blockquote>
<table>
<thead>
<tr>
<th>方式</th>
<th>是否需要返回值</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:blue"&gt;继承 Thread 类&lt;/span&gt;</td>
<td>无返回值</td>
<td>重写 <code>run()</code> 方法，调用 <code>start()</code> 启动线程</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;实现 Runnable 接口&lt;/span&gt;</td>
<td>&lt;span style="color:green"&gt;线程任务无返回值&lt;/span&gt;</td>
<td>实现 <code>run()</code> 方法，传入 <code>Thread</code> 构造器启动</td>
</tr>
<tr>
<td>&lt;span style="color:orange"&gt;实现 Callable 接口&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;线程任务有返回值&lt;/span&gt;</td>
<td>实现 <code>call()</code> 方法，借助 <code>FutureTask</code> 启动</td>
</tr>
</tbody>
</table>
<h3>三、线程相关方法</h3>
<h4>3.1 常用方法</h4>
<blockquote>
<p><code>Thread</code> 类提供了丰富的线程控制方法，<strong>红色标记的是静态方法</strong>。</p>
</blockquote>
<table>
<thead>
<tr>
<th>方法名称</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>String getName()</code></td>
<td>返回此线程的名称</td>
</tr>
<tr>
<td><code>void setName(String name)</code></td>
<td>设置线程的名字（构造方法也可以设置名字）</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;<code>static</code>&lt;/span&gt; <code>Thread currentThread()</code></td>
<td>获取当前线程的对象</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;<code>static</code>&lt;/span&gt; <code>void sleep(long time)</code></td>
<td>让线程休眠指定的时间，单位为<strong>毫秒</strong></td>
</tr>
<tr>
<td><code>setPriority(int newPriority)</code></td>
<td>设置线程的优先级</td>
</tr>
<tr>
<td><code>final int getPriority()</code></td>
<td>获取线程的优先级</td>
</tr>
<tr>
<td><code>final void setDaemon(boolean on)</code></td>
<td>设置为&lt;span style="color:orange"&gt;守护线程&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<pre><code>// 示例：获取并设置线程名
MyThread t1 = new MyThread("线程A");
MyThread t2 = new MyThread("线程B");

t1.setName("高铁");
t2.setName("飞机");

System.out.println(t1.getName());  // 高铁
System.out.println(t2.getName());  // 飞机

// Thread.currentThread()：获取当前线程对象
System.out.println(Thread.currentThread().getName());  // main
</code></pre>
<h4>3.2 线程调度方式</h4>
<blockquote>
<p>Java 线程调度采用<strong>抢占式调度</strong>（线程优先级高的先获取 CPU 资源），但<strong>结果随机</strong>（因为多线程有随机性）。</p>
</blockquote>
<table>
<thead>
<tr>
<th>调度方式</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:green"&gt;抢占式调度&lt;/span&gt;</td>
<td>优先级高的线程先抢占 CPU，&lt;span style="color:green"&gt;Java 采用此方式&lt;/span&gt;</td>
</tr>
<tr>
<td>非抢占式调度</td>
<td>按顺序执行，公平但效率低</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:orange"&gt;优先级范围&lt;/span&gt;：<strong>&lt;span style="color:red"&gt;1 ~ 10&lt;/span&gt;</strong>，&lt;span style="color:blue"&gt;默认值为 5&lt;/span&gt;。优先级高只是<strong>获取 CPU 资源的概率高</strong>，不是绝对先执行。</p>
</blockquote>
<pre><code>MyThread t1 = new MyThread("高铁");
MyThread t2 = new MyThread("飞机");

t1.setPriority(Thread.MIN_PRIORITY);  // 1
t2.setPriority(Thread.MAX_PRIORITY);  // 10

System.out.println(t1.getPriority());  // 1
System.out.println(t2.getPriority());  // 10
</code></pre>
<h3>四、线程安全和同步</h3>
<h4>4.1 线程安全的问题</h4>
<blockquote>
<p><strong>需求</strong>：某电影院目前正在上映国产大片，共有 <strong>100 张票</strong>，而它有 <strong>3 个窗口</strong>卖票。请设计一个程序模拟该电影院卖票。</p>
</blockquote>
<pre><code>class Tickets implements Runnable {
    int tickets = 1;

    @Override
    public void run() {
        while (true) {
            if (tickets &lt;= 0) {
                break;
            }
            System.out.println(Thread.currentThread().getName() + "卖出了第" + tickets + "号票");
            tickets--;
        }
    }
}
</code></pre>
<blockquote>
<p>上面的代码存在<strong>线程安全问题</strong>：多个线程同时操作共享数据 <code>tickets</code>，可能导致卖出同一张票甚至卖出 0 号、-1 号票。</p>
</blockquote>
<h4>4.2 线程安全问题分析</h4>
<blockquote>
<p>三个线程<strong>同时</strong>进入 <code>while</code> 循环，都读取到 <code>tickets = 1</code>，于是都"卖出了第 1 号票"，然后 <code>tickets--</code> 多次，结果出现重复卖票或卖出非法票号。</p>
</blockquote>
<p><strong>安全问题出现的条件</strong>：</p>
<table>
<thead>
<tr>
<th>条件</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;是多线程环境&lt;/span&gt;</td>
<td>必须有多个线程并发执行</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;有共享数据&lt;/span&gt;</td>
<td>多个线程操作同一个数据</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;有多条语句操作共享数据&lt;/span&gt;</td>
<td>多个操作共享数据的代码</td>
</tr>
</tbody>
</table>
<h4>4.3 线程同步</h4>
<blockquote>
<p><strong>同步技术</strong>：将多条语句操作共享数据的代码&lt;span style="color:green"&gt;锁&lt;/span&gt;起来，让任意时刻<strong>只能有一个线程</strong>可以执行。</p>
</blockquote>
<pre><code>flowchart TB
  subgraph 同步前
    direction TB
    T1["线程 1"] --&gt;|"进入"| Code["多条语句操作共享数据"]
    T2["线程 2"] --&gt;|"进入"| Code
    T3["线程 3"] --&gt;|"进入"| Code
  end
  subgraph 同步后
    direction TB
    T1A["线程 1"] --&gt;|"加锁"| LockA["🔒 锁"]
    LockA --&gt;|"执行"| CodeA["多条语句操作共享数据"]
    T2A["线程 2"] -.等待.-&gt; LockA
    T3A["线程 3"] -.等待.-&gt; LockA
  end
</code></pre>
<p>Java 提供三种同步方式：</p>
<table>
<thead>
<tr>
<th>方式</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:blue"&gt;同步代码块&lt;/span&gt;</td>
<td><code>synchronized(锁对象) { ... }</code></td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;同步方法&lt;/span&gt;</td>
<td>在方法返回值前加 <code>synchronized</code> 关键字</td>
</tr>
<tr>
<td>&lt;span style="color:orange"&gt;Lock 锁&lt;/span&gt;</td>
<td><code>java.util.concurrent.locks.Lock</code> 接口，更灵活</td>
</tr>
</tbody>
</table>
<h5>4.3.1 同步代码块</h5>
<pre><code>synchronized(锁对象) {
    多条语句操作共享数据的代码
}
</code></pre>
<blockquote>
<p>&lt;span style="color:red"&gt;⚠ 注意事项&lt;/span&gt;：</p>
<ul>
<li>锁对象可以是任意对象，但是需要保证多条线程的锁对象，&lt;span style="color:red"&gt;是同一把锁&lt;/span&gt;</li>
<li>同步可以解决多线程的数据安全问题，但是&lt;span style="color:orange"&gt;也会降低程序的运行效率&lt;/span&gt;</li>
</ul>
</blockquote>
<pre><code>class Tickets implements Runnable {
    int tickets = 100;
    Object lock = new Object();  // 锁对象

    @Override
    public void run() {
        while (true) {
            synchronized (lock) {
                if (tickets &lt;= 0) {
                    break;
                }
                System.out.println(Thread.currentThread().getName() + "卖出了第" + tickets + "号票");
                tickets--;
            }
        }
    }
}
</code></pre>
<h5>4.3.2 同步方法</h5>
<blockquote>
<p>在方法的<strong>返回值类型前面</strong>加入 <code>synchronized</code> 关键字。</p>
</blockquote>
<pre><code>public synchronized void method() {
    // 多条语句操作共享数据的代码
}
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;小细节&lt;/span&gt;：</p>
<ul>
<li>方法分为<strong>静态</strong>和<strong>非静态</strong></li>
<li>&lt;span style="color:blue"&gt;静态方法&lt;/span&gt;的锁对象是<strong>字节码对象</strong>（<code>类名.class</code>）</li>
<li>&lt;span style="color:green"&gt;非静态方法&lt;/span&gt;的锁对象是 <strong><code>this</code></strong></li>
</ul>
</blockquote>
<h5>4.3.3 Lock 锁</h5>
<blockquote>
<p>使用 <code>Lock</code> 锁，我们可以&lt;span style="color:green"&gt;更清晰&lt;/span&gt;地看到哪里加了锁，哪里释放了锁。相比 <code>synchronized</code>，Lock 锁<strong>更灵活、性能更好</strong>。</p>
</blockquote>
<p><strong>对比</strong>：</p>
<pre><code>// 方式一：同步代码块
synchronized(锁对象) {
    多条语句操作共享数据的代码
}

// 方式二：同步方法
public synchronized void method() { }

// 方式三：Lock 锁
lock.lock();
...
lock.unlock();
</code></pre>
<blockquote>
<p><code>Lock</code> 是<strong>接口</strong>，无法直接创建对象。</p>
</blockquote>
<table>
<thead>
<tr>
<th>构造方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>public ReentrantLock()</code></td>
<td>创建一个 <code>ReentrantLock</code> 的实例</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th>成员方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>void lock()</code></td>
<td>&lt;span style="color:green"&gt;加锁&lt;/span&gt;</td>
</tr>
<tr>
<td><code>void unlock();</code></td>
<td>&lt;span style="color:orange"&gt;释放锁&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<pre><code>import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Lock;

class Tickets implements Runnable {
    int tickets = 100;
    Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            lock.lock();
            try {
                if (tickets &lt;= 0) {
                    break;
                }
                System.out.println(Thread.currentThread().getName() + "卖出了第" + tickets + "号票");
                tickets--;
            } finally {
                lock.unlock();  // 无论是否异常，都需要释放锁
            }
        }
    }
}
</code></pre>
<h3>五、线程池介绍</h3>
<h4>5.1 线程池的好处</h4>
<blockquote>
<p><strong>池</strong>：提前创建好的一组资源（如数据库连接池、常量池）。&lt;span style="color:green"&gt;将线程对象交给线程池维护&lt;/span&gt;，可以<strong>降低系统成本</strong>，从而<strong>提升程序的性能</strong>。</p>
</blockquote>
<p><strong>为什么需要线程池</strong>：</p>
<table>
<thead>
<tr>
<th>问题</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>创建成本高</td>
<td>系统创建一个线程的成本是比较高的，因为它涉及到与操作系统交互</td>
</tr>
<tr>
<td>资源浪费</td>
<td>当程序中需要创建大量生存期很短暂的线程时，频繁的创建和销毁线程，就会严重浪费系统资源</td>
</tr>
</tbody>
</table>
<h4>5.2 线程池学习路径</h4>
<blockquote>
<p>学习线程池分三步：理解线程池的好处 → 使用 JDK 提供的线程池 → 自定义线程池。</p>
</blockquote>
<pre><code>flowchart LR
  A["理解线程池的好处"] --&gt; B["使用 JDK 提供的线程池"]
  B --&gt; C["自定义线程池"]
</code></pre>
<blockquote>
<p>&lt;span style="color:red"&gt;⚠【强制】&lt;/span&gt;线程池<strong>不允许使用 <code>Executors</code> 去创建</strong>，而是通过 <code>ThreadPoolExecutor</code> 的方式，这样的处理方式让写的同学更加明确线程池的运行规则，规避资源耗尽的风险。</p>
<p><strong>说明</strong>：<code>Executors</code> 返回的线程池对象的弊端如下：</p>
<ol>
<li><code>FixedThreadPool</code> 和 <code>SingleThreadPool</code>：允许的请求队列长度为 <code>Integer.MAX_VALUE</code>，&lt;span style="color:red"&gt;可能会堆积大量的请求&lt;/span&gt;，从而导致 <strong>OOM</strong>。</li>
<li><code>CachedThreadPool</code>：允许的创建线程数量为 <code>Integer.MAX_VALUE</code>，&lt;span style="color:red"&gt;可能会创建大量的线程&lt;/span&gt;，从而导致 <strong>OOM</strong>。</li>
</ol>
</blockquote>
<h4>5.3 使用 JDK 提供的线程池</h4>
<blockquote>
<p><code>Executors</code> 中提供静态方法来创建线程池（&lt;span style="color:orange"&gt;了解即可，实际开发推荐使用 <code>ThreadPoolExecutor</code>&lt;/span&gt;）。</p>
</blockquote>
<table>
<thead>
<tr>
<th>方法</th>
<th>介绍</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>static ExecutorService newCachedThreadPool()</code></td>
<td>创建一个默认的线程池</td>
</tr>
<tr>
<td><code>static newFixedThreadPool(int nThreads)</code></td>
<td>创建一个指定最多线程数量的线程池</td>
</tr>
</tbody>
</table>
<h3>六、自定义线程池</h3>
<h4>6.1 ThreadPoolExecutor 类</h4>
<blockquote>
<p><code>ThreadPoolExecutor</code> 是线程池的核心实现类，其构造方法有 <strong>7 个参数</strong>。</p>
</blockquote>
<pre><code>ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
                   BlockingQueue&lt;Runnable&gt; workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
</code></pre>
<h4>6.2 参数详解</h4>
<table>
<thead>
<tr>
<th>参数</th>
<th>含义</th>
<th>约束</th>
</tr>
</thead>
<tbody>
<tr>
<td>参数 1</td>
<td>&lt;span style="color:blue"&gt;核心线程数量&lt;/span&gt;（&lt;span style="color:orange"&gt;正式员工&lt;/span&gt;）</td>
<td>不能小于 0</td>
</tr>
<tr>
<td>参数 2</td>
<td>&lt;span style="color:green"&gt;最大线程数量&lt;/span&gt;（&lt;span style="color:orange"&gt;正式员工 + 临时工&lt;/span&gt;）</td>
<td>不能小于等于 0，&lt;span style="color:red"&gt;最大数量 &gt;= 核心线程数量&lt;/span&gt;</td>
</tr>
<tr>
<td>参数 3</td>
<td>&lt;span style="color:orange"&gt;空闲时间&lt;/span&gt;</td>
<td>不能小于 0</td>
</tr>
<tr>
<td>参数 4</td>
<td>&lt;span style="color:purple"&gt;时间单位&lt;/span&gt;</td>
<td>时间单位</td>
</tr>
<tr>
<td>参数 5</td>
<td>&lt;span style="color:green"&gt;任务队列&lt;/span&gt;（&lt;span style="color:orange"&gt;指定排队人数&lt;/span&gt;）</td>
<td>不能为 null</td>
</tr>
<tr>
<td>参数 6</td>
<td>&lt;span style="color:red"&gt;线程对象工厂&lt;/span&gt;</td>
<td>不能为 null</td>
</tr>
<tr>
<td>参数 7</td>
<td>&lt;span style="color:blue"&gt;拒绝策略&lt;/span&gt;</td>
<td>不能为 null</td>
</tr>
</tbody>
</table>
<pre><code>flowchart TB
  Customers["顾客队列"] --&gt;|"新任务"| Core["核心线程（正式员工） 数 = corePoolSize"]
  Core --&gt;|"核心线程满"| Queue["任务队列 BlockingQueue"]
  Queue --&gt;|"队列满"| Temp["临时线程 (maximumPoolSize - corePoolSize)"]
  Temp --&gt;|"全部忙 + 队列满"| Reject["拒绝策略 RejectedExecutionHandler"]
  Temp --&gt;|"空闲 keepAliveTime"| Temp
  Core --&gt;|"空闲 keepAliveTime"| Core
</code></pre>
<h4>6.3 线程池拒绝策略</h4>
<table>
<thead>
<tr>
<th>策略选项</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:blue"&gt;<code>ThreadPoolExecutor.AbortPolicy</code>&lt;/span&gt;</td>
<td>丢弃任务并抛出 <code>RejectedExecutionException</code> 异常（&lt;span style="color:red"&gt;默认&lt;/span&gt;）</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;<code>ThreadPoolExecutor.DiscardPolicy</code>&lt;/span&gt;</td>
<td>丢弃任务，但是不抛出异常，&lt;span style="color:red"&gt;这是不推荐的做法&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:orange"&gt;<code>ThreadPoolExecutor.DiscardOldestPolicy</code>&lt;/span&gt;</td>
<td>抛弃队列中等待最久的任务，然后把当前任务加入队列中</td>
</tr>
<tr>
<td>&lt;span style="color:purple"&gt;<code>ThreadPoolExecutor.CallerRunsPolicy</code>&lt;/span&gt;</td>
<td>调用任务的 <code>run()</code> 方法，绕过线程池直接执行</td>
</tr>
</tbody>
</table>
<h4>6.4 自定义线程池小结</h4>
<blockquote>
<p>&lt;span style="color:orange"&gt;思考&lt;/span&gt;：临时线程什么时候创建？什么时候会开启拒绝策略？</p>
</blockquote>
<table>
<thead>
<tr>
<th>问题</th>
<th>触发条件</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:green"&gt;临时线程什么时候创建？&lt;/span&gt;</td>
<td>线程任务数 &lt;span style="color:red"&gt;&gt;&lt;/span&gt; &lt;span style="color:blue"&gt;核心线程数&lt;/span&gt; + 任务队列的数量</td>
</tr>
<tr>
<td>&lt;span style="color:orange"&gt;什么时候会开启拒绝策略？&lt;/span&gt;</td>
<td>线程任务数 &lt;span style="color:red"&gt;&gt;&lt;/span&gt; &lt;span style="color:green"&gt;最大线程数&lt;/span&gt; + 任务队列的数量</td>
</tr>
</tbody>
</table>
<pre><code>flowchart TB
  T["提交新任务"] --&gt; Q1{"核心线程 是否已满？"}
  Q1 --&gt;|"否"| C1["创建核心线程执行"]
  Q1 --&gt;|"是"| Q2{"任务队列 是否已满？"}
  Q2 --&gt;|"否"| Q3["任务进入队列等待"]
  Q2 --&gt;|"是"| Q4{"当前线程数 是否达到最大？"}
  Q4 --&gt;|"否"| C2["创建临时线程执行"]
  Q4 --&gt;|"是"| Q5["执行拒绝策略"]
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;小结&lt;/span&gt;：自定义线程池通过 <code>ThreadPoolExecutor</code> 的 7 个参数灵活控制线程池的行为。核心线程处理正常业务，任务队列起到缓冲作用，临时线程应对突发流量，拒绝策略保证系统在极端情况下不会崩溃。</p>
</blockquote>
<h2>第十九章：网络编程</h2>
<blockquote>
<p>网络编程是实现<strong>设备间数据通信</strong>的核心技术，本章从通信架构、网络三要素讲起，重点掌握 &lt;span style="color:green"&gt;UDP&lt;/span&gt; 和 &lt;span style="color:green"&gt;TCP&lt;/span&gt; 两种协议的数据收发方式。</p>
</blockquote>
<h3>一、网络编程基本概念</h3>
<h4>1.1 网络编程</h4>
<blockquote>
<p><strong>网络编程</strong>是让设备中的程序与网络上<strong>其他设备中的程序</strong>进行数据交互的技术（实现&lt;span style="color:green"&gt;网络通信&lt;/span&gt;）。典型场景：手机与手机聊天、网页访问服务器。</p>
</blockquote>
<h4>1.2 基本的通信架构</h4>
<blockquote>
<p>基本的通信架构有 <strong>2 种</strong>形式：&lt;span style="color:green"&gt;CS 架构&lt;/span&gt;、&lt;span style="color:green"&gt;BS 架构&lt;/span&gt;。</p>
</blockquote>
<table>
<thead>
<tr>
<th>架构</th>
<th>全称</th>
<th>典型应用</th>
<th>特点</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:green"&gt;CS 架构&lt;/span&gt;</td>
<td>Client 客户端 / Server 服务端</td>
<td>微信、IntelliJ IDEA、网易云音乐</td>
<td>需要程序员开发客户端软件；需要用户下载安装客户端软件</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;BS 架构&lt;/span&gt;</td>
<td>Browser 浏览器 / Server 服务端</td>
<td>Chrome、Edge、火狐浏览器访问网页</td>
<td>不需要程序员开发客户端；需要用户下载安装浏览器</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:orange"&gt;⚠&lt;/span&gt; 无论是 CS 架构，还是 BS 架构的软件都&lt;span style="color:red"&gt;必须依赖网络编程&lt;/span&gt;！Java 提供了哪些网络编程解决方案？<strong><code>java.net.*</code> 包下提供了网络编程的解决方案！</strong></p>
</blockquote>
<pre><code>flowchart LR
  subgraph CS架构
    direction LR
    A1["Client 客户端"] --&gt;|"请求"| B1["Server 服务端"]
    B1 --&gt;|"响应"| A1
  end
  subgraph BS架构
    direction LR
    A2["Browser 浏览器"] --&gt;|"HTTP请求"| B2["Server 服务端"]
    B2 --&gt;|"HTTP响应"| A2
  end
</code></pre>
<h3>二、网络编程三要素</h3>
<blockquote>
<p><strong>网络通信三要素</strong>：&lt;span style="color:red"&gt;IP 地址&lt;/span&gt;、&lt;span style="color:orange"&gt;端口&lt;/span&gt;、&lt;span style="color:green"&gt;协议&lt;/span&gt;。</p>
</blockquote>
<table>
<thead>
<tr>
<th>要素</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;01 IP 地址&lt;/span&gt;</td>
<td>设备在网络中的地址，&lt;span style="color:red"&gt;是设备在网络中的唯一标识&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:orange"&gt;02 端口&lt;/span&gt;</td>
<td>应用程序在设备中的&lt;span style="color:red"&gt;唯一标识&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;03 协议&lt;/span&gt;</td>
<td>连接和数据在网络中传输的&lt;span style="color:red"&gt;规则&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<h4>2.1 IP 地址</h4>
<blockquote>
<p><strong>IP（Internet Protocol）</strong>：全称"互联网协议地址"，是分配给上网设备的唯一标识。目前，被广泛采用的 IP 地址形式有两种：&lt;span style="color:green"&gt;IPv4&lt;/span&gt;、&lt;span style="color:green"&gt;IPv6&lt;/span&gt;。</p>
</blockquote>
<table>
<thead>
<tr>
<th>类别</th>
<th>位数</th>
<th>字节数</th>
<th>表示法</th>
<th>数量</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:green"&gt;IPv4&lt;/span&gt;</td>
<td>32 位</td>
<td>4 个字节</td>
<td>&lt;span style="color:red"&gt;点分十进制&lt;/span&gt;（如 <code>192.168.1.66</code>）</td>
<td>最多 <code>2^32</code> 个，&lt;span style="color:red"&gt;目前已用完&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;IPv6&lt;/span&gt;</td>
<td>128 位</td>
<td>16 个字节</td>
<td>&lt;span style="color:red"&gt;冒分十六进制&lt;/span&gt;（如 <code>2001:0db8:0000:0023:0008:0800:200c:417a</code>）</td>
<td>最多 <code>2^128</code> 个，&lt;span style="color:green"&gt;可为地球上每一粒沙子编号&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:orange"&gt;小细节&lt;/span&gt;：IPv4 地址一般写成 <code>11000000.10101000.00000001.01000010</code>，转成十进制就是 <code>192.168.1.66</code>。</p>
</blockquote>
<h5>2.1.1 IPv4 地址的分类形式</h5>
<table>
<thead>
<tr>
<th>分类</th>
<th>说明</th>
<th>范围</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:green"&gt;公网地址&lt;/span&gt;</td>
<td>万维网使用</td>
<td>—</td>
</tr>
<tr>
<td>&lt;span style="color:orange"&gt;私有地址&lt;/span&gt;</td>
<td>局域网使用，专门为组织机构内部使用，以此节省 IP</td>
<td><code>192.168.0.0 ~ 192.168.255.255</code></td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:orange"&gt;小结&lt;/span&gt;：常见场景为网吧、公寓、商场、办公楼等小型组织内部使用 &lt;span style="color:green"&gt;192.168.xxx.xxx&lt;/span&gt; 的私有地址。</p>
</blockquote>
<h5>2.1.2 特殊 IP 地址</h5>
<blockquote>
<p><strong><code>127.0.0.1</code></strong>（也可以是 <code>localhost</code>）：是<strong>回送地址</strong>也称<strong>本地回环地址</strong>，也称<strong>本机 IP</strong>，永远只会寻找当前所在本机。
&lt;span style="color:orange"&gt;建议&lt;/span&gt;：自己练习就写 <code>127.0.0.1</code>。</p>
</blockquote>
<pre><code>flowchart LR
  A["你的电脑"] --&gt;|"访问其他电脑"| B["192.168.1.10"]
  A --&gt;|"访问自己"| C["127.0.0.1"]
</code></pre>
<h5>2.1.3 IP 和域名</h5>
<blockquote>
<p>平时上网没有用过 IP，都是用网址的。浏览器访问服务器时，<strong>先通过 DNS 服务器</strong>把网址（域名）解析成 IP 地址，再去访问对应的服务器。</p>
</blockquote>
<pre><code>flowchart LR
  Browser["浏览器"] --&gt;|"网址/域名"| DNS["DNS 服务器"]
  DNS --&gt;|"IP 地址"| Browser
  Browser --&gt;|"访问黑马程序员服务器"| Server["黑马程序员服务器"]
  Server --&gt;|"返回数据"| Browser
</code></pre>
<h5>2.1.4 常用的 CMD 命令</h5>
<table>
<thead>
<tr>
<th>命令</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>ipconfig</code></td>
<td>查看本机 IP 地址</td>
</tr>
<tr>
<td><code>ping</code></td>
<td>检查网络是否连通</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:orange"&gt;小结&lt;/span&gt;：</p>
<ul>
<li><strong>现在如何解决 IPv4 不够的问题？</strong> &lt;span style="color:green"&gt;利用局域网 IP 解决 IP 不够的问题&lt;/span&gt;</li>
<li><strong>特殊的 IP 是什么？</strong> <code>127.0.0.1</code>（永远表示本机）</li>
<li><strong>常见的两个 CMD 命令？</strong> <code>ipconfig</code>、<code>ping</code></li>
</ul>
</blockquote>
<h5>2.1.5 InetAddress 类</h5>
<blockquote>
<p>为了方便对 IP 地址的获取和操作，Java 提供了 <code>InetAddress</code> 类。
<strong><code>InetAddress</code></strong>：此类表示 Internet 协议（IP）地址。</p>
</blockquote>
<table>
<thead>
<tr>
<th>方法名</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;<code>static</code>&lt;/span&gt; <code>InetAddress getByName(String host)</code></td>
<td>确定主机名称的 IP 地址。主机名称可以是机器名称，也可以是 IP 地址</td>
</tr>
<tr>
<td><code>String getHostName()</code></td>
<td>获取此 IP 地址的主机名</td>
</tr>
<tr>
<td><code>String getHostAddress()</code></td>
<td>返回文本显示中的 IP 地址字符串</td>
</tr>
</tbody>
</table>
<pre><code>import java.net.InetAddress;

public class InetAddressDemo {
    public static void main(String[] args) throws Exception {
        InetAddress address = InetAddress.getByName("DESKTOP-ABC123");
        // InetAddress address = InetAddress.getByName("192.168.1.100");

        System.out.println(address.getHostName());   // 主机名
        System.out.println(address.getHostAddress()); // IP 地址字符串
    }
}
</code></pre>
<h4>2.2 端口号</h4>
<blockquote>
<p><strong>端口号</strong>是应用程序在设备中<strong>唯一的标识</strong>。</p>
</blockquote>
<table>
<thead>
<tr>
<th>项目</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>范围</td>
<td>&lt;span style="color:red"&gt;0 ~ 65535&lt;/span&gt;（由两个字节表示的整数）</td>
</tr>
<tr>
<td>知名端口</td>
<td>&lt;span style="color:orange"&gt;0 ~ 1023&lt;/span&gt; 用于一些知名的网络服务或者应用</td>
</tr>
<tr>
<td>自定义端口</td>
<td>&lt;span style="color:green"&gt;1024 以上&lt;/span&gt; 端口号自己使用</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:red"&gt;⚠ 注意&lt;/span&gt;：一个端口号只能被一个应用程序使用。</p>
</blockquote>
<pre><code>flowchart LR
  subgraph 电脑A
    APP1["WeChat 6666"] --&gt; P65533["65533 消息"]
    APP2["QQ 7777"] --&gt; P65533
    APP3["Game 8888"] --&gt; P65533
  end
  P65533 --&gt;|"端口号"| 电脑B
</code></pre>
<h4>2.3 协议</h4>
<blockquote>
<p><strong>计算机网络</strong>中，连接和通信的规则被称为<strong>网络通信协议</strong>。常见的协议有 &lt;span style="color:green"&gt;UDP 协议&lt;/span&gt; 和 &lt;span style="color:green"&gt;TCP 协议&lt;/span&gt;。</p>
</blockquote>
<h5>2.3.1 UDP 协议</h5>
<blockquote>
<p><strong>用户数据报协议（User Datagram Protocol）</strong>。</p>
<ul>
<li>&lt;span style="color:green"&gt;面向无连接&lt;/span&gt;的通信协议</li>
<li>速度快，有大小限制（&lt;span style="color:red"&gt;一次最多发送 64K&lt;/span&gt;）</li>
<li>数据不安全，易丢失数据</li>
<li>不管是否已经连接成功，都会发送</li>
<li>&lt;span style="color:green"&gt;典型应用&lt;/span&gt;：在线视频</li>
</ul>
</blockquote>
<h5>2.3.2 TCP 协议</h5>
<blockquote>
<p><strong>传输控制协议 TCP（Transmission Control Protocol）</strong>。</p>
<ul>
<li>&lt;span style="color:green"&gt;面向连接&lt;/span&gt;的通信协议</li>
<li>速度慢，没有大小限制，数据安全</li>
<li>通过&lt;span style="color:red"&gt;三次握手&lt;/span&gt;建立连接</li>
<li>通过&lt;span style="color:red"&gt;四次挥手&lt;/span&gt;断开连接</li>
<li>&lt;span style="color:green"&gt;典型应用&lt;/span&gt;：下载软件</li>
</ul>
</blockquote>
<pre><code>flowchart LR
  subgraph 三次握手
    direction TB
    A1["客户端"] --&gt;|"① 连接请求"| B1["服务器"]
    B1 --&gt;|"② 响应"| A1
    A1 --&gt;|"③ 确认"| B1
  end
  subgraph 四次挥手
    direction TB
    A2["客户端"] --&gt;|"① 取消请求"| B2["服务器"]
    B2 --&gt;|"② 响应"| A2
    B2 --&gt;|"③ 处理数据后确认"| A2
    A2 --&gt;|"④ 确认"| B2
  end
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;小结&lt;/span&gt;：网络通信三要素是 IP 地址、端口、协议，&lt;span style="color:green"&gt;三者缺一不可&lt;/span&gt;。</p>
</blockquote>
<h3>三、UDP 协议收发数据</h3>
<h4>3.1 UDP 协议发送数据</h4>
<blockquote>
<p>类比快递流程：① 找快递公司 → ② 打包礼物 → ③ 快递公司发送包裹 → ④ 付钱走人。</p>
</blockquote>
<table>
<thead>
<tr>
<th>步骤</th>
<th>类比</th>
<th>代码对应</th>
</tr>
</thead>
<tbody>
<tr>
<td>① 找快递公司</td>
<td>找快递公司</td>
<td>&lt;span style="color:green"&gt;创建发送端的 <code>DatagramSocket</code> 对象&lt;/span&gt;</td>
</tr>
<tr>
<td>② 打包礼物</td>
<td>打包礼物</td>
<td>&lt;span style="color:green"&gt;数据打包（<code>DatagramPacket</code>）&lt;/span&gt;</td>
</tr>
<tr>
<td>③ 快递公司发送包裹</td>
<td>快递公司发送包裹</td>
<td>&lt;span style="color:green"&gt;发送数据&lt;/span&gt;</td>
</tr>
<tr>
<td>④ 付钱走人</td>
<td>付钱走人</td>
<td>&lt;span style="color:green"&gt;释放资源&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<pre><code>import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class UDPSend {
    public static void main(String[] args) throws Exception {
        // ① 创建发送端的 DatagramSocket 对象
        DatagramSocket ds = new DatagramSocket();

        // ② 打包礼物（数据）
        String msg = "你好 UDP";
        byte[] bytes = msg.getBytes();
        InetAddress address = InetAddress.getByName("127.0.0.1");
        int port = 10086;

        DatagramPacket dp = new DatagramPacket(bytes, bytes.length, address, port);

        // ③ 发送数据
        ds.send(dp);

        // ④ 释放资源
        ds.close();
    }
}
</code></pre>
<h4>3.2 UDP 协议接收数据</h4>
<blockquote>
<p>类比快递流程：① 找快递公司 → ② 接收箱子 → ③ 从箱子里面获取礼物 → ④ 签收走人。</p>
</blockquote>
<table>
<thead>
<tr>
<th>步骤</th>
<th>类比</th>
<th>代码对应</th>
</tr>
</thead>
<tbody>
<tr>
<td>① 找快递公司</td>
<td>找快递公司</td>
<td>&lt;span style="color:green"&gt;创建接收端的 <code>DatagramSocket</code> 对象&lt;/span&gt;</td>
</tr>
<tr>
<td>② 接收箱子</td>
<td>接收箱子</td>
<td>&lt;span style="color:green"&gt;接收打包好的数据&lt;/span&gt;</td>
</tr>
<tr>
<td>③ 从箱子里面获取礼物</td>
<td>从箱子里面获取礼物</td>
<td>&lt;span style="color:green"&gt;解析数据包&lt;/span&gt;</td>
</tr>
<tr>
<td>④ 签收走人</td>
<td>签收走人</td>
<td>&lt;span style="color:green"&gt;释放资源&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<pre><code>import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class UDPReceive {
    public static void main(String[] args) throws Exception {
        // ① 创建接收端的 DatagramSocket 对象
        // 必须指定端口号，跟发送端一致
        DatagramSocket ds = new DatagramSocket(10086);

        // ② 接收打包好的数据
        byte[] bytes = new byte[1024];
        DatagramPacket dp = new DatagramPacket(bytes, bytes.length);

        // 该方法是阻塞的，会一直等发送端发送数据
        ds.receive(dp);

        // ③ 解析数据包
        byte[] data = dp.getData();
        int len = dp.getLength();
        String msg = new String(data, 0, len);
        System.out.println("数据是：" + msg);

        // ④ 释放资源
        ds.close();
    }
}
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;小结&lt;/span&gt;：UDP 协议收发数据</p>
<ul>
<li>使用 <code>DatagramSocket</code> 搭建站点</li>
<li>使用 <code>DatagramPacket</code> 打包数据</li>
<li><code>send</code> 方法用于发送</li>
<li><code>receive</code> 方法用于接收</li>
</ul>
</blockquote>
<h3>四、TCP 协议收发数据</h3>
<h4>4.1 TCP 通信程序</h4>
<blockquote>
<p>TCP 通信严格区分&lt;span style="color:green"&gt;客户端&lt;/span&gt;和&lt;span style="color:green"&gt;服务端&lt;/span&gt;。</p>
</blockquote>
<h5>4.1.1 客户端</h5>
<table>
<thead>
<tr>
<th>步骤</th>
<th>说明</th>
<th>API</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>创建 <code>Socket</code> 对象指定 IP 和端口</td>
<td><code>Socket(String host, int port)</code></td>
</tr>
<tr>
<td>2</td>
<td>通过 socket 对象获取传输数据的流对象</td>
<td><code>OutputStream getOutputStream()</code>、<code>InputStream getInputStream()</code></td>
</tr>
<tr>
<td>3</td>
<td>通过流对象收发数据</td>
<td>—</td>
</tr>
<tr>
<td>4</td>
<td>释放资源</td>
<td>—</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:orange"&gt;提示&lt;/span&gt;：这里的流对象不是读写文件中的数据</p>
<ul>
<li>&lt;span style="color:green"&gt;输出流&lt;/span&gt;：写出数据到服务端</li>
<li>&lt;span style="color:green"&gt;输入流&lt;/span&gt;：读取服务端发送过来的数据</li>
</ul>
</blockquote>
<h5>4.1.2 服务端</h5>
<table>
<thead>
<tr>
<th>步骤</th>
<th>说明</th>
<th>API</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>创建 <code>ServerSocket</code> 对象指定端口</td>
<td><code>ServerSocket(int port)</code></td>
</tr>
<tr>
<td>2</td>
<td>响应客户端发送的请求</td>
<td><code>Socket accept()</code></td>
</tr>
<tr>
<td>3</td>
<td>通过 socket 对象获取传输数据的流对象</td>
<td><code>OutputStream getOutputStream()</code>、<code>InputStream getInputStream()</code></td>
</tr>
<tr>
<td>4</td>
<td>通过流对象收发数据</td>
<td>—</td>
</tr>
<tr>
<td>5</td>
<td>释放资源</td>
<td>—</td>
</tr>
</tbody>
</table>
<pre><code>// ================ 客户端 ================
import java.io.OutputStream;
import java.net.Socket;

public class TCPClient {
    public static void main(String[] args) throws Exception {
        // 1. 创建 Socket 对象指定 IP 和端口
        Socket socket = new Socket("127.0.0.1", 10086);

        // 2. 通过 socket 对象获取输出流
        OutputStream os = socket.getOutputStream();

        // 3. 写出数据
        os.write("你好 TCP".getBytes());

        // 4. 释放资源
        os.close();
        socket.close();
    }
}

// ================ 服务端 ================
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TCPServer {
    public static void main(String[] args) throws Exception {
        // 1. 创建 ServerSocket 对象指定端口
        ServerSocket ss = new ServerSocket(10086);

        // 2. 响应客户端发送的请求，得到 Socket 对象
        Socket socket = ss.accept();

        // 3. 通过 socket 对象获取输入流
        InputStream is = socket.getInputStream();

        // 4. 读取数据
        byte[] bytes = new byte[1024];
        int len = is.read(bytes);
        String msg = new String(bytes, 0, len);
        System.out.println("客户端发来：" + msg);

        // 5. 释放资源
        is.close();
        socket.close();
        ss.close();
    }
}
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;小结&lt;/span&gt;：TCP 协议区分客户端和服务端</p>
<ul>
<li>客户端创建 <code>Socket</code> 对象，指定 IP 和端口号</li>
<li>服务端创建 <code>ServerSocket</code> 对象，指定端口号</li>
<li>调用 <code>accept()</code> 响应请求，得到 <code>Socket</code> 对象</li>
<li>两端通过 <code>Socket</code> 对象获取输入输出流，收发数据</li>
</ul>
</blockquote>
<h2>第二十章：Java 高级技术</h2>
<blockquote>
<p>本章是 Java 高级特性的综合章节，涵盖<strong>枚举、类加载器、反射、注解、动态代理</strong>等核心技术。这些技术是<strong>框架的基石</strong>，掌握它们可以更深入地理解框架的运行原理。</p>
</blockquote>
<h3>一、枚举</h3>
<h4>1.1 枚举介绍</h4>
<blockquote>
<p><strong>枚举是 Java 中的一种特殊类型</strong>，一般用来做<strong>信息的标记和分类</strong>。例如用标记来区分春、夏、秋、冬四个季节。</p>
</blockquote>
<p><strong>传统方式 vs 枚举方式</strong>：</p>
<pre><code>// 传统方式：使用 public static final 常量
class Season {
    public static final int SPRING = 1;
    public static final int SUMMER = 2;
    public static final int AUTUMN = 3;
    public static final int WINTER = 4;
}

// 枚举方式：使用 enum 关键字
enum Season {
    SPRING, SUMMER, AUTUMN, WINTER;
}
</code></pre>
<h4>1.2 枚举定义格式</h4>
<blockquote>
<p>&lt;span style="color:green"&gt;枚举本质上是一个类&lt;/span&gt;，但比类多了几个特殊限制。</p>
</blockquote>
<pre><code>修饰符 enum 枚举类名 {
    枚举项1, 枚举项2, 枚举项3...;
}
</code></pre>
<pre><code>public enum Season {
    SPRING, SUMMER, AUTUMN, WINTER;
}
</code></pre>
<h4>1.3 枚举的特点</h4>
<table>
<thead>
<tr>
<th>特点</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>每一个枚举项其实就是该枚举的&lt;span style="color:green"&gt;一个对象&lt;/span&gt;</td>
<td>默认 public static final 修饰</td>
</tr>
<tr>
<td>通过&lt;span style="color:green"&gt;枚举类名&lt;/span&gt;去访问指定的枚举项</td>
<td><code>Season.SPRING</code></td>
</tr>
<tr>
<td>所有枚举类都是 <code>Enum</code> 的子类</td>
<td>继承自 <code>java.lang.Enum</code></td>
</tr>
<tr>
<td>枚举也是类，可以定义成员变量</td>
<td>—</td>
</tr>
<tr>
<td>枚举类第一行必须是枚举项</td>
<td>最后一个枚举项后的分号可以省略，但有其他内容时不能省略</td>
</tr>
<tr>
<td>枚举类可以有构造器，但必须是 <code>private</code> 的</td>
<td>&lt;span style="color:red"&gt;默认也是 private&lt;/span&gt;</td>
</tr>
<tr>
<td>枚举类可以有抽象方法</td>
<td>但&lt;span style="color:red"&gt;枚举项必须重写&lt;/span&gt;该方法</td>
</tr>
</tbody>
</table>
<h4>1.4 枚举的使用场景</h4>
<blockquote>
<p>枚举最适合用于<strong>做信息的标记和分类</strong>。</p>
</blockquote>
<p><strong>场景</strong>：商品状态有 <code>在售</code>/<code>预售</code>/<code>售罄</code> 三种情况。</p>
<pre><code>// 传统方式：定义多个常量
public static final int PRE_SALE = 1;  // 预售
public static final int SALE = 2;      // 在售
public static final int SELLOUT = 3;   // 售罄

// 枚举方式：一目了然
enum GoodState {
    PRE_SALE,   // 预售
    SALE,       // 在售
    SELLOUT     // 售罄
}
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;小结&lt;/span&gt;：使用枚举做信息的标记和分类，&lt;span style="color:green"&gt;代码可读性更高&lt;/span&gt;，&lt;span style="color:green"&gt;入参约束严谨&lt;/span&gt;，&lt;span style="color:green"&gt;代码优雅&lt;/span&gt;。</p>
</blockquote>
<h3>二、类加载器</h3>
<h4>2.1 类加载器介绍</h4>
<blockquote>
<p><strong>类加载器</strong>负责将类的字节码载入<strong>方法区</strong>中。加载时机为：&lt;span style="color:green"&gt;用到就加载&lt;/span&gt;。</p>
</blockquote>
<pre><code>flowchart LR
  A[Java 源文件] --&gt;|javac 编译| B[class 字节码文件]
  B --&gt;|类加载器载入| C[方法区]
</code></pre>
<p><strong>加载时机</strong>（满足以下任一条件即可）：</p>
<table>
<thead>
<tr>
<th>条件</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:green"&gt;创建对象&lt;/span&gt;</td>
<td><code>Student stu = new Student();</code></td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;调用类的静态成员&lt;/span&gt;</td>
<td><code>System.out.println(A.num);</code></td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;初始化继承体系&lt;/span&gt;</td>
<td><code>Zi z = new Zi();</code>（<code>Zi extends Fu</code>）</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;使用反射加载类的字节码&lt;/span&gt;</td>
<td><code>Class.forName("com.xxx");</code></td>
</tr>
</tbody>
</table>
<h4>2.2 类加载器的加载过程</h4>
<blockquote>
<p>类的加载过程分三步：&lt;span style="color:purple"&gt;加载&lt;/span&gt; → &lt;span style="color:red"&gt;链接&lt;/span&gt;（验证、准备、解析） → &lt;span style="color:blue"&gt;初始化&lt;/span&gt;。</p>
</blockquote>
<pre><code>flowchart LR
  A[加载] --&gt;|找到 class| B[链接]
  B --&gt; C[验证]
  C --&gt; D[准备]
  D --&gt; E[解析]
  E --&gt; F[初始化]
</code></pre>
<table>
<thead>
<tr>
<th>阶段</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:purple"&gt;加载&lt;/span&gt;</td>
<td>通过包名 + 类名获取这个类，准备用流进行传输；将类加载到内存中；加载完毕创建一个 <code>Class</code> 对象</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;链接 - 验证&lt;/span&gt;</td>
<td>验证类是否符合 JVM 规范，&lt;span style="color:green"&gt;安全性检查&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;链接 - 准备&lt;/span&gt;</td>
<td>为 <code>static</code> 变量分配空间，&lt;span style="color:green"&gt;设置默认值&lt;/span&gt;（如 <code>String school = null</code>）</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;链接 - 解析&lt;/span&gt;</td>
<td>将常量池中的&lt;span style="color:green"&gt;符号引用&lt;/span&gt;解析为&lt;span style="color:green"&gt;直接引用&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;初始化&lt;/span&gt;</td>
<td>根据程序员程序编码制定的主观计划去初始化类变量和其他资源（如 <code>String school = "传智大学"</code>）</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:orange"&gt;小工具&lt;/span&gt;：<code>jps</code> 是 JDK 提供的一个查看当前 Java 进程的小工具。<code>HSDB</code>（Hotspot Debugger）是一款内置于 SA 中的 GUI 调试工具，可用于调试 JVM 运行时数据。启动命令：</p>
<pre><code>java -cp ./lib/sa-jdi.jar sun.jvm.hotspot.HSDB
</code></pre>
</blockquote>
<h4>2.3 类加载器的分类</h4>
<table>
<thead>
<tr>
<th>类加载器</th>
<th>说明</th>
<th>负责范围</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;Bootstrap class loader&lt;/span&gt;</td>
<td>&lt;span style="color:green"&gt;启动类加载器&lt;/span&gt;，虚拟机的内置类加载器，&lt;span style="color:red"&gt;通常表示为 null&lt;/span&gt;</td>
<td>C++ 实现，获取到的只能是 null</td>
</tr>
<tr>
<td>&lt;span style="color:orange"&gt;Platform class loader&lt;/span&gt;</td>
<td>&lt;span style="color:green"&gt;平台类加载器&lt;/span&gt;，负责加载 JDK 中一些特殊的模块（JDK9 之前是 Extension Class Loader）</td>
<td>负责加载 <code>lib\modules</code> 内部的类（JDK9 之前是 <code>jre\lib\ext</code> 目录下的类）</td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;Application class loader&lt;/span&gt;</td>
<td>&lt;span style="color:green"&gt;应用程序类加载器&lt;/span&gt;</td>
<td>负责加载自己写的类</td>
</tr>
<tr>
<td>自定义类加载器</td>
<td>上级为 Application</td>
<td>目前不做讲解</td>
</tr>
</tbody>
</table>
<p><strong>获取类加载器</strong>：</p>
<table>
<thead>
<tr>
<th>方法</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>public ClassLoader getClassLoader()</code></td>
<td>获取该类的类加载器对象</td>
</tr>
</tbody>
</table>
<h4>2.4 双亲委派模式</h4>
<blockquote>
<p><strong>双亲委派模式</strong>是 Java 类加载器的核心机制。</p>
</blockquote>
<pre><code>flowchart TB
  Start["开始 AppClassLoader"] --&gt; A1{"是否加载过?"}
  A1 --&gt;|是| Loaded["已经加载过 不再加载"]
  A1 --&gt;|否| A2{"是否可以加载?"}
  A2 --&gt;|否| P1["向上委托 PlatformClassLoader"]
  A2 --&gt;|是| Done["自己加载此类"]
  P1 --&gt; P2{"是否加载过?"}
  P2 --&gt;|是| Loaded
  P2 --&gt;|否| P3{"是否可以加载?"}
  P3 --&gt;|否| B1["向上委托 BootStrapClassLoader"]
  P3 --&gt;|是| Done
  B1 --&gt; B2{"是否加载过?"}
  B2 --&gt;|是| Loaded
  B2 --&gt;|否| B3{"是否可以加载?"}
  B3 --&gt;|否| CNF["ClassNotFoundException"]
  B3 --&gt;|是| Done
</code></pre>
<p><strong>双亲委派模式的原则</strong>：</p>
<ol>
<li>如果一个类加载器收到了类加载请求，&lt;span style="color:green"&gt;它并不会自己先去加载&lt;/span&gt;</li>
<li>而是把这个请求&lt;span style="color:green"&gt;委托给父类的加载器&lt;/span&gt;去执行</li>
<li>如果父类加载器还存在其父类加载器，则进一步向上委托，&lt;span style="color:green"&gt;依次递归&lt;/span&gt;</li>
<li>请求最终将到达&lt;span style="color:red"&gt;顶层的启动类加载器&lt;/span&gt;</li>
<li>如果父类加载器可以完成类加载任务，就&lt;span style="color:green"&gt;成功返回&lt;/span&gt;</li>
<li>倘若父类加载器无法完成此加载任务，&lt;span style="color:green"&gt;子加载器才会尝试自己去加载&lt;/span&gt;，这就是双亲委派模式</li>
<li>&lt;span style="color:green"&gt;避免类的重复加载&lt;/span&gt;</li>
</ol>
<blockquote>
<p>&lt;span style="color:orange"&gt;小结&lt;/span&gt;：</p>
<ul>
<li><strong>类加载器</strong>负责将类的字节码载入到方法区中</li>
<li><strong>加载时机</strong>：用到就加载</li>
<li><strong>加载过程</strong>：&lt;span style="color:purple"&gt;加载&lt;/span&gt; → &lt;span style="color:red"&gt;链接（验证、准备、解析）&lt;/span&gt; → &lt;span style="color:blue"&gt;初始化&lt;/span&gt;</li>
</ul>
</blockquote>
<h3>三、反射</h3>
<h4>3.1 反射介绍</h4>
<blockquote>
<p><strong>反射</strong>是<strong>框架技术的灵魂</strong>。反射是在<strong>运行状态中</strong>，对于任意一个类，都能够知道这个类的所有属性和方法；对于任意一个对象，都能够调用它的任意属性和方法。这种<strong>动态获取信息以及动态调用对象方法的功能</strong>称为 Java 语言的反射机制。</p>
</blockquote>
<blockquote>
<p>&lt;span style="color:orange"&gt;理解&lt;/span&gt;：注释是给人看的，&lt;span style="color:green"&gt;注解是给虚拟机看的&lt;/span&gt;。反射操作的是&lt;span style="color:green"&gt;字节码对象&lt;/span&gt;。</p>
</blockquote>
<pre><code>flowchart LR
  Byte["字节码对象"] --&gt;|"解剖"| A1["构造方法对象"]
  Byte --&gt; A2["成员变量对象"]
  Byte --&gt; A3["成员方法对象"]
  A1 --&gt;|"创建"| A1a["创建对象"]
  A2 --&gt;|"赋值、获取"| A2a["赋值 获取"]
  A3 --&gt;|"调用"| A3a["调用"]
</code></pre>
<h4>3.2 反射技术的学习路径</h4>
<ol>
<li>&lt;span style="color:green"&gt;获取类的字节码对象&lt;/span&gt;</li>
<li>反射类中的构造方法，随后创建对象</li>
<li>反射类中的成员变量，完成赋值和获取</li>
<li>反射类中的成员方法，调用成员方法</li>
</ol>
<h4>3.3 获取类的字节码对象</h4>
<blockquote>
<p>获取 <code>Class</code> 对象有&lt;span style="color:green"&gt;三种方式&lt;/span&gt;。</p>
</blockquote>
<table>
<thead>
<tr>
<th>方式</th>
<th>说明</th>
<th>适用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:green"&gt;<code>Class.forName("全类名")</code>&lt;/span&gt;</td>
<td>&lt;span style="color:green"&gt;全类名（包名+类名）&lt;/span&gt;</td>
<td>编译时还不知道类名（&lt;span style="color:red"&gt;最常用&lt;/span&gt;）</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;<code>类名.class</code>&lt;/span&gt;</td>
<td>通过类名直接获取</td>
<td>编译时已确定类名</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;<code>对象.getClass()</code>&lt;/span&gt;</td>
<td>通过对象获取</td>
<td>已有对象时使用</td>
</tr>
</tbody>
</table>
<pre><code>// 1. Class.forName（最常用）
Class&lt;?&gt; clazz1 = Class.forName("com.itheima.Student");

// 2. 类名.class
Class&lt;?&gt; clazz2 = Student.class;

// 3. 对象.getClass()
Student stu = new Student();
Class&lt;?&gt; clazz3 = stu.getClass();
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;小结&lt;/span&gt;：反射是框架技术的灵魂，在运行状态中可以操作任意类中的任意属性任意方法；反射操作的是字节码对象；获取字节码对象的三种方式见上表。</p>
</blockquote>
<h4>3.4 反射类中的构造方法</h4>
<table>
<thead>
<tr>
<th>方法</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:green"&gt;<code>Constructor&lt;?&gt;[] getConstructors()</code>&lt;/span&gt;</td>
<td>返回所有&lt;span style="color:green"&gt;公共构造方法&lt;/span&gt;对象的数组</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;<code>Constructor&lt;?&gt;[] getDeclaredConstructors()</code>&lt;/span&gt;</td>
<td>返回所有构造方法对象的数组</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;<code>Constructor&lt;T&gt; getConstructor(Class&lt;?&gt;... parameterTypes)</code>&lt;/span&gt;</td>
<td>返回单个&lt;span style="color:green"&gt;公共构造方法&lt;/span&gt;对象</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;<code>Constructor&lt;T&gt; getDeclaredConstructor(Class&lt;?&gt;... parameterTypes)</code>&lt;/span&gt;</td>
<td>返回单个构造方法对象</td>
</tr>
</tbody>
</table>
<p><strong>Constructor 类用于创建对象的方法</strong>：</p>
<table>
<thead>
<tr>
<th>方法</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:green"&gt;<code>T newInstance(Object... initargs)</code>&lt;/span&gt;</td>
<td>根据指定的构造方法创建对象</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;<code>setAccessible(boolean flag)</code>&lt;/span&gt;</td>
<td>设置为 true，&lt;span style="color:green"&gt;表示取消访问检查&lt;/span&gt;（暴力反射）</td>
</tr>
</tbody>
</table>
<pre><code>// 通过反射获取无参构造方法并创建对象
Class&lt;?&gt; clazz = Class.forName("com.itheima.Student");
Constructor&lt;?&gt; con = clazz.getDeclaredConstructor();
con.setAccessible(true);  // 取消访问检查
Object obj = con.newInstance();
</code></pre>
<blockquote>
<p>&lt;span style="color:red"&gt;⚠ 反射建议&lt;/span&gt;：&lt;span style="color:red"&gt;禁止暴力反射操作&lt;/span&gt;（生产环境慎用 <code>setAccessible(true)</code>）。</p>
</blockquote>
<h4>3.5 反射类中的成员变量</h4>
<table>
<thead>
<tr>
<th>方法</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:green"&gt;<code>Field[] getFields()</code>&lt;/span&gt;</td>
<td>返回所有&lt;span style="color:green"&gt;公共成员变量&lt;/span&gt;对象的数组</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;<code>Field[] getDeclaredFields()</code>&lt;/span&gt;</td>
<td>返回所有成员变量对象的数组</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;<code>Field getField(String name)</code>&lt;/span&gt;</td>
<td>返回单个&lt;span style="color:green"&gt;公共成员变量&lt;/span&gt;对象</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;<code>Field getDeclaredField(String name)</code>&lt;/span&gt;</td>
<td>返回单个成员变量对象</td>
</tr>
</tbody>
</table>
<p><strong>Field 类的设置和获取方法</strong>：</p>
<table>
<thead>
<tr>
<th>方法</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:green"&gt;<code>void set(Object obj, Object value)</code>&lt;/span&gt;</td>
<td>&lt;span style="color:green"&gt;赋值&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;<code>Object get(Object obj)</code>&lt;/span&gt;</td>
<td>&lt;span style="color:green"&gt;获取值&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<pre><code>Class&lt;?&gt; clazz = Class.forName("com.itheima.Student");
Object obj = clazz.getDeclaredConstructor().newInstance();

// 反射获取 name 成员变量并赋值
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(obj, "张三");
String name = (String) nameField.get(obj);
System.out.println(name);  // 张三
</code></pre>
<h4>3.6 反射类中的成员方法</h4>
<table>
<thead>
<tr>
<th>方法</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:green"&gt;<code>Method[] getMethods()</code>&lt;/span&gt;</td>
<td>返回所有公共成员方法对象的数组，&lt;span style="color:green"&gt;包括继承的&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;<code>Method[] getDeclaredMethods()</code>&lt;/span&gt;</td>
<td>返回所有成员方法对象的数组，&lt;span style="color:red"&gt;不包括继承的&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;<code>Method getMethod(String name, Class&lt;?&gt;... parameterTypes)</code>&lt;/span&gt;</td>
<td>返回单个公共成员方法对象</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;<code>Method getDeclaredMethod(String name, Class&lt;?&gt;... parameterTypes)</code>&lt;/span&gt;</td>
<td>返回单个成员方法对象</td>
</tr>
</tbody>
</table>
<p><strong>Method 类用于执行方法的方法</strong>：</p>
<table>
<thead>
<tr>
<th>方法</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:green"&gt;<code>Object invoke(Object obj, Object... args)</code>&lt;/span&gt;</td>
<td>&lt;span style="color:green"&gt;运行方法&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<pre><code>Class&lt;?&gt; clazz = Class.forName("com.itheima.Student");
Object obj = clazz.getDeclaredConstructor().newInstance();

// 反射调用 study 方法
Method method = clazz.getDeclaredMethod("study");
method.setAccessible(true);
method.invoke(obj);  // 调用 study()
</code></pre>
<h4>3.7 反射练习</h4>
<blockquote>
<p><strong>练习 1（伪泛型）</strong>：请向一个泛型为 <code>Integer</code> 的集合，添加一个 <code>String</code> 字符串。
&lt;span style="color:orange"&gt;思路&lt;/span&gt;：Java 中的泛型是假的，只在编译的时候有效。</p>
</blockquote>
<pre><code>ArrayList&lt;Integer&gt; list = new ArrayList&lt;&gt;();
list.add(1);
list.add(2);

// 通过反射绕过泛型检查
Class&lt;?&gt; clazz = list.getClass();
Method add = clazz.getMethod("add", Object.class);
add.invoke(list, "hello");

System.out.println(list);  // [1, 2, hello]
</code></pre>
<blockquote>
<p><strong>练习 2（配置文件 + 反射）</strong>：编写 <code>Student</code>/<code>Teacher</code>/<code>Worker</code> 三个 JavaBean（属性均为 <code>name</code>、<code>age</code>），通过修改配置文件实现不修改代码切换调用不同类的不同方法。</p>
</blockquote>
<pre><code># config.properties
className=com.itheima.Student
methodName=study
</code></pre>
<pre><code>public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        // 1. 读取配置文件
        Properties prop = new Properties();
        prop.load(new FileInputStream("config.properties"));
        String className = prop.getProperty("className");
        String methodName = prop.getProperty("methodName");

        // 2. 通过反射创建对象
        Class&lt;?&gt; clazz = Class.forName(className);
        Constructor&lt;?&gt; con = clazz.getDeclaredConstructor();
        Object obj = con.newInstance();

        // 3. 通过反射调用方法
        Method method = clazz.getDeclaredMethod(methodName);
        method.invoke(obj);
    }
}
</code></pre>
<h3>四、注解</h3>
<h4>4.1 注解介绍</h4>
<blockquote>
<p><strong>Annotation</strong> 表示注解，是 &lt;span style="color:red"&gt;JDK 1.5&lt;/span&gt; 的新特性。注解的主要作用是&lt;span style="color:green"&gt;对程序进行标注&lt;/span&gt;。</p>
</blockquote>
<blockquote>
<p>&lt;span style="color:orange"&gt;理解&lt;/span&gt;：</p>
<ul>
<li>注释是给人看的，&lt;span style="color:green"&gt;注解是给虚拟机看的&lt;/span&gt;</li>
<li>通过注解可以给类增加额外的信息</li>
<li>编译器或 JVM 可以根据注解来完成对应的功能</li>
</ul>
</blockquote>
<p><strong>JDK 中常见注解</strong>：</p>
<table>
<thead>
<tr>
<th>注解</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:green"&gt;<code>@Override</code>&lt;/span&gt;</td>
<td>表示方法的重写（&lt;span style="color:red"&gt;编译时检查&lt;/span&gt;）</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;<code>@Deprecated</code>&lt;/span&gt;</td>
<td>表示修饰的方法已过时</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;<code>@SuppressWarnings("all")</code>&lt;/span&gt;</td>
<td>&lt;span style="color:green"&gt;压制警告&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<h4>4.2 自定义注解</h4>
<blockquote>
<p>&lt;span style="color:orange"&gt;重要&lt;/span&gt;：自定义注解&lt;span style="color:red"&gt;单独存在意义不大&lt;/span&gt;，一般会跟&lt;span style="color:green"&gt;反射结合起来&lt;/span&gt;使用。</p>
</blockquote>
<h5>4.2.1 自定义注解格式</h5>
<pre><code>public @interface 注解名称 {
    public 属性类型 属性名() default 默认值;
}
</code></pre>
<pre><code>public @interface Anno {
    String show() default "show...";
}
</code></pre>
<p><strong>属性类型</strong>：</p>
<table>
<thead>
<tr>
<th>类型</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:green"&gt;基本数据类型&lt;/span&gt;</td>
<td><code>int</code>、<code>boolean</code> 等</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;String&lt;/span&gt;</td>
<td>字符串</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;Class&lt;/span&gt;</td>
<td>类对象</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;注解&lt;/span&gt;</td>
<td>嵌套注解</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;枚举&lt;/span&gt;</td>
<td><code>enum</code> 类型</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;以上类型的一维数组&lt;/span&gt;</td>
<td>如 <code>int[]</code>、<code>String[]</code></td>
</tr>
</tbody>
</table>
<p><strong>完整示例</strong>：</p>
<pre><code>public @interface MyAnno {
    public static final int num1 = 100;
    public static final String num2 = "abc";
    public static final MyAnno num3 = null;
    public static final Class num4 = String.class;
    public static final int[] num5 = {};

    public abstract String show1() default "show1";
    public abstract int show2() default 132;
    public abstract MyAnno2 show3() default @MyAnno2;
    public abstract Class show4() default String.class;
    public abstract int[] show5() default {1, 2, 3};
}
</code></pre>
<h5>4.2.2 自定义注解使用</h5>
<p><strong>常用位置</strong>：</p>
<ol>
<li>&lt;span style="color:green"&gt;类&lt;/span&gt;</li>
<li>&lt;span style="color:green"&gt;方法&lt;/span&gt;</li>
</ol>
<blockquote>
<p>&lt;span style="color:red"&gt;⚠ 注意&lt;/span&gt;：</p>
<ul>
<li>在使用注解时，如果注解的属性没有给出默认值，&lt;span style="color:green"&gt;需要手动给出&lt;/span&gt;</li>
<li><code>@Anno(name="张三")</code></li>
<li>如果数组中只有一个属性值，在使用时 &lt;span style="color:green"&gt;<code>{}</code> 是可以省略的&lt;/span&gt;</li>
</ul>
</blockquote>
<h5>4.2.3 特殊的属性 value</h5>
<blockquote>
<p>如果只有一个属性名字为 <code>value</code> 没有赋值，使用时&lt;span style="color:green"&gt;直接给出值&lt;/span&gt;，不需要写属性名。</p>
</blockquote>
<pre><code>public @interface SuppressWarnings {
    String[] value();
}

// 简化写法
@SuppressWarnings("all")
public class MyClass { }
</code></pre>
<h4>4.3 元注解</h4>
<blockquote>
<p><strong>元注解</strong>就是用在&lt;span style="color:green"&gt;注解上&lt;/span&gt;的注解。</p>
</blockquote>
<table>
<thead>
<tr>
<th>元注解名</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:green"&gt;<code>@Target</code>&lt;/span&gt;</td>
<td>指定了注解能哪里使用</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;<code>@Retention</code>&lt;/span&gt;</td>
<td>可以理解为&lt;span style="color:green"&gt;保留时间（生命周期）&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<h5>4.3.1 @Target</h5>
<blockquote>
<p><code>@Target</code>：用来标识注解使用的位置，如果没有使用该注解标识，则自定义的注解可以使用在任意位置。</p>
</blockquote>
<p><strong><code>@Target</code> 可使用的值定义在 <code>ElementType</code> 枚举类中</strong>：</p>
<table>
<thead>
<tr>
<th>值</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:green"&gt;<code>TYPE</code>&lt;/span&gt;</td>
<td>&lt;span style="color:green"&gt;类，接口&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;<code>FIELD</code>&lt;/span&gt;</td>
<td>&lt;span style="color:green"&gt;成员变量&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;<code>METHOD</code>&lt;/span&gt;</td>
<td>&lt;span style="color:green"&gt;成员方法&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;<code>PARAMETER</code>&lt;/span&gt;</td>
<td>方法参数</td>
</tr>
<tr>
<td><code>CONSTRUCTOR</code></td>
<td>构造方法</td>
</tr>
<tr>
<td><code>LOCAL_VARIABLE</code></td>
<td>局部变量</td>
</tr>
</tbody>
</table>
<h5>4.3.2 @Retention</h5>
<blockquote>
<p><code>@Retention</code>：用来标识注解的生命周期（有效范围）。</p>
</blockquote>
<p><strong><code>@Retention</code> 可使用的值定义在 <code>RetentionPolicy</code> 枚举类中</strong>：</p>
<table>
<thead>
<tr>
<th>值</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>SOURCE</code></td>
<td>注解只作用在源码阶段，&lt;span style="color:red"&gt;生成的字节码文件中不存在&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;<code>CLASS</code>&lt;/span&gt;</td>
<td>注解作用在源码阶段，字节码文件阶段，&lt;span style="color:red"&gt;运行阶段不存在&lt;/span&gt;，&lt;span style="color:red"&gt;默认值&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;<code>RUNTIME</code>&lt;/span&gt;</td>
<td>注解作用在源码阶段，&lt;span style="color:red"&gt;字节码文件阶段，运行阶段&lt;/span&gt;（&lt;span style="color:green"&gt;反射需要使用&lt;/span&gt;）</td>
</tr>
</tbody>
</table>
<p><strong>自定义测试注解案例</strong>：</p>
<blockquote>
<p>需求：自定义一个注解 <code>@Test</code>，用于指定类的方法上，如果类中的某个方法上使用了该注解，就执行该方法。</p>
</blockquote>
<pre><code>// 1. 自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
}

// 2. 使用注解
public class UserTest {
    @Test
    public void test1() { System.out.println("test1 执行了"); }

    public void test2() { System.out.println("test2 没执行"); }
}

// 3. 通过反射执行
public static void main(String[] args) throws Exception {
    Class&lt;?&gt; clazz = UserTest.class;
    Object obj = clazz.getDeclaredConstructor().newInstance();
    for (Method method : clazz.getDeclaredMethods()) {
        if (method.isAnnotationPresent(Test.class)) {
            method.invoke(obj);
        }
    }
}
</code></pre>
<h3>五、动态代理</h3>
<h4>5.1 动态代理</h4>
<blockquote>
<p><strong>动态代理</strong>是对象的另外一种创建方式，&lt;span style="color:green"&gt;这种方式可以在不修改源码的情况下，增强方法&lt;/span&gt;。</p>
</blockquote>
<p><strong>创建对象的方式</strong>：</p>
<pre><code>Dog d = new Dog();                    // 1. new
Animal a = new Dog();                 // 2. 多态
Dog.class.getConstructor().newInstance();  // 3. 反射
</code></pre>
<h4>5.2 代理</h4>
<blockquote>
<p><strong>代理</strong>：&lt;span style="color:green"&gt;在原有功能基础上进行增强&lt;/span&gt;。例如：房产中介可以在房东原有售卖房产、房屋出租的功能基础上，额外提供代驾、喂养宠物等服务。</p>
</blockquote>
<pre><code>// 1. 共同的接口
public interface Sale {
    void sale(double money);
    void hire(double money);
}

// 2. 被代理对象（房东）
public class Owner implements Sale {
    @Override
    public void sale(double money) {
        System.out.println("售卖房产" + money + "元");
    }

    @Override
    public void hire(double money) {
        System.out.println("出租房产" + money + "元");
    }
}

// 3. 代理对象（中介）
public class SaleImpl implements Sale {
    private Owner owner;

    public SaleImpl(Owner owner) {
        this.owner = owner;
    }

    @Override
    public void sale(double money) {
        money += 20000;       // 代理对象对功能增强（中介费）
        owner.sale(money);    // 被代理对象处理执行原有功能
    }

    @Override
    public void hire(double money) {
        money += 1000;        // 代理对象对功能增强
        owner.hire(money);    // 被代理对象处理执行原有功能
    }
}
</code></pre>
<h4>5.3 JDK 动态代理</h4>
<blockquote>
<p>动态代理&lt;span style="color:green"&gt;在运行时动态生成代理类&lt;/span&gt;，&lt;span style="color:red"&gt;无需手动为每个目标类编写代理类&lt;/span&gt;。</p>
<p>Java 提供了 <code>java.lang.reflect.Proxy</code> 类，&lt;span style="color:green"&gt;提供了使用动态代理方式创建对象的 API&lt;/span&gt;。</p>
</blockquote>
<pre><code>static Object newProxyInstance(ClassLoader loader, Class&lt;?&gt;[] interfaces, InvocationHandler h)
</code></pre>
<p><strong>参数介绍</strong>：</p>
<table>
<thead>
<tr>
<th>参数</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td>参数一</td>
<td>用于指定用哪个&lt;span style="color:green"&gt;类加载器&lt;/span&gt;，去加载生成的实现类</td>
</tr>
<tr>
<td>参数二</td>
<td>指定&lt;span style="color:green"&gt;接口&lt;/span&gt;，这些接口用于指定生成的实现类中有什么，也就是有哪些方法</td>
</tr>
<tr>
<td>参数三</td>
<td>用来指定生成的实现类中&lt;span style="color:green"&gt;不同方法的实现方案&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<p><strong>完整示例</strong>：</p>
<pre><code>public class ProxyUtil {
    public static &lt;T&gt; T createProxy(T obj) {
        // 获取被代理对象的 ClassLoader
        ClassLoader loader = obj.getClass().getClassLoader();
        // 获取被代理对象实现的所有接口
        Class&lt;?&gt;[] interfaces = obj.getClass().getInterfaces();
        // 创建 InvocationHandler
        InvocationHandler h = (proxy, method, args) -&gt; {
            // 增强逻辑
            if (method.getName().equals("sale")) {
                System.out.println("收取中介费 20000 元");
            }
            // 调用被代理对象的原方法
            return method.invoke(obj, args);
        };
        // 返回代理对象
        return (T) Proxy.newProxyInstance(loader, interfaces, h);
    }
}

public static void main(String[] args) {
    Owner owner = new Owner();
    Sale sale = ProxyUtil.createProxy(owner);
    sale.sale(1000000);   // 输出：收取中介费 20000 元 / 售卖房产1020000.0元
    sale.hire(3000);      // 输出：出租房产3000.0元
}
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;小结&lt;/span&gt;：</p>
<ul>
<li>&lt;span style="color:green"&gt;动态代理&lt;/span&gt;是对象的另外一种创建方式，这种方式可以在不修改源码的情况下，增强方法</li>
<li>&lt;span style="color:green"&gt;代理对象&lt;/span&gt;可以在原有功能的基础上进行增强</li>
<li>&lt;span style="color:green"&gt;动态代理&lt;/span&gt;在运行时动态生成代理类，无需手动为每个目标类编写代理类</li>
<li>使用 <code>Proxy</code> 中的 &lt;span style="color:green"&gt;<code>newProxyInstance()</code>&lt;/span&gt; 方法创建代理对象</li>
</ul>
</blockquote>
]]></content>
    <author><name>仙鹤</name></author>
    <category term="编程"/>
  </entry>
  <entry>
    <title>大二上物理</title>
    <link href="https://xianhe51920.github.io/posts/physics-oscillation/" rel="alternate" type="text/html"/>
    <id>https://xianhe51920.github.io/posts/physics-oscillation/</id>
    <published>2026-09-06T00:00:00.000Z</published>
    <updated>2026-09-06T00:00:00.000Z</updated>
    <summary>大二物理：振动方程、能量与合成、机械波</summary>
    <content type="html"><![CDATA[<h1>大二上物理</h1>
<h2>课时一 振动学方程</h2>
<table>
<thead>
<tr>
<th>考点</th>
<th>重要程度</th>
<th>占分</th>
<th>常见题型</th>
</tr>
</thead>
<tbody>
<tr>
<td>1. 认识简谐运动</td>
<td>★★★★</td>
<td>3~5</td>
<td>选择 / 填空</td>
</tr>
<tr>
<td>2. 振动学方程</td>
<td>必考</td>
<td>5~10</td>
<td>大题</td>
</tr>
</tbody>
</table>
<h3>1. 认识简谐运动</h3>
<p>弹簧振子在水平方向上做往复运动，速度在两端最大位移处为零（$v=0$），在平衡位置处最大。可用旋转矢量法直观表示。</p>
<p>&lt;img src="/images/posts/physics-oscillation/assets/1-1-弹簧振子运动.svg" alt="弹簧振子多帧运动示意" style="width:30vw;display:block;margin:0 auto;"/&gt;</p>
<p>简谐运动的基本公式：</p>
<table>
<thead>
<tr>
<th>物理量</th>
<th>表达式</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>运动方程</td>
<td>$x = A\cos(\omega t + \varphi_0)$</td>
<td></td>
</tr>
<tr>
<td>振幅</td>
<td>$A = \sqrt{x_0^2 + \dfrac{v_0^2}{\omega^2}}$</td>
<td>由初始条件决定</td>
</tr>
<tr>
<td>角频率</td>
<td>$\omega = \sqrt{\dfrac{k}{m}} = \dfrac{2\pi}{T}$</td>
<td>弹簧振子</td>
</tr>
<tr>
<td>初相</td>
<td>$\varphi_0$</td>
<td>由初始条件决定</td>
</tr>
<tr>
<td>相位</td>
<td>$\omega t + \varphi_0$</td>
<td></td>
</tr>
<tr>
<td>周期</td>
<td>$T = \dfrac{2\pi}{\omega}$</td>
<td></td>
</tr>
<tr>
<td>频率</td>
<td>$\nu = \dfrac{1}{T} = \dfrac{\omega}{2\pi}$</td>
<td></td>
</tr>
<tr>
<td>速度</td>
<td>$v = \dfrac{dx}{dt} = -A\omega\sin(\omega t + \varphi_0)$，$v_{\max} = A\omega$</td>
<td></td>
</tr>
<tr>
<td>加速度</td>
<td>$a = \dfrac{dv}{dt} = -A\omega^2\cos(\omega t + \varphi_0)$，$a_{\max} = A\omega^2$</td>
<td></td>
</tr>
</tbody>
</table>
<blockquote>
<p>[!tip] 旋转矢量法
用一个长度为 $A$ 的旋转矢量以角速度 $\omega$ 逆时针旋转，矢量在 $x$ 轴上的投影即对应位移 $x$。矢量与 $x$ 轴的夹角即为相位 $\omega t + \varphi_0$，$t=0$ 时的夹角为初相 $\varphi_0$。</p>
<p>&lt;img src="/images/posts/physics-oscillation/assets/1-1-旋转矢量法.svg" alt="旋转矢量法示意" style="width:30vw;"/&gt;</p>
</blockquote>
<blockquote>
<p>[!tip] 弹簧串并联
弹簧串联：$\dfrac{1}{k} = \dfrac{1}{k_1} + \dfrac{1}{k_2}$
弹簧并联：$k = k_1 + k_2$</p>
<p>&lt;img src="/images/posts/physics-oscillation/assets/1-3-弹簧串并联.svg" alt="弹簧串并联模型" style="width:30vw;"/&gt;</p>
</blockquote>
<p><strong>题1.</strong> 一质点按如下规律沿 $x$ 轴作简谐运动：$x = 0.1\cos\left(8\pi t + \dfrac{2\pi}{3}\right)$ (SI)，求此振动的周期、振幅、初相、速度最大值和加速度最大值。</p>
<p><strong>解：</strong></p>
<ul>
<li>振幅 $A = 0.1,\text{m}$</li>
<li>角频率 $\omega = 8\pi$，$T = \dfrac{2\pi}{\omega} = \dfrac{2\pi}{8\pi} = 0.25,\text{s}$</li>
<li>初相 $\varphi_0 = \dfrac{2\pi}{3}$</li>
<li>速度最大值 $v_{\max} = A\omega = 0.1 \times 8\pi = 2.5,\text{m/s}$</li>
<li>加速度最大值 $a_{\max} = A\omega^2 = 0.1 \times (8\pi)^2 = 63.1,\text{m/s}^2$</li>
</ul>
<p><strong>题2.</strong> 质点做简谐运动，振动方程 $x = A\cos(\omega t + \varphi)$，当 $t = T/2$（$T$ 为周期）时，质点的速度为（  ）</p>
<p>$A.\ -A\omega\sin\varphi \quad B.\ A\omega\sin\varphi \quad C.\ -A\omega\cos\varphi \quad D.\ A\omega\cos\varphi \quad E.\ 0$</p>
<p><strong>答：B</strong></p>
<p><strong>解：</strong></p>
<p>$$v = \frac{dx}{dt} = -A\omega\sin(\omega t + \varphi)\Big|_{t=T/2}$$</p>
<p>$$= -A\omega\sin\left(\omega\cdot\frac{T}{2} + \varphi\right) = -A\omega\sin\left(\omega\cdot\frac{2\pi/\omega}{2} + \varphi\right)$$</p>
<p>$$= -A\omega\sin(\pi + \varphi) = A\omega\sin\varphi$$</p>
<p><strong>题3.</strong> 将倔强系数为 $k$ 的轻质弹簧截去一半，然后一端固定，另一端下挂质量为 $m$ 的小球，组成振动系统。那么该系统的频率是（  ）</p>
<p>$A.\ \dfrac{1}{4\pi}\sqrt{\dfrac{k}{m}} \quad B.\ \dfrac{1}{2\pi}\sqrt{\dfrac{k}{m}} \quad C.\ \dfrac{1}{2\sqrt{2}\pi}\sqrt{\dfrac{k}{m}} \quad D.\ \dfrac{\sqrt{2}}{2\pi}\sqrt{\dfrac{k}{m}}$</p>
<p><strong>答：D</strong></p>
<p><strong>解：</strong> 两段半弹簧并联：$\dfrac{1}{k'} + \dfrac{1}{k'} = \dfrac{1}{k} \Rightarrow k' = 2k$</p>
<p>$$\omega = \sqrt{\frac{k'}{m}} = \sqrt{\frac{2k}{m}}, \quad \nu = \frac{\omega}{2\pi} = \frac{\sqrt{2}}{2\pi}\sqrt{\frac{k}{m}}$$</p>
<h3>2. 振动学方程 $x = A\cos(\omega t + \varphi_0)$</h3>
<p><strong>题1.</strong> 已知一物体作简谐运动，周期为 $1,\text{s}$，振动曲线如图所示，求简谐运动的余弦表达式。</p>
<p>&lt;img src="/images/posts/physics-oscillation/assets/1-2-x-t曲线.svg" alt="振动曲线 x-t，A=4cm，周期 1s" style="width:30vw;display:block;margin:0 auto;"/&gt;</p>
<p><strong>解：</strong></p>
<ul>
<li>振幅 $A = 0.04,\text{m}$</li>
<li>周期 $T = 1,\text{s}$，$\omega = \dfrac{2\pi}{T} = 2\pi,\text{rad/s}$</li>
<li>由旋转矢量法得 $\varphi_0 = -\dfrac{\pi}{3}$</li>
</ul>
<p>$$x = 0.04\cos\left(2\pi t - \frac{\pi}{3}\right)$$</p>
<p><strong>题2.</strong> 一质点作简谐运动，振幅为 $A$，在起始位置时刻质点的位移为 $\dfrac{A}{2}$，且向 $x$ 轴的正方向运动，代表此简谐运动的旋转矢量图为：</p>
<p><strong>答：B</strong>（位移 $x = \dfrac{A}{2}$，且向 $+x$ 方向运动 ⟹ 矢量在第一象限，逆时针旋转）</p>
<p><strong>题3.</strong> 质点沿 $x$ 轴作简谐运动，用余弦函数表示，振幅为 $A$，当 $t=0$ 时，质点处于 $x_0 = -\dfrac{A}{2}$ 处且向 $x$ 轴负方向运动，则其初相为：</p>
<p>$A.\ \dfrac{\pi}{6} \quad B.\ \dfrac{2\pi}{3} \quad C.\ \dfrac{4\pi}{3} \quad D.\ \dfrac{\pi}{3}$</p>
<p><strong>答：B</strong></p>
<p><strong>解：</strong> 由旋转矢量法，$\varphi_0 = \dfrac{2\pi}{3}$（位于第二象限，对应 $x = -\dfrac{A}{2}$ 且向 $-x$ 方向旋转分量最大处）。</p>
<p>&lt;img src="/images/posts/physics-oscillation/assets/1-3-旋转矢量.svg" alt="旋转矢量 phi0=2pi/3" style="width:30vw;display:block;margin:0 auto;"/&gt;</p>
<p><strong>题4.</strong> 质点振动的 $x-t$ 曲线如图所示，求：</p>
<p>（1）质点的振动方程；</p>
<p>（2）质点从 $t=0$ 的位置到达 $P$ 点相应位置所需的最短时间。</p>
<p>&lt;img src="/images/posts/physics-oscillation/assets/1-4-x-t曲线-P点.svg" alt="x-t 曲线，P 点 0.10" style="width:30vw;display:block;margin:0 auto;"/&gt;</p>
<p><strong>解：</strong></p>
<p>（1）$A = 0.1$</p>
<p>由旋转矢量法知 $\varphi_0 = -\dfrac{\pi}{3}$，$x = 0.1\cos\left(\omega t - \dfrac{\pi}{3}\right)$</p>
<p>$0 = 0.1\cos\left(\omega t - \dfrac{\pi}{3}\right)$ 时 $\omega t - \dfrac{\pi}{3} = \dfrac{\pi}{2} \Rightarrow \omega = \dfrac{5\pi}{6}$</p>
<p>$$x = 0.1\cos\left(\frac{5\pi}{6}t - \frac{\pi}{3}\right)$$</p>
<p>（2）$t = 0$ 时 $\varphi_0 = -\dfrac{\pi}{3}$；$t = t_P$ 时 $\varphi_P = 0$</p>
<p>$$\Delta t = \frac{\Delta\varphi}{\omega} = \frac{\pi/3}{5\pi/6} = 0.4,\text{s}$$</p>
<p>&lt;img src="/images/posts/physics-oscillation/assets/1-4-旋转矢量-初相-π3.svg" alt="旋转矢量 phi0=-pi/3" style="width:30vw;display:block;margin:0 auto;"/&gt;</p>
<p><strong>题5.</strong> 如图所示，质量为 $1.0\times10^{-2},\text{kg}$ 的子弹，以 $500,\text{m/s}$ 的速度射入并嵌在木块中，同时使弹簧压缩而作简谐运动。设木块质量为 $4.99,\text{kg}$，弹簧的劲度系数为 $k = 8.00\times10^3,\text{N/m}$。若以弹簧原长时木块所在处为坐标原点，向右为轴正方向，求简谐运动方程。</p>
<p>&lt;img src="/images/posts/physics-oscillation/assets/1-5-子弹木块模型.svg" alt="子弹射入木块模型" style="width:30vw;display:block;margin:0 auto;"/&gt;</p>
<p><strong>解：</strong> 由动量守恒</p>
<p>$$mv = (M + m)v_0 \Rightarrow v_0 = \frac{m}{M + m}v = \frac{0.01}{0.01 + 4.99}\times 500 = 1,\text{m/s}$$</p>
<p>$$\omega = \sqrt{\frac{k}{M + m}} = \sqrt{\frac{8\times10^3}{0.01 + 4.99}} = 40,\text{rad/s}$$</p>
<p>$$A = \sqrt{x_0^2 + \frac{v_0^2}{\omega^2}} = \sqrt{0^2 + \frac{1^2}{40^2}} = 2.5\times10^{-2},\text{m}$$</p>
<p>由旋转矢量法知 $\varphi_0 = \dfrac{\pi}{2}$</p>
<p>$$x = 2.5\times10^{-2}\cos\left(40t + \frac{\pi}{2}\right)$$</p>
<p>&lt;img src="/images/posts/physics-oscillation/assets/1-5-旋转矢量.svg" alt="旋转矢量 phi0=pi/2" style="width:30vw;display:block;margin:0 auto;"/&gt;</p>
<hr />
<h2>课时二 振动的能量及合成</h2>
<table>
<thead>
<tr>
<th>考点</th>
<th>重要程度</th>
<th>占分</th>
<th>常见题型</th>
</tr>
</thead>
<tbody>
<tr>
<td>1. 振动的能量</td>
<td>★★★</td>
<td>0~2</td>
<td>选择 / 填空</td>
</tr>
<tr>
<td>2. 振动的合成</td>
<td>★★★</td>
<td>0~2</td>
<td>选择 / 填空</td>
</tr>
</tbody>
</table>
<h3>1. 振动的能量</h3>
<p>简谐运动的<strong>机械能守恒</strong>：</p>
<ul>
<li>机械能 = 动能 + 势能</li>
<li>$E = \dfrac{1}{2}mv^2 + \dfrac{1}{2}kx^2$</li>
<li>在最大位移处（$x=\pm A$，$v=0$）：动能 $E_k = 0$，势能 $E_p = \dfrac{1}{2}kA^2$</li>
<li>总机械能：$E = \dfrac{1}{2}kA^2$（<strong>不变</strong>）</li>
</ul>
<p>&lt;img src="/images/posts/physics-oscillation/assets/2-1-振动的能量.svg" alt="振动的能量" style="width:30vw;display:block;margin:0 auto;"/&gt;</p>
<p><strong>题1.</strong> 质点做简谐运动，从平衡位置运动到最大位移处时，质点的动能 ______，势能 ______，总的机械能 ______。（填增大、减小或不变）</p>
<p><strong>答：</strong> 减小，增大，不变</p>
<p><strong>解：</strong> 从平衡位置（$x=0$，$v$ 最大）到最大位移处（$x=\pm A$，$v=0$），动能 $E_k = \frac{1}{2}mv^2$ 减小，势能 $E_p = \frac{1}{2}kx^2$ 增大；总机械能 $E = \frac{1}{2}kA^2$ 守恒不变。</p>
<p><strong>题2.</strong> 一弹簧振子作简谐运动，当其偏离平衡位置的位移的大小为振幅的 $\dfrac{1}{4}$ 时，其动能为振动总能量的（　）。</p>
<p>$A.\ \dfrac{9}{16}\quad B.\ \dfrac{11}{16}\quad C.\ \dfrac{13}{16}\quad D.\ \dfrac{15}{16}$</p>
<p><strong>答：D</strong></p>
<p><strong>解：</strong></p>
<ul>
<li>势能 $E_p = \dfrac{1}{2}k\left(\dfrac{A}{4}\right)^2 = \dfrac{1}{32}kA^2$</li>
<li>总机械能 $E = \dfrac{1}{2}kA^2$</li>
<li>动能 $E_k = E - E_p = \dfrac{1}{2}kA^2 - \dfrac{1}{32}kA^2 = \dfrac{15}{32}kA^2$</li>
<li>$\dfrac{E_k}{E} = \dfrac{\dfrac{15}{32}kA^2}{\dfrac{1}{2}kA^2} = \dfrac{15}{16}$</li>
</ul>
<p><strong>题3.</strong> 有一水平的弹簧振子，弹簧的劲度系数 $k = 25,\text{N/m}$，物体质量 $m = 1.0,\text{kg}$，物体静止在平衡位置。设以水平向左的恒力 $F = 10,\text{N}$ 作用在物体上（不计一切摩擦），使其由平衡位置向左运动了 $0.05,\text{m}$，此时撤除力 $F$，当物体运动到最左边时开始计时，求物体的运动方程。</p>
<p><strong>解：</strong></p>
<ul>
<li>由机械能守恒 $F\cdot x = \dfrac{1}{2}kA^2$，即 $10\times0.05 = \dfrac{1}{2}\times25\times A^2 \Rightarrow A = 0.2,\text{m}$</li>
<li>角频率 $\omega = \sqrt{\dfrac{k}{m}} = \sqrt{\dfrac{25}{1}} = 5,\text{rad/s}$</li>
<li>由旋转矢量法知 $\varphi_0 = \pi$（物体从最左边 $x=-A$ 处开始计时）</li>
<li>运动方程：$x = 0.2\cos(5t + \pi)$</li>
</ul>
<h3>2. 振动的合成</h3>
<p><strong>同方向同频率</strong>的两个简谐运动 $x_1 = A_1\cos(\omega t + \varphi_1)$、$x_2 = A_2\cos(\omega t + \varphi_2)$ 合成：</p>
<p>$$A = \sqrt{A_1^2 + A_2^2 + 2A_1A_2\cos(\varphi_2 - \varphi_1)}$$</p>
<p>$$\tan\varphi = \frac{A_1\sin\varphi_1 + A_2\sin\varphi_2}{A_1\cos\varphi_1 + A_2\cos\varphi_2}$$</p>
<p>&lt;img src="/images/posts/physics-oscillation/assets/2-2-振动的合成.svg" alt="振动的合成" style="width:30vw;display:block;margin:0 auto;"/&gt;</p>
<p><strong>题1.</strong> 某质点同时参与轴上的两个简谐运动：$x_1 = 0.03\cos\left(2\pi t + \dfrac{\pi}{3}\right)$，$x_2 = 0.05\cos\left(2\pi t - \dfrac{2\pi}{3}\right)$（SI），合成振动的振动方程为 ______。</p>
<p><strong>解：</strong></p>
<p><strong>解法一（旋转矢量法）：</strong> 相位差 $\varphi_2 - \varphi_1 = -\dfrac{2\pi}{3} - \dfrac{\pi}{3} = -\pi$，两振动方向相反，故</p>
<p>$$A = 0.05 - 0.03 = 0.02,\text{m},\qquad \varphi_0 = -\frac{2\pi}{3}$$</p>
<p>$$\Rightarrow x = 0.02\cos\left(2\pi t - \frac{2\pi}{3}\right)$$</p>
<p><strong>解法二（公式法）：</strong></p>
<p>$$A = \sqrt{A_1^2 + A_2^2 + 2A_1A_2\cos(\varphi_2-\varphi_1)} = \sqrt{0.03^2 + 0.05^2 + 2\times0.03\times0.05\cos(-\pi)} = 0.02$$</p>
<p>$$\tan\varphi = \frac{0.03\sin\frac{\pi}{3} + 0.05\sin\left(-\frac{2\pi}{3}\right)}{0.03\cos\frac{\pi}{3} + 0.05\cos\left(-\frac{2\pi}{3}\right)} = \sqrt{3}$$</p>
<p>由旋转矢量法可知 $\varphi = -\dfrac{2\pi}{3}$，故 $x = 0.02\cos\left(2\pi t - \dfrac{2\pi}{3}\right)$。</p>
<hr />
<h2>课时三 机械波</h2>
<table>
<thead>
<tr>
<th>考点</th>
<th>重要程度</th>
<th>占分</th>
<th>常见题型</th>
</tr>
</thead>
<tbody>
<tr>
<td>1. 认识机械波</td>
<td>★★★</td>
<td>0~3</td>
<td>选择、填空</td>
</tr>
<tr>
<td>2. 波动方程</td>
<td>必考</td>
<td>5~10</td>
<td>大题</td>
</tr>
</tbody>
</table>
<h3>1. 认识机械波</h3>
<p><strong>机械波的形成：</strong> 波源振动带动相邻质点依次振动，振动形式在介质中由近及远传播。介质中各质点只在自己的平衡位置附近振动，<strong>不随波迁移</strong>，传播的是振动形式和能量。</p>
<p>&lt;img src="/images/posts/physics-oscillation/assets/3-1-横波形成与传播.svg" alt="横波的形成与传播：绳上质点上下振动、波形向右传播，图中标出波长 λ、振动周期 T 与振幅 A" style="width:30vw;display:block;margin:0 auto;"/&gt;</p>
<p><strong>波速、波长、周期的关系：</strong></p>
<p>$$u = \frac{\lambda}{T} = \lambda \nu$$</p>
<ul>
<li>波速 $u$：波形传播的速度，由<strong>介质</strong>决定</li>
<li>波长 $\lambda$：一个完整波形沿传播方向占据的长度，等于波线上相位差为 $2\pi$ 的两点间距离</li>
<li>周期 $T$：波传播一个波长所需的时间，等于波源的振动周期（由<strong>波源</strong>决定）</li>
</ul>
<p><strong>两点间相位差：</strong></p>
<p>$$\Delta\varphi = \frac{2\pi}{\lambda},\Delta x \quad\Longleftrightarrow\quad \frac{\lambda}{\Delta x} = \frac{2\pi}{\Delta\varphi}$$</p>
<blockquote>
<p>[!tip] 记忆口诀
"见者有份"：波长对应 $2\pi$ 相位，两点距离 $\Delta x$ 占波长多少份，相位差就占 $2\pi$ 多少份。</p>
</blockquote>
<h4>波动方程（沿 +x 方向传播）</h4>
<p>原点 $O$ 的振动方程：$y = A\cos(\omega t + \varphi_0)$</p>
<p>若波沿 $+x$ 方向传播，$x$ 处质点的振动比 $O$ 点<strong>落后</strong> $\Delta t = \dfrac{x}{u}$，故</p>
<p>$$y = A\cos\left[\omega\left(t - \frac{x}{u}\right) + \varphi_0\right] = A\cos\left[2\pi\left(\nu t - \frac{x}{\lambda}\right) + \varphi_0\right] = A\cos\left[\omega t + \varphi_0 - \frac{2\pi}{\lambda}x\right]$$</p>
<p>若波沿 $-x$ 方向传播，$x$ 处质点的振动比 $O$ 点<strong>超前</strong>，则</p>
<p>$$y = A\cos\left[\omega\left(t + \frac{x}{u}\right) + \varphi_0\right] = A\cos\left[2\pi\left(\nu t + \frac{x}{\lambda}\right) + \varphi_0\right] = A\cos\left[\omega t + \varphi_0 + \frac{2\pi}{\lambda}x\right]$$</p>
<p>&lt;img src="/images/posts/physics-oscillation/assets/3-2-波动方程推导.svg" alt="t=0 时刻波形图，y 轴标 A 与 -A，x 轴标波长 λ 与质点距离 x，波沿 +x 传播" style="width:30vw;display:block;margin:0 auto;"/&gt;</p>
<blockquote>
<p>[!warning] 易错点
判断传播方向时，"上坡下、下坡上"：在波形图上升段（沿波传播方向）质点向下振动，下降段质点向上振动。</p>
</blockquote>
<h4>例题</h4>
<p><strong>题1.</strong> 已知一平面简谐波的波动方程为：$y = 2.0\cos\left<a href="%5Ctext%7Bm%7D">2\pi\left(t - \dfrac{x}{8}\right) + \dfrac{\pi}{3}\right</a>$，则此波沿 $x$ 轴 ______ 方向传播，波速为 ______，波长为 ______，原点处初相为 ______。</p>
<p><strong>答：</strong> 正；$8,\text{m/s}$；$8,\text{m}$；$\dfrac{\pi}{3}$</p>
<p><strong>解：</strong> 波动方程与标准式 $y = A\cos\left[2\pi\left(\nu t - \dfrac{x}{\lambda}\right) + \varphi_0\right]$ 对照：</p>
<ul>
<li>括号内为 $\left(t - \dfrac{x}{8}\right)$，$x$ 前为负号 ⟹ 沿 $+x$（正）方向传播</li>
<li>波长 $\lambda = 8,\text{m}$，频率 $\nu = 1,\text{Hz}$</li>
<li>波速 $u = \lambda\nu = 8\times1 = 8,\text{m/s}$</li>
<li>初相 $\varphi_0 = \dfrac{\pi}{3}$</li>
</ul>
<p>&lt;hr&gt;</p>
<p><strong>题2.</strong> 一横波沿绳子传播时，波的表达式为 $y = 0.05\cos(10\pi t - 4\pi x)$（SI），则（　）。</p>
<p>$A.\ 波长为\ 0.5,\text{m} \quad B.\ 波速为\ 5,\text{m/s} \quad C.\ 波速为\ 25,\text{m/s} \quad D.\ 频率为\ 2,\text{Hz}$</p>
<p><strong>答：A</strong></p>
<p><strong>解：</strong> 对照标准式 $y = A\cos\left[\omega t + \varphi_0 - \dfrac{2\pi}{\lambda}x\right]$：</p>
<ul>
<li>由 $-4\pi x = -\dfrac{2\pi}{\lambda}x \Rightarrow \lambda = 0.5,\text{m}$ ✓</li>
<li>$\omega = 10\pi \Rightarrow T = \dfrac{2\pi}{\omega} = 0.2,\text{s}$</li>
<li>$u = \dfrac{\lambda}{T} = \dfrac{0.5}{0.2} = 2.5,\text{m/s}$（B、C 均错）</li>
<li>$\nu = \dfrac{1}{T} = \dfrac{1}{0.2} = 5,\text{Hz}$（D 错）</li>
</ul>
<p>&lt;hr&gt;</p>
<p><strong>题3.</strong> 频率为 $100,\text{Hz}$，传播速度为 $300,\text{m/s}$ 的平面简谐波，波线上距离小于波长的两点振动的相位差为 $\dfrac{\pi}{3}$，则两点相距（　）。</p>
<p>$A.\ 2.86,\text{m} \quad B.\ 2.19,\text{m} \quad C.\ 0.5,\text{m} \quad D.\ 0.25,\text{m}$</p>
<p><strong>答：C</strong></p>
<p><strong>解：</strong></p>
<p>$$\lambda = uT = u\cdot\frac{1}{\nu} = 300 \times \frac{1}{100} = 3,\text{m}$$</p>
<p>$$\Delta\varphi = \frac{2\pi}{\lambda}\Delta x \Rightarrow \frac{\pi}{3} = \frac{2\pi}{3}\Delta x \Rightarrow \Delta x = 0.5,\text{m}$$</p>
<h3>2. 波动方程</h3>
<blockquote>
<p>[!note] 解题三步法
① 求原点振动方程 $y = A\cos(\omega t + \varphi_0)$（旋转矢量法定 $\varphi_0$）
② 求波速 $u = \dfrac{\lambda}{T} = \lambda\nu$
③ 代入波动方程 $y = A\cos\left[\omega\left(t \mp \dfrac{x}{u}\right) + \varphi_0\right]$</p>
</blockquote>
<h4>例题</h4>
<p><strong>题1.</strong> 已知一沿 $x$ 轴正向传播的平面简谐波，时间 $t = 0$ 时的波形如图所示，且 $T = 2,\text{s}$，求：</p>
<p>（1）$O$ 点的振动方程；</p>
<p>（2）该波的波动方程；</p>
<p>（3）$x = 60,\text{m}$ 处质点的振动方程和速度表达式。</p>
<p>&lt;img src="/images/posts/physics-oscillation/assets/3-3-波形图-沿x正向.svg" alt="t=0 波形图，y 轴刻度 10/0/-10 cm，x 轴刻度 0~50 m，波长 λ=40 m，波沿 +x 传播" style="width:30vw;display:block;margin:0 auto;"/&gt;</p>
<p><strong>解：</strong></p>
<p>（1）振幅 $A = 0.1,\text{m}$，$\omega = \dfrac{2\pi}{T} = \dfrac{2\pi}{2} = \pi\ \text{rad/s}$</p>
<p>由旋转矢量法得 $\varphi_0 = -\dfrac{2\pi}{3}$（$t=0$ 时原点位移为负、且向 $-y$ 方向运动），故</p>
<p>$$y = 0.1\cos\left(\pi t - \frac{2\pi}{3}\right)$$</p>
<p>（2）$\lambda = 40,\text{m}$，$u = \dfrac{\lambda}{T} = \dfrac{40}{2} = 20,\text{m/s}$，波沿 $+x$ 传播：</p>
<p>$$y = 0.1\cos\left[\pi\left(t - \frac{x}{20}\right) - \frac{2\pi}{3}\right]$$</p>
<p>（3）将 $x = 60,\text{m}$ 代入：</p>
<p>$$y = 0.1\cos\left[\pi\left(t - \frac{60}{20}\right) - \frac{2\pi}{3}\right] = 0.1\cos\left(\pi t - \frac{11\pi}{3}\right)$$</p>
<p>速度表达式：</p>
<p>$$v = \frac{dy}{dt} = -0.1\pi\sin\left(\pi t - \frac{11\pi}{3}\right)$$</p>
<p>&lt;hr&gt;</p>
<p><strong>题2.</strong> 一平面简谐波在介质中以波速 $u = 2,\text{m/s}$ 沿 $x$ 轴负方向传播，原点 $O$ 处质点的振动曲线如图所示，求：</p>
<p>（1）原点 $O$ 的质点的振动方程；</p>
<p>（2）该波的波动方程；</p>
<p>（3）$x = 20,\text{m}$ 处质点的振动方程。</p>
<p>&lt;img src="/images/posts/physics-oscillation/assets/3-4-波形图-沿x负向.svg" alt="原点振动曲线，y 轴刻度 4/0/-4 cm，t 轴刻度 0~10 s，周期 T=8 s，波沿 -x 传播" style="width:30vw;display:block;margin:0 auto;"/&gt;</p>
<p><strong>解：</strong></p>
<p>（1）振幅 $A = 4\times10^{-2},\text{m}$，周期 $T = 8,\text{s}$，$\omega = \dfrac{2\pi}{T} = \dfrac{2\pi}{8} = \dfrac{\pi}{4}\ \text{rad/s}$</p>
<p>由旋转矢量法得 $\varphi_0 = -\dfrac{\pi}{2}$，故</p>
<p>$$y = 4\times10^{-2}\cos\left(\frac{\pi}{4}t - \frac{\pi}{2}\right)$$</p>
<p>（2）波沿 $-x$ 方向传播，$u = 2,\text{m/s}$：</p>
<p>$$y = 4\times10^{-2}\cos\left[\frac{\pi}{4}\left(t + \frac{x}{2}\right) - \frac{\pi}{2}\right]$$</p>
<p>（3）$x = 20,\text{m}$ 处：</p>
<p>$$y = 4\times10^{-2}\cos\left[\frac{\pi}{4}\left(t + \frac{20}{2}\right) - \frac{\pi}{2}\right] = 4\times10^{-2}\cos\left(\frac{\pi}{4}t + 2\pi\right) = 4\times10^{-2}\cos\left(\frac{\pi}{4}t\right)$$</p>
<p>&lt;hr&gt;</p>
<p><strong>题3.</strong> 一平面简谐波沿 $Ox$ 轴的负方向传播，波长为 $\lambda$，$P$ 处质点的振动规律如图所示，已知 $P$ 点与 $O$ 点的距离为 $d$。</p>
<p>（1）求 $P$ 处质点的振动方程；</p>
<p>（2）求此波的波动表达式。</p>
<p><strong>解：</strong></p>
<p>（1）由振动图：振幅 $A$，$\varphi_0 = \pi$，$t = 1,\text{s}$ 时 $y = 0$，即</p>
<p>$$0 = A\cos(\omega + \pi) \Rightarrow \omega + \pi = \frac{3\pi}{2} \Rightarrow \omega = \frac{\pi}{2}$$</p>
<p>$$y_P = A\cos\left(\frac{\pi}{2}t + \pi\right)$$</p>
<p>（2）$T = \dfrac{2\pi}{\omega} = \dfrac{2\pi}{\pi/2} = 4,\text{s}$，$u = \dfrac{\lambda}{T} = \dfrac{\lambda}{4}$</p>
<p>以 $P$ 为原点（波沿 $-x$ 传播，$O$ 在 $P$ 的左侧 $d$ 处）：</p>
<p>$$y = A\cos\left[\frac{\pi}{2}\left(t + \frac{x}{\lambda/4}\right) + \pi\right] = A\cos\left[\frac{\pi}{2}\left(t + \frac{4x}{\lambda}\right) + \pi\right]$$</p>
<p>以 $O$ 为原点（$O$ 点比 $P$ 点振动超前 $\dfrac{d}{u}$）：</p>
<p>$$y = A\cos\left[\frac{\pi}{2}\left(t + \frac{4(x-d)}{\lambda}\right) + \pi\right]$$</p>
<blockquote>
<p>[!tip] 波沿 $-x$ 传播的波动方程
以振动规律已知的点 $P$ 为原点，$x$ 取正方向为 $-x$ 方向；以 $O$ 为原点时，$O$ 点比 $P$ 点超前 $d/u$，故相位项为 $+\dfrac{2\pi d}{\lambda}$（对应 $t + \dfrac{x+d'}{u}$ 形式）。</p>
</blockquote>
]]></content>
    <author><name>仙鹤</name></author>
    <category term="考试"/>
  </entry>
  <entry>
    <title>Java 核心与 AI 开发基础</title>
    <link href="https://xianhe51920.github.io/posts/java-core-ai-basic/" rel="alternate" type="text/html"/>
    <id>https://xianhe51920.github.io/posts/java-core-ai-basic/</id>
    <published>2026-09-04T00:00:00.000Z</published>
    <updated>2026-09-04T00:00:00.000Z</updated>
    <summary>Java 核心语法与 AI 开发基础</summary>
    <content type="html"><![CDATA[<h1>Java核心与AI开发基础</h1>
<h2>目录</h2>
<ul>
<li><a href="#%E7%AC%AC%E9%9B%B6%E7%AB%A0-%E5%AF%BC%E5%AD%A6%E9%83%A8%E5%88%86">第零章 导学部分</a></li>
<li><a href="#%E7%AC%AC%E4%B8%80%E7%AB%A0-%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA">第一章 开发环境搭建</a></li>
<li><a href="#%E7%AC%AC%E4%BA%8C%E7%AB%A0-idea-%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7">第二章 IDEA 开发工具</a></li>
<li><a href="#%E7%AC%AC%E4%B8%89%E7%AB%A0-java-%E5%9F%BA%E7%A1%80%E8%AF%AD%E6%B3%95">第三章 Java 基础语法</a></li>
<li><a href="#%E7%AC%AC%E5%9B%9B%E7%AB%A0-%E8%BF%90%E7%AE%97%E7%AC%A6">第四章 运算符</a></li>
<li><a href="#%E7%AC%AC%E4%BA%94%E7%AB%A0-%E6%96%B9%E6%B3%95">第五章 方法</a></li>
<li><a href="#%E7%AC%AC%E5%85%AD%E7%AB%A0-%E6%B5%81%E7%A8%8B%E6%8E%A7%E5%88%B6%E8%AF%AD%E5%8F%A5">第六章 流程控制语句</a></li>
<li><a href="#%E7%AC%AC%E4%B8%83%E7%AB%A0-%E6%95%B0%E7%BB%84">第七章 数组</a></li>
<li><a href="#%E7%AC%AC%E5%85%AB%E7%AB%A0-%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E5%9F%BA%E7%A1%80">第八章 面向对象基础</a></li>
<li><a href="#%E7%AC%AC%E4%B9%9D%E7%AB%A0-%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E9%AB%98%E7%BA%A7">第九章 面向对象高级</a></li>
<li><a href="#%E7%AC%AC%E5%8D%81%E7%AB%A0-%E5%B8%B8%E7%94%A8-api">第十章 常用 API</a></li>
<li><a href="#%E7%AC%AC%E5%8D%81%E4%B8%80%E7%AB%A0-%E9%9B%86%E5%90%88%E5%9F%BA%E7%A1%80">第十一章 集合基础</a></li>
</ul>
<h2>第零章 导学部分</h2>
<blockquote>
<p>AI 智能应用开发（Java）导学视频 —— 融合 Java 核心技术与 AI 创新能力
多一句没有，少一句不行。用更短的时间，教会更实用的技术</p>
</blockquote>
<h3>课程介绍</h3>
<h4>课程定位与能力要求</h4>
<table>
<thead>
<tr>
<th>就业方向</th>
<th>岗位技术力</th>
<th>专业技能 &amp; 项目经验</th>
<th>课程阶段</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Java+AI 初级工程师</strong>（1~2 年经验）&lt;br&gt;参与软件需求分析、负责代码模块功能实现、抽象系统能力、参加设计文档及系统能力，重大项目主导设计能力</td>
<td><strong>6 门技术</strong> + <strong>整体项目经验</strong> + <strong>第三方方案</strong></td>
<td>掌握 Java 核心语法、集合、多线程、IO/NIO、掌握 JVM 内存模型与 GC 机制、熟练使用 Spring Boot + MyBatis 开发 Web 应用、掌握分布式技术架构设计、熟悉 Kafka/MQ/Redis 中间件、掌握 MySQL/MongoDB 分布式存储与读写分离、掌握 Java 集合综合使用……</td>
<td><strong>第一阶段</strong>：Java 核心与 AI 开发基础&lt;br&gt;<strong>第二阶段</strong>：Java 核心与 AI 开发进阶</td>
</tr>
<tr>
<td><strong>Java+AI 中级工程师</strong>（3~5 年经验）&lt;br&gt;参与技术方案选型与设计、解决疑难问题、调优与扩展能力、参与重大项目主导设计能力</td>
<td><strong>第三方技术</strong> + <strong>分布式项目经验</strong> + <strong>AI 智能体应用开发</strong></td>
<td>掌握微服务架构、Spring Cloud（Nacos/Sentinel/Seata）、微服务分布式事务、RabbitMQ 消息中间件、Redisson 分布式锁；熟练使用 Java 集合高级 API、Stream API、NIO、Netty；掌握 JVM 调优、SQL 调优；Redis 高级优化等技术；微服务高可用技术；分布式系统架构设计；微服务架构设计</td>
<td>第三阶段：AI 智能 Web 开发&lt;br&gt;第四阶段：企业级智能体项目（神州养车）&lt;br&gt;第五阶段：微服务 SCA&lt;br&gt;第六阶段：互联网分布式微服务项目（云岚到家）&lt;br&gt;第八阶段：沉浸式项目实战（神州养车）&lt;br&gt;第九阶段：Java AI 智能体应用开发&lt;br&gt;第十阶段：BAT 大厂 360° 测试开发 / 就业指导</td>
</tr>
<tr>
<td>能够清晰地表达自己的技术能力和项目经验</td>
<td>—</td>
<td>—</td>
<td>—</td>
</tr>
<tr>
<td>构建「<strong>Java 核心 + AI 创新</strong>」复合竞争力</td>
<td>—</td>
<td>掌握核心概念和技术，深入理解大模型智能体技术概念，包括提示词数据微调（Prompt Tuning）和特征改进微调（PET/Tuna）开源模型应用技术</td>
<td>雷达拓展课：分布式项目集&lt;br&gt;雷达拓展课：AI 大模型开发实战</td>
</tr>
</tbody>
</table>
<h4>课程总目标与时长</h4>
<table>
<thead>
<tr>
<th>总目标</th>
<th>学习时长</th>
<th>课程阶段</th>
<th>任务包</th>
<th>学习周期</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>通过系统学习和实战项目，达到企业招聘需求，实现高薪就业</strong></td>
<td><strong>每周 ≥ 24 小时</strong>&lt;br&gt;（视频时长 12 小时，练习时长 12 小时）</td>
<td>第一阶段：Java 核心与 AI 开发基础</td>
<td>19</td>
<td>3 周（约 72 小时）</td>
</tr>
<tr>
<td></td>
<td></td>
<td>第二阶段：Java 核心与 AI 开发进阶</td>
<td>12</td>
<td><strong>2 周+</strong>（约 56 小时）</td>
</tr>
<tr>
<td></td>
<td></td>
<td>第三阶段：AI 智能 Web 开发</td>
<td>18</td>
<td>7 周（约 168 小时）</td>
</tr>
<tr>
<td></td>
<td></td>
<td>第四阶段：企业级智能体项目（神州养车）</td>
<td>15</td>
<td>4 周（约 96 小时）</td>
</tr>
<tr>
<td></td>
<td></td>
<td>第五阶段：微服务 SCA</td>
<td>15</td>
<td>3 周（约 72 小时）</td>
</tr>
<tr>
<td></td>
<td></td>
<td>第六阶段：互联网分布式微服务项目（云岚到家）</td>
<td>14</td>
<td>4 周（约 96 小时）</td>
</tr>
</tbody>
</table>
<h3>学习计划</h3>
<h4>什么是任务包？</h4>
<blockquote>
<p><strong>任务包</strong>是老师为了帮助我们学好 Java 开发技能，将各阶段内容带分出的具体学习单元。每个任务包围绕学习目标展开，包含一系列具体的学习材料（视频、练习题等）以及预估学习时长，助力我们逐步达成学习目标，扎实吸收知识，实现高效学习。</p>
</blockquote>
<h4>任务包组成</h4>
<table>
<thead>
<tr>
<th>维度</th>
<th>组成</th>
<th>详细说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>学习目标</strong>（技能点）</td>
<td>学习目标</td>
<td>每个任务包都有明确的学习目标，结合具体技能点。告诉你学完后要达到什么水平。例如：「Java 开发环境搭建」任务包中有三个技能点。</td>
</tr>
<tr>
<td></td>
<td>技能点掌握要求</td>
<td>技能点 1：JDK 的下载和安装&lt;br&gt;技能点 2：配置环境变量、path 环境变量配置&lt;br&gt;技能点 3：使用 IDE 编写并运行 Java 代码&lt;br&gt;<strong>要求：不仅要看得懂，还要弄清楚运行环境中的概念，能够跟别人讲解自己写下来</strong>&lt;br&gt;举例 2：使用 IDEA 编写并运行 Java 代码&lt;br&gt;备注：通过实际操作，比如编写和运行简单的 Java 程序，确保能灵活运用这些知识解决实际问题。</td>
</tr>
<tr>
<td><strong>学习材料</strong></td>
<td>视频教程</td>
<td>详细讲解每个技能点，帮助你理解和掌握知识。</td>
</tr>
<tr>
<td></td>
<td>知识总结</td>
<td>通过思维导图，快速复习所学技能点。</td>
</tr>
<tr>
<td></td>
<td>练习题</td>
<td>通过实际操作巩固知识，提升应用能力。</td>
</tr>
<tr>
<td><strong>质量验收</strong></td>
<td>练习题</td>
<td>每个任务包都有练习题，帮助我们巩固所学知识。</td>
</tr>
<tr>
<td></td>
<td>章节测评</td>
<td>每个关键章节结束后，有测评来检查学习进度。</td>
</tr>
<tr>
<td></td>
<td>阶段考试</td>
<td>每个阶段结束后，老师会和我们对一对面试，检查编写能力；沟通表达能力。确保真正掌握了知识。</td>
</tr>
<tr>
<td><strong>时间规划</strong></td>
<td>任务包总时长</td>
<td>明确视频学习、练习和测评的预估时长，帮助我们合理安排时间。</td>
</tr>
<tr>
<td></td>
<td>周计划</td>
<td>根据每周可投入的学习时间，合理分配任务包数量，确保学习进度和质量。</td>
</tr>
</tbody>
</table>
<h4>任务包能带来什么好处？</h4>
<table>
<thead>
<tr>
<th>维度</th>
<th>好处</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>明确学习目标</strong></td>
<td>每个任务包都有清晰的目标，结合具体的技能点和掌握要求，让我们清楚地知道要达成什么。集中精力在关键知识点上，提高学习效率。</td>
</tr>
<tr>
<td><strong>系统化学习路径</strong></td>
<td>通过分步骤的任务包，循序渐进地完成完整的学习体系，帮助我们更好地理解和掌握复杂内容。</td>
</tr>
<tr>
<td><strong>高效时间管理</strong></td>
<td>提供预估的学习时长和周计划，帮助我们合理安排时间，平衡学习与生活。</td>
</tr>
<tr>
<td><strong>提升学习动力</strong></td>
<td>每完成一个任务包，我们都能获得成就感，激发和保持热情，让我们设定确保每个阶段都有反馈和奖励。</td>
</tr>
<tr>
<td><strong>易于跟踪进度</strong></td>
<td>通过练习题、章节测评和阶段测试，清晰跟踪学习进度，及时调整学习计划，确保始终在正确的轨道上前进。</td>
</tr>
</tbody>
</table>
<h4>学习平台：博学谷</h4>
<blockquote>
<p>线上学习灵活，每日任务量可动态调整，但要确保周计划整体完成。
平台提供日历视图，每日展示具体任务（前端学科就业班、Java 后端开发、20 天下载安装、学习阶段汇总、开发 Java 程序等），支持打卡、查看学习路线。</p>
</blockquote>
<h3>学习方法</h3>
<pre><code>flowchart LR
    S1["01 STEP 多动手实操"] --&gt; S2["02 STEP 保持连贯性"] --&gt; S3["03 STEP 学会总结"]
</code></pre>
<blockquote>
<p>学习 Java 的三大步骤：&lt;span style="color:red"&gt;多动手实操&lt;/span&gt; → &lt;span style="color:orange"&gt;保持连贯性&lt;/span&gt; → &lt;span style="color:green"&gt;学会总结&lt;/span&gt;</p>
</blockquote>
<h3>学后测评</h3>
<table>
<thead>
<tr>
<th>测评类型</th>
<th>用途</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>任务包练习</strong></td>
<td>巩固任务包所学知识；编程题无需上传代码，提交后即可查看参考答案</td>
</tr>
<tr>
<td><strong>章节测评</strong></td>
<td>关键章节结束后的阶段检查</td>
</tr>
<tr>
<td><strong>阶段考试</strong></td>
<td>阶段性的综合考试，含面试问答</td>
</tr>
</tbody>
</table>
<blockquote>
<p>任务包练习示例（Java 核心与 AI 开发基础-任务包一[开发环境搭建]）：在 IntelliJ IDEA 中完成以下操作，创建一个 Java 工程并实现一个简单的打印功能。</p>
<p>①创建工程：打开 IntelliJ IDEA，创建一个名为 JavaSePractices 的空工程。
②创建模块、包、类：在 JavaSePractices 工程中，创建一个名为 chapter01 的模块。在 chapter01 模块下，创建一个名为 com.buxuegu.demo01 的包。在 com.buxuegu.demo01 包中，创建一个名为 Test01 的类。
③编写代码：在 Test01 类中，使用 main 方法打印 <code>hello world</code> 的控制台。
④运行程序：运行 Test01 类，确保控制台输出以下内容：<code>hello world</code></p>
</blockquote>
<h3>Java 的应用领域</h3>
<blockquote>
<p>&lt;span style="color:red"&gt;Java 语言拥有 27 年发展历史，长盛不衰，是大型软件的服务端开发首选语言。&lt;/span&gt;</p>
</blockquote>
<table>
<thead>
<tr>
<th>类别</th>
<th>典型应用</th>
</tr>
</thead>
<tbody>
<tr>
<td>互联网应用</td>
<td>抖音、京东、快手、QQ、淘宝、美团、微信、今日头条、钉钉</td>
</tr>
<tr>
<td>资讯社区</td>
<td>知乎、豆瓣、CSDN、猫眼、中国知网</td>
</tr>
<tr>
<td>游戏 / 工具</td>
<td>任天堂</td>
</tr>
</tbody>
</table>
<h3>Java+AI 工程师的核心能力</h3>
<blockquote>
<p>&lt;span style="color:red"&gt;Java 核心功夫是王道&lt;/span&gt; —— 不管有没有 AI，写好 Java 代码都是根本！就像学武功，马步得扎稳。你写的 Java 代码就是指挥棒！代码写得好，逻辑清晰，AI 助手才能更好地执行任务，整个系统才稳定可靠。</p>
</blockquote>
<h4>地基四要素</h4>
<pre><code>flowchart LR
    Base[("地基 Java 核心功夫")]
    Data["数据感"]
    Learn["学习力"]
    Connect["对接 AI"]
    Business["业务眼"]
    Base --- Data
    Base --- Learn
    Base --- Connect
    Base --- Business
</code></pre>
<table>
<thead>
<tr>
<th>要素</th>
<th>含义</th>
<th>Java 中的体现</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>数据感</strong></td>
<td>当好 AI 的原料整理师，把「乱糟糟」变成「整齐齐」</td>
<td>Java 程序收集用户对商品的评价文字</td>
</tr>
<tr>
<td><strong>学习力</strong></td>
<td>拥抱变化的心态</td>
<td>持续学习新框架、新工具</td>
</tr>
<tr>
<td><strong>对接 AI</strong></td>
<td>学会调用 AI「外挂」</td>
<td>在 Java 程序里方便地「调用」现成的 AI 功能</td>
</tr>
<tr>
<td><strong>业务眼</strong></td>
<td>知道用 AI 解决什么问题</td>
<td>区分哪些业务用传统代码、哪些用 AI 服务</td>
</tr>
</tbody>
</table>
<h4>对接 AI 案例</h4>
<table>
<thead>
<tr>
<th>场景</th>
<th>Java 代码负责</th>
<th>AI 服务负责</th>
</tr>
</thead>
<tbody>
<tr>
<td>评论情感分析</td>
<td>把一段用户评论发给 AI 服务</td>
<td>告诉评论的情感是正面还是负面的</td>
</tr>
<tr>
<td>图像识别</td>
<td>把一张图片传给 AI 服务</td>
<td>识别图片里有什么物体</td>
</tr>
</tbody>
</table>
<h4>业务眼 — AI 落地的典型场景</h4>
<table>
<thead>
<tr>
<th>业务领域</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>客服系统</strong></td>
<td>自动回复、智能问答</td>
</tr>
<tr>
<td>图像与视频分析</td>
<td>内容理解、目标检测</td>
</tr>
<tr>
<td>金融交易</td>
<td>风控、量化决策</td>
</tr>
<tr>
<td>保险理赔</td>
<td>智能核损、定损</td>
</tr>
</tbody>
</table>
<h4>4 步能力成长路径</h4>
<pre><code>flowchart LR
    Step1["01 扎扎实实学好 Java 编程基础 入门期间，代码需要自己动手编写 不能依赖 AI 工具"] --&gt; Step2["02 数据意识 收集数据，整理数据，传递数据"]
    Step2 --&gt; Step3["03 对接 AI 开发期间，可以调用现成的、强大的 AI 功能 让程序更智能"]
    Step3 --&gt; Step4["04 业务观察力、持续学习力 分辨出哪些业务需要传统代码实现 哪些业务可以调用 AI 服务"]
</code></pre>
<h3>课程总览（第一、第二阶段）</h3>
<table>
<thead>
<tr>
<th>阶段</th>
<th>章节</th>
<th>章节名</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>第一阶段</strong></td>
<td>第一章</td>
<td>Java 开发环境搭建</td>
</tr>
<tr>
<td></td>
<td>第二章</td>
<td>基础语法</td>
</tr>
<tr>
<td></td>
<td>第三章</td>
<td>流程控制语句</td>
</tr>
<tr>
<td></td>
<td>第四章</td>
<td>数组</td>
</tr>
<tr>
<td></td>
<td>第五章</td>
<td>面向对象基础</td>
</tr>
<tr>
<td></td>
<td>第六章</td>
<td>面向对象高级</td>
</tr>
<tr>
<td></td>
<td>第七章</td>
<td>常用 API</td>
</tr>
<tr>
<td></td>
<td>第八章</td>
<td>综合案例</td>
</tr>
<tr>
<td><strong>第二阶段</strong></td>
<td>第一章</td>
<td>集合高级</td>
</tr>
<tr>
<td></td>
<td>第二章</td>
<td>异常</td>
</tr>
<tr>
<td></td>
<td>第三章</td>
<td>File 类</td>
</tr>
<tr>
<td></td>
<td>第四章</td>
<td>常用 API IO 流</td>
</tr>
<tr>
<td></td>
<td>第五章</td>
<td>多线程</td>
</tr>
<tr>
<td></td>
<td>第六章</td>
<td>网络编程</td>
</tr>
<tr>
<td></td>
<td>第七章</td>
<td>Java 高级技术</td>
</tr>
</tbody>
</table>
<h2>第一章 开发环境搭建</h2>
<blockquote>
<p>培养 AI 新时代 Java 工程师</p>
</blockquote>
<h3>Java 背景介绍</h3>
<h4>Java 语言的历史</h4>
<blockquote>
<p><strong>Java</strong> 是由 <strong>James Gosling</strong>（詹姆斯·高斯林）于 <strong>1995 年</strong>在 <strong>Sun 公司</strong>开发的计算机高级编程语言，在 <strong>2009 年</strong>被 <strong>Oracle 公司</strong>收购。</p>
</blockquote>
<p><strong>James Gosling</strong> —— Java 之父</p>
<table>
<thead>
<tr>
<th>项目</th>
<th>内容</th>
</tr>
</thead>
<tbody>
<tr>
<td>出生</td>
<td>1955 年出生于加拿大</td>
</tr>
<tr>
<td>学士</td>
<td>1977 年获得卡尔加里大学计算机学士学位</td>
</tr>
<tr>
<td>博士</td>
<td>1983 年获得卡内基梅隆大学计算机博士学位</td>
</tr>
<tr>
<td>称号</td>
<td><strong>Java 之父</strong></td>
</tr>
</tbody>
</table>
<h4>Java 的应用领域</h4>
<table>
<thead>
<tr>
<th>方向</th>
<th>典型应用</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>桌面应用开发</strong></td>
<td>IDEA、Eclipse</td>
<td>集成开发工具</td>
</tr>
<tr>
<td><strong>企业级应用开发</strong></td>
<td>微服务、大型互联网应用</td>
<td><strong>Java 的主战场</strong></td>
</tr>
<tr>
<td><strong>移动应用开发</strong></td>
<td>Android</td>
<td>Android 原生应用</td>
</tr>
<tr>
<td><strong>服务器系统</strong></td>
<td>应用的后台</td>
<td>服务端开发</td>
</tr>
<tr>
<td><strong>大数据开发</strong></td>
<td>hadoop</td>
<td>大数据生态</td>
</tr>
<tr>
<td><strong>游戏开发</strong></td>
<td>我的世界 MineCraft</td>
<td>游戏服务端</td>
</tr>
</tbody>
</table>
<h4>Java 的三大技术平台</h4>
<table>
<thead>
<tr>
<th>平台</th>
<th>全称</th>
<th>中文</th>
<th>定位</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>JavaSE</strong></td>
<td>Java Standard Edition</td>
<td><strong>标准版</strong></td>
<td>Java 技术的核心和基础</td>
</tr>
<tr>
<td><strong>JavaEE</strong></td>
<td>Java Enterprise Edition</td>
<td><strong>企业版</strong></td>
<td>企业级应用开发的一套<strong>解决方案</strong></td>
</tr>
<tr>
<td><strong>JavaME</strong></td>
<td>Java Micro Edition</td>
<td><strong>小型版</strong></td>
<td>针对移动设备应用的<strong>解决方案</strong></td>
</tr>
</tbody>
</table>
<h4>本节复习</h4>
<table>
<thead>
<tr>
<th>问题</th>
<th>答案</th>
</tr>
</thead>
<tbody>
<tr>
<td>Java 是什么？</td>
<td>一门高级编程语言</td>
</tr>
<tr>
<td>Java 是哪家公司的？现在属于哪家公司？</td>
<td>Sun 公司、Oracle 公司</td>
</tr>
<tr>
<td>Java 之父是谁？</td>
<td>詹姆斯·高斯林</td>
</tr>
<tr>
<td>Java 能做什么？</td>
<td>基本上什么都可以干，主要做<strong>企业服务端开发</strong></td>
</tr>
<tr>
<td>Java 有哪三大使用平台？</td>
<td><strong>JavaSE</strong>（标准版）、<strong>JavaEE</strong>（企业版）、<strong>JavaME</strong>（小型版）</td>
</tr>
</tbody>
</table>
<h3>JDK 工具的下载和安装</h3>
<h4>JDK 简介</h4>
<blockquote>
<p>开发 Java 程序必须先安装好 <strong>JDK</strong>（<strong>Java Development Kit</strong>），也就是 Java 开发工具包。</p>
</blockquote>
<p><strong>LTS 版本</strong>（Long-Term Support）：<strong>JDK 8、JDK 11、JDK 17、JDK 21</strong>。<strong>很多企业还在使用 JDK 8 / JDK 11</strong>。</p>
<h4>下载步骤</h4>
<p>在 Oracle 的官网可以找到 JDK 的下载链接：<a href="https://www.oracle.com/cn/java/technologies/downloads/">Java Downloads | Oracle 中国</a></p>
<table>
<thead>
<tr>
<th>步骤</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>①</td>
<td>选择 <strong>JDK 版本</strong>（推荐 JDK 17）</td>
</tr>
<tr>
<td>②</td>
<td>选择<strong>操作系统</strong>版本（如 Windows）</td>
</tr>
<tr>
<td>③</td>
<td>选择<strong>安装包类型</strong>（如 x64 Installer）</td>
</tr>
</tbody>
</table>
<h4>安装验证</h4>
<p>安装完成后，通过 CMD 命令行测试是否安装成功：</p>
<table>
<thead>
<tr>
<th>步骤</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>①</td>
<td>按下键盘上的 <strong>Win + R</strong> 键，会打开一个运行窗口</td>
</tr>
<tr>
<td>②</td>
<td>在窗口中输入 <code>cmd</code>，然后按下 Enter 键，弹出 DOS 窗口</td>
</tr>
<tr>
<td>③</td>
<td>在 DOS 窗口输入命令，测试 Java 工具及其版本是否正确</td>
</tr>
</tbody>
</table>
<pre><code>java -version
javac -version
</code></pre>
<h4>JDK bin 目录核心工具</h4>
<table>
<thead>
<tr>
<th>工具</th>
<th>类型</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>java.exe</code></td>
<td><strong>执行工具</strong></td>
<td>启动 JVM 运行 class 文件</td>
</tr>
<tr>
<td><code>javac.exe</code></td>
<td><strong>编译工具</strong></td>
<td>将 <code>.java</code> 源文件翻译成 <code>.class</code> 字节码</td>
</tr>
<tr>
<td><code>javadoc.exe</code></td>
<td>文档工具</td>
<td>生成 API 文档</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:red"&gt;我们写好的 Java 程序都是高级语言，计算机底层是硬件，不能识别这些语言，必须先通过 javac 编译工具进行翻译，然后再通过 java 执行工具执行才可以驱动机器干活。&lt;/span&gt;</p>
</blockquote>
<h4>JDK 的组成</h4>
<pre><code>flowchart TB
    subgraph JDK["JDK（Java Development Kit） Java 开发工具包"]
        direction TB
        subgraph JRE["JRE（Java Runtime Environment） Java 运行环境"]
            direction TB
            JVM["JVM（Java Virtual Machine） Java 虚拟机 真正运行 Java 程序的地方"]
            CoreLib["核心类库 Java 自己写好的程序 给程序员自己的程序调用"]
            JVM --- CoreLib
        end
        DevTools["开发工具 java、javac..."]
    end
</code></pre>
<table>
<thead>
<tr>
<th>组件</th>
<th>含义</th>
<th>角色</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>JVM</strong></td>
<td>Java Virtual Machine，Java 虚拟机</td>
<td>真正运行 Java 程序的地方</td>
</tr>
<tr>
<td><strong>核心类库</strong></td>
<td>Java 自己写好的程序</td>
<td>给程序员自己的程序调用</td>
</tr>
<tr>
<td><strong>JRE</strong></td>
<td>Java Runtime Environment，Java 运行环境</td>
<td>JVM + 核心类库</td>
</tr>
<tr>
<td><strong>JDK</strong></td>
<td>Java Development Kit，Java 开发工具包</td>
<td>JRE + 开发工具（含 java、javac 等）</td>
</tr>
</tbody>
</table>
<h4>DOS 常用命令</h4>
<table>
<thead>
<tr>
<th>常用命令</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>盘符:</code></td>
<td>切换盘符：<code>D:</code>、<code>E:</code></td>
</tr>
<tr>
<td><code>dir</code></td>
<td>查看当前路径下的文件信息</td>
</tr>
<tr>
<td><code>cd</code></td>
<td>进入单级目录：<code>cd itheima</code> / 回退到上一级目录：<code>cd ..</code> / 回退到盘符根目录：<code>cd /</code></td>
</tr>
<tr>
<td><code>cls</code></td>
<td>清屏</td>
</tr>
<tr>
<td><code>tab</code></td>
<td>自动补全指定字符开头的单词</td>
</tr>
<tr>
<td><code>↑</code> 键</td>
<td>查看历史命令</td>
</tr>
<tr>
<td><code>↓</code> 键</td>
<td>查看历史命令</td>
</tr>
<tr>
<td><code>exit</code></td>
<td>退出</td>
</tr>
</tbody>
</table>
<h4>本节复习</h4>
<table>
<thead>
<tr>
<th>问题</th>
<th>答案</th>
</tr>
</thead>
<tbody>
<tr>
<td>要使用 Java，必须先安装什么？去哪里下载？</td>
<td><strong>JDK</strong>（Java Development Kit）开发者工具包；Oracle 官网</td>
</tr>
<tr>
<td>LTS 版本有哪些？很多企业还在使用哪个 JDK 版本？</td>
<td><strong>JDK 8、11、17、21</strong>；很多企业还在使用 <strong>JDK 8 / JDK 11</strong></td>
</tr>
<tr>
<td>如何验证 JDK 是否安装成功了？</td>
<td>打开命令行窗口，输入 <code>java -version</code>、<code>javac -version</code> 看版本号</td>
</tr>
<tr>
<td>JDK 中最重要的 2 个命令行程序是什么？各自的作用是？</td>
<td><code>javac</code>、<code>java</code>；编译工具、执行工具</td>
</tr>
</tbody>
</table>
<h3>第一个 Java 程序 Hello World</h3>
<h4>Java 程序的开发步骤</h4>
<pre><code>flowchart LR
    A["① 编写代码 HelloWorld.java 源代码文件"] --&gt; B["② 编译代码 javac HelloWorld.java → 生成 HelloWorld.class"]
    B --&gt; C["③ 运行代码 java HelloWorld → 输出 Hello World"]
</code></pre>
<table>
<thead>
<tr>
<th>步骤</th>
<th>命令</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>① 编写代码</td>
<td>—</td>
<td>用记事本等编辑器写 <code>HelloWorld.java</code></td>
</tr>
<tr>
<td>② 编译代码</td>
<td><code>javac HelloWorld.java</code></td>
<td>生成 <code>HelloWorld.class</code> 字节码文件</td>
</tr>
<tr>
<td>③ 运行代码</td>
<td><code>java HelloWorld</code></td>
<td>JVM 执行 class，输出 <code>Hello World</code></td>
</tr>
</tbody>
</table>
<pre><code>D:\code&gt;javac HelloWorld.java
D:\code&gt;java HelloWorld
Hello World
</code></pre>
<h4>HelloWorld 代码解析</h4>
<pre><code>public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World !");
    }
}
</code></pre>
<table>
<thead>
<tr>
<th>元素</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>public class HelloWorld</code></td>
<td><code>class</code> 定义一个类，后面跟上的是<strong>类名称</strong>；<code>public</code> 是权限修饰符（暂时理解：限制<strong>类名称和文件名称需要保持一致</strong>）</td>
</tr>
<tr>
<td><code>public static void main(String[] args)</code></td>
<td>程序执行时的<strong>入口点</strong>，<code>main</code> 方法也称之为主方法</td>
</tr>
<tr>
<td><code>System.out.println("Hello World !")</code></td>
<td><strong>打印语句</strong>，使程序在控制台打印双引号所包裹的内容</td>
</tr>
</tbody>
</table>
<h3>Java 程序执行原理</h3>
<h4>机器语言与硬件</h4>
<blockquote>
<p>计算机底层都是<strong>硬件电路</strong>，可以通过<strong>不通电和通电</strong>，表示 <strong>0、1</strong>。
<strong>机器语言</strong>：由 0、1 组成，如 <code>00011100 00110101</code>。</p>
</blockquote>
<h4>编程语言发展历程</h4>
<table>
<thead>
<tr>
<th>阶段</th>
<th>特点</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>机器语言</strong></td>
<td>由 0/1 组成，计算机直接识别，难读难写</td>
</tr>
<tr>
<td><strong>汇编语言</strong></td>
<td>用助记符代替 0/1，仍是低级语言</td>
</tr>
<tr>
<td><strong>高级语言</strong></td>
<td>接近人类语言，需翻译成机器语言才能执行</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:red"&gt;高级语言更简单&lt;/span&gt;，使用接近人类自己的语言书写代码，再将其翻译成计算机能理解的机器指令。</p>
</blockquote>
<h4>Java 的跨平台原理</h4>
<pre><code>flowchart LR
    Java["Java 源代码"] --&gt; Class[".class 字节码"]
    Class --&gt; Win["Windows JVM 虚拟机"]
    Class --&gt; Linux["Linux JVM 虚拟机"]
    Class --&gt; Mac["MacOS JVM 虚拟机"]
</code></pre>
<blockquote>
<p>&lt;span style="color:red"&gt;总结&lt;/span&gt;：在需要运行 Java 应用程序的操作系统上，安装一个与操作系统对应的 <strong>Java 虚拟机（JVM，Java Virtual Machine）</strong> 即可。</p>
</blockquote>
<h4>本节复习</h4>
<table>
<thead>
<tr>
<th>问题</th>
<th>答案</th>
</tr>
</thead>
<tbody>
<tr>
<td>Java 程序的执行原理是什么样的？</td>
<td>不管是什么样高级编程语言，最终要翻译成计算机底层可以识别的机器语言</td>
</tr>
<tr>
<td>机器语言是由什么组成的？</td>
<td><strong>0 和 1</strong></td>
</tr>
<tr>
<td>Java 跨平台指的是什么？原理又是什么？</td>
<td>Java 程序可以在任意操作系统中运行；在不同的操作系统中，安装与之对应版本的 <strong>JVM 虚拟机</strong></td>
</tr>
</tbody>
</table>
<h3>Java 环境配置</h3>
<h4>Path 环境变量</h4>
<blockquote>
<p>&lt;span style="color:red"&gt;Path 环境变量&lt;/span&gt;用于记住程序路径，方便在命令行窗口的<strong>任意目录</strong>启动程序。</p>
</blockquote>
<p><strong>示例</strong>：QQ.exe 在 <code>D:\App\QQ</code> 目录下，从 D: 盘 <code>&gt;QQ</code>、C: 盘 <code>&gt;qq</code>、E: 盘 <code>&gt;qq</code> 都能启动。</p>
<p><strong>Path 环境变量位置</strong>：此电脑 → 属性 → 高级系统设置 → 高级 → 环境变量</p>
<p><strong>Path 环境变量的原理</strong>：当我们在 Path 中配置某个程序路径后，启动命令行窗口启动程序时，系统会按 Path 中配置的目录顺序查找该程序。</p>
<h4>JDK 的环境变量配置</h4>
<blockquote>
<p>较新版本的 JDK 安装时会自动配置 <code>javac</code>、<code>java</code> 程序的路径到 Path 环境变量中去，因此，<code>javac</code>、<code>java</code> 可以直接使用。</p>
<p>&lt;span style="color:red"&gt;注意&lt;/span&gt;：以前的老版本的 JDK 在安装时是没有自动配置 Path 环境变量的，此时需要自己配置 Path 环境变量。</p>
</blockquote>
<p><strong>Path 值示例</strong>：<code>E:\develop\Java\jdk21\bin</code>（本机路径，不要抄，去复制）</p>
<blockquote>
<p>重新配置了环境变量后，必须检测是否配置成功：打开命令行窗口，输入 <code>java -version</code> 分别看版本提示。</p>
</blockquote>
<p><strong>JAVA_HOME</strong>：告诉操作系统 JDK 安装在了哪个位置（将来其他技术要通过这个环境变量找 JDK）。</p>
<h4>本节复习</h4>
<table>
<thead>
<tr>
<th>问题</th>
<th>答案</th>
</tr>
</thead>
<tbody>
<tr>
<td>什么是 Path 环境变量？</td>
<td>用于配置程序的路径，方便我们在命令行窗口的任意目录下启动该程序</td>
</tr>
<tr>
<td>JDK 安装时，关于环境变量的配置，需要注意什么？</td>
<td>较新版本的 JDK 会自动配置 PATH 环境变量，较老的 JDK 版本则不会</td>
</tr>
<tr>
<td>建议如何处理？</td>
<td>建议还是自己配置一下「<strong>PATH</strong>」、「<strong>JAVA_HOME</strong>」</td>
</tr>
</tbody>
</table>
<h2>第二章 IDEA 开发工具</h2>
<blockquote>
<p>&lt;span style="color:blue"&gt;工欲善其事必先利其器&lt;/span&gt;</p>
</blockquote>
<h3>IDEA 介绍和安装</h3>
<h4>IDE 集成环境</h4>
<blockquote>
<p><strong>IDE 集成环境</strong>：把代码编写，编译，执行，调试等多种功能综合到一起的开发工具。</p>
</blockquote>
<h4>主流 Java 开发工具</h4>
<table>
<thead>
<tr>
<th>工具</th>
<th>厂商</th>
<th>特点</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>IntelliJ IDEA</strong></td>
<td>JetBrains</td>
<td>目前使用最多的 Java 开发集成环境</td>
</tr>
<tr>
<td>NetBeans</td>
<td>Oracle</td>
<td>开源</td>
</tr>
<tr>
<td>Eclipse</td>
<td>IBM 开源社区</td>
<td>老牌 IDE</td>
</tr>
</tbody>
</table>
<blockquote>
<p>官方网站：<a href="https://www.jetbrains.com/idea/">IntelliJ IDEA – the Leading Java and Kotlin IDE (jetbrains.com)</a></p>
</blockquote>
<h4>本节小结</h4>
<table>
<thead>
<tr>
<th>关键点</th>
<th>内容</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>IDEA 全称</strong></td>
<td>&lt;span style="color:blue"&gt;IntelliJ IDEA&lt;/span&gt;，是目前使用最多的 Java 开发集成环境</td>
</tr>
<tr>
<td><strong>IDE 集成环境</strong></td>
<td>把代码的编写、编译、运行、调试等多种功能综合到一起的开发工具</td>
</tr>
</tbody>
</table>
<h3>IDEA 中的第一个代码</h3>
<h4>IDEA 创建 Java 项目的代码结构</h4>
<pre><code>flowchart LR
    P["Project 工程"] --&gt; M["Module 模块"]
    M --&gt; Pa["Package 包"]
    Pa --&gt; C["Class 类"]
</code></pre>
<table>
<thead>
<tr>
<th>层级</th>
<th>含义</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Project</strong></td>
<td>工程</td>
<td>一个项目下可以管理多个模块</td>
</tr>
<tr>
<td><strong>Module</strong></td>
<td>模块</td>
<td>独立的业务单元（如商品模块、订单模块）</td>
</tr>
<tr>
<td><strong>Package</strong></td>
<td>包</td>
<td>组织类，避免类名冲突</td>
</tr>
<tr>
<td><strong>Class</strong></td>
<td>类</td>
<td>实际编写代码的地方</td>
</tr>
</tbody>
</table>
<h4>模块化项目示例</h4>
<pre><code>flowchart TB
    subgraph Project["Project 项目"]
        M1["商品模块"]
        M2["搜索模块"]
        M3["订单模块"]
        M4["购物车模块"]
    end
</code></pre>
<h4>模块内部代码结构</h4>
<p>每个模块（Module）内部通常按职责划分为：</p>
<table>
<thead>
<tr>
<th>代码类别</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>前端交互代码</strong></td>
<td>接收用户请求、返回响应数据</td>
</tr>
<tr>
<td><strong>业务代码</strong></td>
<td>处理业务逻辑（如商品查询、下单）</td>
</tr>
<tr>
<td><strong>数据访问代码</strong></td>
<td>与数据库交互（增删改查）</td>
</tr>
</tbody>
</table>
<h4>学习中的目录结构</h4>
<blockquote>
<p>&lt;span style="color:red"&gt;整个基础篇创建一个 Project，今后每一天都创建一个新的 Module&lt;/span&gt;</p>
</blockquote>
<pre><code>BasicCodes/                      ← Project
├── .idea/
├── day01/                       ← Module（第 1 天）
├── day02/                       ← Module（第 2 天）
├── day03/                       ← Module（第 3 天）
├── BasicCodes.iml
├── External Libraries
└── Scratches and Consoles
</code></pre>
<h3>IDEA 的使用操作</h3>
<h4>常用操作</h4>
<table>
<thead>
<tr>
<th>操作类别</th>
<th>操作项</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>类文件</strong></td>
<td>新建类文件、删除类文件、修改类文件</td>
</tr>
<tr>
<td><strong>模块</strong></td>
<td>新建模块、删除模块、修改模块、导入模块</td>
</tr>
<tr>
<td><strong>项目</strong></td>
<td>关闭项目、打开项目、修改项目、新建项目</td>
</tr>
</tbody>
</table>
<h4>常用快捷键</h4>
<table>
<thead>
<tr>
<th>快捷键</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>psvm</code> + 回车</td>
<td>快速生成 main 方法</td>
</tr>
<tr>
<td><code>sout</code> + 回车</td>
<td>快速生成输出语句</td>
</tr>
<tr>
<td><code>Ctrl + Alt + L</code></td>
<td>格式化代码</td>
</tr>
<tr>
<td><code>Alt + Shift + ↑</code></td>
<td>上移当前行</td>
</tr>
<tr>
<td><code>Alt + Shift + ↓</code></td>
<td>下移当前行</td>
</tr>
<tr>
<td><code>Alt + 回车</code></td>
<td>导入包，自动修正</td>
</tr>
<tr>
<td><code>Ctrl + N</code></td>
<td>查找类</td>
</tr>
<tr>
<td><code>Ctrl + R</code></td>
<td>替换文本</td>
</tr>
<tr>
<td><code>Ctrl + F</code></td>
<td>查找文本</td>
</tr>
<tr>
<td><code>Shift + F6</code></td>
<td>重构-重命名</td>
</tr>
<tr>
<td><code>Ctrl + X</code></td>
<td>剪切当前行</td>
</tr>
<tr>
<td><code>Ctrl + D</code></td>
<td>复制行</td>
</tr>
<tr>
<td><code>Ctrl + /</code></td>
<td>批量加入单行注释，再按一次就是取消</td>
</tr>
<tr>
<td><code>Ctrl + Shift + /</code></td>
<td>批量加入多行注释，再按一次就是取消</td>
</tr>
<tr>
<td><code>Alt + 1</code></td>
<td>快速打开或隐藏工程面板</td>
</tr>
</tbody>
</table>
<h2>第三章 Java 基础语法</h2>
<blockquote>
<p>&lt;span style="color:blue"&gt;根基稳，万事可成&lt;/span&gt;</p>
</blockquote>
<h3>注释</h3>
<h4>什么是注释</h4>
<blockquote>
<p>注释是写在程序中对代码进行解释说明的文字，方便自己和其他人查看，以便理解程序的。</p>
</blockquote>
<h4>Java 注释的三种写法</h4>
<table>
<thead>
<tr>
<th>注释类型</th>
<th>写法格式</th>
<th>特点</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>单行注释</strong></td>
<td><code>// 注释内容</code></td>
<td>只能写一行</td>
</tr>
<tr>
<td><strong>多行注释</strong></td>
<td><code>/* 注释内容1&lt;br&gt;注释内容2 */</code></td>
<td>可以写多行</td>
</tr>
<tr>
<td><strong>文档注释</strong></td>
<td><code>/** 注释内容&lt;br&gt;注释内容 */</code></td>
<td>可以生成 API 文档</td>
</tr>
</tbody>
</table>
<pre><code>/**
 * 通过class关键字定义了一个类，类名叫做 HelloWorld
 * public 起到限制作用，限制文件名和类名保持一致
 */
public class HelloWorld {
    public static void main(String[] args) {
        // 这是一个单行注释，有效范围是从 // 开始到当前行结尾
        System.out.println("HelloWorld");

        /*
            这是一个
            多行注释
         */
        System.out.println("HelloWorld");
    }
}
</code></pre>
<blockquote>
<p>&lt;span style="color:red"&gt;注意事项&lt;/span&gt;：&lt;span style="color:red"&gt;注释内容不会参与编译和运行&lt;/span&gt;</p>
</blockquote>
<h4>本节小结</h4>
<table>
<thead>
<tr>
<th>问题</th>
<th>答案</th>
</tr>
</thead>
<tbody>
<tr>
<td>注释是什么？</td>
<td>写在程序中对程序进行解释说明的文字</td>
</tr>
<tr>
<td>Java 程序中书写注释的方式有几种，各自有什么不同？</td>
<td>单行注释：<code>//</code>&lt;br&gt;多行注释：<code>/* */</code>&lt;br&gt;文档注释：<code>/** */</code></td>
</tr>
<tr>
<td>注释有什么特点？为什么？</td>
<td>不影响程序的执行，编译后的 class 文件中已经没有注释了</td>
</tr>
</tbody>
</table>
<h3>关键字</h3>
<h4>关键字介绍</h4>
<blockquote>
<p><strong>关键字</strong>：被 Java 赋予了&lt;span style="color:red"&gt;特定涵义的英文单词&lt;/span&gt;。</p>
</blockquote>
<pre><code>public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("HelloWorld");
    }
}
</code></pre>
<blockquote>
<p>例如：<code>class</code> 表示"创建类"。</p>
</blockquote>
<blockquote>
<p>&lt;span style="color:red"&gt;注意事项&lt;/span&gt;：Java 中的关键字，已经被赋予了特殊的涵义，这些单词不允许使用。</p>
</blockquote>
<h4>Java 关键字大全</h4>
<table>
<thead>
<tr>
<th>分类</th>
<th>关键字</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>访问控制</strong></td>
<td><code>private</code> <code>protected</code> <code>public</code></td>
</tr>
<tr>
<td><strong>类/方法/变量修饰符</strong></td>
<td><code>abstract</code> <code>class</code> <code>extends</code> <code>final</code> <code>implements</code> <code>interface</code> <code>native</code> <code>new</code> <code>static</code> <code>strictfp</code> <code>synchronized</code> <code>transient</code> <code>volatile</code></td>
</tr>
<tr>
<td><strong>程序控制</strong></td>
<td><code>break</code> <code>case</code> <code>continue</code> <code>default</code> <code>do</code> <code>else</code> <code>for</code> <code>if</code> <code>instanceof</code> <code>return</code> <code>switch</code> <code>while</code></td>
</tr>
<tr>
<td><strong>包相关</strong></td>
<td><code>import</code> <code>package</code></td>
</tr>
<tr>
<td><strong>数据类型</strong></td>
<td><code>boolean</code> <code>byte</code> <code>char</code> <code>double</code> <code>float</code> <code>int</code> <code>long</code> <code>short</code> <code>void</code></td>
</tr>
<tr>
<td><strong>错误处理</strong></td>
<td><code>assert</code> <code>catch</code> <code>finally</code> <code>throw</code> <code>throws</code> <code>try</code></td>
</tr>
<tr>
<td><strong>其他</strong></td>
<td><code>const</code> <code>enum</code> <code>goto</code> <code>super</code> <code>this</code></td>
</tr>
</tbody>
</table>
<h3>字面量</h3>
<h4>字面量介绍</h4>
<blockquote>
<p><strong>字面量</strong>：就是程序中能直接书写的数据，学这个知识的重点是：&lt;span style="color:red"&gt;搞清楚 Java 程序中数据的书写格式&lt;/span&gt;。</p>
</blockquote>
<table>
<thead>
<tr>
<th>常用数据</th>
<th>生活中的写法</th>
<th>程序中的写法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>整数</td>
<td>666、-88</td>
<td>666、-88</td>
<td>写法一致</td>
</tr>
<tr>
<td>小数</td>
<td>13.14、-5.21</td>
<td>13.14、-5.21</td>
<td>写法一致</td>
</tr>
<tr>
<td>字符串</td>
<td>黑马程序员</td>
<td><code>"HelloWorld"</code>、<code>"黑马程序员"</code></td>
<td>&lt;span style="color:red"&gt;程序中必须使用双引号&lt;/span&gt;</td>
</tr>
<tr>
<td>字符</td>
<td>A、0、我</td>
<td><code>'A'</code>、<code>'0'</code>、<code>'我'</code></td>
<td>&lt;span style="color:red"&gt;程序中必须使用单引号，有且仅能一个字符&lt;/span&gt;</td>
</tr>
<tr>
<td>布尔值</td>
<td>真、假</td>
<td><code>true</code>、<code>false</code></td>
<td>只有两个值：<code>true</code> 代表真，<code>false</code> 代表假</td>
</tr>
<tr>
<td>空值</td>
<td>—</td>
<td>值是：<code>null</code></td>
<td>一个特殊的值，空值（后面会讲解作用，暂时不管）</td>
</tr>
</tbody>
</table>
<h4>字面量练习</h4>
<blockquote>
<p>需求：请将自己的个人信息打印在控制台（姓名，年龄，性别，身高，婚姻状况）</p>
</blockquote>
<pre><code>public class Main {
    public static void main(String[] args) {
        System.out.println("张三");
        System.out.println(23);
        System.out.println('男');
        System.out.println(180.1);
        System.out.println(false);
    }
}
</code></pre>
<pre><code>张三
23
男
180.1
false

Process finished with exit code 0
</code></pre>
<h4>本节小结</h4>
<table>
<thead>
<tr>
<th>问题</th>
<th>答案</th>
</tr>
</thead>
<tbody>
<tr>
<td>关键字指的是什么？</td>
<td>被 Java 赋予了特定含义的英文单词</td>
</tr>
<tr>
<td>关键字我们能使用吗？</td>
<td>不允许使用</td>
</tr>
<tr>
<td>字面量这个知识是要求同学们学会什么？</td>
<td><strong>数据在程序中的书写格式</strong></td>
</tr>
<tr>
<td>字符、字符串在程序中的书写格式有什么要求？</td>
<td><strong>字符必须单引号围起来，有且仅能一个字符</strong>&lt;br&gt;<strong>字符串必须用双引号围起来</strong></td>
</tr>
<tr>
<td>几个常见的特殊值的书写格式是？</td>
<td><code>true</code>、<code>false</code>、<code>null</code></td>
</tr>
</tbody>
</table>
<h3>变量</h3>
<h4>变量的介绍和定义格式</h4>
<blockquote>
<p><strong>变量</strong>就是内存中的一块区域，可以理解成一个盒子，用来&lt;span style="color:red"&gt;装&lt;/span&gt;程序要处理的&lt;span style="color:red"&gt;数据&lt;/span&gt;的。</p>
</blockquote>
<pre><code>flowchart LR
    A["数据类型 变量名称 = 变量值;"] --&gt; B["自己起的名字"]
    A --&gt; C["赋值"]
    A --&gt; D["限制盒子中只能存储某种数据形式 例如：int（整数类型）、double（小数类型）"]
</code></pre>
<table>
<thead>
<tr>
<th>部分</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>数据类型</strong></td>
<td>限制盒子中只能存储某种数据形式</td>
</tr>
<tr>
<td><strong>变量名称</strong></td>
<td>自己起的名字</td>
</tr>
<tr>
<td><strong>=</strong></td>
<td>赋值（将右边的值赋给左边的变量）</td>
</tr>
<tr>
<td><strong>变量值</strong></td>
<td>实际存储的数据</td>
</tr>
</tbody>
</table>
<p><strong>示例</strong>：<code>int age = 18;</code></p>
<h4>认识变量</h4>
<blockquote>
<p>&lt;span style="color:red"&gt;变量有啥特点？变量里装的数据是可以被替换的。&lt;/span&gt;</p>
</blockquote>
<pre><code>int age = 18;
age = 19;             // 把 18 替换成 19

System.out.println(age);   // 输出 19
age = age + 1;             // 在 19 的基础上 + 1，变成 20

System.out.println(age);   // 输出 20
</code></pre>
<blockquote>
<p>&lt;span style="color:red"&gt;变量有啥应用场景呢？写程序对数据进行处理就很方便了。&lt;/span&gt;</p>
</blockquote>
<h4>本节小结</h4>
<table>
<thead>
<tr>
<th>问题</th>
<th>答案</th>
</tr>
</thead>
<tbody>
<tr>
<td>变量是什么，变量的完整定义格式是什么样的？</td>
<td>变量是内存中的一块区域，可以理解成盒子，用来记住成程序要处理的数据的。&lt;br&gt;<strong>数据类型 变量名称 = 数据;</strong></td>
</tr>
<tr>
<td>为啥要用变量，变量有啥好处？</td>
<td>使用变量记要处理的数据，编写的代码更灵活，管理代码更方便</td>
</tr>
<tr>
<td>变量有什么特点？基于这个特点，变量有啥应用场景？</td>
<td><strong>变量里装的数据可以被替换</strong></td>
</tr>
</tbody>
</table>
<h4>变量的注意事项</h4>
<table>
<thead>
<tr>
<th>序号</th>
<th>注意事项</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>变量要先声明才能使用</td>
</tr>
<tr>
<td>2</td>
<td>变量是什么类型，就必须装什么类型的数据</td>
</tr>
<tr>
<td>3</td>
<td>变量只在自己所归属的 <code>{}</code> 范围内有效</td>
</tr>
<tr>
<td>4</td>
<td>同一个范围内，变量的名称不能一样</td>
</tr>
<tr>
<td>5</td>
<td>变量定义的时候可以不赋初始值，但在使用时，变量里必须有值</td>
</tr>
<tr>
<td>6</td>
<td>一条语句可以定义多个变量，中间使用逗号分隔</td>
</tr>
</tbody>
</table>
<h3>标识符</h3>
<h4>标识符概述</h4>
<blockquote>
<p><strong>标识符</strong>：就是给类，方法，变量等起名字的符号。</p>
</blockquote>
<pre><code>public class Demo {

    public static void main(String[] args) {

        int salary = 12000;
        System.out.println(salary);

        salary = 15000;
        System.out.println(salary);

        int age = 18;
        System.out.println(age);
    }
}
</code></pre>
<h4>标识符命名规则</h4>
<blockquote>
<p>&lt;span style="color:red"&gt;硬性要求&lt;/span&gt;（不满足则编译报错）</p>
</blockquote>
<table>
<thead>
<tr>
<th>规则</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>组成字符</td>
<td>由&lt;span style="color:blue"&gt;数字、字母、下划线（_）和美元符（$）&lt;/span&gt;组成</td>
</tr>
<tr>
<td>开头</td>
<td>&lt;span style="color:red"&gt;不能以数字开头&lt;/span&gt;</td>
</tr>
<tr>
<td>关键字</td>
<td>&lt;span style="color:red"&gt;不能是关键字&lt;/span&gt;</td>
</tr>
<tr>
<td>大小写</td>
<td>区分大小写</td>
</tr>
</tbody>
</table>
<p><strong>判断下列变量名哪些不符合规则</strong>（红色 = 不符合）：</p>
<table>
<thead>
<tr>
<th>变量名</th>
<th>是否符合</th>
<th>原因</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>bj</code></td>
<td>✅</td>
<td>符合</td>
</tr>
<tr>
<td><code>b2</code></td>
<td>✅</td>
<td>符合</td>
</tr>
<tr>
<td><code>2b</code></td>
<td>❌</td>
<td>数字开头</td>
</tr>
<tr>
<td><code>class</code></td>
<td>❌</td>
<td>是关键字</td>
</tr>
<tr>
<td><code>_2b</code></td>
<td>✅</td>
<td>符合</td>
</tr>
<tr>
<td><code>#itheima</code></td>
<td>❌</td>
<td>包含非法字符 <code>#</code></td>
</tr>
<tr>
<td><code>ak47</code></td>
<td>✅</td>
<td>符合</td>
</tr>
<tr>
<td><code>Class</code></td>
<td>✅</td>
<td>符合（首字母大写但不是关键字）</td>
</tr>
<tr>
<td><code>helloworld</code></td>
<td>✅</td>
<td>符合</td>
</tr>
</tbody>
</table>
<h4>标识符命名规范</h4>
<blockquote>
<p>&lt;span style="color:green"&gt;软性建议&lt;/span&gt;（不满足不报错，但建议遵守）</p>
</blockquote>
<table>
<thead>
<tr>
<th>命名法</th>
<th>适用场景</th>
<th>单单词</th>
<th>多单词</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>小驼峰命名法</strong></td>
<td>变量、方法</td>
<td>所有字母小写：<code>name</code></td>
<td>从第二个单词开始，首字母大写：<code>firstName</code></td>
</tr>
<tr>
<td><strong>大驼峰命名法</strong></td>
<td>类</td>
<td>首字母大写：<code>Student</code></td>
<td>每个单词的首字母大写：<code>GoodStudent</code></td>
</tr>
</tbody>
</table>
<h4>本节小结</h4>
<table>
<thead>
<tr>
<th>问题</th>
<th>答案</th>
</tr>
</thead>
<tbody>
<tr>
<td>什么是标识符？</td>
<td>标识符就是名字</td>
</tr>
<tr>
<td>标识符的规则？</td>
<td>由数字、字母、下划线、美元符等组成；&lt;span style="color:red"&gt;且不能数字开头，不能用关键字，不能用特殊符号（&amp;、%）&lt;/span&gt;</td>
</tr>
<tr>
<td>命名建议？</td>
<td>见名知义，驼峰命名</td>
</tr>
</tbody>
</table>
<h3>数据类型</h3>
<h4>数据类型分类</h4>
<pre><code>flowchart LR
    A["数据类型"] --&gt; B["基本数据类型"]
    A --&gt; C["引用数据类型"]
</code></pre>
<h4>八种基本数据类型</h4>
<table>
<thead>
<tr>
<th>数据类型</th>
<th>关键字</th>
<th>取值范围</th>
<th>内存占用</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>整数</strong></td>
<td><code>byte</code></td>
<td>-128 ~ 127</td>
<td>1</td>
</tr>
<tr>
<td><strong>整数</strong></td>
<td><code>short</code></td>
<td>-32768 ~ 32767</td>
<td>2</td>
</tr>
<tr>
<td><strong>整数</strong></td>
<td><strong><code>int</code></strong>（默认）</td>
<td>-2147483648 ~ 2147483647（10 位数）</td>
<td>4</td>
</tr>
<tr>
<td><strong>整数</strong></td>
<td><code>long</code></td>
<td>-9223372036854775808 ~ 9223372036854775807（19 位数）</td>
<td>8</td>
</tr>
<tr>
<td><strong>浮点数</strong></td>
<td><code>float</code></td>
<td>1.401298e-45 到 3.402823e+38</td>
<td>4</td>
</tr>
<tr>
<td><strong>浮点数</strong></td>
<td><strong><code>double</code></strong>（默认）</td>
<td>4.9000000e-324 到 1.797693e+308</td>
<td>8</td>
</tr>
<tr>
<td><strong>字符</strong></td>
<td><code>char</code></td>
<td>0 ~ 65535</td>
<td>2</td>
</tr>
<tr>
<td><strong>布尔</strong></td>
<td><code>boolean</code></td>
<td><code>true</code>、<code>false</code></td>
<td>1</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:orange"&gt;说明&lt;/span&gt;：<code>e+38</code> 表示是乘以 10 的 38 次方；同样，<code>e-45</code> 表示乘以 10 的负 45 次方。</p>
</blockquote>
<pre><code>public class Test {
    public static void main(String[] args) {
        System.out.println(10);     // 整数默认 int 类型
        System.out.println(12.3);   // 小数默认 double 类型
    }
}
</code></pre>
<h4>编码表与 ASCII 码表</h4>
<blockquote>
<p><strong>编码表</strong>：是计算机中字节到字符的一套对应关系。</p>
</blockquote>
<blockquote>
<p><strong>ASCII 码表</strong>：American Standard Code for Information Interchange，美国信息交换标准代码。</p>
</blockquote>
<table>
<thead>
<tr>
<th>项目</th>
<th>内容</th>
</tr>
</thead>
<tbody>
<tr>
<td>数字 <code>0</code></td>
<td>对应 ASCII 码 <code>48</code></td>
</tr>
<tr>
<td>大写字母 <code>A</code></td>
<td>对应 ASCII 码 <code>65</code></td>
</tr>
<tr>
<td>小写字母 <code>a</code></td>
<td>对应 ASCII 码 <code>97</code></td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:red"&gt;注&lt;/span&gt;：表中的 ASCII 字符可以用 <code>ALT + 小键盘上的数字键</code> 输入。</p>
</blockquote>
<h4>数据类型案例</h4>
<blockquote>
<p>需求：请将自己的个人信息，使用变量保存，并展示在控制台。</p>
</blockquote>
<table>
<thead>
<tr>
<th>字段</th>
<th>示例值</th>
<th>数据类型</th>
</tr>
</thead>
<tbody>
<tr>
<td>姓名</td>
<td>黑马谢广坤</td>
<td><code>String</code></td>
</tr>
<tr>
<td>年龄</td>
<td>18</td>
<td><code>int</code></td>
</tr>
<tr>
<td>性别</td>
<td>男</td>
<td><code>char</code></td>
</tr>
<tr>
<td>身高</td>
<td>180.1</td>
<td><code>double</code></td>
</tr>
<tr>
<td>是否单身</td>
<td>是</td>
<td><code>boolean</code></td>
</tr>
</tbody>
</table>
<h4>本节小结</h4>
<table>
<thead>
<tr>
<th>问题</th>
<th>答案</th>
</tr>
</thead>
<tbody>
<tr>
<td>Java 中的数据类型大体分为两类，分别是？</td>
<td><strong>基本数据类型</strong>、<strong>引用数据类型</strong></td>
</tr>
<tr>
<td>整数类型首选？小数类型首选？</td>
<td><strong><code>int</code></strong>、<strong><code>double</code></strong></td>
</tr>
<tr>
<td>定义字符串类型的变量，类型选的是？</td>
<td><strong><code>String</code></strong></td>
</tr>
<tr>
<td>所有整数默认是？小数默认是？</td>
<td>整数默认 <code>int</code>；小数默认 <code>double</code></td>
</tr>
<tr>
<td><code>char</code> 类型的变量，可以接收整数类型的数据吗？</td>
<td>可以（<code>char</code> 取值范围 0 ~ 65535，对应 ASCII 码）</td>
</tr>
</tbody>
</table>
<h2>第四章 运算符</h2>
<blockquote>
<p>对变量和字面量操作的符号</p>
</blockquote>
<h3>算术运算符</h3>
<h4>运算符分类</h4>
<table>
<thead>
<tr>
<th>符号</th>
<th>作用</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>+</code></td>
<td>加</td>
<td>参看小学一年级</td>
</tr>
<tr>
<td><code>-</code></td>
<td>减</td>
<td>参看小学一年级</td>
</tr>
<tr>
<td><code>*</code></td>
<td>乘</td>
<td>参看小学二年级，与 "×" 相同</td>
</tr>
<tr>
<td><code>/</code></td>
<td>除</td>
<td>参看小学二年级，与 "÷" 相同</td>
</tr>
<tr>
<td><code>%</code></td>
<td>取余</td>
<td>获取的是两个数据做除法的&lt;span style="color:red"&gt;余数&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<blockquote>
<p><code>/</code> 和 <code>%</code> 的区别：两个数据做除法，<code>/</code> 取结果的&lt;span style="color:orange"&gt;商&lt;/span&gt;，<code>%</code> 取结果的&lt;span style="color:orange"&gt;余数&lt;/span&gt;。</p>
<p>&lt;span style="color:red"&gt;整数操作只能得到整数&lt;/span&gt;，要想得到小数，必须有浮点数参与运算。</p>
</blockquote>
<h4>案例：数值拆分</h4>
<blockquote>
<p>需求：将数字 123 拆分出个位、十位、百位后，打印在控制台</p>
</blockquote>
<pre><code>整数123的个位为:3
整数123的十位为:2
整数123的百位为:1
</code></pre>
<table>
<thead>
<tr>
<th>拆分位</th>
<th>公式</th>
<th>计算过程</th>
</tr>
</thead>
<tbody>
<tr>
<td>个位</td>
<td><code>数值 % 10</code></td>
<td>123 除以 10（商 12，余数为 3）</td>
</tr>
<tr>
<td>十位</td>
<td><code>数值 / 10 % 10</code></td>
<td>123 除以 10（商 12），12 除以 10（商 1，余数为 2）</td>
</tr>
<tr>
<td>百位</td>
<td><code>数值 / 10 / 10 % 10</code></td>
<td>123 / 10 得到 12，12 / 10 得到 1，1 % 10 得到 1</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>公式总结</strong>：</p>
<ul>
<li>个位：数值 <code>% 10</code></li>
<li>十位：数值 <code>/ 10 % 10</code></li>
<li>百位：数值 <code>/ 10 / 10 % 10</code></li>
<li>千位：数值 <code>/ 10 / 10 / 10 % 10</code></li>
<li>……</li>
</ul>
</blockquote>
<h4>本节小结</h4>
<table>
<thead>
<tr>
<th>问题</th>
<th>答案</th>
</tr>
</thead>
<tbody>
<tr>
<td>整数相除可以直接得到小数结果吗？如果不可以该怎么解决？</td>
<td>不可以，需要加入小数运算</td>
</tr>
<tr>
<td>假设给定一个五位数 65536，获取千位的计算公式是？</td>
<td><code>数值 / 10 / 10 / 10 % 10</code></td>
</tr>
</tbody>
</table>
<h3>字符串拼接</h3>
<blockquote>
<p>当 <code>+</code> 操作中，遇到了字符串，这时 <code>+</code> 就是&lt;span style="color:red"&gt;字符串连接符&lt;/span&gt;，而不是算术运算。</p>
</blockquote>
<pre><code>public class Test {
    public static void main(String[] args) {
        System.out.println(1 + 23);                  // 24
        System.out.println("年龄为:" + 23);           // 年龄为:23
        System.out.println(1 + 99 + "年黑马");        // 100年黑马
    }
}
</code></pre>
<blockquote>
<p>说明：从左到右依次运算，遇到字符串后，所有后续 <code>+</code> 都变为字符串拼接。</p>
</blockquote>
<pre><code>public class Test {
    public static void main(String[] args) {
        System.out.println("年龄为:" + 23 + 1);        // 年龄为:231
    }
}
</code></pre>
<blockquote>
<p>计算过程：</p>
<ol>
<li><code>"年龄为:" + 23</code> → <code>"年龄为:23"</code></li>
<li><code>"年龄为:23" + 1</code> → <code>"年龄为:231"</code></li>
</ol>
</blockquote>
<blockquote>
<p>&lt;span style="color:red"&gt;解决方案&lt;/span&gt;：使用小括号改变运算优先级。</p>
</blockquote>
<pre><code>public class Test {
    public static void main(String[] args) {
        System.out.println("年龄为:" + (23 + 1));      // 年龄为:24
    }
}
</code></pre>
<blockquote>
<p>当 <code>+</code> 号操作中遇到了字符串，就会变成字符串拼接。</p>
</blockquote>
<h3>自增自减运算符</h3>
<table>
<thead>
<tr>
<th>符号</th>
<th>作用</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>++</code></td>
<td>自增</td>
<td>变量自身的值加 1</td>
</tr>
<tr>
<td><code>--</code></td>
<td>自减</td>
<td>变量自身的值减 1</td>
</tr>
</tbody>
</table>
<blockquote>
<p><code>++</code> 和 <code>--</code> 既可以放在变量的后边，也可以放在变量的前边（参考购物车 + / - 按钮）。</p>
</blockquote>
<h4>本节小结</h4>
<table>
<thead>
<tr>
<th>场景</th>
<th>规则</th>
</tr>
</thead>
<tbody>
<tr>
<td>单独使用</td>
<td><code>++</code>、<code>--</code> 单独使用在变量前后无区别</td>
</tr>
<tr>
<td>参与运算</td>
<td><code>++</code> 在前，&lt;span style="color:red"&gt;先自增再操作&lt;/span&gt;；<code>++</code> 在后，&lt;span style="color:orange"&gt;先操作再自增&lt;/span&gt;</td>
</tr>
<tr>
<td>操作数限制</td>
<td><code>++</code>、<code>--</code> 只能操作变量，不能操作字面量</td>
</tr>
</tbody>
</table>
<h3>类型转换</h3>
<pre><code>flowchart LR
    A["类型转换"] --&gt; B["隐式转换"]
    A --&gt; C["强制转换"]
</code></pre>
<h4>隐式转换介绍</h4>
<blockquote>
<p>把一个&lt;span style="color:orange"&gt;取值范围小&lt;/span&gt;的数值或者变量，&lt;span style="color:orange"&gt;赋值&lt;/span&gt;给另一个&lt;span style="color:green"&gt;取值范围大&lt;/span&gt;的变量</p>
</blockquote>
<pre><code>public class Test {
    public static void main(String[] args) {
        int a = 10;
        double b = a;          // 隐式转换：int → double
        System.out.println(b); // 10.0
    }
}
</code></pre>
<pre><code>flowchart LR
    byte --&gt; short --&gt; int --&gt; long --&gt; float --&gt; double
    char --&gt; int
</code></pre>
<h4>运算过程中的隐式转换</h4>
<blockquote>
<p>取值范围小的数据，和取值范围大的数据进行运算，小的会先提升为大的之后，再进行运算</p>
</blockquote>
<pre><code>public class Test {
    public static void main(String[] args) {
        int a = 10;
        double b = 12.3;
        double c = a + b;      // 22.3，a 临时提升为 double
    }
}
</code></pre>
<blockquote>
<p>&lt;span style="color:red"&gt;特殊规则&lt;/span&gt;：&lt;span style="color:blue"&gt;<code>byte</code> <code>short</code> <code>char</code>&lt;/span&gt; 三种数据在运算的时候，都会先提升为 <code>int</code>，然后再进行运算。</p>
</blockquote>
<pre><code>public class Test {
    public static void main(String[] args) {
        byte a = 10;
        byte b = 20;
        byte c = a + b;        // 编译报错：a + b 提升为 int，需要强转
    }
}
</code></pre>
<pre><code>public class Test {
    public static void main(String[] args) {
        byte a = 10;
        byte b = 20;
        int c = a + b;         // 正确：int 接收
    }
}
</code></pre>
<blockquote>
<p><code>char</code> 与 <code>int</code> 相加时，<code>char</code> 会提升为 <code>int</code>（按 ASCII 码取值）。</p>
</blockquote>
<pre><code>public class Test {
    public static void main(String[] args) {
        int a = 1;
        char b = 'a';
        int c = a + b;         // 98（'a' 对应 ASCII 码 97）
        System.out.println(c);
    }
}
</code></pre>
<h4>强制类型转换</h4>
<blockquote>
<p>把一个&lt;span style="color:orange"&gt;取值范围大&lt;/span&gt;的数值或者变量，&lt;span style="color:orange"&gt;赋值&lt;/span&gt;给另一个&lt;span style="color:red"&gt;取值范围小&lt;/span&gt;的变量</p>
<p>&lt;span style="color:red"&gt;不允许直接赋值&lt;/span&gt;，需要加入强制转换</p>
</blockquote>
<p><strong>格式</strong>：<code>目标数据类型 变量名 = (目标数据类型) 被强转的数据;</code></p>
<pre><code>public class Test {
    public static void main(String[] args) {
        double a = 12.3;
        int b = (int) a;       // 12（精度损失，小数部分丢失）
        System.out.println(b);
    }
}
</code></pre>
<h4>本节小结</h4>
<table>
<thead>
<tr>
<th>类别</th>
<th>规则</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>基本的隐式转换</strong></td>
<td>把一个取值范围小的数值或者变量，赋值给另一个取值范围大的变量</td>
</tr>
<tr>
<td><strong>运算中的隐式转换</strong></td>
<td>取值范围小的数据，和取值范围大的数据进行运算，小的会先提升为大的之后，再进行运算；<code>byte</code>、<code>short</code>、<code>char</code> 三种数据运算时会先提升为 <code>int</code></td>
</tr>
<tr>
<td><strong>强制类型转换</strong></td>
<td>取值范围大的数据，给取值范围小的变量赋值，需要强制类型转换；&lt;span style="color:red"&gt;强转有可能造成精度损失&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<h3>赋值运算符</h3>
<h4>基本赋值运算符</h4>
<table>
<thead>
<tr>
<th>符号</th>
<th>作用</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>=</code></td>
<td>赋值</td>
<td><code>a = 10</code>，将 10 赋值给变量 a</td>
</tr>
</tbody>
</table>
<h4>扩展赋值运算符</h4>
<table>
<thead>
<tr>
<th>符号</th>
<th>作用</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>+=</code></td>
<td>加后赋值</td>
<td><code>a += b</code>，将 a + b 的值给 a</td>
</tr>
<tr>
<td><code>-=</code></td>
<td>减后赋值</td>
<td><code>a -= b</code>，将 a - b 的值给 a</td>
</tr>
<tr>
<td><code>*=</code></td>
<td>乘后赋值</td>
<td><code>a *= b</code>，将 a × b 的值给 a</td>
</tr>
<tr>
<td><code>/=</code></td>
<td>除后赋值</td>
<td><code>a /= b</code>，将 a ÷ b 的商给 a</td>
</tr>
<tr>
<td><code>%=</code></td>
<td>取余后赋值</td>
<td><code>a %= b</code>，将 a ÷ b 的余数给 a</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:red"&gt;注意&lt;/span&gt;：扩展赋值运算符隐含了强转效果。</p>
</blockquote>
<h4>本节小结</h4>
<table>
<thead>
<tr>
<th>类别</th>
<th>符号</th>
</tr>
</thead>
<tbody>
<tr>
<td>基本赋值运算符</td>
<td><code>=</code></td>
</tr>
<tr>
<td>扩展赋值运算符</td>
<td><code>+=</code> <code>-=</code> <code>*=</code> <code>/=</code> <code>%=</code>（隐含了强转效果）</td>
</tr>
<tr>
<td>关系运算符</td>
<td><code>&gt;</code> <code>&gt;=</code> <code>&lt;</code> <code>&lt;=</code> <code>==</code> <code>!=</code>（最终返回 <code>boolean</code> 类型结果）</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:red"&gt;警告&lt;/span&gt;：<code>==</code> 是关系运算符，<code>=</code> 是赋值运算符，不要混淆！</p>
</blockquote>
<h3>关系运算符</h3>
<table>
<thead>
<tr>
<th>符号</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>==</code></td>
<td><code>a==b</code>，判断 a 和 b 的值是否相等，成立为 <code>true</code>，不成立为 <code>false</code></td>
</tr>
<tr>
<td><code>!=</code></td>
<td><code>a!=b</code>，判断 a 和 b 的值是否不相等，成立为 <code>true</code>，不成立为 <code>false</code></td>
</tr>
<tr>
<td><code>&gt;</code></td>
<td><code>a&gt;b</code>，判断 a 是否大于 b，成立为 <code>true</code>，不成立为 <code>false</code></td>
</tr>
<tr>
<td><code>&gt;=</code></td>
<td><code>a&gt;=b</code>，判断 a 是否大于等于 b，成立为 <code>true</code>，不成立为 <code>false</code></td>
</tr>
<tr>
<td><code>&lt;</code></td>
<td><code>a&lt;b</code>，判断 a 是否小于 b，成立为 <code>true</code>，不成立为 <code>false</code></td>
</tr>
<tr>
<td><code>&lt;=</code></td>
<td><code>a&lt;=b</code>，判断 a 是否小于等于 b，成立为 <code>true</code>，不成立为 <code>false</code></td>
</tr>
</tbody>
</table>
<blockquote>
<p>关系运算符最终返回的都是 <code>boolean</code> 类型的结果。</p>
</blockquote>
<h3>逻辑运算符</h3>
<blockquote>
<p>把多个条件放在一起运算，最终返回&lt;span style="color:blue"&gt;布尔类型&lt;/span&gt;的值：<code>true</code>、<code>false</code></p>
<p>连接布尔类型的表达式，或者是值。</p>
</blockquote>
<h4>案例：判断成绩是否优秀</h4>
<blockquote>
<p>需求：在程序中，判断一个学生成绩是否在 90~100 之间，是的话程序输出优秀。</p>
</blockquote>
<pre><code>public class Test {
    public static void main(String[] args) {
        int score = 95;
        System.out.println(score &gt;= 90 &amp; score &lt;= 100);  // true
    }
}
</code></pre>
<h4>逻辑运算符分类</h4>
<table>
<thead>
<tr>
<th>符号</th>
<th>介绍</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>&amp;</code></td>
<td>逻辑与</td>
<td>并且，遇 <code>false</code> 则 <code>false</code></td>
</tr>
<tr>
<td><code>|</code></td>
<td>逻辑或</td>
<td>或者，遇 <code>true</code> 则 <code>true</code></td>
</tr>
<tr>
<td><code>!</code></td>
<td>逻辑非</td>
<td>取反</td>
</tr>
<tr>
<td><code>^</code></td>
<td>逻辑异或</td>
<td>相同为 <code>false</code>，不同为 <code>true</code></td>
</tr>
</tbody>
</table>
<h4>短路逻辑运算符</h4>
<table>
<thead>
<tr>
<th>符号</th>
<th>介绍</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>&amp;&amp;</code></td>
<td>短路与</td>
<td>作用和 <code>&amp;</code> 相同，但是有&lt;span style="color:red"&gt;短路效果&lt;/span&gt;</td>
</tr>
<tr>
<td><code>||</code></td>
<td>短路或</td>
<td>作用和 <code>|</code> 相同，但是有&lt;span style="color:red"&gt;短路效果&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th>运算符</th>
<th>左操作数</th>
<th>右操作数</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>&amp;</code>（逻辑与）</td>
<td>无论 <code>true</code> 还是 <code>false</code></td>
<td>右边都要执行</td>
</tr>
<tr>
<td><code>&amp;&amp;</code>（短路与）</td>
<td>左为 <code>true</code></td>
<td>右边执行</td>
</tr>
<tr>
<td><code>&amp;&amp;</code>（短路与）</td>
<td>左为 <code>false</code></td>
<td>&lt;span style="color:red"&gt;右边不执行&lt;/span&gt;</td>
</tr>
<tr>
<td><code>|</code>（逻辑或）</td>
<td>无论 <code>true</code> 还是 <code>false</code></td>
<td>右边都要执行</td>
</tr>
<tr>
<td><code>||</code>（短路或）</td>
<td>左为 <code>false</code></td>
<td>右边执行</td>
</tr>
<tr>
<td><code>||</code>（短路或）</td>
<td>左为 <code>true</code></td>
<td>&lt;span style="color:red"&gt;右边不执行&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<pre><code>flowchart LR
    A["&amp; 逻辑与"] --&gt; A1["遇 false 则 false"]
    B["&amp;&amp; 短路与"] --&gt; B1["左边为 false 右边不执行"]
    C["| 逻辑或"] --&gt; C1["遇 true 则 true"]
    D["|| 短路或"] --&gt; D1["左边为 true 右边不执行"]
    E["! 逻辑非"] --&gt; E1["取反"]
    F["^ 逻辑异或"] --&gt; F1["相同是 false 不同是 true"]
</code></pre>
<blockquote>
<p>&lt;span style="color:red"&gt;注意&lt;/span&gt;：实际开发中，常用的逻辑运算符是：<code>&amp;&amp;</code>、<code>||</code>、<code>!</code></p>
</blockquote>
<h3>三元运算符</h3>
<p><strong>格式</strong>：<code>判断条件 ? 值1 : 值2;</code></p>
<p><strong>执行流程</strong>：</p>
<ol>
<li>首先计算&lt;span style="color:red"&gt;判断条件的值&lt;/span&gt;</li>
<li>如果值为 &lt;span style="color:green"&gt;<code>true</code>&lt;/span&gt;，&lt;span style="color:blue"&gt;值 1&lt;/span&gt; 就是运算结果</li>
<li>如果值为 &lt;span style="color:red"&gt;<code>false</code>&lt;/span&gt;，&lt;span style="color:blue"&gt;值 2&lt;/span&gt; 就是运算结果</li>
</ol>
<pre><code>flowchart LR
    A["判断条件 ?"] --&gt; B{"判断条件的值"}
    B -- true --&gt; C["值 1 （运算结果）"]
    B -- false --&gt; D["值 2 （运算结果）"]
</code></pre>
<blockquote>
<p>&lt;span style="color:green"&gt;总结&lt;/span&gt;：根据判断条件，从两份数据中二者选其一。</p>
</blockquote>
<h3>运算符优先级</h3>
<table>
<thead>
<tr>
<th>优先级</th>
<th>运算符</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td><code>.</code> <code>()</code></td>
</tr>
<tr>
<td>2</td>
<td><code>!</code>、<code>~</code>、<code>++</code>、<code>--</code></td>
</tr>
<tr>
<td>3</td>
<td><code>*</code>、<code>/</code>、<code>%</code></td>
</tr>
<tr>
<td>4</td>
<td><code>+</code>、<code>-</code></td>
</tr>
<tr>
<td>5</td>
<td><code>&lt;&lt;</code>、<code>&gt;&gt;</code>、<code>&gt;&gt;&gt;</code></td>
</tr>
<tr>
<td>6</td>
<td><code>&lt;</code>、<code>&lt;=</code>、<code>&gt;</code>、<code>&gt;=</code>、<code>instanceof</code></td>
</tr>
<tr>
<td>7</td>
<td><code>==</code>、<code>!=</code></td>
</tr>
<tr>
<td>8</td>
<td><code>&amp;</code></td>
</tr>
<tr>
<td>9</td>
<td><code>^</code></td>
</tr>
<tr>
<td>10</td>
<td><code>|</code></td>
</tr>
<tr>
<td>11</td>
<td><code>&amp;&amp;</code></td>
</tr>
<tr>
<td>12</td>
<td><code>||</code></td>
</tr>
<tr>
<td>13</td>
<td><code>?:</code></td>
</tr>
<tr>
<td>14</td>
<td><code>=</code>、<code>+=</code>、<code>-=</code>、<code>*=</code>、<code>/=</code>、<code>%=</code>、<code>&amp;=</code></td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:orange"&gt;规律&lt;/span&gt;：<code>&amp;&amp;</code> 优先级高于 <code>||</code>，赋值运算符优先级最低。</p>
</blockquote>
<h4>优先级案例解析</h4>
<pre><code>public class Test {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        System.out.println(a &gt; b || a &lt; b &amp;&amp; a &gt; b);
        // 解析：&amp;&amp; 优先级高于 ||，先算 a &lt; b &amp;&amp; a &gt; b（false），再算 a &gt; b || false（false || false = false）
    }
}
</code></pre>
<blockquote>
<p>&lt;span style="color:red"&gt;小技巧&lt;/span&gt;：不确定优先级时，用 <code>()</code> 改变优先级，可读性更强。</p>
</blockquote>
<pre><code>public class Test {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        System.out.println((a &gt; b || a &lt; b) &amp;&amp; a &gt; b);
        // 显式加括号：先算 a &gt; b || a &lt; b（true），再算 true &amp;&amp; a &gt; b（true &amp;&amp; false = false）
    }
}
</code></pre>
<h3>Scanner 键盘录入</h3>
<h4>需求</h4>
<blockquote>
<p>请在程序中，提示用户通过键盘输入自己的姓名、年龄，并能在程序中收到这些数据。</p>
<p>Java 已经写好了实现程序，我们调用即可。</p>
</blockquote>
<h4>三个步骤</h4>
<pre><code>import java.util.Scanner;       // ① 导包：告诉程序去 JDK 的哪个包中找扫描器技术

public class Test {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);  // ② 创建对象：代表得到键盘扫描器对象

        System.out.println("请输入您的年龄：");
        int age = sc.nextInt();              // ③ 调用方法：等待接收用户输入数据
        System.out.println("年龄是：" + age);

        System.out.println("请输入您的名称：");
        String name = sc.next();
        System.out.println("欢迎：" + name);
    }
}
</code></pre>
<h4>本节小结</h4>
<blockquote>
<p>Java 程序中如何实现接收用户键盘输入的数据？</p>
<p>使用 Java 提供的类 <code>Scanner</code> 来完成，&lt;span style="color:red"&gt;步骤&lt;/span&gt;如下：</p>
</blockquote>
<table>
<thead>
<tr>
<th>步骤</th>
<th>代码</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td><code>import java.util.Scanner;</code></td>
<td>&lt;span style="color:blue"&gt;导包&lt;/span&gt;</td>
</tr>
<tr>
<td>2</td>
<td><code>Scanner sc = new Scanner(System.in);</code></td>
<td>&lt;span style="color:blue"&gt;创建对象&lt;/span&gt;</td>
</tr>
<tr>
<td>3</td>
<td><code>sc.nextInt()</code> / <code>sc.next()</code> / <code>sc.nextDouble()</code></td>
<td>&lt;span style="color:blue"&gt;调用方法接收数据&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th>方法</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>sc.nextInt()</code></td>
<td>接收整数</td>
</tr>
<tr>
<td><code>sc.next()</code></td>
<td>接字符串</td>
</tr>
<tr>
<td><code>sc.nextDouble()</code></td>
<td>接收浮点数</td>
</tr>
</tbody>
</table>
<h2>第五章 方法</h2>
<blockquote>
<p>函数，实现特定功能的操作步骤集合</p>
</blockquote>
<h3>方法的介绍</h3>
<h4>为什么要使用方法</h4>
<blockquote>
<p>如果将所有功能都写在 <code>main</code> 方法中，代码会臃肿（"臃肿"），不便于维护。</p>
</blockquote>
<pre><code>public class Test {
    public static void main(String[] args) {
        // ...50 行代码
    }
}
</code></pre>
<blockquote>
<p>解决方法：将每个功能<strong>单独封装</strong>到一个方法中，<code>main</code> 中只负责调用。</p>
</blockquote>
<pre><code>public class Test {
    public static void main(String[] args) {

    }

    /** 查看全部商品 */
    public static void showGoodsInfo() { ... }

    /** 上架商品 */
    public static void addGoods() { ... }

    /** 下架商品 */
    public static void deleteGoods() { ... }

    /** 修改商品 */
    public static void updateGoods() { ... }
}
</code></pre>
<h4>方法的概念</h4>
<table>
<thead>
<tr>
<th>维度</th>
<th>内容</th>
</tr>
</thead>
<tbody>
<tr>
<td>别名</td>
<td>方法（method）、函数（function）</td>
</tr>
<tr>
<td>定义</td>
<td>一段具有独立功能的代码块</td>
</tr>
<tr>
<td>特点</td>
<td>&lt;span style="color:red"&gt;不调用就不执行&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<h4>小结</h4>
<blockquote>
<p>方法（函数）就是将&lt;u&gt;具有独立功能的代码&lt;/u&gt;封装起来，组成一个独立的小工具，方便重复使用，提升维护性。</p>
</blockquote>
<table>
<thead>
<tr>
<th>维度</th>
<th>好处</th>
</tr>
</thead>
<tbody>
<tr>
<td>代码管理</td>
<td>可以将挤在一起的臃肿代码按照功能进行分类管理，&lt;span style="color:green"&gt;提高维护性&lt;/span&gt;</td>
</tr>
<tr>
<td>代码复用</td>
<td>&lt;span style="color:green"&gt;提高代码的复用性&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<h3>方法的基本定义和调用</h3>
<h4>定义和调用格式</h4>
<table>
<thead>
<tr>
<th>维度</th>
<th>格式</th>
</tr>
</thead>
<tbody>
<tr>
<td>定义格式</td>
<td><code>public static void 方法名() { // 方法体 }</code></td>
</tr>
<tr>
<td>调用格式</td>
<td><code>方法名();</code></td>
</tr>
</tbody>
</table>
<pre><code>public class Test {
    public static void show() {
        // 方法体
    }

    public static void main(String[] args) {
        show();   // 调用
    }
}
</code></pre>
<h4>案例：方法计算两个整数最大值</h4>
<blockquote>
<p>需求：定义一个方法，方法中定义两个整数变量，求出最大值并打印在控制台</p>
</blockquote>
<table>
<thead>
<tr>
<th>步骤</th>
<th>内容</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>根据格式编写方法，方法名自己给出（见名知意 小驼峰命名法）</td>
</tr>
<tr>
<td>2</td>
<td>方法内部定义出两个 <code>int</code> 类型变量</td>
</tr>
<tr>
<td>3</td>
<td>使用三元运算符求出最大值</td>
</tr>
<tr>
<td>4</td>
<td>调用该方法，让内部的逻辑执行起来</td>
</tr>
</tbody>
</table>
<h3>方法的执行流程</h3>
<h4>方法的调用流程</h4>
<table>
<thead>
<tr>
<th>阶段</th>
<th>行为</th>
</tr>
</thead>
<tbody>
<tr>
<td>方法未被调用时</td>
<td>字节码文件存放在 &lt;span style="color:red"&gt;方法区&lt;/span&gt;</td>
</tr>
<tr>
<td>方法被调用时</td>
<td>需要进入到 &lt;span style="color:red"&gt;栈内存&lt;/span&gt; 中运行</td>
</tr>
</tbody>
</table>
<pre><code>flowchart LR
    A["方法区 字节码文件 例如 main"] --&gt;|被调用| B["栈内存 方法运行时进入的内存"]
</code></pre>
<h4>入栈与出栈</h4>
<table>
<thead>
<tr>
<th>行为</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>入栈</td>
<td>方法被调用时，进入栈内存</td>
</tr>
<tr>
<td>出栈</td>
<td>方法执行完毕后，弹出栈内存</td>
</tr>
</tbody>
</table>
<pre><code>public class MethodDemo1 {
    public static void main(String[] args) {
        System.out.println("开始");
        getMax();
        System.out.println("结束");
    }

    public static void getMax() {
        int num1 = 10;
        int num2 = 20;
        int max = num1 &gt; num2 ? num1 : num2;
        System.out.println(max);
    }
}
</code></pre>
<p>执行流程：</p>
<pre><code>flowchart TD
    M["main 压栈 执行 println 开始"] --&gt; G["getMax 压栈 求 max = 20"]
    G --&gt; G1["println 打印 20"]
    G1 --&gt; G2["getMax 执行完毕 出栈"]
    G2 --&gt; M1["main 继续执行 println 结束"]
    M1 --&gt; M2["main 执行完毕 出栈"]
</code></pre>
<h4>多个方法嵌套调用</h4>
<pre><code>public class MethodDemo2 {
    public static void main(String[] args) {
        study();
    }

    public static void sleep() {
        System.out.println("睡觉");
    }

    public static void eat() {
        System.out.println("吃饭");
    }

    public static void study() {
        eat();
        System.out.println("学习");
        sleep();
    }
}
</code></pre>
<h4>小结</h4>
<table>
<thead>
<tr>
<th>内存区域</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>方法区</td>
<td>&lt;span style="color:red"&gt;字节码文件加载时进入的内存&lt;/span&gt;</td>
</tr>
<tr>
<td>栈内存</td>
<td>&lt;span style="color:red"&gt;方法运行时入栈，方法运行结束出栈&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<h3>带参数的方法</h3>
<h4>为什么要带参数</h4>
<blockquote>
<p>方法中写死的数据不灵活，每次只能处理固定的内容。</p>
</blockquote>
<pre><code>public class MethodDemo1 {
    public static void main(String[] args) {
        getMax();
    }

    public static void getMax() {
        int num1 = 10;
        int num2 = 20;
        int max = num1 &gt; num2 ? num1 : num2;
        System.out.println(max);
    }
}
</code></pre>
<blockquote>
<p>上面的 <code>getMax()</code> 只能计算 10 和 20 的最大值。&lt;br&gt;
解决办法：让方法可以接收调用方传入的数据 → 形参 + 实参</p>
</blockquote>
<h4>定义格式</h4>
<table>
<thead>
<tr>
<th>参数数量</th>
<th>格式</th>
</tr>
</thead>
<tbody>
<tr>
<td>单个参数</td>
<td><code>public static void 方法名 (数据类型 变量名) { // 方法体 }</code></td>
</tr>
<tr>
<td>多个参数</td>
<td><code>public static void 方法名 (数据类型 变量名1, 数据类型 变量名2...) { // 方法体 }</code></td>
</tr>
</tbody>
</table>
<pre><code>public static void call(String name) {
    // ...
}

public static void add(int a, int b) {
    // ...
}
</code></pre>
<h4>调用格式</h4>
<table>
<thead>
<tr>
<th>参数数量</th>
<th>格式</th>
</tr>
</thead>
<tbody>
<tr>
<td>单个参数</td>
<td><code>方法名(参数);</code></td>
</tr>
<tr>
<td>多个参数</td>
<td><code>方法名(参数1, 参数2);</code></td>
</tr>
</tbody>
</table>
<pre><code>call("钢门吹雪");
add(10, 20);
</code></pre>
<h4>形参和实参</h4>
<table>
<thead>
<tr>
<th>名称</th>
<th>全称</th>
<th>出现时机</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;形参&lt;/span&gt;</td>
<td>形式参数</td>
<td>&lt;span style="color:red"&gt;定义&lt;/span&gt;方法时</td>
<td>方法声明的参数，相当于局部变量的声明</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;实参&lt;/span&gt;</td>
<td>实际参数</td>
<td>&lt;span style="color:red"&gt;调用&lt;/span&gt;方法时</td>
<td>传入的具体数据</td>
</tr>
</tbody>
</table>
<pre><code>public class MethodDemo2 {
    public static void main(String[] args) {
        getMax(10, 20);   // 10、20 是实参
    }

    public static void getMax(int num1, int num2) {  // num1、num2 是形参
        int max = num1 &gt; num2 ? num1 : num2;
        System.out.println(max);
    }
}
</code></pre>
<h4>小结</h4>
<table>
<thead>
<tr>
<th>知识点</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>参数作用</td>
<td>&lt;span style="color:green"&gt;带参数的方法可以提高方法的灵活性&lt;/span&gt;</td>
</tr>
<tr>
<td>形参</td>
<td>方法定义时声明的参数</td>
</tr>
<tr>
<td>实参</td>
<td>方法调用时传入的参数</td>
</tr>
</tbody>
</table>
<h3>带返回值的方法</h3>
<h4>为什么需要返回值</h4>
<blockquote>
<p>A 方法产生的数据，B 方法想要使用的话，就需要定义带返回值的方法</p>
</blockquote>
<pre><code>// 之前：A 方法中计算 sum，B 方法想用 sum 是用不了的
public static void methodA(int a, int b) {
    int sum = a + b;
    // ...
}

public static void methodB() {
    System.out.println(sum % 2 == 0);   // 报错，拿不到 sum
}
</code></pre>
<pre><code>// 现在：A 方法通过 return 把数据交给 B 方法
public static int methodA(int a, int b) {
    int sum = a + b;
    return sum;
}

public static void methodB() {
    int result = methodA(10, 20);
    System.out.println(result % 2 == 0);
}
</code></pre>
<h4>定义格式</h4>
<pre><code>public static 数据类型 方法名 (数据类型 变量名1, 数据类型 变量名2...) {
    return 数据值;
}
</code></pre>
<pre><code>public static int add(int a, int b) {
    int c = a + b;
    return c;
}

public static double getNum() {
    return 12.3;
}
</code></pre>
<h4>调用格式</h4>
<table>
<thead>
<tr>
<th>场景</th>
<th>格式</th>
</tr>
</thead>
<tbody>
<tr>
<td>单纯调用</td>
<td><code>方法名(参数...);</code></td>
</tr>
<tr>
<td>接收返回值</td>
<td><code>数据类型 变量名 = 方法名(参数...);</code></td>
</tr>
</tbody>
</table>
<pre><code>add(10, 20);                    // 单纯调用，返回值被丢弃
int result = add(10, 20);       // 用变量接收
</code></pre>
<h4>小结</h4>
<table>
<thead>
<tr>
<th>知识点</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>使用场景</td>
<td>A 方法中的数据，如果 B 方法想要使用的话，就需要设计返回值</td>
</tr>
<tr>
<td>关键字</td>
<td>通过 &lt;span style="color:red"&gt;<code>return</code>&lt;/span&gt; 关键字，将结果数据返回</td>
</tr>
<tr>
<td>返回位置</td>
<td>返回到方法的调用处</td>
</tr>
</tbody>
</table>
<h3>方法通用定义格式</h3>
<h4>完整格式</h4>
<pre><code>public static 返回值类型 方法名(参数) {
    方法体;
    return 数据;
}
</code></pre>
<table>
<thead>
<tr>
<th>组成</th>
<th>含义</th>
<th>类比</th>
</tr>
</thead>
<tbody>
<tr>
<td>参数</td>
<td>制作材料</td>
<td>面粉、酵母、糖…</td>
</tr>
<tr>
<td>方法体</td>
<td>使用材料干活</td>
<td>搅拌、烘烤…</td>
</tr>
<tr>
<td>返回值</td>
<td>成品</td>
<td>面包</td>
</tr>
</tbody>
</table>
<h4>定义方法的两个明确</h4>
<table>
<thead>
<tr>
<th>明确项</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;明确参数&lt;/span&gt;</td>
<td>主要是明确参数的类型和数量</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;明确返回值类型&lt;/span&gt;</td>
<td>方法操作完毕后是否有结果数据，如果有写对应的数据类型，没有写 <code>void</code></td>
</tr>
</tbody>
</table>
<h4>调用方法的两种方式</h4>
<table>
<thead>
<tr>
<th>方法类型</th>
<th>调用方式</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>void</code> 类型的方法</td>
<td>直接调用即可：<code>方法名(参数);</code></td>
</tr>
<tr>
<td>非 <code>void</code> 类型的方法</td>
<td>推荐用变量接收调用：<code>数据类型 变量名 = 方法名(参数);</code></td>
</tr>
</tbody>
</table>
<h4>案例</h4>
<table>
<thead>
<tr>
<th>需求编号</th>
<th>需求</th>
</tr>
</thead>
<tbody>
<tr>
<td>需求 1</td>
<td>设计一个方法，能够计算出 2 个小数的和</td>
</tr>
<tr>
<td>需求 2</td>
<td>设计一个方法，能够计算出 3 个整数的最小值</td>
</tr>
<tr>
<td>需求 3</td>
<td>设计一个方法，能够打印出用户的个人信息（姓名，年龄，身高，性别）</td>
</tr>
</tbody>
</table>
<h3>方法注意事项</h3>
<table>
<thead>
<tr>
<th>序号</th>
<th>注意事项</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>&lt;span style="color:red"&gt;方法不调用就不执行&lt;/span&gt;</td>
</tr>
<tr>
<td>2</td>
<td>方法与方法之间是&lt;span style="color:red"&gt;平级关系，不能嵌套定义&lt;/span&gt;</td>
</tr>
<tr>
<td>3</td>
<td>方法的编写顺序和执行顺序&lt;span style="color:red"&gt;无关&lt;/span&gt;</td>
</tr>
<tr>
<td>4</td>
<td>方法的返回值类型为 <code>void</code> 时，表示该方法没有返回值，没有返回值的方法可以省略 <code>return</code> 语句不写</td>
</tr>
<tr>
<td>5</td>
<td>如果要编写 <code>return</code>，后面不能跟具体的数据（<code>void</code> 方法中）</td>
</tr>
<tr>
<td>6</td>
<td><code>return</code> 语句下面，不能编写代码，因为永远执行不到，属于无效的代码</td>
</tr>
</tbody>
</table>
<pre><code>// 错误示例：return 之后还有代码
public class MethodDemo {
    public static void method() {
        // 代码片段
        return;
        System.out.println("卑微代码");   // ❌ 报错
    }
}
</code></pre>
<h3>方法重载</h3>
<h4>引入</h4>
<blockquote>
<p>需求：提供 4 个计算方法，功能如下</p>
<ul>
<li>2 个 <code>int</code> 相加</li>
<li>2 个 <code>double</code> 相加</li>
<li>3 个 <code>int</code> 相加</li>
<li>3 个 <code>double</code> 相加</li>
</ul>
</blockquote>
<pre><code>public class Test {
    public static int add1(int a, int b) {
        return a + b;
    }

    public static double add2(double a, double b) {
        return a + b;
    }

    public static int add3(int a, int b, int c) {
        return a + b + c;
    }

    public static double add4(double a, double b, double c) {
        return a + b + c;
    }
}
</code></pre>
<blockquote>
<p>但是 4 个 add1/add2/add3/add4 名字太繁琐了，&lt;br&gt;
能否全部用 <code>add</code> 同一个名字？→ 方法重载</p>
</blockquote>
<pre><code>public class Test {
    public static int    add(int a, int b)              { return a + b; }
    public static double add(double a, double b)        { return a + b; }
    public static int    add(int a, int b, int c)       { return a + b + c; }
    public static double add(double a, double b, double c) { return a + b + c; }
}
</code></pre>
<pre><code>flowchart LR
    C1["add(10, 20)"] --&gt; M1["add(int, int)"]
    C2["add(10, 20, 30)"] --&gt; M2["add(int, int, int)"]
    C3["add(12.3, 12.3)"] --&gt; M3["add(double, double)"]
    C4["add(12.3, 12.3, 12.3)"] --&gt; M4["add(double, double, double)"]
</code></pre>
<blockquote>
<p>调用方法的时候，Java 虚拟机会&lt;span style="color:red"&gt;通过参数的不同来区分同名的方法&lt;/span&gt;</p>
</blockquote>
<h4>方法重载的定义</h4>
<table>
<thead>
<tr>
<th>维度</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>位置</td>
<td>在&lt;span style="color:red"&gt;同一个类&lt;/span&gt;中</td>
</tr>
<tr>
<td>方法名</td>
<td>多个方法的&lt;span style="color:red"&gt;方法名相同&lt;/span&gt;</td>
</tr>
<tr>
<td>参数</td>
<td>每个方法具有&lt;span style="color:red"&gt;不同的参数类型或参数个数&lt;/span&gt;</td>
</tr>
<tr>
<td>结果</td>
<td>这些同名的方法，就构成了重载关系（Overload）</td>
</tr>
</tbody>
</table>
<blockquote>
<p>简单记：同一个类中，方法名相同，参数不同的方法&lt;br&gt;
参数不同：&lt;span style="color:green"&gt;个数不同、类型不同、顺序不同&lt;/span&gt;</p>
</blockquote>
<blockquote>
<p>&lt;span style="color:red"&gt;注意：识别方法之间是否是重载关系，只看方法名和参数，跟返回值无关。&lt;/span&gt;</p>
</blockquote>
<h4>重载判断正误</h4>
<table>
<thead>
<tr>
<th>示例</th>
<th>是否构成重载</th>
<th>原因</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>void fn(int a)</code> / <code>int fn(int a)</code></td>
<td>❌</td>
<td>参数相同，仅返回值不同</td>
</tr>
<tr>
<td><code>void fn(int a)</code> / <code>int fn(int a, int b)</code></td>
<td>✅</td>
<td>参数个数不同</td>
</tr>
<tr>
<td><code>void fn(int a)</code> / <code>int fn(double a)</code></td>
<td>✅</td>
<td>参数类型不同</td>
</tr>
<tr>
<td>两个类中的 <code>fn(int a)</code> / <code>fn(double a)</code></td>
<td>❌</td>
<td>不在同一个类中</td>
</tr>
<tr>
<td><code>void fn(int a, double b)</code> / <code>void fn(double a, int b)</code></td>
<td>✅</td>
<td>参数顺序不同</td>
</tr>
<tr>
<td><code>void fn(int a, double b)</code> / <code>void fn(int c, double d)</code></td>
<td>❌</td>
<td>仅形参名不同</td>
</tr>
</tbody>
</table>
<h4>方法重载的好处</h4>
<blockquote>
<p>不用记忆过多繁琐的方法名字</p>
</blockquote>
<pre><code>public class MethodDemo {
    public static void main(String[] args) {
        // 输出调用
        System.out.println(10);       // 同一个 println，传入 int
        System.out.println(10.1);     // 同一个 println，传入 double
        System.out.println(true);     // 同一个 println，传入 boolean
    }
}
</code></pre>
<h4>小结</h4>
<table>
<thead>
<tr>
<th>知识点</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>方法重载 Overload</td>
<td>&lt;span style="color:red"&gt;同一个类中，方法名相同，参数不同的方法&lt;/span&gt;</td>
</tr>
<tr>
<td>重载看返回值吗？</td>
<td>&lt;span style="color:green"&gt;不看，只看参数类型、参数个数、参数顺序&lt;/span&gt;</td>
</tr>
<tr>
<td>重载有啥好处？</td>
<td>&lt;span style="color:green"&gt;不需要记忆太多方法名字&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<h3>IDEA 安装 AI 插件</h3>
<h4>IDEA 集成通义灵码</h4>
<table>
<thead>
<tr>
<th>阶段</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>IDEA 之前的编程方式</td>
<td>程序的编码工作，还是需要程序员思考，并逐行编写</td>
</tr>
<tr>
<td>AI 出现后</td>
<td>IDEA 可以集成很多辅助编程的 AI 编程插件</td>
</tr>
<tr>
<td>推荐插件</td>
<td>阿里巴巴 通义灵码</td>
</tr>
</tbody>
</table>
<blockquote>
<p>官网：<a href="https://lingma.aliyun.com/lingma/download">https://lingma.aliyun.com/lingma/download</a></p>
</blockquote>
<h2>第六章 流程控制语句</h2>
<blockquote>
<p>&lt;span style="color:blue"&gt;控制程序代码执行顺序&lt;/span&gt;
多一句没有，少一句不行。用更短的时间，教会更实用的技术</p>
</blockquote>
<h3>1. 流程控制语句分类</h3>
<table>
<thead>
<tr>
<th>分类</th>
<th>说明</th>
<th>关键字</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;顺序结构&lt;/span&gt;</td>
<td>Java 程序默认的执行流程，没有特定的语法结构，按照代码的先后顺序，依次执行</td>
<td>程序默认</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;分支结构&lt;/span&gt;</td>
<td>根据条件选择性地执行某段代码</td>
<td><code>if</code>、<code>switch</code></td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;循环结构&lt;/span&gt;</td>
<td>让一段代码重复执行很多次</td>
<td><code>for</code>、<code>while</code>、<code>do...while</code></td>
</tr>
</tbody>
</table>
<pre><code>flowchart LR
    S[开始] --&gt; A[语句A] --&gt; B[语句B] --&gt; C[语句C] --&gt; E[结束]
</code></pre>
<pre><code>public class Test {
    public static void main(String[] args) {
        System.out.println("A");
        System.out.println("B");
        System.out.println("C");
    }
}
</code></pre>
<h3>2. 分支结构 if</h3>
<h4>if 的三种格式</h4>
<table>
<thead>
<tr>
<th>格式</th>
<th>语法</th>
<th>适用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;格式 1&lt;/span&gt;</td>
<td><code>if (判断条件) { 语句体; }</code></td>
<td>单条件判断</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;格式 2&lt;/span&gt;</td>
<td><code>if (判断条件) { 语句体1; } else { 语句体2; }</code></td>
<td>二选一</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;格式 3&lt;/span&gt;</td>
<td><code>if (判断条件1) { 语句体1; } else if (判断条件2) { 语句体2; } ... else { 语句体n+1; }</code></td>
<td>多条件区间判断</td>
</tr>
</tbody>
</table>
<p><strong>执行流程</strong>：</p>
<pre><code>flowchart LR
    S[开始] --&gt; C1{判断条件}
    C1 -- true --&gt; B1[执行语句体]
    C1 -- false --&gt; E[结束]
    B1 --&gt; E
</code></pre>
<pre><code>flowchart LR
    S[开始] --&gt; C1{判断条件}
    C1 -- true --&gt; B1[执行语句体1]
    C1 -- false --&gt; B2[执行语句体2]
    B1 --&gt; E[结束]
    B2 --&gt; E
</code></pre>
<pre><code>flowchart LR
    S[开始] --&gt; C1{判断条件1}
    C1 -- true --&gt; B1[执行语句体1]
    C1 -- false --&gt; C2{判断条件2}
    C2 -- true --&gt; B2[执行语句体2]
    C2 -- false --&gt; C3{...}
    C3 -- true --&gt; Bn[执行相应语句体]
    C3 -- false --&gt; Bx[执行语句体n+1]
    B1 --&gt; E[结束]
    B2 --&gt; E
    Bn --&gt; E
    Bx --&gt; E
</code></pre>
<blockquote>
<ol>
<li>&lt;span style="color:orange"&gt;if 分支是什么？&lt;/span&gt;可以根据条件，选择执行某段程序</li>
<li>&lt;span style="color:orange"&gt;if 分支的写法有几种？&lt;/span&gt;三种（见上表）</li>
</ol>
</blockquote>
<h4>案例：考试奖励</h4>
<blockquote>
<p>需求：键盘录入考试成绩，根据成绩所在的区间，程序打印出不同的奖励</p>
</blockquote>
<table>
<thead>
<tr>
<th>成绩区间</th>
<th>奖励</th>
</tr>
</thead>
<tbody>
<tr>
<td>95 ~ 100</td>
<td>自行车一辆</td>
</tr>
<tr>
<td>90 ~ 94</td>
<td>游乐场玩一次</td>
</tr>
<tr>
<td>80 ~ 89</td>
<td>变形金刚一个</td>
</tr>
<tr>
<td>80 分以下</td>
<td>胖揍一顿</td>
</tr>
</tbody>
</table>
<pre><code>Scanner sc = new Scanner(System.in);
System.out.print("请输入考试成绩：");
int score = sc.nextInt();

if (score &gt;= 95 &amp;&amp; score &lt;= 100) {
    System.out.println("自行车一辆");
} else if (score &gt;= 90 &amp;&amp; score &lt;= 94) {
    System.out.println("游乐场玩一次");
} else if (score &gt;= 80 &amp;&amp; score &lt;= 89) {
    System.out.println("变形金刚一个");
} else {
    System.out.println("胖揍一顿");
}
</code></pre>
<h4>if 语句注意事项</h4>
<ul>
<li>&lt;span style="color:red"&gt;if 语句中，如果大括号控制的是一条语句，大括号可以省略不写&lt;/span&gt;</li>
<li>&lt;span style="color:red"&gt;if 语句的 () 和 {} 之间不要写分号&lt;/span&gt;</li>
<li>&lt;span style="color:red"&gt;if 语句的 () 中需要产生 boolean 类型的结果，根据结果决定程序的走向&lt;/span&gt;</li>
</ul>
<h3>3. 分支结构 switch</h3>
<blockquote>
<p>通过比较值来决定执行哪条分支</p>
</blockquote>
<h4>switch 分支的执行流程</h4>
<ol>
<li>&lt;span style="color:orange"&gt;先计算表达式的值&lt;/span&gt;，再拿着这个值去与 <code>case</code> 后的值进行匹配</li>
<li>与哪个 <code>case</code> 后的值匹配为 <code>true</code> 就执行哪个 <code>case</code> 块的代码，&lt;span style="color:red"&gt;遇到 <code>break</code> 就跳出 switch 分支&lt;/span&gt;</li>
<li>如果全部 <code>case</code> 后的值与之匹配都是 <code>false</code>，则执行 <code>default</code> 块的代码</li>
</ol>
<pre><code>switch (表达式) {
    case 值1:
        语句体1;
        break;
    case 值2:
        语句体2;
        break;
    ...
    default:
        语句体n+1;
        break;
}
</code></pre>
<blockquote>
<ul>
<li>&lt;span style="color:green"&gt;if 适合做条件是区间判断的情况&lt;/span&gt;</li>
<li>&lt;span style="color:green"&gt;switch 适合做：条件是比较值的情况、代码优雅、性能较好&lt;/span&gt;</li>
</ul>
</blockquote>
<h4>switch 语句注意事项</h4>
<ol>
<li>&lt;span style="color:red"&gt;表达式类型只能是 <code>byte</code>、<code>short</code>、<code>int</code>、<code>char</code>，不支持 <code>double</code>、<code>float</code>、<code>long</code>&lt;/span&gt;
<ul>
<li>JDK5 开始支持&lt;span style="color:blue"&gt;枚举&lt;/span&gt;</li>
<li>JDK7 开始支持 &lt;span style="color:blue"&gt;String&lt;/span&gt;</li>
</ul>
</li>
<li>&lt;span style="color:red"&gt;<code>case</code> 给出的值不允许重复&lt;/span&gt;，且&lt;span style="color:red"&gt;只能是字面量，不能是变量&lt;/span&gt;</li>
<li>&lt;span style="color:red"&gt;正常使用 switch 的时候，不要忘记写 <code>break</code>，否则会出现穿透现象&lt;/span&gt;</li>
</ol>
<h3>4. Debug 工具</h3>
<blockquote>
<ul>
<li>&lt;span style="color:blue"&gt;Debug&lt;/span&gt;：是供程序员使用的程序调试工具
<ul>
<li>它可以用于&lt;span style="color:orange"&gt;查看程序的执行流程&lt;/span&gt;</li>
<li>也可以用于追踪程序执行过程来&lt;span style="color:orange"&gt;调试程序&lt;/span&gt;</li>
</ul>
</li>
<li>&lt;span style="color:blue"&gt;Debug 调试&lt;/span&gt;，又被称为&lt;span style="color:red"&gt;断点调试&lt;/span&gt;，&lt;span style="color:blue"&gt;断点&lt;/span&gt;其实是一个标记，告诉 Debug 从标记的地方开始查看</li>
</ul>
</blockquote>
<h4>Debug 工具栏</h4>
<table>
<thead>
<tr>
<th>操作</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;程序向下执行一步&lt;/span&gt;</td>
<td>单步执行（F8）</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;跳出当前方法&lt;/span&gt;</td>
<td>从方法中返回到调用处</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;进入方法查看详情&lt;/span&gt;</td>
<td>进入方法体逐行执行（F7）</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;结束 DeBug 模式&lt;/span&gt;</td>
<td>终止本次调试</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;再点一次取消断点&lt;/span&gt;</td>
<td>移除该行断点</td>
</tr>
</tbody>
</table>
<h4>Debug 视图</h4>
<ul>
<li>&lt;span style="color:blue"&gt;Frames&lt;/span&gt;：显示正在执行的方法（<code>main6, Main</code>、<code>getMax:12, Main</code>）</li>
<li>&lt;span style="color:blue"&gt;Variables&lt;/span&gt;：显示变量的变化过程（<code>a = 10</code>、<code>b = 20</code>）</li>
<li>&lt;span style="color:blue"&gt;Console&lt;/span&gt;：控制台输出</li>
</ul>
<h4>Breakpoints 视图</h4>
<blockquote>
<p>统一管理所有断点（Java Line Breakpoints、Java Exception Breakpoints 等），可设置 &lt;span style="color:orange"&gt;Suspend: All / Thread&lt;/span&gt;、&lt;span style="color:orange"&gt;Condition&lt;/span&gt;、&lt;span style="color:orange"&gt;Log message&lt;/span&gt;、&lt;span style="color:orange"&gt;Remove once hit&lt;/span&gt; 等高级属性</p>
</blockquote>
<h3>5. 循环结构 for</h3>
<blockquote>
<p>循环语句的作用：&lt;span style="color:red"&gt;可以将一段代码重复的执行很多次&lt;/span&gt;</p>
</blockquote>
<pre><code>for (初始化语句; 条件判断语句; 条件控制语句) {
    循环体语句;
}
</code></pre>
<h4>跑圈示例</h4>
<blockquote>
<p>循环任务：跑圈
控制循环：根据心中 1 2 3 4 数字的变化进行控制
循环条件：心中的数小于 4，就开始循环任务</p>
</blockquote>
<pre><code>flowchart LR
    S[开始] --&gt; I[初始化语句 int i = 1] --&gt; C{i &lt; 4}
    C -- true --&gt; B[循环体语句 sout 跑圈] --&gt; U[条件控制语句 i++]
    U --&gt; C
    C -- false --&gt; E[结束]
</code></pre>
<h4>for 循环小结</h4>
<blockquote>
<p>循环语句的作用：可以将一段代码重复的执行很多次
for 循环语句格式：</p>
</blockquote>
<pre><code>for (初始化语句; 条件判断语句; 条件控制语句) {
    循环体语句;
}
</code></pre>
<p><strong>执行流程</strong>：</p>
<ol>
<li>初始化语句</li>
<li>执行条件判断语句，看结果是 true，还是 false；&lt;span style="color:red"&gt;false 结束，true 继续&lt;/span&gt;</li>
<li>执行循环体语句</li>
<li>执行条件控制语句</li>
<li>回到 2 继续</li>
</ol>
<h4>案例：循环打印 10 次「黑马程序员」</h4>
<blockquote>
<p>需求：请使用循环，在控制台打印出 10 次「黑马程序员」</p>
</blockquote>
<pre><code>for (int i = 1; i &lt;= 10; i++) {
    System.out.println("黑马程序员");
}
</code></pre>
<h4>案例：模拟计时器</h4>
<blockquote>
<p>需求：请使用循环在控制台打印数字 1~3 和 3~1 的数据</p>
</blockquote>
<table>
<thead>
<tr>
<th>思路积累</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:green"&gt;循环中，用于控制循环的变量还可以放在循环体中，继续进行使用&lt;/span&gt;</td>
<td>—</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;条件控制语句，不一定是 <code>++</code>&lt;/span&gt;</td>
<td>例如 <code>i--</code></td>
</tr>
</tbody>
</table>
<pre><code>// 打印 1 ~ 3
for (int i = 1; i &lt;= 3; i++) {
    System.out.println(i);
}
System.out.println("------------");

// 打印 3 ~ 1
for (int i = 3; i &gt;= 1; i--) {
    System.out.println(i);
}
</code></pre>
<h4>案例：求和</h4>
<blockquote>
<p>需求：求 1-100 之间的偶数和，并把求和结果在控制台输出</p>
</blockquote>
<p><strong>分析</strong>：</p>
<ol>
<li>定义求和变量 <code>sum</code>，准备记录累加的结果</li>
<li>使用 for 循环，分别获取数据 1 ~ 100</li>
<li>循环使用 if 语句，筛选出偶数数据</li>
<li>&lt;span style="color:red"&gt;将每一个偶数数据，和 <code>sum</code> 变量进行累加&lt;/span&gt;</li>
<li>循环结束后，打印求和后的结果</li>
</ol>
<blockquote>
<p>&lt;span style="color:green"&gt;思路积累：今后若遇到的需求是，求 xxx 的和，就要联想到求和变量 <code>sum</code>&lt;/span&gt;</p>
</blockquote>
<pre><code>int sum = 0;
for (int i = 1; i &lt;= 100; i++) {
    if (i % 2 == 0) {
        sum += i;
    }
}
System.out.println("1-100之间的偶数和是：" + sum);
</code></pre>
<h4>案例：水仙花数</h4>
<blockquote>
<p>需求：在控制台输出所有的「水仙花数」</p>
</blockquote>
<table>
<thead>
<tr>
<th>定义</th>
<th>解释</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;水仙花数是一个三位数&lt;/span&gt;</td>
<td>例：111、222、333、370、371、520、999</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;个位、十位、百位的数字立方和等于原数&lt;/span&gt;</td>
<td>例：123 → 1³+2³+3³ = 1+8+27 = 36 ≠ 123（不是水仙花数）&lt;br/&gt;例：371 → 3³+7³+1³ = 27+343+1 = 371 = 371（是水仙花数）</td>
</tr>
</tbody>
</table>
<p><strong>分析</strong>：</p>
<ol>
<li>通过 for 循环，获取到所有的三位数 100 ~ 999</li>
<li>在循环内部，将每一个三位数拆分为（个位，十位，百位）</li>
<li>加入 if 筛选条件：<code>if (ge*ge*ge + shi*shi*shi + bai*bai*bai == 原数)</code></li>
<li>满足条件则输出水仙花数</li>
</ol>
<pre><code>for (int i = 100; i &lt;= 999; i++) {
    int ge = i % 10;
    int shi = i / 10 % 10;
    int bai = i / 100 % 10;
    if (ge*ge*ge + shi*shi*shi + bai*bai*bai == i) {
        System.out.println(i);
    }
}
</code></pre>
<h4>案例：统计水仙花数</h4>
<blockquote>
<p>需求：在控制台输出所有的「水仙花数」，并统计出水仙花数的个数</p>
</blockquote>
<p><strong>分析</strong>：</p>
<ol>
<li>&lt;span style="color:red"&gt;定义计数器变量 <code>count</code>，准备统计水仙花数的个数&lt;/span&gt;</li>
<li>通过 for 循环，获取到所有的三位数 100 ~ 999</li>
<li>在循环内部，将每一个三位数拆分为（个位，十位，百位）</li>
<li>加入 if 筛选条件：<code>if (ge*ge*ge + shi*shi*shi + bai*bai*bai == 原数)</code></li>
<li>&lt;span style="color:red"&gt;满足条件则输出水仙花数，<code>count</code> 变量 <code>++</code>&lt;/span&gt;</li>
<li>打印 <code>count</code> 所记录的值</li>
</ol>
<blockquote>
<p>&lt;span style="color:green"&gt;思路积累：今后若遇到的需求是，统计 xxx，就要联想到计数器变量 <code>count</code>&lt;/span&gt;</p>
</blockquote>
<pre><code>int count = 0;
for (int i = 100; i &lt;= 999; i++) {
    int ge = i % 10;
    int shi = i / 10 % 10;
    int bai = i / 100 % 10;
    if (ge*ge*ge + shi*shi*shi + bai*bai*bai == i) {
        System.out.println(i);
        count++;
    }
}
System.out.println("水仙花数共有：" + count + " 个");
</code></pre>
<h4>for 循环语句注意事项</h4>
<ul>
<li>&lt;span style="color:red"&gt;循环 {} 中定义的变量，在每一轮循环结束后，都会从内存中释放&lt;/span&gt;</li>
<li>&lt;span style="color:red"&gt;循环 () 中定义的变量，在整个循环结束后，都会从内存中释放&lt;/span&gt;</li>
<li>&lt;span style="color:red"&gt;循环语句 () 和 {} 之间不要写分号&lt;/span&gt;</li>
</ul>
<blockquote>
<ul>
<li>循环 () 中定义的变量，在循环结束后还可以继续使用吗？</li>
<li>循环 {} 中定义的变量，什么时候从内存中消失？</li>
<li>循环 () 和 {} 之间写分号，还可以控制循环体吗？</li>
</ul>
</blockquote>
<h4>循环嵌套</h4>
<blockquote>
<p>循环嵌套：在循环语句中，继续出现循环语句。
&lt;span style="color:red"&gt;外循环执行一次，内循环执行一圈&lt;/span&gt;</p>
</blockquote>
<pre><code>for (int i = 1; i &lt;= 5; i++) {
    for (int j = 1; j &lt;= 5; j++) {
        System.out.println("HelloWorld");
    }
}
</code></pre>
<h4>案例：循环嵌套</h4>
<blockquote>
<p>使用 <code>*</code> 在控制台打印出 4 行 5 列的矩形</p>
</blockquote>
<pre><code>* * * * *
* * * * *
* * * * *
* * * * *
</code></pre>
<blockquote>
<p>使用 <code>*</code> 在控制台打印出 5 行的直角三角形</p>
</blockquote>
<pre><code>*
* *
* * *
* * * *
* * * * *
</code></pre>
<pre><code>// 4 行 5 列矩形
for (int i = 1; i &lt;= 4; i++) {
    for (int j = 1; j &lt;= 5; j++) {
        System.out.print("* ");
    }
    System.out.println();
}

// 5 行直角三角形
for (int i = 1; i &lt;= 5; i++) {
    for (int j = 1; j &lt;= i; j++) {
        System.out.print("* ");
    }
    System.out.println();
}
</code></pre>
<blockquote>
<ul>
<li>&lt;span style="color:green"&gt;循环嵌套：在循环中继续编写循环语句&lt;/span&gt;</li>
<li>&lt;span style="color:green"&gt;执行流程：外循环执行一次，内循环执行一圈&lt;/span&gt;</li>
<li>&lt;span style="color:green"&gt;思路：如果一个循环任务还要继续执行多次，使用外循环包裹&lt;/span&gt;</li>
</ul>
</blockquote>
<h3>6. 循环结构 while</h3>
<pre><code>初始化语句;
while (判断语句) {
    循环体语句;
    控制语句;
}
</code></pre>
<h4>案例：珠穆朗玛峰</h4>
<blockquote>
<p>需求：世界最高峰珠穆朗玛峰高度是 8848.86 米 = 8848860 毫米，假如我有一张足够大的纸，它的厚度是 0.1 毫米。请问：该纸张折叠多少次，可以折成珠穆朗玛峰的高度？</p>
</blockquote>
<p><strong>分析</strong>：</p>
<ol>
<li>定义变量存储珠穆朗玛峰的高度、纸张的高度</li>
</ol>
<pre><code>double peakHeight = 8848860;   // 山峰高度
double paperThickness = 0.1;   // 纸张厚度
</code></pre>
<ol>
<li>使用 while 循环来控制纸张折叠，循环条件是（纸张厚度 &lt; 山峰高度），循环每执行一次，就表示纸张折叠一次，并把纸张厚度变为原来两倍</li>
<li>循环外定义计数变量 <code>count</code>，循环每折叠一次纸张，让 <code>count</code> 变量 +1</li>
</ol>
<pre><code>double peakHeight = 8848860;
double paperThickness = 0.1;
int count = 0;
while (paperThickness &lt; peakHeight) {
    paperThickness *= 2;
    count++;
}
System.out.println("需要折叠 " + count + " 次");
</code></pre>
<h4>while 与 for 的区别</h4>
<blockquote>
<ol>
<li>while 循环的格式，执行流程是怎么样的？</li>
</ol>
</blockquote>
<pre><code>初始化语句;
while (循环条件) {
    循环体语句;
    迭代语句;
}
</code></pre>
<pre><code>int i = 1;
while (i &lt;= 3) {
    System.out.println("HelloWorld");
    i++;
}
</code></pre>
<blockquote>
<ol>
<li>while 和 for 有什么区别？什么时候用 for，什么时候用 while？</li>
</ol>
<ul>
<li>&lt;span style="color:green"&gt;功能上是完全一样的，for 能解决的 while 也能解决，反之亦然&lt;/span&gt;</li>
<li>&lt;span style="color:green"&gt;使用规范：知道循环次数，使用 for；不知道循环次数建议使用 while&lt;/span&gt;</li>
</ul>
</blockquote>
<h3>7. 循环结构 do...while</h3>
<pre><code>初始化语句;
do {
    循环体语句;
    条件控制语句;
} while (循环条件);
</code></pre>
<pre><code>int i = 0;
do {
    System.out.println("Hello World!");
    i++;
} while (i &lt; 3);
</code></pre>
<blockquote>
<p>&lt;span style="color:red"&gt;do-while 循环的特点：先执行后判断&lt;/span&gt;</p>
</blockquote>
<h4>三种循环的区别</h4>
<table>
<thead>
<tr>
<th>循环</th>
<th>判断时机</th>
<th>适用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;for 循环&lt;/span&gt;</td>
<td>先判断后执行</td>
<td>已知循环次数</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;while 循环&lt;/span&gt;</td>
<td>先判断后执行</td>
<td>不明确循环次数</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;do...while 循环&lt;/span&gt;</td>
<td>先执行后判断</td>
<td>循环体至少执行一次</td>
</tr>
</tbody>
</table>
<blockquote>
<ul>
<li>&lt;span style="color:green"&gt;for 循环和 while 循环的执行流程是一模一样的，功能上无区别，for 能做的 while 也能做，反之亦然&lt;/span&gt;</li>
<li>&lt;span style="color:green"&gt;使用规范：如果已知循环次数建议使用 for 循环，如果不清楚要循环多少次建议使用 while 循环&lt;/span&gt;</li>
<li>&lt;span style="color:green"&gt;其他区别：for 循环中，控制循环的变量只在循环中使用；while 循环中，控制循环的变量在循环后还可以继续使用&lt;/span&gt;</li>
</ul>
</blockquote>
<pre><code>// for 循环：循环结束后控制变量不可用
for (int i = 0; i &lt; 3; i++) {
    System.out.println("Hello World");
}
System.out.println(i);  // 报错：i 已不可用

// while 循环：循环结束后控制变量仍可用
int i = 0;
while (i &lt; 3) {
    System.out.println("Hello World");
    i++;
}
System.out.println(i);  // 输出 3
</code></pre>
<h3>8. break、continue</h3>
<blockquote>
<p>跳转控制语句</p>
</blockquote>
<table>
<thead>
<tr>
<th>关键字</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;break&lt;/span&gt;</td>
<td>&lt;span style="color:red"&gt;终止&lt;/span&gt; 循环体内容的执行，也就是说结束当前的整个循环</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;continue&lt;/span&gt;</td>
<td>&lt;span style="color:red"&gt;跳过&lt;/span&gt; 某次循环体内容的执行，继续下一次的执行</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>注意事项</strong>：</p>
<ul>
<li>&lt;span style="color:orange"&gt;break：只能在循环，和 switch 当中进行使用&lt;/span&gt;</li>
<li>&lt;span style="color:orange"&gt;continue：只能在循环中进行使用&lt;/span&gt;</li>
</ul>
</blockquote>
<h4>带标号的 break（跳出指定循环）</h4>
<blockquote>
<p>如果出现循环嵌套，默认操作内层循环，如需改动，可以使用&lt;span style="color:red"&gt;标号&lt;/span&gt;控制</p>
</blockquote>
<pre><code>lo:
for (int i = 1; i &lt;= 5; i++) {
    for (int j = 1; j &lt;= 5; j++) {
        if (j == 2) {
            break lo;
        }
    }
}
</code></pre>
<h3>9. Random 生成随机数</h3>
<pre><code>import java.util.Random;

public class Test {
    public static void main(String[] args) {
        Random r = new Random();
        int num1 = r.nextInt(10);
    }
}
</code></pre>
<table>
<thead>
<tr>
<th>步骤</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>① 导包</td>
<td>告诉程序去 JDK 的哪个包中找随机数生成器（<code>import java.util.Random;</code>）</td>
</tr>
<tr>
<td>② 抄代码</td>
<td>代表得到随机数生成器对象（东西）（<code>Random r = new Random();</code>）</td>
</tr>
<tr>
<td>③ 抄代码</td>
<td>生成一个随机数，指定随机范围（<code>int num1 = r.nextInt(10);</code>）</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:red"&gt;注意：<code>r.nextInt(n);</code> 的范围是 0 ~ n-1，&lt;span style="color:red"&gt;不包含 n&lt;/span&gt;&lt;/span&gt;</p>
</blockquote>
<h4>思考：产生一个 1~100 的随机数</h4>
<blockquote>
<p><code>r.nextInt(?)</code> 传入什么参数？
&lt;span style="color:green"&gt;<code>int num = r.nextInt(100) + 1;</code>&lt;/span&gt;（<code>nextInt(100)</code> 得到 0~99，加 1 得到 1~100）</p>
</blockquote>
<h4>案例：猜数字小游戏</h4>
<blockquote>
<ol>
<li>产生一个 1~100 之间的随机数作为中奖数字</li>
<li>随后键盘录入用户猜的数字进行比对</li>
<li>没有猜中的话给出提示，直到用户猜对为止，程序结束</li>
</ol>
</blockquote>
<pre><code>import java.util.Random;
import java.util.Scanner;

public class Test {
    public static void main(String[] args) {
        Random r = new Random();
        int lucky = r.nextInt(100) + 1;

        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.print("请输入：");
            int guess = sc.nextInt();
            if (guess &gt; lucky) {
                System.out.println("猜大了");
            } else if (guess &lt; lucky) {
                System.out.println("猜小了");
            } else {
                System.out.println("恭喜，猜中了！");
                break;
            }
        }
    }
}
</code></pre>
<blockquote>
<p><strong>运行示例</strong>：</p>
<pre><code>请输入：50
猜大了
请输入：25
猜小了
请输入：15
猜小了
请输入：10
猜小了
请输入：12
恭喜，猜中了！
</code></pre>
</blockquote>
<h2>第七章 数组</h2>
<blockquote>
<p>&lt;span style="color:blue"&gt;数组指一种容器，可以存储同种类型的多个值&lt;/span&gt;</p>
</blockquote>
<h3>1. 数组介绍</h3>
<h4>为什么使用数组</h4>
<blockquote>
<p>销售部门共有 10 个员工，需要录入 10 个人的销售业绩，然后&lt;span style="color:orange"&gt;找出最大值&lt;/span&gt;、&lt;span style="color:orange"&gt;找出最小值&lt;/span&gt;、&lt;span style="color:orange"&gt;计算销售总额&lt;/span&gt;。如果使用普通变量，需要定义 10 个 int 变量存储，繁琐且不利于批量操作。</p>
</blockquote>
<h4>什么是数组</h4>
<blockquote>
<ul>
<li>&lt;span style="color:blue"&gt;数组是一种&lt;strong&gt;容器&lt;/strong&gt;，可以用来存储同种数据类型的&lt;strong&gt;多个值&lt;/strong&gt;&lt;/span&gt;</li>
<li>例如：<code>int a = 10; int b = 20; int c = 30;</code> 三个变量分别存储 → <code>int[] arr = {10, 20, 30, ..., 90};</code> 一个数组存储</li>
</ul>
</blockquote>
<blockquote>
<ol>
<li>&lt;span style="color:orange"&gt;数组是什么？能干什么？&lt;/span&gt;
答：一种容器，可以存储同种数据类型的多个值</li>
<li>&lt;span style="color:orange"&gt;什么时候使用数组？&lt;/span&gt;
答：如果要操作的数据有多个，并且还属于一个整体</li>
</ol>
</blockquote>
<h3>2. 数组定义和静态初始化</h3>
<h4>数组定义</h4>
<table>
<thead>
<tr>
<th>格式</th>
<th>语法</th>
<th>范例</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;格式一&lt;/span&gt;</td>
<td><code>数据类型[] 数组名;</code></td>
<td><code>int[] array1;</code></td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;格式二&lt;/span&gt;</td>
<td><code>数据类型 数组名[];</code></td>
<td><code>int array2[];</code></td>
</tr>
</tbody>
</table>
<blockquote>
<ol>
<li>&lt;span style="color:orange"&gt;数组的定义格式为？&lt;/span&gt;  <code>数据类型[] 数组名;</code></li>
<li>&lt;span style="color:orange"&gt;数组的静态初始化格式为？&lt;/span&gt;
<ul>
<li>完整：<code>数据类型[] 数组名 = new 数据类型[]{元素1, 元素2, 元素3...};</code></li>
<li>简化：<code>数据类型[] 数组名 = {元素1, 元素2, 元素3...};</code></li>
</ul>
</li>
<li>&lt;span style="color:orange"&gt;打印数组名会看到什么？&lt;/span&gt;  十六进制内存地址</li>
</ol>
</blockquote>
<h4>数组静态初始化</h4>
<blockquote>
<p>初始化：&lt;span style="color:blue"&gt;就是在内存中，为数组容器开辟空间，并将数据存入容器中的过程&lt;/span&gt;</p>
</blockquote>
<pre><code>// 完整格式
数据类型[] 数组名 = new 数据类型[]{ 元素1, 元素2, 元素3... };
int[] array = new int[]{ 11, 22, 33 };
double[] array2 = new double[]{ 11.1, 22.2, 33.3 };

// 简化格式（开发中推荐使用）
数据类型[] 数组名 = { 元素1, 元素2, 元素3... };
int[] array = { 11, 22, 33 };
double[] array2 = { 11.1, 22.2, 33.3 };
</code></pre>
<blockquote>
<p>&lt;span style="color:red"&gt;注意事项：打印数组名，会看到数组在内存中的十六进制地址值&lt;/span&gt;
例如：<code>[I@10f87f48</code>、<code>[D@b4c966a</code></p>
</blockquote>
<pre><code>flowchart LR
    subgraph M[内存]
        direction TB
        V1[11]
        V2[22]
        V3[33]
    end
    Arr[数组名] --&gt; M
</code></pre>
<h3>3. 数组元素访问</h3>
<pre><code>格式：数组名[索引];
</code></pre>
<blockquote>
<ul>
<li>&lt;span style="color:red"&gt;索引：就是数组容器中空间的编号&lt;/span&gt;，&lt;span style="color:blue"&gt;编号从 0 开始，逐个 +1 增长&lt;/span&gt;</li>
</ul>
</blockquote>
<pre><code>flowchart LR
    subgraph array[array 数组]
        direction TB
        E0[索引 0] --&gt; E1[索引 1] --&gt; E2[索引 2] --&gt; E3[索引 3]
    end
    Caller[array 小组，2 号滚出列] --&gt; E2
</code></pre>
<blockquote>
<ol>
<li>&lt;span style="color:orange"&gt;数组元素访问格式为？&lt;/span&gt;  <code>数组名[索引];</code></li>
<li>&lt;span style="color:orange"&gt;索引（角标、下标）&lt;/span&gt;：指数组中每一个元素的编号，&lt;span style="color:red"&gt;编号从 0 开始，逐个 +1 增长&lt;/span&gt;</li>
<li>&lt;span style="color:orange"&gt;数组名.length：动态获取数组的长度（元素的个数）&lt;/span&gt;</li>
</ol>
</blockquote>
<h3>4. 数组遍历操作</h3>
<blockquote>
<ul>
<li>&lt;span style="color:red"&gt;数组遍历：依次访问数组中的每一个元素&lt;/span&gt;</li>
</ul>
</blockquote>
<h4>数组遍历 - 求偶数和</h4>
<blockquote>
<p><strong>需求</strong>：已知数组元素为 <code>{11, 22, 33, 44, 55}</code>，请将数组中偶数元素取出并求和，最后打印求和结果</p>
</blockquote>
<p><strong>分析</strong>：</p>
<ol>
<li>静态初始化数组</li>
<li>定义求和变量</li>
<li>遍历数组取出每一个元素</li>
<li>判断当前元素是否是偶数，是的话跟求和变量做累加操作</li>
<li>打印求和变量</li>
</ol>
<pre><code>int[] arr = {11, 22, 33, 44, 55};
int sum = 0;
for (int i = 0; i &lt; arr.length; i++) {
    if (arr[i] % 2 == 0) {
        sum += arr[i];
    }
}
System.out.println("数组中所有偶数的和为：" + sum);  // 66
</code></pre>
<h4>数组遍历 - 求最大值</h4>
<blockquote>
<p><strong>需求</strong>：已知数组元素为 <code>{5, 44, 33, 55, 22}</code>，请找出数组中最大值并打印在控制台</p>
</blockquote>
<table>
<thead>
<tr>
<th>选手</th>
<th>战力</th>
<th>索引</th>
</tr>
</thead>
<tbody>
<tr>
<td>选手 0</td>
<td>5</td>
<td>0</td>
</tr>
<tr>
<td>选手 1</td>
<td>44</td>
<td>1</td>
</tr>
<tr>
<td>选手 2</td>
<td>33</td>
<td>2</td>
</tr>
<tr>
<td>选手 3</td>
<td>55</td>
<td>3</td>
</tr>
<tr>
<td>选手 4</td>
<td>22</td>
<td>4</td>
</tr>
</tbody>
</table>
<p><strong>分析</strong>：</p>
<ol>
<li>定义 <code>max</code> 变量来记录擂台上不断变化的数据</li>
<li>数组中 0 号选手先上台，因此 <code>int max = arr[0];</code></li>
<li>遍历数组取出每一个元素，索引从 1 开始</li>
<li>逐个进行比较，找到更大的，<code>max</code> 变量记录更大的数据</li>
<li>遍历后打印 <code>max</code> 变量所记录的值</li>
</ol>
<pre><code>int[] arr = {5, 44, 33, 55, 22};
int max = arr[0];
for (int i = 1; i &lt; arr.length; i++) {
    if (arr[i] &gt; max) {
        max = arr[i];
    }
}
System.out.println("数组最大值为：" + max);  // 55
</code></pre>
<h4>数组元素反转</h4>
<blockquote>
<p><strong>需求</strong>：已知一个数组 <code>arr = {11, 22, 33, 44, 55};</code>，用程序实现把数组中的元素值交换，交换后的数组 <code>arr = {55, 44, 33, 22, 11};</code>，并在控制台输出交换后的数组元素。</p>
</blockquote>
<h5>两变量交换（借助第三变量）</h5>
<blockquote>
<p>需求：实现两个变量的数据交换</p>
</blockquote>
<pre><code>int a = 10;
int b = 20;

// 定义第三个变量，完成交换
int c = a;
a = b;
b = c;
</code></pre>
<blockquote>
<p>思路：使用一个空杯（第三个变量）作为中转，先把 a 倒到 c，再把 b 倒到 a，最后把 c 倒到 b，完成交换</p>
</blockquote>
<h5>反转过程</h5>
<table>
<thead>
<tr>
<th>步骤</th>
<th>操作</th>
<th>数组状态</th>
<th>指针位置</th>
</tr>
</thead>
<tbody>
<tr>
<td>初始</td>
<td><code>int start = 0, end = arr.length - 1;</code></td>
<td><code>11 22 33 44 55 66</code></td>
<td>start=0, end=5</td>
</tr>
<tr>
<td>第一次</td>
<td>交换 <code>arr[0]</code> 和 <code>arr[5]</code></td>
<td><code>66 22 33 44 55 11</code></td>
<td>start=1, end=4</td>
</tr>
<tr>
<td>第二次</td>
<td>交换 <code>arr[1]</code> 和 <code>arr[4]</code></td>
<td><code>66 55 33 44 22 11</code></td>
<td>start=2, end=3</td>
</tr>
<tr>
<td>第三次</td>
<td>交换 <code>arr[2]</code> 和 <code>arr[3]</code></td>
<td><code>66 55 44 33 22 11</code></td>
<td>start=3, end=2（停止）</td>
</tr>
</tbody>
</table>
<pre><code>flowchart LR
    subgraph 0[交换前]
        A0[11] --&gt; A1[22] --&gt; A2[33] --&gt; A3[44] --&gt; A4[55] --&gt; A5[66]
    end
    subgraph 1[第三次后]
        B0[66] --&gt; B1[55] --&gt; B2[44] --&gt; B3[33] --&gt; B4[22] --&gt; B5[11]
    end
    0 --&gt; 1
</code></pre>
<pre><code>// 交换条件：start &lt; end
// 交换：arr[start] 和 arr[end]
// 交换后：start++, end--

int[] arr = {11, 22, 33, 44, 55, 66};

for (int start = 0, end = arr.length - 1; start &lt; end; start++, end--) {
    int temp = arr[start];
    arr[start] = arr[end];
    arr[end] = temp;
}

for (int i = 0; i &lt; arr.length; i++) {
    System.out.print(arr[i] + " ");
}
// 66 55 44 33 22 11
</code></pre>
<h3>5. 数组动态初始化</h3>
<blockquote>
<p>动态初始化：&lt;span style="color:red"&gt;初始化时只指定数组长度，由系统为数组分配初始值&lt;/span&gt;</p>
</blockquote>
<pre><code>格式：数据类型[] 数组名 = new 数据类型[数组长度];
范例：int[] arr = new int[3];
</code></pre>
<table>
<thead>
<tr>
<th>数据类型</th>
<th>明细</th>
<th>默认值</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;基本数据类型&lt;/span&gt;</td>
<td>整数</td>
<td><code>0</code></td>
</tr>
<tr>
<td></td>
<td>小数</td>
<td><code>0.0</code></td>
</tr>
<tr>
<td></td>
<td>字符</td>
<td><code>'\u0000'</code>（常见体现为空白字符）</td>
</tr>
<tr>
<td></td>
<td>布尔</td>
<td><code>false</code></td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;引用数据类型&lt;/span&gt;</td>
<td>类、接口、数组</td>
<td><code>null</code></td>
</tr>
</tbody>
</table>
<h4>动态初始化 vs 静态初始化</h4>
<table>
<thead>
<tr>
<th>维度</th>
<th>动态初始化</th>
<th>静态初始化</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;格式&lt;/span&gt;</td>
<td><code>数据类型[] 数组名 = new 数据类型[长度];</code></td>
<td><code>数据类型[] 数组名 = {元素1, 元素2, 元素3...};</code></td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;特点&lt;/span&gt;</td>
<td>手动指定长度，系统自动分配默认初始化值</td>
<td>手动指定元素，系统计算出数组长度</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;场景&lt;/span&gt;</td>
<td>不明确要操作元素的时候</td>
<td>明确具体要操作的元素</td>
</tr>
</tbody>
</table>
<h4>案例：评委打分</h4>
<blockquote>
<p><strong>需求</strong>：在编程竞赛中，有 6 个评委为参赛的选手打分，分数为 0-100 的整数分。选手的最后得分为：去掉一个最高分和一个最低分后的 4 个评委平均值</p>
</blockquote>
<h3>6. 数组内存图</h3>
<h4>Java 内存分配介绍</h4>
<table>
<thead>
<tr>
<th>区域</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;栈&lt;/span&gt;</td>
<td>方法运行时所进入的内存（局部变量）</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;堆&lt;/span&gt;</td>
<td><code>new</code> 出来的东西会在这块内存中开辟空间，并产生地址</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;方法区&lt;/span&gt;</td>
<td>字节码文件加载时进入的内存（包含本地方法栈、寄存器）</td>
</tr>
</tbody>
</table>
<h4>一个数组内存图</h4>
<blockquote>
<p>简化格式只是简化了代码书写，&lt;span style="color:red"&gt;真正运行期间还是按照完整格式运行的&lt;/span&gt;：<code>int[] arr = new int[]{11,22,33};</code></p>
</blockquote>
<pre><code>flowchart LR
    A["Test.class main"]
    B["main int[] arr → 10f87f48"]
    C["10f87f48 int[] length=3 0:44 1:55 2:66"]
    A --&gt; B --&gt; C
</code></pre>
<blockquote>
<p>输出结果：<code>44 55 66</code></p>
</blockquote>
<h4>两个数组指向相同内存图</h4>
<blockquote>
<p><strong>重要</strong>：&lt;span style="color:red"&gt;两个数组名指向同一个数组时，操作的是同一块内存空间&lt;/span&gt;</p>
</blockquote>
<pre><code>int[] array1 = {11, 22, 33};
int[] array2 = array1;     // array2 指向 array1 的地址
array2[0] = 100;            // 通过 array2 修改元素
// array1[0] 也是 100
</code></pre>
<h4>方法参数传递内存图</h4>
<table>
<thead>
<tr>
<th>数据类型</th>
<th>是否改变原值</th>
<th>原因</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;基本数据类型&lt;/span&gt;</td>
<td>不改变</td>
<td>传递的是数据值，修改形参不影响实参（除非用 <code>return</code> 把结果返回）</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;引用数据类型&lt;/span&gt;</td>
<td>改变</td>
<td>传递的是地址值，方法内通过地址操作堆内存数据</td>
</tr>
</tbody>
</table>
<pre><code>// 基本数据类型 - 形参改变不影响实参
public static void change(int number) {
    number = 200;
}
// 调用前：100，调用后：100

// 基本数据类型 - 用 return 返回
public static int change(int number) {
    number = 200;
    return number;
}
// 调用前：100，调用后：200

// 引用数据类型 - 通过形参影响实参
public static void change(int[] arr) {
    arr[0] = 66;
}
// 调用前：11，调用后：66
</code></pre>
<h3>7. 数组常见问题</h3>
<h4>索引越界异常</h4>
<blockquote>
<ul>
<li>&lt;span style="color:red"&gt;ArrayIndexOutOfBoundsException&lt;/span&gt;：当访问了数组中不存在的索引，就会引发索引越界异常</li>
</ul>
</blockquote>
<h4>空指针异常</h4>
<blockquote>
<ul>
<li>&lt;span style="color:blue"&gt;当引用数据类型变量被赋值为 <code>null</code> 之后，地址的指向被切断，还继续访问堆内存数据，就会引发空指针异常&lt;/span&gt;</li>
</ul>
</blockquote>
<pre><code>public static void main(String[] args) {
    int[] arr = new int[2];
    // 把 null 赋值给数组
    arr = null;
    // 输出元素
    System.out.println(arr[0]);   // NullPointerException
}
</code></pre>
<pre><code>flowchart LR
    M1["方法: main int[] arr = null"]
    H1["10f87f48 int[] length=2 0:0 1:0"]
    M1 -. × .- H1
</code></pre>
<h3>8. 二维数组</h3>
<blockquote>
<ul>
<li>&lt;span style="color:blue"&gt;二维数组是一种&lt;strong&gt;容器&lt;/strong&gt;，该容器用于存储一维数组&lt;/span&gt;</li>
</ul>
</blockquote>
<h4>使用场景</h4>
<blockquote>
<p>要操作的一维数组有多个，并且属于同一个整体，可以考虑使用二维数组</p>
</blockquote>
<pre><code>// 销售部门四个季度的销售业绩
int[] arr = { {22, 66, 44}, {77, 33, 88}, {25, 45, 65}, {11, 66, 99} };
</code></pre>
<h4>二维数组静态初始化</h4>
<table>
<thead>
<tr>
<th>格式</th>
<th>语法</th>
<th>范例</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;完整&lt;/span&gt;</td>
<td><code>数据类型[][] 数组名 = new 数据类型[][]{...};</code></td>
<td><code>int[][] arr = new int[][]{{11,22},{33,44}};</code></td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;简化&lt;/span&gt;</td>
<td><code>数据类型[][] 数组名 = {{...},{...}};</code></td>
<td><code>int[][] arr = {{11,22},{33,44}};</code></td>
</tr>
</tbody>
</table>
<h4>二维数组元素访问</h4>
<pre><code>格式：数组名[索引][索引];
范例：arr[1][0];
</code></pre>
<h4>案例：二维数组遍历</h4>
<blockquote>
<p><strong>需求</strong>：已知一个二维数组 <code>arr = {{11, 22, 33}, {33, 44, 55}};</code>，遍历该数组，取出所有元素并打印</p>
</blockquote>
<p><strong>分析</strong>：</p>
<ol>
<li>遍历二维数组，取出里面每一个一维数组</li>
<li>在遍历的过程中，对每一个一维数组继续完成遍历，获取内部存储的每一个元素</li>
</ol>
<pre><code>int[][] arr = {{11, 22, 33}, {33, 44, 55}};

for (int i = 0; i &lt; arr.length; i++) {
    // arr[i] 就是每一个一维数组
    for (int j = 0; j &lt; arr[i].length; j++) {
        System.out.print(arr[i][j] + " ");
    }
    System.out.println();
}
// 11 22 33
// 33 44 55
</code></pre>
<h2>第八章 面向对象基础</h2>
<blockquote>
<p>&lt;span style="color:blue"&gt;万物皆对象&lt;/span&gt;
面向对象是一种编程的指导思想
以什么形式 组织代码 / 以什么思路 解决问题</p>
</blockquote>
<h3>1. 面向对象介绍</h3>
<blockquote>
<p>面向对象：&lt;span style="color:red"&gt;并不是一个技术，而是一种编程的指导思想&lt;/span&gt;
以什么形式 组织代码 / 以什么思路 解决问题
因为生活中，我们解决问题时，就是采用这种指导思想去解决的
所以，我们写程序去解决问题时，如果也能采用这种指导思想，就会使编程变得非常简单，程序也便于人理解</p>
</blockquote>
<pre><code>flowchart LR
    A[面向对象] --&gt; B[一种编程的指导思想]
    A --&gt; C[以什么形式 组织代码]
    A --&gt; D[以什么思路 解决问题]
</code></pre>
<blockquote>
<p>&lt;span style="color:blue"&gt;万物皆对象&lt;/span&gt;
试卷、钱、手机、人、试柜 → 都是<strong>对象</strong>
老师、学生 → 也是<strong>对象</strong>
老师 t = new 老师(); 试卷 s = t.出题(); 学生 stu = new 学生(); stu.考试(s); t.批卷(s);</p>
</blockquote>
<pre><code>public class Test {
    public static void main(String[] args) {
        老师 t = new 老师();
        试卷 s = t.出题();
        学生 stu = new 学生();
        stu.考试(s);
        t.批卷(s);
    }
}
</code></pre>
<blockquote>
<p>面向对象，重点学什么？</p>
<ul>
<li>&lt;span style="color:red"&gt;学习自己如何设计对象&lt;/span&gt;</li>
<li>&lt;span style="color:red"&gt;学习已有的对象如何使用&lt;/span&gt;</li>
</ul>
</blockquote>
<blockquote>
<p><strong>学习目标</strong>：</p>
<ol>
<li>&lt;span style="color:red"&gt;自己设计对象使用&lt;/span&gt;</li>
<li>&lt;span style="color:red"&gt;掌握已有对象的使用&lt;/span&gt;</li>
</ol>
</blockquote>
<h3>2. 类和对象</h3>
<blockquote>
<ul>
<li>&lt;span style="color:blue"&gt;Java 中想要创建对象，必须先要有类的存在&lt;/span&gt;</li>
<li>&lt;span style="color:blue"&gt;类指的是一组相关属性和行为的集合，我们将其理解为是一张对象的设计图&lt;/span&gt;</li>
</ul>
</blockquote>
<blockquote>
<p><strong>类和对象的关系</strong>：</p>
<ul>
<li>&lt;span style="color:red"&gt;Java 中需要根据类，创建对象&lt;/span&gt;</li>
<li>&lt;span style="color:red"&gt;一个类，可以创建出多个对象&lt;/span&gt;</li>
</ul>
</blockquote>
<h4>类的组成</h4>
<blockquote>
<p>类由两部分组成：&lt;span style="color:red"&gt;属性&lt;/span&gt; + &lt;span style="color:red"&gt;行为&lt;/span&gt;</p>
</blockquote>
<table>
<thead>
<tr>
<th>组成</th>
<th>语法要点</th>
<th>范例</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;属性&lt;/span&gt;（成员变量）</td>
<td>跟之前定义变量的格式一样，&lt;span style="color:red"&gt;只不过位置需要放在方法的外面&lt;/span&gt;</td>
<td><code>String name = "张三";</code>&lt;br&gt;<code>int age = 23;</code></td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;行为&lt;/span&gt;（成员方法）</td>
<td>跟之前定义方法的格式一样，&lt;span style="color:red"&gt;只不过需要去掉 static 关键字&lt;/span&gt;</td>
<td><code>public void study() { System.out.println("学习Java"); }</code>&lt;br&gt;<code>public void eat() { System.out.println("吃饭"); }</code></td>
</tr>
</tbody>
</table>
<h4>创建对象、使用对象</h4>
<table>
<thead>
<tr>
<th>步骤</th>
<th>语法</th>
<th>范例</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;创建对象&lt;/span&gt;</td>
<td><code>类名 对象名 = new 类名();</code></td>
<td><code>Student stu = new Student();</code></td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;使用成员变量&lt;/span&gt;</td>
<td><code>对象名.变量名;</code></td>
<td><code>stu.name</code>&lt;br&gt;<code>stu.age = 24;</code></td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;使用成员方法&lt;/span&gt;</td>
<td><code>对象名.方法名(实际参数);</code></td>
<td><code>stu.study();</code>&lt;br&gt;<code>stu.eat();</code></td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>小结</strong>：</p>
<ul>
<li>&lt;span style="color:green"&gt;类的组成：属性、行为&lt;/span&gt;</li>
<li>&lt;span style="color:green"&gt;属性：成员变量，描述事物的名词&lt;/span&gt;</li>
<li>&lt;span style="color:green"&gt;行为：成员方法，描述事物的动词&lt;/span&gt;</li>
<li>&lt;span style="color:green"&gt;创建对象格式：<code>类名 对象名 = new 类名();</code>&lt;/span&gt;</li>
<li>&lt;span style="color:green"&gt;使用对象格式：<code>对象名.变量名; 对象名.方法名();</code>&lt;/span&gt;</li>
</ul>
</blockquote>
<h4>练习：类的定义与对象的创建和使用</h4>
<blockquote>
<p><strong>需求</strong>：定义一个手机类 <code>Phone</code>，包含属性（品牌 brand、颜色 color、价格 price）和行为（打电话 call、发短信 sendMessage），并编写一个手机测试类 <code>PhoneTest</code> 创建两个手机对象，赋值后打印并调用成员方法。</p>
</blockquote>
<pre><code>public class Phone {
    String brand;
    String color;
    int price;

    public void call(String name) {
        System.out.println("给 " + name + " 打电话");
    }

    public void sendMessage() {
        System.out.println("群发短信");
    }
}

public class PhoneTest {
    public static void main(String[] args) {
        Phone p1 = new Phone();
        p1.brand = "小米";
        p1.color = "白色";
        p1.price = 4999;
        System.out.println(p1.brand + " " + p1.color + " " + p1.price);

        Phone p2 = new Phone();
        p2.brand = "华为";
        p2.color = "黑色";
        p2.price = 6999;
        System.out.println(p2.brand + " " + p2.color + " " + p2.price);

        p1.call("张总");
        p2.sendMessage();
    }
}
</code></pre>
<h3>3. 类和对象内存图</h3>
<blockquote>
<p>一个对象的内存图 → 栈内存（局部变量 + 引用）→ 堆内存（new 出来的对象，成员变量 + 成员方法引用）→ 方法区（字节码）</p>
</blockquote>
<pre><code>flowchart LR
    subgraph 方法区
        M[TestStudent.class main]
    end
    subgraph 栈内存
        S1[main Student stu = 7ef20235]
    end
    subgraph 堆内存
        H1[7ef20235 String name: 张三 int age: 23 成员方法引用地址]
    end
    方法区 --&gt; 栈内存 --&gt; 堆内存
</code></pre>
<pre><code>flowchart LR
    subgraph 方法区
        M[Test.class main]
    end
    subgraph 栈内存
        S1[main Student stu1 = 7ef20235]
        S2[Student stu2 = 27d6c5e0]
    end
    subgraph 堆内存
        H1[7ef20235 name: 张三 age: 23]
        H2[27d6c5e0 name: 李四 age: 24]
    end
    M --&gt; S1 --&gt; H1
    S1 --&gt; H2
</code></pre>
<pre><code>flowchart LR
    subgraph 方法区
        M[Test.class main]
    end
    subgraph 栈内存
        S1[main Student stu1 = 7ef20235 Student stu2 = 7ef20235]
    end
    subgraph 堆内存
        H1[7ef20235 name: 钢门吹雪]
    end
    M --&gt; S1 --&gt; H1
</code></pre>
<h3>4. 成员变量和局部变量区别</h3>
<table>
<thead>
<tr>
<th>区别</th>
<th>&lt;span style="color:red"&gt;成员变量&lt;/span&gt;</th>
<th>&lt;span style="color:red"&gt;局部变量&lt;/span&gt;</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;类中位置&lt;/span&gt;</td>
<td>方法外</td>
<td>方法中</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;初始化值&lt;/span&gt;</td>
<td>有默认初始化值</td>
<td>没有，使用前需要完成赋值</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;内存位置&lt;/span&gt;</td>
<td>堆内存</td>
<td>栈内存</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;生命周期&lt;/span&gt;</td>
<td>随着对象的创建而存在，随着对象的消失而消失</td>
<td>随着方法的调用而存在，随着方法的运行结束而消失</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;作用域&lt;/span&gt;</td>
<td>在自己所属类的大括号中</td>
<td>在自己所归属的大括号中</td>
</tr>
</tbody>
</table>
<h3>5. this 关键字</h3>
<h4>this 可以解决的问题</h4>
<blockquote>
<p>当局部变量和成员变量出现了重名的情况，&lt;span style="color:red"&gt;Java 使用的是就近原则&lt;/span&gt;
但是我就非要用成员变量 → 用 <code>this.成员变量</code></p>
</blockquote>
<pre><code>public class Student {
    String name;
    int age;

    public void sayHello(String name) {
        System.out.println(this.name);   // 我就非要用成员变量
    }
}
</code></pre>
<h4>this 介绍</h4>
<blockquote>
<p>&lt;span style="color:red"&gt;this 代表当前类对象的引用（地址）&lt;/span&gt;</p>
</blockquote>
<pre><code>flowchart LR
    subgraph 栈内存
        S[main Student stu = 0011]
    end
    subgraph 堆内存
        H[0011 this → 0011 name: 钢门吹雪 age: 23]
    end
    S --&gt; H
</code></pre>
<h4>this 小结</h4>
<blockquote>
<ol>
<li>&lt;span style="color:red"&gt;当局部变量和成员变量重名的时候，Java 使用的是就近原则&lt;/span&gt;</li>
<li>&lt;span style="color:red"&gt;this 关键字的作用是：调用本类成员&lt;/span&gt;
<ul>
<li><code>this.本类成员变量;</code> —— 如果不涉及变量重复，<code>this.</code> 可以省略</li>
<li><code>this.本类成员方法();</code> —— 没有前提条件，<code>this.</code> 可以直接省略</li>
</ul>
</li>
<li>&lt;span style="color:red"&gt;this 代表什么呢？代表当前类对象的引用（地址）&lt;/span&gt;</li>
</ol>
</blockquote>
<h3>6. 构造方法</h3>
<blockquote>
<ul>
<li>&lt;span style="color:blue"&gt;构造器：初始化一个新构建的对象，构建、创建对象的时候，所调用的方法&lt;/span&gt;</li>
<li><strong>格式</strong>：
<ol>
<li>&lt;span style="color:red"&gt;方法名与类名相同，大小写也要一致&lt;/span&gt;</li>
<li>&lt;span style="color:red"&gt;没有返回值类型，连 void 都没有&lt;/span&gt;</li>
<li>&lt;span style="color:red"&gt;没有具体的返回值（不能由 return 带回结果数据）&lt;/span&gt;</li>
</ol>
</li>
<li><strong>执行时机</strong>：
<ol>
<li>&lt;span style="color:red"&gt;创建对象的时候调用，每创建一次对象，就会执行一次构造方法&lt;/span&gt;</li>
<li>&lt;span style="color:red"&gt;不能手动调用构造方法&lt;/span&gt;</li>
</ol>
</li>
</ul>
</blockquote>
<pre><code>public class Test {
    public static void main(String[] args) {
        Student stu = new Student();
    }
}
</code></pre>
<h4>构造方法作用</h4>
<blockquote>
<ul>
<li>本质作用：&lt;span style="color:red"&gt;创建对象&lt;/span&gt;</li>
<li>结合构造方法执行时机：&lt;span style="color:red"&gt;给对象中的属性（成员变量）进行初始化&lt;/span&gt;</li>
</ul>
</blockquote>
<h4>构造方法注意事项</h4>
<blockquote>
<ol>
<li><strong>构造方法的创建</strong>：
<ul>
<li>&lt;span style="color:red"&gt;如果没有定义构造方法，系统将给出一个默认的无参数构造方法&lt;/span&gt;</li>
<li>如果定义了构造方法，系统将不再提供默认的构造方法</li>
</ul>
</li>
<li><strong>构造方法的重载</strong>：&lt;span style="color:red"&gt;构造方法也是方法，允许重载关系出现&lt;/span&gt;</li>
<li><strong>推荐的使用方式</strong>：&lt;span style="color:red"&gt;无参数构造方法，和带参数构造方法，都自己手动给出&lt;/span&gt;</li>
</ol>
</blockquote>
<pre><code>class Student {
    int age;
    public Student() {}
    public Student(int age) {
        this.age = 18;
    }
}
</code></pre>
<h4>构造方法执行流程</h4>
<pre><code>flowchart LR
    subgraph 栈内存
        M[main Student stu = 0011 构造方法 Student 调用者 this→0011 String name = 钢门吹雪 int age = 23]
    end
    subgraph 堆内存
        H[0011 this → 0011 name: 钢门吹雪 age: 23]
    end
    M --&gt; H
</code></pre>
<pre><code>public class Student {
    String name;
    int age;
    public Student() {}
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void show() {
        System.out.println(name + "---" + age);
    }
}
</code></pre>
<h4>构造方法小结</h4>
<blockquote>
<ul>
<li><strong>构造方法的作用</strong>：1. 创建对象  2. 给成员变量初始化</li>
<li><strong>构造方法的格式</strong>：1. 方法名与类名相同，大小写也要一致  2. 没有返回值类型，连 void 都没有  3. 没有具体的返回值</li>
<li><strong>构造方法的注意事项</strong>：1. 如果一个类中没有写构造方法，系统会提供一个默认的无参数构造方法  2. 如果手动编写了构造方法，系统将不会提供默认的那个无参构造方法，建议无参构造、带参构造全部手动给出</li>
</ul>
</blockquote>
<h3>7. 封装</h3>
<blockquote>
<p><strong>面向对象三大特征</strong>：&lt;span style="color:red"&gt;封装&lt;/span&gt; / 继承 / 多态</p>
</blockquote>
<h4>封装思想</h4>
<blockquote>
<ul>
<li>&lt;span style="color:red"&gt;封装&lt;/span&gt;就是将&lt;span style="color:red"&gt;数据（属性）&lt;/span&gt;和&lt;span style="color:red"&gt;操作数据的方法&lt;/span&gt;捆绑在一起</li>
<li>组成一个整体，也就是类</li>
<li>同时，通过&lt;span style="color:red"&gt;访问修饰符&lt;/span&gt;来控制对类中属性和方法的访问权限，从而实现&lt;span style="color:red"&gt;信息隐藏&lt;/span&gt;</li>
<li>&lt;span style="color:green"&gt;更好的维护数据&lt;/span&gt;</li>
<li>&lt;span style="color:green"&gt;使用者无需关心内部实现，只要知道如何使用即可&lt;/span&gt;</li>
<li>核心：&lt;span style="color:red"&gt;合理隐藏，合理暴露&lt;/span&gt;</li>
</ul>
</blockquote>
<h4>案例：学生成绩</h4>
<blockquote>
<p><strong>需求</strong>：计算每一名学生的总成绩，展示学生的所有信息</p>
</blockquote>
<table>
<thead>
<tr>
<th>id</th>
<th>姓名</th>
<th>年龄</th>
<th>数学成绩</th>
<th>语文成绩</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>张三</td>
<td>23</td>
<td>90</td>
<td>87</td>
</tr>
<tr>
<td>2</td>
<td>李四</td>
<td>24</td>
<td>69</td>
<td>91</td>
</tr>
</tbody>
</table>
<pre><code>public class Student {
    int id;
    String name;
    int age;
    double mathScore;
    double chineseScore;

    public Student(int id, String name, int age, double mathScore, double chineseScore) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.mathScore = mathScore;
        this.chineseScore = chineseScore;
    }

    // 省略构造方法
    public double getTotalScore() {
        return mathScore + chineseScore;
    }

    public void showStudentInfos() {
        System.out.println("学号：" + id);
        System.out.println("姓名：" + name);
        System.out.println("年龄：" + age);
        System.out.println("数学成绩：" + mathScore);
        System.out.println("语文成绩：" + chineseScore);
    }
}
</code></pre>
<pre><code>public class Test {
    public static void main(String[] args) {
        Student stu1 = new Student(1, "张三", 23, 90, 87);
        stu1.showStudentInfos();

        Student stu2 = new Student(2, "李四", 24, 69, 91);
        stu2.showStudentInfos();
    }
}
</code></pre>
<h4>案例：电话簿</h4>
<blockquote>
<p>合理隐藏：<code>saveLog()</code> 方法设为 <code>private</code>；合理暴露：<code>call()</code> 方法设为 <code>public</code></p>
</blockquote>
<pre><code>public class Phone {
    public void call(String name) {
        System.out.println("给 " + name + " 打电话");
        saveLog();
    }

    private void saveLog() {
        System.out.println("通话记录");
        System.out.println("将联系人 - 时间 - 通话时长保存到本地");
    }
}
</code></pre>
<h4>权限修饰符</h4>
<table>
<thead>
<tr>
<th>修饰符</th>
<th>可修饰</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;private&lt;/span&gt;</td>
<td>成员变量、成员方法、构造方法</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;(default)&lt;/span&gt;</td>
<td>成员变量、成员方法、构造方法</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;protected&lt;/span&gt;</td>
<td>成员变量、成员方法、构造方法</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;public&lt;/span&gt;</td>
<td>成员变量、成员方法、构造方法</td>
</tr>
</tbody>
</table>
<h3>8. JavaBean</h3>
<blockquote>
<p><strong>标准 JavaBean</strong>：</p>
<ol>
<li>&lt;span style="color:red"&gt;这个类中的成员变量都要私有，并且要对外提供相应的 getXxx、setXxx 方法&lt;/span&gt;</li>
<li>&lt;span style="color:red"&gt;类中提供无参，带参构造方法&lt;/span&gt;</li>
<li><strong>实体类的应用场景</strong>：&lt;span style="color:green"&gt;实体类只负责数据存取，而对数据的处理交给其他类来完成，以实现数据和数据业务处理相分离&lt;/span&gt;</li>
</ol>
</blockquote>
<pre><code>public class Student {
    private String name;
    private double score;

    // getter setter 方法
    // 计算学生总成绩
    // 展示学生所有信息
    // ...
}

public class StudentOperator {
    private Student s;
    public StudentOperator(Student s) {
        this.s = s;
    }
}
</code></pre>
<h3>9. static 关键字</h3>
<blockquote>
<ul>
<li>&lt;span style="color:red"&gt;static 是静态的意思&lt;/span&gt;，可以修饰成员变量，也可以修饰成员方法</li>
</ul>
</blockquote>
<h4>static 修饰成员的特点</h4>
<blockquote>
<ol>
<li>&lt;span style="color:red"&gt;被其修饰的成员，被该类的所有对象所共享&lt;/span&gt;</li>
<li>&lt;span style="color:red"&gt;多了一种调用方式，可以通过类名调用&lt;/span&gt;</li>
<li>&lt;span style="color:red"&gt;随着类的加载而加载，优先于对象存在&lt;/span&gt;</li>
</ol>
</blockquote>
<table>
<thead>
<tr>
<th>类型</th>
<th>用途</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;static 成员变量&lt;/span&gt;</td>
<td>共享数据</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;static 成员方法&lt;/span&gt;</td>
<td>常用于工具类</td>
</tr>
</tbody>
</table>
<h4>static 内存图</h4>
<pre><code>flowchart LR
    subgraph 栈内存
        M[main User u1, u2]
    end
    subgraph 堆内存
        H1[u1: name=张三 age=23]
        H2[u2: name=李四 age=24]
        SH[User 静态成员变量区 int onLineNumber = 2]
    end
    subgraph 方法区
        Z[StaticTest1.class main]
    end
    M --&gt; H1
    M --&gt; H2
    H1 --&gt; SH
    H2 --&gt; SH
    Z --&gt; M
</code></pre>
<h4>注意事项</h4>
<blockquote>
<ul>
<li>&lt;span style="color:red"&gt;static 方法中，只能访问静态成员（直接访问）&lt;/span&gt;</li>
<li>&lt;span style="color:red"&gt;static 中不允许使用 this 关键字&lt;/span&gt;</li>
</ul>
</blockquote>
<h4>案例：数组工具类</h4>
<blockquote>
<p><strong>需求</strong>：编写一个类 <code>ArrayTools</code>，内部编写三个方法：</p>
<ol>
<li>从数组中找最大值</li>
<li>从数组中找最小值</li>
<li>打印出数组中所有的元素</li>
</ol>
</blockquote>
<pre><code>public class ArrayTools {
    public static int getMax(int[] arr) {
        int max = arr[0];
        for (int i = 1; i &lt; arr.length; i++) {
            if (arr[i] &gt; max) max = arr[i];
        }
        return max;
    }

    public static int getMin(int[] arr) {
        int min = arr[0];
        for (int i = 1; i &lt; arr.length; i++) {
            if (arr[i] &lt; min) min = arr[i];
        }
        return min;
    }

    public static void printArray(int[] arr) {
        for (int i = 0; i &lt; arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }
}
</code></pre>
<h4>重新认识 main 方法</h4>
<pre><code>public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("HelloWorld");
    }
}
</code></pre>
<table>
<thead>
<tr>
<th>关键字</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;public&lt;/span&gt;</td>
<td>被 JVM 调用，访问权限足够大</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;static&lt;/span&gt;</td>
<td>被 JVM 调用，不用创建对象&lt;br&gt;因为 main 方法是静态的，所以测试类中其他方法也需要是静态的</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;void&lt;/span&gt;</td>
<td>被 JVM 调用，不需要给 JVM 返回值</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;main&lt;/span&gt;</td>
<td>一个通用的名称，虽然不是关键字，但是被 JVM 识别</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;String[] args&lt;/span&gt;</td>
<td>以前用于接收键盘录入数据的，现在没用</td>
</tr>
</tbody>
</table>
<h2>第九章 面向对象高级</h2>
<h3>1. 继承</h3>
<h4>继承介绍</h4>
<blockquote>
<p>&lt;span style="color:blue"&gt;继承是面向对象三大特征之一，可以让类跟类之间产生父子关系&lt;/span&gt;
子类可以&lt;span style="color:orange"&gt;直接使用&lt;/span&gt;父类中&lt;span style="color:orange"&gt;非私有的成员&lt;/span&gt;</p>
</blockquote>
<h4>继承的好处与弊端</h4>
<blockquote>
<p>继承的好处：</p>
<ul>
<li>&lt;span style="color:blue"&gt;提高代码的&lt;strong&gt;复用性&lt;/strong&gt;&lt;/span&gt;</li>
<li>&lt;span style="color:blue"&gt;提高了代码的&lt;strong&gt;维护性&lt;/strong&gt;&lt;/span&gt;</li>
<li>&lt;span style="color:blue"&gt;让类与类之间产生了关系，是&lt;strong&gt;多态的前提&lt;/strong&gt;&lt;/span&gt;</li>
</ul>
<p>继承的弊端：</p>
<ul>
<li>&lt;span style="color:red"&gt;继承让类与类之间产生了关系，&lt;strong&gt;耦合性增强&lt;/strong&gt;&lt;/span&gt;</li>
<li>&lt;span style="color:red"&gt;开发原则：&lt;strong&gt;高内聚，低耦合&lt;/strong&gt;&lt;/span&gt;</li>
</ul>
</blockquote>
<h4>继承的语法格式</h4>
<blockquote>
<p>继承用关键字 <code>extends</code></p>
</blockquote>
<pre><code>public class 子类 extends 父类 { }
</code></pre>
<h4>继承后成员访问特点</h4>
<table>
<thead>
<tr>
<th>成员</th>
<th>访问特点</th>
</tr>
</thead>
<tbody>
<tr>
<td>成员变量</td>
<td>&lt;span style="color:orange"&gt;就近原则：先在子类局部范围找，再到子类成员范围找，最后到父类成员范围找&lt;/span&gt;</td>
</tr>
<tr>
<td>成员方法</td>
<td>&lt;span style="color:orange"&gt;就近原则&lt;/span&gt;</td>
</tr>
<tr>
<td>构造方法</td>
<td>&lt;span style="color:orange"&gt;子类的所有构造方法默认都会访问父类的&lt;strong&gt;无参构造方法&lt;/strong&gt;&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<h4>继承中变量访问</h4>
<blockquote>
<ol>
<li>&lt;span style="color:orange"&gt;子类局部范围有就使用子类的&lt;/span&gt;</li>
<li>&lt;span style="color:orange"&gt;子类成员范围有就使用子类的&lt;/span&gt;</li>
<li>&lt;span style="color:orange"&gt;父类成员范围有就使用父类的&lt;/span&gt;</li>
<li>&lt;span style="color:orange"&gt;如果都没有就报错（不用考虑父类的父类）&lt;/span&gt;</li>
</ol>
</blockquote>
<pre><code>class Fu {
    String name = "Fu";
}

class Zi extends Fu {
    String name = "Zi";
    
    public void show() {
        String name = "局部";
        System.out.println(name);       // 局部
        System.out.println(this.name);  // Zi
        System.out.println(super.name); // Fu
    }
}
</code></pre>
<h4>super 关键字</h4>
<blockquote>
<p><code>super</code> 代表的是&lt;span style="color:red"&gt;父类对象的引用&lt;/span&gt;
<code>this</code> 代表的是&lt;span style="color:red"&gt;当前对象的引用&lt;/span&gt;</p>
</blockquote>
<table>
<thead>
<tr>
<th>关键字</th>
<th>访问成员变量</th>
<th>访问成员方法</th>
<th>访问构造方法</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>this.</code></td>
<td>本类的成员变量</td>
<td>本类的成员方法</td>
<td><code>this(...)</code> 访问本类构造</td>
</tr>
<tr>
<td><code>super.</code></td>
<td>父类的成员变量</td>
<td>父类的成员方法</td>
<td><code>super(...)</code> 访问父类构造</td>
</tr>
</tbody>
</table>
<h4>继承中构造方法访问</h4>
<blockquote>
<ol>
<li>&lt;span style="color:orange"&gt;子类所有构造方法的第一行，默认都有一个 <code>super()</code> 调用父类无参构造&lt;/span&gt;</li>
<li>&lt;span style="orange"&gt;如果父类&lt;strong&gt;没有&lt;/strong&gt;无参构造，子类构造方法中必须显式调用父类的有参构造 <code>super(参数)</code>&lt;/span&gt;</li>
<li>&lt;span style="orange"&gt;子类构造方法中，<code>this()</code> 和 <code>super()</code> &lt;strong&gt;不能同时存在&lt;/strong&gt;，因为它们都必须放在第一行&lt;/span&gt;</li>
<li>&lt;span style="orange"&gt;子类构造方法中，&lt;strong&gt;默认调用父类无参构造&lt;/strong&gt;是 Java 帮我们加上的&lt;/span&gt;</li>
</ol>
</blockquote>
<pre><code>flowchart TB
    A[创建子类对象] --&gt; B[先执行父类构造]
    B --&gt; C[再执行子类构造]
</code></pre>
<h4>继承的特点</h4>
<blockquote>
<ol>
<li>&lt;span style="color:red"&gt;Java 只支持&lt;strong&gt;单继承&lt;/strong&gt;，不支持多继承&lt;/span&gt;（一个子类只能有一个父类）</li>
<li>&lt;span style="color:blue"&gt;Java 支持&lt;strong&gt;多层继承&lt;/strong&gt;（父类可以有父类）&lt;/span&gt;</li>
<li>&lt;span style="color:blue"&gt;一个父类可以有&lt;strong&gt;多个子类&lt;/strong&gt;&lt;/span&gt;</li>
<li>&lt;span style="color:orange"&gt;顶层父类是 <code>Object</code>，所有类都直接或间接继承 Object&lt;/span&gt;</li>
</ol>
</blockquote>
<pre><code>classDiagram
    A &lt;|-- B
    B &lt;|-- C
    A : Object
    B : 父类
    C : 子类
</code></pre>
<h4>方法重写</h4>
<blockquote>
<p>&lt;span style="color:blue"&gt;方法重写：在子类中，出现和父类&lt;strong&gt;一模一样&lt;/strong&gt;的方法声明（方法名、参数列表、返回值类型都相同）&lt;/span&gt;
&lt;span style="color:red"&gt;方法重写的意义：子类可以覆盖父类的实现，提供自己的逻辑&lt;/span&gt;</p>
</blockquote>
<blockquote>
<p>方法重写的注意事项：</p>
<ul>
<li>&lt;span style="color:orange"&gt;私有方法不能被重写（父类私有成员子类不能继承）&lt;/span&gt;</li>
<li>&lt;span style="orange"&gt;子类重写父类方法时，访问权限不能更低（&lt;strong&gt;public &gt; 默认 &gt; private&lt;/strong&gt;）&lt;/span&gt;</li>
<li>&lt;span style="orange"&gt;子类重写父类方法时，建议加上 <code>@Override</code> 注解来校验&lt;/span&gt;</li>
</ul>
</blockquote>
<pre><code>class Fu {
    public void show() {
        System.out.println("Fu show");
    }
}

class Zi extends Fu {
    @Override
    public void show() {
        System.out.println("Zi show");
    }
}
</code></pre>
<h4>继承的练习</h4>
<blockquote>
<p>需求：定义老师类和学生类，找出他们的共同特征，抽到父类中
共同特征：姓名、年龄、吃饭
特有功能：老师 - 讲课；学生 - 学习</p>
</blockquote>
<pre><code>classDiagram
    class Person {
        +String name
        +int age
        +eat()
    }
    class Teacher {
        +teach()
    }
    class Student {
        +study()
    }
    Person &lt;|-- Teacher
    Person &lt;|-- Student
</code></pre>
<h3>2. Object 类</h3>
<h4>Object 类介绍</h4>
<blockquote>
<p>&lt;span style="color:blue"&gt;Object 是所有类的&lt;strong&gt;顶层父类&lt;/strong&gt;，所有类都直接或间接继承 Object&lt;/span&gt;
一个类如果没有显式继承其他类，默认继承 Object</p>
</blockquote>
<h4>常用方法</h4>
<table>
<thead>
<tr>
<th>方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>public String toString()</code></td>
<td>返回对象的字符串表示，默认打印的是&lt;strong&gt;地址值&lt;/strong&gt;</td>
</tr>
<tr>
<td><code>public boolean equals(Object obj)</code></td>
<td>比较两个对象是否相等，默认比较的是&lt;strong&gt;地址值&lt;/strong&gt;</td>
</tr>
<tr>
<td><code>public Class&lt;?&gt; getClass()</code></td>
<td>返回对象的字节码文件对象</td>
</tr>
<tr>
<td><code>protected Object clone()</code></td>
<td>克隆对象</td>
</tr>
</tbody>
</table>
<h4>toString 方法的重写</h4>
<blockquote>
<p>打印对象时，会自动调用 <code>toString()</code> 方法
默认 <code>toString()</code> 返回的是：<code>类名@十六进制地址值</code>，没有意义
所以需要&lt;span style="color:red"&gt;重写 <code>toString()</code>&lt;/span&gt;，返回对象的属性值</p>
</blockquote>
<pre><code>@Override
public String toString() {
    return "Student{name=" + name + ", age=" + age + "}";
}
</code></pre>
<h4>equals 方法的重写</h4>
<blockquote>
<p>默认 <code>equals()</code> 比较的是对象的&lt;strong&gt;地址值&lt;/strong&gt;
业务中通常需要比较对象的&lt;strong&gt;内容&lt;/strong&gt;是否相同，需要重写 <code>equals()</code></p>
</blockquote>
<pre><code>@Override
public boolean equals(Object o) {
    // 如果两个对象的地址相同，代表是同一块内存控件，里面的内容肯定相同
    if (this == o) return true;
    
    // 代码如果可以执行到这里，说明stu1肯定不是null
    // stu1不是null，stu2为null，直接返回fallse
    // 比较零个对象的字节码，如果字节码不相同，代表类型不一致
    if (o == null || getClass() != o.getClass()) return false;
    
    // 向下转型，调用子类特有的属性
    Student student = (Student) o;
    
    // 比较两个对象的属性值
    return age == student.age &amp;&amp; Objects.equals(name, student.name);
}
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;快捷键：<code>Alt + Insert</code> → <code>toString()</code> / <code>equals()</code> 可自动生成&lt;/span&gt;</p>
</blockquote>
<h4>Objects 类</h4>
<blockquote>
<p>&lt;span style="color:blue"&gt;<code>Objects</code> 类&lt;strong&gt;与 <code>Object</code> 仍然是继承关系&lt;/strong&gt;，<code>Objects</code> 类是从 &lt;strong&gt;JDK 1.7&lt;/strong&gt; 开始之后才有的工具类&lt;/span&gt;
&lt;span style="color:orange"&gt;位于 <code>java.util.Objects</code>，是一个&lt;strong&gt;工具类&lt;/strong&gt;，提供一些&lt;strong&gt;静态方法&lt;/strong&gt;来操作对象&lt;/span&gt;</p>
</blockquote>
<h5>Objects 常见方法</h5>
<table>
<thead>
<tr>
<th>方法名</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;<code>public static boolean equals(Object a, Object b)</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;比较两个对象，底层会先进行&lt;strong&gt;非空判断&lt;/strong&gt;，从而可以&lt;strong&gt;避免空指针异常&lt;/strong&gt;，再进行 equals 比较</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;<code>public static boolean isNull(Object obj)</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;判断变量是否为 <code>null</code></td>
</tr>
</tbody>
</table>
<h5>Objects.equals 源码分析</h5>
<pre><code>public static boolean equals(Object a, Object b) {
    return (a == b) || (a != null &amp;&amp; a.equals(b));
}
</code></pre>
<blockquote>
<p>&lt;span style="color:red"&gt;关键点：先比较 <code>a == b</code>（地址），如果为 true 直接返回；否则判断 <code>a != null</code> 后再调用 <code>a.equals(b)</code>，&lt;strong&gt;避免空指针异常&lt;/strong&gt;&lt;/span&gt;</p>
</blockquote>
<h5>Objects 小结</h5>
<blockquote>
<p>&lt;span style="color:orange"&gt;Objects 中的 <code>equals</code> 方法&lt;strong&gt;仅仅是个工具类中的方法&lt;/strong&gt;，底层依赖于我们自己编写的 <code>equals</code> 方法&lt;/span&gt;
&lt;span style="color:blue"&gt;优势：&lt;strong&gt;加入了健壮性判断&lt;/strong&gt;（提前做非空判断，避免空指针异常）&lt;/span&gt;</p>
</blockquote>
<h3>3. final 关键字</h3>
<h4>final 介绍</h4>
<blockquote>
<p>&lt;span style="color:blue"&gt;final 是&lt;strong&gt;最终&lt;/strong&gt;的意思，可以修饰&lt;strong&gt;类、方法、变量&lt;/strong&gt;&lt;/span&gt;</p>
</blockquote>
<h4>final 修饰的特点</h4>
<table>
<thead>
<tr>
<th>修饰对象</th>
<th>效果</th>
</tr>
</thead>
<tbody>
<tr>
<td>修饰类</td>
<td>&lt;span style="color:red"&gt;该类&lt;strong&gt;不能被继承&lt;/strong&gt;（太监类）&lt;/span&gt;</td>
</tr>
<tr>
<td>修饰方法</td>
<td>&lt;span style="color:red"&gt;该方法&lt;strong&gt;不能被重写&lt;/strong&gt;&lt;/span&gt;</td>
</tr>
<tr>
<td>修饰变量</td>
<td>&lt;span style="color:red"&gt;该变量&lt;strong&gt;不能被重新赋值&lt;/strong&gt;，&lt;strong&gt;只能赋值一次&lt;/strong&gt;&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<pre><code>final class Fu { }              // 该类不能被继承
class Zi {
    public final void show() { } // 该方法不能被重写
}
</code></pre>
<h4>final 修饰变量的细节</h4>
<blockquote>
<ul>
<li>&lt;span style="color:orange"&gt;修饰基本数据类型：变量存储的&lt;strong&gt;数据值&lt;/strong&gt;不能发生改变&lt;/span&gt;</li>
<li>&lt;span style="color:orange"&gt;修饰引用数据类型：变量存储的&lt;strong&gt;地址值&lt;/strong&gt;不能发生改变，对象的内容可以改变&lt;/span&gt;</li>
<li>&lt;span style="color:orange"&gt;变量是&lt;strong&gt;成员变量&lt;/strong&gt;时，由于有默认值，用 final 修饰后必须&lt;strong&gt;手动赋值&lt;/strong&gt;（构造方法或直接赋值）&lt;/span&gt;</li>
<li>&lt;span style="color:orange"&gt;final 修饰的变量一般叫&lt;strong&gt;常量&lt;/strong&gt;，命名规范：&lt;strong&gt;所有字母都大写，多个单词用下划线分隔&lt;/strong&gt;&lt;/span&gt;</li>
</ul>
</blockquote>
<pre><code>final int AGE = 18;        // AGE 是常量
final int[] ARR = {1, 2, 3};
// ARR = new int[5];      // 错误，地址值不能改
ARR[0] = 100;              // 正确，内容可以改
</code></pre>
<h3>4. 抽象类</h3>
<h4>抽象类介绍</h4>
<blockquote>
<p>&lt;span style="color:blue"&gt;抽象类是一种&lt;strong&gt;特殊的父类&lt;/strong&gt;，里面可以定义&lt;strong&gt;没有方法体的抽象方法&lt;/strong&gt;&lt;/span&gt;</p>
</blockquote>
<h4>抽象方法</h4>
<blockquote>
<p>&lt;span style="color:red"&gt;抽象方法：将共性的行为（方法）抽取到父类之后，发现每个子类的执行逻辑不一样，此时在父类中不能给出具体的方法体，这种方法就是抽象方法&lt;/span&gt;</p>
</blockquote>
<pre><code>public abstract 返回值类型 方法名(参数列表);   // 抽象方法，没有方法体
</code></pre>
<h4>抽象类定义格式</h4>
<pre><code>public abstract class 类名 {                  // 抽象类
    public abstract 返回值类型 方法名(参数列表); // 抽象方法
}
</code></pre>
<h4>抽象类特点</h4>
<blockquote>
<ol>
<li>&lt;span style="color:orange"&gt;抽象类&lt;strong&gt;不能实例化&lt;/strong&gt;（不能创建对象）&lt;/span&gt;</li>
<li>&lt;span style="color:orange"&gt;抽象类中&lt;strong&gt;可以有构造方法&lt;/strong&gt;，供子类访问父类数据初始化&lt;/span&gt;</li>
<li>&lt;span style="color:orange"&gt;抽象类中&lt;strong&gt;可以有非抽象方法&lt;/strong&gt;，提高代码复用性&lt;/span&gt;</li>
<li>&lt;span style="color:orange"&gt;抽象类的&lt;strong&gt;子类必须重写所有抽象方法&lt;/strong&gt;，否则子类也必须是抽象类&lt;/span&gt;</li>
</ol>
</blockquote>
<pre><code>classDiagram
    class Animal {
        &lt;&lt;abstract&gt;&gt;
        +name: String
        +eat()*
        +drink() 抽象方法
    }
    class Dog {
        +eat()
        +drink()
    }
    Animal &lt;|-- Dog
</code></pre>
<h4>抽象类的意义</h4>
<blockquote>
<p>&lt;span style="color:blue"&gt;强制子类必须按照父类的格式定义方法&lt;/span&gt;
&lt;span style="color:orange"&gt;抽象类存在的意义是为了被子类继承，否则抽象类将毫无意义&lt;/span&gt;</p>
</blockquote>
<pre><code>public abstract class Animal {
    private String name;
    
    public Animal() { }
    public Animal(String name) { this.name = name; }
    
    public abstract void eat();  // 抽象方法
    
    public void sleep() {        // 普通方法
        System.out.println("睡觉");
    }
}

public class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("狗吃骨头");
    }
}
</code></pre>
<h4>抽象类练习</h4>
<blockquote>
<p>需求：定义一个抽象类 <code>Shape</code>，包含抽象方法 <code>getArea()</code> 和 <code>getPerimeter()</code>，让 <code>Circle</code> 和 <code>Rectangle</code> 继承</p>
</blockquote>
<pre><code>classDiagram
    class Shape {
        &lt;&lt;abstract&gt;&gt;
        +getArea()*
        +getPerimeter()*
    }
    class Circle {
        -radius: double
        +getArea()
        +getPerimeter()
    }
    class Rectangle {
        -length: double
        -width: double
        +getArea()
        +getPerimeter()
    }
    Shape &lt;|-- Circle
    Shape &lt;|-- Rectangle
</code></pre>
<h3>5. 接口</h3>
<h4>接口介绍</h4>
<blockquote>
<p>&lt;span style="color:blue"&gt;接口是一种&lt;strong&gt;规则&lt;/strong&gt;，是对行为的抽象&lt;/span&gt;
&lt;span style="color:orange"&gt;Java 中的接口更多体现的是一种&lt;strong&gt;规范&lt;/strong&gt;（约定），是一种引用数据类型&lt;/span&gt;</p>
</blockquote>
<h4>接口的定义格式</h4>
<pre><code>public interface 接口名 { 
    // 成员变量：只能是常量，默认修饰 public static final
    // 成员方法：只能是抽象方法，默认修饰 public abstract
    // JDK8：可以定义默认方法、静态方法
    // JDK9：可以定义私有方法
}
</code></pre>
<h4>接口的实现</h4>
<pre><code>public class 类名 implements 接口名 { 
    // 重写接口中所有抽象方法
}
</code></pre>
<h4>接口的成员特点</h4>
<table>
<thead>
<tr>
<th>成员</th>
<th>特点</th>
</tr>
</thead>
<tbody>
<tr>
<td>成员变量</td>
<td>&lt;span style="color:red"&gt;只能是&lt;strong&gt;常量&lt;/strong&gt;，默认修饰符 <code>public static final</code>&lt;/span&gt;</td>
</tr>
<tr>
<td>成员方法</td>
<td>&lt;span style="color:red"&gt;只能是&lt;strong&gt;抽象方法&lt;/strong&gt;，默认修饰符 <code>public abstract</code>&lt;/span&gt;（JDK8 前）</td>
</tr>
<tr>
<td>构造方法</td>
<td>&lt;span style="color:red"&gt;&lt;strong&gt;没有&lt;/strong&gt;构造方法&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<pre><code>public interface Animal {
    public static final int AGE = 10;   // 常量
    public abstract void eat();          // 抽象方法
}
</code></pre>
<h4>接口的关系</h4>
<table>
<thead>
<tr>
<th>关系</th>
<th>写法</th>
</tr>
</thead>
<tbody>
<tr>
<td>类与类</td>
<td>&lt;span style="color:orange"&gt;继承关系（<code>extends</code>），单继承或多层继承&lt;/span&gt;</td>
</tr>
<tr>
<td>类与接口</td>
<td>&lt;span style="color:orange"&gt;实现关系（<code>implements</code>），可以单实现，也可以&lt;strong&gt;多实现&lt;/strong&gt;&lt;/span&gt;</td>
</tr>
<tr>
<td>接口与接口</td>
<td>&lt;span style="color:orange"&gt;继承关系（<code>extends</code>），可以单继承，也可以&lt;strong&gt;多继承&lt;/strong&gt;&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<pre><code>// 类多实现接口
class A implements B, C { }

// 接口多继承接口
interface D extends B, C { }
</code></pre>
<h4>JDK8 接口新特性</h4>
<blockquote>
<p>&lt;span style="color:blue"&gt;JDK8 之后，接口中可以定义&lt;strong&gt;默认方法&lt;/strong&gt;和&lt;strong&gt;静态方法&lt;/strong&gt;，用来解决接口升级的问题&lt;/span&gt;</p>
</blockquote>
<table>
<thead>
<tr>
<th>方法类型</th>
<th>格式</th>
<th>特点</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;默认方法&lt;/span&gt;</td>
<td><code>public default 返回值类型 方法名(参数) { }</code></td>
<td>&lt;span style="color:orange"&gt;有方法体；实现类可以&lt;strong&gt;继承&lt;/strong&gt;，也可以&lt;strong&gt;重写&lt;/strong&gt;&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;静态方法&lt;/span&gt;</td>
<td><code>public static 返回值类型 方法名(参数) { }</code></td>
<td>&lt;span style="color:orange"&gt;有方法体；只能通过&lt;strong&gt;接口名调用&lt;/strong&gt;，不能通过实现类调用&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<pre><code>public interface MyInterface {
    default void show() {               // 默认方法
        System.out.println("默认方法");
    }
    
    static void staticMethod() {        // 静态方法
        System.out.println("静态方法");
    }
}

// 调用
MyInterface.staticMethod();
</code></pre>
<h4>JDK9 接口新特性</h4>
<blockquote>
<p>&lt;span style="color:blue"&gt;JDK9 之后，接口中可以定义&lt;strong&gt;私有方法&lt;/strong&gt;，用来抽取默认方法或静态方法中重复的代码&lt;/span&gt;</p>
</blockquote>
<pre><code>public interface MyInterface {
    default void show1() {
        methodCommon();
        System.out.println("show1");
    }
    
    default void show2() {
        methodCommon();
        System.out.println("show2");
    }
    
    private void methodCommon() {       // 私有方法，供默认方法使用
        System.out.println("公共代码");
    }
}
</code></pre>
<h3>6. 抽象类和接口的区别</h3>
<table>
<thead>
<tr>
<th>成员</th>
<th>抽象类</th>
<th>接口</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;成员变量&lt;/span&gt;</td>
<td>可以是变量，也可以是常量</td>
<td>&lt;span style="color:orange"&gt;只能是 <code>public static final</code> 常量&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;成员方法&lt;/span&gt;</td>
<td>可以是抽象方法，也可以是非抽象方法</td>
<td>&lt;span style="color:orange"&gt;JDK8 前：只能是 <code>public abstract</code> 抽象方法&lt;br&gt;JDK8 后：可以加 default / static&lt;br&gt;JDK9 后：可以加 private&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;构造方法&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;有&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;无&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;使用场景&lt;/span&gt;</td>
<td>抽取共性代码（is a）</td>
<td>定义规则（like a）</td>
</tr>
</tbody>
</table>
<pre><code>classDiagram
    class 抽象类 {
        +构造方法
        +普通方法
        +抽象方法
        +普通变量
        +常量
    }
    class 接口 {
        +常量
        +抽象方法
        +默认方法(JDK8)
        +静态方法(JDK8)
        +私有方法(JDK9)
    }
</code></pre>
<blockquote>
<p>&lt;span style="color:blue"&gt;设计原则：&lt;strong&gt;接口优先于抽象类&lt;/strong&gt;。当两者都能满足需求时，优先使用接口&lt;/span&gt;</p>
</blockquote>
<h3>7. 多态</h3>
<h4>多态介绍</h4>
<blockquote>
<p>&lt;span style="color:blue"&gt;多态是面向对象三大特征之一，是指&lt;strong&gt;同一行为&lt;/strong&gt;，通过&lt;strong&gt;不同的事物&lt;/strong&gt;，可以体现出&lt;strong&gt;不同的形态&lt;/span&gt;
多态是&lt;span style="color:orange"&gt;方法的多态&lt;/span&gt;，跟属性无关</p>
</blockquote>
<h4>多态的前提</h4>
<blockquote>
<ol>
<li>&lt;span style="color:orange"&gt;要有&lt;strong&gt;继承或实现关系&lt;/strong&gt;&lt;/span&gt;</li>
<li>&lt;span style="color:orange"&gt;要有&lt;strong&gt;方法重写&lt;/strong&gt;&lt;/span&gt;</li>
<li>&lt;span style="color:orange"&gt;要&lt;strong&gt;父类引用指向子类对象&lt;/strong&gt;：<code>Fu f = new Zi();</code>&lt;/span&gt;</li>
</ol>
</blockquote>
<pre><code>Animal a = new Dog();   // 父类引用指向子类对象（向上转型）
a.eat();                // 调用的是 Dog 重写后的方法
</code></pre>
<h4>多态中成员访问特点</h4>
<table>
<thead>
<tr>
<th>成员</th>
<th>访问特点</th>
</tr>
</thead>
<tbody>
<tr>
<td>成员变量</td>
<td>&lt;span style="color:red"&gt;&lt;strong&gt;编译看左边，运行也看左边&lt;/strong&gt;（访问的是父类的成员变量）&lt;/span&gt;</td>
</tr>
<tr>
<td>成员方法</td>
<td>&lt;span style="color:red"&gt;&lt;strong&gt;编译看左边，运行看右边&lt;/strong&gt;（访问的是子类重写后的方法）&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:orange"&gt;记忆口诀：成员方法看右边（运行），其他成员看左边（编译）&lt;/span&gt;</p>
</blockquote>
<h4>多态的好处和弊端</h4>
<blockquote>
<p>好处：</p>
<ul>
<li>&lt;span style="color:blue"&gt;提高了程序的&lt;strong&gt;扩展性&lt;/strong&gt;&lt;/span&gt;（定义方法时，使用父类作为参数，使用时传入子类对象）</li>
</ul>
<p>弊端：</p>
<ul>
<li>&lt;span style="color:red"&gt;多态情况下，&lt;strong&gt;不能访问子类特有的成员&lt;/strong&gt;&lt;/span&gt;</li>
</ul>
</blockquote>
<h4>多态中的转型</h4>
<blockquote>
<p>&lt;span style="color:blue"&gt;向上转型：从子到父（自动转换），父类引用指向子类对象&lt;/span&gt;
&lt;span style="color:orange"&gt;向下转型：从父到子（强制转换），子类引用 指向 父类对象（实际还是子类）&lt;/span&gt;</p>
</blockquote>
<pre><code>Animal a = new Dog();           // 向上转型（自动）
Dog d = (Dog) a;                // 向下转型（强制）
d.watchHouse();                 // 调用 Dog 特有方法
</code></pre>
<h4>instanceof 关键字</h4>
<blockquote>
<p>&lt;span style="color:blue"&gt;用于判断某个对象是否属于某个类型，避免类型转换异常 <code>ClassCastException</code>&lt;/span&gt;</p>
</blockquote>
<pre><code>if (a instanceof Dog) {
    Dog d = (Dog) a;
    d.watchHouse();
} else if (a instanceof Cat) {
    Cat c = (Cat) a;
    c.catchMouse();
}
</code></pre>
<h4>多态内存图</h4>
<pre><code>flowchart LR
    subgraph stack[栈]
        F[Fu 类型引用 a]
    end
    subgraph heap[堆]
        Z[Zi 类对象]
    end
    F --&gt; Z
    Z -.继承自 Fu.-&gt; FU[父类成员]
    Z -.重写.-&gt; FM[方法区]
</code></pre>
<h4>多态练习</h4>
<blockquote>
<p>需求：定义动物类，猫和狗继承动物类，都有 eat 方法；定义测试类，传入任意动物对象调用 eat 方法</p>
</blockquote>
<pre><code>classDiagram
    class Animal {
        &lt;&lt;abstract&gt;&gt;
        +eat()*
    }
    class Cat {
        +eat()
    }
    class Dog {
        +eat()
    }
    Animal &lt;|-- Cat
    Animal &lt;|-- Dog
</code></pre>
<h3>8. 代码块</h3>
<h4>代码块介绍</h4>
<blockquote>
<p>&lt;span style="color:blue"&gt;在 Java 中，使用 <code>{}</code> 括起来的代码被称为&lt;strong&gt;代码块&lt;/strong&gt;&lt;/span&gt;</p>
</blockquote>
<h4>代码块的分类</h4>
<table>
<thead>
<tr>
<th>类型</th>
<th>位置</th>
<th>特点</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;局部代码块&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;方法内部&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;限定变量的&lt;strong&gt;生命周期&lt;/strong&gt;，及早释放内存&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;构造代码块&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;类中方法外&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;每次创建对象都会执行，优先于构造方法执行&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;静态代码块&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;类中方法外，加 <code>static</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;随着类加载而执行，&lt;strong&gt;只执行一次&lt;/strong&gt;，优先于构造代码块执行&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<pre><code>public class Student {
    static {
        System.out.println("静态代码块");     // 加载类时执行
    }
    
    {
        System.out.println("构造代码块");     // 每次 new 都执行
    }
    
    public Student() {
        System.out.println("构造方法");
    }
}
</code></pre>
<h4>静态代码块的应用</h4>
<blockquote>
<p>&lt;span style="color:blue"&gt;用于&lt;strong&gt;类的初始化&lt;/strong&gt;，加载驱动、加载配置文件等&lt;/span&gt;</p>
</blockquote>
<pre><code>public class JDBCUtils {
    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
</code></pre>
<h3>9. 包</h3>
<h4>什么是包</h4>
<blockquote>
<p>&lt;span style="color:blue"&gt;包（package）本质就是&lt;strong&gt;文件夹&lt;/strong&gt;，用来管理类文件的&lt;/span&gt;
通过包，可以将&lt;strong&gt;不同功能的类&lt;/strong&gt;进行&lt;strong&gt;分类管理&lt;/strong&gt;，避免类重名的问题</p>
</blockquote>
<h4>包的定义格式</h4>
<pre><code>package 公司域名倒写.技术名称.包名;
// 范例
package com.itheima.pojo;

public class Student { }
</code></pre>
<blockquote>
<p>规范：&lt;span style="color:orange"&gt;包名建议&lt;strong&gt;全部英文小写&lt;/strong&gt;，且&lt;strong&gt;具备意义&lt;/strong&gt;，一般使用&lt;strong&gt;公司域名倒写&lt;/strong&gt; + &lt;strong&gt;项目名&lt;/strong&gt; + &lt;strong&gt;功能名&lt;/strong&gt;&lt;/span&gt;
注意：&lt;span style="color:red"&gt;package 语句必须是程序的&lt;strong&gt;第一行可执行代码&lt;/strong&gt;&lt;/span&gt;</p>
</blockquote>
<h4>导包</h4>
<blockquote>
<p>相同包下的类可以&lt;strong&gt;直接访问&lt;/strong&gt;，不同包下的类必须&lt;strong&gt;导包&lt;/strong&gt;，才可以使用！</p>
</blockquote>
<pre><code>import 包名.类名;     // 导包格式
import com.itheima.pojo.Student;  // 范例
</code></pre>
<blockquote>
<ol>
<li>&lt;span style="color:orange"&gt;如果导入的类在 <code>java.lang</code> 包下（核心包），就&lt;strong&gt;不需要&lt;/strong&gt;编写 import 导包代码&lt;/span&gt;</li>
<li>&lt;span style="color:orange"&gt;假如一个类中需要用到不同类，而这两个类的名称是一样的，那么默认&lt;strong&gt;只能导入一个类&lt;/strong&gt;，另一个类要&lt;strong&gt;带包名访问&lt;/strong&gt;&lt;/span&gt;</li>
</ol>
</blockquote>
<h4>导包练习</h4>
<pre><code>// 1. 手动导包
import java.util.Scanner;

// 2. 通配符 * 导入整个包的所有类（不建议）
import java.util.*;
</code></pre>
<h3>10. 内部类</h3>
<h4>内部类介绍</h4>
<blockquote>
<p>&lt;span style="color:blue"&gt;内部类就是在一个类的内部又定义了另一个类&lt;/span&gt;
例如：在一个类 A 的内部定义一个类 B，B 就被称为内部类，A 称为外部类</p>
</blockquote>
<h4>内部类的分类</h4>
<table>
<thead>
<tr>
<th>类型</th>
<th>位置</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;成员内部类&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;类中方法外&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;与成员变量、成员方法同级&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;静态内部类&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;类中方法外，加 <code>static</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;静态的成员内部类&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;局部内部类&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;方法内&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;定义在方法中的类&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;匿名内部类&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;方法内&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;没有名字的内部类，&lt;strong&gt;本质是一个对象&lt;/strong&gt;&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<h4>成员内部类</h4>
<pre><code>public class Outer {
    private int num = 10;
    
    public class Inner {
        public void show() {
            System.out.println(num);   // 内部类可以直接访问外部类成员
        }
    }
}

// 创建对象
Outer.Inner oi = new Outer().new Inner();
oi.show();
</code></pre>
<h4>成员内部类访问特点</h4>
<blockquote>
<ul>
<li>&lt;span style="color:orange"&gt;内部类可以&lt;strong&gt;直接访问&lt;/strong&gt;外部类的&lt;strong&gt;所有成员&lt;/strong&gt;（包括私有）&lt;/span&gt;</li>
<li>&lt;span style="color:orange"&gt;外部类要访问内部类的成员，&lt;strong&gt;必须创建对象&lt;/strong&gt;&lt;/span&gt;</li>
<li>&lt;span style="color:orange"&gt;内部类中如果定义了和外部类&lt;strong&gt;同名的成员变量&lt;/strong&gt;，默认访问内部类的&lt;/span&gt;</li>
<li>&lt;span style="color:orange"&gt;访问外部类同名成员：<code>外部类名.this.成员变量</code>&lt;/span&gt;</li>
</ul>
</blockquote>
<h4>静态内部类</h4>
<blockquote>
<p>&lt;span style="color:orange"&gt;静态内部类只能访问外部类的&lt;strong&gt;静态成员&lt;/strong&gt;&lt;/span&gt;</p>
</blockquote>
<pre><code>public class Outer {
    static class Inner {
        public void show() {
            System.out.println("静态内部类");
        }
    }
}

// 创建对象
Outer.Inner oi = new Outer.Inner();
oi.show();
</code></pre>
<h4>局部内部类</h4>
<blockquote>
<p>&lt;span style="color:orange"&gt;定义在方法中的类，外界无法直接使用，需要在&lt;strong&gt;方法内部创建对象&lt;/strong&gt;使用&lt;/span&gt;
可以直接访问外部类的成员，也可以访问方法内的&lt;strong&gt;局部变量&lt;/strong&gt;（final 修饰的常量，JDK8 后 final 可省略）</p>
</blockquote>
<pre><code>public class Outer {
    public void method() {
        int num = 10;
        class Inner {
            public void show() {
                System.out.println(num);   // 访问方法中的局部变量
            }
        }
        new Inner().show();
    }
}
</code></pre>
<h4>匿名内部类</h4>
<blockquote>
<p>&lt;span style="color:blue"&gt;匿名内部类是&lt;strong&gt;局部内部类&lt;/strong&gt;的一种特殊写法，&lt;strong&gt;本质是一个对象&lt;/strong&gt;&lt;/span&gt;</p>
</blockquote>
<pre><code>new 接口名/类名() {
    // 重写方法
}.方法名();
</code></pre>
<pre><code>// 案例
new Inter() {
    @Override
    public void show() {
        System.out.println("匿名内部类");
    }
}.show();
</code></pre>
<h4>匿名内部类的应用场景</h4>
<blockquote>
<p>&lt;span style="color:blue"&gt;当方法的参数是&lt;strong&gt;接口&lt;/strong&gt;或&lt;strong&gt;抽象类&lt;/strong&gt;时，可以直接传递&lt;strong&gt;匿名内部类&lt;/strong&gt;作为实参&lt;/span&gt;</p>
</blockquote>
<pre><code>public static void main(String[] args) {
    useInter(new Inter() {
        @Override
        public void show() {
            System.out.println("show");
        }
    });
}

public static void useInter(Inter i) {
    i.show();
}
</code></pre>
<h3>11. Lambda 表达式</h3>
<h4>Lambda 表达式介绍</h4>
<blockquote>
<p>&lt;span style="color:blue"&gt;Lambda 表达式是 JDK8 之后的新特性，可以用来&lt;strong&gt;简化匿名内部类&lt;/strong&gt;的书写&lt;/span&gt;
只能简化&lt;strong&gt;函数式接口&lt;/strong&gt;的匿名内部类</p>
</blockquote>
<blockquote>
<p>&lt;span style="color:orange"&gt;函数式接口：有且仅有一个抽象方法的接口（可以有其他非抽象方法），可以加 <code>@FunctionalInterface</code> 注解&lt;/span&gt;</p>
</blockquote>
<h4>Lambda 表达式的标准格式</h4>
<pre><code>() -&gt; { }
</code></pre>
<table>
<thead>
<tr>
<th>部分</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>()</code></td>
<td>形参列表，接口中抽象方法的形参列表</td>
</tr>
<tr>
<td><code>-&gt;</code></td>
<td>固定写法，代表指向动作</td>
</tr>
<tr>
<td><code>{}</code></td>
<td>方法体，接口中抽象方法的方法体</td>
</tr>
</tbody>
</table>
<h4>Lambda 表达式练习</h4>
<blockquote>
<ol>
<li>抽象方法无参无返回值</li>
<li>抽象方法有参无返回值</li>
<li>抽象方法有参有返回值</li>
</ol>
</blockquote>
<pre><code>// 案例 1：无参无返回值
useInter(() -&gt; System.out.println("Lambda"));

// 案例 2：有参无返回值
useInter((int num) -&gt; System.out.println(num));

// 案例 3：有参有返回值
useInter((int a, int b) -&gt; a + b);
</code></pre>
<h4>Lambda 表达式的省略规则</h4>
<blockquote>
<ol>
<li>&lt;span style="color:orange"&gt;参数类型可以省略&lt;/span&gt;，但&lt;strong&gt;多个参数不能只省略一个&lt;/strong&gt;</li>
<li>&lt;span style="color:orange"&gt;如果参数&lt;strong&gt;只有一个&lt;/strong&gt;，小括号可以省略&lt;/span&gt;</li>
<li>&lt;span style="color:orange"&gt;如果方法体&lt;strong&gt;只有一句话&lt;/strong&gt;，大括号可以省略，且如果有 <code>return</code> 也要省略&lt;/span&gt;</li>
</ol>
</blockquote>
<pre><code>// 完整写法
(int a, int b) -&gt; { return a + b; }

// 省略参数类型
(a, b) -&gt; { return a + b; }

// 省略大括号和 return
(a, b) -&gt; a + b;
</code></pre>
<h4>Lambda 表达式与匿名内部类的区别</h4>
<table>
<thead>
<tr>
<th>对比项</th>
<th>匿名内部类</th>
<th>Lambda 表达式</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;类型&lt;/span&gt;</td>
<td>编译后产生&lt;strong&gt;单独的字节码文件&lt;/strong&gt;（<code>Outer$1.class</code>）</td>
<td>&lt;span style="color:orange"&gt;编译后&lt;strong&gt;不产生单独的字节码文件&lt;/strong&gt;，对应的字节码会在运行时动态生成&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;使用范围&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;所有接口和抽象类都可以使用&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;只能简化&lt;strong&gt;函数式接口&lt;/strong&gt;的匿名内部类&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<h2>第十章 常用 API</h2>
<h3>1. 常用 API 概述</h3>
<h4>什么是 API</h4>
<blockquote>
<p>&lt;span style="color:blue"&gt;API（&lt;strong&gt;Application Programming Interface&lt;/strong&gt;）—— 应用程序编程接口&lt;/span&gt;
就是别人写好的一些类，给咱们程序员直接拿去调用即可解决问题的</p>
</blockquote>
<h4>已经学习过的 API</h4>
<table>
<thead>
<tr>
<th>API</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;Scanner&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;键盘录入&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;Random&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;随机数&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:blue"&gt;JDK-API 帮助文档：&lt;strong&gt;JDK 提供的类的使用说明书&lt;/strong&gt;，所有 API 都可以在这里查到&lt;/span&gt;</p>
</blockquote>
<h3>2. String 类</h3>
<h4>String 类的特点</h4>
<blockquote>
<ul>
<li>&lt;span style="color:blue"&gt;Java 程序中所有&lt;strong&gt;双引号字符串&lt;/strong&gt;，都是 &lt;strong&gt;String 类的对象&lt;/strong&gt;&lt;/span&gt;</li>
<li>&lt;span style="color:orange"&gt;字符串在&lt;strong&gt;创建之后，其内容不可更改&lt;/strong&gt;&lt;/span&gt;</li>
<li>&lt;span style="color:orange"&gt;字符串虽然不可改变，但是&lt;strong&gt;可以被共享&lt;/strong&gt;&lt;/span&gt;</li>
</ul>
</blockquote>
<pre><code>String s = "abc";   // 字符串"abc"是 String 类的对象，s 指向它
s = "def";          // 不是修改内容，而是把 s 的指向改为新的"def"
</code></pre>
<h4>String 类的常见构造方法</h4>
<table>
<thead>
<tr>
<th>构造方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;<code>public String()</code>&lt;/span&gt;</td>
<td>创建空白字符串，不含任何内容</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;<code>public String(String original)</code>&lt;/span&gt;</td>
<td>根据传入的字符串，创建字符串对象</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;<code>public String(char[] chs)</code>&lt;/span&gt;</td>
<td>根据字符数组，创建字符串对象</td>
</tr>
</tbody>
</table>
<pre><code>// 两种最常用的方式
String s1 = new String("abc");   // 通过构造方法
String s2 = "abc";                // 直接赋值（推荐）
</code></pre>
<h4>String 类的常见面试题（== 比较）</h4>
<blockquote>
<ol>
<li>&lt;span style="color:orange"&gt;字符串字面量赋值，先在字符串常量池中查找；&lt;/span&gt;
&lt;span style="color:red"&gt;如果存在，则&lt;strong&gt;直接共享&lt;/strong&gt;，不创建新对象；不存在则创建新对象并存入池中&lt;/span&gt;</li>
</ol>
</blockquote>
<pre><code>String s1 = "abc";
String s2 = "abc";
System.out.println(s1 == s2);   // true（共享常量池中同一个对象）
</code></pre>
<blockquote>
<ol>
<li>&lt;span style="color:orange"&gt;通过 <code>new</code> 关键字创建的字符串对象，&lt;strong&gt;每次都会在堆内存中开辟新空间&lt;/strong&gt;&lt;/span&gt;</li>
</ol>
</blockquote>
<pre><code>String s1 = "abc";
String s2 = new String("abc");
System.out.println(s1 == s2);   // false（地址不同）
</code></pre>
<blockquote>
<ol>
<li>&lt;span style="color:orange"&gt;字符串拼接 <code>+</code> 的底层会创建 <code>StringBuilder</code> 对象，拼接后再 <code>toString()</code>；&lt;br&gt;结果是&lt;strong&gt;新对象&lt;/strong&gt;，所以和常量池中的不相等&lt;/span&gt;</li>
</ol>
</blockquote>
<pre><code>String s1 = "abc";
String s2 = "ab" + "c";           // 常量拼接，编译期优化 → true
String s3 = s2 + "c";             // 变量拼接 → new StringBuilder().toString() → false
System.out.println(s1 == s3);     // false
</code></pre>
<pre><code>flowchart LR
    subgraph heap[堆内存]
        ST[StringTable 字符串常量池]
        H[new String 对象]
    end
    subgraph stack[栈内存]
        A[s1]
        B[s2]
    end
    A --&gt; ST
    B --&gt; H
    H -.不共享.-&gt; ST
</code></pre>
<blockquote>
<p>&lt;span style="color:red"&gt;结论：比较字符串内容是否相同，请使用 <code>equals()</code> 方法，而不是 <code>==</code>&lt;/span&gt;</p>
</blockquote>
<h4>String 用于比较的方法</h4>
<table>
<thead>
<tr>
<th>方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;<code>public boolean equals(要比较的字符串)</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;完全一样结果才是 <code>true</code>，否则为 <code>false</code>&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;<code>public boolean equalsIgnoreCase(要比较的字符串)</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;忽略大小写的比较&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<pre><code>String s1 = "abc";
String s2 = "ABC";
System.out.println(s1.equals(s2));           // false
System.out.println(s1.equalsIgnoreCase(s2)); // true
</code></pre>
<h4>案例：模拟用户登录</h4>
<blockquote>
<p>需求：已知正确的用户名和密码，请用程序实现模拟用户登录。总共给三次机会，登录之后，给出相应的提示</p>
</blockquote>
<pre><code>import java.util.Scanner;

public class Login {
    public static void main(String[] args) {
        String rightUsername = "itheima";
        String rightPassword = "123456";
        Scanner sc = new Scanner(System.in);
        
        for (int i = 0; i &lt; 3; i++) {
            System.out.print("请输入用户名：");
            String username = sc.next();
            System.out.print("请输入密码：");
            String password = sc.next();
            
            if (rightUsername.equals(username) &amp;&amp; rightPassword.equals(password)) {
                System.out.println("登录成功！");
                break;
            } else {
                System.out.println("登录失败，还有 " + (2 - i) + " 次机会");
            }
        }
    }
}
</code></pre>
<h4>String 字符串的遍历</h4>
<table>
<thead>
<tr>
<th>方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;<code>public char[] toCharArray()</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;将字符串转换为字符数组&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;<code>public char charAt(int index)</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;根据索引找字符&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;<code>public int length()</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;返回字符串的长度&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<pre><code>// 方式一：charAt + length
String s = "abcde";
for (int i = 0; i &lt; s.length(); i++) {
    System.out.println(s.charAt(i));
}

// 方式二：toCharArray
for (char c : s.toCharArray()) {
    System.out.println(c);
}
</code></pre>
<h4>案例：统计字符次数</h4>
<blockquote>
<p>需求：键盘录入一个字符串，统计该字符串中大写字母字符、小写字母字符、数字字符出现的次数（不考虑其他字符）
例如：<code>aAb3&amp;c2B*4CD1</code> → 小写：3 个；大写：4 个；数字：4 个</p>
</blockquote>
<pre><code>String s = new Scanner(System.in).next();
int upper = 0, lower = 0, digit = 0;
for (int i = 0; i &lt; s.length(); i++) {
    char c = s.charAt(i);
    if (c &gt;= 'A' &amp;&amp; c &lt;= 'Z') upper++;
    else if (c &gt;= 'a' &amp;&amp; c &lt;= 'z') lower++;
    else if (c &gt;= '0' &amp;&amp; c &lt;= '9') digit++;
}
System.out.println("大写：" + upper + "，小写：" + lower + "，数字：" + digit);
</code></pre>
<h4>String 字符串的截取方法</h4>
<table>
<thead>
<tr>
<th>方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;<code>public String substring(int beginIndex, int endIndex)</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;截取；&lt;strong&gt;包头不包尾&lt;/strong&gt;，只有返回值才是截取的小串&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;<code>public String substring(int beginIndex)</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;从 beginIndex 截取到末尾&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<pre><code>String s = "abcdef";
System.out.println(s.substring(1, 3));   // bc（包左不包右）
System.out.println(s.substring(2));      // cdef
</code></pre>
<h4>案例：手机号屏蔽</h4>
<blockquote>
<p>需求：以字符串的形式从键盘接受一个手机号，将中间四位号码屏蔽
最终效果为：<code>156****1234</code></p>
</blockquote>
<pre><code>String phone = "15612341234";
String result = phone.substring(0, 3) + "****" + phone.substring(7);
System.out.println(result);   // 156****1234
</code></pre>
<h4>String 字符串的替换方法</h4>
<table>
<thead>
<tr>
<th>方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;<code>public String replace(旧值, 新值)</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;返回值才是&lt;strong&gt;替换之后&lt;/strong&gt;的结果，原串不变&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<pre><code>String s = "abcabc";
String r = s.replace("a", "x");
System.out.println(r);   // xbcxbc
System.out.println(s);   // abcabc（不变）
</code></pre>
<h4>案例：敏感词替换</h4>
<blockquote>
<p>需求：键盘录入一个字符串，如果字符串中包含 <code>(TMD)</code>，则使用 <code>***</code> 替换</p>
</blockquote>
<pre><code>String s = new Scanner(System.in).next();
String r = s.replace("TMD", "***");
System.out.println(r);
</code></pre>
<h4>String 字符串的切割方法</h4>
<table>
<thead>
<tr>
<th>方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;<code>public String[] split(String regex)</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;根据传入的字符串作为规则进行切割；&lt;br&gt;将切割后的内容存入字符串数组中，并将字符串数组返回&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<pre><code>String s = "a,b,c,d";
String[] arr = s.split(",");
for (String x : arr) {
    System.out.println(x);
}
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意：&lt;strong&gt;切割时如果中间存在空格&lt;/strong&gt;，需要用 <code>, +</code> 或 <code>","</code> 处理；&lt;br&gt;切割规则如果是特殊字符（如 <code>.</code>、<code>|</code>），需要转义（加 <code>\\</code>）&lt;/span&gt;</p>
</blockquote>
<h4>String 方法小结</h4>
<table>
<thead>
<tr>
<th>方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;<code>equals(要比较的字符串)</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;比较内容&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;<code>equalsIgnoreCase(要比较的字符串)</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;比较内容，忽略大小写&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;<code>toCharArray()</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;将字符串转换为字符数组&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;<code>charAt(int index)</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;根据索引找字符&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;<code>length()</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;返回字符串的长度&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;<code>substring(int beginIndex)</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;截取到末尾&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;<code>substring(int beginIndex, int endIndex)</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;根据开始和结束索引做截取，&lt;strong&gt;包含头不包含尾&lt;/strong&gt;&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;<code>replace(旧值, 新值)</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;替换&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;<code>split(String regex)</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;切割&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<h3>3. StringBuilder 类</h3>
<h4>StringBuilder 引入</h4>
<blockquote>
<p>&lt;span style="color:blue"&gt;StringBuilder 可以&lt;strong&gt;大幅度提高字符串操作效率&lt;/strong&gt;&lt;/span&gt;
如果在&lt;strong&gt;大量拼接字符串&lt;/strong&gt;的场景下，使用 String 的 <code>+</code> 会反复创建对象，效率低，推荐用 StringBuilder</p>
</blockquote>
<h4>StringBuilder 介绍</h4>
<blockquote>
<ul>
<li>&lt;span style="color:orange"&gt;StringBuilder 是&lt;strong&gt;字符串的缓冲区&lt;/strong&gt;，我们可以将其理解为是一种&lt;strong&gt;容器&lt;/strong&gt;&lt;/span&gt;</li>
<li>&lt;span style="color:orange"&gt;StringBuilder 是一种&lt;strong&gt;可变的字符序列&lt;/strong&gt;&lt;/span&gt;</li>
</ul>
</blockquote>
<h4>StringBuilder 构造方法</h4>
<table>
<thead>
<tr>
<th>构造方法</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;<code>public StringBuilder()</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;创建一个空的字符串缓冲区（容器）&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;<code>public StringBuilder(String str)</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;创建一个字符串缓冲区，并初始化好指定的参数内容&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<pre><code>StringBuilder sb1 = new StringBuilder();
StringBuilder sb2 = new StringBuilder("abc");
</code></pre>
<h4>StringBuilder 常用方法</h4>
<table>
<thead>
<tr>
<th>方法名</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;<code>public StringBuilder append(任意类型)</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;添加数据，并&lt;strong&gt;返回对象本身&lt;/strong&gt;（支持链式编程）&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;<code>public StringBuilder reverse()</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;反转容器中的内容&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;<code>public int length()</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;返回长度（字符出现的个数）&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;<code>public String toString()</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;通过 toString() 就可以实现把 StringBuilder 转换为 String&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<pre><code>StringBuilder sb = new StringBuilder();
sb.append("a").append("b").append("c");   // 链式编程
System.out.println(sb);          // abc
System.out.println(sb.length()); // 3

String s = sb.toString();        // 转成 String
</code></pre>
<h4>案例：回文字符串</h4>
<blockquote>
<p>需求：键盘接受一个字符串，程序判断出该字符串是否是对称字符串，并在控制台打印是或不是
对称字符串：<code>123321</code>、<code>111</code>
非对称字符串：<code>123123</code></p>
</blockquote>
<pre><code>String s = new Scanner(System.in).next();
StringBuilder sb = new StringBuilder(s);
String reverse = sb.reverse().toString();

if (s.equals(reverse)) {
    System.out.println("是对称字符串");
} else {
    System.out.println("不是对称字符串");
}
</code></pre>
<h4>案例：按照指定的格式拼接字符串</h4>
<blockquote>
<p>需求：定义一个方法，把 <code>int</code> 数组中的数据按照指定的格式拼接成一个字符串返回。调用该方法，并在控制台输出结果
例如：数组为 <code>int[] arr = {1,2,3};</code> → 输出 <code>[1, 2, 3]</code></p>
</blockquote>
<pre><code>public static String arrayToString(int[] arr) {
    StringBuilder sb = new StringBuilder("[");
    for (int i = 0; i &lt; arr.length; i++) {
        if (i == arr.length - 1) {
            sb.append(arr[i]).append("]");
        } else {
            sb.append(arr[i]).append(", ");
        }
    }
    return sb.toString();
}
</code></pre>
<h4>StringBuilder 原理（一个加号，堆内存中俩对象）</h4>
<blockquote>
<p>当执行 <code>String s = s1 + s2;</code> 时，底层会做：</p>
<ol>
<li>创建一个 <code>StringBuilder</code> 对象</li>
<li>调用 <code>append()</code> 把 s1、s2 加入</li>
<li>调用 <code>toString()</code> 创建新的 String 对象返回</li>
</ol>
</blockquote>
<pre><code>flowchart LR
    subgraph stack[栈内存]
        s1[s1 = 'a']
        s2[s2 = s1 + 'b']
        s3[s3 = s2 + 'c']
    end
    subgraph heap[堆内存]
        SB1[StringBuilder 'ab']
        ST1[String 'ab']
        SB2[StringBuilder 'abc']
        ST2[String 'abc']
    end
    s1 --&gt; H1[StringTable 'a']
    s2 --&gt; SB1
    SB1 -.toString.-&gt; ST1
    s2 --&gt; SB2
    SB2 -.toString.-&gt; ST2
    s3 --&gt; ST2
</code></pre>
<blockquote>
<p>&lt;span style="color:red"&gt;结论：每次 <code>+</code> 都会产生一个 StringBuilder + 一个新 String 对象，&lt;strong&gt;频繁拼接效率低&lt;/strong&gt;&lt;/span&gt;</p>
</blockquote>
<h4>StringBuilder 拼接原理（append 复用自身）</h4>
<blockquote>
<p>直接使用 StringBuilder 的 <code>append()</code>，方法返回&lt;strong&gt;自身对象&lt;/strong&gt;，始终在同一个缓冲区中操作</p>
</blockquote>
<pre><code>flowchart LR
    subgraph stack[栈内存]
        sb[Reference sb]
    end
    subgraph heap[堆内存]
        BUF[StringBuilder 同一个对象]
    end
    sb --&gt; BUF
    BUF --&gt; A1[append a]
    BUF --&gt; A2[append b]
    BUF --&gt; A3[append c]
</code></pre>
<blockquote>
<p>&lt;span style="color:blue"&gt;优势：&lt;strong&gt;不会产生中间 String 对象&lt;/strong&gt;，效率远高于 <code>+</code> 拼接&lt;/span&gt;</p>
</blockquote>
<h4>StringBuilder 小结</h4>
<blockquote>
<p>&lt;span style="color:orange"&gt;StringBuilder 可以大幅度提升字符串操作效率&lt;/span&gt;
&lt;span style="color:orange"&gt;今后若频繁进行字符串拼接操作，建议使用 StringBuilder&lt;/span&gt;</p>
</blockquote>
<h4>StringBuilder 与 String 互转</h4>
<pre><code>// StringBuilder → String：通过 toString()
String s = sb.toString();

// String → StringBuilder：通过构造方法
StringBuilder sb2 = new StringBuilder("hello");
</code></pre>
<h4>链式编程</h4>
<blockquote>
<p>StringBuilder 的 <code>append()</code> 方法返回的是&lt;strong&gt;对象本身&lt;/strong&gt;，因此可以连续调用</p>
</blockquote>
<pre><code>StringBuilder sb = new StringBuilder();
sb.append("a").append("b").append("c").reverse().append("d");
System.out.println(sb);   // dcba
</code></pre>
<h2>第十一章 集合基础</h2>
<h3>1. 集合概述</h3>
<h4>什么是集合</h4>
<blockquote>
<p>&lt;span style="color:blue"&gt;集合是一种&lt;strong&gt;容器&lt;/strong&gt;，用来装数据的，类似于数组&lt;/span&gt;</p>
</blockquote>
<h4>数组 vs 集合</h4>
<table>
<thead>
<tr>
<th>对比项</th>
<th>数组</th>
<th>集合</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;长度&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;&lt;strong&gt;固定不变&lt;/strong&gt;&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;&lt;strong&gt;可变&lt;/strong&gt;&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;类型&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;基本数据类型 / 引用数据类型&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;只能存放&lt;strong&gt;引用数据类型&lt;/strong&gt;（基本类型用包装类）&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;使用场景&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;元素个数固定不变&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;元素个数经常发生改变&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:red"&gt;结论：开发中&lt;strong&gt;集合用的更多&lt;/strong&gt;，因为业务中数据量往往是动态的&lt;/span&gt;</p>
</blockquote>
<h4>集合的体系结构</h4>
<pre><code>classDiagram
    class Collection
    class Map
    Collection &lt;|-- List
    Collection &lt;|-- Set
    List &lt;|-- ArrayList
    List &lt;|-- LinkedList
    List &lt;|-- Vector
    Set &lt;|-- HashSet
    Set &lt;|-- TreeSet
    Set &lt;|-- LinkedHashSet
    Map &lt;|-- HashMap
    Map &lt;|-- LinkedHashMap
    Map &lt;|-- TreeMap
</code></pre>
<blockquote>
<p>&lt;span style="color:blue"&gt;Collection 接口：单列集合，每次操作存储一个元素&lt;/span&gt;
&lt;span style="color:blue"&gt;Map 接口：双列集合，每次操作存储一对键值对（key-value）&lt;/span&gt;</p>
</blockquote>
<h3>2. ArrayList</h3>
<h4>ArrayList 长度可变原理</h4>
<blockquote>
<ol>
<li>&lt;span style="color:orange"&gt;当创建 ArrayList 集合容器的时候，底层会存在一个&lt;strong&gt;长度为 10&lt;/strong&gt; 的空数组&lt;/span&gt;</li>
<li>&lt;span style="color:orange"&gt;当数组&lt;strong&gt;存满&lt;/strong&gt;后，再次添加新元素时，ArrayList 会&lt;strong&gt;扩容&lt;/strong&gt;原数组 &lt;strong&gt;1.5 倍&lt;/strong&gt; 大小的新数组&lt;/span&gt;</li>
<li>&lt;span style="color:orange"&gt;将原数组数据&lt;strong&gt;拷贝&lt;/strong&gt;到新数组中&lt;/span&gt;</li>
<li>&lt;span style="color:orange"&gt;将新元素添加到新数组中&lt;/span&gt;</li>
</ol>
</blockquote>
<pre><code>flowchart TB
    A[创建 ArrayList 底层数组长度 10] --&gt; B{数组是否存满?}
    B -- 否 --&gt; C[直接添加]
    B -- 是 --&gt; D[扩容原数组 1.5 倍新数组]
    D --&gt; E[拷贝原数据到新数组]
    E --&gt; F[添加新元素]
    F --&gt; B
</code></pre>
<h4>集合和数组的使用选择小结</h4>
<blockquote>
<ul>
<li>&lt;span style="color:orange"&gt;数组：存储的元素个数&lt;strong&gt;固定不变&lt;/strong&gt;&lt;/span&gt;</li>
<li>&lt;span style="color:orange"&gt;集合：存储的元素个数&lt;strong&gt;经常发生改变&lt;/strong&gt;&lt;/span&gt;</li>
</ul>
</blockquote>
<h3>3. 创建集合</h3>
<h4>创建集合的两种方式</h4>
<blockquote>
<p>&lt;span style="color:red"&gt;方案 1（不推荐）：&lt;strong&gt;不写泛型&lt;/strong&gt;，添加数据时可以是任意类型，但取数据时容易出问题&lt;/span&gt;</p>
</blockquote>
<pre><code>ArrayList list = new ArrayList();
list.add(1);
list.add(12.3);
list.add('a');
list.add("abc");
list.add(false);
</code></pre>
<blockquote>
<p>&lt;span style="color:red"&gt;方案 2（推荐）：&lt;strong&gt;写泛型&lt;/strong&gt;，限制集合中只能存储指定类型的元素&lt;/span&gt;</p>
</blockquote>
<pre><code>ArrayList&lt;String&gt; list = new ArrayList&lt;&gt;();
list.add(1);       // ❌ 编译报错，类型不匹配
list.add(12.3);    // ❌
list.add("abc");   // ✅
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;建议使用&lt;strong&gt;方案 2&lt;/strong&gt;，创建集合的时候加入泛型可以使数据&lt;strong&gt;严谨和规范&lt;/strong&gt;&lt;/span&gt;</p>
</blockquote>
<h4>创建集合小结</h4>
<blockquote>
<ul>
<li>&lt;span style="color:orange"&gt;集合在创建的时候&lt;strong&gt;如果不加泛型&lt;/strong&gt;，添加数据的时候可以是&lt;strong&gt;任意类型&lt;/strong&gt;&lt;/span&gt;</li>
<li>&lt;span style="color:orange"&gt;建议创建的时候&lt;strong&gt;加入泛型&lt;/strong&gt;，保证数据的严谨和规范&lt;/span&gt;</li>
<li>&lt;span style="color:orange"&gt;&lt;strong&gt;JDK7 版本&lt;/strong&gt;开始，右侧 <code>&amp;lt;&amp;gt;</code> 中可以&lt;strong&gt;省略类型&lt;/strong&gt;&lt;/span&gt;</li>
</ul>
</blockquote>
<pre><code>// JDK7 前：右侧需要写泛型
ArrayList&lt;String&gt; list = new ArrayList&lt;String&gt;();

// JDK7+：右侧 &lt;&gt; 可省略（菱形语法）
ArrayList&lt;String&gt; list = new ArrayList&lt;&gt;();
</code></pre>
<h3>4. ArrayList 常用成员方法</h3>
<h4>增删改查方法</h4>
<table>
<thead>
<tr>
<th>方法名</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;<code>public boolean add(E e)</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;将指定的元素添加到此集合的&lt;strong&gt;末尾&lt;/strong&gt;&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;<code>public void add(int index, E element)</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;在此集合中的&lt;strong&gt;指定位置&lt;/strong&gt;插入指定的元素&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;<code>public E get(int index)</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;返回指定索引处的元素&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;<code>public int size()</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;返回集合中的元素的个数&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;<code>public E remove(int index)</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;删除指定索引处的元素，&lt;strong&gt;返回被删除的元素&lt;/strong&gt;&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;<code>public boolean remove(Object o)</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;删除指定的元素，&lt;strong&gt;返回删除是否成功&lt;/strong&gt;&lt;/span&gt;</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;<code>public E set(int index, E element)</code>&lt;/span&gt;</td>
<td>&lt;span style="color:orange"&gt;修改指定索引处的元素，&lt;strong&gt;返回被修改的元素&lt;/strong&gt;&lt;/span&gt;</td>
</tr>
</tbody>
</table>
<h4>集合的遍历</h4>
<pre><code>ArrayList&lt;String&gt; list = new ArrayList&lt;&gt;();
list.add("aaa");
list.add("bbb");
list.add("ccc");

// 方式 1：普通 for
for (int i = 0; i &lt; list.size(); i++) {
    System.out.println(list.get(i));
}

// 方式 2：增强 for（推荐）
for (String s : list) {
    System.out.println(s);
}
</code></pre>
<h3>5. 集合练习</h3>
<h4>练习 1：集合存储字符串并遍历</h4>
<blockquote>
<p>需求：创建一个存储字符串的集合，内部存储 3 个字符串元素，使用程序实现在控制台遍历该集合</p>
</blockquote>
<pre><code>ArrayList&lt;String&gt; list = new ArrayList&lt;&gt;();
list.add("张三");
list.add("李四");
list.add("王五");

for (int i = 0; i &lt; list.size(); i++) {
    System.out.println(list.get(i));
}
</code></pre>
<h4>练习 2：集合存储学生对象并遍历</h4>
<blockquote>
<p>需求：创建一个存储学生对象的集合，存储 3 个学生对象，使用程序实现在控制台遍历该集合
学生的姓名和年龄来自于键盘录入</p>
</blockquote>
<pre><code>public class Student {
    private String name;
    private int age;
    
    public Student() { }
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    // 省略 getter / setter
}
</code></pre>
<pre><code>ArrayList&lt;Student&gt; list = new ArrayList&lt;&gt;();
Scanner sc = new Scanner(System.in);

for (int i = 0; i &lt; 3; i++) {
    System.out.print("请输入姓名：");
    String name = sc.next();
    System.out.print("请输入年龄：");
    int age = sc.nextInt();
    
    Student stu = new Student(name, age);
    list.add(stu);
}

for (Student stu : list) {
    System.out.println(stu.getName() + "，" + stu.getAge());
}
</code></pre>
<h4>集合存储对象内存图</h4>
<blockquote>
<p>以 <code>addStudent(list)</code> 三个学生对象为例，逐步展示栈内存和堆内存的变化</p>
</blockquote>
<pre><code>flowchart LR
    subgraph stack[栈内存]
        M[方法 main ArrayList list 001]
        A1[方法 addStudent name 张三 age 23 stu 00A]
        A2[方法 addStudent name 李四 age 24 stu 00B]
        A3[方法 addStudent name 王五 age 25 stu 00C]
    end
    subgraph heap[堆内存]
        L[new ArrayList 001 内部存 00A 00B 00C]
        O1[new Student 00A 张三 23]
        O2[new Student 00B 李四 24]
        O3[new Student 00C 王五 25]
    end
    M --&gt; L
    A1 -.add 后.-&gt; L
    A2 -.add 后.-&gt; L
    A3 -.add 后.-&gt; L
    L --&gt; O1
    L --&gt; O2
    L --&gt; O3
</code></pre>
<blockquote>
<p>&lt;span style="color:red"&gt;关键结论：集合中存储的是对象的&lt;strong&gt;地址值&lt;/strong&gt;，而非对象本身&lt;/span&gt;
&lt;span style="color:orange"&gt;由于 ArrayList 底层是数组，所以数组中存的是对象的地址引用&lt;/span&gt;</p>
</blockquote>
<h4>练习 3：集合删除元素</h4>
<blockquote>
<p>需求：创建一个存储 String 的集合，内部存储 <code>(test, 张三, 李四, test, test)</code> 字符串
删除所有的 <code>test</code> 字符串，删除后，将集合剩余元素打印在控制台</p>
</blockquote>
<pre><code>ArrayList&lt;String&gt; list = new ArrayList&lt;&gt;();
list.add("test");
list.add("张三");
list.add("李四");
list.add("test");
list.add("test");

// 注意：不能一边遍历一边删除，会导致索引错乱
// 方案 1：倒着遍历
for (int i = list.size() - 1; i &gt;= 0; i--) {
    if ("test".equals(list.get(i))) {
        list.remove(i);
    }
}

// 方案 2：使用迭代器（推荐）
Iterator&lt;String&gt; it = list.iterator();
while (it.hasNext()) {
    if ("test".equals(it.next())) {
        it.remove();   // 使用迭代器自己的 remove
    }
}
</code></pre>
<h4>练习 4：集合元素筛选</h4>
<blockquote>
<p>需求：定义一个方法，方法接收一个集合对象（泛型为 <code>Student</code>），方法内部将年龄低于 18 的学生对象找出，并存入新集合对象，方法返回新集合</p>
</blockquote>
<pre><code>public static ArrayList&lt;Student&gt; filterStudents(ArrayList&lt;Student&gt; list) {
    ArrayList&lt;Student&gt; result = new ArrayList&lt;&gt;();
    for (Student s : list) {
        if (s.getAge() &lt; 18) {
            result.add(s);
        }
    }
    return result;
}
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意：返回值必须是一个新的集合，&lt;strong&gt;不能修改原集合&lt;/strong&gt;&lt;/span&gt;</p>
</blockquote>
<h4>集合小结</h4>
<blockquote>
<ul>
<li>&lt;span style="color:blue"&gt;集合是一种&lt;strong&gt;长度可变&lt;/strong&gt;的容器&lt;/span&gt;</li>
<li>&lt;span style="color:blue"&gt;开发中推荐使用&lt;strong&gt;方案 2&lt;/strong&gt;（加泛型）创建集合，保证数据严谨&lt;/span&gt;</li>
<li>&lt;span style="color:blue"&gt;集合中存储的是对象的&lt;strong&gt;地址&lt;/strong&gt;，不是对象本身&lt;/span&gt;</li>
<li>&lt;span style="color:blue"&gt;遍历集合常用&lt;strong&gt;普通 for&lt;/strong&gt;（带索引）和&lt;strong&gt;增强 for&lt;/strong&gt;&lt;/span&gt;</li>
<li>&lt;span style="color:blue"&gt;删除元素不能一边遍历一边删除，要倒着删或用迭代器&lt;/span&gt;</li>
</ul>
</blockquote>
]]></content>
    <author><name>仙鹤</name></author>
    <category term="编程"/>
  </entry>
  <entry>
    <title>C 语言过渡 C++</title>
    <link href="https://xianhe51920.github.io/posts/c-to-cpp/" rel="alternate" type="text/html"/>
    <id>https://xianhe51920.github.io/posts/c-to-cpp/</id>
    <published>2026-09-03T00:00:00.000Z</published>
    <updated>2026-09-03T00:00:00.000Z</updated>
    <summary>从 C 过渡到 C++</summary>
    <content type="html"><![CDATA[<h1>C 语言过渡 C++</h1>
<blockquote>
<p>本笔记按"基础篇 → STL 篇 → 进阶篇 → C++11 篇"四部分整理，弥补 C 与 C++ 在语法、标准库、常用特性上的差异，作为刷题与竞赛入门的过渡资料。</p>
</blockquote>
<h1>一、基础篇</h1>
<blockquote>
<p>本节聚焦 C++ 相对 C 的核心语法变化：命名空间、IO 流、头文件、变量声明、bool、const、string、结构体、引用。</p>
</blockquote>
<h2>C++ 与 C 的核心差异速览</h2>
<table>
<thead>
<tr>
<th>维度</th>
<th>C 语言</th>
<th>C++</th>
</tr>
</thead>
<tbody>
<tr>
<td>输入输出</td>
<td><code>scanf</code> / <code>printf</code></td>
<td><code>cin</code> / <code>cout</code>（流式）</td>
</tr>
<tr>
<td>头文件</td>
<td><code>&lt;stdio.h&gt;</code></td>
<td><code>&lt;iostream&gt;</code>（去 <code>.h</code>、加 <code>c</code> 前缀）</td>
</tr>
<tr>
<td>变量声明</td>
<td>必须出现在语句块开头</td>
<td>任意位置（C++17 起更宽松）</td>
</tr>
<tr>
<td>布尔类型</td>
<td>用 <code>0/1</code> 模拟</td>
<td>原生 <code>bool</code>，值为 <code>true</code> / <code>false</code></td>
</tr>
<tr>
<td>常量</td>
<td><code>#define</code> / <code>enum</code></td>
<td><code>const</code> / <code>constexpr</code></td>
</tr>
<tr>
<td>字符串</td>
<td><code>char[]</code> + 字符串函数</td>
<td>原生 <code>string</code> 类</td>
</tr>
<tr>
<td>结构体</td>
<td>必须写 <code>struct stu a;</code></td>
<td>可省略 <code>struct</code>，直接 <code>stu a;</code></td>
</tr>
<tr>
<td>引用</td>
<td>无（只能传指针）</td>
<td>引用 <code>&amp;</code>（语法糖，本质是指针）</td>
</tr>
<tr>
<td>函数默认值</td>
<td>无</td>
<td>支持参数默认值</td>
</tr>
<tr>
<td>函数重载</td>
<td>不支持</td>
<td>支持</td>
</tr>
</tbody>
</table>
<h2>命名空间 <code>using namespace std</code></h2>
<blockquote>
<p>&lt;span style="color:blue"&gt;<code>std</code>&lt;/span&gt; 是 C++ 标准库的命名空间，<code>cin</code> / <code>cout</code> / <code>string</code> 等都在 <code>std</code> 里。</p>
</blockquote>
<p><strong>两种写法</strong>：</p>
<ul>
<li><code>using namespace std;</code> —— 一次性把 <code>std</code> 全部展开（偷懒，<strong>小项目/刷题</strong>常用）</li>
<li><code>std::cin</code> / <code>std::cout</code> —— 每次显式指定（<strong>正式项目</strong>推荐，避免命名冲突）</li>
</ul>
<pre><code>#include &lt;iostream&gt;

using namespace std;

int main() {
    int n;
    cin &gt;&gt; n;
    cout &lt;&lt; "wohaoshuai" &lt;&lt; n &lt;&lt; endl;

    return 0;
}
</code></pre>
<pre><code>#include &lt;iostream&gt;

int main() {
    int n;
    std::cin &gt;&gt; n;
    std::cout &lt;&lt; "wohaoshuai" &lt;&lt; n &lt;&lt; std::endl;

    return 0;
}
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意&lt;/span&gt;：<code>endl</code> 等价于 <code>'\n' + flush()</code>，频繁使用会降低 IO 性能。刷题数据量大时，可改为 <code>'\n'</code>。</p>
</blockquote>
<h2>cin 和 cout</h2>
<h3>运行速度</h3>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意&lt;/span&gt;：<code>cin</code> / <code>cout</code> 默认会与 C 标准库 <code>scanf</code> / <code>printf</code> 同步，且绑定到 <code>cin.tie(nullptr)</code>，因此<strong>比 <code>scanf</code> / <code>printf</code> 慢</strong>。数据量大时建议关闭同步。</p>
</blockquote>
<p><strong>关闭同步提速</strong>（刷题常用）：</p>
<pre><code>ios::sync_with_stdio(false);
cin.tie(nullptr);
</code></pre>
<p><strong>使用方式</strong>：<code>&gt;&gt;</code> 读入、<code>&lt;&lt;</code> 输出、配合 <code>endl</code> 或 <code>'\n'</code> 换行。</p>
<h2>头文件</h2>
<blockquote>
<p>C++ 标准库头文件<strong>去掉 <code>.h</code></strong>，C 语言风格头文件加 <code>c</code> 前缀（<code>stdio.h</code> → <code>cstdio</code>），这样所有符号都在 <code>std</code> 命名空间里。</p>
</blockquote>
<table>
<thead>
<tr>
<th>C 风格</th>
<th>C++ 风格</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>&lt;stdio.h&gt;</code></td>
<td><code>&lt;cstdio&gt;</code></td>
</tr>
<tr>
<td><code>&lt;string.h&gt;</code></td>
<td><code>&lt;cstring&gt;</code></td>
</tr>
<tr>
<td><code>&lt;math.h&gt;</code></td>
<td><code>&lt;cmath&gt;</code></td>
</tr>
<tr>
<td><code>&lt;stdlib.h&gt;</code></td>
<td><code>&lt;cstdlib&gt;</code></td>
</tr>
<tr>
<td><code>&lt;ctype.h&gt;</code></td>
<td><code>&lt;cctype&gt;</code></td>
</tr>
</tbody>
</table>
<pre><code>#include &lt;cstring&gt;
#include &lt;cmath&gt;
</code></pre>
<h2>变量声明</h2>
<blockquote>
<p>C99 之后 C 语言也允许在 <code>for</code> 循环里定义循环变量，但 C++ 从一开始就支持——这在 C89 时代的 C 里是违法的。</p>
</blockquote>
<pre><code>#include &lt;iostream&gt;

int main() {
    for (int i = 0; i &lt; 10; i++) {
        // 循环体，i 作用域仅限 for 块内
    }
    // 这里使用 i 会编译报错
    return 0;
}
</code></pre>
<h2>bool 变量</h2>
<blockquote>
<p>&lt;span style="color:blue"&gt;<code>bool</code>&lt;/span&gt; 是 C++ 原生类型，只有两个值：<code>true</code> / <code>false</code>。<strong>任何非 0 值</strong> 隐式转换为 <code>true</code>，<code>0</code> 转换为 <code>false</code>。</p>
</blockquote>
<pre><code>#include &lt;iostream&gt;

using namespace std;

int main() {
    bool flag = true;
    bool flag2 = -1;
    bool flag3 = 0;

    cout &lt;&lt; flag &lt;&lt; " " &lt;&lt; flag2 &lt;&lt; " " &lt;&lt; flag3 &lt;&lt; " " &lt;&lt; endl; // 1 1 0

    return 0;
}
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意&lt;/span&gt;：<code>cout</code> 输出 <code>bool</code> 会显示 <code>0/1</code>，想看 <code>true/false</code> 需 <code>cout &lt;&lt; boolalpha &lt;&lt; flag;</code>。</p>
</blockquote>
<h2>const 定义常量</h2>
<blockquote>
<p><code>const</code> 比 <code>#define</code> 更安全（<strong>有类型、参与编译检查</strong>），比 <code>enum</code> 更通用（任何类型都可）。</p>
</blockquote>
<pre><code>#include &lt;iostream&gt;

using namespace std;

int main() {
    const int MAX = 100;

    cout &lt;&lt; MAX &lt;&lt; endl;

    return 0;
}
</code></pre>
<h2>string 类</h2>
<blockquote>
<p>C++ 的 <code>string</code> 是对 <code>char[]</code> 的封装，<strong>自动管理内存</strong>，提供拼接、查找、截取等操作，比 C 的字符串函数安全得多。</p>
</blockquote>
<pre><code>#include &lt;iostream&gt;

using namespace std;

int main() {
    // 定义
    string s = "hello";
    string s2 = " world";

    // 拼接
    string s3 = s + s2;
    cout &lt;&lt; s3 &lt;&lt; endl; // hello world

    // 输入输出
    cin &gt;&gt; s;
    cout &lt;&lt; s &lt;&lt; endl;

    // getline, 让带有空格的输入能被读取
    getline(cin, s);


    // 处理
    // s.length() 获取字符串长度
    cout &lt;&lt; s.length &lt;&lt; endl;

    // s.substr(n, m) 从下标 n 开始，取 m 长度的字串
    cout &lt;&lt; s3.substr(0, 2) &lt;&lt; endl; // he

    return 0;
}
</code></pre>
<p><strong>常用方法速览</strong>：</p>
<table>
<thead>
<tr>
<th>方法</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>s.length()</code> / <code>s.size()</code></td>
<td>获取长度</td>
</tr>
<tr>
<td><code>s1 + s2</code></td>
<td>拼接</td>
</tr>
<tr>
<td><code>s1 == s2</code></td>
<td>比较</td>
</tr>
<tr>
<td><code>s.substr(pos, len)</code></td>
<td>截取子串</td>
</tr>
<tr>
<td><code>s.find(str)</code></td>
<td>查找子串位置，未找到返回 <code>string::npos</code></td>
</tr>
<tr>
<td><code>s.replace(pos, len, str)</code></td>
<td>替换</td>
</tr>
<tr>
<td><code>s.insert(pos, str)</code></td>
<td>插入</td>
</tr>
<tr>
<td><code>s.erase(pos, len)</code></td>
<td>删除</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意&lt;/span&gt;：刷题时输入<strong>含空格的整行</strong>用 <code>getline(cin, s)</code>；<code>cin &gt;&gt; s</code> 遇到空格就停。</p>
</blockquote>
<h2>结构体</h2>
<blockquote>
<p>C++ 中<strong>可省略 <code>struct</code> 关键字</strong>直接使用类型名，结构体里也能写函数（与类几乎等价，区别在默认访问权限）。</p>
</blockquote>
<pre><code>#include &lt;iostream&gt;
using namespace std;

struct stu {
    string name;
    int age;
};

int main() {
    stu a[10]; // 省略 struct

    return 0;
}
</code></pre>
<h2>引用 <code>&amp;</code> 和 取地址 <code>&amp;</code></h2>
<blockquote>
<p>&lt;span style="color:red"&gt;C++ 的 <code>&amp;</code> 和 C 语言的 <code>&amp;</code> 是两个完全不同的符号&lt;/span&gt;：C++ 中 <code>&amp;</code> 在<strong>类型后</strong>是引用声明（语法糖），在<strong>变量前</strong>是取地址。</p>
</blockquote>
<table>
<thead>
<tr>
<th>用法</th>
<th>含义</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>int&amp; r = a;</code></td>
<td>声明引用（<code>r</code> 是 <code>a</code> 的别名）</td>
<td>函数参数传递、避免拷贝</td>
</tr>
<tr>
<td><code>&amp;a</code></td>
<td>取变量 <code>a</code> 的地址</td>
<td><code>int* p = &amp;a;</code></td>
</tr>
<tr>
<td><code>a &amp;&amp; b</code></td>
<td>逻辑与（与 C 相同）</td>
<td><code>if (a &amp;&amp; b) {...}</code></td>
</tr>
</tbody>
</table>
<p><strong>引用的本质</strong>：编译器内部实现为<strong>常量指针</strong>（<code>int* const p = &amp;a;</code>），引用必须在声明时<strong>初始化</strong>、不能重新指向其他变量。</p>
<pre><code>#include &lt;iostream&gt;
using namespace std;

// 形参为引用 → 函数内修改会反映到调用方
void c(int&amp; a) {
    a++;
}

int main() {
    int a = 4;

    c(a);

    cout &lt;&lt; a &lt;&lt; endl; // 5

    return 0;
}
</code></pre>
<blockquote>
<p>&lt;span style="color:green"&gt;引用 vs 指针&lt;/span&gt;：引用更安全（无空引用、不可重绑），语法更简洁；指针更灵活（可空、可改、可算术）。<strong>优先用引用</strong>。</p>
</blockquote>
<h1>二、STL 篇</h1>
<blockquote>
<p>STL（Standard Template Library）是 C++ 的标准模板库，核心是<strong>容器 + 迭代器 + 算法</strong>。刷题 90% 的场景都依赖它。</p>
</blockquote>
<h2>容器分类总览</h2>
<pre><code>graph LR
  STL[STL 容器] --&gt; SEQ[顺序容器]
  STL --&gt; ASSOC[关联容器]
  STL --&gt; UNORD[无序容器]

  SEQ --&gt; V[vector]
  SEQ --&gt; DEQ[deque]
  SEQ --&gt; L[list]

  ASSOC --&gt; S[set]
  ASSOC --&gt; M[map]
  ASSOC --&gt; MS[multiset]
  ASSOC --&gt; MM[multimap]

  UNORD --&gt; US[unordered_set]
  UNORD --&gt; UM[unordered_map]
</code></pre>
<table>
<thead>
<tr>
<th>容器</th>
<th>底层</th>
<th>特点</th>
<th>时间复杂度</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>vector</code></td>
<td>动态数组</td>
<td>随机访问 O(1)，尾部插入 O(1)</td>
<td>随机访问最优</td>
</tr>
<tr>
<td><code>deque</code></td>
<td>分段数组</td>
<td>头尾插入 O(1)</td>
<td>双端队列</td>
</tr>
<tr>
<td><code>list</code></td>
<td>双向链表</td>
<td>任意位置插入 O(1)，无随机访问</td>
<td>频繁插入</td>
</tr>
<tr>
<td><code>set</code> / <code>map</code></td>
<td>红黑树</td>
<td>自动排序，查找 O(log n)</td>
<td>有序场景</td>
</tr>
<tr>
<td><code>unordered_set</code> / <code>unordered_map</code></td>
<td>哈希表</td>
<td>查找 O(1) 平均</td>
<td>不需排序的最快查找</td>
</tr>
</tbody>
</table>
<h2>vector</h2>
<blockquote>
<p>动态数组，<strong>默认容器</strong>，刷题首选。<code>size()</code> 动态变化，超出容量时自动 2 倍扩容。</p>
</blockquote>
<pre><code>#include &lt;iostream&gt;
#include &lt;vector&gt; // 头文件
using namespace std;

int main() {
    // 创建数组
    vector&lt;int&gt; a;            // 不分配空间
    vector&lt;int&gt; b(10);        // 分配 10 个空间，元素值默认为 0
    vector&lt;int&gt; c(10, 2);     // 分配 10 个空间，每个初始化为 2

    // 分配数组大小
    a.resize(10);

    // .size() 获取长度
    cout &lt;&lt; a.size() &lt;&lt; endl; // 10

    // 赋值
    for (int i = 0; i &lt; a.size(); i++) {
        a[i] = i;
        cout &lt;&lt; a[i] &lt;&lt; " ";
    } // 0 1 2 3 4 5 6 7 8 9

    // 末尾添加新数据
    a.push_back(11);
    for (int i = 0; i &lt; a.size(); i++) {
        cout &lt;&lt; a[i] &lt;&lt; " ";
    } // 0 1 2 3 4 5 6 7 8 9 11

    // 迭代器
    for (auto p = a.begin(); p != a.end(); p++) {
        cout &lt;&lt; *p &lt;&lt; " ";
    } // 0 1 2 3 4 5 6 7 8 9 11

    return 0;
}
</code></pre>
<p><strong>常用方法</strong>：</p>
<table>
<thead>
<tr>
<th>方法</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>v.push_back(x)</code></td>
<td>尾部插入</td>
</tr>
<tr>
<td><code>v.pop_back()</code></td>
<td>尾部删除</td>
</tr>
<tr>
<td><code>v.size()</code> / <code>v.empty()</code></td>
<td>长度 / 是否为空</td>
</tr>
<tr>
<td><code>v.resize(n)</code></td>
<td>调整大小</td>
</tr>
<tr>
<td><code>v.clear()</code></td>
<td>清空</td>
</tr>
<tr>
<td><code>v.front()</code> / <code>v.back()</code></td>
<td>访问首尾元素</td>
</tr>
<tr>
<td><code>v.begin()</code> / <code>v.end()</code></td>
<td>迭代器</td>
</tr>
</tbody>
</table>
<h2>set</h2>
<blockquote>
<p>&lt;span style="color:blue"&gt;<code>set</code>&lt;/span&gt; 是<strong>集合</strong>，元素<strong>互异</strong>且<strong>自动从小到大排序</strong>。底层是红黑树，插入/查找/删除都是 O(log n)。</p>
</blockquote>
<pre><code>#include &lt;iostream&gt;
#include &lt;set&gt; // 头文件
using namespace std;

int main() {
    // 创建集合
    set&lt;int&gt; s; // 和 vector 类似，因为集合元素互异，所以不能通过参数初始化

    // 插入
    s.insert(1);
    s.insert(2);
    s.insert(3);

    // 遍历——迭代器
    for (auto p = s.begin(); p != s.end(); p++) {
        cout &lt;&lt; *p &lt;&lt; " ";
    } // 1 2 3

    // 查找 s.find(data)，寻找 data 的地址，返回值是一个指针
    cout &lt;&lt; (s.find(2) != s.end()) &lt;&lt; endl; // 1 表示地址不同，已经找到
    cout &lt;&lt; (s.find(4) != s.end()) &lt;&lt; endl; // 0 地址指向数组后一位空间，没找到

    // 删除
    s.erase(1); // 删除元素 1
    cout &lt;&lt; (s.find(1) != s.end()) &lt;&lt; endl; // 0 未找到，1 已删除

    // 获取长度
    cout &lt;&lt; s.size() &lt;&lt; endl;
    return 0;
}
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意&lt;/span&gt;：<code>s.erase(1)</code> 中的 <code>1</code> 是<strong>值</strong>（会先查找再删除）；如果传迭代器 <code>it</code>，则删除<strong>该位置</strong>元素。</p>
</blockquote>
<h2>map（键值对）</h2>
<blockquote>
<p>&lt;span style="color:blue"&gt;<code>map</code>&lt;/span&gt; 是<strong>键值对集合</strong>，按<strong>键</strong>自动从小到大排序（<code>multimap</code> 允许键重复）。</p>
</blockquote>
<pre><code>#include &lt;iostream&gt;
#include &lt;map&gt; // 头文件
using namespace std;

int main() {
    // 创建键值对
    map&lt;string, int&gt; m;

    // 添加
    m["hello"] = 2;
    m["world"] = 3;

    cout &lt;&lt; "hello : " &lt;&lt; m["hello"] &lt;&lt; endl; // hello : 2

    // 迭代器
    for (auto p = m.begin(); p != m.end(); p++) {
        cout &lt;&lt; p-&gt;first &lt;&lt; ":" &lt;&lt; p-&gt;second &lt;&lt; " ";
    }
    /*
        hello:2 world:3
    */

    // 获取长度
    cout &lt;&lt; m.size() &lt;&lt; endl;

    return 0;
}
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意&lt;/span&gt;：<code>m["key"]</code> 即使 <code>key</code> 不存在也会<strong>插入默认值</strong>，可能掩盖逻辑错误。<strong>判断存在性</strong>用 <code>m.find(key) != m.end()</code> 或 <code>m.count(key) &gt; 0</code>。</p>
</blockquote>
<h2>pair（键值对简述）</h2>
<blockquote>
<p><code>pair&lt;T1, T2&gt;</code> 把两个值绑在一起，常用于 <code>map</code> 的迭代器（<code>p-&gt;first</code> 是键，<code>p-&gt;second</code> 是值）。</p>
</blockquote>
<pre><code>pair&lt;string, int&gt; p = {"age", 18};
cout &lt;&lt; p.first &lt;&lt; "=" &lt;&lt; p.second; // age=18
</code></pre>
<h2>stack（栈）</h2>
<blockquote>
<p>&lt;span style="color:blue"&gt;<code>stack</code>&lt;/span&gt; 是<strong>后进先出</strong>（LIFO）的容器适配器，底层默认用 <code>deque</code> 实现。<strong>只允许访问栈顶</strong>。</p>
</blockquote>
<pre><code>#include &lt;iostream&gt;
#include &lt;stack&gt;
using namespace std;

int main() {
    stack&lt;int&gt; s;

    // 压栈
    s.push(1);
    s.push(2);
    s.push(3);

    // 访问栈顶
    cout &lt;&lt; s.top() &lt;&lt; endl; // 3

    // 出栈
    s.pop();
    cout &lt;&lt; s.top() &lt;&lt; endl; // 2

    // 获取长度
    cout &lt;&lt; s.size() &lt;&lt; endl; // 2

    // 是否为空
    cout &lt;&lt; s.empty() &lt;&lt; endl; // 0 (false)

    return 0;
}
</code></pre>
<p><strong>常用方法</strong>：<code>push</code>（压栈）→ <code>top</code>（访问栈顶）→ <code>pop</code>（出栈）→ <code>size</code> / <code>empty</code>。</p>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意&lt;/span&gt;：<code>pop</code> 只删元素不返回值，必须先用 <code>top()</code> 拿到值再 <code>pop()</code>。</p>
</blockquote>
<h2>queue（队列）</h2>
<blockquote>
<p>&lt;span style="color:blue"&gt;<code>queue</code>&lt;/span&gt; 是<strong>先进先出</strong>（FIFO）的容器适配器。BFS、层次遍历、滑动窗口常用。</p>
</blockquote>
<pre><code>#include &lt;iostream&gt;
#include &lt;queue&gt;
using namespace std;

int main() {
    queue&lt;int&gt; q;

    // 入队
    q.push(1);
    q.push(2);
    q.push(3);

    // 访问队首
    cout &lt;&lt; q.front() &lt;&lt; endl; // 1

    // 访问队尾
    cout &lt;&lt; q.back() &lt;&lt; endl; // 3

    // 出队
    q.pop();
    cout &lt;&lt; q.front() &lt;&lt; endl; // 2

    // 获取长度
    cout &lt;&lt; q.size() &lt;&lt; endl; // 2

    return 0;
}
</code></pre>
<p><strong>常用方法</strong>：<code>push</code>（入队）→ <code>front</code>（队首） / <code>back</code>（队尾）→ <code>pop</code>（出队）→ <code>size</code> / <code>empty</code>。</p>
<h2>deque（双端队列）</h2>
<blockquote>
<p>头尾都能 O(1) 插入/删除，<strong>支持随机访问</strong>（<code>dq[i]</code>）。<code>queue</code> 和 <code>stack</code> 的功能它都覆盖，但常数略大。</p>
</blockquote>
<pre><code>#include &lt;iostream&gt;
#include &lt;deque&gt;
using namespace std;

int main() {
    deque&lt;int&gt; dq = {1, 2, 3};
    dq.push_front(0); // 头部插入
    dq.push_back(4);  // 尾部插入
    cout &lt;&lt; dq[1];    // 1（随机访问）
    return 0;
}
</code></pre>
<h2>unordered_map 和 unordered_set</h2>
<blockquote>
<p>这两个就是<strong>不会排序</strong>的 <code>map</code>（键值对）和 <code>set</code>（集合），底层是<strong>哈希表</strong>。如果刷题时 <code>map</code> / <code>set</code> 超时，换成它们通常能快一档。</p>
</blockquote>
<pre><code>#include &lt;iostream&gt;
#include &lt;unordered_map&gt;
using namespace std;

int main() {
    unordered_map&lt;string, int&gt; um;
    um["a"] = 1;
    um["b"] = 2;
    cout &lt;&lt; um["a"]; // 1
    return 0;
}
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意&lt;/span&gt;：哈希表最坏复杂度 O(n)，且<strong>无序</strong>；如需有序遍历、有序集合，请用 <code>map</code> / <code>set</code>。</p>
</blockquote>
<h2>迭代器分类</h2>
<table>
<thead>
<tr>
<th>类别</th>
<th>支持的操作</th>
<th>典型容器</th>
</tr>
</thead>
<tbody>
<tr>
<td>输入迭代器</td>
<td>只读、<code>++</code></td>
<td><code>istream_iterator</code></td>
</tr>
<tr>
<td>前向迭代器</td>
<td>读写、<code>++</code></td>
<td><code>forward_list</code></td>
</tr>
<tr>
<td>双向迭代器</td>
<td>读写、<code>++</code> / <code>--</code></td>
<td><code>list</code> / <code>set</code> / <code>map</code></td>
</tr>
<tr>
<td>随机访问迭代器</td>
<td>读写、<code>++</code> / <code>--</code> / <code>+n</code> / <code>-n</code> / <code>[]</code></td>
<td><code>vector</code> / <code>deque</code></td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意&lt;/span&gt;：STL 算法（<code>sort</code>、<code>binary_search</code> 等）依赖迭代器类别，<strong><code>sort</code> 不能用于 <code>list</code> / <code>set</code></strong>，这些容器自带排序或 <code>list::sort()</code>。</p>
</blockquote>
<h1>三、进阶篇</h1>
<blockquote>
<p>本节收录 <code>bitset</code>、<code>sort</code>、二分查找、字符处理等刷题常用工具。</p>
</blockquote>
<h2>位运算 bitset</h2>
<blockquote>
<p>&lt;span style="color:blue"&gt;<code>bitset</code>&lt;/span&gt; 类似一个<strong>固定长度的二进制位数组</strong>。位数在编译期必须确定（下标 <code>b[0]</code> 是<strong>低位</strong>，<code>b[size-1]</code> 是<strong>高位</strong>），所以<strong>直接输出整个 <code>bitset</code> 的方向与 <code>b[i]</code> 单个访问方向相反</strong>。</p>
</blockquote>
<pre><code>#include &lt;iostream&gt;
#include &lt;bitset&gt;
using namespace std;

int main() {
    // 初始化
    bitset&lt;5&gt; b;             // 5 表示 5 个二进制位，初始化为 0，00000
    bitset&lt;5&gt; c(3);          // 十进制转二进制，输出 c 的结果为 00011
    bitset&lt;5&gt; d("101");      // 填入字符串，长度不够高位补 0，输出 d 的结果为 00101
    string s = "01010011";
    bitset&lt;5&gt; e(s, 1, 5);    // 从字符串的索引 1 开始取 5 个字符 10100，输出 e 的结果为 10100
    bitset&lt;6&gt; f("10011", 1); // 从字符串的索引 0 开始取 1 个字符高位补零，输出 f 的结果为 000001

    cout &lt;&lt; b &lt;&lt; endl; // 00000
    cout &lt;&lt; c &lt;&lt; endl; // 00011
    cout &lt;&lt; d &lt;&lt; endl; // 00101
    cout &lt;&lt; e &lt;&lt; endl; // 10100
    cout &lt;&lt; f &lt;&lt; endl; // 00011

    // 遍历
    for (int i = 0; i &lt; d.size(); i++) {
        cout &lt;&lt; d[i] &lt;&lt; " ";
    }

    // 处理
    cout &lt;&lt; "是否有 1：" &lt;&lt; d.any() &lt;&lt; endl;
    cout &lt;&lt; "是否不存在 1：" &lt;&lt; d.none() &lt;&lt; endl;
    cout &lt;&lt; "1 的个数：" &lt;&lt; d.count() &lt;&lt; endl;
    cout &lt;&lt; "d 中元素个数：" &lt;&lt; d.size() &lt;&lt; endl;
    cout &lt;&lt; "下标为 i 的元素是不是 1：" &lt;&lt; d.test(1) &lt;&lt; endl;

    d.set(3); // 把下标为 i 处设置为 1

    // 归零
    b.reset();   // 所有位归零
    d.reset(2);  // 第 i 位归零

    // 取反
    d.flip();   // 所有位取反
    d.flip(2);  // 第 i 位取反

    // 转换类型
    cout &lt;&lt; "转换为 unsigned long 类型：" &lt;&lt; d.to_ulong() &lt;&lt; endl;
    cout &lt;&lt; "转换为 string 类型：" &lt;&lt; d.to_string() &lt;&lt; endl;

    return 0;
}
</code></pre>
<p><strong>常用方法</strong>：</p>
<table>
<thead>
<tr>
<th>方法</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>b.any()</code> / <code>b.none()</code></td>
<td>是否存在 / 不存在 1</td>
</tr>
<tr>
<td><code>b.count()</code></td>
<td>1 的个数</td>
</tr>
<tr>
<td><code>b.set(i)</code> / <code>b.reset(i)</code></td>
<td>置 1 / 置 0</td>
</tr>
<tr>
<td><code>b.flip()</code></td>
<td>按位取反</td>
</tr>
<tr>
<td><code>b.test(i)</code></td>
<td>第 i 位是否为 1</td>
</tr>
<tr>
<td><code>b.to_ulong()</code> / <code>b.to_string()</code></td>
<td>转 <code>unsigned long</code> / <code>string</code></td>
</tr>
</tbody>
</table>
<h2>sort 函数</h2>
<blockquote>
<p>&lt;span style="color:blue"&gt;<code>sort</code>&lt;/span&gt; 对区间 <code>[first, last)</code> 进行排序，<strong>默认升序</strong>。<code>vector</code> 容器用 <code>v.begin()</code> 表示头、<code>v.end()</code> 表示尾；原生数组 <code>int arr[]</code> 用 <code>arr</code> 表示首地址、<code>arr + n</code> 表示尾。</p>
</blockquote>
<pre><code>#include &lt;iostream&gt;
#include &lt;algorithm&gt;
#include &lt;vector&gt;
using namespace std;
int main() {
    vector&lt;int&gt; m(10);
    for (int i = 0; i &lt; m.size(); i++) {
        m[i] = i;
    }

    m.push_back(-1);
    // 排序
    sort(m.begin(), m.end());

    for (int i = 0; i &lt; m.size(); i++)
        cout &lt;&lt; m[i] &lt;&lt; " "; // -1 0 1 2 3 4 5 6 7 8 9
    return 0;
}
</code></pre>
<h3>使用 sort 自定义 cmp 函数</h3>
<blockquote>
<p><code>cmp(x, y)</code> 返回 <code>true</code> 表示 <code>x</code> 排在 <code>y</code> <strong>前面</strong>。<strong>返回值必须用严格 <code>&gt;</code> 或 <code>&lt;</code>，不能用 <code>&gt;=</code> 或 <code>&lt;=</code></strong>（否则相等情况会导致排序不稳定 / 未定义行为）。</p>
</blockquote>
<pre><code>#include &lt;iostream&gt;
#include &lt;algorithm&gt;
#include &lt;vector&gt;
using namespace std;

bool cmp(int x, int y) {
    return x &gt; y; // 降序
}
int main() {
    vector&lt;int&gt; v;
    for (int i = 1; i &lt;= 10; i++) {
        v.push_back(11 - i); // 10, 9, 8, ..., 1
    }

    for (auto p = v.begin(); p != v.end(); p++)
        cout &lt;&lt; *p &lt;&lt; " ";

    cout &lt;&lt; endl;

    // 排序
    sort(v.begin(), v.end(), cmp); // 原理：返回值为真，x 放 y 前面；为假，x 放 y 后面

    for (auto p = v.begin(); p != v.end(); p++)
        cout &lt;&lt; *p &lt;&lt; " "; // 10 9 8 7 6 5 4 3 2 1
    return 0;
}
</code></pre>
<p><strong>结构体多关键字排序</strong>：</p>
<pre><code>#include &lt;iostream&gt;
#include &lt;algorithm&gt;
#include &lt;vector&gt;
using namespace std;

struct stu {
    string name;
    int age;
};

bool cmp(stu a, stu b) {
    if (a.age != b.age) {
        return a.age &lt; b.age; // 年龄小的在前
    } else {
        return a.name &lt; b.name; // 同龄按名字字典序
    }
}

int main() {
    stu s[3];
    for (int i = 0; i &lt; 3; i++) {
        cin &gt;&gt; s[i].name &gt;&gt; s[i].age;
    }
    sort(s, s + 3, cmp);
    for (int i = 0; i &lt; 3; i++) {
        cout &lt;&lt; s[i].name &lt;&lt; " " &lt;&lt; s[i].age &lt;&lt; endl;
    }
    return 0;
}
</code></pre>
<h3>stable_sort / partial_sort</h3>
<table>
<thead>
<tr>
<th>函数</th>
<th>特点</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>sort</code></td>
<td>不保证相等元素原顺序（不稳定）</td>
</tr>
<tr>
<td><code>stable_sort</code></td>
<td>稳定排序（相等元素保持原顺序），略慢</td>
</tr>
<tr>
<td><code>partial_sort(beg, mid, end)</code></td>
<td>只排序到 <code>mid</code> 位置（取前 k 小），O(n log k)</td>
</tr>
</tbody>
</table>
<h2>二分查找 lower_bound / upper_bound</h2>
<blockquote>
<p>前提：区间<strong>已排序</strong>。返回值是<strong>迭代器</strong>（或指针），不是下标。</p>
</blockquote>
<table>
<thead>
<tr>
<th>函数</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>lower_bound(beg, end, val)</code></td>
<td>第一个 <code>&gt;= val</code> 的位置</td>
</tr>
<tr>
<td><code>upper_bound(beg, end, val)</code></td>
<td>第一个 <code>&gt; val</code> 的位置</td>
</tr>
<tr>
<td><code>binary_search(beg, end, val)</code></td>
<td>是否存在 <code>val</code>（返回 <code>bool</code>）</td>
</tr>
</tbody>
</table>
<pre><code>#include &lt;algorithm&gt;
vector&lt;int&gt; v = {1, 2, 2, 3, 4};
auto it = lower_bound(v.begin(), v.end(), 2); // 指向第一个 2
auto it2 = upper_bound(v.begin(), v.end(), 2); // 指向 3
</code></pre>
<h2>cctype 头文件的函数</h2>
<blockquote>
<p>用于<strong>单个字符</strong>的判断与转换，常配合 <code>string</code> 遍历使用。</p>
</blockquote>
<table>
<thead>
<tr>
<th>函数</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>isalpha(c)</code></td>
<td>是否字母</td>
</tr>
<tr>
<td><code>islower(c)</code> / <code>isupper(c)</code></td>
<td>是否小写 / 大写字母</td>
</tr>
<tr>
<td><code>isalnum(c)</code></td>
<td>是否字母或数字</td>
</tr>
<tr>
<td><code>isspace(c)</code></td>
<td>是否空白（<code> </code> / <code>\t</code> / <code>\r</code> / <code>\n</code>）</td>
</tr>
<tr>
<td><code>isdigit(c)</code></td>
<td>是否数字</td>
</tr>
<tr>
<td><code>tolower(c)</code></td>
<td>转换为小写字母</td>
</tr>
<tr>
<td><code>toupper(c)</code></td>
<td>转换为大写字母</td>
</tr>
</tbody>
</table>
<h1>四、C++11 篇</h1>
<blockquote>
<p>本节收录 C++11 起加入的现代语法：<code>auto</code>、范围 for、<code>to_string</code>、<code>stoi</code> / <code>stod</code>、lambda、智能指针、<code>nullptr</code>。<code>auto</code>、<code>to_string</code>、<code>stoi</code>、<code>stof</code>、<code>unordered_map</code>、<code>unordered_set</code> 都是 C++11 的标志性特性。</p>
</blockquote>
<h2>auto 声明</h2>
<blockquote>
<p>&lt;span style="color:blue"&gt;<code>auto</code>&lt;/span&gt; 让编译器<strong>根据初始值推断变量类型</strong>，代码更简洁，尤其配合<strong>长类型名</strong>（如迭代器）。</p>
</blockquote>
<p><strong>基础用法</strong>：</p>
<pre><code>auto x = 100;     // int
auto y = 1.5;     // double
auto s = "hello"; // const char*
</code></pre>
<p><strong>迭代器场景</strong>（C++11 之前要写很长）：</p>
<pre><code>vector&lt;int&gt; v = {1, 2, 3};
// 之前：for (vector&lt;int&gt;::iterator it = v.begin(); it != v.end(); ++it)
for (auto it = v.begin(); it != v.end(); ++it) cout &lt;&lt; *it;
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意&lt;/span&gt;：<code>auto</code> 会<strong>丢掉顶层 <code>const</code> 和引用</strong>。如果想保留，用 <code>const auto&amp;</code>（遍历时避免拷贝）。</p>
</blockquote>
<h2>基于范围的 for 循环</h2>
<blockquote>
<p>直接遍历容器或数组的每个元素，<strong>不需要下标</strong>。</p>
</blockquote>
<pre><code>#include &lt;iostream&gt;
#include &lt;vector&gt;
using namespace std;

int main() {
    int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    vector&lt;int&gt; v = {1, 2, 3, 4, 5};

    // 输出数组中的每一个元素的值（不能改变元素的数值）
    for (int i : a) cout &lt;&lt; i &lt;&lt; " ";

    cout &lt;&lt; endl;

    // 将数组中每一个元素都乘以 2，只有在引用的时候才能改变元素的值
    for (int&amp; i : a)
        i *= 2;

    // 所有的容器，配合 auto，都可以使用这种方式来循环
    for (auto i : v) cout &lt;&lt; i &lt;&lt; " ";

    return 0;
}
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意&lt;/span&gt;：默认是<strong>拷贝</strong>遍历，原容器不变；要修改元素必须用 <code>auto&amp;</code> / <code>int&amp;</code> 引用。</p>
</blockquote>
<h2>to_string</h2>
<blockquote>
<p>将<strong>数字转换为字符串</strong>，常用于拼接输出。</p>
</blockquote>
<p><strong>头文件</strong>：<code>#include &lt;string&gt;</code></p>
<pre><code>#include &lt;iostream&gt;
#include &lt;string&gt;
using namespace std;

int main() {
    string s = to_string(123);
    string s2 = to_string(3.14);

    cout &lt;&lt; s &lt;&lt; endl;  // 123
    cout &lt;&lt; s2 &lt;&lt; endl; // 3.140000
    return 0;
}
</code></pre>
<h2>stoi 和 stod</h2>
<table>
<thead>
<tr>
<th>函数</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>stoi(s)</code></td>
<td>字符串 → <code>int</code></td>
</tr>
<tr>
<td><code>stol(s)</code> / <code>stoll(s)</code></td>
<td>字符串 → <code>long</code> / <code>long long</code></td>
</tr>
<tr>
<td><code>stoul(s)</code> / <code>stoull(s)</code></td>
<td>字符串 → <code>unsigned long</code> / <code>unsigned long long</code></td>
</tr>
<tr>
<td><code>stof(s)</code></td>
<td>字符串 → <code>float</code></td>
</tr>
<tr>
<td><code>stod(s)</code></td>
<td>字符串 → <code>double</code></td>
</tr>
<tr>
<td><code>stold(s)</code></td>
<td>字符串 → <code>long double</code></td>
</tr>
</tbody>
</table>
<pre><code>#include &lt;iostream&gt;
#include &lt;string&gt;
using namespace std;

int main() {
    int a = stoi("123");
    double b = stod("123.34");

    cout &lt;&lt; a - 1 &lt;&lt; endl;  // 122
    cout &lt;&lt; b - 1 &lt;&lt; endl;  // 122.34
    return 0;
}
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意&lt;/span&gt;：字符串非数字前缀会抛 <code>std::invalid_argument</code> 异常；数字越界抛 <code>std::out_of_range</code>。</p>
</blockquote>
<h2>lambda 表达式</h2>
<blockquote>
<p>&lt;span style="color:blue"&gt;lambda&lt;/span&gt; 是<strong>匿名函数</strong>，可内联写在调用处，常用于 <code>sort</code> / <code>for_each</code> 等需要传函数对象的场景。</p>
</blockquote>
<p><strong>语法</strong>：<code>[捕获列表](参数) -&gt; 返回值 { 函数体 }</code></p>
<pre><code>#include &lt;algorithm&gt;
#include &lt;vector&gt;
using namespace std;

int main() {
    vector&lt;int&gt; v = {3, 1, 4, 1, 5, 9, 2, 6};
    // 降序排序：lambda 替代独立 cmp 函数
    sort(v.begin(), v.end(), [](int a, int b) { return a &gt; b; });
    return 0;
}
</code></pre>
<p><strong>常用捕获</strong>：</p>
<table>
<thead>
<tr>
<th>写法</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>[]</code></td>
<td>不捕获任何变量</td>
</tr>
<tr>
<td><code>[=]</code></td>
<td>按值捕获所有外部变量</td>
</tr>
<tr>
<td><code>[&amp;]</code></td>
<td>按引用捕获所有外部变量</td>
</tr>
<tr>
<td><code>[x, &amp;y]</code></td>
<td><code>x</code> 按值、<code>y</code> 按引用</td>
</tr>
</tbody>
</table>
<h2>智能指针（简述）</h2>
<blockquote>
<p>&lt;span style="color:orange"&gt;补充知识&lt;/span&gt;：C++11 起标准库提供<strong>自动管理堆内存</strong>的智能指针，避免 <code>new</code> / <code>delete</code> 配对失误导致内存泄漏。</p>
</blockquote>
<table>
<thead>
<tr>
<th>智能指针</th>
<th>特点</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>unique_ptr&lt;T&gt;</code></td>
<td>独占所有权，不可复制，只能 <code>move</code></td>
</tr>
<tr>
<td><code>shared_ptr&lt;T&gt;</code></td>
<td>共享所有权，引用计数，最后一个销毁时释放</td>
</tr>
<tr>
<td><code>weak_ptr&lt;T&gt;</code></td>
<td>不增加引用计数，配合 <code>shared_ptr</code> 解决循环引用</td>
</tr>
</tbody>
</table>
<pre><code>#include &lt;memory&gt;
unique_ptr&lt;int&gt; p = make_unique&lt;int&gt;(10);
shared_ptr&lt;int&gt; sp = make_shared&lt;int&gt;(20);
</code></pre>
<h2>nullptr</h2>
<blockquote>
<p>&lt;span style="color:green"&gt;<code>nullptr</code>&lt;/span&gt; 是 C++11 引入的<strong>空指针常量</strong>，类型严格为 <code>std::nullptr_t</code>，<strong>替代 C 风格的 <code>NULL</code></strong>（<code>NULL</code> 实际是 <code>0</code>，会和整数重载冲突）。</p>
</blockquote>
<pre><code>int* p = nullptr;  // 推荐
int* q = NULL;     // 不推荐，NULL == 0
</code></pre>
<h1>知识小结</h1>
<table>
<thead>
<tr>
<th>要点</th>
<th>说明</th>
<th>重要度</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:blue"&gt;命名空间&lt;/span&gt;</td>
<td><code>using namespace std;</code> 仅刷题用，项目慎用</td>
<td>★★★</td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;cin/cout 提速&lt;/span&gt;</td>
<td><code>ios::sync_with_stdio(false); cin.tie(nullptr);</code></td>
<td>★★★★</td>
</tr>
<tr>
<td>头文件</td>
<td>C 风格去 <code>.h</code> 加 <code>c</code> 前缀</td>
<td>★★★</td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;bool&lt;/span&gt;</td>
<td>原生类型，非 0 即 <code>true</code></td>
<td>★★</td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;string&lt;/span&gt;</td>
<td>自动管理内存，含 <code>substr</code> / <code>find</code> / <code>length</code></td>
<td>★★★★</td>
</tr>
<tr>
<td>结构体</td>
<td>C++ 可省略 <code>struct</code> 关键字</td>
<td>★★</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;引用 <code>&amp;</code> vs 取地址 <code>&amp;</code>&lt;/span&gt;</td>
<td>类型后是引用，变量前是取地址</td>
<td>★★★★★</td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;vector&lt;/span&gt;</td>
<td>顺序容器首选，随机访问 O(1)</td>
<td>★★★★★</td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;set / map&lt;/span&gt;</td>
<td>有序容器，红黑树，O(log n)</td>
<td>★★★★★</td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;stack / queue&lt;/span&gt;</td>
<td>容器适配器，仅暴露受限接口</td>
<td>★★★★</td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;unordered_map / set&lt;/span&gt;</td>
<td>哈希表，平均 O(1)，最坏 O(n)</td>
<td>★★★★</td>
</tr>
<tr>
<td>迭代器</td>
<td><code>begin()</code> / <code>end()</code> 是<strong>半开区间</strong> <code>[begin, end)</code></td>
<td>★★★★</td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;bitset&lt;/span&gt;</td>
<td>编译期定长位数组，索引 0 是低位</td>
<td>★★★</td>
</tr>
<tr>
<td>&lt;span style="color:red"&gt;sort 的 cmp&lt;/span&gt;</td>
<td>必须用严格 <code>&gt;</code> / <code>&lt;</code>，不能用 <code>&gt;=</code> / <code>&lt;=</code></td>
<td>★★★★★</td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;auto&lt;/span&gt;</td>
<td>推断类型，丢掉 <code>const</code> / 引用</td>
<td>★★★★</td>
</tr>
<tr>
<td>范围 for</td>
<td><code>for (auto&amp; x : v)</code> 修改、<code>auto x</code> 拷贝</td>
<td>★★★★</td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;stoi / stod&lt;/span&gt;</td>
<td>字符串转数字，越界抛异常</td>
<td>★★★</td>
</tr>
<tr>
<td>lambda</td>
<td><code>[捕获](参数){...}</code>，常作 <code>sort</code> 第三参数</td>
<td>★★★</td>
</tr>
<tr>
<td>&lt;span style="color:green"&gt;nullptr&lt;/span&gt;</td>
<td>替代 <code>NULL</code>，类型安全</td>
<td>★★★</td>
</tr>
</tbody>
</table>
<h1>补充 整型的选择</h1>
<h3>🧮 C++ 整型速查表（刷题专用）</h3>
<table>
<thead>
<tr>
<th>类型</th>
<th>字节</th>
<th>二进制范围</th>
<th>十进制约等于</th>
<th>适用情况</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>int</code></td>
<td>4</td>
<td>$-2^{31}$ ~ $2^{31}-1$</td>
<td>≈ ±2.147×10⁹</td>
<td>单值 ≤ 2×10⁹</td>
</tr>
<tr>
<td><code>unsigned int</code></td>
<td>4</td>
<td>$0$ ~ $2^{32}-1$</td>
<td>≈ 0 ~ 4.29×10⁹</td>
<td>只用正数且 ≤ 4e9</td>
</tr>
<tr>
<td><code>long long</code></td>
<td>8</td>
<td>$-2^{63}$ ~ $2^{63}-1$</td>
<td>≈ ±9.22×10¹⁸</td>
<td>单值 ≤ 9×10¹⁸</td>
</tr>
<tr>
<td><code>unsigned long long</code></td>
<td>8</td>
<td>$0$ ~ $2^{64}-1$</td>
<td>≈ 1.84×10¹⁹</td>
<td>超大正数（极少用）</td>
</tr>
</tbody>
</table>
<hr />
<h3>🔑 常见次方对照</h3>
<table>
<thead>
<tr>
<th>幂次</th>
<th>十进制近似</th>
<th>对应类型上限</th>
</tr>
</thead>
<tbody>
<tr>
<td>$10^9$</td>
<td>1,000,000,000</td>
<td>&lt; int max</td>
</tr>
<tr>
<td>$10^{18}$</td>
<td>1,000,000,000,000,000,000</td>
<td>&lt; long long max</td>
</tr>
<tr>
<td>$2^{31}$</td>
<td>2,147,483,648</td>
<td>比 int max 大 1 → 用 long long</td>
</tr>
<tr>
<td>$2^{31}-1$</td>
<td>2,147,483,647</td>
<td>int 最大值</td>
</tr>
<tr>
<td>$2^{63}$</td>
<td>9,223,372,036,854,775,808</td>
<td>比 long long max 大 1 → 用 unsigned long long</td>
</tr>
<tr>
<td>$2^{63}-1$</td>
<td>9,223,372,036,854,775,807</td>
<td>long long 最大值</td>
</tr>
</tbody>
</table>
<hr />
<h3>⚠️ 避坑规则（必看）</h3>
<ol>
<li><strong>单值</strong>：看到 $10^9$ → <code>int</code>；看到 $10^{18}$ → <code>long long</code>。</li>
<li><strong>求和</strong>：若数组长度 × 单值上限 &gt; 当前类型范围，总和变量要升一级。</li>
<li><strong>乘法中间值</strong>：<code>int a=1e6, b=1e6; long long c = 1LL * a * b;</code> —— 乘 <code>1LL</code> 强制提升。</li>
<li><strong>不确定时</strong>：一律用 <code>long long</code>（除非二维数组过大，内存不够才精细算）。</li>
<li><strong>不要用 <code>long</code></strong>：不同平台长度不一（Windows 4 字节，Linux 8 字节），刷题统一用 <code>int</code> 和 <code>long long</code>。</li>
</ol>
]]></content>
    <author><name>仙鹤</name></author>
    <category term="编程"/>
  </entry>
  <entry>
    <title>C++ 常用头文件</title>
    <link href="https://xianhe51920.github.io/posts/cpp-headers/" rel="alternate" type="text/html"/>
    <id>https://xianhe51920.github.io/posts/cpp-headers/</id>
    <published>2026-09-03T00:00:00.000Z</published>
    <updated>2026-09-03T00:00:00.000Z</updated>
    <summary>C++ 常用头文件知识</summary>
    <content type="html"><![CDATA[<h1>C++常用头文件</h1>
<blockquote>
<p>本节按头文件分组整理算法竞赛常用 API：<code>cmath</code> 负责数学函数，<code>algorithm</code> 负责通用算法（排序、查找、堆），<code>numeric</code> 负责数值算法（累加、GCD、扫描）。三者搭配 STL 容器，覆盖刷题 90% 的场景。</p>
</blockquote>
<h2>cmath</h2>
<blockquote>
<p><code>&lt;cmath&gt;</code> 是 C 标准数学库的 C++ 包装，所有函数都在 <code>std</code> 命名空间下，<strong>参数和返回值</strong>多为 <code>double</code>。刷题时最常用的是<strong>取整、幂运算、浮点处理</strong>三类。</p>
</blockquote>
<h3>常用函数速览</h3>
<table>
<thead>
<tr>
<th>函数</th>
<th>功能</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>abs(x)</code></td>
<td>整数绝对值</td>
<td>实际定义在 <code>&lt;cstdlib&gt;</code>，C++11 起 <code>&lt;cmath&gt;</code> 也支持</td>
</tr>
<tr>
<td><code>fabs(x)</code></td>
<td>浮点绝对值</td>
<td>等价于 <code>abs(double)</code></td>
</tr>
<tr>
<td><code>sqrt(x)</code></td>
<td>平方根</td>
<td>比 <code>pow(x, 0.5)</code> 更快、更准</td>
</tr>
<tr>
<td><code>cbrt(x)</code></td>
<td>立方根</td>
<td>C++11 起</td>
</tr>
<tr>
<td><code>pow(x, y)</code></td>
<td><code>x^y</code></td>
<td>整数次方<strong>不要</strong>用它，会引入浮点误差</td>
</tr>
<tr>
<td><code>exp(x)</code></td>
<td><code>e^x</code></td>
<td></td>
</tr>
<tr>
<td><code>log(x)</code></td>
<td>自然对数</td>
<td>底为 <code>e</code></td>
</tr>
<tr>
<td><code>log10(x)</code></td>
<td>常用对数</td>
<td>底为 10</td>
</tr>
<tr>
<td><code>log2(x)</code></td>
<td>底为 2 的对数</td>
<td>C++11 起</td>
</tr>
<tr>
<td><code>hypot(x, y)</code></td>
<td><code>sqrt(x² + y²)</code></td>
<td><strong>防数值溢出</strong>专用，见下</td>
</tr>
<tr>
<td><code>max(a, b)</code> / <code>min(a, b)</code></td>
<td>两值最大/最小</td>
<td>也可处理 <code>initializer_list</code>（C++11）</td>
</tr>
<tr>
<td><code>fmax(a, b)</code> / <code>fmin(a, b)</code></td>
<td>浮点版 max/min</td>
<td>处理 NaN 行为不同</td>
</tr>
</tbody>
</table>
<h3>三角函数</h3>
<table>
<thead>
<tr>
<th>函数</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>sin</code> / <code>cos</code> / <code>tan</code></td>
<td>正弦 / 余弦 / 正切（弧度）</td>
</tr>
<tr>
<td><code>asin</code> / <code>acos</code> / <code>atan</code></td>
<td>反正弦 / 反余弦 / 反正切</td>
</tr>
<tr>
<td><code>sinh</code> / <code>cosh</code> / <code>tanh</code></td>
<td>双曲正弦 / 余弦 / 正切</td>
</tr>
<tr>
<td><code>atan2(y, x)</code></td>
<td>二参数反正切，<strong>能区分象限</strong>（比 <code>atan(y/x)</code> 安全）</td>
</tr>
</tbody>
</table>
<p><strong>常用常量</strong>：</p>
<table>
<thead>
<tr>
<th>常量</th>
<th>值</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>M_PI</code></td>
<td>圆周率 π</td>
</tr>
<tr>
<td><code>M_E</code></td>
<td>自然常数 e</td>
</tr>
<tr>
<td><code>M_SQRT2</code></td>
<td>√2</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意&lt;/span&gt;：<code>M_PI</code> 不是 C++ 标准的一部分，GCC/Clang 默认开启，MSVC 需定义 <code>_USE_MATH_DEFINES</code> 后 include <code>&lt;cmath&gt;</code>。严谨做法是<strong>自己定义</strong>：<code>const double PI = acos(-1.0);</code></p>
</blockquote>
<h3>取整函数</h3>
<table>
<thead>
<tr>
<th>函数</th>
<th>功能</th>
<th>示例（<code>x = 2.7</code>）</th>
<th>示例（<code>x = -2.7</code>）</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>ceil(x)</code></td>
<td>向上取整</td>
<td><code>3</code></td>
<td><code>-2</code></td>
</tr>
<tr>
<td><code>floor(x)</code></td>
<td>向下取整</td>
<td><code>2</code></td>
<td><code>-3</code></td>
</tr>
<tr>
<td><code>round(x)</code></td>
<td>四舍五入（远离 0）</td>
<td><code>3</code></td>
<td><code>-3</code></td>
</tr>
<tr>
<td><code>trunc(x)</code></td>
<td>截断小数部分</td>
<td><code>2</code></td>
<td><code>-2</code></td>
</tr>
<tr>
<td><code>fmod(x, y)</code></td>
<td>浮点取模（结果符号同 <code>x</code>）</td>
<td>—</td>
<td>—</td>
</tr>
</tbody>
</table>
<pre><code>#include &lt;cmath&gt;
#include &lt;iostream&gt;
using namespace std;

int main() {
    cout &lt;&lt; ceil(2.7) &lt;&lt; " " &lt;&lt; floor(2.7) &lt;&lt; " " &lt;&lt; round(2.7) &lt;&lt; endl;       // 3 2 3
    cout &lt;&lt; ceil(-2.7) &lt;&lt; " " &lt;&lt; floor(-2.7) &lt;&lt; " " &lt;&lt; round(-2.7) &lt;&lt; endl;     // -2 -3 -3
    return 0;
}
</code></pre>
<blockquote>
<p>&lt;span style="color:red"&gt;⚠ 警告&lt;/span&gt;：<strong>负数取整的方向常与直觉相反</strong>，<code>floor(-2.7) = -3</code>（向更小的整数），刷题务必测试。</p>
</blockquote>
<h3>幂运算与整数次方</h3>
<blockquote>
<p>&lt;span style="color:red"&gt;⚠ 警告&lt;/span&gt;：<code>pow(2, 10)</code> 看似能算 <code>2^10</code>，但<strong>内部用浮点实现</strong>，结果可能因精度损失而不准（如 <code>pow(10, 2) = 99.9999...</code>）。<strong>整数次方</strong>用快速幂（手写）或 <code>1LL &lt;&lt; n</code>（仅限 2 的幂）。</p>
</blockquote>
<pre><code>// 整数快速幂：a^n mod mod（比赛最常用）
long long qpow(long long a, long long n, long long mod) {
    long long res = 1;
    a %= mod;
    while (n &gt; 0) {
        if (n &amp; 1) res = res * a % mod;
        a = a * a % mod;
        n &gt;&gt;= 1;
    }
    return res;
}
</code></pre>
<h3>浮点处理技巧</h3>
<p><strong>1. 浮点比较</strong>：<code>==</code> 比较浮点几乎一定出错，必须用容差：</p>
<pre><code>#include &lt;cmath&gt;
const double EPS = 1e-9;
bool eq(double a, double b) { return fabs(a - b) &lt; EPS; }
bool lt(double a, double b) { return a &lt; b - EPS; }
bool le(double a, double b) { return a &lt; b + EPS; }
</code></pre>
<p><strong>2. 防止 <code>sqrt(x² + y²)</code> 溢出</strong>：</p>
<pre><code>// 错误：x、y 很大时 x*x 先溢出
double len = sqrt(x * x + y * y);

// 正确：hypot 内部做缩放
double len = hypot(x, y);
</code></pre>
<p><strong>3. INF / NaN</strong>：</p>
<pre><code>double inf = numeric_limits&lt;double&gt;::infinity();
double nan = numeric_limits&lt;double&gt;::quiet_NaN();
</code></pre>
<h3>关键注意点</h3>
<table>
<thead>
<tr>
<th>易错点</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;整数次方用 <code>pow</code>&lt;/span&gt;</td>
<td>浮点精度坑，改用快速幂</td>
</tr>
<tr>
<td>三角函数单位是<strong>弧度</strong></td>
<td>转角度：<code>rad * PI / 180</code></td>
</tr>
<tr>
<td>负数 <code>floor</code> / <code>ceil</code></td>
<td>方向与直觉相反</td>
</tr>
<tr>
<td><code>M_PI</code></td>
<td>非标准常量，MSVC 需宏定义</td>
</tr>
<tr>
<td>浮点 <code>==</code></td>
<td>必带 EPS 容差</td>
</tr>
<tr>
<td><code>sqrt(x*x + y*y)</code></td>
<td>大数时改用 <code>hypot(x, y)</code></td>
</tr>
</tbody>
</table>
<h2>algorithm</h2>
<blockquote>
<p><code>&lt;algorithm&gt;</code> 是 STL <strong>算法</strong>层的核心头文件，提供排序、查找、变换、堆等通用算法。所有算法通过<strong>迭代器</strong>操作容器，与具体容器解耦。</p>
</blockquote>
<h3>取最值</h3>
<table>
<thead>
<tr>
<th>函数</th>
<th>范围</th>
<th>返回值</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>min(a, b)</code> / <code>max(a, b)</code></td>
<td>两个值</td>
<td>较小/较大的值</td>
</tr>
<tr>
<td><code>min({a, b, c})</code> / <code>max({...})</code></td>
<td>多个值（C++11 initializer_list）</td>
<td>同上</td>
</tr>
<tr>
<td><code>min_element(begin, end)</code></td>
<td>区间</td>
<td><strong>迭代器</strong>，需 <code>*</code> 解引用</td>
</tr>
<tr>
<td><code>max_element(begin, end)</code></td>
<td>区间</td>
<td><strong>迭代器</strong>，需 <code>*</code> 解引用</td>
</tr>
<tr>
<td><code>minmax(a, b)</code></td>
<td>两个值</td>
<td><code>pair&lt;较小值, 较大值&gt;</code></td>
</tr>
<tr>
<td><code>minmax_element(begin, end)</code></td>
<td>区间</td>
<td><code>pair&lt;最小迭代器, 最大迭代器&gt;</code></td>
</tr>
<tr>
<td><code>clamp(v, lo, hi)</code></td>
<td>单值（C++17）</td>
<td>钳制到 <code>[lo, hi]</code></td>
</tr>
</tbody>
</table>
<pre><code>#include &lt;algorithm&gt;
using namespace std;

vector&lt;int&gt; v = {3, 1, 4, 1, 5, 9, 2, 6};

// 容器里的最大值（迭代器要 * 解引用）
auto it = max_element(v.begin(), v.end());
cout &lt;&lt; *it;            // 9
cout &lt;&lt; it - v.begin(); // 4（下标）

// 三个数
cout &lt;&lt; max({1, 5, 3}); // 5

// 钳制
cout &lt;&lt; clamp(15, 0, 10); // 10
</code></pre>
<h3>排序</h3>
<table>
<thead>
<tr>
<th>函数</th>
<th>特点</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>sort(begin, end)</code></td>
<td>快速排序（实际 introsort），O(n log n)，<strong>不稳定</strong></td>
</tr>
<tr>
<td><code>stable_sort(begin, end)</code></td>
<td>稳定排序，相等元素保持原顺序</td>
</tr>
<tr>
<td><code>partial_sort(begin, mid, end)</code></td>
<td>排序到 <code>mid</code>，<strong>取前 k 小</strong> O(n log k)</td>
</tr>
<tr>
<td><code>nth_element(begin, nth, end)</code></td>
<td>第 <code>nth</code> 位置就位（<strong>前 k 小</strong>，不保证其余顺序）</td>
</tr>
<tr>
<td><code>is_sorted(begin, end)</code></td>
<td>是否已排序（O(n)）</td>
</tr>
</tbody>
</table>
<pre><code>vector&lt;int&gt; v = {3, 1, 4, 1, 5};
sort(v.begin(), v.end());                      // 1 1 3 4 5
partial_sort(v.begin(), v.begin() + 2, v.end());// 前 2 小排到头部
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意&lt;/span&gt;：<code>sort</code> 的 <code>cmp(x, y)</code> 返回 <code>true</code> 表示 <code>x</code> 排前；<strong>必须用严格 <code>&gt;</code> / <code>&lt;</code></strong>，不能 <code>&gt;=</code> / <code>&lt;=</code>。</p>
</blockquote>
<h3>查找</h3>
<table>
<thead>
<tr>
<th>函数</th>
<th>前提</th>
<th>返回值</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>find(begin, end, val)</code></td>
<td>无</td>
<td>第一个等于 <code>val</code> 的迭代器</td>
</tr>
<tr>
<td><code>find_if(begin, end, pred)</code></td>
<td>无</td>
<td>第一个满足 <code>pred</code> 的迭代器</td>
</tr>
<tr>
<td><code>count(begin, end, val)</code></td>
<td>无</td>
<td>等于 <code>val</code> 的元素<strong>个数</strong></td>
</tr>
<tr>
<td><code>count_if(begin, end, pred)</code></td>
<td>无</td>
<td>满足 <code>pred</code> 的元素<strong>个数</strong></td>
</tr>
<tr>
<td><code>binary_search(begin, end, val)</code></td>
<td><strong>已排序</strong></td>
<td><code>bool</code>：是否存在</td>
</tr>
<tr>
<td><code>lower_bound(begin, end, val)</code></td>
<td><strong>已排序</strong></td>
<td>第一个 <code>&gt;= val</code> 的迭代器</td>
</tr>
<tr>
<td><code>upper_bound(begin, end, val)</code></td>
<td><strong>已排序</strong></td>
<td>第一个 <code>&gt; val</code> 的迭代器</td>
</tr>
<tr>
<td><code>equal_range(begin, end, val)</code></td>
<td><strong>已排序</strong></td>
<td><code>[lower, upper)</code> 的 <code>pair</code></td>
</tr>
</tbody>
</table>
<pre><code>vector&lt;int&gt; v = {1, 2, 2, 3, 4};
auto lo = lower_bound(v.begin(), v.end(), 2); // 指向第一个 2
auto hi = upper_bound(v.begin(), v.end(), 2); // 指向 3
cout &lt;&lt; hi - lo; // 2（2 出现了 2 次）
</code></pre>
<h3>修改 / 变换</h3>
<table>
<thead>
<tr>
<th>函数</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>reverse(begin, end)</code></td>
<td>翻转</td>
</tr>
<tr>
<td><code>rotate(begin, mid, end)</code></td>
<td>旋转（<code>mid</code> 移到开头）</td>
</tr>
<tr>
<td><code>shuffle(begin, end, rng)</code></td>
<td>随机打乱（C++11，需 <code>&lt;random&gt;</code> 引擎）</td>
</tr>
<tr>
<td><code>next_permutation(begin, end)</code></td>
<td>下一个排列，到末尾返回 <code>false</code></td>
</tr>
<tr>
<td><code>prev_permutation(begin, end)</code></td>
<td>上一个排列</td>
</tr>
<tr>
<td><code>fill(begin, end, val)</code></td>
<td>填充</td>
</tr>
<tr>
<td><code>replace(begin, end, old, new)</code></td>
<td>替换</td>
</tr>
<tr>
<td><code>unique(begin, end)</code></td>
<td><strong>相邻</strong>去重（需先 <code>sort</code>）</td>
</tr>
<tr>
<td><code>swap(a, b)</code> / <code>iter_swap(it1, it2)</code></td>
<td>交换</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意&lt;/span&gt;：<code>unique</code> 只删<strong>相邻</strong>重复，所以通常先 <code>sort</code> 再 <code>unique</code>。返回值是新结尾迭代器：<code>v.erase(it, v.end())</code>。</p>
</blockquote>
<pre><code>vector&lt;int&gt; v = {1, 2, 2, 3, 2, 4};
sort(v.begin(), v.end());
v.erase(unique(v.begin(), v.end()), v.end()); // 1 2 3 4
</code></pre>
<h3>集合算法（已排序区间）</h3>
<blockquote>
<p>输入区间必须<strong>有序</strong>，结果区间可与输入重叠。</p>
</blockquote>
<table>
<thead>
<tr>
<th>函数</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>merge(a, b, out)</code></td>
<td>归并两个有序区间到 <code>out</code></td>
</tr>
<tr>
<td><code>set_union(a, b, out)</code></td>
<td>并集</td>
</tr>
<tr>
<td><code>set_intersection(a, b, out)</code></td>
<td>交集</td>
</tr>
<tr>
<td><code>set_difference(a, b, out)</code></td>
<td>差集（在 a 不在 b）</td>
</tr>
<tr>
<td><code>set_symmetric_difference(a, b, out)</code></td>
<td>对称差</td>
</tr>
<tr>
<td><code>includes(a, b)</code></td>
<td>b 是否是 a 的子集</td>
</tr>
</tbody>
</table>
<h3>堆（priority_queue 替代品）</h3>
<blockquote>
<p><code>priority_queue</code> 内部用堆，但只暴露受限接口。如需<strong>在任意位置</strong>操作堆，用 algorithm 提供的 4 个函数。</p>
</blockquote>
<table>
<thead>
<tr>
<th>函数</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>make_heap(begin, end)</code></td>
<td>把区间建成<strong>大根堆</strong>（默认 <code>less</code>，最大元素在 <code>begin</code>）</td>
</tr>
<tr>
<td><code>push_heap(begin, end)</code></td>
<td>假设 <code>end-1</code> 是新元素，<strong>上浮</strong>调整</td>
</tr>
<tr>
<td><code>pop_heap(begin, end)</code></td>
<td>把堆顶移到 <code>end-1</code>，<strong>下沉</strong>调整</td>
</tr>
<tr>
<td><code>sort_heap(begin, end)</code></td>
<td>堆排序（区间先 make_heap）</td>
</tr>
<tr>
<td><code>is_heap(begin, end)</code></td>
<td>是否是堆</td>
</tr>
</tbody>
</table>
<pre><code>vector&lt;int&gt; v = {3, 1, 4, 1, 5, 9, 2, 6};
make_heap(v.begin(), v.end()); // 大根堆：9 在 v[0]
pop_heap(v.begin(), v.end()); // 9 移到末尾
cout &lt;&lt; v.back();              // 9
v.pop_back();                  // 真正删除
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意&lt;/span&gt;：默认是<strong>大根堆</strong>（<code>less</code>），想要小根堆传 <code>greater&lt;int&gt;{}</code>：<code>make_heap(v.begin(), v.end(), greater&lt;int&gt;());</code></p>
</blockquote>
<h3>其他常用</h3>
<table>
<thead>
<tr>
<th>函数</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>for_each(begin, end, f)</code></td>
<td>对每个元素执行 <code>f</code>（C++17 起可返回）</td>
</tr>
<tr>
<td><code>transform(a, b, out, f)</code></td>
<td>把 <code>f</code> 应用到元素，写到 <code>out</code></td>
</tr>
<tr>
<td><code>any_of</code> / <code>all_of</code> / <code>none_of</code></td>
<td>谓词判断（任一/全部/无）</td>
</tr>
<tr>
<td><code>equal(a, b)</code> / <code>equal(a, b, pred)</code></td>
<td>两区间是否相等</td>
</tr>
<tr>
<td><code>lexicographical_compare</code></td>
<td>字典序比较</td>
</tr>
<tr>
<td><code>partition</code> / <code>stable_partition</code></td>
<td>按谓词分两段</td>
</tr>
</tbody>
</table>
<h2>numeric</h2>
<blockquote>
<p><code>&lt;numeric&gt;</code> 提供<strong>针对数值</strong>的算法：累加、内积、扫描、序列生成、最大公约数等。<code>accumulate</code> 是刷题出现频率最高的之一。</p>
</blockquote>
<h3>累加 / 归约</h3>
<table>
<thead>
<tr>
<th>函数</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>accumulate(begin, end, init)</code></td>
<td>累加 + 初始值（可自定义二元运算）</td>
</tr>
<tr>
<td><code>accumulate(begin, end, init, op)</code></td>
<td>自定义运算（如 <code>multiplies</code> 求积）</td>
</tr>
<tr>
<td><code>reduce(begin, end, init)</code></td>
<td>类似但<strong>无序</strong>（C++17，并行友好）</td>
</tr>
<tr>
<td><code>inner_product(a, b, init)</code></td>
<td>两区间内积 + 初始值</td>
</tr>
<tr>
<td><code>transform_reduce</code></td>
<td><code>transform</code> + <code>reduce</code> 组合（C++17）</td>
</tr>
</tbody>
</table>
<pre><code>#include &lt;numeric&gt;
vector&lt;int&gt; v = {1, 2, 3, 4, 5};

int sum = accumulate(v.begin(), v.end(), 0);          // 15
int prod = accumulate(v.begin(), v.end(), 1, multiplies&lt;int&gt;()); // 120
// 字符串拼接（init 给空串）
string s = accumulate(v.begin(), v.end(), string(""),
                      [](string a, int x) { return a + to_string(x); });
// "12345"
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意&lt;/span&gt;：<code>accumulate</code> 的初始值是<strong>累加的起点</strong>（不是数组里的元素），整数累加请写 <code>0LL</code> 避免溢出。</p>
</blockquote>
<h3>扫描（部分和 / 相邻差）</h3>
<table>
<thead>
<tr>
<th>函数</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>partial_sum(begin, end, out)</code></td>
<td>前缀和（含当前项）</td>
</tr>
<tr>
<td><code>inclusive_scan</code></td>
<td>C++17，等价 <code>partial_sum</code></td>
</tr>
<tr>
<td><code>exclusive_scan(begin, end, out, init)</code></td>
<td>前缀和（<strong>不含</strong>当前项，从 <code>init</code> 开始）</td>
</tr>
<tr>
<td><code>adjacent_difference(begin, end, out)</code></td>
<td>相邻元素差（首元素不变）</td>
</tr>
</tbody>
</table>
<pre><code>vector&lt;int&gt; v = {1, 2, 3, 4, 5};
vector&lt;int&gt; pre(5), diff(5);
partial_sum(v.begin(), v.end(), pre.begin());        // 1 3 6 10 15
adjacent_difference(v.begin(), v.end(), diff.begin());// 1 1 1 1 1
</code></pre>
<h3>序列生成</h3>
<table>
<thead>
<tr>
<th>函数</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>iota(begin, end, val)</code></td>
<td>从 <code>val</code> 开始递增填充：<code>v[i] = val + i</code></td>
</tr>
</tbody>
</table>
<pre><code>vector&lt;int&gt; v(5);
iota(v.begin(), v.end(), 10); // 10 11 12 13 14
</code></pre>
<h3>GCD / LCM（C++17）</h3>
<table>
<thead>
<tr>
<th>函数</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>gcd(a, b)</code></td>
<td>最大公约数（C++17）</td>
</tr>
<tr>
<td><code>lcm(a, b)</code></td>
<td>最小公倍数（C++17）</td>
</tr>
<tr>
<td><code>midpoint(a, b)</code></td>
<td>两数中点（避免 <code>(a+b)/2</code> 溢出，C++20）</td>
</tr>
</tbody>
</table>
<pre><code>cout &lt;&lt; gcd(12, 18); // 6
cout &lt;&lt; lcm(4, 6);   // 12

// 安全的 a+b/2
long long mid = midpoint(1LL, 1e18);
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意&lt;/span&gt;：C++17 之前的 <code>__gcd</code> 是 GCC 扩展；严谨代码<strong>自己写欧几里得</strong>：</p>
<pre><code>long long gcd(long long a, long long b) { return b ? gcd(b, a % b) : a; }
</code></pre>
</blockquote>
<h3>随机数（<code>&lt;random&gt;</code> 配合使用）</h3>
<blockquote>
<p><code>&lt;random&gt;</code> 虽不在 <code>&lt;numeric&gt;</code> 里，但常一起用。<strong>告别 <code>rand()</code></strong>——<code>rand()</code> 范围小、质量差、且依赖实现。</p>
</blockquote>
<pre><code>#include &lt;random&gt;
#include &lt;ctime&gt;
mt19937 rng(time(nullptr));                        // 32 位引擎
mt19937_64 rng64(chrono::steady_clock::now().time_since_epoch().count()); // 64 位

// 范围 [lo, hi] 均匀整数
uniform_int_distribution&lt;int&gt; dist(1, 100);
int x = dist(rng);

// 范围 [0, 1) 均匀浮点
uniform_real_distribution&lt;double&gt; fdist(0.0, 1.0);
double y = fdist(rng);

// 容器洗牌
shuffle(v.begin(), v.end(), rng);
</code></pre>
<table>
<thead>
<tr>
<th>引擎</th>
<th>位数</th>
<th>用途</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>mt19937</code></td>
<td>32</td>
<td>通用、够用</td>
</tr>
<tr>
<td><code>mt19937_64</code></td>
<td>64</td>
<td>大范围 ID、需要 64 位</td>
</tr>
<tr>
<td><code>default_random_engine</code></td>
<td>视实现</td>
<td>不推荐（质量参差）</td>
</tr>
</tbody>
</table>
<h3>关键注意点</h3>
<table>
<thead>
<tr>
<th>易错点</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;<code>accumulate</code> 初始值用 <code>0</code>&lt;/span&gt;</td>
<td>大数累加用 <code>0LL</code> 防溢出</td>
</tr>
<tr>
<td><code>partial_sum</code> 含当前项</td>
<td>想要"前 i-1 项和"用 <code>exclusive_scan</code></td>
</tr>
<tr>
<td><code>iota</code> 不检查越界</td>
<td>区间要先 <code>resize</code></td>
</tr>
<tr>
<td><code>gcd</code> / <code>lcm</code></td>
<td>C++17 起，旧编译器用 <code>__gcd</code> 或手写</td>
</tr>
<tr>
<td><code>rand()</code></td>
<td>范围小、质量差，统一换 <code>mt19937</code></td>
</tr>
<tr>
<td><code>shuffle</code> 需要引擎</td>
<td>传 <code>mt19937</code>，不能传 <code>time(nullptr)</code></td>
</tr>
</tbody>
</table>
<h2>iomanip</h2>
<blockquote>
<p><code>&lt;iomanip&gt;</code> 提供<strong>格式化输出</strong>的"操控器"（manipulator），用来控制 <code>cout</code> 的宽度、精度、对齐、进制等。<strong>默认状态不会持久化</strong>——除了 <code>fixed</code> / <code>scientific</code> / <code>boolalpha</code> 等"标志型"，像 <code>setw</code> 这种只对<strong>下一次输出</strong>生效。</p>
</blockquote>
<h3>精度与浮点格式</h3>
<table>
<thead>
<tr>
<th>操控器</th>
<th>作用</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>setprecision(n)</code></td>
<td>设置精度（<strong>有效数字位数</strong>）</td>
<td>默认模式</td>
</tr>
<tr>
<td><code>setprecision(n)</code> + <code>fixed</code></td>
<td>设置<strong>小数位数</strong></td>
<td>刷题<strong>最常考</strong></td>
</tr>
<tr>
<td><code>setprecision(n)</code> + <code>scientific</code></td>
<td>设置科学计数法的小数位数</td>
<td></td>
</tr>
<tr>
<td><code>defaultfloat</code></td>
<td>还原默认（C++11）</td>
<td>退出 <code>fixed</code> 模式</td>
</tr>
<tr>
<td><code>showpoint</code> / <code>noshowpoint</code></td>
<td>强制 / 不强制显示小数点</td>
<td></td>
</tr>
</tbody>
</table>
<pre><code>#include &lt;iostream&gt;
#include &lt;iomanip&gt;
using namespace std;

int main() {
    double pi = 3.14159265358979;

    cout &lt;&lt; setprecision(4) &lt;&lt; pi &lt;&lt; endl;         // 3.142（4 位有效数字）
    cout &lt;&lt; fixed &lt;&lt; setprecision(2) &lt;&lt; pi &lt;&lt; endl; // 3.14（保留 2 位小数）
    cout &lt;&lt; defaultfloat &lt;&lt; setprecision(6) &lt;&lt; pi; // 3.14159

    // 默认 cout 输出 1.0 会显示 1，showpoint 强制显示 1.00000
    cout &lt;&lt; fixed &lt;&lt; setprecision(2);
    cout &lt;&lt; 1.0 &lt;&lt; endl; // 1.00
    return 0;
}
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意&lt;/span&gt;：<code>fixed</code> / <code>scientific</code> 是<strong>持久的</strong>，影响后续所有浮点输出；用完想还原要 <code>cout &lt;&lt; defaultfloat;</code>（C++11）。</p>
</blockquote>
<h3>宽度与填充</h3>
<table>
<thead>
<tr>
<th>操控器</th>
<th>作用</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>setw(n)</code></td>
<td>设置下一次输出的<strong>最小宽度</strong></td>
<td><strong>只生效一次</strong></td>
</tr>
<tr>
<td><code>setfill(c)</code></td>
<td>设置填充字符（默认空格）</td>
<td>持久</td>
</tr>
<tr>
<td><code>left</code> / <code>right</code></td>
<td>左对齐 / 右对齐（默认 right）</td>
<td>持久</td>
</tr>
<tr>
<td><code>internal</code></td>
<td>符号左对齐、数值右对齐（如 <code>- 123</code>）</td>
<td>持久</td>
</tr>
</tbody>
</table>
<pre><code>#include &lt;iostream&gt;
#include &lt;iomanip&gt;
using namespace std;

int main() {
    // 题目要求：输出 4 位整数，不足补 0（如 1 → 0001）
    cout &lt;&lt; setfill('0');
    for (int i = 1; i &lt;= 5; i++) {
        cout &lt;&lt; setw(4) &lt;&lt; i &lt;&lt; endl;
    }
    // 0001
    // 0002
    // 0003
    // 0004
    // 0005

    // 对齐：左对齐 + 宽度 10，填充 *
    cout &lt;&lt; setfill('*') &lt;&lt; left &lt;&lt; setw(10) &lt;&lt; "hi" &lt;&lt; "end" &lt;&lt; endl;
    // hi********end
    return 0;
}
</code></pre>
<blockquote>
<p>&lt;span style="color:red"&gt;⚠ 警告&lt;/span&gt;：<code>setw</code> <strong>只对下一次输出生效</strong>！多个字段都要设宽度时，必须<strong>每次都写</strong> <code>setw</code>。</p>
</blockquote>
<h3>进制与布尔</h3>
<table>
<thead>
<tr>
<th>操控器</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>hex</code> / <code>dec</code> / <code>oct</code></td>
<td>十六进制 / 十进制 / 八进制（持久）</td>
</tr>
<tr>
<td><code>setbase(n)</code></td>
<td>进制（只接受 8/10/16，其余按 10 处理）</td>
</tr>
<tr>
<td><code>showbase</code> / <code>noshowbase</code></td>
<td>显示 / 不显示进制前缀（<code>0x</code> / <code>0</code>）</td>
</tr>
<tr>
<td><code>uppercase</code> / <code>nouppercase</code></td>
<td>十六进制字母大写 / 小写</td>
</tr>
<tr>
<td><code>boolalpha</code> / <code>noboolalpha</code></td>
<td>bool 输出 <code>true</code> / <code>false</code> 还是 <code>1</code> / <code>0</code></td>
</tr>
</tbody>
</table>
<pre><code>cout &lt;&lt; hex &lt;&lt; 255;              // ff
cout &lt;&lt; showbase &lt;&lt; hex &lt;&lt; 255;  // 0xff
cout &lt;&lt; uppercase &lt;&lt; hex &lt;&lt; 255; // 0XFF

bool flag = true;
cout &lt;&lt; flag;                    // 1
cout &lt;&lt; boolalpha &lt;&lt; flag;       // true
</code></pre>
<h3>输入侧操控器</h3>
<table>
<thead>
<tr>
<th>操控器</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>ws</code></td>
<td>跳过前导空白字符（常用于 <code>getline</code> 前清空残留换行）</td>
</tr>
<tr>
<td><code>get_money(m)</code> / <code>put_money(m)</code></td>
<td>货币格式（C++11，少用）</td>
</tr>
<tr>
<td><code>get_time(t, fmt)</code> / <code>put_time(t, fmt)</code></td>
<td>时间格式（C++11，少用）</td>
</tr>
</tbody>
</table>
<pre><code>int n;
string s;
cin &gt;&gt; n;
// &gt;&gt; 之后缓冲区留有 '\n'，getline 会读空串
cin.ignore(numeric_limits&lt;streamsize&gt;::max(), '\n');
getline(cin, s);
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意&lt;/span&gt;：刷题<strong>最常见的坑</strong>是 <code>cin &gt;&gt; n</code> 后接 <code>getline(cin, s)</code> 读到空串，必须 <code>cin.ignore</code> 清除残留换行。</p>
</blockquote>
<h3>关键注意点</h3>
<table>
<thead>
<tr>
<th>易错点</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:red"&gt;<code>setw</code> 只生效一次&lt;/span&gt;</td>
<td>多个字段必须<strong>每次都写</strong></td>
</tr>
<tr>
<td><code>setprecision</code> 默认是<strong>有效数字</strong></td>
<td>想保留 n 位小数要加 <code>fixed</code></td>
</tr>
<tr>
<td><code>fixed</code> 持久生效</td>
<td>用完记得 <code>defaultfloat</code> 还原</td>
</tr>
<tr>
<td><code>cout &lt;&lt; bool</code> 输出 <code>0/1</code></td>
<td>想看 <code>true/false</code> 需 <code>boolalpha</code></td>
</tr>
<tr>
<td><code>hex</code> 也持久</td>
<td>想回十进制要 <code>dec</code></td>
</tr>
<tr>
<td><code>cin &gt;&gt; n</code> + <code>getline</code></td>
<td>记得 <code>cin.ignore()</code> 清残留换行</td>
</tr>
</tbody>
</table>
<h1>&lt;climits&gt;</h1>
<h2>极限宏</h2>
<table>
<thead>
<tr>
<th>宏名称</th>
<th>对应类型</th>
<th>值（十进制）</th>
<th>用途</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>INT_MAX</code></td>
<td><code>int</code></td>
<td>2,147,483,647</td>
<td>int 最大值</td>
</tr>
<tr>
<td><code>INT_MIN</code></td>
<td><code>int</code></td>
<td>-2,147,483,648</td>
<td>int 最小值</td>
</tr>
<tr>
<td><code>LLONG_MAX</code></td>
<td><code>long long</code></td>
<td>9,223,372,036,854,775,807</td>
<td>long long 最大值</td>
</tr>
<tr>
<td><code>LLONG_MIN</code></td>
<td><code>long long</code></td>
<td>-9,223,372,036,854,775,808</td>
<td>long long 最小值</td>
</tr>
<tr>
<td><code>ULLONG_MAX</code></td>
<td><code>unsigned long long</code></td>
<td>18,446,744,073,709,551,615</td>
<td>unsigned long long 最大值</td>
</tr>
<tr>
<td><code>LONG_MAX</code></td>
<td><code>long</code></td>
<td>Windows: 2,147,483,647&lt;br&gt;Linux: 9,223,372,036,854,775,807</td>
<td>长度随平台，刷题不推荐</td>
</tr>
<tr>
<td><code>LONG_MIN</code></td>
<td><code>long</code></td>
<td>Windows: -2,147,483,648&lt;br&gt;Linux: -9,223,372,036,854,775,808</td>
<td>同上</td>
</tr>
</tbody>
</table>
<hr />
<h2>整型字面量后缀（无需头文件）</h2>
<table>
<thead>
<tr>
<th>后缀</th>
<th>类型</th>
<th>示例</th>
<th>用途</th>
</tr>
</thead>
<tbody>
<tr>
<td>无后缀</td>
<td><code>int</code></td>
<td><code>123</code></td>
<td>默认整型</td>
</tr>
<tr>
<td><code>L</code></td>
<td><code>long</code></td>
<td><code>123L</code></td>
<td>不推荐（平台相关）</td>
</tr>
<tr>
<td><code>LL</code></td>
<td><code>long long</code></td>
<td><code>123LL</code></td>
<td><strong>明确 64 位有符号</strong></td>
</tr>
<tr>
<td><code>ULL</code></td>
<td><code>unsigned long long</code></td>
<td><code>123ULL</code></td>
<td><strong>明确 64 位无符号</strong></td>
</tr>
<tr>
<td><code>u</code></td>
<td><code>unsigned int</code></td>
<td><code>123u</code></td>
<td>无符号 int</td>
</tr>
<tr>
<td><code>f</code></td>
<td><code>float</code></td>
<td><code>3.14f</code></td>
<td>单精度浮点</td>
</tr>
</tbody>
</table>
<blockquote>
<p>⚠️ <strong>刷题最常用</strong>：<code>1LL * a * b</code> 防止乘法溢出。</p>
</blockquote>
]]></content>
    <author><name>仙鹤</name></author>
    <category term="编程"/>
  </entry>
  <entry>
    <title>Git 与 GitHub</title>
    <link href="https://xianhe51920.github.io/posts/git-github/" rel="alternate" type="text/html"/>
    <id>https://xianhe51920.github.io/posts/git-github/</id>
    <published>2026-08-28T00:00:00.000Z</published>
    <updated>2026-08-28T00:00:00.000Z</updated>
    <summary>Git 与 GitHub 入门与常用命令</summary>
    <content type="html"><![CDATA[<h1>Git 与 GitHub</h1>
<blockquote>
<p>本笔记涵盖 Git 版本控制与 GitHub 协作平台的核心概念、常用命令、多人协作流程及高级特性。
<strong>参考链接</strong><br />
【Git+Github核心概念大串讲，从零到一全攻略，详细实战教程】 https://www.bilibili.com/video/BV1ySLc6QEcB/?share_source=copy_web&amp;vd_source=9d34ca7f21db859ee3f136b49b89515d</p>
</blockquote>
<h1>一、Git 与 GitHub 概述</h1>
<blockquote>
<p>本节介绍版本控制、Git 与 GitHub 的基本概念和分区模型。</p>
</blockquote>
<h2>Git 分区概念</h2>
<p>Git 的数据流转分为四个核心区域：</p>
<pre><code>flowchart LR
  A[&lt;b&gt;工作区&lt;/b&gt; Working Directory] --&gt;|git add| B[&lt;b&gt;暂存区&lt;/b&gt; Staging Area]
  B --&gt;|git commit| C[&lt;b&gt;本地仓库&lt;/b&gt; Local Repository]
  C --&gt;|git push| D[&lt;b&gt;远程仓库&lt;/b&gt; Remote Repository]
  D --&gt;|git pull| A
</code></pre>
<table>
<thead>
<tr>
<th>分区</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:blue"&gt;工作区&lt;/span&gt; (Working Directory)</td>
<td>本地可见的普通文件目录，用户直接编辑修改文件的地方</td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;暂存区&lt;/span&gt; (Staging / Index)</td>
<td>通过 <code>git add</code> 命令将工作区变更暂存的中转区域</td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;本地仓库&lt;/span&gt; (Local Repository)</td>
<td>通过 <code>git commit</code> 将暂存区内容永久存储的版本库</td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;远程仓库&lt;/span&gt; (Remote Repository)</td>
<td>通过 <code>git push</code> 同步到服务器的共享版本库</td>
</tr>
</tbody>
</table>
<h2>核心命令</h2>
<pre><code>git clone   &lt;url&gt;      # 克隆远程仓库到本地
git add     &lt;file&gt;     # 将工作区变更添加到暂存区
git commit  -m "msg"  # 将暂存区变更提交到本地仓库
git push               # 将本地仓库变更推送到远程仓库
git pull               # 拉取远程仓库最新变更
git merge   &lt;branch&gt;   # 合并分支变更
git fetch              # 获取远程仓库最新信息
</code></pre>
<h2>Git 是什么</h2>
<blockquote>
<p>&lt;span style="color:blue"&gt;Git&lt;/span&gt; 是开源免费的 <strong>&lt;span style="color:blue"&gt;分布式版本控制系统&lt;/span&gt;</strong>，用 <code>commit</code> 记录每次完整快照。</p>
</blockquote>
<p><strong>典型应用场景</strong>：管理论⽂修改版本（第⼀版、第⼆版、定稿版等）、协同开发⼤型项⽬（处理成千上万⽂件）。</p>
<p><strong>核⼼&lt;span style="color:green"&gt;优势&lt;/span&gt;</strong>：解决纯⼈⼯版本控制的复杂性，⽀持多⼈协同开发。</p>
<p><strong>仓库管理</strong>：被 &lt;span style="color:blue"&gt;Git&lt;/span&gt; 管理的⽂件夹会⽣成 <code>.git</code> ⼦⽂件夹，使⽤ <code>commit</code> 作为版本控制基本单元，每次 <code>commit</code> 保存仓库完整快照，形成&lt;span style="color:green"&gt;可回溯&lt;/span&gt;的 <code>commit</code> 历史链路。</p>
<p><strong>仓库类型</strong>：&lt;span style="color:blue"&gt;本地仓库&lt;/span&gt;运⾏在开发者个⼈电脑上，&lt;span style="color:blue"&gt;远程仓库&lt;/span&gt;是服务器上的备份仓库⽤于代码分享，可⾃⾏搭建或使⽤ GitHub 等托管服务。</p>
<h2>GitHub 是什么</h2>
<blockquote>
<p>&lt;span style="color:blue"&gt;GitHub&lt;/span&gt; = Git + Hub，是全球最⼤的 <strong>&lt;span style="color:blue"&gt;代码托管与协作平台&lt;/span&gt;</strong>。</p>
</blockquote>
<p><strong>名称解析</strong>：&lt;span style="color:blue"&gt;Git&lt;/span&gt; 指版本控制系统，&lt;span style="color:blue"&gt;Hub&lt;/span&gt; 意为中⼼ / 集合。</p>
<p><strong>核⼼功能</strong>：存储和分享 &lt;span style="color:blue"&gt;Git&lt;/span&gt; 仓库，⽀持多⼈协作开发。</p>
<p><strong>仓库类型</strong>：&lt;span style="color:green"&gt;公开仓库&lt;/span&gt;可被搜索、浏览和学习（示例：Linux 内核、CPython 解释器、Nginx），&lt;span style="color:orange"&gt;私有仓库&lt;/span&gt;仅限指定⼈员访问。同类服务还包括 &lt;span style="color:blue"&gt;Git&lt;/span&gt;Lab、BitBucket 等，开源是计算机科学的基⽯与瑰宝。</p>
<h1>二、环境准备与配置</h1>
<blockquote>
<p>本节介绍 Git、VS Code、GitHub 账号的安装与 AI Agent 的接入方法。</p>
</blockquote>
<h2>Git 安装</h2>
<h3>Windows 安装</h3>
<pre><code># 推荐使用 winget 安装
winget install --id Git.Git -e --source winget
# 或访问 https://git-scm.com 下载安装包
</code></pre>
<p>访问 Git 官⽹下载 Windows 安装包，选择对应 CPU 架构（通常为 x86），运⾏安装程序并默认设置完成安装。</p>
<h3>Mac 安装</h3>
<pre><code>xcode-select --install   # 同意安装命令⾏⼯具包（包含 Git）
git --version            # 验证安装
</code></pre>
<h2>VS Code 安装</h2>
<p>访问 VS Code 官⽹下载对应操作系统版本：Windows 系统直接运⾏安装程序，Mac 系统拖拽到 Applications ⽂件夹。验证命令 <code>code --version</code>。</p>
<h2>GitHub 账号注册</h2>
<p><strong>注册流程</strong>：访问 GitHub 官⽹点击 <code>Sign up</code> → 填写⽤户名、邮箱和密码 → 验证邮箱收取验证码。</p>
<blockquote>
<p>&lt;span style="color:orange"&gt;注意&lt;/span&gt;：国内⽤户可能需要特殊⽹络设置；建议使⽤常⽤邮箱注册；记住账号密码⽤于后续 Git 操作。</p>
</blockquote>
<h2>配置 AI Agent</h2>
<p><strong>配置步骤</strong>：在 GitHub 创建新仓库 → 复制仓库地址备⽤ → 使⽤ Codex 初始化本地项⽬ → 将本地项⽬与远程仓库关联。<strong>关键操作</strong>：通过 AI 辅助完成 Git 配置，需要授权 GitHub 账户访问，确保⽹络连接稳定。</p>
<h2>环境验证</h2>
<pre><code>git --version    # Git 版本
code --version   # VS Code 运⾏状态
# GitHub 账号登录状态、 AI Agent 连接情况
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;常见问题&lt;/span&gt;：⽹络连接问题、权限配置错误、环境变量未设置。</p>
</blockquote>
<h1>三、Git 本地基础操作</h1>
<blockquote>
<p>本节介绍仓库初始化、<code>.gitignore</code> 配置、提交、后悔药和分支等本地操作。</p>
</blockquote>
<h2>初始化仓库</h2>
<blockquote>
<p>&lt;span style="color:blue"&gt;git init&lt;/span&gt; 命令将⼀个普通⽂件夹转变为 Git 管理的仓库。</p>
</blockquote>
<pre><code>cd my-project
git init
# 此时会⽣成隐藏的 .git ⼦⽂件夹
</code></pre>
<p><strong>操作步骤</strong>：在 VS Code 中选择 <code>Open Folder</code> 打开任意⽂件夹（空或⾮空）→ 找到左侧 <code>Source Control</code> ⾯板 → 点击 <code>Initialize Repository</code> 按钮完成初始化。初始化成功后，⽂件夹内会⽣成隐藏的 <code>.git</code> ⼦⽂件夹，存储所有 Git 相关数据。</p>
<blockquote>
<p>&lt;span style="color:red"&gt;删除&lt;/span&gt; 此⽂件夹将取消 Git 管理。</p>
</blockquote>
<h2><code>.gitignore</code> 配置</h2>
<p><code>.gitignore</code> ⽂件⽤于声明仓库中 &lt;span style="color:red"&gt;不应被 Git 管理&lt;/span&gt; 的⽂件/⽬录。</p>
<p><strong>典型应⽤场景</strong>：存储密钥的 <code>.env</code> ⽂件（安全考虑）、Node.js 项⽬的 <code>node_modules/</code> ⽬录（可通过 <code>npm install</code> 重新⽣成）。</p>
<p><strong>配置⽅法</strong>：在仓库根⽬录创建 <code>.gitignore</code> ⽂件，每⾏写⼊⼀个略规则（⽬录需加斜杠，如 <code>node_modules/</code>）。</p>
<blockquote>
<p>&lt;span style="color:green"&gt;最佳实践&lt;/span&gt;：项⽬初始化时就应创建 <code>.gitignore</code>，避免误提交敏感或不必要⽂件。</p>
</blockquote>
<h2>提交 (commit)</h2>
<p><code>commit</code> 意为提交，每次提交保存仓库状态的完整快照，形成可回溯的历史链路。<strong>操作流程</strong>：创建/修改⽂件（如 <code>fruits.txt</code>）→ 在 <code>Source Control</code> ⾯板查看待提交⽂件→ 填写有意义的 commit message（说明本次修改内容）→ 点击 commit 按钮完成提交。</p>
<blockquote>
<p>&lt;span style="color:green"&gt;AI 协作技巧&lt;/span&gt;：让 AI 完成⼩功能点后⽴即提交，便于版本控制。</p>
</blockquote>
<h2>Git 后悔药</h2>
<blockquote>
<p>&lt;span style="color:blue"&gt;后悔药三件套&lt;/span&gt;：<code>discard</code>（未提交）→ <code>reset</code>（已提交未推送）→ <code>revert</code>（已推送）</p>
</blockquote>
<pre><code>flowchart TD
  A{需要回退?} --&gt;|未提交| B["discard 丢弃工作区改动"]
  A --&gt;|已提交| C{推送到远端?}
  C --&gt;|未推送| D["reset 强制回退历史"]
  C --&gt;|已推送| E["revert 生成反向提交"]
</code></pre>
<table>
<thead>
<tr>
<th>操作</th>
<th>适用场景</th>
<th>操作方法</th>
<th>风险</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>discard</code></td>
<td>未提交的更改</td>
<td>VS Code 点击 <code>Discard Changes</code> 按钮</td>
<td>&lt;span style="color:green"&gt;低&lt;/span&gt;：仅影响工作区</td>
</tr>
<tr>
<td><code>reset</code></td>
<td>已提交未推送</td>
<td>命令行 <code>git reset &lt;commit&gt;</code></td>
<td>&lt;span style="color:red"&gt;高&lt;/span&gt;：重写历史</td>
</tr>
<tr>
<td><code>revert</code></td>
<td>已推送</td>
<td>命令行 <code>git revert &lt;commit&gt;</code></td>
<td>&lt;span style="color:green"&gt;低&lt;/span&gt;：不重写历史</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:red"&gt;⚠ 警告&lt;/span&gt;：&lt;span style="color:red"&gt;多⼈协作分⽀禁⽤ reset&lt;/span&gt;，重写历史会导致他⼈分⽀断裂；优先使⽤ &lt;span style="color:green"&gt;revert&lt;/span&gt;。</p>
</blockquote>
<h2>分支</h2>
<blockquote>
<p>&lt;span style="color:blue"&gt;分⽀本质&lt;/span&gt;：指向某次 commit 的可移动指针，HEAD 指向当前分⽀。&lt;span style="color:orange"&gt;分离头指针&lt;/span&gt;状态（<code>HEAD</code> 直接指向某次历史提交）易丢失代码，应避免修改。</p>
</blockquote>
<pre><code>flowchart LR
  M[main] --&gt;|git checkout -b| F[feature]
  F --&gt;|开发 + commit| F
  F --&gt;|git merge| M
</code></pre>
<p><strong>分⽀四操作</strong>：&lt;span style="color:green"&gt;创建&lt;/span&gt;（基于任意提交创建新分⽀，实质是指针操作）、<strong>切换</strong>（在 VS Code 左下⻆选择⽬标分⽀）、<strong>合并</strong>（使⽤ <code>git merge</code> 将特性分⽀合并回主⼲）、<strong>删除</strong>（切换⾄其他分⽀后删除⽬标分⽀）。&lt;span style="color:green"&gt;⼯作模式&lt;/span&gt;：每个开发者/Agent 在独⽴分⽀开发，完成后合并。</p>
<h1>四、Git 高级特性</h1>
<blockquote>
<p>本节介绍工作树、合并冲突等高级功能。</p>
</blockquote>
<h2>工作树 (Worktree)</h2>
<pre><code>flowchart LR
  Main["主仓库 (主工作区)"] --&gt;|git worktree| WT1["worktree 1 (并行副本)"]
  Main --&gt; WT2["worktree 2 (并行副本)"]
  WT1 -.-&gt;|完成后 merge| Main
  WT2 -.-&gt;|完成后 merge| Main
</code></pre>
<p><strong>本质</strong>：创建新分⽀并将代码完整复制到新⽂件夹，主⽂件夹与⼯作树⽂件夹并⾏⼯作互不⼲扰，底层通过 &lt;span style="color:blue"&gt;Git&lt;/span&gt; 关联，改动可轻松合并回主⼲。&lt;span style="color:green"&gt;适⽤场景&lt;/span&gt;：需要并⾏开发测试时使⽤。创建⽅法：命令⾏ <code>claude --worktree &lt;分⽀名&gt;</code>，或图形界⾯右键项⽬选择 "创建永久⼯作树"。</p>
<h2>合并冲突</h2>
<blockquote>
<p>&lt;span style="color:orange"&gt;冲突&lt;/span&gt;：两个分⽀修改同⼀⽂件的同⼀⾏代码时，Git ⽆法⾃动合并，需要⼈⼯/AI 选择保留⽅案。</p>
</blockquote>
<p><strong>解决⽅法</strong>：让 AI 合并时遇到冲突暂停 → ⼈⼯选择保留⽅案：保留某⼀⽅改动，或合并双⽅改动（可指定顺序）。AI 通过⾃然语⾔交互简化了传统 git 冲突解决流程。</p>
<h1>五、远程仓库实战</h1>
<blockquote>
<p>本节介绍远程仓库的创建、克隆、推送与同步等操作。</p>
</blockquote>
<h2>数据流转总览</h2>
<pre><code>flowchart LR
  L["本地工作区"] --&gt;|git add| S[暂存区]
  S --&gt;|git commit| R["本地仓库"]
  R --&gt;|git push| REM["远程仓库"]
  REM --&gt;|git pull| L
</code></pre>
<h2>暂存区的作用</h2>
<p>暂存区作为 <code>commit</code> 前的准备区域，允许选择性提交部分⽂件改动。现代 Git ⼯具（如 VS Code）常将 <code>git add</code> 和 <code>git commit</code> 合并简化操作。</p>
<h2>创建远端仓库</h2>
<p><strong>创建步骤</strong>：点击 GitHub 的 <code>New</code> 按钮 → 设置仓库名称（英⽂）→ 选择公开 (Public) 或私有 (Private) 属性 → 可选添加 <code>README</code>、<code>.gitignore</code> 和许可证⽂件。</p>
<p><strong>仓库属性区别</strong>：&lt;span style="color:green"&gt;公开仓库&lt;/span&gt;代码对所有⼈可⻅，&lt;span style="color:orange"&gt;私有仓库&lt;/span&gt;仅⾃⼰可⻅。</p>
<h2>克隆远端仓库到本地</h2>
<p><strong>克隆流程</strong>：复制 GitHub 仓库地址 → 在 VS Code 中选择 <code>Clone Repository</code> → 粘贴仓库地址 → 选择本地存储路径。<strong>克隆效果</strong>：⾃动创建本地仓库，⽣成⼯作⽬录，建⽴与远端仓库的关联。</p>
<h2>本地提交到远端仓库</h2>
<p><strong>提交流程</strong>：在本地⼯作区修改/新增⽂件 → 执⾏ <code>git commit</code> 提交到本地仓库 → 点击 <code>Publish</code> 按钮推送到远端。</p>
<blockquote>
<p>&lt;span style="color:red"&gt;权限限制&lt;/span&gt;：只有仓库管理员或被授权⽤户才能 push 成功，&lt;span style="color:red"&gt;不能直接 push 到他⼈仓库&lt;/span&gt;。</p>
</blockquote>
<h2>本地仓库绑定远端仓库（第二种方法）</h2>
<pre><code># 1. 初始化本地仓库
git init
# 2. 进⾏⾸次 commit
git add .
git commit -m "init"
# 3. 在 VS Code 点击 Publish 按钮
# 4. 设置远端仓库名称和属性(公开/私有)
</code></pre>
<p><strong>特点</strong>：适⽤于已有本地项⽬的情况，推送后⾃动创建对应的远端仓库。</p>
<h2>远端仓库改动同步到本地</h2>
<pre><code>git pull   # 相当于 git fetch + git merge
# 或在 VS Code 中点击 Sync Changes 按钮
</code></pre>
<p><strong>常⻅场景</strong>：远端仓库直接修改（如 GitHub ⽹⻚端编辑）、协作者推送了新提交。<strong>分⽀显示</strong>：<code>origin/main</code> 表示远端分⽀，<code>main</code> 表示本地分⽀，箭头图标显示同步状态。</p>
<h1>六、GitHub 网页入门</h1>
<blockquote>
<p>本节介绍 GitHub 网页界面、仓库管理、搜索、Issues、快捷键与在线开发环境。</p>
</blockquote>
<h2>仓库网址的构成</h2>
<p>基本结构 <code>github.com/作者名/仓库名</code>，其中 <code>github.com</code> 是平台主域名，中间是开发者⽤户名，最后是仓库英⽂名称。</p>
<h2>仓库界面与代码库</h2>
<p>界⾯中央显示项⽬源代码。代码获取⽅式：直接浏览、点击 <code>Code</code> 按钮下载压缩包、执⾏ <code>git clone &lt;仓库地址&gt;</code>。Commit message 记录修改内容，最后更新⽇期反映项⽬活跃度，&lt;span style="color:orange"&gt;⻓期未更新可能表示项⽬停⽌维护&lt;/span&gt;。</p>
<h2>README 与 Releases</h2>
<p><strong>README</strong> ⾃动展示在仓库⻚⻚，包含项⽬简介、功能说明和使⽤⽅法。<strong>Releases</strong> 提供打包好的软件下载，包含版本号和更新内容，可选择历史版本下载。</p>
<h2>About、Star、Fork</h2>
<p><strong>About 模块</strong>显示项⽬简介、标签和开源协议。<strong>Star</strong> 类似点赞收藏，数量反映项⽬热度。<strong>Fork</strong> 将仓库复制到⾃⼰的账户，可⽤于学习源码或⼆次开发，也可通过 Pull Request 贡献代码。</p>
<h2>搜索功能</h2>
<p><strong>局部搜索</strong>在当前仓库内查找内容，<strong>全局搜索</strong>可搜索整个 GitHub 平台，⽀持⽂件名、代码内容搜索及⾼级搜索语法。</p>
<h2>Issues 功能</h2>
<p><strong>Issues</strong> 是与项⽬作者讨论的平台，可报告 bug 或提出新功能&lt;span style="color:orange"&gt;建议&lt;/span&gt;。状态分 &lt;span style="color:orange"&gt;<code>Open</code>&lt;/span&gt;（未解决）和 &lt;span style="color:green"&gt;<code>Closed</code>&lt;/span&gt;（已解决）。</p>
<h2>GitHub 快捷键</h2>
<table>
<thead>
<tr>
<th>快捷键</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:blue"&gt;/&lt;/span&gt;</td>
<td>快速打开 GitHub 的全局搜索功能栏</td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;t&lt;/span&gt;</td>
<td>在仓库⻚⾯快速定位到⽂件搜索栏，⽀持按⽂件名搜索</td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;l&lt;/span&gt;</td>
<td>快速跳转到指定⾏号（如输⼊ 33 跳转到 33 ⾏）</td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;?&lt;/span&gt;</td>
<td>打开快捷键速查表</td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;gi&lt;/span&gt;</td>
<td>快速进⼊ issues ⻚⾯（先按 g 再按 i）</td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;gc&lt;/span&gt;</td>
<td>快速查看代码（go to code）</td>
</tr>
</tbody>
</table>
<p><strong>⾏操作功能</strong>：<code>Copy line</code> 复制当前⾏、<code>Copy perm link</code> 复制永久链接、<code>git blame</code> 查看提交历史和贡献者。</p>
<h2>在线开发环境</h2>
<p>按 &lt;span style="color:blue"&gt;.&lt;/span&gt; (句号键) 可在⽹⻚版 VS Code 中打开当前仓库，即 <strong><code>Codespaces</code></strong>：云端开发环境（⽀持双核 8GB 内存配置），可在浏览器中调试代码，通过右上⻆ <code>Run</code> 按钮执⾏代码（如 Python 脚本）。调试时优先使⽤ <strong>Codespaces</strong> ⽽⾮本地环境。</p>
<h2>注意事项</h2>
<p>始终可通过 &lt;span style="color:blue"&gt;?&lt;/span&gt; 键调出速查表；调试时优先使⽤ <code>Codespaces</code> ⽽⾮本地环境；在 <code>issues</code> ⻚⾯可使⽤ <code>is:issue is:open</code> 等过滤器搜索。</p>
<h1>七、GitHub 多人协作</h1>
<blockquote>
<p>本节介绍 Fork、PR、Code Review、Cherry Pick、Rebase 等多人协作流程。</p>
</blockquote>
<h2>协作流程总览</h2>
<pre><code>flowchart LR
  A[Fork 复刻] --&gt; B[Clone 到本地]
  B --&gt; C[新建分支]
  C --&gt; D[修改 + commit]
  D --&gt; E[git push]
  E --&gt; F[创建 PR]
  F --&gt; G[Code Review]
  G --&gt; H[Merge 合并]
</code></pre>
<h2>贡献代码前的准备：复刻项目</h2>
<p>贡献者点击 <strong>Fork</strong> 按钮将项⽬复制到⾃⼰的账户下，这是贡献代码的第⼀步。复刻后项⽬会出现在贡献者⾃⼰的 &lt;span style="color:blue"&gt;Git&lt;/span&gt;Hub 账户下。点击 <code>Create fork</code> 即可完成复刻。</p>
<blockquote>
<p>&lt;span style="color:orange"&gt;权限说明&lt;/span&gt;：贡献者没有直接修改原项⽬的权限，必须通过复刻创建个⼈副本后才能进⾏修改。</p>
</blockquote>
<h2>本地修改：克隆与分支创建</h2>
<p>在复刻后的项⽬中点击 <code>Code</code> 获取项⽬地址，使⽤ VS Code 的克隆功能将项⽬下载到本地。&lt;span style="color:red"&gt;修改代码前必须先创建新分⽀，避免直接在 main 分⽀上修改&lt;/span&gt;，这样可以防⽌后续同步代码时产⽣冲突。<strong>修改流程</strong>：通过 VS Code 左下⻆菜单创建分⽀ → 进⾏代码修改 → 提交 commit → 点击 <code>Publish</code> 推送⾄ GitHub。</p>
<h2>例题 1：提交改动与创建合并请求</h2>
<p>修改完成后将分⽀推送到 &lt;span style="color:blue"&gt;Git&lt;/span&gt;Hub，可以在对应分⽀看到提交记录。通过 <code>Compare &amp; pull request</code> 按钮创建合并请求 (<strong>PR</strong>)，将⾃⼰的修改合并回原项⽬。<strong>PR</strong> 会清晰展示两个分⽀间的代码差异，便于管理员审核。</p>
<h2>合并请求的审核与合并</h2>
<p>Pull Request (PR) 是合并请求的提案，贡献者提议将⾃⼰的分⽀合并到主⼲分⽀。项⽬管理员会进⾏ code review（代码审核），可能提出修改意⻅，确认⽆误后才会同意合并。协作流程：复刻项⽬ → 创建分⽀ → 修改代码 → 提交 PR → 等待审核 → 合并完成。</p>
<h2>例题 2：同步母项目代码与解决冲突</h2>
<p>同步必要性：在创建 <strong>PR</strong> 前必须先将⺟项⽬的最新代码同步到⾃⼰的分⽀，确保没有&lt;span style="color:red"&gt;冲突&lt;/span&gt;。</p>
<p>&lt;span style="color:red"&gt;冲突&lt;/span&gt;&lt;span style="color:orange"&gt;预防&lt;/span&gt;：当主⼲和特性分⽀同时修改相同⽂件时会产⽣&lt;span style="color:red"&gt;冲突&lt;/span&gt;，应先在本地解决&lt;span style="color:red"&gt;冲突&lt;/span&gt;再提交 <strong>PR</strong>。</p>
<p>&lt;span style="color:green"&gt;最佳实践&lt;/span&gt;：始终保持特性分⽀与主⼲同步，可以⼤⼤减少合并时的&lt;span style="color:red"&gt;冲突&lt;/span&gt;可能性。</p>
<h2>Codex 操作演示</h2>
<p>操作前需先将分⽀切换为 feature 分⽀。当合并⺟项⽬遇到冲突时，Codex 会提示⽤户选择处理⽅式（如选择 "两个都保留"）。最终采⽤樱桃合并⽅式同步⼦项⽬与⺟项⽬的最新改动。</p>
<h2>推送本地改动并创建 PR</h2>
<pre><code>git push                  # 推送 feature 分⽀
git push -f               # 必要时强制推送
</code></pre>
<p>明确合并⽅向：⼦项⽬ &lt;span style="color:blue"&gt;feature&lt;/span&gt; 分⽀ → ⺟项⽬ &lt;span style="color:blue"&gt;main&lt;/span&gt; 分⽀。填写清晰 <code>PR</code> 标题（如 "⽔果清单，增加更多⽔果"）。</p>
<h2>管理员处理合并请求</h2>
<p><strong>审核流程</strong>：管理员通过 <code>File changes</code> 查看提交代码，可提出修改建议或直接批准。<strong>合并操作</strong>：点击 <code>Merge</code> 按钮确认合并，系统⾃动更新贡献者列表。</p>
<h2>协作者设置与直接推送代码</h2>
<p><strong>协作者添加</strong>：路径 <code>Settings</code> → <code>Collaborators</code> → <code>Add people</code>，需被添加者在 GitHub 收件箱确认邀请。<strong>权限优势</strong>：免去 fork 步骤，可直接创建分⽀并 push 到远端仓库，提交 PR 前需先合并主⼲最新改动。</p>
<h2>Cherry Pick（拣选提交）</h2>
<pre><code>flowchart LR
  subgraph feature 分支
    direction LR
    A1[A 蔬菜] --&gt; A2[B 肉类] --&gt; A3[C 主食]
  end
  M[main] --&gt;|cherry-pick A1| M1[main']
  M1 --&gt;|cherry-pick A3| M2[main'']
</code></pre>
<p><strong>应⽤场景</strong>：当需要将特定分⽀的某些提交（⽽⾮全部）合并到当前分⽀时使⽤。例如 feature 分⽀有 3 次提交（蔬菜清单、⾁类清单、主⻝清单），但只需要合并其中两次。</p>
<pre><code># 1. 复制⽬标提交的 ID
# 2. 在⽬标分⽀执⾏ cherry-pick
git checkout main
git cherry-pick &lt;commit-id-1&gt;
git cherry-pick &lt;commit-id-3&gt;
</code></pre>
<p>&lt;span style="color:green"&gt;效果验证&lt;/span&gt;：通过查看分⽀历史记录，确认只有选择的特定提交被合并（如蔬菜和主⻝提交），未选择的提交（如⾁类）则不会包含，且不会产⽣额外的 <code>merge</code> commit 记录。</p>
<h2>Rebase 变基</h2>
<pre><code>flowchart LR
  subgraph merge [git merge]
    direction TB
    M1[main] --- A1[A]
    M1 --- B1[B]
    A1 --&gt; X["merge commit 保留分叉"]
    B1 --&gt; X
  end
  subgraph rebase [git rebase]
    direction TB
    M2[main] --&gt; C1["B' (重放)"]
    C1 --&gt; D1["A' (重放)"]
  end
</code></pre>
<p><strong>定义</strong>：变基操作属于 Git ⾼级操作，通过改变提交的基准点来整理提交历史。与 merge 相比，rebase 不会⽣成 "Merge branch..." 这类合并提交记录，而是将当前分⽀的提交 "嫁接" 到⽬标分⽀的最新提交之后。&lt;span style="color:green"&gt;适⽤场景&lt;/span&gt;：适合个⼈开发分⽀整理历史，&lt;span style="color:red"&gt;不适合多⼈协作分⽀&lt;/span&gt;。</p>
<pre><code># 命令格式：git rebase &lt;⽬标分⽀&gt;
git checkout feature
git rebase main
# 完成后必须强制推送
git push -f
</code></pre>
<p><strong>执⾏步骤</strong>：确保当前位于需要变基的分⽀（如 feature 分⽀）→ 执⾏ rebase 命令指向⽬标分⽀（如 main 分⽀）→ 处理可能的冲突（如有）→ 完成变基后必须使⽤强制推送 <code>git push -f</code>。<strong>注意事项</strong>：变基后原提交会⽣成新的提交 ID（如 C2 变为 C2'），原分⽀的根基提交会变更到⽬标分⽀的最新提交。</p>
<blockquote>
<p>&lt;span style="color:red"&gt;⚠ 警告&lt;/span&gt;：&lt;span style="color:red"&gt;强制推送覆盖远端历史&lt;/span&gt;，&lt;span style="color:red"&gt;多⼈协作分⽀绝不可⽤&lt;/span&gt; &lt;span style="color:red"&gt;git push -f&lt;/span&gt;。</p>
</blockquote>
<p><strong>操作结果验证</strong>：</p>
<p><strong>历史记录特征</strong>：提交历史变为线性结构（如 main 的 C3→C4→C5→feature 的 C2'），原合并分⽀的提交会按时间顺序重新排列，不会出现合并冲突产⽣的特殊提交记录。<strong>强制推送必要性</strong>：因为分⽀根基已改变，普通推送会被拒绝，强制推送前需确保没有其他协作者基于旧历史进⾏开发。</p>
<h2>知识小结</h2>
<table>
<thead>
<tr>
<th>要点</th>
<th>说明</th>
<th>注意事项</th>
<th>重要度</th>
</tr>
</thead>
<tbody>
<tr>
<td>&lt;span style="color:blue"&gt;HEAD&lt;/span&gt; 指针</td>
<td>指向当前所处的 commit 位置；分离头指针（detached &lt;span style="color:blue"&gt;HEAD&lt;/span&gt;）状态易&lt;span style="color:red"&gt;丢失&lt;/span&gt;代码</td>
<td>避免在分离头指针状态修改代码</td>
<td>&lt;span style="color:red"&gt;★★★&lt;/span&gt;☆☆</td>
</tr>
<tr>
<td>后悔药操作</td>
<td>discard 放弃未提交更改；reset 强制回退历史版本；revert 生成反向提交</td>
<td><strong>多人协作分支&lt;span style="color:red"&gt;禁用&lt;/span&gt; reset</strong>，优先用 revert</td>
<td>&lt;span style="color:red"&gt;★★★★&lt;/span&gt;☆</td>
</tr>
<tr>
<td>Work Tree</td>
<td>创建分支的物理副本文件夹，与主仓库并行开发</td>
<td>需通过 <code>merge</code> 将改动合并回主干</td>
<td>&lt;span style="color:red"&gt;★★★&lt;/span&gt;☆☆</td>
</tr>
<tr>
<td>合并&lt;span style="color:red"&gt;冲突&lt;/span&gt;</td>
<td>多个分支修改同一文件同一行时需人工/AI 解决&lt;span style="color:red"&gt;冲突&lt;/span&gt;</td>
<td>AI 可辅助选择保留内容（如 西瓜+草莓）</td>
<td>&lt;span style="color:red"&gt;★★★★&lt;/span&gt;☆</td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;Git&lt;/span&gt; 分区</td>
<td>工作区 → 暂存区 → 本地仓库 → 远端仓库</td>
<td>VS Code 默认合并 add 和 commit 操作</td>
<td>&lt;span style="color:red"&gt;★★★&lt;/span&gt;☆☆</td>
</tr>
<tr>
<td>远端协作</td>
<td>clone 下载仓库；push 上传代码；pull 同步远端改动（含 fetch+<code>merge</code>）</td>
<td><strong>无权限无法直接 push 他人仓库</strong></td>
<td>&lt;span style="color:red"&gt;★★★&lt;/span&gt;☆☆</td>
</tr>
<tr>
<td>&lt;span style="color:blue"&gt;Git&lt;/span&gt;Hub 功能</td>
<td><strong>Fork</strong> 复制项目；<code>Pull Request</code> 合并请求；<code>Issues</code> 问题讨论；&lt;span style="color:blue"&gt;Releases&lt;/span&gt; 版本发布</td>
<td><strong>公开仓库代码可被所有人查看</strong></td>
<td>&lt;span style="color:red"&gt;★★&lt;/span&gt;☆☆☆</td>
</tr>
<tr>
<td>Cherry Pick</td>
<td>选择性合并特定提交（如仅合并蔬菜+主食提交）</td>
<td>需手动指定 commit ID</td>
<td>&lt;span style="color:red"&gt;★★★★&lt;/span&gt;☆</td>
</tr>
<tr>
<td>Stash</td>
<td>临时存储未完成代码（如紧急切换分支时）</td>
<td>区别于暂存区（&lt;span style="color:blue"&gt;Staging Area&lt;/span&gt;）</td>
<td>&lt;span style="color:red"&gt;★★★&lt;/span&gt;☆☆</td>
</tr>
<tr>
<td>Rebase 变基</td>
<td>更改分支根基，保持提交历史线性整洁</td>
<td><strong>必须&lt;span style="color:red"&gt;强制推送&lt;/span&gt;（<code>git push -f</code>），多人分支&lt;span style="color:red"&gt;禁用&lt;/span&gt;</strong></td>
<td>&lt;span style="color:red"&gt;★★★★★&lt;/span&gt;</td>
</tr>
</tbody>
</table>
]]></content>
    <author><name>仙鹤</name></author>
    <category term="笔记"/>
  </entry>
  <entry>
    <title>C 语言</title>
    <link href="https://xianhe51920.github.io/posts/c-language/" rel="alternate" type="text/html"/>
    <id>https://xianhe51920.github.io/posts/c-language/</id>
    <published>2026-08-23T00:00:00.000Z</published>
    <updated>2026-08-23T00:00:00.000Z</updated>
    <summary>C 语言学习笔记</summary>
    <content type="html"><![CDATA[<h1>C 语言</h1>
<h2>第一章 C 语言入门</h2>
<h3>计算机系统组成</h3>
<p>计算机系统由 &lt;span style="color:red"&gt;<strong>硬件系统</strong>&lt;/span&gt; 和 &lt;span style="color:blue"&gt;<strong>软件系统</strong>&lt;/span&gt; 组成，硬件是物理载体，软件是运行其上的指令集合。</p>
<pre><code>flowchart LR
    A["输入设备 （如鼠标、键盘）"] --&gt;|数据流| B["内存储器"]
    C["中央处理器 (CPU)"] --&gt;|数据流| B
    B --&gt;|数据流| D["输出设备 （如声卡、显卡）"]
    C -.-&gt;|地址| B
    C -.-&gt;|控制流| A
    C -.-&gt;|控制流| D
    B &lt;--&gt; E["外存储器"]
</code></pre>
<table>
<thead>
<tr>
<th>组成部分</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>输入设备</td>
<td>鼠标、键盘、麦克风、扫描仪</td>
</tr>
<tr>
<td>输出设备</td>
<td>显示屏、扬声器、打印机</td>
</tr>
<tr>
<td>中央处理器 (CPU)</td>
<td>运算速度非常快，ns 级别</td>
</tr>
<tr>
<td>运算器</td>
<td>算逻单元，负责数据运算</td>
</tr>
<tr>
<td>控制器</td>
<td>帮助 CPU 获取指令交给运算器</td>
</tr>
<tr>
<td>寄存器</td>
<td>存储 CPU 用来运算的数据</td>
</tr>
<tr>
<td>预取器</td>
<td>从内存中获取程序中的指令</td>
</tr>
<tr>
<td>MMU</td>
<td>虚拟内存映射</td>
</tr>
<tr>
<td>内存储器（内存）</td>
<td>断电后数据丢失，读写速度快</td>
</tr>
<tr>
<td>ROM</td>
<td>read only memory</td>
</tr>
<tr>
<td>RAM</td>
<td>random access memory</td>
</tr>
<tr>
<td>外存储器（硬盘）</td>
<td>断电后数据依然存在，读写速度慢</td>
</tr>
</tbody>
</table>
<blockquote>
<p>&lt;span style="color:orange"&gt;<strong>内存靠「电信号」来存储数据。</strong> 断电没！数据存储不能持久化。<strong>优点</strong>：数据读写速度快。&lt;/span&gt;</p>
<p>&lt;span style="color:orange"&gt;<strong>硬盘采用「磁信号」来存储数据。</strong> 断电依然在。数据能持久化存储。<strong>缺点</strong>：数据读写速度慢。&lt;/span&gt;</p>
</blockquote>
<h3>硬件系统</h3>
<pre><code>mindmap
  root((计算机系统 的组成))
    硬件系统
      主机
        中央处理器
          运算器
          控制器
        内存储器
          只读存储器
          随机存储器
      外部设备
        输入设备
        输出设备
        外存储器
    软件系统
      系统软件
        操作系统
        语言处理系统
        系统服务程序
        数据库管理系统
      应用软件
        文字处理软件
        表格处理软件
        辅助设计软件
        实时控制软件
</code></pre>
<table>
<thead>
<tr>
<th>主机部分</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>CPU</td>
<td>中央处理器</td>
</tr>
<tr>
<td>内存储器</td>
<td>内存条</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th>外设部分</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>输入设备</td>
<td>读入</td>
</tr>
<tr>
<td>输出设备</td>
<td>写出</td>
</tr>
<tr>
<td>外存储器</td>
<td>持久化存储</td>
</tr>
</tbody>
</table>
<h3>CPU 内部结构</h3>
<pre><code>flowchart LR
    subgraph CPU
        YS["运算器"]
        KZ["控制器"]
        YQ["寄存器"]
        YXQ["预取器"]
    end
    YS --&gt; YQ
    KZ --&gt; YQ
    KZ --&gt; YXQ
    YQ --&gt; JC["Cache 缓存"]
    YXQ --&gt; JC
    JC --&gt; NC["内存"]
    NC --&gt; YP["硬盘（磁盘）"]
    YP --&gt; WL["网络"]
    JC -.-&gt;|读入| NC
    NC -.-&gt;|写出| YP
</code></pre>
<h3>软件系统</h3>
<h4>系统软件</h4>
<ul>
<li>&lt;span style="color:red"&gt;<strong>操作系统</strong>&lt;/span&gt;
<ul>
<li>优秀的商业公司、开源组织，站出来，编写一套针对硬件的底层程序。管理声卡、显卡、网卡、磁盘等这些硬件。</li>
<li>&lt;span style="color:orange"&gt;<strong>概念</strong>&lt;/span&gt;：操作系统就是管理计算机硬件与软件资源的一个计算机程序。本质：程序！！</li>
</ul>
</li>
</ul>
<pre><code>flowchart TB
    A["播放 MP3"] --&gt;|向下| OS["操作系统"]
    B["网络聊天"] --&gt;|向下| OS
    C["查看"] --&gt;|向下| OS
    OS --&gt;|向下| SK["声卡"]
    OS --&gt;|向下| XK["显卡"]
    OS --&gt;|向下| WK["网卡"]
    OS --&gt;|向下| CP["磁盘"]
    OS -.-&gt;|向上：图形界面| D["图形界面：普通用户，鼠标点点点"]
    OS -.-&gt;|向上：终端界面| E["终端界面：普通用户、开发人员都可以使用"]
    OS -.-&gt;|向上：系统调用| F["系统调用：操作系统提供给开发人员使用的函数"]
</code></pre>
<ul>
<li>操作系统（OS）向下的作用：提供驱动程序，管理硬件</li>
<li>操作系统（OS）向上的作用：
<ul>
<li>图形界面：普通用户，鼠标点点点</li>
<li>终端界面：普通用户、开发人员都可以使用</li>
<li>系统调用：操作系统提供给开发人员使用的函数</li>
</ul>
</li>
</ul>
<table>
<thead>
<tr>
<th>常用操作系统</th>
<th>别名</th>
</tr>
</thead>
<tbody>
<tr>
<td>Windows</td>
<td>微软</td>
</tr>
<tr>
<td>MacOS</td>
<td>苹果</td>
</tr>
<tr>
<td>Linux</td>
<td>开源</td>
</tr>
<tr>
<td>Unix</td>
<td>-</td>
</tr>
</tbody>
</table>
<ul>
<li>&lt;span style="color:red"&gt;<strong>语言系统</strong>&lt;/span&gt;
<ul>
<li>计算机语言：C、C++、Java、Python、PHP …</li>
<li>机器语言：二进制（10101001）</li>
</ul>
</li>
</ul>
<h4>应用软件</h4>
<p>需要根据实际需求，来制定功能。</p>
<ul>
<li>文件处理</li>
<li>图形处理</li>
<li>表格处理</li>
<li>实时控制</li>
</ul>
<h3>编译器和语言</h3>
<table>
<thead>
<tr>
<th>概念</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>编程语言</td>
<td>控制计算机硬件工作，由字母、特殊字符组成，每种编程语言有自己一套规则、语法</td>
</tr>
<tr>
<td>编译器</td>
<td>CPU 只认识 1010010 二进制码，abc、汉字看不懂；编译器将人类易读易写的语言转换成 CPU 能读懂的语言</td>
</tr>
<tr>
<td>跨编译器</td>
<td>语言不同、语法不同，因此使用的编译器不同；Java 使用 javac 编译器，<strong>不能</strong>拿它来编译 C 语言；C 语言使用 gcc 编译器，<strong>不能</strong>拿它来编译 Java 语言</td>
</tr>
</tbody>
</table>
<h3>C 语言简述</h3>
<h4>计算机语言发展史</h4>
<ol>
<li>&lt;span style="color:blue"&gt;<strong>机器语言</strong>&lt;/span&gt;：101100110</li>
<li>&lt;span style="color:blue"&gt;<strong>汇编语言</strong>&lt;/span&gt;：助记符：abc ← 10100101。问题：硬件不同，指令不同。不同架构（CPU）指令集不同。只能支持某一种特定的硬件，<strong>跨平台性差</strong>。衍生出 B 语言。</li>
<li>&lt;span style="color:blue"&gt;<strong>C 语言</strong>&lt;/span&gt;：借助编译器，就能将 C 代码，转换成各种平台使用的指令。<strong>跨平台</strong>。</li>
<li>&lt;span style="color:blue"&gt;<strong>C++、Java、Oc、Python</strong>&lt;/span&gt;：面向对象编程。<strong>程序扩展性好！</strong></li>
<li>&lt;span style="color:blue"&gt;<strong>SQL 语句</strong>&lt;/span&gt;：人类易理解。</li>
</ol>
<blockquote>
<p>&lt;span style="color:red"&gt;<strong>机器生汇编，汇编生 B，B 生 C，C 生万物！！！</strong>&lt;/span&gt;</p>
</blockquote>
<h4>C 语言标准</h4>
<p><strong>标准简史</strong>：</p>
<ol>
<li>1972 年 C 语言在贝尔实验室诞生，丹尼斯·里奇参考 B 语言开发</li>
<li>1970-80 年代，C 语言被广泛应用，产生很多不同的 C 语言版本，程序可移植性比较差</li>
<li>1983 年，美国国家标准委员会（ANSI）成立了一个小组来制定 C 语言的标准：C 语言支持哪些语法、支持哪些功能等</li>
<li>&lt;span style="color:red"&gt;<strong>1989 年，通过了 C 语言的第一个标准，C89 标准</strong>&lt;/span&gt;</li>
<li>1990 年，国际标准化组织（ISO）和国际电工委员会（IEC）将 C89 标准当做国际的 C 语言标准。C90 标准、C89 和 C90 指的是同一个标准</li>
<li>1994 年 ISO 和 IEC 对 C89 标准进行修订，C94 标准，由于并没有增加新的语法特性，还是叫做 C89 或者 C90</li>
<li>1995 年 ISO 和 IEC 再次做了修正，C95 标准</li>
<li>&lt;span style="color:red"&gt;<strong>1999 年 ISO 和 IEC 发布了 C 语言新标准，C 语言第二个标准。在该标准中，新增许多实用的 C 语言语法特性，增加新的关键字、可变长数组等等。C99 标准</strong>&lt;/span&gt;</li>
<li>2007 年，重新修订了 C 语言</li>
<li>&lt;span style="color:red"&gt;<strong>2011 年，发布新的版本。新增了一些语法，泛型、国际化支持。目前为止最新版本是 C11</strong>&lt;/span&gt;</li>
</ol>
<p><strong>标准的影响</strong>：</p>
<ol>
<li>可将 C 语言的标准理解为 C 语言说明书。但，其并没有强制性约束力。
<blockquote>
<p>如：微软拿到标准，认为有些标准不合理，不支持。微软认为某些特性非常好，但标准中没有，微软自己新增语法。</p>
</blockquote>
</li>
<li>如果编译器不支持标准，我们即使使用标准中的语法仍然会报错。</li>
<li>编译器版本也会影响程序。因此，编写程序之前要确定编译器版本。</li>
</ol>
<p><strong>常见的 C/C++ 编译器</strong>：</p>
<table>
<thead>
<tr>
<th>序号</th>
<th>编译器</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Borland C++</td>
<td>宝蓝公司</td>
</tr>
<tr>
<td>2</td>
<td>Intel C++</td>
<td>英特尔编译器</td>
</tr>
<tr>
<td>3</td>
<td>VC++</td>
<td>微软公司</td>
</tr>
<tr>
<td>4</td>
<td>&lt;span style="color:red"&gt;<strong>g++ 编译器</strong>&lt;/span&gt;</td>
<td>gcc 是编译套件，Linux 默认使用的编译器，对标准支持最好</td>
</tr>
</tbody>
</table>
<h4>C 语言的优缺点</h4>
<p><strong>优点</strong>：</p>
<ul>
<li>学习成本低</li>
<li>运行速度快</li>
<li>功能强大</li>
</ul>
<p><strong>缺点</strong>：</p>
<ul>
<li>代码实现周期长</li>
<li>可移植性差</li>
<li>对经验要求高</li>
<li>对平台库依赖多</li>
</ul>
<h4>C 语言的应用领域</h4>
<ol>
<li>服务器</li>
<li>操作系统</li>
<li>上层应用。MFC、QT</li>
<li>嵌入式</li>
<li>人工智能、硬件驱动</li>
<li>中间件</li>
<li>网络攻防、数据安全</li>
<li>大学必修课</li>
<li>名企、外企</li>
</ol>
<h3>C 语言 32 个关键字</h3>
<blockquote>
<p>&lt;span style="color:red"&gt;<strong>32 个关键字：（由系统定义，不能重作其它定义）</strong>&lt;/span&gt;</p>
</blockquote>
<table>
<thead>
<tr>
<th>-</th>
<th>-</th>
<th>-</th>
<th>-</th>
<th>-</th>
</tr>
</thead>
<tbody>
<tr>
<td>auto</td>
<td>break</td>
<td>case</td>
<td>char</td>
<td>const</td>
</tr>
<tr>
<td>continue</td>
<td>default</td>
<td>do</td>
<td>double</td>
<td>else</td>
</tr>
<tr>
<td>enum</td>
<td>extern</td>
<td>float</td>
<td>for</td>
<td>goto</td>
</tr>
<tr>
<td>if</td>
<td>int</td>
<td>long</td>
<td>register</td>
<td>return</td>
</tr>
<tr>
<td>short</td>
<td>signed</td>
<td>sizeof</td>
<td>static</td>
<td>struct</td>
</tr>
<tr>
<td>switch</td>
<td>typedef</td>
<td>unsigned</td>
<td>union</td>
<td>void</td>
</tr>
<tr>
<td>volatile</td>
<td>while</td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<h3>9 种控制语句</h3>
<table>
<thead>
<tr>
<th>序号</th>
<th>语句</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>if () ~ else ~</td>
</tr>
<tr>
<td>2</td>
<td>for () ~</td>
</tr>
<tr>
<td>3</td>
<td>while () ~</td>
</tr>
<tr>
<td>4</td>
<td>do ~ while ()</td>
</tr>
<tr>
<td>5</td>
<td>continue</td>
</tr>
<tr>
<td>6</td>
<td>break</td>
</tr>
<tr>
<td>7</td>
<td>switch</td>
</tr>
<tr>
<td>8</td>
<td>goto</td>
</tr>
<tr>
<td>9</td>
<td>return</td>
</tr>
</tbody>
</table>
<h3>34 种运算符</h3>
<table>
<thead>
<tr>
<th>类别</th>
<th>运算符</th>
</tr>
</thead>
<tbody>
<tr>
<td>算术运算符</td>
<td>+、-、*、/、%、++、--</td>
</tr>
<tr>
<td>关系运算符</td>
<td>&lt;、&lt;=、==、&gt;、&gt;=、!=</td>
</tr>
<tr>
<td>逻辑运算符</td>
<td>!、&amp;&amp;、||</td>
</tr>
<tr>
<td>位运算符</td>
<td>&lt;&lt;、&gt;&gt;、~、|、^、&amp;</td>
</tr>
<tr>
<td>赋值运算符</td>
<td>= 及其扩展</td>
</tr>
<tr>
<td>条件运算符</td>
<td>?:</td>
</tr>
<tr>
<td>逗号运算符</td>
<td>,</td>
</tr>
<tr>
<td>指针运算符</td>
<td>*、&amp;</td>
</tr>
<tr>
<td>求字节数</td>
<td>sizeof</td>
</tr>
<tr>
<td>强制类型转换</td>
<td>(类型)</td>
</tr>
<tr>
<td>分量运算符</td>
<td>.、-&gt;</td>
</tr>
<tr>
<td>下标运算符</td>
<td>[]</td>
</tr>
<tr>
<td>其它</td>
<td>()</td>
</tr>
</tbody>
</table>
<h3>文本编辑 HelloWorld</h3>
<h4>修改计算机显示扩展名</h4>
<p>打开任意一个目录 → 查看 → 文件扩展名（勾选）</p>
<h4>编写第一个 hello world 程序</h4>
<pre><code>#include &lt;stdio.h&gt;

int main(void)
{
    printf("hello world\n");
    return 0;
}
</code></pre>
<h4>编译 hello world 程序 —— 得到机器能识别的二进制码</h4>
<pre><code># 编译 C 程序，得到二进制码
gcc HelloWorld.c -o HelloWorld.exe

# 运行二进制程序
HelloWorld.exe
</code></pre>
<h4>快捷打开 HelloWorld.c 文件所在目录</h4>
<ol>
<li>进入 HelloWorld.c 文件所在目录</li>
<li>直接在「地址栏」中键入 cmd，不需要 cd 目录。切换。</li>
</ol>
<h3>常见 IDE</h3>
<blockquote>
<p><strong>IDE</strong>：集编辑器、编译器、调试器于一身的集合工具。</p>
</blockquote>
<table>
<thead>
<tr>
<th>平台</th>
<th>IDE</th>
</tr>
</thead>
<tbody>
<tr>
<td>Windows</td>
<td>vs2013、vs2015、vs2017、&lt;span style="color:red"&gt;<strong>vs2019</strong>&lt;/span&gt;、Clion、Qt Creator、Eclipse</td>
</tr>
<tr>
<td>MacOS</td>
<td>Xcode、Clion、Qt Creator、Eclipse</td>
</tr>
<tr>
<td>Linux</td>
<td>vi/vim、Clion、Qt Creator、Eclipse</td>
</tr>
</tbody>
</table>
<h3>VS2019 基本使用</h3>
<ol>
<li>打开 VS2019，新建项目 → 选择 <strong>C++</strong> → <strong>Windows</strong> → <strong>控制台</strong></li>
<li>创建项目，指定项目目录</li>
<li>确保保留「解决方案资源管理器」</li>
<li>创建 helloworld.c 文件：右键源文件 → 添加 → 新建项 → 选择 <strong>C++ 文件 (.cpp)</strong> → 修改扩展名为 <code>.c</code> → 命名 <code>helloworld.c</code></li>
<li>编写 helloworld.c 程序</li>
<li>修改字体：
<ul>
<li>工具 → 选项 → 环境 → 字体和颜色 → 选择字体</li>
<li>Ctrl + 鼠标滚轮 放大、缩小字号</li>
</ul>
</li>
<li>编写完成。点击 <strong>本地 Windows 调试器</strong> 图标运行</li>
</ol>
<h3>解决窗口一闪而过问题</h3>
<ol>
<li>
<p>使用 <strong>函数</strong> 解决。在 <code>return 0;</code> 代码前添加一行代码：</p>
<pre><code>system("pause");
</code></pre>
</li>
<li>
<p>修改 VS2019 工具配置属性解决</p>
<ul>
<li>项目名上，右键 → 属性 → 配置属性 → 链接器 → 系统 → 子系统 → 下拉框中选择「<strong>控制台 (/SUBSYSTEM:CONSOLE)</strong>」→ 点击「应用」→ 点击确定。</li>
</ul>
</li>
</ol>
<h3>HelloWorld 释义</h3>
<pre><code>#include &lt;stdio.h&gt;

int main(void)
{
    printf("hello world!\n");
    return 0;
}
</code></pre>
<h4>代码释义</h4>
<table>
<thead>
<tr>
<th>序号</th>
<th>代码</th>
<th>释义</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td><code>#</code></td>
<td>代表引入头文件专用特殊字符</td>
</tr>
<tr>
<td>2</td>
<td>include</td>
<td>引入头文件专用关键字</td>
</tr>
<tr>
<td>3</td>
<td><code>&lt;&gt;</code></td>
<td>用来包裹库头文件名</td>
</tr>
<tr>
<td>4</td>
<td>stdio.h</td>
<td>使用的头文件。因为程序中使用了 <code>printf()</code> 函数，就必须使用该头文件。std = standard，i = input 输入，o = output 输出</td>
</tr>
<tr>
<td>5</td>
<td>int</td>
<td>main 函数返回值为整型，int</td>
</tr>
<tr>
<td>6</td>
<td>main</td>
<td>整个程序的入口函数。任何 .c 程序，有且只有一个 main 函数</td>
</tr>
<tr>
<td>7</td>
<td>(void)</td>
<td>当前 main 函数没有参数</td>
</tr>
<tr>
<td>8</td>
<td><code>{}</code></td>
<td>内部放函数体</td>
</tr>
<tr>
<td>9</td>
<td>printf("helloworld\n")</td>
<td>见下方</td>
</tr>
<tr>
<td>10</td>
<td>return 0;</td>
<td>见下方</td>
</tr>
</tbody>
</table>
<p><strong>printf 详解</strong>：</p>
<ul>
<li>printf()：C 语言向屏幕输出字符使用的函数</li>
<li>"helloworld"：待写出的字符串内容</li>
<li><code>\n</code>：回车换行</li>
</ul>
<p><strong>return 详解</strong>：</p>
<ul>
<li>return：返回。C 程序要求，main 函数要有返回值。借助 return 实现返回</li>
<li>0：成功！因为 int，返回整数</li>
</ul>
<h4>注意事项</h4>
<ol>
<li>程序中使用的所有的字符，<strong>全部是「英文半角」字符</strong>。</li>
<li>程序中，<strong>严格区分大小写</strong>。</li>
<li><code>;</code> 代表一行结束。<strong>不能使用中文「；」，必须是英文</strong>。</li>
</ol>
<h3>代码运行 4 种模式</h3>
<table>
<thead>
<tr>
<th>序号</th>
<th>模式</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Debug x86</td>
<td>以调试模式，运行 32 位程序</td>
</tr>
<tr>
<td>2</td>
<td>Debug x64</td>
<td>以调试模式，运行 64 位程序</td>
</tr>
<tr>
<td>3</td>
<td>Release x86</td>
<td>以发布模式，运行 32 位程序</td>
</tr>
<tr>
<td>4</td>
<td>Release x64</td>
<td>以发布模式，运行 64 位程序</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>Debug</strong>：调试模式。生成的 .exe 文件比 Release 模式生成文件大。带有调试信息。<strong>学习中，只使用该模式</strong>。</p>
<p><strong>Release</strong>：发布模式。生成的 .exe 文件没有调试信息。文件较小。</p>
</blockquote>
<blockquote>
<p><strong>热键</strong>：运行编写的程序。<strong>Ctrl + F5</strong></p>
</blockquote>
<h3>注释</h3>
<ul>
<li>&lt;span style="color:red"&gt;<strong>单行注释</strong>&lt;/span&gt;：<code>// 待注释的内容</code></li>
<li>&lt;span style="color:red"&gt;<strong>多行注释</strong>&lt;/span&gt;：<code>/* 待注释的内容 */</code>
<ul>
<li>多行注释内，可以嵌套单行注释。多行注释之间不能嵌套。</li>
</ul>
</li>
</ul>
<h3>System 函数</h3>
<ul>
<li><strong>作用</strong>：执行 windows 系统中的指定的命令</li>
<li><strong>命令</strong>：
<ul>
<li>pause：暂停</li>
<li>cmd：启动新的终端</li>
<li>calc：唤起 windows 下的计算器</li>
<li>notepad：唤起 windows 下的记事本</li>
<li>mspaint：唤起 windows 下的画图工具</li>
<li>cls：清空当前 windows 终端中的内容</li>
</ul>
</li>
</ul>
<pre><code>#include &lt;stdio.h&gt;     // 引入头文件 stdio.h ，因为下面使用了 printf() 必须添加此头文件。
#include &lt;Windows.h&gt;   // 引入头文件 Windows.h，因为下面使用 Sleep() 函数。

int main(void)         // main 是程序的入口函数。void 表示没有参数。int 表示返回整数。
{
    printf("hello world1\n");   // 打印 helloworld 字符串，到屏幕。 \n 换行之
    printf("hello world2!\n");
    printf("hello world3!\n");
    printf("hello world4!\n");
    printf("hello world5!\n");
    printf("hello world6!\n");
    printf("hello world7!\n");

    Sleep(3000);          // 使当前程序，打印完 helloworld 后，睡眠 3s 钟

    // system("cmd");     // 3s 后，再有机会执行。启动一个新终端。
    // system("calc");    // 3s 后，启动计算器
    // system("notepad"); // 3s 后，启动记事本
    // system("mspaint"); // 3s 后，启动画图工具
    system("cls");        // 3s 后，将当前 终端 清空。

    return 0;             // 因为 main 返回 int 。所以这里有 return 0;
}
</code></pre>
<blockquote>
<p><strong>Sleep() 函数</strong>，指定程序睡眠。<strong>默认单位：毫秒</strong>。需要使用头文件：<code>#include &lt;Windows.h&gt;</code></p>
</blockquote>
<h2>第二章 核心语法（运行过程、常量、变量、标识符）</h2>
<h3>main 函数种类</h3>
<h4>main 函数标准类型</h4>
<ul>
<li>
<p>无参：</p>
<pre><code>int main(void) { return 0; }
</code></pre>
</li>
<li>
<p>有参：</p>
<pre><code>int main(int argc, char *argv[]) { return 0; }
</code></pre>
</li>
</ul>
<h4>main 函数其他类型</h4>
<blockquote>
<p>&lt;span style="color:orange"&gt;<strong>都能正常运行，但不是 main 的标准语法格式。</strong>&lt;/span&gt;</p>
</blockquote>
<pre><code>// 都能正常运行，但不是 main 的标准语法格式。
void main(int argc, char *argv[]);
void main(void);
int main();
int main(void);
main();
main(int argc, char *argv[]);
</code></pre>
<h3>gcc 编译 4 步骤</h3>
<h4>整体过程</h4>
<pre><code>flowchart LR
    SRC["源文件 hello.c"] -.-&gt;|gcc hello.c -o hello.exe| EXE["可执行文件 hello.exe"]
    SRC -.-&gt;|-E| PRE["预处理"]
    PRE --&gt;|hello.i 预处理文件| COM["编译"]
    COM --&gt;|hello.s 汇编文件| ASM["汇编"]
    ASM --&gt;|hello.o 目标文件 （本质：二进制）| LINK["链接"]
    LINK --&gt; EXE
</code></pre>
<h4>预处理</h4>
<ul>
<li><strong>参数</strong>：<code>-E</code></li>
<li><strong>生成的文件</strong>：xxx.i 预处理文件</li>
<li><strong>使用命令</strong>：<code>gcc -E xxx.c -o xxx.i</code></li>
<li><strong>工具</strong>：预处理器（包含在 gcc 编译集合工具中）</li>
</ul>
<p><strong>完成的工作</strong>：</p>
<ol>
<li>
<p>&lt;span style="color:red"&gt;<strong>头文件展开</strong>&lt;/span&gt;</p>
<ul>
<li>展开 stdio.h 文件内容，和源码一起，放到 xxx.i 文件中</li>
<li>&lt;span style="color:red"&gt;<strong>不检查语法错误！</strong>&lt;/span&gt;可以在此阶段提升任意文件</li>
<li>测试命令：<code>gcc -E hello.c -o hello.i -I（大）.（当前目录）</code></li>
</ul>
</li>
<li>
<p>&lt;span style="color:red"&gt;<strong>宏定义替换</strong>&lt;/span&gt;</p>
<ul>
<li>将宏名，替换成宏值</li>
<li><code>#define PI 3.14</code>【解释】：define：创建宏。PI：宏名 3.14：宏值</li>
</ul>
<pre><code>// 测试案例。使用命令： gcc -E hello.c -o hello.i
#include &lt;stdio.h&gt;

// 定义宏
#define PI 3.14

int main(void)
{
    printf("hello world\n");

    // 使用宏
    printf("PI = %f\n", PI);

    return 0;
}
</code></pre>
</li>
<li>
<p>&lt;span style="color:red"&gt;<strong>替换注释</strong>&lt;/span&gt;</p>
<ul>
<li>把单行、多行注释替换成空行</li>
</ul>
</li>
<li>
<p>&lt;span style="color:red"&gt;<strong>展开条件编译</strong>&lt;/span&gt;</p>
<ul>
<li>根据条件来展开代码</li>
</ul>
<pre><code>// 测试案例
#include &lt;stdio.h&gt;

// 定义宏
// #define PI 3.14   // PI 定义与否，直接决定下面的 -----666 是否打印。

int main(void)
{
    printf("hello world\n");

    // 使用宏
    //printf("PI = %f\n", PI);

    // 使用条件编译，含义是：如果定义了 PI ，那么就打印 -----666，否则不打印。
    #ifdef PI
        printf("-----------------6666\n");
    #endif
        return 0;
}
</code></pre>
</li>
</ol>
<h4>编译</h4>
<ul>
<li><strong>参数</strong>：<code>-S</code></li>
<li><strong>生成的文件</strong>：xxx.s 汇编文件</li>
<li><strong>使用命令</strong>：<code>gcc -S xxx.i -o xxx.s</code></li>
<li><strong>工具</strong>：编译器（包含在 gcc 编译集合工具中）</li>
</ul>
<p><strong>完成的工作</strong>：</p>
<ol>
<li>&lt;span style="color:red"&gt;<strong>逐行检查语法错误！【重点】</strong>&lt;/span&gt; —— 编译过程整个 gcc 编译 4 步骤中，最耗时</li>
<li>将 C 程序翻译成汇编指令。得到 .s 汇编文件</li>
</ol>
<pre><code>.file    "hello.c"
.text
.def    ___main; .scl    2; .type    32; .endef
.section .rdata,"dr"
.LC0:
    .ascii "hello world\0"
.LC2:
    .ascii "PI = %f\12\0"
.LC3:
    .ascii "-----------------6666\0"
    .text
    .globl  main
    .def    main; .scl    2; .type    32; .endef
    .seh_proc   main
main:
    pushq   %rbp
    .seh_pushreg    %rbp
    movq    %rsp, %rbp
    .seh_setframe   %rbp, 0
    subq    $32, %rsp
    .seh_stackalloc 32
    .seh_endprologue
    call    ___main
    leaq    .LC0(%rip), %rcx
    call    puts
    movq    %rcx, %rax
    movq    %rdx, %xmm1
    movq    %rax, %rdx
    leaq    .LC2(%rip), %rcx
    call    printf
    leaq    .LC3(%rip), %rcx
    call    puts
    movl    $0, %eax
    addq    $32, %rsp
    popq    %rbp
    ret
    .seh_endproc
    .section .rdata,"dr"
    .align 8
.LC1:
    .long   1374389535
    .long   1074339512
    .ident  "GCC: (x86_64-posix-sjlj-rev0, Built by MinGW-W64 project) 8.1.0"
    .def    puts; .scl    2; .type    32; .endef
    .def    printf; .scl    2; .type    32; .endef
</code></pre>
<h4>汇编</h4>
<ul>
<li><strong>参数</strong>：<code>-c</code></li>
<li><strong>生成的文件</strong>：xxx.o 目标文件（二进制，人类看不懂）</li>
<li><strong>使用命令</strong>：<code>gcc -c xxx.s -o xxx.o</code></li>
<li><strong>工具</strong>：汇编器（包含在 gcc 编译集合工具中）</li>
<li><strong>完成的工作</strong>：
<ul>
<li>翻译：将汇编指令翻译成对应的二进制指令</li>
</ul>
</li>
</ul>
<h4>连接</h4>
<ul>
<li><strong>参数</strong>：无（-o 不是链接阶段参数，是用来指定文件名）</li>
<li><strong>生成的文件</strong>：xxx.exe 可执行文件（二进制，人类看不懂）</li>
<li><strong>使用命令</strong>：<code>gcc xxx.o -o xxx.exe</code></li>
<li><strong>工具</strong>：链接器（包含在 gcc 编译集合工具中）</li>
<li><strong>完成的工作</strong>：
<ul>
<li>库引入</li>
<li>合并并目标文件</li>
<li>合并并启动例程</li>
</ul>
</li>
</ul>
<pre><code># 完整 4 步骤编译流程
gcc -E hello.c -o hello.i
gcc -S hello.i -o hello.s
gcc -c hello.s -o hello.o
gcc hello.o -o hello.exe

# 一句话完成
gcc hello.c -o hello.exe
</code></pre>
<h4>小结</h4>
<blockquote>
<p>&lt;span style="color:orange"&gt;<strong>gcc 编译的 4 个步骤中，每个步骤直接都可相互转换。</strong>&lt;/span&gt;</p>
</blockquote>
<pre><code>flowchart LR
    SRC["源文件 hello.c"] -.-&gt;|gcc hello.c -o hello.exe| EXE["可执行文件 hello.exe"]
    SRC -.-&gt;|-E| PRE["预处理"]
    PRE --&gt;|-S| COM["编译"]
    COM --&gt;|-c| ASM["汇编"]
    ASM --&gt;|无参| LINK["链接"]
    LINK --&gt; EXE
    SRC -.-&gt;|-S| COM
    SRC -.-&gt;|-c| ASM
    SRC -.-&gt;|无参| LINK
</code></pre>
<h3>printf 格式化输出 int</h3>
<pre><code>#include &lt;stdio.h&gt;

#define PI 3.14

int main(void)
{
    int a = 10;

    printf("%d\n", a);       // %d：格式匹配符，匹配整数。
    printf("a = %d\n", a);   // a = 在 "" 中，代表普通字符串，原样输出。
    printf("%d\n", 100);

    printf("%f\n", PI);      // %f：格式匹配符，匹配小数。
    printf("PI = %f\n", PI); // PI = 在 "" 中，代表普通字符串，原样输出。

    printf("%f\n", 3.45678); // %f：格式匹配符，匹配小数。

    int b = 20;

    printf("%d + %d = %d\n", a, b, a+b);   // +、= 在 "" 中，代表普通字符串，原样输出。
    printf("%d + %d = %d\n", 3, 7, 3+7);
    return 0;
}
</code></pre>
<h3>程序调试</h3>
<ul>
<li><strong>前提</strong>：程序，没有语法错误。—— 语法错误 VS 帮我检查。
<ul>
<li>检查程序中出现的逻辑错误！</li>
</ul>
</li>
<li><strong>核心思想</strong>：
<ul>
<li>让程序一行的执行</li>
</ul>
</li>
<li><strong>添加行号</strong>：
<ul>
<li>工具 → 选项 → 文本编辑器 → c/c++ → 行号（勾选）</li>
</ul>
</li>
</ul>
<h4>程序调试流程</h4>
<ol>
<li>添加断点。——可以添加多个
<ol>
<li>鼠标点击待添加断点行，左侧行号前灰色区域。再次点击取消</li>
<li>光标停止在待添加断点行的任意位置，按 F9 添加断点。再次按 F9 取消断点</li>
</ol>
</li>
<li>测试，必须在 <strong>Debug</strong> 模式下进程。<strong>Release</strong> 模式无效</li>
<li>F5 启动调试</li>
<li>断点停止的位置，是尚未执行的指令</li>
<li>开始调试：
<ol>
<li>&lt;span style="color:red"&gt;<strong>逐语句执行</strong>&lt;/span&gt;。逐语句执行下一行（F11）：遇见函数，进入自定义函数内部，逐条跟踪执行</li>
<li>&lt;span style="color:red"&gt;<strong>逐过程执行</strong>&lt;/span&gt;。逐过程执行下一行（F10）：遇见函数，不进入函数内部，逐条跟踪执行</li>
<li>逐断点执行。代码中有多个断点，直接跳转至下一个断点。—— 点击"继续"，无快捷键</li>
<li>跳出函数。跳出当前断点所在的函数。shift + F10</li>
</ol>
</li>
</ol>
<h3>变量</h3>
<h4>变量 3 要素</h4>
<ol>
<li>变量名：用来在程序中使用</li>
<li>变量类型：开辟内存空间大小</li>
<li>变量值：存储的实际数据</li>
</ol>
<pre><code>flowchart LR
    MEM["内存"]
    MEM --&gt; A["a 空间大小：4字节 变量类型：int 变量值：10 10"]
    MEM --&gt; B["b 空间大小：4字节 变量类型：int 变量值：20 20"]
</code></pre>
<h4>变量定义</h4>
<ul>
<li><strong>定义语法</strong>：类型名 变量名 = 变量值（一般定义方法），<code>int m = 57;</code></li>
<li>会开辟内存空间给变量。<strong>变量声明不会开辟内存空间</strong>。</li>
</ul>
<h4>变量声明</h4>
<ul>
<li><strong>语法</strong>：
<ul>
<li><code>int a;</code> 没有变量值的变量定义，叫做声明</li>
<li><code>extern int a;</code> 添加 extern 关键字</li>
</ul>
</li>
<li><strong>特性</strong>：
<ul>
<li>变量要想使用，<strong>必须</strong>有定义</li>
<li>编译器，在使用变量之前，必须能看到变量定义。如果没有看到，编译器会自动寻找一个变量声明。提升成定义</li>
<li>如果 变量声明前，添加了 extern 关键字，无法提升！</li>
</ul>
</li>
</ul>
<h3>常量</h3>
<blockquote>
<p>不会变化、不能被修改的数据。</p>
</blockquote>
<ol>
<li>"hello"、'A'、57、-10、3.1415926（浮点常量）</li>
<li>&lt;span style="color:red"&gt;<strong>#define PI 3.14</strong>&lt;/span&gt; —— 【宏定义】语法：<code>#define 宏名 宏值</code>
<ul>
<li>强调：没有 <code>;</code> 结束标记</li>
<li>强调：没有 <code>=</code></li>
</ul>
</li>
<li><code>const int a = 10;</code> 定义语法：<code>const 类型名 变量名 = 变量值</code>
<ul>
<li>const 关键字：被该关键字修饰的变量，表示为只读变量</li>
</ul>
</li>
</ol>
<p><strong>练习</strong>：</p>
<pre><code>#define PI 3.1415926      // 定义常量

int main(void)
{
    // 圆的面积：S = PI * r * r
    // 圆的周长：L = 2 * PI * r
    int r = 3;             // 变量的定义

    float S = PI * r * r;  // 表达式。作为变量值。
    float L = 2 * PI * r;

    //printf("圆的面积：%f\n", s);// 28.274334  默认显示 6 位小数。
    //printf("圆的周长：%f\n", l);// 18.849556

    //printf("圆的面积：%.2f\n", s);// 28.27
    //printf("圆的周长：%.2f\n", l);// 18.85指定保留小数点后两位，对第 3 位四舍五入

    float m = 3.4567891;

    printf("m=%6.2f\n", m);
    //共显示6位数，包含小数点，保留小数点后两位，对第3位四舍五入，不是6位用空格补齐。

    printf("m=%06.2f\n", m);
    //共显示6位数，包含小数点，保留小数点后两位，对第3位四舍五入，不是6位用0补齐。

    return 0;
}
</code></pre>
<h3>标识符</h3>
<ul>
<li>变量和常量总称</li>
</ul>
<h4>硬性要求</h4>
<ol>
<li>&lt;span style="color:red"&gt;<strong>标识符不能是关键字、函数名。</strong>&lt;/span&gt; system、printf、int、main、return、..</li>
<li>&lt;span style="color:red"&gt;<strong>只能有字母、数字、下划线组成。</strong>&lt;/span&gt; a-z/A-Z/0-9_
<ul>
<li><code>abc_1</code>/<code>abc_2</code>/<code>_abc_1</code>/<code>a_b_c_d</code></li>
</ul>
</li>
<li>&lt;span style="color:red"&gt;<strong>不能以数字开头。</strong>&lt;/span&gt;
<ul>
<li><code>int a5ir = 10;</code> ok</li>
<li><code>int _34F = 6;</code> ok</li>
<li><code>float 98i_54 = 5.4;</code> error</li>
</ul>
</li>
<li>&lt;span style="color:red"&gt;<strong>大小写严格区分。</strong>&lt;/span&gt;
<ul>
<li>通常 使用大写来定义常量。<code>#define MAX 100</code></li>
<li>通常 使用小写来定义变量</li>
</ul>
</li>
</ol>
<h4>命名规范</h4>
<ol>
<li>&lt;span style="color:blue"&gt;<strong>大驼峰法</strong>&lt;/span&gt;：<code>int HelloWorldHahaHohoHehe = 10;</code>
<ul>
<li>多个单词组成变量名，每个单词首字母大写</li>
</ul>
</li>
<li>&lt;span style="color:blue"&gt;<strong>小驼峰法</strong>&lt;/span&gt;：<code>int helloWorldHahaHohoHehe = 10;</code>
<ul>
<li>多个单词组成变量名，首个单词的首字母小写，其余每个单词首字母大写</li>
</ul>
</li>
<li>&lt;span style="color:blue"&gt;<strong>小写 + 下划线</strong>&lt;/span&gt;：<code>int hello_world_haha_hoho_hehe = 10;</code>
<ul>
<li><strong>C 语言专用！</strong></li>
</ul>
</li>
</ol>
<h3>sizeof 关键字</h3>
<ul>
<li>sizeof 不是函数</li>
<li>求变量、数据类型，占用的内存空间大小</li>
<li>使用方法：
<ol>
<li><code>sizeof(变量名)</code> —— 返回变量大小，单位整数字节</li>
<li><code>sizeof(类型名)</code> —— 返回数据类型大小，单位整数字节</li>
<li><code>sizeof 变量名</code> —— 语法 C 语言支持该写法，不推荐使用</li>
</ol>
</li>
</ul>
<h3>整型</h3>
<h4>有符号整型</h4>
<blockquote>
<p>获取数据类型的最小值、最大值，可以使用 <code>#include &lt;limits.h&gt;</code></p>
</blockquote>
<table>
<thead>
<tr>
<th>整型名</th>
<th>名称</th>
<th>格式匹配符</th>
<th>占用的大小</th>
<th>最小值</th>
<th>最大值</th>
</tr>
</thead>
<tbody>
<tr>
<td>int</td>
<td>整型</td>
<td>%d</td>
<td>4 字节</td>
<td>-2147483648</td>
<td>2147483647</td>
</tr>
<tr>
<td>short</td>
<td>短整型</td>
<td>%hd</td>
<td>2 字节</td>
<td>-32768</td>
<td>32767</td>
</tr>
<tr>
<td>long</td>
<td>长整型</td>
<td>%ld</td>
<td>windows：32/64位：4 字节 Linux 下：32位：4 字节、64位：8 字节</td>
<td>-2147483648</td>
<td>2147483647</td>
</tr>
<tr>
<td>long long</td>
<td>长长整型</td>
<td>%lld</td>
<td>8 字节</td>
<td>-9223372036854775808</td>
<td>9223372036854775807</td>
</tr>
</tbody>
</table>
<pre><code>#include &lt;stdio.h&gt;
#include &lt;limits.h&gt;

int main(void)
{
    printf("int大小 = %u\n", sizeof(int));
    printf("int最小值: %d, 最大值: %d\n", INT_MIN, INT_MAX);

    printf("short大小 = %u\n", sizeof(short));
    printf("short最小值: %hd, 最大值: %hd\n", SHRT_MIN, SHRT_MAX);

    printf("long大小 = %u\n", sizeof(long));
    printf("long最小值: %ld, 最大值: %ld\n", LONG_MIN, LONG_MAX);

    printf("long long大小 = %u\n", sizeof(long long));
    printf("long long最小值: %lld, 最大值: %lld\n", LLONG_MIN, LLONG_MAX);

    return 0;
}
</code></pre>
<h4>无符号整型</h4>
<table>
<thead>
<tr>
<th>整型名</th>
<th>名称</th>
<th>格式匹配符</th>
<th>占用的大小</th>
<th>最小值</th>
<th>最大值</th>
</tr>
</thead>
<tbody>
<tr>
<td>unsigned int</td>
<td>无符号整型</td>
<td>%u</td>
<td>4 字节</td>
<td>0</td>
<td>-</td>
</tr>
<tr>
<td>unsigned short</td>
<td>无符号短整型</td>
<td>%hu</td>
<td>2 字节</td>
<td>0</td>
<td>-</td>
</tr>
<tr>
<td>unsigned long</td>
<td>无符号长整型</td>
<td>%lu</td>
<td>windows：32/64位：4 字节 Linux 下：32位：4 字节、64位：8 字节</td>
<td>0</td>
<td>-</td>
</tr>
<tr>
<td>unsigned long long</td>
<td>无符号长长整型</td>
<td>%llu</td>
<td>8 字节</td>
<td>0</td>
<td>-</td>
</tr>
</tbody>
</table>
<h2>第三章 核心语法（数据类型和进制转换）</h2>
<h3>char 类型</h3>
<h4>基础信息</h4>
<ul>
<li>字符型</li>
<li>单位：一个字节（8 bit 位）</li>
<li>格式匹配符：
<ul>
<li>数值型：
<ul>
<li>有符号：<code>%hhd</code> —— char 显示数值专用格式匹配符</li>
<li>无符号：<code>%hhu</code> —— unsigned char 显示数值专用格式匹配符</li>
</ul>
</li>
<li>字符型：<code>%c</code></li>
</ul>
</li>
<li>取值范围：
<ul>
<li>有符号：-128 ~ 127</li>
<li>无符号：0 ~ 255</li>
</ul>
</li>
<li>程序获取：</li>
</ul>
<pre><code>#include &lt;stdio.h&gt;
#include &lt;limits.h&gt;

int main(void)
{
    // 获取无符号数值范围
    printf("char 无符号 min = 0, max = %hhu\n", UCHAR_MAX);
    // 获取有符号数值范围
    printf("char 有符号 min = %hhd, max = %hhd\n", CHAR_MIN, CHAR_MAX);
    // 获取 char 占用的字节数
    printf("char 大小 = %u\n", sizeof(char));
    // 获取 unsigned char 占用的字节数
    printf("unsigned char 大小 = %u\n", sizeof(unsigned char));

    return 0;
}
</code></pre>
<h4>ASCII 码</h4>
<ul>
<li>char 类型数据，数值对应一个 ASCII 码</li>
<li>ASCII 表（American Standard Code for Information Interchange 美国标准信息交换代码）</li>
</ul>
<table>
<thead>
<tr>
<th>字符</th>
<th>ASCII 码</th>
<th>字符</th>
<th>ASCII 码</th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>48</td>
<td>A</td>
<td>65</td>
</tr>
<tr>
<td>1</td>
<td>49</td>
<td>a</td>
<td>97</td>
</tr>
<tr>
<td>9</td>
<td>57</td>
<td>Z</td>
<td>90</td>
</tr>
<tr>
<td>'A'</td>
<td>65</td>
<td>'a'</td>
<td>97</td>
</tr>
<tr>
<td>'0'</td>
<td>48</td>
<td>'\n'</td>
<td>10</td>
</tr>
<tr>
<td>'\t'</td>
<td>9</td>
<td>'\0'</td>
<td>0</td>
</tr>
</tbody>
</table>
<pre><code>#include &lt;stdio.h&gt;

int main(void)
{
    char ch = 'A';   // 定义变量 ch, 指定初值为 'A';

    printf("ch = %c\n", ch);   // c: character. %c 用来显示字符的 格式匹配符。

    ch = 'm';     // 给变量 ch 赋值成 'm', 覆盖 原来的 'A';

    printf("ch = %c\n", ch);

    ch = 97;     // 使用 范围内的数值 97, 给 ch 赋值.

    printf("ch = %c\n", ch);   // 将数值97, 按照字符格式打印输出。

    ch = 98;     // 使用 范围内的数值 98, 给 ch 赋值.

    printf("ch = %c\n", ch);   // 将数值98, 按照字符格式打印输出。

    return 0;
}
</code></pre>
<h4>练习</h4>
<p>将大写字母，转换成小写字母：</p>
<pre><code>#include &lt;stdio.h&gt;

int main(void)
{
    char ch = 'R';   // char 变量定义

    printf("R 转换的小写为: %c\n", ch+32);   // ch+32 表达式，对应格式匹配符 %c

    char ch2 = 'h'; // char 变量定义

    printf("h 转换的大写为: %c\n", ch2-32); // ch2-32 表达式，对应格式匹配符 %c

    char ch3 = '5';
    // 借助字符 5, 利用 ASCII特性, 打印出 字符9
    printf("打印字符9 = %c\n", ch3+4);

    return 0;
}
</code></pre>
<h4>ASCII 表说明</h4>
<ul>
<li>0 ~ 32 ASCII 码 对应的字符都不可见</li>
<li>常用的 ASCII 码：
<ul>
<li><code>'a'</code>：ASCII 码值 97</li>
<li><code>'A'</code>：ASCII 码值 65</li>
<li><code>'0'</code>：ASCII 码值 48</li>
<li><code>'\n'</code>：ASCII 码值 10</li>
<li><code>'\t'</code>：制表符。tab 键对应的字符。（ASCII 码值 9）</li>
<li><code>'\0'</code>：ASCII 码值 0</li>
</ul>
</li>
</ul>
<h4>char 与 printf 对应关系</h4>
<pre><code>flowchart LR
    A["char ch = 'A';"] --&gt; CH["'A'"]
    CH --&gt;|"%hhd / %d / %hd"| V["65 打印 ch 对应的值"]
    CH --&gt;|"%c"| C["A 打印 ch 对应的字符"]
</code></pre>
<p><strong>练习</strong>：在一个 printf 函数中，打印输出 hello 换行 world 换行</p>
<pre><code>// 方法 1
int main(void)
{
    printf("hello\nworld\n");
    return 0;
}

// 方法 2
int main(void)
{
    char ch = '\n'; // 定义 ch 变量，初值为 '\n'

    printf("hello%cworld%c", ch, ch); // 等价于 printf("hello\nworld\n");

    return 0;
}
</code></pre>
<p>一个 printf 中打印 hello（一个 tab 缩进）world 换行：</p>
<pre><code>int main(void)
{
    char ch1 = '\t';   // 实现 一个 tab 缩进
    char ch2 = '\n';   // 实现 一个换行

    printf("hello%cworld%c", ch1, ch2);

    return 0;
}
</code></pre>
<h4>转义字符</h4>
<ul>
<li><code>'/'</code>：自右向左划，叫做「斜杠」</li>
<li><code>'\'</code>：自左向右划，叫做「反斜杠」。<strong>—— 是转义字符</strong></li>
<li>转义字符的作用：
<ol>
<li>将普通字符，转换为 特殊意
<ul>
<li>如：<code>'\n'</code>：这是一个字符，代表换行</li>
<li>如：<code>'\t'</code>：这是一个字符，代表一个制表符</li>
</ul>
</li>
<li>将特殊字符，还原成本身意
<ul>
<li>如：<code>'\\n'</code>：这样就将一个字符 <code>\n</code>，还原成 两个字符：<code>'\'</code> 和 <code>'n'</code></li>
</ul>
</li>
</ol>
</li>
</ul>
<p><strong>练习</strong>：将特殊字符转换成本身意。</p>
<blockquote>
<p>题目：屏幕上严格输出 如下内容：【"\n"的值是 10】要求显示时要有 <code>"</code></p>
</blockquote>
<pre><code>int main(void)
{
    printf("\"\\n\"的值是 %hhd", '\n');
    return 0;
}
</code></pre>
<h3>实型（浮点型）【了解】</h3>
<ul>
<li>显示小数</li>
</ul>
<h4>基础信息</h4>
<ul>
<li><strong>float</strong>：单精度浮点型。%f 大小：4 字节。（可以使用 sizeof() 求取）
<ul>
<li>4.35 默认会被编译器理解为 double 类型</li>
<li><code>float v = 4.567f;</code>：编译器理解 float</li>
<li><code>%f</code> 格式匹配符，默认保留 6 位小数</li>
</ul>
</li>
<li><strong>double</strong>：双精度浮点型。%lf 大小：8 字节。
<ul>
<li><code>double d = 5.68;</code></li>
</ul>
</li>
</ul>
<h4>取值范围</h4>
<p>使用头文件 <code>#include &lt;float.h&gt;</code> 获取浮点型取值范围：</p>
<pre><code>int main(void)
{
    printf("float 范围: %f ~ %f\n", FLT_MIN, FLT_MAX);
    printf("double 范围: %lf ~ %lf\n", DBL_MIN, DBL_MAX);

    return 0;
}
</code></pre>
<h4>精度问题</h4>
<ul>
<li><strong>float 类型</strong>：
<ul>
<li>精度 6~7 位
<ul>
<li>整数部分 + 小数部分 &lt;= 6 位，准确</li>
<li>整数部分 + 小数部分 == 7 位，可能准确，也可能不准确</li>
<li>整数部分 + 小数部分 &gt; 7 位，不准确</li>
</ul>
</li>
</ul>
</li>
<li><strong>double 类型</strong>：
<ul>
<li>精度 15~16 位
<ul>
<li>整数部分 + 小数部分 &lt;= 15 位，准确</li>
<li>整数部分 + 小数部分 == 16 位，可能准确，也可能不准确</li>
<li>整数部分 + 小数部分 &gt; 16 位，不准确</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code>int main(void)
{
    float f = 439243243.9f;

    printf("f = %f\n", f);

    return 0;
}
</code></pre>
<blockquote>
<p>不同平台（操作系统），对应 float、double 类型实现的精度有可能不同。以上是 Windows 下特性。</p>
<p>float 和 double 不存在"无符号"类型。</p>
</blockquote>
<h3>bool 类型</h3>
<blockquote>
<p>C 语言原来没有 bool 类型。C99 标准中，新增了 bool 类型。C++ 自带 bool 类型。</p>
</blockquote>
<ul>
<li>用处：
<ul>
<li>表示：
<ul>
<li>好、坏</li>
<li>真、假</li>
<li>对、错</li>
<li>是、否</li>
</ul>
</li>
<li>取值：
<ul>
<li>true —— 真 —— 1</li>
<li>false —— 假 —— 0</li>
</ul>
</li>
</ul>
</li>
<li>C 语言使用 bool 的条件：
<ul>
<li>编译器要支持 C99 标准</li>
<li>导入 <code>#include &lt;stdbool.h&gt;</code></li>
</ul>
</li>
<li>bool 类型的大小：
<ul>
<li>1 字节。（<code>sizeof()</code> 求取）</li>
</ul>
</li>
<li>bool 没有专用的格式匹配符。打印时，使用 %d 来打印
<ul>
<li>true —— 真 —— 1</li>
<li>false —— 假 —— 0</li>
</ul>
</li>
</ul>
<pre><code>int main(void)
{
    bool aa = true;   // 定义 bool 类型变量 aa, 初值为 true == 1

    printf("aa = %d\n", aa);

    aa = false;   // 给 bool 类型变量 aa 赋值为 false == 0

    printf("aa = %d\n", aa);

    return 0;
}
</code></pre>
<h3>进制和转换</h3>
<ul>
<li>计算机只使用 2 进制</li>
<li>8 进制、10 进制、16 进制，<strong>统统都是给人类用的！！！</strong></li>
</ul>
<h4>存储知识</h4>
<ul>
<li>1 bit 位 就是一个 二进制位，存 0 或 1</li>
<li>一个字节（Byte） 1B = 8 bit 位</li>
<li>"内存单元" 是计算机内存存储的最小单位，一个内存单元 == 1 字节</li>
<li>1KB = 1024B</li>
<li>1MB = 1024KB</li>
<li>1GB = 1024MB</li>
<li>1TB = 1024GB</li>
</ul>
<h4>2 进制</h4>
<ul>
<li>取值 1 或 0，逢 2 进 1，借 1 当 2</li>
<li>没有格式匹配符</li>
</ul>
<p><strong>10 转 2</strong>：</p>
<ul>
<li>除 2 反向取余法</li>
</ul>
<blockquote>
<p>示例：56 → 111000；173 → 1010 1101</p>
</blockquote>
<p><strong>2 转 10</strong>：</p>
<ul>
<li>掌握 2⁰ ~ 2¹⁰ 各自的取值：</li>
</ul>
<table>
<thead>
<tr>
<th>指数</th>
<th>值</th>
<th>指数</th>
<th>值</th>
<th>指数</th>
<th>值</th>
</tr>
</thead>
<tbody>
<tr>
<td>2⁰</td>
<td>1</td>
<td>2⁴</td>
<td>16</td>
<td>2⁸</td>
<td>256</td>
</tr>
<tr>
<td>2¹</td>
<td>2</td>
<td>2⁵</td>
<td>32</td>
<td>2⁹</td>
<td>512</td>
</tr>
<tr>
<td>2²</td>
<td>4</td>
<td>2⁶</td>
<td>64</td>
<td>2¹⁰</td>
<td>1024</td>
</tr>
<tr>
<td>2³</td>
<td>8</td>
<td>2⁷</td>
<td>128</td>
<td>-</td>
<td>-</td>
</tr>
</tbody>
</table>
<ul>
<li>计算方法，对应二进制位，有 1 累加 2 的指数次幂值，有 0 略过</li>
</ul>
<blockquote>
<p>11101 —— 29</p>
<p>10110 —— 2+4+16 = 22</p>
</blockquote>
<h4>8 进制</h4>
<ul>
<li>逢 8 进 1，借 1 当 8</li>
<li>取值：0/1/2/3/4/5/6/7 最大值 7</li>
<li>格式匹配符：<code>%o</code> 和 <code>%#o</code></li>
<li>表示语法：<code>0</code> 开头</li>
<li>8 进制 ——&gt; 10 进制：
<ul>
<li><code>056 == 8⁰ * 6 + 8¹ * 5 = 6 + 40 = 46</code></li>
<li><code>0123 = 8⁰ * 3 + 8¹ * 2 + 8² * 1 = 3 + 16 + 64 = 83</code></li>
</ul>
</li>
<li>10 进制 ——&gt; 8 进制：
<ul>
<li>除 8 反向取余法</li>
<li>135 —— 0207</li>
</ul>
</li>
</ul>
<p><strong>8 转 2</strong>：</p>
<ul>
<li>转换算法：将 8 进制数，自左向右，每一位，用 421 码展开</li>
<li>练习：
<ul>
<li>056 —— 101110</li>
<li>04735 —— 1001 1101 1101</li>
<li>053261 —— 101 0110 1011 0001</li>
</ul>
</li>
</ul>
<p><strong>2 转 8</strong>：</p>
<ul>
<li>转换方法：自右向左，每 3 个二级制位一组，不足 3 位补 0
<ul>
<li>1101010101111010
<ul>
<li>分组：1 101 010 101 111 010</li>
<li>补齐：001 101 010 101 111 010</li>
<li>八进制数为：0152572</li>
</ul>
</li>
<li>101101111011
<ul>
<li>分组：10 110 111 011</li>
<li>补齐：010 110 111 011</li>
<li>八进制数为：02673</li>
</ul>
</li>
</ul>
</li>
</ul>
<h4>16 进制</h4>
<ul>
<li>逢 16 进 1，借 1 当 16</li>
<li>取值：0~9 10-A/a 11-B/b 12-C/c 13-D/d 14-E/e 15-F/f 最大值：F/f</li>
<li>格式匹配符：<code>%x</code> 或 <code>%#x</code></li>
<li>表示语法：<code>0x</code> 开头</li>
<li>16 进制 ——&gt; 10 进制：
<ul>
<li>将每一个 16 进制位，展开 相加得 10 进制</li>
</ul>
</li>
<li>10 进制 ——&gt; 16 进制：
<ul>
<li>除 16 反向取余法</li>
<li>37：0x25</li>
<li>43：0x2B</li>
</ul>
</li>
</ul>
<p><strong>16 转 2</strong>：</p>
<ul>
<li>转换算法：将 16 进制数，自左向右，每一位，用 8421 码展开
<ul>
<li>0xFAC12：1111 1010 1100 0001 0010</li>
</ul>
</li>
</ul>
<p><strong>2 转 16</strong>：</p>
<ul>
<li>转换方法：自右向左，每 4 个二级制位一组，不足 4 位补 0
<ul>
<li>101001011101011001
<ul>
<li>分组：10 1001 0111 0101 1001</li>
<li>补齐：0010 1001 0111 0101 1001</li>
<li>16 进制数为：0x29759</li>
</ul>
</li>
<li>1110110100111101
<ul>
<li>分组：1110 1101 0011 1101</li>
<li>补齐：1110 1101 0011 1101</li>
<li>16 进制数为：0xED3D</li>
</ul>
</li>
</ul>
</li>
</ul>
<h4>小结</h4>
<ul>
<li>使用频率最高的是 10 进制。其次是：16 进制
<ul>
<li><code>int m = 0x15f4;</code></li>
<li><code>int n = 0345;</code></li>
<li><code>int k = 01011101;</code>
<ul>
<li><strong>不能直接将变量值赋值为 2 进制</strong></li>
<li>上述的赋值，会被编译器理解为 8 进制数</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code>#include &lt;stdio.h&gt;

int main(void)
{
    int a = 56;   // 10 进制数作 a 变量初始化。 -- 定义。

    printf("10进制: a = %d\n", a);
    printf("8进制: a = %o\n", a);
    printf("8进制: a = %#o\n", a);  // 在 % 和 #o 中间添加#，可以在输出时，显示 8 进制前缀。
    printf("16进制: a = %x\n", a);
    printf("16进制: a = %#x\n", a); // 在 % 和 #x 中间添加#，可以在输出时，显示 16 进制前缀。

    return 0;
}
</code></pre>
<h4>常用的格式匹配符</h4>
<table>
<thead>
<tr>
<th>格式符</th>
<th>用途</th>
</tr>
</thead>
<tbody>
<tr>
<td>%d</td>
<td>整型</td>
</tr>
<tr>
<td>%c</td>
<td>字符</td>
</tr>
<tr>
<td>%x</td>
<td>16 进制</td>
</tr>
<tr>
<td>%u</td>
<td>无符号</td>
</tr>
<tr>
<td>%s</td>
<td>字符串</td>
</tr>
<tr>
<td>%o</td>
<td>8 进制</td>
</tr>
<tr>
<td>%#o</td>
<td>带前缀 8 进制</td>
</tr>
<tr>
<td>%#x</td>
<td>带前缀 16 进制</td>
</tr>
<tr>
<td>%hhd</td>
<td>char 有符号</td>
</tr>
<tr>
<td>%hd</td>
<td>short 有符号</td>
</tr>
<tr>
<td>%ld</td>
<td>long</td>
</tr>
<tr>
<td>%lld</td>
<td>long long</td>
</tr>
<tr>
<td>%hhu</td>
<td>char / unsigned char 无符号</td>
</tr>
<tr>
<td>%hu</td>
<td>unsigned short</td>
</tr>
<tr>
<td>%lu</td>
<td>unsigned long</td>
</tr>
<tr>
<td>%llu</td>
<td>unsigned long long</td>
</tr>
<tr>
<td>%f</td>
<td>float</td>
</tr>
<tr>
<td>%lf</td>
<td>double</td>
</tr>
</tbody>
</table>
<h3>编码和存储</h3>
<h4>无符号存储</h4>
<pre><code>unsigned int a = 12;   // 占用 4 字节 32 个 bit 位存储。
空间: 00000000 00000000 00000000 00000000   ----  4 字节。
存储: 00000000 00000000 00000000 00001100

unsigned short b = 15;   // 占用 2 字节 16 个 bit 位存储。
空间: 00000000 00000000                ----  2 字节。
存储: 00000000 00001111
</code></pre>
<h4>有符号存储</h4>
<ul>
<li>需要拿出一个二进制位，专门存储符号。标识正、负
<ul>
<li>选用最高位 为符号位</li>
<li>正：0</li>
<li>负：1</li>
</ul>
</li>
</ul>
<p><strong>有符号正数</strong>：</p>
<pre><code>// 采用 "源码" 存储
int a = 5;
空间: 0 0000000 00000000 00000000 00000000
存储: 0 0000000 00000000 00000000 00000101   --- 表示存储正数 5 。
</code></pre>
<p><strong>有符号负数</strong>：</p>
<ul>
<li>有符号的负数，采用「补码」存储
<ul>
<li>源码：数值的二进制制直接存储</li>
<li>反码：符号位不变，将其余数值位取反</li>
<li>补码：反码 + 1</li>
</ul>
</li>
</ul>
<pre><code>int b = -33;
空间: 0 0000000 00000000 00000000 00000000
源码: 1 0000000 00000000 00000000 00100001
反码: 1 1111111 11111111 11111111 11011110
补码: 1 1111111 11111111 11111111 11011111   --- 负 33 在计算机中实际的存储形式。
</code></pre>
<ul>
<li>小结：
<ul>
<li>int —— 4 字节 —— 32 bit 位
<ul>
<li>有符号：31 个数值位，取值范围 -2³¹ ~ 2³¹ - 1 ——&gt; -2147483648 ~ +2147483647</li>
<li>无符号：32 个数值位，取值范围 0 ~ 2³² - 1 ——&gt; 0 ~ 4294967295</li>
</ul>
</li>
<li>short —— 2 字节 —— 16 bit 位
<ul>
<li>有符号：15 个数值位，取值范围 -2¹⁵ ~ 2¹⁵ - 1 ——&gt; -32768 ~ 32767</li>
<li>无符号：16 个数值位，取值范围 0 ~ 2¹⁶ - 1 ——&gt; 0 ~ 65535</li>
</ul>
</li>
</ul>
</li>
<li>因此，知道数据类型占用内存的大小，就能算出该类型 无符号数、有符号数 对应的取值范围。</li>
</ul>
<h3>数据溢出</h3>
<blockquote>
<p>详见练习章节</p>
</blockquote>
<h3>数值溢出</h3>
<ul>
<li>如果 int 类型变量，已经取最大值（2147483647），再给这个变量 +1，就发了溢出
<ul>
<li>上溢出：最大值 + 1</li>
<li>下溢出：最小值 - 1</li>
</ul>
</li>
</ul>
<pre><code>flowchart LR
    A["int unsigned int 0"] --&gt;|→| B["..."]
    B --&gt;|→| C["2³² - 1"]
    C --&gt;|+1| A
    C --&gt;|-1| B
</code></pre>
<h4>无符号数</h4>
<ul>
<li>取值范围 0 ~ 2³² - 1</li>
<li>最大值 + 1 ——&gt; 0</li>
<li>0 - 1 ——&gt; 最大值</li>
</ul>
<pre><code>// 上溢出
int main(void)
{
    unsigned int a = UINT_MAX;   // 取出无符号最大值

    printf("最大值+1 = %d\n", a + 1);  // 0
    printf("最大值+2 = %d\n", a + 2);  // 1

    return 0;
}

// 下溢出
int main(void)
{
    unsigned int a = 0; // 取出无符号最小值

    printf("最小值-1 = %u, 最大值 = %u\n", a - 1, UINT_MAX);

    return 0;
}
</code></pre>
<h4>有符号数</h4>
<p><strong>上溢出</strong>：</p>
<ul>
<li>最大值 + 1，下溢出 == 最小值</li>
</ul>
<pre><code>// unsigned 上溢出。
int main(void)
{
    int a = INT_MAX;   // 取出无符号最小值

    printf("最大值+1 = %d, 最小值 = %d\n", a+1, INT_MIN);

    return 0;
}

// signed 上溢出。
int main(void)
{
    int a = INT_MAX;   // 取出有符号最小值

    printf("最大值+1 = %d, 最小值 = %d\n", a+1, INT_MIN);

    return 0;
}
</code></pre>
<p><strong>下溢出</strong>：</p>
<ul>
<li>最小值 - 1，下溢出 == 最大值</li>
</ul>
<pre><code>// signed 下溢出。
int main(void)
{
    int a = INT_MIN;   // 取出有符号最小值

    printf("最小值-1 = %d, 最大值 = %d\n", a - 1, INT_MAX);

    return 0;
}
</code></pre>
<h4>小结</h4>
<ul>
<li>大部分编译器，对于数值溢出，采用上述处理方式。但，个别编译器有可能不能</li>
<li>受数值溢出影响，进行数据存储时，要选择恰当的数据类型。389482390483209 —— int</li>
</ul>
<h3>不常用关键字（了解）</h3>
<h2>第四章 核心语法（运算符、if、switch、while）</h2>
<ul>
<li><strong>extern</strong>：表示声明。声明没有内存空间，不能提升为定义</li>
<li><strong>const</strong>：限制一个变量为只读。<strong>—— 常量</strong></li>
<li><strong>Volatile</strong>：防止编译优化</li>
<li><strong>register</strong>：定义一个寄存器变量。<strong>没有内存地址</strong></li>
</ul>
<h3>输入输出函数</h3>
<h4>输出函数</h4>
<ul>
<li><strong>printf()</strong>：<code>%d, %c, %x, %u, %s ...</code></li>
<li><strong>putchar() 函数</strong>：
<ul>
<li>输出一个字符到屏幕</li>
<li><code>'abcZ'</code> 是错误的定义，既不是 char 也不是字符串</li>
</ul>
</li>
</ul>
<pre><code>putchar(97);     // 97 == 'a'

putchar('\n');   // 输出换行符。

putchar('b');

putchar('\n');   // 使用率较高。  printf("\n");

putchar('abcZ'); // 'abcZ' 是错误的定义，既不是 char 也不是字符串。
</code></pre>
<h4>输入函数</h4>
<h5>scanf 函数</h5>
<p><strong>使用</strong>：</p>
<ul>
<li>安装指定个格式匹配符，获取指定类型数据</li>
<li>获取整数：</li>
</ul>
<pre><code>int a;   // 可以定义 a 变量（有内存空间），也可以声明（自动提升成定义，有内存空间）
scanf("%d", &amp;a);     // &amp;: 取变量 a 的地址 ---&gt; 拿到 a 的内存空间。
</code></pre>
<ul>
<li>示例：</li>
</ul>
<pre><code>// scanf 输入
int main(void)
{
    //int a = 10;
    int a;   // 在 scanf 执行时，会提升为定义。

    int ret = scanf("%d", &amp;a);  // scanf 可以从键盘获取用户输入，用户根据 %d 输入整数。

    printf("获取的a为: %d\n", a);

    return EXIT_SUCCESS;
}
</code></pre>
<ul>
<li>一次性获取多个整数：</li>
<li>常见的错误书写方法：</li>
</ul>
<pre><code>int a, b, c;   // 一次性创建多个 int 类型变量。

// scanf 可以从键盘获取用户输入，用户根据 多个%d 输入整数。
int ret = scanf("%d%d%d", &amp;a, &amp;b, &amp;c);

// 上述写法编译器无法识别。输入的 123 应该怎样分配给 3 个 %d
</code></pre>
<ul>
<li>正确的书写方式：（推荐）</li>
</ul>
<pre><code>int main(void)
{
    int a, b, c;   // 一次性创建多个 int 类型变量。

    // scanf 可以从键盘获取用户输入，用户根据 多个%d 输入整数。
    int ret = scanf("%d %d %d", &amp;a, &amp;b, &amp;c);

    printf("获取的a为: %d\n", a);
    printf("获取的b为: %d\n", b);
    printf("获取的c为: %d\n", c);

    return EXIT_SUCCESS;
}
</code></pre>
<ul>
<li>获取字符：</li>
</ul>
<pre><code>// scanf 输入，获取字符。
int main(void)
{
    char ch1, ch2, ch3;   // 一次性创建多个 char 变量

    printf("请输入3个字符，用空格隔分: ");

    scanf("%c %c %c", &amp;ch1, &amp;ch2, &amp;ch3);

    printf("ch1 = %c\n", ch1);
    printf("ch2 = %c\n", ch2);
    printf("ch3 = %c\n", ch3);

    return EXIT_SUCCESS;
}
</code></pre>
<p><strong>注意事项</strong>：</p>
<ol>
<li>
<p>&lt;span style="color:red"&gt;<strong>不要随意</strong>&lt;/span&gt; 在 scanf() 函数中，添加 <code>\n</code>。如果添加了，换行符，会被 scanf 当成 格式来约束用户输入</p>
<ol>
<li>printf() 中的 <code>\n</code> 用来屏幕输出 换行</li>
<li>scanf() 函数中的 <code>\n</code>，不能用来输出，因为这是一个输入函数</li>
</ol>
</li>
<li>
<p>解决 VS 中使用 scanf 函数报错 C4996 错误：</p>
<blockquote>
<p>以下是错误描述</p>
<p>C4996 'scanf': This function or variable may be unsafe. Consider using scanf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.</p>
</blockquote>
</li>
<li>
<p>解决方法：在 .c 文件的<strong>第一行</strong>添加 <code>#define _CRT_SECURE_NO_WARNINGS</code></p>
</li>
</ol>
<h5>getchar 函数</h5>
<ul>
<li>直接从键盘接收一个字符，并将得到的字符对应的 ASCII 返回</li>
</ul>
<pre><code>int main(void)
{
    char ch = getchar();  // 定义 char 变量 ch, 接收 getchar() 函数返回值做为初值。

    printf("ch的数值: %d\n", ch);   // %hhd %hd
    printf("ch的字符: %c\n", ch);

    return EXIT_SUCCESS;
}
</code></pre>
<h3>运算符</h3>
<h4>算数运算符</h4>
<ol>
<li><code>+ - * /</code>：先乘除取余，后加减</li>
<li>除法运算后，得到的结果赋值给整型变量，取整数部分。<code>int c = 10/20;</code> ——&gt; 0</li>
<li>除 0：错误操作，不允许。<code>printf("%d\n", 10/0);</code></li>
<li>对 0 取余：错误操作，不允许。<code>printf("%d\n", 123 % 0);</code></li>
<li>不允许对小数取模。35 % 3.4;</li>
<li>对负数取余，结果为余数的绝对值。<code>printf("%d\n", 10 % -3);</code> ——&gt; 1</li>
</ol>
<h4>自增自减运算符</h4>
<ul>
<li>
<p>&lt;span style="color:red"&gt;<strong>前缀自增 (++)，自减 (--)</strong>&lt;/span&gt;</p>
<ul>
<li>
<p>先自增、自减，再取值</p>
<pre><code>int a = 10;
++a;  // 等价于  a = a+1;
</code></pre>
</li>
</ul>
</li>
<li>
<p>&lt;span style="color:red"&gt;<strong>后缀自增 (++)，自减 (--)</strong>&lt;/span&gt;</p>
<ul>
<li>先取值，再自增、自减</li>
</ul>
</li>
</ul>
<pre><code>// ++ / -- 运算符
int main(void)
{
    // 前缀
    int a = 10;
    printf("%d\n", ++a);  // ++a 等价于 a = a+1;

    // 后缀
    int b = 10;
    printf("%d\n", b++);  // b++ 等价于 b = b+1;

    // 不管前缀、后缀、含有变量 ++/-- 表达式执行完后，变量均发生了自增、自减。
    printf("b = %d\n", b);

    return EXIT_SUCCESS;
}
</code></pre>
<h4>赋值运算符</h4>
<ol>
<li><code>=</code>，在计算机中，只能完成"赋值"操作，一定是右边赋值给左边，也叫单向赋值</li>
<li><code>a += 10</code> // 等价于 <code>a = a+10;</code></li>
<li><code>a -= 30</code> // 等价于 <code>a = a-30;</code></li>
<li><code>a %= 5;</code> // 等价于 <code>a = a%5;</code></li>
</ol>
<h4>比较运算符</h4>
<ul>
<li>真：1（非 0），假：0</li>
<li><code>==</code> 判等符（<code>=</code> 不是用来判等）</li>
<li><code>!=</code> 不等</li>
<li><code>&lt;</code> 小于</li>
<li><code>&lt;=</code> 小于等于</li>
<li><code>&gt;</code> 大于</li>
<li><code>&gt;=</code> 大于等于</li>
<li><strong>【强调】</strong>：数学运算中 13 &lt; var &lt; 16 判断，在计算机中，要写成：<code>var &gt; 13 &amp;&amp; var &lt; 16;</code></li>
</ul>
<h4>逻辑运算符</h4>
<ul>
<li>
<p>0 为假，非 0 为真。（非 0：1、27、-9）</p>
</li>
<li>
<p><strong>逻辑非（!）</strong></p>
<ul>
<li>
<p>非真为假，非假为真</p>
<pre><code>// 逻辑运算符
int main(void)
{
    // 逻辑非
    int a = 34;  // 34 是非0， 默认 a 为真。
    int b = 0;

    printf("!a = %d\n", !a);  // a 为真，非a 为假！ ---&gt; 0
    printf("b = %d\n", !b);   // b=0，b 为假，非b，为真。 ---&gt; 1

    return EXIT_SUCCESS;
}
</code></pre>
</li>
</ul>
</li>
<li>
<p><strong>逻辑与（&amp;&amp;）</strong></p>
<ul>
<li>
<p>同真为真，其余为假</p>
<pre><code>printf("=====&amp;&amp;%d\n", a &amp;&amp; !b);  // a为真，!b为真，真&amp;&amp;真 -- 真。 ----&gt; 1
</code></pre>
</li>
</ul>
</li>
<li>
<p><strong>逻辑或（||）</strong></p>
<ul>
<li>
<p>有真为真，同假为假</p>
<pre><code>printf("-----||%d\n", a || b);   // a 为真，!b 为真，真||真 -- 真。 ---&gt; 1
printf("------%d\n", !a || b);   // !a 为假，b 为假，假||假 -- 假。 ---&gt; 0
printf("-----||%d\n", !a || !b); // !a 为假，b 为假，假||假 -- 假。 ---&gt; 0
</code></pre>
</li>
</ul>
</li>
</ul>
<h4>运算符优先级</h4>
<blockquote>
<p><code>() &gt; ++/--</code>（后缀高于前缀）<code>(强转) ! (逻辑非) sizeof &gt; 算数运算符(先乘除取余，后加减) &gt; 比较运算符 &gt; 逻辑运算符 &gt; 三目运算（条件运算）&gt; 赋值运算符 &gt; 逗号运算符</code></p>
</blockquote>
<ul>
<li>一周左右的时间，记忆运算符优先级</li>
</ul>
<h4>逗号运算符</h4>
<pre><code>int x, y, z;
int a = (x=1, y=2, z=3);  // x=1, y=2, z=3 是一个逗号运算符表达式。运算结果为 a = 3;

int a = (x=1, z=3, y=2?);  // 运算结果为 a = 2?;
</code></pre>
<ul>
<li>含有"," 运算符表达式运算结果，是最后一个子表达式的结果</li>
</ul>
<h4>三目运算符</h4>
<ul>
<li>语法：<code>表达式1 ? 表达式2 : 表达式3</code>
<ul>
<li>表达式 1 必须是一个判断表达式</li>
<li>结果为真：整个三目运算，返回表达式 2</li>
<li>结果为假：整个三目运算，返回表达式 3</li>
</ul>
</li>
</ul>
<pre><code>// 三目运算
int main(void)
{
    int a = 40;
    int b = 4;

    //int m = a &gt; b ? 69 : 10;
    //printf("m = %d\n", m);  // m = 69;

    //int m = a &lt; b ? 69 : 10;
    //printf("m = %d\n", m);   // m = 10;

    // 三目运算支持嵌套
    int m = a &lt; b ? 69 : (a&lt;b?3+5); // 先算表达式3，加5，整个三目运算，取表达式3--&gt;5
    printf("m = %d\n", m);   // m = 5;

    int m = a &lt; b ? 69 : a &lt; b ? 3 : 5;
    printf("m = %d\n", m);   // m = 5;

    return EXIT_SUCCESS;
}
</code></pre>
<ul>
<li>如果不使用 <code>()</code>，三目运算默认的结合性，自右向左</li>
</ul>
<h3>类型转换</h3>
<h4>隐式类型转换</h4>
<ol>
<li>编译器自动完成。（小类型转大类型，同类型大小）</li>
</ol>
<pre><code>flowchart TB
    D["高 double"] --&gt; L["long"]
    L --&gt; UI["unsigned int"]
    UI --&gt; SI["低 signed int"]
    CH["char, short"] --&gt; SI
    F["float"] --&gt; D
</code></pre>
<ol>
<li>由赋值产生：</li>
</ol>
<pre><code>int r = 3;
float s = 3.14 * r * r;
// 3.14 默认类型是 double, r 是 int 类型。运算过程中，转换为 double 类型运算。
// 运算结束，赋值给 s 时，转换为 float。
</code></pre>
<ul>
<li>小类型 ——&gt; 大类型。没问题</li>
<li>大类型 ——&gt; 小类型。可能丢失数据
<ul>
<li>VS 中 Ctrl + F7 只编译，检查语法错误，不运行</li>
</ul>
</li>
</ul>
<pre><code>int main(void)
{
    int a = 321;
    char ch = a;   // 用值为 321 的a变量，给 char 类型赋值。

    printf("ch = %d\n", ch);   // 运行输出 65;

    return EXIT_SUCCESS;
}
</code></pre>
<blockquote>
<p>321：2⁸=256 有，128 没有，64 有，32/16/8/4/2 没有，有 1</p>
<p>0000 0000 0000 0000 0000 0001 0100 0001 —— 321 二进制表现形式</p>
<p>0000 0000 —— char 只有一个字节。</p>
<p>赋值后，char 值为：0100 0001 --- 1 + 64 = 65</p>
</blockquote>
<h4>强制类型转换</h4>
<ul>
<li>语法：
<ol>
<li>强制变量：<code>(目标类型) 变量</code></li>
<li>强制表达式：<code>(目标类型) 表达式</code></li>
</ol>
</li>
</ul>
<pre><code>int main(void)
{
    float price = 3.6;   // 单价
    int weight = 4; // 斤数

    //int sum = weight * (int)price;   // 强制变量。
    //printf("sum = %d\n", sum);  // --- 12

    int sum = (int)(weight * price);   // 强制表达式。
    printf("sum = %d\n", sum);  // --- 14

    return EXIT_SUCCESS;
}
</code></pre>
<h3>if 分支语句</h3>
<ul>
<li>if...else 分支语句，实现一种模糊匹配。匹配一个范围</li>
<li>if...else 分支</li>
</ul>
<pre><code>if (判断表达式) {
    判断表达式为真，执行的代码。
}
else
{
    判断表达式为假，执行的代码。
}
</code></pre>
<ul>
<li>示例：</li>
</ul>
<pre><code>int a;

printf("请输入一个数: ");

int ret = scanf("%d", &amp;a);

if (a &gt; 0) {
    printf("a &gt; 0\n");
}
else
{
    printf("a &lt;= 0\n");
}
</code></pre>
<ul>
<li>多个分支逻辑：</li>
</ul>
<pre><code>if (判断表达式1) {
    判断表达式1为真，执行的代码。
}
else if (判断表达式2)
{
    判断表达式1为假，判断表达式2为真，执行的代码。
}
else if (判断表达式3)
{
    判断表达式1为假，判断表达式2为假，判断表达式3为真，执行的代码。
}
...
else
{
    以上所有判断表达式都为假，执行的代码。
}
</code></pre>
<ul>
<li>示例：</li>
</ul>
<pre><code>int score;

printf("请输入学生成绩: ");

scanf("%d", &amp;score);

if (score &gt;= 90)    // 优秀
{
    printf("优秀\n");
}
else if (score &gt;=70 &amp;&amp; score &lt; 90)         // 70 &lt; score &lt; 90 错误写法。
{
    printf("良好\n");
}
else if (score &gt;= 60 &amp;&amp; score &lt; 70)
{
    printf("及格\n");
}
else
{
    printf("差劲\n");
}
</code></pre>
<p><strong>练习</strong>：</p>
<ul>
<li>编写程序，实现 3 只小猪评体重。屏幕输入 3 个小猪的重量，借助 if 分支，找出最重的那只小猪</li>
</ul>
<pre><code>int main(void)
{
    int pig1, pig2, pig3;

    printf("请输入3只小猪的重量: ");

    scanf("%d %d %d", &amp;pig1, &amp;pig2, &amp;pig3);

    if (pig1 &gt; pig2)  // pig1 重
    {
        if (pig1 &gt; pig3)  // 1、这行不能写分号。  2、缩进使用 tab 实现。
        {
            printf("第一只小猪最重，体重为: %d\n", pig1);
        }
        else
        {
            printf("第3只小猪最重，体重为: %d\n", pig3);
        }
    }
    else     // pig2 重
    {
        if (pig2 &gt; pig3)
        {
            printf("第2只小猪最重，体重为: %d\n", pig2);
        }
        else
        {
            printf("第3只小猪最重，体重为: %d\n", pig3);
        }
    }
    return EXIT_SUCCESS;

    // 其他实现方法：
    // 使用逻辑运算  if (pig1 &gt; pig2 &amp;&amp; pig1 &gt; pig3) ---&gt; pig1。
    // 使用三目运算  pig1 &gt; pig2 ? pig1 : pig2;
}
</code></pre>
<ul>
<li>分支语句中，可以嵌套其他分支语句</li>
<li>else 总是找它前面最近的 未配对的 if 组合使用</li>
</ul>
<h3>switch 分支语句</h3>
<ul>
<li>&lt;span style="color:red"&gt;<strong>精确匹配</strong>&lt;/span&gt;。结构较清晰。较 if 语句执行效率略高</li>
<li>&lt;span style="color:red"&gt;<strong>缺点</strong>&lt;/span&gt;：不能直接判断区间，需要借助表达式运算</li>
</ul>
<pre><code>switch (表达式)
{
    case 1:
        执行语句;
        break;   // 表示一个分支执行结束，跳出 switch.
    case 2:
        执行语句;
        break;   // 表示一个分支执行结束，跳出 switch.
    case N:
        执行语句;
        break;   // 表示一个分支执行结束，跳出 switch.
    default:
        其他情况，执行语句;（上述所有的 case 都不满足）
        break;
}
</code></pre>
<ul>
<li>练习：获取学生成绩，给出优良可差</li>
</ul>
<pre><code>int main(void)
{
    int score;

    printf("请输入学生成绩: ");
    scanf("%d", &amp;score);

    // 将 学生成绩，通过运算，得出可以放入 switch case 分支 的 表达式。
    switch (score/10)
    {
        case 10:
            printf("优秀\n");
            break;   // 表示当前分支结束。
        case 9:
            printf("优秀\n");
            break;
        case 8:
            printf("良好\n");
            break;
        case 7:
            printf("良好\n");
            break;
        case 6:
            printf("及格\n");
            break;
        default:         // 所有 case 都不满足的其他情况。
            printf("不及格\n");
            break;
    }
    return EXIT_SUCCESS;
}
</code></pre>
<h3>case 穿透</h3>
<ul>
<li>一个 case 分支，如果没有 break；它执行完本 case 的代码后，会继续向下，执行下一个 case 分支的代码。这称之为 case 穿透</li>
<li>大多数情况下，一个 case 分支，应该对应一个 break</li>
<li>利用 case 穿透</li>
</ul>
<pre><code>switch (score/10)
{
    case 10:        // 故意让 switch 发生 case 穿透
    case 9:
        printf("优秀\n");
        break;
    case 8:         // 故意让 switch 发生 case 穿透
    case 7:
        printf("良好\n");
        break;
    case 6:
        printf("及格\n");
        break;
    default:         // 所有 case 都不满足的其他情况。
        printf("不及格\n");
        break;
}
</code></pre>
<h3>while 循环语句</h3>
<ul>
<li>语法：</li>
</ul>
<pre><code>while (判别表达式)   // 如果为真，执行循环体，如果为假，跳出循环。
{
    循环体
}
</code></pre>
<p><strong>练习</strong>：</p>
<ul>
<li>敲 7：从 1~100 数数，逢 7 和 7 的倍数，敲桌子</li>
<li>分析：
<ul>
<li>7 的倍数：<code>num % 7 == 0</code></li>
<li>个位含 7：<code>num % 10 == 7</code></li>
<li>十位含 7：<code>num / 10 == 7</code></li>
</ul>
</li>
</ul>
<pre><code>int main(void)
{
    //7的倍数： num % 7 == 0
    //个位含7： num % 10 == 7
    //十位含7： num / 10 == 7

    int num = 1;

    while (num &lt;= 100)
    {
        // 判断敲桌子的时机
        // if ((num % 7 == 0) || (num % 10 == 7) || (num / 10 == 7))
        if (num % 7 == 0 || num % 10 == 7 || num / 10 == 7)
        {
            printf("敲桌子!\n");
        }
        else
        {
            printf("%d\n", num);
        }
        num++;
    }
    return EXIT_SUCCESS;
}
</code></pre>
<h3>do while 循环语句</h3>
<ul>
<li>无论如何先执行一次循环体，然后再判断循环是否应该继续</li>
<li>语法：</li>
</ul>
<pre><code>do {
    循环体
} while (判断表达式);
</code></pre>
<p><strong>练习</strong>：</p>
<ul>
<li>求水仙花数。一个三位数（100~999），各个位上数字的立方 和 等于本数字</li>
<li>分析：
<ul>
<li>3 位数：100~999 —— 如：234、861</li>
<li>个位数：<code>int a = num % 10;</code></li>
<li>十位数：<code>int b = num / 10 % 10;</code></li>
<li>百位数：<code>int c = num / 100;</code></li>
</ul>
</li>
</ul>
<pre><code>int main(void)
{
    //个位数: int a = num % 10;
    //十位数: int b = num / 10 % 10;
    //百位数: int c = num / 100;

    int num = 100;   // 数数从 100 开始。
    int a, b, c;     // 定义存储个位、十位、百位 的变量。

    do {
        a = num % 10;        // 求个位数。
        b = num / 10 % 10;
        c = num / 100;

        // 判断 这个数字是否是"水仙花数".
        if (a * a * a + b * b * b + c * c * c == num)
        {
            printf("水仙花数: %d\n", num);
        }
        num++;   // 不断向后数数。

    } while (num &lt;= 999);

    return EXIT_SUCCESS;
}
</code></pre>
<h2>第五章 核心语法（for、数组）</h2>
<h3>配置 VS2019 快捷导入代码</h3>
<ul>
<li>准备 快捷导入代码的脚本文件，保存至系统目录中（位置自定义）：<code>C:\Software\fastCode</code>（如 #1.snippet、#2.snippet、#3.snippet）</li>
<li>在 VS2019 中配置，使用上述目录中的脚本文件
<ul>
<li>工具 —— 代码片段管理器 —— 修改 Basic 为 Visual C++ —— 选择 上述自定义的目录位置</li>
</ul>
</li>
<li>在程序中使用 快捷导入代码
<ul>
<li><code>#1</code> ---- <code>tab</code> 键</li>
</ul>
</li>
</ul>
<h3>for 循环</h3>
<h4>语法</h4>
<pre><code>for (表达式1; 表达式2; 表达式3)
{
    循环体;
}
</code></pre>
<ul>
<li>循环从 <strong>表达式1</strong> 开始 ——&gt; <strong>表达式2</strong>（判别表达式）——&gt; 真 ——&gt; 执行循环体 ——&gt; 表达式 3 ——&gt; 判断表达式 2
<ul>
<li>真：继续 循环体 ——&gt; 表达式 3 ——&gt; 表达式 2 ...</li>
<li>假：跳出循环。（正常情况下，for 循环的出口是 表达式 2）</li>
</ul>
</li>
</ul>
<h4>练习</h4>
<p>使用 for 循环 求 1~100 的和：</p>
<pre><code>// 1~100 求和       1+2+3+4+5+...+100
int main(void)
{
    // 定义循环因子。
    int i = 0;        // 定义 i, 给初值。

    // 定义变量 记录累加值
    int sum = 0;      // 初值为 0
    for (i = 1; i &lt;= 100; i++)
    {
        sum = sum + i;
    }

    // 循环结束，打印 累加结果
    printf("sum = %d\n", sum);

    return EXIT_SUCCESS;
}
</code></pre>
<h3>for 循环的变换形式</h3>
<ul>
<li>
<p>循环因子 i：</p>
<ul>
<li>
<p>在 for 循环之前定义。在 for 循环，结束后依然能使用</p>
</li>
<li>
<p>定义在 for 循环之内。for 循环结束后，不能使用</p>
<pre><code>for (int i = 1; i &lt;= 100; i++)    // 将 i 的定义放到 for 内 表达式1 上。
{
    sum = sum + i;
}

// 循环结束，打印 累加结果
printf("sum = %d. i = %d\n", sum, i);  // 编译器保存，"未定义标识符"
</code></pre>
</li>
</ul>
</li>
<li>
<p>for 循环 3 个表达式，在使用 时，均可省略。但，2 个 <code>;</code> 不允许省略</p>
<ol>
<li>
<p>省略 表达式 1：</p>
<pre><code>int i = 1;  // 定义 循环因子
int sum = 0;

for (; i &lt;= 100; i++)   // 不写表达式1
{
    sum = sum + i;
}
</code></pre>
</li>
<li>
<p>省略 表达式 3：</p>
<pre><code>int i = 1;  // 定义 循环因子
int sum = 0;

for ( ; i &lt;= 100; )    // 不写表达式1，不写表达式3
{
    sum = sum + i;
    i++;         // 将原来的表达式3 写到循环体中。
}
</code></pre>
</li>
<li>
<p>省略 表达式 2：</p>
<pre><code>int i = 1;  // 定义 循环因子

for ( ; ; ) // 不写表达式2，相当于 for ( ; 1; ) 表达式2为 真(1)。这是一个死循环。
{
    printf("i = %d\n", i);
    i++;
}
// for ( ; ; ) 死循环 相当于  while(1) {};
</code></pre>
</li>
</ol>
</li>
<li>
<p>for 每个表达式中，可以含有多个算式：</p>
</li>
</ul>
<pre><code>int i = 0;   // 定义 循环因子
int a = 0;

for (i=1, a=3; a&lt;20, i&lt;10; i++, a+=3)  // i&lt;10, a &lt;20 也可以写成  i&lt;10 &amp;&amp; a&lt;20
{
    printf("i = %d\n", i);
    printf("a = %d\n", a);
}
</code></pre>
<p><strong>死循环</strong>：</p>
<pre><code>// 方法1:
for(;;)
{
}

// 方法2:
while(1)
{
}
</code></pre>
<h4>练习</h4>
<ul>
<li>猜数字游戏：
<ul>
<li>产生一个随机数，用户键盘输入一个数据，程序提示用户，输入的数据 &gt; &lt;= 随机数。用户根据提示不断变换输入，最终猜中！</li>
</ul>
</li>
</ul>
<p><strong>生成随机数</strong>：</p>
<ol>
<li>
<p>添加一个随机数种子。作用：保证随机是真正的随机</p>
<pre><code>srand(time(NULL));  // 固定写法。

// time(NULL) 获取系统当前时间。unsigned long long 类型。
// srand() 函数来生成随机数，使用 系统时间为 算法的 系数。
</code></pre>
</li>
<li>
<p>添加头文件</p>
<pre><code>// srand() --- &lt;stdlib.h&gt;
// time() --- &lt;time.h&gt;
</code></pre>
</li>
<li>
<p>生成随机数：</p>
<pre><code>int n = rand() % 100;          // 随机数范围: 0 ~ 99
int n = rand() % 100 + 13;     // 随机数范围: 13 ~ 112
int n = rand() % 63 + 17;      // 随机数范围: 0~17 ~ 62+17
</code></pre>
</li>
</ol>
<ul>
<li>思路分析：</li>
</ul>
<pre><code>// 伪代码
int main(void)
{
    1. 生成随机数。(3步骤)
    2. 创建死循环，用户猜测    while(1)
        int num ;
    while (1)
    {
        接收用户输入: scanf("%d", &amp;num);
        if 判断用户输入的数据，与实际随机 大小。
            num &gt; 随机数
            提示用户。继续循环。
        num &lt; 随机数
            提示用户。继续循环。
        num == 随机数
            提示用户 猜中！结束循环。
            break;  跳出循环。
    }
    return 0;
}
</code></pre>
<ul>
<li>编码实现：</li>
</ul>
<pre><code>int main(void)
{
    // 播种随机数种子
    srand(time(NULL));

    // 生成随机数 n
    int n = rand() % 100;   // 范围 0~99

    // 定义 num 变量，存储用户输入的数据。
    int num;

    // 创建 死循环，给用户猜数字。
    for (;;)    // while(1) 等价
    {
        printf("请输入猜测的数字: ");
        // 获取用户输入数据
        scanf("%d", &amp;num);

        // 提示用户，测试方向
        if (num &gt; n)
        {
            printf("猜大了!\n");
        }
        // 如果 if分支、for、while 满足后，执行语句只有一条时，{} 可以省略。
        else if (num &lt; n)
        {
            printf("猜小了!\n");  // 语法允许，不写 {}
        }
        else
        {
            printf("猜中了！！！\n");
            break;  // 不必再循环。
        }
    }
    printf("本尊数: %d\n", n);

    return EXIT_SUCCESS;
}
</code></pre>
<h3>嵌套 for 循环</h3>
<pre><code>int i = 0;  // 外层循环的循环因子
int j = 0;  // 内层循环的循环因子

for (i = 0; i &lt; 10; i++)
{
    for (j = 2; j &lt; 10; j++)
    {
        // 循环体
    }
}
</code></pre>
<ul>
<li>结论：外层循环执行一次，内层循环执行一周</li>
</ul>
<h4>练习</h4>
<ul>
<li>模拟电子表打印</li>
<li>分析：</li>
</ul>
<pre><code>// 最外层
for (i = 0; i &lt; 24; i++)
{
    for (j = 0; j &lt; 60; j++)
    {
        for (k = 0; k &lt; 60; k++)
        {
            打印时间。
        }
    }
}
</code></pre>
<pre><code>11:30:54
11:30:56
...
11:31:00
...
12:00:00
12:00:01
</code></pre>
<ul>
<li>实现：</li>
</ul>
<pre><code>int main(void)
{
    int i, j, k;

    for (i = 0; i &lt; 24; i++)  // 小时
    {
        for (j = 0; j &lt; 60; j++)  // 分钟
        {
            for (k = 0; k &lt; 60; k++)  // 秒
            {
                printf("%02d:%02d:%02d\n", i, j, k);
                Sleep(980);
                system("cls");  // 清屏
            }
        }
    }

    return EXIT_SUCCESS;
}
</code></pre>
<h4>练习</h4>
<ul>
<li>打印正序 9x9 乘法表</li>
</ul>
<pre><code>1x1= 1           // 第1行，打印1列
1x2= 2 2x2= 4     // 第2行，打印2列
1x3= 3 2x3= 6 3x3= 9     // 第3行，打印3列
1x4= 4 2x4= 8 3x4=12 4x4=16    // 第4行，打印4列
...
1x9= 9 2x9=18 3x9=27 4x9=36 5x9=45 6x9=54 7x9=63 8x9=72 9x9=81    // 第9行，9列
</code></pre>
<pre><code>jxi =
第 i 行，打印 i 列。
</code></pre>
<ul>
<li>分析 + 实现：</li>
</ul>
<pre><code>for (i = 1; i &lt;= 9; i++)  // 外层，描述行。
{
    for (j = 1; j &lt;= i; j++)  // 内层，描述每一列。
    {
        printf("%dx%d=%d\t", j, i, i*j);
    }
    printf("\n");
}
</code></pre>
<ul>
<li>打印倒序 9x9 乘法表：</li>
</ul>
<pre><code>1x9= 9 2x9=18 3x9=27 4x9=36 5x9=45 6x9=54 7x9=63 8x9=72 9x9=81    // 第1行
1x8= 8 2x8=16 3x8=24 4x8=32 5x8=40 6x8=48 7x8=56 8x8=64
1x7= 7 2x7=14 3x7=21 4x7=28 5x7=35 6x7=42 7x7=49
1x6= 6 2x6=12 3x6=18 4x6=24 5x6=30 6x6=36
1x5= 5 2x5=10 3x5=15 4x5=20 5x5=25
1x4= 4 2x4= 8 3x4=12 4x4=16
1x3= 3 2x3= 6 3x3= 9
1x2= 2 2x2= 4
1x1= 1
</code></pre>
<pre><code>jxi = 值
</code></pre>
<pre><code>for (i = 9; i &gt;= 1; i--)   // i 控制 行
{
    for (j = 1; j &lt;= i; j++)
    {
        printf("%dx%d=%d\t", j, i, i*j);
    }
    putchar('\n');
}

// 上述省略{}写法无误，不推荐。 --- 推荐下面的写法。
int main(void)
{
    int i, j;
    for (i = 9; i &gt;= 1; i--)
    {
        for (j = 1; j &lt;= i; j++)
        {
            printf("%dx%d=%d\t", j, i, j * i);
        }
        putchar('\n');   // printf("\n");
    }
    return EXIT_SUCCESS;
}
</code></pre>
<h3>跳转语句</h3>
<h4>break</h4>
<ul>
<li><strong>作用 1</strong>：
<ul>
<li>一次 break，可以跳出一重循环。（for、while、do while）</li>
</ul>
</li>
<li><strong>作用 2</strong>：
<ul>
<li>防止 case 穿透。结束 switch()。</li>
</ul>
</li>
</ul>
<h4>continue</h4>
<ul>
<li>
<p><strong>作用</strong>：结束 <strong>本次</strong> 循环，continue 关键字之后的代码，在这次循环中，不执行</p>
</li>
<li>
<p>示例 1：</p>
</li>
</ul>
<pre><code>for (int i = 0; i &lt; 5; i++)
{
    printf("==========1=========\n");
    printf("==========2=========\n");
    if (i == 2) {
        continue;
    }
    printf("==========3=========\n");
    printf("==========4=========\n");
    printf("==========5=========\n");
    printf("\n");
}
</code></pre>
<ul>
<li>示例 2：</li>
</ul>
<pre><code>int main(void)
{
    for (int i = 0; i &lt; 10; i++)
    {
        for (int j = 0; j &lt; 5; j++)
        {
            if (j == 2)
            {
                continue;  // 只跳出（后续代码不执行）本次 j == 3 时的循环。
            }
            printf("i = %d, j= %d\n", i, j);
        }
        printf("\n");
    }
    return EXIT_SUCCESS;
}
</code></pre>
<h4>goto</h4>
<ul>
<li>
<p><strong>语法</strong>：</p>
<ol>
<li>设定一个标签。标签名自定义，一般大写。如：ABC、LABLE、AAA</li>
<li>使用 "goto 标签名" 跳转到标签的位置。（只函数内生效）</li>
</ol>
</li>
<li>
<p>示例：</p>
</li>
</ul>
<pre><code>int main(void)
{
    printf("==========1=========\n");
    printf("==========2=========\n");
    printf("==========3=========\n");

    goto LABLE;
    printf("==========4=========\n");
    printf("==========5=========\n");
    printf("==========6=========\n");
    printf("==========7=========\n");

LABLE:
    printf("==========8=========\n");
    printf("==========9=========\n");

    return EXIT_SUCCESS;
}
</code></pre>
<ul>
<li>示例：</li>
</ul>
<pre><code>int main(void)
{
    int i = 0, j = 0;

    for (i = 0; i &lt; 5; i++)
    {
        if (i == 2)
        {
            goto ABC234;
        }
        printf("i = %d\n", i);
    }

    for (j = 0; j &lt; 5; j++)
    {
ABC234:
        printf("j = %d\n", j);
    }

    return EXIT_SUCCESS;
}
</code></pre>
<blockquote>
<p><code>j = 0</code> 的表达式，没有执行。</p>
</blockquote>
<ul>
<li>goto 语法过于灵活，会打乱程序的执行逻辑，降低代码的可读性。<strong>后续编程中，尽量少用</strong>
<ul>
<li>C 程序中，简单的逻辑，依然可以使用 goto。</li>
</ul>
</li>
</ul>
<h3>数组</h3>
<ul>
<li><strong>什么是数组</strong>：
<ul>
<li>数组是，相同数据类型有序的、连续的存储集合</li>
</ul>
</li>
</ul>
<pre><code>// 定义数组，定义10个类型相同、连续、有序存储 的 数据。
int arr[10] = {19, 2, 23, 4, 5, 6, 10, 7, 8, 9};

//printf("arr[0] = %d\n", arr[0]);   // 取数组的第一个元素。
printf("&amp;arr[0] = %p\n", &amp;arr[0]);   // 取数组的第一个元素的 内存地址。
//printf("&amp;arr[0] = %x\n", &amp;arr[0]);   // 内存地址使用 16 机制数表示。
//printf("&amp;arr[0] = %#x\n", &amp;arr[0]);
printf("&amp;arr[1] = %p\n", &amp;arr[1]);
printf("&amp;arr[2] = %p\n", &amp;arr[2]);
printf("&amp;arr[3] = %p\n", &amp;arr[3]);
</code></pre>
<ul>
<li><strong>%p</strong>：
<ul>
<li>用来打印变量内存地址的专用格式匹配符（占位符）</li>
</ul>
</li>
</ul>
<h4>基本特性</h4>
<ol>
<li>
<p>各个元素，连续存储</p>
</li>
<li>
<p>&lt;span style="color:red"&gt;<strong>数组名为地址</strong>&lt;/span&gt;，是数组首个元素的地址。<code>arr == &amp;arr[0]</code></p>
</li>
<li>
<p>求数组的总大小：</p>
<pre><code>printf("数组的大小: %u\n", sizeof(arr));
</code></pre>
</li>
<li>
<p>求数组每一个元素的大小：</p>
<pre><code>printf("数组元素的大小: %u\n", sizeof(arr[0]));
</code></pre>
</li>
<li>
<p>求数组元素的个数：</p>
<pre><code>printf("数组元素的个数: %d\n", sizeof(arr)/sizeof(arr[0]));
</code></pre>
</li>
<li>
<p>数组第一个元素的下标：0</p>
</li>
<li>
<p>数组最后一个元素的下标：</p>
<pre><code>sizeof(arr)/sizeof(arr[0]) - 1
</code></pre>
</li>
</ol>
<h4>数组初始化</h4>
<pre><code>// 初始化方法1
int arr[5] = {3, 7, 2, 1, 9};

// 初始化方法2 [多用]
int arr[5] = {3, 7};        // 剩余未初始化的元素，默认值 0

// 初始化方法3 [多用]
int arr[5] = {0};   // 初始化一个 全部元素为 0 的数组。 ---闭0  常用。

// 初始化方法4 [多用]
int arr[] = {3, 7, 2, 1, 6, 9, 13};   // 编译器会自动求取数组元素个数。

// 初始化方法5
int arr[] = {0};   // 定义了只有一个元素的数组，值为 0

// 初始化方法6 [多用]
int arr[10];   // 声明了一个有10个元素数组。
arr[0] = 5;
arr[1] = 6;
arr[2] = 7;  // 剩余未初始化的元素，默认值 --- 随机数。
</code></pre>
<h4>练习</h4>
<ul>
<li>数组元素逆序</li>
</ul>
<pre><code>// 数组元素逆序
int main(void)
{
    int arr[] = {1, 6, 8, 0, 4, 3, 9, 2};  // 变为：{2, 9, 3, 4, 0, 8, 6, 1}

    // 获取数组的元素个数
    int n = sizeof(arr) / sizeof(arr[0]);

    int i = 0;     // 从前向后
    int j = n-1;   // 从后向前
    int tmp = 0;   // 定义临时变量。

    // 交换数组元素之前，打印数组的所有元素。
    for (int i = 0; i &lt; n; i++)
    {
        printf("%d ", arr[i]);
    }
    putchar('\n');

    // 循环交换数组元素。
    while (i &lt; j)
    {
        tmp = arr[i];     // 三杯水变量交换法
        arr[i] = arr[j];
        arr[j] = tmp;
        i++;   // 不断后移
        j--;   // 不断前移
    }

    // 交换数组元素之后，打印数组的所有元素。
    for (int i = 0; i &lt; n; i++)
    {
        printf("%d ", arr[i]);
    }
    putchar('\n');

    return 0;
}
</code></pre>
<h3>冒泡排序</h3>
<pre><code>flowchart TB
    A0["2 4 0 5 7 8 1 3 9"] --&gt; A1["2 0 4 5 7 1 3 8"]
    A1 --&gt; A2["2 0 4 5 3 1 7 8"]
    A2 --&gt; A3["2 0 4 5 3 1 7 8"]
    A3 --&gt; A4["2 0 4 5 3 1 7 8"]
    A4 --&gt; A5["2 0 4 5 3 1 7"]
    A5 --&gt; A6["2 0 4 5 3 1"]
    A6 --&gt; A7["2 0 4 5 3"]
    A7 --&gt; A8["2 0 4 5"]
    A8 --&gt; A9["2 0 4"]
    A9 --&gt; A10["2 0"]
</code></pre>
<blockquote>
<p>N 个数，证 N-1 阶</p>
</blockquote>
<pre><code>int main(void)
{
    int i, j, tmp;

    int xjp[] = {12, 2, 32, 14, 62, 54, 27, 89, 9, 10, 3};

    // 求取数组元素个数。
    int n = sizeof(xjp) / sizeof(xjp[0]);

    // 开始排序。
    for (i = 0; i &lt; n - 1; i++)   // 外层控制行
    {
        for (j = 0; j &lt; n - 1 - i; j++)  // 内层控制列。

            // 相邻两两比较，三杯水交换
            if (xjp[j] &gt; xjp[j + 1])
            {
                tmp = xjp[j];
                xjp[j] = xjp[j + 1];
                xjp[j + 1] = tmp;
            }
    }
    // 打印排序结果。
    for (i = 0; i &lt; n; i++)
    {
        printf("%d ", xjp[i]);
    }
    printf("\n");

    return EXIT_SUCCESS;
}
</code></pre>
<h2>第六章 核心语法（二维数组、字符串、函数）</h2>
<h3>二维数组</h3>
<h4>基本使用</h4>
<pre><code>int arr[10] = {1,2,3,4,5,6,7}; // 一维数组

{1,2,3,4,5,6,7}
{1,2,3,4,5,6,7}
{1,2,3,4,5,6,7} // 多个一维数组，组成二维数组。
</code></pre>
<h4>定义语法</h4>
<pre><code>int arr[r][c] = {数组元素}
int arr[2][3] =
{
    {2, 5, 8},            // 第0行
    {7, 9, 10}            // 第1行
};
// 常规写法：
int arr[3][5] = {{2, 3, 54, 56, 7}, {2, 67, 4, 35, 9}, {1, 4, 9, 3, 78}};
</code></pre>
<h4>打印</h4>
<pre><code>// 自动补齐的 for 自带的 size_t 来源：
// 查看：方法1：右键 -- 转到定义
//      方法2：F12
typedef unsigned int size_t;   // 给 unsigned int 起别名，叫 size_t

// 以下是打印 2 维数组的方法：
int arr[3][5] = { {2, 3, 54, 56, 7}, {2, 67, 4, 35, 9}, {1, 4, 9, 3, 78} };

for (size_t i = 0; i &lt; 3; i++)   // 行
{
    for (size_t j = 0; j &lt; 5; j++) // 列
    {
        printf("%d ", arr[i][j]);
    }
    printf("\n");
}
</code></pre>
<h4>特性</h4>
<ul>
<li>
<p>数组大小</p>
<pre><code>printf("数组大小：%u\n", sizeof(arr));
</code></pre>
</li>
<li>
<p>一行大小</p>
<pre><code>printf("数组一行大小：%u\n", sizeof(arr[0]));
</code></pre>
</li>
<li>
<p>一个元素大小</p>
<pre><code>printf("数组一个元素大小：%u\n", sizeof(arr[0][0]));
</code></pre>
</li>
<li>
<p>行数</p>
<pre><code>int row = sizeof(arr) / sizeof(arr[0]);   // 数组总大小 / 每行大小
</code></pre>
</li>
<li>
<p>列数</p>
<pre><code>int col = sizeof(arr[0]) / sizeof(arr[0][0]);   // 一行大小 / 每个元素大小
</code></pre>
</li>
<li>
<p>地址合一</p>
<pre><code>数组的地址 == 数组的首元素地址 == 数组的首行地址
printf("%p\n", arr);   // 数组的首地址
printf("%p\n", arr[0]);   // 数组首行地址
printf("%p\n", &amp;arr[0][0]);   // 数组首元素的地址
</code></pre>
</li>
</ul>
<h4>初始化</h4>
<h5>常规初始化</h5>
<pre><code>int arr[3][5] = { {2, 3, 54, 56, 7}, {2, 67, 4, 35, 9}, {1, 4, 9, 3, 78} };
</code></pre>
<h5>不完全初始化</h5>
<pre><code>int arr[3][5] = {{2,3},, {2, 67, 4}, {1, 4, 16, 78}};   // 未被初始化的数值，为0
int arr[3][5] = {0};   // 初值全部为0的二维数组
int arr[3][5] = { 2, 3, 4, 5, 6, 7, 8, 9, 99, 2, 16, 78}; //【少见】系统自动分配行列
</code></pre>
<h5>不完全指定行列初始化</h5>
<pre><code>int arr[][] = {1, 23, 4, 56, 7, 8};  【错误】 // 二维数组定义，至少需要指定 列值。
int arr[][2] = { 1, 23, 4, 56, 7, 8, 10};   // 可以不指定行值。

int row = sizeof(arr) / sizeof(arr[0]);
int col = sizeof(arr[0]) / sizeof(arr[0][0]);

for (size_t i = 0; i &lt; row; i++)   // 行
{
    for (size_t j = 0; j &lt; col; j++)   // 列
    {
        printf("%d ", arr[i][j]);
    }
    printf("\n");
}
</code></pre>
<h4>练习</h4>
<ul>
<li>求出 5 名学生 3 门功课的总成绩。（总成绩：一个学生的总成绩。一门功课的总成绩）</li>
</ul>
<pre><code>int main(void)
{
    int scores[5][3];   // 5个学生，3门功课

    int row = sizeof(scores) / sizeof(scores[0]);
    int col = sizeof(scores[0]) / sizeof(scores[0][0]);

    // 获取 5 个学生 3门功课成绩
    for (size_t i = 0; i &lt; row; i++)
    {
        for (size_t j = 0; j &lt; col; j++)
        {
            scanf("%d", &amp;scores[i][j]);
        }
    }
    // 一门功课的总成绩
    for (size_t i = 0; i &lt; col; i++)   // 一次取出，每个学生的一门功课
    {
        int sum = 0;   // 累加每门功课的分数。
        for (size_t j = 0; j &lt; row; j++)   // 每门功课第几个学生
        {
            sum += scores[j][i];
        }
        printf("第%d门功课总成绩：%d\n", i + 1, sum);
    }

    // 求每个学生的总成绩
    for (size_t i = 0; i &lt; row; i++)   // 每个学生
    {
        int sum = 0;   // 累加每个学生的成绩。

        for (size_t j = 0; j &lt; col; j++)   // 每个学生的成绩
        {
            sum += scores[i][j];  // sum = sum + scores[i][j];
        }
        printf("第%d个学生的总成绩为：%d\n", i+1, sum);
    }
    //printf("-----------------------------------\n");

    ///// 打印 5 个学生 3门功课成绩
    //for (size_t i = 0; i &lt; row; i++)
    //{
    //  for (size_t j = 0; j &lt; col; j++)
    //  {
    //      printf("%d ", scores[i][j]);
    //  }
    //  printf("\n");
    //}
    //system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h3>多维数组（了解）</h3>
<ul>
<li>三维数组：<code>[层][行][列]</code></li>
<li>语法：<code>类型名 数组名[层][行][列]</code></li>
</ul>
<pre><code>int arr[3][3][4] =
{
    {
        {{12, 3, 4, 5}},    // 第0行
        {{12, 3, 4, 5}},    // 第1行
        {{12, 3, 4, 5}}     // 第2行
    }, // 第0层
    {
        {}, // 第0行
        {}, // 第1行
        {}  // 第2行
    }    // 第1层
};
</code></pre>
<ul>
<li>打印：</li>
</ul>
<pre><code>int main(void)
{
    int arr[3][4][2] =
    {
        {
            {1, 2},
            {3, 4},
            {5, 6},
            {7, 8}
        },
        {
            {12, 24},
            {31, 49},
            {5, 46},
            {17, 88}
        },
        {
            {122, 24},
            {311, 419},
            {15, 46},
            {17, 188}
        }
    };

    for (size_t i = 0; i &lt; 3; i++) // 层
    {
        for (size_t j = 0; j &lt; 4; j++) // 行
        {
            for (size_t k = 0; k &lt; 2; k++) // 列
            {
                printf("%d ", arr[i][j][k]);
            }
            printf("\n");
        }
        printf("\n\n");
    }

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<pre><code>int arr[2][3][4] = {1,2,3,4,5,6,7,8,9};

int arr[][3][4] = {1,2,3,4,5,6,7,8,9}; // 层数，可以省略。

数组的首地址 == 首层地址 == 首层首行地址 == 首元素地址。

4维、5维、6维。。。。。N维

int arr[2][3]
short arr[2][3]
float arr[2]
long long arr[2][3][5]
</code></pre>
<h3>字符串</h3>
<ul>
<li>一串字符。C 语言中，一定使用 <code>'\0'</code> 结束。</li>
</ul>
<h4>字符数组和字符串的区别</h4>
<ul>
<li>
<p>字符数组</p>
<pre><code>char str1[5] = {'h','e','l','l','o'};   // 不是字符串，没有 \0
</code></pre>
</li>
<li>
<p>字符串</p>
<pre><code>char str2[6] = {'h','e','l','l','o','\0'};  【麻烦】
char str3[6] = "hello";  // 自动带有 \0 结束标志.
</code></pre>
</li>
</ul>
<h4>字符串输出</h4>
<ul>
<li><code>printf("%s")</code>
<ul>
<li>打印字符串。挨着从字符串的第一个字符顺序向后打印，打印到 <code>'\0'</code> 结束，没碰到 <code>'\0'</code> 不结束。</li>
<li><code>'a' != "a"('a','\0')</code></li>
<li><code>'abc'</code> 是一个错误定义！既不是字符串，也不是有效字符。</li>
</ul>
</li>
</ul>
<h4>其他格式匹配符</h4>
<ul>
<li>
<p><code>%Ns</code>：</p>
<ul>
<li>
<p>显示 N 个字符的字符串，不足 N 用空格向右填充。</p>
<pre><code>printf("%9s\n", str);
</code></pre>
</li>
</ul>
</li>
<li>
<p><code>%0Ns</code>：</p>
<ul>
<li>
<p>显示 N 个字符的字符串，不足 N 用 0 向左填充。</p>
<pre><code>printf("%09s\n", str);
</code></pre>
</li>
</ul>
</li>
<li>
<p><code>%-Ns</code>：</p>
<ul>
<li>
<p>显示 N 个字符的字符串，不足 N 用空格向左填充。</p>
<pre><code>printf("%-9s\n", str);
</code></pre>
</li>
</ul>
</li>
<li>
<p><code>%%</code>：与字符串无直接关系。</p>
<ul>
<li>显示一个 <code>%</code>。转义字符 <code>%</code>，<code>%</code> 无效。转义 <code>%</code>，使用 <code>%%</code> 本身。</li>
<li>输出 [10 % 3 = 1]：<code>printf("10 %% 3 = 1\n");</code></li>
</ul>
</li>
</ul>
<h4>练习</h4>
<ul>
<li>键盘输入字符串，存至 str 中，统计每个字母出现的次数。</li>
<li>分析</li>
</ul>
<pre><code>flowchart LR
    H["h"] --&gt; IDX0["索引 0"]
    E["e"] --&gt; IDX1["索引 1"]
    L1["l"] --&gt; IDX2["索引 2"]
    L2["l"] --&gt; IDX3["索引 3"]
    O["o"] --&gt; IDX4["索引 4"]
    W["w"] --&gt; IDX5["索引 5"]
    O2["o"] --&gt; IDX6["索引 6"]
    R["r"] --&gt; IDX7["索引 7"]
    L3["l"] --&gt; IDX8["索引 8"]
    D["d"] --&gt; IDX9["索引 9"]
    IDX0 --&gt; A["a=97 d=100 l(小写) = 'L' - 'a' 的下标 L(大写) = 'l' - 'A' 的下标 获取d的下标 = 'd' - 'a' = 3"]
</code></pre>
<pre><code>int main(void)
{
    char str[1024] = { 0 };   // 定义有10个元素的字符数组，初值均为0

    for (size_t i = 0; i &lt; 11; i++)
    {
        scanf("%c", &amp;str[i]);  // helloworld
    }
    // 定义一个有26个元素的数组，初始化成 0
    char count[26] = { 0 };   // 代码26个英文字符出现的次数。

    for (size_t i = 0; i &lt; 11; i++)
    {
        int index = str[i] - 'a';    // 提取每个字符，在 count 表中对应的下标。
        count[index]++;    // 向对应字符，位置++，代表该字符出现了一次。
    }

    // 循环遍历 count 数组，打印每个字符，出现的次数。
    for (size_t i = 0; i &lt; 26; i++)
    {
        if (count[i] != 0)   // 0 == \0
        {
            printf("%c字符，在字符串%s中，出现了%d次\n", i+'a', str, count[i]);
        }
    }

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h4>scanf 获取字符串</h4>
<pre><code>char str[1024] = {0};   // 定义字符串存储的空间，保证足够大。
scanf("%s", str);
</code></pre>
<ul>
<li>注意事项：
<ul>
<li>
<p>用于存储字符串的空间，必须足够大！防止溢出。</p>
</li>
<li>
<p><code>%s</code> 遇到空格和 <code>\n</code> 终止。</p>
<pre><code>char str[1024] = {0};
scanf("%s", str);  --- 获取 "hello world haha xixi" 字符串
printf("%s", str);  --- 输出 hello
</code></pre>
</li>
<li>
<p>借助「正则表达式」，可以获取带有空格的字符串。<code>scanf("%[^\n]", str);</code></p>
</li>
</ul>
</li>
</ul>
<h3>字符串操作函数</h3>
<h4>gets</h4>
<ul>
<li>从（键盘）标准输入 stdin 获取字符串。返回字符串首地址，可以获取带有空格的字符串，不保存 <code>\n</code>，将其替换为 0</li>
</ul>
<pre><code>#include &lt;stdio.h&gt;

char *gets(char *s);   // char * 等价于 char []
    参数：用来存储字符串的空间地址。
    返回值：返回实际获取到的字符串的首地址。
// 示例
char str[1024] = {0};
printf("获取的字符串为：%s\n", gets(str));
</code></pre>
<h4>fgets</h4>
<ul>
<li>从（键盘）标准输入 stdin 获取字符串。<strong>一定</strong>会给字符串预留 0 空间。可以获取带有空格的字符串。如果空间足够，保留 <code>\n</code>，如果空间不足，不保留 <code>\n</code>。</li>
</ul>
<pre><code>#include &lt;stdio.h&gt;
char *fgets(char *s, int size, FILE *stream);   // char * 等价于 char []
    参数1：用来存储字符串的空间地址。
    参数2：空间的大小。（严格对应实际空间的大小）
    参数3：读取字符串的位置。 -- stdin（键盘）

    返回值：返回实际获取到的字符串的首地址。

// 示例：
char str[15];
int len = sizeof(str);
printf("获取到的字符串为：%s\n", fgets(str, len, stdin));
</code></pre>
<h4>puts</h4>
<ul>
<li>将字符串输出到 屏幕 标准输出 stdout，输出后会自动向屏幕输出 <code>\n</code></li>
</ul>
<pre><code>// printf("%s\n", "hello")

#include &lt;stdio.h&gt;

int puts(const char *s);   // char * 等价于 char []
    参数：待 写入到屏幕的字符串。
    返回值：
        成功：0，失败：-1
// 示例：
char str[] = "hello world";
int ret = puts(str);
printf("ret = %d\n", ret);
</code></pre>
<h4>fputs</h4>
<ul>
<li>将字符串输出到 屏幕 标准输出 stdout，不自动添加 <code>\n</code> 字符。</li>
</ul>
<pre><code>#include &lt;stdio.h&gt;

int fputs(const char * str, FILE * stream);   // char * 等价于 char []
    参数1：待输出到屏幕的字符串。
    参数2：写入的位置。 -- stdout 标准输出，屏幕。
    返回值：
        成功：0，失败：-1
// 示例：
char str[] = "hello world\n";

int ret = fputs(str, stdout);

printf("ret = %d\n", ret);
</code></pre>
<h4>strlen</h4>
<ul>
<li>作用：获取一个字符串有效字符个数（字符串的长度）。不含 <code>\0</code>（碰到 <code>\0</code> 结束）</li>
</ul>
<pre><code>#include &lt;string.h&gt;

size_t strlen(const char *s);
    参数：待 求长度的字符串
    返回：有效字符个数
// 示例：
char str[] = "hello world\n";
printf("有效长度=%u\n", strlen(str));   // 不含有 \0 长度
printf("sizeof=%u\n", sizeof(str));     // 含有 \0 长度
</code></pre>
<ul>
<li>实现 strlen 函数</li>
</ul>
<pre><code>int main(void)
{
    char str[] = "hello world";

    int i = 0;
    while (str[i] != '\0')
    {
        i++;
    }
    printf("不含\0的字符串长度为：%d\n", i);

    printf("strlen = %d\n", strlen(str)) ;

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h4>字符串追加</h4>
<pre><code>flowchart LR
    subgraph str1
        S1_0["h"]
        S1_1["e"]
        S1_2["l"]
        S1_3["l"]
        S1_4["o"]
        S1_5["\0"]
    end
    str1 --&gt;|i == 5| str3_0["h"]:::target
    subgraph str2
        S2_0["w"]
        S2_1["o"]
        S2_2["r"]
        S2_3["l"]
        S2_4["d"]
        S2_5["\0"]
    end
    str2 --&gt;|j = 0 ++| str3
    subgraph str3
        S3_0["h"]
        S3_1["e"]
        S3_2["l"]
        S3_3["l"]
        S3_4["o"]
        S3_5["w"]
        S3_6["o"]
        S3_7["r"]
        S3_8["l"]
        S3_9["d"]
        S3_10["\0"]
    end
    classDef target fill:#f96
</code></pre>
<pre><code>int main(void)
{
    char str1[] = "hello";
    char str2[] = "world";
    char str3[100];

    // 循环将 str1 中的字符，依次写入到 str3 中
    int i = 0;
    while (str1[i] != '\0')
    {
        str3[i] = str1[i];
        i++;
    }
    // 循环结束，str3 = [hello] 无 \0
    // printf("i = %d\n", i);  --- 循环结束为 5。

    int j = 0;  // 循环 str2

    // 循环将 str2 中的字符，接着str1的内容顺序写入到 str3 中
    while (str2[j])  // while(str2[i] != 0) == while (str2[i] != '\0')
    {
        str3[i+j] = str2[j];
        j++;
    }
    // 循环结束，str3 = [helloworld] 无 \0

    // 手动添加 \0 结束标记
    str3[i + j] = '\0';

    printf("str3 = %s\n", str3);

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h3>函数</h3>
<h4>函数的作用</h4>
<ul>
<li>提高代码复用率。</li>
<li>提高程序模块化组织性。</li>
</ul>
<h4>函数分类</h4>
<ul>
<li>系统库函数：标准库。libc
<ol>
<li>必须引入头文件 <code>#include &lt;xxx.h&gt;</code> ---- 函数声明。</li>
<li>根据函数库函数原型，调用函数。</li>
</ol>
</li>
<li>用户自定义函数：
<ul>
<li><code>bubble_sort()</code>、<code>myPrint()</code></li>
<li>除了需要提供函数原型之外，还需要提供函数实现。</li>
</ul>
</li>
</ul>
<h4>使用函数</h4>
<ul>
<li>函数定义、函数声明、函数调用</li>
</ul>
<h5>函数定义</h5>
<ul>
<li>函数定义必须包含"函数原型" 和 "函数体"
<ul>
<li>
<p>函数原型：返回值类型 + 函数名 + 形参列表。</p>
<ul>
<li>
<p>形参列表：形参参数列表。一定包含：类型名、形参名</p>
<pre><code>// 加法函数
int add(int a, int b)
</code></pre>
</li>
</ul>
</li>
<li>
<p>函数体：一对 <code>{}</code> 包裹函数实现。</p>
<pre><code>// 例子：
int add(int a, int b)
{
    int ret = a + b;
    return ret;
}
// int test(char ch, short b, int arr[], int m)
</code></pre>
</li>
</ul>
</li>
</ul>
<h5>函数调用</h5>
<ul>
<li>包含：函数名(实参列表)：
<ul>
<li>
<p>实参（实际参数）：在调用时，传参必须严格按照形参填充。（参数个数、类型、顺序）</p>
</li>
<li>
<p>实参在调用时，没有 类型描述符。</p>
<pre><code>// 例子
int m = 10;
int n = 20;
int ret = add(m, n);
</code></pre>
</li>
</ul>
</li>
</ul>
<h5>函数声明</h5>
<ul>
<li>包含：函数原型(返回值类型 + 函数名 + 形参列表) + ";"</li>
<li>要求，在函数调用之前，编译器，必须见过函数定义。否则需要函数声明。</li>
<li>如果，没有函数声明，编译器默认做"隐式声明"。
<ul>
<li>隐式声明：【不要依赖】
<ul>
<li>编译器认为所有的函数，返回值都是 int。</li>
<li>可以根据函数调用，推测函数原型。</li>
</ul>
</li>
</ul>
</li>
<li><code>#include &lt;xxx.h&gt;</code> 内部，包含 函数声明。</li>
</ul>
<h4>exit 函数</h4>
<ul>
<li><code>return</code> 关键字：
<ul>
<li>返回当前函数调用。将返回值返回调用者。（在底层，会调用 <code>_exit()</code> 函数。）</li>
</ul>
</li>
<li><code>exit()</code> 函数：
<ul>
<li>退出当前程序</li>
</ul>
</li>
</ul>
<pre><code>// 函数声明
// int test(int a, char ch);
int test(int, char); // 函数声明de 简化写法。声明时，形参名 可以省略。

int main(void)
{
    // 函数调用
    int ret = test(10, 'a');  // test函数，调用结束，return 给 main

    printf("test函数返回: ret = %d\n", ret);

    // return 0;   // 返回给调用者（自动例程）-- 作用：结束程序。
    exit(0);   // 结束程序。
}

// 函数定义
int test(int a, char ch)
{
    printf("a = %d\n", a);
    printf("ch = %c\n", ch);

    // return 97;   // 返回给调用者。程序不结束。
    // 结束程序
    exit(97);   // 使用 #include &lt;stdlib.h&gt;
}
</code></pre>
<h4>多文件编程</h4>
<ul>
<li>
<p>头文件守卫：为了防止头文件被重复包含。 --- head.h</p>
<ol>
<li>
<p><code>#pragma once</code> 是 VS 自动生成的。只应用于 windows 系统。</p>
<pre><code>#ifndef _HEAD_H_    // 习惯写成这样。
#define _HEAD_H_
//... 头文件内容：#include &lt;xx.h&gt;/宏定义 #define PI 3.14/函数声明/类型定义
#endif

// 示例：
#ifndef _HEAD_H_    // 标准引入"头文件守卫"
#define _HEAD_H_

// include 头文件
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;math.h&gt;
#include &lt;time.h&gt;

// 函数声明
int add(int a, int b);
int sub(int a, int b);
// 宏定义
#define PI 3.14

// 类型定义


#endif
</code></pre>
</li>
</ol>
</li>
<li>
<p><code>&lt;&gt;</code> 包含的是，系统库头文件。</p>
</li>
<li>
<p><code>""</code> 包裹的是，用户自定义头文件。</p>
</li>
</ul>
<pre><code>// main函数所在的 .c 文件中：
#include "head.h"
</code></pre>
<h2>第七章 高级语法（指针）</h2>
<h3>指针</h3>
<h4>指针和内存单元</h4>
<ul>
<li>指针：地址！</li>
<li>指针变量：用存储地址的变量！</li>
<li>内存单元：是计算机中内存最小的存储单位。内存单元大小 —— 1字节(8bit位)。
<ul>
<li>每个内存单元，都有一个唯一的编号。</li>
<li>这个内存单元的编号，称之为"地址"</li>
</ul>
</li>
</ul>
<pre><code>flowchart TB
    subgraph 内存
        M3["0000 0000"] --- M2["0000 0000"] --- M1["0000 0000"] --- M0["0000 1010"]
    end
    M3 --- A3["0xff03"]
    M2 --- A2["0xff02"]
    M1 --- A1["0xff01"]
    M0 --- A0["0xff00"]
    A0 -.-&gt; M0
    M3 --- HIGH["高"]
    A0 --- LOW["低"]
</code></pre>
<blockquote>
<p><code>int a = 10;</code> 占用 4 字节（由 int 决定）</p>
</blockquote>
<h4>指针的定义和使用</h4>
<pre><code>int a = 10;
int *p = &amp;a;

// printf("%p = %d\n", *p);  // 间接引用 -- 右值。
// 解引用、间接引用。
*p = 250;   // 间接引用 -- 左值。
printf("a = %d\n", a);
</code></pre>
<ul>
<li><code>*p</code> 作用：
<ul>
<li>将 p 变量的内容，取出，当成地址看待。找到该地址对应的内存空间。
<ul>
<li>如果做左值，存储数据到空间中。</li>
<li>如果做右值，取出空间中的内容。</li>
</ul>
</li>
</ul>
</li>
</ul>
<h4>指针类型大小</h4>
<ul>
<li>指针，算一种自定义数据类型。<code>int *</code></li>
<li>指针的大小，与类型无关！只与当前使用的平台架构有关（32位：4字节、64位：8字节）。</li>
</ul>
<pre><code>int main(void)
{
    printf("int * 的大小：%u\n", sizeof(int*));
    printf("short * 的大小：%u\n", sizeof(short*));
    printf("char * 的大小：%u\n", sizeof(char*));
    printf("long * 的大小：%u\n", sizeof(long*));
    printf("long long * 的大小：%u\n", sizeof(long long *));
}
</code></pre>
<ul>
<li>可以在一条语句中，同时定义多个指针变量、普通变量。</li>
</ul>
<pre><code>int a, b, c;   // 多个普通变量

int *p1, *p2, *p3;   // 多个指针变量。 每个变量都有一个自己的 *

int a, *b, *c, d;   // 定义整型变量a、d，同时定义指针变量 b、c
</code></pre>
<h4>空指针和野指针</h4>
<h5>野指针</h5>
<ol>
<li>
<p>没有使用"有效"的地址，给指针初始化。</p>
<pre><code>int* p;   // 没有给 p 指定一个有效地址。
*p = 1000;
</code></pre>
</li>
<li>
<p>p 指针变量有值，但是，该值不是一个有效的地址。</p>
<pre><code>int* p = 10;   // 没有给 p 指定一个有效地址。
*p = 1000;
</code></pre>
</li>
</ol>
<p>【结论】：编程时，杜绝野指针。</p>
<h5>空指针</h5>
<ul>
<li><code>NULL == 0 == '\0'</code></li>
</ul>
<pre><code>int* p = NULL;   // #define NULL ((void *)0)   // 定义一个空指针。
*p = 1000;       // *p 所指向的内存空间，是一个"无效访问区域"。
</code></pre>
<h4>泛型指针（万能指针 void *）</h4>
<ul>
<li>可以接受任意一种变量的地址，但是，在使用时【必须】借助"强制类型转换"具体化数据类型。</li>
<li><code>void *</code> 类型的大小。32位：4字节。64位：8字节。</li>
</ul>
<pre><code>int main(void)
{
    printf("void * 的大小：%u\n", sizeof(void *));

    char ch = 'R';
    void* p;       // 泛型指针（万能指针）

    p = &amp;ch;

    printf("*p = %c", *((char *)p));   // 将p变量类型，(i)void* 强制成 char *

    return EXIT_SUCCESS;
}
</code></pre>
<h4>const 关键字</h4>
<h5>修饰变量</h5>
<pre><code>const int a = 20;   // a 为只读变量，不能修改。
//*a = 200;   // 不可以修改
int* p = &amp;a;
*p = 677;
printf("a = %d\n", a);   // 借助指针可以修改 const 普通变量的值。
</code></pre>
<h5>修饰指针</h5>
<pre><code>// 方式1
const int *p;   // 向后（右）作用

// 示例：
int a = 10;
int b = 20;
const int *p = &amp;a;
*p = 500;   //【失败】：将 a 的值，改为 500。不能改！
p = &amp;b;   //【成功】：可以修改 p 变量的内容（地址）。

// 方式2
int const *p;   // 向后（右）作用
// 作用方式同上！
</code></pre>
<ul>
<li>常用：在函数内，限制指针所指向的内存空间，为只读（不允许修改）</li>
</ul>
<h4>指针和数组</h4>
<h5>数组名</h5>
<ul>
<li>数组名，是地址常量。 —— 不可以被修改。赋值（=、+=、-=、*=、/=、%= 带有副作用的运算符）</li>
</ul>
<pre><code>int a[3] = { 1,2,3 };   // a 就是数组地址。
int b[3];     // b 是常量
b = a;   // 不允许！！因为 b 是地址常量。
</code></pre>
<ul>
<li>指针，是变量。可以使用数组名，给指针赋值。</li>
</ul>
<pre><code>int *p = a;   // a 就是数组地址。允许！！
</code></pre>
<h5>取数组元素</h5>
<pre><code>int arr[] = {1, 3, 5, 7, 9};
int *p = arr;   // 使用数组地址，给p指针变量初始化

// 结论：
arr[0] == *(arr+0) == p[0] == *(p+0)
arr[1] == *(arr+1) == p[1] == *(p+1)
arr[2] == *(arr+2) == p[2] == *(p+2)
...
arr[N] == *(arr+N) == p[N] == *(p+N)
</code></pre>
<h5>指针和数组名区别</h5>
<ol>
<li>指针是变量，数组名是常量。</li>
<li><code>sizeof（指针）</code> ——&gt; 4字节、8字节。</li>
<li><code>sizeof（数组名）</code> ——&gt; 数组实际的字节数。</li>
</ol>
<h5>指针的算术运算</h5>
<h6>数据类型对指针的作用</h6>
<ol>
<li>
<p>间接引用（解引用）：</p>
<ul>
<li>指针的数据类型，确定了从指针存储的地址开始，向后读取的字节数。（与指针本身存储空间无关）</li>
</ul>
<pre><code>int a = 0x12345678;

//int* p = &amp;a;
//int* p;   --- 0x12345678;
//short* p;   --- 0x5678;
char* p;   //  --- 0x78;
p = &amp;a;

printf("%#x\n", *p);
</code></pre>
</li>
<li>
<p>加减运算。</p>
<ul>
<li>指针的数据类型，决定了指针进行 +/- 操作时，向后/前 跳过的字节数。</li>
</ul>
<pre><code>int *    +1   实际加过 4 字节。
short *  +1   实际加过 2 字节。
char *   +1   实际加过 1 字节。
long long *  +1   实际加过 8 字节。
</code></pre>
</li>
</ol>
<h6>指针++操作数组</h6>
<pre><code>int main(void)
{
    int arr[] = {1,2,3,4,5,6,7,8,9,0};
    int* p = arr;

    int n = sizeof(arr) / sizeof(arr[0]);

    //for (size_t i = 0; i &lt; n; i++)
    //{
    //  printf("%d ", arr[i]);
    //}

    for (size_t i = 0; i &lt; n; i++)
    {
        printf("%d ", *p);
        p++;    // p = p+1, 一次加过 一个 int 大小（一个数组元素）。
    }
    // p值随着循环，不断变化。 打印结束后，p指向一块无效的内存空间（野指针）
    printf("\n");

    return 0;
}
</code></pre>
<h5>练习</h5>
<ul>
<li>练习：使用 指针给空数组连续赋值。 再使用"指针挪移"方法打印这个数组。</li>
</ul>
<pre><code>int main(void)
{
    int arr[10];
    int n = sizeof(arr) / sizeof(arr[0]);
    int* p = arr;   // p 指向 arr[0]
    printf("*p = %p\n", p);

    // 使用 指针，给空数组连续赋值。
    for (size_t i = 0; i &lt; n; i++)
    {
        //arr[i] = i + 10;
        *(p + i) = 10 + i;    // *(p + i) == arr[i];
    }
    // 循环结束时，p指向谁？？ 依然指向 arr[0];
    printf("循环结束后：p = %p\n", p);

    // 使用"指针挪移"方法打印这个数组。
    for (size_t i = 0; i &lt; n; i++)
    {
        printf("%d ", *p);
        p++;     // p = p+1, 一次加过 一个 int 大小（一个数组元素）。
    }
    printf("\n");
    // 循环结束时，p指向谁？？ 指向数组尾元素的下一个内存（野指针）。
    printf("2 for 循环结束后：p = %p\n", p);

    return 0;
}
</code></pre>
<h5>指针 +- 整数</h5>
<ol>
<li>
<p>普通指针变量 + 整数</p>
<pre><code>char *p;   p+1    偏过1个字节。
short *p;  p+1    偏过2个字节。
int *p;    p+1    偏过4个字节。
</code></pre>
</li>
<li>
<p>在数组中，+ 整数</p>
<pre><code>short arr[] = {1, 3, 5, 8, 12, 17, 19};
short *p = arr+3;

p + 3;   向后（右）偏过 3 个元素。 6 个字节。
p - 2;   向前（左）偏过 2 个元素。 4 个字节。
</code></pre>
</li>
<li>
<p><code>&amp;数组名 + 1</code></p>
<ul>
<li><code>&amp;数组名 + 1</code>，加过的是一个数组的总大小。</li>
</ul>
<pre><code>int main(void)
{
    short arr[] = {1,2,3,4,5,6,7,8,9,0};
    printf("arr = %p\n", arr);
    printf("&amp;arr[0] = %p\n", &amp;arr[0]);
    printf("arr+1 = %p\n", arr+1);

    printf("&amp;arr = %p\n", &amp;arr);   // 取"整个数组"的地址。
    printf("&amp;arr+1 = %p\n", &amp;arr + 1);   // +1，跳过一个数组 short [10]
    return 0;
}
</code></pre>
</li>
</ol>
<h5>指针其他运算</h5>
<ul>
<li>指针 <code>*</code> <code>/</code> <code>%</code>
<ul>
<li>不允许！！！</li>
</ul>
</li>
<li>指针+ - 指针
<ul>
<li>
<p>指针+ - 指针</p>
<ul>
<li>不允许 error!</li>
</ul>
</li>
<li>
<p>指针-指针：</p>
<ul>
<li>普通变量来说，语法允许，但，无实际意义。</li>
<li>对于数组来说，得到偏移过的元素个数。</li>
</ul>
<pre><code>int main(void)
{
    int a[] = {1,2,3,4,5,6,7,8,9,0};
    int* p = a;   // 保存数组首地址。

    p = &amp;a[3];   // 修改p保存的地址
    printf("p-a = %d\n", p - a);   // ---- 3

    int* q = &amp;a[8];
    printf("q-p = %d\n", q - p);   // ---- 5

    return 0;
}
</code></pre>
</li>
</ul>
</li>
</ul>
<h5>指针实现 strlen()</h5>
<ul>
<li>
<p>借助 数组 实现</p>
<pre><code>// 数组实现
int myStrlen1(char str[])
{
    int i = 0;
    while (str[i] != '\0')
    {
        i++;
    }
    return i;
}
</code></pre>
</li>
<li>
<p>借助 指针 实现</p>
<pre><code>// 指针实现
int myStrlen2(char str[])
{
    char* p = str;
    while (*p != '\0')
    {
        p++;
    }
    return p-str;   // 返回元素个数。
}
</code></pre>
</li>
</ul>
<h5>指针比较运算</h5>
<ol>
<li>
<p>普通变量。</p>
<ul>
<li>语法允许，但无实际意义。</li>
</ul>
</li>
<li>
<p>数组。</p>
<ul>
<li>对于数组来说，地址之间可以比大小。得到元素存储的先后顺序。</li>
</ul>
<pre><code>int arr[] = {1,2,3,5,6,7,8};
int* p = &amp;arr[2];

if (p &gt; arr) {
    printf("成立\n");
}
else if (p &lt; arr) {
    printf("不成立");
}
else {
    printf("==\n");
}
</code></pre>
</li>
<li>
<p>判断 NULL</p>
<pre><code>int* p;
int a = 10;
// p = NULL  // 初始化空指针。
p = &amp;a;

if (p != NULL)
{
    printf("p is not NULL\n");
}
else
{
    printf("p is NULL\n");
}
</code></pre>
</li>
</ol>
<h4>指针数组</h4>
<ol>
<li>
<p>指针数组的本质，是一个二级指针。</p>
<pre><code>int a = 10;
int b = 20;
int c = 30;

int *arr[] = {&amp;a, &amp;b, &amp;c};   // int型指针数组，保存地址的数组。

printf("*arr[0] = %d\n", *arr[0]);   // arr[0] == *(arr+0)
printf("*arr[0] = %d\n", *(arr + 0));
printf("**arr = %d\n", **arr);      // * 结合性，自右向左。
</code></pre>
</li>
<li>
<p>二维数组，是指针数组，是二级指针。</p>
<pre><code>int a[] = { 10 };
int b[] = { 20 };
int c[] = { 30 };

int* arr[] = {a, b, c};   // 存地址。

printf("arr[0][0] = %d\n", arr[0][0]);
printf("(*(arr+0)) = %d\n", *(*(arr+0)));
printf("**arr = %d\n", **arr);   // 二级指针的简介应用。
</code></pre>
</li>
</ol>
<pre><code>
## 第八章 高级语法（多级指针、指针和字符串）

### 多级指针

- 多级指针不能跳跃定义。有一级，才能定义二级；有二级才能定义三级；有三级，才能定义4级、。。。

```c
int a = 10;   // 普通变量
int *p = &amp;a;       // 一级指针，是变量的地址。
int **pp = &amp;p;     // 二级指针，是一级指针的地址。【重点】
int ***ppp = &amp;pp;  // 三级指针，是二级指针的地址。
int ****pppp = &amp;ppp;// 四级指针，是三级指针的地址。
......
*pp == pp == &amp;p
**ppp == *pp == p == &amp;a;
***pppp == **ppp == *p == a;
</code></pre>
<pre><code>flowchart LR
    A["a 0xaa00"] --&gt; P["p 0xaa00"]
    P --&gt; PP["pp 0xbb00"]
    PP --&gt; PPP["ppp 0xcc00"]
</code></pre>
<pre><code>int a = 10;
*ppp == pp == &amp;p
**ppp == *pp == p == &amp;a
***ppp == **pp == *p == a
</code></pre>
<h3>指针和函数</h3>
<h4>栈帧</h4>
<ul>
<li>当函数被调用时，系统会在 stack 空间上申请一块内存，用来给函数调用提供空间。存储 形参 和 局部变量。（定义在函数内部的变量）。</li>
<li>函数调用结束时，这块内存空间，会被自动释放（消失）。</li>
</ul>
<pre><code>flowchart TB
    subgraph 程序运行空间
        subgraph 栈空间
            subgraph main栈帧
                A1["m"]:::active
            end
            subgraph test栈帧
                A2["a"]:::active
            end
        end
    end
</code></pre>
<h4>传值和传址</h4>
<h5>传值（值传递）</h5>
<ul>
<li>函数调用期间，实参将自己的数据值，拷贝一份给形参。</li>
</ul>
<pre><code>// 定义函数，交换两个数。 -- 3杯水法
int swap1(int a, int b)   // a、b 形参
{
    int tmp = 0;   // 局部变量
    tmp = a;
    a = b;
    b = tmp;
}

int main(void)
{
    int m = 10;
    int n = 55;

    // 调用 函数 swap1，交换两个数。
    swap1(m, n);   // m、n 实参。

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h5>传址（传引用）</h5>
<ul>
<li>函数调用期间，实参将自己的"地址值"，拷贝一份 赋值给形参。
<ul>
<li>可以在 A 栈帧中，借助地址，修改B栈帧上的变量数据。</li>
</ul>
</li>
</ul>
<pre><code>// 定义函数，交换两个数。 -- 传引用（传址）
int swap2(int *a, int *b)   // a、b 形参。
{
    int tmp = 0;

    tmp = *a;
    *a = *b;
    *b = tmp;
}

int main(void)
{
    int m = 10;
    int n = 55;

    swap2(&amp;m, &amp;n);

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h4>数组做函数参数</h4>
<ul>
<li>数组做函数参数时，传递的不再是整个数组，而是数组的首地址（指针）。</li>
</ul>
<pre><code>int main(void)
{
    int arr[] = {1, 4, 6, 7, 9, 0};

    printf("main : sizeof(arr) = %u\n", sizeof(arr));   // 整个数组的大小。

    // 调用 test 函数，传参 数组。
    test(arr);   // 实参！

    system("pause");
    return EXIT_SUCCESS;
}

// 定义函数，用数组做参数
void test(int arr[])   // 形参
{
    printf("test : sizeof(arr) = %u\n", sizeof(arr));   // 指针的大小。

    printf("arr[0] = %d\n", arr[0]);
}
</code></pre>
<ul>
<li>当整型数组做函数参数时，通常在函数定义中，封装2个参数，一个表数组首地址，另一个表元素个数。</li>
</ul>
<pre><code>// 冒泡排序
//void BubbleSort(int arr[])  这种传参，无法在函数内，求元素个数。
void BubbleSort(int arr[], int n)
{
    for (int i = 0; i &lt; n - 1; i++)
    {
        for (int j = 0; j &lt; n - 1 - i; j++)
        {
            if (arr[j] &gt; arr[j + 1])
            {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

int main(void)
{
    int arr[] = {1, 4, 8, 12, 19, 43, 2, 7, 15};

    // 获取元素个数
    int n = sizeof(arr) / sizeof(arr[0]);

    // 排序
    BubbleSort(arr, n);

    // 打印排序后结果
    for (size_t i = 0; i &lt; n; i++)
    {
        printf("%d ", arr[i]);
    }
    putchar('\n');

    return 0;
}
</code></pre>
<ul>
<li>数组做函数参数，也可以写成指针的形式。（本质一样）</li>
</ul>
<pre><code>void Bubblesort(int arr[], int n) == void Bubblesort(int *arr, int n)
</code></pre>
<h4>指针做函数返回值</h4>
<ul>
<li>指针做函数返回值，不能返回【局部变量的地址】。
<ul>
<li>当函数调用结束，栈帧空间释放，局部变量的地址，无效。</li>
</ul>
</li>
<li>数组做函数返回值，不允许！（C语言中，只能写成 指针形式）</li>
</ul>
<h3>指针和字符串</h3>
<h4>基本知识</h4>
<pre><code>char str1[] = {'h','i','\0'};  【麻烦】   变量，可读可写。
char str2[] = "hi";                 变量，可读可写。
char *str3 = "hi";                  常量，只读。
char *str4 = {'h','i','\0'};   //  错误！
</code></pre>
<p>演示 demo1：</p>
<ul>
<li>char *str2 = "hello" 是一个常量。不允许修改。</li>
</ul>
<pre><code>int main0401(void)
{
    char str1[] = "hello";   // 相当于 {'h','e','l','l','o','\0'}
    char* str2 = "";

    str1[0] = 'R';   // 相当于 {'h','e','l','l','o','\0'}是变量，可以随意修改。
    printf("%s\n", str1);

    str2[0] = 'R';   // "hello" 是字符串常量。 不能修改！
    printf("%s\n", str2);

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<p>演示 demo2：</p>
<ul>
<li>同一个字符串常量，可以给多个不同的指针赋值。</li>
<li><code>char* str2 = "hello"</code> 和 <code>char* n = "hello"</code>，地址值都相同。都是字符串"hello"的地址。</li>
</ul>
<pre><code>int main(void)
{
    char str1[] = "hello";   // 相当于 {'h','e','l','l','o','\0'}
    char m[] = "hello";

    char* str2 = "hello";
    char* n = "hello";

    printf("str1 = %p\n", str1);
    printf("m    = %p\n", m);   // 数组定义的 hello 地址不同。

    printf("str2 = %p\n", str2);
    printf("n    = %p\n", n);   // 指针定义的 hello 是字符串常量。是同一个地址。

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<p>【结论】：当字符串（含有 <code>\0</code> 字符数组），做函数参数时，不需要提供 2 个参数。因为每个字符串都有 <code>\0</code>。</p>
<h4>练习</h4>
<h5>字符串比较（strcmp() 函数）</h5>
<ul>
<li>比较 str1 和 str2，如果相同返回 0，不同则依次比较ASCII码，str1 &gt; str2 返回 1，否则返回 -1
<ul>
<li>按对应的位置，比较字符的大小。不比较ASCII 码和。</li>
</ul>
</li>
<li>分析：
<ul>
<li>循环，依次比较两个字符串中 对应位字符。<code>\0</code> 结束。都相同 ---&gt; 0</li>
<li>对应位不同，比较字符的 ASCII 码。 str1 &gt; str2 ---&gt; 1；str1 &lt; str2 ---&gt; -1</li>
</ul>
</li>
</ul>
<pre><code>// 数组的实现方式
int myStrcmp(char *str1, char *str2)
{
    int i = 0;
    while (str1[i] == str2[i])   // *(str1+i) == *(str2+i)
    {
        if (str1[i] == '\0')
        {
            return 0;    // 2个字符串，一样!
        }
        i++;
    }
    // str1 和 str2 有字符不同。
    return str1[i] &gt; str2[i] ? 1 : -1;
}

// 指针的实现方式
int myStrcmp2(char* str1, char* str2)
{
    while (*str1 == *str2)   // *(str1+i) == *(str2+i)
    {
        if (*str1 == '\0')
        {
            return 0;    // 2个字符串，一样!
        }
        str1++;
        str2++;
    }
    // str1 和 str2 有字符不同。
    return *str1 &gt; *str2 ? 1 : -1;
}
</code></pre>
<h5>字符串拷贝（strcpy 函数）</h5>
<ul>
<li>将一个字符串中的所有字符，依次拷贝存放到另一空字符数组中。</li>
</ul>
<pre><code>// 数组版：
void myStrcpy(char* src, char* dst)
{
    int i = 0;
    while (src[i] != '\0')   // while(src[i] != 0)   while(strc[i])
    {
        dst[i] = src[i];
        i++;
    }
    dst[i] = '\0';   //main中的 dst 初始化为0。此步可以省略。
}

// 指针版
void myStrcpy2(char* src, char* dst)
{
    while (*src != '\0')   // while(src[i] != 0)   while(strc[i])
    {
        *dst = *src;
        src++;
        dst++;
    }
    *dst = '\0';   //main中的 dst 初始化为0。此步可以省略。
}
</code></pre>
<h5>在字符串中查找字符出现的位置（strchr 函数）</h5>
<ul>
<li><code>"helloworld" 'e' ---&gt; "elloworld"</code></li>
<li><code>'l' ---&gt; "lloworld"</code></li>
<li><code>'r' ---&gt; "rld"</code></li>
</ul>
<pre><code>// 指针版
char* myStrchr(char* str, char ch)
{
    while (*str)   //while (*str != '\0')  ==while (*str != 0)
    {
        if (*str == ch)
        {
            return str;
        }
        str++;
    }
    return NULL;   // 在 str 中，没有找到 ch
}

// 数组版
char* myStrchr2(char* str, char ch)
{
    int i = 0;
    while (str[i])   //while (str[i] != '\0') == while (str[i] != 0)
    {
        if (str[i] == ch)    // str[i] == *(str+i)
        {
            return &amp;str[i];
        }
        i++;
    }
    return NULL;   // 在 str 中，没有找到 ch
}
</code></pre>
<h5>字符串去空格</h5>
<ul>
<li><code>"ni chou sha ? chou ni za di ! " ---&gt; "nichousha?chounizadi!"</code></li>
</ul>
<pre><code>// 封装函数，去除字符串中空格  --- 数组版
void str_no_space(char* src, char* dst)
{
    int i = 0;     // 遍历 src 字符串
    int j = 0;     // 记录 dst存储位置。
    while (src[i])
    {
        if (src[i] != ' ')    // 只有不为空格，才存到 dst中。
        {
            dst[j] = src[i];
            j++;        // 不为空格，后移。为空格，j不动。
        }
        i++;
    }
    dst[j] = '\0';
}

// 封装函数，去除字符串中空格  --- 指针版
void str_no_space2(char* src, char* dst)
{
    while (*src)
    {
        if (*src != ' ')    // 只有不为空格，才存储到 dst中。
        {
            *dst = *src;
            dst++;     // 不为空格，指针后移。为空格，指针不动
        }
        src++;
    }
    *dst = '\0';
}

int main(void)
{
    char str[] = "ni chou sha ? chou ni za di ! zai chou yi ge shi shi";
    char dst[1024] = {0};

    // 调用函数，去除 str中的空格，保存到 dst 中
    // str_no_space(str, dst);
    str_no_space2(str, dst);

    printf("dst = %s\n", dst);

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h4>带参数的 main 函数</h4>
<ul>
<li>
<p>无参版：</p>
<pre><code>int main(void) { return 0; }
</code></pre>
</li>
<li>
<p>有参版：</p>
<pre><code>int main(int argc, char *argv[]) { return 0; }
    参数1：表示给 main 函数传递的参数的 总个数。
    参数2：是一个数组，数组的每一个元素都是 字符串 （char *）
</code></pre>
</li>
</ul>
<p>测试代码：</p>
<pre><code>int main(int argc, char *argv[])
{
    int i = 0;

    for (i = 0; i &lt; argc; i++)
    {
        printf("argv[%d] = %s\n", i, argv[i]);
    }

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h4>测试方法</h4>
<ol>
<li>在终端中，使用 gcc 编译得到可执行文件，如 test.exe</li>
<li>不能获取带有空格的字符串！！！空格是分隔符。</li>
</ol>
<pre><code>gcc 09-带参数的main.c -o test.exe

test.exe aa bb cc dd ee
test.exe hello world haha xixi hoho heihei .....
argc: --- 6
argv[0] = test.exe
argv[1] = aa
argv[2] = bb
argv[3] = cc
argv[4] = dd
argv[5] = ee
</code></pre>
<ol>
<li>在 VS 中，项目名称上 右键 —— 属性 —— 配置属性 —— 调试 —— 命令行参数 —— 写入 待测试的命令行参数。</li>
</ol>
<h4>字符串练习</h4>
<h5>str 中 substr 出现的次数</h5>
<pre><code>str : "hellolllolllolllo "
substr: "llo"
</code></pre>
<p>写函数测试，llo 在 "hellolllolllolllo" 出现了多少次。</p>
<p>学习、使用 strstr() 函数。再实现上述的题目。</p>
<h5>求字符串非空格元素个数</h5>
<pre><code>"ni chou sha ? chou ni za di ! zai chou yi ge shi shi" 统计这里，除空格外 字符 的个数。
</code></pre>
<h5>字符串逆置</h5>
<pre><code>"hello" ---&gt; "olleh"
"word" ---&gt; "drow"
</code></pre>
<h5>判断字符串是回文</h5>
<pre><code>abcba --- 是回文
amkilolikma --- 是回文。
abccba --- 是回文。
abcdba --- 不是回文。
</code></pre>
<h2>第九章 高级语法（字符串和内存管理）</h2>
<h3>字符串练习</h3>
<h4>str 中 substr 出现的次数</h4>
<pre><code>str = "hellollloabclllollo "
substr: "llo" strlen("llo")
</code></pre>
<p>写函数测试，llo 在 "hellolllolllolllo" 出现了多少次。</p>
<ul>
<li>strstr() 函数</li>
</ul>
<pre><code>#include &lt;string.h&gt;
char *strstr(const char *haystack, const char *needle);
char *strstr(const char *str, const char *substr);
    参数1：原串
    参数2：子串
    返回值：
        成功：返回子串在原串中的位置（地址值）
        失败：NULL
</code></pre>
<ul>
<li>测试 strstr()</li>
</ul>
<pre><code>// 测试 strstr
int main(void)
{
    char* ret = strstr("hellollloabclllollo", "llo");
    printf("str = %s\n", ret);

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<pre><code>count = 0;
strstr("hellolllollo", "llo")  ---&gt; "llolllollo"   ---&gt;count++
"llolllollo" += strlen("llo");  ---&gt; "llollo"
strstr("llollo", "llo")  ---&gt; "llollo"   ---&gt; count++
"llollo" += strlen("llo");  ---&gt; "llo"
strstr("llo", "llo")  ---&gt; "llo"   ---&gt;count++
"llo" += strlen("llo");  ---&gt; ""
strstr("", "llo")  ---&gt; NULL
</code></pre>
<pre><code>// 封装函数，统计 str字符串中，substr出现的次数
int substr_times(char* str, char* substr)
{
    int count = 0;  // 定义变量统计substr出现的次数。
    char *p = strstr(str, substr);   // ---&gt; "llollloabclloxyzllollo";

    // 循环的截取 str 串，判断剩余 str串中是否包含 substr
    while (p != NULL)     // while (p)
    {
        count++;
        p += strlen(substr);     // "llollloabclloxyzllollo";

        p = strstr(p, substr);
    }
    return count;
}

// 在 str字符串中，找子串 substr出现的次数
int main(void)
{
    char str[] = "hell9qaqllloabclloxyzlmllollo";
    char substr[] = "llo";

    int ret = substr_times(str, substr);
    printf("%s 串中，%s 子串 出现 %d 次\n", str, substr, ret);

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h4>求字符串非空格元素个数</h4>
<pre><code>"ni chou sha ? chou ni za di ! zai chou yi ge shi shi" 统计这里，除空格外 字符 的个数。
</code></pre>
<pre><code>// 统计非空格数
int no_space_num(char* str)
{
    int count = 0;
    // 指针方式实现。
    char* p = str;
    while (*p)
    {
        if (*p != ' ')
        {
            count++;
        }
        p++;
    }
    return count;
}

int main(void)
{
    char str[] = "hello ni hao ma world?";

    int ret = no_space_num(str);

    printf("ret = %d\n", ret);

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h4>字符串逆置</h4>
<pre><code>"hello" ---&gt; "olleh"
"word" ---&gt; "drow"
</code></pre>
<ul>
<li>参考"数组逆置"实现，day05</li>
</ul>
<pre><code>// h e l l o
// 字符串的逆序
void str_inverse(char* str)
{
    char* start = str;       // 记录首个元素的地址
    char* end = str + strlen(str)-1;    // 记录最后一个元素的地址

    // 循环交换字符串首尾元素
    while (start &lt; end)
    {
        char tmp = *start;      // 三杯水交换字符元素。
        *start = *end;
        *end = tmp;

        start++;   // 首元素指针后移
        end--;     // 尾元素指针前移
    }
}

int main(void)
{
    char str[] = "this is a test";

    str_inverse(str);

    printf("%s\n", str);

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h4>判断字符串是回文</h4>
<pre><code>// 判断字符串是否是回文
int str_is_abcba(char* str)
{
    char* start = str;
    char* end = str + strlen(str) - 1;

    while (start &lt; end)
    {
        if (*start != *end)
        {
            return 0;     // 不是回文
        }
        start++;
        end--;
    }
    return 1;
}

int main(void)
{
    char str[] = "abcmmcba";

    int ret = str_is_abcba(str);
    if (ret == 1)     // 是回文
    {
        printf("%s 是回文！\n", str);
    }
    else if (ret == 0)
    {
        printf("%s 不是回文！\n", str);
    }

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h3>字符串处理函数</h3>
<ul>
<li>全部都是标准C库函数。使用头文件 <code>#include &lt;string.h&gt;</code></li>
</ul>
<h4>字符串拷贝</h4>
<h5>strcpy</h5>
<pre><code>char *strcpy(char *dest, const char *src);    // src:source  dest:dst
</code></pre>
<ul>
<li>将 src 的内容，拷贝给 dest。返回 dest，<strong>dest空间要足够大</strong>。
<ul>
<li>strcpy 函数，不去检查 dest 是否足够大。 ——【不安全函数】</li>
</ul>
</li>
<li>函数调用结束，返回值和 dest 结果一致。</li>
</ul>
<pre><code>// 字符串拷贝
int main(void)
{
    char str[] = "you will be die if you copy me!";

    char dst[100] = { 0 };

    char *p = strcpy(dst, str);

    printf("dest = %s\n", dst);
    printf("*p = %s\n", p);

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h5>strncpy</h5>
<pre><code>char *strncpy(char *dest, const char *src, size_t n);    // 安全
</code></pre>
<ul>
<li>将 src 的内容，拷贝给 dest。只拷贝 n 个字节。<strong>dest空间要足够大</strong>。通常 n 与 dest 的空间大小一致。</li>
</ul>
<p>特性：</p>
<ul>
<li><code>n &gt; src</code>：只拷贝 src 大小。</li>
<li><code>n &lt; src</code>：只拷贝 n 个字节。不会自动添加 <code>\0</code></li>
</ul>
<pre><code>// 测试
int main(void)
{
    char str[] = "hello world";

    char dst[100] = { 0 };

    char* p = strncpy(dst, str, sizeof(dst));

    //for (size_t i = 0; i &lt; 10; i++)
    //{
    //  printf("%c\n", p[i]);
    //}

    printf("%s\n", p);

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h4>字符串拼接</h4>
<h5>strcat</h5>
<pre><code>char *strcat(char *dest, const char *src);
</code></pre>
<ul>
<li>将 src 中内容，拼接到 dest 后。返回拼接成功的字符串。——需要保证 dest 空间足够大。</li>
<li>函数调用结束后，dest 和 返回值结果相同。</li>
</ul>
<pre><code>int main(void)
{
    char str[] = "hello world";

    char dst[100] = "haha hoho xixi";

    char *p = strcat(dst, str);

    printf("p = %s\n", p);
    printf("dst = %s\n", dst);

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h5>strncat</h5>
<pre><code>char *strncat(char *dest, const char *src, size_t n);
</code></pre>
<ul>
<li>将 src 中前 n 个字符，拼接到 dest 后。返回拼接成功的字符串。——需要保证 dest 空间足够大。</li>
<li>函数调用结束后，dest 和 返回值结果相同。</li>
</ul>
<pre><code>// 字符串拼接 strncat
int main(void)
{
    char str[] = "hello world";

    char dst[100] = "haha hoho xixi";

    char* p = strncat(dst, str, 7);

    printf("p = %s\n", p);
    printf("dst = %s\n", dst);

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h4>字符串比较</h4>
<ul>
<li>字符串比较可以使用 <code>&gt;</code> <code>&lt;</code> <code>&gt;=</code> <code>&lt;=</code> <code>==</code> <code>!=</code>，字符串 不允许使用。</li>
</ul>
<h5>strcmp</h5>
<pre><code>int strcmp(const char *s1, const char *s2);
</code></pre>
<ul>
<li>比较 s1 和 s2 两个字符串，如果相等 返回 0；</li>
<li>如果不相等，进一步比 s1 和 s2 对应位上的 ASCII 码值。
<ul>
<li>s1 &gt; s2 返回 1</li>
<li>s1 &lt; s2 返回 -1</li>
</ul>
</li>
</ul>
<pre><code>int main(void)
{
    char s1[] = "helloz";

    char s2[] = "helloworld";

    printf("ret = %d\n", strcmp(s1, s2));    // 不比较 ASCII 的 和

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h5>strncmp</h5>
<pre><code>int strncmp(const char *s1, const char *s2, size_t n);
</code></pre>
<ul>
<li>比较 s1 和 s2 两个字符串的前 n 个字符，如果相等 返回 0；</li>
<li>如果不相等，进一步比 s1 和 s2 对应位上的 ASCII 码值。（不比较 ASCII 的和）</li>
</ul>
<pre><code>char s1[] = "helloz";
char s2[] = "helloworld";
printf("ret = %d\n", strncmp(s1, s2, 5));   --- 0
</code></pre>
<h4>字符串格式化输入、输出</h4>
<ul>
<li>s —— string.</li>
</ul>
<h5>sprintf</h5>
<pre><code>int sprintf(char *str, const char *format, ...);
// ... 代表 这是一个参数可变的函数。
</code></pre>
<ul>
<li>对应 printf 记忆。作用将 原来输出到屏幕的"格式化字符串"，写到 参数1的 str 中。</li>
</ul>
<pre><code>// printf("%d + %d = %d\n", 10, 24, 10+24);

char str[1024] = {0};     // 保证空间足够大
sprintf(str, "%d + %d = %d\n", 10, 24, 10 + 24);    // 写到 str 中。不打印屏幕

puts(str);
printf("---%s", str);
</code></pre>
<h5>sscanf</h5>
<pre><code>int sscanf(const char *str, const char *format, ...);
</code></pre>
<ul>
<li>对应 scanf 记忆。作用将 原来从键盘获取到的"格式化字符串"，从 参数1的 str 中获取。</li>
</ul>
<pre><code>int a, b, c;

// scanf("%d+%d=%d", &amp;a, &amp;b, &amp;c);    // 从键盘 stdin 读取。

char str[] = "10+20=30";       // 提供给 sscanf 参数1 使用。

int ret = sscanf(str, "%d+%d=%d", &amp;a, &amp;b, &amp;c);

printf("a = %d\n", a);
printf("b = %d\n", b);
printf("c = %d\n", c);
</code></pre>
<h4>字符串查找字符、子串</h4>
<h5>strchr</h5>
<pre><code>char *strchr(const char *s, int c);
</code></pre>
<ul>
<li>在字符串 s 中，找字符 c 出现的位置。返回 字符在字符串中的地址。</li>
</ul>
<pre><code>// 字符串中找字符出现位置 strchr
int main(void)
{
    printf("%s\n", strchr("hellohehexixihoho", 'i'));

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h5>strrchr</h5>
<ul>
<li>r: right</li>
</ul>
<pre><code>char *strrchr(const char *s, int c);
</code></pre>
<ul>
<li>自右向左，在字符串 s 中，找字符 c 出现的位置。返回 字符在字符串中的地址。</li>
</ul>
<pre><code>int main(void)
{
    //printf("%s\n", strchr("hellohehexixihoho", 'i'));
    printf("%s\n", strrchr("hellohehexixihoho", 'x'));

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h5>strstr</h5>
<pre><code>char *strstr(const char *str, const char *substr);
</code></pre>
<ul>
<li>在字符串 str 中，找寻子串 substr 第一次出现的位置。返回地址。</li>
</ul>
<h4>字符串分割</h4>
<h5>strtok</h5>
<pre><code>char *strtok(char *str, const char *delim);
    参数1：待拆分字符串
    参数2：分割符组成的字符串。 strtok("www.baidu.com", ".");  // 写成 '.' 错误！
</code></pre>
<ul>
<li>按照（参2）既定的分割符，来拆分字符串。<code>www.baidu.com</code> 按 <code>"."</code> 拆分。</li>
</ul>
<pre><code>// 测试1：
int main(void)
{
    char str[] = "www.itcast.cn";   // ---&gt; "www\0itcast.cn";
    char *p = strtok(str, ".");     // strtok调用完成，会将 分割符用 \0 替换。

    printf("p = %s\n", p);

    // 调用一次 strtok 分割之后，再去打印原串 str
    for (size_t i = 0; i &lt; 13; i++)
    {
        //printf("%c\n", str[i]);
        printf("%d\n", str[i]);   // 打印每个字符的 ASCII码
    }

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<ul>
<li>结论：
<ul>
<li>strtok() 函数，直接在原串上对字符串分割。不能分割字符串常量。<code>char *str = "hello";</code></li>
<li>strtok() 函数调用结束，会将分割符，替换成 <code>\0</code></li>
<li>第一次用 strtok 拆分，参数1 传待拆分的原串中。第1+次拆分，参数1 传 NULL。</li>
</ul>
</li>
</ul>
<pre><code>// 字符串分割 strtok
int main(void)
{
    char str[] = "www.itcast.cn.net.com";   // ---&gt; "www\0itcast.cn";
    //char *str = "www.itcast.cn.net";    // 字符串常量。

    char *p = strtok(str, ".");     // strtok调用完成，会将 分割符用 \0 替换。

    printf("p = %s\n", p);

    // 循环按 "." 拆分 剩余 字符串。
    while (1)
    {
        p = strtok(NULL, ".");
        if (p == NULL)
        {
            break;
        }
        printf("p = %s\n", p);
    }
}
</code></pre>
<ul>
<li>练习：
<ul>
<li>拆分字符串 <code>"www.itcast.cn$This is a test$for strtok"</code>，按 分割符 <code>"$"</code>.</li>
</ul>
</li>
</ul>
<pre><code>//拆分 字符串 "www.itcast.cn$This is a test$for strtok"，按 分割符 "$" ...
int main(void)
{
    char str[] = "www.itcast.cn$This is a test$for strtok";

    // 调用第一次
    char* p = strtok(str, ".$");   // 分割符有3个：'.' / '$' / ' ' 空格
    printf("p = %s\n", p);

    // 后续 N+1次调用。while循环，参数NULL
    while (1)
    {
        p = strtok(NULL, ".$");
        if (p == NULL)
        {
            break;
        }
        printf("循环中 p = %s\n", p);
    }

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h4>字符串转换</h4>
<ul>
<li>a: 代表字符串 string.</li>
<li>将字符串转换成数、小数、长整数、 长长整型。</li>
<li>使用这类函数进行转换时，要求 原串必须是可转换的字符串。
<ul>
<li>错误使用："abc123"、"yxac123"、"1245dke89" 不能正确转换。</li>
</ul>
</li>
</ul>
<h5>atoi</h5>
<pre><code>#include &lt;stdlib.h&gt;
int atoi(const char *nptr);

double atof(const char *nptr);
long atol(const char *nptr);
long long atoll(const char *nptr);
</code></pre>
<p>示例：</p>
<pre><code>char str1[] = "12abc3456";
int num = atoi(str1);
printf("num = %d\n", num);

char str2[] = "3.14";
double num2 = atof(str2);
printf("num2 = %lf\n", num2);

char str3[] = "34568490354";
long long num3 = atoll(str3);
printf("num3 = %lld\n", num3);
</code></pre>
<h3>内存管理</h3>
<h4>局部变量</h4>
<ul>
<li>概念：
<ul>
<li>定义在函数内部的变量。</li>
</ul>
</li>
<li>作用域：
<ul>
<li>从定义位置开始，到包裹该变量的第一个右大括号结束。（函数作用域、块作用域。）</li>
</ul>
</li>
</ul>
<pre><code>void test(void)
{
    {
        int m = 10;   // 局部变量。 -- 块作用域。出了 } 不能使用。
    }
    printf("---m = %d\n", m);

    //int i = 0;
    for (size_t i = 0; i &lt; 10; i++)
    {
        printf("m = %d\n", m);
    }
    //printf("i = %d\n", i);
}
</code></pre>
<h4>全局变量</h4>
<ul>
<li>概念：
<ul>
<li>定义在函数外部的变量。</li>
</ul>
</li>
<li>作用域：
<ul>
<li>从定义位置开始，默认到本文件内部。其他文件如果想使用，可以通过"声明"的方式，将作用域导出。</li>
</ul>
</li>
</ul>
<h4>static 变量</h4>
<h5>static 全局变量</h5>
<ul>
<li>定义语法：
<ul>
<li>在全局变量定义之前，添加 static 关键字。如：<code>static int a = 10;</code></li>
</ul>
</li>
<li>作用域：
<ul>
<li>被限制在本文件内部，不允许通过"声明"方式导出作用域。（java不同）</li>
</ul>
</li>
</ul>
<h5>static 局部变量</h5>
<ul>
<li>定义语法：
<ul>
<li>在局部变量定义之前，添加 static 关键字。</li>
</ul>
</li>
<li>作用域：
<ul>
<li>从定义位置开始，到包裹该变量的第一个右大括号结束。</li>
</ul>
</li>
<li>特性：
<ul>
<li>静态局部变量，只定义一次。相当于，在全局位置定义。通常用来做计数器。</li>
</ul>
</li>
</ul>
<pre><code>void test08(void)
{
    static int b = 10;   // 静态局部变量
    printf("%d\n", b++);
}

int main(void)
{
    for (size_t i = 0; i &lt; 10; i++)
    {
        test08();   // 调用 test08 10 次。
    }

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h4>static 函数</h4>
<ul>
<li>全局函数：
<ul>
<li>就是"函数"。</li>
<li>定义语法：函数原型 + 函数体。</li>
</ul>
</li>
<li>static 函数：
<ul>
<li>定义语法：<code>static 函数原型 + 函数体。</code></li>
<li>特性：
<ul>
<li>static 函数，只能在本文件内使用。其他文件即使声明也无法使用。</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code>// A.c 文件中有如下代码
// static 关键字修饰 test09 函数限制在本文件内。 外部文件，不能访问
static void test09(void)
{
    for (size_t i = 0; i &lt; 5; i++)
    {
        printf("i = %d\n", i);
    }
}

//-------------------

//B.C 文件中调用上述函数。由于 static， B.C 不能使用 test09函数，会报错！
#define _CRT_SECURE_NO_WARNINGS
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;math.h&gt;
#include &lt;time.h&gt;

// void test09(void);   // 声明函数
extern void test09(void);   // 声明函数

int main(void)
{
    test09();

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h3>生命周期</h3>
<ul>
<li>助记：
<ul>
<li>生命周期：出生 --- 死亡 80岁。生命周期：80年。</li>
<li>作用域：班花：全班。村长：全村。县长：全县。</li>
</ul>
</li>
</ul>
<ol>
<li>局部变量：定义位置开始，函数调用结束（存储在栈stack上）。 —— 函数被调用期间。</li>
<li>全局变量：从程序启动开始（早于 main() 函数），程序终止结束。 —— 程序执行期间。</li>
<li>static 局部变量：从程序启动开始，程序终止结束（定义在全局位置）。 —— 程序执行期间。</li>
<li>static 全局变量：从程序启动开始，程序终止结束。 —— 程序执行期间。</li>
<li>全局函数：从程序启动开始，程序终止结束。 —— 程序执行期间。</li>
<li>static 函数：从程序启动开始，程序终止结束。 —— 程序执行期间。</li>
</ol>
<h3>命名冲突</h3>
<ul>
<li>如果全局变量和局部变量命名冲突。采用就近原则。</li>
<li>&lt;span style="color:red"&gt;<strong>强烈不推荐！</strong>&lt;/span&gt;</li>
</ul>
<h3>内存4区模型</h3>
<ol>
<li>代码段：<code>.text</code> 段。存储程序源码（二进制形式）</li>
<li>数据段：只读数据段 <code>.rodata</code>、初始化数据段 <code>.data</code>、未初始化数据段 <code>.bss</code>。</li>
<li>stack：栈。在其之上开辟帧栈。（较小：windows：1M~10M，Linux：8M~16M）
<ul>
<li>存储特性：后进先出 FILO (LIFO)</li>
</ul>
</li>
<li>heap：堆。给用户自定义提供空间。（较大：约1.3G+）</li>
</ol>
<h3>Heap 堆空间</h3>
<h4>开劈/释放 heap 空间</h4>
<ul>
<li>在 heap 上开劈空间</li>
</ul>
<pre><code>#include &lt;stdlib.h&gt;

void *malloc(size_t size);    // 向 系统申请内存空间，在 heap 上。单位：字节。
    参数：申请空间的大小。
    返回值：
        成功：heap 内存空间的首地址。
        失败：NULL
// 申请成功的内存，通常拿来当成"数组" 使用
</code></pre>
<ul>
<li>释放 heap 申请的空间</li>
</ul>
<pre><code>void free(void *ptr);
    参：就是 malloc 函数的返回值。
</code></pre>
<ul>
<li>示例：</li>
</ul>
<pre><code>int main(void)
{
    // int arr[10];
    // 申请能存储 10 个 int 数的空间。 40字节。
    int *p = (int *)malloc(sizeof(int)*10);    // 强转的目的，方便阅读代码。
    if (p == NULL)
    {
        printf("malloc error\n");
        return -1;   // 退出程序。 -1, 非正常结束。
    }

    // 写 - 数据到 malloc 申请的空间
    for (size_t i = 0; i &lt; 10; i++)
    {
        p[i] = i + 10;   // 存 数据到 malloc 申请的空间中。
    }

    // 从malloc申请的空间中，读数据。
    for (size_t i = 0; i &lt; 10; i++)
    {
        printf("%d ", *(p+i));
    }
    printf("\n");

    // 释放malloc申请的空间。
    free(p);

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h4>使用 heap 空间注意事项</h4>
<ol>
<li>申请的 heap 堆内存空间连续，当成"数组"使用。</li>
<li>free 后的空间，不会立即失效。通常将 free后的地址，置为 NULL。</li>
<li>free 地址必须是 malloc 函数返回的地址。否则，报错！</li>
<li>如果 malloc 后的地址一定会变化，通常使用临时变量 tmp 保存。</li>
</ol>
<h4>二级指针对应的 heap 空间</h4>
<h4>内存操作函数</h4>
<h5>memset</h5>
<pre><code>#include &lt;string.h&gt;
void *memset(void *s, int c, size_t n);
</code></pre>
<h5>memcpy</h5>
<pre><code>void *memcpy(void *dest, const void *src, size_t n);
</code></pre>
<h5>memmove</h5>
<pre><code>void *memmove(void *dest, const void *src, size_t n);
</code></pre>
<h5>memcmp</h5>
<pre><code>int memcmp(const void *s1, const void *s2, size_t n);
</code></pre>
<h4>内存常见问题</h4>
<ol>
<li>申请 0 字节空间
<ul>
<li>C 语言中，允许申请 0 字节的内存空间。</li>
<li>0 字节的空间，不能拿来使用。</li>
</ul>
</li>
<li>free 空指针
<ul>
<li>空 NULL 指针，反复 free，不会报错。</li>
<li>非空的指针，反复 free，&lt;span style="color:red"&gt;<strong>会报错！！！</strong>&lt;/span&gt; —— 推荐 free 后的指针，一定置 NULL。</li>
</ul>
</li>
<li>越界访问
<ul>
<li>不允许！！！导致程序崩溃。</li>
</ul>
</li>
<li>free ++ 后的地址
<ul>
<li>不能正常释放。</li>
<li>如果程序中，必须要将 p++，定义临时变量，保存 p 值。以便 free 释放。</li>
</ul>
</li>
</ol>
<h2>第十章 高级语法（内存管理、结构体、联合体）</h2>
<h3>heap 空间操作</h3>
<h4>二级指针对应的 heap 空间</h4>
<ul>
<li><code>int **p = int *p[3] ==&gt; (int *, int *, int *) ==&gt; [(1, 2, 3, 54, 5), int *, int *]</code></li>
</ul>
<pre><code>flowchart LR
    A["malloc(12) int* int* int*"] --&gt; B["10 / 29 / 34 / ..."]
    A --&gt; C["10 / 29 / 34 / ..."]
    A --&gt; D["10 / 29 / 34 / ..."]
</code></pre>
<pre><code>int main(void)
{
    // 给外层空间malloc申请内存
    int** p = malloc(sizeof(int *) * 3);
    if (p == NULL)
    {
        printf("malloc error!\n");
        return -1;
    }
    // 给 内层指针 申请 malloc 空间
    for (size_t i = 0; i &lt; 3; i++)
    {
        p[i] = malloc(sizeof(int) * 5);
        if (p[i] == NULL)
        {
            printf("malloc p[i] error!\n");
            return -1;
        }
    }
    // 使用空间 -- 写
    for (size_t i = 0; i &lt; 3; i++)
    {
        for (size_t j = 0; j &lt; 5; j++)
        {
            p[i][j] = i + j;    // 随意初始化值。
        }
    }
    // 使用空间 -- 读
    for (size_t i = 0; i &lt; 3; i++)
    {
        for (size_t j = 0; j &lt; 5; j++)
        {
            printf("%d ", *(*(p + i) + j));    // p[i][j] == *(p+i)[j] == *(*(p+i)+j)
        }
        printf("\n");
    }

    // free空间时，应该先释放 内层空间，再释放外层
    for (size_t i = 0; i &lt; 3; i++)
    {
        free(p[i]);
        p[i] = NULL;
    }

    // 释放外层空间
    free(p);
    p = NULL;

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h4>以 char **p 为例</h4>
<pre><code>// 先申请外层指针。
char **p = malloc(sizeof(char *) * 5);
// 申请内层指针
for (int i = 0; i&lt;5; i++)
{
    p[i] = malloc(sizeof(char) * 10);    // 字符串长度 &lt;= 10个字符。
}
// 写
for (int i = 0; i&lt;5; i++)
{
    // p[i] = "hello";  错误!
    strcpy(p[i],"hello");
}
// 释放外层
for (int i = 0; i&lt;5; i++)
{
    free(p[i]);
    p[i] = NULL;
}
// 释放外层
free(p);
p = NULL;
</code></pre>
<h3>内存操作函数</h3>
<ul>
<li>以 4 个函数，专门用来操作 heap 内层。stack 由系统自动申请，自动释放。</li>
</ul>
<h4>memset</h4>
<pre><code>#include &lt;string.h&gt;
void *memset(void *s, int c, size_t n);
    参1：内存首地址。
    参2：置成什么。一般传 0
    参3：内存大小。单位：字节。
    返回值：
        成功：设置后的地址。
        失败：NULL

memset(首地址, 0, 空间大小);
</code></pre>
<ul>
<li>绝大多数，memset 用来将申请好的 heap 内存，置 0。 —— 单位：字节。</li>
</ul>
<pre><code>int* p = (int *)malloc(sizeof(int) * 10);
if (p == NULL)
{
    printf("malloc error");
    return -1;
}
// 将申请好的内存，全部置 0
memset(p, 0, sizeof(int) * 10);

// 直接打印申请好的空间内容。
for (size_t i = 0; i &lt; 10; i++)
{
    printf("%d ", p[i]);
}

free(p);
p = NULL;
</code></pre>
<ul>
<li>memset 函数按"字节"设置。
<ul>
<li>置 0 ——&gt; 每一个字节都为 0。</li>
<li>&lt;span style="color:orange"&gt;<strong>置 1 ——&gt; 每一个字节都为 1。</strong>&lt;/span&gt; —— 4字节 == 0x01010101 == 16843009</li>
</ul>
</li>
</ul>
<h4>memcpy</h4>
<ul>
<li>以字节为单元，内存拷贝。<code>strcpy</code> ——&gt; 只能拷贝字符串。</li>
</ul>
<pre><code>void *memcpy(void *dest, const void *src, size_t n);
</code></pre>
<pre><code>// 拷贝内存中的整型数据
int arr[10] = { 1, 2, 3, 4, 5 };
int arr2[10];

memcpy(arr2, arr, sizeof(int)*10);

for (size_t i = 0; i &lt; 10; i++)
{
    printf("%d ", arr2[i]);
}
printf("\n");

// 拷贝内存中的字符串
char str[] = "hello world";
char p[100];
//memcpy(p, str, strlen(str)+1);   // 按内存拷贝
strcpy(p, str);    // 字符串拷贝
strncpy(p, str, strlen(str) + 1);

printf("p = %s\n", p);
</code></pre>
<h4>memmove</h4>
<ul>
<li>作用完全等同于 memcpy，以字节为单元，内存拷贝。---- &lt;span style="color:green"&gt;<strong>安全的！</strong>&lt;/span&gt;</li>
<li>拷贝的 src 和 dest 之间如果有重叠，memcpy由于底层实现原因，有可能出错。推荐使用 memmove。</li>
</ul>
<pre><code>void *memmove(void *dest, const void *src, size_t n);
</code></pre>
<h4>memcmp</h4>
<ul>
<li>以字节为单位，比较内存！</li>
<li>作用可以完全参照，strncmp。</li>
<li>规则：s1 == s2 --&gt; 0, s1 &gt; s2 --&gt; 1, s2 &lt; s2 --&gt; -1</li>
</ul>
<pre><code>int memcmp(const void *s1, const void *s2, size_t n);
</code></pre>
<pre><code>int arr1[] = { 14, 56, 22, 59, 21, 49, 25, 86, 89, 66, 71, 31, 98 };
int arr2[] = { 14, 56, 29, 59, 21};

int ret = memcmp(arr1, arr2, 5 * sizeof(int));

printf("ret = %d\n", ret);
</code></pre>
<h3>函数内申请空间使用</h3>
<h4>A 函数内申请空间，A 函数使用</h4>
<ul>
<li>注意上述 4 点。</li>
</ul>
<pre><code>int *p = malloc(10);
free(p); p = NULL;
</code></pre>
<h4>A 函数内申请空间，B 函数使用</h4>
<ul>
<li>例1</li>
</ul>
<pre><code>// A函数中申请空间，B函数使用。
int* func1(void)
{
    int* p = malloc(4);
    *p = 456;
    return p;    // 返回的 heap 堆空间的地址值，函数调用结束，地址 有效。
    //return &amp;p;    // 返回的 stack 栈空间的地址值，函数调用结束，地址 无效。
}

int main(void)
{
    int* ret = NULL;
    ret = func1();

    printf("%d\n", *ret);
    free(ret);
    ret = NULL;

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<ul>
<li>例2</li>
</ul>
<pre><code>// A函数中申请空间，B函数使用。
int* func2(int *p)
{
    p = malloc(4);
    return p;
}

int main(void)
{
    int* ret = NULL;
    ret = func2(ret);

    *ret = 345;    // 写
    printf("%d\n", *ret);    // 读

    free(ret);
    ret = NULL;

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<ul>
<li>例3</li>
</ul>
<pre><code>void func3(int** p)    // int** 是一级指针的地址。
{
    *p = malloc(4);
}

int main(void)
{
    int* ret = NULL;

    func3(&amp;ret);    // 传 一级指针的地址。
    // func3 函数，调用完成，ret指针，不在为 NULL，而是指向一块有效的 heap 空间地址。
    *ret = 789;
    printf("%d\n", *ret);

    free(ret);
    ret = NULL;

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h3>结构体</h3>
<h4>结构体定义语法</h4>
<ul>
<li>复合类型：用户自定义类型：<code>int []</code>、<code>int *</code>、<code>char **</code>、<code>struct student</code></li>
</ul>
<pre><code>// 定义结构体类型
struct student {
    int age;       // 成员变量 -- 属性。不能被赋初值。
    int num;
    char name[10];
};
// 用结构体类型，定义变量。
struct student a;    // 定义了一个结构体类型的变量 a。
</code></pre>
<ul>
<li>
<p>以上定义了一个结构体类型。名字叫 <code>struct student</code>。</p>
<ul>
<li><code>struct student</code> 的地位，等同于 <code>int</code>、<code>char</code>、<code>short</code>、<code>char *</code>、<code>int[]</code>、<code>long long</code></li>
</ul>
</li>
<li>
<p>通常 结构体类型定义在 全局位置。或者，放到 <code>xxx.h</code> 头文件。</p>
</li>
<li>
<p>头文件：</p>
</li>
</ul>
<pre><code>// 头文件守卫
#ifndef _XXX_H_
#define _XXX_H_
/* #include、宏定义、函数声明、类型定义（结构体类型）。
#endif
</code></pre>
<h4>普通结构体变量</h4>
<h5>定义语法</h5>
<pre><code>struct student stu1, stu2, stu3;    // 一次定义3个变量。没赋初值。
struct student stu = {18, 1, "Andy"};
</code></pre>
<h5>访问成员方法</h5>
<ul>
<li>使用 <code>.</code> 访问成员。</li>
</ul>
<pre><code>// 定义一个结构体变量，赋初值。
struct student stu = {18, 1, "Andy"};

printf("first: age = %d, name = %s, num = %d\n",
    stu.age, stu.name, stu.num);

stu.age = 118;
stu.num = 119;
//stu.name = "cuihua";    // name为地址常量，不能被赋值。
strcpy(stu.name, "cuihua");

printf("last: age = %d, name = %s, num = %d\n",
    stu.age, stu.name, stu.num);
</code></pre>
<ul>
<li>普通变量使用 "-&gt;" 访问成员 ---- &lt;span style="color:orange"&gt;<strong>不常用！</strong>&lt;/span&gt;</li>
</ul>
<pre><code>(&amp;stu)-&gt;age = 118;
(&amp;stu)-&gt;num = 119;
</code></pre>
<h4>结构体指针变量</h4>
<h5>定义语法</h5>
<pre><code>struct student *p1, *p2, *p3;    // 一次定义3个指针变量。野指针！！！
</code></pre>
<h5>访问成员方法</h5>
<ul>
<li>使用 "-&gt;" 访问成员。</li>
<li>避免野指针、空指针：</li>
</ul>
<pre><code>// 1. struct student stu, *p1; //一次定义两个结构体变量，一个普通变量stu。另一指针变量p1
p1 = &amp;stu;    // 给指针初始化。

// 2. struct student *p1;
p1 = (struct student *)malloc(sizeof(struct student));
</code></pre>
<ul>
<li>指针使用 "." 访问成员。 ---- &lt;span style="color:orange"&gt;<strong>不常用！</strong>&lt;/span&gt;</li>
</ul>
<pre><code>(*p1).age = 18;
strcpy((*p1).name, "cuihua");
(*p1).num = 119;
</code></pre>
<h4>非常规定义语法（了解）</h4>
<pre><code>struct student {
    int age;
    int num;
    char name[10];
} s1, *s2;    // 定义结构体类型的同时，定义1个结构体变量 s1, 一个指针变量s2

struct {    // 匿名结构体
    int age;
    int num;
    char name[10];
} s3, *s4;    // 定义匿名结构体类型的同时，定义1个结构体变量 s3, 一个指针变量s4。

// 无法再定义其他变量。
</code></pre>
<h4>结构体数组</h4>
<pre><code>struct student {
    int age;
    int num;
    char name[10];
};
struct student stu[5] = {{18, 1, "Andy"}, {19, 2, "Lucy"}, {118, 3, "李四"}};
int n = sizeof(stu) / sizeof(stu[0]);
for (int i = 0; i&lt;n; i++)
{
    printf("age=%d,num=%d,name=%s\n", stu[i].age, stu[i].num, stu[i].name);
}
</code></pre>
<ul>
<li><code>struct student *stu;</code> // 要求 指针指向能存储3个student 元素的空间，并给3个元素赋初值。访问。</li>
</ul>
<pre><code>struct student *stu;    // 野指针.

// 得到到heap堆空间，当成数组使用。
stu = malloc(sizeof(struct student)*3);    // 等价于 struct student stu[3];

// 给数组的第1个元素赋值。
stu[0].age = 11;
stu[0].num = 111;
strcpy(stu[0].name, "aaa");

// 给数组的第2个元素赋值。
stu[1].age = 22;
stu[1].num = 222;
strcpy(stu[1].name, "bbb");

// 给数组的第3个元素赋值。
stu[2].age = 33;
stu[2].num = 333;
strcpy(stu[2].name, "ccc");

//int n = sizeof(stu) / sizeof(stu[0]);    // 不能求元素个数。

for (int i = 0; i &lt; 3; i++)
{
    printf("age=%d,num=%d,name=%s\n", stu[i].age, stu[i].num, stu[i].name);
}

free(stu);
stu = NULL;
</code></pre>
<h4>结构体嵌套</h4>
<pre><code>struct person {
    int age;
    char name[10]);
    // 类型
};
struct student {
    struct person man;    // person 类型的变量，作为 student 类型成员。
    int id;
    char addr[100];
};

int main(void)
{
    struct student stu = {{18, "zhaolu"}, 1, "北京朝阳区"};

    printf("age = %d\n", stu.man.age);
    printf("name = %s\n", stu.man.name);
    printf("addr = %s\n", stu.addr);

    // 修改
    stu.man.age = 119;
    strcpy(stu.man.name, "张三丰");
    strcpy(stu.addr, "武当山");

    printf("\nage = %d\n", stu.man.age);
    printf("name = %s\n", stu.man.name);
    printf("addr = %s\n", stu.addr);

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h3>做函数参数、返回值</h3>
<h4>结构体变量赋值</h4>
<ul>
<li>主要应用于，函数调用期间，实参给形参赋值。</li>
<li>要求：
<ul>
<li>结构体变量赋值时，必须&lt;span style="color:red"&gt;<strong>类型相同、成员个数一致、顺序一致</strong>&lt;/span&gt;。</li>
</ul>
</li>
</ul>
<h4>做参数、返回值</h4>
<ul>
<li>&lt;span style="color:green"&gt;<strong>传值：</strong>&lt;/span&gt;
<ul>
<li>结构体变量做函数参数，将结构体变量的值(实参)，拷贝一份给形参。</li>
<li>形参、实参 共 2 份结构体。</li>
</ul>
</li>
<li>&lt;span style="color:green"&gt;<strong>传址：</strong>&lt;/span&gt;
<ul>
<li>结构体指针变量做函数参数，将结构体的地址值做实参，拷贝一份给形参。</li>
<li>形参、实参 共 1 份结构体。</li>
</ul>
</li>
<li>&lt;span style="color:red"&gt;<strong>结论：</strong>&lt;/span&gt;
<ul>
<li>结构体做函数参数、返回值时，通常采用 "传址" 方式，节省空间。</li>
</ul>
</li>
</ul>
<pre><code>void func08(struct student **m)
{
    *m = malloc(sizeof(struct student));
    if (NULL == *m)
    {
        printf("malloc error\n");
        return -1;
    }
    //p-&gt;age = 100;
    //p-&gt;num = 1;
    //strcpy(p-&gt;name, "zyx");
    (*m)-&gt;age = 100;
    (*m)-&gt;num = 1;
    strcpy((*m)-&gt;name, "zyx");
}

int main(void)
{
    struct student* p = NULL;    // 空指针

    func08(&amp;p);

    //p-&gt;age = 100;
    //p-&gt;num = 1;
    //strcpy(p-&gt;name, "zyx");

    printf("age=%d, name=%s, num=%d\n", p-&gt;age, p-&gt;name, p-&gt;num);

    free(p);
    p = NULL;

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h3>含有指针成员的结构体</h3>
<ul>
<li>申请内存：&lt;span style="color:green"&gt;<strong>先申请外层空间，再申请内层空间。</strong>&lt;/span&gt;</li>
<li>释放内存：&lt;span style="color:green"&gt;<strong>先释放内层空间，再释放外层空间。</strong>&lt;/span&gt;</li>
</ul>
<pre><code>struct student {
    int age;
    int num;
    char *name;    // 野指针.
};

int main(void)
{
    struct student* p;    //野指针.

    // 给 p 初始化堆空间
    p = malloc(sizeof(struct student));
    if (NULL == p)
    {
        printf("malloc p error\n");
        return -1;
    }
    // 给成员变量 name 开辟堆空间
    p-&gt;name = malloc(sizeof(char) * 100);
    if (NULL == p-&gt;name)
    {
        printf("malloc p-&gt;name error\n");
        return -1;
    }
    // 写数据到结构体中
    p-&gt;age = 100;
    p-&gt;num = 10;
    strcpy(p-&gt;name, "张三丰");

    printf("age=%d, name=%s, num=%d\n", p-&gt;age, p-&gt;name, p-&gt;num);

    // 先释放内层空间
    free(p-&gt;name);
    p-&gt;name = NULL;

    free(p);
    p = NULL;

    return 0;
}
</code></pre>
<h3>typedef 关键字</h3>
<ul>
<li>给现有的数据类型起别名。&lt;span style="color:red"&gt;<strong>【注意】：不能定义新的数据类型。</strong>&lt;/span&gt;</li>
</ul>
<pre><code>typedef unsigned int  size_t;    // 给 unsigned int 起别名叫 size_t

int a;    a 是变量。
typedef int a;    a 变成了 类型名。 a b;    定义一个整型变量 b. (可读性差)
</code></pre>
<ul>
<li>通常使用 typedef 定义过的类型，添加一个 <code>"_t"</code> 结尾。</li>
<li>定义语法：
<ul>
<li><code>typedef 旧类型名 新类型名_t;</code></li>
</ul>
</li>
</ul>
<pre><code>typedef struct student {
    int age;
    int num;
    char *name;    // 野指针.
} stu_t;    // 新类型名：stu_t;
// 定义变量
struct student stu1;    // 依然可以正常使用
stu_t stu2;    // 定义一个 struct student 类型的变量。
</code></pre>
<ul>
<li>使用 typedef 的好处：
<ol>
<li>简化类型名。</li>
<li>便于代码的修改和维护。</li>
</ol>
</li>
</ul>
<pre><code>typedef long long int32_t;    // int ----&gt; long long

struct student {
    int age;
    int32_t num;
    char *name;    // 野指针.
    int32_t num1;
    int32_t num2;
    int32_t num3;
    int32_t num4;
} stu_t;
</code></pre>
<h3>共用体（联合体）</h3>
<pre><code>union test {
    char ch;
    short sh;
    int var;
};    // 创建一个联合体类型。
</code></pre>
<ul>
<li>特性：
<ul>
<li>内部所有成员变量的地址一致。等同于整个联合体的地址。</li>
<li>联合体的大小，是内部成员变量中，最大的那个成员的大小。（对齐）</li>
<li>&lt;span style="color:red"&gt;<strong>修改其中一个成员的值，其他成员的值也跟着变化。</strong>&lt;/span&gt;</li>
</ul>
</li>
</ul>
<pre><code>flowchart LR
    subgraph struct [struct test]
        direction TB
        s1["var 4字节"]
        s2["sh 2字节"]
        s3["ch 1字节"]
    end
    subgraph union [union test]
        direction TB
        u1["4字节"]
        u2["共享区域"]
    end
</code></pre>
<pre><code>typedef union test {
    char ch;
    short sh;
    int var;
} test_t;

int main(void)
{
    test_t obj;

    obj.var = 0x87654321;

    printf("&amp;obj       = %p\n", &amp;obj);
    printf("&amp;obj.ch = %p\n", &amp;obj.ch);
    printf("&amp;obj.sh = %p\n", &amp;obj.sh);
    printf("&amp;obj.var= %p\n", &amp;obj.var);

    printf("sizeof(test_t) = %u\n", sizeof(test_t));

    printf("var = %#x\n", obj.var);
    printf("sh  = %#x\n", obj.sh);
    printf("ch  = %#x\n", obj.ch);

    obj.ch = 0xAA;

    printf("var = %#x\n", obj.var);
    printf("sh  = %#x\n", obj.sh);
    printf("ch  = %#x\n", obj.ch);

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h2>第十一章 高级语法（文件基础）</h2>
<h3>枚举</h3>
<ul>
<li>语法：<code>enum 枚举名 { 枚举常量 };</code></li>
</ul>
<pre><code>enum color {red, green, blue, black, pink, yellow};
</code></pre>
<ul>
<li>枚举常量：
<ul>
<li>必须是整型常量，不允许是浮点数。可以是负值。默认值从 0 开始，后续常量较前一个 +1</li>
<li>可以给任意一个常量赋初值，后续常量较前一个 +1。</li>
</ul>
</li>
</ul>
<pre><code>enum color {red, green=-5, blue, black, pink=-18, yellow};
</code></pre>
<ul>
<li>示例：</li>
</ul>
<pre><code>//enum color { red, green = -5, blue, black, pink = 18, yellow };
enum { red, green = -5, blue, black, pink = 18, yellow };

int main(void)
{
    int flg = 2;
    if (flg == blue)
    {
        printf("blue is -4\n");
    }
    else
    {
        printf("bule is not %d, blue=%d\n", flg, blue);
    }

    printf("red = %d, yellow = %d\n", red, yellow);

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h3>文件</h3>
<h4>系统文件</h4>
<ul>
<li><code>scanf</code> -- 键盘 -- 标准输入 -- stdin -- 0</li>
<li><code>printf</code> -- 屏幕 -- 标准输出 -- stdout -- 1</li>
<li><code>perror</code> -- 屏幕 -- 标准错误 -- stderr -- 2</li>
</ul>
<p>以上 3 个文件，为系统文件。应用程序启动时，这3个文件被系统自动打开，程序执行结束，由系统自动关闭（隐式回收）。</p>
<pre><code>fclose(stdout);    // 关闭文件。
printf("hello world\n");    // 报错！！！
</code></pre>
<h4>文件分类</h4>
<ul>
<li>设备文件：（与硬件有直接关系）
<ul>
<li>屏幕、键盘、网卡、声卡、显卡、扬声器 …</li>
</ul>
</li>
<li>磁盘文件：
<ul>
<li>文本文件：文件内容为 ASCII 码</li>
<li>二进制文件：文件内容为二进制编码数据。</li>
</ul>
</li>
</ul>
<h4>文件指针</h4>
<ul>
<li>普通指针</li>
</ul>
<pre><code>int *p;    // 野指针.
p = &amp;a;    // 初始化方法1
p = malloc()    // 初始化方法2
</code></pre>
<ul>
<li>文件指针</li>
</ul>
<pre><code>FILE *fp;    // 野指针.
</code></pre>
<ul>
<li>文件指针，借助"文件操作函数"来改变 fp 为空、为野的情况！
<ul>
<li>举例：fopen()---&gt; 将 fp 变为 非野。</li>
</ul>
</li>
</ul>
<pre><code>flowchart LR
    A["FILE *fp 野指针"] --&gt;|"fopen()"| B["打开的文件描述信息"]
    B &lt;--&gt; C["内存"]
    C &lt;--&gt; D["磁盘"]
</code></pre>
<ul>
<li>操作文件，可以使用的函数：fputc、fgetc、fputs、fgets、fread、fwrite ...</li>
</ul>
<h4>文件操作一般步骤</h4>
<ol>
<li>打开文件：<code>fopen()</code> ------&gt; <code>FILE *fp;</code></li>
<li>读写文件：<code>fputc、fgetc、fputs、fgets、fread、fwrite...</code></li>
<li>关闭文件：<code>fclose()</code></li>
</ol>
<h4>文件操作</h4>
<h5>打开、关闭文件</h5>
<ul>
<li>打开文件</li>
</ul>
<pre><code>#include &lt;stdio.h&gt;
FILE *fp;    // 野指针
FILE * fopen(const char * filename, const char * mode);
    参1：待打开的文件名（访问路径）
    参2：文件打开权限。（如字，值多前3个）
        r：只读方式打开文件，文件如果不存在，报错！存在，以只读方式打开。
        w：只写方式打开文件，文件如果不存在，创建一个空文件。文件已经存在，清空并打开。
        w+：读、写方式打开文件，文件如果不存在，创建一个空文件。文件已经存在，清空并打开。
        r+：读、写方式打开文件，文件如果不存在，报错！存在，以读、写方式打开。
        a：以追加方式打开文件。
        b：操作二进制文件使用的。(windows)
    返回值：
        成功：返回打开文件的文件指针(fp) 【强调】：这个fp指针，不使用"解引用"操作数据。
        失败：NULL
</code></pre>
<ul>
<li>关闭文件</li>
</ul>
<pre><code>#include &lt;stdio.h&gt;
int fclose(FILE * stream);
    参：打开的文件fp（fopen()返回值）
    返回值：
        成功：0
        失败：-1
</code></pre>
<ul>
<li>示例：</li>
</ul>
<pre><code>int main(void)
{
    FILE* fp;

    // 打开文件
    //fp = fopen("C:\\itcast\\test.txt", "r");    // 错误传参
    //fp = fopen("C:\\itcast\\test.txt", "r");    // 正确传参
    //fp = fopen("C:/itcast/test.txt", "r");    // 正确传参
    //fp = fopen("C:/itcast/test2.txt", "r");    // 指定r打开，文件不存在，报错
    fp = fopen("C:/itcast/test2.txt", "w");    // 指定w打开，文件不存在创建，存在，清空
    if (fp == NULL)
    {
        perror("fopen error");    // printf("fopen error\n");
        return -1;
    }

    // 读写文件...

    // 关闭
    int ret = fclose(fp);
    printf("ret = %d, ----------finish\n", ret);

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h5>绝对、相对路径</h5>
<ul>
<li>绝对路径：
<ul>
<li>从系统磁盘的盘符开始，找到待访问的文件的路径。</li>
<li>windows 下的书写方法：
<ol>
<li><code>C:\\users\\afei\\Desktop\\TTTTTT\\01-复习.avi</code></li>
<li><code>C:/users/afei/Desktop/TTTTTT/01-复习.avi</code> -- 也是 Linux 系统。</li>
</ol>
</li>
</ul>
</li>
<li>相对路径：
<ol>
<li>如果在 VS 环境下，使用 Ctrl+F5 编译执行，文件的相对路径是相对于 <code>day11.vcxproj</code> 所在目录位置。不是相对于 .c 文件。</li>
<li>如果双击 .c 文件同级目录下的 Debug 目录下的 xxx.exe 文件，文件的相对路径是相对于 xxx.exe 所在的目录位置。</li>
<li>如果 gcc 生成的 xxx.exe 文件，运行。文件的相对路径是相对于 xxx.exe 所在的目录位置。</li>
</ol>
</li>
</ul>
<h5>按字符写文件 fputc</h5>
<pre><code>int fputc(int ch, FILE * stream);    // 将指定一个字符，写入指定文件
    参1：待写入的 字符
    参2：打开的文件 fp （fopen的返回值）
    返回值：
        成功：写入到文件中的那个字符的 ASCII
        失败：-1
</code></pre>
<ul>
<li>练习：创建一个新文件，向该文件中写入 26 个大写英文字母。</li>
</ul>
<pre><code>int main(void)
{
    char* filename = "03test.txt";    // 相对路径
    int ret = 0;
    char ch = 'A';

    FILE* fp = fopen(filename, "w");    // 文件存在，会清空
    if (NULL == fp)
    {
        perror("fopen error");
        return -1;
    }
    // 循环写26个大写字母 到 文件中。
    while (ch &lt;= 'Z')
    {
        ret = fputc(ch, fp);
        ch++;
    }

    ret = fclose(fp);
    printf("ret = %d, ----------finish\n", ret);

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<ul>
<li>fputc 向文件中写字符时，&lt;span style="color:green"&gt;<strong>文件读写指针（参照光标理解），会自动后移。</strong>&lt;/span&gt;</li>
</ul>
<h5>按字符读文件 fgetc</h5>
<pre><code>int fgetc(FILE * stream);    // 从指定文件中，读取一个字符。
    参：待读取的文件fp(fopen()的返回值)
    返回值：
        成功：实际读到的字符的ASCII
        失败：-1
</code></pre>
<pre><code>void read_file(void)
{
    char* filename = "03test.txt";
    int ret = 0;
    char ch = 0;    // 存储读到的字符

    FILE* fp = fopen(filename, "r");    // r方式打开有文件。
    if (NULL == fp)
    {
        perror("fopen error");
        return -1;
    }
    // 从文件中读 字符
    while (1)
    {
        ch = fgetc(fp);
        //printf("ch = %c\n", ch);
        if (ch == EOF)    // 已经读到文件末尾.
        {
            break;
        }
        printf("ch = %c\n", ch);    // 写到这里，不读 EOF 结束标记。
    }

    ret = fclose(fp);
    printf("ret = %d, ----------finish\n", ret);
}

int main(void)
{
    //write_file();
    read_file();    // 读一个已经存有数据的文件
    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<ul>
<li>fgetc 在读取文件时，&lt;span style="color:green"&gt;<strong>文件读写指针（参照光标理解），会自动后移。</strong>&lt;/span&gt;</li>
<li>&lt;span style="color:red"&gt;<strong>文本文件，结束处，系统会自动添加一个结束标记 EOF --&gt; -1 (#define EOF -1)</strong>&lt;/span&gt;
<ul>
<li>文件关闭时，系统自动添加。</li>
</ul>
</li>
</ul>
<h5>feof 函数</h5>
<pre><code>int feof(FILE * stream);    // 判断是否到达文件结尾。
    参：fopen()返回值
    返回值：
        到达文件结尾 ---&gt; 非0【真】
        没到达文件结尾 ---&gt; 0【假】
</code></pre>
<ul>
<li>作用：
<ul>
<li>用来判断文件是否到达结尾。既能判断文本文件，也能判断二进制文件。</li>
</ul>
</li>
<li>特性：
<ul>
<li>&lt;span style="color:red"&gt;<strong>要想使用 feof() 判断到达文件结尾，在 feof() 调用之前，必须要有 读文件的函数调用。</strong>&lt;/span&gt;</li>
</ul>
</li>
</ul>
<pre><code>FILE* fp = fopen("04test.txt", "r");
if (NULL == fp)
{
    perror("fopen error");
    return -1;
}

while (1)
{
    printf("没有到达文件结尾\n");
    // 没有这个fgetc函数读文件，feof函数，无法正常判断到达文件结尾。
    fgetc(fp);    // 一次读一个字符，读到的字符直接丢弃！
    if (feof(fp))
    {
        break;
    }
}

fclose(fp);
</code></pre>
<h5>按行读文件 fgets</h5>
<ul>
<li>获取一个字符串，以 <code>\n</code> 作为结束标记。自动添加 <code>\0</code>。空间足够大，读 <code>\n</code>。空间不足，舍弃 <code>\n</code>。一定会预留空间 存 <code>\0</code></li>
</ul>
<pre><code>char * fgets(char *str, int size, FILE * stream);
    参1：用来存储字符串的空间首地址
    参2：空间大小
    参3：数据来源的文件fp。
    返回值：
        成功：返回实际读到的字符串。
        失败：NULL

// 示例：
char buf[10];
printf("%s", fgets(buf, 10, stdin));    "hello" ---&gt; hello\n\0
printf("%s", fgets(buf, 10, stdin));    "helloworld" ---&gt; helloworl\0
</code></pre>
<h5>按行写文件 fputs</h5>
<ul>
<li>写出一个字符串，到文件中。如果字符串中没有 <code>\n</code>，不会写 <code>\n</code></li>
</ul>
<pre><code>int fputs(const char * str, FILE * stream);
    参1：将写出的字符串首地址。
    参2：写出源的文件fp。
    返回值：
        成功：0
        失败：-1

// 示例：
char *str = "hello";
fputs(str, stdout);    ----&gt; 不添加 \n
</code></pre>
<h5>练习：接收用户键盘输入，写入文件</h5>
<ul>
<li>获取用户键盘输入，将所有内容，写入文件。规定：如果用户输入了 ":wq"，终止接收用户输入，将之前读到的数据，保存成一个文件。</li>
<li>提示：
<ul>
<li>从 stdin 中读入到程序中。写出到文件 fp。</li>
<li>feof() 在本题中用不上，用 strcmp 判断 读到的句子，是不是 ":wq\n"。</li>
</ul>
</li>
</ul>
<pre><code>// 练习：接收用户键盘输入，写入文件。遇见 :wq 停止接收，保存成文件
int main(void)
{
    // 创建文件，具备写权限
    FILE* fp = fopen("05test.txt", "w");
    if (NULL == fp)
    {
        perror("fopen error");
        return -1;
    }

    // 创建一个空间，保存读到的数据内容
    char buf[MAX] = { 0 };

    // 从键盘读
    while (1)
    {
        fgets(buf, MAX, stdin);
        // 每读的一行数据，都判断是否是 ":wq\n"
        if (strcmp(buf, ":wq\n") == 0)
        {
            break;    // 用户输入了结束标志，终止读入，保存文件。
        }
        // 写入fp 文件中
        fputs(buf, fp);
    }

    // 关闭文件。
    fclose(fp);

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h5>练习：文件版四则运算</h5>
<ul>
<li>说明：</li>
<li>文件中有 表达式：</li>
</ul>
<pre><code>//四则运算.txt
10/2=
10*3=
4+3=
8-6=
</code></pre>
<ul>
<li>读出表达式，运算，将结果写回文件。</li>
</ul>
<pre><code>//四则运算.txt
10/2=5
10*3=30
4+3=7
8-6=2
</code></pre>
<ul>
<li>分析：
<ul>
<li><code>10/2= ---&gt; fgets(buf, 4096, 四则运算.txt 对应的 fp) ---&gt; "10/2=\n" ---&gt; 10 / 2 =</code></li>
<li><code>strtok()、sscanf()--&gt;选sscanf()实现 ---&gt; sscanf(buf, "%d%c%d=\n", &amp;a, &amp;ch, &amp;b) --&gt;</code></li>
<li><code>a = 10, b = 2, ch ='/'</code></li>
</ul>
</li>
</ul>
<pre><code>switch (ch) {
case '/':
    ret = a / b;
    break;
case '*':
    ret = a * b;
    break;
...
}
</code></pre>
<ul>
<li><code>fopen("", "w")</code> 清空原来只有表达式没有结果的文件，将带有结果的表达式直接覆盖。</li>
<li>拼接上述 字符串，使用 <code>sprintf() / strcat() --&gt; "10/2=5\n10*3=30\n4+3=7\n8-6=2\n"</code></li>
<li>最终写出：<code>char result[] "10/2=5\n10*3=30\n4+3=7\n8-6=2\n" ---fputs(result, fp)</code></li>
<li>实现</li>
</ul>
<pre><code>#define BUF_MAX 4096

// 写算式到文件中
void write_file06(void)
{
    FILE* fp = fopen("C:/itcast/四则运算.txt", "w");
    if (NULL == fp)
    {
        perror("fopen error");
        return;    // 退出函数调用。
    }

    fputs("10/2=\n", fp);
    fputs("10*3=\n", fp);
    fputs("4+3=\n", fp);
    fputs("10-2=\n", fp);

    fclose(fp);
}

// 从文件中读算式、提取、拆分、计算、写回
void read_file06(void)
{
    // 创建存储算式的 空间
    char buf[BUF_MAX] = { 0 };
    // 创建空间，保存算式及运算结果
    char str[BUF_MAX] = { 0 };
    // 创建空间，保存 4 个带有结果的算式
    char result[BUF_MAX] = { 0 };

    // 定义变量，保存运算数和运算符，结果
    int a, b, ret;
    char ch;

    FILE* fp = fopen("C:/itcast/四则运算.txt", "r");
    if (NULL == fp)
    {
        perror("fopen error");
        return;    // 退出函数调用。
    }
    // 循环读取文件中的算式
    while (1)
    {
        fgets(buf, BUF_MAX, fp);    // buf = "10/2=\n\0"

        // 判断是否到达文件结尾。
        if (feof(fp))
        {
            break;
        }

        // 将 sscanf 的返回值强转为 void
        (void)sscanf(buf, "%d%c%d=\n", &amp;a, &amp;ch, &amp;b);    // a:10  b:2  ch:'/'
        // 根据不同的运算符，做不同运算
        switch (ch) {
        case '/':
            ret = a / b;
            break;
        case '*':
            ret = a * b;
            break;
        case '+':
            ret = a + b;
            break;
        case '-':
            ret = a - b;
            break;
        default:
            printf("运算符错误\n");
            break;
        }
        // 拼接 ret 到 算式上。
        sprintf(str, "%d%c%d=%d\n", a, ch, b, ret);    // 10/2=5

        // 测试、是否能正常获取数据，拼接成带结果的表达式。
        //printf("%s", str);

        // 拼接 4 个子算式，到 一个 大空间中
        strcat(result, str);
    }

    // 测试、拼接 4 个子算式、到 一个 大空间中、打印输出。
    printf("%s", result);

    fclose(fp);

    // 清空原来没有结果的算式所在的文件。
    fp = fopen("C:/itcast/四则运算.txt", "w");
    if (NULL == fp)
    {
        perror("fopen error");
        return;    // 退出函数调用。
    }
    // 将既有算式，又有结果的字符串，写入到同一个文件中。
    fputs(result, fp);

    fclose(fp);
}

int main(void)
{
    // 测试写出算式到文件。
    write_file06();

    getchar();    // 从键盘读一个字符，如果用户不输入，程序不继续，阻塞等。

    // 测试读取算式
    read_file06();

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<ul>
<li>getchar() 函数
<ul>
<li>从键盘获取一个字符。返回 ASCII。如果用户不输入，程序都向后执行。</li>
</ul>
</li>
<li>putchar() 函数
<ul>
<li>向屏幕输出一个字符。</li>
<li><code>putchar('m');</code></li>
</ul>
</li>
</ul>
<h2>第十二章 高级语法（文件进阶）</h2>
<h3>格式化读写文件</h3>
<h4>fprintf() 函数</h4>
<ul>
<li><code>printf ---- sprintf ---- fprintf</code>
<ul>
<li>变参函数，参数列表中，有 "..."。最后一个固定参数通常是一个模式描述串(包含格式匹配符)，函数实际调用时传递的参数的个数、类型、顺序，由这个固定参数决定。</li>
</ul>
</li>
</ul>
<pre><code>printf("hello");
printf("%s", "hello");
printf("%d = %d%c%d\n", 10+5, 10, '+', 5);    ----&gt; 屏幕

char buf[1024];    // 内存空间 --- 缓冲区
sprintf(buf, "%d = %d%c%d\n", 10+5, 10, '+', 5)    ----&gt; buf 中
</code></pre>
<ul>
<li>函数原型</li>
</ul>
<pre><code>#include &lt;stdio.h&gt;
int fprintf(FILE * stream, const char * format, ...);    ----&gt; 文件fp 中
</code></pre>
<ul>
<li>测试</li>
</ul>
<pre><code>FILE* fp = fopen("abc", "w");    // 相对路径法
if (!fp)    // NULL == fp
{
    perror("fopen error");
    return -1;
}

fprintf(fp, "%d%c%d=%d\n", 10, '+', 7, 10+7);

fclose(fp);
</code></pre>
<h4>fscanf() 函数</h4>
<ul>
<li><code>scanf ---- sscanf ---- fscanf</code></li>
</ul>
<pre><code>int m;
scanf("%d", &amp;m);                键盘 ---&gt; m

char str[] = "98";
sscanf(str, "%d", &amp;m);          str ---&gt; m

FILE *fp = fopen("r");
fscanf(fp, "%d", &amp;m);           fp指向的文件中 ---&gt; m
</code></pre>
<ul>
<li>函数原型</li>
</ul>
<pre><code>int fscanf(FILE * stream, const char *format, ...);
</code></pre>
<ul>
<li>测试：</li>
</ul>
<pre><code>int a, b, c;
char ch;

FILE* fp = fopen("abc", "r");    // 相对路径法
if (!fp)    // NULL == fp
{
    perror("fopen error");
    return -1;
}

(void)fscanf(fp, "%d%c%d=%d\n", &amp;a, &amp;ch, &amp;b, &amp;c);

printf("a = %d\n", a);
printf("b = %d\n", b);
printf("c = %d\n", c);
printf("ch = %c\n", ch);

printf("%d%c%d=%d", a, ch, b, c);

fclose(fp);
</code></pre>
<h4>格式化读写特性（扩展知识）</h4>
<h5>返回值</h5>
<pre><code>int fprintf(FILE * stream, const char * format, ...);
</code></pre>
<ul>
<li>fprintf() 函数的返回值：
<ul>
<li>成功：实际写入文件的字符个数。</li>
<li>失败：-1</li>
</ul>
</li>
</ul>
<pre><code>int fscanf(FILE * stream, const char * format, ...);
</code></pre>
<ul>
<li>fscanf() 函数的返回值：
<ul>
<li>成功：正确匹配的个数。</li>
<li>失败：-1</li>
</ul>
</li>
</ul>
<h5>fscanf 读取特性</h5>
<ol>
<li>边界溢出问题。存储读取数据的空间，在使用之前，应该进行清空。否则会出现边界溢出异常。
<ul>
<li>清空：<code>memset(buf, 0, sizeof(buf));</code></li>
</ul>
</li>
<li>&lt;span style="color:red"&gt;<strong>fscanf 函数，每次在调用的同时，都会判断，下一次调用是否能成功匹配参2，如果不匹配提前结束读取文件（feof(fp) 为真）</strong>&lt;/span&gt;</li>
</ol>
<h5>练习：文件版排序</h5>
<ul>
<li>生成随机数，写入文件。将文件内乱序随机数读出，排好序再写回文件。</li>
</ul>
<pre><code>// 生成随机数，写入文件
void write_rand(void)
{
    FILE* fp = fopen("test03.txt", "w");    // 相对路径法
    if (!fp)    // NULL == fp
    {
        perror("fopen error");
        return -1;
    }

    // 播种种机种子
    srand(time(NULL));
    for (size_t i = 0; i &lt; 10; i++)
    {
        //int num = rand() % 100;    // 0 -- 99
        fprintf(fp, "%d\n", rand() % 100);    // 将生成的随机数写入到文件。
    }

    fclose(fp);
}

void BubbleSort(int* src, int len)
{
    for (int i = 0; i &lt; len - 1; i++)
    {
        for (int j = 0; j &lt; len - 1 - i; j++)
        {
            if (src[j] &gt; src[j + 1])
            {
                int temp = src[j];
                src[j] = src[j + 1];
                src[j + 1] = temp;
            }
        }
    }
}

// 从文件中，读取随机数
void read_rand(void)
{
    // 定义数组，存储 10 个随机数
    int arr[10] = { 0 }, i = 0;

    FILE* fp = fopen("test03.txt", "r");    // 相对路径法
    if (!fp)    // NULL == fp
    {
        perror("fopen error");
        return -1;
    }
    // 循环读取文件内的随机数
    while (1)
    {
        (void)fscanf(fp, "%d\n", &amp;arr[i]);
        i++;
        if (feof(fp))    // 先存储，后判断，防止最后一个元素丢失。
        {
            break;
        }
    }
    // 使用冒泡排序
    BubbleSort(arr, sizeof(arr)/sizeof(arr[0]));

    fclose(fp);    //关闭随机数据的文件

    // 清空 随机数据的文件
    fp = fopen("test03.txt", "w");
    if (!fp)    // NULL == fp
    {
        perror("fopen error");
        return -1;
    }

    for (size_t i = 0; i &lt; 10; i++)
    {
        fprintf(fp, "%d\n", arr[i]);    // 将排好序的数组写入到文件。
    }

    fclose(fp);
}

int main(void)
{
    write_rand();

    getchar();    // 从键盘获取一个字符，如果用户不输入，就阻塞程序，不向下执行。

    read_rand();

    printf("--------------finish\n");

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h3>按块读写文件</h3>
<table>
<thead>
<tr>
<th>读写函数</th>
<th>对应关系</th>
</tr>
</thead>
<tbody>
<tr>
<td>fgetc</td>
<td>fputc</td>
</tr>
<tr>
<td>fgets</td>
<td>fputs</td>
</tr>
<tr>
<td>fprintf</td>
<td>fscanf</td>
</tr>
</tbody>
</table>
<p>以上 3 组函数，默认用来处理文本文件。</p>
<p>fwrite - fread 既可以处理文本文件，也可以处理二进制文件。</p>
<h4>fwrite() 函数</h4>
<ul>
<li>写出数据到文件中。</li>
</ul>
<pre><code>#include &lt;stdio.h&gt;
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
    参1：待写出的数据的首地址。
    参2：待写出数据的大小（一次写多大）
    参3：写出的个数（写多少次）    写出数据的总大小 = 参2 x 参3
    参4：文件fp
    返回值：
        成功：永远返回参3。通常调用函数时，将 参2传1，参3代表实际写出的字节数。
        失败：0
// fwrite 函数，写入数据到文件中时，是按 二进制 写入。
</code></pre>
<ul>
<li>测试</li>
</ul>
<pre><code>typedef struct student {
    int age;
    char name[10];
    int num;
} stu_t;

// 按块写文件、fwrite
void write_struct(void)
{
    // 定义结构体数组
    stu_t stu[4] = {
        18, "afei", 10,
        20, "andy", 20,
        30, "lily", 30,
        16, "james", 40
    };

    FILE* fp = fopen("test04.txt", "w");    // 相对路径法
    if (!fp)    // NULL == fp
    {
        perror("fopen error");
        return ;
    }

    int ret = fwrite(&amp;stu[0], 1, sizeof(stu_t) * 4, fp);
    if (ret == 0)
    {
        perror("fwrite error");
        return ;
    }
    printf("ret = %d\n", ret);

    fclose(fp);
}
</code></pre>
<h4>fread() 函数</h4>
<ul>
<li>从文件fp中读取数据。</li>
</ul>
<pre><code>size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
    参1：读取到的数据存放的地址。
    参2：一次读取数据的字节数（一次读多大）
    参3：读多少次    读出数据的总大小 = 参2 x 参3
    参4：文件fp
    返回值：
        成功：永远返回参3。 通常调用函数时，将 参2传1，参3代表实际读出的字节数。
        0 : 1) 读失败
            2) 到达文件结尾 == feof(fp)为真
</code></pre>
<ul>
<li>测试</li>
</ul>
<pre><code>typedef struct student {
    int age;
    char name[10];
    int num;
    char addr[100];
} stu_t;

// 按块读文件、fread，一次读一个 stu_t 元素
void read_struct(void)
{
    FILE* fp = fopen("test04.txt", "r");    // 相对路径法
    if (!fp)    // NULL == fp
    {
        perror("fopen error");
        return;
    }

    stu_t s1;

    // 从文件中，按块读取。
    int ret = fread(&amp;s1, 1, sizeof(stu_t), fp);
    printf("ret = %d\n", ret);

    printf("age=%d, name=%s, num=%d\n", s1.age, s1.name, s1.num);

    fclose(fp);
}

// 按块读文件、fread，一次读所有 stu_t 元素
void read_struct2(void)
{
    FILE* fp = fopen("test04.txt", "r");    // 相对路径法
    if (!fp)    // NULL == fp
    {
        perror("fopen error");
        return;
    }

    stu_t s1[10];    // stu_t *s1 = malloc(sizeof(stu_t) * 1024);
    int i = 0;

    // 从文件中，循环按块读取。
    while (1)
    {
        int ret = fread(&amp;s1[i], 1, sizeof(stu_t), fp);
        //if (ret == 0)    // 效果一样。
        if (feof(fp))    // 效果一样。
        {
            printf("读取到文件结尾\n");
            break;
        }
        printf("age=%d, name=%s, num=%d\n", s1[i].age, s1[i].name, s1[i].num);
        i++;
    }
    fclose(fp);
}
</code></pre>
<h4>练习：大文件拷贝</h4>
<ul>
<li>已知一个任意类型的文件，对该文件复制，产生一个相同的新文件。</li>
<li>实现思路：
<ol>
<li>打开两个文件。一个 "r"，另一个 "w"</li>
<li>从 r 中 fread，fwrite 写到 w 文件中</li>
<li>循环读，判断到达文件结尾，跳出循环。</li>
<li>关闭2个文件。</li>
</ol>
</li>
<li>注意: 在windows下，打开二进制文件（mp4, MP3, avi, jpg）读写，需要 "b"。如："rb"、"wb"</li>
</ul>
<pre><code>int main(void)
{
    // 创建一个缓存区（内存空间），用来存储读到的数据。
    char buf[4096] = { 0 };
    int ret = 0;

    FILE* rfp = fopen("C:\\users\\afei\\Desktop\\TTTTTT\\11-午后回顾.avi", "rb");
    if (!rfp)
    {
        perror("fopen error");
        return -1;
    }
    FILE* wfp = fopen("mycopy.avi", "wb");
    if (!rfp)
    {
        perror("fopen error");
        return -1;
    }

    // 循环从文件中读，写到另一个文件中。
    while (1)
    {
        ret = fread(buf, 1, sizeof(buf), rfp);
        if (ret == 0)
        {
            break;    // 读到文件末尾。
        }
        printf("ret = %d\n", ret);
        // 没有读到文件末尾，将实际读到的数据，"原封不动"的写入 wfp 中。
        fwrite(buf, 1, ret, wfp);
    }

    fclose(rfp);
    fclose(wfp);

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h3>随机位置读写文件</h3>
<ul>
<li>&lt;span style="color:green"&gt;<strong>强调：文件读写指针，在一个文件内，只有一个。读、写都使用这一个。</strong>&lt;/span&gt;</li>
</ul>
<h4>fseek</h4>
<pre><code>#include &lt;stdio.h&gt;
int fseek(FILE *stream, long offset, int whence);    // 修改文件偏移量(文件读写指针)
    参1：文件fp
    参2：偏移量（矢量：正数向后，负数向前）
    参3：偏移的起始位置
        SEEK_SET：文件开头位置
        SEEK_CUR：当前位置
        SEEK_END：文件结尾位置
    返回值：成功：0    失败：-1
</code></pre>
<h4>ftell</h4>
<pre><code>long ftell(FILE *stream);    // 获取文件偏移量（文件读写指针位置）
    返回值：从当前读写位置，到文件起始位置的偏移量。

// 借助 ftell(fp) + fseek(fp, -50, SEEK_END) 来求取文件大小。
</code></pre>
<h4>rewind</h4>
<pre><code>void rewind(FILE *stream);    // 回卷文件读写指针，将文件读写指针移动到起始位置。
// 相当于：fseek(fp, 0, SEEK_SET);
</code></pre>
<h3>其他文件相关操作</h3>
<h4>Linux 和 Windows 文件区别</h4>
<ol>
<li>对于二进制文件操作，Windows 下必须要使用 "b"。Linux下 二进制文件和文本文件操作没区别。</li>
<li>windows下的回车换行 <code>\r\n</code>，回车 <code>\r</code>，换行 <code>\n</code>。Linux 下 回车换行 <code>\n</code>。</li>
<li>&lt;span style="color:red"&gt;<strong>对文件指针：</strong>&lt;/span&gt;
<ul>
<li>先写后读可以直接操作。windows 和 Linux 一致。</li>
<li>先读后写。Linux无序修改。Windows下需要在写操作之前，添加 <code>fseek(fp, 0, SEEK_CUR)</code> 函数调用，获取文件读写指针，再来写。才能生效。</li>
</ul>
</li>
</ol>
<pre><code>int main(int argc, char* argv[])
{
    FILE* fp = fopen("test1.txt", "r+");

    char buf[6] = { 0 };
    char* ptr = fgets(buf, 6, fp);

    printf("buf=%s, ptr=%s\n", ptr, buf);

    fseek(fp, 0, SEEK_CUR);    //获取文件读写指针。如果没有这句，win下程序会崩溃。

    int ret = fputs("AAAA", fp);
    printf("ret = %d\n", ret);

    fclose(fp);

    return 0;
}
</code></pre>
<h4>获取文件状态</h4>
<ul>
<li><code>ftell(fp) + fseek(fp, 0, SEEK_END)</code> 可以获取文件大小。此种方法获取文件大小，必须要打开文件，文件打开、关闭操作，对于系统而言，系统资源消耗较大。</li>
</ul>
<pre><code>#include &lt;sys/types.h&gt;
#include &lt;sys/stat.h&gt;
int stat(const char *path, struct stat *buf);    // status
    参1：文件访问路径
    参2：文件属性结构体指针（传出参数：函数调用结束时，充当函数返回值）
    返回值：成功：0    失败：-1
// 示例：
struct stat buf;
stat("待打开文件", &amp;buf);
buf.st_size    获取文件的实际大小。
</code></pre>
<ul>
<li>获取文件大小</li>
</ul>
<pre><code>struct stat buf;

int ret = stat("test06.txt", &amp;buf);    // buf传出参数
printf("ret = %d\n", ret);

printf("获取文件的大小为：%d\n", buf.st_size);
</code></pre>
<h4>删除、重命名文件</h4>
<pre><code>int remove(const char *pathname);    // 删除

int rename(const char *oldpath, const char *newpath);    // 重命名


// 重命名：
//int ret = rename("test06.txt", "呜呜哈嘿.txt");
//printf("ret = %d\n", ret);

// 删除文件
int ret = remove("呜呜哈嘿.txt");
printf("ret = %d\n", ret);
</code></pre>
<h3>缓冲区刷新</h3>
<pre><code>flowchart LR
    A[程序] --&gt;|"写入数据"| B["缓冲区 (512B)"]
    B --&gt;|"刷新到"| C["磁盘 磁道 扇区"]
    C --&gt;|"读入"| B
    B --&gt;|"读取数据"| A
</code></pre>
<blockquote>
<p>&lt;span style="color:orange"&gt;<strong>磁盘</strong>：物理操作，速度慢。&lt;/span&gt;
&lt;span style="color:orange"&gt;<strong>内存</strong>：电子操作，速度快。&lt;/span&gt;
&lt;span style="color:green"&gt;<strong>缓冲区</strong>：存在的意义，一次性物理访问磁盘盘过程时，尽可能多的读数据，保存在内存中。&lt;/span&gt;
&lt;span style="color:blue"&gt;<strong>预读入、缓输出。</strong>&lt;/span&gt;</p>
</blockquote>
<ul>
<li>标准输出 -- stdout -- 标准输出缓冲区。
<ul>
<li>写给屏幕的数据，都是先存入缓冲区中，由缓冲区一次性刷新到物理设备（屏幕）</li>
</ul>
</li>
<li>标准输入 -- stdin -- 标准输入缓冲区。
<ul>
<li>从键盘读取的数据，直接读到缓冲区，由缓冲区给程序提供数据。</li>
</ul>
</li>
<li>缓冲机制：
<ol>
<li>&lt;span style="color:green"&gt;<strong>行缓冲</strong>&lt;/span&gt;：遇到 <code>\n</code> 刷新缓冲区的数据到物理设备上。<code>printf();</code></li>
<li>&lt;span style="color:green"&gt;<strong>全缓冲</strong>&lt;/span&gt;：缓冲区存满，数据才刷新到物理设备上、文件。</li>
<li>&lt;span style="color:green"&gt;<strong>无缓冲</strong>&lt;/span&gt;：缓冲区中只要有数据，立即刷新到物理设备。<code>perror</code></li>
</ol>
</li>
<li>手动刷新缓冲区的方法：</li>
</ul>
<pre><code>#include &lt;stdio.h&gt;
int fflush(FILE *stream);
    参：文件fp
    返回值：成功：0    失败：-1
</code></pre>
<ul>
<li>当文件关闭时，会强制刷新缓冲区，写入磁盘。 —— 隐式回收</li>
<li>隐式回收：关闭文件，刷新缓冲区，释放malloc申请的内存。</li>
</ul>
<pre><code>int main(void)
{
    FILE* fp = fopen("test10.txt", "w-");
    if (!fp)
    {
        perror("fopen error");
        return -1;
    }
    char ch = 0;

    while (1)
    {
        (void)scanf("%c", &amp;ch);
        if (ch == ':')
        {
            break;
        }
        fputc(ch, fp);

        // 手动刷新缓冲区，写入物理磁盘。
        fflush(fp);
    }
    fclose(fp);    // 当文件关闭时，会强制刷新缓冲区，写入磁盘。

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h2>第十三章 练习（电话簿管理系统）</h2>
<h3>实现流程</h3>
<ol>
<li>定义联系人的数据结构（结构体）</li>
<li>创建 存储联系人数据的 static 全局数组 和 记录联系人实际数量的 static 全局变量。</li>
<li>搭建电话簿的主业务逻辑框架（工作流程）</li>
<li>定义并实现相应的功能函数
<ol>
<li>添加联系人</li>
<li>删除联系人</li>
<li>查找联系人</li>
<li>显示联系人</li>
</ol>
</li>
<li>组织上述功能函数的调用逻辑。</li>
<li>整体测试、微调。</li>
</ol>
<h3>框架搭建1</h3>
<h4>main.c</h4>
<pre><code>// 管理程序运行的主业务逻辑
#define _CRT_SECURE_NO_WARNINGS
#include "telephoneBook.h"

// 定义枚举，使用枚举常量描述事件
enum {ADD=1, DEL, SREACH, SHOW, EXIT};

// 显示菜单
void show_menu(void)
{
    printf("---------显示菜单-----------\n");
}

// 电话簿启动、工作
void startworking(void)
{
    // 电话簿具体工作内容
    while (1)
    {
        // 显示菜单
        show_menu();

        // 接收用户输入(1.添加，2.删除，3.查找，4.显示，5.退出)
        int number = 0;
        printf("请在菜单中选择您的需求：");
        (void)scanf("%d", &amp;number);

        // 根据用户输入，作用相应操作
        switch (number)
        {
        case ADD:
            printf("1. 添加联系人\n");
            break;
        case DEL:
            printf("2. 删除联系人\n");
            break;
        case SREACH:
            printf("3. 查找联系人\n");
            break;
        case SHOW:
            printf("4. 显示联系人\n");
            break;
        case EXIT:
            printf("5. 退出系统\n");
            return;    // 结束整个电话簿系统
        default:
            break;
        }
    }
}

int main(void)
{
    startworking();

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h4>telephoneBook.c</h4>
<pre><code>// 管理电话簿 相关信息
#include "telephoneBook.h"

// 定义 结构体数组 存储联系人
static person_t numbers[MAX_PERSON_NUM];

// 描述数组中，实际存储联系人个数
static int size = 0;

// 具体添加联系人
// 具体删除联系人
// 具体查找联系人
// 显示所有联系人
</code></pre>
<h4>telephoneBook.h</h4>
<pre><code>// #pragma once    // windows 系统自带防止头文件重复包含。

#ifndef _TELEPHONE_BOOK_H_
#define _TELEPHONE_BOOK_H_

// 系统库头文件引入
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;math.h&gt;
#include &lt;time.h&gt;

// 函数声明
void show_menu(void);

// 宏定义，常量描述数组容量
#define MAX_PERSON_NUM 2048

// 定义联系人结构体
typedef struct person {
    char name[32];    // 联系人姓名
    char phone[16];   // 联系人电话
} person_t;

#endif
</code></pre>
<h3>框架搭建2</h3>
<h4>main.c</h4>
<pre><code>// 管理程序运行的主业务逻辑
#define _CRT_SECURE_NO_WARNINGS

#include "telephoneBook.h"

// 定义枚举，使用枚举常量描述事件
enum {ADD=1, DEL, SREACH, SHOW, EXIT};

// 显示菜单
void show_menu(void)
{
    printf("\n欢迎使用简易电话簿系统\n\n");
    printf("\t1. 添加联系人\n\n");
    printf("\t2. 删除联系人\n\n");
    printf("\t3. 查找联系人\n\n");
    printf("\t4. 显示联系人\n\n");
    printf("\t5. 退出系统\n\n");
}

// ADD分支
void add(void)
{
    printf("\n\t【姓名】为：");
    char name[32] = { 0 };        // 临时获取用户输入、存储联系人姓名
    (void)scanf("%s", name);

    printf("\n\t【电话】为：");
    char phone[16] = { 0 };        // 临时获取用户输入、存储联系人姓名
    (void)scanf("%s", phone);

    // 相联系人数组中，添加一个新联系人
    add_person(name, phone);

    printf("\n联系人【%s】添加成功！\n", name);
}

// DEL分支
void del(void)
{
    printf("\n\t将【删除】的联系人姓名为：");
    char name[32] = { 0 };        // 临时获取用户输入、存储联系人姓名
    (void)scanf("%s", name);

    // 从联系人数组中，删除联系人
    del_person(name);

    printf("\n联系人【%s】已成功删除！\n", name);
}

// SREACH分支
void search(void)
{
    printf("\n\t将【查找】的联系人姓名为：");
    char name[32] = { 0 };        // 临时获取用户输入、存储联系人姓名
    (void)scanf("%s", name);

    // 从联系人数组中，查找联系人的电话
    char *phone = search_person(name);
    if (NULL == phone)
    {
        printf("\n抱歉！电话簿中【%s】查无此人！\n", name);
    }
    else
    {
        printf("n联系人【%s】的电话为：%s\n", name, phone);
    }
}

// SHOW分支  --- 可以不封装该函数，直接调用 telephoneBook.c 中的 show_person()
//void show(void)
//{
//    printf("4. 显示联系人\n");
//}

// 电话簿启动、工作
void startworking(void)
{
    // 电话簿具体工作内容
    while (1)
    {
        // 显示菜单
        show_menu();

        // 接收用户输入(1.添加，2.删除，3.查找，4.显示，5.退出)
        int number = 0;
        printf("请在菜单中选择您的需求：");
        (void)scanf("%d", &amp;number);

        // 根据用户输入，作用相应操作
        switch (number)
        {
        case ADD:
            add();
            break;
        case DEL:
            del();
            break;
        case SREACH:
            search();
            break;
        case SHOW:
            show_person();
            break;
        case EXIT:
            printf("\n欢迎下次再使用电话簿系统，再见~~\n");
            return;        // 结束整个电话簿系统
        default:
            break;
        }
    }
}

int main(void)
{
    startworking();

    system("pause");
    return EXIT_SUCCESS;
}
</code></pre>
<h4>telephoneBook.c</h4>
<pre><code>// 管理电话簿 相关信息
#include "telephoneBook.h"

// 定义 结构体数组 存储联系人
static person_t numbers[MAX_PERSON_NUM];

// 描述数组中，实际存储联系人个数
static int size = 0;

// 具体添加联系人
void add_person(const char* name, const char* phone)
{

}

// 具体删除联系人
void del_person(const char* name)
{

}

// 具体查找联系人 ---- 返回联系人的电话
char *search_person(const char* name)
{
    return NULL;
}

// 显示所有联系人
void show_person(void)
{

}
</code></pre>
<h4>telephoneBook.h</h4>
<pre><code>// #pragma once    // windows 系统自带防止头文件重复包含。

#ifndef _TELEPHONE_BOOK_H_
#define _TELEPHONE_BOOK_H_

// 系统库头文件引入
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;math.h&gt;
#include &lt;time.h&gt;

// 函数声明
void add_person(const char* name, const char* phone);
void del_person(const char* name);
char* search_person(const char* name);
void show_person(void);

void show_menu(void);
void add(void);
void del(void);
void search(void);

// 宏定义，常量描述数组容量
#define MAX_PERSON_NUM 2048

// 定义联系人结构体
typedef struct person {
    char name[32];    // 联系人姓名
    char phone[16];   // 联系人电话
} person_t;

#endif
</code></pre>
<h3>具体功能函数实现</h3>
<h4>添加联系人</h4>
<pre><code>// 具体添加联系人
void add_person(const char* name, const char* phone)
{
    // 校验参数
    if (NULL == name || NULL == phone)
    {
        printf("add_person 添加联系人失败！参数错误！\n");
        return;
    }

    strcpy(numbers[size].name, name);    // 存姓名到数组
    strcpy(numbers[size].phone, phone);  // 存电话到数组

    ++size;    // 更新size
}
</code></pre>
]]></content>
    <author><name>仙鹤</name></author>
    <category term="编程"/>
  </entry>
  <entry>
    <title>线性代数笔记</title>
    <link href="https://xianhe51920.github.io/posts/linear-algebra/" rel="alternate" type="text/html"/>
    <id>https://xianhe51920.github.io/posts/linear-algebra/</id>
    <published>2026-08-22T00:00:00.000Z</published>
    <updated>2026-08-22T00:00:00.000Z</updated>
    <summary>大一下线代：线性代数</summary>
    <content type="html"><![CDATA[<h1>线性代数笔记</h1>
<hr />
<h2>目录</h2>
<h3><a href="#%E7%AC%AC%E4%B8%80%E9%83%A8%E5%88%86%E8%A1%8C%E5%88%97%E5%BC%8F">第一部分：行列式</a></h3>
<ul>
<li><a href="#11%E8%A1%8C%E5%88%97%E5%BC%8F%E7%9A%84%E6%80%A7%E8%B4%A8">1.1 行列式的性质</a></li>
<li><a href="#12%E8%A1%8C%E5%88%97%E5%BC%8F%E7%9A%84%E8%AE%A1%E7%AE%97">1.2 行列式的计算</a></li>
<li><a href="#13%E5%85%8B%E8%8E%B1%E5%A7%86%E6%B3%95%E5%88%99">1.3 克莱姆法则</a></li>
</ul>
<h3><a href="#%E7%AC%AC%E4%BA%8C%E9%83%A8%E5%88%86%E7%9F%A9%E9%98%B5">第二部分：矩阵</a></h3>
<ul>
<li><a href="#21%E7%9F%A9%E9%98%B5%E7%9A%84%E6%A6%82%E5%BF%B5">2.1 矩阵的概念</a></li>
<li><a href="#22%E7%9F%A9%E9%98%B5%E7%9A%84%E5%8A%A0%E6%B3%95%E5%87%8F%E6%B3%95%E4%B8%8E%E6%95%B0%E4%B9%98">2.2 矩阵的加法、减法与数乘</a></li>
<li><a href="#23%E7%9F%A9%E9%98%B5%E7%9A%84%E4%B9%98%E6%B3%95">2.3 矩阵的乘法</a></li>
<li><a href="#24%E6%96%B9%E9%98%B5%E7%9A%84%E5%B9%82">2.4 方阵的幂</a></li>
<li><a href="#25%E7%9F%A9%E9%98%B5%E7%9A%84%E8%BD%AC%E7%BD%AE">2.5 矩阵的转置</a></li>
<li><a href="#26%E6%96%B9%E9%98%B5%E7%9A%84%E8%A1%8C%E5%88%97%E5%BC%8F">2.6 方阵的行列式</a></li>
<li><a href="#27%E6%96%B9%E9%98%B5%E7%9A%84%E4%BC%B4%E9%9A%8F%E7%9F%A9%E9%98%B5">2.7 方阵的伴随矩阵</a></li>
<li><a href="#28%E9%80%86%E7%9F%A9%E9%98%B5">2.8 逆矩阵</a></li>
<li><a href="#29%E7%9F%A9%E9%98%B5%E7%9A%84%E5%88%9D%E7%AD%89%E5%8F%98%E6%8D%A2">2.9 矩阵的初等变换</a></li>
<li><a href="#210%E5%88%9D%E7%AD%89%E7%9F%A9%E9%98%B5">2.10 初等矩阵</a></li>
<li><a href="#211%E7%9F%A9%E9%98%B5%E7%9A%84%E7%AD%89%E4%BB%B7">2.11 矩阵的等价</a></li>
<li><a href="#212%E5%88%9D%E7%AD%89%E5%8F%98%E6%8D%A2%E6%B3%95%E6%B1%82%E9%80%86%E7%9F%A9%E9%98%B5">2.12 初等变换法求逆矩阵</a></li>
<li><a href="#213%E5%88%86%E5%9D%97%E7%9F%A9%E9%98%B5">2.13 分块矩阵</a></li>
<li><a href="#214%E7%9F%A9%E9%98%B5%E7%9A%84%E7%A7%A9">2.14 矩阵的秩</a></li>
</ul>
<h3><a href="#%E7%AC%AC%E4%B8%89%E9%83%A8%E5%88%86%E5%90%91%E9%87%8F">第三部分：向量</a></h3>
<ul>
<li><a href="#31%E5%90%91%E9%87%8F%E7%9A%84%E6%A6%82%E5%BF%B5%E4%B8%8E%E7%BA%BF%E6%80%A7%E8%BF%90%E7%AE%97">3.1 向量的概念与线性运算</a></li>
<li><a href="#32%E5%90%91%E9%87%8F%E7%9A%84%E7%BA%BF%E6%80%A7%E7%BB%84%E5%90%88%E4%B8%8E%E7%BA%BF%E6%80%A7%E8%A1%A8%E7%A4%BA">3.2 向量的线性组合与线性表示</a></li>
<li><a href="#33%E5%90%91%E9%87%8F%E7%BB%84%E7%9A%84%E7%BA%BF%E6%80%A7%E7%9B%B8%E5%85%B3%E6%80%A7">3.3 向量组的线性相关性</a></li>
<li><a href="#34%E6%9E%81%E5%A4%A7%E7%BA%BF%E6%80%A7%E6%97%A0%E5%85%B3%E7%BB%84">3.4 极大线性无关组</a></li>
</ul>
<h3><a href="#%E7%AC%AC%E5%9B%9B%E9%83%A8%E5%88%86%E7%BA%BF%E6%80%A7%E6%96%B9%E7%A8%8B%E7%BB%84">第四部分：线性方程组</a></h3>
<ul>
<li><a href="#41%E7%BA%BF%E6%80%A7%E6%96%B9%E7%A8%8B%E7%BB%84%E7%9A%84%E8%A1%A8%E7%A4%BA%E6%B3%95">4.1 线性方程组的表示法</a></li>
<li><a href="#42%E7%BA%BF%E6%80%A7%E6%96%B9%E7%A8%8B%E7%BB%84%E8%A7%A3%E7%9A%84%E5%88%A4%E5%AE%9A">4.2 线性方程组解的判定</a></li>
<li><a href="#43%E7%BA%BF%E6%80%A7%E6%96%B9%E7%A8%8B%E7%BB%84%E7%9A%84%E8%A7%A3%E7%9A%84%E6%80%A7%E8%B4%A8">4.3 线性方程组的解的性质</a></li>
<li><a href="#44%E9%BD%90%E6%AC%A1%E7%BA%BF%E6%80%A7%E6%96%B9%E7%A8%8B%E7%BB%84%E7%9A%84%E5%9F%BA%E7%A1%80%E8%A7%A3%E7%B3%BB">4.4 齐次线性方程组的基础解系</a></li>
<li><a href="#45%E7%BA%BF%E6%80%A7%E6%96%B9%E7%A8%8B%E7%BB%84%E8%A7%A3%E7%9A%84%E7%BB%93%E6%9E%84">4.5 线性方程组解的结构</a></li>
<li><a href="#46%E7%BA%BF%E6%80%A7%E6%96%B9%E7%A8%8B%E7%BB%84%E7%9A%84%E6%B1%82%E8%A7%A3">4.6 线性方程组的求解</a></li>
</ul>
<h3><a href="#%E7%AC%AC%E4%BA%94%E9%83%A8%E5%88%86%E7%89%B9%E5%BE%81%E5%80%BC%E4%B8%8E%E7%89%B9%E5%BE%81%E5%90%91%E9%87%8F">第五部分：特征值与特征向量</a></h3>
<ul>
<li><a href="#51%E7%89%B9%E5%BE%81%E5%80%BC%E4%B8%8E%E7%89%B9%E5%BE%81%E5%90%91%E9%87%8F%E7%9A%84%E5%AE%9A%E4%B9%89%E4%B8%8E%E5%85%B3%E7%B3%BB">5.1 特征值与特征向量的定义与关系</a></li>
<li><a href="#52%E7%89%B9%E5%BE%81%E5%80%BC%E4%B8%8E%E7%89%B9%E5%BE%81%E5%90%91%E9%87%8F%E7%9A%84%E6%B1%82%E6%B3%95">5.2 特征值与特征向量的求法</a></li>
<li><a href="#53%E7%89%B9%E5%BE%81%E5%80%BC%E4%B8%8E%E7%89%B9%E5%BE%81%E5%90%91%E9%87%8F%E7%9A%84%E6%80%A7%E8%B4%A8">5.3 特征值与特征向量的性质</a></li>
<li><a href="#54%E7%9B%B8%E4%BC%BC%E7%9F%A9%E9%98%B5%E7%9A%84%E6%A6%82%E5%BF%B5%E4%B8%8E%E6%80%A7%E8%B4%A8">5.4 相似矩阵的概念与性质</a></li>
<li><a href="#55%E7%9F%A9%E9%98%B5%E7%9A%84%E5%AF%B9%E8%A7%92%E5%8C%96">5.5 矩阵的对角化</a></li>
<li><a href="#56%E5%86%85%E7%A7%AF%E4%B8%8E%E6%AD%A3%E4%BA%A4">5.6 内积与正交</a></li>
<li><a href="#57%E5%AE%9E%E5%AF%B9%E7%A7%B0%E7%9F%A9%E9%98%B5%E7%9A%84%E7%9B%B8%E4%BC%BC%E5%AF%B9%E8%A7%92%E5%8C%96">5.7 实对称矩阵的相似对角化</a></li>
</ul>
<h3><a href="#%E7%AC%AC%E5%85%AD%E9%83%A8%E5%88%86%E4%BA%8C%E6%AC%A1%E5%9E%8B">第六部分：二次型</a></h3>
<ul>
<li><a href="#61%E4%BA%8C%E6%AC%A1%E5%9E%8B%E5%8F%8A%E7%9F%A9%E9%98%B5%E8%A1%A8%E7%A4%BA">6.1 二次型及矩阵表示</a></li>
<li><a href="#62%E5%8C%96%E4%BA%8C%E6%AC%A1%E5%9E%8B%E4%B8%BA%E6%A0%87%E5%87%86%E5%BD%A2">6.2 化二次型为标准形</a></li>
<li><a href="#63%E4%BA%8C%E6%AC%A1%E5%9E%8B%E4%B8%8E%E5%AF%B9%E7%A7%B0%E7%9F%A9%E9%98%B5%E7%9A%84%E6%9C%89%E5%AE%9A%E6%80%A7">6.3 二次型与对称矩阵的有定性</a></li>
</ul>
<hr />
<h1>第一部分：行列式</h1>
<hr />
<h2>1.1 行列式的性质</h2>
<ol>
<li><strong>按行/列展开不变性</strong>：n阶矩阵 $A$ 的行列式按任意一行（列）展开，其值相等。</li>
</ol>
<hr />
<ol>
<li><strong>两行（列）全相同则行列式为0</strong>：若n阶行列式有两行（列）对应元素全相等，则行列式值为0。</li>
</ol>
<hr />
<ol>
<li>
<p><strong>行（列）的可拆分性</strong>：某一行（列）的元素为两数之和时，行列式可拆分为两个行列式之和：</p>
<h1>$$
\begin{vmatrix}
a_{11} &amp; a_{12} &amp; \dots &amp; a_{1n} \
b_i + c_i &amp; b_{i2} + c_{i2} &amp; \dots &amp; b_{in} + c_{in} \
\vdots &amp; \vdots &amp; &amp; \vdots \
a_{n1} &amp; a_{n2} &amp; \dots &amp; a_{nn}
\end{vmatrix}</h1>
<p>\begin{vmatrix}
a_{11} &amp; a_{12} &amp; \dots &amp; a_{1n} \
b_i &amp; b_{i2} &amp; \dots &amp; b_{in} \
\vdots &amp; \vdots &amp; &amp; \vdots \
a_{n1} &amp; a_{n2} &amp; \dots &amp; a_{nn}
\end{vmatrix}
+
\begin{vmatrix}
a_{11} &amp; a_{12} &amp; \dots &amp; a_{1n} \
c_i &amp; c_{i2} &amp; \dots &amp; c_{in} \
\vdots &amp; \vdots &amp; &amp; \vdots \
a_{n1} &amp; a_{n2} &amp; \dots &amp; a_{nn}
\end{vmatrix}
$$</p>
</li>
</ol>
<hr />
<ol>
<li><strong>初等变换对行列式的影响</strong>：
<ul>
<li>某一行（列）乘以数 $k$，行列式值变为原来的 $k$ 倍：$\det A_1 = k\det A$（$A_1$ 为 $A$ 的某行乘 $k$ 得到的矩阵）</li>
<li>某一行（列）的 $k$ 倍加到另一行（列），行列式值不变：$\det A_2 = \det A$（$A_2$ 为 $A$ 经过该变换后的矩阵）</li>
<li>交换两行（列），行列式值变号：$\det A_3 = -\det A$（$A_3$ 为 $A$ 交换两行后的矩阵）</li>
</ul>
</li>
</ol>
<hr />
<ol>
<li><strong>转置不变性</strong>：行列式与其转置行列式值相等，即 $\det A^T = \det A$。</li>
</ol>
<h3>行列式的推论</h3>
<ol>
<li>若行列式的某一行（列）元素全为0，则行列式值为0。</li>
<li>若行列式的某两行（列）对应元素成比例，则行列式值为0。</li>
</ol>
<hr />
<h2>1.2 行列式的计算</h2>
<ol>
<li><strong>范德蒙德行列式</strong>：
$$
V_n = \begin{vmatrix}
1 &amp; 1 &amp; \dots &amp; 1 \
x_1 &amp; x_2 &amp; \dots &amp; x_n \
x_1^2 &amp; x_2^2 &amp; \dots &amp; x_n^2 \
\vdots &amp; \vdots &amp; &amp; \vdots \
x_1^{n-1} &amp; x_2^{n-1} &amp; \dots &amp; x_n^{n-1}
\end{vmatrix}
= \prod_{1 \le i &lt; j \le n} (x_j - x_i)
= (x_2-x_1)(x_3-x_1)\dots(x_n-x_1)(x_3-x_2)\dots(x_n-x_2)\dots(x_n-x_{n-1})
$$</li>
</ol>
<hr />
<ol>
<li><strong>拉普拉斯定理推论（分块行列式）</strong>：
$$
\begin{vmatrix}
A &amp; O \
C &amp; B
\end{vmatrix}
= |A||B|, \quad
\begin{vmatrix}
A &amp; O \
O &amp; B
\end{vmatrix}
= |A||B|, \quad
\begin{vmatrix}
A &amp; C \
O &amp; B
\end{vmatrix}
= |A||B|
$$
$$
\begin{vmatrix}
O &amp; A \
B &amp; O
\end{vmatrix}
= (-1)^{mn}|A||B| \quad (m,n为A,B的阶数)
$$
（其中 $A,B$ 为方阵，$O$ 为零矩阵，$C$ 为任意矩阵）</li>
</ol>
<hr />
<h2>1.3 克莱姆法则</h2>
<ol>
<li><strong>引理</strong>：行列式的任一行元素乘另一行对应元素的代数余子式之和为 0。</li>
</ol>
<hr />
<ol>
<li><strong>克拉默法则</strong>：设 $n$ 阶矩阵 $A$ 可逆，则线性方程组 $Ax = b$ 有唯一解 $x=(x_1,x_2,\dots,x_n)^T$，其中
$$
x_j = \frac{\det A_j}{\det A} \quad (j=1,2,\dots,n)
$$
（$\det A_j$ 是用 $b$ 替换 $\det A$ 的第 $j$ 列得到的行列式）</li>
</ol>
<hr />
<ol>
<li><strong>齐次线性方程组判定</strong>：对于 $Ax=0$：
<ul>
<li>若 $\det A \neq 0$，方程组只有零解；</li>
<li>若 $\det A = 0$，方程组有非零解。</li>
</ul>
</li>
</ol>
<hr />
<h1>第二部分：矩阵</h1>
<hr />
<h2>2.1 矩阵的概念</h2>
<h3>矩阵与行列式对比</h3>
<table>
<thead>
<tr>
<th></th>
<th>行列式</th>
<th>矩阵</th>
</tr>
</thead>
<tbody>
<tr>
<td>本质</td>
<td>数</td>
<td>数表</td>
</tr>
<tr>
<td>形状</td>
<td>必须是方的（行数=列数）</td>
<td>行数和列数不一定相等</td>
</tr>
</tbody>
</table>
<h3>矩阵的定义</h3>
<ol>
<li><strong>矩阵</strong>：$m \times n$ 的数表</li>
<li><strong>同型矩阵</strong>：矩阵 $A$、$B$ 的行数和列数都相等</li>
<li><strong>矩阵相等</strong>：$A=B \iff$ 是同型矩阵，且对应位置元素相等</li>
</ol>
<h3>几类特殊矩阵</h3>
<ol>
<li><strong>方阵</strong>：行数=列数的矩阵</li>
<li><strong>行矩阵</strong>：只有一行的矩阵，如 $A = \begin{pmatrix} 1 &amp; 2 &amp; 3 \end{pmatrix}$</li>
<li><strong>列矩阵</strong>：只有一列的矩阵，如 $B = \begin{pmatrix} 1 \ 2 \ 3 \end{pmatrix}$</li>
<li><strong>零矩阵</strong>：元素全为 0 的矩阵，如 $\begin{pmatrix} 0 &amp; 0 \ 0 &amp; 0 \end{pmatrix}$、$\begin{pmatrix} 0 &amp; 0 &amp; 0 \end{pmatrix}$</li>
<li><strong>负矩阵</strong>：如 $A = \begin{pmatrix} 1 &amp; 3 &amp; 0 \ 2 &amp; -4 &amp; 5 \end{pmatrix}$，其负矩阵 $-A = \begin{pmatrix} -1 &amp; -3 &amp; 0 \ -2 &amp; 4 &amp; -5 \end{pmatrix}$</li>
<li><strong>上三角形矩阵</strong>：主对角线下方的元素全为 0 的矩阵，如 $\begin{pmatrix} 1 &amp; 2 &amp; 3 \ 0 &amp; -1 &amp; 4 \ 0 &amp; 0 &amp; 5 \end{pmatrix}$、$\begin{pmatrix} 0 &amp; 1 \ 0 &amp; 4 \end{pmatrix}$</li>
<li><strong>下三角形矩阵</strong>：主对角线上方的元素全为 0 的矩阵，如 $\begin{pmatrix} 1 &amp; 0 \ 3 &amp; 2 \end{pmatrix}$、$\begin{pmatrix} 0 &amp; 0 &amp; 0 \ -2 &amp; 5 &amp; 0 \ 0 &amp; 1 &amp; 4 \end{pmatrix}$</li>
<li><strong>对角形矩阵</strong>：非主对角线元素全为 0 的矩阵，如 $\begin{pmatrix} 1 &amp; 0 &amp; 0 \ 0 &amp; 2 &amp; 0 \ 0 &amp; 0 &amp; 3 \end{pmatrix}$、$\begin{pmatrix} 1 &amp; 0 \ 0 &amp; 3 \end{pmatrix}$，也可记为 $\text{diag}(1,2,3)$</li>
<li><strong>数量矩阵</strong>：主对角线上元素相同的对角矩阵，如 $\begin{pmatrix} a &amp; 0 &amp; 0 \ 0 &amp; a &amp; 0 \ 0 &amp; 0 &amp; a \end{pmatrix}$</li>
<li><strong>单位矩阵</strong>：主对角线上元素全为 1 的对角矩阵记作 $I$ 或 $E$：
$$
I = E = \begin{pmatrix}
1 &amp; &amp; &amp; \
&amp; 1 &amp; &amp; \
&amp; &amp; \ddots &amp; \
&amp; &amp; &amp; 1
\end{pmatrix}
$$</li>
</ol>
<hr />
<h2>2.2 矩阵的加法、减法与数乘</h2>
<h3>矩阵的加、减运算</h3>
<ul>
<li><strong>定义</strong>：同型矩阵对应位置元素相加/相减。</li>
<li><strong>运算规律</strong>：
<ol>
<li>$A+B = B+A$（交换律）</li>
<li>$(A+B)+C = A+(B+C)$（结合律）</li>
<li>$A+O = A$（$O$ 为零矩阵）</li>
<li>$A+(-A) = O$</li>
<li>$A-B = A+(-B)$</li>
<li>$A+B = C \iff A = C-B$</li>
</ol>
</li>
</ul>
<h3>矩阵的数乘运算</h3>
<ul>
<li><strong>定义</strong>：用数 $k$ 乘以矩阵 $A$ 的每一个元素，例：
$$
A = \begin{pmatrix} a &amp; b \ c &amp; d \end{pmatrix}, \quad kA = \begin{pmatrix} ka &amp; kb \ kc &amp; kd \end{pmatrix}
$$</li>
</ul>
<h3>公因子提取对比</h3>
<ol>
<li>矩阵：所有元素均有公因子 $k$，$k$ 向外提一次。</li>
<li>行列式：某一行（列）有公因子 $k$，$k$ 向外提一次。</li>
<li>行列式：所有元素均有公因子 $k$，$k$ 向外提 $n$ 次（$n$ 为阶数）。</li>
</ol>
<h3>数乘的运算规律</h3>
<ol>
<li>$k(A+B) = kA + kB$</li>
<li>$(k+l)A = kA + lA$</li>
<li>$k(lA) = (kl)A = l(kA)$</li>
<li>$1A = A$</li>
<li>$(-1)A = -A$</li>
</ol>
<hr />
<h2>2.3 矩阵的乘法</h2>
<h3>定义</h3>
<ol>
<li><strong>可乘条件</strong>：$A_{m \times n}$ 与 $B_{p \times q}$ 相乘，需要 $n = p$</li>
<li><strong>结果形状</strong>：$AB$ 是 $m \times q$ 矩阵。</li>
</ol>
<h3>矩阵乘法不满足的规律</h3>
<ol>
<li>不满足交换律：
<ul>
<li>AB，BA都有意义，二者不一定相等</li>
<li>AB有意义，BA不一定有意义</li>
</ul>
</li>
<li>不满足消去律：
<ul>
<li>若AB=AC，且A≠O，推不出B=C</li>
<li>若BA=CA，且A≠O，推不出B=C</li>
</ul>
</li>
<li>两个非零矩阵的成绩可能是零矩阵
<ul>
<li>若AB=O，推不出A=O，B=O</li>
</ul>
</li>
</ol>
<h3>矩阵乘法满足的运算规律</h3>
<ol>
<li><strong>结合律</strong>：$(AB)C = A(BC)$</li>
<li><strong>分配律</strong>：
<ul>
<li>左分配律：$(A+B)C = AC + BC$</li>
<li>右分配律：$C(A+B) = CA + CB$</li>
</ul>
</li>
<li><strong>数乘结合律</strong>：$k(AB) = (kA)B = A(kB)$</li>
<li><strong>单位矩阵性质</strong>：$AE = EA = A$</li>
<li><strong>数量矩阵性质</strong>：若 $A = aE$（数量矩阵），则 $AB = aB$</li>
</ol>
<h3>矩阵的可交换性</h3>
<p>若 $AB = BA$，则称 $A$ 与 $B$ 可交换。常见可交换情况：</p>
<ol>
<li>可交换的矩阵一定是同阶方阵</li>
<li>不同阶矩阵一定不可交换</li>
<li>AB≠BA，一定不可交换</li>
<li>单位矩阵E和任一同阶方阵可交换</li>
<li>两个同阶对角矩阵可交换</li>
</ol>
<hr />
<h2>2.4 方阵的幂</h2>
<ul>
<li><strong>定义</strong>：设 $A$ 为方阵，定义 $A^k = \underbrace{A \cdot A \cdot \dots \cdot A}_{k\text{个}}$，规定 $A^0 = E$。</li>
<li><strong>性质</strong>（$A$ 为方阵，$k,k_1,k_2$ 为非负整数，$k,l$ 为常数）：
<ol>
<li>$A^{k_1}A^{k_2} = A^{k_1+k_2}$</li>
<li>$(A^{k_1})^{k_2} = A^{k_1k_2}$</li>
<li>$(lA)^k = l^kA^k$</li>
</ol>
</li>
</ul>
<p><strong>注：由于矩阵乘法不满足交换律，一般情况下，$(AB)^k \neq A^kB^k$（$A,B$为同阶方阵）</strong></p>
<p>若矩阵$A,B$可交换，则有：
$A^2 - B^2 = (A+B)(A-B) = (A-B)(A+B)$
$(A+B)^2 = A^2 + 2AB + B^2$
$(A-B)^2 = A^2 - 2AB + B^2$
$A^3 - B^3 = (A-B)(A^2 + AB + B^2)$
$A^3 + B^3 = (A+B)(A^2 - AB + B^2)$
$(AB)^k = A^kB^k$</p>
<h3>恒成立公式</h3>
<p>$A^2 - E = (A+E)(A-E)$
$(A+E)^2 = A^2 + 2A + E$
$(A-E)^2 = A^2 - 2A + E$
$A^3 - E = (A-E)(A^2 + A + E)$
$A^3 + E = (A+E)(A^2 - A + E)$</p>
<h3>方阵多项式</h3>
<p>设$f(x) = a_mx^m + a_{m-1}x^{m-1} + \dots + a_1x + a_0$，则
$f(A) = a_mA^m + a_{m-1}A^{m-1} + \dots + a_1A + a_0E$</p>
<hr />
<h2>2.5 矩阵的转置</h2>
<h3>定义</h3>
<p>将矩阵的行依次变为列，列依次变为行，得到的新矩阵称为原矩阵的<strong>转置矩阵</strong>，记作 $A^T$。
例：若 $A = \begin{pmatrix} 1 &amp; 2 &amp; 3 \ 0 &amp; -1 &amp; 4 \end{pmatrix}$，则
$$
A^T = \begin{pmatrix} 1 &amp; 0 \ 2 &amp; -1 \ 3 &amp; 4 \end{pmatrix}
$$</p>
<h3>转置矩阵的性质</h3>
<ol>
<li>$(A^T)^T = A$</li>
<li>$(A+B)^T = A^T + B^T$</li>
<li>$(kA)^T = kA^T$（$k$ 为常数）</li>
<li>$(AB)^T = B^T A^T$（反序法则）</li>
<li>$(A^k)^T = (A^T)^k$（$k$ 为正整数）</li>
</ol>
<h3>对称矩阵与反对称矩阵</h3>
<h4>对称矩阵</h4>
<p>若 $n$ 阶矩阵 $A$ 满足 $A^T = A$，则称 $A$ 为<strong>对称矩阵</strong>。
等价条件：$A = (a_{ij})<em>{n×n}$ 为对称矩阵 $\iff a</em>{ij} = a_{ji}$（$i,j=1,2,\dots,n$）。
例：$A = \begin{pmatrix} 1 &amp; 2 &amp; 0 \ 2 &amp; 3 &amp; -1 \ 0 &amp; -1 &amp; 4 \end{pmatrix}$</p>
<p><strong>对称矩阵的结论</strong>：</p>
<ol>
<li>若 $A,B$ 为同阶对称矩阵，则 $A+B$、$A-B$ 仍为对称矩阵
<ul>
<li>$(A+B)^T=A^T+B^T=A+B$</li>
<li>$(A-B)^T=A^T-B^T=A-B$</li>
</ul>
</li>
</ol>
<hr />
<ol>
<li>若 $A$ 为对称矩阵，则 $kA$、$A^m$ 仍为对称矩阵（$k$ 为常数，$m$ 为正整数）</li>
</ol>
<hr />
<ol>
<li>若 $A,B$ 为同阶对称矩阵，则 $AB$ 为对称矩阵的充要条件是 $AB=BA$
<ul>
<li>证：$(AB)^T=B^TA^T=BA=AB$</li>
</ul>
</li>
</ol>
<hr />
<ol>
<li>对任意 $m×n$ 矩阵 $A$，则 $A^TA$、$AA^T$ 均为对称矩阵</li>
</ol>
<h4>反对称矩阵</h4>
<p>设 $A=(a_{ij})<em>{n×n}$，$A$ 为反对称矩阵 $\Leftrightarrow A^T=-A \Leftrightarrow a</em>{ij}=-a_{ji}$（$i,j=1,2,3\cdots n$）
注：$A$ 为反对称矩阵，主对角线元素全为0；关于主对角线对称位置的元素互为相反数</p>
<p><strong>反对称矩阵的结论</strong>：</p>
<ol>
<li>若 $A,B$ 为同阶反对称矩阵，则 $A+B$、$A-B$ 仍为反对称矩阵</li>
<li>若 $A$ 为反对称矩阵，则 $kA$ 仍为反对称矩阵</li>
<li>若 $A$ 为反对称矩阵，$k$ 为正整数，则 $A^k$ 为对称矩阵（$k$ 为偶数）、反对称矩阵（$k$ 为奇数）；$(A^k)^T=(A^T)^k=(-A)^k=(-1)^kA^k$</li>
</ol>
<hr />
<h2>2.6 方阵的行列式</h2>
<p>设 $n$ 阶方阵 $A$，则 $A$ 的行列式为 $|A|$
注：1. $A$ 的行列式 $|A|$ 是一个数；2. 只有方阵才有行列式</p>
<h3>方阵行列式的性质：</h3>
<p>设 $A,B$ 为 $n$ 阶方阵，$k$ 为常数，$m$ 为正整数：</p>
<ol>
<li>$|A^T|=|A|$</li>
<li>$|kA|=k^n|A|$</li>
<li>$|AB|=|A|\cdot|B|$</li>
<li>$|A^m|=|A|^m$，如 $|A^3|=|AAA|=|A||A||A|=|A|^3$</li>
<li>$|E|=1$（$E$ 为单位矩阵）</li>
</ol>
<hr />
<hr />
<h2>2.7 方阵的伴随矩阵</h2>
<h3>伴随矩阵的定义</h3>
<p>设 $ A=(a_{ij})<em>{n×n} $，$ |A| $ 中元素 $ a</em>{ij} $ 的代数余子式为 $ A_{ij} $（$ i,j=1,2,\dots,n $），$ A $ 的伴随矩阵 $ A^* $ 定义为：
$$
A^* = \begin{pmatrix}
A_{11} &amp; A_{21} &amp; \dots &amp; A_{n1} \
A_{12} &amp; A_{22} &amp; \dots &amp; A_{n2} \
\vdots &amp; \vdots &amp; &amp; \vdots \
A_{1n} &amp; A_{2n} &amp; \dots &amp; A_{nn}
\end{pmatrix}
$$</p>
<p><strong>注意</strong>：</p>
<ol>
<li>$ A^* $ 的元素是 $ a_{ij} $ 的代数余子式，是把行的代数余子式放到列上；</li>
<li>任一方阵都有伴随矩阵；</li>
<li>只有方阵才有伴随矩阵。</li>
</ol>
<h3>伴随矩阵的性质</h3>
<ol>
<li>对任意方阵 $ A $，有 $ AA^* = A^<em>A = |A|E $（不管 $ A $ 可逆与否，均成立）
例：
$$
A = \begin{pmatrix} a_{11} &amp; a_{12} &amp; a_{13} \ a_{21} &amp; a_{22} &amp; a_{23} \ a_{31} &amp; a_{32} &amp; a_{33} \end{pmatrix}, \quad A^</em> = \begin{pmatrix} A_{11} &amp; A_{21} &amp; A_{31} \ A_{12} &amp; A_{22} &amp; A_{32} \ A_{13} &amp; A_{23} &amp; A_{33} \end{pmatrix}
$$
$$
AA^* = \begin{pmatrix} |A| &amp; 0 &amp; 0 \ 0 &amp; |A| &amp; 0 \ 0 &amp; 0 &amp; |A| \end{pmatrix} = |A| \begin{pmatrix} 1 &amp; &amp; \ &amp; 1 &amp; \ &amp; &amp; 1 \end{pmatrix} = |A|E
$$</li>
</ol>
<hr />
<ol>
<li>若 $ A $ 为 $ n $ 阶方阵，则 $ |A^<em>| = |A|^{n-1} $
证：由 $ AA^</em> = |A|E $，两边取行列式得：
$$
|A||A^<em>| = | |A|E | = |A|^n |E| = |A|^n
$$
两边除以 $ |A| $（$ |A|≠0 $ 时），得 $ |A^</em>| = |A|^{n-1} $。</li>
</ol>
<hr />
<ol>
<li>若 $ A $ 为方阵，则 $ (A^T)^* = (A^*)^T $</li>
</ol>
<hr />
<ol>
<li>若 $ A $ 为 $ n $ 阶方阵，$ k $ 为常数，则 $ (kA)^* = k^{n-1}A^* $
证：以3阶矩阵为例：
$$
kA = \begin{pmatrix} ka &amp; kb &amp; kc \ kd &amp; ke &amp; kf \ kh &amp; ki &amp; kg \end{pmatrix}, \quad (kA)<em>{ij} = (-1)^{i+j} \begin{vmatrix} ke &amp; kf \ ki &amp; kg \end{vmatrix} = k^2 A</em>{ij}
$$
推广得 $ (kA)^* = k^{n-1}A^* $。</li>
</ol>
<hr />
<ol>
<li>若 $ A=\begin{pmatrix}a&amp;b\c&amp;d\end{pmatrix} $，则 $ A^*=\begin{pmatrix}d&amp;-b\-c&amp;a\end{pmatrix} $，主对换，负号易号。</li>
</ol>
<hr />
<h2>2.8 逆矩阵</h2>
<h3>逆矩阵的定义</h3>
<ol>
<li>
<p>设 $ A $ 为 $ n $ 阶方阵，若存在 $ n $ 阶方阵 $ B $，使 $ AB=BA=E $，则称 $ A $ 是可逆矩阵，$ B $ 为 $ A $ 的逆矩阵，记作 $ A^{-1} $。
注：任何方阵未必都有逆矩阵；若方阵 $ A $ 可逆，则 $ A^{-1} $ 是唯一的（证明：若 $ B_1,B_2 $ 满足 $ B_1A=E,AB_2=E $，则 $ B_1=B_1E=B_1(AB_2)=(B_1A)B_2=EB_2=B_2 $）。</p>
</li>
<li>
<p>设 $ A $ 为 $ n $ 阶方阵，若 $ |A|≠0 $，称 $ A $ 是非奇异矩阵，可逆；若 $ |A|=0 $，称 $ A $ 是奇异矩阵，不可逆。</p>
</li>
</ol>
<h3>矩阵可逆的判定及逆矩阵的求法</h3>
<p>定理1：方阵 $ A $ 可逆的充要条件是 $ A $ 为非奇异矩阵，即 $ |A|≠0 $，并且当 $ A $ 可逆时，$ A^{-1}=\frac{1}{|A|}A^* $。</p>
<p>定理2（对角形矩阵）：$ \text{diag}A=\text{diag}(a_1,a_2,\dots,a_n) $ 可逆的充要条件是 $ a_1,a_2,\dots,a_n $ 都不等于0，且 $ A^{-1}=\text{diag}(\frac{1}{a_1},\frac{1}{a_2},\dots,\frac{1}{a_n}) $。</p>
<p>定理3：设 $ A $ 是 $ n $ 阶方阵，若存在 $ n $ 阶方阵 $ B $，使 $ AB=E $ 或 $ BA=E $，则 $ A $ 可逆，且 $ A^{-1}=B $。</p>
<h3>可逆矩阵的性质</h3>
<ol>
<li>若 $ A $ 可逆，则 $ A^{-1} $ 也可逆，且 $ (A^{-1})^{-1}=A $。</li>
</ol>
<hr />
<ol>
<li>若 $ A $ 可逆，则 $ A^T $ 也可逆，且 $ (A^T)^{-1}=(A^{-1})^T $（证明：$ A^T(A^{-1})^T=(A^{-1}A)^T=E^T=E $）。</li>
</ol>
<hr />
<ol>
<li>若 $ A $ 可逆，$ k $ 为非零常数，则 $ kA $ 也可逆，且 $ (kA)^{-1}=\frac{1}{k}A^{-1} $。</li>
</ol>
<hr />
<ol>
<li>若 $ A $ 可逆，则 $ A^* $ 也可逆，且 $ (A^<em>)^{-1}=(A^{-1})^</em>=\frac{1}{|A|}A $。
证：$ AA^<em>=A^<em>A=|A|E $，则 $ (\frac{1}{|A|}A)A^</em>=A^</em>(\frac{1}{|A|}A)=E $，故 $ A^* $ 可逆。
附：$ |kA|=k^n|A| $，$ (kA)^<em>=k^{n-1}A^</em> $，$ (kA)^{-1}=\frac{1}{k}A^{-1} $。
运算顺序可交换：$ (A^T)^{-1}=(A^{-1})^T $，$ (A^<em>)^T=(A^T)^</em> $，$ (A^<em>)^{-1}=(A^{-1})^</em> $。</li>
</ol>
<hr />
<ol>
<li>若 $ A,B $ 同阶可逆，则 $ AB $ 也可逆，$ (AB)^{-1}=B^{-1}A^{-1} $。
证：$ |A|≠0,|B|≠0 $，则 $ |AB|=|A||B|≠0 $，$ AB $ 可逆；$ ABB^{-1}A^{-1}=AEA^{-1}=AA^{-1}=E $。</li>
</ol>
<hr />
<ol>
<li>若 $ A $ 可逆，$ m $ 为正整数，则 $ A^m $ 也可逆，且 $ (A^m)^{-1} = (A^{-1})^m $。
$ |A| \neq 0 $，$ |A^m| = |A|^m \neq 0 $，
$ A^m(A^{-1})^m = (A^{-1}A)^m = E^m = E $。</li>
</ol>
<hr />
<ol>
<li>若 $ A $ 可逆，则 $ |A^{-1}| = \frac{1}{|A|} $。</li>
</ol>
<hr />
<ol>
<li>若 $ A $ 可逆，则 $ A^{-1} = \frac{1}{|A|}A^* $。</li>
</ol>
<hr />
<h3>矩阵方程</h3>
<p>若 $ A,B $ 均为可逆矩阵，则矩阵方程：</p>
<ol>
<li>$ AX = C $，其解为 $ X = A^{-1}C $。
证：$ A^{-1}AX = A^{-1}C \Rightarrow X = A^{-1}C $</li>
</ol>
<hr />
<ol>
<li>$ XA = C $，其解为 $ X = CA^{-1} $。
证：$ XAA^{-1} = CA^{-1} \Rightarrow X = CA^{-1} $</li>
</ol>
<hr />
<ol>
<li>$ AXB = C $，其解为 $ X = A^{-1}CB^{-1} $。
证：$ A^{-1}AXB B^{-1} = A^{-1}CB^{-1} \Rightarrow X = A^{-1}CB^{-1} $</li>
</ol>
<hr />
<h2>2.9 矩阵的初等变换</h2>
<h3>定义</h3>
<p>矩阵初等变换分为：行变换和列变换。</p>
<p><strong>初等行变换</strong>：</p>
<ol>
<li>交换矩阵的两行；</li>
<li>用数 $ k \neq 0 $ 乘矩阵某一行的所有元素；</li>
<li>把矩阵某一行所有元素的 $ l $ 倍加到另一行对应的元素上去。</li>
</ol>
<p><strong>标准形矩阵</strong>：元素只有 $ 1 $ 和 $ 0 $ 组成，且矩阵左上角是单位矩阵，其余元素全为 $ 0 $。
例：
$$
\begin{pmatrix}1&amp;0&amp;0&amp;0\0&amp;1&amp;0&amp;0\0&amp;0&amp;0&amp;0\end{pmatrix}, \quad
\begin{pmatrix}1&amp;0&amp;0\0&amp;1&amp;0\0&amp;0&amp;0\end{pmatrix}, \quad
\begin{pmatrix}1&amp;0\0&amp;1\end{pmatrix}
$$</p>
<p><strong>定理</strong>：任何矩阵都能通过初等变换化为标准型矩阵，此矩阵称为矩阵的标准形。</p>
<h3>行阶梯形矩阵</h3>
<ol>
<li><strong>定义</strong>：若矩阵满足以下条件，则称为<strong>行阶梯形矩阵</strong>：
<ul>
<li>若矩阵存在零行，则零行都在非零行的下方；</li>
<li>任一非零行从左到右第一个非零元素（非零首元）所在的列中，该元素左下方的元素全为零。</li>
</ul>
</li>
</ol>
<hr />
<ol>
<li><strong>行简化阶梯形矩阵</strong>：行阶梯形矩阵的非零首元均为 $1$，且非零首元所在列的其他元素全为零。</li>
</ol>
<hr />
<ol>
<li><strong>例子</strong>：
$$
\begin{pmatrix}
-1 &amp; 0 &amp; 3 \
0 &amp; 2 &amp; 0 \
0 &amp; 0 &amp; 5
\end{pmatrix}, \quad
\begin{pmatrix}
1 &amp; 2 &amp; 0 &amp; 0 \
0 &amp; 0 &amp; 1 &amp; 0 \
0 &amp; 0 &amp; 0 &amp; 1
\end{pmatrix}
$$</li>
</ol>
<hr />
<ol>
<li><strong>定理</strong>：任何矩阵都可通过初等行变换化为唯一的行阶梯形矩阵；再通过初等行变换化为唯一的行简化阶梯形矩阵；最终可通过初等列变换化为唯一的标准形矩阵，其中“$1$”的个数等于矩阵的秩 $r(A)$。</li>
</ol>
<hr />
<h2>2.10 初等矩阵</h2>
<h3>定义</h3>
<p>由单位矩阵 $E$ 经过一次初等行（列）变换得到的矩阵，称为<strong>初等矩阵</strong>，分为三类：</p>
<ul>
<li>交换 $E$ 的第 $i,j$ 两行（列），得到 $E(i,j)$；</li>
<li>用非零数 $k$ 乘 $E$ 的第 $i$ 行（列），得到 $E(i(k))$；</li>
<li>把 $E$ 的第 $j$ 行的 $l$ 倍加到第 $i$ 行，得到 $E(i,j(l))$。</li>
</ul>
<h3>性质</h3>
<ul>
<li><strong>行列式非零</strong>：$|E(i,j)| = -1$，$|E(i(k))| = k$，$|E(i,j(l))| = 1$</li>
<li><strong>转置仍为同类型初等矩阵</strong>：$E(i,j)^T = E(i,j)$，$E(i(k))^T = E(i(k))$，$E(i,j(l))^T = E(j,i(l))$</li>
<li><strong>可逆，且逆矩阵仍为同类型初等矩阵</strong>：$E(i,j)^{-1} = E(i,j)$，$E(i(k))^{-1} = E(i(1/k))$，$E(i,j(l))^{-1} = E(i,j(-l))$</li>
</ul>
<h3>初等矩阵与矩阵初等变换的关系</h3>
<p>设 $ A $ 为 $ m×n $ 矩阵，则：</p>
<ol>
<li>对 $ A $ 进行一次初等<strong>行变换</strong>得到的矩阵，等于用同类型的 $ m $ 阶初等矩阵<strong>左乘</strong> $ A $；</li>
<li>对 $ A $ 进行一次初等<strong>列变换</strong>得到的矩阵，等于用同类型的 $ n $ 阶初等矩阵<strong>右乘</strong> $ A $。（左乘对应行变换，右乘对应列变换）</li>
</ol>
<h2>2.11 矩阵的等价</h2>
<h3>定义</h3>
<p>若矩阵 $A$ 可经过有限次初等变换化为矩阵 $B$，则称 $A$ 与 $B$ 等价，记作 $A \cong B$。
$A \cong B \iff A \to B$。</p>
<h3>矩阵等价的性质</h3>
<p>矩阵的等价具有下列性质：</p>
<ol>
<li>反身性：对任何矩阵 $A$，都有 $A \cong A$；</li>
<li>对等称性：若矩阵 $A \cong B$，则 $B \cong A$；</li>
<li>传递性：若矩阵 $A \cong B$，$B \cong C$，则 $A \cong C$。</li>
</ol>
<h3>矩阵等价的有关结论</h3>
<ol>
<li>任意一个矩阵 $A_{m×n}$ 都和其标准形矩阵
$$
D=\begin{pmatrix}
1 &amp; &amp; &amp; &amp; \
&amp; \ddots &amp; &amp; &amp; \
&amp; &amp; 1 &amp; &amp; \
&amp; &amp; &amp; 0 &amp; \
&amp; &amp; &amp; &amp; \ddots \
&amp; &amp; &amp; &amp; &amp; 0
\end{pmatrix}_{m×n}
$$
等价。</li>
</ol>
<hr />
<ol>
<li>矩阵 $A \cong B$ 的充要条件是存在一系列初等矩阵 $P_1, P_2, \dots, P_s, Q_1, Q_2, \dots, Q_t$，使
$$
P_s \cdots P_2 P_1 A Q_1 Q_2 \cdots Q_t = B
$$</li>
</ol>
<hr />
<ol>
<li>矩阵 $A \cong B$ 的充要条件是存在可逆矩阵 $P,Q$，使 $PAQ=B$。</li>
</ol>
<hr />
<ol>
<li>若矩阵 $A \cong B$，则 $A$ 与 $B$ 的标准形相同。</li>
</ol>
<hr />
<ol>
<li>若矩阵 $A \cong B$，则 $A$ 与 $B$ 的秩相等，即 $r(A)=r(B)$。</li>
</ol>
<hr />
<ol>
<li>若 $A,B$ 为同型矩阵，则 $A \cong B$ 的充要条件是 $r(A)=r(B)$。</li>
</ol>
<hr />
<ol>
<li>若 $A, B$ 为同阶方阵，且 $A \cong B$，则 $|A| = k|B|$（$k \neq 0$。</li>
</ol>
<hr />
<ol>
<li>若 $A, B$ 为同阶方阵，且 $A \cong B$，则 $A, B$ 同时可逆，或同时不可逆。</li>
</ol>
<hr />
<ol>
<li>设 $A$ 为 $n$ 阶方阵，则 $A$ 可逆的充要条件为 $A \cong E$。</li>
</ol>
<hr />
<ol>
<li>设 $A$ 为方阵，则 $A$ 可逆的充要条件是 $A$ 可以表示为有限个初等矩阵的乘积。</li>
</ol>
<hr />
<h2>2.12 初等变换法求逆矩阵</h2>
<h3>具体步骤</h3>
<ol>
<li>第一步：利用矩阵 $A$ 和同阶单位矩阵 $E$，作 $n \times 2n$ 的矩阵 $(A \vdots E)$；</li>
</ol>
<hr />
<ol>
<li>第二步：对 $(A \vdots E)$ 进行初等行变换，将其化为行简化阶梯形矩阵：
$$
(A \vdots E) \xrightarrow{\text{行}} (B \vdots D)
$$
（注：只能进行行变换）
其中，$(B \vdots D)$ 为行简化阶梯形矩阵，此时相应的有 $A \xrightarrow{\text{行}} B$，$E \xrightarrow{\text{行}} D$；</li>
</ol>
<hr />
<ol>
<li>第三步：判定得结论：
$$
\begin{cases}
\text{若 } B=E, \text{ 则 } A \text{ 可逆，且 } A^{-1}=D; \
\text{若 } B \neq E, \text{ 则 } A \text{ 不可逆.}
\end{cases}
$$</li>
</ol>
<h3>示例</h3>
<p>求矩阵 $ A = \begin{pmatrix} 1 &amp; 2 &amp; 3 \ 2 &amp; 2 &amp; 1 \ 3 &amp; 4 &amp; 3 \end{pmatrix} $ 的逆矩阵。</p>
<p>构造增广矩阵：
$$
(A \mid E) = \begin{pmatrix} 1 &amp; 2 &amp; 3 &amp; 1 &amp; 0 &amp; 0 \ 2 &amp; 2 &amp; 1 &amp; 0 &amp; 1 &amp; 0 \ 3 &amp; 4 &amp; 3 &amp; 0 &amp; 0 &amp; 1 \end{pmatrix}
$$</p>
<p>进行初等行变换：</p>
<ul>
<li>
<p>$ r_2 - 2r_1 $，$ r_3 - 3r_1 $：
$$
\begin{pmatrix} 1 &amp; 2 &amp; 3 &amp; 1 &amp; 0 &amp; 0 \ 0 &amp; -2 &amp; -5 &amp; -2 &amp; 1 &amp; 0 \ 0 &amp; -2 &amp; -6 &amp; -3 &amp; 0 &amp; 1 \end{pmatrix}
$$</p>
</li>
<li>
<p>$ r_3 - r_2 $：
$$
\begin{pmatrix} 1 &amp; 2 &amp; 3 &amp; 1 &amp; 0 &amp; 0 \ 0 &amp; -2 &amp; -5 &amp; -2 &amp; 1 &amp; 0 \ 0 &amp; 0 &amp; -1 &amp; -1 &amp; -1 &amp; 1 \end{pmatrix}
$$</p>
</li>
<li>
<p>$ r_3 \times (-1) $：
$$
\begin{pmatrix} 1 &amp; 2 &amp; 3 &amp; 1 &amp; 0 &amp; 0 \ 0 &amp; -2 &amp; -5 &amp; -2 &amp; 1 &amp; 0 \ 0 &amp; 0 &amp; 1 &amp; 1 &amp; 1 &amp; -1 \end{pmatrix}
$$</p>
</li>
<li>
<p>$ r_2 + 5r_3 $，$ r_1 - 3r_3 $：
$$
\begin{pmatrix} 1 &amp; 2 &amp; 0 &amp; -2 &amp; -3 &amp; 3 \ 0 &amp; -2 &amp; 0 &amp; 3 &amp; 6 &amp; -5 \ 0 &amp; 0 &amp; 1 &amp; 1 &amp; 1 &amp; -1 \end{pmatrix}
$$</p>
</li>
<li>
<p>$ r_2 \times (-\frac{1}{2}) $：
$$
\begin{pmatrix} 1 &amp; 2 &amp; 0 &amp; -2 &amp; -3 &amp; 3 \ 0 &amp; 1 &amp; 0 &amp; -\frac{3}{2} &amp; -3 &amp; \frac{5}{2} \ 0 &amp; 0 &amp; 1 &amp; 1 &amp; 1 &amp; -1 \end{pmatrix}
$$</p>
</li>
<li>
<p>$ r_1 - 2r_2 $：
$$
\begin{pmatrix} 1 &amp; 0 &amp; 0 &amp; 1 &amp; 3 &amp; -2 \ 0 &amp; 1 &amp; 0 &amp; -\frac{3}{2} &amp; -3 &amp; \frac{5}{2} \ 0 &amp; 0 &amp; 1 &amp; 1 &amp; 1 &amp; -1 \end{pmatrix}
$$</p>
</li>
</ul>
<p>因此，$ A^{-1} = \begin{pmatrix} 1 &amp; 3 &amp; -2 \ -\frac{3}{2} &amp; -3 &amp; \frac{5}{2} \ 1 &amp; 1 &amp; -1 \end{pmatrix} $。</p>
<h3>初等变换法解矩阵方程</h3>
<p>设 $A$ 为 $n$ 阶可逆矩阵，求解矩阵方程 $AX=B$，有下列两种方法：</p>
<ol>
<li><strong>方法一</strong>：先求出 $A^{-1}$，然后求 $X=A^{-1}B$ 即可。</li>
</ol>
<hr />
<ol>
<li><strong>方法二（初等变换法）</strong>：只适用于方程 $AX=B$。
对矩阵 $(A \vdots B)$ 进行初等行变换，将其化为如下形式：
$$
(A \vdots B) \xrightarrow{\text{行}} (E \vdots C)
$$
（注：只能进行行变换）
则 $X=C$。
即将 $A$ 对应的左边子块化为单位矩阵 $E$ 时，$B$ 对应的右边子块化出的矩阵 $C$ 即为所求的矩阵 $X$。</li>
</ol>
<hr />
<ol>
<li>对于 $AX=B$，需要构建2n * n的矩阵,把 $A$ 和 $B$ 放在矩阵的上边和下边,然后对矩阵进行初等列变换,直到 $A$ 变化为单位矩阵 $E$。</li>
</ol>
<h2>2.13 分块矩阵</h2>
<ol>
<li>将 $A$ 进行如下分块：
$$
A=
\begin{pmatrix}
1 &amp; 2 &amp; 0 &amp; \bigm| &amp; -1 &amp; 3 \
0 &amp; -1 &amp; 1 &amp; \bigm| &amp; 2 &amp; 1 \
0 &amp; 1 &amp; 5 &amp; \bigm| &amp; -2 &amp; 2 \
\hline
1 &amp; 3 &amp; 4 &amp; \bigm| &amp; 0 &amp; 6
\end{pmatrix}
$$</li>
</ol>
<p>于是 $A$ 成为分块矩阵：
$$
A=
\begin{pmatrix}
A_{11} &amp; A_{12} \
A_{21} &amp; A_{22} \
A_{31} &amp; A_{32}
\end{pmatrix}
$$</p>
<hr />
<ol>
<li>将 $A$ 按行分块，可得
$$
A=
\begin{pmatrix}
1 &amp; 2 &amp; 0 &amp; -1 &amp; 3 \
0 &amp; -1 &amp; 1 &amp; 2 &amp; 1 \
0 &amp; 1 &amp; 5 &amp; -2 &amp; 2 \
1 &amp; 3 &amp; 4 &amp; 0 &amp; 6
\end{pmatrix}
=
\begin{pmatrix}
\alpha_1 \
\alpha_2 \
\alpha_3 \
\alpha_4
\end{pmatrix}
$$
其中
$$
\alpha_1=(1\ 2\ 0\ -1\ 3),\quad
\alpha_2=(0\ -1\ 1\ 2\ 1),
$$
$$
\alpha_3=(0\ 1\ 5\ -2\ 2),\quad
\alpha_4=(1\ 3\ 4\ 0\ 6).
$$
$\alpha_1,\alpha_2,\alpha_3,\alpha_4$ 也叫 $A$ 的行向量组。</li>
</ol>
<hr />
<ol>
<li>将 $A$ 按列分块，可得：
$$
A=
\begin{pmatrix}
1 &amp; 2 &amp; 0 &amp; -1 &amp; 3 \
0 &amp; -1 &amp; 1 &amp; 2 &amp; 1 \
0 &amp; 1 &amp; 5 &amp; -2 &amp; 2 \
1 &amp; 3 &amp; 4 &amp; 0 &amp; 6
\end{pmatrix}
=
(\beta_1\ \beta_2\ \beta_3\ \beta_4\ \beta_5)
$$
其中
$$
\beta_1=
\begin{pmatrix}
1 \
0 \
0 \
1
\end{pmatrix},\
\beta_2=
\begin{pmatrix}
2 \
-1 \
1 \
3
\end{pmatrix},\
\beta_3=
\begin{pmatrix}
0 \
1 \
5 \
4
\end{pmatrix},\
\beta_4=
\begin{pmatrix}
-1 \
2 \
-2 \
0
\end{pmatrix},\
\beta_5=
\begin{pmatrix}
3 \
1 \
2 \
6
\end{pmatrix}
$$
$\beta_1,\beta_2,\beta_3,\beta_4,\beta_5$ 也叫 $A$ 的列向量组。</li>
</ol>
<p>将矩阵分块是为了简化计算，具体如何分块，要根据具体情况而定。</p>
<h3>1.特殊的分块矩阵</h3>
<ol>
<li><strong>上三角形分块矩阵</strong>：形如
$$
\begin{pmatrix}
A_1 &amp; * &amp; \dots &amp; * \
&amp; A_2 &amp; \dots &amp; * \
&amp; &amp; \ddots &amp; \vdots \
&amp; &amp; &amp; A_k
\end{pmatrix}
$$
的分块方阵，其中 $A_1, A_2, \dots, A_k$ 为方阵。</li>
</ol>
<hr />
<ol>
<li>
<p><strong>下三角形分块矩阵</strong>：形如</p>
<p>$$
\begin{pmatrix}
A_1 &amp; &amp; &amp; \</p>
<ul>
<li>&amp; A_2 &amp; &amp; \
\vdots &amp; \vdots &amp; \ddots &amp; \</li>
<li>&amp; * &amp; \dots &amp; A_k
\end{pmatrix}
$$
的分块方阵，其中 $A_1, A_2, \dots, A_k$ 均为方阵。</li>
</ul>
</li>
</ol>
<hr />
<ol>
<li><strong>对角形分块矩阵</strong>：形如
$$
\begin{pmatrix}
A_1 &amp; &amp; &amp; \
&amp; A_2 &amp; &amp; \
&amp; &amp; \ddots &amp; \
&amp; &amp; &amp; A_k
\end{pmatrix}
$$
的分块方阵，其中 $A_1, A_2, \dots, A_k$ 均为方阵。</li>
</ol>
<hr />
<h3>2.分块矩阵的运算</h3>
<ol>
<li>分块矩阵的加法与数乘
设矩阵 $A_{m\times n}$、$B_{m\times n}$ 按同一方式分块：
$$
A =
\begin{pmatrix}
A_{11} &amp; A_{12} &amp; \dots &amp; A_{1t} \
A_{21} &amp; A_{22} &amp; \dots &amp; A_{2t} \
\vdots &amp; \vdots &amp; &amp; \vdots \
A_{s1} &amp; A_{s2} &amp; \dots &amp; A_{st}
\end{pmatrix},\quad
B =
\begin{pmatrix}
B_{11} &amp; B_{12} &amp; \dots &amp; B_{1t} \
B_{21} &amp; B_{22} &amp; \dots &amp; B_{2t} \
\vdots &amp; \vdots &amp; &amp; \vdots \
B_{s1} &amp; B_{s2} &amp; \dots &amp; B_{st}
\end{pmatrix}
$$
则
$$
A+B=
\begin{pmatrix}
A_{11}+B_{11} &amp; A_{12}+B_{12} &amp; \dots &amp; A_{1t}+B_{1t} \
A_{21}+B_{21} &amp; A_{22}+B_{22} &amp; \dots &amp; A_{2t}+B_{2t} \
\cdots &amp; \cdots &amp; \cdots &amp; \cdots \
A_{s1}+B_{s1} &amp; A_{s2}+B_{s2} &amp; \dots &amp; A_{st}+B_{st}
\end{pmatrix}
$$</li>
</ol>
<p>设矩阵 $A$ 按分块形式表示为
$$
A=
\begin{pmatrix}
A_{11} &amp; A_{12} &amp; \dots &amp; A_{1t} \
A_{21} &amp; A_{22} &amp; \dots &amp; A_{2t} \
\vdots &amp; \vdots &amp; &amp; \vdots \
A_{s1} &amp; A_{s2} &amp; \dots &amp; A_{st}
\end{pmatrix}
$$
则 $kA$ 的分块形式为
$$
kA=
\begin{pmatrix}
kA_{11} &amp; kA_{12} &amp; \dots &amp; kA_{1t} \
kA_{21} &amp; kA_{22} &amp; \dots &amp; kA_{2t} \
\vdots &amp; \vdots &amp; &amp; \vdots \
kA_{s1} &amp; kA_{s2} &amp; \dots &amp; kA_{st}
\end{pmatrix}
$$</p>
<hr />
<ol>
<li>分块矩阵的乘法
设矩阵 $A_{m\times l}$、$B_{l\times n}$，把 $A$、$B$ 分块，使 $A$ 的列分块和 $B$ 的行分块相同：
$$
A =
\begin{pmatrix}
A_{11} &amp; A_{12} &amp; \dots &amp; A_{1t} \
A_{21} &amp; A_{22} &amp; \dots &amp; A_{2t} \
\vdots &amp; \vdots &amp; &amp; \vdots \
A_{s1} &amp; A_{s2} &amp; \dots &amp; A_{st}
\end{pmatrix},
\quad
B =
\begin{pmatrix}
B_{11} &amp; B_{12} &amp; \dots &amp; B_{1r} \
B_{21} &amp; B_{22} &amp; \dots &amp; B_{2r} \
\vdots &amp; \vdots &amp; &amp; \vdots \
B_{t1} &amp; B_{t2} &amp; \dots &amp; B_{tr}
\end{pmatrix}
$$</li>
</ol>
<hr />
<p>设分块矩阵：
$$
A=
\begin{pmatrix}
A_1 &amp; A_2 &amp; A_3 \
A_4 &amp; A_5 &amp; A_6
\end{pmatrix},\quad
B=
\begin{pmatrix}
B_1 &amp; B_2 \
B_3 &amp; B_4 \
B_5 &amp; B_6
\end{pmatrix}
$$
则分块乘法结果为：
$$
AB=
\begin{pmatrix}
A_1B_1+A_2B_3+A_3B_5 &amp; A_1B_2+A_2B_4+A_3B_6 \
A_4B_1+A_5B_3+A_6B_5 &amp; A_4B_2+A_5B_4+A_6B_6
\end{pmatrix}
$$</p>
<hr />
<p><strong>分块矩阵乘法的前提条件：</strong>
矩阵 $A$ 的列分法与矩阵 $B$ 的行分法必须完全相同，即 $A$ 的列数与 $B$ 的行数相同，且分块的方式一致，以保证子块之间可以进行矩阵乘法运算。</p>
<hr />
<p>特别地，若 $A,B$ 是同阶对角形分块矩阵，且分块方式相同：
$$
A =
\begin{pmatrix}
A_1 &amp; &amp; &amp; \
&amp; A_2 &amp; &amp; \
&amp; &amp; \ddots &amp; \
&amp; &amp; &amp; A_k
\end{pmatrix},\quad
B =
\begin{pmatrix}
B_1 &amp; &amp; &amp; \
&amp; B_2 &amp; &amp; \
&amp; &amp; \ddots &amp; \
&amp; &amp; &amp; B_k
\end{pmatrix}
$$
则
$$
AB =
\begin{pmatrix}
A_1B_1 &amp; &amp; &amp; \
&amp; A_2B_2 &amp; &amp; \
&amp; &amp; \ddots &amp; \
&amp; &amp; &amp; A_kB_k
\end{pmatrix},\quad
A^m =
\begin{pmatrix}
A_1^m &amp; &amp; &amp; \
&amp; A_2^m &amp; &amp; \
&amp; &amp; \ddots &amp; \
&amp; &amp; &amp; A_k^m
\end{pmatrix}
$$</p>
<h3>3.分块矩阵的转置</h3>
<p>设分块矩阵
$$
A=
\begin{pmatrix}
A_1 &amp; A_2 \
A_3 &amp; A_4
\end{pmatrix}
$$
则其转置为
$$
A^T=
\begin{pmatrix}
A_1^T &amp; A_3^T \
A_2^T &amp; A_4^T
\end{pmatrix}
$$</p>
<hr />
<p><strong>分块矩阵转置的两步法：</strong></p>
<ol>
<li><strong>整体转置</strong>：将子块视为普通元素，对分块矩阵整体进行转置（交换行和列）。</li>
<li><strong>子块转置</strong>：对每个子块分别求其转置。</li>
</ol>
<hr />
<h3>4.分块矩阵的逆矩阵</h3>
<ol>
<li>设 $A, B$ 为方阵，则上三角分块矩阵 $ \begin{pmatrix} A &amp; C \ O &amp; B \end{pmatrix} $ 可逆的充要条件是 $A, B$ 都可逆，且其逆矩阵为：
$$
\begin{pmatrix} A &amp; C \ O &amp; B \end{pmatrix}^{-1} = \begin{pmatrix} A^{-1} &amp; -A^{-1}CB^{-1} \ O &amp; B^{-1} \end{pmatrix}
$$</li>
</ol>
<hr />
<ol>
<li>设 $A, B$ 为方阵，则下三角分块矩阵 $ \begin{pmatrix} A &amp; O \ C &amp; B \end{pmatrix} $ 可逆的充要条件是 $A, B$ 都可逆，且其逆矩阵为：
$$
\begin{pmatrix} A &amp; O \ C &amp; B \end{pmatrix}^{-1} = \begin{pmatrix} A^{-1} &amp; O \ -B^{-1}CA^{-1} &amp; B^{-1} \end{pmatrix}
$$</li>
</ol>
<hr />
<ol>
<li>设 $A_1, A_2, \dots, A_k$ 为方阵，则对角分块矩阵
$$
\begin{pmatrix}
A_1 &amp; &amp; &amp; \
&amp; A_2 &amp; &amp; \
&amp; &amp; \ddots &amp; \
&amp; &amp; &amp; A_k
\end{pmatrix}
$$
可逆的充要条件是 $A_1, A_2, \dots, A_k$ 都可逆，且其逆矩阵为：</li>
</ol>
<h1>$$
\begin{pmatrix}
A_1 &amp; &amp; &amp; \
&amp; A_2 &amp; &amp; \
&amp; &amp; \ddots &amp; \
&amp; &amp; &amp; A_k
\end{pmatrix}^{-1}</h1>
<p>\begin{pmatrix}
A_1^{-1} &amp; &amp; &amp; \
&amp; A_2^{-1} &amp; &amp; \
&amp; &amp; \ddots &amp; \
&amp; &amp; &amp; A_k^{-1}
\end{pmatrix}
$$</p>
<hr />
<ol>
<li>设 $A = \begin{pmatrix} &amp; B \ C &amp; \end{pmatrix}$，其中 $B, C$ 为方阵且均可逆，则其逆矩阵为：
$$
A^{-1} = \begin{pmatrix} &amp; C^{-1} \ B^{-1} &amp; \end{pmatrix}
$$</li>
</ol>
<hr />
<ol>
<li>设 $A,B,C$ 均为可逆方阵，则分块对角矩阵的逆矩阵满足：</li>
</ol>
<h1>$$
\begin{pmatrix}
A &amp; &amp; \
&amp; B &amp; \
&amp; &amp; C
\end{pmatrix}^{-1}</h1>
<p>\begin{pmatrix}
A^{-1} &amp; &amp; \
&amp; B^{-1} &amp; \
&amp; &amp; C^{-1}
\end{pmatrix}
$$</p>
<hr />
<ol>
<li>设 $A,B,C$ 均为可逆方阵，则反对角分块矩阵的逆矩阵为：</li>
</ol>
<h1>$$
\begin{pmatrix} &amp; &amp; A \ &amp; B &amp; \ C &amp; &amp; \end{pmatrix}^{-1}</h1>
<p>\begin{pmatrix} &amp; &amp; C^{-1} \ &amp; B^{-1} &amp; \ A^{-1} &amp; &amp; \end{pmatrix}
$$
该结论是反对角分块矩阵求逆的通用形式，核心规律是：</p>
<ol>
<li>每个子块取自身的逆；</li>
<li>逆矩阵中的子块位置与原矩阵呈中心对称。</li>
</ol>
<hr />
<h3>5.分块矩阵的行列式</h3>
<h3>分块三角矩阵的行列式性质</h3>
<p>一.若 $A_1, A_2, \dots, A_k$ 均为方阵，则：</p>
<ol>
<li>
<p><strong>上三角分块矩阵</strong>：</p>
<p>$$
\begin{vmatrix}
A_1 &amp; * &amp; \dots &amp; * \
&amp; A_2 &amp; \dots &amp; * \
&amp; &amp; \ddots &amp; \vdots \
&amp; &amp; &amp; A_k
\end{vmatrix}
= |A_1| \cdot |A_2| \cdot \dots \cdot |A_k|
$$</p>
</li>
<li>
<p><strong>下三角分块矩阵</strong>：</p>
<p>$$
\begin{vmatrix}
A_1 &amp; &amp; &amp; \</p>
<ul>
<li>&amp; A_2 &amp; &amp; \
\vdots &amp; \vdots &amp; \ddots &amp; \</li>
<li>&amp; * &amp; \dots &amp; A_k
\end{vmatrix}
= |A_1| \cdot |A_2| \cdot \dots \cdot |A_k|
$$</li>
</ul>
</li>
<li>
<p>对角分块矩阵的行列式性质：</p>
</li>
</ol>
<p>若 $A_1, A_2, \dots, A_k$ 均为方阵，则：
$$
\begin{vmatrix}
A_1 &amp; &amp; &amp; \
&amp; A_2 &amp; &amp; \
&amp; &amp; \ddots &amp; \
&amp; &amp; &amp; A_k
\end{vmatrix}
= |A_1| \cdot |A_2| \cdot \dots \cdot |A_k|
$$</p>
<hr />
<p>二.若 $A$ 为 $m$ 阶方阵，$B$ 为 $n$ 阶方阵，则：</p>
<p>$$
\left|\begin{pmatrix}
O &amp; A \
B &amp; C
\end{pmatrix}\right| = (-1)^{mn} |A| \cdot |B|
$$
$$
\left|\begin{pmatrix}
C &amp; A \
B &amp; O
\end{pmatrix}\right| = (-1)^{mn} |A| \cdot |B|
$$
$$
\left|\begin{pmatrix}
O &amp; A \
B &amp; O
\end{pmatrix}\right| = (-1)^{mn} |A| \cdot |B|
$$</p>
<hr />
<h2>2.14 矩阵的秩</h2>
<ol>
<li>
<p>矩阵的子式：
设矩阵 $A=(a_{ij})_{m\times n}$，在 $A$ 中任取 $k$ 行 $k$ 列（$k\le\min(m,n)$），位于这些行列交叉点处的 $k^2$ 个元素，按原位置不变得到的 $k$ 阶行列式，称为矩阵 $A$ 的一个 $k$ 阶子式。</p>
</li>
<li>
<p>矩阵的秩：矩阵 $A$ 中非零子式的最高阶数，称为矩阵 $A$ 的秩，记作 $r(A)$。</p>
</li>
</ol>
<p>例如：
$$
A = \begin{pmatrix}
0 &amp;  1 &amp; 2 &amp; 0 \
0 &amp; 0 &amp; 0 &amp; 0 \
1 &amp; 0 &amp; 5 &amp; -3
\end{pmatrix}
$$</p>
<h3>子式分析</h3>
<ol>
<li><strong>1阶子式</strong>：存在非零值，如 $|1|=1$、$|2|$、$|0|$ 等，说明矩阵至少存在1阶非零子式。</li>
<li><strong>2阶子式</strong>：存在非零值，如 $\begin{vmatrix} 0 &amp; 1 \ 1 &amp; 0 \end{vmatrix}=-1$，说明矩阵至少存在2阶非零子式。</li>
<li><strong>3阶子式</strong>：所有3阶子式的值均为0，如 $\begin{vmatrix} 0 &amp; 1 &amp; 2 \ 0 &amp; 0 &amp; 0 \ 1 &amp; 0 &amp; 5 \end{vmatrix}=0$，说明矩阵不存在3阶非零子式。</li>
</ol>
<p>根据矩阵秩的定义，该矩阵的秩为<strong>2</strong>。</p>
<hr />
<p>注:</p>
<ol>
<li>零矩阵没有非零子式，规定零矩阵的秩为零。</li>
<li>若 $A \neq O$，则 $r(A) \ge 1$。</li>
<li>对任意矩阵 $A_{m \times n}$，有 $0 \le r(A) \le \min(m,n)$。</li>
</ol>
<hr />
<h3>满秩矩阵与降秩矩阵</h3>
<p>定义 2.15：
设 $A$ 为 $m \times n$ 矩阵，若 $r(A) = \min(m,n)$，称 $A$ 为<strong>满秩矩阵</strong>。</p>
<ul>
<li>若 $r(A) = m$，称 $A$ 为<strong>行满秩矩阵</strong>；</li>
<li>若 $r(A) = n$，称 $A$ 为<strong>列满秩矩阵</strong>。</li>
</ul>
<hr />
<p>若 $r(A) &lt; \min(m,n)$，称 $A$ 为<strong>降秩矩阵</strong>。</p>
<hr />
<p>特别地，若 $A$ 为 $n$ 阶方阵：</p>
<ul>
<li>若 $r(A) = n$，称 $A$ 为<strong>满秩矩阵</strong>；</li>
<li>若 $r(A) &lt; n$，称 $A$ 为<strong>降秩矩阵</strong>。</li>
</ul>
<hr />
<p>由定义可知：若 $A$ 为 $n$ 阶方阵，则</p>
<ul>
<li>$A$ 满秩 $\iff r(A)=n \iff |A| \neq 0 \iff A$ 可逆 $\iff A$ 非奇异；</li>
<li>$A$ 降秩 $\iff r(A)&lt;n \iff |A| = 0 \iff A$ 不可逆 $\iff A$ 奇异。</li>
</ul>
<hr />
<h3>矩阵秩的性质</h3>
<ol>
<li>$r(A)=r(r&gt;0) \iff$ 矩阵 $A$ 中至少有一个 $r$ 阶子式不等于零，而所有的 $r+1$ 阶子式全等于零（或根本没有 $r+1$ 阶子式）。</li>
</ol>
<hr />
<ol>
<li>$r(A) \ge r \iff$ 矩阵 $A$ 中至少有一个 $r$ 阶子式不等于零。</li>
</ol>
<hr />
<ol>
<li>$r(A) &lt; r \iff$ 矩阵 $A$ 中所有 $r$ 阶子式全等于零。</li>
</ol>
<hr />
<ol>
<li>$r(A)=r(A^T)$，$r(A)=r(-A)$，$r(kA)=r(A)\ (k\neq 0)$。</li>
</ol>
<hr />
<ol>
<li>$r(A)=0\iff A=O$，$r(A)\ge 1\iff A\neq O$。</li>
</ol>
<hr />
<ol>
<li>若 $A \neq O$，则 $A$ 的任意两行（列）元素对应成比例 $\iff r(A)=1$。</li>
</ol>
<hr />
<ol>
<li>若 $A$ 为行阶梯形矩阵，则 $r(A)$ 等于 $A$ 中非零行的行数。</li>
</ol>
<hr />
<ol>
<li>初等变换不改变矩阵的秩。即若 $A \cong B$，则 $r(A)=r(B)$。</li>
</ol>
<hr />
<ol>
<li>若 $A, B$ 为同型矩阵，则 $A \cong B \iff r(A)=r(B)$。</li>
</ol>
<hr />
<ol>
<li>若 $A$ 的标准形为 $D=\begin{pmatrix}1&amp;&amp;&amp;&amp;\&amp;\ddots&amp;&amp;&amp;\&amp;&amp;1&amp;&amp;\&amp;&amp;&amp;0&amp;\&amp;&amp;&amp;&amp;\ddots\&amp;&amp;&amp;&amp;&amp;0\end{pmatrix}$，则 $r(A)$ 等于 $D$ 中1的个数。</li>
</ol>
<hr />
<ol>
<li>若 $A,B$ 同为 $m \times n$ 矩阵，则 $r(A \pm B) \le r(A) + r(B)$。</li>
</ol>
<hr />
<ol>
<li>若 $A$ 为 $m\times n$ 矩阵，$B$ 为 $n\times s$ 矩阵，则
$$
r(A)+r(B)-n\le r(AB)\le \min(r(A), r(B)).
$$</li>
</ol>
<hr />
<ol>
<li>若 $A$ 为 $m\times n$ 矩阵，$B$ 为 $n\times s$ 矩阵，且 $AB=O$，则
$$
r(A)+r(B)\le n.
$$</li>
</ol>
<hr />
<ol>
<li></li>
</ol>
<p>$$
r(A^T A)=r(AA^T)=r(A)=r(A^T).
$$</p>
<hr />
<ol>
<li>若 $A$ 为可逆方阵，则
$$
r(AB)=r(B),\quad r(CA)=r(C).
$$</li>
</ol>
<hr />
<p>（16）若 $A$ 为 $n$ 阶方阵（$n\ge2$），$A^<em>$ 为 $A$ 的伴随矩阵，则：
$$
r(A^</em>)=
\begin{cases}
n, &amp; r(A)=n; \
1, &amp; r(A)=n-1; \
0, &amp; r(A)&lt;n-1.
\end{cases}
$$</p>
<hr />
<h3>矩阵秩的求法</h3>
<ol>
<li>定义法
若矩阵 $A$ 中有不等于零的 $r$ 阶子式，而所有的 $r+1$ 阶子式全都等于零（或根本没有 $r+1$ 阶子式），则 $r(A)=r$。</li>
</ol>
<hr />
<ol>
<li>初等变换法
利用矩阵的初等行变换，将矩阵化为<strong>行阶梯形矩阵</strong>，行阶梯形矩阵的<strong>非零行的行数</strong>即为矩阵的秩。</li>
</ol>
<hr />
<ol>
<li>行列式法
若 $A$ 为 $n$ 阶方阵，通过计算行列式 $|A|$ 来判断秩：</li>
</ol>
<ul>
<li>若 $|A| \neq 0$，则 $r(A)=n$（矩阵满秩）；</li>
<li>若 $|A| = 0$，则 $r(A)&lt;n$（矩阵降秩）。</li>
</ul>
<hr />
<h1>第三部分：向量</h1>
<hr />
<h2>3.1 向量的概念与线性运算</h2>
<h3>向量的概念</h3>
<ol>
<li>
<p>零向量：所有分量全为零的向量，记为 $\boldsymbol{0}$。</p>
<p>如：
$$\boldsymbol{0} = (0, 0, \dots, 0) \quad \text{或} \quad \boldsymbol{0} = \begin{pmatrix} 0 \ 0 \ \vdots \ 0 \end{pmatrix}$$</p>
</li>
</ol>
<hr />
<ol>
<li>
<p>负向量：将每个分量都取相反数得到的向量。</p>
<p>向量 $\boldsymbol{\alpha}=(a_1, a_2, \dots, a_n)$ 的负向量为 $-\boldsymbol{\alpha}=(-a_1, -a_2, \dots, -a_n)$。</p>
<p>向量 $\boldsymbol{\beta} = \begin{pmatrix} b_1 \ b_2 \ \vdots \ b_m \end{pmatrix}$ 的负向量为 $-\boldsymbol{\beta} = \begin{pmatrix} -b_1 \ -b_2 \ \vdots \ -b_m \end{pmatrix}$。</p>
</li>
</ol>
<hr />
<ol>
<li>向量的转置：按矩阵的转置来求。</li>
</ol>
<hr />
<ol>
<li>向量的相等：两个同种类型的向量相等 $\Leftrightarrow$ 相同位置的分量对应相等。</li>
</ol>
<p>如：设向量 $\boldsymbol{\alpha}=(a_1, a_2, \dots, a_n)$，$\boldsymbol{\beta}=(b_1, b_2, \dots, b_n)$，则
$$\boldsymbol{\alpha}=\boldsymbol{\beta} \Leftrightarrow a_1=b_1,\ a_2=b_2,\ \dots,\ a_n=b_n$$</p>
<hr />
<h3>向量的线性运算</h3>
<p><strong>1. 向量的加法与减法</strong></p>
<p>定义 3.2：设两个 $n$ 维向量 $\boldsymbol{\alpha}=(a_1, a_2, \dots, a_n)$，$\boldsymbol{\beta}=(b_1, b_2, \dots, b_n)$，</p>
<ul>
<li>
<p>向量加法定义为：
$$\boldsymbol{\alpha} + \boldsymbol{\beta} = (a_1+b_1,\ a_2+b_2,\ \dots,\ a_n+b_n)$$
（对应分量相加）</p>
</li>
<li>
<p>借助负向量和加法，定义向量的减法：
$$\boldsymbol{\alpha} - \boldsymbol{\beta} = \boldsymbol{\alpha} + (-\boldsymbol{\beta}) = (a_1-b_1,\ a_2-b_2,\ \dots,\ a_n-b_n)$$</p>
</li>
</ul>
<hr />
<p><strong>2. 向量的数乘（定义3.3）</strong></p>
<p>设向量 $\boldsymbol{\alpha}=(a_1, a_2, \dots, a_n)$，$k$ 是一个常数。数 $k$ 与向量 $\boldsymbol{\alpha}$ 的乘法（简称数乘）定义为：
$$k\boldsymbol{\alpha}=(ka_1,\ ka_2,\ \dots,\ ka_n)$$
（每个分量都乘 $k$）</p>
<p>示例：
$$5\begin{pmatrix} 1 \ 2 \ 3 \end{pmatrix} = \begin{pmatrix} 5 \ 10 \ 15 \end{pmatrix}$$
（该运算可用于提公因子）</p>
<hr />
<p><strong>3. 向量的运算律</strong></p>
<ol>
<li>交换律：$\boldsymbol{\alpha} + \boldsymbol{\beta} = \boldsymbol{\beta} + \boldsymbol{\alpha}$</li>
</ol>
<hr />
<ol>
<li>结合律：$(\boldsymbol{\alpha} + \boldsymbol{\beta}) + \boldsymbol{\gamma} = \boldsymbol{\alpha} + (\boldsymbol{\beta} + \boldsymbol{\gamma})$</li>
</ol>
<hr />
<ol>
<li>零向量性质：$\boldsymbol{\alpha} + \boldsymbol{0} = \boldsymbol{0} + \boldsymbol{\alpha} = \boldsymbol{\alpha}$（其中 $\boldsymbol{0}$ 为同型的 $n$ 维零向量）</li>
</ol>
<hr />
<ol>
<li>负向量性质：$\boldsymbol{\alpha} + (-\boldsymbol{\alpha}) = (-\boldsymbol{\alpha}) + \boldsymbol{\alpha} = \boldsymbol{0}$（其中 $\boldsymbol{0}$ 为同型的 $n$ 维零向量）</li>
</ol>
<hr />
<ol>
<li>$1 \cdot \boldsymbol{\alpha} = \boldsymbol{\alpha}$</li>
</ol>
<hr />
<ol>
<li>$(kl)\boldsymbol{\alpha} = k(l\boldsymbol{\alpha}) = l(k\boldsymbol{\alpha})$</li>
</ol>
<hr />
<ol>
<li>$k(\boldsymbol{\alpha} + \boldsymbol{\beta}) = k\boldsymbol{\alpha} + k\boldsymbol{\beta}$</li>
</ol>
<hr />
<ol>
<li>$(k+l)\boldsymbol{\alpha} = k\boldsymbol{\alpha} + l\boldsymbol{\alpha}$</li>
</ol>
<hr />
<p>并且还可以推出：</p>
<p>(i) $\boldsymbol{\alpha} + \boldsymbol{\beta} = \boldsymbol{\gamma} \iff \boldsymbol{\alpha} = \boldsymbol{\gamma} - \boldsymbol{\beta} \iff \boldsymbol{\beta} = \boldsymbol{\gamma} - \boldsymbol{\alpha}$</p>
<p>(ii) $k\boldsymbol{\alpha} = \boldsymbol{0} \iff k=0$ 或 $\boldsymbol{\alpha} = \boldsymbol{0}$（$k$ 为实数，$\boldsymbol{\alpha}$ 为向量）。</p>
<hr />
<h2>3.2 向量的线性组合与线性表示</h2>
<p><strong>定义 3.4</strong></p>
<p>设 $\boldsymbol{\alpha}_1, \boldsymbol{\alpha}_2, \dots, \boldsymbol{\alpha}_m$ 是一组 $n$ 维向量，$k_1, k_2, \dots, k_m$ 是一组常数，则称
$$k_1\boldsymbol{\alpha}_1 + k_2\boldsymbol{\alpha}_2 + \dots + k_m\boldsymbol{\alpha}_m$$
为向量组 $\boldsymbol{\alpha}_1, \boldsymbol{\alpha}_2, \dots, \boldsymbol{\alpha}_m$ 的一个<strong>线性组合</strong>，其中 $k_1, k_2, \dots, k_m$ 称为<strong>组合系数</strong>。</p>
<hr />
<p><strong>定义 3.5</strong></p>
<p>设 $\boldsymbol{\beta}, \boldsymbol{\alpha}_1, \boldsymbol{\alpha}_2, \dots, \boldsymbol{\alpha}_m$ 是 $n$ 维向量，若存在常数 $k_1, k_2, \dots, k_m$，使关系式
$$\boldsymbol{\beta} = k_1\boldsymbol{\alpha}_1 + k_2\boldsymbol{\alpha}_2 + \dots + k_m\boldsymbol{\alpha}_m$$
成立，则称 $\boldsymbol{\beta}$ 是向量组 $\boldsymbol{\alpha}_1, \boldsymbol{\alpha}_2, \dots, \boldsymbol{\alpha}_m$ 的线性组合，也称 $\boldsymbol{\beta}$ 可由向量组 $\boldsymbol{\alpha}_1, \boldsymbol{\alpha}_2, \dots, \boldsymbol{\alpha}_m$ 线性表示。称 $k_1, k_2, \dots, k_m$ 为组合系数。</p>
<hr />
<table>
<thead>
<tr>
<th>β 可由 α₁, …, αₛ 表示</th>
<th>Ax=β 方程组</th>
</tr>
</thead>
<tbody>
<tr>
<td>可以表示</td>
<td>有解</td>
</tr>
<tr>
<td>不可以表示</td>
<td>无解</td>
</tr>
<tr>
<td>可以唯一地表示</td>
<td>唯一解</td>
</tr>
<tr>
<td>可以表示，不唯一</td>
<td>无穷多解</td>
</tr>
</tbody>
</table>
<hr />
<p><strong>有关向量的线性组合与线性表示的结论</strong>：</p>
<ol>
<li>零向量是任意向量组的线性组合。</li>
<li>向量组 $\alpha_1, \alpha_2, \dots, \alpha_n$ 中任一向量 $\alpha_i$（$1 \le i \le n$）均可由本向量组线性表示。</li>
<li>任一$n$维向量$\alpha=(a_1,a_2,\dots,a_n)$都可由$n$维基本单位向量组$\varepsilon_1=(1,0,0,\dots,0)$，$\varepsilon_2=(0,1,0,\dots,0)$，…，$\varepsilon_n=(0,0,0,\dots,1)$线性表示，且表示法唯一。</li>
</ol>
<p>即有
$$\alpha = a_1\varepsilon_1 + a_2\varepsilon_2 + \dots + a_n\varepsilon_n$$</p>
<p>组合系数即为$\alpha$的分量。</p>
<h2>3.3 向量组的线性相关性</h2>
<p><strong>定义 3.7</strong></p>
<p>给定向量组 $\alpha_1,\alpha_2,\dots,\alpha_n$，如果存在不全为零的数 $k_1,k_2,\dots,k_n$，使
$$k_1\alpha_1 + k_2\alpha_2 + \dots + k_n\alpha_n = 0 \tag{*}$$
则称向量组 $\alpha_1,\alpha_2,\dots,\alpha_n$ <strong>线性相关</strong>，而称 $k_1,k_2,\dots,k_n$ 为一组相关系数。否则，称向量组 $\alpha_1,\alpha_2,\dots,\alpha_n$ <strong>线性无关</strong>。</p>
<p>线性无关的等价表述：</p>
<ol>
<li>不存在不全为零的 $k_1,\dots,k_n$，使 (*) 式成立；</li>
<li>若 (*) 式成立，则 $k_1,\dots,k_n$ 必全取 $0$；</li>
<li>对任意不全为 $0$ 的 $k_1,\dots,k_n$，都有 $k_1\alpha_1 + \dots + k_n\alpha_n \neq 0$。</li>
</ol>
<p>补充：$0\alpha_1 + 0\alpha_2 + \dots + 0\alpha_m = 0$</p>
<hr />
<h3>线性相关性性质的结论</h3>
<p><strong>（1）</strong>：一个向量$\alpha$ 线性相关 $\iff \alpha = 0$；<br />
一个向量$\alpha$ 线性无关 $\iff \alpha \neq 0$。
<strong>（2）</strong>：两个非零向量$\alpha$与$\beta$线性相关$\iff \alpha$与$\beta$的分量对应成比例。
<strong>（3）</strong>：如果向量组中有一部分向量（称为部分组）线性相关，则该向量组线性相关。
<strong>（4）</strong>：若向量组线性无关，则其任一部分组也线性无关。
<strong>（5）</strong>：含有零向量的向量组必线性相关。
<strong>（6）</strong>：$n$维单位坐标向量组必线性无关。
<strong>（7）</strong>：设$r$维向量$\alpha_1,\alpha_2,\dots,\alpha_m$线性无关，$\beta_1,\beta_2,\dots,\beta_m$为$\alpha_1,\alpha_2,\dots,\alpha_m$的$r+l$维接长向量，则$\beta_1,\beta_2,\dots,\beta_m$线性无关。
<strong>（8）</strong>：设$\alpha_1,\alpha_2,\dots,\alpha_m$是$r$维向量，$\beta_1,\beta_2,\dots,\beta_m$是其$r+l$维接长向量，若$\beta_1,\beta_2,\dots,\beta_m$线性相关，则$\alpha_1,\alpha_2,\dots,\alpha_m$也线性相关。
<strong>（9）</strong>：若向量组中有两个向量成比例，则向量组必线性相关。</p>
<hr />
<h2>3.4 极大线性无关组</h2>
<h3>定义</h3>
<p>设向量组 $ T $ 的一个部分组 $ \alpha_{i_1}, \alpha_{i_2}, \dots, \alpha_{i_r} $ 满足：</p>
<ol>
<li><strong>线性无关</strong>：$ \alpha_{i_1}, \alpha_{i_2}, \dots, \alpha_{i_r} $ 线性无关；</li>
<li><strong>极大性</strong>：向量组 $ T $ 中任意一个向量都可由 $ \alpha_{i_1}, \alpha_{i_2}, \dots, \alpha_{i_r} $ 线性表示。</li>
</ol>
<p>则称 $ \alpha_{i_1}, \alpha_{i_2}, \dots, \alpha_{i_r} $ 是向量组 $ T $ 的一个<strong>极大线性无关组</strong>。</p>
<h3>性质</h3>
<p><strong>（1）</strong>：向量组与其极大线性无关组等价。<br />
<strong>（2）</strong>：线性无关的向量组，其极大线性无关组为向量组本身。<br />
<strong>（3）</strong>：向量组的极大线性无关组可能不唯一。<br />
<strong>（4）</strong>：向量组和它的任一极大线性无关组等价。<br />
<strong>（5）</strong>：同一向量组的两个极大线性无关组等价，所含向量个数相等。<br />
<strong>（6）</strong>：若向量组（Ⅰ）可由向量组（Ⅱ）线性表示，则向量组（Ⅰ）的极大线性无关组可由向量组（Ⅱ）的极大线性无关组线性表示。<br />
<strong>（7）</strong>：等价的向量组，其极大线性无关组也等价。</p>
<hr />
<h3>极大线性无关组的求法-初等矩阵变换法</h3>
<h4>方法原理</h4>
<p>将向量组中的向量作为列向量构成矩阵 $ A $，对 $ A $ 进行初等行变换化为行阶梯形矩阵。行阶梯形矩阵中非零行的首元（主元）所在的列，对应的原矩阵中的列向量即为向量组的一个极大线性无关组。</p>
<p><strong>核心依据</strong>：初等行变换不改变矩阵列向量之间的线性相关性。</p>
<h4>具体步骤</h4>
<ol>
<li><strong>构造矩阵</strong>：设向量组为 $ \alpha_1, \alpha_2, \dots, \alpha_s $，构造矩阵 $ A = (\alpha_1, \alpha_2, \dots, \alpha_s) $；</li>
<li><strong>初等行变换</strong>：对 $ A $ 进行初等行变换，化为行阶梯形矩阵 $ B $；</li>
<li><strong>确定主元列</strong>：在 $ B $ 中找出非零行首元所在的列（主元列）；</li>
<li><strong>对应原向量</strong>：原矩阵 $ A $ 中与 $ B $ 主元列对应的列向量，构成极大线性无关组。</li>
</ol>
<h4>示例</h4>
<p>求向量组 $ \alpha_1 = (1, 2, 3)^T $，$ \alpha_2 = (2, 4, 6)^T $，$ \alpha_3 = (1, 0, 1)^T $，$ \alpha_4 = (3, 2, 5)^T $ 的极大线性无关组。</p>
<ol>
<li>
<p><strong>构造矩阵</strong>：
$$
A = (\alpha_1, \alpha_2, \alpha_3, \alpha_4) = \begin{pmatrix}
1 &amp; 2 &amp; 1 &amp; 3 \
2 &amp; 4 &amp; 0 &amp; 2 \
3 &amp; 6 &amp; 1 &amp; 5
\end{pmatrix}
$$</p>
</li>
<li>
<p><strong>初等行变换</strong>：
$$
A \xrightarrow{r_2-2r_1, r_3-3r_1} \begin{pmatrix}
1 &amp; 2 &amp; 1 &amp; 3 \
0 &amp; 0 &amp; -2 &amp; -4 \
0 &amp; 0 &amp; -2 &amp; -4
\end{pmatrix} \xrightarrow{r_3-r_2} \begin{pmatrix}
1 &amp; 2 &amp; 1 &amp; 3 \
0 &amp; 0 &amp; -2 &amp; -4 \
0 &amp; 0 &amp; 0 &amp; 0
\end{pmatrix}
$$</p>
</li>
<li>
<p><strong>确定主元列</strong>：行阶梯形矩阵中，第1列和第3列为主元列（非零行首元所在列）。</p>
</li>
<li>
<p><strong>对应原向量</strong>：原矩阵中第1列和第3列对应的向量 $ \alpha_1, \alpha_3 $ 是一个极大线性无关组。</p>
</li>
</ol>
<h4>向量的线性表示</h4>
<p>若需将其他向量用极大线性无关组表示，可继续将行阶梯形矩阵化为行简化阶梯形矩阵：
$$
\begin{pmatrix}
1 &amp; 2 &amp; 1 &amp; 3 \
0 &amp; 0 &amp; -2 &amp; -4 \
0 &amp; 0 &amp; 0 &amp; 0
\end{pmatrix} \xrightarrow{r_2 \times (-\frac{1}{2})} \begin{pmatrix}
1 &amp; 2 &amp; 1 &amp; 3 \
0 &amp; 0 &amp; 1 &amp; 2 \
0 &amp; 0 &amp; 0 &amp; 0
\end{pmatrix} \xrightarrow{r_1-r_3} \begin{pmatrix}
1 &amp; 2 &amp; 0 &amp; 1 \
0 &amp; 0 &amp; 1 &amp; 2 \
0 &amp; 0 &amp; 0 &amp; 0
\end{pmatrix}
$$</p>
<p>由此可得：</p>
<ul>
<li>$ \alpha_2 = 2\alpha_1 $（第2列：$ (2, 0, 0)^T = 2(1, 0, 0)^T $）</li>
<li>$ \alpha_4 = \alpha_1 + 2\alpha_3 $（第4列：$ (1, 2, 0)^T = 1(1, 0, 0)^T + 2(0, 1, 0)^T $）</li>
</ul>
<h4>注意事项</h4>
<ol>
<li><strong>行变换而非列变换</strong>：必须使用初等行变换，不能使用初等列变换；</li>
<li><strong>主元列的选择</strong>：不同的初等行变换可能得到不同的主元列位置，但极大线性无关组所含向量个数相同；</li>
<li><strong>结果验证</strong>：可通过验证所选向量组线性无关，且其他向量可由其线性表示来确认正确性；</li>
<li><strong>行简化阶梯形</strong>：化为行简化阶梯形矩阵可直接得到线性表示系数。</li>
</ol>
<hr />
<p>定义 3.9
向量组 $\alpha_1,\alpha_2,\dots,\alpha_s$ 的任一极大线性无关组所含向量的个数，称为该向量组的秩，记作 $r(\alpha_1,\alpha_2,\dots,\alpha_s)$。</p>
<p>规定：零向量组的秩为 $0$，$0 \le r(\alpha_1,\alpha_2,\dots,\alpha_s) \le s$。</p>
<hr />
<h3>相关结论</h3>
<ol>
<li>向量组的秩唯一。</li>
<li>向量组线性无关 $\iff r(\alpha_1,\alpha_2,\dots,\alpha_s)=s$
向量组线性相关 $\iff r(\alpha_1,\alpha_2,\dots,\alpha_s)&lt;s$</li>
<li>若向量组(Ⅰ)可由向量组(Ⅱ)线性表示，则 $r(\text{Ⅰ}) \le r(\text{Ⅱ})$。</li>
<li>等价向量组的秩相等。</li>
<li>向量组的秩 $\le$ 向量个数，向量组的秩 $\le$ 向量维数。</li>
<li>秩为 $r$ 的向量组，存在 $r$ 个线性无关向量，任意 $r+1$ 个向量必线性相关。</li>
<li>秩为 $r(r&gt;0)$ 的向量组，任意 $r$ 个线性无关的部分组都是其极大线性无关组。</li>
</ol>
<h3>矩阵的行秩与列秩</h3>
<p>矩阵行向量组的秩称为<strong>行秩</strong>，列向量组的秩称为<strong>列秩</strong>。
行秩 $=$ 列秩 $= r(A)$</p>
<hr />
<h1>第四部分：线性方程组</h1>
<hr />
<h2>4.1 线性方程组的表示法</h2>
<h3>一般形式</h3>
<p>$$
\begin{cases}
a_{11}x_1 + a_{12}x_2 + \dots + a_{1n}x_n = b_1 \
a_{21}x_1 + a_{22}x_2 + \dots + a_{2n}x_n = b_2 \
\quad\quad\quad\quad\quad\quad\quad\quad\vdots \
a_{m1}x_1 + a_{m2}x_2 + \dots + a_{mn}x_n = b_m
\end{cases}
$$</p>
<ul>
<li>$m$：方程的个数</li>
<li>$n$：未知量的个数</li>
</ul>
<hr />
<h3>矩阵形式</h3>
<ul>
<li>系数矩阵：
$$
A = \begin{pmatrix}
a_{11} &amp; a_{12} &amp; \dots &amp; a_{1n} \
a_{21} &amp; a_{22} &amp; \dots &amp; a_{2n} \
\vdots &amp; \vdots &amp; &amp; \vdots \
a_{m1} &amp; a_{m2} &amp; \dots &amp; a_{mn}
\end{pmatrix}
$$</li>
<li>增广矩阵：
$$
\overline{A} = (A,b) = \begin{pmatrix}
a_{11} &amp; a_{12} &amp; \dots &amp; a_{1n} &amp; b_1 \
a_{21} &amp; a_{22} &amp; \dots &amp; a_{2n} &amp; b_2 \
\vdots &amp; \vdots &amp; &amp; \vdots &amp; \vdots \
a_{m1} &amp; a_{m2} &amp; \dots &amp; a_{mn} &amp; b_m
\end{pmatrix}
$$</li>
<li>未知数列向量：
$$
x = \begin{pmatrix} x_1 \ x_2 \ \vdots \ x_n \end{pmatrix}
$$</li>
<li>常数列向量：
$$
b = \begin{pmatrix} b_1 \ b_2 \ \vdots \ b_m \end{pmatrix}
$$</li>
</ul>
<hr />
<h4>分类</h4>
<ol>
<li>
<p><strong>非齐次线性方程组</strong>
常数项 $b_1,b_2,\dots,b_m$ 不全为零，矩阵形式为：
$$Ax = b$$</p>
</li>
<li>
<p><strong>齐次线性方程组（导出组）</strong>
常数项 $b_1,b_2,\dots,b_m$ 全为零，即：
$$
\begin{cases}
a_{11}x_1 + a_{12}x_2 + \dots + a_{1n}x_n = 0 \
a_{21}x_1 + a_{22}x_2 + \dots + a_{2n}x_n = 0 \
\quad\quad\quad\quad\quad\quad\quad\quad\vdots \
a_{m1}x_1 + a_{m2}x_2 + \dots + a_{mn}x_n = 0
\end{cases}
$$
矩阵形式为：
$$Ax = 0$$</p>
</li>
</ol>
<hr />
<h3>向量形式</h3>
<p>将系数矩阵 $ A $ 按列分块，即 $ A = (\alpha_1, \alpha_2, \dots, \alpha_n) $，根据向量的线性运算，则非齐次线性方程组（4.1）可以表示为向量形式：
$$
x_1\alpha_1 + x_2\alpha_2 + \dots + x_n\alpha_n = b
$$</p>
<p>齐次线性方程组（4.2）的向量形式为：
$$
x_1\alpha_1 + x_2\alpha_2 + \dots + x_n\alpha_n = 0
$$</p>
<p>其中，$ \alpha_1, \alpha_2, \dots, \alpha_n $ 为系数矩阵 $ A $ 的列向量组。</p>
<hr />
<h2>4.2 线性方程组解的判定</h2>
<p>定义 4.1.1 线性方程组的下列三种变换，称为线性方程组的初等变换：</p>
<ol>
<li>交换两个方程的位置；</li>
<li>在方程两端乘以数 $ k \neq 0 $；</li>
<li>某方程两端 $ l $ 倍后加到另一个方程上。</li>
</ol>
<hr />
<h3>n 元非齐次线性方程组 $Ax = b$ 解的判定</h3>
<table>
<thead>
<tr>
<th>解的情况</th>
<th>充要条件</th>
</tr>
</thead>
<tbody>
<tr>
<td>有解</td>
<td>$ r(A) = r(\overline{A}) $</td>
</tr>
<tr>
<td>有唯一解</td>
<td>$ r(A) = r(\overline{A}) = n $</td>
</tr>
<tr>
<td>有无穷多解</td>
<td>$ r(A) = r(\overline{A}) &lt; n $</td>
</tr>
<tr>
<td>无解</td>
<td>$ r(A) \neq r(\overline{A}) $</td>
</tr>
</tbody>
</table>
<hr />
<h4>逻辑关系</h4>
<ul>
<li>$ r(A) \neq r(\overline{A}) \implies $ 无解</li>
<li>$ r(A) = r(\overline{A}) \implies $ 有解
<ul>
<li>$ r(A) = r(\overline{A}) = n \implies $ 唯一解</li>
<li>$ r(A) = r(\overline{A}) &lt; n \implies $ 无穷多解</li>
</ul>
</li>
</ul>
<hr />
<h3>齐次线性方程组解的判定</h3>
<h4>核心结论</h4>
<ol>
<li>
<p><strong>$Ax=0$ 有非零解</strong>
$\iff$ 向量组 $\alpha_1, \alpha_2, \dots, \alpha_n$ 线性相关
$\iff r(\alpha_1, \alpha_2, \dots, \alpha_n) &lt; n$
$\iff r(A) &lt; n$</p>
</li>
<li>
<p><strong>$Ax=0$ 只有零解</strong>
$\iff$ 向量组 $\alpha_1, \alpha_2, \dots, \alpha_n$ 线性无关
$\iff r(\alpha_1, \alpha_2, \dots, \alpha_n) = n$
$\iff r(A) = n$</p>
</li>
</ol>
<hr />
<h4>定理 4.2</h4>
<p>$n$ 元齐次线性方程组 $Ax=0$ 有非零解的充要条件是其系数矩阵 $A$ 的秩小于未知量的个数，即 $r(A) &lt; n$。<br />
$Ax=0$ 只有零解的充要条件是其系数矩阵 $A$ 的秩等于未知量的个数，即 $r(A) = n$。</p>
<table>
<thead>
<tr>
<th>解的情况</th>
<th>充要条件</th>
</tr>
</thead>
<tbody>
<tr>
<td>有非零解</td>
<td>$r(A) &lt; n$</td>
</tr>
<tr>
<td>只有零解</td>
<td>$r(A) = n$</td>
</tr>
</tbody>
</table>
<hr />
<h4>推论 4.1</h4>
<p>对于齐次线性方程组 $Ax=0$，若方程个数 $m$ 小于未知数个数 $n$，则 $Ax=0$ 必有非零解。<br />
（理由：$r(A) \le \min{m, n} = m &lt; n$）</p>
<h4>推论 4.2</h4>
<p>对于齐次线性方程组 $Ax=0$，若方程个数等于未知数个数（即 $A$ 为方阵）：</p>
<ul>
<li>$Ax=0$ 有非零解 $\iff |A| = 0$</li>
<li>$Ax=0$ 只有零解 $\iff |A| \neq 0$</li>
</ul>
<hr />
<h2>4.3 线性方程组的解的性质</h2>
<h3>齐次线性方程组解的性质</h3>
<ul>
<li><strong>性质1</strong>：若 $\xi_1, \xi_2$ 是 $Ax=0$ 的解，则 $\xi_1+\xi_2$ 也是 $Ax=0$ 的解。
证明：$A(\xi_1+\xi_2)=A\xi_1+A\xi_2=0+0=0$。</li>
<li><strong>性质2</strong>：若 $\xi$ 是 $Ax=0$ 的解，则对任意常数 $k$，$k\xi$ 也是 $Ax=0$ 的解。
证明：$A(k\xi)=kA\xi=k\cdot0=0$。</li>
<li><strong>性质3</strong>：若 $\xi_1, \xi_2, \dots, \xi_s$ 是 $Ax=0$ 的解，则它们的线性组合 $c_1\xi_1+c_2\xi_2+\dots+c_s\xi_s$（$c_1,c_2,\dots,c_s$ 为任意常数）也是 $Ax=0$ 的解。
证明：$A(c_1\xi_1+\dots+c_s\xi_s)=c_1A\xi_1+\dots+c_sA\xi_s=0$。</li>
</ul>
<hr />
<h3>非齐次线性方程组解的性质</h3>
<p>（$Ax=b$ 与其导出组 $Ax=0$ 的解的关系）</p>
<ul>
<li><strong>性质4</strong>：设 $\eta_1, \eta_2$ 是 $Ax=b$ 的任意两个解，则 $\eta_1-\eta_2$ 是导出组 $Ax=0$ 的解。
证明：$A(\eta_1-\eta_2)=A\eta_1-A\eta_2=b-b=0$。</li>
<li><strong>性质5</strong>：设 $\eta$ 是 $Ax=b$ 的解，$\xi$ 是导出组 $Ax=0$ 的解，则 $\xi+\eta$ 是 $Ax=b$ 的解。
证明：$A(\eta+\xi)=A\eta+A\xi=b+0=b$。</li>
<li><strong>性质6</strong>：若 $\eta^<em>$ 是 $Ax=b$ 的一个特解，则 $Ax=b$ 的任一解都可表示为 $\xi+\eta^</em>$，其中 $\xi$ 是导出组 $Ax=0$ 的解。</li>
</ul>
<hr />
<h2>4.4 齐次线性方程组的基础解系</h2>
<h3>定义 4.1 基础解系</h3>
<p>设 $\xi_1, \xi_2, \dots, \xi_s$ 是齐次线性方程组 $Ax=0$ 的解向量，若满足：</p>
<ol>
<li>$\xi_1, \xi_2, \dots, \xi_s$ 线性无关；</li>
<li>方程组 $Ax=0$ 的任一解向量都可以由 $\xi_1, \xi_2, \dots, \xi_s$ 线性表示。
则称 $\xi_1, \xi_2, \dots, \xi_s$ 是 $Ax=0$ 的一个<strong>基础解系</strong>。</li>
</ol>
<hr />
<h3>定理 4.3</h3>
<p>对于 $n$ 元齐次线性方程组 $Ax=0$，若系数矩阵的秩 $r(A)=r &lt; n$，则：</p>
<ul>
<li>$Ax=0$ 一定存在基础解系；</li>
<li>基础解系中含有 $n-r$ 个线性无关的解向量。</li>
</ul>
<hr />
<h3>示例：求基础解系</h3>
<ol>
<li>写出齐次方程组：
$$
\begin{cases}
x_1 + x_2 + x_3 + 4x_4 - 3x_5 = 0 \
x_1 - x_2 + 3x_3 - 2x_4 - x_5 = 0 \
2x_1 + x_2 + 3x_3 + 5x_4 - 5x_5 = 0 \
3x_1 + x_2 + 5x_3 + 6x_4 - 7x_5 = 0
\end{cases}
$$</li>
<li>对系数矩阵做初等行变换，化为阶梯形：
$$
A = \begin{pmatrix}
1 &amp; 1 &amp; 1 &amp; 4 &amp; -3 \
1 &amp; -1 &amp; 3 &amp; -2 &amp; -1 \
2 &amp; 1 &amp; 3 &amp; 5 &amp; -5 \
3 &amp; 1 &amp; 5 &amp; 6 &amp; -7
\end{pmatrix}
\rightarrow
\begin{pmatrix}
1 &amp; 0 &amp; 2 &amp; 1 &amp; -2 \
0 &amp; 1 &amp; -1 &amp; 3 &amp; -1 \
0 &amp; 0 &amp; 0 &amp; 0 &amp; 0 \
0 &amp; 0 &amp; 0 &amp; 0 &amp; 0
\end{pmatrix}
$$</li>
<li>写出同解方程组：
$$
\begin{cases}
x_1 = -2x_3 - x_4 + 2x_5 \
x_2 = x_3 - 3x_4 + x_5
\end{cases}
$$</li>
<li>令自由变量 $(x_3, x_4, x_5)$ 分别取 $(1,0,0), (0,1,0), (0,0,1)$，得：
$$
\xi_1 = \begin{pmatrix} -2 \ 1 \ 1 \ 0 \ 0 \end{pmatrix}, \quad
\xi_2 = \begin{pmatrix} -1 \ -3 \ 0 \ 1 \ 0 \end{pmatrix}, \quad
\xi_3 = \begin{pmatrix} 2 \ 1 \ 0 \ 0 \ 1 \end{pmatrix}
$$
$\xi_1, \xi_2, \xi_3$ 即为该方程组的一个基础解系。</li>
</ol>
<hr />
<ol>
<li>基础解系的判定</li>
</ol>
<p>根据基础解系的定义及存在条件，可知：</p>
<p>向量组 $\xi_1, \xi_2, \dots, \xi_s$ 是 $n$ 元齐次线性方程组 $Ax=0$ 的基础解系，当且仅当：</p>
<ul>
<li>（Ⅰ）$\xi_1, \xi_2, \dots, \xi_s$ 是 $Ax=0$ 的解向量；</li>
<li>（Ⅱ）$\xi_1, \xi_2, \dots, \xi_s$ 线性无关；</li>
<li>（Ⅲ）方程组 $Ax=0$ 的任一解向量都可以由 $\xi_1, \xi_2, \dots, \xi_s$ 线性表示。</li>
</ul>
<hr />
<p>向量组 $\xi_1,\xi_2,\dots,\xi_s$ 是 $n$ 元齐次线性方程组 $Ax=0$ 的基础解系，当且仅当：</p>
<ul>
<li>（Ⅰ）$\xi_1,\xi_2,\dots,\xi_s$ 是 $Ax=0$ 的解向量；</li>
<li>（Ⅱ）$\xi_1,\xi_2,\dots,\xi_s$ 线性无关；</li>
<li>（Ⅲ）向量组 $\xi_1,\xi_2,\dots,\xi_s$ 中向量的个数等于 $n-r(A)$（即能表示方程组的所有解）。</li>
</ul>
<hr />
<h3>基础解系的性质（具备极大线性无关组的性质）</h3>
<ol>
<li>当 $Ax=0$ 有非零解时，基础解系存在；</li>
<li>若 $Ax=0$ 的基础解系存在，则基础解系不唯一；</li>
<li>$Ax=0$ 的不同基础解系之间可以互相线性表示；</li>
<li>$Ax=0$ 的不同基础解系中所含向量的个数相等，均为 $n - r(A)$（其中 $n$ 为未知数的个数，$r(A)$ 为系数矩阵的秩）。</li>
<li>$Ax=0$ 的任意 $n-r(A)$ 个线性无关的解向量都是它的基础解系（其中 $n$ 为未知数的个数）。</li>
<li>若 $\xi_1,\xi_2,\dots,\xi_s$ 是 $Ax=0$ 的一个基础解系，则 $Ax=0$ 的任一解都可用 $\xi_1,\xi_2,\dots,\xi_s$ 线性表示。</li>
</ol>
<h2>4.5 线性方程组解的结构</h2>
<h3>齐次线性方程组解的结构（定理4.4）</h3>
<p>当齐次线性方程组 $Ax=0$ 只有零解时，问题自然解决；当有非零解时，有如下结论：</p>
<p>对于 $n$ 元齐次线性方程组 $Ax=0$，若系数矩阵 $A$ 的秩 $r(A)=r&lt;n$，且 $\xi_1,\xi_2,\dots,\xi_{n-r}$ 为其一个基础解系，则方程组的通解可表示为：
$$
x = c_1\xi_1 + c_2\xi_2 + \dots + c_{n-r}\xi_{n-r}
$$
其中 $c_1,c_2,\dots,c_{n-r}$ 为任意常数。</p>
<p>由于基础解系不唯一，齐次线性方程组的通解形式也不唯一，但都可以表示方程组的所有解。</p>
<hr />
<h3>非齐次线性方程组解的结构（定理4.5）</h3>
<p>对于 $n$ 元非齐次线性方程组 $Ax=b$，若 $r(A)=r(\overline{A})=r&lt;n$，且：</p>
<ul>
<li>$\alpha_0$ 是 $Ax=b$ 的一个特解，</li>
<li>$\xi_1,\xi_2,\dots,\xi_{n-r}$ 为其导出组 $Ax=0$ 的一个基础解系，</li>
</ul>
<p>则方程组的通解可表示为：
$$
x = \alpha_0 + c_1\xi_1 + c_2\xi_2 + \dots + c_{n-r}\xi_{n-r}
$$
其中 $c_1,c_2,\dots,c_{n-r}$ 为任意常数。</p>
<hr />
<h2>4.6 线性方程组的求解</h2>
<h3>一、齐次线性方程组 $ A\mathbf{x} = \mathbf{0} $</h3>
<h4>求解步骤</h4>
<ol>
<li><strong>写出系数矩阵</strong> $ A $；</li>
<li><strong>初等行变换</strong>：将 $ A $ 化为<strong>行简化阶梯形矩阵</strong>；</li>
<li><strong>确定秩</strong>：设 $ r(A) = r $，未知数个数 $ n $；</li>
<li><strong>判断解的情况</strong>：
<ul>
<li>若 $ r = n $，方程组只有零解；</li>
<li>若 $ r &lt; n $，方程组有非零解（无穷多解）；</li>
</ul>
</li>
<li><strong>写出基础解系</strong>：
<ul>
<li>确定自由未知量（非主元列对应的未知数）；</li>
<li>令自由未知量分别取 $ (1,0,\dots,0), (0,1,\dots,0), \dots, (0,0,\dots,1) $；</li>
<li>求出对应的解向量，即为基础解系 $ \xi_1, \xi_2, \dots, \xi_{n-r} $；</li>
</ul>
</li>
<li><strong>写出通解</strong>：
$$
\mathbf{x} = c_1\xi_1 + c_2\xi_2 + \dots + c_{n-r}\xi_{n-r}
$$
其中 $ c_1, c_2, \dots, c_{n-r} $ 为任意常数。</li>
</ol>
<h4>示例</h4>
<p>求解齐次线性方程组：
$$
\begin{cases}
x_1 + x_2 + x_3 + x_4 = 0 \
2x_1 + 3x_2 + 4x_3 + 5x_4 = 0 \
3x_1 + 4x_2 + 5x_3 + 6x_4 = 0
\end{cases}
$$</p>
<p><strong>解</strong>：</p>
<ol>
<li>
<p>系数矩阵：
$$
A = \begin{pmatrix}
1 &amp; 1 &amp; 1 &amp; 1 \
2 &amp; 3 &amp; 4 &amp; 5 \
3 &amp; 4 &amp; 5 &amp; 6
\end{pmatrix}
$$</p>
</li>
<li>
<p>初等行变换：
$$
A \xrightarrow{r_2-2r_1, r_3-3r_1} \begin{pmatrix}
1 &amp; 1 &amp; 1 &amp; 1 \
0 &amp; 1 &amp; 2 &amp; 3 \
0 &amp; 1 &amp; 2 &amp; 3
\end{pmatrix} \xrightarrow{r_3-r_2} \begin{pmatrix}
1 &amp; 1 &amp; 1 &amp; 1 \
0 &amp; 1 &amp; 2 &amp; 3 \
0 &amp; 0 &amp; 0 &amp; 0
\end{pmatrix}
$$
$$
\xrightarrow{r_1-r_2} \begin{pmatrix}
1 &amp; 0 &amp; -1 &amp; -2 \
0 &amp; 1 &amp; 2 &amp; 3 \
0 &amp; 0 &amp; 0 &amp; 0
\end{pmatrix}
$$</p>
</li>
<li>
<p>确定秩：$ r(A) = 2 $，$ n = 4 $，$ n-r = 2 $，有无穷多解。</p>
</li>
<li>
<p>确定自由未知量：$ x_3, x_4 $ 为自由未知量。</p>
</li>
<li>
<p>求基础解系：</p>
<ul>
<li>令 $ x_3 = 1, x_4 = 0 $，得 $ x_1 = 1, x_2 = -2 $，$ \xi_1 = (1, -2, 1, 0)^T $；</li>
<li>令 $ x_3 = 0, x_4 = 1 $，得 $ x_1 = 2, x_2 = -3 $，$ \xi_2 = (2, -3, 0, 1)^T $。</li>
</ul>
</li>
<li>
<p>通解：
$$
\mathbf{x} = c_1\begin{pmatrix} 1 \ -2 \ 1 \ 0 \end{pmatrix} + c_2\begin{pmatrix} 2 \ -3 \ 0 \ 1 \end{pmatrix}
$$
（$ c_1, c_2 $ 为任意常数）</p>
</li>
</ol>
<hr />
<h3>二、非齐次线性方程组 $ A\mathbf{x} = \mathbf{b} $</h3>
<h4>求解步骤</h4>
<ol>
<li><strong>写出增广矩阵</strong> $ \bar{A} = (A \mid \mathbf{b}) $；</li>
<li><strong>初等行变换</strong>：将 $ \bar{A} $ 化为<strong>行简化阶梯形矩阵</strong>；</li>
<li><strong>判断解的情况</strong>：
<ul>
<li>若 $ r(A) \neq r(\bar{A}) $，方程组无解；</li>
<li>若 $ r(A) = r(\bar{A}) = n $，方程组有唯一解；</li>
<li>若 $ r(A) = r(\bar{A}) = r &lt; n $，方程组有无穷多解；</li>
</ul>
</li>
<li><strong>求解</strong>：
<ul>
<li><strong>唯一解</strong>：直接读出解；</li>
<li><strong>无穷多解</strong>：先求特解 $ \alpha_0 $，再求导出组 $ A\mathbf{x} = \mathbf{0} $ 的基础解系 $ \xi_1, \xi_2, \dots, \xi_{n-r} $；</li>
</ul>
</li>
<li><strong>写出通解</strong>：
$$
\mathbf{x} = \alpha_0 + c_1\xi_1 + c_2\xi_2 + \dots + c_{n-r}\xi_{n-r}
$$
其中 $ c_1, c_2, \dots, c_{n-r} $ 为任意常数。</li>
</ol>
<h4>示例</h4>
<p>求解非齐次线性方程组：
$$
\begin{cases}
x_1 + x_2 + x_3 = 2 \
2x_1 + 3x_2 + 4x_3 = 7 \
3x_1 + 4x_2 + 5x_3 = 9
\end{cases}
$$</p>
<p><strong>解</strong>：</p>
<ol>
<li>
<p>增广矩阵：
$$
\bar{A} = \begin{pmatrix}
1 &amp; 1 &amp; 1 &amp; 2 \
2 &amp; 3 &amp; 4 &amp; 7 \
3 &amp; 4 &amp; 5 &amp; 9
\end{pmatrix}
$$</p>
</li>
<li>
<p>初等行变换：
$$
\bar{A} \xrightarrow{r_2-2r_1, r_3-3r_1} \begin{pmatrix}
1 &amp; 1 &amp; 1 &amp; 2 \
0 &amp; 1 &amp; 2 &amp; 3 \
0 &amp; 1 &amp; 2 &amp; 3
\end{pmatrix} \xrightarrow{r_3-r_2} \begin{pmatrix}
1 &amp; 1 &amp; 1 &amp; 2 \
0 &amp; 1 &amp; 2 &amp; 3 \
0 &amp; 0 &amp; 0 &amp; 0
\end{pmatrix}
$$
$$
\xrightarrow{r_1-r_2} \begin{pmatrix}
1 &amp; 0 &amp; -1 &amp; -1 \
0 &amp; 1 &amp; 2 &amp; 3 \
0 &amp; 0 &amp; 0 &amp; 0
\end{pmatrix}
$$</p>
</li>
<li>
<p>判断：$ r(A) = r(\bar{A}) = 2 &lt; 3 $，有无穷多解。</p>
</li>
<li>
<p>求特解：令自由未知量 $ x_3 = 0 $，得 $ x_1 = -1, x_2 = 3 $，特解 $ \alpha_0 = (-1, 3, 0)^T $。</p>
</li>
<li>
<p>求导出组的基础解系：</p>
<ul>
<li>令 $ x_3 = 1 $，得 $ x_1 = 1, x_2 = -2 $，$ \xi_1 = (1, -2, 1)^T $。</li>
</ul>
</li>
<li>
<p>通解：
$$
\mathbf{x} = \begin{pmatrix} -1 \ 3 \ 0 \end{pmatrix} + c_1\begin{pmatrix} 1 \ -2 \ 1 \end{pmatrix}
$$
（$ c_1 $ 为任意常数）</p>
</li>
</ol>
<hr />
<h3>三、含参数的线性方程组</h3>
<h4>讨论步骤</h4>
<ol>
<li>对增广矩阵 $ \bar{A} $ 进行初等行变换，化为行阶梯形矩阵；</li>
<li>根据参数的取值，讨论 $ r(A) $ 与 $ r(\bar{A}) $ 的关系；</li>
<li>确定方程组有解、无解、唯一解、无穷多解的条件；</li>
<li>在有解的情况下求解。</li>
</ol>
<h4>示例</h4>
<p>讨论方程组解的情况：
$$
\begin{cases}
x_1 + x_2 + x_3 = 1 \
x_1 + 2x_2 + ax_3 = 2 \
x_1 + 4x_2 + a^2x_3 = a
\end{cases}
$$</p>
<p><strong>解</strong>：</p>
<ol>
<li>
<p>增广矩阵：
$$
\bar{A} = \begin{pmatrix}
1 &amp; 1 &amp; 1 &amp; 1 \
1 &amp; 2 &amp; a &amp; 2 \
1 &amp; 4 &amp; a^2 &amp; a
\end{pmatrix}
\xrightarrow{r_2-r_1, r_3-r_1} \begin{pmatrix}
1 &amp; 1 &amp; 1 &amp; 1 \
0 &amp; 1 &amp; a-1 &amp; 1 \
0 &amp; 3 &amp; a^2-1 &amp; a-1
\end{pmatrix}
$$
$$
\xrightarrow{r_3-3r_2} \begin{pmatrix}
1 &amp; 1 &amp; 1 &amp; 1 \
0 &amp; 1 &amp; a-1 &amp; 1 \
0 &amp; 0 &amp; a^2-3a+2 &amp; a-4
\end{pmatrix}
$$</p>
</li>
<li>
<p>讨论：</p>
<ul>
<li>$ a^2-3a+2 = (a-1)(a-2) $</li>
<li>当 $ a \neq 1 $ 且 $ a \neq 2 $ 时，$ r(A) = r(\bar{A}) = 3 = n $，有唯一解；</li>
<li>当 $ a = 1 $ 时，$ \bar{A} \to \begin{pmatrix} 1 &amp; 1 &amp; 1 &amp; 1 \ 0 &amp; 1 &amp; 0 &amp; 1 \ 0 &amp; 0 &amp; 0 &amp; -3 \end{pmatrix} $，$ r(A) = 2 \neq r(\bar{A}) = 3 $，无解；</li>
<li>当 $ a = 2 $ 时，$ \bar{A} \to \begin{pmatrix} 1 &amp; 1 &amp; 1 &amp; 1 \ 0 &amp; 1 &amp; 1 &amp; 1 \ 0 &amp; 0 &amp; 0 &amp; -2 \end{pmatrix} $，$ r(A) = 2 \neq r(\bar{A}) = 3 $，无解。</li>
</ul>
</li>
</ol>
<hr />
<h3>四、线性方程组解的结构定理</h3>
<p><strong>定理</strong>：设 $ n $ 元线性方程组 $ A\mathbf{x} = \mathbf{b} $ 的系数矩阵 $ A $ 的秩 $ r(A) = r $，则：</p>
<ol>
<li>若 $ r(A) \neq r(\bar{A}) $，方程组无解；</li>
<li>若 $ r(A) = r(\bar{A}) = n $，方程组有唯一解；</li>
<li>若 $ r(A) = r(\bar{A}) = r &lt; n $，方程组有无穷多解，通解为：
$$
\mathbf{x} = \alpha_0 + c_1\xi_1 + c_2\xi_2 + \dots + c_{n-r}\xi_{n-r}
$$
其中：
<ul>
<li>$ \alpha_0 $ 是 $ A\mathbf{x} = \mathbf{b} $ 的一个特解；</li>
<li>$ \xi_1, \xi_2, \dots, \xi_{n-r} $ 是导出组 $ A\mathbf{x} = \mathbf{0} $ 的基础解系；</li>
<li>$ c_1, c_2, \dots, c_{n-r} $ 为任意常数。</li>
</ul>
</li>
</ol>
<hr />
<h3>五、解题技巧总结</h3>
<ol>
<li><strong>行简化阶梯形</strong>：尽量化为行简化阶梯形，可直接读出解；</li>
<li><strong>自由未知量选择</strong>：通常选非主元列对应的未知量为自由未知量；</li>
<li><strong>特解求法</strong>：令所有自由未知量为 0，可直接得到一个特解；</li>
<li><strong>基础解系求法</strong>：对每个自由未知量轮流取 1，其余取 0；</li>
<li><strong>参数讨论</strong>：注意使分母为 0 或使某行全为 0 的参数值；</li>
<li><strong>验证</strong>：可将求得的解代入原方程组验证正确性。</li>
</ol>
<hr />
<h1>第五部分：特征值与特征向量</h1>
<hr />
<h2>5.1 特征值与特征向量的定义与关系</h2>
<h3>特征值与特征向量定义</h3>
<p>设 $A$ 为 $n$ 阶方阵，$\alpha$ 为 $n$ 维<strong>非零列向量</strong>，$\lambda$ 为数。若满足：
$$A\alpha = \lambda\alpha$$
则称 $\lambda$ 为方阵 $A$ 的<strong>特征值</strong>，$\alpha$ 为 $A$ 对应于特征值 $\lambda$ 的<strong>特征向量</strong>。</p>
<h3>关键说明</h3>
<ol>
<li>
<p><strong>矩阵 $A$ 必须是方阵</strong>
若 $A$ 不是方阵，则 $A\alpha$ 与 $\lambda\alpha$ 的维度无法匹配，因此特征值/特征向量仅对方阵定义。</p>
</li>
<li>
<p><strong>特征值 $\lambda$ 是一个数，可取 0</strong>
$$
\begin{pmatrix} 1 &amp; -1 \ 1 &amp; -1 \end{pmatrix}
\begin{pmatrix} 1 \ 1 \end{pmatrix}
= 0 \times \begin{pmatrix} 1 \ 1 \end{pmatrix}
$$
此处 $\lambda=0$ 为该矩阵的一个特征值。</p>
</li>
<li>
<p><strong>特征向量 $\alpha$ 是非零列向量</strong></p>
</li>
</ol>
<ul>
<li>列向量：满足矩阵乘法维度要求；</li>
<li>非零向量：$\alpha=0$ 时等式恒成立，无法体现矩阵特性，故特征向量规定为非零。</li>
</ul>
<hr />
<h3>特征值与特征向量的关系</h3>
<p><strong>性质1</strong> 若$\alpha$是矩阵$A$对应于特征值$\lambda$的特征向量，则$k\alpha(k\neq0)$也是矩阵$A$对应于特征值$\lambda$的特征向量。</p>
<p><strong>性质2</strong> 给定方阵$A$，特征向量$\alpha$只能属于一个特征值。</p>
<p><strong>性质3</strong> 给定方阵$A$，若$\alpha_1,\alpha_2$都是对应于特征值$\lambda$的特征向量，当线性组合$k_1\alpha_1+k_2\alpha_2\neq0$时，该组合也是对应于特征值$\lambda$的特征向量。</p>
<h2>5.2 特征值与特征向量的求法</h2>
<h3>特征值的求法（公式：$|\lambda E - A| = 0$）</h3>
<hr />
<h4>步骤1：写出 $\lambda E - A$</h4>
<ul>
<li>构造方法：
<ol>
<li>$\lambda$ 仅出现在主对角线上；</li>
<li>其余元素为矩阵 $A$ 对应元素的相反数。</li>
</ol>
</li>
<li>示例：若 $A=\begin{pmatrix}1&amp;1&amp;1\2&amp;2&amp;2\3&amp;3&amp;3\end{pmatrix}$，则
$$
\lambda E - A = \begin{pmatrix}
\lambda-1 &amp; -1 &amp; -1 \
-2 &amp; \lambda-2 &amp; -2 \
-3 &amp; -3 &amp; \lambda-3
\end{pmatrix}
$$</li>
</ul>
<h4>步骤2：写出 $|\lambda E - A|$</h4>
<p>将矩阵 $\lambda E - A$ 改写为行列式形式（熟练后可省略步骤1）：
$$
|\lambda E - A| = \begin{vmatrix}
\lambda-1 &amp; -1 &amp; -1 \
-2 &amp; \lambda-2 &amp; -2 \
-3 &amp; -3 &amp; \lambda-3
\end{vmatrix}
$$</p>
<h4>步骤3：解特征方程 $|\lambda E - A| = 0$</h4>
<p>根据矩阵类型，有以下常见情况：</p>
<h5>情况1：2阶矩阵</h5>
<p>直接展开行列式计算。</p>
<ul>
<li>例：$A=\begin{pmatrix}1&amp;1\4&amp;1\end{pmatrix}$
$$
|\lambda E - A| = \begin{vmatrix}\lambda-1 &amp; -1 \ -4 &amp; \lambda-1\end{vmatrix} = (\lambda-1)^2 - 4 = (\lambda+1)(\lambda-3)=0
$$
解得：$\lambda_1=-1,\ \lambda_2=3$。</li>
</ul>
<h5>情况2：对角、上三角、下三角矩阵</h5>
<p>特征值等于主对角线元素，直接写出结果。</p>
<ul>
<li>例：上三角矩阵 $A=\begin{pmatrix}-3&amp;6&amp;9\0&amp;4&amp;-11\0&amp;0&amp;7\end{pmatrix}$，特征值为 $\lambda_1=-3,\ \lambda_2=4,\ \lambda_3=7$。</li>
</ul>
<h5>情况3：特征行列式零元素较多</h5>
<p>按某一行（列）展开，优先选择只有一个非零元素的行/列；若零元素不足，可先用行列式性质消零再展开。</p>
<h5>情况4：行和（列和）相等的矩阵</h5>
<p>先将所有列加到第1列，提取公因子，再化为上三角行列式。</p>
<ul>
<li>例：$A=\begin{pmatrix}1&amp;1&amp;1\1&amp;1&amp;1\1&amp;1&amp;1\end{pmatrix}$
$$
|\lambda E - A| = \begin{vmatrix}\lambda-1 &amp; -1 &amp; -1 \ -1 &amp; \lambda-1 &amp; -1 \ -1 &amp; -1 &amp; \lambda-1\end{vmatrix} = (\lambda-3)\lambda^2 = 0
$$
解得：$\lambda_1=\lambda_2=0,\ \lambda_3=3$。</li>
</ul>
<h5>情况5：行列式中有对应相等/相反数元素</h5>
<p>利用行列式性质消元，构造零元素，再展开求解。</p>
<ul>
<li>例：$A=\begin{pmatrix}-1&amp;1&amp;2\-2&amp;2&amp;2\-2&amp;1&amp;3\end{pmatrix}$，通过行变换消元后可得 $|\lambda E - A|=(\lambda-1)^2(\lambda-2)=0$，解得 $\lambda_1=\lambda_2=1,\ \lambda_3=2$。</li>
</ul>
<hr />
<h3>特征向量的求法</h3>
<p>求出特征值后，特征向量的求解就是求齐次线性方程组的非零解。</p>
<p>推导过程：
$$
A\alpha = \lambda\alpha
$$
$$
\lambda\alpha - A\alpha = 0
$$
$$
(\lambda E - A)\alpha = 0,\ \alpha \text{ 为非零向量}
$$
即：<strong>求齐次线性方程组 $(\lambda E - A)x = 0$ 的非零解</strong>。</p>
<hr />
<h3>【例10】矩阵特征值与特征向量求解</h3>
<p>求矩阵
$$
A=\begin{pmatrix}
-1 &amp; 1 &amp; 0 \
-4 &amp; 3 &amp; 0 \
1 &amp; 0 &amp; 2
\end{pmatrix}
$$
的特征值与特征向量。</p>
<ol>
<li>
<p><strong>求特征值</strong>
计算特征行列式：
$$
|\lambda E - A| = \begin{vmatrix}
\lambda+1 &amp; -1 &amp; 0 \
4 &amp; \lambda-3 &amp; 0 \
-1 &amp; 0 &amp; \lambda-2
\end{vmatrix}
= (\lambda-2)(-1)^{3+3} \begin{vmatrix} \lambda+1 &amp; -1 \ 4 &amp; \lambda-3 \end{vmatrix}
= (\lambda-2)(\lambda-1)^2
$$
令 $|\lambda E - A|=0$，解得特征值：
$$
\lambda_1=2,\quad \lambda_2=\lambda_3=1
$$</p>
</li>
<li>
<p><strong>对特征值 $\lambda_1=2$ 求特征向量</strong>
构造并化简矩阵 $2E-A$：
$$
2E-A = \begin{pmatrix}
3 &amp; -1 &amp; 0 \
4 &amp; -1 &amp; 0 \
-1 &amp; 0 &amp; 0
\end{pmatrix}
\rightarrow
\begin{pmatrix}
0 &amp; -1 &amp; 0 \
0 &amp; 0 &amp; 0 \
1 &amp; 0 &amp; 0
\end{pmatrix}
\rightarrow
\begin{pmatrix}
1 &amp; 0 &amp; 0 \
0 &amp; 1 &amp; 0 \
0 &amp; 0 &amp; 0
\end{pmatrix}
$$
得同解方程组：
$$
\begin{cases}
x_1 = 0 \
x_2 = 0
\end{cases}
$$
取 $x_3=1$，得基础解系：
$$
c_1\begin{pmatrix} 0 \ 0 \ 1 \end{pmatrix},\ c_1\neq 0
$$</p>
</li>
<li>
<p><strong>对特征值 $\lambda_2=\lambda_3=1$ 求特征向量</strong>
构造并化简矩阵 $E-A$：
$$
E-A = \begin{pmatrix}
2 &amp; -1 &amp; 0 \
4 &amp; -2 &amp; 0 \
-1 &amp; 0 &amp; -1
\end{pmatrix}
\rightarrow
\begin{pmatrix}
2 &amp; -1 &amp; 0 \
0 &amp; 0 &amp; 0 \
-1 &amp; 0 &amp; -1
\end{pmatrix}
\rightarrow
\begin{pmatrix}
1 &amp; 0 &amp; 1 \
0 &amp; 1 &amp; 2 \
0 &amp; 0 &amp; 0
\end{pmatrix}
$$
得同解方程组：
$$
\begin{cases}
x_1 = -x_3 \
x_2 = -2x_3
\end{cases}
$$
取 $x_3=1$，得基础解系：
$$
c_2\begin{pmatrix} -1 \ -2 \ 1 \end{pmatrix},\ c_2\neq 0
$$</p>
</li>
</ol>
<hr />
<h2>5.3 特征值与特征向量的性质</h2>
<h2>特征值与特征向量全套性质整理</h2>
<h3>性质1</h3>
<p>$n$阶矩阵$A$在复数域内必有$n$个特征值（重根按重数计算）。</p>
<blockquote>
<p>备注：该知识点一般仅作了解，通常不考核。</p>
</blockquote>
<h3>性质2</h3>
<p>$n$阶方阵$A$与其转置矩阵$A^\mathrm{T}$有相同的特征多项式，因此二者特征值完全相同；
⚠️ 注意：$A$和$A^\mathrm{T}$特征向量一般不相等。</p>
<h3>性质3（迹与行列式公式，高频考点）</h3>
<p>设$n$阶方阵$A$全部特征值为$\lambda_1,\lambda_2,\dots,\lambda_n$：</p>
<ol>
<li>特征值之和 = 矩阵主对角线元素之和（矩阵的迹）
$$\lambda_1+\lambda_2+\dots+\lambda_n = a_{11}+a_{22}+\dots+a_{nn}$$</li>
<li>特征值之积 = 方阵行列式
$$\lambda_1\lambda_2\cdots\lambda_n = |A|$$</li>
</ol>
<h3>性质4（可逆性等价判定）</h3>
<ol>
<li>$n$阶方阵$A$可逆 $\iff$ $A$所有特征值都不为$0$；</li>
<li>$n$阶方阵$A$不可逆 $\iff$ $A$至少有一个特征值为$0$。</li>
</ol>
<h3>性质5</h3>
<p>方阵$A$对应<strong>互不相同特征值</strong>的特征向量线性无关。</p>
<h3>性质6</h3>
<p>取自不同特征值、各自内部线性无关的特征向量，合并之后整体仍线性无关。</p>
<h3>性质7（重特征向量个数约束）</h3>
<ol>
<li>若$\lambda$是$A$的$k$重特征值，则对应于$\lambda$的线性无关特征向量数量$\le k$；</li>
<li>单特征值（一重特征值）有且仅有1个线性无关的特征向量。</li>
</ol>
<h3>性质8（矩阵变换后特征值对照表，必考）</h3>
<p>设$\lambda$是$A$的特征值，$\boldsymbol{\alpha}$是对应特征向量：</p>
<table>
<thead>
<tr>
<th>矩阵</th>
<th>$A$</th>
<th>$A^m$</th>
<th>$kA$</th>
<th>$A+E$</th>
<th>$f(A)$多项式矩阵</th>
<th>$A^2+2A+3E$</th>
<th>$A^{-1}$</th>
<th>$A^*$伴随矩阵</th>
</tr>
</thead>
<tbody>
<tr>
<td>对应特征值</td>
<td>$\lambda$</td>
<td>$\lambda^m$</td>
<td>$k\lambda$</td>
<td>$\lambda+1$</td>
<td>$f(\lambda)$</td>
<td>$\lambda^2+2\lambda+3$</td>
<td>$\dfrac{1}{\lambda}$</td>
<td>$\dfrac{</td>
</tr>
<tr>
<td>特征向量</td>
<td>$\boldsymbol{\alpha}$</td>
<td>$\boldsymbol{\alpha}$</td>
<td>$\boldsymbol{\alpha}$</td>
<td>$\boldsymbol{\alpha}$</td>
<td>$\boldsymbol{\alpha}$</td>
<td>$\boldsymbol{\alpha}$</td>
<td>$\boldsymbol{\alpha}$</td>
<td>$\boldsymbol{\alpha}$</td>
</tr>
</tbody>
</table>
<h4>配套例题</h4>
<p>已知$8$是$A$的特征值，$|A|=15$：</p>
<ul>
<li>$2A$特征值：$2\times8$</li>
<li>$-14A$特征值：$-14\times8$</li>
<li>$A^3$特征值：$8^3$</li>
<li>$3A^2$特征值：$3\times8^2$</li>
<li>$A+E$特征值：$8+1$</li>
<li>$2A^2+A+3E$特征值：$2\times8^2+8+3$</li>
<li>$A^4+A^3+2A^2+5A-6E$特征值：$8^4+8^3+2\times8^2+5\times8-6$</li>
<li>$A^{-1}$特征值：$\dfrac{1}{8}$</li>
<li>$A^*$特征值：$\dfrac{15}{8}$</li>
</ul>
<h3>推论</h3>
<p>若$\lambda$是方阵$A$的特征值，$f(x)$为多项式，且$f(A)=\boldsymbol{O}$（零矩阵），则必有$f(\lambda)=0$。</p>
<h3>性质9（秩1矩阵专用结论）</h3>
<p>$n$阶方阵$A=(a_{ij})$，若$r(A)=1$：</p>
<ul>
<li>一个特征值：$\lambda_1=\displaystyle\sum_{i=1}^n a_{ii}$（矩阵的迹）</li>
<li>剩余$n-1$个特征值：$\lambda_2=\lambda_3=\dots=\lambda_n=0$</li>
</ul>
<h2>5.4 相似矩阵的概念与性质</h2>
<h3>一、相似矩阵定义（定义5.2）</h3>
<p>设 $A,B$ 均为 $n$ 阶方阵，若存在可逆矩阵 $P$，满足
$$P^{-1}AP=B$$
则称矩阵 $A$ 与 $B$ <strong>相似</strong>，记作 $A\sim B$，$B$ 是 $A$ 的相似矩阵。</p>
<p>特殊定义：若方阵 $A$ 能和对角矩阵相似，则称 $A$ <strong>可对角化</strong>。</p>
<hr />
<h3>二、相似矩阵整体性质</h3>
<p><strong>性质1：相似是等价关系</strong>
等价关系满足三条公理：</p>
<ol>
<li><strong>反身性</strong>：$A\sim A$</li>
<li><strong>对称性</strong>：若 $A\sim B$，则 $B\sim A$</li>
<li><strong>传递性</strong>：若 $A\sim B,\ B\sim C$，则 $A\sim C$</li>
</ol>
<p>推论：$A\sim B \implies A$ 与 $B$ 等价（$A\cong B$）。</p>
<p><strong>性质2：秩相等</strong>
若 $A\sim B$，则 $r(A)=r(B)$。
依据：相似矩阵必等价，等价矩阵秩相同。</p>
<p><strong>性质3：特征多项式、特征值完全相同</strong>
若 $A\sim B$，则 $|\lambda E-A|=|\lambda E-B|$，二者特征值一致。</p>
<p><strong>推论</strong></p>
<blockquote>
<p>若 $A$ 相似于对角矩阵，则 $A$ 的全部特征值就是该对角矩阵主对角线元素。</p>
</blockquote>
<p><strong>性质4：行列式、迹相等</strong>
若 $A\sim B$，则：
$$|A|=|B|,\quad \mathrm{tr}(A)=\mathrm{tr}(B)$$</p>
<p><strong>性质5：可逆性同步</strong></p>
<ol>
<li>$|A|,|B|$ 同时为0，或同时非0；</li>
<li>$A$ 可逆 $\iff B$ 可逆；</li>
<li>$A$ 不可逆 $\iff B$ 不可逆。
简言之：相似矩阵要么同时可逆，要么同时不可逆。</li>
</ol>
<p><strong>性质6：逆矩阵、伴随矩阵也相似（$A,B$ 均可逆时）</strong></p>
<ol>
<li>$A^{-1}\sim B^{-1}$</li>
<li>$A^{<em>}\sim B^{</em>}$</li>
</ol>
<p><strong>性质7：幂矩阵相似</strong>
若 $A\sim B$，$m$ 为正整数，则 $A^m\sim B^m$。</p>
<p><strong>性质8：转置矩阵相似</strong>
若 $A\sim B$，则 $A^\mathrm{T}\sim B^\mathrm{T}$。</p>
<p><strong>性质9：相似矩阵的性质</strong>
若 $A\sim B$，$k$ 为常数，$f(x)$ 为一元多项式，则：
$$kA\sim kB,\quad A+kE\sim B+kE,\quad f(A)\sim f(B)$$</p>
<hr />
<h3>$A\sim B$ 完整推论导图</h3>
<p>$$
A\sim B\Rightarrow
\begin{cases}
A\cong B\Rightarrow r(A)=r(B)\
A、B\text{ 特征值相同}
\begin{cases}
|A|=|B|\
\mathrm{tr}(A)=\mathrm{tr}(B)
\end{cases}\
A、B\text{ 可逆性完全一致}\
A^{-1}\sim B^{-1},\ A^<em>\sim B^</em>\
A^\mathrm{T}\sim B^\mathrm{T}\
kA\sim kB\
f(A)\sim f(B)\
A^m\sim B^m\ (m\text{ 正整数})
\end{cases}
$$</p>
<h2>5.5 矩阵的对角化</h2>
<h3>一、可对角化三大核心问题</h3>
<ol>
<li>如何判断方阵 $A$ 是否能相似对角化？</li>
<li>若可对角化，可逆变换矩阵 $P$ 如何求解？</li>
<li>对角矩阵 $\Lambda$ 如何求解？</li>
</ol>
<hr />
<h3>二、可对角化判定定理</h3>
<p><strong>定理5.1（充要核心定理）</strong>
$n$ 阶方阵 $A$ <strong>可对角化</strong> $\iff A$ 拥有 $n$ 个线性无关的特征向量。</p>
<p><strong>定理5.2（充分条件，常用快速判定）</strong>
若 $n$ 阶方阵 $A$ 有 $n$ 个<strong>互不相同</strong>的特征值，则 $A$ 一定可对角化。</p>
<blockquote>
<p>注意：该条件只是充分非必要，有重特征值也有可能可对角化。</p>
</blockquote>
<p><strong>定理5.3（重特征值判定充要条件）</strong>
$n$ 阶方阵 $A$ 可对角化 $\iff$ 每一个 $s$ 重特征值 $\lambda$，对应的线性无关特征向量个数恰好等于重数 $s$，等价表述：</p>
<ol>
<li>齐次方程组 $(\lambda E-A)\boldsymbol{x}=\boldsymbol 0$ 的基础解系含 $s$ 个向量；</li>
<li>$n-r(\lambda E-A)=s$；</li>
<li>$r(\lambda E-A)=n-s$。</li>
</ol>
<hr />
<h3>三、$P$ 与对角矩阵 $\Lambda$ 求解公式</h3>
<p>设 $A\boldsymbol\alpha_i=\lambda_i\boldsymbol\alpha_i\quad(i=1,2,\dots,n)$，$\boldsymbol\alpha_1,\boldsymbol\alpha_2,\dots,\boldsymbol\alpha_n$ 线性无关：
$$
A(\boldsymbol\alpha_1,\boldsymbol\alpha_2,\dots,\boldsymbol\alpha_n)
=(\boldsymbol\alpha_1,\boldsymbol\alpha_2,\dots,\boldsymbol\alpha_n)
\begin{pmatrix}
\lambda_1 &amp; &amp; \
&amp; \lambda_2 &amp; \
&amp; &amp; \ddots &amp; \
&amp; &amp; &amp; \lambda_n
\end{pmatrix}
$$
记：
$$
P=(\boldsymbol\alpha_1,\boldsymbol\alpha_2,\dots,\boldsymbol\alpha_n),\quad
\Lambda=\mathrm{diag}(\lambda_1,\lambda_2,\dots,\lambda_n)
$$
则有 $P^{-1}AP=\Lambda$。</p>
<p><strong>关键配对规则</strong></p>
<blockquote>
<ul>
<li>$P$ 的第 $i$ 列 = 特征值 $\lambda_i$ 对应的特征向量 $\boldsymbol\alpha_i$；</li>
<li>$P$ 列向量顺序、$\Lambda$ 对角线特征值顺序必须一一对应，顺序可同步调换。</li>
</ul>
</blockquote>
<hr />
<h3>四、完整例题演示</h3>
<p><strong>题目</strong>
$$
A=\begin{pmatrix}
1 &amp; -2 &amp; 2 \
-2 &amp; -2 &amp; 4 \
2 &amp; 4 &amp; -2
\end{pmatrix}
$$
判断能否对角化；若可以，求 $P,\Lambda$ 满足 $P^{-1}AP=\Lambda$。</p>
<blockquote>
<p><strong>步骤1：求特征值</strong>
$$
|\lambda E-A|=(\lambda-2)^2(\lambda+7)=0
$$
特征值：$\lambda_1=\lambda_2=2$（二重），$\lambda_3=-7$（一重）。</p>
</blockquote>
<blockquote>
<p><strong>步骤2：判定可对角化</strong>
二重特征值 $\lambda=2$ 解方程组 $(2E-A)\boldsymbol x=\boldsymbol0$，得到2个线性无关特征向量；一重特征值天然对应1个线性无关特征向量，总计3个线性无关特征向量，$A$ 可对角化。</p>
</blockquote>
<blockquote>
<p><strong>步骤3：求特征向量</strong></p>
</blockquote>
<ul>
<li>$\lambda_1=\lambda_2=2$：$\boldsymbol\alpha_1=\begin{pmatrix}-2\1\0\end{pmatrix},\boldsymbol\alpha_2=\begin{pmatrix}2\0\1\end{pmatrix}$</li>
<li>$\lambda_3=-7$：$\boldsymbol\alpha_3=\begin{pmatrix}1\2\-2\end{pmatrix}$</li>
</ul>
<blockquote>
<p><strong>步骤4：构造 $P,\Lambda$</strong>
$$
P=(\boldsymbol\alpha_1,\boldsymbol\alpha_2,\boldsymbol\alpha_3)=
\begin{pmatrix}
-2 &amp; 2 &amp; 1 \
1 &amp; 0 &amp; 2 \
0 &amp; 1 &amp; -2
\end{pmatrix},\quad
\Lambda=
\begin{pmatrix}
2 &amp; &amp; \
&amp; 2 &amp; \
&amp; &amp; -7
\end{pmatrix}
$$
满足 $P^{-1}AP=\Lambda$。</p>
</blockquote>
<h2>5.6 内积与正交</h2>
<h3>一、向量内积（数量积）四条基本性质</h3>
<p>设 $\boldsymbol \alpha,\boldsymbol \beta,\boldsymbol \alpha_1,\boldsymbol \alpha_2$ 为 $n$ 维向量，$k$ 为实数：</p>
<ol>
<li><strong>非负性</strong>：$(\boldsymbol\alpha,\boldsymbol\alpha)\ge 0$，且 $(\boldsymbol\alpha,\boldsymbol\alpha)=0 \iff \boldsymbol\alpha=\boldsymbol 0$
若 $\boldsymbol\alpha=(x_1,x_2,\dots,x_n)$，则 $(\boldsymbol\alpha,\boldsymbol\alpha)=x_1^2+x_2^2+\dots+x_n^2$。</li>
<li><strong>对称性</strong>：$(\boldsymbol\alpha,\boldsymbol\beta)=(\boldsymbol\beta,\boldsymbol\alpha)$</li>
<li><strong>齐次性</strong>：$(k\boldsymbol\alpha,\boldsymbol\beta)=k(\boldsymbol\alpha,\boldsymbol\beta)$</li>
<li><strong>线性可加性</strong>
$$
\begin{align*}
(\boldsymbol\alpha_1+\boldsymbol\alpha_2,\boldsymbol\beta)&amp;=(\boldsymbol\alpha_1,\boldsymbol\beta)+(\boldsymbol\alpha_2,\boldsymbol\beta)\
(\boldsymbol\alpha,\boldsymbol\beta_1+\boldsymbol\beta_2)&amp;=(\boldsymbol\alpha,\boldsymbol\beta_1)+(\boldsymbol\alpha,\boldsymbol\beta_2)
\end{align*}
$$</li>
</ol>
<hr />
<h3>二、向量长度（范数/模）</h3>
<h4>1. 定义</h4>
<p>$$
|\boldsymbol\alpha|=\sqrt{(\boldsymbol\alpha,\boldsymbol\alpha)}=\sqrt{x_1^2+x_2^2+\dots+x_n^2}
$$
满足 $(\boldsymbol\alpha,\boldsymbol\alpha)=|\boldsymbol\alpha|^2$。</p>
<h3>2. 单位向量与单位化</h3>
<ul>
<li><strong>单位向量</strong>：长度 $|\boldsymbol\alpha|=1$ 的向量；</li>
<li><strong>单位化公式</strong>：非零向量 $\boldsymbol\alpha$，令
$$
\boldsymbol\beta=\frac{1}{|\boldsymbol\alpha|}\boldsymbol\alpha
$$
$\boldsymbol\beta$ 即为单位向量，该变换过程称为<strong>单位化</strong>。</li>
</ul>
<hr />
<h3>三、正交相关定义</h3>
<p><strong>定义5.6 向量正交</strong>
若 $(\boldsymbol\alpha,\boldsymbol\beta)=0$，称 $\boldsymbol\alpha$ 与 $\boldsymbol\beta$ <strong>正交</strong>，记作 $\boldsymbol\alpha\perp\boldsymbol\beta$。
备注：</p>
<ol>
<li>零向量和任意向量都正交；</li>
<li>只有零向量和自身正交。</li>
</ol>
<p><strong>定义5.7 正交向量组</strong>
两两正交的<strong>非零向量</strong>构成的向量组；单个非零向量也属于正交向量组。</p>
<p><strong>定义5.8 标准正交（单位正交）向量组</strong>
每个向量都是单位向量的正交向量组，满足：
$$
(\boldsymbol\alpha_i,\boldsymbol\alpha_j)=
\begin{cases}
1,&amp;i=j\
0,&amp;i\ne j
\end{cases}
$$</p>
<p><strong>定理5.4</strong>
<strong>正交向量组一定线性无关</strong>；线性无关向量组不一定正交。</p>
<hr />
<h3>四、施密特正交化（线性无关组 → 等价正交组）</h3>
<h4>1. 正交化公式</h4>
<p>已知线性无关向量组 $\boldsymbol\alpha_1,\boldsymbol\alpha_2,\dots,\boldsymbol\alpha_s$：
$$
\begin{align*}
\boldsymbol\beta_1&amp;=\boldsymbol\alpha_1\
\boldsymbol\beta_2&amp;=\boldsymbol\alpha_2-\frac{(\boldsymbol\alpha_2,\boldsymbol\beta_1)}{(\boldsymbol\beta_1,\boldsymbol\beta_1)}\boldsymbol\beta_1\
\boldsymbol\beta_3&amp;=\boldsymbol\alpha_3-\frac{(\boldsymbol\alpha_3,\boldsymbol\beta_1)}{(\boldsymbol\beta_1,\boldsymbol\beta_1)}\boldsymbol\beta_1-\frac{(\boldsymbol\alpha_3,\boldsymbol\beta_2)}{(\boldsymbol\beta_2,\boldsymbol\beta_2)}\boldsymbol\beta_2
\end{align*}
$$
得到的 $\boldsymbol\beta_1,\boldsymbol\beta_2,\boldsymbol\beta_3$ 两两正交，且与原向量组等价。</p>
<h4>2. 再单位化得到标准正交组</h4>
<p>$$
\boldsymbol\gamma_1=\frac{\boldsymbol\beta_1}{|\boldsymbol\beta_1|},\quad
\boldsymbol\gamma_2=\frac{\boldsymbol\beta_2}{|\boldsymbol\beta_2|},\quad
\boldsymbol\gamma_3=\frac{\boldsymbol\beta_3}{|\boldsymbol\beta_3|}
$$
$\boldsymbol\gamma_1,\boldsymbol\gamma_2,\boldsymbol\gamma_3$ 就是和原向量组等价的<strong>标准正交向量组</strong>。</p>
<h2>5.7 实对称矩阵的相似对角化</h2>
<h3>一、正交相似定义（定义5.10）</h3>
<p>设 $A,B$ 为 $n$ 阶方阵，若存在<strong>正交矩阵 $Q$</strong>，满足：
$$Q^{-1}AQ=B$$
则称 $A$ 与 $B$ <strong>正交相似</strong>。
补充：正交矩阵满足 $Q^{-1}=Q^\mathrm{T}$，因此上式等价于 $Q^\mathrm{T}AQ=B$。</p>
<hr />
<h3>二、实对称矩阵核心7条性质</h3>
<p>实对称矩阵满足 $\boldsymbol A^\mathrm{T}=A$，全部性质罗列如下：
<strong>性质1</strong>
实对称矩阵所有特征值都是<strong>实数</strong>，对应的特征向量全为实向量。</p>
<p><strong>性质2</strong>
属于<strong>不同特征值</strong>的特征向量互相正交。</p>
<blockquote>
<p>一般方阵仅能保证线性无关，实对称矩阵可加强为正交。</p>
</blockquote>
<p><strong>性质3</strong>
对 $s$ 重特征值 $\lambda$，恒有 $r(\lambda E-A)=n-s$。</p>
<p><strong>性质4</strong>
$s$ 重特征值恰好对应**$s$ 个线性无关**的特征向量。</p>
<p><strong>性质5</strong>
$n$ 阶实对称矩阵一定拥有 $n$ 个线性无关的特征向量。</p>
<p><strong>性质6</strong>
<strong>实对称矩阵必定可以相似对角化</strong>（无条件可对角化）。</p>
<p><strong>性质7</strong>
实对称矩阵一定能和对角矩阵<strong>正交相似</strong>；
即存在正交矩阵 $Q$，使得
$$Q^{-1}AQ=Q^\mathrm{T}AQ=\Lambda=\mathrm{diag}(\lambda_1,\lambda_2,\dots,\lambda_n)$$
$\lambda_1,\lambda_2,\dots,\lambda_n$ 是 $A$ 的全部特征值。</p>
<hr />
<h3>三、实对称矩阵两种对角化完整步骤</h3>
<h4>前置通用两步（两种方式共用）</h4>
<ol>
<li>求特征值：解特征方程 $|\lambda E-A|=0$，得到全部特征值 $\lambda_1,\lambda_2,\dots,\lambda_n$；</li>
<li>求特征向量：对每个特征值 $\lambda_i$，解 $(\lambda_i E-A)\boldsymbol x=\boldsymbol 0$，得到 $n$ 个线性无关特征向量 $\boldsymbol\alpha_1,\boldsymbol\alpha_2,\dots,\boldsymbol\alpha_n$。</li>
</ol>
<h4>方式1：普通相似对角化（构造可逆矩阵 $P$）</h4>
<ol>
<li>直接拼接特征向量构成可逆矩阵：$P=(\boldsymbol\alpha_1,\boldsymbol\alpha_2,\dots,\boldsymbol\alpha_n)$</li>
<li>结果：$P^{-1}AP=\Lambda$，$A\sim\Lambda$</li>
</ol>
<blockquote>
<p>适用：题目只要求相似对角化，不要求正交矩阵。</p>
</blockquote>
<h4>方式2：正交相似对角化（构造正交矩阵 $Q$，必考）</h4>
<ol>
<li><strong>正交化处理</strong>
<ul>
<li>不同特征值对应的特征向量天然正交，无需处理；</li>
<li>同一重特征值内部多个线性无关特征向量，用<strong>施密特正交化</strong>得到正交向量组 $\boldsymbol\beta_1,\boldsymbol\beta_2,\dots,\boldsymbol\beta_n$。</li>
</ul>
</li>
<li><strong>单位化</strong>
$$\boldsymbol\gamma_i=\frac{1}{|\boldsymbol\beta_i|}\boldsymbol\beta_i,\quad i=1,2,\dots,n$$</li>
<li>拼接正交矩阵：$Q=(\boldsymbol\gamma_1,\boldsymbol\gamma_2,\dots,\boldsymbol\gamma_n)$</li>
<li>结果：
$$Q^{-1}AQ=Q^\mathrm{T}AQ=\Lambda$$
$A$ 与对角阵正交相似。</li>
</ol>
<blockquote>
<p>适用：题目明确要求正交矩阵、二次型标准化。</p>
</blockquote>
<hr />
<h3>四、实对称矩阵正交相似对角化分三类情形完整操作</h3>
<h4>实对称矩阵正交相似对角化分三类情形完整操作</h4>
<p>设三阶实对称矩阵 $\boldsymbol A$，要构造正交矩阵 $\boldsymbol Q$ 使得 $\boldsymbol Q^\mathrm{T}\boldsymbol A\boldsymbol Q=\boldsymbol \Lambda$，按特征值重数分3种情况处理：</p>
<h4>情形一：$\lambda_1,\lambda_2,\lambda_3$ 三个互不相同单特征值</h4>
<p>实对称矩阵<strong>不同特征值对应的特征向量天然正交</strong>，无需施密特正交化，只做单位化：</p>
<ol>
<li>对每个特征值求出特征向量 $\boldsymbol\alpha_1,\boldsymbol\alpha_2,\boldsymbol\alpha_3$；</li>
<li>分别单位化：
$$\boldsymbol\gamma_1=\frac{\boldsymbol\alpha_1}{|\boldsymbol\alpha_1|},\quad
\boldsymbol\gamma_2=\frac{\boldsymbol\alpha_2}{|\boldsymbol\alpha_2|},\quad
\boldsymbol\gamma_3=\frac{\boldsymbol\alpha_3}{|\boldsymbol\alpha_3|}$$</li>
<li>正交矩阵 $\boldsymbol Q=(\boldsymbol\gamma_1,\boldsymbol\gamma_2,\boldsymbol\gamma_3)$。</li>
</ol>
<hr />
<h4>情形二：$\lambda_1$ 单根，$\lambda_2=\lambda_3$ 二重特征值</h4>
<ol>
<li>单特征值 $\lambda_1$：特征向量 $\boldsymbol\alpha_1$ 直接单位化得到 $\boldsymbol\gamma_1$；</li>
<li>二重特征值 $\lambda_2=\lambda_3$：解方程组得到2个线性无关特征向量 $\boldsymbol\alpha_2,\boldsymbol\alpha_3$
<ul>
<li>这两个同特征值的向量<strong>不一定正交</strong>，必须先做<strong>施密特正交化</strong>得到正交向量 $\boldsymbol\beta_2,\boldsymbol\beta_3$；</li>
<li>再分别单位化得到 $\boldsymbol\gamma_2,\boldsymbol\gamma_3$；</li>
</ul>
</li>
<li>拼接正交矩阵 $\boldsymbol Q=(\boldsymbol\gamma_1,\boldsymbol\gamma_2,\boldsymbol\gamma_3)$。</li>
</ol>
<hr />
<h4>情形三：$\lambda_1=\lambda_2=\lambda_3$ 三重特征值</h4>
<p>3个线性无关特征向量 $\boldsymbol\alpha_1,\boldsymbol\alpha_2,\boldsymbol\alpha_3$ 全部属于同一个重特征值，互相不一定正交：</p>
<ol>
<li>先对 $\boldsymbol\alpha_1,\boldsymbol\alpha_2,\boldsymbol\alpha_3$ 整体做<strong>施密特正交化</strong>，得到两两正交的 $\boldsymbol\beta_1,\boldsymbol\beta_2,\boldsymbol\beta_3$；</li>
<li>再逐个单位化得到 $\boldsymbol\gamma_1,\boldsymbol\gamma_2,\boldsymbol\gamma_3$；</li>
<li>拼接得到正交矩阵 $\boldsymbol Q=(\boldsymbol\gamma_1,\boldsymbol\gamma_2,\boldsymbol\gamma_3)$。</li>
</ol>
<hr />
<h2>核心规则总结</h2>
<ol>
<li><strong>跨不同特征值</strong>：特征向量自动正交，跳过正交化，仅单位化；</li>
<li><strong>同一多重特征值内部</strong>：线性无关特征向量不一定正交，<strong>必须施密特正交化 + 单位化两步</strong>；</li>
<li>最终全部单位化后的列向量拼成矩阵，一定是正交矩阵，满足 $\boldsymbol Q^{-1}=\boldsymbol Q^\mathrm{T}$。</li>
</ol>
<h3>补充关键对比</h3>
<table>
<thead>
<tr>
<th>矩阵类型</th>
<th>能否对角化</th>
<th>不同特征值特征向量关系</th>
<th>能否正交相似对角化</th>
</tr>
</thead>
<tbody>
<tr>
<td>普通 $n$ 阶方阵</td>
<td>不一定，要看无关特征向量个数</td>
<td>仅线性无关</td>
<td>不一定</td>
</tr>
<tr>
<td>实对称矩阵</td>
<td>一定可以</td>
<td>互相正交</td>
<td>一定可以</td>
</tr>
</tbody>
</table>
<hr />
<h1>第六部分：二次型</h1>
<hr />
<h2>6.1 二次型及矩阵表示</h2>
<h3>一、二次型定义（定义6.1）</h3>
<h4>1. 文字定义</h4>
<p>$n$ 个变量 $x_1,x_2,\dots,x_n$ 的<strong>二次齐次多项式</strong>称为 $n$ 元二次型，简称二次型；系数全为实数时，称作<strong>实二次型</strong>，本课程只讨论实二次型。
展开形式：
$$
\begin{align*}
f(x_1,x_2,\dots,x_n)
=&amp;a_{11}x_1^2+2a_{12}x_1x_2+2a_{13}x_1x_3+\dots+2a_{1n}x_1x_n\
&amp;+a_{22}x_2^2+2a_{23}x_2x_3+\dots+2a_{2n}x_2x_n\
&amp;+\dots+a_{nn}x_n^2
\end{align*}
$$</p>
<h4>2. 判断规则</h4>
<ul>
<li>只允许二次项（平方项 $x_i^2$、交叉项 $x_ix_j,i\ne j$）；</li>
<li>不能出现一次项、常数项、三次及更高次项。</li>
</ul>
<p>例题判断结果：</p>
<ol>
<li>$f=2x_1^2-x_2^2+4x_3^2+5x_1x_2-x_1x_3+3x_2x_3$：✅ 是二次型</li>
<li>$f=x_1x_2-x_1x_3+3x_2x_3$：✅ 是二次型（无平方项仅有交叉项也成立）</li>
<li>$f=x_1^2-2x_2^2+4x_3^2$：✅ 是二次型（纯平方项，标准型）</li>
<li>$f=-x_1^2+x_2^2+x_1x_2-x_1x_3+\boldsymbol3$：❌ 含常数项，不是</li>
<li>$f=x^2-xy+\boldsymbol y$：❌ 含一次项，不是</li>
</ol>
<hr />
<h3>二、二次型矩阵表达式（核心）</h3>
<h4>1. 矩阵标准写法</h4>
<p>记
$$
\boldsymbol x=\begin{pmatrix}x_1\x_2\\vdots\x_n\end{pmatrix},\quad
A=(a_{ij}),\ A^\mathrm{T}=A
$$
二次型唯一对称矩阵形式：
$$
f(x_1,x_2,\dots,x_n)=\boldsymbol x^\mathrm{T}A\boldsymbol x
$$
<strong>规定：二次型的矩阵 $A$ 必须是实对称矩阵</strong>。</p>
<hr />
<h3>三、二次型标准形（定义6.3）</h3>
<h4>1. 定义</h4>
<p>二次型只含平方项、不含任何交叉项 $x_ix_j(i\ne j)$：
$$
f=d_1x_1^2+d_2x_2^2+\dots+d_nx_n^2
$$
称作二次型的一个<strong>标准形</strong>；$d_i$ 可为任意实数（包含0）。</p>
<h4>2. 由二次型写出对称矩阵 $A$ 口诀</h4>
<ol>
<li>平方项 $x_i^2$ 系数直接放到主对角线 $a_{ii}$；</li>
<li>交叉项 $2a,x_ix_j$，系数除以 $2$，分别填入 $a_{ij}$、$a_{ji}$，保证对称。</li>
</ol>
<p>示例：
$$
f(x_1,x_2,x_3)=x_1^2+2x_1x_2+3x_2^2-8x_2x_3+x_3^2
$$
$$
A=\begin{pmatrix}
1 &amp; 1 &amp; 0\
1 &amp; 3 &amp; -4\
0 &amp; -4 &amp; 1
\end{pmatrix}
$$</p>
<h4>3. 由对称矩阵还原二次型口诀</h4>
<ol>
<li>主对角线元素 $a_{ii}$ 为 $x_i^2$ 系数；</li>
<li>上三角元素 $a_{ij}$ 乘以 $2$，作为 $x_ix_j$ 交叉项系数。</li>
</ol>
<p>示例：
$$
A=\begin{pmatrix}3&amp;1&amp;0\1&amp;-1&amp;-2\0&amp;-2&amp;5\end{pmatrix}
$$
$$
f=3x_1^2-x_2^2+5x_3^2+2x_1x_2-4x_2x_3
$$</p>
<h4>4. 二次型的秩</h4>
<p>二次型的秩 = 它的对称矩阵 $A$ 的秩：
$$
r(f)=r(A)
$$</p>
<blockquote>
<p>非对称矩阵形式必须先对称化，再求秩。</p>
</blockquote>
<hr />
<h3>四、二次型规范形（定义6.4）</h3>
<h4>1. 定义</h4>
<p>标准形的平方项系数只能取 $\boldsymbol 1,-1,0$ 时，称为二次型的<strong>规范形</strong>，标准排布形式：
$$
f=z_1^2+\dots+z_p^2-z_{p+1}^2-\dots-z_r^2
$$
其中 $r=r(A)$ 是二次型的秩，$p\le r\le n$。</p>
<h4>2. 对应矩阵结构</h4>
<p>规范形的矩阵是对角阵，对角元依次为 $p$ 个$1$、$(r-p)$个$-1$、$(n-r)$个$0$，分块写法：
$$
\Lambda=\begin{pmatrix}
E_p &amp; &amp; \
&amp; -E_{r-p} &amp; \
&amp; &amp; O_{n-r}
\end{pmatrix}
$$</p>
<h4>3. 关键结论</h4>
<p><strong>惯性定理</strong>：任意实二次型的规范形<strong>唯一</strong>，与可逆线性变换的选取无关。</p>
<hr />
<h3>惯性指数与符号差（定义6.5）</h3>
<p>设二次型秩为 $r$</p>
<ol>
<li><strong>正惯性指数 $p$</strong>：标准形/规范形里，系数为正数的平方项个数；</li>
<li><strong>负惯性指数 $q$</strong>：标准形/规范形里，系数为负数的平方项个数；</li>
<li><strong>符号差</strong>：$p-q$；</li>
<li>核心等式：$\boldsymbol r=p+q$。</li>
</ol>
<hr />
<h3>线性变换（定义6.6）</h3>
<h4>1. 矩阵表达</h4>
<p>两组变量 $\boldsymbol x=(x_1,\dots,x_n)^\mathrm T,\boldsymbol y=(y_1,\dots,y_n)^\mathrm T$，变换记作：
$$
\boldsymbol x=C\boldsymbol y
$$
$C=(c_{ij})$ 是 $n$ 阶变换矩阵。</p>
<h4>2. 两类重要线性变换</h4>
<ol>
<li><strong>可逆线性变换</strong>
$|C|\ne 0$（$C$ 满秩、非退化、非奇异），逆变换：$\boldsymbol y=C^{-1}\boldsymbol x$。</li>
<li><strong>正交变换</strong>
若 $C$ 是正交矩阵（$C^{-1}=C^\mathrm T$），则 $\boldsymbol x=C\boldsymbol y$ 为正交线性变换。</li>
</ol>
<h4>3. 变换复合定理（定理6.1）</h4>
<p>多个线性变换连续复合后的总矩阵 = 各变换矩阵依次相乘；
多个可逆线性变换复合后，仍然是可逆线性变换。</p>
<hr />
<h3>可逆线性变换作用于二次型（定理6.2）</h3>
<ol>
<li>二次型 $\boldsymbol x^\mathrm T A\boldsymbol x$ 做可逆变换 $\boldsymbol x=C\boldsymbol y$：
$$
\boldsymbol x^\mathrm T A\boldsymbol x
=(C\boldsymbol y)^\mathrm T A(C\boldsymbol y)
=\boldsymbol y^\mathrm T (C^\mathrm T A C)\boldsymbol y
=\boldsymbol y^\mathrm T B\boldsymbol y
$$
其中 $B=C^\mathrm T A C$。</li>
<li>结论：
<ul>
<li>变换后依旧是二次型；</li>
<li>新矩阵 $B$ 与原矩阵 $A$ <strong>合同</strong>；</li>
<li>秩不变：$r(B)=r(A)$，即二次型秩在可逆变换下恒定。</li>
</ul>
</li>
</ol>
<hr />
<h3>标准形、规范形、惯性指数关系梳理</h3>
<table>
<thead>
<tr>
<th>形式</th>
<th>系数取值</th>
<th>是否唯一</th>
<th>核心标识量</th>
</tr>
</thead>
<tbody>
<tr>
<td>标准形</td>
<td>任意实数</td>
<td>不唯一</td>
<td>正、负惯性指数唯一不变</td>
</tr>
<tr>
<td>规范形</td>
<td>仅 $1,-1,0$</td>
<td><strong>唯一</strong></td>
<td>$p,q,r$ 全部固定不变</td>
</tr>
</tbody>
</table>
<hr />
<h3>合同矩阵定义（定义6.8）</h3>
<p>设 $A,B$ 为 $n$ 阶方阵，若存在<strong>可逆矩阵 $C$</strong>，满足：
$$C^\mathrm{T}AC=B$$
则称矩阵 $A$ 与 $B$ <strong>合同</strong>，记作 $A\simeq B$；该变换称作合同变换，$C$ 是合同变换矩阵。</p>
<h4>相似、正交相似、合同公式对比</h4>
<table>
<thead>
<tr>
<th>关系</th>
<th>核心等式</th>
<th>变换矩阵要求</th>
</tr>
</thead>
<tbody>
<tr>
<td>相似</td>
<td>$P^{-1}AP=B$</td>
<td>$P$ 可逆</td>
</tr>
<tr>
<td>正交相似</td>
<td>$P^{-1}AP=P^\mathrm{T}AP=B$</td>
<td>$P$ 正交矩阵</td>
</tr>
<tr>
<td>合同</td>
<td>$P^\mathrm{T}AP=B$</td>
<td>$P$ 可逆</td>
</tr>
</tbody>
</table>
<hr />
<h3>合同关系三大等价性质（等价关系）</h3>
<ol>
<li><strong>反身性</strong>：任意方阵 $A$，$A\simeq A$；</li>
<li><strong>对称性</strong>：若 $A\simeq B$，则 $B\simeq A$；</li>
<li><strong>传递性</strong>：若 $A\simeq B,\ B\simeq C$，则 $A\simeq C$。</li>
</ol>
<hr />
<h3>合同矩阵重要推论</h3>
<ol>
<li><strong>秩相等</strong>：$A\simeq B \implies r(A)=r(B)$，同时 $A,B$ 等价；</li>
<li><strong>对称性同步</strong>：$A$ 是对称矩阵 $\iff B$ 是对称矩阵；</li>
<li><strong>可逆性一致</strong>：$A,B$ 同时可逆或同时不可逆；若均可逆，则 $A^{-1}\simeq B^{-1}$；</li>
<li><strong>转置也合同</strong>：$A\simeq B \implies A^\mathrm{T}\simeq B^\mathrm{T}$。</li>
</ol>
<hr />
<h3>二次型视角下的合同结论</h3>
<h4>结论1（普通可逆线性变换）</h4>
<p>二次型 $f=\boldsymbol x^\mathrm{T}A\boldsymbol x$，做可逆变换 $\boldsymbol x=C\boldsymbol y$，得到 $f=\boldsymbol y^\mathrm{T}(C^\mathrm{T}AC)\boldsymbol y=\boldsymbol y^\mathrm{T}B\boldsymbol y$，则 $A\simeq B$。</p>
<h4>结论2（正交变换特殊情况）</h4>
<p>正交变换 $\boldsymbol x=Q\boldsymbol y$（$Q$ 正交矩阵，$Q^\mathrm{T}=Q^{-1}$），此时
$$B=Q^\mathrm{T}AQ=Q^{-1}AQ$$
$A$ 与 $B$ <strong>既相似，又合同</strong>。</p>
<hr />
<h2>6.2 化二次型为标准形</h2>
<h3>方法1：配方法（可逆线性变换，标准形不唯一）</h3>
<h4>分类操作步骤</h4>
<ol>
<li>
<p><strong>含平方项（第一题例题）</strong>
优先对出现平方项的变量配方，逐步消去交叉项：
$$f(x_1,x_2,x_3)=x_1^2-3x_2^2+4x_3^2-2x_1x_2+2x_1x_3-6x_2x_3$$
先把含 $x_1$ 的项全部收拢配方，再依次对 $x_2$ 配方，最终化成纯平方项：
$$f=y_1^2-y_2^2+4y_3^2$$
同时写出可逆线性变换 $\boldsymbol x=C\boldsymbol y$，$C$ 为可逆变换矩阵。</p>
</li>
<li>
<p><strong>无平方项（只有交叉项，第二题例题）</strong>
先做可逆变换构造出平方项：
$$\begin{cases}x_1=y_1+y_2\x_2=y_1-y_2\x_3=y_3\end{cases}$$
代入后产生 $y_1^2,y_2^2$，再继续常规配方，最终得到标准形。</p>
</li>
</ol>
<h4>配套定理6.3</h4>
<p>任意 $n$ 元二次型，都能通过<strong>可逆线性变换</strong>化为标准形：
$$f=d_1y_1^2+d_2y_2^2+\dots+d_ny_n^2$$
⚠️ 配方法得到的标准形系数 $d_i$ 不唯一，但<strong>正、负惯性指数固定不变</strong>。</p>
<hr />
<h3>方法2：正交变换法（正交相似，标准形唯一）</h3>
<h4>核心原理</h4>
<p>正交变换化二次型 = 实对称矩阵正交相似对角化。
正交矩阵 $Q$ 满足 $Q^\mathrm T=Q^{-1}$，因此 $Q^\mathrm TAQ=Q^{-1}AQ=\Lambda$，$A$ 和对角阵<strong>既相似又合同</strong>。</p>
<h4>定理6.5</h4>
<p>实二次型 $f=\boldsymbol x^\mathrm TA\boldsymbol x$（$A^\mathrm T=A$），正交变换 $\boldsymbol x=Q\boldsymbol y$ 可化为：
$$f=\lambda_1y_1^2+\lambda_2y_2^2+\dots+\lambda_ny_n^2$$
$\lambda_1,\lambda_2,\dots,\lambda_n$ 恰好是矩阵 $A$ 的全部特征值。</p>
<h4>完整解题步骤（例题演示）</h4>
<p>$$f=2x_1^2+x_2^2-4x_1x_2-4x_2x_3$$</p>
<ol>
<li>写出对称矩阵
$$A=\begin{pmatrix}2&amp;-2&amp;0\-2&amp;1&amp;-2\0&amp;-2&amp;0\end{pmatrix}$$</li>
<li>解特征方程 $|\lambda E-A|=0$，求出特征值 $\lambda_1=1,\lambda_2=4,\lambda_3=-2$；</li>
<li>逐个求特征向量，不同特征值向量天然正交；</li>
<li>全部特征向量单位化，拼成正交矩阵 $Q$；</li>
<li>正交变换 $\boldsymbol x=Q\boldsymbol y$，直接写出标准形：
$$f=y_1^2+4y_2^2-2y_3^2$$</li>
</ol>
<hr />
<h2>二、实对称矩阵合同判定核心结论</h2>
<h3>定理6.4</h3>
<p>任意 $n$ 阶实对称矩阵 $A$，<strong>一定合同于对角矩阵</strong>。</p>
<h3>定理6.8（合同充要条件）</h3>
<p>实对称矩阵 $A\simeq B$ 的等价判定（全部互为充要）：</p>
<ol>
<li>秩相等 + 正惯性指数相等；</li>
<li>秩相等 + 负惯性指数相等；</li>
<li>正、负惯性指数全部相同；</li>
<li>规范形完全一致；</li>
<li>正特征值个数、负特征值个数分别对应相等。</li>
</ol>
<h3>重要推论</h3>
<p>若两个实对称矩阵<strong>相似</strong>，则二者一定合同。</p>
<blockquote>
<p>解释：相似特征值完全相同，正负特征值数量一致，惯性指数相等，必然合同；反之合同不一定相似。</p>
</blockquote>
<hr />
<h3>三、两种化标准形方法对比表</h3>
<table>
<thead>
<tr>
<th>对比维度</th>
<th>配方法（可逆线性变换）</th>
<th>正交变换法</th>
</tr>
</thead>
<tbody>
<tr>
<td>变换矩阵性质</td>
<td>$C$ 可逆，无正交要求</td>
<td>$Q$ 正交矩阵，$Q^\mathrm T=Q^{-1}$</td>
</tr>
<tr>
<td>$A$ 与对角阵关系</td>
<td>仅合同，不相似</td>
<td>既合同，又相似</td>
</tr>
<tr>
<td>标准形系数</td>
<td>任意实数，不唯一</td>
<td>只能是 $A$ 的特征值，唯一</td>
</tr>
<tr>
<td>惯性指数</td>
<td>唯一不变</td>
<td>唯一不变</td>
</tr>
<tr>
<td>适用场景</td>
<td>只求标准形、规范形、惯性指数</td>
<td>题目明确要求正交变换、二次曲面几何变换</td>
</tr>
</tbody>
</table>
<hr />
<h3>四、做题易错提醒 💡</h3>
<ol>
<li>配方法得到的变换矩阵 $C$ 必须验算行列式 $|C|\ne0$，保证变换可逆；</li>
<li>正交变换标准形系数固定为特征值，不需要额外配方，直接写结果；</li>
<li>只有实对称矩阵能用「惯性指数相等」判定合同，普通方阵不适用该结论；</li>
<li>相似 $\implies$ 合同（实对称），合同 $\nRightarrow$ 相似。</li>
</ol>
<h2>6.3 二次型与对称矩阵的有定性</h2>
<h3>一、二次型定性五大分类（定义6.9）</h3>
<p>设实二次型 $f(\boldsymbol x)=\boldsymbol x^\mathrm T A \boldsymbol x,\ A^\mathrm T=A$，对任意非零实向量 $\boldsymbol x\ne\boldsymbol 0$：</p>
<ol>
<li><strong>正定</strong>：恒有 $f(\boldsymbol x)&gt;0$</li>
<li><strong>负定</strong>：恒有 $f(\boldsymbol x)&lt;0$</li>
<li><strong>半正定</strong>：恒有 $f(\boldsymbol x)\ge0$，存在非零向量使 $f=0$</li>
<li><strong>半负定</strong>：恒有 $f(\boldsymbol x)\le0$，存在非零向量使 $f=0$</li>
<li><strong>不定</strong>：既有 $\boldsymbol x_1$ 使 $f&gt;0$，又有 $\boldsymbol x_2$ 使 $f&lt;0$</li>
</ol>
<h4>例题定性判断</h4>
<ol>
<li>$f=2x_1^2+x_2^2+5x_3^2$：正定</li>
<li>$f=-x_1^2-3x_2^2-4x_3^2$：负定</li>
<li>$f=x_1^2+3x_2^2+0x_3^2$：半正定（取 $\boldsymbol x=(0,0,1)^\mathrm T,f=0$）</li>
</ol>
<hr />
<h3>二、正定二次型核心性质（定理6.9）</h3>
<p>正定二次型经过<strong>任意可逆线性变换</strong>，仍然是正定二次型。</p>
<blockquote>
<p>本质：可逆变换不改变正惯性指数，正定要求正惯性指数 $p=n$，因此不变。</p>
</blockquote>
<hr />
<h3>三、正定矩阵（实对称）充要判定方法（全套考研结论）</h3>
<p>设 $A$ 为 $n$ 阶实对称矩阵，下述条件<strong>全部等价</strong>：</p>
<h4>方法1：惯性指数判定</h4>
<p>正惯性指数 $p=n$；规范形为 $y_1^2+y_2^2+\dots+y_n^2$。</p>
<h4>方法2：合同判定</h4>
<p>$A$ 合同于单位矩阵 $E$，$\exists$ 可逆阵 $C$，$C^\mathrm T A C=E$。</p>
<h4>方法3：矩阵分解判定</h4>
<p>存在可逆矩阵 $C$，使得 $\boldsymbol A=C^\mathrm T C$。</p>
<h4>方法4：特征值判定</h4>
<p>$A$ 的所有特征值 $\lambda_i&gt;0$。</p>
<h4>方法5：顺序主子式判定（霍尔维茨定理，最常用）</h4>
<p>$A$ 的<strong>各阶顺序主子式全部大于0</strong>：
$$
D_k=\begin{vmatrix}
a_{11} &amp; \dots &amp; a_{1k}\
\vdots &amp; &amp; \vdots\
a_{k1} &amp; \dots &amp; a_{kk}
\end{vmatrix}&gt;0,\quad k=1,2,\dots,n
$$
$k$ 阶顺序主子式：取矩阵前 $k$ 行、前 $k$ 列构成子方阵的行列式。</p>
<hr />
<h3>四、正定矩阵必要条件（只能排除，不能判定正定）</h3>
<ol>
<li>$A$ 一定是对称矩阵（正定前提）；</li>
<li>主对角线元素全部 $a_{ii}&gt;0$；</li>
<li>行列式 $|A|&gt;0$；</li>
<li>$A$ 可逆。</li>
</ol>
<hr />
<h3>五、正定矩阵常用运算性质</h3>
<ol>
<li>$A^\mathrm T,A^{-1},A^*$ 均正定；</li>
<li>$kA\ (k&gt;0)$、$A^m$（$m$ 正整数）正定；</li>
<li>$A,B$ 同阶正定/半正定 $\implies A+B$ 正定。</li>
</ol>
<hr />
<h3>六、做题思路速查表</h3>
<table>
<thead>
<tr>
<th>已知条件</th>
<th>首选判定方法</th>
</tr>
</thead>
<tbody>
<tr>
<td>已化标准形/规范形</td>
<td>数正惯性指数是否等于阶数 $n$</td>
</tr>
<tr>
<td>给出全部特征值</td>
<td>逐个检查是否全大于0</td>
</tr>
<tr>
<td>低阶方阵、无特征值</td>
<td>逐阶计算顺序主子式，全部&gt;0即正定</td>
</tr>
<tr>
<td>矩阵表达式变形证明题</td>
<td>证 $A=C^\mathrm T C$ 或 $A\simeq E$</td>
</tr>
</tbody>
</table>
<hr />
<h4>例7 证明：$A,B$ 均 $n$ 阶正定矩阵 $\implies A+B$ 正定</h4>
<ol>
<li>
<p><strong>验证对称性</strong>
正定矩阵必对称：$A^\mathrm T=A,\ B^\mathrm T=B$
$$(A+B)^\mathrm T=A^\mathrm T+B^\mathrm T=A+B$$
故 $A+B$ 是实对称矩阵，满足二次型矩阵前提。</p>
</li>
<li>
<p><strong>正定定义验证</strong>
任取非零列向量 $\boldsymbol x\ne\boldsymbol 0$：
$$\boldsymbol x^\mathrm T(A+B)\boldsymbol x=\boldsymbol x^\mathrm T A\boldsymbol x+\boldsymbol x^\mathrm T B\boldsymbol x$$
$A,B$ 正定，因此 $\boldsymbol x^\mathrm T A\boldsymbol x&gt;0,\ \boldsymbol x^\mathrm T B\boldsymbol x&gt;0$，相加得：
$$\boldsymbol x^\mathrm T(A+B)\boldsymbol x&gt;0$$
由正定定义，$A+B$ 正定。</p>
</li>
</ol>
<hr />
<h4>例8 已知实对称矩阵 $A^2-3A+2E=O$，证 $A+E$ 正定</h4>
<ol>
<li>
<p>特征值满足矩阵多项式方程：若 $\lambda$ 是 $A$ 的特征值，则
$$\lambda^2-3\lambda+2=0 \implies (\lambda-1)(\lambda-2)=0$$
解得 $A$ 的特征值只能是 $\lambda_1=1,\ \lambda_2=2$。</p>
</li>
<li>
<p>设 $A$ 特征值为 $\lambda$，则 $A+E$ 的特征值为 $\lambda+1$：</p>
</li>
</ol>
<ul>
<li>$\lambda=1$ 时，$\lambda+1=2&gt;0$</li>
<li>$\lambda=2$ 时，$\lambda+1=3&gt;0$</li>
</ul>
<ol>
<li>$A$ 实对称 $\implies A+E$ 实对称；<strong>全部特征值大于0</strong>，故 $A+E$ 正定。</li>
</ol>
<hr />
<h4>例9 求 $t$ 的取值，使二次型正定</h4>
<p>$$f(x_1,x_2,x_3)=2x_1^2+x_2^2+x_3^2+2x_1x_2+tx_2x_3$$
<strong>步骤1：写出实对称矩阵</strong></p>
<p>$$A=\begin{pmatrix}
2 &amp; 1 &amp; 0\
1 &amp; 1 &amp; \dfrac t2\
0 &amp; \dfrac t2 &amp; 1
\end{pmatrix}$$</p>
<p><strong>步骤2：顺序主子式全大于0（霍尔维茨定理）</strong>
1阶顺序主子式：$D_1=2&gt;0$，恒成立；
2阶顺序主子式：</p>
<p>$$D_2=\begin{vmatrix}2&amp;1\1&amp;1\end{vmatrix}=2-1=1&gt;0$$
3阶顺序主子式：</p>
<p>$$
D_3=
\begin{vmatrix}
2 &amp; 1 &amp; 0\
1 &amp; 1 &amp; \frac t2\
0 &amp; \frac t2 &amp; 1
\end{vmatrix}
=2\left(1-\frac{t^2}{4}\right)-1\cdot\left(1-0\right)
=1-\frac{t^2}{2}&gt;0
$$</p>
<p><strong>步骤3：解不等式</strong>
$$
1-\frac{t^2}{2}&gt;0 \implies t^2&lt;2
$$
最终取值范围：
$$\boldsymbol{-\sqrt{2}&lt;t&lt;\sqrt{2}}$$</p>
<hr />
<p><strong>三类正定证明题型方法总结</strong></p>
<table>
<thead>
<tr>
<th>题型特征</th>
<th>首选方法</th>
</tr>
</thead>
<tbody>
<tr>
<td>矩阵相加、线性组合证明正定</td>
<td>原始定义 $\boldsymbol x^\mathrm T A\boldsymbol x&gt;0$</td>
</tr>
<tr>
<td>给出矩阵多项式方程（$f(A)=O$）</td>
<td>求特征值，验证所有特征值$&gt;0$</td>
</tr>
<tr>
<td>含参数二次型求参数范围</td>
<td>顺序主子式逐阶大于0，解不等式</td>
</tr>
</tbody>
</table>
]]></content>
    <author><name>仙鹤</name></author>
    <category term="考试"/>
  </entry>
  <entry>
    <title>概率论与数理统计</title>
    <link href="https://xianhe51920.github.io/posts/probability-statistics/" rel="alternate" type="text/html"/>
    <id>https://xianhe51920.github.io/posts/probability-statistics/</id>
    <published>2026-06-30T00:00:00.000Z</published>
    <updated>2026-06-30T00:00:00.000Z</updated>
    <summary>大二上概率论与数理统计</summary>
    <content type="html"><![CDATA[<h1>概率论与数理统计-速成课笔记</h1>
<hr />
<h1>第一章</h1>
<h2>第一讲 随机事件及其概率（一）</h2>
<blockquote>
<p>本讲包含 2 种题型。</p>
</blockquote>
<hr />
<h3>一、利用"四大公式"求事件概率</h3>
<blockquote>
<p><strong>"四大公式"</strong>：加法公式、减法公式、乘法公式、条件概率公式。</p>
</blockquote>
<h4>例1</h4>
<p><strong>（1）</strong> 设 $P(A)=0.5$，$P(A\cup B)=0.8$，且 $A,B$ 互不相容，求 $P(B)$。</p>
<p><strong>（2）</strong> 设 $P(A)=P(B)=P(C)=\dfrac{1}{4}$，$P(AC)=\dfrac{1}{8}$，$P(AB)=P(BC)=0$，求 "$A,B,C$ 至少发生一个" 的概率。</p>
<hr />
<p><strong>【分析】</strong></p>
<p><strong>① "四大公式"</strong></p>
<ul>
<li>
<p><strong>加法公式</strong></p>
<ol>
<li>$P(A\cup B)$ 的含义："$A,B$ 至少发生一个"。（问：$A\cup B\cup C$ 呢？）</li>
<li>$P(A\cup B)=P(A)+P(B)-P(AB)$</li>
<li>$P(A\cup B\cup C)=P(A)+P(B)+P(C)-P(AB)-P(AC)-P(BC)+P(ABC)$</li>
</ol>
<blockquote>
<p>口诀："<strong>加奇减偶</strong>"。</p>
</blockquote>
</li>
</ul>
<hr />
<p><strong>【解】</strong></p>
<p><strong>（1）</strong> 由 $A,B$ 互不相容，则 $AB=\varnothing$，又 $P(A\cup B)=0.8$</p>
<p>$$\Rightarrow P(A)+P(B)-P(AB)=0.8$$</p>
<p>$$\therefore P(B)=0.8-P(A)=0.8-0.5=0.3$$</p>
<blockquote>
<p>补充：$ABC \subseteq AB$，$P(ABC) \leq P(AB) = 0$（互不相容），所以 $P(ABC)=0$。</p>
</blockquote>
<p><strong>（2）</strong> "$A,B,C$ 至少发生一个" $= P(A\cup B\cup C)$</p>
<p>$$= P(A)+P(B)+P(C)-P(AB)-P(AC)-P(BC)+P(ABC)$$</p>
<p>$$= \dfrac{1}{4}+\dfrac{1}{4}+\dfrac{1}{4}-0-\dfrac{1}{8}-0+0 = \dfrac{5}{8}$$</p>
<hr />
<h4>例2</h4>
<p>设 $P(A)=0.7$，$P(A-B)=0.3$，求 $P(\overline{AB})$。</p>
<p><strong>【分析】</strong></p>
<p><strong>① 减法公式</strong>：$P(A-B)=P(A)-P(AB)$</p>
<p><strong>② 对立事件</strong>：$P(\overline{A})=1-P(A)$</p>
<p><strong>【解】</strong></p>
<p>① 由 $P(A-B)=0.3 \Rightarrow P(A)-P(AB)=0.3$</p>
<p>$$\Rightarrow P(AB)=P(A)-0.3=0.7-0.3=0.4$$</p>
<p>② $P(\overline{AB})=1-P(AB)=1-0.4=0.6$</p>
<hr />
<h4>例3</h4>
<p>设 $P(B)=0.4$，$P(A\cup B)=0.5$，求 $P(A|\overline{B})$。</p>
<p><strong>【分析】</strong></p>
<p><strong>① 条件概率公式</strong>：$P(A|B)=\dfrac{P(AB)}{P(B)}$</p>
<p><strong>②</strong> $P(A-B)=P(A)-P(AB)$</p>
<p><strong>【解】</strong></p>
<ol>
<li>由 $P(B)=0.4$，$P(A\cup B)=0.5$，则：</li>
</ol>
<p>$$P(A)+P(B)-P(AB)=0.5$$</p>
<p>$$\Rightarrow P(A)-P(AB)=0.5-P(B)=0.5-0.4=0.1$$</p>
<ol>
<li></li>
</ol>
<p>$$P(A|\overline{B}) = \dfrac{P(A\overline{B})}{P(\overline{B})} = \dfrac{P(A)-P(AB)}{1-P(B)} = \dfrac{0.1}{1-0.4} = \dfrac{1}{6}$$</p>
<hr />
<h4>例4</h4>
<p>设 $A,B$ 相互独立，$P(B)=0.4$，$P(A\cup B)=0.5$，求 $P(\overline{B}|A)$。</p>
<p><strong>【分析】</strong></p>
<p><strong>①</strong> $A$ 与 $B$ 独立 $\Longleftrightarrow$ $A$ 与 $B$ 互不影响 $\Longleftrightarrow$ $P(B|A)=P(B)$</p>
<p>$$\Longleftrightarrow P(AB)=P(A)P(B)$$</p>
<p><strong>②</strong> $A$ 与 $B$ 独立 $\Longleftrightarrow$ $B$ 是否发生不受 $A$ 的影响 $\Longleftrightarrow$ $\overline{B}$ 与 $A$，$\overline{B}$ 与 $\overline{A}$，$B$ 与 $\overline{A}$ 独立</p>
<p>$$\Longleftrightarrow P(\overline{B}|A)=P(\overline{B})$$</p>
<blockquote>
<p>口诀："<strong>有爸无爸，对妈独立无影响</strong>"。</p>
</blockquote>
<p><strong>【解】</strong></p>
<p><strong>法一</strong>：由 $A,B$ 独立 $\Rightarrow P(AB)=P(A)P(B)=0.4P(A)$</p>
<p>而 $P(A\cup B)=0.5 \Rightarrow P(A)+P(B)-P(AB)=0.5$</p>
<p>$$\Rightarrow P(A)+0.4-0.4P(A)=0.5 \Rightarrow P(A)=\dfrac{1}{4}$$</p>
<p>$$P(\overline{B}|A)=\dfrac{P(A\overline{B})}{P(A)}=\dfrac{P(A)-P(AB)}{P(A)}=\dfrac{P(A)-0.4P(A)}{P(A)}=0.6$$</p>
<p><strong>法二</strong>：由 $A,B$ 独立 $\Rightarrow$ $\overline{B}$ 与 $A$ 独立</p>
<p>$$\Rightarrow P(\overline{B}|A)=P(\overline{B})=1-P(B)=1-0.4=0.6$$</p>
<hr />
<h3>二、古典型求概率</h3>
<h4>例1</h4>
<p>袋中有 5 只白球，6 只黑球，从中任取 2 球，求：</p>
<ol>
<li>取到 1 白 1 黑 的概率；</li>
<li>至少取到 1 只黑球的概率。</li>
</ol>
<p><strong>【分析】</strong></p>
<p><strong>① 古典型概率</strong>：事件 $A$ 发生的概率公式：</p>
<p>$$P(A)=\dfrac{A\text{ 中可能的点数}}{\Omega\text{ 中可能的点数}}$$</p>
<p><strong>② 排列</strong>：$A_{11}^{2}$ 表示从 11 个不同元素中任取 2 个排成一列的排法：$A_{11}^{2}=11\times 10$</p>
<p><strong>组合</strong>：$C_{11}^{2}$ 表示从 11 个不同元素中任取 2 个（不计顺序）的取法：$C_{11}^{2}=\dfrac{11\times 10}{2!}$</p>
<p><strong>【解】</strong></p>
<p><strong>（1）法一</strong> 设 $A$ = "取到 1 黑 1 白球"，$\Omega$ = "从 11 球中任取 2 球"（不计顺序，一次取 2 球）</p>
<p>$$P(A)=\dfrac{C_{5}^{1}\cdot C_{6}^{1}}{C_{11}^{2}}=\dfrac{5\times 6}{\dfrac{11\times 10}{2!}}=\dfrac{6}{11}$$</p>
<blockquote>
<p>（分步相乘，分类相加）</p>
</blockquote>
<p><strong>法二</strong> 设 $A$ = "取到 1 黑 1 白球"，$\Omega$ = "从 11 球中任取 2 球"（共取 2 次，一次取 1 球，分类："先白后黑" + "先黑后白"）</p>
<p>$$P(A)=\dfrac{A_{5}^{1}\cdot A_{6}^{1}+A_{6}^{1}\cdot A_{5}^{1}}{A_{11}^{2}}=\dfrac{5\times 6+6\times 5}{11\times 10}=\dfrac{6}{11}$$</p>
<blockquote>
<p>答案是一样的。</p>
</blockquote>
<p><strong>（2）</strong> 设 $B$ = "至少取到 1 只黑球"，$\overline{B}$ = "2 只球均为白球"（不计顺序）</p>
<p>$$P(B)=1-P(\overline{B})=1-\dfrac{C_{5}^{2}}{C_{11}^{2}}=1-\dfrac{\dfrac{5\times 4}{2!}}{\dfrac{11\times 10}{2!}}=1-\dfrac{2}{11}=\dfrac{9}{11}$$</p>
<hr />
<h2>第二讲 随机事件及其概率（二）</h2>
<blockquote>
<p>本讲包含 2 种题型。</p>
</blockquote>
<hr />
<h3>三、全概率公式与贝叶斯公式</h3>
<p><strong>【分析】</strong> ① 在用题中，若适合分两个阶段完成，则用全概率公式或贝叶斯公式。</p>
<p><strong>① 全概率公式</strong>：</p>
<p>$$P(A)=P(AB_{1}\cup AB_{2}\cup AB_{3})=P(B_{1})P(A|B_{1})+P(B_{2})P(A|B_{2})+P(B_{3})P(A|B_{3})$$</p>
<p><strong>② 贝叶斯公式</strong>：</p>
<p>$$P(B_{i}|A)=\dfrac{P(AB_{i})}{P(A)}=\dfrac{P(B_{i})P(A|B_{i})}{\text{全概率公式}}$$</p>
<blockquote>
<p><strong>步骤</strong>：</p>
<ul>
<li><strong>第1步</strong>：把第一阶段所有可能结果看成完备组 $B_{1},B_{2},B_{3}$；</li>
<li><strong>第2步</strong>：把第二阶段的某个可能结果看成 $A$；</li>
<li><strong>第3步</strong>：用全概率或贝叶斯公式。</li>
</ul>
</blockquote>
<hr />
<h4>例1</h4>
<p>甲袋中有 5 只红球、4 只白球；乙袋中有 4 只红球、5 只白球。先从甲中任取 1 只球放入乙中，再从乙中取 1 只球，求：</p>
<ol>
<li>从乙中取到的球是白球的概率；</li>
<li>若已知从乙中取到 1 白球，问从甲中取到的球是白球的概率。</li>
</ol>
<p><strong>【解】</strong></p>
<p><strong>（1）</strong> 第 1 阶段：甲→乙</p>
<ul>
<li>$B_{1}=$ 从甲中取到红球，$P(B_{1})=\dfrac{5}{9}$</li>
<li>$B_{2}=$ 从甲中取到白球，$P(B_{2})=\dfrac{4}{9}$</li>
</ul>
<p>第 2 阶段：从乙中取 1 球，$A=$ "从乙中取到白球"</p>
<p>由全概率公式：</p>
<p>$$P(A)=P(B_{1})P(A|B_{1})+P(B_{2})P(A|B_{2})=\dfrac{5}{9}\times\dfrac{5}{10}+\dfrac{4}{9}\times\dfrac{6}{10}=\dfrac{49}{90}$$</p>
<p><strong>（2）</strong> 由题意（贝叶斯公式）：</p>
<p>$$P(B_{2}|A)=\dfrac{P(B_{2}A)}{P(A)}=\dfrac{P(B_{2})P(A|B_{2})}{49/90}=\dfrac{\dfrac{4}{9}\times\dfrac{6}{10}}{49/90}=\dfrac{24}{49}$$</p>
<hr />
<h4>例2</h4>
<p>已知男性中有 $5%$ 是色盲，女性中有 $0.25%$ 是色盲，今从男女人数相等的人群中随机挑选一人，恰好是色盲，问此人是男性的概率。</p>
<p><strong>【解】</strong></p>
<p>第一阶段：从人群中选人</p>
<ul>
<li>$B_{1}=$ "选到男性"</li>
<li>$B_{2}=$ "选到女性"</li>
</ul>
<p>第二阶段：判断此人是否色盲，$A=$ "发现此人是色盲"</p>
<p><strong>①</strong> 全概率公式：</p>
<p>$$P(A)=P(B_{1})P(A|B_{1})+P(B_{2})P(A|B_{2})=\dfrac{1}{2}\times 5%+\dfrac{1}{2}\times 0.25%=\dfrac{21}{800}$$</p>
<p><strong>②</strong> 贝叶斯公式：</p>
<p>$$P(B_{1}|A)=\dfrac{P(B_{1})P(A|B_{1})}{P(A)}=\dfrac{\dfrac{1}{2}\times 5%}{21/800}=\dfrac{20}{21}$$</p>
<hr />
<h3>四、伯努利概型求概率</h3>
<p><strong>【分析】</strong> 当出现"独立"、"$n$ 重试验"、"每次试验只有 $A$ 与 $\overline{A}$ 两种结果"，则用伯努利概型：</p>
<p>$$P_{n}(k)=C_{n}^{k},p^{k}(1-p)^{n-k}, \quad \text{其中 } p=P(A),\ 1-p=P(\overline{A})$$</p>
<hr />
<h4>例</h4>
<p>某人打靶的命中率为 $0.8$，现独立射击 5 次，问 5 次中命中 2 次的概率。</p>
<p><strong>【解】</strong> 5 次独立重复射击，$p=0.8$，则：</p>
<p>$$P{\text{5 次试验中命中 2 次}}=P_{5}(2)=C_{5}^{2}\cdot p^{2}\cdot(1-p)^{3}=C_{5}^{2}\cdot 0.8^{2}\cdot 0.2^{3}$$</p>
<hr />
]]></content>
    <author><name>仙鹤</name></author>
    <category term="考试"/>
  </entry>
  <entry>
    <title>线性代数公式要点</title>
    <link href="https://xianhe51920.github.io/posts/linear-algebra-formulas/" rel="alternate" type="text/html"/>
    <id>https://xianhe51920.github.io/posts/linear-algebra-formulas/</id>
    <published>2026-06-23T00:00:00.000Z</published>
    <updated>2026-06-23T00:00:00.000Z</updated>
    <summary>大一下线代：公式要点速览</summary>
    <content type="html"><![CDATA[<h1>线性代数公式要点</h1>
<hr />
<h2>第一部分：行列式</h2>
<h3>概念性质</h3>
<ol>
<li><strong>按行/列展开不变性</strong>：n阶矩阵 $A$ 的行列式按任意一行（列）展开，其值相等。</li>
</ol>
<hr />
<ol>
<li><strong>两行（列）全相同则行列式为0</strong>：若n阶行列式有两行（列）对应元素全相等，则行列式值为0。</li>
</ol>
<hr />
<ol>
<li><strong>行（列）的可拆分性</strong>：某一行（列）的元素为两数之和时，行列式可拆分为两个行列式之和。</li>
</ol>
<hr />
<ol>
<li><strong>初等变换对行列式的影响</strong>：
<ul>
<li>某一行（列）乘以数 $k$，行列式值变为原来的 $k$ 倍；</li>
<li>某一行（列）的 $k$ 倍加到另一行（列），行列式值不变；</li>
<li>交换两行（列），行列式值变号。</li>
</ul>
</li>
</ol>
<hr />
<ol>
<li><strong>转置不变性</strong>：行列式与其转置行列式值相等，即 $\det A^T = \det A$。</li>
</ol>
<hr />
<ol>
<li><strong>行列式的推论</strong>：若行列式的某一行（列）元素全为0，则行列式值为0；若行列式的某两行（列）对应元素成比例，则行列式值为0。</li>
</ol>
<hr />
<ol>
<li><strong>范德蒙德行列式</strong>：特殊行列式，结果为所有下标较大的元素减去下标较小的元素的乘积。</li>
</ol>
<hr />
<ol>
<li><strong>拉普拉斯定理推论（分块行列式）</strong>：上三角分块、对角分块行列式等于对角线上各子块行列式的乘积；反对角分块行列式等于 $(-1)^{mn}|A||B|$。</li>
</ol>
<hr />
<ol>
<li><strong>克拉默法则</strong>：设 $n$ 阶矩阵 $A$ 可逆，则线性方程组 $Ax = b$ 有唯一解 $x_j = \frac{\det A_j}{\det A}$。</li>
</ol>
<hr />
<ol>
<li><strong>齐次线性方程组判定</strong>：对于 $Ax=0$，若 $\det A \neq 0$，方程组只有零解；若 $\det A = 0$，方程组有非零解。</li>
</ol>
<h3>公式</h3>
<ol>
<li>$n$阶行列式定义：$D = \sum(-1)^t a_{1p_1}a_{2p_2}\dots a_{np_n}$</li>
</ol>
<hr />
<ol>
<li>转置行列式：$\det A^T = \det A$</li>
</ol>
<hr />
<ol>
<li>行乘常数 $k$：$\det A_1 = k\det A$</li>
</ol>
<hr />
<ol>
<li>交换两行：$\det A_3 = -\det A$</li>
</ol>
<hr />
<ol>
<li>范德蒙德行列式：$V_n = \prod_{1 \le i &lt; j \le n} (x_j - x_i)$</li>
</ol>
<hr />
<ol>
<li>上三角分块行列式：$\begin{vmatrix} A &amp; C \ O &amp; B \end{vmatrix} = |A||B|$</li>
</ol>
<hr />
<ol>
<li>对角分块行列式：$\begin{vmatrix} A &amp; O \ O &amp; B \end{vmatrix} = |A||B|$</li>
</ol>
<hr />
<ol>
<li>反对角分块行列式：$\begin{vmatrix} O &amp; A \ B &amp; O \end{vmatrix} = (-1)^{mn}|A||B|$</li>
</ol>
<hr />
<ol>
<li>克拉默法则：$x_j = \frac{\det A_j}{\det A}$（$j=1,2,\dots,n$）</li>
</ol>
<hr />
<h2>第二部分：矩阵</h2>
<h3>概念性质</h3>
<ol>
<li><strong>矩阵定义</strong>：$m \times n$ 的数表。</li>
</ol>
<hr />
<ol>
<li><strong>同型矩阵</strong>：矩阵 $A$、$B$ 的行数和列数都相等。</li>
</ol>
<hr />
<ol>
<li><strong>矩阵相等</strong>：$A=B \iff$ 是同型矩阵，且对应位置元素相等。</li>
</ol>
<hr />
<ol>
<li><strong>特殊矩阵</strong>：方阵、行矩阵、列矩阵、零矩阵、负矩阵、上/下三角矩阵、对角矩阵、数量矩阵、单位矩阵。</li>
</ol>
<hr />
<ol>
<li><strong>矩阵加法运算规律</strong>：交换律 $A+B = B+A$，结合律 $(A+B)+C = A+(B+C)$，$A+O = A$，$A+(-A) = O$。</li>
</ol>
<hr />
<ol>
<li><strong>矩阵数乘运算规律</strong>：$k(A+B) = kA + kB$，$(k+l)A = kA + lA$，$k(lA) = (kl)A$，$1A = A$，$(-1)A = -A$。</li>
</ol>
<hr />
<ol>
<li><strong>矩阵乘法条件</strong>：$A_{m \times n}$ 与 $B_{p \times q}$ 相乘，需要 $n = p$，结果是 $m \times q$ 矩阵。</li>
</ol>
<hr />
<ol>
<li><strong>矩阵乘法不满足交换律和消去律</strong>：$AB \neq BA$，$AB=AC$ 推不出 $B=C$，两个非零矩阵的乘积可能是零矩阵。</li>
</ol>
<hr />
<ol>
<li><strong>矩阵乘法满足的运算规律</strong>：结合律 $(AB)C = A(BC)$，分配律 $(A+B)C = AC + BC$ 和 $C(A+B) = CA + CB$，数乘结合律 $k(AB) = (kA)B = A(kB)$，单位矩阵性质 $AE = EA = A$。</li>
</ol>
<hr />
<ol>
<li><strong>方阵的幂</strong>：$A^k = \underbrace{A \cdot A \cdot \dots \cdot A}_{k\text{个}}$，$A^0 = E$，$A^{k_1}A^{k_2} = A^{k_1+k_2}$，$(A^{k_1})^{k_2} = A^{k_1k_2}$。</li>
</ol>
<hr />
<ol>
<li><strong>矩阵的转置</strong>：$(A^T)^T = A$，$(A+B)^T = A^T + B^T$，$(kA)^T = kA^T$，$(AB)^T = B^T A^T$。</li>
</ol>
<hr />
<ol>
<li><strong>对称矩阵</strong>：$A^T = A$，即 $a_{ij} = a_{ji}$；同阶对称矩阵之和、差仍对称；$kA$、$A^m$ 仍对称；$AB$ 对称的充要条件是 $AB=BA$。</li>
</ol>
<hr />
<ol>
<li><strong>反对称矩阵</strong>：$A^T = -A$，即 $a_{ij} = -a_{ji}$，主对角线元素全为0；同阶反对称矩阵之和、差仍反对称；$kA$ 仍反对称。</li>
</ol>
<hr />
<ol>
<li><strong>方阵行列式性质</strong>：$|A^T|=|A|$，$|kA|=k^n|A|$，$|AB|=|A|\cdot|B|$，$|A^m|=|A|^m$。</li>
</ol>
<hr />
<ol>
<li><strong>伴随矩阵</strong>：$AA^* = A^<em>A = |A|E$，$|A^</em>| = |A|^{n-1}$，$(A^T)^* = (A^<em>)^T$，$(kA)^</em> = k^{n-1}A^*$。</li>
</ol>
<hr />
<ol>
<li><strong>逆矩阵</strong>：$A$ 可逆 $\iff |A| \neq 0$，$A^{-1} = \frac{1}{|A|}A^*$；$(A^{-1})^{-1}=A$，$(A^T)^{-1}=(A^{-1})^T$，$(kA)^{-1}=\frac{1}{k}A^{-1}$，$(AB)^{-1}=B^{-1}A^{-1}$。</li>
</ol>
<hr />
<ol>
<li><strong>矩阵方程解</strong>：$AX=C \Rightarrow X=A^{-1}C$，$XA=C \Rightarrow X=CA^{-1}$，$AXB=C \Rightarrow X=A^{-1}CB^{-1}$。</li>
</ol>
<hr />
<ol>
<li><strong>初等矩阵</strong>：可逆，逆矩阵仍为同类型初等矩阵；$E(i,j)^{-1}=E(i,j)$，$E(i(k))^{-1}=E(i(1/k))$，$E(i,j(l))^{-1}=E(i,j(-l))$。</li>
</ol>
<hr />
<ol>
<li><strong>矩阵等价</strong>：$A \cong B \iff$ 存在可逆矩阵 $P,Q$ 使 $PAQ=B \iff r(A)=r(B)$。</li>
</ol>
<hr />
<ol>
<li><strong>矩阵的秩</strong>：非零子式的最高阶数，$0 \le r(A) \le \min(m,n)$；$r(A)=r(A^T)$，$r(kA)=r(A)\ (k \neq 0)$。</li>
</ol>
<hr />
<ol>
<li><strong>矩阵秩的不等式</strong>：$r(A+B) \le r(A)+r(B)$，$r(AB) \le \min(r(A),r(B))$，$r(A)+r(B)-n \le r(AB)$。</li>
</ol>
<hr />
<ol>
<li><strong>伴随矩阵的秩</strong>：当 $r(A)=n$ 时，$r(A^<em>)=n$；当 $r(A)=n-1$ 时，$r(A^</em>)=1$；当 $r(A)&lt;n-1$ 时，$r(A^*)=0$。</li>
</ol>
<h3>公式</h3>
<ol>
<li>矩阵加法：$A+B = (a_{ij}+b_{ij})$</li>
</ol>
<hr />
<ol>
<li>矩阵数乘：$kA = (ka_{ij})$</li>
</ol>
<hr />
<ol>
<li>矩阵乘法：$AB = (\sum_{k=1}^n a_{ik}b_{kj})$</li>
</ol>
<hr />
<ol>
<li>转置性质：$(AB)^T = B^TA^T$</li>
</ol>
<hr />
<ol>
<li>方阵行列式：$|kA| = k^n|A|$，$|AB| = |A||B|$</li>
</ol>
<hr />
<ol>
<li>伴随矩阵性质：$AA^* = A^<em>A = |A|E$，$|A^</em>| = |A|^{n-1}$</li>
</ol>
<hr />
<ol>
<li>逆矩阵公式：$A^{-1} = \frac{1}{|A|}A^*$</li>
</ol>
<hr />
<ol>
<li>逆矩阵性质：$(AB)^{-1} = B^{-1}A^{-1}$，$|A^{-1}| = \frac{1}{|A|}$</li>
</ol>
<hr />
<ol>
<li>矩阵方程解：$AX=C \Rightarrow X=A^{-1}C$，$AXB=C \Rightarrow X=A^{-1}CB^{-1}$</li>
</ol>
<hr />
<ol>
<li>初等变换法求逆：$(A \mid E) \xrightarrow{\text{行}} (E \mid A^{-1})$</li>
</ol>
<hr />
<ol>
<li>分块三角行列式：$\begin{vmatrix} A &amp; C \ O &amp; B \end{vmatrix} = |A||B|$</li>
</ol>
<hr />
<ol>
<li>分块对角逆矩阵：$\mathrm{diag}(A,B,C)^{-1} = \mathrm{diag}(A^{-1},B^{-1},C^{-1})$</li>
</ol>
<hr />
<ol>
<li>秩的不等式：$r(A)+r(B)-n \le r(AB) \le \min(r(A),r(B))$</li>
</ol>
<hr />
<ol>
<li>矩阵乘积为零：$AB=O \Rightarrow r(A)+r(B) \le n$</li>
</ol>
<hr />
<ol>
<li>秩与转置：$r(A^T A)=r(AA^T)=r(A)$</li>
</ol>
<hr />
<ol>
<li>可逆矩阵不改变秩：若 $A$ 可逆，则 $r(AB)=r(B)$，$r(CA)=r(C)$</li>
</ol>
<hr />
<h2>第三部分：向量</h2>
<h3>概念性质</h3>
<ol>
<li><strong>向量的线性运算</strong>：加法 $\boldsymbol{\alpha} + \boldsymbol{\beta} = (a_1+b_1, \dots, a_n+b_n)$，数乘 $k\boldsymbol{\alpha} = (ka_1, \dots, ka_n)$。</li>
</ol>
<hr />
<ol>
<li><strong>向量运算律</strong>：交换律、结合律、零向量性质、负向量性质、数乘结合律、分配律。</li>
</ol>
<hr />
<ol>
<li><strong>线性组合</strong>：$k_1\boldsymbol{\alpha}_1 + k_2\boldsymbol{\alpha}_2 + \dots + k_m\boldsymbol{\alpha}_m$。</li>
</ol>
<hr />
<ol>
<li><strong>线性表示</strong>：若存在常数 $k_1,\dots,k_m$ 使 $\boldsymbol{\beta} = k_1\boldsymbol{\alpha}_1 + \dots + k_m\boldsymbol{\alpha}_m$，则 $\boldsymbol{\beta}$ 可由向量组线性表示。</li>
</ol>
<hr />
<ol>
<li><strong>线性表示与方程组的关系</strong>：$\boldsymbol{\beta}$ 可由向量组表示 $\iff$ 方程组 $Ax=\boldsymbol{\beta}$ 有解。</li>
</ol>
<hr />
<ol>
<li><strong>线性相关</strong>：存在不全为零的数 $k_1,\dots,k_n$ 使 $k_1\boldsymbol{\alpha}_1 + \dots + k_n\boldsymbol{\alpha}_n = \boldsymbol{0}$。</li>
</ol>
<hr />
<ol>
<li><strong>线性无关</strong>：若 $k_1\boldsymbol{\alpha}_1 + \dots + k_n\boldsymbol{\alpha}_n = \boldsymbol{0}$，则 $k_1=\dots=k_n=0$。</li>
</ol>
<hr />
<ol>
<li><strong>线性相关性性质</strong>：一个向量线性相关 $\iff \boldsymbol{\alpha} = \boldsymbol{0}$；两个向量线性相关 $\iff$ 对应分量成比例；部分组线性相关则整体线性相关；整体线性无关则部分组线性无关；含零向量的向量组必线性相关。</li>
</ol>
<hr />
<ol>
<li><strong>极大线性无关组</strong>：满足线性无关且能表示向量组中任一向量的部分组。</li>
</ol>
<hr />
<ol>
<li><strong>向量组的秩</strong>：极大线性无关组所含向量的个数。</li>
</ol>
<hr />
<ol>
<li><strong>等价向量组</strong>：能互相线性表示的向量组，秩相等。</li>
</ol>
<hr />
<ol>
<li><strong>矩阵的行秩与列秩</strong>：行秩 = 列秩 = $r(A)$。</li>
</ol>
<h3>公式</h3>
<ol>
<li>向量加法：$\boldsymbol{\alpha} + \boldsymbol{\beta} = (a_1+b_1, \dots, a_n+b_n)$</li>
</ol>
<hr />
<ol>
<li>向量数乘：$k\boldsymbol{\alpha} = (ka_1, ka_2, \dots, ka_n)$</li>
</ol>
<hr />
<ol>
<li>线性组合：$k_1\boldsymbol{\alpha}_1 + k_2\boldsymbol{\alpha}_2 + \dots + k_m\boldsymbol{\alpha}_m$</li>
</ol>
<hr />
<ol>
<li>线性表示：$\boldsymbol{\beta} = k_1\boldsymbol{\alpha}_1 + k_2\boldsymbol{\alpha}_2 + \dots + k_m\boldsymbol{\alpha}_m$</li>
</ol>
<hr />
<ol>
<li>线性相关判定：$k_1\boldsymbol{\alpha}_1 + k_2\boldsymbol{\alpha}_2 + \dots + k_n\boldsymbol{\alpha}_n = \boldsymbol{0}$（$k_i$ 不全为0）</li>
</ol>
<hr />
<ol>
<li>向量组的秩：$r(\boldsymbol{\alpha}_1,\dots,\boldsymbol{\alpha}_s)$ = 极大线性无关组向量个数</li>
</ol>
<hr />
<ol>
<li>矩阵秩与向量组秩：$r(A)$ = 矩阵列向量组的秩 = 矩阵行向量组的秩</li>
</ol>
<hr />
<ol>
<li>等价向量组秩关系：等价向量组的秩相等</li>
</ol>
<hr />
<h2>第四部分：线性方程组</h2>
<h3>概念性质</h3>
<ol>
<li><strong>线性方程组的三种形式</strong>：一般形式、矩阵形式 $Ax=b$、向量形式 $x_1\boldsymbol{\alpha}_1 + \dots + x_n\boldsymbol{\alpha}_n = b$。</li>
</ol>
<hr />
<ol>
<li><strong>非齐次线性方程组解的判定</strong>：
<ul>
<li>有解 $\iff r(A) = r(\overline{A})$；</li>
<li>有唯一解 $\iff r(A) = r(\overline{A}) = n$；</li>
<li>有无穷多解 $\iff r(A) = r(\overline{A}) &lt; n$；</li>
<li>无解 $\iff r(A) \neq r(\overline{A})$。</li>
</ul>
</li>
</ol>
<hr />
<ol>
<li><strong>齐次线性方程组解的判定</strong>：
<ul>
<li>有非零解 $\iff r(A) &lt; n$；</li>
<li>只有零解 $\iff r(A) = n$。</li>
</ul>
</li>
</ol>
<hr />
<ol>
<li><strong>齐次方程组解的性质</strong>：解的线性组合仍是解。</li>
</ol>
<hr />
<ol>
<li><strong>非齐次方程组解的性质</strong>：两个解之差是导出组的解；解与导出组解之和仍是解。</li>
</ol>
<hr />
<ol>
<li><strong>基础解系</strong>：线性无关且能表示所有解的解向量组，含 $n-r(A)$ 个向量。</li>
</ol>
<hr />
<ol>
<li><strong>齐次方程组通解</strong>：$\boldsymbol{x} = c_1\boldsymbol{\xi}<em>1 + c_2\boldsymbol{\xi}<em>2 + \dots + c</em>{n-r}\boldsymbol{\xi}</em>{n-r}$。</li>
</ol>
<hr />
<ol>
<li><strong>非齐次方程组通解</strong>：$\boldsymbol{x} = \boldsymbol{\alpha}_0 + c_1\boldsymbol{\xi}<em>1 + c_2\boldsymbol{\xi}<em>2 + \dots + c</em>{n-r}\boldsymbol{\xi}</em>{n-r}$（$\boldsymbol{\alpha}_0$ 为特解）。</li>
</ol>
<h3>公式</h3>
<ol>
<li>矩阵形式：$Ax = b$（非齐次），$Ax = \boldsymbol{0}$（齐次）</li>
</ol>
<hr />
<ol>
<li>向量形式：$x_1\boldsymbol{\alpha}_1 + x_2\boldsymbol{\alpha}_2 + \dots + x_n\boldsymbol{\alpha}_n = b$</li>
</ol>
<hr />
<ol>
<li>增广矩阵：$\overline{A} = (A \mid b)$</li>
</ol>
<hr />
<ol>
<li>非齐次解判定：$r(A) = r(\overline{A})$ 有解；$r(A) \neq r(\overline{A})$ 无解</li>
</ol>
<hr />
<ol>
<li>齐次解判定：$r(A) &lt; n$ 有非零解；$r(A) = n$ 只有零解</li>
</ol>
<hr />
<ol>
<li>齐次通解：$\boldsymbol{x} = c_1\boldsymbol{\xi}<em>1 + c_2\boldsymbol{\xi}<em>2 + \dots + c</em>{n-r}\boldsymbol{\xi}</em>{n-r}$</li>
</ol>
<hr />
<ol>
<li>非齐次通解：$\boldsymbol{x} = \boldsymbol{\alpha}_0 + c_1\boldsymbol{\xi}<em>1 + c_2\boldsymbol{\xi}<em>2 + \dots + c</em>{n-r}\boldsymbol{\xi}</em>{n-r}$</li>
</ol>
<hr />
<ol>
<li>基础解系向量数：$n - r(A)$</li>
</ol>
<hr />
<h2>第五部分：特征值与特征向量</h2>
<h3>概念性质</h3>
<ol>
<li><strong>特征值与特征向量定义</strong>：若 $A\boldsymbol{\alpha} = \lambda\boldsymbol{\alpha}$（$\boldsymbol{\alpha} \neq \boldsymbol{0}$），则 $\lambda$ 为特征值，$\boldsymbol{\alpha}$ 为特征向量。</li>
</ol>
<hr />
<ol>
<li><strong>特征方程</strong>：$|\lambda E - A| = 0$。</li>
</ol>
<hr />
<ol>
<li><strong>特征向量求法</strong>：解齐次方程组 $(\lambda E - A)\boldsymbol{x} = \boldsymbol{0}$ 的非零解。</li>
</ol>
<hr />
<ol>
<li><strong>特征值性质</strong>：
<ul>
<li>特征值之和 = 矩阵迹：$\lambda_1+\dots+\lambda_n = a_{11}+\dots+a_{nn}$；</li>
<li>特征值之积 = 行列式：$\lambda_1\lambda_2\cdots\lambda_n = |A|$；</li>
<li>$A$ 可逆 $\iff$ 所有特征值不为0。</li>
</ul>
</li>
</ol>
<hr />
<ol>
<li><strong>矩阵变换后特征值</strong>：若 $\lambda$ 是 $A$ 的特征值，则 $k\lambda$ 是 $kA$ 的特征值，$\lambda^m$ 是 $A^m$ 的特征值，$\frac{1}{\lambda}$ 是 $A^{-1}$ 的特征值，$\frac{|A|}{\lambda}$ 是 $A^*$ 的特征值。</li>
</ol>
<hr />
<ol>
<li><strong>相似矩阵定义</strong>：若存在可逆矩阵 $P$ 使 $P^{-1}AP = B$，则 $A \sim B$。</li>
</ol>
<hr />
<ol>
<li><strong>相似矩阵性质</strong>：特征值相同，秩相等，行列式相等，迹相等；$A^{-1}\sim B^{-1}$，$A^<em>\sim B^</em>$，$A^m\sim B^m$。</li>
</ol>
<hr />
<ol>
<li><strong>矩阵可对角化判定</strong>：$A$ 有 $n$ 个线性无关特征向量；或 $n$ 个互不相同特征值；或每个 $k$ 重特征值对应 $k$ 个线性无关特征向量。</li>
</ol>
<hr />
<ol>
<li><strong>内积性质</strong>：非负性、对称性、齐次性、线性可加性。</li>
</ol>
<hr />
<table>
<thead>
<tr>
<th>10. <strong>向量长度</strong>：$|\boldsymbol{\alpha}| = \sqrt{(\boldsymbol{\alpha},\boldsymbol{\alpha})}$。</th>
</tr>
</thead>
</table>
<ol>
<li><strong>正交向量组</strong>：两两正交的非零向量组，一定线性无关。</li>
</ol>
<hr />
<ol>
<li><strong>施密特正交化</strong>：将线性无关向量组化为正交向量组。</li>
</ol>
<hr />
<ol>
<li><strong>实对称矩阵性质</strong>：特征值都是实数，不同特征值的特征向量正交，必可对角化，可正交相似对角化。</li>
</ol>
<h3>公式</h3>
<ol>
<li>特征值定义：$A\boldsymbol{\alpha} = \lambda\boldsymbol{\alpha}$（$\boldsymbol{\alpha} \neq \boldsymbol{0}$）</li>
</ol>
<hr />
<ol>
<li>特征方程：$|\lambda E - A| = 0$</li>
</ol>
<hr />
<ol>
<li>特征向量方程：$(\lambda E - A)\boldsymbol{x} = \boldsymbol{0}$</li>
</ol>
<hr />
<ol>
<li>特征值之和：$\lambda_1 + \lambda_2 + \dots + \lambda_n = \mathrm{tr}(A)$</li>
</ol>
<hr />
<ol>
<li>特征值之积：$\lambda_1\lambda_2\cdots\lambda_n = |A|$</li>
</ol>
<hr />
<ol>
<li>相似矩阵定义：$P^{-1}AP = B$</li>
</ol>
<hr />
<ol>
<li>对角化公式：$P^{-1}AP = \Lambda = \mathrm{diag}(\lambda_1,\dots,\lambda_n)$</li>
</ol>
<hr />
<ol>
<li>向量内积：$(\boldsymbol{\alpha},\boldsymbol{\beta}) = a_1b_1 + a_2b_2 + \dots + a_nb_n$</li>
</ol>
<hr />
<table>
<thead>
<tr>
<th>9. 向量长度：$|\boldsymbol{\alpha}| = \sqrt{x_1^2 + x_2^2 + \dots + x_n^2}$</th>
</tr>
</thead>
</table>
<table>
<thead>
<tr>
<th>10. 单位化公式：$\boldsymbol{\beta} = \frac{1}{|\boldsymbol{\alpha}|}\boldsymbol{\alpha}$</th>
</tr>
</thead>
</table>
<ol>
<li>施密特正交化：$\boldsymbol{\beta}_1 = \boldsymbol{\alpha}_1$，$\boldsymbol{\beta}_2 = \boldsymbol{\alpha}_2 - \frac{(\boldsymbol{\alpha}_2,\boldsymbol{\beta}_1)}{(\boldsymbol{\beta}_1,\boldsymbol{\beta}_1)}\boldsymbol{\beta}_1$，$\boldsymbol{\beta}_3 = \boldsymbol{\alpha}_3 - \frac{(\boldsymbol{\alpha}_3,\boldsymbol{\beta}_1)}{(\boldsymbol{\beta}_1,\boldsymbol{\beta}_1)}\boldsymbol{\beta}_1 - \frac{(\boldsymbol{\alpha}_3,\boldsymbol{\beta}_2)}{(\boldsymbol{\beta}_2,\boldsymbol{\beta}_2)}\boldsymbol{\beta}_2$</li>
</ol>
<hr />
<ol>
<li>正交相似对角化：$Q^TAQ = Q^{-1}AQ = \Lambda$（$Q$ 正交矩阵）</li>
</ol>
<hr />
<h2>第六部分：二次型</h2>
<h3>概念性质</h3>
<ol>
<li><strong>二次型定义</strong>：$n$ 个变量的二次齐次多项式，$f(x_1,\dots,x_n) = \boldsymbol{x}^TA\boldsymbol{x}$（$A^T = A$）。</li>
</ol>
<hr />
<ol>
<li><strong>标准形</strong>：只含平方项的二次型，$f = d_1y_1^2 + d_2y_2^2 + \dots + d_ny_n^2$。</li>
</ol>
<hr />
<ol>
<li><strong>规范形</strong>：平方项系数仅为 $1,-1,0$ 的标准形，唯一。</li>
</ol>
<hr />
<ol>
<li><strong>惯性定理</strong>：实二次型的规范形唯一，正惯性指数 $p$ 和负惯性指数 $q$ 固定不变。</li>
</ol>
<hr />
<ol>
<li><strong>合同矩阵定义</strong>：若存在可逆矩阵 $C$ 使 $C^TAC = B$，则 $A \simeq B$。</li>
</ol>
<hr />
<ol>
<li><strong>合同性质</strong>：秩相等，对称性同步，可逆性一致。</li>
</ol>
<hr />
<ol>
<li><strong>化二次型为标准形方法</strong>：配方法（可逆变换）、正交变换法（标准形系数为特征值）。</li>
</ol>
<hr />
<ol>
<li><strong>二次型定性分类</strong>：正定（$f &gt; 0$）、负定（$f &lt; 0$）、半正定（$f \ge 0$）、半负定（$f \le 0$）、不定。</li>
</ol>
<hr />
<ol>
<li><strong>正定矩阵判定</strong>：正惯性指数 $p = n$；合同于 $E$；特征值全大于0；各阶顺序主子式全大于0（霍尔维茨定理）。</li>
</ol>
<hr />
<ol>
<li><strong>正定矩阵性质</strong>：$A^T, A^{-1}, A^*$ 均正定；$kA(k&gt;0)$、$A^m$ 正定；正定矩阵之和正定。</li>
</ol>
<h3>公式</h3>
<ol>
<li>二次型矩阵形式：$f = \boldsymbol{x}^TA\boldsymbol{x}$（$A^T = A$）</li>
</ol>
<hr />
<ol>
<li>二次型展开形式：$f = \sum_{i=1}^n a_{ii}x_i^2 + 2\sum_{1 \le i &lt; j \le n} a_{ij}x_ix_j$</li>
</ol>
<hr />
<ol>
<li>标准形：$f = d_1y_1^2 + d_2y_2^2 + \dots + d_ny_n^2$</li>
</ol>
<hr />
<ol>
<li>规范形：$f = z_1^2 + \dots + z_p^2 - z_{p+1}^2 - \dots - z_r^2$</li>
</ol>
<hr />
<ol>
<li>二次型的秩：$r(f) = r(A)$</li>
</ol>
<hr />
<ol>
<li>合同矩阵定义：$C^TAC = B$</li>
</ol>
<hr />
<ol>
<li>惯性指数关系：$r = p + q$，符号差 = $p - q$</li>
</ol>
<hr />
<ol>
<li>正交变换化标准形：$f = \lambda_1y_1^2 + \lambda_2y_2^2 + \dots + \lambda_ny_n^2$（$\lambda_i$ 为特征值）</li>
</ol>
<hr />
<ol>
<li>顺序主子式：$D_k = \begin{vmatrix} a_{11} &amp; \dots &amp; a_{1k} \ \vdots &amp; &amp; \vdots \ a_{k1} &amp; \dots &amp; a_{kk} \end{vmatrix}$</li>
</ol>
<hr />
<ol>
<li>正定矩阵判定：$D_k &gt; 0$（$k=1,2,\dots,n$）</li>
</ol>
]]></content>
    <author><name>仙鹤</name></author>
    <category term="考试"/>
  </entry>
  <entry>
    <title>大学物理B1</title>
    <link href="https://xianhe51920.github.io/posts/physics-b1/" rel="alternate" type="text/html"/>
    <id>https://xianhe51920.github.io/posts/physics-b1/</id>
    <published>2026-06-22T00:00:00.000Z</published>
    <updated>2026-06-22T00:00:00.000Z</updated>
    <summary>大一下物理：大学物理 B1</summary>
    <content type="html"><![CDATA[<h1>大学物理B1</h1>
<hr />
<h2>目录</h2>
<ol>
<li><a href="#01%E8%B4%A8%E7%82%B9%E8%BF%90%E5%8A%A8%E5%AD%A6%E4%B8%80">01质点运动学（一）</a></li>
<li><a href="#02%E8%B4%A8%E7%82%B9%E8%BF%90%E5%8A%A8%E5%AD%A6%E4%BA%8C">02质点运动学（二）</a></li>
<li><a href="#03%E5%B8%B8%E8%A7%81%E5%8A%9B%E5%92%8C%E7%89%9B%E9%A1%BF%E4%B8%89%E5%AE%9A%E5%BE%8B">03常见力和牛顿三定律</a></li>
<li><a href="#04%E5%8A%A8%E9%87%8F%E5%86%B2%E9%87%8F%E5%8A%A8%E9%87%8F%E5%AE%88%E6%81%92">04动量,冲量,动量守恒</a></li>
<li><a href="#05%E8%B4%A8%E7%82%B9%E8%BF%90%E5%8A%A8%E7%9A%84%E5%8A%9F%E5%92%8C%E8%83%BD">05质点运动的功和能</a></li>
<li><a href="#01%E7%94%B5%E5%9C%BA%E5%BC%BA%E5%BA%A6">01电场强度</a></li>
<li><a href="#02%E7%94%B5%E9%80%9A%E9%87%8F%E9%AB%98%E6%96%AF%E5%AE%9A%E7%90%86">02电通量,高斯定理</a></li>
<li><a href="#03%E7%94%B5%E5%8A%BF%E7%94%B5%E5%8A%BF%E8%83%BD">03电势,电势能</a></li>
<li><a href="#04%E5%AF%BC%E4%BD%93">04导体</a></li>
<li><a href="#05%E7%94%B5%E5%AE%B9%E5%99%A8">05电容器</a></li>
<li><a href="#06%E7%94%B5%E4%BB%8B%E8%B4%A8%E7%94%B5%E5%9C%BA%E8%83%BD">06电介质,电场能</a></li>
<li><a href="#07%E7%A3%81%E6%84%9F%E5%BA%94%E5%BC%BA%E5%BA%A6">07磁感应强度</a></li>
<li><a href="#08%E7%A3%81%E9%80%9A%E9%87%8F%E9%AB%98%E6%96%AF%E5%AE%9A%E7%90%86">08磁通量,高斯定理</a></li>
<li><a href="#09%E5%AE%89%E5%9F%B9%E5%8A%9B%E7%A3%81%E5%8A%9B%E7%9F%A9%E6%B4%9B%E4%BC%A6%E5%85%B9%E5%8A%9B">09安培力,磁力矩,洛伦兹力</a></li>
<li><a href="#10%E7%A3%81%E4%BB%8B%E8%B4%A8%E7%A3%81%E5%9C%BA%E8%83%BD">10磁介质,磁场能</a></li>
<li><a href="#11%E7%94%B5%E7%A3%81%E6%84%9F%E5%BA%94">11电磁感应</a></li>
<li><a href="#12%E8%87%AA%E6%84%9F%E5%92%8C%E4%BA%92%E6%84%9F%E9%80%89%E5%AD%A6">12自感和互感（选学）</a></li>
</ol>
<hr />
<h1>01质点运动学（一）</h1>
<h2>考点概览</h2>
<table>
<thead>
<tr>
<th>考点</th>
<th>重要程度</th>
<th>占分</th>
<th>常见题型</th>
</tr>
</thead>
<tbody>
<tr>
<td>1. 位移/速度/加速度</td>
<td>基础知识</td>
<td>0~3</td>
<td>选择</td>
</tr>
<tr>
<td>2. $\vec{r} \to \vec{v} \to \vec{a}$ 型</td>
<td>必考</td>
<td>5~10</td>
<td>大题</td>
</tr>
<tr>
<td>3. $\vec{a} \to \vec{v} \to \vec{r}$ 型</td>
<td>必考</td>
<td>5~10</td>
<td>大题</td>
</tr>
<tr>
<td>4. 相对运动</td>
<td>★★</td>
<td>0~3</td>
<td>填空</td>
</tr>
</tbody>
</table>
<hr />
<h2>1. 位移/速度/加速度</h2>
<h3>基本概念和公式</h3>
<p>① <strong>位矢（位置矢量）</strong>：
$$\vec{r} = x\vec{i} + y\vec{j}$$</p>
<p>② <strong>位移</strong>：
$$\Delta\vec{r} = \vec{r}_B - \vec{r}_A$$</p>
<p>③ <strong>路程</strong>：$\overset{\frown}{AB}$（弧长）</p>
<p>④ <strong>速度</strong>：</p>
<ul>
<li>平均速度：$\vec{\bar{v}} = \frac{\Delta\vec{r}}{\Delta t} = \frac{\vec{r}_B - \vec{r}_A}{t_2 - t_1}$</li>
<li>瞬时速度：$\vec{v} = \frac{d\vec{r}}{dt} = \frac{dx}{dt}\vec{i} + \frac{dy}{dt}\vec{j}$</li>
<li>大小：$|\vec{v}| = \left|\frac{d\vec{r}}{dt}\right| = \sqrt{\left(\frac{dx}{dt}\right)^2 + \left(\frac{dy}{dt}\right)^2} = \frac{ds}{dt}$</li>
</ul>
<p>⑤ <strong>加速度</strong>：
$$\vec{a} = \frac{d\vec{v}}{dt} = \frac{dv_x}{dt}\vec{i} + \frac{dv_y}{dt}\vec{j} = \frac{dv}{dt}\vec{\tau} + \frac{v^2}{R}\vec{n}$$</p>
<ul>
<li>切向加速度：$a_t = \frac{dv}{dt}$</li>
<li>法向加速度：$a_n = \frac{v^2}{R}$</li>
<li>大小：$|\vec{a}| = \sqrt{\left(\frac{dv_x}{dt}\right)^2 + \left(\frac{dv_y}{dt}\right)^2} = \sqrt{\left(\frac{dv}{dt}\right)^2 + \left(\frac{v^2}{R}\right)^2}$</li>
</ul>
<p>/2.png)</p>
<h3>例题</h3>
<p><strong>题1</strong>：一质点在xoy平面内运动，其运动学方程为$x = 3\cos4t$，$y = 3\sin4t$，则$t$时刻质点的位矢$\vec{r}(t) =$ ______</p>
<p><strong>解</strong>：$\vec{r} = x\vec{i} + y\vec{j} = 3\cos4t\ \vec{i} + 3\sin4t\ \vec{j}$</p>
<hr />
<p><strong>题2</strong>：已知平面内运动方程为$x = at^2$，$y = bt^2$（其中$a, b$为常量），则该质点运动轨迹为（ ）</p>
<p>A. 双曲线 B. 抛物线 C. 圆周 D. 直线</p>
<p><strong>解</strong>：
$$\begin{cases} x = at^2 \ y = bt^2 \end{cases} \Rightarrow \frac{x}{y} = \frac{a}{b} \Rightarrow y = \frac{b}{a}x$$
轨迹为直线，选<strong>D</strong></p>
<hr />
<p><strong>题3</strong>：一运动质点在某瞬间矢径$\vec{r}(x, y)$的端点处，其速度大小为（ ）</p>
<p>A. $\frac{dr}{dt}$ B. $\frac{d\vec{r}}{dt}$ C. $\frac{d|\vec{r}|}{dt}$ D. $\sqrt{\left(\frac{dx}{dt}\right)^2 + \left(\frac{dy}{dt}\right)^2}$</p>
<p><strong>解</strong>：速度大小为$|\vec{v}| = \sqrt{\left(\frac{dx}{dt}\right)^2 + \left(\frac{dy}{dt}\right)^2}$，选<strong>D</strong></p>
<p>/4.png)</p>
<hr />
<p><strong>题4</strong>：质点作半径为$R$的变速圆周运动时的加速度大小为（ ）（$v$表示任一时刻质点的速率）</p>
<p>A. $\frac{dv}{dt}$ B. $\frac{v^2}{R}$ C. $\frac{dv}{dt} + \frac{v^2}{R}$ D. $\sqrt{\left(\frac{dv}{dt}\right)^2 + \left(\frac{v^2}{R}\right)^2}$</p>
<p><strong>解</strong>：加速度大小为$|\vec{a}| = \sqrt{\left(\frac{dv}{dt}\right)^2 + \left(\frac{v^2}{R}\right)^2}$，选<strong>D</strong></p>
<p>/5.png)</p>
<hr />
<h2>2. $\vec{r} \to \vec{v} \to \vec{a}$ 型</h2>
<p>已知位矢求速度和加速度，解题方法：<strong>求导</strong></p>
<h3>例题</h3>
<p><strong>题1</strong>：质点的运动方程为$\vec{r} = (2 + 2t^2)\vec{i} + \left(\frac{1}{3}t^3 + 2t\right)\vec{j}$，求$t = 2$时的速度和加速度。</p>
<p><strong>解</strong>：
$$\vec{v} = \frac{d\vec{r}}{dt} = 4t\vec{i} + (t^2 + 2)\vec{j}$$
$$\vec{v}\big|_{t=2} = 8\vec{i} + 6\vec{j}$$</p>
<p>$$\vec{a} = \frac{d\vec{v}}{dt} = 4\vec{i} + 2t\vec{j}$$
$$\vec{a}\big|_{t=2} = 4\vec{i} + 4\vec{j}$$</p>
<hr />
<p><strong>题2</strong>：已知某质点的运动方程为$x = 2t$，$y = 2 - t^2$，式中$x$以$m$计，$t$以$s$计，求：</p>
<ol>
<li>位置矢量表达式，速度和加速度表达式；</li>
<li>前2s内质点的平均速度和平均加速度；</li>
<li>第2s内质点的平均速度；</li>
<li>计算1s末和2s末质点的加速度。</li>
</ol>
<p><strong>解</strong>：</p>
<p>(1)
$$\vec{r} = 2t\vec{i} + (2 - t^2)\vec{j}$$
$$\vec{v} = \frac{d\vec{r}}{dt} = 2\vec{i} - 2t\vec{j}$$
$$\vec{a} = \frac{d\vec{v}}{dt} = -2\vec{j}$$</p>
<p>(2) 前2s内（$t=0$到$t=2$）：
$$\vec{r}_0 = 2\vec{j}, \quad \vec{v}_0 = 2\vec{i}$$
$$\vec{r}_2 = 4\vec{i} - 2\vec{j}, \quad \vec{v}_2 = 2\vec{i} - 4\vec{j}$$
$$\Delta\vec{r} = \vec{r}_2 - \vec{r}_0 = 4\vec{i} - 4\vec{j}$$
$$\vec{\bar{v}} = \frac{\Delta\vec{r}}{\Delta t} = \frac{4\vec{i} - 4\vec{j}}{2 - 0} = 2\vec{i} - 2\vec{j}$$
$$\Delta\vec{v} = \vec{v}_2 - \vec{v}_0 = -4\vec{j}$$
$$\vec{\bar{a}} = \frac{\Delta\vec{v}}{\Delta t} = \frac{-4\vec{j}}{2 - 0} = -2\vec{j}$$</p>
<p>(3) 第2s内（$t=1$到$t=2$）：
$$\vec{r}_1 = 2\vec{i} + \vec{j}, \quad \vec{r}_2 = 4\vec{i} - 2\vec{j}$$
$$\Delta\vec{r} = \vec{r}_2 - \vec{r}_1 = 2\vec{i} - 3\vec{j}$$
$$\vec{\bar{v}} = \frac{\Delta\vec{r}}{\Delta t} = 2\vec{i} - 3\vec{j}$$</p>
<p>(4) 1s末和2s末质点的加速度：
$$\vec{a} = -2\vec{j}$$</p>
<hr />
<h2>3. $\vec{a} \to \vec{v} \to \vec{r}$ 型</h2>
<p>已知加速度求速度和位矢，解题方法：<strong>积分</strong></p>
<h3>解题方法</h3>
<p>$$a = \frac{dv}{dt} \Rightarrow dv = adt \Rightarrow \int_{v_0}^{v} dv = \int_{0}^{t} adt$$</p>
<p>$$v = \frac{dx}{dt} \Rightarrow dx = vdt \Rightarrow \int_{x_0}^{x} dx = \int_{0}^{t} vdt$$</p>
<h3>匀变速直线运动公式</h3>
<p>$$v = v_0 + at$$
$$x = x_0 + v_0t + \frac{1}{2}at^2$$
$$v_2^2 - v_1^2 = 2a(x_2 - x_1)$$</p>
<h3>例题</h3>
<p><strong>题1</strong>：设质点沿$x$轴作匀变速直线运动，加速度为$a$不随时间变化，初速度为$v_0$，初位置为$x_0$，试根据速度、加速度的定义求出该质点的速度公式和运动学方程。</p>
<p><strong>解</strong>：
$$a = \frac{dv}{dt} \Rightarrow dv = adt \Rightarrow \int_{v_0}^{v} dv = \int_{0}^{t} adt$$
$$v - v_0 = at \Rightarrow v = v_0 + at$$</p>
<p>$$v = \frac{dx}{dt} \Rightarrow dx = vdt = (v_0 + at)dt \Rightarrow \int_{x_0}^{x} dx = \int_{0}^{t} (v_0 + at)dt$$
$$x - x_0 = v_0t + \frac{1}{2}at^2 \Rightarrow x = x_0 + v_0t + \frac{1}{2}at^2$$</p>
<hr />
<p><strong>题2</strong>：一质点做直线运动，加速度$a = 2m/s^2$，开始时$v_1 = 2m/s$，一段时间后$v_2 = 6m/s$，问质点在这段时间内的位移大小。</p>
<p><strong>解</strong>：由$v_2^2 - v_1^2 = 2a(x_2 - x_1)$
$$6^2 - 2^2 = 2 \times 2 \times (x_2 - x_1)$$
$$36 - 4 = 4(x_2 - x_1)$$
$$x_2 - x_1 = 8\ m$$</p>
<hr />
<p><strong>题3</strong>：质点沿直线运动，加速度$a = 4 - t^2$，式中$a$的单位为$m/s^2$，$t$的单位为$s$，如果当$t = 3s$时，$x = 9m$，$v = 2m/s$，求质点的运动方程。</p>
<p><strong>解</strong>：
$$a = 4 - t^2 = \frac{dv}{dt} \Rightarrow dv = (4 - t^2)dt \Rightarrow \int_{v_0}^{v} dv = \int_{0}^{t} (4 - t^2)dt$$
$$v = v_0 + 4t - \frac{1}{3}t^3 \quad \text{①}$$</p>
<p>$$v = \frac{dx}{dt} \Rightarrow dx = vdt = \left(v_0 + 4t - \frac{1}{3}t^3\right)dt \Rightarrow \int_{x_0}^{x} dx = \int_{0}^{t} \left(v_0 + 4t - \frac{1}{3}t^3\right)dt$$
$$x = x_0 + v_0t + 2t^2 - \frac{1}{12}t^4 \quad \text{②}$$</p>
<p>将$t = 3s$，$x = 9m$，$v = 2m/s$代入①②得：
$$v_0 = -1m/s, \quad x_0 = 0.75m$$</p>
<p>所以质点的运动方程为：
$$x = 0.75 - t + 2t^2 - \frac{1}{12}t^4 \quad (m)$$</p>
<hr />
<p><strong>题4</strong>：一质点沿一直线运动，其加速度为$a = -2x$，式中$x$的单位为$m$，$a$的单位为$m/s^2$。试求该质点的速度$v$与位置坐标$x$之间的关系。设当$x_0 = 1$时，$v_0 = 4m/s$。</p>
<p><strong>解</strong>：
$$a = -2x = \frac{dv}{dt} = \frac{dv}{dx} \cdot \frac{dx}{dt} = \frac{dv}{dx} \cdot v$$
$$\frac{dv}{dx} \cdot v = -2x$$</p>
<p>分离变量得：
$$vdv = -2xdx \Rightarrow \int_{v_0}^{v} vdv = \int_{x_0}^{x} -2xdx$$</p>
<p>$$\frac{1}{2}v^2 - \frac{1}{2}v_0^2 = -x^2 + x_0^2$$</p>
<p>代入$x_0 = 1$，$v_0 = 4$：
$$\frac{1}{2}v^2 - 8 = -x^2 + 1$$
$$v^2 = -2x^2 + 18$$</p>
<hr />
<h2>4. 相对运动</h2>
<h3>相对关系</h3>
<p>$$\vec{v}<em>{绝对} = \vec{v}</em>{牵连} + \vec{v}<em>{相对}$$
$$\vec{a}</em>{绝对} = \vec{a}<em>{牵连} + \vec{a}</em>{相对}$$</p>
<h3>例题</h3>
<p><strong>题1</strong>：甲船以$v_1 = 10m/s$的速度向南航行，乙船以$v_2 = 10m/s$的速度向东航行，则甲船上的人观察乙船的速度大小为 ______</p>
<p><strong>解</strong>：</p>
<ul>
<li>$v_1$：牵连速度（甲船相对于地面）</li>
<li>$v_2$：绝对速度（乙船相对于地面）</li>
</ul>
<p>$$\vec{v}<em>2 = \vec{v}<em>1 + \vec{v}</em>{相对}$$
$$\vec{v}</em>{相对} = \vec{v}_2 - \vec{v}_1$$</p>
<p>由勾股定理：
$$v_{相对} = \sqrt{v_1^2 + v_2^2} = \sqrt{10^2 + 10^2} = 10\sqrt{2}\ m/s$$</p>
<p>/12.png)</p>
<hr />
<h1>02质点运动学（二）</h1>
<h2>考点概览</h2>
<table>
<thead>
<tr>
<th>考点</th>
<th>重要程度</th>
<th>占分</th>
<th>题型</th>
</tr>
</thead>
<tbody>
<tr>
<td>1. 角位移/角速度/角加速度</td>
<td>基础知识</td>
<td>不单独出题</td>
<td>无</td>
</tr>
<tr>
<td>2. $\theta \to \omega \to \beta$ 型</td>
<td>★★</td>
<td>3~10</td>
<td>选填为主，偶尔大题</td>
</tr>
<tr>
<td>3. $\beta \to \omega \to \theta$ 型</td>
<td>★★★</td>
<td>3~10</td>
<td>选填为主，偶尔大题</td>
</tr>
<tr>
<td>4. 角量与线量关系</td>
<td>必考</td>
<td>3~10</td>
<td>选填为主，偶尔大题</td>
</tr>
</tbody>
</table>
<hr />
<h2>1. 角位移/角速度/角加速度</h2>
<h3>基本概念和公式</h3>
<ul>
<li><strong>角位移</strong>：$\theta$，单位：$\text{rad}$（弧度）</li>
<li><strong>角速度</strong>：$\omega = \frac{d\theta}{dt}$，单位：$\text{rad/s}$</li>
<li><strong>角加速度</strong>：$\beta = \frac{d\omega}{dt} = \frac{d^2\theta}{dt^2}$，单位：$\text{rad/s}^2$</li>
<li><strong>周期</strong>：$T = \frac{2\pi}{\omega}$，单位：$\text{s}$（秒）</li>
</ul>
<p>/2.png)</p>
<hr />
<h2>2. $\theta \to \omega \to \beta$ 型</h2>
<p>已知角位置求角速度和角加速度，解题方法：<strong>求导</strong></p>
<h3>例题</h3>
<p><strong>题1</strong>：某质点上的角位置和时间关系为$\theta = 4t - 3t^2 + t^3$（SI），则在2秒末的角速度大小$\omega =$ ______，角加速度大小$\beta =$ ______，在2秒末到4秒末这段时间内，平均角速度大小$\bar{\omega} =$ ______。</p>
<p><strong>解</strong>：
$$\omega = \frac{d\theta}{dt} = 4 - 6t + 3t^2$$
$$\omega\big|_{t=2} = 4 - 6 \times 2 + 3 \times 2^2 = 4\ \text{rad/s}$$</p>
<p>$$\beta = \frac{d\omega}{dt} = -6 + 6t$$
$$\beta\big|_{t=2} = -6 + 6 \times 2 = 6\ \text{rad/s}^2$$</p>
<p>$$t = 2\ \text{s}\ \text{时}\ \theta_2 = 4$$
$$t = 4\ \text{s}\ \text{时}\ \theta_4 = 32$$
$$\Delta\theta = 32 - 4 = 28$$</p>
<p>$$\bar{\omega} = \frac{\Delta\theta}{\Delta t} = \frac{28}{4 - 2} = 14\ \text{rad/s}$$</p>
<hr />
<h2>3. $\beta \to \omega \to \theta$ 型</h2>
<p>已知角加速度求角速度和角位置，解题方法：<strong>积分</strong></p>
<h3>解题方法</h3>
<p>$$\beta = \frac{d\omega}{dt} \Rightarrow d\omega = \beta dt \Rightarrow \int_{\omega_0}^{\omega} d\omega = \int_{0}^{t} \beta dt$$</p>
<p>$$\omega = \frac{d\theta}{dt} \Rightarrow d\theta = \omega dt \Rightarrow \int_{\theta_0}^{\theta} d\theta = \int_{0}^{t} \omega dt$$</p>
<h3>匀变速圆周运动公式</h3>
<p>$$\omega = \omega_0 + \beta t$$
$$\theta = \theta_0 + \omega_0t + \frac{1}{2}\beta t^2$$
$$\omega_2^2 - \omega_1^2 = 2\beta(\theta_2 - \theta_1)$$</p>
<h3>例题</h3>
<p><strong>题1</strong>：已知匀变速圆周运动，角加速度为$\beta$，$t = 0$时，角速度为$\omega_0$，角位移为$\theta_0$，试用定义公式，求角速度和角位移表达式。</p>
<p><strong>解</strong>：
$$\beta = \frac{d\omega}{dt} \Rightarrow d\omega = \beta dt \Rightarrow \int_{\omega_0}^{\omega} d\omega = \int_{0}^{t} \beta dt$$
$$\omega - \omega_0 = \beta t \Rightarrow \omega = \omega_0 + \beta t$$</p>
<p>$$\omega = \frac{d\theta}{dt} \Rightarrow d\theta = \omega dt = (\omega_0 + \beta t)dt \Rightarrow \int_{\theta_0}^{\theta} d\theta = \int_{0}^{t} (\omega_0 + \beta t)dt$$
$$\theta - \theta_0 = \omega_0t + \frac{1}{2}\beta t^2 \Rightarrow \theta = \theta_0 + \omega_0t + \frac{1}{2}\beta t^2$$</p>
<hr />
<p><strong>题2</strong>：搅拌机叶片以恒定角加速度$1.50\ \text{rad/s}^2$转动，求：
(1) 从静止启动后经过多少时间角速度将达到$36.0\ \text{rad/s}$？
(2) 在此时间内共转过多少转？</p>
<p><strong>解</strong>：
(1) 由$\omega = \omega_0 + \beta t$得：
$$36 = 0 + 1.5t \Rightarrow t = 24\ \text{s}$$</p>
<p>(2) 由$\theta = \theta_0 + \omega_0t + \frac{1}{2}\beta t^2$：
$$\theta = 0 + 0 + \frac{1}{2} \times 1.5 \times 24^2 = 432\ \text{rad}$$</p>
<p>$$n = \frac{\theta}{2\pi} = \frac{432}{2 \times 3.14} = 68.8\ \text{（转）}$$</p>
<hr />
<p><strong>题3</strong>：绕定轴转动的飞轮，均匀减速，$t = 0$时$\omega_0 = 5\ \text{rad/s}$，$t = 20$时$\omega = 0.8\omega_0$，则飞轮的角加速度$\beta =$ ______ $\text{rad/s}^2$，转过的角度$\theta =$ ______ $\text{rad}$。</p>
<p><strong>解</strong>：
$$t = 0\ \text{时}\ \omega_0 = 5,\quad t = 20\ \text{时}\ \omega = 0.8\omega_0 = 4$$</p>
<p>由$\omega = \omega_0 + \beta t$：
$$4 = 5 + \beta \times 20 \Rightarrow \beta = -0.05\ \text{rad/s}^2$$</p>
<p>由$\omega^2 - \omega_0^2 = 2 \cdot \beta \cdot \Delta\theta$：
$$\Delta\theta = \frac{\omega^2 - \omega_0^2}{2\beta} = \frac{4^2 - 5^2}{-2 \times 0.05} = 90\ \text{rad}$$</p>
<hr />
<h2>4. 角量与线量关系（必考）</h2>
<h3>基本关系</h3>
<table>
<thead>
<tr>
<th>物理量</th>
<th>角量表达式</th>
<th>线量表达式</th>
<th>关系</th>
</tr>
</thead>
<tbody>
<tr>
<td>速度</td>
<td>$\omega$</td>
<td>$v$</td>
<td>$v = \omega R$</td>
</tr>
<tr>
<td>切向加速度</td>
<td>$\beta$</td>
<td>$a_t$</td>
<td>$a_t = \beta R$</td>
</tr>
<tr>
<td>法向加速度</td>
<td>$\omega$</td>
<td>$a_n$</td>
<td>$a_n = \omega^2 R = \frac{v^2}{R}$</td>
</tr>
</tbody>
</table>
<p>/7.png)</p>
<h3>例题</h3>
<p><strong>题1</strong>：质点在作半径为$R$的圆周运动：</p>
<ul>
<li>质点的线速度$v$与角速度$\omega$的关系为$v = \omega R$；</li>
<li>质点的切向加速度$a_t$与角加速度$\beta$的关系为$a_t = \beta R$；</li>
<li>质点的法向加速度$a_n$与角速度$\omega$的关系为$a_n = \omega^2 R$。</li>
</ul>
<hr />
<p><strong>题2</strong>：质点沿半径为$R$的圆周运动，运动学方程为$\theta = 3 + 2t^2$（SI），则$t$时刻质点的法向加速度大小为$a_n =$ ______，切向加速度$a_t =$ ______。</p>
<p><strong>解</strong>：
$$\omega = \frac{d\theta}{dt} = 4t,\quad \beta = \frac{d\omega}{dt} = 4$$</p>
<p>$$a_n = \omega^2 R = (4t)^2 R = 16Rt^2$$</p>
<p>$$a_t = \beta R = 4R$$</p>
<hr />
<p><strong>题3</strong>：一质点在半径为$0.1\ \text{m}$的圆周上运动，其角位置变化关系为$\theta = 2 + 4t^3$（rad）。问：
(1) 在$t = 2\ \text{s}$时，质点的法向加速度和切向加速度大小各为多少？
(2) 当切向加速度大小恰好等于总加速度大小的一半时，$\theta$值为多少？
(3) 在什么时刻，切向加速度和法向加速度恰好大小相等？</p>
<p><strong>解</strong>：
$$\omega = \frac{d\theta}{dt} = 12t^2,\quad \beta = \frac{d\omega}{dt} = 24t$$</p>
<p>(1)
$$a_n = \omega^2 R = (12t^2)^2 \times 0.1 = 14.4t^4$$
$$a_n\big|_{t=2} = 14.4 \times 2^4 = 230.4\ \text{m/s}^2$$</p>
<p>$$a_t = \beta R = 24t \times 0.1 = 2.4t$$
$$a_t\big|_{t=2} = 2.4 \times 2 = 4.8\ \text{m/s}^2$$</p>
<p>(2)
$$a = \sqrt{a_t^2 + a_n^2} = \sqrt{(2.4t)^2 + (14.4t^4)^2}$$</p>
<p>依题意$a_t = \frac{1}{2}a$：
$$2.4t = \frac{1}{2}\sqrt{(2.4t)^2 + (14.4t^4)^2}$$
$$t^3 = \frac{\sqrt{3}}{6}$$
$$\theta = 2 + 4t^3 = 2 + 4 \times \frac{\sqrt{3}}{6} = 3.15\ \text{rad}$$</p>
<p>(3)
依题意$a_t = a_n$：
$$2.4t = 14.4t^4$$
解得：$t = 0.55\ \text{s}$</p>
<hr />
<h1>03常见力和牛顿三定律</h1>
<h2>考点概览</h2>
<table>
<thead>
<tr>
<th>考点</th>
<th>重要程度</th>
<th>占分</th>
<th>常见题型</th>
</tr>
</thead>
<tbody>
<tr>
<td>1. 常见力</td>
<td>基础知识</td>
<td>5~10</td>
<td>选择、填空、大题</td>
</tr>
<tr>
<td>2. 牛顿三定律</td>
<td>基础知识</td>
<td>5~10</td>
<td>选择、填空、大题</td>
</tr>
</tbody>
</table>
<hr />
<h2>1. 常见力</h2>
<h3>基本概念和公式</h3>
<ol>
<li>
<p><strong>重力</strong>：$G = mg$，$g = 9.8N/kg$</p>
</li>
<li>
<p><strong>弹力</strong>：$F = kx$（$k$为弹性系数）</p>
</li>
<li>
<p><strong>摩擦力</strong>：</p>
<ul>
<li>①滑动摩擦：$f = \mu_k N$（$\mu_k$为滑动摩擦系数，$N$为支持力）</li>
<li>②静摩擦：$0 \leq f \leq \mu_s N$（$\mu_s$为最大静摩擦系数）</li>
</ul>
</li>
<li>
<p><strong>万有引力</strong>：$F = G\frac{Mm}{r^2}$，$G = 6.67 \times 10^{-11} N \cdot m^2/kg^2$</p>
</li>
</ol>
<hr />
<h2>2. 牛顿三定律</h2>
<h3>牛顿三定律内容</h3>
<ol>
<li>
<p><strong>牛顿第一定律</strong>：不受力或合外力为0，质点保持静止或匀速直线运动</p>
</li>
<li>
<p><strong>牛顿第二定律</strong>：$F = ma$（力是物体产生加速度的原因）</p>
</li>
<li>
<p><strong>牛顿第三定律</strong>：$F_{作用} = F_{反作用}$（作用力等于反作用力）</p>
</li>
</ol>
<h3>解题步骤</h3>
<ol>
<li>选物体</li>
<li>分析受力</li>
<li>列方程</li>
<li>求解</li>
</ol>
<h3>例题</h3>
<p><strong>题1</strong>：关于摩擦力的说法，下列哪一种说法正确（ ）</p>
<p>A. 摩擦力总是阻碍物体运动
B. 摩擦力的方向总是与物体运动方向相反
C. 摩擦力总是对物体做负功
D. 以上说法都不对</p>
<p><strong>解</strong>：选<strong>D</strong></p>
<hr />
<p><strong>题2</strong>：用水平力$F_N$把一个物体压着靠在粗糙的竖直墙面上保持静止，当$F_N$逐渐增大时，物体所受到的静摩擦力$F_f$的大小（ ）</p>
<p>A. 不为零，但保持不变
B. 随$F_N$成正比增大
C. 开始随$F_N$增大，达到某一最大值后就保持不变
D. 无法确定</p>
<p><strong>解</strong>：$F_N = N$，$F_N$增大则$N$增大，但静摩擦力$F_f = mg$，与$F_N$无关，保持不变。选<strong>A</strong></p>
<hr />
<p><strong>题3</strong>：两物体A和B，质量分别是$m_1$和$m_2$，互相接触放在光滑水平面上，对物体A施以水平推力$F$，则物体A对物体B的作用力等于______。</p>
<p><strong>解</strong>：
$$F = (m_1 + m_2)a \quad \text{①}$$
$$F - F_{AB} = m_1a \quad \text{②}$$</p>
<p>由①②得：$F_{AB} = \frac{m_2}{m_1 + m_2}F$</p>
<hr />
<p><strong>题4</strong>：设电梯中有一质量可以忽略的滑轮，在滑轮两侧用轻绳悬挂着质量分别为$m_1$和$m_2$的重物A和B，已知$m_1 &gt; m_2$，当电梯匀速上升，求绳中的张力$T$和物体A相对于电梯的加速度$a$。</p>
<p><strong>解</strong>：
对A受力分析：$m_1g - T = m_1a$</p>
<p>对B受力分析：$T - m_2g = m_2a$</p>
<p>联立解得：
$$T = \frac{2m_1m_2}{m_1 + m_2}g$$
$$a = \frac{m_1 - m_2}{m_1 + m_2}g$$</p>
<hr />
<p><strong>题5</strong>：一艘行驶的质量为$m$的快艇，在发动机关闭后，受到一阻力作用，且$f = -kv^2$，式中$k$为正常数，求快艇在关闭发动机后速度与行驶距离的关系。（已知发动机关闭时快艇速度为$v_0$）</p>
<p><strong>解</strong>：根据牛顿第二定律：
$$f = -kv^2 = ma = m\frac{dv}{dt} = m\frac{dv}{dx} \cdot \frac{dx}{dt} = mv\frac{dv}{dx}$$
$$-kv = m\frac{dv}{dx}$$</p>
<p>分离变量：$\frac{1}{v}dv = -\frac{k}{m}dx$</p>
<p>两边同时积分：
$$\int_{v_0}^{v} \frac{1}{v}dv = \int_{0}^{x} -\frac{k}{m}dx$$
$$\ln v - \ln v_0 = -\frac{k}{m}x$$
$$\ln \frac{v}{v_0} = -\frac{k}{m}x$$
$$\frac{v}{v_0} = e^{-\frac{k}{m}x}$$
$$v = v_0 e^{-\frac{k}{m}x}$$</p>
<hr />
<p><strong>题6</strong>：简述牛顿定律的适用范围</p>
<p>(1) 只适用于低速运动的物体（与光速比速度较低）；
(2) 只适用于宏观物体，不适用于微观原子；
(3) 参照系应为惯性系。</p>
<hr />
<h1>04动量,冲量,动量守恒</h1>
<h2>考点概览</h2>
<table>
<thead>
<tr>
<th>考点</th>
<th>重要程度</th>
<th>占分</th>
<th>常见题型</th>
</tr>
</thead>
<tbody>
<tr>
<td>1. 动量定理</td>
<td>必考</td>
<td>5~10</td>
<td>选/填</td>
</tr>
<tr>
<td>2. 动量守恒</td>
<td>必考</td>
<td>5~10</td>
<td>大题</td>
</tr>
</tbody>
</table>
<hr />
<h2>1. 动量定理</h2>
<h3>基本概念和公式</h3>
<ul>
<li><strong>动量</strong>：$\vec{p} = m\vec{v}$，单位：$kg \cdot m/s$</li>
<li><strong>动量定理</strong>：$\vec{I} = m\vec{v}_2 - m\vec{v}<em>1 = \int</em>{t_1}^{t_2} \vec{F}dt$，单位：$N \cdot s$</li>
</ul>
<h3>要点</h3>
<p>①冲量为矢量，等于动量的矢量差，也等于冲力对时间的积分</p>
<p>②$\vec{F}$为冲力，为合外力</p>
<p>③$\vec{F}$若为常力，$\vec{I} = m\vec{v}_2 - m\vec{v}_1 = \vec{F} \cdot \Delta t$</p>
<h3>例题</h3>
<p><strong>题1</strong>：一物体质量为$m$，$t_1$时刻速度大小为$v_0$，方向沿$x$负方向，$t_2$时刻速度大小仍为$v_0$，方向沿$y$轴正方向，$t_1$到$t_2$冲量大小为______</p>
<p><strong>解</strong>：
$$I = \sqrt{(mv_0)^2 + (mv_0)^2} = \sqrt{2}mv_0$$</p>
<hr />
<p><strong>题2</strong>：一垒球的质量$m = 0.20kg$，如果其投出时的速度为$30m/s$，被棒击回的速度为$50m/s$，方向相反，球的冲量大小为______；球与棒的接触时间为$\Delta t = 0.0020s$，则棒击打垒球的平均冲力$F =$ ______</p>
<p><strong>解</strong>：
$$I = mv_2 - mv_1 = 0.2 \times 50 - 0.2 \times (-30) = 16\ N \cdot s$$</p>
<p>$$I = F \cdot \Delta t$$
$$16 = F \times 0.002$$
$$F = 8000N$$</p>
<hr />
<p><strong>题3</strong>：质量为$3kg$的静止物体在水平力$F = 3t^2$（$N$）作用下，在光滑水平面上作直线运动，物体在$0 \sim 3$秒内获得的冲量______$N \cdot s$，第3秒末物体的速度值______$m/s$</p>
<p><strong>解</strong>：
$$I = \int_{t_1}^{t_2} Fdt = \int_{0}^{3} 3t^2 dt = 27\ N \cdot s$$</p>
<p>$$I = mv_2 - mv_1 = mv_2$$
$$27 = 3 \times v_2 \Rightarrow v_2 = 9\ m/s$$</p>
<hr />
<p><strong>题4</strong>：一静止的质点，在合力为$\vec{F} = 10t\vec{i} + 2(2-t)\vec{j}$作用下，在$2s$末的动量为______</p>
<p><strong>解</strong>：
$$F_x = 10t,\quad F_y = 2(2-t)$$</p>
<p>$$mv_{x_2} - 0 = \int_{0}^{2} F_x dt = \int_{0}^{2} 10t dt = 20$$</p>
<p>$$mv_{y_2} - 0 = \int_{0}^{2} F_y dt = \int_{0}^{2} 2(2-t) dt = 4$$</p>
<p>$$\vec{p}_2 = 20\vec{i} + 4\vec{j}$$</p>
<hr />
<h2>2. 动量守恒</h2>
<h3>动量守恒条件</h3>
<ol>
<li>
<p>$F_{合} = 0$：不受外力或所受合外力为零</p>
</li>
<li>
<p>内力不改变系统的总动量（例如：爆炸）</p>
</li>
<li>
<p>内力远大于外力时，也可认为$F_{合} = 0$</p>
</li>
</ol>
<h3>动量守恒定律</h3>
<p>$$\sum m_i \vec{v}<em>{i1} = \sum m_i \vec{v}</em>{i2}$$</p>
<h3>例题</h3>
<p><strong>题1</strong>：粒子B的质量是粒子A的质量的4倍，开始时粒子A的速度为$3\vec{i}$，粒子B的速度为$2\vec{i}$，由于两者的相互作用，粒子A的速度变为$7\vec{i}$，此时粒子B的速度为（ ）</p>
<p>A. $\vec{i}$ B. $2\vec{i}$ C. $0$ D. $5\vec{i}$</p>
<p><strong>解</strong>：由动量守恒：
$$m \cdot 3 + 4m \cdot 2 = m \cdot 7 + 4m \cdot v_B$$
$$3 + 8 = 7 + 4v_B$$
$$v_B = 1$$</p>
<p>选<strong>A</strong></p>
<hr />
<p><strong>题2</strong>：一人站在长度为$4m$的船一端，船漂浮于静止水面上。船的质量为$600kg$，人的质量为$80kg$，若此人从船头走到船尾，则船相对于水面移动了多少米？（忽略水对船的阻力）</p>
<p><strong>解</strong>：水平方向动量守恒：
$$mv_{人} = Mv_{船} \Rightarrow m\frac{dx_1}{dt} = M\frac{dx_2}{dt}$$</p>
<p>整理得：$mdx_1 = Mdx_2$</p>
<p>两边同时积分：
$$\int_{0}^{x_1} mdx_1 = \int_{0}^{x_2} Mdx_2 \Rightarrow mx_1 = Mx_2$$</p>
<p>即：$80x_1 = 600x_2$</p>
<p>又：$x_1 + x_2 = 4$</p>
<p>联立两式解得：
$$\begin{cases} x_1 = 3.53m \ x_2 = 0.47m \end{cases}$$</p>
<p>故船移动了$0.47m$</p>
<hr />
<p><strong>题3</strong>：下列几种说法正确的是（ ）</p>
<p>(1) 作用力的冲量与反作用力的冲量总是等值相反的
(2) 系统的内力不能改变系统的总动量
(3) 冲量的方向与物体动量的方向相同
(4) 以恒力作用于物体，时间越长，物体的动量越大</p>
<p>A. 只有(1)是正确的 B. (1)(2)是正确的 C. (1)(3)是正确的 D. (2)(4)是正确的</p>
<p><strong>解</strong>：选<strong>B</strong></p>
<hr />
<p><strong>题4</strong>：判断下列运动是否动量守恒</p>
<table>
<thead>
<tr>
<th>运动类型</th>
<th>是否守恒</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>匀速圆周运动</td>
<td>不守恒</td>
<td>动量方向不断改变</td>
</tr>
<tr>
<td>卫星绕地球</td>
<td>不守恒</td>
<td>受万有引力作用</td>
</tr>
<tr>
<td>弹性碰撞</td>
<td>守恒</td>
<td>系统动量守恒，机械能守恒</td>
</tr>
<tr>
<td>非弹性碰撞</td>
<td>守恒</td>
<td>系统动量守恒，机械能减小</td>
</tr>
<tr>
<td>完全非弹性碰撞</td>
<td>守恒</td>
<td>系统动量守恒，机械能损失最大</td>
</tr>
<tr>
<td>子弹打击木块（绳）</td>
<td>守恒</td>
<td>水平方向无外力</td>
</tr>
<tr>
<td>子弹打击木块（杆）</td>
<td>不守恒</td>
<td>杆受外力作用</td>
</tr>
</tbody>
</table>
<hr />
<h1>05质点运动的功和能</h1>
<h2>考点概览</h2>
<table>
<thead>
<tr>
<th>考点</th>
<th>重要程度</th>
<th>占分</th>
<th>常见题型</th>
</tr>
</thead>
<tbody>
<tr>
<td>1. 做功</td>
<td>必考</td>
<td>5~10</td>
<td>填空、大题</td>
</tr>
<tr>
<td>2. 动能定理</td>
<td>必考</td>
<td>5~10</td>
<td>填空、大题</td>
</tr>
<tr>
<td>3. 保守力、势能</td>
<td>★★</td>
<td>5~10</td>
<td>填空、大题</td>
</tr>
<tr>
<td>4. 机械能守恒</td>
<td>必考</td>
<td>5~10</td>
<td>填空、大题</td>
</tr>
</tbody>
</table>
<hr />
<h2>1. 做功</h2>
<h3>基本概念和公式</h3>
<p>① <strong>元功</strong>：$dA = \vec{F} \cdot d\vec{r}$</p>
<p>② <strong>功</strong>：
$$A = \int \vec{F} \cdot d\vec{r}$$</p>
<p>当 $F = f(x)$ 时：
$$A = \int_{x_1}^{x_2} F dx$$</p>
<p>③ <strong>恒力做功</strong>：
$$A = \vec{F} \cdot \vec{r} = F_x x + F_y y + F_z z$$</p>
<h3>例题</h3>
<p><strong>题1</strong>：某质点在力 $F = (4 + 5x)$ 的作用下沿 $x$ 轴做直线运动，在从 $x = 0$ 移动到 $x = 1$ 的过程中，力 $F$ 所做的功为______</p>
<p><strong>解</strong>：
$$A = \int_{0}^{1} F dx = \int_{0}^{1} (4 + 5x) dx = 4x + \frac{5}{2}x^2 \Big|_{0}^{1} = 4 + \frac{5}{2} = 6.5\ J$$</p>
<hr />
<p><strong>题2</strong>：一质点在恒力 $\vec{F} = 3\vec{i} - 5\vec{j} + 6\vec{k}$ 的作用下产生位移为 $\vec{r} = 2\vec{i} - 4\vec{j} - 3\vec{k}$，在该位移过程中所做的功为______</p>
<p><strong>解</strong>：
$$A = \vec{F} \cdot \vec{r} = 3 \times 2 + (-5) \times (-4) + 6 \times (-3) = 6 + 20 - 18 = 8\ J$$</p>
<hr />
<p><strong>题3</strong>：一人从 $10m$ 深的井中提水，起始桶中装有 $10kg$ 的水，由于水桶漏水，每升高 $1m$ 要漏去 $0.2kg$ 的水，水桶被匀速地从井中提到井口，求人所做的功。</p>
<p><strong>解</strong>：</p>
<p>设水桶升高 $x$ 米时，水的质量为：
$$m = 10 - 0.2x$$</p>
<p>人所施加的力为：
$$F = mg = (10 - 0.2x)g$$</p>
<p>人所做的功为：
$$A = \int_{0}^{10} F dx = \int_{0}^{10} (10 - 0.2x)g dx = g \left(10x - 0.1x^2\right) \Big|_{0}^{10} = g(100 - 10) = 90g = 882\ J$$</p>
<hr />
<h2>2. 动能定理</h2>
<h3>基本概念和公式</h3>
<p>① <strong>动能</strong>：
$$E_k = \frac{1}{2}mv^2$$</p>
<p>② <strong>动能定理</strong>：
$$A = E_{k2} - E_{k1} = \frac{1}{2}mv_2^2 - \frac{1}{2}mv_1^2$$</p>
<p>合外力对物体所做的功等于物体动能的增量。</p>
<h3>例题</h3>
<p><strong>题1</strong>：质量为 $1kg$ 的物体运动速率由 $2m/s$ 增加到 $4m/s$ 的过程中，合外力对它所做的功为______</p>
<p><strong>解</strong>：
$$A = \frac{1}{2}mv_2^2 - \frac{1}{2}mv_1^2 = \frac{1}{2} \times 1 \times 4^2 - \frac{1}{2} \times 1 \times 2^2 = 8 - 2 = 6\ J$$</p>
<hr />
<p><strong>题2</strong>：用铁锤把钉子敲入墙面木板，设木板对钉子的阻力与钉子进入木板的深度成正比，即 $F = kx$。若第一次打击时，能把钉子打入木板 $1cm$。第二次打击时，保持第一次打击的速度，第二次能把钉子打入的深度为______</p>
<p><strong>解</strong>：</p>
<p>设第一次打击前钉子速度为 $v$，第一次打击后钉子深度为 $x_1 = 1cm$</p>
<p>第一次打击：
$$\frac{1}{2}mv^2 = \int_{0}^{x_1} kx dx = \frac{1}{2}kx_1^2$$</p>
<p>第二次打击（钉子从深度 $x_1$ 打到深度 $x_2$）：
$$\frac{1}{2}mv^2 = \int_{x_1}^{x_2} kx dx = \frac{1}{2}k(x_2^2 - x_1^2)$$</p>
<p>由两次打击动能相等：
$$\frac{1}{2}kx_1^2 = \frac{1}{2}k(x_2^2 - x_1^2)$$
$$x_1^2 = x_2^2 - x_1^2$$
$$x_2^2 = 2x_1^2$$
$$x_2 = \sqrt{2}x_1 = \sqrt{2}cm$$</p>
<p>第二次打入深度：
$$\Delta x = x_2 - x_1 = (\sqrt{2} - 1)cm$$</p>
<hr />
<p><strong>题3</strong>：质量均匀分布的钢性链条，总长为 $L$，伸出光滑桌面的长度为 $a$，若由静止释放。求：链条全部脱离光滑桌面时速率。</p>
<p><strong>解</strong>：</p>
<p>设链条线密度为 $\lambda = \frac{m}{L}$</p>
<p>链条从伸出 $a$ 到全部脱离桌面，重心下降高度：
$$h = \frac{L}{2} - \frac{a}{2} = \frac{L - a}{2}$$</p>
<p>链条重力做功：
$$A = mg \cdot h = m \cdot g \cdot \frac{L - a}{2}$$</p>
<p>由动能定理：
$$A = \frac{1}{2}mv^2 - 0$$
$$mg \cdot \frac{L - a}{2} = \frac{1}{2}mv^2$$
$$v = \sqrt{g(L - a)}$$</p>
<hr />
<h2>3. 保守力、势能</h2>
<h3>基本概念和公式</h3>
<p>① <strong>保守力</strong>：做功与路径无关，只与始末位置有关的力。</p>
<p>② <strong>保守力的特点</strong>：</p>
<ul>
<li>保守力做功与路径无关，只与始末位置有关</li>
<li>保守力沿一闭合路径运动一周，做功为零</li>
<li>势能的引入是以保守力做功为前提</li>
<li>不同的势能零点，对应的势能值不一样</li>
<li>势能大小 = 保守力把物体移至势能零点所做的功</li>
<li>保守力做正功，对应势能减小</li>
<li>功能计算时，功-能计算一个，就要忽略这个保守力</li>
</ul>
<p>③ <strong>常用保守力势能及零势能点</strong>：</p>
<table>
<thead>
<tr>
<th>保守力</th>
<th>势能表达式</th>
<th>常用零势能点</th>
</tr>
</thead>
<tbody>
<tr>
<td>重力 $G = mg$</td>
<td>$E_p = mgh$</td>
<td>地面</td>
</tr>
<tr>
<td>弹力 $F = kx$</td>
<td>$E_p = \frac{1}{2}kx^2$</td>
<td>平衡位置</td>
</tr>
<tr>
<td>万有引力 $F = G\frac{Mm}{r^2}$</td>
<td>$E_p = -G\frac{Mm}{r}$</td>
<td>无穷远处</td>
</tr>
<tr>
<td>静电力 $F = \frac{1}{4\pi\varepsilon_0}\frac{q_1q_2}{r^2}$</td>
<td>$E_p = \frac{1}{4\pi\varepsilon_0}\frac{q_1q_2}{r}$</td>
<td>无穷远处</td>
</tr>
</tbody>
</table>
<hr />
<h2>4. 机械能守恒</h2>
<h3>基本概念和公式</h3>
<p>① <strong>机械能</strong>：
$$E = E_k + E_p = \frac{1}{2}mv^2 + E_p$$</p>
<p>② <strong>机械能守恒定律</strong>：
$$E_{k1} + E_{p1} = E_{k2} + E_{p2}$$</p>
<ol>
<li>机械能只包含动能和势能</li>
<li>系统内只有保守力做功，其他内力和一切外力都不做功</li>
</ol>
<h3>例题</h3>
<p><strong>题1</strong>：如图所示，质量 $m = 2kg$ 的物体从静止开始，沿 $1/4$ 圆弧从 $A$ 滑到 $B$，在 $B$ 处的速度大小为 $v = 6m/s$，已知圆的半径 $R = 4m$，则物体从 $A$ 到 $B$ 的过程中摩擦力对它所做的功为______</p>
<p><strong>解</strong>：</p>
<p>$A$ 点机械能：
$$E_A = mgR$$</p>
<p>$B$ 点机械能：
$$E_B = \frac{1}{2}mv^2$$</p>
<p>摩擦力做功等于机械能损失：
$$A_f = E_B - E_A = \frac{1}{2}mv^2 - mgR = \frac{1}{2} \times 2 \times 6^2 - 2 \times 9.8 \times 4 = 36 - 78.4 = -42.4\ J$$</p>
<hr />
<p><strong>题2</strong>：质量为 $m$ 的子弹，穿过如图所示的摆锤后，速率由 $v$ 变为 $\frac{v}{2}$。已知摆锤的质量为 $M$，摆线的长度为 $L$，如果摆锤能在垂直平面内完成一个完全的圆周运动，子弹的速度最小值应为多大？</p>
<p><strong>解</strong>：</p>
<p>子弹穿过摆锤瞬间，动量守恒：
$$mv = m \cdot \frac{v}{2} + Mv'$$
$$v' = \frac{m}{M} \cdot \frac{v}{2}$$</p>
<p>摆锤可以完成圆周运动，则在最高点满足：
$$Mg = M\frac{v_{最高}^2}{L}$$
$$v_{最高} = \sqrt{gL}$$</p>
<p>摆锤开始上扬，满足机械能守恒：
$$\frac{1}{2}Mv'^2 = \frac{1}{2}Mv_{最高}^2 + Mg \cdot 2L$$
$$\frac{1}{2}M \cdot \left(\frac{m}{M} \cdot \frac{v}{2}\right)^2 = \frac{1}{2}M \cdot gL + 2MgL$$
$$\frac{m^2v^2}{4M^2} = 5gL$$
$$v = \frac{2M}{m}\sqrt{5gL}$$</p>
<hr />
<p><strong>题3</strong>：子弹水平地射入一端固定在弹簧上的木块内，由弹簧压缩的距离求出子弹的速度。已知子弹质量是 $0.02kg$，木块质量是 $8.98kg$，弹簧的劲度系数是 $100N/m$，子弹射入木块后，弹簧被压缩 $0.1m$。设木块与平面间的动摩擦因数为 $0.2$，求子弹的速度。</p>
<p><strong>解</strong>：</p>
<p>子弹射入木块瞬间，动量守恒：
$$mv = (m + M)v'$$
$$v' = \frac{m}{m + M}v = \frac{0.02}{0.02 + 8.98}v = \frac{0.02}{9}v$$</p>
<p>弹簧开始压缩，满足功能关系：
$$\frac{1}{2}(m + M)v'^2 - \mu(m + M)g \cdot x = \frac{1}{2}kx^2$$
$$\frac{1}{2} \times 9 \times \left(\frac{0.02}{9}v\right)^2 - 0.2 \times 9 \times 9.8 \times 0.1 = \frac{1}{2} \times 100 \times 0.1^2$$
$$\frac{1}{2} \times 9 \times \frac{0.0004}{81}v^2 - 1.764 = 0.5$$
$$\frac{0.0004}{18}v^2 = 2.264$$
$$v^2 = \frac{2.264 \times 18}{0.0004} = 101880$$
$$v = 319.2\ m/s$$</p>
<hr />
<p><strong>题4</strong>：对于一个物体来说，在下列的哪种情况下系统的机械能守恒（ ）</p>
<p>A. 合外力为0<br />
B. 合外力不做功<br />
C. 外力和非保守力都不做功<br />
D. 外力和保守内力都不做功</p>
<p><strong>解</strong>：机械能守恒条件：系统内只有保守力做功，其他内力和一切外力都不做功。选<strong>C</strong></p>
<hr />
<p><strong>题5</strong>：对功的概念有以下几种说法：</p>
<p>(1) 保守力作正功时，系统内相应的势能增加；<br />
(2) 质点运动经一闭合路径，保守力对质点作的功为零；<br />
(3) 作用力与反作用力大小相等，方向相反，所以两者所做功的代数和必为零。</p>
<p>以上说法正确的是（ ）</p>
<p>A. (1)(2)是正确的<br />
B. (2)(3)是正确的<br />
C. 只有(2)是正确的<br />
D. 只有(3)是正确的</p>
<p><strong>解</strong>：</p>
<ul>
<li>(1) 错误：保守力做正功时，势能减小</li>
<li>(2) 正确：保守力沿闭合路径做功为零</li>
<li>(3) 错误：作用力与反作用力做功的代数和不一定为零</li>
</ul>
<p>选<strong>C</strong></p>
<hr />
<h1>01电场强度</h1>
<h2>考点概览</h2>
<table>
<thead>
<tr>
<th>考点</th>
<th>重要程度</th>
<th>占分</th>
<th>常见题型</th>
</tr>
</thead>
<tbody>
<tr>
<td>1. 离散型</td>
<td>★★</td>
<td>0~3</td>
<td>选择/填空</td>
</tr>
<tr>
<td>2. 连续型</td>
<td>★★★★★</td>
<td>5~10</td>
<td>选择/填空/大题</td>
</tr>
<tr>
<td>3. 结论型</td>
<td>★★★★★</td>
<td>5~10</td>
<td>选择/填空/大题</td>
</tr>
</tbody>
</table>
<hr />
<h2>1. 求电场强度—离散型</h2>
<h3>基本概念和公式</h3>
<p><strong>库仑定律</strong>：
$$F = \frac{1}{4\pi\varepsilon_0} \cdot \frac{q \cdot q_0}{r^2}$$
(1) 同性相斥，异性相吸
(2) 电场力跟$q, q_0$电量成正比，跟$r^2$成反比
(3) 真空介电常量：$\varepsilon_0 = 8.85 \times 10^{-12}\ C^2/(N \cdot m^2)$</p>
<p><strong>电场强度</strong>：
$$E = \frac{F}{q_0} = \frac{1}{4\pi\varepsilon_0} \cdot \frac{q}{r^2}$$
(矢量，有大小有方向)</p>
<p>(1) $\vec{E}$方向判定：将正电荷放入电场中，受力方向即$\vec{E}$的方向
(2) 电场强度跟场源电荷$q$和位置$r$有关，与试探电荷$q_0$无关</p>
<h3>例题</h3>
<p><strong>题1</strong>：在点电荷$q_1$激发的电场中有一试探电荷$q_0$受力为$\vec{F}$，若将另一个点电荷$q_2$移入该区域后，$q_0$与$q_1$之间的作用力将（ ）</p>
<p>A. 不变 B. 变大 C. 变小 D. 无法确定</p>
<p><strong>解</strong>：$q_0$与$q_1$之间的作用力只与它们之间的距离和电量有关，与$q_2$无关。选<strong>A</strong></p>
<hr />
<p><strong>题2</strong>：如图所示，求在$P$点和$Q$点处的场强和方向？</p>
<p><strong>解</strong>：</p>
<p><strong>P点</strong>：
$$E = E_1 - E_2 = \frac{1}{4\pi\varepsilon_0} \cdot \frac{q}{(2l)^2} - \frac{1}{4\pi\varepsilon_0} \cdot \frac{q}{(4l)^2} = \frac{3q}{64\pi\varepsilon_0 l^2}$$
方向沿$x$轴正方向</p>
<p><strong>Q点</strong>：
$$E_1 = E_2 = \frac{1}{4\pi\varepsilon_0} \cdot \frac{q}{(2l)^2} = \frac{q}{16\pi\varepsilon_0 l^2}$$
$$E = E_y = \frac{q}{16\pi\varepsilon_0 l^2}$$
方向沿$y$轴负方向</p>
<hr />
<h2>2. 求电场强度—连续型</h2>
<h3>解题方法</h3>
<p>$$dE = \frac{1}{4\pi\varepsilon_0} \cdot \frac{dq}{r^2}$$
$$E = \int dE = \int \frac{1}{4\pi\varepsilon_0} \cdot \frac{dq}{r^2}$$</p>
<p>常用积分公式：
$$\int xdx = \frac{1}{2}x^2 + C$$
$$\int dx = x + C$$</p>
<h3>例题</h3>
<p><strong>题1</strong>：均匀带电细棒，棒长$L$，电荷线密度$\lambda$，求：棒的延长线上与棒的右端相距$d$处的场强。</p>
<p><strong>解</strong>：在带电棒上取一线元$dx$，则$dx$所带的电荷量：$dq = \lambda \cdot dx$</p>
<p>在$P$处：
$$dE = \frac{1}{4\pi\varepsilon_0} \cdot \frac{\lambda \cdot dx}{(L + d - x)^2}$$</p>
<p>$$E = \int dE = \int_{0}^{L} \frac{1}{4\pi\varepsilon_0} \cdot \frac{\lambda}{(L + d - x)^2} dx$$
$$= \frac{\lambda}{4\pi\varepsilon_0} \cdot \frac{1}{(L + d - x)} \bigg|_{0}^{L} = \frac{\lambda}{4\pi\varepsilon_0} \left( \frac{1}{d} - \frac{1}{L + d} \right)$$</p>
<p>另一种坐标设定（原点在棒右端）：
$$dE = \frac{1}{4\pi\varepsilon_0} \cdot \frac{\lambda \cdot dx}{(x + d)^2}$$</p>
<p>$$E = \int_{0}^{L} \frac{1}{4\pi\varepsilon_0} \cdot \frac{\lambda}{(x + d)^2} dx$$
$$= \frac{\lambda}{4\pi\varepsilon_0} \cdot \frac{-1}{(x + d)} \bigg|_{0}^{L} = \frac{\lambda}{4\pi\varepsilon_0} \left( \frac{1}{d} - \frac{1}{L + d} \right)$$</p>
<hr />
<p><strong>题2</strong>：一个半径为$R$的均匀带正电半圆环，电荷线密度为$\lambda$，求环心处$O$点的场强和方向。</p>
<p><strong>解</strong>：在半圆环上任取线元$dl$，$dl = Rd\theta$，所带电量：$dq = \lambda \cdot dl = \lambda \cdot Rd\theta$</p>
<p>在$O$点产生的场强：
$$dE = \frac{1}{4\pi\varepsilon_0} \cdot \frac{\lambda \cdot dl}{R^2} = \frac{\lambda}{4\pi\varepsilon_0 R^2} dl$$</p>
<p>分量分解：
$$dE_x = dE \cdot \sin\theta = \frac{\lambda}{4\pi\varepsilon_0 R^2} \sin\theta \cdot Rd\theta = \frac{\lambda}{4\pi\varepsilon_0 R} \sin\theta d\theta$$
$$dE_y = dE \cdot \cos\theta = \frac{\lambda}{4\pi\varepsilon_0 R} \cos\theta d\theta$$</p>
<p>积分：
$$E_x = \int_{0}^{\pi} \frac{\lambda}{4\pi\varepsilon_0 R} \sin\theta d\theta = \frac{\lambda}{2\pi\varepsilon_0 R}$$
$$E_y = \int_{0}^{\pi} \frac{\lambda}{4\pi\varepsilon_0 R} \cos\theta d\theta = 0$$</p>
<p>$$E = E_x = \frac{\lambda}{2\pi\varepsilon_0 R}$$
方向朝$x$正方向</p>
<hr />
<h2>3. 求电场强度—结论型</h2>
<h3>常用结论汇总</h3>
<table>
<thead>
<tr>
<th>带电体</th>
<th>场强公式</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>点电荷</td>
<td>$E = \frac{1}{4\pi\varepsilon_0} \cdot \frac{q}{r^2}$</td>
<td></td>
</tr>
<tr>
<td>无限长均匀带电直线</td>
<td>$E = \frac{\lambda}{2\pi\varepsilon_0 r}$</td>
<td>$r$为到直线距离</td>
</tr>
<tr>
<td>无限大均匀带电平面</td>
<td>$E = \frac{\sigma}{2\varepsilon_0}$</td>
<td>与距离无关</td>
</tr>
<tr>
<td>均匀带电圆柱面</td>
<td>$E = 0$（$r &lt; R$）&lt;br&gt;$E = \frac{\lambda}{2\pi\varepsilon_0 r}$（$r &gt; R$）</td>
<td>高斯推导</td>
</tr>
<tr>
<td>均匀带电球体</td>
<td>$E = \frac{qr}{4\pi\varepsilon_0 R^3}$（$r &lt; R$）&lt;br&gt;$E = \frac{q}{4\pi\varepsilon_0 r^2}$（$r &gt; R$）</td>
<td>高斯推导</td>
</tr>
</tbody>
</table>
<h3>例题</h3>
<p><strong>题1</strong>：均匀带电"无限大"平面上的面电荷密度为$\sigma_0$，其场强与场点到带电平板的距离无关，其场强大小为______</p>
<p><strong>解</strong>：$E = \frac{\sigma_0}{2\varepsilon_0}$</p>
<hr />
<p><strong>题2</strong>：真空中两个平行的"无限大"均匀带电平面，电荷面密度为$+\sigma$和$-2\sigma$，则$A, B, C$三个区域的场强分别为：</p>
<p><strong>解</strong>：</p>
<ul>
<li>$A$区：$\vec{E}_A = \frac{\sigma}{2\varepsilon_0}\vec{i}$</li>
<li>$B$区：$\vec{E}_B = \frac{3\sigma}{2\varepsilon_0}\vec{i}$</li>
<li>$C$区：$\vec{E}_C = -\frac{\sigma}{2\varepsilon_0}\vec{i}$</li>
</ul>
<hr />
<p><strong>题3</strong>：一宽为$b$的无限长均匀带电平面薄板，其电荷面密度为$\sigma$，试求平板所在平面内，距薄板边缘为$a$处的电场强度。</p>
<p><strong>解</strong>：薄板内任取一窄条，宽为$dx$，窄条可视为无限长带电线，线密度：$\lambda = \sigma dx$</p>
<p>无限长带电线在$P$点的场强：
$$dE = \frac{\sigma dx}{2\pi\varepsilon_0 \left(a + \frac{b}{2} - x\right)}$$</p>
<p>整个薄板在$P$点的场强：
$$E = \int_{-\frac{b}{2}}^{\frac{b}{2}} \frac{\sigma dx}{2\pi\varepsilon_0 \left(a + \frac{b}{2} - x\right)} = \frac{\sigma}{2\pi\varepsilon_0} \ln\frac{a + b}{a}$$</p>
<hr />
<h1>02电通量,高斯定理</h1>
<h2>考点概览</h2>
<table>
<thead>
<tr>
<th>考点</th>
<th>重要程度</th>
<th>占分</th>
<th>常见题型</th>
</tr>
</thead>
<tbody>
<tr>
<td>1. 电通量</td>
<td>★★★★★</td>
<td>0~3</td>
<td>选择、填空</td>
</tr>
<tr>
<td>2. 高斯定理求场强</td>
<td>必考</td>
<td>5~10</td>
<td>大题</td>
</tr>
</tbody>
</table>
<hr />
<h2>1. 电通量</h2>
<h3>电场线性质</h3>
<p>(1) 电场线总是起自正电荷，终止于负电荷。</p>
<p>(2) 电场线不会自成闭合线，任意两条电场线也不会相交。</p>
<p>(3) 电场线密度大的地方，电场强度$E$越大。</p>
<h3>电通量公式</h3>
<p>(1) <strong>电通量</strong>：$\Phi_e = E \cdot S$</p>
<p>(2) $E$和$S$必须是垂直关系</p>
<p>(3) 对于<strong>曲面</strong>，向外穿出为正，向内穿入为负</p>
<p><strong>三种情况</strong>：</p>
<ul>
<li>平面垂直电场：$\Phi_e = E \cdot S$</li>
<li>平面与电场成$\theta$角：$\Phi_e = E \cdot S \cdot \cos\theta$</li>
<li>曲面：$d\Phi_e = \vec{E} \cdot d\vec{S}$，$\Phi_e = \int d\Phi_e = \iint \vec{E} \cdot d\vec{S}$</li>
</ul>
<h3>例题</h3>
<p><strong>题1</strong>：一电场强度为$\vec{E}$的均匀电场，$\vec{E}$的方向沿$x$轴正向，则通过图中一半径为$R$的半球面的电场强度通量为（ ）</p>
<p>A. $\frac{\pi R^2 E}{2}$ B. $0$ C. $2\pi R^2 E$ D. $\pi R^2 E$</p>
<p><strong>解</strong>：选<strong>B</strong>（电场线穿入和穿出相等，总通量为0）</p>
<hr />
<p><strong>题2</strong>：如图，在电场强度$E$的匀强电场中，有一半径为$R$的半球面，场强$E$的方向与半球面对称轴平行，穿过此半球面的电通量为（ ）</p>
<p>A. $2\pi R^2 E$ B. $\sqrt{2}\pi R^2 E$ C. $\pi R^2 E$ D. $\frac{\pi R^2 E}{2}$</p>
<p><strong>解</strong>：$\Phi_e = E \cdot S = \pi \cdot R^2 E$，选<strong>C</strong></p>
<hr />
<p><strong>题3</strong>：有一半径为$R$的半球面如图所示，放在均匀电场中，通过半球面的电通量（ ）</p>
<p>A. $2\pi R^2 E$ B. $-\pi R^2 E$ C. $2\pi R^2 \cos\theta$ D. $-\pi R^2 E \cos\theta$</p>
<p><strong>解</strong>：$\Phi_e = -E \cdot S \cos\theta = -\pi \cdot R^2 E \cos\theta$，选<strong>D</strong></p>
<hr />
<h2>2. 静电场中的高斯定理</h2>
<h3>高斯定理内容</h3>
<p>闭合曲面的电通量等于包含电荷量除以$\varepsilon_0$：</p>
<p>$$\Phi_e = \oiint \vec{E} \cdot d\vec{S} = \frac{1}{\varepsilon_0} \sum q_{内}$$</p>
<h3>例题</h3>
<p><strong>题1</strong>：求电荷量为$q$的均匀带电球壳内外场强分布。</p>
<p><strong>解</strong>：由高斯定理：
$$\oiint \vec{E} \cdot d\vec{S} = E \cdot \oiint dS = E \cdot 4\pi r^2 = \frac{1}{\varepsilon_0} \sum q_{内}$$</p>
<p>$$\Rightarrow E = \frac{\sum q_{内}}{4\pi \varepsilon_0 r^2}$$</p>
<ul>
<li>$r &lt; R$时：$\sum q_{内} = 0 \Rightarrow E = 0$</li>
<li>$r &gt; R$时：$\sum q_{内} = q \Rightarrow E = \frac{q}{4\pi \varepsilon_0 r^2}$</li>
</ul>
<hr />
<p><strong>题2</strong>：半径为$R$的球体，均匀带电$q$，试求场强分布。</p>
<p><strong>解</strong>：由高斯定理：
$$\oiint \vec{E} \cdot d\vec{S} = E \cdot 4\pi r^2 = \frac{1}{\varepsilon_0} \sum q_{内} \Rightarrow E = \frac{\sum q_{内}}{4\pi \varepsilon_0 r^2}$$</p>
<p>体密度：$\rho = \frac{q}{V} = \frac{q}{\frac{4}{3}\pi R^3} = \frac{3q}{4\pi R^3}$</p>
<ul>
<li>$r &lt; R$时：$\sum q_{内} = \rho \cdot V_r = \frac{3q}{4\pi R^3} \cdot \frac{4}{3}\pi r^3 = \frac{qr^3}{R^3}$</li>
</ul>
<p>$$E = \frac{qr^3/R^3}{4\pi \varepsilon_0 r^2} = \frac{qr}{4\pi \varepsilon_0 R^3}$$</p>
<ul>
<li>$r &gt; R$时：$\sum q_{内} = q \Rightarrow E = \frac{q}{4\pi \varepsilon_0 r^2}$</li>
</ul>
<hr />
<p><strong>题3</strong>：一半径为$R$的带电球体，其电荷密度$\rho = A \cdot r$（$A &gt; 0$），试求球内外的场强分布。</p>
<p><strong>解</strong>：由高斯定理：
$$\oiint \vec{E} \cdot d\vec{S} = E \cdot 4\pi r^2 = \frac{1}{\varepsilon_0} \sum q_{内} \Rightarrow E = \frac{\sum q_{内}}{4\pi \varepsilon_0 r^2}$$</p>
<p>$dV = 4\pi r^2 \cdot dr$，$dq = \rho \cdot dV = Ar \cdot 4\pi r^2 dr$</p>
<ul>
<li>$r &lt; R$时：$\sum q_{内} = \int_0^r Ar \cdot 4\pi r^2 dr = \pi A r^4$</li>
</ul>
<p>$$E = \frac{\pi A r^4}{4\pi \varepsilon_0 r^2} = \frac{A r^2}{4\varepsilon_0}$$</p>
<ul>
<li>$r &gt; R$时：$\sum q_{内} = \int_0^R Ar \cdot 4\pi r^2 dr = \pi A R^4$</li>
</ul>
<p>$$E = \frac{\pi A R^4}{4\pi \varepsilon_0 r^2} = \frac{A R^4}{4\varepsilon_0 r^2}$$</p>
<hr />
<p><strong>题4</strong>：求半径为$R$，线密度为$\lambda$的无限长均匀带电圆柱体内外场强分布。</p>
<p><strong>解</strong>：由高斯定理：
$$\oiint \vec{E} \cdot d\vec{S} = E \cdot \oiint dS = E \cdot 2\pi r h = \frac{1}{\varepsilon_0} \sum q_{内}$$</p>
<p>$$\Rightarrow E = \frac{\sum q_{内}}{2\pi \varepsilon_0 r h}$$</p>
<ul>
<li>$r &lt; R$时：$\sum q_{内} = \frac{\lambda h}{\pi R^2 h} \cdot \pi r^2 h = \frac{r^2}{R^2} \lambda h$</li>
</ul>
<p>$$E = \frac{\frac{r^2}{R^2} \lambda h}{2\pi \varepsilon_0 r h} = \frac{\lambda r}{2\pi \varepsilon_0 R^2}$$</p>
<ul>
<li>$r &gt; R$时：$\sum q_{内} = \lambda h$</li>
</ul>
<p>$$E = \frac{\lambda h}{2\pi \varepsilon_0 r h} = \frac{\lambda}{2\pi \varepsilon_0 r}$$</p>
<hr />
<p><strong>题5</strong>：点电荷$q$位于一边长为$a$的立方体中心，试求：
(1) 在该点电荷电场中穿过立方体的任一个面的电通量；
(2) 若将该场源点电荷移动到立方体的一个顶点上，则穿过立方体各面的电通量。</p>
<p><strong>解</strong>：</p>
<p>(1) $\Phi_e = \oiint_S \vec{E} \cdot d\vec{S} = \frac{q}{\varepsilon_0}$</p>
<p>$$\Rightarrow \Phi_e' = \frac{\Phi_e}{6} = \frac{q}{6\varepsilon_0}$$</p>
<p>(2) $\Phi_e = \oiint_S \vec{E} \cdot d\vec{S} = \frac{q}{\varepsilon_0}$</p>
<p>$$\Rightarrow \Phi_e' = \frac{\Phi_e}{24} = \frac{q}{24\varepsilon_0}$$</p>
<ul>
<li>与电荷直接相连的三个面电通量为$0$</li>
<li>与电荷不相连的三个面电通量为$\frac{q}{24\varepsilon_0}$</li>
</ul>
<hr />
<p><strong>题6</strong>：点电荷$q_1, q_2, q_3, q_4, q_5$在真空中的分布如图所示，图中为闭合曲面，则通过该闭合曲面的电场强度通量为（ ）</p>
<p>A. $\frac{q_1+q_2+q_3}{\varepsilon_0}$ B. $\frac{q_1+q_2+q_3+q_4+q_5}{\varepsilon_0}$ C. $\frac{q_2+q_3+q_5}{\varepsilon_0}$ D. $\frac{q_1+q_2+q_4}{\varepsilon_0}$</p>
<p><strong>解</strong>：选<strong>D</strong>，电通量只与高斯面内电荷有关</p>
<hr />
<p><strong>题7</strong>：如图所示，闭合曲面$S$内有一点电荷$q$，$P$为$S$面上任一点，$S$面外有一点电荷$q'$，设通过$S$的电通量为$\Phi$，$P$点的场强为$\vec{E}_P$，当$q'$从$A$点移动到$B$点时（ ）</p>
<p>A. $\Phi$改变，$\vec{E}_P$不变 B. $\Phi, \vec{E}_P$都不变</p>
<p>C. $\Phi, \vec{E}_P$都改变 D. $\Phi$不变，$\vec{E}_P$改变</p>
<p><strong>解</strong>：$\Phi$只与高斯面内电荷有关，$\vec{E}_P$由面内和面外电荷共同决定。选<strong>D</strong></p>
<hr />
<p><strong>题8</strong>：在高斯定理中，$\oiint_S \vec{E} \cdot d\vec{S} = \frac{q}{\varepsilon_0}$，$S$和$q$的物理意义是（ ）</p>
<p>A. $S$为闭合面，$q$为$S$内的总电量</p>
<p>B. $S$为闭合面，$q$为空间总电量</p>
<p>C. $S$为任意曲面，$q$为$S$上分布的电量</p>
<p>D. $S$为任意曲面，$q$为空间总电量</p>
<p><strong>解</strong>：选<strong>A</strong></p>
<hr />
<p><strong>题9</strong>：对于高斯定理$\oiint_S \vec{E} \cdot d\vec{S} = \frac{q}{\varepsilon_0}$，以下说法正确的是（ ）</p>
<p>A. 高斯面上的场强仅由高斯面内的电荷所产生</p>
<p>B. 若$\oiint_S \vec{E} \cdot d\vec{S} = 0$则高斯面内必无任何电荷分布</p>
<p>C. 若高斯面上的场强处处为零，则高斯面内无净电荷</p>
<p>D. 高斯面内净电荷等于零，则高斯面上的场强也必等于零</p>
<p><strong>解</strong>：选<strong>C</strong></p>
<hr />
<p><strong>题10</strong>：关于高斯定理的理解有下面几种说法，其中正确的是（ ）</p>
<p>A. 如果高斯面内无电荷，则高斯面上$\vec{E}$处处为零</p>
<p>B. 如果高斯面上$\vec{E}$处处不为零，则该面内必有电荷</p>
<p>C. 如果高斯面上$\vec{E}$处处为零，则该面内必无电荷</p>
<p>D. 如果高斯面内净电荷不为零，则通过该面的电通量必不为零</p>
<p><strong>解</strong>：选<strong>D</strong></p>
<hr />
<h3>高斯定理要点总结</h3>
<p>(1) 电通量与高斯面面内电荷有关，与电荷的位置以及高斯面面外电荷无关</p>
<p>(2) $\sum q_{内}$是指高斯面内的净电荷（所有正负电荷的代数和）</p>
<p>(3) 高斯面上的场强$E$，不仅由面内电荷影响，还由面外电荷影响</p>
<h1>03电势,电势能</h1>
<h2>考点概览</h2>
<table>
<thead>
<tr>
<th>考点</th>
<th>重要程度</th>
<th>占分</th>
<th>常见题型</th>
</tr>
</thead>
<tbody>
<tr>
<td>1. 电势</td>
<td>必考</td>
<td>5~10</td>
<td>大题</td>
</tr>
<tr>
<td>2. 电势能</td>
<td>★★★</td>
<td>0~3</td>
<td>选择、填空</td>
</tr>
<tr>
<td>3. 电势与电场关系</td>
<td>★★★</td>
<td>3~5</td>
<td>选择、填空</td>
</tr>
</tbody>
</table>
<hr />
<h2>1. 电势</h2>
<h3>基本概念和公式</h3>
<p><strong>做功与路径无关</strong> → 电场力是保守力 → 电势能</p>
<p><strong>电势能</strong>：$W_M = \int_{M}^{\infty} \vec{F} \cdot d\vec{l} = \int_{M}^{\infty} q_0 \vec{E} \cdot d\vec{l}$</p>
<p><strong>电势零点</strong>：可以自定义，一般默认无穷远处</p>
<p><strong>电势定义</strong>：
$$V = \frac{W_M}{q_0} = \int_{r}^{\infty} \vec{E} \cdot d\vec{l} = \frac{q}{4\pi \varepsilon_0 r}$$</p>
<p>电势在数值上等于单位正电荷从该点沿任意路径到零势能点电场力做功。</p>
<h3>求电势三种类型</h3>
<p>① <strong>离散型</strong>：$V = \frac{q}{4\pi \varepsilon_0 r}$</p>
<p>② <strong>连续型</strong>：$dV = \frac{dq}{4\pi \varepsilon_0 r}$，$V = \int \frac{dq}{4\pi \varepsilon_0 r}$</p>
<p>③ <strong>已知场强求电势</strong>：$V = \int_{起点}^{\infty} \vec{E} \cdot d\vec{l}$</p>
<h3>例题：连续型</h3>
<p><strong>题1</strong>：一均匀带电半圆环，半径为$R$，电量为$+Q$，求环心处的电势。</p>
<p><strong>解</strong>：带电圆环上取线元$dl$，则$dl$所带的电量：$dq = \frac{Q}{\pi R} \cdot dl$</p>
<p>在$O$点产生的电势：
$$dV = \frac{dq}{4\pi \varepsilon_0 r} = \frac{Q dl}{4\pi^2 \varepsilon_0 R^2}$$</p>
<p>$$V = \int dV = \int_{0}^{\pi R} \frac{Q dl}{4\pi^2 \varepsilon_0 R^2} = \frac{Q}{4\pi \varepsilon_0 R}$$</p>
<p>结论：圆环/半圆环/部分圆环，总带电量为$Q$，在圆心处的电势：$V_O = \frac{Q}{4\pi \varepsilon_0 R}$</p>
<hr />
<p><strong>题2</strong>：一绝缘细棒弯成半径为$R$的1/4圆形环，使之均匀带正电，电荷线密度为$+\lambda$，设无穷远处为电势零点，则在1/4圆环圆心$O$处的电势为（ ）</p>
<p>A. $\frac{\lambda}{8\varepsilon_0}$ B. $\frac{\lambda}{\varepsilon_0}$ C. $\frac{\lambda}{2\varepsilon_0}$ D. $\frac{\lambda}{4\varepsilon_0}$</p>
<p><strong>解</strong>：1/4圆形环带电量：$Q = \lambda \cdot \frac{1}{4} \cdot 2\pi R = \frac{1}{2}\pi R \lambda$</p>
<p>所以在$O$点的电势：
$$V = \frac{Q}{4\pi \varepsilon_0 R} = \frac{\frac{1}{2}\pi R \lambda}{4\pi \varepsilon_0 R} = \frac{\lambda}{8\varepsilon_0}$$</p>
<p>选<strong>A</strong></p>
<hr />
<h3>例题：已知场强求电势</h3>
<p><strong>题1</strong>：求电荷量为$q$的均匀带电球壳内外电势分布。</p>
<p><strong>解</strong>：先由高斯定理求场强：
$$\oiint \vec{E} \cdot d\vec{S} = E \cdot 4\pi r^2 = \frac{1}{\varepsilon_0} \sum q_{内} \Rightarrow E = \frac{\sum q_{内}}{4\pi \varepsilon_0 r^2}$$</p>
<ul>
<li>$r &lt; R$时：$\sum q_{内} = 0 \Rightarrow E_1 = 0$</li>
<li>$r &gt; R$时：$\sum q_{内} = q \Rightarrow E_2 = \frac{q}{4\pi \varepsilon_0 r^2}$</li>
</ul>
<p>再由场强求电势：</p>
<ul>
<li>
<p>$r &lt; R$时：$V = \int_{起点}^{\infty} \vec{E} \cdot d\vec{l} = \int_{r}^{R} E_1 dr + \int_{R}^{\infty} E_2 dr = \int_{r}^{R} 0 dr + \int_{R}^{\infty} \frac{q}{4\pi \varepsilon_0 r^2} dr = \frac{q}{4\pi \varepsilon_0 R}$</p>
</li>
<li>
<p>$r &gt; R$时：$V = \int_{起点}^{\infty} \vec{E} \cdot d\vec{l} = \int_{r}^{\infty} E_2 dr = \int_{r}^{\infty} \frac{q}{4\pi \varepsilon_0 r^2} dr = \frac{q}{4\pi \varepsilon_0 r}$</p>
</li>
</ul>
<hr />
<p><strong>题2</strong>：如图所示，在半径为$R_1$和$R_2$的两个同心球面上分别均匀带电$q_1$和$q_2$，求三个区域内的电势分布。</p>
<p><strong>解</strong>：</p>
<p>(1) 先求场强分布：
$$\oiint \vec{E} \cdot d\vec{S} = E \cdot 4\pi r^2 = \frac{1}{\varepsilon_0} \sum q_{内} \Rightarrow E = \frac{\sum q_{内}}{4\pi \varepsilon_0 r^2}$$</p>
<ul>
<li>$r &lt; R_1$时：$\sum q_{内} = 0 \Rightarrow E_1 = 0$</li>
<li>$R_1 &lt; r &lt; R_2$时：$\sum q_{内} = q_1 \Rightarrow E_2 = \frac{q_1}{4\pi \varepsilon_0 r^2}$</li>
<li>$r &gt; R_2$时：$\sum q_{内} = q_1 + q_2 \Rightarrow E_3 = \frac{q_1 + q_2}{4\pi \varepsilon_0 r^2}$</li>
</ul>
<p>(2) 电势分布：</p>
<ul>
<li>
<p>$r &lt; R_1$时：
$$V_1 = \int_{r}^{R_1} 0 dr + \int_{R_1}^{R_2} \frac{q_1}{4\pi \varepsilon_0 r^2} dr + \int_{R_2}^{\infty} \frac{q_1 + q_2}{4\pi \varepsilon_0 r^2} dr = \frac{1}{4\pi \varepsilon_0} \left( \frac{q_2}{R_2} + \frac{q_1}{R_1} \right)$$</p>
</li>
<li>
<p>$R_1 &lt; r &lt; R_2$时：
$$V_2 = \int_{r}^{R_2} \frac{q_1}{4\pi \varepsilon_0 r^2} dr + \int_{R_2}^{\infty} \frac{q_1 + q_2}{4\pi \varepsilon_0 r^2} dr = \frac{1}{4\pi \varepsilon_0} \left( \frac{q_1}{r} + \frac{q_2}{R_2} \right)$$</p>
</li>
<li>
<p>$r &gt; R_2$时：
$$V_3 = \int_{r}^{\infty} \frac{q_1 + q_2}{4\pi \varepsilon_0 r^2} dr = \frac{q_1 + q_2}{4\pi \varepsilon_0 r}$$</p>
</li>
</ul>
<hr />
<p><strong>题3</strong>：半径为$R_1$和$R_2$的两个无限长同轴圆柱面，单位长度上分别带有电量$\lambda$和$-\lambda$，试求：(1) 空间场强分布；(2) 两圆柱面之间的电势差。</p>
<p><strong>解</strong>：</p>
<p>(1) 由高斯定理：
$$\oiint \vec{E} \cdot d\vec{S} = E \cdot 2\pi r h = \frac{1}{\varepsilon_0} \sum q_{内} \Rightarrow E = \frac{\sum q_{内}}{2\pi \varepsilon_0 r h}$$</p>
<ul>
<li>$r &lt; R_1$时：$\sum q_{内} = 0 \Rightarrow E_1 = 0$</li>
<li>$R_1 &lt; r &lt; R_2$时：$\sum q_{内} = \lambda h \Rightarrow E_2 = \frac{\lambda}{2\pi \varepsilon_0 r}$</li>
<li>$r &gt; R_2$时：$\sum q_{内} = \lambda h - \lambda h = 0 \Rightarrow E_3 = 0$</li>
</ul>
<p>(2) 两圆柱面之间的电势差：
$$V_{AB} = \int_{R_1}^{R_2} \vec{E}<em>2 \cdot d\vec{r} = \int</em>{R_1}^{R_2} \frac{\lambda}{2\pi \varepsilon_0 r} dr = \frac{\lambda}{2\pi \varepsilon_0} \ln \frac{R_2}{R_1}$$</p>
<hr />
<p><strong>题4</strong>：一电荷$q = 10^{-9}C$，$A, B, C$三点分别距离该点电荷10cm, 20cm, 30cm。若选$B$点电势为零，则$A$点的电势为______，$C$点的电势为______。</p>
<p><strong>解</strong>：</p>
<p>$$V_A = \int_{r_A}^{r_B} \vec{E} \cdot d\vec{l} = \int_{r_A}^{r_B} \frac{q}{4\pi \varepsilon_0 r^2} dr = \frac{q}{4\pi \varepsilon_0} \left( \frac{1}{r_A} - \frac{1}{r_B} \right)$$
$$= \frac{10^{-9}}{4\pi \times 8.85 \times 10^{-12}} \left( \frac{1}{0.1} - \frac{1}{0.2} \right) = 45\ V$$</p>
<p>$$V_C = \int_{r_C}^{r_B} \vec{E} \cdot d\vec{l} = \int_{r_C}^{r_B} \frac{q}{4\pi \varepsilon_0 r^2} dr = \frac{q}{4\pi \varepsilon_0} \left( \frac{1}{r_C} - \frac{1}{r_B} \right)$$
$$= \frac{10^{-9}}{4\pi \times 8.85 \times 10^{-12}} \left( \frac{1}{0.3} - \frac{1}{0.2} \right) = -15\ V$$</p>
<hr />
<h2>2. 电势能</h2>
<h3>基本概念和公式</h3>
<p><strong>电势能</strong> = 电荷 × 电势 = 电场力做功</p>
<p>$$W_M = q_0 V = \int_{M}^{零势能点} \vec{F} \cdot d\vec{l}$$</p>
<p><strong>电场力做正功</strong> = <strong>电势能减小</strong></p>
<p>$$A = -(W_2 - W_1)$$</p>
<h3>例题</h3>
<p><strong>题1</strong>：如图所示，在$A、B$两点处有电量分别为$+q、-q$的点电荷，$A、B$间的距离为$2R$，现将另一正试验点电荷$q_0$从$O$点经半圆弧移动到$C$点，电场力所做的功为______。</p>
<p><strong>解</strong>：</p>
<p>$O$点电势：$V_O = \frac{q}{4\pi \varepsilon_0 R} + \frac{-q}{4\pi \varepsilon_0 R} = 0$</p>
<p>$C$点电势：$V_C = \frac{q}{4\pi \varepsilon_0 \cdot 3R} + \frac{-q}{4\pi \varepsilon_0 R} = -\frac{q}{6\pi \varepsilon_0 R}$</p>
<p>电势能变化：$W_2 - W_1 = q_0 V_C - q_0 V_O = -\frac{q q_0}{6\pi \varepsilon_0 R}$</p>
<p>故电场力做功：$A = -(W_2 - W_1) = \frac{q q_0}{6\pi \varepsilon_0 R}$</p>
<hr />
<p><strong>题2</strong>：一电量为$q$的点电荷位于圆心$O$处，$A, B, C, D$为同一圆周上的四点，现将一试验电荷从$A$点分别移动到$B, C, D$各点，则（ ）</p>
<p>A. 从$A$到$B$，电场力做功最大</p>
<p>B. 从$A$到各点，电场力做功相等</p>
<p>C. 从$A$到$D$，电场力做功最大</p>
<p>D. 从$A$到$C$，电场力做功最大</p>
<p><strong>解</strong>：同一圆周上各点电势相等：$V_A = V_B = V_C = V_D$</p>
<p>电势能相等：$q_0 V_A = q_0 V_B = q_0 V_C = q_0 V_D$</p>
<p>所以从$A$到各点，电势能变化为0，电场力做功相等。选<strong>B</strong></p>
<hr />
<h2>3. 电场和电势关系</h2>
<h3>基本关系</h3>
<p>(1) $E = 0 \nRightarrow V = 0$，$E$大小 $\nRightarrow V$大小</p>
<p>(2) 电场线密的地方电场强度越大</p>
<p>(3) <strong>电势沿电场线方向减小</strong></p>
<p>(4) 某点电势随电势零点不同而不同</p>
<p>(5) $\vec{E} = -\left( \frac{\partial V}{\partial x}, \frac{\partial V}{\partial y}, \frac{\partial V}{\partial z} \right)$</p>
<h3>例题</h3>
<p><strong>题1</strong>：描述静电场性质的两个基本物理量是______。</p>
<p><strong>解</strong>：电场强度和电势</p>
<hr />
<p><strong>题2</strong>：在某电场区域内的电场线（实线）和等势面（虚线）如图所示，由图判断出正确结论为（ ）</p>
<p>A. $E_A &gt; E_B &gt; E_C$，$V_A &gt; V_B &gt; V_C$</p>
<p>B. $E_A &gt; E_B &gt; E_C$，$V_A &lt; V_B &lt; V_C$</p>
<p>C. $E_A &lt; E_B &lt; E_C$，$V_A &gt; V_B &gt; V_C$</p>
<p>D. $E_A &lt; E_B &lt; E_C$，$V_A &lt; V_B &lt; V_C$</p>
<p><strong>解</strong>：电场线越密场强越大，故$E_A &lt; E_B &lt; E_C$；电势沿电场线方向减小，故$V_A &gt; V_B &gt; V_C$。选<strong>C</strong></p>
<hr />
<h1>04导体</h1>
<h2>考点概览</h2>
<table>
<thead>
<tr>
<th>考点</th>
<th>重要程度</th>
<th>占分</th>
<th>题型</th>
</tr>
</thead>
<tbody>
<tr>
<td>1. 静电平衡</td>
<td>必考</td>
<td>3~6</td>
<td>选择/填空</td>
</tr>
<tr>
<td>2. 导体的场强和电势</td>
<td>★★★★★</td>
<td>5~10</td>
<td>大题</td>
</tr>
</tbody>
</table>
<hr />
<h2>1. 静电平衡</h2>
<h3>静电平衡条件</h3>
<p>(1) 电荷分布在外表面，内部场强处处为零（$E = 0$）</p>
<p>(2) 导体表面电场强度 $E = \frac{\sigma}{\varepsilon_0}$</p>
<p>(3) 导体是等势体，表面是等势面，但场强并非处处相等</p>
<p>(4) 导体表面曲率越大（尖锐），电荷密度越大</p>
<h3>例题</h3>
<p><strong>题1</strong>：半径为$R$的金属球体是一个等势体，其电势$V = 100V$，则球心处的场强大小为______</p>
<p><strong>解</strong>：$E = 0$（导体内部场强处处为零）</p>
<hr />
<p><strong>题2</strong>：均匀带电无限大平面附近的场强大小为______，均匀带电无限大平面导体附近的场强大小为______</p>
<p><strong>解</strong>：$\frac{\sigma}{2\varepsilon_0}$；$\frac{\sigma}{\varepsilon_0}$</p>
<hr />
<p><strong>题3</strong>：如图所示，金属球$B$被一同心金属球壳$A$所包围，分别给$A,B$两导体以电量$+5C$和$+3C$，则$A$球壳的外表面带电量为（ ）</p>
<p>A. $0C$ B. $+5C$ C. $+8C$ D. $-3C$</p>
<p><strong>解</strong>：$B$球带$+3C$，$A$球壳内表面感应$-3C$，$A$球壳外表面带电量为$5 + 3 = +8C$。选<strong>C</strong></p>
<hr />
<p><strong>题4</strong>：一个带电导体达到静电平衡时（ ）</p>
<p>A. 表面上电荷密度较大处电势较高</p>
<p>B. 导体内任意两点的电势差等于零</p>
<p>C. 导体内部的电势比表面的电势高</p>
<p>D. 表面曲率较大处电势较高</p>
<p><strong>解</strong>：导体是等势体，内部任意两点电势差为零。选<strong>B</strong></p>
<hr />
<p><strong>题5</strong>：如图所示将一个电量为$q$的点电荷放在一个半径为$R$的不带电的导体球附近，点电荷距离导体球球心为$d$，设无穷远处为零势能点，则在导体球球心$O$点有（ ）</p>
<p>A. $E = 0$，$V = \frac{q}{4\pi \varepsilon_0 d}$</p>
<p>B. $E = \frac{q}{4\pi \varepsilon_0 d^2}$，$V = \frac{q}{4\pi \varepsilon_0 d}$</p>
<p>C. $E = 0$，$V = 0$</p>
<p>D. $E = \frac{q}{4\pi \varepsilon_0 d^2}$，$V = \frac{q}{4\pi \varepsilon_0 R}$</p>
<p><strong>解</strong>：导体内部场强为零；电势由点电荷$q$和感应电荷共同产生，球心电势等于点电荷$q$在球心处产生的电势。选<strong>A</strong></p>
<hr />
<p><strong>题6</strong>：一个带电的空腔导体球壳，内半径为$R$，在空腔内离球心的距离为$d$处（$d &lt; R$），固定一点电荷$+q$，则在球心$O$处的电势为______；用导线把球壳接地后，再把地线撤销，选无穷远为电势零点，则球心$O$处的电势为______</p>
<p><strong>解</strong>：</p>
<p>(1) 根据电势叠加：
$$V_O = \frac{q}{4\pi \varepsilon_0 d} + \frac{-q}{4\pi \varepsilon_0 R} + \frac{q}{4\pi \varepsilon_0 R} = \frac{q}{4\pi \varepsilon_0 d}$$</p>
<p>(2) 接地后，球壳外表面电荷为零：
$$V_O = \frac{q}{4\pi \varepsilon_0 d} + \frac{-q}{4\pi \varepsilon_0 R} = \frac{q}{4\pi \varepsilon_0} \left( \frac{1}{d} - \frac{1}{R} \right)$$</p>
<hr />
<h2>2. 导体中的电场和电势</h2>
<h3>例题</h3>
<p><strong>题1</strong>：有一外半径为$R_3$，内半径为$R_2$的金属球壳，在球壳内放一半径为$R_1$的同心金属球，若使金属球带有$q$的正电荷。求：(1) 电荷分布；(2) 电场分布；(3) 电势分布。</p>
<p><strong>解</strong>：</p>
<p>(1) <strong>电荷分布</strong>：</p>
<ul>
<li>金属球表面：$+q$</li>
<li>球壳内表面：$-q$</li>
<li>球壳外表面：$+q$</li>
</ul>
<p>(2) <strong>电场分布</strong>（由高斯定理）：
$$\oiint \vec{E} \cdot d\vec{S} = E \cdot 4\pi r^2 = \frac{1}{\varepsilon_0} \sum q_{内} \Rightarrow E = \frac{\sum q_{内}}{4\pi \varepsilon_0 r^2}$$</p>
<ul>
<li>$r &lt; R_1$时：$\sum q_{内} = 0 \Rightarrow E_1 = 0$</li>
<li>$R_1 &lt; r &lt; R_2$时：$\sum q_{内} = q \Rightarrow E_2 = \frac{q}{4\pi \varepsilon_0 r^2}$</li>
<li>$R_2 &lt; r &lt; R_3$时：$\sum q_{内} = q - q = 0 \Rightarrow E_3 = 0$</li>
<li>$r &gt; R_3$时：$\sum q_{内} = q - q + q = q \Rightarrow E_4 = \frac{q}{4\pi \varepsilon_0 r^2}$</li>
</ul>
<p>(3) <strong>电势分布</strong>：</p>
<ul>
<li>
<p>$r &lt; R_1$时：
$$V = \frac{q}{4\pi \varepsilon_0 R_1} - \frac{q}{4\pi \varepsilon_0 R_2} + \frac{q}{4\pi \varepsilon_0 R_3} = \frac{q}{4\pi \varepsilon_0} \left( \frac{1}{R_1} - \frac{1}{R_2} + \frac{1}{R_3} \right)$$</p>
</li>
<li>
<p>$R_1 &lt; r &lt; R_2$时：
$$V = \frac{q}{4\pi \varepsilon_0 r} - \frac{q}{4\pi \varepsilon_0 R_2} + \frac{q}{4\pi \varepsilon_0 R_3} = \frac{q}{4\pi \varepsilon_0} \left( \frac{1}{r} - \frac{1}{R_2} + \frac{1}{R_3} \right)$$</p>
</li>
<li>
<p>$R_2 &lt; r &lt; R_3$时：
$$V = \frac{q}{4\pi \varepsilon_0 r} - \frac{q}{4\pi \varepsilon_0 r} + \frac{q}{4\pi \varepsilon_0 R_3} = \frac{q}{4\pi \varepsilon_0 R_3}$$</p>
</li>
<li>
<p>$r &gt; R_3$时：
$$V = \frac{q}{4\pi \varepsilon_0 r} - \frac{q}{4\pi \varepsilon_0 r} + \frac{q}{4\pi \varepsilon_0 r} = \frac{q}{4\pi \varepsilon_0 r}$$</p>
</li>
</ul>
<hr />
<h1>05电容器</h1>
<h2>考点概览</h2>
<table>
<thead>
<tr>
<th>考点</th>
<th>重要程度</th>
<th>占分</th>
<th>常见题型</th>
</tr>
</thead>
<tbody>
<tr>
<td>1. 电容器</td>
<td>★★★★★</td>
<td>3~5</td>
<td>选择、填空</td>
</tr>
</tbody>
</table>
<hr />
<h2>1. 电容器</h2>
<h3>基本概念和公式</h3>
<p><strong>电容定义</strong>：$C = \frac{q}{U}$，单位：$F$（法拉），$1\mu F = 10^{-6}F$，$1pF = 10^{-12}F$</p>
<p><strong>关键要点</strong>：</p>
<p>(1) 电容器是由导体构成，电荷量等值异号，分布在极板内侧</p>
<p>(2) $C = \frac{q}{U}$，$q$代表一侧的电荷量取正，$U$代表极板间电压</p>
<p>(3) 极板间电场强度：$E = \frac{\sigma}{\varepsilon_0} = \frac{U}{d}$，电势差：$U = \frac{q}{C} = E \cdot d$</p>
<p>(4) 极板间相互作用力：$F = \frac{1}{2} E \cdot q$</p>
<p>(5) 含介质：$C = \varepsilon_r C_0$，$E = \frac{E_0}{\varepsilon_r}$，$\varepsilon = \varepsilon_0 \cdot \varepsilon_r$</p>
<p><strong>电介质参数</strong>：</p>
<ul>
<li>$\varepsilon$：介电常量（电容率）</li>
<li>$\varepsilon_0$：真空中介电常量（真空中电容率）</li>
<li>$\varepsilon_r$：相对介电常量（相对电容率），$\varepsilon_r \geq 1$，真空中$\varepsilon_r = 1$</li>
</ul>
<h3>常见电容器电容公式</h3>
<table>
<thead>
<tr>
<th>电容器类型</th>
<th>真空中电容</th>
<th>含介质$\varepsilon_r$电容</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>平行板电容器</strong></td>
<td>$C = \frac{\varepsilon_0 S}{d}$</td>
<td>$C = \frac{\varepsilon_0 \varepsilon_r S}{d}$</td>
</tr>
<tr>
<td><strong>圆柱形电容器</strong></td>
<td>$C = \frac{2\pi \varepsilon_0 l}{\ln R_B / R_A}$</td>
<td>$C = \frac{2\pi \varepsilon_0 \varepsilon_r l}{\ln R_B / R_A}$</td>
</tr>
<tr>
<td><strong>球形电容器</strong></td>
<td>$C = 4\pi \varepsilon_0 \frac{R_A R_B}{R_B - R_A}$</td>
<td>$C = 4\pi \varepsilon_0 \varepsilon_r \frac{R_A R_B}{R_B - R_A}$</td>
</tr>
<tr>
<td><strong>孤立导体电容器</strong></td>
<td>$C = 4\pi \varepsilon_0 R_A$</td>
<td>$C = 4\pi \varepsilon_0 \varepsilon_r R_A$</td>
</tr>
</tbody>
</table>
<h3>电容器串并联</h3>
<p><strong>串联</strong>：$\frac{1}{C} = \frac{1}{C_1} + \frac{1}{C_2}$</p>
<p><strong>并联</strong>：$C = C_1 + C_2$</p>
<h3>例题</h3>
<p><strong>题1</strong>：求面积为$S$，两极板间距离为$d$的平行板电容器的电容。</p>
<p><strong>解</strong>：
电荷 $q = \sigma \cdot S$</p>
<p>电势差：$U = E \cdot d = \frac{\sigma}{\varepsilon_0} \cdot d$</p>
<p>电容：$C = \frac{q}{U} = \frac{\sigma S}{\frac{\sigma}{\varepsilon_0} \cdot d} = \frac{\varepsilon_0 S}{d}$</p>
<p>含介质时：$C = \frac{\varepsilon_0 \varepsilon_r S}{d}$</p>
<p>结论：电容大小只与极板大小、形状、相对位置以及电介质有关</p>
<hr />
<p><strong>题2</strong>：设两平行板电容器的电容为$C$，若将两极板的正对面积增大到原来的2倍，极板间距离增大到原来的3倍，则此电容器的电容变为______。</p>
<p><strong>解</strong>：平行板电容器：$C = \frac{\varepsilon_0 S}{d}$</p>
<p>$S' \to 2S$，$d' \to 3d$</p>
<p>$$C' = \frac{\varepsilon_0 S'}{d'} = \frac{\varepsilon_0 \cdot 2S}{3d} = \frac{2}{3} \cdot \frac{\varepsilon_0 S}{d} = \frac{2}{3}C$$</p>
<hr />
<p><strong>题3</strong>：当平行板电容器板间为真空时，其电容为$C_0$，板间场强大小为$E_0$，当充满着相对介电常数为$\varepsilon_r$的电介质，则电容为______。</p>
<p><strong>解</strong>：$C = \varepsilon_r C_0$</p>
<hr />
<p><strong>题4</strong>：真空中半径为$R$的金属球的电容为______，若放在无限大相对介电常量为$\varepsilon_r$的电介质中电容为______。</p>
<p><strong>解</strong>：$C = 4\pi \varepsilon_0 R$；$C = 4\pi \varepsilon_0 \varepsilon_r R$</p>
<hr />
<p><strong>题5</strong>：四个电容器连接如图，则$a,b$两端的等效电容为______。</p>
<p><strong>解</strong>：
$$\frac{1}{C_1} = \frac{1}{12} + \frac{1}{4} = \frac{1}{3} \Rightarrow C_1 = 3\mu F$$</p>
<p>$$C_2 = C_1 + 2 = 3 + 2 = 5\mu F$$</p>
<p>$$\frac{1}{C_{ab}} = \frac{1}{C_2} + \frac{1}{20} = \frac{1}{5} + \frac{1}{20} = \frac{1}{4} \Rightarrow C_{ab} = 4\mu F$$</p>
<hr />
<p><strong>题6</strong>：平行板电容器两板间距离为$d$，极板面积为$S$，在真空时的电容、自由电荷面密度、电势差、电场强度的大小分别用$C_0, \sigma_0, U_0, E_0$表示。</p>
<p>(1) 如充电后与电源断开，将$\varepsilon_r$的均匀介质充满电容器，则：</p>
<p>$C =$<strong><strong><strong>，$\sigma =$</strong></strong></strong>，$U =$<strong><strong><strong>，$E =$</strong></strong></strong></p>
<p>(2) 与电源保持联接，将$\varepsilon_r$的均匀介质充满电容器，则：</p>
<p>$C =$<strong><strong><strong>，$\sigma =$</strong></strong></strong>，$U =$<strong><strong><strong>，$E =$</strong></strong></strong></p>
<p><strong>解</strong>：</p>
<p>(1) 电荷量$Q$不变：
$$C = \varepsilon_r C_0$$
$$\sigma = \frac{Q}{S} = \sigma_0$$
$$U = \frac{Q}{C} = \frac{Q}{\varepsilon_r C_0} = \frac{U_0}{\varepsilon_r}$$
$$E = \frac{U}{d} = \frac{U_0}{\varepsilon_r d} = \frac{E_0}{\varepsilon_r}$$</p>
<p>(2) 电压不变$U = U_0$：
$$C = \varepsilon_r C_0$$
$$\sigma = \frac{Q}{S} = \frac{C \cdot U}{S} = \frac{\varepsilon_r C_0 \cdot U}{S} = \frac{\varepsilon_r Q_0}{S} = \varepsilon_r \sigma_0$$
$$U = U_0$$
$$E = \frac{U}{d} = \frac{U_0}{d} = E_0$$</p>
<hr />
<p><strong>题7</strong>：一平行板空气电容器的两极板相距$d$，$A$和$B$极板上面电荷密度分别为$+\sigma$和$-\sigma$，其间插入与极板大小相等，厚度为$d/2$的金属板后，金属板与极板间场强大小为______，金属板内的场强大小为______，两极板间的电势差为______。</p>
<p><strong>解</strong>：插入金属板，极板电荷不变，$\sigma$不变</p>
<ul>
<li>金属与极板间：$E = \frac{\sigma}{\varepsilon_0}$</li>
<li>金属板是导体，内部：$E = 0$</li>
<li>两极板间电势差：$U_{AB} = E d_1 + E d_2 = E(d_1 + d_2) = E \cdot \frac{d}{2} = \frac{d\sigma}{2\varepsilon_0}$</li>
</ul>
<p>结论：电势差会减小，与金属板放置的位置无关</p>
<hr />
<p><strong>题8</strong>：一空气平行板电容器，电容为$C$，两极板间距离为$d$，充电后，两极板间的相互作用力为$F$，则两极板间的电势差为______，极板上的电荷为______。</p>
<p><strong>解</strong>：$C = \frac{\varepsilon_0 S}{d}$，$F = \frac{1}{2} E q$，$E = \frac{2F}{q}$</p>
<p>$$U = \frac{q}{C} = E \cdot d = \frac{2F}{q} \cdot d$$</p>
<p>$$\Rightarrow q = \sqrt{2FCd}$$</p>
<p>$$U = \frac{q}{C} = \frac{\sqrt{2FCd}}{C} = \sqrt{2F d / C}$$</p>
<hr />
<h1>06电介质,电场能</h1>
<h2>考点概览</h2>
<table>
<thead>
<tr>
<th>考点</th>
<th>重要程度</th>
<th>占分</th>
<th>常见题型</th>
</tr>
</thead>
<tbody>
<tr>
<td>1. 电介质高斯定理</td>
<td>★★★★★★</td>
<td>5~10</td>
<td>大题</td>
</tr>
<tr>
<td>2. 电场能</td>
<td>★★★</td>
<td>3~5</td>
<td>选择、填空</td>
</tr>
</tbody>
</table>
<hr />
<h2>1. 电介质高斯定理</h2>
<h3>基本概念和公式</h3>
<p><strong>电介质中的高斯定理</strong>：$\oiint \vec{D} \cdot d\vec{S} = \sum q_{内}$，适用：含介质和真空</p>
<p><strong>电位移</strong>：$D = \varepsilon_0 \varepsilon_r E$，$\sum q_{内}$为自由电荷代数和</p>
<p><strong>真空中</strong>：$\varepsilon_r = 1$，$D = \varepsilon_0 E$</p>
<p><strong>电介质中的场强</strong>：$E = E_0 - E' = \frac{E_0}{\varepsilon_r}$</p>
<p><strong>极化电荷面密度</strong>：$\sigma' = \left( 1 - \frac{1}{\varepsilon_r} \right) \sigma_0$</p>
<h3>例题</h3>
<p><strong>题1</strong>：在半径为$R_1$的金属球之外包有一层外半径为$R_2$的均匀电介质球壳，介质的相对介电常数为$\varepsilon_r$，金属球带电$Q$，试求：(1) 电介质内外的场强；(2) 电介质内外的电势。</p>
<p><strong>解</strong>：</p>
<p>(1) 由电介质高斯定理：$\oiint \vec{D} \cdot d\vec{S} = D \cdot 4\pi r^2 = \sum q_{内} \Rightarrow D = \frac{\sum q_{内}}{4\pi r^2} \Rightarrow E = \frac{D}{\varepsilon_0 \varepsilon_r}$</p>
<ul>
<li>$r &lt; R_1$时：$\sum q_{内} = 0 \Rightarrow D = 0 \Rightarrow E_1 = 0$</li>
<li>$R_1 &lt; r &lt; R_2$时：$\sum q_{内} = Q \Rightarrow D = \frac{Q}{4\pi r^2} \Rightarrow E_2 = \frac{Q}{4\pi \varepsilon_0 \varepsilon_r r^2}$</li>
<li>$r &gt; R_2$时：$\sum q_{内} = Q \Rightarrow D = \frac{Q}{4\pi r^2} \Rightarrow E_3 = \frac{Q}{4\pi \varepsilon_0 r^2}$</li>
</ul>
<p>(2) 电势分布：</p>
<ul>
<li>
<p>$r &lt; R_1$时：
$$V = \int_{R_1}^{R_2} E_2 dr + \int_{R_2}^{\infty} E_3 dr = \int_{R_1}^{R_2} \frac{Q}{4\pi \varepsilon_0 \varepsilon_r r^2} dr + \int_{R_2}^{\infty} \frac{Q}{4\pi \varepsilon_0 r^2} dr = \frac{Q}{4\pi \varepsilon_0 \varepsilon_r} \left( \frac{1}{R_1} - \frac{1}{R_2} \right) + \frac{Q}{4\pi \varepsilon_0 R_2}$$</p>
</li>
<li>
<p>$R_1 &lt; r &lt; R_2$时：
$$V = \int_{r}^{R_2} E_2 dr + \int_{R_2}^{\infty} E_3 dr = \int_{r}^{R_2} \frac{Q}{4\pi \varepsilon_0 \varepsilon_r r^2} dr + \int_{R_2}^{\infty} \frac{Q}{4\pi \varepsilon_0 r^2} dr = \frac{Q}{4\pi \varepsilon_0 \varepsilon_r} \left( \frac{1}{r} - \frac{1}{R_2} \right) + \frac{Q}{4\pi \varepsilon_0 R_2}$$</p>
</li>
<li>
<p>$r &gt; R_2$时：
$$V = \int_{r}^{\infty} E_3 dr = \int_{r}^{\infty} \frac{Q}{4\pi \varepsilon_0 r^2} dr = \frac{Q}{4\pi \varepsilon_0 r}$$</p>
</li>
</ul>
<hr />
<p><strong>题2</strong>：一圆柱形电容器由半径为$R_1$的导线和与它同轴的导体圆筒构成，圆筒长为$l$，内半径为$R_2$，导线与圆筒间充满相对电容率$\varepsilon_r$的电介质，设沿轴线单位长度上导线的电量为$\lambda$，圆筒的电量为$-\lambda$，略去边缘效应，求：(1) 电介质中电位移$D$，场强$E$；(2) 两极板的电势差。</p>
<p><strong>解</strong>：</p>
<p>(1) $\oiint \vec{D} \cdot d\vec{S} = D \cdot 2\pi r h = \sum q_{内} \Rightarrow D = \frac{\sum q_{内}}{2\pi r h} \Rightarrow E = \frac{D}{\varepsilon_0 \varepsilon_r}$</p>
<p>$R_1 &lt; r &lt; R_2$时：$\sum q_{内} = \lambda h$</p>
<p>$$\Rightarrow D = \frac{\lambda h}{2\pi r h} = \frac{\lambda}{2\pi r} \Rightarrow E = \frac{\lambda}{2\pi \varepsilon_0 \varepsilon_r r}$$</p>
<p>(2) 两极板间电势差：
$$U_{AB} = \int_{R_1}^{R_2} \vec{E} \cdot d\vec{r} = \int_{R_1}^{R_2} \frac{\lambda}{2\pi \varepsilon_0 \varepsilon_r r} dr = \frac{\lambda}{2\pi \varepsilon_0 \varepsilon_r} \ln \frac{R_2}{R_1}$$</p>
<hr />
<p><strong>题3</strong>：静电场中，作闭合曲面$S$，若有$\oiint_S \vec{D} \cdot d\vec{S} = 0$（式中$\vec{D}$为电位移矢量），则$S$内面必定（ ）</p>
<p>A. 既无自由电荷，又无束缚电荷</p>
<p>B. 没有自由电荷</p>
<p>C. 自由电荷和束缚电荷的代数和为零</p>
<p>D. 自由电荷的代数和为零</p>
<p><strong>解</strong>：由$\oiint \vec{D} \cdot d\vec{S} = \sum q_{内}$，$\sum q_{内} = 0$表示自由电荷代数和为零。选<strong>D</strong></p>
<hr />
<h2>2. 电场能</h2>
<h3>基本概念和公式</h3>
<p><strong>电场能密度</strong>（单位：$J/m^3$）：
$$w_e = \frac{1}{2} D E = \frac{1}{2} \varepsilon_0 \varepsilon_r E^2$$</p>
<p><strong>电场能</strong>：
$$W = \int_V w_e dV$$</p>
<p><strong>电容器储存的电场能</strong>：
$$W = \frac{Q^2}{2C} = \frac{1}{2} C (V_1 - V_2)^2 = \frac{1}{2} Q (V_1 - V_2)$$</p>
<h3>例题</h3>
<p><strong>题1</strong>：在相对电容率$\varepsilon_r = 4$的各向同性均匀电介质中，与能量密度$w_e = 2 \times 10^6 J/m^3$相应的电场强度大小$E =$______$N/C$</p>
<p><strong>解</strong>：由$w_e = \frac{1}{2} \varepsilon_0 \varepsilon_r E^2$：
$$E = \sqrt{\frac{2w_e}{\varepsilon_0 \varepsilon_r}} = \sqrt{\frac{2 \times 2 \times 10^6}{8.85 \times 10^{-12} \times 4}} = 3.36 \times 10^8 N/C$$</p>
<hr />
<p><strong>题2</strong>：一个$3\mu F$的电容被接到$12V$的电源，则储存在电容器中的能量为______</p>
<p><strong>解</strong>：$3\mu F = 3 \times 10^{-6}F$
$$W = \frac{1}{2} C (V_1 - V_2)^2 = \frac{1}{2} \times 3 \times 10^{-6} \times 12^2 = 2.16 \times 10^{-4} J$$</p>
<hr />
<p><strong>题3</strong>：有一半径为$R$的带电导体球，带电量为$q$，放在相对电容率为$\varepsilon_r$的无限大均匀介质中，则此系统的电场能量为（ ）</p>
<p>A. $\frac{q^2}{8\pi \varepsilon_0 \varepsilon_r R}$</p>
<p>B. $\frac{q}{4\pi \varepsilon_0 \varepsilon_r R^2}$</p>
<p>C. $\frac{q}{8\pi \varepsilon_0 \varepsilon_r R^2}$</p>
<p>D. $\frac{q^2}{4\pi \varepsilon_0 \varepsilon_r R^2}$</p>
<p><strong>解</strong>：$C = 4\pi \varepsilon_0 \varepsilon_r R$
$$W = \frac{Q^2}{2C} = \frac{q^2}{2 \times 4\pi \varepsilon_0 \varepsilon_r R} = \frac{q^2}{8\pi \varepsilon_0 \varepsilon_r R}$$</p>
<p>选<strong>A</strong></p>
<hr />
<h1>07磁感应强度</h1>
<h2>考点概览</h2>
<table>
<thead>
<tr>
<th>考点</th>
<th>重要程度</th>
<th>占分</th>
<th>常见题型</th>
</tr>
</thead>
<tbody>
<tr>
<td>1. 毕奥-萨伐尔定律</td>
<td>★★★★</td>
<td>3~5</td>
<td>选择、填空</td>
</tr>
<tr>
<td>2. 安培环路定理</td>
<td>必考</td>
<td>5~10</td>
<td>大题</td>
</tr>
</tbody>
</table>
<hr />
<h2>1. 毕奥-萨伐尔定律</h2>
<h3>基本概念</h3>
<p><strong>毕奥-萨伐尔定律</strong>（毕萨定理）：描述电流元在空间产生的磁感应强度</p>
<p>$$dB = \frac{\mu_0}{4\pi} \cdot \frac{Idl \cdot \sin\theta}{r^2}$$</p>
<p>$$B = \int dB = \frac{\mu_0}{4\pi} \int \frac{Idl \sin\theta}{r^2}$$</p>
<p><strong>单位</strong>：$T$（特斯拉）</p>
<p><strong>真空中磁导率</strong>：$\mu_0 = 4\pi \times 10^{-7} T \cdot m/A$</p>
<h3>磁场线图示</h3>
<ul>
<li>直线电流：同心圆磁场线</li>
<li>环形电流：类似条形磁铁的磁场线</li>
<li>螺线管：内部匀强磁场</li>
</ul>
<p><strong>符号约定</strong>：</p>
<ul>
<li>$\otimes$：垂直纸面向内</li>
<li>$\odot$：垂直纸面向外</li>
</ul>
<h3>毕奥-萨伐尔定律基本公式</h3>
<h3>例题：载流直导线</h3>
<p><strong>题1</strong>：在长为$L$的一段载流直导线中，通有电流$I$，求距离导线为$a$处一点$P$的磁感应强度。</p>
<p><strong>解</strong>：任取一电流元$Idl$</p>
<p>$$dB = \frac{\mu_0}{4\pi} \cdot \frac{Idl \sin\theta}{r^2}$$</p>
<p>$$B = \int dB = \frac{\mu_0}{4\pi} \int \frac{Idl \sin\theta}{r^2}$$</p>
<p>通过变量替换：$r = \frac{a}{\sin\theta}$，$dl = a \csc^2\theta d\theta$</p>
<p>$$B = \frac{\mu_0 I}{4\pi a} \int_{\theta_1}^{\theta_2} \sin\theta d\theta = \frac{\mu_0 I}{4\pi a} (\cos\theta_1 - \cos\theta_2)$$</p>
<p>方向垂直纸面向里</p>
<h3>常见结论汇总</h3>
<table>
<thead>
<tr>
<th>电流类型</th>
<th>磁感应强度公式</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>有限长直导线</strong></td>
<td>$B = \frac{\mu_0 I}{4\pi a} (\cos\theta_1 - \cos\theta_2)$</td>
</tr>
<tr>
<td><strong>无限长直导线</strong></td>
<td>$B = \frac{\mu_0 I}{2\pi a}$</td>
</tr>
<tr>
<td><strong>半无限长直导线</strong></td>
<td>$B = \frac{\mu_0 I}{4\pi a}$</td>
</tr>
<tr>
<td><strong>无限大电流平面</strong></td>
<td>$B = \frac{1}{2} \mu_0 \alpha$</td>
</tr>
<tr>
<td><strong>圆弧（圆心角$\varphi$）</strong></td>
<td>$B = \frac{\mu_0 I}{4\pi R} \varphi$</td>
</tr>
<tr>
<td><strong>圆电流（圆心处）</strong></td>
<td>$B = \frac{\mu_0 I}{2R}$</td>
</tr>
<tr>
<td><strong>螺绕环</strong></td>
<td>$B = \mu_0 nI = \frac{\mu_0 NI}{2\pi R}$</td>
</tr>
<tr>
<td><strong>长直螺线管</strong></td>
<td>$B = \mu_0 nI = \frac{\mu_0 NI}{L}$</td>
</tr>
</tbody>
</table>
<h3>例题</h3>
<p><strong>题2</strong>：真空中，一根无限长的直导线，通过的电流强度为$I$，在距离导线$r$处的磁感应强度为（ ）</p>
<p>A. $\frac{\mu_0 I}{r}$ B. $\frac{\mu_0 I}{2r}$ C. $\frac{\mu_0 I}{2\pi r}$ D. $\frac{\mu_0 I}{4\pi r}$</p>
<p><strong>解</strong>：选<strong>C</strong>，$B = \frac{\mu_0 I}{2\pi r}$</p>
<hr />
<p><strong>题3</strong>：真空中一根无限长导线弯成如图形状，设各线段都在同一平面内（纸面内），水平部分的延长线经过圆心$O$，竖直向下的导线垂直于水平方向。导线中通有电流$I$，求图中$O$处的磁感应强度的大小和方向。</p>
<p><strong>解</strong>：</p>
<ul>
<li>$AB$段和$CD$段过$O$点，在$O$点不产生磁场</li>
<li>$\overset{\frown}{BC}$段：$B_1 = \frac{\mu_0 I}{2R_1} \cdot \frac{1}{2} = \frac{\mu_0 I}{4R_1}$，朝内</li>
<li>$DE$段：$B_2 = \frac{\mu_0 I}{4\pi \cdot \sqrt{2}R_2/2} (\cos\frac{\pi}{4} - \cos\frac{3\pi}{4}) = \frac{\mu_0 I}{2\pi R_2}$，朝外</li>
<li>$\overset{\frown}{EF}$段：$B_3 = \frac{\mu_0 I}{2R_2} \cdot \frac{1}{4} = \frac{\mu_0 I}{8R_2}$，朝外</li>
<li>$FG$段：$B_4 = \frac{\mu_0 I}{2\pi R_2} \cdot \frac{1}{2} = \frac{\mu_0 I}{4\pi R_2}$，朝外</li>
</ul>
<p>$$B = B_2 + B_3 + B_4 - B_1 = \frac{3\mu_0 I}{4\pi R_2} + \frac{\mu_0 I}{8R_2} - \frac{\mu_0 I}{4R_1}$$</p>
<p>方向朝外</p>
<hr />
<p><strong>题4</strong>：如图所示，一无限长薄电流板均匀通有电流$I$，电流板宽为$a$，求在电流板同一平面内距板边为$a$的$P$点处的磁感应强度。</p>
<p><strong>解</strong>：电流板上取窄条$dx$</p>
<p>窄条的电流：$dI = \frac{I}{a} \cdot dx$</p>
<p>$dI$在$P$点处产生的磁感应强度：
$$dB = \frac{\mu_0 dI}{2\pi (2a - x)} = \frac{\mu_0 I}{2\pi a (2a - x)} dx$$</p>
<p>$$B = \int dB = \int_0^a \frac{\mu_0 I}{2\pi a (2a - x)} dx = \frac{\mu_0 I}{2\pi a} \ln 2$$</p>
<p>方向垂直纸面向里</p>
<hr />
<h2>2. 安培环路定理</h2>
<h3>基本概念和公式</h3>
<p><strong>安培环路定理</strong>：
$$\oint \vec{B} \cdot d\vec{l} = \mu_0 \sum I_{内}$$</p>
<p><strong>关键要点</strong>：</p>
<p>(1) 安培环路定理只与环路内电流有关，与电流位置以及环路外电流无关</p>
<p>(2) $\sum I_{内}$是指环路内的净电流（所有电流的代数和）</p>
<p>(3) 环路上的磁感应强度$B$，不仅由环路内电流影响，还由环路外电流影响</p>
<h3>例题</h3>
<p><strong>题1</strong>：已知半径为$R$的铜线，通过电流为$I$，电流在导线横截面上均匀分布，求导线内外磁感应强度的分布？</p>
<p><strong>解</strong>：由$\oint \vec{B} \cdot d\vec{l} = B \cdot \oint dl = B \cdot 2\pi r = \mu_0 \sum I_{内} \Rightarrow B = \frac{\mu_0 \sum I_{内}}{2\pi r}$</p>
<ul>
<li>$r &lt; R$时：$\sum I_{内} = \frac{I}{\pi R^2} \cdot \pi r^2 = \frac{Ir^2}{R^2}$</li>
</ul>
<p>$$B = \frac{\mu_0 Ir}{2\pi R^2}$$</p>
<ul>
<li>$r &gt; R$时：$\sum I_{内} = I$</li>
</ul>
<p>$$B = \frac{\mu_0 I}{2\pi r}$$</p>
<p>方向沿逆时针方向</p>
<hr />
<p><strong>题2</strong>：如图所示的空心柱形导体半径分别为$R_1$和$R_2$，导体内载流有电流$I$，设电流$I$均匀分布在导线的横截面上，求导体内部各点（$R_1 &lt; r &lt; R_2$）的磁感应强度$B$。</p>
<p><strong>解</strong>：由$\oint \vec{B} \cdot d\vec{l} = B \cdot 2\pi r = \mu_0 \sum I_{内} \Rightarrow B = \frac{\mu_0 \sum I_{内}}{2\pi r}$</p>
<p>$$\sum I_{内} = \frac{I}{\pi (R_2^2 - R_1^2)} \cdot \pi (r^2 - R_1^2) = \frac{I (r^2 - R_1^2)}{R_2^2 - R_1^2}$$</p>
<p>$$\Rightarrow B = \frac{\mu_0 I (r^2 - R_1^2)}{2\pi r (R_2^2 - R_1^2)}$$</p>
<p>方向逆时针</p>
<hr />
<p><strong>题3</strong>：如图所示，垂直流出纸面的电流为$2I$，垂直流进纸面的电流为$I$，两电流均为稳恒电流，则环路积分正确的是（ ）</p>
<p>A. $\oint_L \vec{B} \cdot d\vec{l} = 2\mu_0 I$ B. $\oint_L \vec{B} \cdot d\vec{l} = 3\mu_0 I$</p>
<p>C. $\oint_L \vec{B} \cdot d\vec{l} = \mu_0 I$ D. $\oint_L \vec{B} \cdot d\vec{l} = -\mu_0 I$</p>
<p><strong>解</strong>：垂直纸面向里为正，$\sum I_{内} = I - 2I = -I$</p>
<p>$$\oint_L \vec{B} \cdot d\vec{l} = \mu_0 \sum I_{内} = -\mu_0 I$$</p>
<p>选<strong>D</strong></p>
<hr />
<p><strong>题4</strong>：取一闭合积分回路$L$，使三根载流导线穿过它所围成的面，现改变三根导线之间的相互间隔，但不越出积分回路，则（ ）</p>
<p>A. 回路$L$内的$I$改变，$L$上各点的$\vec{B}$改变</p>
<p>B. 回路$L$内的$I$改变，$L$上各点的$\vec{B}$不变</p>
<p>C. 回路$L$内的$I$不变，$L$上各点的$\vec{B}$改变</p>
<p>D. 回路$L$内的$I$不变，$L$上各点的$\vec{B}$不变</p>
<p><strong>解</strong>：回路$L$内的净电流$I$不变（三根导线都在回路内），但各电流位置变化会导致环路上各点的$\vec{B}$改变。选<strong>C</strong></p>
<hr />
<p><strong>题5</strong>：下列说法中正确的是（ ）</p>
<p>A. 闭合回路上各点磁感应强度都为零时，回路内穿过电流的代数和必为零</p>
<p>B. 闭合回路上各点磁感应强度都为零时，回路内一定没有电流通过</p>
<p>C. 磁感应强度沿闭合回路积分不为零时，回路上任意一点的磁感应强度都不可能为零</p>
<p>D. 磁感应强度沿闭合回路积分为零时，回路上各点的磁感应强度必为零</p>
<p><strong>解</strong>：由$\oint \vec{B} \cdot d\vec{l} = \mu_0 \sum I_{内} = 0 \Rightarrow \sum I_{内} = 0$，但回路外电流仍会产生磁场。选<strong>A</strong></p>
<hr />
<p><strong>题6</strong>：如图，在一圆形电流$I$所在的平面内，选取一个同心圆形闭合回路$L$，则由安培环路定理可知（ ）</p>
<p>A. $\oint_L \vec{B} \cdot d\vec{l} = 0$，且环路上任意一点$B = 0$</p>
<p>B. $\oint_L \vec{B} \cdot d\vec{l} = 0$，且环路上任意一点$B \neq 0$</p>
<p>C. $\oint_L \vec{B} \cdot d\vec{l} \neq 0$，且环路上任意一点$B \neq 0$</p>
<p>D. $\oint_L \vec{B} \cdot d\vec{l} \neq 0$，且环路上任意一点$B =$常量</p>
<p><strong>解</strong>：回路$L$内没有电流，$\sum I_{内} = 0$，所以$\oint_L \vec{B} \cdot d\vec{l} = 0$；但圆形电流在回路$L$上各点会产生磁场，$B \neq 0$。选<strong>B</strong></p>
<hr />
<h1>08磁通量,高斯定理</h1>
<h2>考点概览</h2>
<table>
<thead>
<tr>
<th>考点</th>
<th>重要程度</th>
<th>占分</th>
<th>常见题型</th>
</tr>
</thead>
<tbody>
<tr>
<td>1. 磁通量、高斯定理</td>
<td>必考</td>
<td>5~10</td>
<td>大题</td>
</tr>
</tbody>
</table>
<hr />
<h2>1. 磁通量、高斯定理</h2>
<h3>基本概念和公式</h3>
<p><strong>磁通量</strong>：$\Phi_m = B \cdot S$，单位：$Wb$（韦伯）</p>
<p><strong>三种情况</strong>：</p>
<p>(1) 平面垂直磁场：$\Phi_m = B \cdot S$</p>
<p>(2) 平面与磁场成$\theta$角：$\Phi_m = B \cdot S \cdot \cos\theta$</p>
<p>(3) 曲面：$d\Phi_m = \vec{B} \cdot d\vec{S}$，$\Phi_m = \int d\Phi_m = \iint \vec{B} \cdot d\vec{S}$</p>
<p><strong>关键要点</strong>：</p>
<p>(1) $B$和$S$必须是垂直关系</p>
<p>(2) 对于曲面，向外穿出为正，向内穿入为负</p>
<p>(3) <strong>磁场中高斯定理</strong>：$\Phi_m = \oiint \vec{B} \cdot d\vec{S} = 0$，沿闭合曲面积分为零：穿进等于穿出</p>
<h3>例题</h3>
<p><strong>题1</strong>：如图所示，在磁感应强度为$\vec{B}$的均匀磁场中作一半径为$r$的半球面$S$，则通过半球面$S$的磁通量为（ ）</p>
<p>A. $2\pi r^2 B$ B. $-\pi r^2 B$ C. $-2\pi r B$ D. $\pi r^2 B$</p>
<p><strong>解</strong>：$\Phi_m = -B \cdot S = -B \cdot \pi r^2$，选<strong>B</strong></p>
<hr />
<p><strong>题2</strong>：在稳恒磁场中，穿过任一闭合曲面的总磁通量必然为______</p>
<p><strong>解</strong>：$0$（磁场高斯定理）</p>
<hr />
<p><strong>题3</strong>：一个密绕细长螺线管，每米长度上绕有1000匝细导线，螺线管的横截面积为$0.001m^2$。当在螺线管中通入10A的电流时，它的横截面上的磁通量为______$Wb$（$\mu_0 = 4\pi \times 10^{-7} T \cdot m/A$）</p>
<p><strong>解</strong>：
$$B = \mu_0 nI = 4\pi \times 10^{-7} \times 1000 \times 10 = 1.256 \times 10^{-2} T$$</p>
<p>$$\Phi_m = B \cdot S = 1.256 \times 10^{-2} \times 0.001 = 1.256 \times 10^{-5} Wb$$</p>
<hr />
<p><strong>题4</strong>：如图，一无限长直导线通以电流$I$，若有一矩形导体线框与直导线共面且距导线为$a$，试求通过矩形导体线框的磁通量。</p>
<p><strong>解</strong>：矩形线框中取窄条宽为$dx$，$dS = l \cdot dx$</p>
<p>载流直导线在$x$处产生的磁感应强度：$B = \frac{\mu_0 I}{2\pi x}$</p>
<p>$$d\Phi_m = B \cdot dS = B \cdot l \cdot dx = \frac{\mu_0 I l}{2\pi x} dx$$</p>
<p>$$\Phi_m = \int d\Phi_m = \int_{a}^{a+b} \frac{\mu_0 I l}{2\pi x} dx = \frac{\mu_0 I l}{2\pi} \ln \frac{a+b}{a}$$</p>
<hr />
<p><strong>题5</strong>：磁场中的高斯定理为$\oiint \vec{B} \cdot d\vec{S} = 0$，它表明磁场是（ ）</p>
<p>A. 非保守力 B. 保守力 C. 无源场 D. 有源场</p>
<p><strong>解</strong>：选<strong>C</strong></p>
<h3>电场与磁场的对比</h3>
<table>
<thead>
<tr>
<th>定理</th>
<th>电场</th>
<th>磁场</th>
</tr>
</thead>
<tbody>
<tr>
<td>高斯定理</td>
<td>$\Phi_e = \oiint \vec{E} \cdot d\vec{S} = \frac{1}{\varepsilon_0} \sum q_{内}$</td>
<td>$\Phi_m = \oiint \vec{B} \cdot d\vec{S} = 0$</td>
</tr>
<tr>
<td>性质</td>
<td>有源场</td>
<td>无源场</td>
</tr>
<tr>
<td>环路定理</td>
<td>$\oint \vec{E} \cdot d\vec{l} = 0$</td>
<td>$\oint \vec{B} \cdot d\vec{l} = \mu_0 \sum I_{内}$</td>
</tr>
<tr>
<td>性质</td>
<td>无旋场，保守场</td>
<td>有旋场</td>
</tr>
</tbody>
</table>
<hr />
<h1>09安培力,磁力矩,洛伦兹力</h1>
<h2>考点概览</h2>
<table>
<thead>
<tr>
<th>考点</th>
<th>重要程度</th>
<th>占分</th>
<th>常见题型</th>
</tr>
</thead>
<tbody>
<tr>
<td>1. 安培力</td>
<td>★★★</td>
<td>0~5</td>
<td>选择、填空</td>
</tr>
<tr>
<td>2. 磁矩、磁力矩</td>
<td>★★★★★★</td>
<td>2~5</td>
<td>选择、填空、大题</td>
</tr>
<tr>
<td>3. 洛伦兹力</td>
<td>★★</td>
<td>0~3</td>
<td>选择、填空</td>
</tr>
</tbody>
</table>
<hr />
<h2>1. 安培力</h2>
<h3>基本概念和公式</h3>
<p><strong>安培力</strong>：$F = B \cdot I \cdot L$，条件：$B \perp I \perp L$</p>
<p><strong>一般情况</strong>：$F = B \cdot I \cdot L \cdot \sin\alpha$（$\alpha$为电流方向与磁场方向夹角）</p>
<p><strong>电流元受力</strong>：$dF = BIdl$，$F = \int dF$</p>
<p><strong>右手定则</strong>：掌心向里，四指指向电流方向，大拇指指向受力方向</p>
<h3>例题</h3>
<p><strong>题1</strong>：如图所示，一根长直导线载有电流$I_1$，矩形回路载有电流$I_2$，两者共面，试计算：作用在回路各边上的安培力以及线圈所受合外力。</p>
<p><strong>解</strong>：</p>
<ul>
<li>$AB$段：$F_1 = B \cdot I \cdot L = \frac{\mu_0 I_1}{2\pi a} \cdot I_2 \cdot l = \frac{\mu_0 I_1 I_2 l}{2\pi a}$，朝左</li>
<li>$BC$段：$dF = B \cdot I \cdot dx = \frac{\mu_0 I_1}{2\pi x} I_2 dx$，$F_2 = \int dF = \frac{\mu_0 I_1 I_2}{2\pi} \ln \frac{a+b}{a}$，朝上</li>
<li>$CD$段：$F_3 = B \cdot I \cdot L = \frac{\mu_0 I_1}{2\pi (a+b)} \cdot I_2 \cdot l = \frac{\mu_0 I_1 I_2 l}{2\pi (a+b)}$，朝右</li>
<li>$DA$段：$F_4 = F_2 = \frac{\mu_0 I_1 I_2}{2\pi} \ln \frac{a+b}{a}$，朝下</li>
</ul>
<p>合力：$F = F_1 - F_3 = \frac{\mu_0 I_1 I_2 l}{2\pi} \left( \frac{1}{a} - \frac{1}{a+b} \right)$</p>
<hr />
<p><strong>题2</strong>：图示表示一段半圆形导线，通有电流$I$，圆的半径为$R$，放在均匀磁场$B$中，磁场与导线平面垂直，求磁场作用在半圆形导线上的安培力$F =$______。</p>
<p><strong>解</strong>：$F = B \cdot I \cdot L = B \cdot I \cdot 2R = 2BIR$</p>
<p>结论：任意形状的载流导线在均匀磁场中所受安培力等于连接导线两端的直导线所受的安培力</p>
<hr />
<h2>2. 磁矩、磁力矩</h2>
<h3>基本概念和公式</h3>
<p><strong>磁矩</strong>：$p_m = I \cdot S$，单位：$A \cdot m^2$</p>
<p><strong>磁力矩</strong>：$M = B p_m \sin\theta$（$\theta$为$B$和$p_m$夹角）</p>
<p><strong>特殊情况</strong>：</p>
<ul>
<li>$\theta = \frac{\pi}{2}$时，磁力矩最大：$M_{max} = BIS$</li>
<li>$\theta = 0$时，磁力矩最小：$M_{min} = 0$</li>
</ul>
<p><strong>磁力矩做功</strong>：$A = I \Delta \Phi_m$</p>
<h3>例题</h3>
<p><strong>题1</strong>：一半径为$R = 0.10m$的圆形闭合线圈，通有稳恒电流$I = 10A$，则此线圈磁矩$p_m$的大小是______</p>
<p><strong>解</strong>：$p_m = I \cdot S = I \cdot \pi R^2 = 10 \times 3.14 \times 0.1^2 = 0.314 A \cdot m^2$</p>
<hr />
<p><strong>题2</strong>：一正方形线圈，由细导线做成，边长为$a$，共有$N$匝，可以绕通过其相对两边中点的一个竖直轴自由转动，现在线圈中通有电流$I$，并把线圈放在均匀的水平外磁场$\vec{B}$中，求线圈磁矩与磁场$\vec{B}$的夹角为$\theta$时，线圈受到的转动力矩为______</p>
<p><strong>解</strong>：单匝磁矩：$p_m = I \cdot S = Ia^2$</p>
<p>单匝磁力矩：$M = B p_m \sin\theta = BIa^2 \sin\theta$</p>
<p>总磁力矩：$M_{总} = NM = NBIa^2 \sin\theta$</p>
<hr />
<p><strong>题3</strong>：如图所示，半径为$R$的半圆形载流线圈在均匀磁场$\vec{B}$中所受的磁力矩大小______</p>
<p><strong>解</strong>：$\theta = \pi$，$M = B p_m \sin\pi = 0$</p>
<hr />
<p><strong>题4</strong>：一半圆形闭合线圈半径$R = 0.1m$，通过电流$I = 10A$，放在均匀磁场中，磁场方向与线圈平面平行，$B = 0.5T$</p>
<p>(1) 求线圈所受力矩的大小</p>
<p>(2) 若此线圈受力矩的作用转到线圈平面与磁场垂直的位置，则力矩做功多少？</p>
<p><strong>解</strong>：</p>
<p>(1) 磁矩：$p_m = I \cdot S = I \cdot \frac{1}{2}\pi R^2 = 10 \times \frac{1}{2}\pi \times 0.1^2 = 0.157 A \cdot m^2$</p>
<p>磁力矩：$M = B p_m \sin\frac{\pi}{2} = 0.5 \times 0.157 \times 1 = 7.85 \times 10^{-2} N \cdot m$</p>
<p>(2) 磁通量该变量：$\Delta \Phi_m = BS - 0 = BS$</p>
<p>磁力矩做功：$A = I \Delta \Phi_m = IBS = 10 \times 0.5 \times \frac{1}{2}\pi \times 0.1^2 = 7.84 \times 10^{-2} J$</p>
<hr />
<h2>3. 洛伦兹力</h2>
<h3>基本概念和公式</h3>
<p><strong>洛伦兹力</strong>：$F = Bqv$（$v \perp B$）</p>
<p><strong>圆周运动半径</strong>：$F = Bqv = m \frac{v^2}{R} \Rightarrow R = \frac{mv}{Bq}$</p>
<p><strong>周期</strong>：$T = 2\pi \frac{m}{Bq}$</p>
<p><strong>洛伦兹力不做功</strong>（力始终与速度垂直）</p>
<h3>例题</h3>
<p><strong>题1</strong>：</p>
<ul>
<li>一带电粒子平行磁感线射入匀强磁场，则它作<strong>匀速直线运动</strong></li>
<li>一带电粒子垂直磁感线射入匀强磁场，则它作<strong>匀速圆周运动</strong></li>
<li>一带电粒子与磁感线成任意角射入匀强磁场，则它作<strong>螺旋运动</strong></li>
</ul>
<hr />
<p><strong>题2</strong>：图为四个带电粒子在$O$点沿相同方向垂直于磁感线射入均匀磁场后的偏转轨迹的照片。磁场方向垂直纸面向外，轨迹所对应的四个粒子的质量相等，电荷大小也相等，则其中动能最大的带负电的粒子的轨迹是（ ）</p>
<p>A. $Oa$ B. $Ob$ C. $Oc$ D. $Od$</p>
<p><strong>解</strong>：$E_k = \frac{1}{2}mv^2$，$R = \frac{mv}{Bq}$，$R$越大则$v$越大，$E_k$越大。负电荷受力方向与正电荷相反，选<strong>C</strong></p>
<hr />
<h1>10磁介质,磁场能</h1>
<h2>考点概览</h2>
<table>
<thead>
<tr>
<th>考点</th>
<th>重要程度</th>
<th>占分</th>
<th>常见题型</th>
</tr>
</thead>
<tbody>
<tr>
<td>1. 磁介质中安培环路定理</td>
<td>★★★★★</td>
<td>5~10</td>
<td>大题</td>
</tr>
<tr>
<td>2. 磁场能量</td>
<td>★★</td>
<td>2~3</td>
<td>选择、填空</td>
</tr>
</tbody>
</table>
<hr />
<h2>1. 磁介质中安培环路定理</h2>
<h3>基本概念和公式</h3>
<p><strong>磁介质中安培环路定理</strong>：$\oint \vec{H} \cdot d\vec{l} = \sum I_{内}$，适用介质和真空</p>
<p><strong>真空中安培环路定理</strong>：$\oint \vec{B} \cdot d\vec{l} = \mu_0 \sum I_{内}$</p>
<p><strong>磁场强度</strong>：$H = \frac{B}{\mu_0 \mu_r} = \frac{B}{\mu}$</p>
<p><strong>磁感应强度与磁场强度关系</strong>：$B = \mu_0 \mu_r H = \mu H$</p>
<p><strong>磁导率</strong>：</p>
<ul>
<li>$\mu$：磁导率</li>
<li>$\mu_0$：真空中磁导率</li>
<li>$\mu_r$：相对磁导率，$\mu_r = \frac{B}{B_0}$</li>
</ul>
<p><strong>三种磁介质</strong>：</p>
<ul>
<li>顺磁质：$\mu_r &gt; 1$</li>
<li>抗磁质：$\mu_r &lt; 1$</li>
<li>铁磁质：$\mu_r \gg 1$</li>
</ul>
<p><strong>真空中相对磁导率</strong>：$\mu_r = 1$，$H = \frac{B}{\mu_0}$</p>
<h3>例题</h3>
<p><strong>题1</strong>：长直圆柱形铜导线，外面包一层相对磁导率为$\mu_r$的圆筒形磁介质。导线半径为$R_1$，磁介质的半径为$R_2$，导线内有均匀分布的电流$I$通过，铜的相对磁导率可取1，求导线和介质内外的磁场强度$H$和磁感应强度$B$的分布。</p>
<p><strong>解</strong>：由$\oint \vec{H} \cdot d\vec{l} = H \cdot 2\pi r = \sum I_{内} \Rightarrow H = \frac{\sum I_{内}}{2\pi r}$，$B = \mu_0 \mu_r H$</p>
<ul>
<li>
<p>$r &lt; R_1$时（导线内部，$\mu_r = 1$）：
$$\sum I_{内} = \frac{I}{\pi R_1^2} \cdot \pi r^2 = \frac{Ir^2}{R_1^2}$$
$$H = \frac{Ir}{2\pi R_1^2}$$
$$B = \frac{\mu_0 Ir}{2\pi R_1^2}$$</p>
</li>
<li>
<p>$R_1 &lt; r &lt; R_2$时（磁介质内部）：
$$\sum I_{内} = I$$
$$H = \frac{I}{2\pi r}$$
$$B = \frac{\mu_0 \mu_r I}{2\pi r}$$</p>
</li>
<li>
<p>$r &gt; R_2$时（介质外部，真空）：
$$\sum I_{内} = I$$
$$H = \frac{I}{2\pi r}$$
$$B = \frac{\mu_0 I}{2\pi r}$$</p>
</li>
</ul>
<hr />
<p><strong>题2</strong>：磁介质有三种，用相对磁导率$\mu_r$表征它们的各自特性是（ ）</p>
<p>A. 顺磁质$\mu_r &gt; 0$，抗磁质$\mu_r &lt; 0$，铁磁质$\mu_r \gg 1$</p>
<p>B. 顺磁质$\mu_r &gt; 1$，抗磁质$\mu_r = 1$，铁磁质$\mu_r \gg 1$</p>
<p>C. 顺磁质$\mu_r &gt; 1$，抗磁质$\mu_r &lt; 1$，铁磁质$\mu_r \gg 1$</p>
<p>D. 顺磁质$\mu_r &gt; 0$，抗磁质$\mu_r &lt; 0$，铁磁质$\mu_r &gt; 1$</p>
<p><strong>解</strong>：选<strong>C</strong></p>
<hr />
<h2>2. 磁场能量</h2>
<h3>基本概念和公式</h3>
<p><strong>磁能密度</strong>：$w_m = \frac{1}{2} \frac{B^2}{\mu_0}$</p>
<p><strong>磁场能</strong>：$W_m = \int_V w_m dV$</p>
<h3>例题</h3>
<p><strong>题1</strong>：真空中一长直密绕螺线管通以电流$I$，管上单位长度绕$n$匝导线，内部的磁能密度为$w_m =$______</p>
<p><strong>解</strong>：$B = \mu_0 nI$
$$w_m = \frac{1}{2} \frac{B^2}{\mu_0} = \frac{\mu_0 n^2 I^2}{2}$$</p>
<hr />
<p><strong>题2</strong>：真空中一根无限长直细导线上通电流$I$，则距导线垂直距离为$a$的空间某点处的磁能密度为（ ）</p>
<p>A. $\frac{1}{2} \mu_0 \left( \frac{\mu_0 I}{2\pi a} \right)^2$</p>
<p>B. $\frac{1}{2\mu_0} \left( \frac{\mu_0 I}{2\pi a} \right)^2$</p>
<p>C. $\frac{1}{2} \left( \frac{2\pi a}{\mu_0 I} \right)^2$</p>
<p>D. $\frac{1}{2\mu_0} \left( \frac{\mu_0 I}{2a} \right)^2$</p>
<p><strong>解</strong>：$B = \frac{\mu_0 I}{2\pi a}$
$$w_m = \frac{1}{2} \frac{B^2}{\mu_0} = \frac{1}{2\mu_0} \left( \frac{\mu_0 I}{2\pi a} \right)^2$$</p>
<p>选<strong>B</strong></p>
<hr />
<h1>11电磁感应</h1>
<p>（待整理：2.11电磁感应文件夹）</p>
<hr />
<h1>12自感和互感（选学）</h1>
<h2>考点概览</h2>
<table>
<thead>
<tr>
<th>考点</th>
<th>重要程度</th>
<th>占分</th>
<th>常见题型</th>
</tr>
</thead>
<tbody>
<tr>
<td>1. 自感和互感</td>
<td>★★</td>
<td>0~3</td>
<td>选择、填空</td>
</tr>
<tr>
<td>2. 麦克斯韦方程组</td>
<td>★★★★★★</td>
<td>2~5</td>
<td>选择、填空</td>
</tr>
</tbody>
</table>
<p>提示：查看自己往年试卷，看下是否考察这部分内容</p>
<hr />
<h2>1. 自感与互感</h2>
<h3>基本概念和公式</h3>
<p><strong>自感</strong>：</p>
<ul>
<li>自感系数：$L = \frac{\Phi}{I}$</li>
<li>自感电动势：$\varepsilon = L \frac{dI}{dt}$</li>
</ul>
<p><strong>互感</strong>：</p>
<ul>
<li>互感系数：$M = \frac{\Phi_2}{I_1} = \frac{\Phi_1}{I_2}$</li>
</ul>
<h3>例题</h3>
<p><strong>题1</strong>：一无铁芯的长直螺线管，在保持其半径和总匝数不变的情况下，把螺线管拉长一些，则它的自感系数将（ ）</p>
<p>A. 增大 B. 减小 C. 不变 D. 不能确定</p>
<p><strong>解</strong>：长直螺线管内的磁感应强度：$B = \mu_0 nI = \mu_0 \frac{N}{l} I$</p>
<p>磁通量：$\Phi = NBS = N \cdot \mu_0 \frac{N}{l} I \cdot \pi R^2 = \frac{\mu_0 N^2 I}{l} \pi R^2$</p>
<p>自感系数：$L = \frac{\Phi}{I} = \frac{\mu_0 N^2}{l} \pi R^2$</p>
<p>拉长后$l$增大，$L$减小。选<strong>B</strong></p>
<hr />
<p><strong>题2</strong>：一自感线圈中，电流强度在0.002s内均匀地由10A增加到12A，此过程中线圈内自感电动势为400V，则线圈的自感系数为$L =$______$H$</p>
<p><strong>解</strong>：由$\varepsilon = L \frac{dI}{dt}$：
$$400 = L \cdot \frac{12 - 10}{0.002} \Rightarrow L = 0.4 H$$</p>
<hr />
<p><strong>题3</strong>：一边长为$a$的正方形线圈放在一根长直导线旁，线圈与直导线共面。其中线圈的中心距长直导线为$\frac{3a}{2}$，线圈的一组对边与直导线平行。此时，正方形线圈与直导线的互感系数为（ ）</p>
<p>A. $\frac{\mu_0 a}{\pi} \ln 2$ B. $\frac{\mu_0 a}{2\pi} \ln 2$ C. $\frac{\mu_0}{2\pi} \ln 2$ D. $\frac{\mu_0}{\pi} \ln 2$</p>
<p><strong>解</strong>：在$x$处取窄条，宽为$dx$</p>
<p>$$d\Phi = B \cdot dS = \frac{\mu_0 I}{2\pi x} \cdot adx = \frac{\mu_0 I a}{2\pi x} dx$$</p>
<p>$$\Phi = \int d\Phi = \int_{a}^{2a} \frac{\mu_0 I a}{2\pi x} dx = \frac{\mu_0 I a}{2\pi} \ln 2$$</p>
<p>$$M = \frac{\Phi_2}{I_1} = \frac{\frac{\mu_0 I a}{2\pi} \ln 2}{I} = \frac{\mu_0 a}{2\pi} \ln 2$$</p>
<p>选<strong>B</strong></p>
<hr />
<h2>2. 麦克斯韦方程组</h2>
<h3>基本概念和公式</h3>
<p><strong>1. 电场高斯定理</strong> — 电场是有源场，电荷总伴随着电场
$$\oiint \vec{D} \cdot d\vec{S} = \sum q = \int_V \rho dV$$</p>
<p><strong>2. 磁场高斯定理</strong> — 磁场是无源场，磁力线闭合
$$\oiint \vec{B} \cdot d\vec{S} = 0$$</p>
<p><strong>3. 变化的电场产生磁场</strong>（全电流安培环路定理）
$$\oint \vec{H} \cdot d\vec{l} = I + I_d = \iint_S \vec{j} \cdot d\vec{S} + \iint_S \frac{\partial \vec{D}}{\partial t} \cdot d\vec{S}$$</p>
<p><strong>4. 变化的磁场一定伴随着电场</strong>
$$\oint \vec{E} \cdot d\vec{l} = -\frac{d\Phi}{dt} = -\iint_S \frac{\partial \vec{B}}{\partial t} \cdot d\vec{S}$$</p>
<h3>例题</h3>
<p><strong>题1</strong>：反应电磁场基本性质和规律的积分形式的麦克斯韦方程为：</p>
<p>A. $\oiint_S \vec{D} \cdot d\vec{S} = \int_V \rho dV$</p>
<p>B. $\oint_l \vec{E} \cdot d\vec{l} = -\iint_S \frac{\partial \vec{B}}{\partial t} \cdot d\vec{S}$</p>
<p>C. $\oiint_S \vec{B} \cdot d\vec{S} = 0$</p>
<p>D. $\oint_l \vec{H} \cdot d\vec{l} = \iint_S \left( \vec{j} + \frac{\partial \vec{D}}{\partial t} \right) d\vec{S}$</p>
<p>试判断下列结论是包含于或等效于哪一个麦克斯韦方程式：</p>
<p>(1) 变化的磁场一定伴随有电场：<strong>B</strong></p>
<p>(2) 变化的电场产生磁场：<strong>D</strong></p>
<p>(3) 磁场是无源场，磁力线闭合：<strong>C</strong></p>
<p>(4) 电荷总伴随着电场：<strong>A</strong></p>
]]></content>
    <author><name>仙鹤</name></author>
    <category term="考试"/>
  </entry>
  <entry>
    <title>大物B1公式要点速览</title>
    <link href="https://xianhe51920.github.io/posts/physics-b1-formulas/" rel="alternate" type="text/html"/>
    <id>https://xianhe51920.github.io/posts/physics-b1-formulas/</id>
    <published>2026-06-22T00:00:00.000Z</published>
    <updated>2026-06-22T00:00:00.000Z</updated>
    <summary>大一下物理 B1 公式要点速览</summary>
    <content type="html"><![CDATA[<h1>大学物理B1公式要点速览</h1>
<hr />
<h2>01质点运动学（一）</h2>
<table>
<thead>
<tr>
<th>公式名称</th>
<th>公式</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>位矢</td>
<td>$\vec{r} = x\vec{i} + y\vec{j}$</td>
<td>位置矢量</td>
</tr>
<tr>
<td>位移</td>
<td>$\Delta\vec{r} = \vec{r}_B - \vec{r}_A$</td>
<td>矢量差</td>
</tr>
<tr>
<td>平均速度</td>
<td>$\vec{\bar{v}} = \frac{\Delta\vec{r}}{\Delta t}$</td>
<td>位移与时间比值</td>
</tr>
<tr>
<td>瞬时速度</td>
<td>$\vec{v} = \frac{d\vec{r}}{dt} = \frac{dx}{dt}\vec{i} + \frac{dy}{dt}\vec{j}$</td>
<td>位矢对时间求导</td>
</tr>
<tr>
<td>速度大小</td>
<td>$|\vec{v}| = \frac{ds}{dt} = \sqrt{\left(\frac{dx}{dt}\right)^2 + \left(\frac{dy}{dt}\right)^2}$</td>
<td>速率</td>
</tr>
<tr>
<td>加速度</td>
<td>$\vec{a} = \frac{d\vec{v}}{dt} = \frac{dv}{dt}\vec{\tau} + \frac{v^2}{R}\vec{n}$</td>
<td>速度对时间求导</td>
</tr>
<tr>
<td>切向加速度</td>
<td>$a_t = \frac{dv}{dt}$</td>
<td>改变速度大小</td>
</tr>
<tr>
<td>法向加速度</td>
<td>$a_n = \frac{v^2}{R}$</td>
<td>改变速度方向</td>
</tr>
<tr>
<td>加速度大小</td>
<td>$|\vec{a}| = \sqrt{\left(\frac{dv}{dt}\right)^2 + \left(\frac{v^2}{R}\right)^2}$</td>
<td>总加速度大小</td>
</tr>
<tr>
<td>相对速度</td>
<td>$\vec{v}<em>{绝对} = \vec{v}</em>{牵连} + \vec{v}_{相对}$</td>
<td>相对运动关系</td>
</tr>
<tr>
<td>相对加速度</td>
<td>$\vec{a}<em>{绝对} = \vec{a}</em>{牵连} + \vec{a}_{相对}$</td>
<td>相对加速度关系</td>
</tr>
<tr>
<td>匀变速直线运动（速度）</td>
<td>$v = v_0 + at$</td>
<td>速度公式</td>
</tr>
<tr>
<td>匀变速直线运动（位移）</td>
<td>$x = x_0 + v_0t + \frac{1}{2}at^2$</td>
<td>位移公式</td>
</tr>
<tr>
<td>匀变速直线运动（速度位移）</td>
<td>$v_2^2 - v_1^2 = 2a(x_2 - x_1)$</td>
<td>速度位移关系</td>
</tr>
</tbody>
</table>
<hr />
<h2>02质点运动学（二）</h2>
<table>
<thead>
<tr>
<th>公式名称</th>
<th>公式</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>角位移</td>
<td>$\theta$</td>
<td>单位：rad</td>
</tr>
<tr>
<td>角速度</td>
<td>$\omega = \frac{d\theta}{dt}$</td>
<td>角位置对时间求导</td>
</tr>
<tr>
<td>角加速度</td>
<td>$\beta = \frac{d\omega}{dt} = \frac{d^2\theta}{dt^2}$</td>
<td>角速度对时间求导</td>
</tr>
<tr>
<td>周期</td>
<td>$T = \frac{2\pi}{\omega}$</td>
<td>转动一周时间</td>
</tr>
<tr>
<td>线速度与角速度</td>
<td>$v = \omega R$</td>
<td>角量与线量关系</td>
</tr>
<tr>
<td>切向加速度与角加速度</td>
<td>$a_t = \beta R$</td>
<td>角量与线量关系</td>
</tr>
<tr>
<td>法向加速度与角速度</td>
<td>$a_n = \omega^2 R = \frac{v^2}{R}$</td>
<td>角量与线量关系</td>
</tr>
<tr>
<td>匀变速圆周运动（角速度）</td>
<td>$\omega = \omega_0 + \beta t$</td>
<td>角速度公式</td>
</tr>
<tr>
<td>匀变速圆周运动（角位移）</td>
<td>$\theta = \theta_0 + \omega_0t + \frac{1}{2}\beta t^2$</td>
<td>角位移公式</td>
</tr>
<tr>
<td>匀变速圆周运动（角速度角位移）</td>
<td>$\omega_2^2 - \omega_1^2 = 2\beta(\theta_2 - \theta_1)$</td>
<td>角速度角位移关系</td>
</tr>
</tbody>
</table>
<hr />
<h2>03常见力和牛顿三定律</h2>
<table>
<thead>
<tr>
<th>公式名称</th>
<th>公式</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>重力</td>
<td>$G = mg$</td>
<td>$g = 9.8N/kg$</td>
</tr>
<tr>
<td>弹力（胡克定律）</td>
<td>$F = kx$</td>
<td>$k$为弹性系数</td>
</tr>
<tr>
<td>滑动摩擦力</td>
<td>$f = \mu_k N$</td>
<td>$\mu_k$为滑动摩擦系数</td>
</tr>
<tr>
<td>最大静摩擦力</td>
<td>$f_{max} = \mu_s N$</td>
<td>$\mu_s$为静摩擦系数</td>
</tr>
<tr>
<td>万有引力</td>
<td>$F = G\frac{Mm}{r^2}$</td>
<td>$G = 6.67 \times 10^{-11} N \cdot m^2/kg^2$</td>
</tr>
<tr>
<td>牛顿第二定律</td>
<td>$\vec{F} = m\vec{a}$</td>
<td>力是产生加速度的原因</td>
</tr>
</tbody>
</table>
<hr />
<h2>04动量,冲量,动量守恒</h2>
<table>
<thead>
<tr>
<th>公式名称</th>
<th>公式</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>动量</td>
<td>$\vec{p} = m\vec{v}$</td>
<td>单位：$kg \cdot m/s$</td>
</tr>
<tr>
<td>动量定理</td>
<td>$\vec{I} = m\vec{v}_2 - m\vec{v}<em>1 = \int</em>{t_1}^{t_2} \vec{F}dt$</td>
<td>冲量等于动量变化</td>
</tr>
<tr>
<td>常力冲量</td>
<td>$\vec{I} = \vec{F} \cdot \Delta t$</td>
<td>力为常量时</td>
</tr>
<tr>
<td>动量守恒定律</td>
<td>$\sum m_i \vec{v}<em>{i1} = \sum m_i \vec{v}</em>{i2}$</td>
<td>条件：合外力为零</td>
</tr>
</tbody>
</table>
<hr />
<h2>05质点运动的功和能</h2>
<table>
<thead>
<tr>
<th>公式名称</th>
<th>公式</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>元功</td>
<td>$dA = \vec{F} \cdot d\vec{r}$</td>
<td>力与位移点积</td>
</tr>
<tr>
<td>功</td>
<td>$A = \int \vec{F} \cdot d\vec{r}$</td>
<td>元功积分</td>
</tr>
<tr>
<td>一维变力做功</td>
<td>$A = \int_{x_1}^{x_2} F dx$</td>
<td>$F = f(x)$时</td>
</tr>
<tr>
<td>恒力做功</td>
<td>$A = \vec{F} \cdot \vec{r} = F_x x + F_y y + F_z z$</td>
<td>力为常量时</td>
</tr>
<tr>
<td>动能</td>
<td>$E_k = \frac{1}{2}mv^2$</td>
<td>动能定义</td>
</tr>
<tr>
<td>动能定理</td>
<td>$A = E_{k2} - E_{k1} = \frac{1}{2}mv_2^2 - \frac{1}{2}mv_1^2$</td>
<td>合外力做功等于动能增量</td>
</tr>
<tr>
<td>重力势能</td>
<td>$E_p = mgh$</td>
<td>零势能点：地面</td>
</tr>
<tr>
<td>弹性势能</td>
<td>$E_p = \frac{1}{2}kx^2$</td>
<td>零势能点：平衡位置</td>
</tr>
<tr>
<td>万有引力势能</td>
<td>$E_p = -G\frac{Mm}{r}$</td>
<td>零势能点：无穷远</td>
</tr>
<tr>
<td>静电力势能</td>
<td>$E_p = \frac{1}{4\pi\varepsilon_0}\frac{q_1q_2}{r}$</td>
<td>零势能点：无穷远</td>
</tr>
<tr>
<td>机械能</td>
<td>$E = E_k + E_p$</td>
<td>动能+势能</td>
</tr>
<tr>
<td>机械能守恒</td>
<td>$E_{k1} + E_{p1} = E_{k2} + E_{p2}$</td>
<td>条件：只有保守力做功</td>
</tr>
</tbody>
</table>
<hr />
<h2>06电场强度</h2>
<table>
<thead>
<tr>
<th>公式名称</th>
<th>公式</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>库仑定律</td>
<td>$F = \frac{1}{4\pi\varepsilon_0} \cdot \frac{q \cdot q_0}{r^2}$</td>
<td>点电荷间作用力</td>
</tr>
<tr>
<td>真空介电常量</td>
<td>$\varepsilon_0 = 8.85 \times 10^{-12}\ C^2/(N \cdot m^2)$</td>
<td>常量</td>
</tr>
<tr>
<td>电场强度定义</td>
<td>$\vec{E} = \frac{\vec{F}}{q_0} = \frac{1}{4\pi\varepsilon_0} \cdot \frac{q}{r^2}$</td>
<td>单位正电荷受力</td>
</tr>
<tr>
<td>点电荷场强</td>
<td>$E = \frac{1}{4\pi\varepsilon_0} \cdot \frac{q}{r^2}$</td>
<td>球对称分布</td>
</tr>
<tr>
<td>无限长均匀带电直线场强</td>
<td>$E = \frac{\lambda}{2\pi\varepsilon_0 r}$</td>
<td>$\lambda$为线密度</td>
</tr>
<tr>
<td>无限大均匀带电平面场强</td>
<td>$E = \frac{\sigma}{2\varepsilon_0}$</td>
<td>$\sigma$为面密度，与距离无关</td>
</tr>
<tr>
<td>均匀带电圆柱面场强</td>
<td>$E = 0$（$r &lt; R$）；$E = \frac{\lambda}{2\pi\varepsilon_0 r}$（$r &gt; R$）</td>
<td>高斯定理推导</td>
</tr>
<tr>
<td>均匀带电球体场强</td>
<td>$E = \frac{qr}{4\pi\varepsilon_0 R^3}$（$r &lt; R$）；$E = \frac{q}{4\pi\varepsilon_0 r^2}$（$r &gt; R$）</td>
<td>高斯定理推导</td>
</tr>
<tr>
<td>连续带电体场强</td>
<td>$dE = \frac{1}{4\pi\varepsilon_0} \cdot \frac{dq}{r^2}$，$E = \int dE$</td>
<td>积分求解</td>
</tr>
</tbody>
</table>
<hr />
<h2>07电通量,高斯定理</h2>
<table>
<thead>
<tr>
<th>公式名称</th>
<th>公式</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>电通量（平面垂直）</td>
<td>$\Phi_e = E \cdot S$</td>
<td>$E$与$S$垂直</td>
</tr>
<tr>
<td>电通量（平面倾斜）</td>
<td>$\Phi_e = E \cdot S \cdot \cos\theta$</td>
<td>$\theta$为$E$与$S$夹角</td>
</tr>
<tr>
<td>电通量（曲面）</td>
<td>$d\Phi_e = \vec{E} \cdot d\vec{S}$，$\Phi_e = \iint \vec{E} \cdot d\vec{S}$</td>
<td>积分求解</td>
</tr>
<tr>
<td>高斯定理</td>
<td>$\Phi_e = \oiint \vec{E} \cdot d\vec{S} = \frac{1}{\varepsilon_0} \sum q_{内}$</td>
<td>闭合曲面电通量</td>
</tr>
</tbody>
</table>
<hr />
<h2>08电势,电势能</h2>
<table>
<thead>
<tr>
<th>公式名称</th>
<th>公式</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>电势定义</td>
<td>$V = \frac{W_M}{q_0} = \int_{r}^{\infty} \vec{E} \cdot d\vec{l}$</td>
<td>单位正电荷电势能</td>
</tr>
<tr>
<td>点电荷电势</td>
<td>$V = \frac{q}{4\pi \varepsilon_0 r}$</td>
<td>离散型</td>
</tr>
<tr>
<td>连续带电体电势</td>
<td>$dV = \frac{dq}{4\pi \varepsilon_0 r}$，$V = \int \frac{dq}{4\pi \varepsilon_0 r}$</td>
<td>连续型</td>
</tr>
<tr>
<td>已知场强求电势</td>
<td>$V = \int_{起点}^{\infty} \vec{E} \cdot d\vec{l}$</td>
<td>场强积分法</td>
</tr>
<tr>
<td>电势差</td>
<td>$U_{AB} = V_A - V_B = \int_{A}^{B} \vec{E} \cdot d\vec{l}$</td>
<td>两点电势差</td>
</tr>
<tr>
<td>电势能</td>
<td>$W_M = q_0 V$</td>
<td>电荷与电势乘积</td>
</tr>
<tr>
<td>电场力做功与电势能</td>
<td>$A = -(W_2 - W_1)$</td>
<td>电场力做正功，势能减小</td>
</tr>
<tr>
<td>场强与电势关系</td>
<td>$\vec{E} = -\left( \frac{\partial V}{\partial x}, \frac{\partial V}{\partial y}, \frac{\partial V}{\partial z} \right)$</td>
<td>电势梯度负值</td>
</tr>
</tbody>
</table>
<hr />
<h2>09导体</h2>
<table>
<thead>
<tr>
<th>公式名称</th>
<th>公式</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>导体内部场强</td>
<td>$E = 0$</td>
<td>静电平衡条件</td>
</tr>
<tr>
<td>导体表面场强</td>
<td>$E = \frac{\sigma}{\varepsilon_0}$</td>
<td>$\sigma$为表面电荷密度</td>
</tr>
<tr>
<td>均匀带电无限大平面导体场强</td>
<td>$E = \frac{\sigma}{\varepsilon_0}$</td>
<td>导体表面</td>
</tr>
<tr>
<td>均匀带电无限大平面场强</td>
<td>$E = \frac{\sigma}{2\varepsilon_0}$</td>
<td>带电平面两侧</td>
</tr>
</tbody>
</table>
<hr />
<h2>10电容器</h2>
<table>
<thead>
<tr>
<th>公式名称</th>
<th>公式</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>电容定义</td>
<td>$C = \frac{q}{U}$</td>
<td>单位：F（法拉）</td>
</tr>
<tr>
<td>平行板电容器（真空）</td>
<td>$C = \frac{\varepsilon_0 S}{d}$</td>
<td>$S$为极板面积，$d$为间距</td>
</tr>
<tr>
<td>平行板电容器（介质）</td>
<td>$C = \frac{\varepsilon_0 \varepsilon_r S}{d}$</td>
<td>$\varepsilon_r$为相对介电常数</td>
</tr>
<tr>
<td>圆柱形电容器（真空）</td>
<td>$C = \frac{2\pi \varepsilon_0 l}{\ln R_B / R_A}$</td>
<td>$l$为长度</td>
</tr>
<tr>
<td>圆柱形电容器（介质）</td>
<td>$C = \frac{2\pi \varepsilon_0 \varepsilon_r l}{\ln R_B / R_A}$</td>
<td>含介质</td>
</tr>
<tr>
<td>球形电容器（真空）</td>
<td>$C = 4\pi \varepsilon_0 \frac{R_A R_B}{R_B - R_A}$</td>
<td>同心球壳</td>
</tr>
<tr>
<td>球形电容器（介质）</td>
<td>$C = 4\pi \varepsilon_0 \varepsilon_r \frac{R_A R_B}{R_B - R_A}$</td>
<td>含介质</td>
</tr>
<tr>
<td>孤立导体球电容（真空）</td>
<td>$C = 4\pi \varepsilon_0 R$</td>
<td>单个导体球</td>
</tr>
<tr>
<td>孤立导体球电容（介质）</td>
<td>$C = 4\pi \varepsilon_0 \varepsilon_r R$</td>
<td>含介质</td>
</tr>
<tr>
<td>电容器串联</td>
<td>$\frac{1}{C} = \frac{1}{C_1} + \frac{1}{C_2}$</td>
<td>等效电容</td>
</tr>
<tr>
<td>电容器并联</td>
<td>$C = C_1 + C_2$</td>
<td>等效电容</td>
</tr>
<tr>
<td>极板间场强</td>
<td>$E = \frac{\sigma}{\varepsilon_0} = \frac{U}{d}$</td>
<td>电场与电压关系</td>
</tr>
<tr>
<td>极板间作用力</td>
<td>$F = \frac{1}{2} E \cdot q$</td>
<td>极板相互作用</td>
</tr>
<tr>
<td>含介质电容</td>
<td>$C = \varepsilon_r C_0$</td>
<td>电容增大$\varepsilon_r$倍</td>
</tr>
<tr>
<td>含介质场强</td>
<td>$E = \frac{E_0}{\varepsilon_r}$</td>
<td>场强减小$\varepsilon_r$倍</td>
</tr>
<tr>
<td>介电常量关系</td>
<td>$\varepsilon = \varepsilon_0 \cdot \varepsilon_r$</td>
<td>介电常量乘积</td>
</tr>
</tbody>
</table>
<hr />
<h2>11电介质,电场能</h2>
<table>
<thead>
<tr>
<th>公式名称</th>
<th>公式</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>电介质高斯定理</td>
<td>$\oiint \vec{D} \cdot d\vec{S} = \sum q_{内}$</td>
<td>$D$为电位移矢量</td>
</tr>
<tr>
<td>电位移与场强关系</td>
<td>$D = \varepsilon_0 \varepsilon_r E$</td>
<td>$D$与$E$关系</td>
</tr>
<tr>
<td>电介质中总场强</td>
<td>$E = E_0 - E' = \frac{E_0}{\varepsilon_r}$</td>
<td>自由电荷+极化电荷场强</td>
</tr>
<tr>
<td>极化电荷面密度</td>
<td>$\sigma' = \left( 1 - \frac{1}{\varepsilon_r} \right) \sigma_0$</td>
<td>极化电荷与自由电荷关系</td>
</tr>
<tr>
<td>电场能密度</td>
<td>$w_e = \frac{1}{2} D E = \frac{1}{2} \varepsilon_0 \varepsilon_r E^2$</td>
<td>单位体积电场能</td>
</tr>
<tr>
<td>电场能</td>
<td>$W = \int_V w_e dV$</td>
<td>体积分</td>
</tr>
<tr>
<td>电容器储存能量</td>
<td>$W = \frac{Q^2}{2C} = \frac{1}{2} C U^2 = \frac{1}{2} Q U$</td>
<td>三种表达式</td>
</tr>
</tbody>
</table>
<hr />
<h2>12磁感应强度</h2>
<table>
<thead>
<tr>
<th>公式名称</th>
<th>公式</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>毕奥-萨伐尔定律</td>
<td>$dB = \frac{\mu_0}{4\pi} \cdot \frac{Idl \cdot \sin\theta}{r^2}$</td>
<td>电流元产生磁场</td>
</tr>
<tr>
<td>真空中磁导率</td>
<td>$\mu_0 = 4\pi \times 10^{-7} T \cdot m/A$</td>
<td>常量</td>
</tr>
<tr>
<td>有限长直导线磁场</td>
<td>$B = \frac{\mu_0 I}{4\pi a} (\cos\theta_1 - \cos\theta_2)$</td>
<td>$\theta_1, \theta_2$为两端夹角</td>
</tr>
<tr>
<td>无限长直导线磁场</td>
<td>$B = \frac{\mu_0 I}{2\pi a}$</td>
<td>特殊情况</td>
</tr>
<tr>
<td>半无限长直导线磁场</td>
<td>$B = \frac{\mu_0 I}{4\pi a}$</td>
<td>一端在无穷远</td>
</tr>
<tr>
<td>无限大电流平面磁场</td>
<td>$B = \frac{1}{2} \mu_0 \alpha$</td>
<td>$\alpha$为面电流密度</td>
</tr>
<tr>
<td>圆弧电流磁场（圆心处）</td>
<td>$B = \frac{\mu_0 I}{4\pi R} \varphi$</td>
<td>$\varphi$为圆心角（弧度）</td>
</tr>
<tr>
<td>圆电流磁场（圆心处）</td>
<td>$B = \frac{\mu_0 I}{2R}$</td>
<td>$\varphi = 2\pi$</td>
</tr>
<tr>
<td>螺绕环磁场</td>
<td>$B = \mu_0 nI = \frac{\mu_0 NI}{2\pi R}$</td>
<td>$n$为单位长度匝数</td>
</tr>
<tr>
<td>长直螺线管磁场</td>
<td>$B = \mu_0 nI = \frac{\mu_0 NI}{L}$</td>
<td>内部匀强磁场</td>
</tr>
<tr>
<td>安培环路定理</td>
<td>$\oint \vec{B} \cdot d\vec{l} = \mu_0 \sum I_{内}$</td>
<td>环路积分等于$\mu_0$倍环路内电流</td>
</tr>
</tbody>
</table>
<hr />
<h2>13磁通量,高斯定理</h2>
<table>
<thead>
<tr>
<th>公式名称</th>
<th>公式</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>磁通量（平面垂直）</td>
<td>$\Phi_m = B \cdot S$</td>
<td>$B$与$S$垂直</td>
</tr>
<tr>
<td>磁通量（平面倾斜）</td>
<td>$\Phi_m = B \cdot S \cdot \cos\theta$</td>
<td>$\theta$为$B$与$S$夹角</td>
</tr>
<tr>
<td>磁通量（曲面）</td>
<td>$d\Phi_m = \vec{B} \cdot d\vec{S}$，$\Phi_m = \iint \vec{B} \cdot d\vec{S}$</td>
<td>积分求解</td>
</tr>
<tr>
<td>磁场高斯定理</td>
<td>$\Phi_m = \oiint \vec{B} \cdot d\vec{S} = 0$</td>
<td>闭合曲面磁通量为零</td>
</tr>
</tbody>
</table>
<hr />
<h2>14安培力,磁力矩,洛伦兹力</h2>
<table>
<thead>
<tr>
<th>公式名称</th>
<th>公式</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>安培力（垂直）</td>
<td>$F = B \cdot I \cdot L$</td>
<td>$B \perp I \perp L$</td>
</tr>
<tr>
<td>安培力（一般）</td>
<td>$F = B \cdot I \cdot L \cdot \sin\alpha$</td>
<td>$\alpha$为电流与磁场夹角</td>
</tr>
<tr>
<td>电流元受力</td>
<td>$dF = BIdl$，$F = \int dF$</td>
<td>积分求解</td>
</tr>
<tr>
<td>磁矩</td>
<td>$p_m = I \cdot S$</td>
<td>单位：$A \cdot m^2$</td>
</tr>
<tr>
<td>磁力矩</td>
<td>$M = B p_m \sin\theta$</td>
<td>$\theta$为$B$与$p_m$夹角</td>
</tr>
<tr>
<td>最大磁力矩</td>
<td>$M_{max} = BIS$</td>
<td>$\theta = \frac{\pi}{2}$</td>
</tr>
<tr>
<td>最小磁力矩</td>
<td>$M_{min} = 0$</td>
<td>$\theta = 0$</td>
</tr>
<tr>
<td>磁力矩做功</td>
<td>$A = I \Delta \Phi_m$</td>
<td>电流乘以磁通量变化</td>
</tr>
<tr>
<td>洛伦兹力</td>
<td>$F = Bqv$</td>
<td>$v \perp B$</td>
</tr>
<tr>
<td>圆周运动半径</td>
<td>$R = \frac{mv}{Bq}$</td>
<td>带电粒子在磁场中</td>
</tr>
<tr>
<td>周期</td>
<td>$T = 2\pi \frac{m}{Bq}$</td>
<td>圆周运动周期</td>
</tr>
</tbody>
</table>
<hr />
<h2>15磁介质,磁场能</h2>
<table>
<thead>
<tr>
<th>公式名称</th>
<th>公式</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>磁介质安培环路定理</td>
<td>$\oint \vec{H} \cdot d\vec{l} = \sum I_{内}$</td>
<td>$H$为磁场强度</td>
</tr>
<tr>
<td>磁场强度</td>
<td>$H = \frac{B}{\mu_0 \mu_r} = \frac{B}{\mu}$</td>
<td>$H$与$B$关系</td>
</tr>
<tr>
<td>磁感应强度与磁场强度</td>
<td>$B = \mu_0 \mu_r H = \mu H$</td>
<td>含磁介质</td>
</tr>
<tr>
<td>相对磁导率</td>
<td>$\mu_r = \frac{B}{B_0}$</td>
<td>真空中$\mu_r = 1$</td>
</tr>
<tr>
<td>顺磁质</td>
<td>$\mu_r &gt; 1$</td>
<td>磁介质类型</td>
</tr>
<tr>
<td>抗磁质</td>
<td>$\mu_r &lt; 1$</td>
<td>磁介质类型</td>
</tr>
<tr>
<td>铁磁质</td>
<td>$\mu_r \gg 1$</td>
<td>磁介质类型</td>
</tr>
<tr>
<td>磁能密度</td>
<td>$w_m = \frac{1}{2} \frac{B^2}{\mu_0}$</td>
<td>单位体积磁场能</td>
</tr>
<tr>
<td>磁场能</td>
<td>$W_m = \int_V w_m dV$</td>
<td>体积分</td>
</tr>
</tbody>
</table>
<hr />
<h2>16电磁感应（选学）</h2>
<table>
<thead>
<tr>
<th>公式名称</th>
<th>公式</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>法拉第电磁感应定律</td>
<td>$\varepsilon = -\frac{d\Phi}{dt}$</td>
<td>感应电动势</td>
</tr>
<tr>
<td>动生电动势</td>
<td>$\varepsilon = \int (\vec{v} \times \vec{B}) \cdot d\vec{l}$</td>
<td>导体运动产生</td>
</tr>
<tr>
<td>感生电动势</td>
<td>$\varepsilon = \oint \vec{E}_v \cdot d\vec{l} = -\frac{d\Phi}{dt}$</td>
<td>变化磁场产生</td>
</tr>
<tr>
<td>互感电动势</td>
<td>$\varepsilon_2 = -M \frac{dI_1}{dt}$</td>
<td>互感产生</td>
</tr>
</tbody>
</table>
<hr />
<h2>17自感和互感（选学）</h2>
<table>
<thead>
<tr>
<th>公式名称</th>
<th>公式</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>自感系数</td>
<td>$L = \frac{\Phi}{I}$</td>
<td>自感定义</td>
</tr>
<tr>
<td>自感电动势</td>
<td>$\varepsilon = L \frac{dI}{dt}$</td>
<td>自感电动势</td>
</tr>
<tr>
<td>互感系数</td>
<td>$M = \frac{\Phi_2}{I_1} = \frac{\Phi_1}{I_2}$</td>
<td>互感定义</td>
</tr>
</tbody>
</table>
<hr />
<h2>18麦克斯韦方程组（选学）</h2>
<table>
<thead>
<tr>
<th>公式名称</th>
<th>公式</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>电场高斯定理</td>
<td>$\oiint_S \vec{D} \cdot d\vec{S} = \int_V \rho dV$</td>
<td>电场有源</td>
</tr>
<tr>
<td>磁场高斯定理</td>
<td>$\oiint_S \vec{B} \cdot d\vec{S} = 0$</td>
<td>磁场无源</td>
</tr>
<tr>
<td>全电流安培环路定理</td>
<td>$\oint_l \vec{H} \cdot d\vec{l} = \iint_S \left( \vec{j} + \frac{\partial \vec{D}}{\partial t} \right) d\vec{S}$</td>
<td>变化电场产生磁场</td>
</tr>
<tr>
<td>法拉第电磁感应定律</td>
<td>$\oint_l \vec{E} \cdot d\vec{l} = -\iint_S \frac{\partial \vec{B}}{\partial t} \cdot d\vec{S}$</td>
<td>变化磁场产生电场</td>
</tr>
</tbody>
</table>
<hr />
<h2>物理常量汇总</h2>
<table>
<thead>
<tr>
<th>常量名称</th>
<th>符号</th>
<th>数值</th>
<th>单位</th>
</tr>
</thead>
<tbody>
<tr>
<td>真空介电常量</td>
<td>$\varepsilon_0$</td>
<td>$8.85 \times 10^{-12}$</td>
<td>$C^2/(N \cdot m^2)$</td>
</tr>
<tr>
<td>真空中磁导率</td>
<td>$\mu_0$</td>
<td>$4\pi \times 10^{-7}$</td>
<td>$T \cdot m/A$</td>
</tr>
<tr>
<td>万有引力常量</td>
<td>$G$</td>
<td>$6.67 \times 10^{-11}$</td>
<td>$N \cdot m^2/kg^2$</td>
</tr>
<tr>
<td>重力加速度</td>
<td>$g$</td>
<td>$9.8$</td>
<td>$m/s^2$</td>
</tr>
</tbody>
</table>
]]></content>
    <author><name>仙鹤</name></author>
    <category term="考试"/>
  </entry>
  <entry>
    <title>响应式布局</title>
    <link href="https://xianhe51920.github.io/posts/response/" rel="alternate" type="text/html"/>
    <id>https://xianhe51920.github.io/posts/response/</id>
    <published>2026-06-15T00:00:00.000Z</published>
    <updated>2026-06-15T00:00:00.000Z</updated>
    <summary>媒体查询，bootstrap</summary>
    <content type="html"><![CDATA[<h1>响应式布局</h1>
<ul>
<li>媒体查询</li>
<li>BootStrap响应式</li>
</ul>
<p><strong>响应式布局</strong>是一种网页设计方法是，使网页能够根据<strong>用户设备的屏幕尺寸，分辨率，方向（横屏/书评）等特性</strong>自动调整布局和内容**，提供一致且优化的用户体验。其核心目标是“一次设计，多端适配”</p>
<table>
<thead>
<tr>
<th>优点</th>
<th>缺点</th>
</tr>
</thead>
<tbody>
<tr>
<td>1. 一套代码适配多端设备</td>
<td>1. 复杂布局需较多调试</td>
</tr>
<tr>
<td>2. 节省开发和维护成本</td>
<td>2. 过度使用媒体查询可能影响性能</td>
</tr>
<tr>
<td>3. 用户体验一致性高</td>
<td>3. 需精准计算断点（什么屏幕下，怎么显示）</td>
</tr>
</tbody>
</table>
<hr />
<p><strong>响应式布局核心技术</strong><br />
<strong>布局方式</strong></p>
<ul>
<li>弹性盒子（Flexbox）</li>
<li>网格布局（CSS Grid）</li>
<li>百分比布局（流式布局）</li>
<li>单位可借助 <code>vw</code> 实现</li>
</ul>
<p><strong>断点（Breakpoints）设置</strong></p>
<ul>
<li>根据常见设备宽度设置断点
<ul>
<li>手机：<code>&lt; 768px</code></li>
<li>平板：<code>768px ~ 1024px</code></li>
<li>PC：<code>&gt; 1024px</code></li>
</ul>
</li>
<li>根据不同设备宽度显示不同布局样式</li>
</ul>
<table>
<thead>
<tr>
<th>断点名称</th>
<th>最小宽度</th>
<th>CSS 媒体查询</th>
<th>适用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td>xs</td>
<td>&lt;576px</td>
<td><code>@media (min-width: 0)</code></td>
<td>超小设备（手机竖屏）</td>
</tr>
<tr>
<td>sm</td>
<td>≥576px</td>
<td><code>@media (min-width: 576px)</code></td>
<td>平板竖屏/小屏手机横屏</td>
</tr>
<tr>
<td>md</td>
<td>≥768px</td>
<td><code>@media (min-width: 768px)</code></td>
<td>平板横屏/桌面小屏</td>
</tr>
<tr>
<td>lg</td>
<td>≥992px</td>
<td><code>@media (min-width: 992px)</code></td>
<td>桌面中屏</td>
</tr>
<tr>
<td>xl</td>
<td>≥1200px</td>
<td><code>@media (min-width: 1200px)</code></td>
<td>桌面大屏</td>
</tr>
<tr>
<td>xxl</td>
<td>≥1400px</td>
<td><code>@media (min-width: 1400px)</code></td>
<td>超宽屏（4K 显示器等）</td>
</tr>
</tbody>
</table>
<hr />
<p><strong>响应式布局实现方案</strong></p>
<ol>
<li>
<p><strong>媒体查询</strong>
媒体查询是 CSS3 的功能，允许根据设备的特性（如屏幕宽度、高度、分辨率、设备类型等）应用不同的样式规则。它是实现响应式设计的核心技术。</p>
</li>
<li>
<p><strong>第三方框架 Bootstrap</strong>
预置样式和组件减少重复编码，缩短项目周期，适配 PC、移动端及主流浏览器。后续还可以学习其他不同的框架。</p>
</li>
</ol>
<h2>媒体查询</h2>
<h3>1. 基本语法</h3>
<pre><code>/* 方式一：@media规则 */
@media 媒体类型 and (媒体特性) {
    /* CSS样式 */
}

/* 方式二：在link标签中引入 */
&lt;link media="媒体类型 and (媒体特性)" href="styles.css"&gt;

/* 方式三：在CSS中使用@import */
@import url("styles.css") 媒体类型 and (媒体特性);
</code></pre>
<h3>2. 媒体类型</h3>
<table>
<thead>
<tr>
<th>媒体类型</th>
<th>说明</th>
<th>常用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>screen</code></td>
<td>屏幕设备</td>
<td>网页显示（最常用）</td>
</tr>
<tr>
<td><code>print</code></td>
<td>打印设备</td>
<td>打印机、打印预览</td>
</tr>
<tr>
<td><code>all</code></td>
<td>所有设备</td>
<td>默认值</td>
</tr>
<tr>
<td><code>speech</code></td>
<td>语音合成器</td>
<td>屏幕阅读器</td>
</tr>
<tr>
<td><code>tv</code></td>
<td>电视设备</td>
<td>智能电视</td>
</tr>
</tbody>
</table>
<h3>3. 媒体特性</h3>
<table>
<thead>
<tr>
<th>媒体特性</th>
<th>说明</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>width</code></td>
<td>视口宽度</td>
<td><code>@media (width: 768px)</code></td>
</tr>
<tr>
<td><code>height</code></td>
<td>视口高度</td>
<td><code>@media (height: 1024px)</code></td>
</tr>
<tr>
<td><code>min-width</code></td>
<td>最小宽度（及以上）</td>
<td><code>@media (min-width: 768px)</code></td>
</tr>
<tr>
<td><code>max-width</code></td>
<td>最大宽度（及以下）</td>
<td><code>@media (max-width: 1200px)</code></td>
</tr>
<tr>
<td><code>min-height</code></td>
<td>最小高度</td>
<td><code>@media (min-height: 600px)</code></td>
</tr>
<tr>
<td><code>max-height</code></td>
<td>最大高度</td>
<td><code>@media (max-height: 800px)</code></td>
</tr>
<tr>
<td><code>orientation</code></td>
<td>屏幕方向</td>
<td><code>@media (orientation: landscape)</code></td>
</tr>
<tr>
<td><code>aspect-ratio</code></td>
<td>宽高比</td>
<td><code>@media (aspect-ratio: 16/9)</code></td>
</tr>
</tbody>
</table>
<h3>4. 逻辑运算符</h3>
<pre><code>/* and：同时满足多个条件 */
@media screen and (min-width: 768px) and (max-width: 1024px) {
    /* 平板设备 */
}

/* not：对整个媒体查询取反 */
@media not screen {
    /* 非屏幕设备 */
}

/* only：仅在指定媒体类型生效 */
@media only screen {
    /* 仅屏幕设备 */
}

/* 逗号（或）：满足任一条件 */
@media (min-width: 768px), (orientation: landscape) {
    /* 宽度≥768px 或 横屏 */
}
</code></pre>
<h3>5. 常见使用场景</h3>
<h4>场景一：根据屏幕宽度改变布局</h4>
<pre><code>/* 默认样式（移动端） */
.container {
    width: 100%;
    padding: 10px;
}

/* 平板及以上设备 */
@media (min-width: 768px) {
    .container {
        width: 750px;
        padding: 20px;
    }
}

/* 桌面及以上设备 */
@media (min-width: 992px) {
    .container {
        width: 970px;
        padding: 30px;
    }
}

/* 大屏幕设备 */
@media (min-width: 1200px) {
    .container {
        width: 1170px;
        padding: 40px;
    }
}
</code></pre>
<h4>场景二：隐藏/显示元素</h4>
<pre><code>/* 默认显示 */
.sidebar {
    display: block;
}

/* 小屏幕隐藏侧边栏 */
@media (max-width: 768px) {
    .sidebar {
        display: none;
    }
}
</code></pre>
<h4>场景三：横屏/竖屏适配</h4>
<pre><code>/* 横屏模式 */
@media (orientation: landscape) {
    .container {
        flex-direction: row;
    }
}

/* 竖屏模式 */
@media (orientation: portrait) {
    .container {
        flex-direction: column;
    }
}
</code></pre>
<h4>场景四：Retina屏幕高清显示</h4>
<pre><code>/* 普通屏幕 */
.logo {
    background-image: url("logo.png");
}

/* Retina屏幕（2倍密度） */
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
    .logo {
        background-image: url("logo@2x.png");
    }
}
</code></pre>
<h3>6. 移动优先 vs 桌面优先</h3>
<h4>移动优先（推荐）</h4>
<pre><code>/* 1. 先编写移动端基础样式 */
.box {
    width: 100%;
    font-size: 14px;
}

/* 2. 逐步添加更大屏幕的样式 */
@media (min-width: 768px) {
    .box {
        width: 50%;
        font-size: 16px;
    }
}

@media (min-width: 1200px) {
    .box {
        width: 33.33%;
        font-size: 18px;
    }
}
</code></pre>
<h4>桌面优先</h4>
<pre><code>/* 1. 先编写桌面端样式 */
.box {
    width: 33.33%;
    font-size: 18px;
}

/* 2. 逐步添加小屏幕的样式 */
@media (max-width: 1199px) {
    .box {
        width: 50%;
        font-size: 16px;
    }
}

@media (max-width: 767px) {
    .box {
        width: 100%;
        font-size: 14px;
    }
}
</code></pre>
<h3>7. 最佳实践</h3>
<h4>断点设置建议</h4>
<pre><code>/* 超小屏幕（手机竖屏）&lt;576px */
/* 小屏幕（手机横屏/小平板）576px-767px */
/* 中等屏幕（平板）768px-991px */
/* 大屏幕（桌面）992px-1199px */
/* 超大屏幕（桌面大屏）≥1200px */
</code></pre>
<h4>实用技巧</h4>
<pre><code>/* 1. 使用min-width而不是精确值 */
@media (min-width: 768px) { /* ✓ 好 */ }
@media (width: 768px) { /* ✗ 差：768px的设备很少 */ }

/* 2. 合理控制媒体查询数量 */
@media (min-width: 576px) { /* 小屏平板 */ }
@media (min-width: 768px) { /* 平板 */ }
@media (min-width: 992px) { /* 桌面 */ }
@media (min-width: 1200px) { /* 大屏桌面 */ }

/* 3. 使用CSS变量管理断点 */
:root {
    --breakpoint-sm: 576px;
    --breakpoint-md: 768px;
    --breakpoint-lg: 992px;
    --breakpoint-xl: 1200px;
}

/* 4. 配合flex和grid使用 */
.grid {
    display: grid;
    grid-template-columns: 1fr;
}

@media (min-width: 768px) {
    .grid {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (min-width: 1200px) {
    .grid {
        grid-template-columns: repeat(4, 1fr);
    }
}
</code></pre>
<h3>8. 浏览器兼容性</h3>
<table>
<thead>
<tr>
<th>功能</th>
<th>Chrome</th>
<th>Firefox</th>
<th>Safari</th>
<th>Edge</th>
<th>IE</th>
</tr>
</thead>
<tbody>
<tr>
<td>基础媒体查询</td>
<td>1+</td>
<td>1+</td>
<td>1+</td>
<td>12+</td>
<td>9+</td>
</tr>
<tr>
<td>min/max前缀</td>
<td>1+</td>
<td>1+</td>
<td>1+</td>
<td>12+</td>
<td>9+</td>
</tr>
<tr>
<td>orientation</td>
<td>3+</td>
<td>6+</td>
<td>5+</td>
<td>12+</td>
<td>9+</td>
</tr>
<tr>
<td>aspect-ratio</td>
<td>3+</td>
<td>6+</td>
<td>5+</td>
<td>12+</td>
<td>✗</td>
</tr>
</tbody>
</table>
<h3>9. 常见错误避免</h3>
<pre><code>/* ✗ 错误1：媒体查询位置不当 */
/* 媒体查询应该放在样式规则之后 */
.box { color: red; }
@media (min-width: 768px) { .box { color: blue; } }

/* ✗ 错误2：断点重叠 */
@media (min-width: 768px) { .box { width: 750px; } }
@media (min-width: 750px) { .box { width: 970px; } } /* 750px处重叠 */

/* ✓ 正确：断点保持合理间距 */
@media (min-width: 768px) { .box { width: 750px; } }
@media (min-width: 992px) { .box { width: 970px; } }

/* ✗ 错误3：单位混淆 */
@media (min-width: 768) { /* ✗ 缺少单位 */ }
@media (min-width: 768px) { /* ✓ 正确 */ }

/* ✗ 错误4：媒体特性拼写错误 */
@media (min-width: 768px) and (max-with: 1200px) { /* ✗ max-with */ }
@media (min-width: 768px) and (max-width: 1200px) { /* ✓ max-width */ }
</code></pre>
<h2>BootStrap响应式</h2>
<h3>BootStrap框架介绍和引用</h3>
<p><strong>什么是Bootstrap</strong></p>
<p>Bootstrap 是由 Twitter 公司开发维护的一套<strong>前端UI框架</strong>，它提供了一套现成的CSS样式和组件，开发者可以直接引用这些类名，快速搭建响应式网页。</p>
<p><strong>Bootstrap优势</strong></p>
<table>
<thead>
<tr>
<th>优势</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>响应式设计</td>
<td>一套代码适配手机、平板、PC等多端设备</td>
</tr>
<tr>
<td>标准化</td>
<td>提供统一的视觉风格和组件规范</td>
</tr>
<tr>
<td>开发效率高</td>
<td>大量预置样式和组件，无需从零写CSS</td>
</tr>
<tr>
<td>跨浏览器兼容</td>
<td>兼容主流浏览器，减少兼容性调试</td>
</tr>
<tr>
<td>可定制</td>
<td>支持按需定制，也可以覆盖默认样式</td>
</tr>
</tbody>
</table>
<p><strong>Bootstrap版本</strong></p>
<table>
<thead>
<tr>
<th>版本</th>
<th>特点</th>
<th>适用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bootstrap 3</td>
<td>经典稳定，支持IE8+，国内网课最常用</td>
<td>学习入门、传统项目</td>
</tr>
<tr>
<td>Bootstrap 4</td>
<td>引入Flexbox，移除IE支持，新增卡片等组件</td>
<td>现代项目</td>
</tr>
<tr>
<td>Bootstrap 5</td>
<td>移除jQuery依赖，新增RTL支持，纯CSS组件</td>
<td>新项目推荐</td>
</tr>
</tbody>
</table>
<p><strong>引入方式</strong></p>
<p>方式一：CDN引入（推荐学习时使用）</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html lang="zh-CN"&gt;
&lt;head&gt;
    &lt;meta charset="utf-8"&gt;
    &lt;!-- 必须配置viewport，否则响应式无效 --&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1"&gt;
    &lt;title&gt;Bootstrap示例&lt;/title&gt;
    &lt;!-- Bootstrap核心CSS --&gt;
    &lt;link href="https://cdn.jsdelivr.net/npm/bootstrap@3.4.1/dist/css/bootstrap.min.css" rel="stylesheet"&gt;
    &lt;!-- jQuery（Bootstrap3的JS依赖jQuery） --&gt;
    &lt;script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"&gt;&lt;/script&gt;
    &lt;!-- Bootstrap核心JS --&gt;
    &lt;script src="https://cdn.jsdelivr.net/npm/bootstrap@3.4.1/dist/js/bootstrap.min.js"&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;!-- 页面内容 --&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>方式二：本地引入</p>
<ol>
<li>下载Bootstrap压缩包并解压</li>
<li>将css、js、fonts文件夹复制到项目中</li>
<li>在HTML中引入本地文件</li>
</ol>
<pre><code>&lt;link rel="stylesheet" href="bootstrap/css/bootstrap.min.css"&gt;
&lt;script src="bootstrap/js/jquery.min.js"&gt;&lt;/script&gt;
&lt;script src="bootstrap/js/bootstrap.min.js"&gt;&lt;/script&gt;
</code></pre>
<p><strong>注意事项</strong>：</p>
<ul>
<li><code>viewport</code> meta标签是响应式的必要条件，必须添加</li>
<li>Bootstrap 3的JS插件依赖jQuery，需要先引入jQuery</li>
<li>引入顺序：先CSS，再jQuery，最后Bootstrap JS</li>
</ul>
<hr />
<h3>BootStrap框架基本使用</h3>
<p><strong>容器（Container）</strong></p>
<p>Bootstrap需要一个容器来包裹页面内容，提供两种容器类：</p>
<table>
<thead>
<tr>
<th>容器类</th>
<th>特点</th>
<th>适用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>.container</code></td>
<td>固定宽度，居中显示，两侧留白</td>
<td>PC端页面、常规布局</td>
</tr>
<tr>
<td><code>.container-fluid</code></td>
<td>100%宽度，充满视口</td>
<td>全屏布局、移动端</td>
</tr>
</tbody>
</table>
<pre><code>&lt;!-- 固定宽度容器 --&gt;
&lt;div class="container"&gt;
  &lt;h1&gt;固定宽度容器&lt;/h1&gt;
  &lt;p&gt;在不同屏幕尺寸下有不同的固定宽度，并居中显示&lt;/p&gt;
&lt;/div&gt;

&lt;!-- 全屏宽度容器 --&gt;
&lt;div class="container-fluid"&gt;
  &lt;h1&gt;全屏宽度容器&lt;/h1&gt;
  &lt;p&gt;始终占满整个屏幕宽度&lt;/p&gt;
&lt;/div&gt;
</code></pre>
<p>.container在不同断点下的宽度：</p>
<table>
<thead>
<tr>
<th>屏幕尺寸</th>
<th>断点宽度</th>
<th>container宽度</th>
</tr>
</thead>
<tbody>
<tr>
<td>超小屏（手机）</td>
<td>&lt;768px</td>
<td>100%</td>
</tr>
<tr>
<td>小屏（平板）</td>
<td>≥768px</td>
<td>750px</td>
</tr>
<tr>
<td>中屏（桌面）</td>
<td>≥992px</td>
<td>970px</td>
</tr>
<tr>
<td>大屏（大桌面）</td>
<td>≥1200px</td>
<td>1170px</td>
</tr>
</tbody>
</table>
<p><strong>按钮（Buttons）</strong></p>
<p>Bootstrap提供了多种预定义按钮样式：</p>
<pre><code>&lt;!-- 基础按钮 --&gt;
&lt;button class="btn btn-default"&gt;默认按钮&lt;/button&gt;

&lt;!-- 按钮颜色样式 --&gt;
&lt;button class="btn btn-default"&gt;默认&lt;/button&gt;
&lt;button class="btn btn-primary"&gt;主要&lt;/button&gt;
&lt;button class="btn btn-success"&gt;成功&lt;/button&gt;
&lt;button class="btn btn-info"&gt;信息&lt;/button&gt;
&lt;button class="btn btn-warning"&gt;警告&lt;/button&gt;
&lt;button class="btn btn-danger"&gt;危险&lt;/button&gt;
&lt;button class="btn btn-link"&gt;链接&lt;/button&gt;

&lt;!-- 按钮尺寸 --&gt;
&lt;button class="btn btn-primary btn-lg"&gt;大按钮&lt;/button&gt;
&lt;button class="btn btn-primary"&gt;默认按钮&lt;/button&gt;
&lt;button class="btn btn-primary btn-sm"&gt;小按钮&lt;/button&gt;
&lt;button class="btn btn-primary btn-xs"&gt;超小按钮&lt;/button&gt;

&lt;!-- 块级按钮（占满整行） --&gt;
&lt;button class="btn btn-primary btn-block"&gt;块级按钮&lt;/button&gt;

&lt;!-- 按钮状态 --&gt;
&lt;button class="btn btn-primary active"&gt;激活状态&lt;/button&gt;
&lt;button class="btn btn-primary disabled"&gt;禁用状态&lt;/button&gt;
</code></pre>
<p>也可以用在<code>&lt;a&gt;</code>标签上：</p>
<pre><code>&lt;a href="#" class="btn btn-primary"&gt;链接按钮&lt;/a&gt;
&lt;input type="button" class="btn btn-success" value="输入框按钮"&gt;
</code></pre>
<p><strong>排版类</strong></p>
<pre><code>&lt;!-- 标题 --&gt;
&lt;h1&gt;h1. Bootstrap标题 &lt;small&gt;副标题&lt;/small&gt;&lt;/h1&gt;
&lt;h2&gt;h2. Bootstrap标题&lt;/h2&gt;
&lt;h3&gt;h3. Bootstrap标题&lt;/h3&gt;

&lt;!-- 页面主体（突出显示） --&gt;
&lt;p class="lead"&gt;这是一个突出显示的段落&lt;/p&gt;

&lt;!-- 文本对齐 --&gt;
&lt;p class="text-left"&gt;左对齐&lt;/p&gt;
&lt;p class="text-center"&gt;居中&lt;/p&gt;
&lt;p class="text-right"&gt;右对齐&lt;/p&gt;

&lt;!-- 文本颜色 --&gt;
&lt;p class="text-muted"&gt;柔和灰色&lt;/p&gt;
&lt;p class="text-primary"&gt;主要蓝色&lt;/p&gt;
&lt;p class="text-success"&gt;成功绿色&lt;/p&gt;
&lt;p class="text-info"&gt;信息蓝色&lt;/p&gt;
&lt;p class="text-warning"&gt;警告橙色&lt;/p&gt;
&lt;p class="text-danger"&gt;危险红色&lt;/p&gt;

&lt;!-- 文本大小写 --&gt;
&lt;p class="text-lowercase"&gt;LOWERCASE 转为小写&lt;/p&gt;
&lt;p class="text-uppercase"&gt;uppercase 转为大写&lt;/p&gt;
&lt;p class="text-capitalize"&gt;capitalized 首字母大写&lt;/p&gt;
</code></pre>
<p><strong>列表</strong></p>
<pre><code>&lt;!-- 无序列表（去点） --&gt;
&lt;ul class="list-unstyled"&gt;
  &lt;li&gt;列表项1&lt;/li&gt;
  &lt;li&gt;列表项2&lt;/li&gt;
  &lt;li&gt;列表项3&lt;/li&gt;
&lt;/ul&gt;

&lt;!-- 内联列表（横向排列） --&gt;
&lt;ul class="list-inline"&gt;
  &lt;li&gt;首页&lt;/li&gt;
  &lt;li&gt;产品&lt;/li&gt;
  &lt;li&gt;关于&lt;/li&gt;
&lt;/ul&gt;
</code></pre>
<p><strong>表格</strong></p>
<pre><code>&lt;table class="table table-striped table-bordered table-hover"&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;姓名&lt;/th&gt;
      &lt;th&gt;年龄&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr class="success"&gt;
      &lt;td&gt;张三&lt;/td&gt;
      &lt;td&gt;20&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;李四&lt;/td&gt;
      &lt;td&gt;22&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr class="danger"&gt;
      &lt;td&gt;王五&lt;/td&gt;
      &lt;td&gt;25&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</code></pre>
<p>表格样式类：</p>
<ul>
<li><code>.table</code> - 基础表格样式（必加）</li>
<li><code>.table-striped</code> - 斑马纹</li>
<li><code>.table-bordered</code> - 带边框</li>
<li><code>.table-hover</code> - 鼠标悬停高亮</li>
<li><code>.table-condensed</code> - 紧凑表格</li>
</ul>
<p><strong>表单</strong></p>
<pre><code>&lt;form&gt;
  &lt;div class="form-group"&gt;
    &lt;label&gt;用户名&lt;/label&gt;
    &lt;input type="text" class="form-control" placeholder="请输入用户名"&gt;
  &lt;/div&gt;
  &lt;div class="form-group"&gt;
    &lt;label&gt;密码&lt;/label&gt;
    &lt;input type="password" class="form-control" placeholder="请输入密码"&gt;
  &lt;/div&gt;
  &lt;button type="submit" class="btn btn-primary"&gt;提交&lt;/button&gt;
&lt;/form&gt;
</code></pre>
<hr />
<h3>BootStrap栅格布局</h3>
<p><strong>栅格系统简介</strong></p>
<p>Bootstrap的栅格系统（Grid System）是一套<strong>响应式、移动设备优先</strong>的流式栅格布局系统。它将页面容器平均划分为<strong>12列</strong>，通过给列添加类名来控制元素在不同屏幕下的宽度。</p>
<p><strong>栅格系统工作原理</strong></p>
<pre><code>┌──────────────────────────────────────────┐
│              container                   │
│  ┌───────────────────────────────────┐   │
│  │            row (12列)             │   │
│  ├─────┬─────┬─────┬─────┬─────┬─────┤   │
│  │col- │col- │col- │col- │col- │col- │   │
│  │ md-2│ md-2│ md-2│ md-2│ md-2│ md-2│   │
│  └─────┴─────┴─────┴─────┴─────┴─────┘   │
└──────────────────────────────────────────┘
</code></pre>
<p>核心要点：</p>
<ol>
<li><strong>行（row）必须放在container中</strong></li>
<li><strong>列（col）必须作为row的直接子元素</strong></li>
<li><strong>内容应该放在列（col）中</strong></li>
<li><strong>每行12列</strong>，列数之和超过12会自动换行</li>
</ol>
<p><strong>栅格参数（断点）</strong></p>
<table>
<thead>
<tr>
<th></th>
<th>超小屏 &lt;br&gt;手机 (&lt;768px)</th>
<th>小屏 &lt;br&gt;平板 (≥768px)</th>
<th>中屏 &lt;br&gt;桌面 (≥992px)</th>
<th>大屏 &lt;br&gt;大桌面 (≥1200px)</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>类名前缀</strong></td>
<td><code>.col-xs-*</code></td>
<td><code>.col-sm-*</code></td>
<td><code>.col-md-*</code></td>
<td><code>.col-lg-*</code></td>
</tr>
<tr>
<td><strong>列数</strong></td>
<td>12</td>
<td>12</td>
<td>12</td>
<td>12</td>
</tr>
<tr>
<td><strong>最大列宽</strong></td>
<td>自动</td>
<td>~62px</td>
<td>~81px</td>
<td>~97px</td>
</tr>
<tr>
<td><strong>间距(gutter)</strong></td>
<td>30px（左右各15px）</td>
<td>30px</td>
<td>30px</td>
<td>30px</td>
</tr>
<tr>
<td><strong>是否浮动</strong></td>
<td>是</td>
<td>是</td>
<td>是</td>
<td>是</td>
</tr>
</tbody>
</table>
<p><strong>基础用法</strong></p>
<p>示例1：等宽列（三列布局）</p>
<pre><code>&lt;div class="container"&gt;
  &lt;div class="row"&gt;
    &lt;div class="col-md-4"&gt;第一列（占4列，4/12 = 1/3）&lt;/div&gt;
    &lt;div class="col-md-4"&gt;第二列（占4列）&lt;/div&gt;
    &lt;div class="col-md-4"&gt;第三列（占4列）&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p>示例2：不等宽列（侧边栏+主内容）</p>
<pre><code>&lt;div class="container"&gt;
  &lt;div class="row"&gt;
    &lt;div class="col-md-3"&gt;侧边栏（占3列）&lt;/div&gt;
    &lt;div class="col-md-9"&gt;主内容区（占9列）&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p>示例3：移动端和桌面端不同布局</p>
<pre><code>&lt;div class="container"&gt;
  &lt;div class="row"&gt;
    &lt;!-- 手机：12列（满宽），平板/桌面：6列（半宽） --&gt;
    &lt;div class="col-xs-12 col-md-6"&gt;左侧内容&lt;/div&gt;
    &lt;!-- 手机：12列（满宽），平板/桌面：6列（半宽） --&gt;
    &lt;div class="col-xs-12 col-md-6"&gt;右侧内容&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p>列组合示例代码（带颜色区分）：</p>
<pre><code>&lt;style&gt;
  .row &gt; div {
    height: 50px;
    background-color: pink;
    border: 1px solid #fff;
  }
&lt;/style&gt;

&lt;div class="container"&gt;
  &lt;!-- 12列：12个col-md-1 --&gt;
  &lt;div class="row"&gt;
    &lt;div class="col-md-1"&gt;1&lt;/div&gt;
    &lt;div class="col-md-1"&gt;2&lt;/div&gt;
    &lt;div class="col-md-1"&gt;3&lt;/div&gt;
    &lt;div class="col-md-1"&gt;4&lt;/div&gt;
    &lt;div class="col-md-1"&gt;5&lt;/div&gt;
    &lt;div class="col-md-1"&gt;6&lt;/div&gt;
    &lt;div class="col-md-1"&gt;7&lt;/div&gt;
    &lt;div class="col-md-1"&gt;8&lt;/div&gt;
    &lt;div class="col-md-1"&gt;9&lt;/div&gt;
    &lt;div class="col-md-1"&gt;10&lt;/div&gt;
    &lt;div class="col-md-1"&gt;11&lt;/div&gt;
    &lt;div class="col-md-1"&gt;12&lt;/div&gt;
  &lt;/div&gt;
  &lt;!-- 6列：6个col-md-2 --&gt;
  &lt;div class="row"&gt;
    &lt;div class="col-md-2"&gt;2&lt;/div&gt;
    &lt;div class="col-md-2"&gt;2&lt;/div&gt;
    &lt;div class="col-md-2"&gt;2&lt;/div&gt;
    &lt;div class="col-md-2"&gt;2&lt;/div&gt;
    &lt;div class="col-md-2"&gt;2&lt;/div&gt;
    &lt;div class="col-md-2"&gt;2&lt;/div&gt;
  &lt;/div&gt;
  &lt;!-- 4列：4个col-md-3 --&gt;
  &lt;div class="row"&gt;
    &lt;div class="col-md-3"&gt;3&lt;/div&gt;
    &lt;div class="col-md-3"&gt;3&lt;/div&gt;
    &lt;div class="col-md-3"&gt;3&lt;/div&gt;
    &lt;div class="col-md-3"&gt;3&lt;/div&gt;
  &lt;/div&gt;
  &lt;!-- 3列：3个col-md-4 --&gt;
  &lt;div class="row"&gt;
    &lt;div class="col-md-4"&gt;4&lt;/div&gt;
    &lt;div class="col-md-4"&gt;4&lt;/div&gt;
    &lt;div class="col-md-4"&gt;4&lt;/div&gt;
  &lt;/div&gt;
  &lt;!-- 2列：2个col-md-6 --&gt;
  &lt;div class="row"&gt;
    &lt;div class="col-md-6"&gt;6&lt;/div&gt;
    &lt;div class="col-md-6"&gt;6&lt;/div&gt;
  &lt;/div&gt;
  &lt;!-- 1列：1个col-md-12 --&gt;
  &lt;div class="row"&gt;
    &lt;div class="col-md-12"&gt;12&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p><strong>列偏移（列与列之间的距离）</strong></p>
<p>使用 <code>.col-md-offset-*</code> 类可以将列向右偏移*个列宽：</p>
<pre><code>&lt;div class="container"&gt;
  &lt;div class="row"&gt;
    &lt;!-- 向右偏移3列，实际位置在第4列开始 --&gt;
    &lt;div class="col-md-4 col-md-offset-3"&gt;
      占4列，向右偏移3列
    &lt;/div&gt;
  &lt;/div&gt;
  
  &lt;!-- 两列居中分开 --&gt;
  &lt;div class="row"&gt;
    &lt;div class="col-md-3"&gt;左侧&lt;/div&gt;
    &lt;div class="col-md-3 col-md-offset-6"&gt;右侧（偏移6列靠右）&lt;/div&gt;
  &lt;/div&gt;
  
  &lt;!-- 居中显示 --&gt;
  &lt;div class="row"&gt;
    &lt;!-- 占6列，偏移3列，实现居中（(12-6)/2=3） --&gt;
    &lt;div class="col-md-6 col-md-offset-3"&gt;
      这个div水平居中
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p>常用的offset组合：</p>
<table>
<thead>
<tr>
<th>需求</th>
<th>类名</th>
</tr>
</thead>
<tbody>
<tr>
<td>列居中（占6列）</td>
<td><code>col-md-6 col-md-offset-3</code></td>
</tr>
<tr>
<td>列居中（占4列）</td>
<td><code>col-md-4 col-md-offset-4</code></td>
</tr>
<tr>
<td>列居中（占8列）</td>
<td><code>col-md-8 col-md-offset-2</code></td>
</tr>
<tr>
<td>两列两端对齐</td>
<td><code>col-md-3</code> + <code>col-md-3 col-md-offset-6</code></td>
</tr>
</tbody>
</table>
<hr />
<h3>BootStrap列布局和字体图标</h3>
<p><strong>列排序（push/pull）</strong></p>
<p>使用 <code>.col-md-push-*</code> 向右推，<code>.col-md-pull-*</code> 向左拉，可以改变列的顺序：</p>
<pre><code>&lt;div class="row"&gt;
  &lt;!-- 左列向右推6列（到右侧） --&gt;
  &lt;div class="col-md-6 col-md-push-6"&gt;
    原来在左边，现在在右边
  &lt;/div&gt;
  &lt;!-- 右列向左拉6列（到左侧） --&gt;
  &lt;div class="col-md-6 col-md-pull-6"&gt;
    原来在右边，现在在左边
  &lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p>应用场景：移动端和PC端内容顺序互换</p>
<pre><code>&lt;div class="row"&gt;
  &lt;!-- PC端：主内容在左(9列)，侧边栏在右(3列) --&gt;
  &lt;!-- 移动端：侧边栏显示在上方，主内容在下方 --&gt;
  &lt;div class="col-md-9 col-md-push-3"&gt;主内容区&lt;/div&gt;
  &lt;div class="col-md-3 col-md-pull-9"&gt;侧边栏&lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p><strong>列嵌套</strong></p>
<p>栅格系统支持列嵌套，即在列中再使用row和col：</p>
<pre><code>&lt;div class="row"&gt;
  &lt;div class="col-md-9"&gt;
    第一列占9列
    &lt;!-- 在col-md-9中嵌套新的行（自动分成12列） --&gt;
    &lt;div class="row"&gt;
      &lt;div class="col-md-6"&gt;嵌套第一列（占父级的一半）&lt;/div&gt;
      &lt;div class="col-md-6"&gt;嵌套第二列（占父级的一半）&lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="col-md-3"&gt;
    第二列占3列
  &lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p>注意：嵌套的row不需要container了，因为父级col已经有padding。</p>
<p><strong>响应式工具（显示/隐藏）</strong></p>
<p>通过类名控制元素在不同屏幕下的显示与隐藏：</p>
<table>
<thead>
<tr>
<th>类名</th>
<th>超小屏 &lt;768px</th>
<th>小屏 ≥768px</th>
<th>中屏 ≥992px</th>
<th>大屏 ≥1200px</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>.visible-xs-*</code></td>
<td>可见</td>
<td>隐藏</td>
<td>隐藏</td>
<td>隐藏</td>
</tr>
<tr>
<td><code>.visible-sm-*</code></td>
<td>隐藏</td>
<td>可见</td>
<td>隐藏</td>
<td>隐藏</td>
</tr>
<tr>
<td><code>.visible-md-*</code></td>
<td>隐藏</td>
<td>隐藏</td>
<td>可见</td>
<td>隐藏</td>
</tr>
<tr>
<td><code>.visible-lg-*</code></td>
<td>隐藏</td>
<td>隐藏</td>
<td>隐藏</td>
<td>可见</td>
</tr>
<tr>
<td><code>.hidden-xs</code></td>
<td>隐藏</td>
<td>可见</td>
<td>可见</td>
<td>可见</td>
</tr>
<tr>
<td><code>.hidden-sm</code></td>
<td>可见</td>
<td>隐藏</td>
<td>可见</td>
<td>可见</td>
</tr>
<tr>
<td><code>.hidden-md</code></td>
<td>可见</td>
<td>可见</td>
<td>隐藏</td>
<td>可见</td>
</tr>
<tr>
<td><code>.hidden-lg</code></td>
<td>可见</td>
<td>可见</td>
<td>可见</td>
<td>隐藏</td>
</tr>
</tbody>
</table>
<pre><code>&lt;!-- 只在手机上显示 --&gt;
&lt;div class="visible-xs-block"&gt;手机端菜单按钮&lt;/div&gt;

&lt;!-- 在手机上隐藏，其他尺寸显示 --&gt;
&lt;div class="hidden-xs"&gt;PC端侧边栏&lt;/div&gt;
</code></pre>
<p><strong>字体图标（Glyphicons）</strong></p>
<p>Bootstrap 3 内置了 Glyphicons Halflings 字体图标，共包含200多个图标。</p>
<p><strong>使用方法</strong>：</p>
<pre><code>&lt;!-- 基本用法 --&gt;
&lt;span class="glyphicon glyphicon-search"&gt;&lt;/span&gt;

&lt;!-- 图标配合文字 --&gt;
&lt;button class="btn btn-primary"&gt;
  &lt;span class="glyphicon glyphicon-plus"&gt;&lt;/span&gt; 新增
&lt;/button&gt;

&lt;button class="btn btn-danger"&gt;
  &lt;span class="glyphicon glyphicon-trash"&gt;&lt;/span&gt; 删除
&lt;/button&gt;

&lt;!-- 图标大小 --&gt;
&lt;span class="glyphicon glyphicon-star" style="font-size: 20px;"&gt;&lt;/span&gt;
&lt;span class="glyphicon glyphicon-star" style="font-size: 30px;"&gt;&lt;/span&gt;

&lt;!-- 图标颜色 --&gt;
&lt;span class="glyphicon glyphicon-heart" style="color: red;"&gt;&lt;/span&gt;
&lt;span class="glyphicon glyphicon-ok" style="color: green;"&gt;&lt;/span&gt;
</code></pre>
<p><strong>常用字体图标类名</strong>：</p>
<table>
<thead>
<tr>
<th>类名</th>
<th>图标含义</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>glyphicon-search</code></td>
<td>搜索（放大镜）</td>
</tr>
<tr>
<td><code>glyphicon-user</code></td>
<td>用户</td>
</tr>
<tr>
<td><code>glyphicon-home</code></td>
<td>首页（房子）</td>
</tr>
<tr>
<td><code>glyphicon-heart</code></td>
<td>心形/收藏</td>
</tr>
<tr>
<td><code>glyphicon-star</code></td>
<td>星星</td>
</tr>
<tr>
<td><code>glyphicon-plus</code></td>
<td>加号/新增</td>
</tr>
<tr>
<td><code>glyphicon-minus</code></td>
<td>减号</td>
</tr>
<tr>
<td><code>glyphicon-trash</code></td>
<td>删除/垃圾桶</td>
</tr>
<tr>
<td><code>glyphicon-pencil</code></td>
<td>编辑/铅笔</td>
</tr>
<tr>
<td><code>glyphicon-cog</code> / <code>glyphicon-wrench</code></td>
<td>设置/齿轮</td>
</tr>
<tr>
<td><code>glyphicon-film</code></td>
<td>视频/影片</td>
</tr>
<tr>
<td><code>glyphicon-music</code></td>
<td>音乐</td>
</tr>
<tr>
<td><code>glyphicon-camera</code></td>
<td>相机</td>
</tr>
<tr>
<td><code>glyphicon-envelope</code></td>
<td>信封/邮件</td>
</tr>
<tr>
<td><code>glyphicon-shopping-cart</code></td>
<td>购物车</td>
</tr>
<tr>
<td><code>glyphicon-ok</code></td>
<td>对勾/成功</td>
</tr>
<tr>
<td><code>glyphicon-remove</code></td>
<td>叉号/关闭</td>
</tr>
<tr>
<td><code>glyphicon-chevron-left</code></td>
<td>左箭头</td>
</tr>
<tr>
<td><code>glyphicon-chevron-right</code></td>
<td>右箭头</td>
</tr>
<tr>
<td><code>glyphicon-arrow-up</code></td>
<td>上箭头</td>
</tr>
<tr>
<td><code>glyphicon-arrow-down</code></td>
<td>下箭头</td>
</tr>
</tbody>
</table>
<p><strong>Bootstrap 5 图标（Bootstrap Icons）</strong></p>
<p>Bootstrap 4/5 不再内置Glyphicons，推荐使用 Bootstrap Icons：</p>
<pre><code>&lt;!-- 引入Bootstrap Icons --&gt;
&lt;link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css"&gt;

&lt;!-- 使用 --&gt;
&lt;i class="bi bi-search"&gt;&lt;/i&gt;
&lt;i class="bi bi-person"&gt;&lt;/i&gt;
&lt;i class="bi bi-heart"&gt;&lt;/i&gt;
</code></pre>
<p><strong>字体图标使用原理</strong>：</p>
<p>字体图标本质上是一种特殊字体，通过CSS的<code>::before</code>伪元素插入字符，每个类名对应一个图标的Unicode编码：</p>
<pre><code>.glyphicon-search::before {
  content: "\e003";  /* Unicode编码，渲染为搜索图标 */
}
</code></pre>
<p>优点：</p>
<ul>
<li>加载速度快（字体文件比图片小）</li>
<li>可任意缩放不失真</li>
<li>可用CSS修改颜色、大小、阴影等</li>
<li>支持CSS动画</li>
</ul>
<h2>其他技巧</h2>
<h3>svg裁剪背景</h3>
<p>有时候，想要裁剪更复杂的形状，但是clip-path只能裁剪矩形。这时候，可以使用svg的clip-path属性来裁剪更复杂的形状。</p>
<p><strong>示例</strong></p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html lang="zh-CN"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
    &lt;title&gt;SVG裁剪背景示例&lt;/title&gt;
    &lt;style&gt;
        /* 定义SVG裁剪路径 */
        .clip-svg {
            clip-path: url(#myClip);
        }

        /* 被裁剪的元素 */
        .clipped-element {
            width: 400px;
            height: 300px;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            clip-path: url(#myClip);
        }

        /* 对比：不裁剪的元素 */
        .normal-element {
            width: 400px;
            height: 300px;
            background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
            margin-top: 20px;
        }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;!-- 定义SVG裁剪路径 --&gt;
    &lt;svg width="0" height="0"&gt;
        &lt;defs&gt;
            &lt;!-- 复杂形状裁剪路径：波浪形 --&gt;
            &lt;clipPath id="myClip"&gt;
                &lt;path d="M0,50 Q100,0 200,50 T400,50 L400,300 L0,300 Z" /&gt;
            &lt;/clipPath&gt;
            
            &lt;!-- 其他裁剪路径示例 --&gt;
            &lt;!-- 圆形裁剪 --&gt;
            &lt;clipPath id="circleClip"&gt;
                &lt;circle cx="200" cy="150" r="100" /&gt;
            &lt;/clipPath&gt;
            
            &lt;!-- 多边形裁剪（六边形） --&gt;
            &lt;clipPath id="hexagonClip"&gt;
                &lt;polygon points="200,0 400,75 400,225 200,300 0,225 0,75" /&gt;
            &lt;/clipPath&gt;
            
            &lt;!-- 文字裁剪 --&gt;
            &lt;clipPath id="textClip"&gt;
                &lt;text x="50" y="150" font-size="60" font-weight="bold"&gt;HELLO&lt;/text&gt;
            &lt;/clipPath&gt;
        &lt;/defs&gt;
    &lt;/svg&gt;

    &lt;!-- 使用裁剪路径的元素 --&gt;
    &lt;h3&gt;使用SVG裁剪路径的元素（波浪形状）：&lt;/h3&gt;
    &lt;div class="clipped-element"&gt;&lt;/div&gt;

    &lt;h3&gt;对比：不裁剪的元素：&lt;/h3&gt;
    &lt;div class="normal-element"&gt;&lt;/div&gt;

    &lt;!-- 其他裁剪形状示例 --&gt;
    &lt;h3&gt;圆形裁剪：&lt;/h3&gt;
    &lt;div style="width: 400px; height: 300px; background: linear-gradient(45deg, #FF6B6B, #4ECDC4); clip-path: url(#circleClip);"&gt;&lt;/div&gt;

    &lt;h3&gt;六边形裁剪：&lt;/h3&gt;
    &lt;div style="width: 400px; height: 300px; background: linear-gradient(45deg, #45B7D1, #96CEB4); clip-path: url(#hexagonClip);"&gt;&lt;/div&gt;

    &lt;h3&gt;文字裁剪：&lt;/h3&gt;
    &lt;div style="width: 400px; height: 300px; background: url('https://picsum.photos/400/300') center/cover; clip-path: url(#textClip);"&gt;&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p><strong>关键要点：</strong></p>
<ol>
<li><strong>SVG定义</strong>：在HTML中定义SVG的<code>&lt;defs&gt;</code>标签，包含各种<code>&lt;clipPath&gt;</code>元素</li>
<li><strong>裁剪路径ID</strong>：每个<code>&lt;clipPath&gt;</code>需要一个唯一的ID，如<code>id="myClip"</code></li>
<li><strong>CSS引用</strong>：在CSS中使用<code>clip-path: url(#myClip)</code>引用SVG裁剪路径</li>
<li><strong>复杂形状</strong>：SVG裁剪路径可以创建任意复杂的形状，如波浪、多边形、文字等</li>
</ol>
<p><strong>SVG裁剪路径类型对比：</strong></p>
<table>
<thead>
<tr>
<th>裁剪类型</th>
<th>SVG元素</th>
<th>适用场景</th>
<th>复杂度</th>
</tr>
</thead>
<tbody>
<tr>
<td>简单形状</td>
<td><code>&lt;circle&gt;</code>、<code>&lt;rect&gt;</code></td>
<td>圆形、矩形裁剪</td>
<td>低</td>
</tr>
<tr>
<td>多边形</td>
<td><code>&lt;polygon&gt;</code></td>
<td>星形、六边形等</td>
<td>中</td>
</tr>
<tr>
<td>自定义路径</td>
<td><code>&lt;path&gt;</code></td>
<td>波浪、复杂曲线</td>
<td>高</td>
</tr>
<tr>
<td>文字裁剪</td>
<td><code>&lt;text&gt;</code></td>
<td>文字形状背景</td>
<td>高</td>
</tr>
</tbody>
</table>
<p><strong>与CSS clip-path对比：</strong></p>
<table>
<thead>
<tr>
<th>特性</th>
<th>CSS clip-path</th>
<th>SVG clip-path</th>
</tr>
</thead>
<tbody>
<tr>
<td>基础形状</td>
<td>支持（circle、polygon等）</td>
<td>支持</td>
</tr>
<tr>
<td>复杂路径</td>
<td>不支持</td>
<td>✓ 支持</td>
</tr>
<tr>
<td>文字裁剪</td>
<td>不支持</td>
<td>✓ 支持</td>
</tr>
<tr>
<td>动画支持</td>
<td>✓ 支持</td>
<td>✓ 支持</td>
</tr>
<tr>
<td>浏览器兼容</td>
<td>较好</td>
<td>略差（IE不支持）</td>
</tr>
</tbody>
</table>
<p><strong>实用技巧：</strong></p>
<pre><code>/* 1. 响应式裁剪 */
.clipped-responsive {
    clip-path: url(#responsiveClip);
}

/* 2. 动画裁剪 */
.clipped-animated {
    clip-path: url(#animatedClip);
    animation: clip-animation 3s infinite;
}

@keyframes clip-animation {
    0% { clip-path: url(#clip1); }
    50% { clip-path: url(#clip2); }
    100% { clip-path: url(#clip1); }
}

/* 3. hover效果 */
.clipped-hover {
    clip-path: url(#normalClip);
    transition: clip-path 0.3s ease;
}

.clipped-hover:hover {
    clip-path: url(#hoverClip);
}
</code></pre>
<p><strong>注意事项：</strong></p>
<ul>
<li>SVG裁剪路径需要在HTML中定义，不能在CSS中直接创建</li>
<li>裁剪路径的坐标系相对于被裁剪元素的左上角（0,0）</li>
<li>被裁剪的部分会完全隐藏，不显示任何内容</li>
<li>SVG裁剪路径支持动画和过渡效果</li>
<li>浏览器兼容性：IE不支持，Edge12+支持</li>
</ul>
<hr />
<h3>TDK网站优化标签</h3>
<p>TDK是 <strong>Title</strong>、<strong>Description</strong> 和 <strong>Keywords</strong> 三个元标签的缩写，它们是网站SEO优化的核心元素，直接影响搜索引擎对网页的理解和排名。</p>
<table>
<thead>
<tr>
<th>标签</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Title</strong></td>
<td>决定搜索结果的标题显示，是搜索引擎判断页面主题的首要依据</td>
</tr>
<tr>
<td><strong>Description</strong></td>
<td>生成搜索结果的摘要描述，影响用户点击率（CTR）</td>
</tr>
<tr>
<td><strong>Keywords</strong></td>
<td>提供页面主题关键词（尽管现代搜索引擎已弱化其权重，仍部分参考）</td>
</tr>
</tbody>
</table>
<p>三者共同构成网页的"第一印象"，优化不当可能导致排名下降或点击流失。</p>
<p><strong>代码示例：</strong></p>
<pre><code>&lt;title&gt;vivo智能手机官方网站 - X200s 影像旗舰&lt;/title&gt;

&lt;meta name="keywords" content="vivo, vivo官网, vivo智能手机大全, vivo手机最新报价, vivo手机图片, X200系列, S20系列, X Fold3, vivo Pad5 Pro, vivo WATCH 5, iQOO 13"&gt;

&lt;meta name="description" content="手机领域的国际化品牌，vivo官网提供手机资讯、售前咨询、在线购买、售后服务等功能；vivo当季明星..."&gt;
</code></pre>
<blockquote>
<p><strong>提示</strong>：国内几乎每个网站都会有这三大标签，是SEO优化的基础。</p>
</blockquote>
<hr />
<h3>图片遮罩 mask-image</h3>
<p><code>mask-image</code>的核心作用是通过定义一张图片或一个渐变来充当遮罩，从而控制一个元素的可视区域。</p>
<p><strong>语法</strong>：</p>
<pre><code>.box {
mask-image: url('图片URL');
/* 或者 */
mask-image: linear-gradient(45deg, #FF6B6B, #4ECDC4);
}
</code></pre>
<hr />
<h3>自定义浏览器滚动条</h3>
<p>浏览器滚动条包含两个部分，轨道和滑块。</p>
<pre><code>/* 滚动条设置宽度和高度 */
::-webkit-scrollbar {
  width: 10px;
}
/* 设置轨道背景色，圆角等 */
::-webkit-scrollbar-track {
  background-color: var(--vivo-dark);
}
/* 设置滑块样式 */
::-webkit-scrollbar-thumb {
  background: linear-gradient(45deg, var(--vivo-blue), var(--vivo-cyan));
  border-radius: 10px;
}
/* 鼠标经过滑块 */
::-webkit-scrollbar-thumb:hover {
  background: linear-gradient(45deg, var(--vivo-cyan), var(--vivo-blue));
}
</code></pre>
]]></content>
    <author><name>仙鹤</name></author>
    <category term="前端"/>
  </entry>
  <entry>
    <title>css移动网页开发</title>
    <link href="https://xianhe51920.github.io/posts/mobile_pages/" rel="alternate" type="text/html"/>
    <id>https://xianhe51920.github.io/posts/mobile_pages/</id>
    <published>2026-06-10T00:00:00.000Z</published>
    <updated>2026-06-10T00:00:00.000Z</updated>
    <summary>移动端基础知识，vw/vm适配，rem适配</summary>
    <content type="html"><![CDATA[<h1>移动网页开发</h1>
<ol>
<li>移动端知识</li>
<li>vw/vmin适配方案</li>
<li>.rem适配方案</li>
</ol>
<h2>移动端知识</h2>
<p><strong>常见的移动端开发</strong></p>
<ul>
<li><strong>原生APP开发</strong>：针对安卓，ios，鸿蒙移动操作系统开发的APP</li>
<li><strong>小程序端</strong>：微信小程序，支付宝小程序，uniapp等</li>
<li><strong>web端(H5)</strong>：H5,CSS3,JavaScript等技术</li>
</ul>
<p>PC页面会设置版心，移动端如何设置呢？</p>
<blockquote>
<p>移动端页面宽度根据<strong>设备宽度</strong>来确定</p>
</blockquote>
<p><strong>视口标签</strong>：</p>
<pre><code>&lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
</code></pre>
<table>
<thead>
<tr>
<th>属性名</th>
<th>作用</th>
<th>取值</th>
<th>默认值</th>
<th>示例</th>
<th>注意事项</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>width</code></td>
<td>设置视口宽度</td>
<td><code>device-width</code> 或具体数值（如 375）</td>
<td>980px（移动端默认）</td>
<td><code>&lt;meta name="viewport" content="width=device-width"&gt;</code></td>
<td>移动端默认的980px称为布局视口，<code>device-width</code> 以匹配设备宽度称为理想视口</td>
</tr>
<tr>
<td><code>initial-scale</code></td>
<td>页面初始缩放比例</td>
<td>数值（如 1.0）</td>
<td>1.0</td>
<td><code>&lt;meta name="viewport" content="initial-scale=1.0"&gt;</code></td>
<td>与 <code>width</code> 和 <code>device-width</code> 配合使用，避免缩放混乱</td>
</tr>
<tr>
<td><code>minimum-scale</code></td>
<td>允许用户缩放的最小比例</td>
<td>数值（如 0.5）</td>
<td>0.1</td>
<td><code>&lt;meta name="viewport" content="minimum-scale=0.5"&gt;</code></td>
<td>若设为 1.0，则用户无法放大页面</td>
</tr>
<tr>
<td><code>maximum-scale</code></td>
<td>允许用户缩放的最大比例</td>
<td>数值（如 2.0）</td>
<td>10.0</td>
<td><code>&lt;meta name="viewport" content="maximum-scale=3.0"&gt;</code></td>
<td>限制缩放范围，但可能影响无障碍访问</td>
</tr>
<tr>
<td><code>user-scalable</code></td>
<td>是否允许用户手动缩放</td>
<td><code>yes</code> 或 <code>no</code></td>
<td><code>yes</code></td>
<td><code>&lt;meta name="viewport" content="user-scalable=no"&gt;</code></td>
<td>禁用缩放可提升体验，但需谨慎使用（无障碍场景需权衡）</td>
</tr>
</tbody>
</table>
<h3>物理像素和CSS像素</h3>
<p><strong>物理像素（也称设备像素）</strong>：是设备屏幕的实际物理像素数量，即硬件成眠的像素点中枢，出厂默认。<br />
<strong>例如</strong>：一台设备的屏幕分辨率为2352*1170，表示横向有2352个物理像素，纵向有1170个物理像素。<br />
<strong>硬件相关</strong>：由屏幕硬件决定，不同设备的物理分辨率可能不同。</p>
<blockquote>
<p>将更多的物理像素点压缩至一块屏幕里，从而达到更多的分辨率，<strong>提高了屏幕显示的细腻程度</strong></p>
</blockquote>
<p><strong>CSS像素</strong>（也称设备独立像素或者逻辑像素）：<br />
是CSS中使用的虚拟像素单位，用于<strong>布局和样式计算</strong><br />
它是一个抽象概念，不直接对应无力像素，而是通过**设备像素比（Device Pixel Ratio, DPR）**与物理像素关联。<br />
比如：PC端，一个CSS像素就是一个物理像素，但是手机端要看DPR</p>
<p><img src="/images/posts/mobile_pages/image.png" alt="alt text" /></p>
<hr />
<p>设计稿：<br />
移动端设计稿通常以<code>750</code>作为标准宽度（尤其是微信小程序，H5页面等）</p>
<ol>
<li>早期移动端设计以<code>iPhone</code>为基准，，750px称为行业默认规范</li>
<li>iPhone 6/7/8 的物理分辨率是750×1334px，设计稿按750px宽度制作时，能直接对应这些设备的1:1视觉还原，方便设计师精准把控细节（如字体大小，间距等）</li>
</ol>
<p>设计稿按750px（2倍大小）制作后：</p>
<ol>
<li>开发时直接缩放到50%（如CSS中写375px），即可适配逻辑分辨率。</li>
<li>图片和图标按`2倍尺寸输出，在高清屏上显示更清晰。（2倍图）</li>
</ol>
<h2>vw/vmin适配方案</h2>
<p>实现移动端适配方案：</p>
<ol>
<li>响应式：<strong>一套代码</strong>通过<strong>响应式</strong>方案完成pc，平板，手机端的适配（vivo官网，华为官网等）</li>
<li>单位适配：pc和平板通过<strong>响应式</strong>完成适配。<strong>单独制作手机端页面</strong>，通过rem，vw单位完成页面适配（淘宝手机端，B站手机端，京东手机端等）</li>
</ol>
<blockquote>
<p>不管哪种方案，页面布局还是使用grid，flex百分比等，元素大小需要进行适配</p>
</blockquote>
<p>vw/vh单位适配：<br />
vw是一种基于<strong>视口宽度</strong>的相对单位，非常适合用于移动端适配<br />
<strong>vw单位定义</strong>：</p>
<ul>
<li>1vw=视窗宽度的1%</li>
<li>如果视窗宽度是375px，则1vw=3.75px</li>
<li>同理100vw=等于视窗完整宽度</li>
<li>vh：视窗高度的1%</li>
<li>注意vw和%不一样，%相对于<strong>父容器</strong></li>
</ul>
<p>注意：</p>
<blockquote>
<p>网页是主要对<strong>屏幕宽度</strong>适配，所以通常用vw即可，vh很少用到</p>
</blockquote>
<p><strong>设计稿转换公式</strong>：<br />
vw值=（元素在设计稿中的px值 / 设计稿宽度）× 100vw<br />
<code>(106px/375px) * 100vw = 28.26vw</code></p>
<p><strong>方式1：使用calc函数</strong></p>
<pre><code>.nav {
  width: calc(106 / 375 * 100vw);
  height: calc(106 / 375 * 100vw);
}
</code></pre>
<p>书写繁琐</p>
<p><strong>方式2：使用CSS预处理器（推荐）</strong></p>
<ul>
<li>less或者sass实现，后续工程化讲</li>
</ul>
<p>但是实际开发里面直接写<code>px</code>不需要计算</p>
<p><strong>方式3：结合PostCSS插件（最佳实践）</strong></p>
<ul>
<li>less/sass代码直接写<code>px</code></li>
<li>使用postcss-px-to-viewport插件，打包发布的时候会统一自动转换为<code>VW</code></li>
<li>但是需要脚手架知识，后续vue阶段讲</li>
</ul>
<p><strong>方式4：使用trae插件转换（当前阶段）</strong><br />
<code>px to rem &amp; rpx &amp; vw(cssrem)</code></p>
<h3>独立圆角属性</h3>
<p>css提供了四个独立的圆角属性，分别控制四个角：<br />
<code>border-top-left-radius</code>左上角圆角<br />
<code>border-top-right-radius</code>右上角圆角<br />
<code>border-bottom-left-radius</code>左下角圆角<br />
<code>border-bottom-right-radius</code>右下角圆角</p>
<p>如果后面跟两个值分别表示该角的<strong>水平半径和垂直半径</strong></p>
<h3>点击高亮</h3>
<pre><code>a {
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
  /* 移除移动端点击高亮 */
}
</code></pre>
<h3>移动端适配</h3>
<p>vmin/vmax单位：<br />
当设备旋转（如手机从竖屏转横屏时），视口的宽高会变化，但<code>vmin</code>始终取当前宽高的<strong>较小值</strong>，<code>vmax</code>取较大值</p>
<table>
<thead>
<tr>
<th>特性</th>
<th>vw (Viewport Width)</th>
<th>vmin (Viewport Minimum)</th>
</tr>
</thead>
<tbody>
<tr>
<td>定义</td>
<td>视口宽度的 1%</td>
<td>视口较小尺寸（宽度或高度）的 1%</td>
</tr>
<tr>
<td>计算依据</td>
<td>仅基于视口宽度（width）</td>
<td>取视口宽度与高度的较小值</td>
</tr>
<tr>
<td>方向敏感性</td>
<td>始终基于宽度，无视口方向</td>
<td>根据视口方向动态调整（宽度或高度取较小值）</td>
</tr>
<tr>
<td>竖屏（如手机）</td>
<td>基于宽度（如 375px 宽度的 50vw = 187.5px）</td>
<td>基于高度（如 812px 高度的 50vmin = 406px）</td>
</tr>
<tr>
<td>代码示例</td>
<td><code>width: 50vw;</code>（视口宽度的 50%）</td>
<td><code>height: 50vmin;</code>（视口较小边的 50%）</td>
</tr>
<tr>
<td>优势</td>
<td>精准控制横向尺寸</td>
<td>自动适应不同方向，适合全屏或动态比例布局</td>
</tr>
<tr>
<td>劣势</td>
<td>无法响应视口高度变化</td>
<td>在宽屏设备上可能不如 vw 直观</td>
</tr>
</tbody>
</table>
<hr />
<p>如果需要照顾手机端横屏状态，可以使用 <code>vmin</code>，比如 B 站全站就采用 <code>vmin</code> 来适配横竖屏场景。</p>
<h2>rem适配方案</h2>
<p>rem单位适配：<br />
通过动态修改<strong>根元素</strong>（html）的<code>font-size</code>属性，使得页面元素基于rem的布局能够自适应不同屏幕尺寸。<br />
比如京东、淘宝、小米移动端使用rem适配</p>
<h3>动态修改html文字大小（基准值）方案：</h3>
<p>方式一：媒体查询方案(京东写法)</p>
<pre><code>html {
  font-size: 20px;
  font-size: 5.33333vw;
}

@media screen and (max-width: 320px) {
  html {
    font-size: 17.0667px;
  }
}

@media screen and (min-width: 540px) {
  html {
    font-size: 28.8px;
  }
}
</code></pre>
<hr />
<p>方式二：js方式引入（推荐）<br />
阿里巴巴手淘团队开发的移动端适配方案<code>Flexible.js</code>通过动态调整根元素<code>html</code>的<code>font-size</code>属性，结合rem单位实现页面自适应（拼多多，淘宝写法）。</p>
<pre><code>&lt;script src="./js/flexible.js"&gt;&lt;/script&gt;
</code></pre>
<pre><code>//set 1rem = view width / 10
function setRemUnit() {
  var rem = docEl.clientWidth / 10
  docEl.style.fontSize = rem + 'px'
}
</code></pre>
<p>方式三：使用PostCSS插件（最佳实践）<br />
<strong>设计稿转换公式：</strong><br />
rem值 = 设计稿元素尺寸 / html的font-size（<strong>基准值</strong>）<br />
<code>(106 / 37.5) = 2.826rem</code></p>
<ul>
<li>less/sass代码直接写<code>px</code></li>
<li>使用postcss-pxtorem插件，打包发布的时候会容易自动转换为<code>rem</code></li>
<li>但是需要脚手架知识，后续Vue阶段讲</li>
</ul>
<p>方式四：转换插件（目前阶段）</p>
<ol>
<li>Flexible.js实现动态修改html根字号大小</li>
<li>利用插件实现px转换为rem单位</li>
</ol>
]]></content>
    <author><name>仙鹤</name></author>
    <category term="前端"/>
  </entry>
  <entry>
    <title>css前沿技术拓展</title>
    <link href="https://xianhe51920.github.io/posts/frontier_expansion/" rel="alternate" type="text/html"/>
    <id>https://xianhe51920.github.io/posts/frontier_expansion/</id>
    <published>2026-06-07T00:00:00.000Z</published>
    <updated>2026-06-07T00:00:00.000Z</updated>
    <summary>包含SVG图片,裁剪，时间线等知识</summary>
    <content type="html"><![CDATA[<h1>前言技术拓展</h1>
<h2>SVG图片</h2>
<p>SVG(Scalable Vector Graphics)是一种基于XML的<strong>矢量图形</strong>标准，由W3C制定，支持无损缩放，交互性和动态效果。其核心点包括：</p>
<ul>
<li><strong>矢量特性</strong>：无论放大或做小均保持清晰，文件体积小，适合网络传输，尤其适用于高分辨率设备</li>
<li><strong>可编辑性</strong>：直接通过文本编辑器修改SVG代码，支持颜色，形状，动画参数的实时调整</li>
<li><strong>交互性</strong>：支持<code>javascript</code>和<code>CSS</code>控制，可实现点击，悬停等动态响应</li>
<li><strong>兼容性</strong>：主流浏览器（Chrome,Firefox,Safari,Edge）均支持SVG格式，移动端适配性强</li>
</ul>
<blockquote>
<p><a href="https://www.iconfont.cn/">SVG图片下载</a></p>
</blockquote>
<p><strong>svg组成</strong></p>
<ul>
<li>svg根元素。svg图标必须包裹在<code>&lt;svg&gt;</code>标签内</li>
<li><code>&lt;path&gt;</code>路径。通过<code>d</code>属性定义路径指令。<code>M</code>:移动画笔到坐标点，<code>Z</code>:闭合路径</li>
</ul>
<p><strong>svg图标常见css属性</strong></p>
<blockquote>
<p>svg是行内块元素，可以设置大小，移动位置，动画等。但是有自己特殊的样式属性</p>
</blockquote>
<table>
<thead>
<tr>
<th>属性</th>
<th>描述</th>
<th>取值示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>fill</code></td>
<td>填充颜色（支持颜色值、渐变、图案），不需要时可改为 <code>none</code></td>
<td><code>fill: #f00;</code></td>
</tr>
<tr>
<td><code>stroke</code></td>
<td>定义描边颜色（支持颜色值、渐变、图案）</td>
<td><code>stroke: #f00;</code></td>
</tr>
<tr>
<td><code>stroke-width</code></td>
<td>描边宽度（支持像素、百分比、px、em 等单位）</td>
<td><code>stroke-width: 2px;</code></td>
</tr>
<tr>
<td><code>stroke-dasharray</code></td>
<td>虚线模式（实线长度 + 间隔长度）</td>
<td><code>stroke-dasharray: 10;</code></td>
</tr>
<tr>
<td><code>stroke-dashoffset</code></td>
<td>调整虚线与间隔的起始位置（负值表示向左偏移）</td>
<td></td>
</tr>
</tbody>
</table>
<p><strong>获得svg图片描边的长度</strong><br />
给svg添加id属性，例如<code>&lt;svg id="svg-icon"&gt;&lt;/svg&gt;</code>，在浏览器<code>console</code>中使用javascript,<code>svg-icon.getTotalLength()</code>即可获得svg图片描边的长度
<strong>clip-path</strong></p>
<blockquote>
<p>clip-path属性用于定义元素的剪裁路径</p>
</blockquote>
<h2>clip-path</h2>
<table>
<thead>
<tr>
<th>取值</th>
<th>描述</th>
<th>取值示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>circle()</code></td>
<td>圆形裁剪</td>
<td><code>circle(50% at 50% 50%)</code></td>
</tr>
<tr>
<td><code>ellipse()</code></td>
<td>椭圆裁剪</td>
<td><code>ellipse(50% 30% at 50% 50%)</code></td>
</tr>
<tr>
<td><code>polygon()</code></td>
<td>多边形裁剪</td>
<td><code>polygon(50% 0%, 100% 100%, 0% 100%)</code></td>
</tr>
<tr>
<td><code>inset()</code></td>
<td>矩形裁剪</td>
<td><code>inset(10% 20% 30% 10%)</code></td>
</tr>
<tr>
<td><code>path()</code></td>
<td>任意路径裁剪</td>
<td><code>path('M0 0 L100 100 Z')</code></td>
</tr>
<tr>
<td><code>url()</code></td>
<td>引用SVG clipPath元素</td>
<td><code>url(#clipId)</code></td>
</tr>
</tbody>
</table>
<p><strong>clip-path使用示例</strong></p>
<pre><code>/* 圆形裁剪 */
.element {
  clip-path: circle(50% at 50% 50%);
}

/* 多边形裁剪（三角形）*/
.element {
  clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
}

/* 动画效果 */
.element {
  clip-path: circle(0% at 50% 50%);
  transition: clip-path 0.5s ease;
}
.element:hover {
  clip-path: circle(50% at 50% 50%);
}
</code></pre>
<h2>scale()</h2>
<blockquote>
<p>scale()属性用于缩放元素的大小，默认缩放比例为1，即不缩放</p>
</blockquote>
<h2>rotate()</h2>
<p><code>turn</code>单位用于旋转元素，1turn等于360度</p>
<h2>filter()</h2>
<blockquote>
<p>filter()属性用于对元素进行图像处理，如模糊，锐化，颜色转换等</p>
</blockquote>
<table>
<thead>
<tr>
<th>滤镜函数</th>
<th>语法示例与说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>blur()</code></td>
<td>高斯模糊：<code>blur(5px)</code></td>
</tr>
<tr>
<td><code>brightness()</code></td>
<td>调整元素亮度：<code>brightness(150%)</code>&lt;br&gt;- <code>100%</code>（或 <code>1</code>）为原始亮度&lt;br&gt;- <code>&lt;100%</code>（或 <code>&lt;1</code>）变暗&lt;br&gt;- <code>&gt;100%</code>（或 <code>&gt;1</code>）变亮</td>
</tr>
<tr>
<td><code>contrast()</code></td>
<td>调整元素对比度：<code>contrast(200%)</code>（取值规则同 <code>brightness()</code>）</td>
</tr>
<tr>
<td><code>saturate()</code></td>
<td>调整背景饱和度：取值 <code>0%</code>（无色彩）~<code>100%</code>（原饱和度）~<code>&gt;100%</code>（更高饱和）</td>
</tr>
<tr>
<td><code>grayscale()</code></td>
<td>将元素转换为灰度图：<code>grayscale(100%)</code>&lt;br&gt;- <code>0%</code>（或 <code>0</code>）为原始色彩&lt;br&gt;- <code>100%</code>（或 <code>1</code>）为完全灰度</td>
</tr>
<tr>
<td><code>hue-rotate()</code></td>
<td>调整元素色相（改变颜色倾向）：<code>hue-rotate(90deg)</code></td>
</tr>
<tr>
<td><code>sepia()</code></td>
<td>将元素转换为深褐色（复古效果）：<code>sepia(70%)</code>&lt;br&gt;- <code>0%</code>（或 <code>0</code>）为原始色彩&lt;br&gt;- <code>100%</code>（或 <code>1</code>）为完全深褐色</td>
</tr>
<tr>
<td><code>drop-shadow()</code></td>
<td>为元素添加投影（类似 <code>box-shadow</code>，但支持非矩形元素）：<code>drop-shadow(5px 5px 5px #669)</code></td>
</tr>
</tbody>
</table>
<h2>backdrop-filter()</h2>
<p>背景滤镜的使用和filter()属性类似，只是作用范围不同。backdrop-filter()属性用于对元素的背景进行滤镜处理，影响元素的透明度和颜色。</p>
<h2>动画时间线</h2>
<blockquote>
<p>animation-timeline属性用于定义动画的时间线</p>
</blockquote>
<p><strong>animation-timeline取值</strong></p>
<table>
<thead>
<tr>
<th>取值</th>
<th>描述</th>
<th>取值示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>scroll()</code></td>
<td>滚动时间线，绑定滚动条位置</td>
<td><code>animation-timeline: scroll();</code></td>
</tr>
<tr>
<td><code>view()</code></td>
<td>视图时间线，绑定元素在视口中的可见比例</td>
<td><code>animation-timeline: view();</code></td>
</tr>
<tr>
<td><code>auto</code></td>
<td>使用animation-name定义的动画</td>
<td><code>animation-timeline: auto;</code></td>
</tr>
</tbody>
</table>
<p><strong>scroll()滚动时间线</strong></p>
<pre><code>/* 默认绑定根元素滚动条 */
.scrollbar {
  animation-timeline: scroll();
}

/* 绑定最近的可滚动祖先容器 */
.element {
  animation-timeline: scroll(nearest);
}

/* 绑定根元素滚动条 */
.element {
  animation-timeline: scroll(root);
}

/* 绑定特定元素的滚动条 */
.container {
  overflow-y: auto;
  scrollbar-gutter: stable;
}
.progress {
  animation-timeline: scroll(self);
}
</code></pre>
<p><strong>scroll()滚动时间线示例</strong></p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
  &lt;style&gt;
    * { margin: 0; padding: 0; }
    body { height: 3000px; }
    .navbar {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 100px;
      background: #fff;
      box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
    }
    .scrollbar {
      width: 0%;
      height: 3px;
      background: linear-gradient(90deg, #96f, #bf70ff, #e67aff, #ff89dc, #ffa176, #ffb90f);
      animation: scrollbar linear forwards;
      animation-timeline: scroll();
    }
    @keyframes scrollbar {
      0% { width: 0%; }
      100% { width: 100%; }
    }
  &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;div class="navbar"&gt;
    &lt;div class="scrollbar"&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="content"&gt;
    &lt;p&gt;里面有很多的文字&lt;/p&gt;
    &lt;!-- 更多内容产生滚动 --&gt;
  &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p><strong>view()视图时间线</strong></p>
<pre><code>/* 绑定元素在视口中的可见比例 */
.element {
  animation: fade-in linear;
  animation-timeline: view();
}

@keyframes fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* 带有滚动轴的视图时间线 */
.element {
  animation: slide-in linear;
  animation-timeline: view(scroll());
}

@keyframes slide-in {
  from { transform: translateY(100%); }
  to { transform: translateY(0); }
}

/* 自定义视图时间线范围 */
.element {
  animation: scale linear;
  animation-timeline: view(inline);
  animation-range: entry 0% cover 50%;
}
</code></pre>
<p><strong>view()视图时间线示例</strong></p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
  &lt;style&gt;
    .container {
      height: 200vh;
      padding: 50px;
    }
    .card {
      height: 300px;
      background: #f0f0f0;
      margin-bottom: 100px;
      animation: slide-in linear both;
      animation-timeline: view();
    }
    @keyframes slide-in {
      from {
        transform: translateY(100%);
        opacity: 0;
      }
      to {
        transform: translateY(0);
        opacity: 1;
      }
    }
  &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;div class="container"&gt;
    &lt;div class="card"&gt;卡片1&lt;/div&gt;
    &lt;div class="card"&gt;卡片2&lt;/div&gt;
    &lt;div class="card"&gt;卡片3&lt;/div&gt;
  &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p><strong>animation-range视图时间线范围</strong></p>
<table>
<thead>
<tr>
<th>取值</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>cover 0%</code></td>
<td>元素完全进入视口前</td>
</tr>
<tr>
<td><code>cover 100%</code></td>
<td>元素完全离开视口后</td>
</tr>
<tr>
<td><code>entry 0%</code></td>
<td>元素开始进入视口时</td>
</tr>
<tr>
<td><code>entry 100%</code></td>
<td>元素刚好完全进入视口时</td>
</tr>
<tr>
<td><code>exit 0%</code></td>
<td>元素开始离开视口时</td>
</tr>
<tr>
<td><code>exit 100%</code></td>
<td>元素刚好完全离开视口时</td>
</tr>
</tbody>
</table>
<p><strong>animation-range使用示例</strong></p>
<pre><code>.element {
  animation: scale linear both;
  animation-timeline: view();
  animation-range: entry 0% cover 40%;
}

@keyframes scale {
  from { transform: scale(0.5); }
  to { transform: scale(1); }
}
</code></pre>
<p><strong>animation-duration和animation-timeline的关系</strong></p>
<pre><code>/* 当使用scroll()或view()时，animation-duration表示滚动距离对应的时间 */
.progress {
  animation-name: progress;
  animation-duration: 1s;
  animation-timeline: scroll();
  animation-fill-mode: both;
}

@keyframes progress {
  from { width: 0; }
  to { width: 100%; }
}

/* 如果想要动画速度与滚动同步，设置duration为1s即可 */
</code></pre>
<p><strong>兼容性说明</strong></p>
<blockquote>
<p>动画时间线API是较新的CSS特性，目前主要在Chrome、Edge等Chromium内核浏览器中得到支持，Firefox和Safari支持有限。</p>
</blockquote>
<h2>变量和函数</h2>
<p>CSS增强了逻辑性和动态计算能力，比如CSS变量和一些动态函数(calc(),clamp()等)</p>
<p><strong>使用场景</strong>：</p>
<ul>
<li>主体切换</li>
<li>响应式设计</li>
<li>交互动画等</li>
</ul>
<h3>变量</h3>
<p>CSS<strong>变量</strong>（也可称为自定义属性）是一种<strong>动态存储</strong>和<strong>复用值</strong>的机制，它让样式管理更灵活，可维护。变量可以理解为一个<strong>容器</strong>，里面可以存放数据</p>
<p><strong>定义变量</strong>：通过<code>--变量名</code>定义变量</p>
<pre><code>  --color: #007bff;
</code></pre>
<p><strong>使用变量</strong>：通过<code>var(--变量名)</code>使用变量</p>
<pre><code>  color: var(--color);
</code></pre>
<p><strong>作用域</strong>：变量的作用范围</p>
<pre><code>:root { /* 全局变量 */
  --color: #007bff;
}
.box { /* 局部变量 */
  --color: #0056b3;
}
</code></pre>
<ul>
<li>全局作用域：通过<code>:root</code>元素定义，作用于整个网页</li>
<li>局部作用域：在特定选择器中定义，仅影响<strong>该元素及其子元素</strong>的样式</li>
</ul>
<h2>CSS3计算能力</h2>
<p><code>calc()</code>函数可以执行数学运算（加减乘除），支持混合单位
<strong>示例</strong></p>
<pre><code>  width: calc(80% - 10px); /* 父元素的宽度的80%减去10px */
</code></pre>
<ul>
<li>加减乘除运算写法：<code>+</code>, <code>-</code>, <code>*</code>, <code>/</code>，注意符号左右两侧保留空格</li>
</ul>
<h2>vw/vh</h2>
<p>vw和vh是CSS中的<code>视口单位</code>（viewport units），用于基于浏览器视口（viewport）的储存来定义元素的尺寸或位置</p>
<p>vw和vh始终基于<code>浏览器可视区域</code>的实施尺寸计算（不包含浏览器工具栏、地址栏、标签栏等界面元素），他们的值会自动更新。特别适合<code>响应式设计</code>,<code>全屏布局</code>，<code>移动适配</code>等场景。</p>
<p>vw（viewport width）：表示视口宽度的<code>1%</code><br />
vh（viewport height）：表示视口高度的<code>1%</code></p>
]]></content>
    <author><name>仙鹤</name></author>
    <category term="前端"/>
  </entry>
  <entry>
    <title>交互动效设计</title>
    <link href="https://xianhe51920.github.io/posts/interaction/" rel="alternate" type="text/html"/>
    <id>https://xianhe51920.github.io/posts/interaction/</id>
    <published>2026-06-06T00:00:00.000Z</published>
    <updated>2026-06-06T00:00:00.000Z</updated>
    <summary>CSS交互动效知识</summary>
    <content type="html"><![CDATA[<h1>交互动效设计</h1>
<h1>一.变换</h1>
<p>css<code>transform</code>是元素进行<code>2D/3D</code>变换的核心属性，支持平移、旋转、倾斜、缩放、倾斜等效果，且<code>不破坏</code>原有文档流布局</p>
<p><strong>场景</strong>：
应用非常广泛，通过简介的语法实现复杂的视觉动态效果</p>
<p><strong>坐标系</strong>：<br />
变换的时候很多情况需要按照坐标轴来变化，介绍2D坐标系和3D坐标系</p>
<p>2D坐标系
<img src="/images/posts/interaction/image.png" alt="alt text" /></p>
<blockquote>
<p>盒子往<strong>右</strong>是<strong>正值</strong>，往<strong>左</strong>是<strong>负值</strong>
盒子往<strong>下</strong>是<strong>正值</strong>，往<strong>上</strong>是<strong>负值</strong></p>
</blockquote>
<p>3D坐标系
<img src="/images/posts/interaction/image-1.png" alt="alt text" /></p>
<h2>基础变换函数-2D</h2>
<table>
<thead>
<tr>
<th>变换类型</th>
<th>函数语法</th>
<th>核心描述</th>
<th>应用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td>平移</td>
<td><code>translate(x, y)</code>&lt;br&gt;<code>translateX(x)</code>&lt;br&gt;<code>translateY(y)</code></td>
<td>沿 X/Y 轴移动元素，参数可为像素或百分比（百分比相对于元素自身尺寸）</td>
<td>微调元素位置、悬停偏移效果</td>
</tr>
<tr>
<td>旋转</td>
<td><code>rotate(angle)</code></td>
<td>以元素中心为基点顺时针旋转，需加单位 <code>deg</code>（负值为逆时针）</td>
<td>图标旋转、卡片翻转</td>
</tr>
<tr>
<td>缩放</td>
<td><code>scale(sx, sy)</code>&lt;br&gt;<code>scaleX(sx)</code>&lt;br&gt;<code>scaleY(sy)</code></td>
<td>X/Y 轴按比例缩放，单参数时等比例缩放（&gt;1 放大，&lt;1 缩小，支持百分比）</td>
<td>悬停放大、焦点突出</td>
</tr>
<tr>
<td>倾斜</td>
<td><code>skew(x-angle, y-angle)</code>&lt;br&gt;<code>skewX(angle)</code>&lt;br&gt;<code>skewY(angle)</code></td>
<td>沿 X/Y 轴扭曲元素形状，参数为倾斜角度（支持负值）</td>
<td>斜切导航栏、动态图表</td>
</tr>
</tbody>
</table>
<h3>1.变换函数-2D平移</h3>
<p>平移（<code>translate</code>）沿X/Y轴移动元素位置，<strong>不改变</strong>元素的实际<strong>布局</strong>（原位置仍保留）</p>
<p><strong>场景</strong>：</p>
<ol>
<li><strong>悬停元素微调</strong>。鼠标放入元素上下或者左右移动（添加过渡更优雅）</li>
<li><strong>元素居中</strong>。元素实现水平垂直居中</li>
</ol>
<ul>
<li>平移不影响页面布局，仅是视觉上的移动</li>
<li>添加鼠标经过元素移动，优先<code>transform</code>而不是通过<code>left</code>，<code>top</code>等，性能更佳</li>
<li>如果单位是百分比，相对于<strong>元素自身尺寸</strong>，而非父容器</li>
</ul>
<p><strong>示例</strong>：</p>
<pre><code>.box {
  transition: transform 0.3s;
}

.box:hover {
  transform: translate(10px, 10px);
}
</code></pre>
<h3>2.变换函数-2D旋转</h3>
<p>旋转（<code>rotate</code>）通过改变元素在平面或空间中的<strong>角度</strong>实现视觉效果</p>
<p><strong>场景</strong>：</p>
<ol>
<li><strong>悬停动画（如按钮旋转）</strong></li>
<li><strong>加载动画（无线循环旋转）</strong></li>
</ol>
<p><strong>语法</strong>：</p>
<pre><code>.box {
  transform: rotate(360deg); /* 旋转 */
  transform-origin: center center; /* 旋转的基点，默认中心 */
}
</code></pre>
<ul>
<li>参数单位：deg(度)，正值是顺时针，负值是逆时针</li>
<li><code>transform-origin</code>设置<strong>旋转中心点</strong></li>
<li>属性值支持left，top等，也可以支持数字比如像素和百分比等</li>
</ul>
<p>&lt;font color="red"&gt;
注意：<br />
&lt;/font&gt;
<strong>行内元素</strong>的布局特性（无法设置宽高，盒模型限制，transform基准异常）会破环旋转效果的稳定性，所以文字类要转换行内块或者块级元素，比如字体图标需要转换</p>
<h3>3.变换函数-2D缩放</h3>
<p>缩放（<code>scale</code>）用于调整元素尺寸，且<strong>不改变</strong>元素在文档流中的原始占位</p>
<p><strong>场景</strong>：</p>
<ol>
<li><strong>悬停放大</strong>。鼠标悬停在元素上时，元素放大</li>
</ol>
<p><strong>语法</strong>：
<code>transform: scale(1.5);</code> 整体放大1.5倍
<code>transform: scale(1.5, 1);</code> 水平方向放大，垂直方向不放大</p>
<ul>
<li>单参数：同时作用于x轴和y轴，等比例缩放</li>
<li>双参数：分别作用于x轴和y轴，不同比例缩放</li>
</ul>
<h3>4.变换函数-2D倾斜</h3>
<p>倾斜（<code>skew</code>）用于对元素进行二维*<strong>倾斜</strong>变换，通过沿x轴或y轴扭曲元素的几何形状</p>
<p><strong>场景</strong>：</p>
<ol>
<li>鼠标经过元素倾斜效果</li>
</ol>
<p><strong>语法</strong>：
<code>transform: skew(30deg, 30deg);</code> 水平方向倾斜30度，垂直方向倾斜30度
<code>transform: skewX(30deg);</code> 水平方向倾斜30度，垂直方向不倾斜
<code>transform: skewY(30deg);</code> 垂直方向倾斜30度，水平方向不倾斜</p>
<ul>
<li>单参数：y轴默认倾斜角度为0</li>
<li>双参数：分别作用于x轴和y轴，不同角度倾斜</li>
<li><code>transform-origin</code>设置<strong>倾斜中心点</strong>，默认中心</li>
</ul>
<h3>5.过渡transition完整写法</h3>
<p><strong><code>transition</code>完整写法</strong>：
<code>transition: 过渡属性 持续时间 速度曲线 延迟时间;</code></p>
<ul>
<li><code>过渡属性</code>：过渡属性属性值变化时触发过渡效果</li>
<li><code>持续时间</code>：属性值变化时的过渡时间，单位秒</li>
<li><code>速度曲线</code>：属性值变化时的过渡曲线</li>
<li><code>延迟时间</code>：属性值变化时的过渡时间延迟，单位秒</li>
</ul>
<p><strong>过渡属性</strong>：
过渡属性的值可以是：</p>
<ul>
<li><code>none</code>：表示没有过渡效果</li>
<li><code>all</code>：表示所有可过渡的属性都参与过渡（默认值）</li>
<li>具体属性名：如 <code>transform</code>、<code>opacity</code>、<code>background-color</code>、<code>width</code>、<code>height</code>、<code>margin</code>、<code>padding</code>、<code>border</code>、<code>box-shadow</code> 等常用CSS属性</li>
</ul>
<blockquote>
<p><strong>注意</strong>：并非所有CSS属性都可以过渡，只有具有中间值的属性才能实现平滑过渡效果。颜色、尺寸、位置、透明度等连续性属性最适合过渡。</p>
</blockquote>
<p><strong>速度曲线</strong>：</p>
<table>
<thead>
<tr>
<th>速度曲线参数</th>
<th>效果描述</th>
<th>曲线可视化</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>ease</strong></td>
<td>默认值，慢速开始 → 加速 → 慢速结束，适合大多数自然过渡（如按钮悬停）</td>
<td><img src="/images/posts/interaction/image-2.png" alt="alt text" /></td>
</tr>
<tr>
<td><strong>linear</strong></td>
<td>匀速运动，无加速/减速，适合机械感效果（如进度条）</td>
<td><img src="/images/posts/interaction/image-3.png" alt="alt text" /></td>
</tr>
<tr>
<td><strong>ease-in</strong></td>
<td>慢速开始 → 逐渐加速，适合元素从静止启动（如弹窗出现）</td>
<td><img src="/images/posts/interaction/image-4.png" alt="alt text" /></td>
</tr>
<tr>
<td><strong>ease-out</strong></td>
<td>快速开始 → 逐渐减速，适合元素退出（如关闭动画）</td>
<td><img src="/images/posts/interaction/image-5.png" alt="alt text" /></td>
</tr>
<tr>
<td><strong>ease-in-out</strong></td>
<td>慢速开始和结束，中间加速，适合对称性动画（如页面切换）</td>
<td><img src="/images/posts/interaction/image-6.png" alt="alt text" /></td>
</tr>
<tr>
<td><strong>cubic-bezier(x1,y1,x2,y2)</strong></td>
<td>贝塞尔曲线。通过四值（0-1范围内）自定义速度曲线，实现弹跳、骤停等创意效果（如按钮点击反馈）</td>
<td><img src="/images/posts/interaction/image-7.png" alt="alt text" /></td>
</tr>
</tbody>
</table>
<hr />
<p><strong>工具</strong>：</p>
<ol>
<li><a href="https://cubic-bezier.com"><code>可视化编辑贝塞尔曲线</code></a></li>
<li><a href="https://easing.net"><code>获取常用缓动函数的预设值</code></a></li>
</ol>
<h3>6.变换函数-复合写法</h3>
<p>在css中，transform属性的复合写法是将每个函数用空格隔开（多个变换函数组合使用）</p>
<p><strong>语法</strong>；
<code>transform: A() B() C();</code></p>
<p><strong>顺序</strong>：</p>
<ul>
<li><strong>核心规则</strong>：<code>从右到左</code>的执行顺序</li>
<li>实际的执行顺序是：先执行最右侧的<code>C()</code>，再执行<code>B()</code>，最后执行<code>A()</code></li>
</ul>
<p><strong>示例</strong>：轮胎移动效果</p>
<pre><code>.img {
  transition: transform 0.3s;
}

.box:hover img {
  transform: translate(10px, 10px) rotate(360deg);
}
</code></pre>
<p>&lt;font color="red"&gt;
注意：
&lt;/font&gt;
如果translate和rotate写反了，会导致元素先移动再旋转，效果不符合预期</p>
<h2>3D变换与透视</h2>
<p>css3D效果通过将二维元素在三位空间中进行变换，为网页添加<strong>立体感</strong>和动态交互体验</p>
<p><strong>场景</strong>：</p>
<ol>
<li>卡片翻转效果</li>
<li>3D幻灯片/轮播图</li>
<li>数据可视化。立体图表（如3D柱状图等）</li>
</ol>
<blockquote>
<p>相比JavaScript或WebGL，css3D利用GPU加速，动画更流畅，性能更高效</p>
</blockquote>
<p><strong>3D坐标系</strong>：<br />
网页中的三维坐标系：<br />
<img src="/images/posts/interaction/image-8.png" alt="alt text" />
<strong>坐标数值</strong>：</p>
<ul>
<li><code>x轴</code>代表左右方向。右边是正值，左边是负值</li>
<li><code>y轴</code>代表上下方向。上边是正值，下边是负值</li>
<li><code>z轴</code>代表前后方向。<code>接近屏幕是正值，远离屏幕是负值</code>。</li>
</ul>
<blockquote>
<p>默认情况下，元素在二维平面上呈现，Z轴值为0<br />
当应用3D变换时，元素便可以在三维空间中自由变换</p>
</blockquote>
<h2>3D旋转</h2>
<p>旋转（<code>rotate</code>）通过改变元素在3D空间中的<strong>角度</strong>实现视觉效果</p>
<p><strong>语法</strong>：
<code>transform: rotateX(30deg);</code> 围绕<code>x轴</code>旋转30度
<code>transform: rotateY(30deg);</code> 围绕<code>y轴</code>旋转30度
<code>transform: rotateZ(30deg);</code> 围绕<code>z轴</code>旋转30度</p>
<ul>
<li>参数单位：<code>deg</code>（度），正值是<strong>顺时针旋转</strong>，负值是<strong>逆时针旋转</strong></li>
<li>rotateZ和二维里面的rotate()一样的，只是在3D空间中旋转</li>
</ul>
<h2>透视（Perspective）</h2>
<p>”透视“（<code>Perspective</code>）一词源于拉丁文”perspclre“（看透），指在<strong>平面上</strong>描绘物体的口康健关系的方法或技术。</p>
<p>在css中，透视效果（<code>perspective</code>）用于模拟人眼观察3D空间时的进大远小效果</p>
<p><img src="/images/posts/interaction/image-9.png" alt="alt text" /></p>
<p><strong>语法</strong>：<br />
<code>perspective: 1000px;</code> 透视距离为1000px</p>
<ul>
<li>数值越小，透视效果越强</li>
<li>给父元素添加，里面<strong>所有子元素</strong>都会添加透视效果（常用）</li>
<li>给子元素添加，<strong>当前元素</strong>添加透视效果</li>
</ul>
<p><strong>同时使用多个属性</strong>：
<code>transform: perspective(1000px) rotateX(30deg);</code></p>
<ul>
<li>注意：<code>perspective()</code>必须作为<code>transform</code>属性的<strong>第一个元素</strong>（否则无效）</li>
</ul>
<p><strong>3D旋转方向</strong>：左手大拇指指向坐标轴正方向，四指指向的方向就是正值方向</p>
<h3>3D旋转-两面翻转的盒子示例</h3>
<p><strong>backface-visibility</strong>：当元素旋转时，是否显示背面面</p>
<ul>
<li><code>visible</code>：显示背面</li>
<li><code>hidden</code>：隐藏背面</li>
</ul>
<p><strong>示例</strong>：创建一个正面粉色反面蓝色，悬停时可翻转的盒子</p>
<pre><code>.box {
  position: relative;
  margin: 100px auto;
  width: 600px;
  height: 300px;
}

.box .front,
.box .back {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  /* 元素翻转隐藏背面 */
  backface-visibility: hidden;
  transition: transform 0.6s;
}

.box .front {
  background-color: lightblue;
  transform: rotateY(0deg);
}

.box .back {
  background-color: pink;
  transform: rotateY(-180deg);
}

.box:hover .front {
  transform: rotateY(180deg);
}
.box:hover .back {
  transform: rotateY(0deg);
}
</code></pre>
<h2>3D位移</h2>
<p>CSS 3D函数<code>translatrZ()</code>与<code>translateX3d()</code></p>
<p><strong>语法</strong>：<br />
<code>transform: translateX3d(xpx, ypx, zpx);</code> 沿着<code>x轴</code>移动xpx，<code>y轴</code>移动ypx，<code>z轴</code>移动zpx</p>
<ul>
<li>在三维空间中同时沿<code>x轴</code>、<code>y轴</code>、<code>z轴</code>平移元素，支持更复杂的空间定位</li>
<li>位移建议通过3D变换强制启用GPU渲染，提升动画流畅度</li>
</ul>
<hr />
<p><strong>语法</strong>：<br />
<code>transform: translateZ(zpx);</code></p>
<ul>
<li>沿z轴平移元素，实现进大远小的立体效果</li>
<li>正值元素放大，负值元素缩小</li>
<li>需父容器设置透视距离（<code>perspective</code>），否则无视觉变化</li>
</ul>
<p><strong>开启3D空间</strong>：<br />
在变换的时候（如卡片翻转）要使子元素保留3D位置，父元素需要设置<code>transform-style: preserve-3d;</code></p>
<ul>
<li><code>transform-style</code>默认值为<code>flat</code>，即2D空间，不保留3D位置</li>
<li>开启3D空间后，子元素会保留3D位置，实现3D效果</li>
</ul>
<hr />
<h1>二.动画</h1>
<h2>动画的基本使用</h2>
<p>CSS3的<code>animation</code>是一种通过定义关键帧和动画属性来实现元素<strong>动态</strong>效果的技术</p>
<p><strong>优势</strong>：</p>
<ul>
<li>性能高效：通过浏览器原生支持，利用GPU加速渲染，避免频繁的javascript计算开销</li>
<li>代码简洁：解决了传统CSS过渡（<code>transition</code>）只能定义两帧状态的局限性，可以利用关键帧实现更复杂动画</li>
<li>交互增强：支持与用户操作（如悬停、点击）结合，提升网页的动态变现里和用户体验</li>
</ul>
<h3>关键帧</h3>
<p><strong>关键帧</strong>（keyframe）是动画和视频制作中用于定义动作或状态变化的核心<strong>节点</strong>，它决定了动画的其实，转折和结束<strong>状态</strong>，中间帧（插帧）通过算法自动生成，形成连贯的动画效果</p>
<h3>动画的使用</h3>
<p>动画的使用要先定义动画，再使用动画</p>
<p><strong>1.定义动画</strong>：</p>
<pre><code>@keyframes move {
  0% {
    transform: translateX(0);
  }
  50% {
    transform: translateX(50px);
  }
  100% {
    transform: translateX(100px);
  }
}
</code></pre>
<ul>
<li><code>@keyframes</code>：定义动画的关键帧</li>
<li><code>move</code>：动画名称，自定义</li>
<li><code>0% 50% 100%</code>：关键帧的时间点，0%表示动画开始，50%表示动画中间，100%表示动画结束</li>
<li>关键帧可以定义多个，每个关键帧可以设置不同的属性值</li>
</ul>
<pre><code>@keyframes move {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(100px);
  }
}
</code></pre>
<ul>
<li><code>from</code>：动画开始时的状态，相当于<code>0%</code></li>
<li><code>to</code>：动画结束时的状态，相当于<code>100%</code></li>
</ul>
<p><strong>2.使用动画</strong>：动画属性要写到<strong>目标元素里面</strong></p>
<pre><code>.box {
  animation: move 1s;
}
</code></pre>
<ul>
<li><code>animation</code>：应用动画</li>
<li><code>move</code>：动画名称，与定义动画时的名称一致</li>
<li><code>1s</code>：动画持续时间，单位为秒</li>
</ul>
<h3>animation属性完整写法</h3>
<p><code>animation: 动画名称 动画持续时间 速度曲线 延迟时间 播放次数 播放方向 执行完毕状态;</code></p>
<ul>
<li>动画名称和动画时长是<strong>必填项</strong>，其余可以省略，但是要保证书写顺序</li>
<li>动画属性要写到<strong>目标元素</strong>里面</li>
</ul>
<p>|子属性|默认值|说明|
|速度曲线|ease|动画速度曲线，可选值有<code>linear</code>（匀速）和<code>cubic-bezier()</code>（自定义贝塞尔曲线）等|
|延迟时间|0s|动画延迟时间，单位为秒|
|播放次数|1|动画播放次数，无限循环播放为<code>infinite</code>|
|播放方向|normal|动画播放方向，可选值还有<code>reverse</code>（反向播放）和<code>alternate</code>（交替播放）|
|执行完毕状态|none|动画结束后状态（如<code>forwards</code>保留最后一帧，<code>backwards</code>回到第一帧）|</p>
<h3>step逐帧动画</h3>
<p>速度曲线除了常见的linear、ease、cubic-bezier等，还包含<code>steps()</code>函数，steps()是CSS动画中用于控制动画<strong>分段执行</strong>的计时函数，它通过将动画<strong>分割</strong>为离散的<strong>步骤</strong>，实现类似传统帧动画（逐帧动画）的跳跃效果</p>
<p><strong>语法</strong>：</p>
<pre><code>animation: move 1s steps(8) infinite;
</code></pre>
<ul>
<li>steps(步数)，步数为<strong>正整数</strong></li>
<li>经常和背景图片（<strong>精灵图</strong>）来实现动画效果</li>
</ul>
<p><strong>逐帧动画原理</strong>：</p>
<ol>
<li>移动图片。其实是通过变化背景位置，展示不同的图片帧，实现动画效果</li>
<li>图片有N个不同图形，steps()步数就写N</li>
</ol>
<p><strong>示例</strong>：</p>
<ul>
<li>盒子大小：宽度548px，高度513px</li>
<li>背景图片大小：宽度13700px，高度513px</li>
<li>背景图片一共有25张小图片组成</li>
</ul>
<pre><code>@keyframes move {
  0% {
    background-position: 0 0;
  }
  100% {
    background-position: -13700px 0;
  }
}

.box {
  width: 548px;
  height: 513px;
  background: url(./images/box.png) no-repeat left/13700px 513px;
  /* 使用动画 */
  animation: move 1s steps(25) infinite;
}
</code></pre>
<h4>动画单写属性</h4>
<table>
<thead>
<tr>
<th>动画子属性</th>
<th>默认值</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>animation-timing-function</code></td>
<td><code>ease</code></td>
<td>动画速度曲线，支持 <code>linear</code>、<code>cubic-bezier()</code> 等</td>
</tr>
<tr>
<td><code>animation-delay</code></td>
<td><code>0s</code></td>
<td>动画延迟时间，可设为负值（跳过部分动画）</td>
</tr>
<tr>
<td><code>animation-iteration-count</code></td>
<td><code>1</code></td>
<td>播放次数，若需无限循环需显式声明 <code>infinite</code></td>
</tr>
<tr>
<td><code>animation-direction</code></td>
<td><code>normal</code></td>
<td>播放方向，可选 <code>reverse</code>（反向）、<code>alternate</code>（交替）</td>
</tr>
<tr>
<td><code>animation-fill-mode</code></td>
<td><code>none</code></td>
<td>执行完毕状态，动画结束后是否保留样式（如 <code>forwards</code> 保留最后一帧）</td>
</tr>
<tr>
<td><code>animation-play-state</code></td>
<td><code>running</code></td>
<td>暂停或者继续动画，需单独设置 <code>paused</code> 暂停动画</td>
</tr>
</tbody>
</table>
<h2>三.其他效果</h2>
<h3>box-reflect: 倒影效果</h3>
<p><code>box-reflect</code>是css中用于为元素创建倒影效果
<strong>语法</strong>；
<code>box-reflect: 倒影方向 倒影距离 倒影图像</code></p>
<ol>
<li>倒影方向（必写）：</li>
</ol>
<ul>
<li>above:倒影在元素上方</li>
<li>below:倒影在元素下方</li>
<li>left:倒影在元素左侧</li>
<li>right:倒影在元素右侧</li>
</ul>
<ol>
<li>倒影距离（必写）：</li>
</ol>
<ul>
<li>倒影距离，单位为像素</li>
</ul>
<ol>
<li>倒影图像（可选）：</li>
</ol>
<ul>
<li>倒影图像，可选值还有<code>none</code>（无倒影）</li>
<li><code>url(./images/reflect.png)</code>：倒影图像，可选值还有<code>linear-gradient()</code>（线性渐变）、<code>radial-gradient()</code>（径向渐变）等</li>
</ul>
<blockquote>
<p>注意，因为兼容性问题，前面需要添加<code>-webkit-</code>前缀，例如<code>-webkit-box-reflect: below 10px #f5f5f5;</code></p>
</blockquote>
<h3>:has() 父亲选择器</h3>
<p><code>:has()</code>选择器是css中用于选择包含指定子元素的元素
<strong>语法</strong>；
<code>:has(子元素)</code></p>
<ol>
<li>子元素（必写）：
<ul>
<li>子元素，可选值还有<code>::first-child</code>（第一个子元素）、<code>::last-child</code>（最后一个子元素）、<code>::nth-child(第n个子元素)</code>（第n个子元素）等</li>
<li>例如：<code>:has(子元素)</code>：选择包含子元素的元素</li>
<li>例如：<code>:has(子元素):nth-child(2)</code>：选择包含子元素的第2个子元素</li>
</ul>
</li>
</ol>
<h3>:not() 反选器</h3>
<p><code>:not()</code>选择器是css中用于选择不包含指定子元素的元素
<strong>语法</strong>；
<code>:not(子元素)</code></p>
<ol>
<li>子元素（必写）：
<ul>
<li>子元素，可选值还有<code>::first-child</code>（第一个子元素）、<code>::last-child</code>（最后一个子元素）、<code>::nth-child(第n个子元素)</code>（第n个子元素）等</li>
<li>例如：<code>:not(子元素)</code>：选择不包含子元素的元素</li>
<li>例如：<code>:not(子元素):nth-child(2)</code>：选择不包含子元素的第2个子元素</li>
</ul>
</li>
</ol>
<h3>opacity 透明度</h3>
<p><code>opacity</code>是css中用于设置元素透明度的属性
<strong>语法</strong>；
<code>opacity: 透明度值</code></p>
<ol>
<li>透明度值（必写）：
<ul>
<li>透明度值，取值范围为0到1之间</li>
<li>0：完全透明</li>
<li>1：完全不透明</li>
<li>0.5：半透明</li>
<li>例如：<code>opacity: 0.5;</code>：设置元素透明度为0.5</li>
</ul>
</li>
</ol>
]]></content>
    <author><name>仙鹤</name></author>
    <category term="前端"/>
  </entry>
  <entry>
    <title>现代网页布局</title>
    <link href="https://xianhe51920.github.io/posts/page_layouts/" rel="alternate" type="text/html"/>
    <id>https://xianhe51920.github.io/posts/page_layouts/</id>
    <published>2026-06-03T00:00:00.000Z</published>
    <updated>2026-06-03T00:00:00.000Z</updated>
    <summary>正常布局，模式转换布局，弹性布局，定位布局，网格布局，多列布局</summary>
    <content type="html"><![CDATA[<h1>现代网页布局</h1>
<p>CSS 布局是网页设计的核心技术之一，用于控制元素在页面中的排列方式</p>
<h4>常见布局技术</h4>
<table>
<thead>
<tr>
<th>布局技术</th>
<th>英文名称</th>
<th>核心特点</th>
</tr>
</thead>
<tbody>
<tr>
<td>正常布局</td>
<td><code>normal</code></td>
<td>遵循浏览器默认的文档流规则</td>
</tr>
<tr>
<td>模式转换布局</td>
<td><code>display</code></td>
<td>通过 <code>display</code> 属性改变元素显示模式</td>
</tr>
<tr>
<td>弹性布局</td>
<td><code>flexbox</code></td>
<td>一维布局，适合灵活排列子元素</td>
</tr>
<tr>
<td>定位布局</td>
<td><code>position</code></td>
<td>通过定位属性精确控制元素位置</td>
</tr>
<tr>
<td>网格布局</td>
<td><code>grid</code></td>
<td>二维布局，适合复杂的行列结构</td>
</tr>
<tr>
<td>多列布局</td>
<td><code>column</code></td>
<td>实现文本或元素的多列排版</td>
</tr>
</tbody>
</table>
<hr />
<p>每种技术都有它们的用途，各有优缺点，相互辅助</p>
<ul>
<li>简单布局：优先使用 <strong>Flexbox（一维）</strong> 或 <strong>Grid（二维）</strong>。</li>
<li>复杂响应式布局：使用 <strong>Grid + 媒体查询</strong>。</li>
<li>文本内容分栏：使用多列布局（<code>column-count</code>）。</li>
<li>兼容旧浏览器：使用 浮动布局 或 Flexbox 的降级方案。</li>
<li>趋势：CSS Grid 逐渐成为主流，支持更复杂的布局场景。</li>
</ul>
<h1>一.CSS 正常布局流</h1>
<p>正常布局流是指在不对页面进行任何布局控制时，浏览器<strong>默认</strong>的 HTML 布局方式，也称为<strong>标准流</strong>。它是 CSS 布局的基石，页面大的布局基本就是利用区块元素<strong>上下罗列</strong>而成。</p>
<hr />
<h3>区块元素（block）</h3>
<ul>
<li>独占一行，宽度默认撑满父容器。</li>
<li>垂直方向排列，可设置宽高。</li>
<li>示例：<code>div</code>、<code>p</code>、<code>h1</code> 等。</li>
</ul>
<h3>行内元素（inline）</h3>
<ul>
<li>水平方向依次排列，直到容器宽度不足则换行。</li>
<li>宽度和高度由内容决定，无法直接设置。</li>
<li>示例：<code>span</code>、<code>img</code>、<code>strong</code> 等。</li>
</ul>
<h3>文档流方向</h3>
<p>默认从上到下、从左到右排列。</p>
<h1>二.display转换</h1>
<p>正常流中的所有内容都有一个<code>display</code>的属性，用作指定元素的显示模式。<br />
但是开发的时候，我们需要一个<strong>块级盒子一行排列</strong>，或者<strong>行内元素需要设置大小</strong>，怎么办？<br />
<code>display</code>属性允许我们更改元素默认的显示模式</p>
<ul>
<li><code>display: block;</code>：转换为区块元素，独占一行，可设置宽高，默认宽度撑满父容器</li>
<li><code>display: inline;</code>：转换为行内元素，不独占一行，不可设置宽高，默认宽度由内容决定</li>
<li><code>display: inline-block;</code>：转换为行内块元素，不独占一行，可设置宽高，默认宽度由内容决定</li>
</ul>
<table>
<thead>
<tr>
<th>属性值</th>
<th>是否独占一行</th>
<th>能否设置宽高</th>
<th>默认宽度</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>block</code></td>
<td>是</td>
<td>能</td>
<td>撑满父容器</td>
</tr>
<tr>
<td><code>inline</code></td>
<td>否</td>
<td>不能</td>
<td>内容决定</td>
</tr>
<tr>
<td><code>inline-block</code></td>
<td>否</td>
<td>能</td>
<td>内容决定</td>
</tr>
</tbody>
</table>
<hr />
<p>表单元素默认inline-block,其他元素想要转换可以: <code>display: inline-block;</code></p>
<h3>使用场景</h3>
<ol>
<li>让块级盒子一行显示</li>
<li>让行内盒子具备宽度和高度</li>
</ol>
<h3>注意</h3>
<ol>
<li>行内块元素中间会有空白缝隙，给父元素字号改为<code>0</code>可以去掉</li>
<li>实际开发适合于<strong>对间距要求不高</strong>的情况下可以转换</li>
<li>如果真的想要精细布局，请用flex或者grid布局更合适</li>
</ol>
<h1>三.被放弃的浮动</h1>
<p>浮动(float)最早是做“文字环绕”效果的</p>
<p>浮动(float)可以让元素<strong>脱离文档流</strong>，向左或向右浮动，知道碰到<strong>父容器边缘或其他浮动元素</strong></p>
<table>
<thead>
<tr>
<th>属性值</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>float: left;</code></td>
<td>向左浮动</td>
</tr>
<tr>
<td><code>float: right;</code></td>
<td>向右浮动</td>
</tr>
</tbody>
</table>
<p>浮动带来的影响：</p>
<ol>
<li>父盒子<strong>没有高度</strong>（很多情况下不能给父亲指定高度）</li>
<li><strong>子元素浮动</strong></li>
<li>影响其他盒子布局了</li>
</ol>
<h2>清除浮动</h2>
<p><strong>清楚浮动</strong>也可以理解为<strong>闭合浮动</strong>，简单来说，就是让浮动的元素尽量<strong>控制在父盒子</strong>内，不要影响其他盒子</p>
<p><strong>清楚浮动主要有四种方式</strong>：</p>
<p>1.<strong>额外标签法</strong>：在浮动元素的最后面<strong>新增</strong>一个<strong>块级标签</strong>。添加属性<code>clear: both;</code></p>
<p>2.<strong>单伪元素清除浮动</strong>：<strong>父元素添加伪元素</strong></p>
<pre><code>.clearfix::after{
  visibility: hidden;
  content: "";
  clear: both;
  display: block;
  height: 0;
}
</code></pre>
<p>3.<strong>双伪元素清除浮动</strong>：<strong>父元素添加双伪元素</strong></p>
<pre><code>.clearfix::after,
.clearfix::before{
  content: "";
  display: table;
} 
.clearfix::after{
  clear: both;
}
</code></pre>
<p>4.<strong>overflow属性清除浮动</strong>：<strong>父元素添加overflow属性</strong></p>
<pre><code>.clearfix{
  overflow: auto;
}
</code></pre>
<h1>四.弹性盒子</h1>
<p>Flexbox是CSS弹性盒子布局模块（Flexible Box Layout Module）的缩写，可以快速实现元素的<strong>对齐、分布和空间分配</strong>。</p>
<h2>弹性盒布局-display:flex</h2>
<p><strong>弹性盒子核心</strong>：</p>
<ol>
<li><strong>父控子（亲父子）</strong></li>
</ol>
<ul>
<li>父盒子<strong>控制</strong>子盒子如何排列布局</li>
<li>父盒子称为<strong>容器</strong>，子盒子称为<strong>项目</strong></li>
</ul>
<ol>
<li><strong>主轴和交叉轴（侧轴）</strong></li>
</ol>
<ul>
<li>主轴（main axis）：默认水平方向，项目沿主轴排列</li>
<li>交叉轴（cross axis）：默认垂直方向，项目沿交叉轴排列</li>
</ul>
<p><img src="/images/posts/page_layouts/image.png" alt="alt text" /></p>
<p><strong>容器（父盒子）属性</strong>：</p>
<table>
<thead>
<tr>
<th>属性</th>
<th>作用</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td>display</td>
<td>定义元素为 Flex 容器</td>
<td>.container { display: flex; }</td>
</tr>
<tr>
<td>flex-direction</td>
<td>定义主轴方向（项目排列方向）</td>
<td>.container { flex-direction: row; }</td>
</tr>
<tr>
<td>flex-wrap</td>
<td>控制是否换行</td>
<td>.container { flex-wrap: wrap; }</td>
</tr>
<tr>
<td>justify-content</td>
<td>定义主轴上的对齐方式（整体分布）</td>
<td>.container { justify-content: center; }</td>
</tr>
<tr>
<td>align-items</td>
<td>定义交叉轴上的对齐方式（单行时整体对齐）</td>
<td>.container { align-items: center; }</td>
</tr>
<tr>
<td>align-content</td>
<td>定义多行时交叉轴上的对齐方式（仅当 flex-wrap: wrap 且内容换行时生效）</td>
<td>.container { align-content: space-between; }</td>
</tr>
</tbody>
</table>
<p><strong>项目（子盒子）属性</strong>：</p>
<table>
<thead>
<tr>
<th>属性</th>
<th>作用</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td>order</td>
<td>定义项目的排列顺序（默认 0，数值越小越靠前）</td>
<td>.item { order: -1; }</td>
</tr>
<tr>
<td>flex-grow</td>
<td>定义项目的放大比例（默认 0，即不放大）</td>
<td>.item { flex-grow: 1; }</td>
</tr>
<tr>
<td>flex-shrink</td>
<td>定义项目的缩小比例（默认 1，空间不足时等比缩小）</td>
<td>.item { flex-shrink: 0; }</td>
</tr>
<tr>
<td>flex-basis</td>
<td>定义项目在主轴方向上的初始大小（优先级高于 width/height）</td>
<td>.item { flex-basis: 200px; }</td>
</tr>
<tr>
<td>flex</td>
<td>flex-grow、flex-shrink、flex-basis 的简写</td>
<td>.item { flex: 1; }</td>
</tr>
<tr>
<td>align-self</td>
<td>覆盖容器的 align-items，单独定义某个项目的交叉轴对齐方式</td>
<td>.item { align-self: center; }</td>
</tr>
</tbody>
</table>
<p>容器（父盒子）设置<code>display: flex;</code>可以让盒子按照<strong>主轴</strong>方式排列。</p>
<ul>
<li>如果子元素有大小，则按照<strong>给定大小</strong>来显示</li>
<li>如果子元素没有大小，则<strong>拉伸</strong>从满父容器</li>
<li>若子元素总宽度超过容器宽度，默认会<strong>压缩</strong>子元素</li>
</ul>
<blockquote>
<p>开发中大部分情况下，父容器设置display: flex,子元素设定大小</p>
</blockquote>
<h2>弹性盒布局-主轴和交叉轴对齐</h2>
<p><code>justify-content</code>定义主轴上的<strong>对齐方式</strong></p>
<table>
<thead>
<tr>
<th>属性值</th>
<th>效果</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td>flex-start</td>
<td>左对齐（默认）</td>
<td>子元素靠左排列</td>
</tr>
<tr>
<td>flex-end</td>
<td>右对齐</td>
<td>子元素靠右排列</td>
</tr>
<tr>
<td>center</td>
<td>居中对齐</td>
<td>子元素居中</td>
</tr>
<tr>
<td>space-between</td>
<td>两端对齐</td>
<td>首个子元素放置于起点，末尾元素放置于终点</td>
</tr>
<tr>
<td>space-around</td>
<td>项目两侧间隔相等</td>
<td>每个子元素周围分配相同的空间</td>
</tr>
<tr>
<td>space-evenly</td>
<td>项目间隔均匀分布</td>
<td>每个子元素之间的间隔相等</td>
</tr>
</tbody>
</table>
<p><code>align-items</code>定义交叉轴上的对齐方式（单行时整体对齐）</p>
<table>
<thead>
<tr>
<th>属性值</th>
<th>作用描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>flex-start</td>
<td>项目在交叉轴起点对齐（默认值）</td>
</tr>
<tr>
<td>flex-end</td>
<td>项目在交叉轴终点对齐</td>
</tr>
<tr>
<td>center</td>
<td>项目在交叉轴居中对齐</td>
</tr>
<tr>
<td>stretch</td>
<td>项目拉伸填充整个容器高度（需子项目无固定高度）</td>
</tr>
</tbody>
</table>
<h2>弹性盒布局-改变主轴方向flex-direction</h2>
<p><code>flex-direction</code>定义主轴方向（改变主轴方向）</p>
<table>
<thead>
<tr>
<th>属性值</th>
<th>描述</th>
<th>示例效果</th>
<th>代码示例</th>
</tr>
</thead>
<tbody>
<tr>
<td>row</td>
<td>主轴为水平方向，项目从左至右排列（默认值）</td>
<td>A B C</td>
<td>.container { flex-direction: row; }</td>
</tr>
<tr>
<td>row-reverse</td>
<td>主轴为水平方向，项目从右至左排列</td>
<td>C B A</td>
<td>.container { flex-direction: row-reverse; }</td>
</tr>
<tr>
<td>column</td>
<td>主轴为垂直方向，项目从上至下排列</td>
<td>ABC</td>
<td>.container { flex-direction: column; }</td>
</tr>
<tr>
<td>column-reverse</td>
<td>主轴为垂直方向，项目从下至上排列</td>
<td>CBA</td>
<td>.container { flex-direction: column-reverse; }</td>
</tr>
</tbody>
</table>
<h2>弹性盒布局-换行wrap以及多行交叉轴对齐</h2>
<p><code>flex-wrap</code>控制是否换行</p>
<table>
<thead>
<tr>
<th>属性值</th>
<th>排列效果</th>
</tr>
</thead>
<tbody>
<tr>
<td>nowrap</td>
<td>不换行，所有项目横向排列（可能会被压缩）</td>
</tr>
<tr>
<td>wrap</td>
<td>换行，第一行在上方，第二行在下方</td>
</tr>
<tr>
<td>wrap-reverse</td>
<td>换行，第一行在下方，第二行在上方（顺序翻转）</td>
</tr>
</tbody>
</table>
<hr />
<p><code>align-content</code>定义多行时<strong>交叉轴</strong>上的对齐方式（仅当flex-wrap:wrap且内容换行时生效）</p>
<table>
<thead>
<tr>
<th>属性值</th>
<th>效果</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>flex-start</strong></td>
<td>上对齐</td>
<td>子元素在容器头部（顶部）对齐，默认排列方式</td>
</tr>
<tr>
<td><strong>flex-end</strong></td>
<td>下对齐</td>
<td>子元素在容器尾部（底部）对齐</td>
</tr>
<tr>
<td><strong>center</strong></td>
<td>居中对齐</td>
<td>子元素在容器垂直方向居中排列</td>
</tr>
<tr>
<td><strong>space-between</strong></td>
<td>两端对齐</td>
<td>子元素平均分布在容器内，首尾子元素分别贴紧容器两端（中间无空隙）</td>
</tr>
<tr>
<td><strong>space-around</strong></td>
<td>两侧间隔相等</td>
<td>每个子元素两侧的间距相等，整体间距均匀分布</td>
</tr>
<tr>
<td><strong>space-evenly</strong></td>
<td>间隔均匀分布</td>
<td>子元素及容器两端的间距完全一致，分布最均匀</td>
</tr>
</tbody>
</table>
<blockquote>
<p>开发中大部分情况下，该属性使用较少，大部分子元素顺序排列即可</p>
</blockquote>
<h2>弹性盒布局-子项目flex伸缩以及gap间隙</h2>
<h3>子项目flex伸缩</h3>
<p>子元素的属性用于控制自身的尺寸、顺序或对齐方式。</p>
<hr />
<p><strong>语法</strong>：
<code>flex: 1;</code></p>
<ul>
<li>表示该子元素占据<strong>1份剩余空间</strong>，并且可以伸缩盒子大小。</li>
<li>数字为正整数，代表子元素在剩余空间中所占的份数。</li>
</ul>
<hr />
<table>
<thead>
<tr>
<th>属性</th>
<th>作用</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td>flex-grow</td>
<td>定义子元素剩余空间分配放大比例（默认0，即不放大）</td>
<td>.item { flex-grow: 1; }</td>
</tr>
<tr>
<td>flex-shrink</td>
<td>定义子元素剩余空间分配缩小比例（默认1，空间不足时等比缩小，0为不缩小）</td>
<td>.item { flex-shrink: 0; }</td>
</tr>
<tr>
<td>flex-basis</td>
<td>定义项目在主轴方向上的初始大小（默认auto，优先级高于width/height）</td>
<td>.item { flex-basis: 200px; }</td>
</tr>
<tr>
<td>flex</td>
<td>flex-grow、flex-shrink、flex-basis的简写flex: 1 → 1 1 0%（等比放大/收缩，初始无基准尺寸）flex: 2 → 2 1 0%（等比放大/收缩，初始无基准尺寸）</td>
<td>.item { flex: 1; }</td>
</tr>
<tr>
<td>align-self</td>
<td>覆盖容器的align-items，单独定义某个项目的交叉轴对齐方式</td>
<td>.item { align-self: center; }</td>
</tr>
<tr>
<td>order</td>
<td>定义项目的排列顺序（默认0，数值越小越靠前）</td>
<td>.item { order: -1; }</td>
</tr>
</tbody>
</table>
<hr />
<h3>gap间距</h3>
<ul>
<li><code>gap</code>：简写属性，用于设置行与列之间的间隙（间距）</li>
<li>使用场景：让子元素之间保留间隙</li>
<li>语法：
<ul>
<li><code>gap: 20px;</code>：行和列之间保持20像素间隙</li>
<li><code>gap: 20px 30px;</code>：行间距20像素，列间距30像素</li>
</ul>
</li>
<li>注意：<code>gap</code> 需写在父元素（容器）上</li>
</ul>
<hr />
<h2>小总结</h2>
<h3>容器属性（作用于父元素）</h3>
<table>
<thead>
<tr>
<th>属性</th>
<th>取值示例（含注释）</th>
<th>中文说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>display</td>
<td>flex —— 启用弹性盒布局</td>
<td>将元素设为 Flex 容器，子元素变为 Flex 项目</td>
</tr>
<tr>
<td>flex-direction</td>
<td>row —— 水平从左到右（默认）row-reverse —— 水平从右到左column —— 垂直从上到下column-reverse —— 垂直从下到上</td>
<td>定义主轴方向（即项目排列方向）</td>
</tr>
<tr>
<td>flex-wrap</td>
<td>nowrap —— 不换行（默认）wrap —— 换行，新行在下方wrap-reverse —— 换行，新行在上方</td>
<td>控制项目是否换行及堆叠方向</td>
</tr>
<tr>
<td>justify-content</td>
<td>flex-start —— 主轴起点对齐（默认）flex-end —— 主轴终点对齐center —— 主轴居中对齐space-between —— 两端对齐，中间等间距space-around —— 每个项目两侧等间距space-evenly —— 所有间距（包括边缘）完全相等</td>
<td>定义项目在主轴上的对齐与分布方式</td>
</tr>
<tr>
<td>align-items</td>
<td>stretch —— 拉伸填满交叉轴（默认，高度/宽度未设时生效）flex-start —— 交叉轴起点对齐flex-end —— 交叉轴终点对齐center —— 交叉轴居中对齐baseline —— 按文字基线对齐</td>
<td>定义单行项目在交叉轴上的对齐方式</td>
</tr>
<tr>
<td>align-content</td>
<td>stretch —— 行拉伸填满剩余空间（默认）flex-start —— 多行交叉轴起点对齐flex-end —— 多行交叉轴终点对齐center —— 多行居中对齐space-between —— 行两端对齐，中间空隙均等space-around —— 行之间均匀环绕</td>
<td>定义多行项目在交叉轴上的分布方式（仅当 flex-wrap: wrap 且有多行时生效）</td>
</tr>
</tbody>
</table>
<h3>项目属性（作用于子元素）</h3>
<table>
<thead>
<tr>
<th>属性</th>
<th>取值示例（含注释）</th>
<th>中文说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>flex-grow</td>
<td>0 —— 不放大（默认）1、2 —— 按比例放大（如两个项目都为1，则平分剩余空间）</td>
<td>空间有剩余时，项目的放大比例</td>
</tr>
<tr>
<td>flex-shrink</td>
<td>1 —— 可缩小（默认）0 —— 禁止缩小2 —— 缩小比例更高</td>
<td>空间不足时，项目的缩小比例</td>
</tr>
<tr>
<td>flex-basis</td>
<td>auto —— 根据内容或 width/height 决定（默认）100px —— 初始主轴尺寸为100px20% —— 占主轴20%</td>
<td>分配多余空间前，项目在主轴上的基准尺寸</td>
</tr>
<tr>
<td>flex</td>
<td>0 1 auto —— 默认值（不放大、不缩小、基准为auto）1 —— 常用：等价于 1 1 0%（自动占满剩余空间，自适应大小）none —— 等价于 0 0 auto（固定大小，不伸缩）</td>
<td>flex-grow、flex-shrink、flex-basis 的简写形式</td>
</tr>
<tr>
<td>align-self</td>
<td>auto —— 继承容器的 align-items（默认）flex-start / center / stretch 等 —— 覆盖容器设置</td>
<td>单个项目在交叉轴上的对齐方式（可单独设置）</td>
</tr>
<tr>
<td>order</td>
<td>0 —— 默认顺序-1 —— 排在前面2 —— 排在后面</td>
<td>控制项目显示顺序（数值越小越靠前，可为负数）</td>
</tr>
</tbody>
</table>
<h2>弹性盒布局-淘宝京东多行伸缩布局</h2>
<p><strong>淘宝</strong>：</p>
<pre><code>&lt;html&gt;
  &lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
    &lt;title&gt;弹性盒布局-子项目flex伸缩以及gap间隙&lt;/title&gt;
    &lt;style&gt;
      *{
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
      .w {
        width: 80%;
        margin: 20px auto;

      }
      .box {
        display: flex;
        flex-wrap: wrap;

        margin-left: -8px;
        margin-right: -8px;
      }
      .box .item {
        flex: 0 0 16.666666%;
        height: 120px;
        margin-bottom: 16px;
        /* margin-right: 16px; */
        padding: 0 8px;
      }
      .item .content{
        width: 100%;
        height: 100%;
        background-color: lightblue;
      }
      .footer{
        width: 80%;
        height: 120px;
        background-color: black;
        margin: 0 auto;
      }
    &lt;/style&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;div class="w"&gt;
      &lt;div class="box"&gt;
      &lt;div class="item"&gt;
        &lt;div class="content"&gt;&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="item"&gt;
        &lt;div class="content"&gt;&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="item"&gt;
        &lt;div class="content"&gt;&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="item"&gt;
        &lt;div class="content"&gt;&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="item"&gt;
        &lt;div class="content"&gt;&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="item"&gt;
        &lt;div class="content"&gt;&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="item"&gt;
        &lt;div class="content"&gt;&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="item"&gt;
        &lt;div class="content"&gt;&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="item"&gt;
        &lt;div class="content"&gt;&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="item"&gt;
        &lt;div class="content"&gt;&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="item"&gt;
        &lt;div class="content"&gt;&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="item"&gt;
        &lt;div class="content"&gt;&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="item"&gt;
        &lt;div class="content"&gt;&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="item"&gt;
        &lt;div class="content"&gt;&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="item"&gt;
        &lt;div class="content"&gt;&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="item"&gt;
        &lt;div class="content"&gt;&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="item"&gt;
        &lt;div class="content"&gt;&lt;/div&gt;
      &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="footer"&gt;
      &lt;div class="content"&gt;&lt;/div&gt;
    &lt;/div&gt;
  &lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p><strong>京东</strong>：</p>
<pre><code>&lt;html&gt;
  &lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
    &lt;title&gt;弹性盒布局-子项目flex伸缩以及gap间隙&lt;/title&gt;
    &lt;style&gt;
      *{
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }

      .box {
        display: flex;
        flex-wrap: wrap;
        width: 80%;
        margin: 0 auto;
        border: 1px solid red;
      }
      .box .item {
        flex: 1;
        width: 80px;
        height: 120px;
        background-color: pink;

        min-width: calc(16.666666666666664% - 16px);
        max-width: calc(16.666666666666664% - 16px);

        margin: 0 8px 16px;
      }
      .footer{
        width: 80%;
        height: 120px;
        background-color: black;
        margin: 0 auto;
      }
    &lt;/style&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;div class="box"&gt;
      &lt;div class="item"&gt;1&lt;/div&gt;
      &lt;div class="item"&gt;2&lt;/div&gt;
      &lt;div class="item"&gt;3&lt;/div&gt;
      &lt;div class="item"&gt;4&lt;/div&gt;
      &lt;div class="item"&gt;5&lt;/div&gt;
      &lt;div class="item"&gt;6&lt;/div&gt;
      &lt;div class="item"&gt;7&lt;/div&gt;
      &lt;div class="item"&gt;8&lt;/div&gt;
      &lt;div class="item"&gt;9&lt;/div&gt;
      &lt;div class="item"&gt;10&lt;/div&gt;
      &lt;div class="item"&gt;11&lt;/div&gt;
      &lt;div class="item"&gt;12&lt;/div&gt;
      &lt;div class="item"&gt;13&lt;/div&gt;
      &lt;div class="item"&gt;14&lt;/div&gt;
      &lt;div class="item"&gt;15&lt;/div&gt;
      &lt;div class="item"&gt;16&lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="footer"&gt;
    &lt;/div&gt;
  &lt;/body&gt;
&lt;/html&gt;
</code></pre>
<blockquote>
<p>这种多行多列这种布局，不是flex布局的强项，
所以我们一般会使用grid布局来实现。</p>
</blockquote>
<h2>弹性盒布局-瀑布流百度图片综合案例</h2>
<pre><code>&lt;html&gt;
  &lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
    &lt;title&gt;瀑布流&lt;/title&gt;
    &lt;link rel="stylesheet" href="./瀑布流.css"&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;div class="box"&gt;
      &lt;div class="column"&gt;
        &lt;div class="a"&gt;
          &lt;img src="/images/posts/page_layouts/1.jpg" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./2.jpg" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./3.jpg" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./4.png" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./5.jpg" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./6.jpg" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./7.jpg" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./8.jpg" alt=""&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="column"&gt;
        &lt;div class="a"&gt;
          &lt;img src="./9.jpg" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./10.jpg" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./11.jpg" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./12.jpg" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./13.jpg" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./14.webp" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./15.png" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./16.png" alt=""&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="column"&gt;
        &lt;div class="a"&gt;
          &lt;img src="./17.png" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./18.webp" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./19.webp" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./20.jpg" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./21.webp" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./22.webp" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./23.webp" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./24.webp" alt=""&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="column"&gt;
        &lt;div class="a"&gt;
          &lt;img src="./25.webp" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./26.webp" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./27.webp" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./28.webp" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./29.png" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./30.png" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./31.jpg" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./32.jpg" alt=""&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="column"&gt;
        &lt;div class="a"&gt;
          &lt;img src="./33.webp" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./34.webp" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./35.jpg" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./36.jpg" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./37.jpg" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./38.jpg" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./39.jpg" alt=""&gt;
        &lt;/div&gt;
        &lt;div class="a"&gt;
          &lt;img src="./40.jpg" alt=""&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;

  &lt;/body&gt;
&lt;/html&gt;
</code></pre>
<pre><code>* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.box {
  gap: 16px;
  display: flex;
  width: 80%;
  margin: 10px auto;
}

.box .column {
  flex: 1;
}

.box .column .a {
  overflow: hidden;
  display: block;
  border-radius: 20px;
  width: 100%;
  margin-bottom: 16px;
}

.box .column .a img {
  width: 100%;
}
</code></pre>
<h3>垂直对齐-解决底部空白缝隙的问题</h3>
<p>基线：浏览器**行内元素（行内块元素）**排版中存在用于对齐的基线（baseline）</p>
<p>![alt text](image-1.png null)</p>
<blockquote>
<p>图片默认和文字基线对齐</p>
</blockquote>
<p><strong>解决方法</strong></p>
<ol>
<li>把图片转换为<strong>块级元素</strong></li>
<li>设置图片对齐方式<strong>不是基线对齐即可</strong></li>
</ol>
<p><code>vertical-align</code>属性值</p>
<table>
<thead>
<tr>
<th>值</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>baseline</td>
<td>默认值。元素的基线对齐</td>
</tr>
<tr>
<td>top</td>
<td>把元素的垂直对齐位置设置为顶部</td>
</tr>
<tr>
<td>middle</td>
<td>把元素的垂直对齐位置设置为中间</td>
</tr>
<tr>
<td>bottom</td>
<td>把元素的垂直对齐位置设置为底部</td>
</tr>
</tbody>
</table>
<pre><code>img {
  &lt;!-- 防止浏览器默认给图片添加边框 --&gt;
  border: 0;
  &lt;!-- 垂直居中对齐 --&gt;
  vertical-align: middle;
}
</code></pre>
<h1>五.定位布局</h1>
<p>CSS定位布局（<strong>position</strong>）是控制元素在页面中位置的核心技术之一。通过定位，可以实现元素脱离文档流，层叠，固定在<strong>特定位置</strong>的布局效果（<strong>定位跟位置有关</strong>）</p>
<p><em><strong>场景</strong></em>：</p>
<ol>
<li><strong>固定导航栏</strong>：页面滚动时导航栏始终<strong>固定</strong>在视口顶部</li>
<li><strong>吸顶效果</strong>：元素在滚动到某个位置后固定</li>
<li><strong>弹出菜单/下拉框</strong>：鼠标悬停是显示浮动菜单</li>
<li><strong>悬浮效果</strong>：元素悬浮在其他元素上方
...</li>
</ol>
<p><strong>定位分类</strong>：</p>
<ul>
<li>相对定位</li>
<li>绝对定位</li>
<li>固定定位</li>
<li>粘性定位</li>
</ul>
<h2>相对定位</h2>
<p>CSS相对定位（position: <strong>relative</strong>;）是布局中常用的定位方式，其核心在于<strong>元素相对于自身正常位置进行偏移</strong></p>
<p><strong>场景</strong>：</p>
<blockquote>
<p>和绝对定位搭配使用，主要作用是给绝对定位当<strong>爹</strong>的</p>
</blockquote>
<p><strong>语法</strong>：</p>
<pre><code>/* 设定元素为相对定位 */
.relative {
  position: relative;
}
</code></pre>
<ul>
<li>相对于自身<strong>原来位置</strong>移动距离</li>
<li><strong>不脱离</strong>正常流，元素原位置仍被<strong>保留</strong>，其他元素按布局排列</li>
<li>可以通过<code>top</code>、<code>right</code>、<code>bottom</code>、<code>left</code>属性进行偏移，也可以通过<code>inset</code>属性，同时设置四个方向的偏移距离</li>
<li>优先级：若同时设置<code>top</code>和<code>bottom</code>，仅生效<code>top</code>，同理<code>left</code>覆盖<code>right</code></li>
</ul>
<p><strong>示例</strong>：</p>
<pre><code>/* 相对定位 */
.relative {
  position: relative;
  /* 相对自身顶部向下移动10px */
  top: 10px;
  /* 相对自身左侧向右移动10px */
  left: 10px;
}
</code></pre>
<h2>绝对定位</h2>
<p>CSS绝对定位（position: <strong>absolute</strong>;）核心是<strong>脱离正常文档流</strong>并基于<strong>定位基准</strong>进行偏移</p>
<p><strong>场景</strong>：</p>
<ol>
<li><strong>弹出菜单/下拉框</strong>：鼠标悬停是显示浮动菜单</li>
<li><strong>悬浮效果</strong>：元素悬浮在其他元素上方</li>
</ol>
<p><img src="/images/posts/page_layouts/image-2.png" alt="alt text" />
<img src="/images/posts/page_layouts/image-3.png" alt="alt text" /></p>
<p><strong>语法</strong>:</p>
<pre><code>/* 设定元素为绝对定位 */
.absolute {
  position: absolute;
}
</code></pre>
<ul>
<li>元素<strong>脱离正常流</strong>，不占据空间，其他元素按原布局排列</li>
<li>相对于最近的<strong>已定位祖先元素</strong>（position非static）移动位置，若都无定位，则相对于<strong>视口</strong>来定位</li>
<li>可以通过<code>top</code>、<code>right</code>、<code>bottom</code>、<code>left</code>属性进行偏移</li>
<li>优先级：若同时设置<code>top</code>和<code>bottom</code>，仅生效<code>top</code>，同理<code>left</code>覆盖<code>right</code></li>
</ul>
<p><strong>示例</strong>：</p>
<pre><code>/* 绝对定位 */
/* 父元素 */
.father {
  position: relative;
  width: 200px;
  height: 200px;
  background-color: pink;
}

/* 子元素 */
.son {
  position: absolute;
  width: 100px;
  height: 100px;
  background-color: red;
  /* 相对父元素顶部向下移动10px */
  top: 10px;
  /* 相对父元素左侧向右移动10px */
  left: 10px;
}
</code></pre>
<h2>定位-子绝父相</h2>
<p>为什么是子绝父相？</p>
<ol>
<li><strong>子</strong>元素可以悬浮其他元素上方，就不能占有位置，影响其他元素布局，所以<strong>绝</strong>对定位</li>
<li>但是子元素不能随便乱跑，要配合父元素移动位置，此时父元素需要加定位</li>
<li><strong>父</strong>元素需要站原来的位置，不能影响其他元素布局，此时<strong>相</strong>对定位最合适</li>
</ol>
<h3>小技巧1-圆点变化</h3>
<p><img src="/images/posts/page_layouts/image-4.png" alt="alt text" /></p>
<p>如果用边框，会比较麻烦：
<img src="/images/posts/page_layouts/image-5.png" alt="alt text" /></p>
<ol>
<li>边框要增大，需要修改盒子模型为<code>box-sizing: content-box;</code></li>
<li>盒子变大，会影响后面小圆点的排列</li>
</ol>
<p>如果用<strong>阴影</strong>更简单：
<img src="/images/posts/page_layouts/image-6.png" alt="alt text" />
阴影不占位置，所以不会增加盒子大小，从而不影响布局</p>
<pre><code>/* 阴影 */
.shadow {
  box-shadow: 0 0 0 10px rgba(0, 0, 0, 0.5);
}
</code></pre>
<hr />
<h3>小技巧2-定位的盒子垂直居中</h3>
<p><img src="/images/posts/page_layouts/image-7.png" alt="alt text" /></p>
<pre><code>/* 定位的盒子垂直居中 */
.vertical-center {
  width: 100px;
  height: 100px;
  background-color: blue;
  position: absolute;
  /* 往下走父盒子自身高度的一半 */
  top: 50%;
  /* 往上走自身高度的一半 */
  margin-top: -50px;
}
</code></pre>
<hr />
<h2>固定定位</h2>
<p>CSS固定定位（position: <strong>fixed</strong>;）是一种将元素脱离文档流并始终相对于<strong>浏览器窗口</strong>定位的布局技术</p>
<p><strong>场景</strong>：</p>
<ol>
<li><strong>固定导航栏</strong>：页面滚动时导航栏始终<strong>固定</strong>在视口顶部</li>
<li><strong>页面广告</strong>：广告覆盖整个页面</li>
</ol>
<p><strong>语法</strong>：</p>
<pre><code>/* 设定元素为固定定位 */
.fixed {
  position: fixed;
}
</code></pre>
<ul>
<li>元素<strong>脱离正常流</strong>，不占据空间，其他元素按原布局排列</li>
<li>相对于<strong>浏览器窗口</strong>定位位置，滚动页面时位置不变</li>
<li>可以通过<code>top</code>、<code>right</code>、<code>bottom</code>、<code>left</code>属性进行偏移</li>
<li>优先级：若同时设置<code>top</code>和<code>bottom</code>，仅生效<code>top</code>，同理<code>left</code>覆盖<code>right</code></li>
</ul>
<p><strong>示例</strong>：</p>
<pre><code>/* 固定定位 */
.fixed {
  /* 固定定位 */
  position: fixed;
  width: 600px;
  height: 120px;
  border-radius: 15px;
  /* 偏移调整 */
  left: 50%;
  margin-left: -300px;
  bottom: 20px;
  /* 背景颜色 */
  background-color: pink;
  /* 文字对齐 */
  text-align: center;
  line-height: 60px;
}
</code></pre>
<p><strong>效果</strong>：
<img src="/images/posts/page_layouts/image-8.png" alt="alt text" /></p>
<hr />
<h2>粘性定位</h2>
<p>CSS粘性定位（position: <strong>sticky</strong>;）是一种<strong>混合定位模式</strong>，结合了相对定位（relative）和固定定位（fixed）的特性</p>
<p><strong>场景</strong>：</p>
<ol>
<li><strong>吸顶效果</strong>：元素在滚动到某个位置后固定</li>
<li><strong>变革表头固定</strong>：长表格滚动时表头始终可见</li>
</ol>
<p><strong>语法</strong>：</p>
<pre><code>/* 设定元素为粘性定位 */
.sticky {
  position: sticky;
}
</code></pre>
<ul>
<li>元素在滚动到指定位置（如<code>top: 10px</code>），相对于<strong>最近的已定位祖先元素</strong>前为相对定位，之后转为固定定位</li>
<li><strong>父容器</strong>的overdlow需为<code>visible</code>,否则粘性定位无效</li>
<li>可以通过<code>top</code>、<code>right</code>、<code>bottom</code>、<code>left</code>属性进行偏移</li>
<li>需指定<code>top</code>,<code>right</code>,<code>bottom</code>,<code>left</code>中之一，才可使粘性定位生效</li>
</ul>
<p><strong>示例</strong>（滑动表格，表头固定）：</p>
<pre><code>/* 粘性定位 */
.sticky {
  position: sticky;
  top: 0px;
}
</code></pre>
<h2>定位布局-层叠顺序</h2>
<p><code>z-index</code>属性用于控制元素在三维空间中的<strong>层叠顺序</strong>（即z轴方向）。当多个元素在同一个平面（如同一行或列）重叠时，<code>z-index</code>决定哪个元素在最上层</p>
<p><strong>场景</strong>：</p>
<ol>
<li>提高层级悬浮效果（如导航栏）</li>
<li>实现图片层叠效果
<img src="/images/posts/page_layouts/image-9.png" alt="alt text" /></li>
</ol>
<p><strong>语法</strong>：</p>
<pre><code>/* 设定元素为层叠顺序 */
.z-index {
  z-index: 数值;
}
</code></pre>
<ul>
<li>值类型：整数（整数，负数，零）或<code>auto</code>，数值越大，层级越高</li>
<li>默认值：<code>auto</code>（等同于未设置，后出现的元素覆盖前者）</li>
<li>生效条件：仅对定位元素（<code>position</code>值为<code>relative</code>、<code>absolute</code>、<code>fixed</code>、<code>sticky</code>）有效</li>
</ul>
<h2>定位总结</h2>
<table>
<thead>
<tr>
<th>定位类型</th>
<th>脱离文档流</th>
<th>定位基准</th>
<th>层叠控制</th>
<th>典型场景</th>
</tr>
</thead>
<tbody>
<tr>
<td>static</td>
<td>否</td>
<td>默认文档流</td>
<td>无</td>
<td>普通元素布局</td>
</tr>
<tr>
<td>relative</td>
<td>否</td>
<td>自身正常位置</td>
<td>支持</td>
<td>微调元素、子绝父相</td>
</tr>
<tr>
<td>absolute</td>
<td>是</td>
<td>最近定位祖先 / 视口</td>
<td>支持</td>
<td>弹窗、下拉菜单、悬浮元素</td>
</tr>
<tr>
<td>fixed</td>
<td>是</td>
<td>视口</td>
<td>支持</td>
<td>固定导航栏、返回顶部按钮</td>
</tr>
<tr>
<td>sticky</td>
<td>否</td>
<td>视口或滚动祖先</td>
<td>支持</td>
<td>吸顶导航栏、侧边栏固定</td>
</tr>
</tbody>
</table>
<p><strong>定位属性</strong>：</p>
<ul>
<li><strong>position</strong>: （<code>static/relative/absolute/fixed/sticky</code>）</li>
<li><strong>偏移属性</strong>：（<code>top</code>、<code>right</code>、<code>bottom</code>、<code>left</code>）</li>
<li><strong>z-index</strong>：（控制层叠顺序，数值越大层级越高）</li>
</ul>
<p><strong>常见组合</strong>：</p>
<ul>
<li><strong>固定导航栏</strong>：<code>position: fixed;</code> + <code>z-index: 100;</code></li>
<li><strong>弹窗</strong>：<code>position: absolute;</code> + 居中技巧 + <code>z-index: 1000;</code></li>
<li><strong>吸顶效果</strong>：<code>position: sticky;</code> + <code>top: 0;</code></li>
</ul>
<h1>六.网格布局</h1>
<h2>1.基本概念</h2>
<p>网格布局时一种<strong>二位布局</strong>模型，允许开发者通过定义行（rows）和列（columns）来精确控制网页元素的位置和尺寸，还可以实现<strong>响应式</strong>设计<br />
<img src="/images/posts/page_layouts/image-10.png" alt="alt text" /></p>
<blockquote>
<p>网格布局的核心：创建好网格并放入各类元素<br />
开发常见：单行布局用flex，多行或者响应式用grid</p>
</blockquote>
<h2>2.网格容器</h2>
<p>容器（父盒子）设置<code>display: grid;</code>（<strong>块级</strong>）或者<code>display: inline-grid;</code>（<strong>行内</strong>）<br />
与弹性盒子不同的是，在定义网格后，网页并不会马上发生变化，因为<code>display: grid;</code>的声明之创建了一个只有一列的网格容器</p>
<h2>3.网格轨道</h2>
<p>网格轨道（grid track）决定了网格容器的基础布局结构，为子元素提供放置的位置</p>
<p><strong>绘制网格</strong>：（网格轨道）
<code>grid-template-columns</code>定义网格中的列，每个值代表一列的宽度
<code>grid-template-rows</code>定义网格中的行，每个值代表一行的高度</p>
<p><code>grid-template: grid-template-columns / grid-template-rows;</code> 同时定义列和行</p>
<p><strong>属性值</strong>：</p>
<ul>
<li>几个属性值代表创建几列/行</li>
<li>长度单位 （px、fr等）</li>
</ul>
<table>
<thead>
<tr>
<th>属性值</th>
<th>说明</th>
<th>示例</th>
<th>应用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td>固定长度</td>
<td>使用 px、em 等固定单位定义列宽</td>
<td><code>grid-template-columns: 100px 200px;</code></td>
<td>需要精确控制列宽的固定布局</td>
</tr>
<tr>
<td>百分比</td>
<td>按容器宽度百分比分配列宽</td>
<td><code>grid-template-columns: 30% 70%;</code></td>
<td>响应式布局中按比例划分列</td>
</tr>
<tr>
<td>fr 单位</td>
<td>分配轨道剩余空间的比例（1fr 表示一份，总和为容器剩余空间，fr 是 fraction 缩写，意为分数）</td>
<td><code>grid-template-columns: 1fr 2fr;</code></td>
<td>需要自适应比例分配的布局</td>
</tr>
<tr>
<td>auto</td>
<td>列宽由内容自动撑开</td>
<td><code>grid-template-columns: auto 100px;</code></td>
<td>内容宽度不确定时的灵活布局</td>
</tr>
<tr>
<td>repeat() 函数</td>
<td>简化重复的列定义</td>
<td><code>grid-template-columns: repeat(3, 1fr);</code>（等效于 <code>1fr 1fr 1fr</code>）</td>
<td>多列等宽布局</td>
</tr>
<tr>
<td>minmax() 函数</td>
<td>定义列宽的最小值和最大值</td>
<td><code>grid-template-columns: minmax(100px, 1fr);</code></td>
<td>响应式布局中限制列宽范围</td>
</tr>
</tbody>
</table>
<p><strong>示例</strong>：</p>
<pre><code>.grid {
  display: grid;
  /* 定义三列，每列宽度为200px */
  grid-template-columns: 200px 200px 200px;
  /* 定义三行，每行高度为200px */
  grid-template-rows: 200px 200px 200px;
}
</code></pre>
<blockquote>
<p>技巧：如果想不限制行数，可以不设置<code>grid-template-rows</code>，给子盒子定义高度即可，父盒子会自动调整行数以适应子盒子的高度</p>
</blockquote>
<h3>repeat() 函数</h3>
<p><strong>语法</strong>：
<code>repeat(次数，轨道尺寸)</code>或<code>repeat(自动填充，轨道尺寸)</code></p>
<p><strong>自动填充</strong>：适用于响应式布局中“列数随容器宽度变化”的场景
<code>auto-fill</code>：尽可能多地创建列
<code>auto-fit</code>：尽可能拉伸列填满容器（会合并空白，列宽不小于minmax的最小值）</p>
<p><strong>区别</strong>：</p>
<ul>
<li><code>auto-fill</code>：会创建 as many columns as possible（尽可能多地创建列），直到容器宽度不足，留有空白空间
<img src="/images/posts/page_layouts/image-11.png" alt="alt text" /></li>
</ul>
<blockquote>
<p>空白空间的地方也是被创建的列，只是没有内容，总共不止4列</p>
</blockquote>
<ul>
<li><code>auto-fit</code>：会创建 as many columns as possible（尽可能多地伸展列），直到容器宽度不足，填满空白空间
<img src="/images/posts/page_layouts/image-12.png" alt="alt text" /></li>
</ul>
<blockquote>
<p>空白的地方被拉伸填满，总共只有4列</p>
</blockquote>
<h3>minmax() 函数</h3>
<p><strong>语法</strong>：
<code>minmax(最小值，最大值)</code></p>
<p><strong>最小值</strong>：定义轨道的最小尺寸
<strong>最大值</strong>：定义轨道的最大尺寸</p>
<p><strong>响应式布局示例</strong>：</p>
<pre><code>.grid {
  display: grid;
  /* 响应式布局，列数随容器宽度变化，列宽在100px到1fr之间 */
  grid-template-columns: minmax(auto-fill, minmax(100px, 1fr));
}
</code></pre>
<h2>4.网格轨道-对齐方式</h2>
<p><code>justify-content</code>是控制<strong>列轨道</strong>（Columns Tracks）在容器内水平分布
<code>align-content</code>是控制<strong>行轨道</strong>（Rows Tracks）在容器内垂直分布</p>
<table>
<thead>
<tr>
<th>属性值</th>
<th>水平方向效果</th>
<th>垂直方向效果</th>
</tr>
</thead>
<tbody>
<tr>
<td>start（默认值）</td>
<td>左对齐</td>
<td>顶部对齐</td>
</tr>
<tr>
<td>end</td>
<td>右对齐</td>
<td>底部对齐</td>
</tr>
<tr>
<td>center</td>
<td>水平居中对齐</td>
<td>垂直居中对齐</td>
</tr>
<tr>
<td>space-around</td>
<td>两侧留出相等的空白，项目周围空间均匀分布</td>
<td>上下留出相等的空白，项目周围空间均匀分布</td>
</tr>
<tr>
<td>space-between</td>
<td>首尾项目贴边</td>
<td>上下项目贴边</td>
</tr>
<tr>
<td>space-evenly</td>
<td>项目间、首尾与边界的空白相等</td>
<td>项目间、首尾与边界的空白相等</td>
</tr>
</tbody>
</table>
<h2>5.网格间距</h2>
<p><code>gap</code>简写属性用于设置行与列之间的间隙（网格间距）</p>
<p><strong>语法</strong>：
<code>gap: 20px;</code> 行和列之间的保持20像素间隙
<code>gap: 20px 40px;</code> 行之间的保持20像素间隙，列之间的保持40像素间隙</p>
<p><strong>注意</strong>：gap是简写形式也可以分开写
<code>column-gap: 20px;</code> 行之间的保持20像素间隙
<code>row-gap: 40px;</code> 列之间的保持40像素间隙</p>
<h2>6.网格线</h2>
<p>网格线会在使用网格布局时被创建，<strong>线编号</strong>从<em>1</em>开始计算，<em>3</em>列就会有<em>4</em>根线
<img src="/images/posts/page_layouts/image-12.png" alt="alt text" /></p>
<p><strong>使用场景</strong>：</p>
<ol>
<li>实现<em>元素跨越</em>多个网格单元</li>
</ol>
<p><img src="/images/posts/page_layouts/image-13.png" alt="alt text" /></p>
<p><strong>实现语法1</strong>：</p>
<ol>
<li><strong>跨列</strong>：<code>grid-column: 开始线编号/结束线编号;</code></li>
<li><strong>跨行</strong>：<code>grid-row: 开始线编号/结束线编号;</code></li>
</ol>
<p>&lt;font color="red"&gt;
注意：该属性要加到<strong>项目</strong>（子元素）上
&lt;/font&gt;</p>
<p><strong>示例</strong>：</p>
<pre><code>.grid .item:first-child {
  /* ... */
  /* 跨列 */
  grid-column: 1 / 3;
  /* 跨行 */
  grid-row: 1 / 3;
}
</code></pre>
<p><strong>效果</strong>：
<img src="/images/posts/page_layouts/image-14.png" alt="alt text" /></p>
<hr />
<p><strong>实现语法2</strong>：</p>
<ol>
<li><strong>跨列</strong>：<code>grid-column: 开始线编号/span 跨单元格数量;</code></li>
<li><strong>跨行</strong>：<code>grid-row: 开始线编号/span 跨单元格数量;</code></li>
</ol>
<p><strong>示例</strong>：</p>
<pre><code>.grid .item:first-child {
  /* ... */
  /* 跨列 1到3列 */
  grid-column: 1 / span 2;
  /* 跨行 1到3行 */
  grid-row: 1 / span 2;
}
</code></pre>
<blockquote>
<p>效果同上</p>
</blockquote>
<h2>7.网格填充方式</h2>
<p><code>grid-auto-flow</code>决定网格容器中子元素<strong>排列填充</strong>方式</p>
<p><strong>语法</strong>：</p>
<ol>
<li><code>grid-auto-flow: column;</code>
子元素按先行后列顺序填充，优先填满一列后换列
<img src="/images/posts/page_layouts/image-15.png" alt="alt text" /></li>
<li><code>grid-auto-flow: row;</code>
子元素按先列后行顺序填充，优先填满一行后换行
<img src="/images/posts/page_layouts/image-16.png" alt="alt text" /></li>
</ol>
<p><strong>场景</strong>：
b站布局中有些子元素是按照列方式显示
<img src="/images/posts/page_layouts/image-17.png" alt="alt text" /></p>
<h3>object-fit</h3>
<p><code>object-fit</code>是css中用于控制替换元素（如<code>&lt;img&gt;</code>、<code>&lt;video&gt;</code>、<code>&lt;iframe&gt;</code>等加载外部资源的元素）内容如何适应其容器尺寸的属性</p>
<table>
<thead>
<tr>
<th>值</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>fill</td>
<td>默认值。拉伸内容以完全填充容器，不保持宽高比，可能导致内容变形（最常用但需谨慎）</td>
</tr>
<tr>
<td>contain</td>
<td>保持内容固有宽高比，缩放至完全适应容器内部（内容全部可见），容器可能有空白（类似“适应”模式）</td>
</tr>
<tr>
<td>cover</td>
<td>保持内容固有宽高比，缩放至覆盖容器（内容可能部分被裁剪），容器无空白（类似“填充”模式）</td>
</tr>
</tbody>
</table>
<p><strong>配合object-position</strong>：</p>
<p><code>object-position</code> 用于控制替换元素内容在容器内的对齐位置，配合 <code>object-fit</code> 使用可以精确控制内容的显示区域。</p>
<p><strong>语法</strong>：</p>
<pre><code>object-position: &lt;x-position&gt; &lt;y-position&gt;;
</code></pre>
<p><strong>常用值</strong>：</p>
<table>
<thead>
<tr>
<th>值类型</th>
<th>示例</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>关键字</td>
<td><code>left</code> / <code>center</code> / <code>right</code> / <code>top</code> / <code>bottom</code></td>
<td>基本方向对齐</td>
</tr>
<tr>
<td>百分比</td>
<td><code>50% 50%</code></td>
<td>水平和垂直位置百分比（默认值，居中）</td>
</tr>
<tr>
<td>长度值</td>
<td><code>10px 20px</code></td>
<td>具体像素或其他长度单位</td>
</tr>
<tr>
<td>混合值</td>
<td><code>left 50%</code></td>
<td>关键字与百分比/长度混合使用</td>
</tr>
</tbody>
</table>
<p><strong>使用示例</strong>：</p>
<pre><code>/* 左对齐 */
img {
  object-fit: cover;
  object-position: left;
}

/* 左上角对齐 */
img {
  object-fit: cover;
  object-position: 0 0;
}

/* 向左移动10%，垂直居中 */
img {
  object-fit: cover;
  object-position: 10% center;
}

/* 向右移动20px，向下移动30px */
img {
  object-fit: cover;
  object-position: 20px 30px;
}
</code></pre>
<p><strong>应用场景</strong>：</p>
<ul>
<li>图片裁剪时需要显示特定区域（如人物照片只显示面部）</li>
<li>响应式布局中保持图片的重要内容可见</li>
<li>视频播放器中控制视频画面的显示位置</li>
</ul>
<p><strong>注意事项</strong>：</p>
<ul>
<li>仅对替换元素有效（<code>img</code>、<code>video</code>、<code>canvas</code>等）</li>
<li>需要与 <code>object-fit</code> 配合使用才能发挥最大效果</li>
<li>百分比值的参考系是容器的尺寸，而非内容本身的尺寸</li>
</ul>
<h2>8.项目对齐方式</h2>
<p>在css grid布局中，控制单元格里的子元素的对齐方式</p>
<p><strong>场景</strong>：</p>
<ol>
<li>元素在网格内对齐
<img src="/images/posts/page_layouts/image-18.png" alt="alt text" /></li>
</ol>
<p><strong>语法</strong>：</p>
<ol>
<li><code>justfy-items: 水平对齐方式;</code> 水平方向（行轴）</li>
<li><code>align-items: 垂直对齐方式;</code> 垂直方向（列轴）</li>
<li><code>place-items: 水平对齐方式 垂直对齐方式;</code> 同时设置水平和垂直对齐方式</li>
</ol>
<blockquote>
<p>为了提高可读性和兼容性，体长使用前面两种方式</p>
</blockquote>
<p>&lt;font color="red"&gt;
注意：
&lt;/font&gt;</p>
<p>该属性要加到网格容器上，而不是项目上<br />
不要和<code>align-content</code>，<code>justify-content</code>混淆<br />
<a href="#4%E7%BD%91%E6%A0%BC%E8%BD%A8%E9%81%93-%E5%AF%B9%E9%BD%90%E6%96%B9%E5%BC%8F"><code>点我跳转轨道对齐方式</code></a></p>
<p><strong>属性值</strong>：</p>
<table>
<thead>
<tr>
<th>属性值</th>
<th>水平方向效果</th>
<th>垂直方向效果</th>
<th>代码示例</th>
</tr>
</thead>
<tbody>
<tr>
<td>start</td>
<td>对齐单元格起始边缘（左对齐）</td>
<td>对齐单元格起始边缘（顶部对齐）</td>
<td><code>.container { justify-items: start; }</code></td>
</tr>
<tr>
<td>end</td>
<td>对齐单元格结束边缘（右对齐）</td>
<td>对齐单元格结束边缘（底部对齐）</td>
<td><code>.container { justify-items: end; }</code></td>
</tr>
<tr>
<td>center</td>
<td>水平居中对齐</td>
<td>垂直居中对齐</td>
<td><code>.container { justify-items: center; }</code></td>
</tr>
<tr>
<td>stretch</td>
<td>拉伸填满单元格宽度（默认值）</td>
<td>拉伸填满单元格高度（默认值）</td>
<td><code>.container { justify-items: stretch; }</code></td>
</tr>
</tbody>
</table>
<h1>七.多列布局</h1>
<p>多列布局用于将元素的内容<strong>自动分割</strong>为指定数量的列</p>
<p><strong>场景</strong>：</p>
<ol>
<li><strong>长文章分栏</strong>：文章自动分列，中间有间隙，也可以做响应式</li>
<li><strong>图片瀑布流</strong>
<img src="/images/posts/page_layouts/image-19.png" alt="alt text" /></li>
</ol>
<blockquote>
<p><strong>优势</strong>：自动分列，无需额外准备列盒子</p>
</blockquote>
<p><img src="/images/posts/page_layouts/image-20.png" alt="alt text" /></p>
<p><strong>实现语法</strong>：</p>
<table>
<thead>
<tr>
<th>属性</th>
<th>作用</th>
<th>示例值</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>column-count</code></td>
<td>根据容器宽度和 <code>column-count</code> 值，自动分配列宽</td>
<td><code>column-count: 3;</code> 分为 3 列</td>
</tr>
<tr>
<td><code>column-gap</code></td>
<td>列之间的间距</td>
<td><code>20px</code>、<code>normal</code>（默认 1em）</td>
</tr>
<tr>
<td><code>column-rule</code></td>
<td>列间的分隔线样式（颜色、宽度、样式）</td>
<td><code>column-rule: 1px solid #ccc;</code></td>
</tr>
</tbody>
</table>
<p>&lt;font color="red"&gt;注意：  &lt;/font&gt;</p>
<ol>
<li>底部子元素被“<strong>切断</strong>”。<br />
原因：<code>column-count</code>的本质是将内容按垂直方向<strong>自动分割到多列</strong>（类似报纸排版）<br />
内容会优先填满一列后再流入下一列</li>
</ol>
<p><strong>解决方案</strong>：
<code>break-inside: avoid-column;</code> 强制元素不跨列分割（默认auto，允许跨列）</p>
<blockquote>
<p>该属性给子元素添加，而不是容器</p>
</blockquote>
<h1>八.鼠标样式</h1>
<p>在css中，<code>cursor</code>属性用于控制<strong>鼠标指针</strong>在元素上的<strong>显示样式</strong>，通过改变光标形态可以向用户传递交互提示（如“可点击”、“不可选中”等），从而提升界面交互体验。
<img src="/images/posts/page_layouts/image-21.png" alt="alt text" /></p>
<table>
<thead>
<tr>
<th>值</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>default</td>
<td>默认箭头（通常是系统默认的箭头光标）</td>
</tr>
<tr>
<td>pointer</td>
<td>手型（指尖朝上的小手），常用于可点击元素（如链接 <code>&lt;a&gt;</code>、按钮 <code>&lt;button&gt;</code>）</td>
</tr>
<tr>
<td>text</td>
<td>I 型竖线（类似文本输入光标），用于可编辑文本区域（如 <code>&lt;textarea&gt;</code>、<code>contenteditable</code> 元素）</td>
</tr>
<tr>
<td>wait</td>
<td>等待（旋转圆圈或沙漏），表示操作进行中（如加载、提交时）</td>
</tr>
<tr>
<td>help</td>
<td>帮助（带问号的箭头），提示用户需要帮助（如提示信息区域）</td>
</tr>
<tr>
<td>not-allowed</td>
<td>禁止（圆圈带斜线），表示操作不可用（如禁用的按钮）</td>
</tr>
<tr>
<td>grab</td>
<td>抓取（手型带抓取动作），表示可拖动（如可拖拽的元素）</td>
</tr>
<tr>
<td>grabbing</td>
<td>抓取中（手型收紧），表示正在拖动（配合 <code>grab</code> 使用）</td>
</tr>
<tr>
<td>zoom-in</td>
<td>放大（+号），表示放大操作（如图片预览时）</td>
</tr>
<tr>
<td>zoom-out</td>
<td>缩小（-号），表示缩小操作（如图片预览时）</td>
</tr>
</tbody>
</table>
<p><strong>示例</strong>：</p>
<pre><code>/* 鼠标悬停时显示手型 */
.item:hover {
  cursor: pointer;
}
</code></pre>
<h1>九.CSS书写顺序</h1>
<p>合理的属性顺序应遵循【<strong>布局</strong>→<strong>盒模型</strong>→<strong>视觉</strong>→<strong>交互</strong>→<strong>其他</strong>】的逻辑，核心目标是让代码更易读、更易维护。实际开发中可根据项目需求微调，但保持团队<strong>一致性</strong>是关键。</p>
<table>
<thead>
<tr>
<th>顺序</th>
<th>常见属性列表</th>
</tr>
</thead>
<tbody>
<tr>
<td>布局相关</td>
<td><code>display</code>（显示类型）、<code>visibility</code>（可见性）、<code>position</code>（定位方式）、<code>top/right/bottom/left</code>（定位偏移）、<code>float</code>（浮动）、<code>clear</code>（清除浮动）、<code>overflow</code>（溢出处理）、<code>z-index</code>（层叠顺序）、<code>clip</code>（裁剪）</td>
</tr>
<tr>
<td>盒模型</td>
<td><code>box-sizing</code>（盒模型计算方式）、<code>width/min-width/max-width</code>（宽度）、<code>height/min-height/max-height</code>（高度）、<code>margin</code>（外边距）、<code>padding</code>（内边距）、<code>border</code>（边框）、<code>outline</code>（轮廓，可选）</td>
</tr>
<tr>
<td>视觉样式</td>
<td><code>color</code>（文本颜色）、<code>background</code>（背景，含 <code>background-color/background-image</code> 等）、<code>font</code>（字体合写）、<code>font-size</code>（字号）、<code>font-family</code>（字体系列）、<code>line-height</code>（行高）、<code>text-align</code>（文本对齐）、<code>text-indent</code>（首行缩进）、<code>letter-spacing</code>（字间距）、<code>white-space</code>（空白处理）</td>
</tr>
<tr>
<td>交互与动画</td>
<td><code>cursor</code>（鼠标指针）、<code>opacity</code>（透明度）、<code>transition</code>（过渡）、<code>animation</code>（动画）、<code>transform</code>（变换，如旋转、缩放）</td>
</tr>
<tr>
<td>其他（特殊/低频）</td>
<td><code>filter</code>（滤镜）、<code>clip-path</code>（裁剪路径）、<code>backdrop-filter</code>（背景滤镜）、<code>will-change</code>（优化提示）、<code>touch-action</code>（触摸行为）、<code>scroll-behavior</code>（滚动行为）</td>
</tr>
</tbody>
</table>
]]></content>
    <author><name>仙鹤</name></author>
    <category term="前端"/>
  </entry>
  <entry>
    <title>CSS基础知识入门</title>
    <link href="https://xianhe51920.github.io/posts/css_base/" rel="alternate" type="text/html"/>
    <id>https://xianhe51920.github.io/posts/css_base/</id>
    <published>2026-06-01T00:00:00.000Z</published>
    <updated>2026-06-01T00:00:00.000Z</updated>
    <summary>包含选择器，盒子模型等css基础知识</summary>
    <content type="html"><![CDATA[<h1>CSS3核心技术</h1>
<p><strong>板块</strong></p>
<ol>
<li>CSS3基础知识</li>
<li>CSS3选择器</li>
<li>CSS3盒子模型</li>
<li>CSS3精灵图</li>
<li>CSS3字体图标</li>
</ol>
<hr />
<h1>CSS简介</h1>
<p>CSS(Cascading Style Sheets,层叠样式表)。是用来控制网页在浏览器中的<strong>显示外观</strong>的语言。<br />
<strong>CSS3</strong>现在已被大部分现代浏览器支持，而下一版CSS4仍在开发中。</p>
<p><strong>作用</strong>：<br />
<strong>样式美化</strong>：文本样式，背景，边框等<br />
<strong>布局与定位</strong>：元素排列，响应式设计<br />
<strong>动画交互</strong>：伪类，过渡，动画</p>
<blockquote>
<p>CSS的核心是&lt;ins&gt;控制视觉表现&lt;/ins&gt;</p>
</blockquote>
<p><strong>html</strong>：html是结构，用于展示内容<br />
<strong>css</strong>：css是样式，用于设置网页的样式及布局</p>
<p><strong>简单示例</strong>：</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
  &lt;head&gt;
    &lt;meta charset="UTF-8" /&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0" /&gt;
    &lt;title&gt;document&lt;/title&gt;
    &lt;style&gt;
      div {
        font-size: 20px;
        color: red;
      }
    &lt;/style&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;div&gt;文字内容&lt;/div&gt;
  &lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>这样div元素的文字内容就会显示为20px的红色字体。</p>
<hr />
<h1>CSS分类（位置分类）</h1>
<p>1.<strong>内联样式表（行内样式表）</strong><br />
样式写到标签内部，可以控制当前标签的样式，特殊情况使用。</p>
<pre><code>&lt;p style="color: red; font-size: 14px;"&gt;文字内容&lt;/p&gt;
</code></pre>
<p>2.<strong>内部样式表</strong><br />
写到<code>&lt;head&gt;</code>标签中，脱离结构，可以控制当前页面的所有标签，较为常用。</p>
<pre><code>&lt;style&gt;
  div {
    font-size: 20px;
    color: red;
  }
&lt;/style&gt;
</code></pre>
<p>3.<strong>外部样式表</strong><br />
单独新建一个CSS文件，完全脱离结构，可以控制整个网站的所有标签，最常用。</p>
<pre><code>&lt;link rel="stylesheet" href="./css/index.css" /&gt;
</code></pre>
<hr />
<h1>初识CSS选择器</h1>
<p><strong>选择器是什么?</strong><br />
CSS选择器是CSS规则的一部分，它是<strong>选择 HTML 元素</strong>的方式。</p>
<p><em>CSS属性</em>：
采取<strong>键值对</strong>形式。属性名:属性值;<br />
<img src="/images/posts/css_base/image.png" alt="alt text" /><br />
<strong>CSS选择器</strong>：选择标签<br />
<strong>CSS属性</strong>：设置标签的样式</p>
<h4>选择器分类</h4>
<p>根据使用场景不同，选择器也分不同类型</p>
<ul>
<li>基础选择器</li>
<li>关系选择器</li>
<li>分组选择器</li>
<li>伪类和伪元素</li>
<li>属性选择器
...</li>
</ul>
<hr />
<h1>CSS基础选择器</h1>
<p>基础选择器指由单个选择器组成，常见的有：</p>
<ol>
<li><strong>类型选择器</strong><pre><code>p {
  color: gold;
}
</code></pre>
</li>
</ol>
<hr />
<ol>
<li><strong>类选择器</strong><pre><code>.box {
  color: gold;
}
</code></pre>
</li>
</ol>
<hr />
<ol>
<li><strong>id选择器</strong><pre><code>#box {
  color: gold;
}
</code></pre>
</li>
</ol>
<hr />
<ol>
<li><strong>通配符选择器</strong><pre><code>* {
  color: gold;
}
</code></pre>
</li>
</ol>
<hr />
<h2>类型选择器</h2>
<p>类型选择器选择某个类型的元素，也称为标签选择器。</p>
<pre><code>/* 选择所有div标签修改样式 */
div {
  color: gold;
}

/* 选择所有span标签修改样式 */
span {
  color: green;
}
</code></pre>
<p><img src="/images/posts/css_base/image-1.png" alt="alt text" /></p>
<p><strong>CSS层叠性</strong>：<br />
CSS 规则可以同时作用于一个元素，浏览器通过特定规则决定最终生效的样式。层叠性解决了样式冲突问题。<br />
<strong>原则</strong>：后定义的样式覆盖先前的样式（就近原则）。</p>
<h3>CSS书写规范</h3>
<p>良好的书写规范，让我们更专业。虽然代码自动格式化，但是我们还需要了解：</p>
<ol>
<li>选择器和大括号中间保留1个空格距离。</li>
<li>属性名和属性值中间也要保留1个空格。</li>
<li>每个CSS属性单独占一行。后期会打包压缩，无需担心体积问题。</li>
</ol>
<p><strong>示例代码</strong></p>
<pre><code>p {
  color: gold;
  font-size: 20px;
}
</code></pre>
<h3>CSS注释 + AI注释</h3>
<p>CSS注释的快捷键是 <code>ctrl + /</code>，注意注释内容左右两侧保持一个空格距离是良好习惯。</p>
<p><strong>示例代码</strong></p>
<pre><code>span {
  /* 这里添加注释 */
  color: green;
}
</code></pre>
<p><strong>AI工具注释</strong></p>
<ol>
<li>单个属性注释可以在CSS属性后面打一个<strong>空格</strong>即可自动添加注释。</li>
<li>如果想要给多个属性添加注释，<strong>选中CSS添加到右侧对话区，输入提示词即可</strong>。</li>
</ol>
<hr />
<h2>类选择器</h2>
<p>类选择器选择某<strong>一个元素</strong>或者<strong>多个元素</strong>。<br />
场景：<br />
<img src="/images/posts/css_base/image-2.png" alt="alt text" /><br />
<strong>语法</strong>：<br />
<strong>CSS:</strong></p>
<pre><code>&lt;style&gt;
  .pink {
    color: pink;
  }
&lt;/style&gt;
</code></pre>
<p><code>.类名 { 样式声明; ... }</code></p>
<p><strong>HTML:</strong></p>
<pre><code>&lt;ul&gt;
  &lt;li&gt;猪爸爸&lt;/li&gt;
  &lt;li&gt;猪妈妈&lt;/li&gt;
  &lt;li&gt;乔治&lt;/li&gt;
  &lt;li class="pink"&gt;佩奇&lt;/li&gt;
&lt;/ul&gt;
</code></pre>
<p><code>&lt;标签 class="类名"&gt;</code></p>
<p><strong>类选择器注意事项</strong>：</p>
<ol>
<li>类名自定义，不能是中文、纯数字。</li>
<li>多个英文单词组成尽量使用 <code>-</code> 链接。</li>
<li>命名要有意义，尽量见名知义。</li>
<li><code>class</code> 属性可以有多类名，中间用空格隔开。</li>
<li>类选择器的权重（优先级）高于类型选择器（标签选择器）。</li>
</ol>
<hr />
<h2>id选择器</h2>
<p>选择 HTML 中具有特定 <code>id</code> 属性的唯一元素</p>
<p><strong>语法</strong>：</p>
<p><strong>CSS:</strong></p>
<pre><code>#header {
  color: red;
}
</code></pre>
<p><code>#id名 { 样式声明; ... }</code></p>
<p><strong>HTML:</strong></p>
<pre><code>&lt;div id="header"&gt;修改样式&lt;/div&gt;
</code></pre>
<p><code>&lt;标签 id="id名"&gt;</code></p>
<h3>id选择器和类选择器区别</h3>
<p><strong>类选择器</strong></p>
<ul>
<li><strong>语法</strong>：以 <code>.</code> 开头，后跟类名（如 <code>.nav</code>）</li>
<li><strong>作用</strong>：选择 <code>class</code> 属性的一个或多个元素</li>
<li><strong>特点</strong>：可以使用多次</li>
<li><strong>类似</strong>：身份证的名字，可以使用多次</li>
<li><strong>场景</strong>：后期修改样式基本都是类选择器</li>
</ul>
<p><strong>id选择器</strong></p>
<ul>
<li><strong>语法</strong>：以 <code>#</code> 开头，后跟 id 名（如 <code>#header</code>）</li>
<li><strong>作用</strong>：选择特定 <code>id</code> 属性的唯一元素</li>
<li><strong>特点</strong>：同一页面中，id 值必须唯一（不能重复）</li>
<li><strong>类似</strong>：身份证的编号，唯一，只能用一次</li>
<li><strong>场景</strong>：后期主要是配合 JavaScript 添加交互效果</li>
</ul>
<hr />
<h2>通配符选择器</h2>
<p>通配符可以选择 HTML 中<strong>所有</strong>的标签，进行样式修改。
特殊情况下通过通配符清除所有元素的默认边距和填充，统一不同浏览器的<strong>默认样式</strong>。<br />
<strong>语法</strong>：</p>
<pre><code>* {
  margin: 0; /* 去除所有元素的外边距 */
  padding: 0; /* 去除所有元素的内边距 */
  box-sizing: border-box; /* 统一盒模型，包括内边距和边框 */
}
</code></pre>
<h2>CSS基础选择器总结</h2>
<table>
<thead>
<tr>
<th>选择器类型</th>
<th>语法示例</th>
<th>匹配范围</th>
<th>复用性</th>
<th>典型使用场景</th>
<th>注意事项</th>
</tr>
</thead>
<tbody>
<tr>
<td>类型选择器</td>
<td><code>div { ... }</code></td>
<td>匹配所有指定HTML标签元素</td>
<td>某一类型标签</td>
<td>全局样式重置、基础标签样式（如body, p）</td>
<td>避免滥用，可能导致样式冲突</td>
</tr>
<tr>
<td>类选择器</td>
<td><code>.nav { ... }</code></td>
<td>匹配所有含指定class的元素</td>
<td>多次使用</td>
<td>多元素共享样式</td>
<td>优先使用，避免与ID选择器冲突</td>
</tr>
<tr>
<td>ID选择器</td>
<td><code>#header { ... }</code></td>
<td>匹配唯一含指定id的元素</td>
<td>唯一性</td>
<td>唯一元素标识</td>
<td>必须唯一，避免样式覆盖风险</td>
</tr>
<tr>
<td>通配符选择器</td>
<td><code>* { ... }</code></td>
<td>匹配所有元素</td>
<td>全局覆盖</td>
<td>全局样式重置</td>
<td>性能较差，慎用</td>
</tr>
</tbody>
</table>
<hr />
<p>大部分情况下，我们使用<strong>类选择器</strong>，是最重要的。</p>
<hr />
<h1>CSS关系选择器</h1>
<p>关系选择器是通过<strong>位置关系</strong>来选择目标元素（标签），可以更精准选择某些元素。常见的有：</p>
<ol>
<li><strong>后代选择器</strong><pre><code>.box p {
  color: pink;
}
</code></pre>
选择 <code>.box</code> 元素内部<strong>所有层级</strong>的 <code>p</code> 元素（包括子、孙等后代）。</li>
</ol>
<hr />
<ol>
<li><strong>子代选择器</strong><pre><code>.box &gt; p {
  color: pink;
}
</code></pre>
选择 <code>.box</code> 元素内部<strong>直接子层级</strong>的 <code>p</code> 元素（仅父子关系，不包含孙级及以下）。</li>
</ol>
<hr />
<ol>
<li><strong>邻接兄弟选择器</strong><pre><code>h2 + p {
  color: red;
}
</code></pre>
选择 <code>h2</code> 元素<strong>紧挨着的下一个兄弟</strong> <code>p</code> 元素（仅相邻的第一个兄弟）。</li>
</ol>
<hr />
<ol>
<li><strong>通用兄弟选择器</strong><pre><code>h2 ~ p {
  color: blue;
}
</code></pre>
选择 <code>h2</code> 元素<strong>后面所有同级</strong>的 <code>p</code> 元素（不要求紧邻）。</li>
</ol>
<hr />
<h2>后代选择器</h2>
<p>选择某个元素的<strong>后代元素（不限层级，包括子元素，孙元素等）</strong>。<br />
<strong>场景</strong>：<br />
<img src="/images/posts/css_base/image-3.png" alt="alt text" /><br />
图中有的链接字大，有的字小，就是通过后代选择器来实现的。</p>
<p><strong>语法</strong>：</p>
<pre><code>父元素 后代元素 {
  样式声明;
  ...
}
</code></pre>
<p><strong>示例</strong>：<br />
<strong>CSS</strong>：</p>
<pre><code>div p {
  color: pink;
}
</code></pre>
<hr />
<p><strong>HTML</strong>：</p>
<pre><code>&lt;div&gt;
  &lt;p&gt;我是文字&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;我是文字&lt;/p&gt;
</code></pre>
<hr />
<p>&lt;font color="red"&gt;
注：后代选择器可以是任意层级，不仅仅是一层，还可以是多层。
&lt;/font&gt;</p>
<pre><code>ul li a {
  color: pink;
}
</code></pre>
<p><strong>更好的写法，结合类选择器，使选择更精确</strong></p>
<pre><code>.nav li a {
  color: pink;
}
</code></pre>
<pre><code>&lt;ul class="nav"&gt;
  &lt;li&gt;&lt;a href="#"&gt;首页&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href="#"&gt;关于我们&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href="#"&gt;联系我们&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</code></pre>
<p>这样，就可以只修改类为 <code>.nav</code> 的 <code>ul</code> 元素下的 <code>a</code> 元素的颜色，而不影响其他 <code>ul</code> 元素下的 <code>a</code> 元素。</p>
<hr />
<p><strong>说明</strong>：
选择div标签里面的p元素，中间空格隔开。
父级div作用是限定，子元素p修改样式。
父级和子集都可以是任意选择器。</p>
<hr />
<h2>子代选择器</h2>
<p>选择某个元素的<strong>直接子元素（仅限一层）</strong>。<br />
<strong>语法</strong>：</p>
<pre><code>父元素 &gt; 子元素 {
  样式声明;
  ...
}
</code></pre>
<p>选择父元素里面的第一层子元素，中间用 <code>&gt;</code> 隔开。</p>
<hr />
<p><strong>示例</strong>：</p>
<p><strong>CSS</strong>：</p>
<pre><code>div &gt; span {
  color: pink;
}
</code></pre>
<hr />
<p><strong>HTML</strong>：</p>
<pre><code>&lt;div&gt;
  &lt;span&gt;我是儿子&lt;/span&gt;
  &lt;p&gt;
    &lt;span&gt;我是孙子&lt;/span&gt;
  &lt;/p&gt;
&lt;/div&gt;
</code></pre>
<p><strong>说明</strong>：<br />
这样，就可以只修改 <code>div</code> 元素直接子元素 <code>span</code>”我是儿子“ 的颜色，而不影响 <code>p</code> 元素或 <code>p</code> 元素的子元素 <code>span</code>“我是孙子”的颜色。</p>
<hr />
<p><strong>补充</strong>：
和后代选择器类似，可以使用多个选择器组合</p>
<hr />
<h2>兄弟选择器</h2>
<p>兄弟选择器分为<strong>邻接兄弟选择器</strong>和<strong>通用兄弟选择器</strong>：</p>
<h3>邻接兄弟选择器</h3>
<p>选中紧跟在指定元素后面的第一个同级元素。<br />
<strong>语法</strong>：</p>
<pre><code>指定元素 + 同级元素 {
  样式声明;
  ...
}
</code></pre>
<hr />
<h3>通用兄弟选择器</h3>
<p>选中指定元素后面所有同级元素（不要求紧邻）。<br />
<strong>语法</strong>：</p>
<pre><code>指定元素 ~ 同级元素 {
  样式声明;
  ...
}
</code></pre>
<hr />
<p>我们用以下<strong>HTML结构</strong>来详细说明<strong>邻接兄弟选择器</strong>和<strong>通用兄弟选择器</strong>的区别。</p>
<pre><code>&lt;div class="box"&gt;
  &lt;p&gt;文字内容&lt;/p&gt;
  &lt;p&gt;文字内容&lt;/p&gt;
  &lt;p&gt;文字内容&lt;/p&gt;
  &lt;h2&gt;标题&lt;/h2&gt;
  &lt;p&gt;文字内容&lt;/p&gt;
  &lt;p&gt;文字内容&lt;/p&gt;
  &lt;p&gt;文字内容&lt;/p&gt;
&lt;/div&gt;
</code></pre>
<p>使用<strong>邻接兄弟选择器</strong>选中紧跟在h2后面的第一个p元素</p>
<pre><code>h2 + p {
  color: pink;
}
</code></pre>
<p><strong>效果</strong>：<br />
<img src="/images/posts/css_base/image-4.png" alt="alt text" /></p>
<p>使用<strong>通用兄弟选择器</strong>选中紧跟在h2后面的所有p元素</p>
<pre><code>h2 ~ p {
  color: pink;
}
</code></pre>
<p><strong>效果</strong>：<br />
<img src="/images/posts/css_base/image-5.png" alt="alt text" /></p>
<hr />
<h2>关系选择器总结</h2>
<table>
<thead>
<tr>
<th>选择器</th>
<th>语法</th>
<th>选择范围</th>
<th>实例</th>
</tr>
</thead>
<tbody>
<tr>
<td>后代选择器</td>
<td><code>A B</code></td>
<td>所有后代（跨层级）</td>
<td><code>ul li {color: pink;}</code></td>
</tr>
<tr>
<td>子代选择器</td>
<td><code>A &gt; B</code></td>
<td>直接子元素（仅一层）</td>
<td><code>div&gt;span {color: pink;}</code></td>
</tr>
<tr>
<td>邻接兄弟选择器</td>
<td><code>A + B</code></td>
<td>紧邻的下一个同级元素</td>
<td><code>h2+p {}</code> 标题后的第一个p元素</td>
</tr>
<tr>
<td>通用兄弟选择器</td>
<td><code>A ~ B</code></td>
<td>A之后的所有同级元素</td>
<td><code>h2~p {}</code> 标题后的所有p元素</td>
</tr>
</tbody>
</table>
<p><strong>后代选择器</strong>是我们最常用的。</p>
<hr />
<h1>CSS文本样式</h1>
<p>CSS文本样式用于控制网页中文字的外观，包括字体、颜色、对齐、间距等，主要分为两大类：</p>
<p><strong>字体样式</strong><br />
比如：使用哪种字体，字体大小，字体粗体、斜体，等等</p>
<p><strong>文本布局</strong><br />
比如：文本对齐、行高、字间距、首行缩进等</p>
<hr />
<p>&lt;font color="red"&gt;
注意：文字是无法直接通过CSS来更改样 式的，必须要用适合的<strong>标签来包裹它们</strong>，本质是<strong>修改标签</strong>样式，里面文字跟随样式变化
&lt;/font&gt;</p>
<hr />
<h2>字体样式</h2>
<p>给文字设置颜色、大小、粗体、装饰等效果</p>
<table>
<thead>
<tr>
<th>字体样式</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>color</code></td>
<td>设置字体颜色</td>
</tr>
<tr>
<td><code>font-family</code></td>
<td>字体族</td>
</tr>
<tr>
<td><code>font-size</code></td>
<td>字体大小</td>
</tr>
<tr>
<td><code>font-style</code></td>
<td>字体风格</td>
</tr>
<tr>
<td><code>font-weight</code></td>
<td>字体粗体</td>
</tr>
<tr>
<td><code>text-decoration</code></td>
<td>字体装饰</td>
</tr>
</tbody>
</table>
<hr />
<h3>字体颜色color</h3>
<p>最常见的颜色指定方式：使用关键字、十六进制值和 <code>rgb()</code> 值。<br />
学习可以用关键字，<strong>开发用十六进制</strong>，特殊情况用rgb。</p>
<p><strong>1. 关键字</strong><br />
这些颜色通常不会在生产环境的网站中使用，主要学习使用。<br />
比如：<code>red</code>、<code>green</code>、<code>blue</code>、<code>pink</code>等。<br />
示例代码：</p>
<pre><code>p {
  color: pink;
}
</code></pre>
<hr />
<p><strong>2. 十六进制</strong><br />
这个是开发最常用的。实际开发中，我们从设计稿直接复制即可。<br />
比如：红色 <code>#FF0000</code> 或者 <code>#F00</code>。<br />
示例代码：</p>
<pre><code>p {
  color: #f00;
}
</code></pre>
<hr />
<p><strong>3. rgb格式</strong><br />
<code>rgb()</code> 函数接受三个参数，分别表示颜色的红、绿和蓝。也是设计稿复制的。<br />
比如：红色 <code>rgb(255,0,0)</code>。<br />
示例代码：</p>
<pre><code>p {
  color: rgb(255, 0, 0);
}
</code></pre>
<hr />
<p><strong>4. rgba格式</strong><br />
<code>rgba()</code> 函数与 <code>rgb()</code> 类似，但多了一个参数表示透明度。0是完全透明，1是不透明<br />
比如：红色 <code>rgba(255,0,0, 0.3)</code> 表示红色透明度为 0.3。<br />
示例代码：</p>
<pre><code>p {
  color: rgba(255, 0, 0, 0.3);
}
</code></pre>
<hr />
<h3>字体族font-family</h3>
<p>给定一个有<strong>先后顺序</strong>的，由字体名或者字体族名组成的列表来为选定的元素设置字体</p>
<pre><code>body {
  fonot-familiy: Arial, Helvetica, sans-serif;
}
</code></pre>
<p>属性值用<strong>逗号隔开</strong>。浏览器会选择列表中第一个该计算机上有安装的单位</p>
<blockquote>
<p>在实际开发中，都是规定好的可以直接复制即可，无需记忆</p>
</blockquote>
<hr />
<h3>字体大小font-size</h3>
<p><code>font-size</code>可以设置字体大小</p>
<pre><code>p {
  font-size: 18px;
}
</code></pre>
<p><strong>像素px</strong></p>
<blockquote>
<hr />
<p>CSS像素(CSS Pixel)是CSS中用于定义长度，尺寸的单位(简写为px)<br />
<strong>绝对单位</strong></p>
</blockquote>
<blockquote>
<p>不同浏览器默认字体大小不一样，谷歌默认16px，建议给body标签统一指定大小，做到浏览器统一</p>
</blockquote>
<hr />
<h3>字体风格font-style</h3>
<p>font-style用来打开和关闭文本italic(斜体)<br />
<strong>属性值</strong>：</p>
<ul>
<li><code>normal</code>：将文本设置为普通字体(将存在的斜体关闭)</li>
<li><code>italic</code>：如果当前字体的斜体版本可用，那么文本设置为斜体版本</li>
</ul>
<pre><code>p {
  font-style: italic; /* 设置斜体样式 */
}
</code></pre>
<p><strong>最常见</strong>：让em或者i标签<strong>取消默认倾斜</strong></p>
<pre><code>em {
  font-style: normal;
}

i {
  font-style: normal;
}
</code></pre>
<h3>字体粗体font-weight</h3>
<p><code>font-weight</code>设置文字的粗体大小</p>
<p><strong>使用场景</strong>:</p>
<blockquote>
<ol>
<li>很多标题是不要加粗的，此时可以用CSS取消加粗</li>
<li>部分大批量文字可以利用CSS样式加粗</li>
</ol>
</blockquote>
<hr />
<p><strong>属性值</strong>：</p>
<ul>
<li>normal：正常粗细</li>
<li>bold：加粗
其他值受属性影响基本不用</li>
</ul>
<hr />
<p><strong>数字属性值</strong>（常用）：</p>
<ul>
<li>400：正常粗细</li>
<li>700：加粗
取值范围100到900之间，常用就是400和700</li>
</ul>
<hr />
<p><strong>示例</strong>：</p>
<pre><code>p {
  font-weight: 700;
}
</code></pre>
<h3>字体装饰text-decoration</h3>
<p>设置/取消字体上的文本装饰</p>
<blockquote>
<p><strong>使用场景</strong>：</p>
<ol>
<li>最常见设置链接下划线，比如取消下划线等</li>
<li>特殊情况添加删除线</li>
</ol>
</blockquote>
<hr />
<p><strong>属性值</strong>：</p>
<ul>
<li>none:取消文本装饰</li>
<li>underline:下划线</li>
<li>overline:上划线</li>
<li>line-through:穿过文本的线</li>
</ul>
<hr />
<p><strong>示例</strong>：</p>
<pre><code>a {
  text-decoration: none;
}
</code></pre>
<h3>字体样式总结</h3>
<table>
<thead>
<tr>
<th>属性</th>
<th>描述</th>
<th>常见取值</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>color</code></td>
<td>设置文本内容的颜色</td>
<td>颜色名称(red, blue)&lt;br/&gt;六进制(#F00000)&lt;br/&gt;RGB(rgb(255,0,0))&lt;br/&gt;RGBA(rgba(255,0,0,0.5))</td>
<td><code>color: red;</code>&lt;br&gt;<code>color: #3366cc;</code></td>
</tr>
<tr>
<td><code>font-family</code></td>
<td>设置文本的字体系列</td>
<td>无衬线字体</td>
<td><code>font-family: "Microsoft YaHei", sans-serif;</code></td>
</tr>
<tr>
<td><code>font-size</code></td>
<td>设置文本的大小</td>
<td><code>px</code></td>
<td><code>font-size: 16px;</code></td>
</tr>
<tr>
<td><code>font-style</code></td>
<td>设置文本的样式</td>
<td><code>normal</code>(正常)、<code>italic</code>(斜体)</td>
<td><code>font-style: normal;</code></td>
</tr>
<tr>
<td><code>font-weight</code></td>
<td>设置文本的粗细</td>
<td>数值(100-900)&lt;br/&gt;关键字(<code>normal</code>, <code>bold</code>)</td>
<td><code>font-weight: 400;</code>&lt;br&gt;<code>font-weight: 700;</code></td>
</tr>
<tr>
<td><code>text-decoration</code></td>
<td>设置文本的装饰效果</td>
<td><code>none</code>(无装饰)&lt;br/&gt;<code>underline</code>(下划线)&lt;br/&gt;<code>overline</code>(上划线)&lt;br/&gt;<code>line-through</code>(中划线)</td>
<td><code>text-decoration: underline;</code>&lt;br&gt;<code>text-decoration: line-through;</code></td>
</tr>
</tbody>
</table>
<hr />
<h2>文本布局</h2>
<p>作用域文本的对齐，缩进，间距等布局功能的属性</p>
<h3>文本布局样式表</h3>
<table>
<thead>
<tr>
<th>字体样式</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>text-align</code></td>
<td>文本对齐</td>
</tr>
<tr>
<td><code>text-indent</code></td>
<td>首行缩进</td>
</tr>
<tr>
<td><code>letter-spacing</code></td>
<td>文本字符间距</td>
</tr>
<tr>
<td><code>line-height</code></td>
<td>行高</td>
</tr>
</tbody>
</table>
<hr />
<h3>文本对齐text-align</h3>
<p>控制文本在它所在的块级盒子内<strong>如何水平对齐</strong></p>
<blockquote>
<p><strong>使用场景</strong>：</p>
<ol>
<li>文本/图片在盒子<strong>水平对齐</strong></li>
<li>文章文字<strong>两端对齐</strong></li>
</ol>
</blockquote>
<p><img src="/images/posts/css_base/image-6.png" alt="alt text" /></p>
<p><strong>属性值</strong>：</p>
<ul>
<li>left:文本左对齐</li>
<li>right:文本右对齐</li>
<li>center:文本水平居中对齐</li>
<li>justify:自动改变字间距，两端对齐</li>
</ul>
<p><strong>示例</strong>：</p>
<pre><code>p {
  text-align: center; /* 居中对齐 */
}
</code></pre>
<hr />
<h3>首行缩进text-indent</h3>
<p>设置<strong>块级元素</strong>中文本行前面空格（<strong>缩进</strong>）的长度</p>
<blockquote>
<p><strong>使用场景</strong>：</p>
<ol>
<li>段落首行缩进两个字的效果</li>
<li>logo隐藏文字效果(后续讲解)</li>
</ol>
</blockquote>
<p><strong>属性值</strong>：<code>数字</code><br />
常见单位是em，相对单位，<strong>本元素的文字大小</strong>1em等于当前元素的字体大小，如果当前元素没有大小，则按照父元素文字大小</p>
<p><strong>示例</strong>：</p>
<pre><code>p {
  text-indent: 2em; /* 缩进2个文字大小 */
}
</code></pre>
<hr />
<h3>字间距letter-spacing</h3>
<p>用于设置文本字符的间距</p>
<p><strong>使用场景</strong>：</p>
<ol>
<li>调整字与字间距，用户体验更好</li>
</ol>
<p><strong>属性值</strong>：<code>数字</code>
常见单位是px ，默认是0px</p>
<p><strong>示例</strong>：</p>
<pre><code>p {
  letter-spacing: 2px; /* 字间距2px */
}
</code></pre>
<hr />
<h3>行高line-height</h3>
<p>设置文本每行的高</p>
<p><strong>使用场景</strong>：</p>
<ol>
<li>设置多行文本之间的<strong>上下间距</strong>。</li>
<li>让单行文本垂直居中。</li>
</ol>
<p><strong>常用属性值</strong>：</p>
<ul>
<li>数字px</li>
<li>数字不带单位(当前字体大小的倍数)</li>
</ul>
<p><strong>示例</strong>：</p>
<pre><code>p {
  line-height: 24px; /* 行高24px */
}
</code></pre>
<hr />
<p><strong>单行文本垂直居中原理</strong>：<br />
<strong>行高组成</strong>：</p>
<ol>
<li>上间距</li>
<li>字体大小</li>
<li>下间距</li>
</ol>
<p><img src="/images/posts/css_base/image-7.png" alt="alt text" /></p>
<p><strong>行高等于盒子高度，即可实现垂直居中</strong></p>
<hr />
<h3>文本布局总结</h3>
<table>
<thead>
<tr>
<th>属性名</th>
<th>作用</th>
<th>核心取值</th>
<th>注意事项</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>text-align</code></td>
<td>设置文本的水平对齐方式</td>
<td><code>left</code>、<code>right</code>、<code>center</code>、<code>justify</code>（两端对齐）</td>
<td>仅对块级元素生效</td>
<td><code>p { text-align: center; }</code></td>
</tr>
<tr>
<td><code>text-indent</code></td>
<td>设置首行文本的缩进量</td>
<td>长度值（如 20px、2em）</td>
<td>仅对块级元素生效</td>
<td><code>p { text-indent: 2em; }</code></td>
</tr>
<tr>
<td><code>letter-spacing</code></td>
<td>设置文本中字符之间的间距</td>
<td>数值</td>
<td>负值可使字符重叠（需谨慎使用）</td>
<td><code>h1 { letter-spacing: 2px; }</code></td>
</tr>
<tr>
<td><code>line-height</code></td>
<td>设置文本行与行之间的垂直间距</td>
<td>像素或者倍数</td>
<td>1.5 表示当前字体大小1.5倍；行高等于容器高时，单行文字垂直居中</td>
<td><code>p { line-height: 1.6; }</code></td>
</tr>
</tbody>
</table>
<hr />
<h2>font简写</h2>
<p>font简写属性在一个声明中设置多个字体属性</p>
<p><strong>使用场景</strong>：</p>
<ol>
<li>给整个页面设置相关字体样式</li>
</ol>
<p><strong>语法</strong>：</p>
<pre><code>font: font-style font-weight font-size/line-height font-family;
</code></pre>
<ul>
<li>其中font-size和font-family必须写</li>
<li>其他可以省略，默认显示</li>
<li>有严格的书写顺序</li>
</ul>
<p>忘了属性请跳转到<a href="#%E5%AD%97%E4%BD%93%E6%A0%B7%E5%BC%8F%E6%80%BB%E7%BB%93">字体样式总结</a></p>
<p><strong>示例</strong>：</p>
<pre><code>p {
  font: italic bold 16px/24px "微软雅黑";
}
</code></pre>
<pre><code>p {
  font: 18px/1.5 "宋体";
}
</code></pre>
<hr />
<h2>CSS继承性</h2>
<p>子元素自动<strong>继承</strong>父元素某些CSS样式属性。</p>
<ul>
<li>主要继承跟文字相关的样式属性，比如字体，颜色，文本对齐等</li>
<li>如果子元素定义自己样式，会<strong>优先自己</strong>样式，不会继承父元素样式</li>
</ul>
<hr />
<h1>分组选择器</h1>
<p>是将不同的选择器<strong>组合</strong>在一起，使用<strong>逗号分割</strong><br />
也称为<strong>并集选择器</strong></p>
<p><strong>使用场景</strong>：</p>
<ol>
<li>给多个元素设置相同的样式</li>
</ol>
<p><strong>语法</strong>：</p>
<pre><code>选择器1,
选择器2,
选择器3 {
  /* 样式声明 */
}
</code></pre>
<p><strong>示例</strong>：</p>
<pre><code>p,
h1,
h2 {
  color: red; /* 所有p、h1、h2元素文字颜色都是红色 */
}
</code></pre>
<hr />
<h1>伪类选择器</h1>
<p>选择元素的<strong>特定状态</strong>或<strong>结构位置</strong>，符号是冒号<code>:</code></p>
<p><strong>对比</strong>：<br />
类选择器：<code>.类名</code><br />
伪类选择器：<code>:伪类名</code></p>
<p><strong>分类</strong>：</p>
<ol>
<li><strong>状态伪类</strong>
<ul>
<li>根据用户交互或状态变化选择</li>
<li>比如：鼠标经过链接，表单获得焦点等</li>
</ul>
</li>
<li><strong>结构伪类</strong>
<ul>
<li>根据元素的位置选择</li>
<li>比如：选择第五个第十个元素，选择第三个元素等</li>
</ul>
</li>
<li><strong>表单伪类</strong>
<ul>
<li>针对表单元素的状态选择</li>
<li>比如：表单禁用，选中复选框等</li>
</ul>
</li>
</ol>
<hr />
<h2>状态伪类选择器</h2>
<h3>链接伪类</h3>
<p>链接伪类用于根据链接的<strong>不同状态</strong>（如未访问，悬停，点击等）为其添加样式，从而提升用户体验和界面交互性。</p>
<hr />
<p><strong>使用场景</strong>：</p>
<ol>
<li>设置链接不同状态的样式</li>
</ol>
<p><img src="/images/posts/css_base/image-8.png" alt="alt text" /></p>
<p><img src="/images/posts/css_base/image-9.png" alt="alt text" /></p>
<hr />
<p><strong>链接伪类使用</strong>：</p>
<table>
<thead>
<tr>
<th>链接伪类</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>a:link</code></td>
<td>未访问链接的默认样式</td>
</tr>
<tr>
<td><code>a:visited</code></td>
<td>已访问链接的样式</td>
</tr>
<tr>
<td><code>a:hover</code></td>
<td>鼠标悬浮在链接上时的反馈</td>
</tr>
<tr>
<td><code>a:active</code></td>
<td>链接被点击时的瞬时状态（按下到松开）</td>
</tr>
</tbody>
</table>
<p><strong>伪类顺序规则（LVHA 顺序）</strong>
<code>a:link -&gt; a:visited -&gt; a:hover -&gt; a:active</code></p>
<p><strong>注意事项</strong>：</p>
<ul>
<li>链接伪类的顺序不能颠倒，否则会导致样式失效</li>
<li>链接伪类可以与其他选择器组合使用，比如：<code>p a:hover</code>表示段落中的悬停链接</li>
</ul>
<hr />
<p><strong>示例</strong>：</p>
<pre><code>a:link {
  color: blue; /* 未访问链接为蓝色 */
}

a:visited {
  color: purple; /* 已访问链接为紫色 */
}

a:hover {
  color: red; /* 鼠标悬停链接为红色 */
}

a:active {
  color: green; /* 链接被点击时为绿色 */
}
</code></pre>
<hr />
<p><strong>实际开发中，一般这样写</strong>：</p>
<p><strong>淘宝</strong></p>
<pre><code>a {
  text-decoration: none;
}
a:hover {
  text-decoration: underline;
}
</code></pre>
<hr />
<p><strong>京东</strong></p>
<pre><code>a {
  color: #666;
  text-decoration: none;
}
a:hover {
  color: #ff0f23;
}
</code></pre>
<hr />
<p><strong>小米</strong></p>
<pre><code>a {
  color: #757575;
}
a,
a:hover {
  text-decoration: none;
}
a:hover {
  color: #ff6700;
}
</code></pre>
<hr />
<h3>用户行为伪类</h3>
<p>用户以某种方式和文档<strong>交互</strong>的时候使用，这些用户行为伪类，有时叫做<strong>动态伪类</strong>。</p>
<p><strong>使用场景</strong>：</p>
<ol>
<li>鼠标经过元素的时候，修改样式</li>
<li>表单获得焦点的时候，修改样式</li>
</ol>
<p><img src="/images/posts/css_base/image-10.png" alt="alt text" /></p>
<hr />
<p><strong>用户行为伪类使用</strong>：</p>
<table>
<thead>
<tr>
<th>用户行为伪类</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>:hover</code></td>
<td>鼠标经过元素</td>
</tr>
<tr>
<td><code>:focus</code></td>
<td>获得焦点的元素（表单）</td>
</tr>
</tbody>
</table>
<p><strong>示例</strong>：</p>
<p><strong>鼠标经过元素</strong></p>
<pre><code>div {
  background-color: pink;
}
div:hover {
  background-color: red;
  color: red;
  /* 鼠标经过元素，背景颜色和文字颜色都变成红色 */
}
</code></pre>
<p><strong>获得焦点的元素（表单）</strong></p>
<pre><code>input {
  border: 1px solid #ccc;
}
input:focus {
  border: 2px solid red;
  width: 200px;
  /* 表单获得焦点时，边框变成红色,宽度变成200px */
}
</code></pre>
<hr />
<h2>结构伪类选择器</h2>
<p>根据<strong>元素的位置</strong>选择目标元素。</p>
<p><strong>使用场景</strong>：</p>
<ol>
<li>选择第一个或者最后一个或者第n个元素</li>
</ol>
<p><img src="/images/posts/css_base/image-11.png" alt="alt text" /><br />
每个元素都有左竖线，但是第一个元素的被去掉了，这就是伪类选择器的作用。</p>
<hr />
<p><strong>结构伪类选择器使用</strong>：</p>
<table>
<thead>
<tr>
<th>结构伪类</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>:first-child</code></td>
<td>一组兄弟元素中的第一个元素</td>
</tr>
<tr>
<td><code>:last-child</code></td>
<td>一组兄弟元素中的最后一个元素</td>
</tr>
<tr>
<td><code>:nth-child(n)</code></td>
<td>一组兄弟元素列表中第n个元素</td>
</tr>
</tbody>
</table>
<hr />
<p>对于<code>nth-child(n)</code>，n可以是：</p>
<ul>
<li>数字</li>
<li>关键字：<code>even</code>（偶数），<code>odd</code>（奇数）</li>
<li>公式：
<ul>
<li><code>3n</code>：3的倍数，表示第3个、第6个、第9个...等元素</li>
<li><code>n+2</code>：表示第二个及以后的元素</li>
<li><code>-n+3</code>：表示前三个元素</li>
<li>注意公式的n取值从<code>0</code>开始计算</li>
</ul>
</li>
</ul>
<hr />
<p><strong>示例</strong>：</p>
<pre><code>ul li:first-child {
  color: pink;
  /* 第一个li元素的字体颜色为粉色 */
}

ul li:nth-child(5) {
  color: blue;
  /* 第五个li元素的字体颜色为蓝色 */
}

ul li:last-child {
  color: green;
  /* 最后一个li元素的字体颜色为绿色 */
}
</code></pre>
<hr />
<h2>表单伪类选择器</h2>
<p>针对表单元素的状态</p>
<p><strong>使用场景</strong>：</p>
<ol>
<li>表单按钮禁用的时候，调整颜色</li>
<li>复选框选中的修改样式</li>
</ol>
<p><img src="/images/posts/css_base/image-12.png" alt="alt text" /></p>
<p><img src="/images/posts/css_base/image-13.png" alt="alt text" /></p>
<hr />
<p><strong>表单伪类选择器使用</strong>：</p>
<table>
<thead>
<tr>
<th>表单伪类选择器</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>:disabled</code></td>
<td>禁用的表单元素</td>
</tr>
<tr>
<td><code>:checked</code></td>
<td>选中的复选框或单选框</td>
</tr>
</tbody>
</table>
<hr />
<p><strong>示例</strong>：</p>
<p><strong>禁用的表单元素</strong></p>
<pre><code>button:disabled {
  background-color: #ccc;
  color: #666;
  cursor: not-allowed;
  /* 禁用的按钮背景颜色为灰色，文字颜色为深灰色，鼠标悬停时显示不能点击的图标 */
}
</code></pre>
<pre><code>&lt;button type="submit" disabled&gt;提交&lt;/button&gt; /*
提交按钮被禁用时，背景颜色为灰色，文字颜色为深灰色，鼠标悬停时显示不能点击的图标
*/
</code></pre>
<p><strong>选中的复选框或单选框</strong></p>
<pre><code>input:checked + label {
  font-weight: bold;
  color: red;
  /* 复选框1被选中时，文字加粗，颜色为红色 */
}
</code></pre>
<pre><code>&lt;input type="checkbox" id="checkbox1" /&gt;
&lt;label for="checkbox1"&gt;复选框1&lt;/label&gt;
/* 复选框1被选中时，文字加粗，颜色为红色 */
</code></pre>
<hr />
<h1>伪元素选择器</h1>
<p>选择元素特定的部分（使用双冒号<code>::</code>）</p>
<p><strong>对比</strong>:</p>
<ul>
<li>类选择器：选择元素的某个类名（<code>.nav</code>）</li>
<li>伪类选择器：选择元素的某个状态（<code>:hover</code>）</li>
<li>伪元素选择器：选择元素的某个部分（<code>::before</code>）</li>
</ul>
<p><strong>使用场景</strong>：</p>
<ol>
<li>让表单里面的<code>placeholder</code>文字变颜色</li>
<li>做更多的修饰效果</li>
</ol>
<hr />
<p><strong>伪元素选择器使用</strong>：</p>
<ol>
<li>选择特定部分</li>
<li>插入内容</li>
</ol>
<hr />
<h2>1.选择特定部分</h2>
<table>
<thead>
<tr>
<th>伪元素选择器</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>::first-line</code></td>
<td>选择首行</td>
</tr>
<tr>
<td><code>::first-letter</code></td>
<td>首字母</td>
</tr>
<tr>
<td><code>::placeholder</code></td>
<td>选择input或者textarea占位符（常用）</td>
</tr>
</tbody>
</table>
<p><strong>示例</strong>：</p>
<pre><code>input::placeholder {
  color: #bbb;
  line-height: 30px;
  /* input或者textarea占位符文字颜色为灰色, 行高为30px */
}
</code></pre>
<hr />
<h2>2.插入内容</h2>
<p>本质上插入的是盒子模型中的内容区域（content area）</p>
<table>
<thead>
<tr>
<th>伪元素选择器</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>::before</code></td>
<td>元素内插入伪元素，作为第一个元素</td>
</tr>
<tr>
<td><code>::after</code></td>
<td>元素内插入伪元素，作为最后一个元素</td>
</tr>
</tbody>
</table>
<p><strong>示例</strong>：</p>
<pre><code>div::before {
  content: "开始";
  color: red;
  background-color: #eee;
  padding: 5px;
  /* 元素内插入伪元素，作为第一个元素，内容为"开始"，颜色为红色，背景颜色为浅灰色，内边距为5px */
}

div::after {
  content: "结束";
  color: red;
  background-color: #eee;
  padding: 5px;
  /* 元素内插入伪元素，作为最后一个元素，内容为"结束"，颜色为红色，背景颜色为浅灰色，内边距为5px */
}
</code></pre>
<hr />
<h1>属性选择器</h1>
<p>根据元素的<strong>属性特征</strong>来精准定位元素，从而实现更灵活的样式控制<br />
<strong>使用场景</strong>：</p>
<ol>
<li>表单样式控制</li>
<li>图标字体控制</li>
<li>国际化语言适配等等</li>
</ol>
<p><strong>属性选择器分类</strong>：</p>
<table>
<thead>
<tr>
<th>属性选择器</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>[属性]</code></td>
<td>匹配包含指定属性的元素</td>
</tr>
<tr>
<td><code>[属性=值]</code></td>
<td>匹配属性值等于指定值的元素</td>
</tr>
<tr>
<td><code>[属性^=值]</code></td>
<td>匹配属性值以指定字符串开头的元素</td>
</tr>
<tr>
<td><code>[属性$=值]</code></td>
<td>匹配属性值以指定字符串结尾的元素</td>
</tr>
<tr>
<td><code>[属性*=值]</code></td>
<td>匹配属性值任意位置包含指定子串的元素</td>
</tr>
</tbody>
</table>
<p><strong>示例</strong>：</p>
<pre><code>/* 属性选择器 */
/* 1. 选择包含属性（所有带class属性的a标签） */
a[class] {
  color: #333;
}

/* 2. 选择属性等于值 完全匹配（class="red"的a标签） */
a[class="red"] {
  color: red;
}

/* 3. 选择属性值以xx开头（class以"font"开头的a标签） */
a[class^="font"] {
  color: blue;
}

/* 4. 选择属性值以xx结尾（class以"16"结尾的a标签） */
a[class$="16"] {
  color: purple;
}

/* 5. 选择属性包含xx（class包含"on"的a标签，此处匹配"font"） */
a[class*="font"] {
  font-weight: bold;
}
</code></pre>
<p><strong>HTML结构</strong>：</p>
<pre><code>&lt;a href="#"&gt;修改文字颜色&lt;/a&gt; &lt;br /&gt;
&lt;!-- 注释：所有带class属性的a标签文字颜色为#333 --&gt;
&lt;a href="#" class="font"&gt;修改文字颜色&lt;/a&gt; &lt;br /&gt;
&lt;!-- 注释：class="font"的a标签文字颜色为蓝色 --&gt;
&lt;a href="#" class="font14"&gt;修改文字颜色&lt;/a&gt; &lt;br /&gt;
&lt;!-- 注释：class="font14"的a标签文字颜色为紫色 --&gt;
&lt;a href="#" class="font16"&gt;修改文字颜色&lt;/a&gt; &lt;br /&gt;
&lt;!-- 注释：class="font16"的a标签文字颜色为紫色 --&gt;
&lt;a href="#" class="red"&gt;修改文字颜色&lt;/a&gt; &lt;br /&gt;
&lt;!-- 注释：class="red"的a标签文字颜色为红色 --&gt;
</code></pre>
<hr />
<h1>CSS三大特性</h1>
<h2>继承性</h2>
<p>子元素继承父元素样式<br />
主要是跟文字相关的样式继承<br />
如果子元素设置了自己的样式，就会覆盖继承的样式（因为继承的权重是<code>0,0,0,0</code>，详情请看优先级）</p>
<hr />
<h2>层叠性</h2>
<p>后面样式会覆盖前面样式<br />
主要解决<strong>样式冲突</strong>问题<br />
但是要看<strong>选择器权重</strong>来决定优先级</p>
<hr />
<h2>优先级</h2>
<p>浏览器通过优先级来判断哪些属性值与一个元素值相关，优先级是基于不同种类<strong>选择器</strong>组成的<strong>匹配规则</strong>。<br />
优先级由<strong>选择器</strong>的权重决定，权重高的规则<strong>覆盖</strong>权重低的规则</p>
<h3>原则</h3>
<ol>
<li>优先级相等的时候，CSS中<strong>最后</strong>的那个声明的样式将会被应用到元素上。（层叠就近原则）</li>
<li>其余判断哪个选择器<strong>权重</strong>高，就优先执行那个样式</li>
<li>权重是4位一组，是分开的层级，不能进位。</li>
</ol>
<table>
<thead>
<tr>
<th>选择器类型</th>
<th>示例</th>
<th>权重值</th>
<th>优先级说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>!important</code></td>
<td><code>color: red !important;</code></td>
<td>无限大</td>
<td>强制覆盖所有规则（慎用）</td>
</tr>
<tr>
<td>内联样式</td>
<td><code>&lt;div style="color: red"&gt;</code></td>
<td><code>(1, 0, 0, 0)</code></td>
<td>行内样式权重最高 <code>(1,0,0,0)</code></td>
</tr>
<tr>
<td>ID 选择器</td>
<td><code>#myId</code></td>
<td><code>(0, 1, 0, 0)</code></td>
<td>每个 ID 增加 <code>0,1,0,0</code></td>
</tr>
<tr>
<td>类/属性/伪类</td>
<td><code>.class, [type="text"]</code></td>
<td><code>(0, 0, 1, 0)</code></td>
<td>每个类/属性/伪类增加 <code>0,0,1,0</code></td>
</tr>
<tr>
<td>类型（标签）/伪元素</td>
<td><code>div, ::after</code></td>
<td><code>(0, 0, 0, 1)</code></td>
<td>每个标签/伪元素增加 <code>0,0,0,1</code></td>
</tr>
<tr>
<td>通配符/继承</td>
<td><code>*</code>, 继承的样式</td>
<td><code>(0, 0, 0, 0)</code></td>
<td>通配符和继承权重最低</td>
</tr>
</tbody>
</table>
<h3>权重累加</h3>
<p>权重是累加的，每个选择器的层级权重相加，最终得到一个4位的权重值。</p>
<p><strong>示例</strong>：#nav .item a 的权重为：<br />
(0,1,0,0) + (0,0,1,0) + (0,0,0,1) = (0,1,1,1)</p>
<p><strong>tip</strong>:查看权重可以使用trae鼠标悬停在选择器上查看，也可以通过浏览器的调试工具查看。</p>
<hr />
<h1>盒子模型</h1>
<p>所有的元素都被一个个的“盒子”包围着，学会盒子模型是实现<strong>准确布局</strong>，处理<strong>元素排列</strong>的关键。</p>
<p>在CSS中，我们有几种类型的盒子，一般分为<strong>区块盒子</strong>（block boxes）和<strong>行内盒子</strong>（inline boxes）。</p>
<p><strong>区块盒子</strong>：</p>
<ul>
<li>盒子会产生换行</li>
<li>width和height属性可以发挥作用</li>
<li>不设置宽度则默认和是父元素空间的100%</li>
<li>内边距，外边距和边框会撑大元素</li>
<li>常见的比如：div，p，h，ul，table等</li>
</ul>
<p><strong>行内盒子</strong>：</p>
<ul>
<li>盒子不会产生换行</li>
<li>width和height属性将<strong>不起作用</strong></li>
<li><strong>垂直方向</strong>的内边距，外边距不起效果</li>
<li><strong>水平方向</strong>的内边距，外边距会有效果</li>
<li>常见的比如：span，em，a，strong等</li>
</ul>
<h2>盒子模型组成</h2>
<p>CSS盒模型整体上适用于<strong>区块盒子</strong>，包含盒子<strong>内容</strong>，<strong>内边距</strong>，<strong>外边距</strong>，<strong>边框</strong>四部分。</p>
<ul>
<li><strong>盒子内容</strong>：显示内容的区域，由内容或者指定宽度高度来决定内容大小</li>
<li><strong>内边距 padding</strong>：元素内容与边框之间的距离，由padding属性设置</li>
<li><strong>外边距 margin</strong>：元素边框与其他元素之间的距离，由margin属性设置</li>
<li><strong>边框 border</strong>：元素边框，由border属性设置</li>
</ul>
<p><img src="/images/posts/css_base/image-14.png" alt="alt text" /></p>
<p>使用调试工具可以查看盒子模型的组成
<img src="/images/posts/css_base/image-15.png" alt="alt text" /></p>
<hr />
<h3>盒子模型组成-边框</h3>
<p>border属性用于设置盒子边框</p>
<h4>使用场景一</h4>
<ol>
<li>设置盒子四条或者单独边框</li>
</ol>
<p><strong>属性值</strong>：<br />
border: 边框粗细 边框样式 边框颜色</p>
<ul>
<li>边框有三部分属性值组成，中间必须用<strong>空格</strong>隔开</li>
<li>三部分属性值没有先后顺序</li>
</ul>
<p><strong>边框样式表</strong>：</p>
<table>
<thead>
<tr>
<th>边框样式</th>
<th>描述</th>
<th>视觉效果</th>
</tr>
</thead>
<tbody>
<tr>
<td>dotted</td>
<td>点状边框</td>
<td>圆点组成的虚线</td>
</tr>
<tr>
<td>dashed</td>
<td>虚线边框</td>
<td>短横线组成的虚线</td>
</tr>
<tr>
<td>solid</td>
<td>实线边框</td>
<td>连续的实线</td>
</tr>
<tr>
<td>double</td>
<td>双线边框</td>
<td>两条实线组成的边框</td>
</tr>
<tr>
<td>groove</td>
<td>凹槽边框</td>
<td>3D凹槽效果</td>
</tr>
<tr>
<td>ridge</td>
<td>垄状边框</td>
<td>3D垄状效果</td>
</tr>
<tr>
<td>inset</td>
<td>内凹边框</td>
<td>3D内凹效果</td>
</tr>
<tr>
<td>outset</td>
<td>外凹边框</td>
<td>3D外凹效果</td>
</tr>
</tbody>
</table>
<p><strong>示例</strong>：</p>
<pre><code>div {
  border: 1px solid #f1f1f1;
}
</code></pre>
<h4>使用场景二</h4>
<ol>
<li>四个边框不同</li>
<li>底部一条线做分割线</li>
</ol>
<p><strong>属性</strong>：<br />
根据方位名词。top,bottom,left,right</p>
<ul>
<li>border-top: 1px solid #f1f1f1;</li>
<li>border-bottom: 1px solid #f1f1f1;</li>
<li>border-left: 1px solid #f1f1f1;</li>
<li>border-right: 1px solid #f1f1f1;</li>
</ul>
<p><strong>示例</strong>：</p>
<pre><code>div {
  border-top: 3px solid #ff9900;
  border-bottom: 1px solid pink;
  border-left: 1px solid pink;
  border-right: 1px solid pink;
}
</code></pre>
<hr />
<h3>盒子模型组成-圆角边框</h3>
<p><code>border-radius</code>属性用于设置盒子圆角边框</p>
<p><strong>使用场景</strong>：</p>
<ol>
<li>盒子设置圆角更好看</li>
<li>盒子或者图片设置为圆形</li>
</ol>
<p><strong>属性值</strong>：<br />
border-radius: 圆角大小</p>
<p><strong>示例</strong>：</p>
<pre><code>div {
  border-radius: 10px;
}
</code></pre>
<hr />
<p><strong>特殊场景一</strong>：</p>
<ol>
<li>胶囊圆角</li>
<li>盒子或者图片设置为圆形</li>
</ol>
<p><strong>实现</strong>：</p>
<ul>
<li>胶囊圆角：长方形将圆角设置为<strong>盒子高度的一半</strong></li>
<li>圆形：正方形将圆角设置为<strong>盒子宽度的一半</strong>或者<strong>50%</strong></li>
</ul>
<p><strong>示例</strong>：</p>
<pre><code>/* 胶囊 */
div {
  width: 200px;
  height: 100px;
  border-radius: 50px;
}

/* 圆形 */
div {
  width: 200px;
  height: 200px;
  border-radius: 50%;
}
</code></pre>
<hr />
<p><strong>特殊场景二</strong>：</p>
<ol>
<li>分别设置不同角的圆角大小</li>
</ol>
<table>
<thead>
<tr>
<th>圆角写法</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>border-radius: 10px;</td>
<td>所有角都设置为10px</td>
</tr>
<tr>
<td>border-radius: 10px 20px;</td>
<td>左上，右下是10px，右上，左下是20px</td>
</tr>
<tr>
<td>border-radius: 10px 20px 30px;</td>
<td>左上是10px, 右上，左下是20px，右下是30px</td>
</tr>
<tr>
<td>border-radius: 10px 20px 30px 40px;</td>
<td>左上是10px, 右上是20px，右下是30px，左下是40px</td>
</tr>
</tbody>
</table>
<hr />
<p><strong>圆角原理图示</strong>：<br />
<img src="/images/posts/css_base/image-16.png" alt="alt text" /></p>
<hr />
<h3>盒子模型组成-内边距</h3>
<p><code>padding</code>属性用于设置盒子内边距，内边距是内容与边框之间的距离。</p>
<p><strong>使用场景</strong>：</p>
<ol>
<li>让盒子内容和边框保留一定距离，更美观</li>
</ol>
<h4>内边距写法（多值规则：顺时针记忆）</h4>
<table>
<thead>
<tr>
<th>内边距写法</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>padding: 10px;</code></td>
<td>上下左右4个内边距均为10px</td>
</tr>
<tr>
<td><code>padding: 10px 20px;</code></td>
<td>上下内边距为10px，左右内边距为20px</td>
</tr>
<tr>
<td><code>padding: 10px 20px 30px;</code></td>
<td>上内边距10px，左右内边距20px，下内边距30px</td>
</tr>
<tr>
<td><code>padding: 10px 20px 30px 40px;</code></td>
<td>上10px → 右20px → 下30px → 左40px（顺时针顺序）</td>
</tr>
</tbody>
</table>
<p><strong>示例</strong>：</p>
<pre><code>div {
  padding: 10px;
}
</code></pre>
<hr />
<h4>单边内边距设置</h4>
<p>可通过方位名词单独设置某一侧的内边距：</p>
<ul>
<li><code>padding-left: 10px;</code> 设置左内边距</li>
<li><code>padding-right: 10px;</code> 设置右内边距</li>
<li><code>padding-top: 10px;</code> 设置上内边距</li>
<li><code>padding-bottom: 10px;</code> 设置下内边距</li>
</ul>
<p><strong>示例</strong>：</p>
<pre><code>div {
  padding-left: 10px;
}
</code></pre>
<hr />
<h3>盒子模型组成-外边距</h3>
<p>外边距<code>margin</code>是盒子周围一圈看不到的空间，它会把其他元素退离盒子</p>
<p><strong>使用场景</strong>：</p>
<ol>
<li>让盒子与其他元素之间有一定距离，更美观</li>
</ol>
<table>
<thead>
<tr>
<th>外边距写法</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>margin: 10px;</code></td>
<td>上下左右4个外边距均为10px</td>
</tr>
<tr>
<td><code>margin: 10px 20px;</code></td>
<td>上下外边距为10px，左右外边距为20px</td>
</tr>
<tr>
<td><code>margin: 10px 20px 30px;</code></td>
<td>上外边距10px，左右外边距20px，下外边距30px</td>
</tr>
<tr>
<td><code>margin: 10px 20px 30px 40px;</code></td>
<td>上10px → 右20px → 下30px → 左40px（顺时针顺序）</td>
</tr>
</tbody>
</table>
<p><strong>示例</strong>：</p>
<pre><code>div {
  margin: 10px;
}
</code></pre>
<hr />
<h4>单边外边距设置</h4>
<p>可通过方位名词单独设置某一侧的外边距：</p>
<ul>
<li><code>margin-left: 10px;</code> 设置左外边距</li>
<li><code>margin-right: 10px;</code> 设置右外边距</li>
<li><code>margin-top: 10px;</code> 设置上外边距</li>
<li><code>margin-bottom: 10px;</code> 设置下外边距</li>
</ul>
<p><strong>示例</strong>：</p>
<pre><code>div {
  margin-left: 10px;
}
</code></pre>
<hr />
<p><strong>注意事项</strong>：</p>
<ol>
<li><strong>行内元素</strong>：左右外边距生效，上下外边距无效。</li>
<li><strong>行内元素</strong>：设置宽度和高度也无效。</li>
</ol>
<hr />
<h3>盒子模型-尺寸计算</h3>
<p>在 CSS 盒子模型的默认定义（标准盒模型）中：</p>
<ul>
<li>元素的 <code>width</code>/<code>height</code> 仅指<strong>内容区域</strong>的大小。</li>
<li><code>padding</code>（内边距）和 <code>border</code>（边框）会<strong>额外增加</strong>盒子的总尺寸。</li>
</ul>
<p>实际宽度计算示例</p>
<ul>
<li>
<p>基础情况：</p>
<ul>
<li>内容宽度：<code>200px</code></li>
<li>无 padding、无 border</li>
<li><strong>盒子实际宽度 = 200px</strong></li>
</ul>
</li>
<li>
<p>带 padding 和 border 的情况：</p>
<ul>
<li>内容宽度：<code>200px</code></li>
<li>边框：<code>5px</code>（左右各 5px）</li>
<li>内边距：<code>10px</code>（左右各 10px）</li>
<li>计算：200 + 5 + 5 + 10 + 10 = 230</li>
<li><strong>盒子实际宽度 = 230px</strong></li>
</ul>
</li>
</ul>
<p>结论：在默认盒模型下</p>
<ul>
<li><strong>总宽度 = width + 左右padding + 左右border</strong></li>
<li><strong>总高度 = height + 上下padding + 上下border</strong></li>
<li>这会导致布局计算变得复杂，容易超出预期尺寸。</li>
</ul>
<hr />
<p>box-sizing 属性说明<br />
<code>box-sizing</code> 用于定义元素的盒模型计算方式，控制 <code>width</code> 和 <code>height</code> 是否包含 <code>padding</code> 和 <code>border</code>。</p>
<hr />
<p>语法</p>
<pre><code>box-sizing: 属性值;
</code></pre>
<p><strong>属性值说明</strong>：</p>
<table>
<thead>
<tr>
<th>属性值</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>content-box</code></td>
<td><strong>默认值</strong>。<code>width</code>/<code>height</code> 仅包含内容区域，不包含 <code>padding</code> 和 <code>border</code>。&lt;br&gt;理解：<code>width = 内容宽度</code></td>
</tr>
<tr>
<td><code>border-box</code></td>
<td><code>width</code>/<code>height</code> 包含内容、<code>padding</code> 和 <code>border</code>。&lt;br&gt;理解：<code>width = border + padding + 内容宽度</code></td>
</tr>
</tbody>
</table>
<hr />
<p>当设置 <code>box-sizing: border-box;</code> 时：</p>
<ul>
<li>元素总宽度固定为 <code>200px</code></li>
<li>即使有 <code>border: 5px</code> 和 <code>padding: 10px</code>，内容区域会自动缩小，总宽度仍保持 <code>200px</code></li>
</ul>
<hr />
<h2>盒子背景（background）</h2>
<p><code>background</code> 用于设置元素背景相关属性，包括：</p>
<ul>
<li>背景颜色</li>
<li>背景图片</li>
<li>背景位置</li>
<li>背景重复方式</li>
<li>背景渐变等</li>
</ul>
<hr />
<p><strong>使用场景</strong>：</p>
<ol>
<li>给盒子添加背景图片，让界面更精美</li>
<li>给列表添加统一小图标，实现装饰效果</li>
<li>给页面设置大尺寸背景图，增强视觉冲击力</li>
<li>纯 CSS 实现背景渐变效果，无需图片</li>
</ol>
<hr />
<h3>background背景常见属性</h3>
<p>| 属性 | 作用 | 常用值 | 示例代码 |
| ----------------------- | -------------------- | ----------------------------------------------------------- | -----------------0--------------- |
| <code>background-color</code> | 设置元素背景颜色 | 颜色名称、十六进制、RGB、透明度 | <code>background-color: #f0f0f0;</code> |
| <code>background-image</code> | 设置背景图片 | <code>url(image.jpg)</code> | <code>background-image: url(bg.png);</code> |
| <code>background-repeat</code> | 控制背景图片是否重复 | <code>repeat</code>（默认）、<code>no-repeat</code>、<code>repeat-x</code>、<code>repeat-y</code> | <code>background-repeat: no-repeat;</code> |
| <code>background-position</code> | 定位背景图片位置 | <code>x y</code>（如 <code>center top</code>，或 <code>px</code>、<code>%</code> 单位） | <code>background-position: center;</code> |
| <code>background-size</code> | 调整背景图片尺寸 | 默认 <code>auto</code>、<code>cover</code>（覆盖）、<code>contain</code>（包含）或 <code>px</code>、<code>%</code> | <code>background-size: cover;</code> |
| <code>background-attachment</code> | 背景是否随页面滚动 | <code>scroll</code>（默认）、<code>fixed</code> | <code>background-attachment: fixed;</code> |</p>
<p><strong>示例</strong>：</p>
<pre><code>div {
  /* 背景颜色 */
  background-color: #f0f0f0;
  /* 背景图片 */
  background-image: url(bg.png);
  /* 背景图片不重复 */
  background-repeat: no-repeat;
  /* 背景图片居中 */
  background-position: center;
  /* 背景图片覆盖 */
  background-size: cover;
  /* 背景图片固定不动 */
  background-attachment: fixed;
}
</code></pre>
<h3>背景复合写法</h3>
<p><code>background</code> 属性可以同时设置多个背景属性，包括背景颜色，背景图片，背景位置，背景重复方式，背景尺寸，背景固定不动等。</p>
<p><strong>背景复合写法</strong>：</p>
<pre><code>div {
  background: 颜色 图片 重复 固定 位置/尺寸; /* 顺序无关 */
}
</code></pre>
<p><strong>示例</strong>：</p>
<pre><code>div {
  div {
  background: #f0f0f0 url(bg.png) no-repeat fixed center / cover;
}
}
</code></pre>
<h3>背景渐变</h3>
<p>在CSS中，可使用<code>linear-gradient</code>(线性渐变)和<code>radial-gradient</code>(径向渐变)实现背景渐变效果。</p>
<p><strong>场景</strong>:</p>
<ol>
<li>文字底色渐变甚至动画，更吸引用户</li>
<li>盒子添加更加美观</li>
</ol>
<table>
<thead>
<tr>
<th>属性/方法</th>
<th>描述</th>
<th>示例代码</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>linear-gradient(方向, 颜色1 位置, 颜色2 位置...)</code></td>
<td>线性渐变（方向可控）</td>
<td><code>background: linear-gradient(to right, #ff6b6b, #4ecdc4)</code>&lt;br&gt;<code>background-image: linear-gradient(90deg, #ff6b6b 30%, #4ecdc4 70%)</code></td>
</tr>
<tr>
<td><code>radial-gradient(形状, 颜色1, 颜色2...)</code></td>
<td>径向渐变（形状和位置可控）</td>
<td><code>radial-gradient(circle, #ff9a9e, #fad0c4)</code></td>
</tr>
</tbody>
</table>
<h4>线性渐变</h4>
<ol>
<li>方向：可以是<strong>方位名词</strong>（如 <code>to right</code>、<code>to left</code>、<code>to top</code>、<code>to bottom</code>）或<strong>角度(deg)</strong>（如 <code>90deg</code>、<code>45deg</code> 等）。</li>
<li>色标位置：可以指定颜色和其在渐变中的位置（如 <code>#ff6b6b 30%</code> 表示在 30% 位置使用 <code>#ff6b6b</code> 颜色）,不是必须写的。</li>
</ol>
<p><strong>示例</strong>：</p>
<pre><code>div {
  /* 线性渐变：从左到右，从红色到蓝色 */
  background: linear-gradient(to right, #ff6b6b, #4ecdc4);
}

.gradient-box {
  /* 线性渐变：从左上角到右下角，从红色到蓝色 */
  width: 300px;
  height: 200px;
  background: linear-gradient(45deg, #ff6b6b, #4ecdc4);
}
</code></pre>
<p><strong>文字背景线性渐变</strong>：</p>
<pre><code>div {
  /* 设置背景渐变 */
  background: linear-gradient(to right, pink, red);

  /* 兼容性写法：谷歌老版本浏览器 */
  -webkit-background-clip: text;
  /* 文字范围裁剪背景 */
  background-clip: text;

  /* 文本填充颜色设置为透明，让背景渐变显示出来 */
  -webkit-text-fill-color: transparent;
}
</code></pre>
<h4>径向渐变</h4>
<ol>
<li>形状：可以是 <code>circle</code>（圆形）或 <code>ellipse</code>（椭圆形）。</li>
<li>色标位置：与线性渐变相同。</li>
</ol>
<p><strong>示例</strong>：</p>
<pre><code>div {
  /* 径向渐变：从中心向四周，从红色到蓝色 */
  background: radial-gradient(circle, #ff6b6b, #4ecdc4);
}
</code></pre>
<h2>盒子阴影</h2>
<p>CSS 中的 <code>box-shadow</code> 属性用于在元素的框架上添加阴影效果。</p>
<p><strong>场景</strong>:</p>
<ol>
<li>给盒子添加阴影，使页面看起来更加立体</li>
<li>鼠标经过元素显示阴影，更加突出元素</li>
</ol>
<p><strong>语法</strong>：
box-shadow: x偏移量 y偏移量 模糊半径 扩展半径 颜色 (内阴影);</p>
<ul>
<li>多个属性用空格隔开</li>
<li>x轴偏移量和y轴偏移量必填，其他属性可省略采取默认值</li>
<li>默认是外阴影，若要内阴影，需添加 <code>inset</code> 关键字</li>
</ul>
<p><strong>示例</strong>：</p>
<pre><code>div {
  box-shadow: 0 0 10px 5px rgba(0, 0, 0, 0.3);
}

div:hover {
  box-shadow: 0 0 10px 5px rgba(0, 0, 0, 0.5);
}
</code></pre>
<h2>过渡</h2>
<p>过渡效果（transition）用于在元素的属性值发生变化时，平滑地过渡（而不是瞬间切换）。</p>
<p><strong>场景</strong>：</p>
<ol>
<li>鼠标经过图片放大</li>
<li>表单获得焦点，输入框变宽</li>
</ol>
<p><strong>语法</strong>：<br />
transition：过渡属性 过渡时间 过渡函数 延迟时间；</p>
<ul>
<li>属性值中间空格隔开</li>
<li>过渡属性如果都要变化可以写<code>all</code></li>
<li>过渡时间单位是秒（s）或毫秒（ms）</li>
<li>过渡函数可选，默认是<code>ease</code>，常用值有<code>linear</code>（线性）、<code>ease</code>（默认，先慢后快）、<code>ease-in</code>（慢开始）、<code>ease-out</code>（慢结束）、<code>ease-in-out</code>（慢开始慢结束）</li>
<li>延迟时间可选，默认是0，单位是秒（s）或毫秒（ms）</li>
</ul>
<p><strong>示例</strong>：</p>
<pre><code>/* 过渡写到盒子上，不是写在:hover上 */
.box {
  /* 宽度 */
  width: 100px;
  /* 高度 */
  height: 100px;
  /* 背景颜色 */
  background-color: #f0f0f0;
  /* 过渡属性：所有属性 */
  transition: all 0.3s ease-in-out;
}

.box :hover {
  /* 显示阴影 */
  box-shadow: 0 0 10px 5px rgba(0, 0, 0, 0.3);
}
</code></pre>
<h2>样式初始化</h2>
<p>浏览器对HTML元素有默认样式（如 <code>margin</code>、<code>padding</code>、<code>font-size</code> 等）。不同样式浏览器默认样式可能不一致，导致跨浏览器兼容问题，我们需要对这些默认样式进行初始化。</p>
<p><strong>初始化的核心目的</strong>：</p>
<ul>
<li>统一浏览器默认样式，消除差异</li>
<li>减少后续开发中的冗余代码</li>
<li>提高代码可维护性</li>
</ul>
<h3>最佳实践</h3>
<h4>小型项目</h4>
<pre><code>/* 全局重置 */
* {
  margin: 0; /* 清除所有元素的 margin */
  padding: 0; /* 清除所有元素的 padding */
  box-sizing: border-box; /* 推荐同时设置盒模型 */
}

/* 重置列表样式 */
ul,
ol {
  list-style: none; /* 去除列表样式 */
}

/* 重置链接样式 */
a {
  text-decoration: none; /* 去除下划线 */
}
</code></pre>
<h4>中大型项目:推荐Normalize.css</h4>
<p><strong>推荐方案</strong>：中大型项目推荐使用 <code>Normalize.css</code> 进行样式重置，它能在保留有用浏览器默认样式的同时，统一不同浏览器的渲染表现。
<strong>引入方式</strong>：</p>
<pre><code>&lt;link rel="stylesheet" href="./css/Normalize.css" /&gt;
</code></pre>
<p><strong>官网下载地址</strong>：<a href="https://necolas.github.io/normalize.css/">https://necolas.github.io/normalize.css/</a>
Vue 官网等项目就引入了该 CSS 文件。</p>
<h1>字体图标</h1>
<p>字体图标是一种将图标以字体形式嵌入网页的技术，允许开发者像使用文字一样通过 CSS 控制图标的样式（如颜色、大小、阴影等）。</p>
<hr />
<h4>使用场景</h4>
<ol>
<li>导航菜单图标</li>
<li>按钮操作图标</li>
<li>结合动画效果</li>
</ol>
<h4>优势</h4>
<ol>
<li><strong>矢量缩放</strong>：无损放大缩小</li>
<li><strong>样式灵活</strong>：通过 CSS 直接修改颜色、大小、阴影等属性</li>
<li><strong>减少 HTTP 请求</strong>：一个字体文件可包含多个图标，比多张图片更高效</li>
<li><strong>兼容性好</strong>：支持所有现代浏览器，甚至部分旧版浏览器</li>
</ol>
<h2>字体图标库</h2>
<p>字体图标要么设计师提供，要么去字体库下载。</p>
<table>
<thead>
<tr>
<th>图标库名称</th>
<th>特点</th>
<th>官网链接</th>
</tr>
</thead>
<tbody>
<tr>
<td>Font Awesome</td>
<td>图标最全，支持免费版和Pro付费版</td>
<td><a href="https://fontawesome.com">fontawesome.com</a></td>
</tr>
<tr>
<td>Bootstrap Icons</td>
<td>Bootstrap生态内图标，简单易用</td>
<td><a href="https://icons.getbootstrap.com">icons.getbootstrap.com</a></td>
</tr>
<tr>
<td>icomoon</td>
<td>最早推出了第一个自定义图标字体生成器</td>
<td><a href="https://icomoon.io">icomoon.io</a></td>
</tr>
<tr>
<td>iconfont</td>
<td>阿里字体库，包含淘宝图标库和阿里妈妈图标库（免费）</td>
<td><a href="http://www.iconfont.cn/">http://www.iconfont.cn/</a></td>
</tr>
</tbody>
</table>
<h2>使用字体图标</h2>
<p>先以icon font图标库为例，其他图标库使用方法类似。</p>
<h3>字体图标使用步骤</h3>
<ol>
<li>
<p><strong>下载字体图标文件</strong>
去官网或者由设计师提供字体图标文件，将其保存到项目目录下。
各个文件的作用：</p>
<ul>
<li><code>iconfont.css</code>：包含字体图标类名的 CSS 样式表，用于定义图标对应的字体和 Unicode 编码。</li>
<li><code>iconfont.ttf</code>、<code>iconfont.woff</code>、<code>iconfont.woff2</code>：字体文件，包含实际的图标字形。浏览器会根据用户设备支持的字体格式加载对应的字体文件。</li>
</ul>
</li>
<li>
<p><strong>引入到HTML文件中</strong>
根据下载的压缩包，使用 <code>&lt;link&gt;</code> 标签引入对应的 CSS 文件。</p>
<pre><code>&lt;link rel="stylesheet" href="./fonts/iconfont.css" /&gt;
</code></pre>
</li>
<li>
<p><strong>使用字体图标</strong></p>
<ul>
<li>一般通过标签调用类名来选择对应的字体图标。</li>
<li>可根据实际需求，通过 CSS 调整图标的样式，如颜色、大小、位置等。</li>
</ul>
<pre><code>&lt;i class="iconfont icon-home"&gt;&lt;/i&gt;
</code></pre>
</li>
</ol>
<hr />
<h1>CSS精灵图</h1>
<p>CSS精灵图是将多个小图标或图像合并到一张大图中，通过调整 <code>background-position</code> 属性来显示特定部分的图像技术。</p>
<hr />
<h4>使用场景</h4>
<ol>
<li>导航菜单图标</li>
<li>按钮操作图标</li>
<li>复杂彩色小图标更适合精灵图</li>
</ol>
<h4>优势</h4>
<ol>
<li><strong>减少HTTP请求</strong>：多个小图标合并为一张图片，只需一次请求</li>
<li><strong>提升性能</strong>：减少网络开销，尤其适合移动端或低带宽场景</li>
<li><strong>统一管理</strong>：方便维护图标集，避免文件分散</li>
</ol>
<hr />
<p>通过合理使用CSS精灵图，可以有效优化网页性能。对于复杂场景（如高清屏适配），建议结合SVG或字体图标使用。</p>
<hr />
<h4>CSS精灵图原理</h4>
<ol>
<li><strong>给盒子添加背景图片</strong>：将合并后的精灵图作为元素的背景图片。</li>
<li><strong>通过背景定位对齐</strong>：使用 <code>background-position</code> 属性调整背景图的位置，使目标图标区域与元素可视区域对齐。</li>
<li><strong>注意网页坐标规则</strong>：网页背景定位的坐标原点在左上角，x轴向右、y轴向下，因此 <code>background-position</code> 的负值用于向左、向上移动背景图。</li>
</ol>
<p><strong>在线测量工具</strong>：<a href="https://www.tugaigai.com/online_ps/">https://www.tugaigai.com/online_ps/</a></p>
<h4>示例</h4>
<pre><code>.icon-home {
  /* 背景图片 */
  background-image: url("./sprite.png");
  /* 背景定位 */
  background-position: -10px -10px;
  /* 图标大小 */
  width: 32px;
  height: 32px;
}
</code></pre>
<pre><code>graph LR
    A["CSS3核心技术"] --&gt; B["CSS简介 作用：样式美化/布局/动画"]
    A --&gt; C["CSS分类 内联/内部/外部"]
    A --&gt; D["CSS选择器"]
    A --&gt; E["CSS文本样式"]
    A --&gt; F["CSS三大特性 继承/层叠/优先级"]
    A --&gt; G["盒子模型"]
    A --&gt; H["字体图标 iconfont等"]
    A --&gt; I["精灵图 背景定位"]

    D --&gt; D1["基础选择器"]
    D1 --&gt; D1a["类型选择器 div {}"]
    D1 --&gt; D1b["类选择器 .class {} 可复用"]
    D1 --&gt; D1c["ID选择器 #id {} 唯一"]
    D1 --&gt; D1d["通配符 * {} 全局重置"]

    D --&gt; D2["关系选择器"]
    D2 --&gt; D2a["后代 A B"]
    D2 --&gt; D2b["子代 A &gt; B"]
    D2 --&gt; D2c["邻接兄弟 A + B"]
    D2 --&gt; D2d["通用兄弟 A ~ B"]

    D --&gt; D3["分组选择器 A, B"]

    D --&gt; D4["伪类选择器"]
    D4 --&gt; D4a["状态伪类"]
    D4a --&gt; D4a1["link/visited/hover/active （LVHA顺序）"]
    D4a --&gt; D4a2["hover（任何元素）"]
    D4a --&gt; D4a3["focus（表单焦点）"]
    D4 --&gt; D4b["结构伪类"]
    D4b --&gt; D4b1["first-child"]
    D4b --&gt; D4b2["last-child"]
    D4b --&gt; D4b3["nth-child(n)"]
    D4 --&gt; D4c["表单伪类"]
    D4c --&gt; D4c1["disabled"]
    D4c --&gt; D4c2["checked"]

    D --&gt; D5["伪元素选择器"]
    D5 --&gt; D5a["first-line/first-letter"]
    D5 --&gt; D5b["placeholder"]
    D5 --&gt; D5c["before/after 需content"]

    D --&gt; D6["属性选择器"]
    D6 --&gt; D6a["[attr]"]
    D6 --&gt; D6b["[attr=value]"]
    D6 --&gt; D6c["[attr^=value] 开头"]
    D6 --&gt; D6d["[attr$=value] 结尾"]
    D6 --&gt; D6e["[attr*=value] 包含"]

    E --&gt; E1["字体样式"]
    E1 --&gt; E1a["color 颜色"]
    E1 --&gt; E1b["font-family 字体族"]
    E1 --&gt; E1c["font-size 大小"]
    E1 --&gt; E1d["font-style 斜体"]
    E1 --&gt; E1e["font-weight 粗细"]
    E1 --&gt; E1f["text-decoration 装饰"]

    E --&gt; E2["文本布局"]
    E2 --&gt; E2a["text-align 水平对齐"]
    E2 --&gt; E2b["text-indent 首行缩进"]
    E2 --&gt; E2c["letter-spacing 字间距"]
    E2 --&gt; E2d["line-height 行高 垂直居中"]

    E --&gt; E3["font简写 顺序：style weight size/line-height family"]

    F --&gt; F1["继承性 文字相关"]
    F --&gt; F2["层叠性 后定义覆盖"]
    F --&gt; F3["优先级 !important &gt; 内联 &gt; ID &gt; 类 &gt; 标签 &gt; 通配符"]

    G --&gt; G1["盒子类型 block/inline"]
    G --&gt; G2["盒子组成"]
    G2 --&gt; G2a["内容 content"]
    G2 --&gt; G2b["边框 border 样式：solid/dashed/dotted等"]
    G2 --&gt; G2c["圆角 border-radius px/% 胶囊=高一半"]
    G2 --&gt; G2d["内边距 padding 上右下左"]
    G2 --&gt; G2e["外边距 margin 行内上下无效"]
    G2 --&gt; G2f["尺寸计算 box-sizing content-box / border-box"]

    G --&gt; G3["背景 background"]
    G3 --&gt; G3a["背景色 background-color"]
    G3 --&gt; G3b["背景图 background-image"]
    G3 --&gt; G3c["背景重复 background-repeat"]
    G3 --&gt; G3d["背景位置 background-position"]
    G3 --&gt; G3e["背景尺寸 background-size cover/contain"]
    G3 --&gt; G3f["背景固定 background-attachment"]
    G3 --&gt; G3g["复合写法 background"]
    G3 --&gt; G3h["渐变 linear-gradient / radial-gradient"]

    G --&gt; G4["盒子阴影 box-shadow x偏移 y偏移 模糊 扩展 颜色 inset"]
    G --&gt; G5["过渡 transition 属性 时长 曲线 延迟"]
    G --&gt; G6["样式初始化 *重置 / Normalize.css"]

    H --&gt; H1["使用步骤 下载→引入→加类名"]
    I --&gt; I1["原理：背景图+定位"]
    I --&gt; I2["background-position负值"]
</code></pre>
]]></content>
    <author><name>仙鹤</name></author>
    <category term="前端"/>
  </entry>
  <entry>
    <title>用C语言实现扫雷的扩散逻辑</title>
    <link href="https://xianhe51920.github.io/posts/mysweeper/" rel="alternate" type="text/html"/>
    <id>https://xianhe51920.github.io/posts/mysweeper/</id>
    <published>2026-05-03T00:00:00.000Z</published>
    <updated>2026-05-03T00:00:00.000Z</updated>
    <summary>复盘一下设计逻辑</summary>
    <content type="html"><![CDATA[<h1>用C语言实现扫雷的扩散逻辑</h1>
<p>刚学习完C语言数组部分的内容，跟着b站老师用C语言写了个扫雷游戏，但是老师并没有教我们写扫雷扩散逻辑，所以我就自己设计了一个扫雷的扩散逻辑</p>
<h2>什么是扫雷的扩散逻辑</h2>
<p>如图所示，当我们翻开一个位置（图中红色圆圈），如果这个位置为空白区，意味着它的相邻区域（图中红框标记范围）是没有雷的，相邻区域大概可以分为两种情况，一种是数字，一种是空白区。如果是数字，则会直接显示出来；如果是空，除了会显示出来，还会继续检查周围区域，从而继续扩散，直到周围没有空区域为止
<img src="/images/posts/mysweeper/3.png" alt="扫雷图例" /></p>
<h2>扩散函数的设计</h2>
<p>因为之前有py的基础，看到这种问题第一反应就是用递归的思想去解决。递归调用的条件是周围存在空白区域，递归停止的条件则是周围不存在空白区域，一下是我自己设计的函数</p>
<pre><code>//扩散逻辑
//char mine[][COLS] 存放雷的字符数组，1为雷，0为安全
//char show[][COLS] 存放游玩界面的字符数组，*表示未翻开，#表示雷，0表示四周八个方向没有雷，其他数字表示雷的个数
// int row col 扫雷区域的行数和列数
// int x y 玩家选择的坐标
// *p 存放int win的地址，当检查到一个区域不是雷，win++,win到达一定值判定游戏胜利
void AutoFind(char mine[][COLS], char show[][COLS], int row, int col, int x, int y, int* p){
    //周围区域相对坐标
	int dx[] = {-1,-1,1,1,-1,0,0,1};
	int dy[] = {-1,1,-1,1,0,-1,1,0};
    for(int k = 0; k &lt; 8; k++){
        //搜寻具体坐标
		int i = x + dx[k];
		int j = y + dy[k];
		//确认检查的坐标在扫雷区域内部，并且这个位置不是雷，且未显示
		if(1 &lt;= i &amp;&amp; i &lt;= row &amp;&amp; 1 &lt;= j &amp;&amp; j &lt;= col &amp;&amp; mine[i][j] != '1' &amp;&amp; show[i][j] == '*'){
			int jud = CotMine(mine, i, j);//计算雷的数量
			switch (jud) {
			case 0:
				show[i][j] = '0';//如果雷数量为0，则显示
				*p += 1;
				AutoFind(mine, show, row, col, i, j, p);//雷数量为0，进行递归扩散
				break;
			default:
				*p += 1;
				show[i][j] = '0' + jud;//如果数量不为零，则直接显示
				break;
			}
		}
	}
}
</code></pre>
<p>最后能实现的效果就是这样的（翻开前和翻开后）
<img src="/images/posts/mysweeper/1.png" alt="翻开前的效果" />
<img src="/images/posts/mysweeper/2.png" alt="翻开后的效果" />
最后，我会把我扫雷的源代码分享给大家，供大家学习使用，第一次写这么多代码并没有很好的划分各种功能，导致标记功能的制作难以下手，还请谅解</p>
]]></content>
    <author><name>仙鹤</name></author>
    <category term="编程"/>
  </entry>
  <entry>
    <title>python竞赛常用模块总结</title>
    <link href="https://xianhe51920.github.io/posts/py_mod/" rel="alternate" type="text/html"/>
    <id>https://xianhe51920.github.io/posts/py_mod/</id>
    <published>2026-01-31T12:00:00.000Z</published>
    <updated>2026-01-31T12:00:00.000Z</updated>
    <summary>python竞赛常用模块总结</summary>
    <content type="html"><![CDATA[<h1>python竞赛常用模块总结</h1>
<h1>一.快速读取输入</h1>
<p>使用sys模块，读取效率较高，推荐使用。</p>
<h2>简单快读模板</h2>
<p>重新定义input函数，使读取效率加快。</p>
<pre><code>import sys
input = lambda: sys.stdin.readline().strip()
</code></pre>
<hr />
<h2>快速读取一行<code>sys.stdin.readline()</code></h2>
<pre><code>import sys

# 读取一行，包含末尾的\n
line = sys.stdin.readline()

# 去掉首尾换行符
line = sys.stdin.readline().strip()

# 读取整数
n = int(sys.stdin.readline())

# 读取多个整数
a, b = map(int, sys.stdin.readline().split())
</code></pre>
<hr />
<h2>快速读取所有输入<code>sys.stdin.read()</code></h2>
<pre><code>import sys

# 读取所有输入，返回一个字符串
all_input = sys.stdin.read()

# 常用模式：按空白字符分割
data = sys.stdin.read().split()

# 转换为整数列表
numbers = list(map(int, sys.stdin.read().split()))
</code></pre>
<h1>二.常用数学函数</h1>
<h2>内置数学函数</h2>
<h3>基本数学运算</h3>
<table>
<thead>
<tr>
<th>函数</th>
<th>名称</th>
<th>说明</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>abs(x)</code></td>
<td>绝对值函数</td>
<td>返回x的绝对值</td>
<td><code>abs(-5) → 5</code></td>
</tr>
<tr>
<td><code>pow(x, y[, z])</code></td>
<td>快速幂</td>
<td><code>pow(x, y)</code>返回$x^y$&lt;br/&gt;<code>pow(x, y, z)</code>返回$x^y \bmod z$</td>
<td><code>pow(2, 3) → 8</code>, <code>pow(2, 3, 5) → 3</code></td>
</tr>
<tr>
<td><code>round(x,n)</code></td>
<td>四舍五入</td>
<td><code>round(x)</code>保留整数&lt;br/&gt;<code>round(x, n)</code>保留<code>n</code>位小数</td>
<td><code>round(3.14) → 3</code>&lt;br/&gt;<code>round(3.14159, 2) → 3.14</code></td>
</tr>
<tr>
<td><code>divmod(x, y)</code></td>
<td>整除函数</td>
<td>返回<code>(x//y, x%y)</code></td>
<td><code>divmod(7, 2) → (3, 1)</code></td>
</tr>
</tbody>
</table>
<hr />
<h3>进制转换</h3>
<table>
<thead>
<tr>
<th>函数</th>
<th>名称</th>
<th>说明</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>bin(x)</code></td>
<td>二进制转换</td>
<td>返回整数x的二进制表示</td>
<td><code>bin(10) → '0b1010'</code></td>
</tr>
<tr>
<td><code>oct(x)</code></td>
<td>八进制转换</td>
<td>返回整数x的八进制表示</td>
<td><code>oct(10) → '0o12'</code></td>
</tr>
<tr>
<td><code>hex(x)</code></td>
<td>十六进制转换</td>
<td>返回整数x的十六进制表示</td>
<td><code>hex(10) → '0xa'</code></td>
</tr>
</tbody>
</table>
<hr />
<h2>math模块</h2>
<p>导入math模块</p>
<pre><code>import math
</code></pre>
<h3>基本数学运算</h3>
<table>
<thead>
<tr>
<th>函数/常量</th>
<th>名称</th>
<th>说明</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>sqrt(x)</code></td>
<td>平方根</td>
<td>返回x的平方根</td>
<td><code>sqrt(4) → 2.0</code></td>
</tr>
<tr>
<td><code>ceil(x)</code></td>
<td>向上取整</td>
<td>返回大于等于x的最小整数</td>
<td><code>ceil(3.14) → 4</code></td>
</tr>
<tr>
<td><code>floor(x)</code></td>
<td>向下取整</td>
<td>返回小于等于x的最大整数</td>
<td><code>floor(3.99) → 3</code></td>
</tr>
</tbody>
</table>
<hr />
<h3>数论函数</h3>
<table>
<thead>
<tr>
<th>函数</th>
<th>名称</th>
<th>说明</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>gcd(a, b)</code></td>
<td>最大公约数</td>
<td>返回a和b的最大公约数</td>
<td><code>gcd(12, 18) → 6</code></td>
</tr>
<tr>
<td><code>lcm(a, b)</code></td>
<td>最小公倍数</td>
<td>返回a和b的最小公倍数</td>
<td><code>lcm(4, 6) → 12</code></td>
</tr>
<tr>
<td><code>comb(n, k)</code></td>
<td>组合数</td>
<td>返回$C_n^k$</td>
<td><code>comb(5, 2) → 10</code></td>
</tr>
<tr>
<td><code>perm(n, k)</code></td>
<td>排列数</td>
<td>返回$P_n^k$</td>
<td><code>perm(5, 2) → 20</code></td>
</tr>
<tr>
<td><code>factorial(n)</code></td>
<td>阶乘</td>
<td>返回$n!$</td>
<td><code>factorial(5) → 120</code></td>
</tr>
</tbody>
</table>
<hr />
<h3>数学常量</h3>
<table>
<thead>
<tr>
<th>常量</th>
<th>名称</th>
<th>说明</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>pi</code></td>
<td>圆周率</td>
<td>数学常量π</td>
<td><code>pi → 3.14159...</code></td>
</tr>
<tr>
<td><code>e</code></td>
<td>自然对数底</td>
<td>数学常量e</td>
<td><code>e → 2.71828...</code></td>
</tr>
<tr>
<td><code>inf</code></td>
<td>无穷大</td>
<td>正无穷大</td>
<td><code>inf → inf</code></td>
</tr>
<tr>
<td><code>nan</code></td>
<td>非数字</td>
<td>非数字值</td>
<td><code>nan → nan</code></td>
</tr>
</tbody>
</table>
<hr />
<h3>其他函数</h3>
<table>
<thead>
<tr>
<th>函数</th>
<th>名称</th>
<th>说明</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>isqrt(x)</code></td>
<td>整数平方根</td>
<td>返回x的整数平方根</td>
<td><code>isqrt(16) → 4</code></td>
</tr>
<tr>
<td><code>dist(p, q)</code></td>
<td>距离</td>
<td>返回两点之间的欧几里得距离</td>
<td><code>dist((0,0), (3,4)) → 5.0</code></td>
</tr>
</tbody>
</table>
<h1>三.is判断函数</h1>
<p>判断字符串是否满足某种条件，返回True或False。</p>
<h2>字符串判断方法（str对象方法）</h2>
<h3>字符类型判断</h3>
<table>
<thead>
<tr>
<th>方法</th>
<th>名称</th>
<th>说明</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>isalnum()</code></td>
<td>字母数字判断</td>
<td>字符串是否由字母和数字组成</td>
<td><code>"abc123".isalnum() → True</code></td>
</tr>
<tr>
<td><code>isalpha()</code></td>
<td>字母判断</td>
<td>字符串是否只由字母组成</td>
<td><code>"abc".isalpha() → True</code></td>
</tr>
<tr>
<td><code>isdecimal()</code></td>
<td>十进制数字</td>
<td>字符串是否只包含十进制数字</td>
<td><code>"123".isdecimal() → True</code></td>
</tr>
<tr>
<td><code>isdigit()</code></td>
<td>数字判断</td>
<td>字符串是否只包含数字</td>
<td><code>"123".isdigit() → True</code></td>
</tr>
</tbody>
</table>
<h3>格式与大小写判断</h3>
<table>
<thead>
<tr>
<th>方法</th>
<th>名称</th>
<th>说明</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>islower()</code></td>
<td>小写判断</td>
<td>字符串是否至少有一个字母且所有字母都是小写</td>
<td><code>"abc".islower() → True</code></td>
</tr>
<tr>
<td><code>isupper()</code></td>
<td>大写判断</td>
<td>字符串是否至少有一个字母且所有字母都是大写</td>
<td><code>"ABC".isupper() → True</code></td>
</tr>
<tr>
<td><code>istitle()</code></td>
<td>标题判断</td>
<td>字符串是否每个单词首字母大写</td>
<td><code>"Hello World".istitle() → True</code></td>
</tr>
</tbody>
</table>
<hr />
<h1>常用数据结构模块</h1>
<p>导入collections模块</p>
<pre><code>from collections import deque, Counter
</code></pre>
<p><strong>常用数据结构</strong>：</p>
<table>
<thead>
<tr>
<th>类/函数</th>
<th>名称</th>
<th>说明</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>deque</code></td>
<td>双端队列</td>
<td>线程安全，快速从两端添加或删除元素</td>
<td><code>deque([1,2,3])</code></td>
</tr>
<tr>
<td><code>Counter</code></td>
<td>计数器</td>
<td>统计可哈希对象</td>
<td><code>Counter('abcabc')</code></td>
</tr>
</tbody>
</table>
<h2>deque 双端队列</h2>
<table>
<thead>
<tr>
<th>方法</th>
<th>说明</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>deque()</code></td>
<td>初始化</td>
<td>创建一个新的双端队列</td>
</tr>
<tr>
<td><code>append(x)</code></td>
<td>右端添加x</td>
<td><code>d.append(4)</code></td>
</tr>
<tr>
<td><code>appendleft(x)</code></td>
<td>左端添加x</td>
<td><code>d.appendleft(0)</code></td>
</tr>
<tr>
<td><code>pop()</code></td>
<td>右端弹出</td>
<td><code>d.pop() → 4</code></td>
</tr>
<tr>
<td><code>popleft()</code></td>
<td>左端弹出</td>
<td><code>d.popleft() → 0</code></td>
</tr>
<tr>
<td><code>extend(iterable)</code></td>
<td>右端扩展</td>
<td><code>d.extend([5,6])</code></td>
</tr>
<tr>
<td><code>extendleft(iterable)</code></td>
<td>左端扩展</td>
<td><code>d.extendleft([-1,-2])</code></td>
</tr>
<tr>
<td><code>rotate(n)</code></td>
<td>旋转n步</td>
<td><code>d.rotate(1)</code></td>
</tr>
<tr>
<td><code>clear()</code></td>
<td>清空队列</td>
<td><code>d.clear()</code></td>
</tr>
</tbody>
</table>
<hr />
<h2>Counter 计数器</h2>
<table>
<thead>
<tr>
<th>方法</th>
<th>说明</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Counter()</code></td>
<td>初始化</td>
<td>创建一个新的计数器，可以像字典一样使用</td>
</tr>
<tr>
<td><code>most_common(n)</code></td>
<td>前n个最常见元素</td>
<td><code>Counter('abracadabra').most_common(3)</code> → <code>[('a', 5), ('b', 2), ('r', 2)]</code></td>
</tr>
<tr>
<td><code>total()</code></td>
<td>计数总和</td>
<td><code>Counter('abc').total() → 3</code></td>
</tr>
</tbody>
</table>
<hr />
<h1>堆操作</h1>
<p>导入heapq模块</p>
<pre><code>import heapq
</code></pre>
<p>堆默认为最小堆，即堆顶为最小元素。</p>
<table>
<thead>
<tr>
<th>函数</th>
<th>名称</th>
<th>说明</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>heapify(x)</code></td>
<td>堆化</td>
<td>将列表x转换为堆</td>
<td><code>heapify([3,1,2])</code> → 堆为[1,3,2]</td>
</tr>
<tr>
<td><code>heappush(heap, item)</code></td>
<td>入堆</td>
<td>将item加入堆</td>
<td><code>heappush(heap, 4)</code></td>
</tr>
<tr>
<td><code>heappop(heap)</code></td>
<td>出堆</td>
<td>弹出并返回堆顶元素</td>
<td><code>heappop(heap)</code> → 1</td>
</tr>
<tr>
<td><code>heappushpop(heap, item)</code></td>
<td>先入后出</td>
<td>将item入堆然后弹出堆顶</td>
<td><code>heappushpop(heap, 0)</code> → 0</td>
</tr>
<tr>
<td><code>heapreplace(heap, item)</code></td>
<td>先出后入</td>
<td>弹出堆顶然后将item入堆</td>
<td><code>heapreplace(heap, 5)</code> → 1</td>
</tr>
<tr>
<td><code>nlargest(n, iterable)</code></td>
<td>前n个最大</td>
<td>返回iterable中前n个最大元素</td>
<td><code>nlargest(3, [1,5,2,4,3])</code> → [5,4,3]</td>
</tr>
<tr>
<td><code>nsmallest(n, iterable)</code></td>
<td>前n个最小</td>
<td>返回iterable中前n个最小元素</td>
<td><code>nsmallest(3, [1,5,2,4,3])</code> → [1,2,3]</td>
</tr>
</tbody>
</table>
<hr />
<h1>二分查找</h1>
<p>导入bisect模块</p>
<pre><code>import bisect
</code></pre>
<p>这些函数用于在已排序的列表中进行二分查找。</p>
<table>
<thead>
<tr>
<th>函数</th>
<th>名称</th>
<th>说明</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>bisect_left(a, x)</code></td>
<td>左插入点</td>
<td>返回第一个大于等于x的索引</td>
<td><code>bisect_left([1,2,4], 2)</code> → 1</td>
</tr>
<tr>
<td><code>bisect(a, x)</code></td>
<td>右插入点</td>
<td>返回第一个大于x的索引</td>
<td><code>bisect([1,2,4], 2)</code> → 2</td>
</tr>
<tr>
<td><code>insort_left(a, x)</code></td>
<td>左插入</td>
<td>找到第一个大于等于x的索引，将x插入该位置</td>
<td><code>insort_left([1,2,4], 2)</code> → [1,2,2,4]</td>
</tr>
<tr>
<td><code>insort(a, x)</code></td>
<td>右插入</td>
<td>找到第一个大于x的索引，将x插入该位置</td>
<td><code>insort([1,2,4], 2)</code> → [1,2,2,4]</td>
</tr>
</tbody>
</table>
<hr />
<h1>迭代与序列</h1>
<p><strong>所有函数概览表</strong></p>
<table>
<thead>
<tr>
<th>函数</th>
<th>名称</th>
<th>说明</th>
<th>时间复杂度</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>len()</code></td>
<td>获取长度</td>
<td>返回对象长度（元素个数）</td>
<td>O(1)</td>
<td><code>len([1,2,3]) → 3</code></td>
</tr>
<tr>
<td><code>reversed()</code></td>
<td>反转序列</td>
<td>返回序列的反向迭代器</td>
<td>O(1)</td>
<td><code>list(reversed([1,2,3])) → [3,2,1]</code></td>
</tr>
<tr>
<td><code>sorted()</code></td>
<td>排序</td>
<td>返回排序后的新列表</td>
<td>O(n log n)</td>
<td><code>sorted([3,1,2]) → [1,2,3]</code></td>
</tr>
<tr>
<td><code>enumerate()</code></td>
<td>枚举</td>
<td>返回(索引, 元素)对的迭代器</td>
<td>O(1)</td>
<td><code>list(enumerate(['a','b'])) → [(0,'a'),(1,'b')]</code></td>
</tr>
<tr>
<td><code>zip()</code></td>
<td>组合</td>
<td>将多个可迭代对象组合成元组迭代器</td>
<td>O(1)</td>
<td><code>list(zip([1,2],['a','b'])) → [(1,'a'),(2,'b')]</code></td>
</tr>
<tr>
<td><code>slice()</code></td>
<td>创建切片对象</td>
<td>创建切片对象用于扩展切片</td>
<td>O(1)</td>
<td><code>arr[slice(1,3)]</code></td>
</tr>
<tr>
<td><code>filter()</code></td>
<td>过滤</td>
<td>过滤可迭代对象，保留满足条件的元素</td>
<td>O(n)</td>
<td><code>list(filter(lambda x: x&gt;0, [-1,0,1,2])) → [1,2]</code></td>
</tr>
<tr>
<td><code>map()</code></td>
<td>映射</td>
<td>对可迭代对象的每个元素应用函数</td>
<td>O(n)</td>
<td><code>list(map(str, [1,2,3])) → ['1','2','3']</code></td>
</tr>
<tr>
<td><code>all()</code></td>
<td>所有为真</td>
<td>所有元素都为真时返回True</td>
<td>O(n)</td>
<td><code>all([True, True]) → True</code></td>
</tr>
<tr>
<td><code>any()</code></td>
<td>任一为真</td>
<td>任一元素为真时返回True</td>
<td>O(n)</td>
<td><code>any([False, True]) → True</code></td>
</tr>
</tbody>
</table>
<hr />
<h2>reversed()-反转序列</h2>
<pre><code># 反转列表（返回迭代器）
list(reversed([1, 2, 3])) # [3, 2, 1]

# 反转字符串
list(reversed("abc")) # ['c', 'b', 'a']

# 反转范围对象
list(reversed(range(5))) # [4, 3, 2, 1, 0]

# 与切片反转对比
arr = [1, 2, 3]
arr[::-1] # [3, 2, 1]（返回新列表）
reversed(arr) # &lt;list_reverseiterator&gt;（返回迭代器）

# 遍历反向迭代器
for item in reversed([1, 2, 3]):
    print(item)                     # 3, 2, 1
</code></pre>
<p><strong>特点</strong></p>
<ul>
<li>返回迭代器，节省内存</li>
<li>原始序列不会被修改</li>
</ul>
<hr />
<hr />
<h2>enumerate()-枚举索引</h2>
<pre><code># 基本用法
list(enumerate(['a', 'b', 'c']))  # [(0, 'a'), (1, 'b'), (2, 'c')]

# 指定起始索引
list(enumerate(['a', 'b', 'c'], start=1))  # [(1, 'a'), (2, 'b'), (3, 'c')]
</code></pre>
<hr />
<h2>zip()-组合多个可迭代对象</h2>
<pre><code># 组合两个列表
list(zip([1, 2, 3], ['a', 'b', 'c']))  # [(1, 'a'), (2, 'b'), (3, 'c')]

# 长度不等时以最短的为准
list(zip([1, 2, 3], ['a', 'b']))       # [(1, 'a'), (2, 'b')]

# 组合三个列表
list(zip([1, 2], ['a', 'b'], ['x', 'y']))  # [(1, 'a', 'x'), (2, 'b', 'y')]

# 解压（使用*操作符）
pairs = [(1, 'a'), (2, 'b'), (3, 'c')]
nums, letters = zip(*pairs)            # nums=(1,2,3), letters=('a','b','c')

# 矩阵转置
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
transposed = list(zip(*matrix))        # [(1,4,7), (2,5,8), (3,6,9)]

# 遍历组合
names = ['Alice', 'Bob', 'Charlie']
scores = [85, 92, 78]
for name, score in zip(names, scores):
    print(f"{name}: {score}")  # Alice: 85, Bob: 92, Charlie: 78
</code></pre>
<hr />
<h2>slice()-创建切片对象</h2>
<pre><code># 创建切片对象
s = slice(1, 4) # 等价于 [1:4]
arr = [0, 1, 2, 3, 4, 5]
arr[s] # [1, 2, 3]

# 带步长的切片
s = slice(0, 5, 2) # 等价于 [0:5:2]
arr[s] # [0, 2, 4]

# 省略开始或结束
s = slice(None, 3) # [:3]
arr[s] # [0, 1, 2]

# 动态创建切片
def get_slice(arr, start=None, stop=None, step=None):
    s = slice(start, stop, step)
    return arr[s]

get_slice([0,1,2,3,4], 1, 4) # [1, 2, 3]

# 获取切片属性
s = slice(1, 5, 2)
s.start # 1
s.stop # 5
s.step # 2
</code></pre>
<hr />
<h2>filter()-过滤序列</h2>
<pre><code># 过滤偶数
nums = [1, 2, 3, 4, 5, 6]
even = list(filter(lambda x: x % 2 == 0, nums))  # [2, 4, 6]

# 过滤非空字符串
words = ['hello', '', 'world', '', '!']
non_empty = list(filter(None, words))            # ['hello', 'world', '!']
# 注意：filter(None, iterable)会过滤掉bool值为False的元素

# 过滤正数
list(filter(lambda x: x &gt; 0, [-2, -1, 0, 1, 2]))  # [1, 2]

# 使用函数作为过滤条件
def is_vowel(char):
    return char.lower() in 'aeiou'
list(filter(is_vowel, 'hello world')) # ['e', 'o', 'o']

# 惰性求值（返回迭代器）
filtered = filter(lambda x: x &gt; 0, [-1, 0, 1, 2])
next(filtered) # 1
</code></pre>
<hr />
<h2>map()-映射函数</h2>
<pre><code># 转换为字符串
list(map(str, [1, 2, 3]))                # ['1', '2', '3']

# 平方运算
list(map(lambda x: x**2, [1, 2, 3, 4]))  # [1, 4, 9, 16]

# 多个序列运算
list(map(lambda x, y: x + y, [1, 2, 3], [10, 20, 30]))  # [11, 22, 33]

# 内置函数作为映射函数
list(map(abs, [-1, 2, -3, 4]))           # [1, 2, 3, 4]

# 多参数函数
def add(a, b, c):
    return a + b + c

list(map(add, [1, 2, 3], [10, 20, 30], [100, 200, 300]))  # [111, 222, 333]
</code></pre>
<hr />
<h2>all()和any()-逻辑判断</h2>
<pre><code># all(): 所有元素为True时返回True
all([True, True, True])           # True
all([True, False, True])          # False
all([])                           # True（空序列）

# any(): 任一元素为True时返回True
any([False, False, True])         # True
any([False, False, False])        # False
any([])                           # False（空序列）

# 实际应用
# 检查所有数字是否为正数
all(x &gt; 0 for x in [1, 2, 3, 4])   # True
all(x &gt; 0 for x in [1, 2, -3, 4])  # False

# 检查是否有正数
any(x &gt; 0 for x in [-1, -2, 3])    # True

# 检查所有字符串是否非空
strings = ['hello', 'world', '']
all(strings) # False（空字符串为False）
any(strings) # True

# 结合map使用
all(map(str.isalpha, 'abc123')) # False（有数字）
all(map(str.isalpha, 'abc')) # True
</code></pre>
]]></content>
    <author><name>仙鹤</name></author>
    <category term="笔记"/>
  </entry>
  <entry>
    <title>大一上数学公式整理</title>
    <link href="https://xianhe51920.github.io/posts/math_guide/" rel="alternate" type="text/html"/>
    <id>https://xianhe51920.github.io/posts/math_guide/</id>
    <published>2026-01-20T00:00:00.000Z</published>
    <updated>2026-01-20T00:00:00.000Z</updated>
    <summary>方便复习的同学快速查阅</summary>
    <content type="html"><![CDATA[<h1>高等数学上公式汇总</h1>
<p>这学期的高等数学也是通关了，之前记了一些公式就拿出来分享一下吧，给需要补考的同学们提供一些便利</p>
<p><img src="/images/posts/math_guide/%E6%88%90%E7%BB%A9.png" alt="数学成绩" /></p>
<h2>目录</h2>
<ol>
<li><a href="#%E4%B8%8D%E5%AE%9A%E7%A7%AF%E5%88%86">不定积分</a></li>
<li><a href="#%E7%AD%89%E4%BB%B7%E6%97%A0%E7%A9%B7%E5%B0%8F">等价无穷小</a></li>
<li><a href="#%E5%87%BD%E6%95%B0%E6%B1%82%E5%AF%BC">函数求导</a></li>
<li><a href="#%E7%A7%AF%E5%88%86%E5%AD%A6">积分学</a></li>
<li><a href="#%E6%9E%81%E9%99%90%E4%B8%8E%E8%BF%9E%E7%BB%AD">极限与连续</a></li>
<li><a href="#%E8%A7%A3%E5%BE%AE%E5%88%86%E6%96%B9%E7%A8%8B">解微分方程</a></li>
<li><a href="#%E6%B3%B0%E5%8B%92%E5%B1%95%E5%BC%80%E5%BC%8F">泰勒展开式</a></li>
<li><a href="#%E5%BE%AE%E5%88%86%E5%AD%A6">微分学</a></li>
</ol>
<hr />
<h1>不定积分</h1>
<h2>常用不定积分公式</h2>
<h3>基本积分表</h3>
<ul>
<li>$\int k , dx = kx + C$</li>
<li>$\int x^\mu , dx = \frac{x^{\mu+1}}{\mu+1} + C$ ($\mu \neq -1$)</li>
<li>$\int \frac{1}{x} , dx = \ln|x| + C$</li>
<li>$\int a^x , dx = \frac{a^x}{\ln a} + C$</li>
<li>$\int e^x , dx = e^x + C$</li>
<li>$\int \sin x , dx = -\cos x + C$</li>
<li>$\int \cos x , dx = \sin x + C$</li>
<li>$\int \sec^2 x , dx = \tan x + C$</li>
<li>$\int \csc^2 x , dx = -\cot x + C$</li>
<li>$\int \sec x \tan x , dx = \sec x + C$</li>
<li>$\int \csc x \cot x , dx = -\csc x + C$</li>
<li>$\int \frac{1}{\sqrt{1-x^2}} , dx = \arcsin x + C$</li>
<li>$\int \frac{1}{1+x^2} , dx = \arctan x + C$</li>
<li>$\int \tan x , dx = -\ln|\cos x| + C$</li>
<li>$\int \cot x , dx = \ln|\sin x| + C$</li>
</ul>
<h3>重要补充公式</h3>
<ul>
<li>$\int \frac{1}{a^2+x^2} , dx = \frac{1}{a} \arctan \frac{x}{a} + C$</li>
<li>$\int \frac{1}{\sqrt{a^2-x^2}} , dx = \arcsin \frac{x}{a} + C$</li>
<li>$\int \frac{1}{x^2-a^2} , dx = \frac{1}{2a} \ln \left| \frac{x-a}{x+a} \right| + C$</li>
<li>$\int \frac{1}{\sqrt{x^2 \pm a^2}} , dx = \ln |x + \sqrt{x^2 \pm a^2}| + C$</li>
</ul>
<hr />
<h1>等价无穷小</h1>
<h2>常用等价无穷小 (当 $x \to 0$ 时)</h2>
<ul>
<li>$\sin x \sim x$</li>
<li>$\tan x \sim x$</li>
<li>$\arcsin x \sim x$</li>
<li>$\arctan x \sim x$</li>
<li>$1 - \cos x \sim \frac{1}{2}x^2$</li>
<li>$e^x - 1 \sim x$</li>
<li>$\ln(1+x) \sim x$</li>
<li>$(1+x)^\alpha - 1 \sim \alpha x$</li>
<li>$a^x - 1 \sim x \ln a$</li>
<li>$\sqrt[n]{1+x} - 1 \sim \frac{x}{n}$</li>
<li>$x - \sin x \sim \frac{1}{6}x^3$</li>
<li>$\tan x - x \sim \frac{1}{3}x^3$</li>
<li>$x - \ln(1+x) \sim \frac{1}{2}x^2$</li>
</ul>
<hr />
<h1>函数求导</h1>
<h2>常用函数求导公式</h2>
<h3>基本初等函数导数</h3>
<ul>
<li>$(C)' = 0$</li>
<li>$(x^\mu)' = \mu x^{\mu-1}$</li>
<li>$(a^x)' = a^x \ln a$</li>
<li>$(e^x)' = e^x$</li>
<li>$(\log_a x)' = \frac{1}{x \ln a}$</li>
<li>$(\ln x)' = \frac{1}{x}$</li>
<li>$(\sin x)' = \cos x$</li>
<li>$(\cos x)' = -\sin x$</li>
<li>$(\tan x)' = \sec^2 x$</li>
<li>$(\cot x)' = -\csc^2 x$</li>
<li>$(\sec x)' = \sec x \tan x$</li>
<li>$(\csc x)' = -\csc x \cot x$</li>
<li>$(\arcsin x)' = \frac{1}{\sqrt{1-x^2}}$</li>
<li>$(\arccos x)' = -\frac{1}{\sqrt{1-x^2}}$</li>
<li>$(\arctan x)' = \frac{1}{1+x^2}$</li>
<li>$(\text{arccot } x)' = -\frac{1}{1+x^2}$</li>
</ul>
<h3>求导法则</h3>
<ul>
<li>$(u \pm v)' = u' \pm v'$</li>
<li>$(uv)' = u'v + uv'$</li>
<li>$\left(\frac{u}{v}\right)' = \frac{u'v - uv'}{v^2}$</li>
<li>$[f(g(x))]' = f'(g(x)) \cdot g'(x)$</li>
</ul>
<hr />
<h1>积分学</h1>
<h2>积分学重要补充公式</h2>
<h3>定积分基本公式与性质</h3>
<ul>
<li>$\int_a^b f(x) dx = F(b) - F(a)$</li>
<li>$\int_a^b [kf(x) \pm lg(x)] dx = k\int_a^b f(x) dx \pm l\int_a^b g(x) dx$</li>
<li>$\int_a^b f(x) dx = \int_a^c f(x) dx + \int_c^b f(x) dx$</li>
</ul>
<h3>定积分计算技巧</h3>
<ul>
<li>$\int_a^b f(x) dx = \int_\alpha^\beta f[\varphi(t)] \varphi'(t) dt$</li>
<li>$\int_a^b u dv = [uv]_a^b - \int_a^b v du$</li>
<li>若 $f(x)$ 偶函数，则 $\int_{-a}^a f(x) dx = 2\int_0^a f(x) dx$</li>
<li>若 $f(x)$ 奇函数，则 $\int_{-a}^a f(x) dx = 0$</li>
</ul>
<h3>常用定积分公式</h3>
<ul>
<li>$\int_0^{\pi/2} \sin^n x dx = \int_0^{\pi/2} \cos^n x dx =
\begin{cases}
\frac{(n-1)!!}{n!!} \cdot \frac{\pi}{2} &amp; n\text{为偶数}\
\frac{(n-1)!!}{n!!} &amp; n\text{为奇数}
\end{cases}$</li>
<li>$\int_0^{+\infty} e^{-x^2} dx = \frac{\sqrt{\pi}}{2}$</li>
<li>$\int_0^\pi x f(\sin x) dx = \frac{\pi}{2} \int_0^\pi f(\sin x) dx$</li>
</ul>
<h3>反常积分</h3>
<ul>
<li>$\int_a^{+\infty} f(x) dx = \lim\limits_{t \to +\infty} \int_a^t f(x) dx$</li>
<li>$\int_{-\infty}^b f(x) dx = \lim\limits_{t \to -\infty} \int_t^b f(x) dx$</li>
<li>$\int_a^b f(x) dx = \lim\limits_{t \to a^+} \int_t^b f(x) dx$</li>
</ul>
<h3>积分应用公式</h3>
<ul>
<li>直角坐标面积：$S = \int_a^b |f(x) - g(x)| dx$</li>
<li>极坐标面积：$S = \frac{1}{2} \int_\alpha^\beta r^2(\theta) d\theta$</li>
<li>绕x轴旋转体体积：$V = \pi \int_a^b [f(x)]^2 dx$</li>
<li>绕y轴旋转体体积（柱壳法）：$V = 2\pi \int_a^b x |f(x)| dx$</li>
<li>直角坐标弧长：$L = \int_a^b \sqrt{1 + [f'(x)]^2} dx$</li>
<li>参数方程弧长：$L = \int_\alpha^\beta \sqrt{[x'(t)]^2 + [y'(t)]^2} dt$</li>
<li>极坐标弧长：$L = \int_\alpha^\beta \sqrt{r^2(\theta) + [r'(\theta)]^2} d\theta$</li>
</ul>
<hr />
<h1>极限与连续</h1>
<h2>极限与连续的重要公式</h2>
<h3>两个重要极限</h3>
<ol>
<li>$\lim\limits_{x \to 0} \frac{\sin x}{x} = 1$</li>
<li>$\lim\limits_{x \to \infty} \left(1 + \frac{1}{x}\right)^x = e$</li>
</ol>
<h3>连续性相关</h3>
<ul>
<li>零点定理：若 $f(x)$ 在 $[a,b]$ 上连续，且 $f(a) \cdot f(b) &lt; 0$，则存在 $\xi \in (a,b)$，使 $f(\xi) = 0$</li>
<li>介值定理：若 $f(x)$ 在 $[a,b]$ 上连续，则对于任意 $C \in [m, M]$，存在 $\xi \in [a,b]$，使 $f(\xi) = C$</li>
</ul>
<hr />
<h1>解微分方程</h1>
<h2>微分方程的基本概念与一阶微分方程</h2>
<h3>一阶微分方程的基本类型</h3>
<ol>
<li>可分离变量方程：$\frac{dy}{dx} = f(x)g(y)$</li>
<li>齐次方程：$\frac{dy}{dx} = f\left(\frac{y}{x}\right)$</li>
<li>一阶线性微分方程：$\frac{dy}{dx} + P(x)y = Q(x)$
通解公式：$y = e^{-\int P(x)dx} \left[ \int Q(x)e^{\int P(x)dx}dx + C \right]$</li>
<li>伯努利方程：$\frac{dy}{dx} + P(x)y = Q(x)y^n \quad (n \neq 0,1)$</li>
</ol>
<h2>可降阶的高阶微分方程</h2>
<ul>
<li>$y^{(n)} = f(x)$</li>
<li>$y'' = f(x, y')$</li>
<li>$y'' = f(y, y')$</li>
</ul>
<h2>高阶线性微分方程</h2>
<h3>二阶常系数齐次线性方程</h3>
<p>形式：$y'' + py' + qy = 0$
特征方程：$r^2 + pr + q = 0$
通解：</p>
<ol>
<li>两个不等实根 $r_1, r_2$：$y = C_1e^{r_1x} + C_2e^{r_2x}$</li>
<li>两个相等实根 $r_1 = r_2$：$y = (C_1 + C_2x)e^{r_1x}$</li>
<li>共轭复根 $r = \alpha \pm i\beta$：$y = e^{\alpha x}(C_1\cos\beta x + C_2\sin\beta x)$</li>
</ol>
<hr />
<h1>泰勒展开式</h1>
<h2>常用函数的麦克劳林展开式（到 $n$ 阶）</h2>
<h3>指数函数</h3>
<p>$$ e^x = 1 + x + \frac{x^2}{2!} + \frac{x^3}{3!} + \cdots + \frac{x^n}{n!} + o(x^n) $$</p>
<h3>三角函数</h3>
<p><strong>正弦函数</strong>：
$$ \sin x = x - \frac{x^3}{3!} + \frac{x^5}{5!} - \cdots + (-1)^n \frac{x^{2n+1}}{(2n+1)!} + o(x^{2n+2}) $$</p>
<p><strong>余弦函数</strong>：
$$ \cos x = 1 - \frac{x^2}{2!} + \frac{x^4}{4!} - \cdots + (-1)^n \frac{x^{2n}}{(2n)!} + o(x^{2n+1}) $$</p>
<h3>对数函数</h3>
<p>$$ \ln(1+x) = x - \frac{x^2}{2} + \frac{x^3}{3} - \cdots + (-1)^{n-1} \frac{x^n}{n} + o(x^n) $$</p>
<h3>二项式展开</h3>
<p>$$ (1+x)^\alpha = 1 + \alpha x + \frac{\alpha(\alpha-1)}{2!}x^2 + \cdots + \frac{\alpha(\alpha-1)\cdots(\alpha-n+1)}{n!}x^n + o(x^n) $$</p>
<h3>几何级数</h3>
<p>$$ \frac{1}{1-x} = 1 + x + x^2 + x^3 + \cdots + x^n + o(x^n) $$</p>
<h3>其他重要展开</h3>
<p><strong>反正切函数</strong>：
$$ \arctan x = x - \frac{x^3}{3} + \frac{x^5}{5} - \cdots + (-1)^n \frac{x^{2n+1}}{2n+1} + o(x^{2n+2}) $$</p>
<p><strong>反正弦函数</strong>：
$$ \arcsin x = x + \frac{1}{6}x^3 + \frac{3}{40}x^5 + \cdots + o(x^{2n+1}) $$</p>
<hr />
<h1>微分学</h1>
<h2>微分学重要补充公式</h2>
<h3>微分公式与法则</h3>
<ul>
<li>$dy = f'(x)dx$</li>
<li>$d(C) = 0$</li>
<li>$d(x^\mu) = \mu x^{\mu-1} dx$</li>
<li>$d(\sin x) = \cos x dx$</li>
<li>$d(\cos x) = -\sin x dx$</li>
<li>$d(e^x) = e^x dx$</li>
<li>$d(\ln x) = \frac{1}{x} dx$</li>
</ul>
<h3>高阶导数公式</h3>
<ul>
<li>$(x^n)^{(n)} = n!$</li>
<li>$(\sin x)^{(n)} = \sin\left(x + \frac{n\pi}{2}\right)$</li>
<li>$(\cos x)^{(n)} = \cos\left(x + \frac{n\pi}{2}\right)$</li>
<li>$(e^x)^{(n)} = e^x$</li>
<li>$(\ln x)^{(n)} = (-1)^{n-1} \frac{(n-1)!}{x^n}$</li>
<li><strong>莱布尼茨公式</strong>：$(uv)^{(n)} = \sum\limits_{k=0}^{n} C_n^k u^{(n-k)} v^{(k)}$</li>
</ul>
<h3>微分中值定理</h3>
<ul>
<li>罗尔定理：若 $f(x)$ 在 $[a,b]$ 连续，$(a,b)$ 可导，且 $f(a) = f(b)$，则存在 $\xi \in (a,b)$，使 $f'(\xi) = 0$</li>
<li>拉格朗日中值定理：若 $f(x)$ 在 $[a,b]$ 连续，$(a,b)$ 可导，则存在 $\xi \in (a,b)$，使 $f(b) - f(a) = f'(\xi)(b-a)$</li>
<li>柯西中值定理：若 $f(x), g(x)$ 在 $[a,b]$ 连续，$(a,b)$ 可导，且 $g'(x) \neq 0$，则存在 $\xi \in (a,b)$，使 $\frac{f(b)-f(a)}{g(b)-g(a)} = \frac{f'(\xi)}{g'(\xi)}$</li>
</ul>
<h3>函数单调性与凹凸性</h3>
<ul>
<li>单调性判别：若 $f'(x) &gt; 0$，则 $f(x)$ 单调递增；若 $f'(x) &lt; 0$，则 $f(x)$ 单调递减</li>
<li>凹凸性判别：若 $f''(x) &gt; 0$，则 $f(x)$ 凹；若 $f''(x) &lt; 0$，则 $f(x)$ 凸</li>
</ul>
]]></content>
    <author><name>仙鹤</name></author>
    <category term="考试"/>
  </entry>
  <entry>
    <title>HTML入门指导</title>
    <link href="https://xianhe51920.github.io/posts/html_guide/" rel="alternate" type="text/html"/>
    <id>https://xianhe51920.github.io/posts/html_guide/</id>
    <published>2026-01-18T00:00:00.000Z</published>
    <updated>2026-01-18T00:00:00.000Z</updated>
    <summary>看b站课程自己做的笔记</summary>
    <content type="html"><![CDATA[<h1>html学习笔记</h1>
<p>近期学习了html，做了一些笔记，拿出来分享一下，学过的同学可以拿来查漏补缺，没有学过的同学也可以作为入门参考。</p>
<h1>目录</h1>
<ul>
<li><a href="#%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7-trae-ai%E7%BC%96%E8%BE%91%E5%99%A8">开发工具-Trae AI编辑器</a></li>
<li><a href="#%E6%B5%8F%E8%A7%88%E5%99%A8%E5%92%8C%E8%B0%83%E8%AF%95%E5%B7%A5%E5%85%B7">浏览器和调试工具</a></li>
<li><a href="#html%E5%88%9D%E4%BD%93%E9%AA%8C">HTML初体验</a>
<ul>
<li><a href="#01-%E5%88%9B%E5%BB%BA%E9%A1%B9%E7%9B%AE%E6%96%87%E4%BB%B6%E5%A4%B9">01 创建项目文件夹</a></li>
<li><a href="#02-%E5%BF%AB%E9%80%9F%E7%BC%96%E5%86%99%E4%BB%A3%E7%A0%81">02 快速编写代码</a></li>
<li><a href="#03-%E6%B5%8F%E8%A7%88%E5%99%A8%E9%A2%84%E8%A7%88">03 浏览器预览</a></li>
</ul>
</li>
<li><a href="#%E5%B8%B8%E7%94%A8%E9%85%8D%E7%BD%AE%E8%AE%BE%E7%BD%AE">常用配置设置</a>
<ul>
<li><a href="#%E7%BC%96%E8%BE%91%E5%99%A8%E5%AD%97%E4%BD%93%E5%A4%A7%E5%B0%8F">编辑器字体大小</a></li>
<li><a href="#%E4%BB%A3%E7%A0%81%E5%AD%97%E4%BD%93%E5%A4%A7%E5%B0%8F">代码字体大小</a></li>
<li><a href="#%E8%AE%BE%E7%BD%AE%E5%AD%97%E7%AC%A6%E7%BC%A9%E8%BF%9B">设置字符缩进</a></li>
<li><a href="#%E4%BF%9D%E5%AD%98%E8%87%AA%E5%8A%A8%E6%A0%BC%E5%BC%8F%E5%8C%96%E4%BB%A3%E7%A0%81">保存自动格式化代码</a></li>
</ul>
</li>
<li><a href="#html%E7%AE%80%E4%BB%8B">html简介</a>
<ul>
<li><a href="#%E4%BB%80%E4%B9%88%E6%98%AFhtml">什么是HTML?</a></li>
<li><a href="#%E6%A0%87%E7%AD%BE%E8%AF%AD%E6%B3%95">标签语法</a></li>
</ul>
</li>
<li><a href="#html%E6%96%87%E6%A1%A3">html文档</a>
<ul>
<li><a href="#%E6%96%87%E6%A1%A3%E7%B1%BB%E5%9E%8B">文档类型</a></li>
<li><a href="#html%E5%85%83%E7%B4%A0">html元素</a></li>
<li><a href="#head-%E5%85%83%E7%B4%A0"><code>&lt;head&gt;</code> 元素</a></li>
<li><a href="#%E5%AD%97%E7%AC%A6%E9%9B%86">字符集</a></li>
<li><a href="#%E7%A7%BB%E5%8A%A8%E7%AB%AF%E9%A1%B5%E9%9D%A2%E9%80%82%E9%85%8D">移动端页面适配</a></li>
<li><a href="#title%E5%85%83%E7%B4%A0">title元素</a></li>
<li><a href="#body%E5%85%83%E7%B4%A0">body元素</a></li>
</ul>
</li>
<li><a href="#%E6%A0%87%E7%AD%BE%E5%85%B3%E7%B3%BB%E4%BB%A5%E5%8F%8Aai%E5%B7%A5%E5%85%B7%E6%8E%92%E9%94%99">标签关系以及AI工具排错</a>
<ul>
<li><a href="#%E6%A0%87%E7%AD%BE%E5%85%B3%E7%B3%BB">标签关系</a></li>
<li><a href="#ai%E5%B7%A5%E5%85%B7%E6%8E%92%E9%94%99">AI工具排错</a></li>
</ul>
</li>
<li><a href="#%E6%A0%87%E9%A2%98%E5%92%8C%E6%AE%B5%E8%90%BD%E6%A0%87%E7%AD%BE%E4%BB%A5%E5%8F%8A%E8%AF%AD%E4%B9%89%E5%8C%96">标题和段落标签以及语义化</a>
<ul>
<li><a href="#html%E7%9A%84%E6%A0%87%E9%A2%98%E5%92%8C%E6%AE%B5%E8%90%BD">html的标题和段落</a></li>
<li><a href="#%E8%AF%AD%E4%B9%89%E5%8C%96%E4%BB%8B%E7%BB%8D">语义化介绍</a></li>
</ul>
</li>
<li><a href="#%E5%BC%BA%E8%B0%83%E9%87%8D%E8%A6%81%E6%80%A7%E6%A0%87%E7%AD%BE">强调重要性标签</a></li>
<li><a href="#%E6%B3%A8%E9%87%8A%E6%A0%87%E7%AD%BE">注释标签</a></li>
<li><a href="#%E5%9D%97%E7%BA%A7%E5%85%83%E7%B4%A0%E5%92%8C%E5%86%85%E8%81%94%E5%85%83%E7%B4%A0">块级元素和内联元素</a>
<ul>
<li><a href="#%E5%9D%97%E7%BA%A7%E5%85%83%E7%B4%A0">块级元素</a></li>
<li><a href="#%E5%86%85%E8%81%94%E5%85%83%E7%B4%A0">内联元素</a></li>
</ul>
</li>
<li><a href="#%E5%9B%BE%E5%83%8F%E6%A0%87%E7%AD%BE%E4%BB%A5%E5%8F%8A%E5%B8%B8%E8%A7%81%E6%A0%BC%E5%BC%8F">图像标签以及常见格式</a>
<ul>
<li><a href="#%E5%9B%BE%E5%83%8F%E6%A0%87%E7%AD%BE-img">图像标签 img</a></li>
<li><a href="#%E5%B8%B8%E8%A7%81%E5%9B%BE%E7%89%87%E6%A0%BC%E5%BC%8F">常见图片格式</a></li>
</ul>
</li>
<li><a href="#%E8%B7%AF%E5%BE%84%E5%88%86%E7%B1%BB">路径分类</a>
<ul>
<li><a href="#%E7%9B%B8%E5%AF%B9%E8%B7%AF%E5%BE%84">相对路径</a></li>
<li><a href="#%E7%BB%9D%E5%AF%B9%E8%B7%AF%E5%BE%84">绝对路径</a></li>
</ul>
</li>
<li><a href="#%E9%9F%B3%E8%A7%86%E9%A2%91%E6%A0%87%E7%AD%BE">音视频标签</a>
<ul>
<li><a href="#%E8%A7%86%E9%A2%91%E6%A0%87%E7%AD%BE">视频标签</a></li>
<li><a href="#%E9%9F%B3%E9%A2%91%E6%A0%87%E7%AD%BE">音频标签</a></li>
</ul>
</li>
<li><a href="#%E5%88%9B%E5%BB%BA%E8%B6%85%E7%BA%A7%E9%93%BE%E6%8E%A5%E4%BB%A5%E5%8F%8A%E9%94%9A%E7%82%B9%E9%93%BE%E6%8E%A5">创建超级链接以及锚点链接</a>
<ul>
<li><a href="#%E8%AF%AD%E6%B3%95">语法</a></li>
<li><a href="#%E5%85%B6%E4%BB%96%E5%B1%9E%E6%80%A7">其他属性</a></li>
<li><a href="#%E6%9C%80%E4%BD%B3%E9%93%BE%E6%8E%A5%E5%AE%9E%E8%B7%B5">最佳链接实践</a></li>
<li><a href="#%E9%94%9A%E7%82%B9%E9%93%BE%E6%8E%A5">锚点链接</a></li>
</ul>
</li>
<li><a href="#%E5%B8%83%E5%B1%80%E6%A0%87%E7%AD%BE">布局标签</a>
<ul>
<li><a href="#%E7%BD%91%E7%AB%99%E7%BB%93%E6%9E%84%E6%A0%87%E7%AD%BE">网站结构标签</a></li>
<li><a href="#%E6%97%A0%E8%AF%AD%E4%B9%89%E6%A0%87%E7%AD%BE">无语义标签</a></li>
</ul>
</li>
<li><a href="#%E5%88%97%E8%A1%A8%E6%A0%87%E7%AD%BE">列表标签</a>
<ul>
<li><a href="#%E6%97%A0%E5%BA%8F%E5%88%97%E8%A1%A8-ul">无序列表 ul</a></li>
<li><a href="#%E6%9C%89%E5%BA%8F%E5%88%97%E8%A1%A8-ol">有序列表 ol</a></li>
<li><a href="#%E6%8F%8F%E8%BF%B0%E5%88%97%E8%A1%A8-dl">描述列表 dl</a></li>
</ul>
</li>
<li><a href="#%E8%A1%A8%E6%A0%BC%E6%A0%87%E7%AD%BE%E5%92%8Cai%E5%90%88%E5%B9%B6%E5%8D%95%E5%85%83%E6%A0%BC">表格标签和AI合并单元格</a>
<ul>
<li><a href="#%E8%A1%A8%E6%A0%BC%E8%AF%AD%E6%B3%95">表格语法</a></li>
<li><a href="#%E8%A1%A8%E6%A0%BC%E5%90%88%E5%B9%B6%E5%8D%95%E5%85%83%E6%A0%BC">表格合并单元格</a></li>
</ul>
</li>
<li><a href="#%E8%A1%A8%E5%8D%95">表单</a>
<ul>
<li><a href="#%E8%A1%A8%E5%8D%95%E5%AE%B9%E5%99%A8">表单容器</a></li>
<li><a href="#%E8%A1%A8%E5%8D%95%E6%8E%A7%E4%BB%B6">表单控件</a></li>
<li><a href="#%E8%BE%85%E5%8A%A9%E6%A0%87%E7%AD%BE-label">辅助标签-label</a></li>
</ul>
</li>
<li><a href="#%E5%AD%97%E7%AC%A6%E5%AE%9E%E4%BD%93">字符实体</a></li>
</ul>
<hr />
<h1>开发工具-Trae AI编辑器</h1>
<p>字节跳动旗下的AI代码编辑器。</p>
<p>Trae与AI深度集成，提供:</p>
<ul>
<li>智能问答</li>
<li>代码自动补全</li>
<li>基于Agent的AI自动编程能力等</li>
</ul>
<p>现阶段主要是AI辅助开发:</p>
<ul>
<li>根据提示自动生成代码</li>
<li>根据错误检查修复代码</li>
<li>代码解读和分析</li>
<li>生成注释和代码优化</li>
<li>AI技术测评<br />
……</li>
</ul>
<hr />
<h1>浏览器和调试工具</h1>
<p>在浏览器中打开调试工具:</p>
<ol>
<li>右击页面空白处 → 检查。</li>
<li>快捷键 F12</li>
</ol>
<hr />
<h1>HTML初体验</h1>
<h3>01 创建项目文件夹</h3>
<p>所有相关素材都放到这个文件夹里面，这个文件夹也称为 <strong>根目录</strong></p>
<h3>02 快速编写代码</h3>
<p>快捷生成代码并编写，可以使用编辑器自带的Emmet语法</p>
<h3>03 浏览器预览</h3>
<p>通过浏览器插件 live Server 在编辑器中直接打开浏览器预览效果</p>
<hr />
<h1>常用配置设置</h1>
<h3>编辑器字体大小</h3>
<ul>
<li>ctrl + + 放大</li>
<li>ctrl + - 缩小</li>
</ul>
<h3>代码字体大小</h3>
<ul>
<li>文件→首选项→设置（ctrl+,）→Editor设置→Font Size</li>
</ul>
<h3>设置字符缩进</h3>
<ul>
<li>常用设置→ tabSize</li>
<li>为了和后面Vue衔接，tab缩进2个字符。</li>
</ul>
<h3>保存自动格式化代码</h3>
<ul>
<li>文本格式化→格式化→Format on Save</li>
</ul>
<hr />
<h1>html简介</h1>
<h2>什么是HTML?</h2>
<p>超文本标记语言 ---- HyperText Markup Language，是一种用来告知浏览器如何组织页面的标记语言。</p>
<h3>知识点：</h3>
<ol>
<li>HTML 由一系列的标签组成。</li>
<li>标记也称为标签（元素）。</li>
<li>大小写都可以，建议小写。</li>
</ol>
<p>简单说，学习HTML就是学习标签</p>
<h2>标签语法</h2>
<h3>标签组成</h3>
<p>符号：标签用 &lt;&gt; 尖括号表示<br />
组成：开始标签、结束标签和内容<br />
别称：标签也称为元素</p>
<h3>标签分类</h3>
<p>大部分都是双标签<br />
少数单标签</p>
<h3>开发技巧</h3>
<p>Trae编辑器中直接写标签，然后按Tab或者回车即可</p>
<hr />
<h1>html文档</h1>
<h2>文档类型</h2>
<pre><code>&lt;!DOCTYPE html&gt;
</code></pre>
<p>HTML5 的文档类型声明，它告诉浏览器当前页面是使用 HTML5 规范编写的</p>
<h2>html元素</h2>
<pre><code>&lt;html&gt;
</code></pre>
<p>在元素中，<code>lang</code>用于声明网页的主要语言，帮助浏览器、搜索引擎等正确处理页面内容。</p>
<ul>
<li><code>en</code>代表英语，这意味着该页面的主要语言是英语</li>
<li><code>zh-CN</code>代表中文。</li>
</ul>
<hr />
<h2><code>&lt;head&gt;</code> 元素</h2>
<p>头部元素。包含了文档的元（meta）数据。<br />
主要保存供机器处理的信息，而非人类可读信息</p>
<h2>字符集</h2>
<pre><code>&lt;meta charset="UTF-8"&gt;
</code></pre>
<p>该文档的字符集设置为UTF-8</p>
<p>UTF-8 包括绝大多数人类书面语言的大多数字符。</p>
<p>有了这个设置，页面现在可以处理它可能包含的任何文本内容，如果不加这句话可能会引起乱码。</p>
<hr />
<h2>移动端页面适配</h2>
<pre><code>&lt;meta name="viewport" content="width=device-width,initial-scale=1.0"&gt;
</code></pre>
<p>开发者能确保网页在移动设备上以最佳状态呈现，提升用户浏览体验。</p>
<h2>title元素</h2>
<p>这设置了页面的标题，也就是出现在该页面加载的浏览器标签中的内容</p>
<h2>body元素</h2>
<p>包含了页面所有显示在页面上的内容，包含文本、图片、视频、游戏、播放音频轨道等等</p>
<hr />
<h1>标签关系以及AI工具排错</h1>
<h2>标签关系</h2>
<h3>并列关系</h3>
<p>也称为兄弟关系</p>
<pre><code>&lt;head&gt;&lt;/head&gt;
&lt;body&gt;&lt;/body&gt;
</code></pre>
<h3>嵌套关系</h3>
<p>也称为父子关系</p>
<pre><code>&lt;head&gt;
  &lt;title&gt;&lt;/title&gt;
&lt;/head&gt;
</code></pre>
<h2>AI工具排错</h2>
<p>实际开发中多层嵌套最容易出问题。<br />
但是我们可以利用小技巧或者AI工具帮助我们理清标签的关系</p>
<hr />
<h1>标题和段落标签以及语义化</h1>
<h2>html的标题和段落</h2>
<h3>标题标签 h</h3>
<p><strong>场景</strong>：在新闻和文章的页面中，都离不开标题，用来突出显示文章主题</p>
<h4>语法</h4>
<pre><code>&lt;h1&gt;一级标题&lt;/h1&gt;
&lt;h2&gt;二级标题&lt;/h2&gt;
&lt;h3&gt;三级标题&lt;/h3&gt;
&lt;h4&gt;四级标题&lt;/h4&gt;
&lt;h5&gt;五级标题&lt;/h5&gt;
&lt;h6&gt;六级标题&lt;/h6&gt;
</code></pre>
<h4>显示特点</h4>
<p><strong>显示特点</strong>
标题文字会加粗显示，并且每行只显示一个</p>
<p><strong>h1 唯一性</strong>
最好只对每个页面使用一次<br />
<code>&lt;h1&gt;</code>——这是顶级标题<br />
如：新闻的标题、网页的logo部分</p>
<p><strong>层次性</strong>
在现有的六个标题层次中，除非觉得有必要否则应该争取每页使用不超过三个</p>
<h3>段落标签 p</h3>
<p>HTML 元素表示文本的一个段落。</p>
<p><strong>语法</strong>：</p>
<pre><code>&lt;p&gt;里面是段落文本&lt;/p&gt;
</code></pre>
<p>每行只显示一个，文字显示不会自动换行</p>
<p>段落的相关样式请用 CSS 设置。</p>
<h2>语义化介绍</h2>
<p>指根据内容结构和含义选择恰当的html元素
<strong>语义化的好处</strong>：</p>
<ol>
<li>更清晰的代码结构</li>
<li>对搜索引擎更友好</li>
<li>更好的可访问性...</li>
</ol>
<p><strong>语义性</strong>：</p>
<blockquote>
<p>HTML 的主要工作之一是赋予文本结构，使浏览器能够按照开发者的意图显示 HTML 文档。本文介绍了如何使用 HTML 通过定义标题和段落来提供基本的页面结构。</p>
<p>实现结构
所涉及的元素具体代表什么，完全取决于作者编辑的内容，只要层次结构是合理的。在创建此类结构时，只需要记住一条最佳实践。</p>
<p>为什么我们需要结构化？
这是因为元素有素给内容结构，所以浏览器不知道什么是标题，什么是段落。</p>
<p>用户在阅读网页时，往往会快速浏览以查找相关内容，经常是阅读开头的标题（我们通常在一个网页上会花费很少的时间）。如果用户不能在几秒内看到一些有用的内容，他们很可能会感到沮丧并离开。</p>
<p>为什么我们需要语义？
我们需要确保使用了正确的元素来给予内容正确的含义、作用以及外形。在这里，h1 元素也是一个语义元素。它所包裹的文本具有“页面上的顶级标题”的作用（或意义）。</p>
<p>一般来说，浏览器会给它一个更大的字形来让它看上去像个标题。更重要的是，它的语义将以多种方式被使用，比如通过上文提到的搜索引擎和屏幕阅读器。</p>
</blockquote>
<hr />
<h1>强调重要性标签</h1>
<p>HTML 也提供了相应的标签，使其具有加粗、倾斜、下划线等效果，来着重强调某些文字。</p>
<p><strong>带有语义现在更推荐使用</strong></p>
<table>
<thead>
<tr>
<th>标签</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>&lt;strong&gt;&lt;/strong&gt;</code></td>
<td>加粗</td>
</tr>
<tr>
<td><code>&lt;em&gt;&lt;/em&gt;</code></td>
<td>倾斜</td>
</tr>
<tr>
<td><code>&lt;ins&gt;&lt;/ins&gt;</code></td>
<td>下划线</td>
</tr>
<tr>
<td><code>&lt;del&gt;&lt;/del&gt;</code></td>
<td>删除线</td>
</tr>
</tbody>
</table>
<p><strong>无语义，不推荐使用，除非特殊情况</strong></p>
<table>
<thead>
<tr>
<th>标签</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>&lt;b&gt;&lt;/b&gt;</code></td>
<td>加粗</td>
</tr>
<tr>
<td><code>&lt;i&gt;&lt;/i&gt;</code></td>
<td>倾斜</td>
</tr>
<tr>
<td><code>&lt;u&gt;&lt;/u&gt;</code></td>
<td>下划线</td>
</tr>
<tr>
<td><code>&lt;s&gt;&lt;/s&gt;</code></td>
<td>删除线</td>
</tr>
</tbody>
</table>
<hr />
<h1>注释标签</h1>
<p>在 HTML 中，注释标签用于添加不会在浏览器中显示的注释或说明。</p>
<p>语法：</p>
<pre><code>&lt;!-- 这里是注释内容 --&gt;
</code></pre>
<blockquote>
<p>快捷键：ctrl + /</p>
</blockquote>
<p>特点：</p>
<ol>
<li>注释内容不会显示在网页上</li>
<li>可以跨越多行</li>
<li>常用于代码说明、临时禁用代码或给其他开发者留言</li>
</ol>
<h1>块级元素和内联元素</h1>
<p><strong>HTML标签分类</strong>：<br />
在 HTML 中有两种元素类别：<br />
块级元素和内联元素。<br />
分类不同决定着展示形式不一样。</p>
<h2>块级元素</h2>
<ul>
<li>块级元素独占一行</li>
<li>它可以嵌套其他元素</li>
<li>常见的比如p、h、div等</li>
</ul>
<h2>内联元素</h2>
<ul>
<li>可以一行放多个，通常与文本一起使用</li>
<li>不能嵌套块级元素，可以嵌套其他内联元素</li>
<li>常见比如 strong、em、a 等</li>
</ul>
<p>&lt;font color="red"&gt;注意事项：&lt;/font&gt;<br />
p里面不能嵌套其他块级元素<br />
段落里面主要放文字相关，比如内联元素</p>
<p>但是元素分类不是一直不可更改的，后面的CSS可以通过display改变他们显示模式</p>
<hr />
<h1>图像标签以及常见格式</h1>
<h2>图像标签 img</h2>
<p><strong>作用</strong>：在网页中显示图片<br />
<strong>语法</strong>：</p>
<pre><code>&lt;img src="图片路径" alt="图片描述"&gt;
</code></pre>
<p><strong>语法说明</strong>:
<strong>单标签（空元素）</strong></p>
<ul>
<li>默认包含两个属性：<strong>src</strong> 和 <strong>alt</strong></li>
</ul>
<p><strong>src 属性</strong></p>
<ul>
<li>指向要插入到页面的<strong>图像地址</strong></li>
</ul>
<p><strong>alt 属性</strong></p>
<ul>
<li><strong>备选文本</strong>，用于在图片无法显示或者因为网速慢情况下显示的文字</li>
</ul>
<p>&lt;font color="red"&gt;<strong>注意</strong>：&lt;/font&gt;</p>
<ul>
<li>属性采取键值对形式：属性="值"</li>
<li>属性之间没有必要的先后顺序</li>
<li>但是属性之间必须有空格分隔</li>
</ul>
<p><strong>其他属性</strong>：</p>
<table>
<thead>
<tr>
<th>属性</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td>width</td>
<td>设置图片<strong>宽度</strong>（建议css修改）</td>
</tr>
<tr>
<td>height</td>
<td>设置图片<strong>高度</strong>（建议css修改）</td>
</tr>
<tr>
<td>title</td>
<td><strong>图像标题</strong>。鼠标悬停显示的文本</td>
</tr>
</tbody>
</table>
<p><strong>图片属性总结</strong>：</p>
<table>
<thead>
<tr>
<th>属性</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td>src</td>
<td>指向要插入到页面的<strong>图像地址</strong></td>
</tr>
<tr>
<td>alt</td>
<td><strong>备选文本</strong>。图片无法显示提示的文字</td>
</tr>
<tr>
<td>width</td>
<td>设置图片<strong>宽度</strong>（建议css修改）</td>
</tr>
<tr>
<td>height</td>
<td>设置图片<strong>高度</strong>（建议css修改）</td>
</tr>
<tr>
<td>title</td>
<td><strong>图像标题</strong>。鼠标悬停显示的文本</td>
</tr>
</tbody>
</table>
<p>&lt;font color="red"&gt;<strong>注意</strong>：&lt;/font&gt;</p>
<ul>
<li>属性采取键值对形式：属性="值"</li>
<li>属性之间没有必要的先后顺序，但属性之间必须有空格分隔</li>
</ul>
<h2>常见图片格式</h2>
<h3>常见图片格式对照表</h3>
<table>
<thead>
<tr>
<th>格式</th>
<th>特点</th>
<th>适用场景</th>
</tr>
</thead>
<tbody>
<tr>
<td>jpeg / jpg</td>
<td>有损压缩技术，放大会变得模糊或有锯齿</td>
<td>摄影照片、网页图片（非透明背景）</td>
</tr>
<tr>
<td>png</td>
<td>无损压缩，支持透明度</td>
<td>Logo、网页图形、需要透明度的图像</td>
</tr>
<tr>
<td>gif</td>
<td>支持动画，最多256色（索引色）；支持简单动画和透明背景</td>
<td>简单动画、表情包、低色彩图形</td>
</tr>
<tr>
<td>webp</td>
<td>Google 开发的现代格式；支持有损/无损压缩、透明度和动画</td>
<td>网页优化（替代 JPEG/PNG/GIF），代表：淘宝等</td>
</tr>
<tr>
<td>avif</td>
<td>基于 AV1 视频编码，支持高压缩率和 HDR；压缩效率优于 WebP</td>
<td>未来网页优化，需高性能编码的场景（B站、京东等）</td>
</tr>
</tbody>
</table>
<hr />
<h1>路径分类</h1>
<p><strong>路径</strong>：<br />
在html中，<strong>路径</strong>用于<strong>指定</strong>文件（如图像，样式表现或其他网页）的<strong>位置</strong><br />
白话：图片相对于html文件的位置，html怎么找到图片</p>
<h2>相对路径</h2>
<p>相对于<strong>当前文件位置</strong>的路径</p>
<h3>图片路径引用</h3>
<p>1.<strong>同一目录</strong></p>
<ul>
<li><strong>说明</strong>：直接使用文件名即可，<code>./</code> 表示当前文件夹（可省略）</li>
<li><strong>示例</strong>：</li>
</ul>
<pre><code>&lt;img src="pig.jpg" alt="这是佩奇"&gt;
&lt;img src="./pig.jpg" alt="这是佩奇"&gt;
</code></pre>
<p>2.<strong>下级子目录</strong></p>
<ul>
<li><strong>说明</strong>：使用 <code>目录名/文件名</code> 的形式</li>
<li><strong>示例</strong>：</li>
</ul>
<pre><code>&lt;img src="img/pig.jpg" alt="这是佩奇"&gt;
</code></pre>
<p>3.<strong>上级目录</strong></p>
<ul>
<li><strong>说明</strong>：使用 <code>../</code> 返回上一级目录</li>
<li><strong>示例</strong>：</li>
</ul>
<pre><code>&lt;img src="../pig.jpg" alt="这是佩奇"&gt;
</code></pre>
<h2>绝对路径</h2>
<p>绝对路径是<strong>从根目录开始的完整路径</strong>，包含完整的URL地址</p>
<h3>图片路径引用</h3>
<p>1.<strong>从盘符开始</strong>（本地绝对路径）<br />
适用于本地文件的完整路径，直接写盘符开头的文件位置。<br />
示例：</p>
<pre><code>&lt;img src="E:\HTML5\代码\pic.jpg" alt="示例图片"&gt;  
</code></pre>
<p>2.<strong>完整网络地址（URL）</strong><br />
适用于互联网上的图片，直接写完整的 HTTP/HTTPS 地址。<br />
示例：</p>
<pre><code>&lt;img src="https://www.example.com/images/pic.jpg" alt="示例图片"&gt;  
</code></pre>
<hr />
<h1>音视频标签</h1>
<h2>视频标签</h2>
<p>使网页变得更加丰富多彩，吸引用户
<strong>语法</strong>：</p>
<pre><code>&lt;video src=""&gt;&lt;/video&gt;  
&lt;video src="video.mp4" controls width="300"&gt;&lt;/video&gt;  
</code></pre>
<p><strong>核心属性</strong>：</p>
<ul>
<li><strong>src 属性</strong>：指向要插入到页面的视频地址</li>
<li><strong>controls</strong>：显示浏览器自带播放控件</li>
<li><strong>width/height 属性</strong>：设置视频的宽度和高度</li>
</ul>
<p><strong>视频标签其他属性</strong>：</p>
<table>
<thead>
<tr>
<th>属性</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td>autoplay</td>
<td>自动播放</td>
</tr>
<tr>
<td>loop</td>
<td>循环播放</td>
</tr>
<tr>
<td>muted</td>
<td>静音</td>
</tr>
<tr>
<td>poster</td>
<td>预览图像</td>
</tr>
</tbody>
</table>
<p><strong>视频标签兼容性写法：</strong></p>
<p>不同浏览器支持的视频格式有所差异</p>
<table>
<thead>
<tr>
<th>浏览器</th>
<th>MP4</th>
<th>WebM</th>
<th>Ogg</th>
</tr>
</thead>
<tbody>
<tr>
<td>Internet Explorer</td>
<td>YES</td>
<td>NO</td>
<td>NO</td>
</tr>
<tr>
<td>Chrome</td>
<td>YES</td>
<td>YES</td>
<td>YES</td>
</tr>
<tr>
<td>Firefox</td>
<td>YES</td>
<td>YES</td>
<td>YES</td>
</tr>
<tr>
<td>Safari</td>
<td>YES</td>
<td>NO</td>
<td>NO</td>
</tr>
<tr>
<td>Opera</td>
<td>YES (从 Opera 25 起)</td>
<td>YES</td>
<td>YES</td>
</tr>
</tbody>
</table>
<p>为了视频尽可能兼容不同浏览器，建议使用以下兼容性写法：</p>
<pre><code>&lt;video controls controls muted loop poster="poster.jpg"&gt;  
  &lt;source src="video.ogg" type="video/ogg"&gt;  
  &lt;source src="video.mp4" type="video/mp4"&gt;  
  &lt;source src="video.webm" type="video/webm"&gt;  
  &lt;p&gt;您的浏览器不支持 HTML 5 Video 标签，请升级浏览器。&lt;/p&gt;  
&lt;/video&gt;  
</code></pre>
<p><strong>核心原理</strong>：</p>
<ol>
<li>将 <code>src</code> 属性放在几个单独的 <code>&lt;source&gt;</code> 元素当中，这些元素分别指向各自的资源。</li>
<li>浏览器会检查 <code>&lt;source&gt;</code> 元素，并且播放第一个与其自身相匹配的媒体。</li>
<li>每个 <code>&lt;source&gt;</code> 元素都含有 <code>type</code> 属性，浏览器也会通过检查这个属性来迅速跳过那些不支持的格式。如果没有添加 <code>type</code> 属性，浏览器会尝试加载每一个文件，直到找到一个能正确播放的格式，但这样会消耗大量的时间和资源。</li>
</ol>
<h2>音频标签</h2>
<p>使网页变得更加丰富多彩，吸引用户<br />
<strong>语法</strong>：</p>
<pre><code>&lt;audio src=""&gt;&lt;/audio&gt;  
&lt;audio src="audio.mp3" controls&gt;&lt;/audio&gt;  
</code></pre>
<p><strong>核心属性</strong>：</p>
<ul>
<li><strong>src 属性</strong>：指向要插入到页面的音频地址</li>
<li><strong>controls</strong>：显示浏览器自带播放控件</li>
</ul>
<p><strong>音频标签其他属性</strong>：</p>
<table>
<thead>
<tr>
<th>属性</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td>autoplay</td>
<td>自动播放</td>
</tr>
<tr>
<td>loop</td>
<td>循环播放</td>
</tr>
<tr>
<td>muted</td>
<td>静音</td>
</tr>
</tbody>
</table>
<p><strong>音频标签兼容性写法：</strong></p>
<p>不同浏览器支持的音频格式有所差异</p>
<table>
<thead>
<tr>
<th>浏览器</th>
<th>MP3</th>
<th>WAV</th>
<th>Ogg</th>
</tr>
</thead>
<tbody>
<tr>
<td>Internet Explorer</td>
<td>YES</td>
<td>NO</td>
<td>NO</td>
</tr>
<tr>
<td>Chrome</td>
<td>YES</td>
<td>YES</td>
<td>YES</td>
</tr>
<tr>
<td>Firefox</td>
<td>YES</td>
<td>YES</td>
<td>YES</td>
</tr>
<tr>
<td>Safari</td>
<td>YES</td>
<td>NO</td>
<td>NO</td>
</tr>
<tr>
<td>Opera</td>
<td>YES (从 Opera 25 起)</td>
<td>YES</td>
<td>YES</td>
</tr>
</tbody>
</table>
<p><strong>音频标签其他兼容性写法：</strong></p>
<pre><code>&lt;audio controls controls muted loop&gt;  
  &lt;source src="audio.mp3" type="audio/mpeg"&gt;  
  &lt;source src="audio.wav" type="audio/wav"&gt;  
  &lt;source src="audio.ogg" type="audio/ogg"&gt;  
  &lt;p&gt;您的浏览器不支持 HTML 5 Audio 标签，请升级浏览器。&lt;/p&gt;  
&lt;/audio&gt;  
</code></pre>
<p><strong>核心原理</strong>：和视频标签的兼容性写法类似，只是将视频标签的 <code>&lt;source&gt;</code> 元素替换为音频标签的 <code>&lt;source&gt;</code> 元素。</p>
<hr />
<h1>创建超级链接以及锚点链接</h1>
<p>超链接使我们能够将我们的文档链接到任何其他文档（或其他资源），也可以链接到文档的指定部分。</p>
<h2>语法</h2>
<pre><code>&lt;a href=""&gt;&lt;/a&gt;  
&lt;a href="https://www.deepseek.com/"&gt;DeepSeek官网&lt;/a&gt;  
</code></pre>
<p><strong>核心属性</strong>：</p>
<ul>
<li><strong>href 属性</strong>（也称为超文本引用或目标）：它将包含一个网址，用来创建一个基本链接。</li>
</ul>
<p><strong>链接内容</strong>：<br />
链接可以包含除了<strong>自身之外</strong>的其他元素，比如文字、标题、图片、视频等。</p>
<h2>其他属性</h2>
<p><strong>title 属性</strong></p>
<ul>
<li>作用：鼠标悬停时的提示文字</li>
<li>示例：</li>
</ul>
<pre><code>&lt;a href="https://www.example.com/" title="这是示例网站"&gt;示例链接&lt;/a&gt;  
</code></pre>
<p><strong>target 属性</strong></p>
<ul>
<li>作用：控制链接打开的页面方式</li>
<li>常用取值：
<ul>
<li><code>_self</code>：当前窗口打开（默认值）</li>
<li><code>_blank</code>：新窗口打开</li>
<li><code>_parent</code>：父窗口打开</li>
<li><code>_top</code>：顶部窗口打开</li>
</ul>
</li>
</ul>
<h2>最佳链接实践</h2>
<h3>空连接</h3>
<p>在HTML中，空连接通常指的是没有实际指向目标的超链接，符号是<code>#</code></p>
<pre><code>&lt;a href="#"&gt;商品列表&lt;/a&gt;
</code></pre>
<h3>下载链接</h3>
<p>如果是exe或者压缩包点击是下载</p>
<pre><code>&lt;a href="download.exe"&gt;下载window版本&lt;/a&gt;
</code></pre>
<h3>邮件链接</h3>
<p>某些简单场景或个人使用情况下使用。<br />
公司或者流量大的网站慎用，有垃圾邮件等风险，其他方案替代。</p>
<pre><code>&lt;a href="mailto:pig@mozilla.org"&gt;给我发邮件&lt;/a&gt;
</code></pre>
<h2>锚点链接</h2>
<p>锚点链接允许用户在同一个页面内跳转到指定位置。<br />
非常适合<strong>长页面</strong>导航。</p>
<h3>锚点链接实现步骤</h3>
<p><strong>定义锚点目标</strong><br />
使用<code>id</code>属性创建锚点目标<br />
比如：</p>
<pre><code>&lt;h2 id="1"&gt;第一部分&lt;/h2&gt;
</code></pre>
<p><strong>创建跳转链接</strong><br />
使用<code>#</code>标记锚点目标<br />
比如：</p>
<pre><code>&lt;a href="#1"&gt;跳转到第一部分&lt;/a&gt;
</code></pre>
<h3>多学一招</h3>
<p>想要点击链接之后，<strong>页面具有滑动效果。</strong><br />
请在<code>&lt;head&gt;</code>标签中添加一下css代码。</p>
<pre><code>&lt;style&gt;
html {
  scroll-behavior: smooth;
}
&lt;/style&gt;
</code></pre>
<hr />
<h1>布局标签</h1>
<h1>布局标签</h1>
<h2>网站结构标签</h2>
<p>网页的外观多种多样，但是大概都包含：页眉、导航栏、主内容、侧边栏、页脚等</p>
<table>
<thead>
<tr>
<th>标签</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>&lt;header&gt;</code></td>
<td>网页页眉(头部)</td>
</tr>
<tr>
<td><code>&lt;main&gt;</code></td>
<td>网页内容。每个页面只能有一个</td>
</tr>
<tr>
<td><code>&lt;nav&gt;</code></td>
<td>导航栏</td>
</tr>
<tr>
<td><code>&lt;article&gt;</code></td>
<td>文章相关</td>
</tr>
<tr>
<td><code>&lt;section&gt;</code></td>
<td>分块</td>
</tr>
<tr>
<td><code>&lt;aside&gt;</code></td>
<td>侧边栏</td>
</tr>
<tr>
<td><code>&lt;footer&gt;</code></td>
<td>页面页脚(底部)</td>
</tr>
</tbody>
</table>
<p><img src="/images/posts/html_guide/image.png" alt="image" /></p>
<p>&lt;font color="red"&gt;
这些标签受浏览器兼容性问题影响，PC端根据公司需求，移动端放心使用
&lt;/font&gt;</p>
<h2>无语义标签</h2>
<p>没有合适语义标签时，在进行一些布局时候可以选择：div 和 span 标签</p>
<h3>div标签</h3>
<p>特点：</p>
<ol>
<li>块级元素：默认独占一行，前后会自动换行。</li>
<li>通常用于布局结构，作为其他元素的容器。</li>
<li>可以包含其他块级或行内元素。</li>
<li>默认没有语义。</li>
</ol>
<h3>span标签</h3>
<p>特点：</p>
<ol>
<li>行内元素：不会换行，仅包裹内容的一部分。</li>
<li>用于对文本或行内元素的局部样式或操作。</li>
<li>默认没有语义。</li>
</ol>
<h2>列表标签</h2>
<p>HTML 列表是网页内容组织的重要元素。可以让我们显示内容更加整齐有序（后面的笔记会有详细内容）</p>
<hr />
<h1>列表标签</h1>
<h2>无序列表 ul</h2>
<p>顺序无关紧要的列表<br />
无序列表是我们布局中非常常用，常用于一些整齐对齐的模块中使用</p>
<p><strong>场景</strong></p>
<p><img src="/images/posts/html_guide/image-1.png" alt="image-1|503x373" />
<img src="/images/posts/html_guide/image-2.png" alt="image-2|253x500" /></p>
<h3>基本语法</h3>
<pre><code>&lt;ul&gt;
  &lt;li&gt;猪爸爸&lt;/li&gt;
  &lt;li&gt;猪妈妈&lt;/li&gt;
  &lt;li&gt;佩奇&lt;/li&gt;
  &lt;li&gt;乔治&lt;/li&gt;
&lt;/ul&gt;
</code></pre>
<p><strong>渲染效果</strong></p>
<ul>
<li>猪爸爸</li>
<li>猪妈妈</li>
<li>佩奇</li>
<li>乔治</li>
</ul>
<p><strong>标签说明</strong><br />
<strong><code>&lt;ul&gt;</code></strong></p>
<ul>
<li>定义列表的容器</li>
<li>只能包含 <code>&lt;li&gt;</code> 元素</li>
</ul>
<p><strong><code>&lt;li&gt;</code></strong></p>
<ul>
<li>定义列表的选项</li>
<li>里面可以放其他HTML元素</li>
</ul>
<h2>有序列表 ol</h2>
<p>顺序有关紧要的列表<br />
有序列表再布局中较少，了解即可，实际开发即使有顺序，我们一般也是用其他属性</p>
<h2>有序列表ol</h2>
<h3>基本语法</h3>
<pre><code>&lt;ol&gt;
  &lt;li&gt;看视频&lt;/li&gt;
  &lt;li&gt;写代码&lt;/li&gt;
  &lt;li&gt;做笔记&lt;/li&gt;
  &lt;li&gt;多复习&lt;/li&gt;
&lt;/ol&gt;
</code></pre>
<p><strong>渲染效果</strong></p>
<ol>
<li>看视频</li>
<li>写代码</li>
<li>做笔记</li>
<li>多复习</li>
</ol>
<p><strong>标签说明</strong><br />
<strong><code>&lt;ol&gt;</code></strong></p>
<ul>
<li>定义列表的容器</li>
<li>只能包含 <code>&lt;li&gt;</code> 元素</li>
</ul>
<p><strong><code>&lt;li&gt;</code></strong></p>
<ul>
<li>定义列表的选项</li>
<li>里面可以放其他HTML元素</li>
</ul>
<h2>描述列表 dl</h2>
<p>标记一组项目及相关描述<br />
描述列表再我们布局中主要是在页面底部</p>
<p><strong>场景</strong></p>
<p><img src="/images/posts/html_guide/image-3.png" alt="image-3" /></p>
<h2>描述列表dl</h2>
<h3>基本语法</h3>
<pre><code>&lt;dl&gt;
  &lt;dt&gt;家电&lt;/dt&gt;
  &lt;dd&gt;电视&lt;/dd&gt;
  &lt;dd&gt;冰箱&lt;/dd&gt;
  &lt;dd&gt;空调&lt;/dd&gt;
  &lt;dd&gt;烟灶&lt;/dd&gt;
&lt;/dl&gt;
</code></pre>
<p><strong>渲染效果</strong><br />
家电
电视
冰箱
空调
烟灶</p>
<p><strong>标签说明</strong><br />
<strong><code>&lt;dl&gt;</code></strong></p>
<ul>
<li>定义列表的容器</li>
<li>只能包含 <code>&lt;dt&gt;</code> 和 <code>&lt;dd&gt;</code> 元素</li>
</ul>
<p><strong><code>&lt;dt&gt;</code></strong></p>
<ul>
<li>定义被描述的术语</li>
<li>通常显示为左对齐或加粗</li>
<li>一个 <code>&lt;dt&gt;</code> 可以对应多个 <code>&lt;dd&gt;</code></li>
</ul>
<p><strong><code>&lt;dd&gt;</code></strong></p>
<ul>
<li>包含术语的定义或描述</li>
<li>通常显示为缩进形式</li>
<li>可以包含段落、图片、链接等其他HTML元素</li>
</ul>
<hr />
<h1>表格标签和AI合并单元格</h1>
<p>表格作用：以结构化的方式展示行列数据，使信息清晰，易读且便于对比<br />
网页场景：主要用于<strong>数据展示</strong>或者后台管理系统的<strong>信息展示</strong></p>
<p><strong>场景</strong></p>
<p><img src="/images/posts/html_guide/image-4.png" alt="image-4" /></p>
<h2>表格语法</h2>
<p><strong>表格基本语法</strong></p>
<pre><code>&lt;table border="1"&gt;
  &lt;!--border属性：表格边框属性(现阶段用来看效果，后期用css替代)--&gt;
    &lt;tr&gt;
      &lt;th&gt;姓名&lt;/th&gt;
      &lt;th&gt;年龄&lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;佩奇&lt;/td&gt;
      &lt;td&gt;6&lt;/td&gt;
    &lt;/tr&gt;
&lt;/table&gt;
</code></pre>
<p><strong>表格基本组成</strong></p>
<table>
<thead>
<tr>
<th>标签</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>&lt;table&gt;</code></td>
<td>表格容器标签</td>
</tr>
<tr>
<td><code>&lt;tr&gt;</code></td>
<td>行标签</td>
</tr>
<tr>
<td><code>&lt;td&gt;</code></td>
<td>单元格标签</td>
</tr>
<tr>
<td><code>&lt;th&gt;</code></td>
<td>表头单元格（可以让里面的文字水平和垂直居中）</td>
</tr>
<tr>
<td><code>&lt;border&gt;</code></td>
<td>表格边框属性(现阶段用来看效果，后期用css替代)</td>
</tr>
</tbody>
</table>
<p><img src="/images/posts/html_guide/image-5.png" alt="image-5" /></p>
<p>表格结构标签：增强表格语义，让表格结构更加清晰</p>
<table>
<thead>
<tr>
<th>标签</th>
<th>作用</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>&lt;thead&gt;</code></td>
<td>定义表格的头部区域</td>
</tr>
<tr>
<td><code>&lt;tbody&gt;</code></td>
<td>定义表格的主体内容</td>
</tr>
<tr>
<td><code>&lt;tfoot&gt;</code></td>
<td>定义表格的底部区域</td>
</tr>
</tbody>
</table>
<p><strong>示例</strong>：</p>
<pre><code>&lt;table border="1"&gt;
  &lt;!--border属性：表格边框属性(现阶段用来看效果，后期用css替代)--&gt;
  &lt;thead&gt;
    &lt;!--thead标签：定义表格的头部区域--&gt;
    &lt;tr&gt;
      &lt;th&gt;姓名&lt;/th&gt;
      &lt;th&gt;年龄&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;!--tbody标签：定义表格的主体内容--&gt;
    &lt;tr&gt;
      &lt;td&gt;佩奇&lt;/td&gt;
      &lt;td&gt;6&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
  &lt;tfoot&gt;
    &lt;!--tfoot标签：定义表格的底部区域--&gt;
    &lt;tr&gt;
      &lt;td colspan="2"&gt;合计1人&lt;/td&gt;
      &lt;!--colspan属性：合并列单元格--&gt;
    &lt;/tr&gt;
  &lt;/tfoot&gt;
&lt;/table&gt;
</code></pre>
<h2>表格合并单元格</h2>
<p>表格开发中很少使用合并，因为会导致表格难以维护，且可能影响响应式适配（尤其在移动端）。如果特殊情况，可以借助于AI。</p>
<p><strong>表格合并原理</strong></p>
<ol>
<li>确定合并类型：跨行（rowspan）或跨列（colspan）</li>
<li>遵循左上原则，找到目标单元格并填写合并数量</li>
<li>删除合并后多余的单元格</li>
</ol>
<p><img src="/images/posts/html_guide/image-6.png" alt="image-6" /></p>
<p><strong>示例</strong>：</p>
<pre><code>&lt;table border="1"&gt;
  &lt;tr&gt;
    &lt;th&gt;姓名&lt;/th&gt;
    &lt;th&gt;年龄&lt;/th&gt;
    &lt;th&gt;性别&lt;/th&gt;
    &lt;th&gt;爱好&lt;/th&gt;
    &lt;th&gt;居住地&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;张三&lt;/td&gt;
    &lt;td&gt;18&lt;/td&gt;
    &lt;td&gt;男&lt;/td&gt;
    &lt;td&gt;足球&lt;/td&gt;
    &lt;td&gt;北京&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;李四&lt;/td&gt;
    &lt;td&gt;20&lt;/td&gt;
    &lt;td&gt;女&lt;/td&gt;
    &lt;td&gt;篮球&lt;/td&gt;
    &lt;td&gt;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;王五&lt;/td&gt;
    &lt;td&gt;22&lt;/td&gt;
    &lt;td&gt;男&lt;/td&gt;
    &lt;td&gt;篮球&lt;/td&gt;
    &lt;td&gt;深圳&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td colspan="5"&gt;日期：2023年10月1日&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
</code></pre>
<p><strong>渲染效果</strong></p>
<p><img src="/images/posts/html_guide/image-7.png" alt="image-7" /></p>
<hr />
<h1>表单</h1>
<p><strong>表单</strong>：是用于<strong>收集用户输入数据</strong>，并将数据提交到后端进行处理
<strong>场景</strong>：</p>
<ul>
<li>用户登录/注册</li>
<li>搜索框</li>
<li>联系表单</li>
<li>问卷调查</li>
<li>订单支付</li>
<li>文件上传</li>
</ul>
<p><strong>表单的核心标签有三部分组成</strong>：</p>
<p><strong>表单容器</strong><br />
<code>&lt;form&gt;</code>：</p>
<blockquote>
<p>定义表单的容器，包裹所有表单控件。<br />
默认包含action属性</p>
</blockquote>
<p><strong>表单控件</strong></p>
<blockquote>
<p>包含 <code>&lt;input&gt;</code> 通用输入控件、<code>&lt;textarea&gt;</code>：多行文本输入框
<code>&lt;select&gt;</code> 下拉选择框、<code>&lt;button&gt;</code>：自定义按钮等</p>
</blockquote>
<p><strong>辅助标签</strong><br />
<code>&lt;label&gt;</code>：</p>
<blockquote>
<p>关联输入控件的文本标签，提升可访问性（点击标签可聚焦输入框）<br />
更好的提高表单的用户体验</p>
</blockquote>
<hr />
<h2>表单容器</h2>
<p><strong>form 标签</strong>
作用：定义表单的容器，包裹所有表单控件。</p>
<pre><code>&lt;form action=""&gt;&lt;/form&gt;
</code></pre>
<p>action 属性定义了在提交表单时，应该把所收集的数据送给谁（URL）去处理。</p>
<p>目前我们还不涉及数据交互，所以这里暂且为空即可</p>
<hr />
<h2>表单控件</h2>
<blockquote>
<p>input表单：通用输入控件，包含输入框、单选框、复选框等
textarea表单：多行文本输入框
select下拉表单：下拉选择框
button按钮：自定义按钮</p>
</blockquote>
<hr />
<h3>表单控件-input</h3>
<p>输入标签<code>&lt;input&gt;</code>是最常用的表单元素之一，它可以创建文本输入框、密码框、单选框、复选框等。<br />
<code>type</code>属性定义了输入框的类型</p>
<table>
<thead>
<tr>
<th>type属性值</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>text</td>
<td>单行文本框</td>
</tr>
<tr>
<td>password</td>
<td>密码框</td>
</tr>
<tr>
<td>radio</td>
<td>单选框</td>
</tr>
<tr>
<td>checkbox</td>
<td>复选框</td>
</tr>
<tr>
<td>file</td>
<td>文件域</td>
</tr>
</tbody>
</table>
<hr />
<p>1.<strong>单行文本框和密码框(text/password)</strong></p>
<p><strong>场景</strong></p>
<p><img src="/images/posts/html_guide/image-8.png" alt="image-8" /></p>
<p><strong>type属性值</strong></p>
<table>
<thead>
<tr>
<th>type属性值</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>text</td>
<td>单行文本框</td>
</tr>
<tr>
<td>password</td>
<td>密码框</td>
</tr>
</tbody>
</table>
<p><strong>其他属性</strong></p>
<table>
<thead>
<tr>
<th>其他属性</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>placeholder</td>
<td>提示信息</td>
</tr>
<tr>
<td>name</td>
<td>元素的名称</td>
</tr>
<tr>
<td>maxlength</td>
<td>允许的最大字符数</td>
</tr>
<tr>
<td>accesskey</td>
<td>使元素获得焦点的快捷键(按下指定的键组合，即可将焦点移动到该元素)&lt;br/&gt;win系统：alt+键值&lt;br/&gt;mac系统：option+键值</td>
</tr>
<tr>
<td>autocomplete</td>
<td>用于控制表单的自动填充行为，帮助浏览器决定是否根据用户历史输入自动填充字段值&lt;br&gt;取值 on / off</td>
</tr>
</tbody>
</table>
<p><strong>示例</strong></p>
<pre><code>&lt;form action=""&gt;
  &lt;ul&gt;
    &lt;li&gt;
    账号：&lt;input type="text" name="username" placeholder="请输入账号" accesskey="u"&gt;
    &lt;!--accesskey属性：使元素获得焦点的快捷键(win系统：alt+键值，mac系统：option+键值)--&gt;
    &lt;/li&gt;
    &lt;li&gt;
    密码：&lt;input type="password" name="password" placeholder="请输入密码" maxlength="12"&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/form&gt;
</code></pre>
<hr />
<p>2.<strong>单选框和复选框(radio/checkbox)</strong></p>
<p><strong>场景</strong></p>
<p><img src="/images/posts/html_guide/image-9.png" alt="image-9" /></p>
<p><strong>type属性值</strong></p>
<table>
<thead>
<tr>
<th>type属性值</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>radio</td>
<td>单选框</td>
</tr>
<tr>
<td>checkbox</td>
<td>复选框</td>
</tr>
</tbody>
</table>
<p><strong>其他属性</strong></p>
<table>
<thead>
<tr>
<th>其他属性</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>name</td>
<td>表单名称实现分组</td>
</tr>
<tr>
<td>value</td>
<td>表单值</td>
</tr>
<tr>
<td>checked</td>
<td>是否默认选中</td>
</tr>
</tbody>
</table>
<p><strong>示例</strong></p>
<pre><code>&lt;form action=""&gt;
  &lt;ul&gt;
    &lt;!-- 2. 单选框 复选框和文件域 --&gt;
    &lt;li&gt;
      性别:
      &lt;input type="radio" name="gender" value="0" checked&gt; 女
      &lt;input type="radio" name="gender" value="1"&gt; 男
    &lt;/li&gt;
    &lt;li&gt;
      爱好:
      &lt;input type="checkbox" name="hobby" value="0" checked&gt; 足球
      &lt;input type="checkbox" name="hobby" value="1"&gt; 篮球
      &lt;input type="checkbox" name="hobby" value="2"&gt; 双色球
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/form&gt;
</code></pre>
<hr />
<p>3.<strong>文件域(file)</strong></p>
<p><strong>场景</strong></p>
<p><img src="/images/posts/html_guide/image-10.png" alt="image-10" /></p>
<p><strong>type属性值</strong></p>
<table>
<thead>
<tr>
<th>type属性值</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>file</td>
<td>文件域</td>
</tr>
</tbody>
</table>
<p><strong>其他属性</strong></p>
<table>
<thead>
<tr>
<th>其他属性</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>multiple</td>
<td>允许选择多个文件</td>
</tr>
<tr>
<td>accept</td>
<td>规定选择的文件类型，多个类型中间用逗号分割</td>
</tr>
</tbody>
</table>
<p><strong>示例</strong></p>
<pre><code>&lt;form action=""&gt;
  &lt;ul&gt;
    &lt;li&gt;
      头像:
      &lt;input type="file" name="file" multiple accept=".jpg,.png,.webp"&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/form&gt;
</code></pre>
<p><strong>accept常用属性值补充</strong></p>
<table>
<thead>
<tr>
<th>accept属性值</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>image/*</td>
<td>只允许选择图片文件</td>
</tr>
<tr>
<td>audio/*</td>
<td>只允许选择音频文件</td>
</tr>
<tr>
<td>video/*</td>
<td>只允许选择视频文件</td>
</tr>
<tr>
<td>application/*</td>
<td>只允许选择应用程序文件</td>
</tr>
</tbody>
</table>
<hr />
<h3>表单控件-textarea</h3>
<p><code>&lt;textarea&gt;</code> HTML 元素是一个多行纯文本编辑控件，适用于允许用户输入大量自由格式文本的场景，例如<strong>评论</strong>或<strong>反馈表单</strong>。<br />
textarea 多行文本框也称为<strong>文本域</strong>。</p>
<p><strong>场景</strong></p>
<p><img src="/images/posts/html_guide/image-11.png" alt="image-11" /></p>
<p><strong>基本语法</strong></p>
<pre><code>&lt;textarea&gt;&lt;/textarea&gt;
</code></pre>
<p><strong>常见属性</strong></p>
<table>
<thead>
<tr>
<th>常见属性</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>name</td>
<td>表单名称</td>
</tr>
<tr>
<td>placeholder</td>
<td>提示信息</td>
</tr>
<tr>
<td>rows</td>
<td>文本行数，正整数，默认为2</td>
</tr>
<tr>
<td>cols</td>
<td>文本列数，正整数，默认20</td>
</tr>
</tbody>
</table>
<blockquote>
<p>文本域textarea利用CSS来设定样式，比如宽高边框等</p>
</blockquote>
<p><strong>示例</strong></p>
<pre><code>&lt;form action=""&gt;
  留言:
  &lt;textarea name="msg" cols="30" rows="10" placeholder="请输入留言"&gt;&lt;/textarea&gt;
&lt;/form&gt;
</code></pre>
<h3>表单控件-select</h3>
<p>HTML<code>&lt;select&gt;</code>元素表示一个提供选项菜单的控件</p>
<p><strong>场景</strong></p>
<p><img src="/images/posts/html_guide/image-12.png" alt="image-12" /></p>
<p><strong>语法示例</strong></p>
<pre><code>&lt;select name="" id=""&gt;
  &lt;option value="北京"&gt;北京&lt;/option&gt;
  &lt;option value="深圳"&gt;深圳&lt;/option&gt;
  &lt;option value="上海" selected&gt;上海&lt;/option&gt;
  &lt;option value="广州"&gt;广州&lt;/option&gt;
&lt;/select&gt;
</code></pre>
<p><code>&lt;select&gt;</code> 元素是容器，<code>&lt;option&gt;</code>是每一个选项标签，每个选项要跟一个值<br />
要想默认选中一个选项，可以添加 <code>selected</code> 属性。</p>
<blockquote>
<p>因为select很难修改为好看的效果，大部分下拉列表可以通过其他标签模拟实现</p>
</blockquote>
<hr />
<h3>表单控件-button</h3>
<p>HTML <code>&lt;button&gt;</code> 标签定义一个按钮，元素内部可以放置内容，比如文本或图像</p>
<p><strong>场景</strong></p>
<p><img src="/images/posts/html_guide/image-13.png" alt="image-13" /></p>
<p><strong>语法示例</strong></p>
<pre><code>&lt;!--中间文字写什么，按钮就显示什么--&gt;
  &lt;button&gt;搜索&lt;/button&gt;
  &lt;button&gt;登录&lt;/button&gt;
</code></pre>
<blockquote>
<p>disabled属性可以禁用按钮，禁用后的按钮不能点击，如下图</p>
</blockquote>
<p><img src="/images/posts/html_guide/image-14.png" alt="image-14" /></p>
<hr />
<h2>辅助标签-label</h2>
<p><code>&lt;label&gt;</code> 表示用户界面中某个元素的说明，提升可访问性（点击标签可聚焦输入框）</p>
<p><strong>场景</strong></p>
<p><img src="/images/posts/html_guide/image-15.png" alt="image-15" /></p>
<p><strong>两种使用方式</strong>：</p>
<p><strong>方式一：</strong> 利用for和id相关联</p>
<pre><code>&lt;label for="nan"&gt;男&lt;/label&gt;
&lt;input type="radio" id="nan" name="sex"&gt;
</code></pre>
<p><strong>方式二：</strong> 直接包含</p>
<pre><code>&lt;label&gt;男
  &lt;input type="radio" name="sex"&gt;
&lt;/label&gt;
</code></pre>
<blockquote>
<p>通过label标签也可以实现单击标签就可以锁定输入框的效果<br />
<strong>示例</strong></p>
</blockquote>
<pre><code>&lt;label&gt;
  用户名:
  &lt;input type="text" name="username"&gt;
&lt;/label&gt;
</code></pre>
<hr />
<h1>字符实体</h1>
<p>字符实体是一段以连字号（&amp;）开头、以分号（;）结尾的文本（字符串）。<br />
常用于显示保留字符和不可见的字符（如“不换行空格”）</p>
<h3>常用字符实体对照表</h3>
<table>
<thead>
<tr>
<th>字符</th>
<th>实体</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>&amp;</code></td>
<td><code>&amp;amp;</code></td>
<td>与符号</td>
</tr>
<tr>
<td><code>&lt;</code></td>
<td><code>&amp;lt;</code></td>
<td>小于号</td>
</tr>
<tr>
<td><code>&gt;</code></td>
<td><code>&amp;gt;</code></td>
<td>大于号</td>
</tr>
<tr>
<td><code>"</code></td>
<td><code>&amp;quot;</code></td>
<td>引号</td>
</tr>
<tr>
<td><code> </code></td>
<td><code>&amp;nbsp;</code></td>
<td>不换行空格</td>
</tr>
<tr>
<td><code>–</code></td>
<td><code>&amp;ndash;</code></td>
<td>短破折号</td>
</tr>
<tr>
<td><code>—</code></td>
<td><code>&amp;mdash;</code></td>
<td>长破折号</td>
</tr>
<tr>
<td><code>©</code></td>
<td><code>&amp;copy;</code></td>
<td>版权符号</td>
</tr>
<tr>
<td><code>®</code></td>
<td><code>&amp;reg;</code></td>
<td>注册商标符号</td>
</tr>
<tr>
<td><code>™</code></td>
<td><code>&amp;trade;</code></td>
<td>商标符号</td>
</tr>
<tr>
<td><code>≈</code></td>
<td><code>&amp;asymp;</code></td>
<td>近似等于符号</td>
</tr>
<tr>
<td><code>≠</code></td>
<td><code>&amp;ne;</code></td>
<td>不等于符号</td>
</tr>
<tr>
<td><code>£</code></td>
<td><code>&amp;pound;</code></td>
<td>英镑符号</td>
</tr>
<tr>
<td><code>€</code></td>
<td><code>&amp;euro;</code></td>
<td>欧元符号</td>
</tr>
<tr>
<td><code>°</code></td>
<td><code>&amp;deg;</code></td>
<td>度符号</td>
</tr>
</tbody>
</table>
]]></content>
    <author><name>仙鹤</name></author>
    <category term="前端"/>
  </entry>
  <entry>
    <title>markdown综合教程</title>
    <link href="https://xianhe51920.github.io/posts/markdown_guide/" rel="alternate" type="text/html"/>
    <id>https://xianhe51920.github.io/posts/markdown_guide/</id>
    <published>2025-12-29T00:00:00.000Z</published>
    <updated>2025-12-29T00:00:00.000Z</updated>
    <summary>一小时速成markdown</summary>
    <content type="html"><![CDATA[<h2>目录</h2>
<h3>基础部分</h3>
<ul>
<li><a href="#%E4%B8%80%E6%A0%87%E9%A2%98%E8%AF%AD%E6%B3%95">一.标题语法</a></li>
<li><a href="#%E4%BA%8C%E6%AE%B5%E8%90%BD%E8%AF%AD%E6%B3%95">二.段落语法</a></li>
<li><a href="#%E4%B8%89markdown%E6%8D%A2%E8%A1%8C%E8%AF%AD%E6%B3%95">三.markdown换行语法</a></li>
<li><a href="#%E5%9B%9B%E5%BC%BA%E8%B0%83%E8%AF%AD%E6%B3%95">四.强调语法</a></li>
<li><a href="#%E4%BA%94%E5%BC%95%E7%94%A8%E8%AF%AD%E6%B3%95">五.引用语法</a></li>
<li><a href="#%E5%85%AD%E5%88%97%E8%A1%A8%E8%AF%AD%E6%B3%95">六.列表语法</a></li>
<li><a href="#%E4%B8%83%E4%BB%A3%E7%A0%81%E8%AF%AD%E6%B3%95">七.代码语法</a></li>
<li><a href="#%E5%85%AB%E5%88%86%E5%89%B2%E7%BA%BF%E8%AF%AD%E6%B3%95">八.分割线语法</a></li>
<li><a href="#%E4%B9%9D%E9%93%BE%E6%8E%A5%E8%AF%AD%E6%B3%95">九.链接语法</a></li>
<li><a href="#%E5%8D%81%E5%9B%BE%E7%89%87%E8%AF%AD%E6%B3%95">十.图片语法</a></li>
<li><a href="#%E5%8D%81%E4%B8%80%E8%BD%AC%E4%B9%89%E5%AD%97%E7%AC%A6%E8%AF%AD%E6%B3%95">十一.转义字符语法</a></li>
</ul>
<h3>进阶部分</h3>
<ul>
<li><a href="#%E4%B8%80%E8%A1%A8%E6%A0%BC">一.表格</a></li>
<li><a href="#%E4%BA%8C%E5%9B%B4%E6%A0%8F%E4%BB%A3%E7%A0%81%E5%9D%97">二.围栏代码块</a></li>
<li><a href="#%E4%B8%89%E8%84%9A%E6%B3%A8">三.脚注</a></li>
<li><a href="#%E6%A0%87%E9%A2%98%E4%B8%80">四.标题编号</a></li>
<li><a href="#%E4%BA%94%E5%AE%9A%E4%B9%89%E5%88%97%E8%A1%A8">五.定义列表</a></li>
<li><a href="#%E5%85%AD%E5%88%A0%E9%99%A4%E7%BA%BF">六.删除线</a></li>
<li><a href="#%E4%B8%83%E4%BB%BB%E5%8A%A1%E5%88%97%E8%A1%A8">七.任务列表</a></li>
<li><a href="#%E5%85%AB%E4%BD%BF%E7%94%A8emoji%E8%A1%A8%E6%83%85">八.使用Emoji表情</a></li>
<li><a href="#%E4%B9%9D%E8%87%AA%E5%8A%A8%E7%BD%91%E5%9D%80%E9%93%BE%E6%8E%A5">九.自动网址链接</a></li>
<li><a href="#%E5%8F%98%E9%80%9A%E6%95%99%E7%A8%8B">变通教程</a></li>
</ul>
<hr />
<p>&lt;!--- 文件分隔符: 1.标题语法.md --&gt;</p>
<h1>一.标题语法</h1>
<p>“#” + 空格 + 标题名称，一个“#”表示一级标题，以此类推，一、二级标题有使用若干“=”，“-”分割的特殊表示方法</p>
<p><strong>代码示例：</strong></p>
<pre><code># 一级标题
## 二级标题
### 三级标题
#### 四级标题
##### 五级标题
###### 六级标题
一级标题
=======
二级标题
-------
</code></pre>
<p><strong>效果预览：</strong></p>
<h1>一级标题</h1>
<h2>二级标题</h2>
<h3>三级标题</h3>
<h4>四级标题</h4>
<h5>五级标题</h5>
<h6>六级标题</h6>
<h1>一级标题</h1>
<h2>二级标题</h2>
<p>&lt;!--- 文件分隔符: 2.段落语法.md --&gt;</p>
<hr />
<h1>二.段落语法</h1>
<p>要分割段落，只需要在两个段落之间添加空白行即可</p>
<p><strong>代码示例：</strong></p>
<pre><code>我喜欢你，
你喜欢我吗

我不喜欢你，
我们还是算了吧
</code></pre>
<p><strong>效果预览：</strong></p>
<p>我喜欢你，
你喜欢我吗</p>
<p>我不喜欢你，
我们还是算了吧</p>
<hr />
<p>&lt;!--- 文件分隔符: 3.换行语法.md --&gt;</p>
<h1>三.markdown换行语法</h1>
<h2>1.空格换行</h2>
<p>在内容末尾敲击至少两个空格，在回车，即可换行</p>
<p><strong>代码示例：</strong></p>
<pre><code>有你在的今天  
今天本是平凡的一天  
却成为了还不错的一天  
</code></pre>
<p><strong>效果预览：</strong><br />
有你在的今天<br />
今天本是平凡的一天<br />
却成为了还不错的一天</p>
<h2>2.标签换行</h2>
<p>在内容末尾添加<code>&lt;br&gt;</code>标签即可</p>
<p><strong>代码示例：</strong></p>
<pre><code>虽然没有什么特别的事&lt;br&gt;
但只要有你在就足够了&lt;br&gt;
</code></pre>
<p><strong>效果预览：</strong><br />
虽然没有什么特别的事&lt;br&gt;
但只要有你在就足够了&lt;br&gt;</p>
<p>&lt;!--- 文件分隔符: 4.强调语法.md --&gt;</p>
<h1>四.强调语法</h1>
<h2>1.粗体</h2>
<h3>(1).星号加粗</h3>
<p>在文字两端都添加两个星号<code>*</code>
<strong>代码示例：</strong></p>
<pre><code>**直到现在**
</code></pre>
<p><strong>效果预览：</strong><br />
<strong>直到现在</strong></p>
<h3>(2).下划线加粗</h3>
<p>在文字两端都添加两个下划线<code>_</code></p>
<p><strong>代码示例：</strong></p>
<pre><code>__你依然是我的光__  
</code></pre>
<p><strong>效果预览：</strong><br />
<strong>你依然是我的光</strong></p>
<p><strong>注</strong>：要在中间部分加粗最好使用星号</p>
<h2>2.斜体</h2>
<h3>(1).星号</h3>
<p>在文字两端都添加一个星号<code>*</code><br />
<strong>代码示例：</strong></p>
<pre><code>*你啊*
</code></pre>
<p><strong>效果预览：</strong>
<em>你啊</em></p>
<h3>(2).下划线</h3>
<p>在文字两端都添加一个下划线<code>_</code><br />
<strong>代码示例：</strong></p>
<pre><code>_在不知不觉中已经成为了我每天的日常了_
</code></pre>
<p><strong>效果预览：</strong>
<em>在不知不觉中已经成为了我每天的日常了</em></p>
<h2>3.同时使用</h2>
<p>可以有很多种组合方式，效果都是一样的</p>
<p><strong>代码示例：</strong></p>
<pre><code>***和你说话时***
_**和你说话时**_
*_和你说话时__*
</code></pre>
<p><strong>效果预览：</strong><br />
<em><strong>和你说话时</strong></em></p>
<p><em><strong>注</strong>.在中间使用强调语法强调一律使用星号</em></p>
<p>&lt;!--- 文件分隔符: 5.引用语法.md --&gt;</p>
<h1>五.引用语法</h1>
<h2>1.块引用</h2>
<p><code>&gt;</code> + 空格 + 引用内容
<strong>代码示例：</strong></p>
<pre><code>&gt; 总觉得每天都会变得开心一点
</code></pre>
<p><strong>效果预览：</strong></p>
<blockquote>
<p>总觉得每天都会变得开心一点</p>
</blockquote>
<h2>2.多个段落的块引用</h2>
<p>在段落之间的空白行添加<code>&gt;</code>即可</p>
<p><strong>代码示例：</strong></p>
<pre><code>&gt; hello world
&gt;
&gt; say hello
</code></pre>
<p><strong>效果预览：</strong></p>
<blockquote>
<p>hello world</p>
<p>say hello</p>
</blockquote>
<h2>3.嵌套块引用</h2>
<p>引用也是可以嵌套的</p>
<p><strong>代码示例：</strong></p>
<pre><code>&gt; 这是一个引用
&gt;
&gt;&gt;这是引用中的引用
&gt;&gt;
&gt;&gt;&gt;这是引用中的引用中的引用
</code></pre>
<p><strong>效果预览：</strong></p>
<blockquote>
<p>这是一个引用</p>
<blockquote>
<p>这是引用中的引用</p>
<blockquote>
<p>这是引用中的引用中的引用</p>
</blockquote>
</blockquote>
</blockquote>
<h2>4.带有其他元素的块引用</h2>
<p>部分语法不支持，请自行尝试</p>
<p><strong>代码示例：</strong></p>
<pre><code>&gt; # 一级标题
&gt; **粗体**  
&gt; _斜体_  
&gt; - 无序列表
</code></pre>
<p><strong>效果预览：</strong></p>
<blockquote>
<h1>一级标题</h1>
<p><strong>粗体</strong><br />
<em>斜体</em></p>
<ul>
<li>无序列表</li>
</ul>
</blockquote>
<p>&lt;!--- 文件分隔符: 6.列表语法.md --&gt;</p>
<h1>六.列表语法</h1>
<h2>1.有序列表</h2>
<p><code>n.</code> + <code>空格</code> + <code>内容</code><br />
列表也支持嵌套</p>
<h3>每日计划</h3>
<p><strong>代码示例：</strong></p>
<pre><code>1. 吃饭
    1. 吃早饭
    2. 吃午饭
    3. 吃晚饭
2. 睡觉
    1. 早上睡
    2. 中午睡
    3. 晚上睡
        1. 做个好梦
        2. 做个噩梦
</code></pre>
<p><strong>效果预览：</strong></p>
<ol>
<li>吃饭
<ol>
<li>吃早饭</li>
<li>吃午饭</li>
<li>吃晚饭</li>
</ol>
</li>
<li>睡觉
<ol>
<li>早上睡</li>
<li>中午睡</li>
<li>晚上睡
<ol>
<li>做个好梦</li>
<li>做个噩梦</li>
</ol>
</li>
</ol>
</li>
</ol>
<h2>2.无序列表</h2>
<p><strong>代码示例：</strong></p>
<pre><code># 1.加号
+ 学习C语言
     + 数据类型
     + 常量变量
     + 作用域
     + 操作符

# 2.减号
- 背1000个单词
  - 500个四级单词
  - 500个六级单词
      - 以a开头的单词

# 3.星号
* 跑1000m路程  
</code></pre>
<p><strong>效果预览：</strong></p>
<ul>
<li>学习C语言
<ul>
<li>数据类型</li>
<li>常量变量</li>
<li>作用域</li>
<li>操作符</li>
</ul>
</li>
</ul>
<ul>
<li>背1000个单词</li>
<li>500个四级单词</li>
<li>500个六级单词
<ul>
<li>以a开头的单词</li>
</ul>
</li>
</ul>
<ul>
<li>跑1000m路程</li>
</ul>
<p><em><strong>注</strong>:列表中同样可以使用其他语法格式</em></p>
<p>&lt;!--- 文件分隔符: 7.代码语法.md --&gt;</p>
<h1>七.代码语法</h1>
<p>可以将自己写的代码放进代码块中，这样更加美观</p>
<h2>1.反引号</h2>
<p><strong>代码示例：</strong></p>
<pre><code>`print(hello world)`
</code></pre>
<p><strong>效果预览：</strong><br />
<code>print(hello world)</code></p>
<h2>2.转义反引号</h2>
<p>需要在代码块里面写出反引号，要在外部额外添加反引号</p>
<p><strong>代码示例：</strong></p>
<pre><code>``print(`hello world`)``
</code></pre>
<p><strong>效果预览：</strong><br />
<code>print(`hello world`)</code></p>
<h2>3.代码块</h2>
<h3>(1).缩进式代码块</h3>
<p>给所写的代码每一行添加四个空格<br />
<strong>代码示例：</strong></p>
<pre><code>    #include &lt;stdio.h&gt;
    int main()
    {
        print(hello world);
        
        return 0;
    }
</code></pre>
<p><strong>效果预览：</strong></p>
<pre><code>#include &lt;stdio.h&gt;
int main()
{
    print(hello world);
    
    return 0;
}
</code></pre>
<h3>(2).反引号代码块</h3>
<p><strong>代码示例：</strong>
在代码上下使用首尾呼应的三反引号<code>```</code>，在第一个部分可添加代码语言名称，用来显示高亮的代码</p>
<pre><code>```py
print(hello world)
</code></pre>
<pre><code>

**效果预览：**
```py
print(hello world)
</code></pre>
<h3>(3).波浪号代码块</h3>
<p>使用三波浪号<code>~~~</code>，与反引号同理
<strong>代码示例：</strong></p>
<pre><code>~~~python
print(hello world)
~~~
</code></pre>
<p><strong>效果预览：</strong></p>
<pre><code>print(hello world)
</code></pre>
<p>&lt;!--- 文件分隔符: 8.分割线语法.md --&gt;</p>
<h1>八.分割线语法</h1>
<p>使用三个及以上的星号(***).破折号(---).或下划线(___)</p>
<p><strong>代码示例：</strong></p>
<pre><code>***

---

___
</code></pre>
<p><strong>效果预览：</strong></p>
<hr />
<hr />
<hr />
<p><em><strong>注</strong>:为了兼容性，请在分割线前后均添加空白行</em></p>
<p>&lt;!--- 文件分隔符: 9.链接语法.md --&gt;</p>
<h1>九.链接语法</h1>
<p>链接文本放在中括号内，链接地址放在后面的括号中，链接title可选。</p>
<h2>1.无title</h2>
<p><strong>代码示例：</strong></p>
<pre><code>[仙鹤的学习小窝](http://47.108.140.232)
</code></pre>
<p><strong>效果预览：</strong><br />
<a href="http://47.108.140.232">仙鹤的学习小窝</a></p>
<h2>2.有title</h2>
<p><strong>代码示例：</strong></p>
<pre><code>[仙鹤的学习小窝](http://47.108.140.232 "坚持学习，无限进步")
</code></pre>
<p><strong>效果预览：</strong><br />
<a href="http://47.108.140.232">仙鹤的学习小窝</a></p>
<h2>3.尖括号链接</h2>
<p>使用尖括号可以很方便地把URL或者email地址变成可点击的链接</p>
<p><strong>代码示例：</strong></p>
<pre><code>&lt;http://47.108.140.232/&gt;  
&lt;xkl51920@gmail.com&gt;  
</code></pre>
<p><strong>效果预览：</strong><br />
<a href="http://47.108.140.232/">http://47.108.140.232/</a><br />
<a href="mailto:xkl51920@gmail.com">xkl51920@gmail.com</a></p>
<h2>4. 带格式化的链接</h2>
<h3>(1).强调链接, 在链接语法前后增加星号</h3>
<p><strong>代码示例：</strong></p>
<pre><code>**[仙鹤的学习小窝](http://47.108.140.232)**
</code></pre>
<p><strong>效果预览：</strong><br />
<strong><a href="http://47.108.140.232">仙鹤的学习小窝</a></strong></p>
<h3>(2)要将链接表示为代码，请在方括号中添加反引号</h3>
<p><strong>代码示例：</strong></p>
<pre><code>[`仙鹤的学习小窝`](http://47.108.140.232)  
</code></pre>
<p><strong>效果预览：</strong><br />
<a href="http://47.108.140.232"><code>仙鹤的学习小窝</code></a></p>
<h2>5. 引用链接</h2>
<p><strong>分为两部分</strong></p>
<p><strong>代码示例：</strong></p>
<pre><code>[仙鹤的学习小窝][1]  

[1]: http://47.108.140.232 "坚持学习，无限进步"
</code></pre>
<p><strong>效果预览：</strong><br />
<a href="http://47.108.140.232">仙鹤的学习小窝</a></p>
<h3>(1).第一部分</h3>
<p>尽管不是必需的，可以在第一组和第二组括号之间包含一个空格。第二组括号中的标签不区分大小写，可以包含字母，数字，空格或标点符号。</p>
<h3>(2).第二部分</h3>
<p>引用类型链接的第二部分使用以下属性设置格式：</p>
<ol>
<li>放在括号中的标签，其后紧跟一个冒号和至少一个空格（例如[label]:）。</li>
<li>链接的URL，可以选择将其括在尖括号中。</li>
<li>链接的可选标题，可以将其括在双引号，单引号或括号中。</li>
</ol>
<p><em><strong>注</strong>：第二部分的上一行不能有任何内容</em></p>
<pre><code>[1]: http://47.108.140.232
[1]: http://47.108.140.232 "坚持学习，无限进步"
[1]: http://47.108.140.232 '坚持学习，无限进步'
[1]: http://47.108.140.232 (坚持学习，无限进步)
[1]: &lt;http://47.108.140.232&gt; "坚持学习，无限进步"
[1]: &lt;http://47.108.140.232&gt; '坚持学习，无限进步'
[1]: &lt;http://47.108.140.232&gt; (坚持学习，无限进步)
</code></pre>
<p><strong>链接最佳实践</strong></p>
<blockquote>
<p>不同的 Markdown 应用程序处理URL中间的空格方式不一样。<br />
为了兼容性，请尽量使用%20代替空格。<br />
<code>[仙鹤的学习小窝](http://%2047.108.140.232%20)</code></p>
</blockquote>
<p>&lt;!--- 文件分隔符: 10. 图片语法.md --&gt;</p>
<h1>十.图片语法</h1>
<h2>1.基本图片语法</h2>
<p>要添加图像，请使用感叹号 (!), 然后在方括号增加替代文本，图片链接放在圆括号里，括号里的链接后可以增加一个可选的图片标题文本。</p>
<p><strong>代码示例：</strong></p>
<pre><code>![一只可爱的特莉波卡](/images/posts/markdown_guide/小波卡.jpg)
</code></pre>
<p><strong>效果预览：</strong><br />
<img src="/images/posts/markdown_guide/%E5%B0%8F%E6%B3%A2%E5%8D%A1.jpg" alt="一只可爱的特莉波卡" /></p>
<h2>2.链接图片语法</h2>
<p>给图片增加链接，请将图像的Markdown括在方括号中，然后将链接添加在圆括号中。</p>
<p><strong>代码示例：</strong></p>
<pre><code>[![特莉波卡-冬日](https://image.emoera.cn:443/uploads/ubpY6WS-s3X8.webp)](https://image.emoera.cn:443/uploads/ubpY6WS-s3X8.webp)
</code></pre>
<p><strong>效果预览：</strong><br />
<a href="https://image.emoera.cn:443/uploads/ubpY6WS-s3X8.webp"><img src="https://image.emoera.cn:443/uploads/ubpY6WS-s3X8.webp" alt="特莉波卡-冬日" /></a></p>
<p>来自E时代图床</p>
<p>&lt;!--- 文件分隔符: 11.转义字符语法.md --&gt;</p>
<h1>十一.转义字符语法</h1>
<h2>1.基本的转义语法</h2>
<p>要显示原本用于格式化Markdown文档的字符，请在字符前面添加反斜杠字符 \ 。</p>
<p><strong>代码示例：</strong></p>
<pre><code>\`E时代是CDTU最强算法开发社团\`
</code></pre>
<p><strong>效果预览：</strong><br />
`E时代是CDTU最强算法开发社团`</p>
<h2>2.可做转义的字符</h2>
<p><strong>代码示例：</strong></p>
<pre><code>\ 
\` 
\* 
\_ 
\{\}
\[\]
\()
\# 
\+ 
\- 
\. 
\! 
\|
</code></pre>
<p><strong>效果预览：</strong><br />
\
`
*
_
{}
[]
()
#
+
-
.
!
|</p>
<p>&lt;!--- 文件分隔符: 1.表格.md --&gt;</p>
<h1>一.表格</h1>
<h2>1.基础表格</h2>
<p>要添加表，请使用三个或多个连字符（---）创建每列的标题，并使用管道（|）分隔每列。您可以选择在表的任一端添加管道</p>
<p><strong>代码示例：</strong></p>
<pre><code>| 类型关键字 | 中文名称 | 字节大小 |
|-------|------|------|
| char  | 字符型  | 1    |
| int   | 整型   | 4    |
</code></pre>
<p><strong>效果预览：</strong></p>
<table>
<thead>
<tr>
<th>类型关键字</th>
<th>中文名称</th>
<th>字节大小</th>
</tr>
</thead>
<tbody>
<tr>
<td>char</td>
<td>字符型</td>
<td>1</td>
</tr>
<tr>
<td>int</td>
<td>整型</td>
<td>4</td>
</tr>
</tbody>
</table>
<h2>2.表格对齐</h2>
<p>您可以通过在标题行中的连字符的左侧，右侧或两侧添加冒号（:），将列中的文本对齐到左侧，右侧或中心</p>
<p><strong>代码示例：</strong></p>
<pre><code>| 类型关键字 | 中文名称 | 字节大小 |
|:------|:----:|-----:|
| short | 短整型  |    2 |
| long  | 长整型  |  4/8 |
</code></pre>
<p><strong>效果预览：</strong></p>
<table>
<thead>
<tr>
<th>类型关键字</th>
<th>中文名称</th>
<th>字节大小</th>
</tr>
</thead>
<tbody>
<tr>
<td>short</td>
<td>短整型</td>
<td>2</td>
</tr>
<tr>
<td>long</td>
<td>长整型</td>
<td>4/8</td>
</tr>
</tbody>
</table>
<h2>3.格式化表格中的文字</h2>
<p>您可以在表格中设置文本格式。例如，您可以添加链接，代码（仅反引号（`）中的单词或短语，而不是代码块）和强调。</p>
<p><strong><em>您不能添加标题，块引用，列表，水平规则，图像或HTML标签。</em></strong></p>
<h3><strong>表格中的格式化</strong></h3>
<p><strong>代码示例：</strong></p>
<pre><code>| 可以使用  | 不可以使用  |
|-------|--------|
| 添加链接  | 添加标题   |
| 反引号代码 | 代码块    |
| 强调    | 标题     |
|       | 块引用    |
|       | 列表     |
|       | 水平规则   |
|       | 图像     |
|       | HTML标签 |
</code></pre>
<p><strong>效果预览：</strong></p>
<table>
<thead>
<tr>
<th>可以使用</th>
<th>不可以使用</th>
</tr>
</thead>
<tbody>
<tr>
<td>添加链接</td>
<td>添加标题</td>
</tr>
<tr>
<td>反引号代码</td>
<td>代码块</td>
</tr>
<tr>
<td>强调</td>
<td>标题</td>
</tr>
<tr>
<td></td>
<td>块引用</td>
</tr>
<tr>
<td></td>
<td>列表</td>
</tr>
<tr>
<td></td>
<td>水平规则</td>
</tr>
<tr>
<td></td>
<td>图像</td>
</tr>
<tr>
<td></td>
<td>HTML标签</td>
</tr>
</tbody>
</table>
<h2>4.在表中转义管道字符</h2>
<p>您可以使用表格的HTML字符代码（|）,或者使用反斜杠（\）转义的方式,在表中显示竖线（|）字符.</p>
<p><strong>代码示例：</strong></p>
<pre><code>|   表头   |
|:------:|
|   \|   |
| &amp;#124; |
</code></pre>
<p><strong>效果预览：</strong></p>
<table>
<thead>
<tr>
<th>表头</th>
</tr>
</thead>
<tbody>
<tr>
<td>|</td>
</tr>
<tr>
<td>|</td>
</tr>
</tbody>
</table>
<p>&lt;!--- 文件分隔符: 2.围栏代码块.md --&gt;</p>
<h1>二.围栏代码块</h1>
<h2>1.反引号代码块</h2>
<p><strong>代码示例：</strong></p>
<pre><code>```c
#include &lt;stdio.h&gt;
int main() 
{
    int a = 0;
    int b = 0;
    a = 10;             /*给a赋值
    b = 20;             给b赋值*/
    printf("a+b=%d\n", a + b);

    return 0;
}
```
</code></pre>
<p><strong>效果预览：</strong></p>
<pre><code>#include &lt;stdio.h&gt;
int main() 
{
    int a = 0;
    int b = 0;
    a = 10;             /*给a赋值
    b = 20;             给b赋值*/
    printf("a+b=%d\n", a + b);

    return 0;
}
</code></pre>
<h2>2.波浪号代码块</h2>
<p><strong>代码示例：</strong></p>
<pre><code>~~~c
#include &lt;stdio.h&gt;
int main() 
{
    int a = 0;
    int b = 0;
    a = 10;             /*给a赋值*/
    b = 20;             /*给b赋值*/
    printf("a+b=%d\n", a + b);

    return 0;
}
~~~
</code></pre>
<p><strong>效果预览：</strong></p>
<pre><code>#include &lt;stdio.h&gt;
int main() 
{
    int a = 0;
    int b = 0;
    a = 10;             /*给a赋值*/
    b = 20;             /*给b赋值*/
    printf("a+b=%d\n", a + b);

    return 0;
}
</code></pre>
<h2>3.语法高亮</h2>
<p>要添加语法突出显示，请在受防护的代码块之前的反引号旁边指定一种语言</p>
<p><strong>代码示例：</strong></p>
<pre><code>```python
print("hello world")
```

```json
{
  "firstName": "John",
  "lastName": "Smith",
  "age": 25
}
```
</code></pre>
<p><strong>效果预览：</strong></p>
<pre><code>print("hello world")
</code></pre>
<pre><code>{
  "firstName": "John",
  "lastName": "Smith",
  "age": 25
}
</code></pre>
<p>&lt;!--- 文件分隔符: 3.脚注.md --&gt;</p>
<h1>三.脚注</h1>
<p><strong>代码示例：</strong></p>
<pre><code>这是一个脚注[^1]，用来解释内容

[^1]: 这是一个解释，用来解释脚注标记的内容
</code></pre>
<p><strong>效果预览：</strong><br />
这是一个脚注<a href="%E8%BF%99%E6%98%AF%E4%B8%80%E4%B8%AA%E8%A7%A3%E9%87%8A%EF%BC%8C%E7%94%A8%E6%9D%A5%E8%A7%A3%E9%87%8A%E8%84%9A%E6%B3%A8%E6%A0%87%E8%AE%B0%E7%9A%84%E5%86%85%E5%AE%B9">^1</a>，用来解释内容</p>
<hr />
<p><em><strong>注</strong>有的编辑器可能不支持脚注，会将其识别为链接</em></p>
<p>&lt;!--- 文件分隔符: 4.标题编号.md --&gt;</p>
<h1>四.标题编号</h1>
<p>第一部分：在标题后面添加空格和中括号，并且在中括号填写地址名称</p>
<p>第二部分：在中括号填写展示的文本，在小括号填写地址，即可通过点击文字进行跳转</p>
<p><strong>代码示例：</strong></p>
<pre><code># 四.标题编号
[四.标题编号](#标题一)
</code></pre>
<p><strong>效果预览：</strong><br />
<a href="#%E6%A0%87%E9%A2%98%E4%B8%80">四.标题编号</a></p>
<p><em><strong>注</strong>:有的编辑器可能不支持这种扩展格式，而是这样的</em></p>
<p><strong>代码示例：</strong></p>
<pre><code># 四.标题编号
[点击跳转到语法综合文档](#markdown教程)
</code></pre>
<p><strong>效果预览：</strong></p>
<p><a href="#markdown%E6%95%99%E7%A8%8B">点击跳转到语法综合文档</a></p>
<p>&lt;!--- 文件分隔符: 5.定义列表.md --&gt;</p>
<h1>五.定义列表</h1>
<h2>包括术语和定义两部分</h2>
<h2>核心用途</h2>
<ol>
<li>整理术语表(如技术文档中的概念解释)</li>
<li>编写FAQ回答(问题 = 术语，答案 = 定义)</li>
<li>说明功能/参数(功能名称 = 术语，用法 = 定义)</li>
<li>替代表格的简单"两列结构"，排版更简洁</li>
</ol>
<h3>示例</h3>
<p><strong>代码示例：</strong></p>
<pre><code>Markdown
: 一种轻量级标记语言，用于快速编写格式化文档，语法简洁易记。

Typora
: 一款支持实时预览的 Markdown 编辑器，  
  支持定义列表.表格.公式等扩展语法，  
  排版美观且操作直观。

锚点跳转
: 通过 `[链接文字](#ID)` 语法，实现页面内快速跳转的功能，  
常用于长文档的目录导航。
</code></pre>
<p><strong>效果预览：</strong></p>
<p>Markdown
: 一种轻量级标记语言，用于快速编写格式化文档，语法简洁易记。</p>
<p>Typora
: 一款支持实时预览的 Markdown 编辑器，<br />
支持定义列表.表格.公式等扩展语法，<br />
排版美观且操作直观。</p>
<p>锚点跳转
: 通过 <code>[链接文字](#ID)</code> 语法，实现页面内快速跳转的功能，<br />
常用于长文档的目录导航。</p>
<p>&lt;!--- 文件分隔符: 6.删除线.md --&gt;</p>
<h1>六.删除线</h1>
<p>您可以通过在单词中心放置一条水平线来删除单词。结果看起来像这样。此功能使您可以指示某些单词是一个错误，要从文档中删除。若要删除单词，请在单词前后使用两个波浪号~~。</p>
<p><strong>代码示例：</strong></p>
<pre><code>~~世界是平坦的。~~ 我们现在知道世界是圆的。
</code></pre>
<p><strong>效果预览：</strong><br />
<s>世界是平坦的。</s> 我们现在知道世界是圆的。</p>
<p>&lt;!--- 文件分隔符: 7.任务列表.md --&gt;</p>
<h1>七.任务列表</h1>
<p><strong>代码示例：</strong></p>
<p>未完成：<code>- [ ] 内容</code> 中括号填写<code>空格</code><br />
已完成：<code>- [×] 内容</code> 中括号填写小写“<code>x</code>”</p>
<pre><code>
## 今日计划
- [ ] 吃饭
- [ ] 睡觉
- [x] 做作业
</code></pre>
<p><strong>效果预览：</strong></p>
<h2>今日计划</h2>
<ul>
<li>[ ] 吃饭</li>
<li>[ ] 睡觉</li>
<li>[x] 做作业</li>
</ul>
<p>&lt;!--- 文件分隔符: 8.使用emoji表情.md --&gt;</p>
<h1>八.使用Emoji表情</h1>
<h2>1.复制粘贴表情符号</h2>
<p>在大多数情况下，您可以简单地从Emojipedia (opens new window)等来源复制表情符号并将其粘贴到文档中。许多Markdown应用程序会自动以Markdown格式的文本显示表情符号。从Markdown应用程序导出的HTML和PDF文件应显示表情符号。</p>
<p>Tip: 如果您使用的是静态网站生成器，请确保将HTML页面编码为UTF-8。</p>
<p><img src="https://via.placeholder.com/300x150/FFD700/000000?text=Emoji%E8%A1%A8%E6%83%85" alt="Emoji表情" /></p>
<h2>2.使用表情符号简码</h2>
<p>一些Markdown应用程序允许您通过键入表情符号短代码来插入表情符号。这些以冒号开头和结尾，并包含表情符号的名称</p>
<p><strong>代码示例：</strong></p>
<pre><code>:tent:  
:joy:
</code></pre>
<p><strong>效果预览：</strong><br />
:tent:<br />
:joy:</p>
<p>&lt;!--- 文件分隔符: 9.自动网址链接.md --&gt;</p>
<h1>九.自动网址链接</h1>
<p>许多Markdown处理器会自动将URL转换为链接。这意味着如果您输入http://47.108.140.232/，即使您未使用方括号，您的Markdown处理器也会自动将其转换为链接</p>
<p><strong>代码示例：</strong></p>
<pre><code>http://47.108.140.232
</code></pre>
<p><strong>效果预览：</strong><br />
http://47.108.140.232</p>
<h2>禁用自动网址链接</h2>
<p>通过反引号代码来解决</p>
<p><strong>代码示例：</strong></p>
<pre><code>`http://47.108.140.232/`
</code></pre>
<p><strong>效果预览：</strong><br />
<code>http://47.108.140.232/</code></p>
<p>&lt;!--- 文件分隔符: 变通教程.md --&gt;</p>
<h1>变通教程</h1>
<h2>下划线</h2>
<p>Markdown 原生不支持下划线，但如果你的 Markdown 处理器支持 HTML，可以使用 &lt;ins&gt; 标签：</p>
<p><strong>代码示例：</strong></p>
<pre><code>一些文字 &lt;ins&gt;将被加下划线&lt;/ins&gt;。
</code></pre>
<p><strong>效果预览：</strong><br />
一些文字 &lt;ins&gt;将被加下划线&lt;/ins&gt;。</p>
<h2>缩进</h2>
<p>Markdown里的空格和制表符有特殊用途，比如创建换行或代码块。如果你想用Tab缩进段落，可以使用&amp;nbsp;</p>
<p><strong>代码示例：</strong></p>
<pre><code>&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;这是一个缩进的段落。 
</code></pre>
<p><strong>效果预览：</strong><br />
    这是一个缩进的段落。</p>
<h2>文字居中</h2>
<p>Markdown 没有文本对齐的语法，但可以使用 HTML 标签 <s>&lt;center&gt;</s>（已废弃）或 CSS 解决：</p>
<p><strong>代码示例：</strong></p>
<pre><code>&lt;p style="text-align:center"&gt;这段文字居中显示。&lt;/p&gt;
</code></pre>
<p><strong>效果预览：</strong><br />
&lt;p style="text-align:center"&gt;这段文字居中显示。&lt;/p&gt;</p>
<h2>文字颜色</h2>
<p>Markdown 不支持更改文字颜色，但 HTML 可以：</p>
<p><strong>代码示例：</strong></p>
<pre><code>&lt;font color="red"&gt;这段文字是红色的！&lt;/font&gt;  
&lt;p style="color:blue"&gt;这段文字是蓝色的。&lt;/p&gt;
</code></pre>
<p><strong>效果预览：</strong><br />
&lt;font color="red"&gt;这段文字是红色的！&lt;/font&gt;<br />
&lt;p style="color:blue"&gt;这段文字是蓝色的。&lt;/p&gt;</p>
]]></content>
    <author><name>仙鹤</name></author>
    <category term="教程"/>
  </entry>
</feed>
