<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>fyupeng</title>
  
  
  <link href="http://fyupeng.github.io/atom.xml" rel="self"/>
  
  <link href="http://fyupeng.github.io/"/>
  <updated>2026-03-05T08:55:35.304Z</updated>
  <id>http://fyupeng.github.io/</id>
  
  <author>
    <name>fyupeng</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>利用OpenResty统计网站访问量</title>
    <link href="http://fyupeng.github.io/2026/03/05/%E5%88%A9%E7%94%A8OpenResty%E7%BB%9F%E8%AE%A1%E7%BD%91%E7%AB%99%E8%AE%BF%E9%97%AE%E9%87%8F/"/>
    <id>http://fyupeng.github.io/2026/03/05/%E5%88%A9%E7%94%A8OpenResty%E7%BB%9F%E8%AE%A1%E7%BD%91%E7%AB%99%E8%AE%BF%E9%97%AE%E9%87%8F/</id>
    <published>2026-03-05T07:34:18.000Z</published>
    <updated>2026-03-05T08:55:35.304Z</updated>
    
    <content type="html"><![CDATA[<h1 id="系列运维文章目录"><a href="#系列运维文章目录" class="headerlink" title="系列运维文章目录"></a>系列运维文章目录</h1><blockquote><p>作者：<strong>fyupeng</strong><br>技术专栏：☞ <a href="https://github.com/fyupeng">https://github.com/fyupeng</a><br>项目地址：☞ <a href="https://github.com/fyupeng/distributed-blog-system-api">https://github.com/fyupeng/distributed-blog-system-api</a><br>访问量预览项目效果：☞ <a href="https://www.fyupeng.cn/detail?name=home&articleId=250904CN0FMKT0X4">https://www.fyupeng.cn/detail?name=home&amp;articleId=250904CN0FMKT0X4</a></p></blockquote><hr><p>效果图：</p><p><img src="https://yupeng-tuchuang.oss-cn-shenzhen.aliyuncs.com/14fd121a503146f8bb75d45a4aa77ff5.png" alt="效果图"></p><hr><h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>例如：随着人工智能的不断发展，机器学习这门技术也越来越重要，很多人都开启了学习机器学习，本文就介绍了机器学习的基础内容。</p><hr><h1 id="一、openresty是什么？"><a href="#一、openresty是什么？" class="headerlink" title="一、openresty是什么？"></a>一、openresty是什么？</h1><p>OpenResty 是一个基于 Nginx 与 Lua 的高性能 Web 平台，集成了大量精良的 Lua 库、第三方模块及常用工具。其核心是通过 LuaJIT 即时编译器扩展 Nginx 的能力，使开发者能够用 Lua 脚本编写高性能的 Web 应用、网关和动态流量处理逻辑。</p><h2 id="1-核心特点"><a href="#1-核心特点" class="headerlink" title="1.核心特点"></a>1.核心特点</h2><p><strong>高性能：</strong> 基于 Nginx 的事件驱动架构和 LuaJIT 的高效执行，适合高并发场景。<br><strong>动态能力：</strong> 通过 Lua 脚本实现动态路由、认证、日志处理等逻辑，无需重启服务。<br><strong>丰富生态：</strong> 内置 Lua-resty 系列库（如 HTTP 客户端、Redis/MySQL 驱动），支持自定义模块开发。<br><strong>灵活扩展：</strong> 兼容 Nginx 原生配置，可直接复用现有 Nginx 插件。</p><h2 id="2-典型应用场景"><a href="#2-典型应用场景" class="headerlink" title="2.典型应用场景"></a>2.典型应用场景</h2><p><strong>API 网关：</strong> 实现鉴权、限流、请求转发等逻辑。<br><strong>动态 CDN：</strong> 通过 Lua 脚本实时调整缓存策略或内容改写。<br><strong>微服务入口：</strong> 聚合后端服务响应，减少客户端请求次数。<br><strong>实时监控：</strong> 在流量层嵌入日志采集或性能分析脚本。</p><h2 id="3-基础示例"><a href="#3-基础示例" class="headerlink" title="3.基础示例"></a>3.基础示例</h2><p>以下是一个简单的 Lua 脚本示例，用于在 Nginx 中返回动态内容：</p><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">location /hello &#123;  </span><br><span class="line">    default_type <span class="string">&#x27;text/plain&#x27;</span>;  </span><br><span class="line">    content_by_lua_block &#123;  </span><br><span class="line">        ngx.say(<span class="string">&quot;Hello, OpenResty!&quot;</span>)  </span><br><span class="line">    &#125;  </span><br><span class="line">&#125;  </span><br></pre></td></tr></table></figure><h2 id="4-学习资源"><a href="#4-学习资源" class="headerlink" title="4.学习资源"></a>4.学习资源</h2><p>官方文档：<a href="https://openresty.org/">https://openresty.org/</a><br>GitHub 仓库：<a href="https://github.com/openresty/openresty">https://github.com/openresty/openresty</a><br>书籍：《OpenResty 最佳实践》</p><p>OpenResty 适用于需要兼顾性能与灵活性的中间层开发，尤其适合替代传统编程语言实现的网关或代理服务。</p><hr><h1 id="二、使用步骤"><a href="#二、使用步骤" class="headerlink" title="二、使用步骤"></a>二、使用步骤</h1><h2 id="1-脚本编写"><a href="#1-脚本编写" class="headerlink" title="1.脚本编写"></a>1.脚本编写</h2><p><strong>ip_counter.lua</strong> 文件内容如下：</p><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">local</span> shell   = <span class="built_in">require</span> <span class="string">&quot;resty.shell&quot;</span></span><br><span class="line"><span class="keyword">local</span> <span class="built_in">log</span>     = <span class="string">&quot;/usr/local/nginx/logs/access.log&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">--[[ 把数字月 → 英文缩写 ]]</span></span><br><span class="line"><span class="keyword">local</span> enMonth = &#123;</span><br><span class="line">    <span class="string">&quot;Jan&quot;</span>,<span class="string">&quot;Feb&quot;</span>,<span class="string">&quot;Mar&quot;</span>,<span class="string">&quot;Apr&quot;</span>,<span class="string">&quot;May&quot;</span>,<span class="string">&quot;Jun&quot;</span>,</span><br><span class="line">    <span class="string">&quot;Jul&quot;</span>,<span class="string">&quot;Aug&quot;</span>,<span class="string">&quot;Sep&quot;</span>,<span class="string">&quot;Oct&quot;</span>,<span class="string">&quot;Nov&quot;</span>,<span class="string">&quot;Dec&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">--[[ 根据传入 date 返回 grep 要用的正则串 及 实际查询范围 ]]</span></span><br><span class="line"><span class="keyword">local</span> <span class="function"><span class="keyword">function</span> <span class="title">build_grep_pat</span><span class="params">(date)</span></span></span><br><span class="line">    <span class="comment">-- 1) 完整日期  2025-03-15</span></span><br><span class="line">    <span class="keyword">local</span> y,m,d = <span class="built_in">date</span>:<span class="built_in">match</span>(<span class="string">&quot;^(%d%d%d%d)%-(%d%d)%-(%d%d)$&quot;</span>)</span><br><span class="line">    <span class="keyword">if</span> y <span class="keyword">then</span></span><br><span class="line">        <span class="keyword">return</span> (<span class="string">&quot;%s/%s/%s&quot;</span>):<span class="built_in">format</span>(d, enMonth[<span class="built_in">tonumber</span>(m)], y),  <span class="string">&quot;day&quot;</span></span><br><span class="line">    <span class="keyword">end</span></span><br><span class="line">    <span class="comment">-- 2) 只有年月  2025-03</span></span><br><span class="line">    y,m = <span class="built_in">date</span>:<span class="built_in">match</span>(<span class="string">&quot;^(%d%d%d%d)%-(%d%d)$&quot;</span>)</span><br><span class="line">    <span class="keyword">if</span> y <span class="keyword">then</span></span><br><span class="line">        <span class="keyword">return</span> (<span class="string">&quot;%s/%s&quot;</span>):<span class="built_in">format</span>(enMonth[<span class="built_in">tonumber</span>(m)], y), <span class="string">&quot;month&quot;</span></span><br><span class="line">    <span class="keyword">end</span></span><br><span class="line">    <span class="comment">-- 3) 只有年  2025</span></span><br><span class="line">    y = <span class="built_in">date</span>:<span class="built_in">match</span>(<span class="string">&quot;^(%d%d%d%d)$&quot;</span>)</span><br><span class="line">    <span class="keyword">if</span> y <span class="keyword">then</span></span><br><span class="line">        <span class="comment">-- 构造  Jan/2025|Feb/2025|...|Dec/2025</span></span><br><span class="line">        <span class="keyword">local</span> t = &#123;&#125;</span><br><span class="line">        <span class="keyword">for</span> _, mon <span class="keyword">in</span> <span class="built_in">ipairs</span>(enMonth) <span class="keyword">do</span></span><br><span class="line">            t[#t+<span class="number">1</span>] = mon..<span class="string">&quot;/&quot;</span>..y</span><br><span class="line">        <span class="keyword">end</span></span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">table</span>.<span class="built_in">concat</span>(t,<span class="string">&quot;|&quot;</span>), <span class="string">&quot;year&quot;</span></span><br><span class="line">    <span class="keyword">end</span></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nil</span>,<span class="string">&quot;invalid format&quot;</span></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="comment">--[[ 统计函数 ]]</span></span><br><span class="line"><span class="keyword">local</span> <span class="function"><span class="keyword">function</span> <span class="title">count_ip</span><span class="params">(date)</span></span></span><br><span class="line">    <span class="keyword">local</span> pat,range = build_grep_pat(<span class="built_in">date</span>)</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> pat <span class="keyword">then</span> <span class="keyword">return</span> <span class="literal">nil</span>,range <span class="keyword">end</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">local</span> cmd</span><br><span class="line">    <span class="keyword">if</span> range == <span class="string">&quot;year&quot;</span> <span class="keyword">then</span></span><br><span class="line">        <span class="comment">-- 一整年：egrep  &quot;Jan/2025|Feb/2025|...|Dec/2025&quot;</span></span><br><span class="line">        cmd = (<span class="string">&#x27;egrep &quot;%s&quot; %s | awk &quot;&#123;print \\$1&#125;&quot; | sort -u | wc -l&#x27;</span>):<span class="built_in">format</span>(pat, <span class="built_in">log</span>)</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="comment">-- 一个月或一天</span></span><br><span class="line">        cmd = (<span class="string">&#x27;grep &quot;%s&quot; %s | awk &quot;&#123;print \\$1&#125;&quot; | sort -u | wc -l&#x27;</span>):<span class="built_in">format</span>(pat, <span class="built_in">log</span>)</span><br><span class="line">    <span class="keyword">end</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">local</span> ok,out,err = shell.run(cmd, <span class="literal">nil</span>, <span class="number">5000</span>)</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> ok <span class="keyword">then</span> <span class="keyword">return</span> <span class="literal">nil</span>,err <span class="keyword">or</span> <span class="string">&quot;shell error&quot;</span> <span class="keyword">end</span></span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">tonumber</span>(out:<span class="built_in">match</span>(<span class="string">&quot;%d+&quot;</span>))</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="comment">---------------- 入口 ----------------</span></span><br><span class="line"><span class="keyword">local</span> <span class="built_in">date</span> = ngx.var.arg_date</span><br><span class="line"><span class="keyword">if</span> <span class="keyword">not</span> <span class="built_in">date</span> <span class="keyword">then</span></span><br><span class="line">    ngx.<span class="built_in">status</span>=<span class="number">400</span></span><br><span class="line">    ngx.say(<span class="string">&#x27;&#123;&quot;msg&quot;:&quot;missing ?date=YYYY[-MM][-DD]&quot;&#125;&#x27;</span>)</span><br><span class="line">    <span class="keyword">return</span></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">local</span> n,err = count_ip(<span class="built_in">date</span>)</span><br><span class="line"><span class="keyword">if</span> <span class="keyword">not</span> n <span class="keyword">then</span></span><br><span class="line">    ngx.<span class="built_in">status</span>=<span class="number">500</span></span><br><span class="line">    ngx.say((<span class="string">&#x27;&#123;&quot;msg&quot;:&quot;%s&quot;&#125;&#x27;</span>):<span class="built_in">format</span>(err))</span><br><span class="line">    <span class="keyword">return</span></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line">ngx.header.content_type = <span class="string">&quot;application/json&quot;</span></span><br><span class="line">ngx.say((<span class="string">&#x27;&#123;&quot;date&quot;:&quot;%s&quot;,&quot;ipCount&quot;:%d&#125;&#x27;</span>):<span class="built_in">format</span>(<span class="built_in">date</span>, n))</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="2-代理配置"><a href="#2-代理配置" class="headerlink" title="2.代理配置"></a>2.代理配置</h2><p><strong>nginx.conf</strong> 文件内容如下：</p><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">server &#123;</span><br><span class="line">        listen       <span class="number">9999</span>;</span><br><span class="line">        server_name  localhost;</span><br><span class="line"></span><br><span class="line">        #charset koi8-r;</span><br><span class="line"></span><br><span class="line">        #access_log  logs/host.access.<span class="built_in">log</span>  main;</span><br><span class="line"></span><br><span class="line">        location / &#123;</span><br><span class="line">            root   html;</span><br><span class="line">            index  index.html index.htm;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        # 示例：使用OpenResty的Lua模块处理请求</span><br><span class="line"></span><br><span class="line">        location /hello &#123;</span><br><span class="line">            default_type <span class="string">&#x27;text/plain&#x27;</span>;</span><br><span class="line">            content_by_lua_block &#123;</span><br><span class="line">                ngx.say(<span class="string">&quot;Hello, OpenResty!&quot;</span>)</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        # 统计 ip 数量</span><br><span class="line">        location /ipCount &#123;</span><br><span class="line">             content_by_lua_file  /usr/<span class="keyword">local</span>/openresty/nginx/lua/ip_counter.lua;</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure><h2 id="3-启动程序"><a href="#3-启动程序" class="headerlink" title="3.启动程序"></a>3.启动程序</h2><h3 id="3-1启动OpenResty服务"><a href="#3-1启动OpenResty服务" class="headerlink" title="3.1启动OpenResty服务"></a>3.1启动OpenResty服务</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">openresty -p /usr/local/openresty/nginx -c conf/nginx.conf</span><br></pre></td></tr></table></figure><h3 id="3-2验证配置"><a href="#3-2验证配置" class="headerlink" title="3.2验证配置"></a>3.2验证配置</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">openresty -t -p /usr/local/openresty/nginx -c conf/nginx.conf</span><br></pre></td></tr></table></figure><h3 id="3-3热重载配置不中断服务"><a href="#3-3热重载配置不中断服务" class="headerlink" title="3.3热重载配置不中断服务"></a>3.3热重载配置不中断服务</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">openresty -s reload -p /usr/local/openresty/nginx</span><br></pre></td></tr></table></figure><h3 id="3-4接口调试"><a href="#3-4接口调试" class="headerlink" title="3.4接口调试"></a>3.4接口调试</h3><p>浏览器或postman调用接口 <a href="http://localhost:9999/ipCount">http://localhost:9999/ipCount?date=${YYYY[-MM][-DD]}</a></p><p>例如：</p><p>date=2025<br>date=2025-12<br>date=2025-12-01</p><hr><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>共同学习，与君共勉！</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;系列运维文章目录&quot;&gt;&lt;a href=&quot;#系列运维文章目录&quot; class=&quot;headerlink&quot; title=&quot;系列运维文章目录&quot;&gt;&lt;/a&gt;系列运维文章目录&lt;/h1&gt;&lt;blockquote&gt;
&lt;p&gt;作者：&lt;strong&gt;fyupeng&lt;/strong&gt;&lt;br&gt;技术</summary>
      
    
    
    
    <category term="Java笔记" scheme="http://fyupeng.github.io/categories/Java%E7%AC%94%E8%AE%B0/"/>
    
    
    <category term="MongoDB" scheme="http://fyupeng.github.io/tags/MongoDB/"/>
    
    <category term="数据库" scheme="http://fyupeng.github.io/tags/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    
  </entry>
  
  <entry>
    <title>ES索引重建（零工具纯脚本执行）</title>
    <link href="http://fyupeng.github.io/2026/03/05/%E7%B4%A2%E5%BC%95%E9%87%8D%E5%BB%BA%EF%BC%88%E9%9B%B6%E5%B7%A5%E5%85%B7%E7%BA%AF%E8%84%9A%E6%9C%AC%E6%89%A7%E8%A1%8C%EF%BC%89/"/>
    <id>http://fyupeng.github.io/2026/03/05/%E7%B4%A2%E5%BC%95%E9%87%8D%E5%BB%BA%EF%BC%88%E9%9B%B6%E5%B7%A5%E5%85%B7%E7%BA%AF%E8%84%9A%E6%9C%AC%E6%89%A7%E8%A1%8C%EF%BC%89/</id>
    <published>2026-03-05T07:31:18.000Z</published>
    <updated>2026-03-05T07:31:17.476Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>作者：<strong>fyupeng</strong><br>技术专栏：☞ <a href="https://github.com/fyupeng">https://github.com/fyupeng</a><br>项目地址：☞ <a href="https://github.com/fyupeng/distributed-blog-system-api">https://github.com/fyupeng/distributed-blog-system-api</a><br>项目预览地址：☞ <a href="https://www.fyupeng.cn/detail?name=home&articleId=250904CN0FMKT0X4&auth=youkeUser">预览</a></p></blockquote><hr><blockquote><p>在日常开发与业务迭代过程中，索引结构的调整是 Elasticsearch 应用场景中非常常见的需求 —— 随着业务需求的更新（比如字段新增 / 类型调整、分词规则优化、数据结构适配业务逻辑变更等），原有 ES 索引的 Mapping/Settings 配置往往无法满足新的业务诉求。而 ES 本身不支持直接修改已创建索引的核心配置（如字段类型、分词器），因此索引重建就成为解决这类问题的核心方案。</p></blockquote><blockquote><p>回归正题，索引重建主要包括这几步：<br>数据备份与迁移、旧索引清理与新索引重建、数据回迁与验证和业务切换。这里为了方便，业务切换采取的服务重启自动重建索引。</p></blockquote><h3 id="第一步：拷贝源索引上的数据-到-临时索引上"><a href="#第一步：拷贝源索引上的数据-到-临时索引上" class="headerlink" title="第一步：拷贝源索引上的数据 到 临时索引上"></a>第一步：拷贝源索引上的数据 到 临时索引上</h3><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl -XPOST &quot;http://localhost:<span class="number">9200</span>/_reindex&quot; -H &quot;Content-<span class="built_in">Type</span>: application/json; charset=UTF-<span class="number">8</span>&quot; -d &quot;&#123;\&quot;source\&quot;:&#123;\&quot;index\&quot;:\&quot;scs-ckp-task-<span class="number">2026</span>\&quot;&#125;,\&quot;dest\&quot;:&#123;\&quot;index\&quot;:\&quot;scs-ckp-task-<span class="number">2026</span>-<span class="built_in">copy</span>\&quot;&#125;,\&quot;max_docs\&quot;:<span class="number">100000</span>&#125;&quot;</span><br></pre></td></tr></table></figure><h3 id="第二步：验证临时索引是否创建，数据是否与原数据一致"><a href="#第二步：验证临时索引是否创建，数据是否与原数据一致" class="headerlink" title="第二步：验证临时索引是否创建，数据是否与原数据一致"></a>第二步：验证临时索引是否创建，数据是否与原数据一致</h3><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># 原索引数据计数</span><br><span class="line">curl -XGET &quot;http://localhost:<span class="number">9200</span>/scs-ckp-task-<span class="number">2026</span>/_count&quot;</span><br></pre></td></tr></table></figure><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"># 临时索引数据计数</span><br><span class="line">curl -XGET &quot;http://localhost:<span class="number">9200</span>/scs-ckp-task-<span class="number">2026</span>-<span class="built_in">copy</span>/_count&quot;</span><br><span class="line"># 返回成功结果示例：&#123;&quot;count&quot;:<span class="number">28675</span>,&quot;_shards&quot;:&#123;&quot;total&quot;:<span class="number">1</span>,&quot;successful&quot;:<span class="number">1</span>,&quot;skipped&quot;:<span class="number">0</span>,&quot;failed&quot;:<span class="number">0</span>&#125;&#125;</span><br></pre></td></tr></table></figure><h3 id="第三步：删除原索引（谨慎操作，临时索引要保留，为了重建索引）"><a href="#第三步：删除原索引（谨慎操作，临时索引要保留，为了重建索引）" class="headerlink" title="第三步：删除原索引（谨慎操作，临时索引要保留，为了重建索引）"></a>第三步：删除原索引（谨慎操作，临时索引要保留，为了重建索引）</h3><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">curl -XDELETE &quot;http://localhost:<span class="number">9200</span>/scs-ckp-task-<span class="number">2026</span>&quot;</span><br><span class="line"># 返回成功结果示例： &#123;&quot;acknowledged&quot;:true&#125;</span><br></pre></td></tr></table></figure><h3 id="第四步：波塞冬重新部署-SCS-服务"><a href="#第四步：波塞冬重新部署-SCS-服务" class="headerlink" title="第四步：波塞冬重新部署 SCS 服务"></a>第四步：波塞冬重新部署 SCS 服务</h3><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"># 保证执行以下命令能查询出结果</span><br><span class="line">curl -XGET &quot;http://localhost:<span class="number">9200</span>/scs-ckp-task-<span class="number">2026</span>/_count&quot;</span><br><span class="line"># 返回成功结果示例：&#123;&quot;count&quot;:<span class="number">0</span>,&quot;_shards&quot;:&#123;&quot;total&quot;:<span class="number">3</span>,&quot;successful&quot;:<span class="number">3</span>,&quot;skipped&quot;:<span class="number">0</span>,&quot;failed&quot;:<span class="number">0</span>&#125;&#125;</span><br></pre></td></tr></table></figure><h4 id="第五步：临时索引把数据重新复制回新索引"><a href="#第五步：临时索引把数据重新复制回新索引" class="headerlink" title="第五步：临时索引把数据重新复制回新索引"></a>第五步：临时索引把数据重新复制回新索引</h4><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl -XPOST &quot;http://localhost:<span class="number">9200</span>/_reindex&quot; -H &quot;Content-<span class="built_in">Type</span>: application/json; charset=UTF-<span class="number">8</span>&quot; -d &quot;&#123;\&quot;source\&quot;:&#123;\&quot;index\&quot;:\&quot;scs-ckp-task-<span class="number">2026</span>-<span class="built_in">copy</span>\&quot;&#125;,\&quot;dest\&quot;:&#123;\&quot;index\&quot;:\&quot;scs-ckp-task-<span class="number">2026</span>\&quot;&#125;,\&quot;max_docs\&quot;:<span class="number">100000</span>&#125;&quot;</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;作者：&lt;strong&gt;fyupeng&lt;/strong&gt;&lt;br&gt;技术专栏：☞ &lt;a href=&quot;https://github.com/fyupeng&quot;&gt;https://github.com/fyupeng&lt;/a&gt;&lt;br&gt;项目地址：☞ &lt;a href=</summary>
      
    
    
    
    <category term="Java笔记" scheme="http://fyupeng.github.io/categories/Java%E7%AC%94%E8%AE%B0/"/>
    
    
    <category term="MongoDB" scheme="http://fyupeng.github.io/tags/MongoDB/"/>
    
    <category term="数据库" scheme="http://fyupeng.github.io/tags/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    
  </entry>
  
  <entry>
    <title>使用EasyExcel自定义导出表格</title>
    <link href="http://fyupeng.github.io/2026/03/05/%E4%BD%BF%E7%94%A8EasyExcel%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AF%BC%E5%87%BA%E8%A1%A8%E6%A0%BC/"/>
    <id>http://fyupeng.github.io/2026/03/05/%E4%BD%BF%E7%94%A8EasyExcel%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AF%BC%E5%87%BA%E8%A1%A8%E6%A0%BC/</id>
    <published>2026-03-05T07:29:18.000Z</published>
    <updated>2026-03-05T07:29:54.559Z</updated>
    
    <content type="html"><![CDATA[<h2 id="一、介绍"><a href="#一、介绍" class="headerlink" title="一、介绍"></a>一、介绍</h2><blockquote><p>作者：<strong>fyupeng</strong><br>技术专栏：☞ <a href="https://github.com/fyupeng">https://github.com/fyupeng</a><br>分布式博客项目地址：☞ <a href="https://github.com/fyupeng/distributed-blog-system-api">https://github.com/fyupeng/distributed-blog-system-api</a></p></blockquote><hr><blockquote><p>留给读者</p></blockquote><p>工作中难免会遇到数据导出的问题，最简单的解决方案就是引入阿里的<code>EasyExcel</code>来解决。</p><h2 id="二、代码"><a href="#二、代码" class="headerlink" title="二、代码"></a>二、代码</h2><ul><li><p>实体类</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Person</span> &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@ExcelProperty(value = &quot;序号&quot;, index = 0)</span></span><br><span class="line">    <span class="keyword">private</span> String seqNumber;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@ExcelProperty(value = &quot;名称&quot;, index = 1)</span></span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@ExcelProperty(value = &quot;爱好&quot;, index = 2)</span></span><br><span class="line">    <span class="keyword">private</span> String hobby;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>实现代码</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">static</span> <span class="type">DateTimeFormatter</span> <span class="variable">DATE_TIME_FORMAT</span> <span class="operator">=</span> DateTimeFormatter.ofPattern(<span class="string">&quot;yyyy-MM-dd&quot;</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">export</span><span class="params">(HttpServletRequest request, HttpServletResponse response)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line"></span><br><span class="line">      <span class="type">Person</span> <span class="variable">person</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Person</span>();</span><br><span class="line">      person.setName(<span class="string">&quot;小明&quot;</span>);</span><br><span class="line">      person.setHobby(<span class="string">&quot;苹果&quot;</span>);</span><br><span class="line">      List&lt;Person&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">      list.add(person);</span><br><span class="line"></span><br><span class="line">      <span class="type">String</span> <span class="variable">fileName</span> <span class="operator">=</span> <span class="string">&quot;人.xlsx&quot;</span>;</span><br><span class="line">      </span><br><span class="line">      <span class="type">SXSSFWorkbook</span> <span class="variable">sheets</span> <span class="operator">=</span> exportExcel(list);</span><br><span class="line">      response.setContentType(<span class="string">&quot;application/octet-stream;charset=UTF8&quot;</span>);</span><br><span class="line">      response.setHeader(<span class="string">&quot;Content-Type&quot;</span>, <span class="string">&quot;application/vnd.ms-excel&quot;</span>);</span><br><span class="line">      fileName = URLEncoder.encode(fileName, <span class="string">&quot;UTF-8&quot;</span>) + DATE_TIME_FORMAT.format(LocalDate.now());</span><br><span class="line">      response.setHeader(<span class="string">&quot;Content-Disposition&quot;</span>, <span class="string">&quot;attachment;filename=&quot;</span> + fileName + <span class="string">&quot;.xlsx&quot;</span>);</span><br><span class="line">      response.addHeader(<span class="string">&quot;Cache-Control&quot;</span>, <span class="string">&quot;no-cache&quot;</span>);</span><br><span class="line">      <span class="type">OutputStream</span> <span class="variable">out</span> <span class="operator">=</span> response.getOutputStream();</span><br><span class="line">      sheets.write(out);</span><br><span class="line">      out.flush();</span><br><span class="line">      out.close();</span><br><span class="line"></span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="keyword">private</span> <span class="keyword">static</span> SXSSFWorkbook <span class="title function_">exportExcel</span><span class="params">(List&lt;Person&gt; moduleList)</span> &#123;</span><br><span class="line">      <span class="comment">// 标题</span></span><br><span class="line">      String[] title = &#123;<span class="string">&quot;序号&quot;</span>, <span class="string">&quot;名称&quot;</span>, <span class="string">&quot;爱好&quot;</span>&#125;;</span><br><span class="line">      <span class="comment">// 创建一个工作薄</span></span><br><span class="line">      <span class="type">SXSSFWorkbook</span> <span class="variable">workbook</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SXSSFWorkbook</span>();</span><br><span class="line">      <span class="comment">// 创建一个工作表 sheet</span></span><br><span class="line">      <span class="type">SXSSFSheet</span> <span class="variable">sheet1</span> <span class="operator">=</span> workbook.createSheet(<span class="string">&quot;Sheet1&quot;</span>);</span><br><span class="line">      <span class="comment">// 创建第二行</span></span><br><span class="line">      <span class="type">Row</span> <span class="variable">row</span> <span class="operator">=</span> sheet1.createRow(<span class="number">1</span>);</span><br><span class="line">      <span class="comment">// 创建单元格</span></span><br><span class="line">      <span class="type">Cell</span> <span class="variable">cell</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">      <span class="comment">// 创建表头</span></span><br><span class="line">      <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; title.length; i++) &#123;</span><br><span class="line">         cell = row.createCell(i);</span><br><span class="line">         <span class="comment">// 设置样式</span></span><br><span class="line">         <span class="type">CellStyle</span> <span class="variable">cellStype</span> <span class="operator">=</span> workbook.createCellStyle();</span><br><span class="line">         cellStype.setAlignment(HorizontalAlignment.CENTER); <span class="comment">// 设置字体居中</span></span><br><span class="line">         <span class="comment">// 设置字体</span></span><br><span class="line">         <span class="type">Font</span> <span class="variable">font</span> <span class="operator">=</span> workbook.createFont();</span><br><span class="line">         font.setFontHeightInPoints((<span class="type">short</span>) <span class="number">13</span>);</span><br><span class="line">         cellStype.setFont(font);</span><br><span class="line">         cell.setCellStyle(cellStype);</span><br><span class="line">         cell.setCellValue(title[i]);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="comment">// 从第三行开始追加数据</span></span><br><span class="line">      <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">2</span>; i &lt; (moduleList.size() + <span class="number">2</span>); i++) &#123;</span><br><span class="line">         <span class="comment">// 创建第i行</span></span><br><span class="line">         <span class="type">Row</span> <span class="variable">nextRow</span> <span class="operator">=</span> sheet1.createRow(i);</span><br><span class="line">         <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt; <span class="number">3</span>; j++) &#123;</span><br><span class="line">            <span class="type">Person</span> <span class="variable">person</span> <span class="operator">=</span> moduleList.get(i - <span class="number">2</span>);</span><br><span class="line">            <span class="type">Cell</span> <span class="variable">cell2</span> <span class="operator">=</span> nextRow.createCell(j);</span><br><span class="line">            <span class="keyword">if</span> (j == <span class="number">0</span>) &#123;</span><br><span class="line">               cell2.setCellValue(String.valueOf(i - <span class="number">1</span>));</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (j == <span class="number">1</span>) &#123;</span><br><span class="line">               cell2.setCellValue(person.getName());</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (j == <span class="number">2</span>) &#123;</span><br><span class="line">               cell2.setCellValue(person.getHobby());</span><br><span class="line">            &#125;</span><br><span class="line">         &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">return</span> workbook;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></li><li><p>展示效果<br><img src="https://i-blog.csdnimg.cn/direct/83aca6afa8a54328a736a4f4a62c31ca.png" alt="效果展示"></p></li></ul><h2 id="三、总结"><a href="#三、总结" class="headerlink" title="三、总结"></a>三、总结</h2><p>生活总能给你带来惊喜，工作中总结未尝不是？</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;一、介绍&quot;&gt;&lt;a href=&quot;#一、介绍&quot; class=&quot;headerlink&quot; title=&quot;一、介绍&quot;&gt;&lt;/a&gt;一、介绍&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;作者：&lt;strong&gt;fyupeng&lt;/strong&gt;&lt;br&gt;技术专栏：☞ &lt;a href=&quot;ht</summary>
      
    
    
    
    <category term="Java笔记" scheme="http://fyupeng.github.io/categories/Java%E7%AC%94%E8%AE%B0/"/>
    
    
    <category term="MongoDB" scheme="http://fyupeng.github.io/tags/MongoDB/"/>
    
    <category term="数据库" scheme="http://fyupeng.github.io/tags/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    
  </entry>
  
  <entry>
    <title>使用EasyExcel导出明细数据</title>
    <link href="http://fyupeng.github.io/2026/03/05/%E4%BD%BF%E7%94%A8EasyExcel%E5%AF%BC%E5%87%BA%E6%98%8E%E7%BB%86%E6%95%B0%E6%8D%AE/"/>
    <id>http://fyupeng.github.io/2026/03/05/%E4%BD%BF%E7%94%A8EasyExcel%E5%AF%BC%E5%87%BA%E6%98%8E%E7%BB%86%E6%95%B0%E6%8D%AE/</id>
    <published>2026-03-05T07:12:18.000Z</published>
    <updated>2026-03-05T07:14:41.482Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>作者：<strong>fyupeng</strong><br>技术专栏：☞ <a href="https://github.com/fyupeng">https://github.com/fyupeng</a><br>分布式博客项目地址：☞ <a href="https://github.com/fyupeng/distributed-blog-system-api">https://github.com/fyupeng/distributed-blog-system-api</a></p></blockquote><hr><blockquote><p>留给读者</p></blockquote><p>工作中难免会遇到数据导出的问题，最简单的解决方案就是引入阿里的<code>EasyExcel</code>来解决。</p><h2 id="一、介绍"><a href="#一、介绍" class="headerlink" title="一、介绍"></a>一、介绍</h2><p>EasyExcel是alibaba开源的一个Excel工具，容易上手，基本能够满足企业的需求。</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.alibaba<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>easyexcel<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.2.6<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><h2 id="二、代码"><a href="#二、代码" class="headerlink" title="二、代码"></a>二、代码</h2><h3 id="1-实体注解"><a href="#1-实体注解" class="headerlink" title="1. 实体注解"></a>1. 实体注解</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@NoArgsConstructor</span></span><br><span class="line"><span class="meta">@AllArgsConstructor</span></span><br><span class="line"><span class="meta">@ToString</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@Builder(toBuilder = true)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Entity</span> <span class="keyword">implements</span> <span class="title class_">Serializable</span> &#123;</span><br><span class="line"><span class="comment">// 导出value对应表格头，index对应排序（从0下标开始）</span></span><br><span class="line"><span class="meta">@ExcelProperties(value = &quot;编码&quot;, index = 0)</span></span><br><span class="line"><span class="keyword">private</span> String code;</span><br><span class="line"></span><br><span class="line"><span class="meta">@ExcelProperties(value = &quot;名称&quot;, index = 1)</span></span><br><span class="line"><span class="keyword">private</span> String name;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="2-核心代码"><a href="#2-核心代码" class="headerlink" title="2. 核心代码"></a>2. 核心代码</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">export</span><span class="params">(HttpServletRequest request, HttpServletResponse response)</span> &#123;</span><br><span class="line"><span class="type">String</span> <span class="variable">fileName</span> <span class="operator">=</span> <span class="string">&quot;导出明细.xlsx&quot;</span>;</span><br><span class="line"><span class="type">OutputStream</span> <span class="variable">out</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">response.setContentType(<span class="string">&quot;application/octet-stream;charset=UTF-8&quot;</span>);</span><br><span class="line">response.setHeader(<span class="string">&quot;Content-Type&quot;</span>, <span class="string">&quot;application/vnd.ms-excel&quot;</span>);</span><br><span class="line">fileName = URLEncoder.encode(fileName, <span class="string">&quot;UTF-8&quot;</span>);</span><br><span class="line">response.setHeader(<span class="string">&quot;Content-Disposition&quot;</span>, <span class="string">&quot;attachment;filename=&quot;</span>, fileName);</span><br><span class="line">response.addHeader(<span class="string">&quot;Cache-Control&quot;</span>, <span class="string">&quot;no-cache&quot;</span>);</span><br><span class="line">out = response.getOutputStream();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建 Exce 文件写入流</span></span><br><span class="line"><span class="type">ExcelWriter</span> <span class="variable">excelWriter</span> <span class="operator">=</span> EasyExcel.write(out).build();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 新增 Sheet 表格</span></span><br><span class="line"><span class="type">WriteSheet</span> <span class="variable">sheet</span> <span class="operator">=</span> EasyExcel.writeSheet(<span class="number">0</span>, <span class="string">&quot;基本信息&quot;</span>).head(Entity.class).build();</span><br><span class="line">List&lt;Entity&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">excelWriter.write(list, sheet);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 关闭 excel 写入流</span></span><br><span class="line">excelWriter.finish();</span><br><span class="line"><span class="comment">// 内存刷入响应流并清空内存</span></span><br><span class="line">response.flushBuffer();</span><br><span class="line">&#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line"><span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Exception</span>(<span class="string">&quot;明细导出失败！&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="三、总结"><a href="#三、总结" class="headerlink" title="三、总结"></a>三、总结</h2><p>每日总结归档，学习会遗忘，但学习后总结不会，它能让你变得越来越强！</p>]]></content>
    
    
      
      
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;作者：&lt;strong&gt;fyupeng&lt;/strong&gt;&lt;br&gt;技术专栏：☞ &lt;a href=&quot;https://github.com/fyupeng&quot;&gt;https://github.com/fyupeng&lt;/a&gt;&lt;br&gt;分布式博客项目地址：☞ &lt;a </summary>
      
    
    
    
    <category term="Java笔记" scheme="http://fyupeng.github.io/categories/Java%E7%AC%94%E8%AE%B0/"/>
    
    
    <category term="MongoDB" scheme="http://fyupeng.github.io/tags/MongoDB/"/>
    
    <category term="数据库" scheme="http://fyupeng.github.io/tags/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    
  </entry>
  
  <entry>
    <title>nacos一键启动、停止、查看状态脚本</title>
    <link href="http://fyupeng.github.io/2023/02/28/nacos%E4%B8%80%E9%94%AE%E5%90%AF%E5%8A%A8%E3%80%81%E5%81%9C%E6%AD%A2%E3%80%81%E6%9F%A5%E7%9C%8B%E7%8A%B6%E6%80%81%E8%84%9A%E6%9C%AC/"/>
    <id>http://fyupeng.github.io/2023/02/28/nacos%E4%B8%80%E9%94%AE%E5%90%AF%E5%8A%A8%E3%80%81%E5%81%9C%E6%AD%A2%E3%80%81%E6%9F%A5%E7%9C%8B%E7%8A%B6%E6%80%81%E8%84%9A%E6%9C%AC/</id>
    <published>2023-02-28T02:17:50.000Z</published>
    <updated>2023-02-28T03:11:47.921Z</updated>
    
    <content type="html"><![CDATA[<h1 id="一、直接给代码"><a href="#一、直接给代码" class="headerlink" title="一、直接给代码"></a>一、直接给代码</h1><h2 id="1-start-sh"><a href="#1-start-sh" class="headerlink" title="1. start.sh"></a>1. start.sh</h2><ul><li><strong>使用方法</strong><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[root<span class="variable">@localhost</span>] <span class="comment">#ls</span></span><br><span class="line">nacos</span><br><span class="line">./start.sh ./nacos -m standalone <span class="comment"># 单机模式</span></span><br><span class="line">./start.sh ./nacos -m cluster <span class="comment"># 集群模式</span></span><br></pre></td></tr></table></figure></li><li><strong>代码</strong><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/bin/bash</span></span><br><span class="line"></span><br><span class="line">function read_dir() &#123;</span><br><span class="line">  <span class="keyword">for</span> file <span class="keyword">in</span> <span class="string">`ls $1`</span></span><br><span class="line">  <span class="keyword">do</span></span><br><span class="line">    <span class="keyword">if</span> [[ -d <span class="variable">$1</span><span class="string">&quot;/&quot;</span><span class="variable">$file</span> ]];</span><br><span class="line">      <span class="keyword">then</span></span><br><span class="line">        read_dir <span class="variable">$1</span><span class="string">&quot;/&quot;</span><span class="variable">$file</span> <span class="variable">$2</span> <span class="variable">$3</span></span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">      <span class="keyword">if</span> [[ -f <span class="variable">$1</span><span class="string">&quot;/&quot;</span><span class="variable">$file</span> ]] &amp;&amp; [[ <span class="string">&quot;$file&quot;</span> = startup.sh ]];</span><br><span class="line">        <span class="keyword">then</span></span><br><span class="line">          cd ./<span class="variable">$1</span></span><br><span class="line">          <span class="keyword">if</span> [[ <span class="string">&quot;$2&quot;</span> = <span class="string">&quot;-m&quot;</span> ]];</span><br><span class="line">    <span class="keyword">then</span></span><br><span class="line">      <span class="keyword">if</span> [[ <span class="string">&quot;$3&quot;</span> = <span class="string">&quot;standalone&quot;</span> ]];</span><br><span class="line">        <span class="keyword">then</span></span><br><span class="line">          ./startup.sh -m standalone &gt; <span class="regexp">/dev/null</span></span><br><span class="line">          echo $<span class="number">1</span><span class="variable">$file</span> 单机模式启动成功！</span><br><span class="line">  cd - &gt; <span class="regexp">/dev/null</span></span><br><span class="line">      elif [[ <span class="string">&quot;$3&quot;</span> = <span class="string">&quot;cluster&quot;</span> ]];</span><br><span class="line">        <span class="keyword">then</span></span><br><span class="line">          ./startup.sh -m cluster &gt; <span class="regexp">/dev/null</span></span><br><span class="line">           echo $<span class="number">1</span><span class="variable">$file</span> 集群模式启动成功！</span><br><span class="line">  cd - &gt; <span class="regexp">/dev/null</span></span><br><span class="line">      <span class="keyword">else</span></span><br><span class="line">        echo <span class="string">&quot;1adUsage: ./startup.sh [Directory] -m [standalone | cluster]&quot;</span></span><br><span class="line">      fi</span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">    echo <span class="string">&quot;2Usage: ./startup.sh [Directory] -m [standalone | cluster]&quot;</span></span><br><span class="line">    exit -<span class="number">1</span>;</span><br><span class="line">          fi</span><br><span class="line">      fi</span><br><span class="line">    fi</span><br><span class="line">  done</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 读取第一个参数</span></span><br><span class="line">read_dir <span class="variable">$1</span> <span class="variable">$2</span> <span class="variable">$3</span></span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure></li></ul><h2 id="2-status-sh"><a href="#2-status-sh" class="headerlink" title="2. status.sh"></a>2. status.sh</h2><ul><li><p><strong>使用方法</strong></p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[root<span class="variable">@localhost</span>] <span class="comment">#ls</span></span><br><span class="line">nacos</span><br><span class="line">./status.sh ./nacos -m standalone <span class="comment"># 单机模式</span></span><br><span class="line">./status.sh ./nacos -m cluster <span class="comment"># 集群模式</span></span><br></pre></td></tr></table></figure></li><li><p><strong>代码</strong></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">!/bin/bash</span></span><br><span class="line"></span><br><span class="line">target_dir=$1</span><br><span class="line"></span><br><span class="line">if [[ &quot;$target_dir&quot; = &quot;&quot; ]] || [[ ! -d $target_dir ]];</span><br><span class="line">then</span><br><span class="line">  echo &quot;Usage: ./status.sh [Dir]&quot;</span><br><span class="line">fi</span><br><span class="line"></span><br><span class="line">pid=`ps ax | grep -i &#x27;nacos.nacos&#x27; | grep $&#123;target_dir&#125; | grep java | grep -v grep | awk &#x27;&#123;print $1&#125;&#x27;`</span><br><span class="line"></span><br><span class="line">if [[ -z &quot;$pid&quot; ]];</span><br><span class="line">then</span><br><span class="line">  echo &quot;No NacosServer running.&quot;</span><br><span class="line"> exit -1;</span><br><span class="line">fi</span><br><span class="line"></span><br><span class="line">echo &quot;The nacosServer $1&quot;nacos-server.jar&quot;  is running, it&#x27;s pids as follow:&quot;</span><br><span class="line">echo &quot;$pid&quot;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="3-stop-sh"><a href="#3-stop-sh" class="headerlink" title="3.stop.sh"></a>3.stop.sh</h2></li><li><p><strong>使用方法</strong></p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">[root<span class="variable">@localhost</span>] <span class="comment">#ls</span></span><br><span class="line">nacos</span><br><span class="line">./status.sh ./nacos -m standalone <span class="comment"># 单机模式</span></span><br><span class="line">./status.sh ./nacos -m cluster <span class="comment"># 集群模式</span></span><br></pre></td></tr></table></figure></li><li><p><strong>代码</strong></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">!/bin/bash</span></span><br><span class="line"></span><br><span class="line">target_dir=$1</span><br><span class="line"></span><br><span class="line">pid=`ps ax | grep -i &#x27;nacos.nacos&#x27; | grep $&#123;target_dir&#125; | grep java | grep -v grep | awk &#x27;&#123;print $1&#125;&#x27;`</span><br><span class="line">if [ -z &quot;$pid&quot; ] ; then</span><br><span class="line">        echo &quot;No nacosServer running.&quot;</span><br><span class="line">        exit -1;</span><br><span class="line">fi</span><br><span class="line"></span><br><span class="line">echo &quot;The nacosServer $1&quot;nacos-server.jar&quot;  is running, it&#x27;s pids as follow:&quot;</span><br><span class="line">echo  &quot;$pid&quot;</span><br><span class="line"></span><br><span class="line">kill $&#123;pid&#125;</span><br><span class="line"></span><br><span class="line">echo &quot;Services stop successfully！ which has been killed forcibly!&quot;</span><br><span class="line">echo pid as follow: </span><br><span class="line">echo &quot;$pid&quot;</span><br><span class="line"></span><br></pre></td></tr></table></figure></li></ul><h1 id="二、结束语"><a href="#二、结束语" class="headerlink" title="二、结束语"></a>二、结束语</h1><p>评论区可留言，可私信，可互相交流学习，共同进步，小生会努力写出优质文章，期待同友多多回访。</p><blockquote><p>专注品质，热爱生活。<br>交流技术，寻求同志。</p></blockquote>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;一、直接给代码&quot;&gt;&lt;a href=&quot;#一、直接给代码&quot; class=&quot;headerlink&quot; title=&quot;一、直接给代码&quot;&gt;&lt;/a&gt;一、直接给代码&lt;/h1&gt;&lt;h2 id=&quot;1-start-sh&quot;&gt;&lt;a href=&quot;#1-start-sh&quot; class=&quot;head</summary>
      
    
    
    
    <category term="脚本工具" scheme="http://fyupeng.github.io/categories/%E8%84%9A%E6%9C%AC%E5%B7%A5%E5%85%B7/"/>
    
    
    <category term="Java工具" scheme="http://fyupeng.github.io/tags/Java%E5%B7%A5%E5%85%B7/"/>
    
  </entry>
  
  <entry>
    <title>vue一键启动、停止、查看状态实用工具</title>
    <link href="http://fyupeng.github.io/2023/02/01/vue%E4%B8%80%E9%94%AE%E5%90%AF%E5%8A%A8%E3%80%81%E5%81%9C%E6%AD%A2%E3%80%81%E6%9F%A5%E7%9C%8B%E7%8A%B6%E6%80%81%E5%AE%9E%E7%94%A8%E5%B7%A5%E5%85%B7/"/>
    <id>http://fyupeng.github.io/2023/02/01/vue%E4%B8%80%E9%94%AE%E5%90%AF%E5%8A%A8%E3%80%81%E5%81%9C%E6%AD%A2%E3%80%81%E6%9F%A5%E7%9C%8B%E7%8A%B6%E6%80%81%E5%AE%9E%E7%94%A8%E5%B7%A5%E5%85%B7/</id>
    <published>2023-02-01T04:07:59.000Z</published>
    <updated>2023-02-01T04:08:28.254Z</updated>
    
    <content type="html"><![CDATA[<h1 id="一、直接给代码"><a href="#一、直接给代码" class="headerlink" title="一、直接给代码"></a>一、直接给代码</h1><h2 id="1-start-sh"><a href="#1-start-sh" class="headerlink" title="1. start.sh"></a>1. start.sh</h2><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/bin/bash</span></span><br><span class="line">function read_dir() &#123;</span><br><span class="line"><span class="keyword">if</span> [ <span class="variable">$#</span> -eq <span class="number">0</span> ]</span><br><span class="line"><span class="keyword">then</span></span><br><span class="line">echo <span class="string">&quot;Usage: $0 [DirFile]&quot;</span></span><br><span class="line">exit <span class="number">1</span></span><br><span class="line">fi</span><br><span class="line"></span><br><span class="line">echo <span class="string">&quot;Service is starting....&quot;</span> </span><br><span class="line"></span><br><span class="line"></span><br><span class="line">cd ./<span class="variable">$1</span></span><br><span class="line">mkdir logs/ &amp;&gt; <span class="regexp">/dev/null</span> &amp;</span><br><span class="line">nohup npm run serve &gt;&amp; logs/catalina-<span class="variable">$(</span>date +%Y-%m-%d).log &amp;</span><br><span class="line"></span><br><span class="line">echo <span class="string">&quot;Service starting succuesful!&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">read_dir <span class="variable">$1</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="2-stop-sh"><a href="#2-stop-sh" class="headerlink" title="2. stop.sh"></a>2. stop.sh</h2><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># !/bin/bash</span></span><br><span class="line"></span><br><span class="line">function read_dir() &#123;</span><br><span class="line"><span class="keyword">if</span> [ <span class="variable">$#</span> -eq <span class="number">0</span> ]</span><br><span class="line"><span class="keyword">then</span></span><br><span class="line">echo <span class="string">&quot;Usage: $0 [DirFile]&quot;</span></span><br><span class="line">exit <span class="number">1</span></span><br><span class="line">fi</span><br><span class="line"></span><br><span class="line">echo <span class="string">&quot;Service is stop....&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> [[ -f <span class="variable">$1</span> ]]</span><br><span class="line"><span class="keyword">then</span></span><br><span class="line">echo <span class="string">&quot;$1 is not a DirFile!&quot;</span></span><br><span class="line">exit <span class="number">1</span></span><br><span class="line">fi</span><br><span class="line"></span><br><span class="line">pid=<span class="string">`ps -ef | grep $1 | grep -v grep | awk &#x27;&#123;print $2&#125;&#x27;`</span></span><br><span class="line"><span class="keyword">if</span> [ -z <span class="variable">$pid</span> ]; <span class="keyword">then</span></span><br><span class="line">echo <span class="string">&quot;&quot;</span></span><br><span class="line">echo <span class="string">&quot;Service $1 is not running! It&#x27;s not necessary to stop it!&quot;</span></span><br><span class="line">echo <span class="string">&quot;&quot;</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">kill -<span class="number">9</span> <span class="variable">$pid</span></span><br><span class="line">echo <span class="string">&quot;&quot;</span></span><br><span class="line">echo <span class="string">&quot;Servuce stop successfuly! pid:$&#123;pid&#125; which has been killid forcibly!&quot;</span></span><br><span class="line">echo <span class="string">&quot;&quot;</span></span><br><span class="line">fi</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">read_dir <span class="variable">$1</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="3-status-sh"><a href="#3-status-sh" class="headerlink" title="3.status.sh"></a>3.status.sh</h2><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># !/bin/bash</span></span><br><span class="line"></span><br><span class="line">function read_dir() &#123;</span><br><span class="line"><span class="keyword">if</span> [ <span class="variable">$#</span> -eq <span class="number">0</span> ]</span><br><span class="line"><span class="keyword">then</span></span><br><span class="line">echo <span class="string">&quot;Usage: $0 [DirFile]&quot;</span></span><br><span class="line">exit <span class="number">1</span></span><br><span class="line">fi</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> [[ -f <span class="variable">$1</span> ]]</span><br><span class="line"><span class="keyword">then</span></span><br><span class="line">echo <span class="string">&quot;$1 is not a DirFile!&quot;</span></span><br><span class="line">exit <span class="number">1</span></span><br><span class="line">fi</span><br><span class="line"></span><br><span class="line">pid=<span class="string">`ps -ef | grep $1 | grep -v grep | awk &#x27;&#123;print $2&#125;&#x27;`</span></span><br><span class="line"><span class="keyword">if</span> [ -z <span class="variable">$pid</span> ]; <span class="keyword">then</span></span><br><span class="line">echo <span class="string">&quot;&quot;</span></span><br><span class="line">echo <span class="string">&quot;Service $1 is not running!&quot;</span></span><br><span class="line">echo <span class="string">&quot;&quot;</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">echo <span class="string">&quot;&quot;</span></span><br><span class="line">echo <span class="string">&quot;Servuce $1 is running. It&#x27;s pids=$&#123;pid&#125;&quot;</span></span><br><span class="line">echo <span class="string">&quot;&quot;</span></span><br><span class="line">fi</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">read_dir <span class="variable">$1</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><h1 id="二、结束语"><a href="#二、结束语" class="headerlink" title="二、结束语"></a>二、结束语</h1><p>评论区可留言，可私信，可互相交流学习，共同进步，欢迎各位给出意见或评价，本人致力于做到优质文章，希望能有幸拜读各位的建议！</p><blockquote><p>专注品质，热爱生活。<br>交流技术，寻求同志。<br>—— 嗝屁小孩纸 QQ：1160886967</p></blockquote>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;一、直接给代码&quot;&gt;&lt;a href=&quot;#一、直接给代码&quot; class=&quot;headerlink&quot; title=&quot;一、直接给代码&quot;&gt;&lt;/a&gt;一、直接给代码&lt;/h1&gt;&lt;h2 id=&quot;1-start-sh&quot;&gt;&lt;a href=&quot;#1-start-sh&quot; class=&quot;head</summary>
      
    
    
    
    <category term="脚本工具" scheme="http://fyupeng.github.io/categories/%E8%84%9A%E6%9C%AC%E5%B7%A5%E5%85%B7/"/>
    
    
    <category term="Java工具" scheme="http://fyupeng.github.io/tags/Java%E5%B7%A5%E5%85%B7/"/>
    
  </entry>
  
  <entry>
    <title>jar包批量启动、查看状态、关闭脚本</title>
    <link href="http://fyupeng.github.io/2023/02/01/jar%E5%8C%85%E6%89%B9%E9%87%8F%E5%90%AF%E5%8A%A8%E3%80%81%E6%9F%A5%E7%9C%8B%E7%8A%B6%E6%80%81%E3%80%81%E5%85%B3%E9%97%AD%E8%84%9A%E6%9C%AC/"/>
    <id>http://fyupeng.github.io/2023/02/01/jar%E5%8C%85%E6%89%B9%E9%87%8F%E5%90%AF%E5%8A%A8%E3%80%81%E6%9F%A5%E7%9C%8B%E7%8A%B6%E6%80%81%E3%80%81%E5%85%B3%E9%97%AD%E8%84%9A%E6%9C%AC/</id>
    <published>2023-02-01T03:55:30.000Z</published>
    <updated>2023-02-01T03:58:10.664Z</updated>
    
    <content type="html"><![CDATA[<h1 id="一、直接给代码"><a href="#一、直接给代码" class="headerlink" title="一、直接给代码"></a>一、直接给代码</h1><h2 id="1-start-sh"><a href="#1-start-sh" class="headerlink" title="1. start.sh"></a>1. start.sh</h2><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env bash</span></span><br><span class="line">  </span><br><span class="line"><span class="comment"># 配置文件名称</span></span><br><span class="line"><span class="comment"># (该配置文件放置在jar包同级目录下并且必须存在已经配置文件名称具备统一性！！！请根据实际的配置文件名称进行修改)</span></span><br><span class="line"><span class="variable constant_">CONFIG_FILE_NAME</span>=<span class="string">&quot;application.properties&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动一个目录下的所有jar包</span></span><br><span class="line">function read_dir()&#123;</span><br><span class="line"><span class="keyword">for</span> file <span class="keyword">in</span> <span class="string">`ls $1`</span></span><br><span class="line"><span class="keyword">do</span></span><br><span class="line">  <span class="comment">#如果当前文件是文件夹则递归处理</span></span><br><span class="line">  <span class="keyword">if</span> [ -d <span class="variable">$1</span><span class="string">&quot;/&quot;</span><span class="variable">$file</span> ];</span><br><span class="line">  <span class="keyword">then</span></span><br><span class="line">    read_dir <span class="variable">$1</span><span class="string">&quot;/&quot;</span><span class="variable">$file</span></span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">    <span class="comment"># 当前文件不是一个文件夹</span></span><br><span class="line">    <span class="keyword">if</span> [[ -f <span class="variable">$1</span><span class="string">&quot;/&quot;</span><span class="variable">$file</span> ]];</span><br><span class="line">    <span class="keyword">then</span></span><br><span class="line">        <span class="comment"># 如果当前文件是一个.jar结尾的文件则启动它</span></span><br><span class="line">        <span class="keyword">if</span> [[ <span class="variable">$&#123;</span><span class="symbol">file:</span><span class="number">0</span>-<span class="number">4</span>&#125; == <span class="string">&#x27;.jar&#x27;</span> ]];</span><br><span class="line">        <span class="keyword">then</span></span><br><span class="line">  echo <span class="variable">$1</span>/<span class="variable">$file</span> 开始启动...</span><br><span class="line">          cd ./<span class="variable">$1</span></span><br><span class="line">          <span class="comment">#nohup java -jar $1&quot;/&quot;$file --spring.config.location=$1&quot;/&quot;$CONFIG_FILE_NAME &gt; /dev/null &amp;</span></span><br><span class="line">   <span class="keyword">if</span> [[ ! -d <span class="string">&quot;./logs/&quot;</span> ]];</span><br><span class="line">  <span class="keyword">then</span>  </span><br><span class="line">          mkdir <span class="string">&quot;./logs&quot;</span> &gt; <span class="regexp">/dev/nul</span></span><br><span class="line">  fi</span><br><span class="line">      nohup java -jar -Dlogging.config=<span class="string">&quot;./config/logback.xml&quot;</span> <span class="string">&quot;./&quot;</span><span class="variable">$file</span> &gt; <span class="string">&quot;./logs/catalina.log&quot;</span> &amp;</span><br><span class="line">     <span class="comment">#nohup java -jar ./$file &gt; ./logs/catalina.log &amp;</span></span><br><span class="line">            echo <span class="variable">$1</span><span class="string">&quot;/&quot;</span><span class="variable">$file</span>  启动成功!</span><br><span class="line">    echo <span class="string">&quot;&quot;</span></span><br><span class="line">            cd - &gt; <span class="regexp">/dev/null</span></span><br><span class="line">        fi</span><br><span class="line">    fi</span><br><span class="line">  fi</span><br><span class="line">done</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">#读取第一个参数</span></span><br><span class="line">read_dir <span class="variable">$1</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="2-status-sh"><a href="#2-status-sh" class="headerlink" title="2. status.sh"></a>2. status.sh</h2><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env bash</span></span><br><span class="line"><span class="comment"># 查看某个目录下所有jar程序的状态</span></span><br><span class="line">function read_dir()&#123;</span><br><span class="line"><span class="keyword">for</span> file <span class="keyword">in</span> <span class="string">`ls $1`</span></span><br><span class="line"><span class="keyword">do</span></span><br><span class="line">  <span class="comment">#如果当前文件是文件夹则递归处理</span></span><br><span class="line">  <span class="keyword">if</span> [ -d <span class="variable">$1</span><span class="string">&quot;/&quot;</span><span class="variable">$file</span> ]</span><br><span class="line">  <span class="keyword">then</span></span><br><span class="line">    read_dir <span class="variable">$1</span><span class="string">&quot;/&quot;</span><span class="variable">$file</span></span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">    <span class="comment"># 当前文件不是一个文件夹</span></span><br><span class="line">    <span class="keyword">if</span> [[ -f <span class="variable">$1</span><span class="string">&quot;/&quot;</span><span class="variable">$file</span> ]]</span><br><span class="line">    <span class="keyword">then</span></span><br><span class="line">        <span class="keyword">if</span> [[ <span class="variable">$&#123;</span><span class="symbol">file:</span><span class="number">0</span>-<span class="number">4</span>&#125; == <span class="string">&#x27;.jar&#x27;</span> ]];</span><br><span class="line">        <span class="keyword">then</span></span><br><span class="line">            <span class="comment"># 获取pid</span></span><br><span class="line">                pid=<span class="string">`ps -ef | grep $file | grep -v grep | awk &#x27;&#123;print $2&#125;&#x27;`</span></span><br><span class="line">            <span class="comment"># -z 表示如果$pid为空时则输出提示</span></span><br><span class="line">                <span class="keyword">if</span> [ -z <span class="variable">$pid</span> ];<span class="keyword">then</span></span><br><span class="line">                        echo <span class="string">&quot;&quot;</span></span><br><span class="line">                echo <span class="string">&quot;Service $file is not running!&quot;</span></span><br><span class="line">                        echo <span class="string">&quot;&quot;</span></span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                        echo <span class="string">&quot;&quot;</span></span><br><span class="line">                echo <span class="string">&quot;Service $1&quot;</span>/<span class="string">&quot;$file is running. It&#x27;s pids=$&#123;pid&#125;&quot;</span></span><br><span class="line">                        echo <span class="string">&quot;&quot;</span></span><br><span class="line">                fi</span><br><span class="line">        fi</span><br><span class="line">    fi</span><br><span class="line">  fi</span><br><span class="line">done</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">#读取第一个参数</span></span><br><span class="line">read_dir <span class="variable">$1</span></span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="3-stop-sh"><a href="#3-stop-sh" class="headerlink" title="3.stop.sh"></a>3.stop.sh</h2><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env bash</span></span><br><span class="line"><span class="comment"># 停止一个目录下的所有jar程序</span></span><br><span class="line">function read_dir()&#123;</span><br><span class="line"><span class="keyword">for</span> file <span class="keyword">in</span> <span class="string">`ls $1`</span></span><br><span class="line"><span class="keyword">do</span></span><br><span class="line">  <span class="comment">#如果当前文件是文件夹则递归处理</span></span><br><span class="line">  <span class="keyword">if</span> [ -d <span class="variable">$1</span><span class="string">&quot;/&quot;</span><span class="variable">$file</span> ]</span><br><span class="line">  <span class="keyword">then</span></span><br><span class="line">    read_dir <span class="variable">$1</span><span class="string">&quot;/&quot;</span><span class="variable">$file</span></span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">    <span class="comment"># 当前文件不是一个文件夹</span></span><br><span class="line">    <span class="keyword">if</span> [[ -f <span class="variable">$1</span><span class="string">&quot;/&quot;</span><span class="variable">$file</span> ]]</span><br><span class="line">    <span class="keyword">then</span></span><br><span class="line">        <span class="keyword">if</span> [[ <span class="variable">$&#123;</span><span class="symbol">file:</span><span class="number">0</span>-<span class="number">4</span>&#125; == <span class="string">&#x27;.jar&#x27;</span> ]];</span><br><span class="line">        <span class="keyword">then</span></span><br><span class="line">            <span class="comment"># 获取pid</span></span><br><span class="line">            <span class="comment"># 模糊匹配 $file 进程| 过滤自身命令进程 | 输出进程表中的进程号</span></span><br><span class="line">                pid=<span class="string">`ps -ef | grep $file | grep -v grep | awk &#x27;&#123;print $2&#125;&#x27;`</span></span><br><span class="line">            <span class="comment"># -z 表示如果$pid为空时则输出提示</span></span><br><span class="line">                <span class="keyword">if</span> [ -z <span class="variable">$pid</span> ]; <span class="keyword">then</span></span><br><span class="line">                echo <span class="string">&quot;Service $file is not running! It&#x27;s not necessary to stop it!&quot;</span></span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                <span class="comment"># 杀死进程</span></span><br><span class="line">                        kill -<span class="number">9</span> <span class="variable">$pid</span></span><br><span class="line">                        echo <span class="string">&quot;Service stop successfully！pid:$&#123;pid&#125; which has been killed forcibly!&quot;</span></span><br><span class="line">echo <span class="string">&quot;&quot;</span></span><br><span class="line">                fi</span><br><span class="line">        fi</span><br><span class="line">    fi</span><br><span class="line">  fi</span><br><span class="line">done</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">#读取第一个参数</span></span><br><span class="line">read_dir <span class="variable">$1</span></span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure><h1 id="二、结束语"><a href="#二、结束语" class="headerlink" title="二、结束语"></a>二、结束语</h1><p>评论区可留言，可私信，可互相交流学习，共同进步，欢迎各位给出意见或评价，本人致力于做到优质文章，希望能有幸拜读各位的建议！</p><blockquote><p>专注品质，热爱生活。<br>交流技术，寻求同志。</p></blockquote>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;一、直接给代码&quot;&gt;&lt;a href=&quot;#一、直接给代码&quot; class=&quot;headerlink&quot; title=&quot;一、直接给代码&quot;&gt;&lt;/a&gt;一、直接给代码&lt;/h1&gt;&lt;h2 id=&quot;1-start-sh&quot;&gt;&lt;a href=&quot;#1-start-sh&quot; class=&quot;head</summary>
      
    
    
    
    <category term="脚本工具" scheme="http://fyupeng.github.io/categories/%E8%84%9A%E6%9C%AC%E5%B7%A5%E5%85%B7/"/>
    
    
    <category term="Java工具" scheme="http://fyupeng.github.io/tags/Java%E5%B7%A5%E5%85%B7/"/>
    
  </entry>
  
  <entry>
    <title>宕机检测、重启、告警开发工具</title>
    <link href="http://fyupeng.github.io/2023/02/01/%E5%AE%95%E6%9C%BA%E6%A3%80%E6%B5%8B%E3%80%81%E9%87%8D%E5%90%AF%E3%80%81%E5%91%8A%E8%AD%A6%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/"/>
    <id>http://fyupeng.github.io/2023/02/01/%E5%AE%95%E6%9C%BA%E6%A3%80%E6%B5%8B%E3%80%81%E9%87%8D%E5%90%AF%E3%80%81%E5%91%8A%E8%AD%A6%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/</id>
    <published>2023-02-01T03:55:08.000Z</published>
    <updated>2023-02-01T03:57:29.585Z</updated>
    
    <content type="html"><![CDATA[<h1 id="一、直接给代码"><a href="#一、直接给代码" class="headerlink" title="一、直接给代码"></a>一、直接给代码</h1><h2 id="1-keepalived-conf"><a href="#1-keepalived-conf" class="headerlink" title="1. keepalived.conf"></a>1. keepalived.conf</h2><p>安装详见 <a href="https://blog.csdn.net/F15217283411/article/details/120801130">keepalived安装教程</a><br>配置前请备份好原文件，新建文件夹<code>/etc/keepalived</code>并将副本<code>keepalived.conf</code>复制到<code>/etc/keepalived</code>即可。</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line">!Configuration File <span class="keyword">for</span> keepalived</span><br><span class="line">global_defs &#123;</span><br><span class="line">        router_id master</span><br><span class="line">        notification_email &#123;</span><br><span class="line">          ******<span class="variable">@163</span>.com  </span><br><span class="line">        &#125;</span><br><span class="line">        notification_email_from ******<span class="variable">@126</span>.com</span><br><span class="line">        smtp_server <span class="number">127.0</span>.<span class="number">0.1</span></span><br><span class="line">        smtp_connect_timeout <span class="number">30</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">vrrp_script chk_http_port &#123;</span><br><span class="line">        <span class="comment"># 脚本检测工具，见 3.nginx_check.sh</span></span><br><span class="line">        script <span class="string">&quot;/usr/local/src/nginx_check.sh&quot;</span></span><br><span class="line">        interval <span class="number">2</span> <span class="comment">#（检测脚本执行的间隔）</span></span><br><span class="line">        weight -<span class="number">20</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">vrrp_instance <span class="variable constant_">VI_1</span> &#123;</span><br><span class="line">        state <span class="variable constant_">MASTER</span> <span class="comment"># 备份服务器上将 MASTER 改为 BACKUP</span></span><br><span class="line">        <span class="comment">## 具体网卡请 键入命令 ip a 查看有哪些来进行配置</span></span><br><span class="line">        interface eth0 /<span class="regexp">/网卡</span></span><br><span class="line"><span class="regexp">        virtual_router_id 1 # 主、备机的 virtual_router_id 必须相同</span></span><br><span class="line"><span class="regexp">        priority 100 # 主、备机取不同的优先级，主机值较大，备份机值较小</span></span><br><span class="line"><span class="regexp">        advert_int 1</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">        authentication &#123;</span></span><br><span class="line"><span class="regexp">                auth_type PASS</span></span><br><span class="line"><span class="regexp">                auth_pass root123</span></span><br><span class="line"><span class="regexp">        &#125;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">        track_script&#123; # 这里调用检测脚本执行，一秒检测一次</span></span><br><span class="line"><span class="regexp">               chk_http_port</span></span><br><span class="line"><span class="regexp">        &#125;</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">        # 根据 内网子网 设置虚拟地址，用于转发，keepalived集群不在此章节</span></span><br><span class="line"><span class="regexp">        virtual_ipaddress &#123;</span></span><br><span class="line"><span class="regexp">                192.168.0.100/</span><span class="number">24</span> dev eth0 label <span class="symbol">eth0:</span><span class="number">1</span></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">#配置keepalived发生故障转移时，触发执行的脚本</span></span><br><span class="line">        notify_master <span class="string">&quot;/etc/keepalived/notify.sh master &quot;</span></span><br><span class="line">        notify_backup <span class="string">&quot;/etc/keepalived/notify.sh backup &quot;</span></span><br><span class="line">        notify_fault  <span class="string">&quot;/etc/keepalived/notify.sh fault &quot;</span></span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="2-notify-sh"><a href="#2-notify-sh" class="headerlink" title="2. notify.sh"></a>2. notify.sh</h2><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/bin/bash</span></span><br><span class="line"><span class="comment">#接收者邮箱，多个以空格分隔</span></span><br><span class="line">contact=(******<span class="variable">@163</span>.com ******<span class="variable">@qq</span>.com)</span><br><span class="line"><span class="comment">#本机ip</span></span><br><span class="line"><span class="variable constant_">HOST_IP</span>=<span class="number">192.168</span>.<span class="number">0.100</span>/<span class="number">24</span></span><br><span class="line">notify() &#123;</span><br><span class="line"><span class="comment">#邮件主题</span></span><br><span class="line">    mailsubject=<span class="string">&quot;vip转移,$HOST_IP keepalived to be $1&quot;</span></span><br><span class="line">    <span class="comment">#邮件正文</span></span><br><span class="line">    mailbody=<span class="string">&quot;$(date +&#x27;%F %T&#x27;): 节点切换, $HOST_IP keepalived changed to be $1&quot;</span></span><br><span class="line">    <span class="keyword">for</span> receiver <span class="keyword">in</span> <span class="variable">$&#123;</span>contact[*]&#125;</span><br><span class="line">    <span class="keyword">do</span></span><br><span class="line">        <span class="comment">#发送邮件</span></span><br><span class="line">        echo <span class="string">&quot;$mailbody&quot;</span> |<span class="params"> mail -s &quot;$mailsubject&quot; $receiver</span></span><br><span class="line"><span class="params">    done</span></span><br><span class="line"><span class="params">&#125;</span></span><br><span class="line"><span class="params"><span class="keyword">case</span> $1 <span class="keyword">in</span></span></span><br><span class="line"><span class="params">master)</span></span><br><span class="line"><span class="params">    notify master</span></span><br><span class="line"><span class="params">    ;;</span></span><br><span class="line"><span class="params">backup)</span></span><br><span class="line"><span class="params">    notify backup</span></span><br><span class="line"><span class="params">    ;;</span></span><br><span class="line"><span class="params">fault)</span></span><br><span class="line"><span class="params">    notify fault</span></span><br><span class="line"><span class="params">    ;;</span></span><br><span class="line"><span class="params">*)</span></span><br><span class="line"><span class="params">    echo &quot;Usage:&#123;master</span>|backup|<span class="params">fault&#125;&quot;</span></span><br><span class="line"><span class="params">    exit 1</span></span><br><span class="line"><span class="params">    ;;</span></span><br><span class="line"><span class="params">esac</span></span><br></pre></td></tr></table></figure><h2 id="3-nginx-check-sh"><a href="#3-nginx-check-sh" class="headerlink" title="3.nginx_check.sh"></a>3.nginx_check.sh</h2><p>以<code>nginx</code>为例，脚本用于宕机检测和重启</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env bash</span></span><br><span class="line">echo <span class="string">&#x27; a&#x27;</span></span><br><span class="line">A=<span class="string">`ps -C nginx --no-header |wc -l`</span></span><br><span class="line"><span class="keyword">if</span> [ <span class="variable">$A</span> -eq <span class="number">0</span> ];<span class="keyword">then</span></span><br><span class="line">        <span class="comment"># 开启nginx服务</span></span><br><span class="line">        /usr/local/nginx/sbin/nginx</span><br><span class="line">        sleep <span class="number">1</span></span><br><span class="line">        <span class="comment"># nginx没有启动成功</span></span><br><span class="line">        <span class="keyword">if</span> [ <span class="string">`ps -C nginx --no-header |wc -l`</span> -eq <span class="number">0</span> ];<span class="keyword">then</span></span><br><span class="line">                <span class="comment"># 杀死keepalived进程</span></span><br><span class="line">                killall keepalived</span><br><span class="line">        fi</span><br><span class="line">fi</span><br></pre></td></tr></table></figure><h2 id="4-mail-rc"><a href="#4-mail-rc" class="headerlink" title="4.mail.rc"></a>4.mail.rc</h2><p>告警邮件发送配置，需要安装相关依赖</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yum -y install mailx</span><br></pre></td></tr></table></figure><p>配置<code>/etc/mail.rc</code></p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># This is the configuration file for Heirloom mailx (formerly</span></span><br><span class="line"><span class="comment"># known under the name &quot;nail&quot;.</span></span><br><span class="line"><span class="comment"># See mailx(1) for further options.</span></span><br><span class="line"><span class="comment"># This file is not overwritten when &#x27;make install&#x27; is run in</span></span><br><span class="line"><span class="comment"># the mailx build process again.</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Sccsid @(#)nail.rc2.11 (gritter) 8/2/08</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># Do not forward to mbox by default since this is likely to be</span></span><br><span class="line"><span class="comment"># irritating for most users today.</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 末尾追加6行配置即可</span></span><br><span class="line"></span><br><span class="line"><span class="comment">#发送邮件的邮箱</span></span><br><span class="line">set from=******<span class="variable">@126</span>.com</span><br><span class="line"><span class="comment">#smtp邮件服务器</span></span><br><span class="line">set smtp=smtp.<span class="number">126</span>.com</span><br><span class="line"><span class="comment">#发送邮件的邮箱用户</span></span><br><span class="line">set smtp-auth-user=******<span class="variable">@126</span>.com</span><br><span class="line"><span class="comment">#邮箱授权码</span></span><br><span class="line">set smtp-auth-password=******</span><br><span class="line">set smtp-auth=login</span><br><span class="line">set ssl-verify=ignore</span><br><span class="line"></span><br></pre></td></tr></table></figure><h1 id="二、效果展示"><a href="#二、效果展示" class="headerlink" title="二、效果展示"></a>二、效果展示</h1><blockquote><p><code>keepalived</code> 服务</p></blockquote><ul><li>启动<figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">systemctl start keepalived</span><br></pre></td></tr></table></figure></li><li>查看状态<figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">systemctl status keepalived</span><br></pre></td></tr></table></figure></li><li>关闭<figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">systemctl stop keepalived</span><br></pre></td></tr></table></figure></li><li>重启<figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">systemctl restart keepalived</span><br></pre></td></tr></table></figure>手动关闭<code>nginx</code>服务，<code>keepalived</code>自动检测并重启<blockquote><p>注意</p></blockquote></li></ul><p>检测周期必须大于<code>notify.sh</code>脚本睡眠时间。</p><blockquote><p>告警邮件</p></blockquote><p><img src="https://img-blog.csdnimg.cn/6fbb9cffe08d44809353e14dd19e2f21.png" alt="在这里插入图片描述"></p><h1 id="三、结束语"><a href="#三、结束语" class="headerlink" title="三、结束语"></a>三、结束语</h1><p>评论区可留言，可私信，可互相交流学习，共同进步，欢迎各位给出意见或评价，本人致力于做到优质文章，希望能有幸拜读各位的建议！</p><blockquote><p>专注品质，热爱生活。<br>交流技术，寻求同志。</p></blockquote>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;一、直接给代码&quot;&gt;&lt;a href=&quot;#一、直接给代码&quot; class=&quot;headerlink&quot; title=&quot;一、直接给代码&quot;&gt;&lt;/a&gt;一、直接给代码&lt;/h1&gt;&lt;h2 id=&quot;1-keepalived-conf&quot;&gt;&lt;a href=&quot;#1-keepalived-con</summary>
      
    
    
    
    <category term="脚本工具" scheme="http://fyupeng.github.io/categories/%E8%84%9A%E6%9C%AC%E5%B7%A5%E5%85%B7/"/>
    
    
    <category term="Java脚本" scheme="http://fyupeng.github.io/tags/Java%E8%84%9A%E6%9C%AC/"/>
    
  </entry>
  
  <entry>
    <title>TCP三次握手和四次挥手</title>
    <link href="http://fyupeng.github.io/2023/01/09/TCP%E4%B8%89%E6%AC%A1%E6%8F%A1%E6%89%8B%E5%92%8C%E5%9B%9B%E6%AC%A1%E6%8C%A5%E6%89%8B/"/>
    <id>http://fyupeng.github.io/2023/01/09/TCP%E4%B8%89%E6%AC%A1%E6%8F%A1%E6%89%8B%E5%92%8C%E5%9B%9B%E6%AC%A1%E6%8C%A5%E6%89%8B/</id>
    <published>2023-01-09T09:43:24.000Z</published>
    <updated>2023-01-09T09:44:48.118Z</updated>
    
    <content type="html"><![CDATA[<h2 id="三次握手"><a href="#三次握手" class="headerlink" title="三次握手"></a>三次握手</h2><ul><li><p>先<code>ping</code>域名为<a href="http://www.baidu.com，便于`DNS`解析。`ping`走的协议就包括`DNS`、`ARP`和`ICMP`。![image-20230109150250842](https://yupeng-tuchuang.oss-cn-shenzhen.aliyuncs.com/image-20230109150250842.png)">www.baidu.com，便于`DNS`解析。`ping`走的协议就包括`DNS`、`ARP`和`ICMP`。![image-20230109150250842](https://yupeng-tuchuang.oss-cn-shenzhen.aliyuncs.com/image-20230109150250842.png)</a></p></li><li><p>接着使用<code>Wireshark</code>去抓包，抓包这里导航栏直接过滤<code>ip</code>就可以了，输入<code>ip.host == 183.232.231.174</code><img src="https://yupeng-tuchuang.oss-cn-shenzhen.aliyuncs.com/image-20230109150551142.png" alt="image-20230109150551142"></p></li><li><p>接着直接在浏览器输入百度域名<a href="http://www.baidu.com访问请求，Wireshark就能抓取包来分析了![image-20230109150841554](https://yupeng-tuchuang.oss-cn-shenzhen.aliyuncs.com/image-20230109150841554.png)">www.baidu.com访问请求，Wireshark就能抓取包来分析了![image-20230109150841554](https://yupeng-tuchuang.oss-cn-shenzhen.aliyuncs.com/image-20230109150841554.png)</a></p></li><li><p>这里我们可以看到，本地通过<code>ipconfig</code>就可以看到<code>source</code>那一列为自己的<code>ip</code>地址，而且是客户端为前后各一次，服务端是第二次。</p><ul><li><p>第一次为<code>SYN</code>标记请求，全英文名我的理解是<code>Synchronized</code>，即为同步，表示同步连接请求；                     <img src="https://yupeng-tuchuang.oss-cn-shenzhen.aliyuncs.com/image-20230109151812384.png" alt="image-20230109151812384"></p></li><li><p>第二次为<code>SYN</code> + <code>ACK</code>组合标记请求，准确来说是两次请求（<code>SYN</code>第一次，<code>ACK</code>第二次），只不过优化对它进行合并了，<code>SYN</code>也表示连接，显而易见，想建立连接两方都要与对方发送同步包请求连接，<code>ACK</code>表示百度接收到了你的同步包，但怎么告知人家？用<code>ACK = SYN(第一次握手的Sequence) + 1</code>，算是一种默契，你收到该包后客户端完成了连接，但百度还未连接成功；   <img src="https://yupeng-tuchuang.oss-cn-shenzhen.aliyuncs.com/image-20230109151909319.png" alt="image-20230109151909319"></p></li><li><p>第三次为<code>ACK</code>标记请求，百度第二次握手发了一个<code>ACK</code>标记，说明这个包是对你前一个包的回应，由前两个图可知。剩下的一个<code>SYN</code>标记，是表明这个包也是一个连接请求包，需要你发回<code>ACK</code>包，即表明我收到了，但得告知你，最后你收到后，你也成功完成连接。                                                                                          <img src="https://yupeng-tuchuang.oss-cn-shenzhen.aliyuncs.com/image-20230109152138802.png" alt="image-20230109152138802"></p></li></ul></li></ul><h2 id="四次挥手"><a href="#四次挥手" class="headerlink" title="四次挥手"></a>四次挥手</h2><ul><li><p>三次挥手需要客户端与服务端断开连接，我这里使用vmware去连接虚拟机centos来演示。跟三次握手一样，你要抓那个网络的包，你就要在这个网络中去连接，比如我的是<code>192.168.10.1</code>这个，用这个通过子网分配给了一台虚拟机<code>192.168.10.100</code><img src="https://yupeng-tuchuang.oss-cn-shenzhen.aliyuncs.com/image-20230109162807968.png" alt="image-20230109162807968"></p></li><li><p>通过<code>xsell</code>连接后，使用命令<code>init 0</code>来断开连接，这会让服务器和客户端都断开连接。分清楚我是虚拟机，服务器是虚拟适配器<code>VMnet8</code>，因为我是在虚拟机里主动命令断开的。抓取信息如下：（将<code>FIN</code>顺带的<code>ACK</code>忽略，就是标准的四次挥手）</p><p><img src="https://yupeng-tuchuang.oss-cn-shenzhen.aliyuncs.com/image-20230109162327318.png" alt="image-20230109162327318"></p><ul><li><p>第一次为<code>FIN</code>标记请求，表示自己要断开连接了，不会再发送消息。<img src="https://yupeng-tuchuang.oss-cn-shenzhen.aliyuncs.com/image-20230109162432906.png" alt="image-20230109162432906"></p></li><li><p>第二次为<code>ACK</code>标记请求，表示服务器收到了，然后服务器不再接收消息，但还是会发送消息，所以这是还未发送<code>FIN</code>包。从这个时候我还是可以接收服务器的消息的，所以我这边还不能断开。   而有些不用接收数据就可以是三次挥手，是将<code>ACK</code>与<code>FIN</code>结合一起，如上图就是，只不过是发<code>FIN</code>请求时带上了<code>ACK</code>，但第一次还是发了<code>ACK</code>，才不会让对方以为没收到导致重发，这样才不会对网络造成阻塞。                                   <img src="https://yupeng-tuchuang.oss-cn-shenzhen.aliyuncs.com/image-20230109162455919.png" alt="image-20230109162455919"></p></li><li><p>第三次为<code>FIN </code>标记请求，这时<code>FIN</code>表示服务器收到我要断开连接的消息，那它也准备断开连接了，不再发送消息，那我也不必再接收了，于是我就完成挥手，原则上是可以断开了，但服务端还不知情，还是不能断开。这里要注意，其实第二次与第三次之间应该还是有很多次请求，这些请求是消息的发送和接收，不算入断开连接中的挥手，而且这些请求都跟第二次挥手以及本次挥手的<code>Acknowledgment number</code>相同。          <img src="https://yupeng-tuchuang.oss-cn-shenzhen.aliyuncs.com/image-20230109162521403.png" alt="image-20230109162521403"></p></li><li><p>第四次是<code>ACK</code>标记请求，就是我要告知<code>VMnet8</code>我已经完成挥手了，你可以断开了。<code>VMnet8</code>收到就可以立即断开，而我等待<code>2MSL</code>时间内没收到服务端重发包就可以断开。因为<code>MSL</code>即为包在网络中的最大生存时间，我发一次和对方未收到超时重发。<img src="https://yupeng-tuchuang.oss-cn-shenzhen.aliyuncs.com/image-20230109162541330.png" alt="image-20230109162541330"></p></li></ul></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;三次握手&quot;&gt;&lt;a href=&quot;#三次握手&quot; class=&quot;headerlink&quot; title=&quot;三次握手&quot;&gt;&lt;/a&gt;三次握手&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;先&lt;code&gt;ping&lt;/code&gt;域名为&lt;a href=&quot;http://www.baidu.com，便于</summary>
      
    
    
    
    <category term="面经" scheme="http://fyupeng.github.io/categories/%E9%9D%A2%E7%BB%8F/"/>
    
    
    <category term="计算机网络面经" scheme="http://fyupeng.github.io/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E9%9D%A2%E7%BB%8F/"/>
    
  </entry>
  
  <entry>
    <title>牛客必会SQL</title>
    <link href="http://fyupeng.github.io/2022/12/06/%E7%89%9B%E5%AE%A2%E5%BF%85%E4%BC%9ASQL/"/>
    <id>http://fyupeng.github.io/2022/12/06/%E7%89%9B%E5%AE%A2%E5%BF%85%E4%BC%9ASQL/</id>
    <published>2022-12-06T01:17:59.000Z</published>
    <updated>2022-12-06T01:19:27.466Z</updated>
    
    <content type="html"><![CDATA[<ul><li><p>牛客SQL</p></li><li><p><a href="#1-sql91-%E8%BF%94%E5%9B%9E%E8%B4%AD%E4%B9%B0%E4%BB%B7%E6%A0%BC%E4%B8%BA-10-%E7%BE%8E%E5%85%83%E6%88%96%E4%BB%A5%E4%B8%8A%E4%BA%A7%E5%93%81%E7%9A%84%E9%A1%BE%E5%AE%A2%E5%88%97%E8%A1%A8">1. <strong>SQL91</strong> <strong>返回购买价格为 10 美元或以上产品的顾客列表</strong></a></p><ul><li><a href="#%E6%8F%8F%E8%BF%B0">描述</a></li><li><a href="#%E7%A4%BA%E4%BE%8B1">示例1</a></li><li><a href="#%E8%A7%A3%E6%B3%951---%E5%86%85%E8%81%94">解法1 - 内联</a></li><li><a href="#%E8%A7%A3%E6%B3%952---%E5%AD%90%E6%9F%A5%E8%AF%A2">解法2 - 子查询</a></li></ul></li><li><p><a href="#2-sql92-%E7%A1%AE%E5%AE%9A%E5%93%AA%E4%BA%9B%E8%AE%A2%E5%8D%95%E8%B4%AD%E4%B9%B0%E4%BA%86-prod_id-%E4%B8%BA-br01-%E7%9A%84%E4%BA%A7%E5%93%81%E4%B8%80">2. <strong>SQL92</strong> <strong>确定哪些订单购买了 prod_id 为 BR01 的产品（一）</strong></a></p><ul><li><a href="#%E6%8F%8F%E8%BF%B0-1">描述</a></li><li><a href="#%E7%A4%BA%E4%BE%8B1-1">示例1</a></li><li><a href="#%E8%A7%A3%E6%B3%951---%E5%86%85%E8%81%94-1">解法1 - 内联</a></li><li><a href="#%E8%A7%A3%E6%B3%952---%E5%AD%90%E6%9F%A5%E8%AF%A2-1">解法2 - 子查询</a></li></ul></li><li><p><a href="#3-sql93-%E8%BF%94%E5%9B%9E%E8%B4%AD%E4%B9%B0-prod_id-%E4%B8%BA-br01-%E7%9A%84%E4%BA%A7%E5%93%81%E7%9A%84%E6%89%80%E6%9C%89%E9%A1%BE%E5%AE%A2%E7%9A%84%E7%94%B5%E5%AD%90%E9%82%AE%E4%BB%B6%E4%B8%80">3. <strong>SQL93</strong> <strong>返回购买 prod_id 为 BR01 的产品的所有顾客的电子邮件（一）</strong></a></p><ul><li><a href="#%E6%8F%8F%E8%BF%B0-2">描述</a></li><li><a href="#%E7%A4%BA%E4%BE%8B1-2">示例1</a></li><li><a href="#%E8%A7%A3%E6%B3%951">解法1</a></li><li><a href="#%E8%A7%A3%E6%B3%952">解法2</a></li></ul></li><li><p><a href="#4-sql94-%E8%BF%94%E5%9B%9E%E6%AF%8F%E4%B8%AA%E9%A1%BE%E5%AE%A2%E4%B8%8D%E5%90%8C%E8%AE%A2%E5%8D%95%E7%9A%84%E6%80%BB%E9%87%91%E9%A2%9D">4. <strong>SQL94</strong> <strong>返回每个顾客不同订单的总金额</strong></a></p><ul><li><a href="#%E6%8F%8F%E8%BF%B0-3">描述</a></li><li><a href="#%E7%A4%BA%E4%BE%8B1-3">示例1</a></li></ul></li><li><p><a href="#5-sql100-%E7%A1%AE%E5%AE%9A%E6%9C%80%E4%BD%B3%E9%A1%BE%E5%AE%A2%E7%9A%84%E5%8F%A6%E4%B8%80%E7%A7%8D%E6%96%B9%E5%BC%8F%E4%BA%8C">5. <strong>SQL100</strong> <strong>确定最佳顾客的另一种方式（二）</strong></a></p><ul><li><a href="#%E6%8F%8F%E8%BF%B0-4">描述</a></li><li><a href="#%E7%A4%BA%E4%BE%8B1-4">示例1</a></li><li><a href="#%E8%A7%A3%E6%B3%95">解法</a></li></ul></li><li><p><a href="#6-sql108-%E7%BB%84%E5%90%88-products-%E8%A1%A8%E4%B8%AD%E7%9A%84%E4%BA%A7%E5%93%81%E5%90%8D%E7%A7%B0%E5%92%8C-customers-%E8%A1%A8%E4%B8%AD%E7%9A%84%E9%A1%BE%E5%AE%A2%E5%90%8D%E7%A7%B0">6. SQL108 组合 Products 表中的产品名称和 Customers 表中的顾客名称</a></p><ul><li><a href="#%E6%8F%8F%E8%BF%B0-5">描述</a></li><li><a href="#%E7%A4%BA%E4%BE%8B1-5">示例1</a></li><li><a href="#%E8%A7%A3%E6%B3%95-1">解法</a></li></ul></li></ul><h3 id="1-SQL91-返回购买价格为-10-美元或以上产品的顾客列表"><a href="#1-SQL91-返回购买价格为-10-美元或以上产品的顾客列表" class="headerlink" title="1. SQL91 返回购买价格为 10 美元或以上产品的顾客列表"></a>1. <strong>SQL91</strong> <strong>返回购买价格为 10 美元或以上产品的顾客列表</strong></h3><h4 id="描述"><a href="#描述" class="headerlink" title="描述"></a>描述</h4><p><code>OrderItems</code>表示订单商品表，含有字段订单号：<code>order_num</code>、订单价格：<code>item_price</code>；<code>Orders</code>表代表订单信息表，含有顾客<code>id</code>：<code>cust_id</code>和订单号：<code>order_num</code></p><p><code>OrderItems</code>表</p><table><thead><tr><th><code>order_num</code></th><th><code>item_price</code></th></tr></thead><tbody><tr><td><code>a1</code></td><td>10</td></tr><tr><td><code>a2</code></td><td>1</td></tr><tr><td><code>a2</code></td><td>1</td></tr><tr><td><code>a4</code></td><td>2</td></tr><tr><td><code>a5</code></td><td>5</td></tr><tr><td><code>a2</code></td><td>1</td></tr><tr><td><code>a7</code></td><td>7</td></tr></tbody></table><p>Orders表</p><table><thead><tr><th><code>order_num</code></th><th><code>cust_id</code></th></tr></thead><tbody><tr><td><code>a1</code></td><td><code>cust10</code></td></tr><tr><td><code>a2</code></td><td><code>cust1</code></td></tr><tr><td><code>a2</code></td><td><code>cust1</code></td></tr><tr><td><code>a4</code></td><td><code>cust2</code></td></tr><tr><td><code>a5</code></td><td><code>cust5</code></td></tr><tr><td><code>a2</code></td><td><code>cust1</code></td></tr><tr><td><code>a7</code></td><td><code>cust7</code></td></tr></tbody></table><p>【问题】使用子查询，返回购买价格为 10 美元或以上产品的顾客列表，结果无需排序。<br>注意：你需要使用 <code>OrderItems</code> 表查找匹配的订单号（<code>order_num</code>），然后使用Order 表检索这些匹配订单的顾客 ID（<code>cust_id</code>）。</p><p>【示例结果】返回顾客id <code>cust_id</code></p><table><thead><tr><th><code>cust_id</code></th></tr></thead><tbody><tr><td><code>cust10</code></td></tr></tbody></table><p>【示例解析】</p><p><code>cust10</code>顾客下单的订单为<code>a1</code>，<code>a1</code>的售出价格大于等于10</p><h4 id="示例1"><a href="#示例1" class="headerlink" title="示例1"></a>示例1</h4><p>输入：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">DROP</span> <span class="keyword">TABLE</span> IF <span class="keyword">EXISTS</span> `OrderItems`;</span><br><span class="line">  <span class="keyword">CREATE</span> <span class="keyword">TABLE</span> IF <span class="keyword">NOT</span> <span class="keyword">EXISTS</span> `OrderItems`(</span><br><span class="line">    order_num <span class="type">VARCHAR</span>(<span class="number">255</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;商品订单号&#x27;</span>,</span><br><span class="line">    item_price <span class="type">INT</span>(<span class="number">16</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;售出价格&#x27;</span></span><br><span class="line">  );</span><br><span class="line">  <span class="keyword">INSERT</span> `OrderItems` <span class="keyword">VALUES</span> (<span class="string">&#x27;a1&#x27;</span>,<span class="number">10</span>),(<span class="string">&#x27;a2&#x27;</span>,<span class="number">1</span>),(<span class="string">&#x27;a2&#x27;</span>,<span class="number">1</span>),(<span class="string">&#x27;a4&#x27;</span>,<span class="number">2</span>),(<span class="string">&#x27;a5&#x27;</span>,<span class="number">5</span>),(<span class="string">&#x27;a2&#x27;</span>,<span class="number">1</span>),(<span class="string">&#x27;a7&#x27;</span>,<span class="number">7</span>);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">DROP</span> <span class="keyword">TABLE</span> IF <span class="keyword">EXISTS</span> `Orders`;</span><br><span class="line">  <span class="keyword">CREATE</span> <span class="keyword">TABLE</span> IF <span class="keyword">NOT</span> <span class="keyword">EXISTS</span> `Orders`(</span><br><span class="line">    order_num <span class="type">VARCHAR</span>(<span class="number">255</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;商品订单号&#x27;</span>,</span><br><span class="line">    cust_id <span class="type">VARCHAR</span>(<span class="number">255</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;顾客id&#x27;</span></span><br><span class="line">  );</span><br><span class="line">  <span class="keyword">INSERT</span> `Orders` <span class="keyword">VALUES</span> (<span class="string">&#x27;a1&#x27;</span>,<span class="string">&#x27;cust10&#x27;</span>),(<span class="string">&#x27;a2&#x27;</span>,<span class="string">&#x27;cust1&#x27;</span>),(<span class="string">&#x27;a2&#x27;</span>,<span class="string">&#x27;cust1&#x27;</span>),(<span class="string">&#x27;a4&#x27;</span>,<span class="string">&#x27;cust2&#x27;</span>),(<span class="string">&#x27;a5&#x27;</span>,<span class="string">&#x27;cust5&#x27;</span>),(<span class="string">&#x27;a2&#x27;</span>,<span class="string">&#x27;cust1&#x27;</span>),(<span class="string">&#x27;a7&#x27;</span>,<span class="string">&#x27;cust7&#x27;</span>);</span><br></pre></td></tr></table></figure><p>输出：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cust10</span><br></pre></td></tr></table></figure><h4 id="解法1-内联"><a href="#解法1-内联" class="headerlink" title="解法1 - 内联"></a>解法1 - 内联</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> cust_id</span><br><span class="line"><span class="keyword">from</span> OrderItems oi</span><br><span class="line"><span class="keyword">inner</span> <span class="keyword">join</span> Orders o</span><br><span class="line"><span class="keyword">on</span> oi.order_num <span class="operator">=</span> o.order_num</span><br><span class="line"><span class="keyword">where</span> item_price <span class="operator">&gt;=</span> <span class="number">10</span></span><br></pre></td></tr></table></figure><h4 id="解法2-子查询"><a href="#解法2-子查询" class="headerlink" title="解法2 - 子查询"></a>解法2 - 子查询</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> cust_id</span><br><span class="line"><span class="keyword">from</span> Orders</span><br><span class="line"><span class="keyword">where</span> order_num <span class="keyword">in</span></span><br><span class="line">(</span><br><span class="line">    <span class="keyword">select</span> order_num</span><br><span class="line">    <span class="keyword">from</span> OrderItems</span><br><span class="line">    <span class="keyword">where</span> item_price <span class="operator">&gt;=</span> <span class="number">10</span></span><br><span class="line">)</span><br><span class="line"></span><br></pre></td></tr></table></figure><ul><li><p>数据量大的情况下，使用连接查询效率更高，因为子查询相当于for循环，要执行多次子查询，而连接只需要查询一次;</p></li><li><p>数据量小的情况下，子查询更容易控制和操作。</p></li></ul><h3 id="2-SQL92-确定哪些订单购买了-prod-id-为-BR01-的产品（一）"><a href="#2-SQL92-确定哪些订单购买了-prod-id-为-BR01-的产品（一）" class="headerlink" title="2. SQL92 确定哪些订单购买了 prod_id 为 BR01 的产品（一）"></a>2. <strong>SQL92</strong> <strong>确定哪些订单购买了 prod_id 为 BR01 的产品（一）</strong></h3><h4 id="描述-1"><a href="#描述-1" class="headerlink" title="描述"></a>描述</h4><p>表<code>OrderItems</code>代表订单商品信息表，<code>prod_id</code>为产品<code>id</code>；<code>Orders</code>表代表订单表有<code>cust_id</code>代表顾客id和订单日期<code>order_date</code></p><p><code>OrderItems</code>表</p><table><thead><tr><th><code>prod_id</code></th><th><code>order_num</code></th></tr></thead><tbody><tr><td><code>BR01</code></td><td><code>a0001</code></td></tr><tr><td><code>BR01</code></td><td><code>a0002</code></td></tr><tr><td><code>BR02</code></td><td><code>a0003</code></td></tr><tr><td>BR02</td><td><code>a0013</code></td></tr></tbody></table><p><code>Orders</code>表</p><table><thead><tr><th><code>order_num</code></th><th><code>cust_id</code></th><th><code>order_date</code></th></tr></thead><tbody><tr><td><code>a0001</code></td><td><code>cust10</code></td><td><code>2022-01-01 00:00:00</code></td></tr><tr><td><code>a0002</code></td><td><code>cust1</code></td><td><code>2022-01-01 00:01:00</code></td></tr><tr><td><code>a0003</code></td><td><code>cust1</code></td><td><code>2022-01-02 00:00:00</code></td></tr><tr><td><code>a0013</code></td><td><code>cust2</code></td><td><code>2022-01-01 00:20:00</code></td></tr></tbody></table><p>【问题】</p><p>编写 <code>SQL</code> 语句，使用子查询来确定哪些订单（在 <code>OrderItems</code> 中）购买了 <code>prod_id</code> 为 “<code>BR01</code>“ 的产品，然后从 <code>Orders</code> 表中返回每个产品对应的顾客 <code>ID</code>（<code>cust_id</code>）和订单日期（<code>order_date</code>），按订购日期对结果进行升序排序。</p><p>【示例结果】返回顾客<code>id</code> <code>cust_id</code>和定单日期<code>order_date</code>。</p><table><thead><tr><th><code>cust_id</code></th><th><code>order_date</code></th></tr></thead><tbody><tr><td><code>cust10</code></td><td><code>2022-01-01 00:00:00</code></td></tr><tr><td><code>cust1</code></td><td><code>2022-01-01 00:01:00</code></td></tr></tbody></table><p>【示例解析】 </p><p>产品id为”<code>BR01</code>“的订单<code>a0001</code>和<code>a002</code>的下单顾客<code>cust10</code>和<code>cust1</code>的下单时间分别为<code>2022-01-01 00:00:00</code>和<code>2022-01-01 00:01:00</code></p><h4 id="示例1-1"><a href="#示例1-1" class="headerlink" title="示例1"></a>示例1</h4><p>输入：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">DROP TABLE IF EXISTS `OrderItems`;</span><br><span class="line">  CREATE TABLE IF NOT EXISTS `OrderItems`(</span><br><span class="line">    prod_id VARCHAR(255) NOT NULL COMMENT &#x27;产品id&#x27;,</span><br><span class="line">    order_num VARCHAR(255) NOT NULL COMMENT &#x27;商品订单号&#x27;</span><br><span class="line">  );</span><br><span class="line">  INSERT `OrderItems` VALUES (&#x27;BR01&#x27;,&#x27;a0001&#x27;),(&#x27;BR01&#x27;,&#x27;a0002&#x27;),(&#x27;BR02&#x27;,&#x27;a0003&#x27;),(&#x27;BR02&#x27;,&#x27;a0013&#x27;);</span><br><span class="line"></span><br><span class="line">  DROP TABLE IF EXISTS `Orders`;</span><br><span class="line">  CREATE TABLE IF NOT EXISTS `Orders`(</span><br><span class="line">    order_num VARCHAR(255) NOT NULL COMMENT &#x27;商品订单号&#x27;,</span><br><span class="line">    cust_id VARCHAR(255) NOT NULL COMMENT &#x27;顾客id&#x27;,</span><br><span class="line">    order_date TIMESTAMP NOT NULL COMMENT &#x27;下单时间&#x27;</span><br><span class="line">  );</span><br><span class="line">  INSERT `Orders` VALUES (&#x27;a0001&#x27;,&#x27;cust10&#x27;,&#x27;2022-01-01 00:00:00&#x27;),(&#x27;a0002&#x27;,&#x27;cust1&#x27;,&#x27;2022-01-01 00:01:00&#x27;),(&#x27;a0003&#x27;,&#x27;cust1&#x27;,&#x27;2022-01-02 00:00:00&#x27;),(&#x27;a0013&#x27;,&#x27;cust2&#x27;,&#x27;2022-01-01 00:20:00&#x27;);</span><br></pre></td></tr></table></figure><p>复制</p><p>输出：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">cust10|2022-01-01 00:00:00</span><br><span class="line">cust1|2022-01-01 00:01:00</span><br></pre></td></tr></table></figure><h4 id="解法1-内联-1"><a href="#解法1-内联-1" class="headerlink" title="解法1 - 内联"></a>解法1 - 内联</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> cust_id, order_date</span><br><span class="line"><span class="keyword">from</span> OrderItems oi</span><br><span class="line"><span class="keyword">inner</span> <span class="keyword">join</span></span><br><span class="line">Orders o</span><br><span class="line"><span class="keyword">on</span> oi.order_num <span class="operator">=</span> o.order_num</span><br><span class="line"><span class="keyword">where</span> prod_id <span class="operator">=</span> <span class="string">&#x27;BR01&#x27;</span></span><br><span class="line"><span class="keyword">order</span> <span class="keyword">by</span> order_date</span><br></pre></td></tr></table></figure><h4 id="解法2-子查询-1"><a href="#解法2-子查询-1" class="headerlink" title="解法2 - 子查询"></a>解法2 - 子查询</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> cust_id, order_date</span><br><span class="line"><span class="keyword">from</span> Orders</span><br><span class="line"><span class="keyword">where</span> order_num <span class="keyword">in</span></span><br><span class="line">(</span><br><span class="line">    <span class="keyword">select</span> order_num</span><br><span class="line">    <span class="keyword">from</span> OrderItems</span><br><span class="line">    <span class="keyword">where</span> prod_id <span class="operator">=</span> <span class="string">&#x27;BR01&#x27;</span></span><br><span class="line">)</span><br><span class="line"><span class="keyword">order</span> <span class="keyword">by</span> order_date</span><br></pre></td></tr></table></figure><h3 id="3-SQL93-返回购买-prod-id-为-BR01-的产品的所有顾客的电子邮件（一）"><a href="#3-SQL93-返回购买-prod-id-为-BR01-的产品的所有顾客的电子邮件（一）" class="headerlink" title="3. SQL93 返回购买 prod_id 为 BR01 的产品的所有顾客的电子邮件（一）"></a>3. <strong>SQL93</strong> <strong>返回购买 prod_id 为 BR01 的产品的所有顾客的电子邮件（一）</strong></h3><h4 id="描述-2"><a href="#描述-2" class="headerlink" title="描述"></a>描述</h4><p>你想知道订购 <code>BR01</code> 产品的日期，有表<code>OrderItems</code>代表订单商品信息表，<code>prod_id</code>为产品<code>id</code>；<code>Orders</code>表代表订单表有<code>cust_id</code>代表顾客<code>id</code>和订单日期<code>order_date</code>；<code>Customers</code>表含有<code>cust_email</code> 顾客邮件和<code>cust_id</code>顾客<code>id</code></p><p><code>OrderItems</code>表</p><table><thead><tr><th><code>prod_id</code></th><th><code>order_num</code></th></tr></thead><tbody><tr><td><code>BR01</code></td><td><code>a0001</code></td></tr><tr><td><code>BR01</code></td><td><code>a0002</code></td></tr><tr><td><code>BR02</code></td><td><code>a0003</code></td></tr><tr><td><code>BR02</code></td><td><code>a0013</code></td></tr></tbody></table><p><code>Orders</code>表</p><table><thead><tr><th><code>order_num</code></th><th><code>cust_id</code></th><th><code>order_date</code></th></tr></thead><tbody><tr><td><code>a0001</code></td><td><code>cust10</code></td><td><code>2022-01-01 00:00:00</code></td></tr><tr><td><code>a0002</code></td><td><code>cust1</code></td><td><code>2022-01-01 00:01:00</code></td></tr><tr><td><code>a0003</code></td><td><code>cust1</code></td><td><code>2022-01-02 00:00:00</code></td></tr><tr><td><code>a0013</code></td><td><code>cust2</code></td><td><code>2022-01-01 00:20:00</code></td></tr></tbody></table><p><code>Customers</code>表代表顾客信息，<code>cust_id</code>为顾客<code>id</code>，<code>cust_email</code>为顾客<code>email</code></p><table><thead><tr><th><code>cust_id</code></th><th><code>cust_email</code></th></tr></thead><tbody><tr><td><code>cust10</code></td><td><code>cust10@cust.com</code></td></tr><tr><td><code>cust1</code></td><td><code>cust1@cust.com</code></td></tr><tr><td><code>cust2</code></td><td><code>cust2@cust.com</code></td></tr></tbody></table><p>【问题】返回购买 <code>prod_id</code> 为<code>BR01</code> 的产品的所有顾客的电子邮件（<code>Customers</code> 表中的 <code>cust_email</code>），结果无需排序。</p><p>提示：这涉及 <code>SELECT</code> 语句，最内层的从 <code>OrderItems</code> 表返回 <code>order_num</code>，中间的从 <code>Customers</code> 表返回 <code>cust_id</code>。</p><p>【示例结果】</p><p>返回顾客<code>email</code> <code>cust_email</code></p><table><thead><tr><th><code>cust_email</code></th></tr></thead><tbody><tr><td><code>cust10@cust.com</code></td></tr><tr><td><code>cust1@cust.com</code></td></tr></tbody></table><p>`【示例解析】 </p><p>产品id为<code>BR01</code>的订单<code>a0001</code>和<code>a002</code>的下单顾客<code>cust10</code>和<code>cust1</code>的顾客<code>email</code> <code>cust_email</code>分别是：<a href="mailto:&#99;&#x75;&#115;&#x74;&#x31;&#x30;&#x40;&#99;&#x75;&#x73;&#x74;&#46;&#x63;&#x6f;&#109;">&#99;&#x75;&#115;&#x74;&#x31;&#x30;&#x40;&#99;&#x75;&#x73;&#x74;&#46;&#x63;&#x6f;&#109;</a> 、<a href="mailto:&#x63;&#x75;&#x73;&#116;&#x31;&#64;&#x63;&#117;&#115;&#116;&#46;&#99;&#111;&#x6d;">&#x63;&#x75;&#x73;&#116;&#x31;&#64;&#x63;&#117;&#115;&#116;&#46;&#99;&#111;&#x6d;</a></p><h4 id="示例1-2"><a href="#示例1-2" class="headerlink" title="示例1"></a>示例1</h4><p>输入：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">DROP</span> <span class="keyword">TABLE</span> IF <span class="keyword">EXISTS</span> `OrderItems`;</span><br><span class="line">  <span class="keyword">CREATE</span> <span class="keyword">TABLE</span> IF <span class="keyword">NOT</span> <span class="keyword">EXISTS</span> `OrderItems`(</span><br><span class="line">    prod_id <span class="type">VARCHAR</span>(<span class="number">255</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;产品id&#x27;</span>,</span><br><span class="line">    order_num <span class="type">VARCHAR</span>(<span class="number">255</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;商品订单号&#x27;</span></span><br><span class="line">  );</span><br><span class="line">  <span class="keyword">INSERT</span> `OrderItems` <span class="keyword">VALUES</span> (<span class="string">&#x27;BR01&#x27;</span>,<span class="string">&#x27;a0001&#x27;</span>),(<span class="string">&#x27;BR01&#x27;</span>,<span class="string">&#x27;a0002&#x27;</span>),(<span class="string">&#x27;BR02&#x27;</span>,<span class="string">&#x27;a0003&#x27;</span>),(<span class="string">&#x27;BR02&#x27;</span>,<span class="string">&#x27;a0013&#x27;</span>);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">DROP</span> <span class="keyword">TABLE</span> IF <span class="keyword">EXISTS</span> `Orders`;</span><br><span class="line">  <span class="keyword">CREATE</span> <span class="keyword">TABLE</span> IF <span class="keyword">NOT</span> <span class="keyword">EXISTS</span> `Orders`(</span><br><span class="line">    order_num <span class="type">VARCHAR</span>(<span class="number">255</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;商品订单号&#x27;</span>,</span><br><span class="line">    cust_id <span class="type">VARCHAR</span>(<span class="number">255</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;顾客id&#x27;</span>,</span><br><span class="line">    order_date <span class="type">TIMESTAMP</span> <span class="keyword">NOT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;下单时间&#x27;</span></span><br><span class="line">  );</span><br><span class="line">  <span class="keyword">INSERT</span> `Orders` <span class="keyword">VALUES</span> (<span class="string">&#x27;a0001&#x27;</span>,<span class="string">&#x27;cust10&#x27;</span>,<span class="string">&#x27;2022-01-01 00:00:00&#x27;</span>),(<span class="string">&#x27;a0002&#x27;</span>,<span class="string">&#x27;cust1&#x27;</span>,<span class="string">&#x27;2022-01-01 00:01:00&#x27;</span>),(<span class="string">&#x27;a0003&#x27;</span>,<span class="string">&#x27;cust1&#x27;</span>,<span class="string">&#x27;2022-01-02 00:00:00&#x27;</span>),(<span class="string">&#x27;a0013&#x27;</span>,<span class="string">&#x27;cust2&#x27;</span>,<span class="string">&#x27;2022-01-01 00:20:00&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">DROP</span> <span class="keyword">TABLE</span> IF <span class="keyword">EXISTS</span> `Customers`;</span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> IF <span class="keyword">NOT</span> <span class="keyword">EXISTS</span> `Customers`(</span><br><span class="line">    cust_id <span class="type">VARCHAR</span>(<span class="number">255</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;顾客id&#x27;</span>,</span><br><span class="line">    cust_email <span class="type">VARCHAR</span>(<span class="number">255</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;顾客email&#x27;</span></span><br><span class="line">  );</span><br><span class="line"><span class="keyword">INSERT</span> `Customers` <span class="keyword">VALUES</span> (<span class="string">&#x27;cust10&#x27;</span>,<span class="string">&#x27;cust10@cust.com&#x27;</span>),(<span class="string">&#x27;cust1&#x27;</span>,<span class="string">&#x27;cust1@cust.com&#x27;</span>),(<span class="string">&#x27;cust2&#x27;</span>,<span class="string">&#x27;cust2@cust.com&#x27;</span>);</span><br></pre></td></tr></table></figure><p>输出：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">cust10<span class="variable">@cust</span>.com</span><br><span class="line">cust1<span class="variable">@cust</span>.com</span><br></pre></td></tr></table></figure><h4 id="解法1"><a href="#解法1" class="headerlink" title="解法1"></a>解法1</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> cust_email</span><br><span class="line"><span class="keyword">from</span> OrderItems oi</span><br><span class="line"><span class="keyword">inner</span> <span class="keyword">join</span> Orders o</span><br><span class="line"><span class="keyword">inner</span> <span class="keyword">join</span> Customers c</span><br><span class="line"><span class="keyword">on</span> oi.order_num <span class="operator">=</span> o.order_num <span class="keyword">and</span></span><br><span class="line">o.cust_id <span class="operator">=</span> c.cust_id</span><br><span class="line"><span class="keyword">where</span> prod_id <span class="operator">=</span> <span class="string">&#x27;BR01&#x27;</span></span><br></pre></td></tr></table></figure><h4 id="解法2"><a href="#解法2" class="headerlink" title="解法2"></a>解法2</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> cust_email</span><br><span class="line"><span class="keyword">from</span> Customers</span><br><span class="line"><span class="keyword">where</span> cust_id <span class="keyword">in</span></span><br><span class="line">(</span><br><span class="line">    <span class="keyword">select</span> cust_id</span><br><span class="line">    <span class="keyword">from</span> Orders</span><br><span class="line">    <span class="keyword">where</span> order_num <span class="keyword">in</span></span><br><span class="line">    (</span><br><span class="line">        <span class="keyword">select</span> order_num</span><br><span class="line">        <span class="keyword">from</span> OrderItems</span><br><span class="line">        <span class="keyword">where</span> prod_id <span class="operator">=</span> <span class="string">&#x27;BR01&#x27;</span></span><br><span class="line">    )</span><br><span class="line">)</span><br></pre></td></tr></table></figure><h3 id="4-SQL94-返回每个顾客不同订单的总金额"><a href="#4-SQL94-返回每个顾客不同订单的总金额" class="headerlink" title="4. SQL94 返回每个顾客不同订单的总金额"></a>4. <strong>SQL94</strong> <strong>返回每个顾客不同订单的总金额</strong></h3><h4 id="描述-3"><a href="#描述-3" class="headerlink" title="描述"></a>描述</h4><p>我们需要一个顾客 <code>ID</code> 列表，其中包含他们已订购的总金额。</p><p><code>OrderItems</code>表代表订单信息，<code>OrderItems</code>表有订单号：<code>order_num</code>和商品售出价格：<code>item_price</code>、商品数量：<code>quantity</code>。</p><table><thead><tr><th><code>order_num</code></th><th><code>item_price</code></th><th><code>quantity</code></th></tr></thead><tbody><tr><td><code>a0001</code></td><td><code>10</code></td><td><code>105</code></td></tr><tr><td><code>a0002</code></td><td><code>1</code></td><td><code>1100</code></td></tr><tr><td><code>a0002</code></td><td><code>1</code></td><td><code>200</code></td></tr><tr><td><code>a0013</code></td><td><code>2</code></td><td><code>1121</code></td></tr><tr><td><code>a0003</code></td><td><code>5</code></td><td><code>10</code></td></tr><tr><td><code>a0003</code></td><td><code>1</code></td><td><code>19</code></td></tr><tr><td><code>a0003</code></td><td><code>7</code></td><td><code>5</code></td></tr></tbody></table><p><code>Orders</code>表订单号：<code>order_num</code>、顾客<code>id</code>：<code>cust_id</code></p><table><thead><tr><th><code>order_num</code></th><th><code>cust_id</code></th></tr></thead><tbody><tr><td><code>a0001</code></td><td><code>cust10</code></td></tr><tr><td><code>a0002</code></td><td><code>cust1</code></td></tr><tr><td><code>a0003</code></td><td><code>cust1</code></td></tr><tr><td><code>a0013</code></td><td><code>cust2</code></td></tr></tbody></table><p>【问题】</p><p>编写 <code>SQL</code>语句，返回顾客 <code>ID</code>（<code>Orders</code> 表中的 <code>cust_id</code>），并使用子查询返回<code>total_ordered</code> 以便返回每个顾客的订单总数，将结果按金额从大到小排序。</p><p>提示：你之前已经使用 <code>SUM()</code>计算订单总数。</p><p>【示例结果】返回顾客<code>id</code> <code>cust_id</code>和<code>total_order</code>下单总额</p><table><thead><tr><th><code>cust_id</code></th><th><code>total_ordered</code></th></tr></thead><tbody><tr><td><code>cust2</code></td><td><code>2242</code></td></tr><tr><td><code>cust1</code></td><td><code>1300</code></td></tr><tr><td><code>cust10</code></td><td><code>1050</code></td></tr><tr><td><code>cust2</code></td><td><code>104</code></td></tr></tbody></table><p>【示例解析】cust2在Orders里面的订单a0013，a0013的售出价格是2售出数量是1121，总额是2242，最后返回cust2的支付总额是2242。</p><h4 id="示例1-3"><a href="#示例1-3" class="headerlink" title="示例1"></a>示例1</h4><p>输入：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">DROP</span> <span class="keyword">TABLE</span> IF <span class="keyword">EXISTS</span> `OrderItems`;</span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> IF <span class="keyword">NOT</span> <span class="keyword">EXISTS</span> `OrderItems`(</span><br><span class="line">order_num <span class="type">VARCHAR</span>(<span class="number">255</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;商品订单号&#x27;</span>,</span><br><span class="line">item_price <span class="type">INT</span>(<span class="number">16</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;售出价格&#x27;</span>,</span><br><span class="line">quantity <span class="type">INT</span>(<span class="number">16</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;商品数量&#x27;</span></span><br><span class="line">);</span><br><span class="line"><span class="keyword">INSERT</span> `OrderItems` <span class="keyword">VALUES</span> (<span class="string">&#x27;a0001&#x27;</span>,<span class="number">10</span>,<span class="number">105</span>),(<span class="string">&#x27;a0002&#x27;</span>,<span class="number">1</span>,<span class="number">1100</span>),(<span class="string">&#x27;a0002&#x27;</span>,<span class="number">1</span>,<span class="number">200</span>),(<span class="string">&#x27;a0013&#x27;</span>,<span class="number">2</span>,<span class="number">1121</span>),(<span class="string">&#x27;a0003&#x27;</span>,<span class="number">5</span>,<span class="number">10</span>),(<span class="string">&#x27;a0003&#x27;</span>,<span class="number">1</span>,<span class="number">19</span>),(<span class="string">&#x27;a0003&#x27;</span>,<span class="number">7</span>,<span class="number">5</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">DROP</span> <span class="keyword">TABLE</span> IF <span class="keyword">EXISTS</span> `Orders`;</span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> IF <span class="keyword">NOT</span> <span class="keyword">EXISTS</span> `Orders`(</span><br><span class="line">  order_num <span class="type">VARCHAR</span>(<span class="number">255</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;商品订单号&#x27;</span>,</span><br><span class="line">  cust_id <span class="type">VARCHAR</span>(<span class="number">255</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;顾客id&#x27;</span></span><br><span class="line">);</span><br><span class="line"><span class="keyword">INSERT</span> `Orders` <span class="keyword">VALUES</span> (<span class="string">&#x27;a0001&#x27;</span>,<span class="string">&#x27;cust10&#x27;</span>),(<span class="string">&#x27;a0003&#x27;</span>,<span class="string">&#x27;cust1&#x27;</span>),(<span class="string">&#x27;a0013&#x27;</span>,<span class="string">&#x27;cust2&#x27;</span>);</span><br></pre></td></tr></table></figure><p>输出：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">cust2<span class="operator">|</span><span class="number">2242.000</span></span><br><span class="line">cust10<span class="operator">|</span><span class="number">1050.000</span></span><br><span class="line">cust1<span class="operator">|</span><span class="number">104.000</span></span><br></pre></td></tr></table></figure><h4 id="解法"><a href="#解法" class="headerlink" title="解法"></a>解法</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> cust_id, <span class="built_in">sum</span>(item_price <span class="operator">*</span> quantity) <span class="keyword">as</span> total_ordered</span><br><span class="line"><span class="keyword">from</span> OrderItems oi</span><br><span class="line"><span class="keyword">inner</span> <span class="keyword">join</span> Orders o</span><br><span class="line"><span class="keyword">on</span> oi.order_num <span class="operator">=</span> o.order_num</span><br><span class="line"><span class="keyword">group</span> <span class="keyword">by</span> cust_id</span><br><span class="line"><span class="keyword">order</span> <span class="keyword">by</span> <span class="built_in">sum</span>(item_price <span class="operator">*</span> quantity) <span class="keyword">desc</span></span><br></pre></td></tr></table></figure><h3 id="5-SQL100-确定最佳顾客的另一种方式（二）"><a href="#5-SQL100-确定最佳顾客的另一种方式（二）" class="headerlink" title="5. SQL100 确定最佳顾客的另一种方式（二）"></a>5. <strong>SQL100</strong> <strong>确定最佳顾客的另一种方式（二）</strong></h3><h4 id="描述-4"><a href="#描述-4" class="headerlink" title="描述"></a>描述</h4><p><code>OrderItems</code>表代表订单信息，确定最佳顾客的另一种方式是看他们花了多少钱，<code>OrderItems</code>表有订单号<code>order_num</code>和<code>item_price</code>商品售出价格、<code>quantity</code>商品数量</p><table><thead><tr><th><code>order_num</code></th><th><code>item_price</code></th><th><code>quantity</code></th></tr></thead><tbody><tr><td><code>a1</code></td><td><code>10</code></td><td><code>105</code></td></tr><tr><td><code>a2</code></td><td><code>1</code></td><td><code>1100</code></td></tr><tr><td><code>a2</code></td><td><code>1</code></td><td><code>200</code></td></tr><tr><td><code>a4</code></td><td><code>2</code></td><td><code>1121</code></td></tr><tr><td><code>a5</code></td><td><code>5</code></td><td><code>10</code></td></tr><tr><td><code>a2</code></td><td><code>1</code></td><td><code>19</code></td></tr><tr><td><code>a7</code></td><td><code>7</code></td><td><code>5</code></td></tr></tbody></table><p><code>Orders</code>表含有字段<code>order_num</code> 订单号、<code>cust_id</code>顾客id</p><table><thead><tr><th><code>order_num</code></th><th><code>cust_id</code></th></tr></thead><tbody><tr><td><code>a1</code></td><td><code>cust10</code></td></tr><tr><td><code>a2</code></td><td><code>cust1</code></td></tr><tr><td><code>a3</code></td><td><code>cust2</code></td></tr><tr><td><code>a4</code></td><td><code>cust22</code></td></tr><tr><td><code>a5</code></td><td><code>cust221</code></td></tr><tr><td><code>a7</code></td><td><code>cust2217</code></td></tr></tbody></table><p>顾客表<code>Customers</code>有字段<code>cust_id</code> 客户<code>id</code>、<code>cust_name</code> 客户姓名</p><table><thead><tr><th><code>cust_id</code></th><th><code>cust_name</code></th></tr></thead><tbody><tr><td><code>cust10</code></td><td><code>andy</code></td></tr><tr><td><code>cust1</code></td><td><code>ben</code></td></tr><tr><td><code>cust2</code></td><td><code>tony</code></td></tr><tr><td><code>cust22</code></td><td><code>tom</code></td></tr><tr><td><code>cust221</code></td><td><code>an</code></td></tr><tr><td><code>cust2217</code></td><td><code>hex</code></td></tr></tbody></table><p>【问题】编写 <code>SQL</code> 语句，返回订单总价不小于<code>1000</code> 的客户名称和总额（<code>OrderItems</code> 表中的<code>order_num</code>）。</p><p>提示：需要计算总和（<code>item_price</code> 乘以 <code>quantity</code>）。按总额对结果进行排序，请使用<code>INNER</code> <code>JOIN</code> 语法。</p><p>【示例结果】</p><table><thead><tr><th><code>cust_name</code></th><th><code>total_price</code></th></tr></thead><tbody><tr><td><code>andy</code></td><td><code>1050</code></td></tr><tr><td><code>ben</code></td><td><code>1319</code></td></tr><tr><td><code>tom</code></td><td><code>2242</code></td></tr></tbody></table><p>【示例解析】</p><p>总额（<code>item_price</code> 乘以 <code>quantity</code>）大于等于<code>1000</code>的订单号，例如<code>a2</code>对应的顾客id为<code>cust1</code>，<code>cust1</code>的顾客名称<code>cust_name</code>是<code>ben</code>，最后返回<code>ben</code>作为<code>order_num</code> <code>a2</code>的<code>quantity</code> * <code>item_price</code>总和的结果<code>1319</code>。</p><h4 id="示例1-4"><a href="#示例1-4" class="headerlink" title="示例1"></a>示例1</h4><p>输入：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">DROP</span> <span class="keyword">TABLE</span> IF <span class="keyword">EXISTS</span> `OrderItems`;</span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> IF <span class="keyword">NOT</span> <span class="keyword">EXISTS</span> `OrderItems`(</span><br><span class="line">order_num <span class="type">VARCHAR</span>(<span class="number">255</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;商品订单号&#x27;</span>,</span><br><span class="line">item_price <span class="type">INT</span>(<span class="number">16</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;售出价格&#x27;</span>,</span><br><span class="line">quantity <span class="type">INT</span>(<span class="number">16</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;商品数量&#x27;</span></span><br><span class="line">);</span><br><span class="line"><span class="keyword">INSERT</span> `OrderItems` <span class="keyword">VALUES</span> (<span class="string">&#x27;a1&#x27;</span>,<span class="number">10</span>,<span class="number">105</span>),(<span class="string">&#x27;a2&#x27;</span>,<span class="number">1</span>,<span class="number">1100</span>),(<span class="string">&#x27;a2&#x27;</span>,<span class="number">1</span>,<span class="number">200</span>),(<span class="string">&#x27;a4&#x27;</span>,<span class="number">2</span>,<span class="number">1121</span>),(<span class="string">&#x27;a5&#x27;</span>,<span class="number">5</span>,<span class="number">10</span>),(<span class="string">&#x27;a2&#x27;</span>,<span class="number">1</span>,<span class="number">19</span>),(<span class="string">&#x27;a7&#x27;</span>,<span class="number">7</span>,<span class="number">5</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">DROP</span> <span class="keyword">TABLE</span> IF <span class="keyword">EXISTS</span> `Customers`;</span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> IF <span class="keyword">NOT</span> <span class="keyword">EXISTS</span> `Customers`(</span><br><span class="line">cust_id <span class="type">VARCHAR</span>(<span class="number">255</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;客户id&#x27;</span>,</span><br><span class="line">cust_name <span class="type">VARCHAR</span>(<span class="number">255</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;客户姓名&#x27;</span></span><br><span class="line">);</span><br><span class="line"><span class="keyword">INSERT</span> `Customers` <span class="keyword">VALUES</span> (<span class="string">&#x27;cust10&#x27;</span>,<span class="string">&#x27;andy&#x27;</span>),(<span class="string">&#x27;cust1&#x27;</span>,<span class="string">&#x27;ben&#x27;</span>),(<span class="string">&#x27;cust2&#x27;</span>,<span class="string">&#x27;tony&#x27;</span>),(<span class="string">&#x27;cust22&#x27;</span>,<span class="string">&#x27;tom&#x27;</span>),(<span class="string">&#x27;cust221&#x27;</span>,<span class="string">&#x27;an&#x27;</span>),(<span class="string">&#x27;cust2217&#x27;</span>,<span class="string">&#x27;hex&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">DROP</span> <span class="keyword">TABLE</span> IF <span class="keyword">EXISTS</span> `Orders`;</span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> IF <span class="keyword">NOT</span> <span class="keyword">EXISTS</span> `Orders`(</span><br><span class="line">  order_num <span class="type">VARCHAR</span>(<span class="number">255</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;商品订单号&#x27;</span>,</span><br><span class="line">  cust_id <span class="type">VARCHAR</span>(<span class="number">255</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;顾客id&#x27;</span></span><br><span class="line">);</span><br><span class="line"><span class="keyword">INSERT</span> `Orders` <span class="keyword">VALUES</span> (<span class="string">&#x27;a1&#x27;</span>,<span class="string">&#x27;cust10&#x27;</span>),(<span class="string">&#x27;a2&#x27;</span>,<span class="string">&#x27;cust1&#x27;</span>),(<span class="string">&#x27;a3&#x27;</span>,<span class="string">&#x27;cust2&#x27;</span>),(<span class="string">&#x27;a4&#x27;</span>,<span class="string">&#x27;cust22&#x27;</span>),(<span class="string">&#x27;a5&#x27;</span>,<span class="string">&#x27;cust221&#x27;</span>),(<span class="string">&#x27;a7&#x27;</span>,<span class="string">&#x27;cust2217&#x27;</span>);</span><br></pre></td></tr></table></figure><p>输出：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">andy<span class="operator">|</span><span class="number">1050.000</span></span><br><span class="line">ben<span class="operator">|</span><span class="number">1319.000</span></span><br><span class="line">tom<span class="operator">|</span><span class="number">2242.000</span></span><br></pre></td></tr></table></figure><h4 id="解法-1"><a href="#解法-1" class="headerlink" title="解法"></a>解法</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> cust_name, <span class="built_in">sum</span>(item_price <span class="operator">*</span> quantity) <span class="keyword">as</span> total_price</span><br><span class="line"><span class="keyword">from</span> OrderItems oi</span><br><span class="line"><span class="keyword">inner</span> <span class="keyword">join</span> Orders o</span><br><span class="line"><span class="keyword">inner</span> <span class="keyword">join</span> Customers c</span><br><span class="line"><span class="keyword">on</span> oi.order_num <span class="operator">=</span> o.order_num</span><br><span class="line"><span class="keyword">and</span> o.cust_id <span class="operator">=</span> c.cust_id</span><br><span class="line"><span class="keyword">group</span> <span class="keyword">by</span> cust_name</span><br><span class="line"><span class="keyword">having</span> <span class="built_in">sum</span>(item_price <span class="operator">*</span> quantity) <span class="operator">&gt;=</span> <span class="number">1000</span></span><br><span class="line"><span class="keyword">order</span> <span class="keyword">by</span> total_price</span><br></pre></td></tr></table></figure><h3 id="6-SQL108-组合-Products-表中的产品名称和-Customers-表中的顾客名称"><a href="#6-SQL108-组合-Products-表中的产品名称和-Customers-表中的顾客名称" class="headerlink" title="6. SQL108 组合 Products 表中的产品名称和 Customers 表中的顾客名称"></a>6. SQL108 组合 Products 表中的产品名称和 Customers 表中的顾客名称</h3><h4 id="描述-5"><a href="#描述-5" class="headerlink" title="描述"></a>描述</h4><p><code>Products</code>表含有字段<code>prod_name</code>代表产品名称</p><table><thead><tr><th><code>prod_name</code></th></tr></thead><tbody><tr><td><code>flower</code></td></tr><tr><td><code>rice</code></td></tr><tr><td><code>ring</code></td></tr><tr><td><code>umbrella</code></td></tr></tbody></table><p><code>Customers</code>表代表顾客信息，<code>cust_name</code>代表顾客名称</p><table><thead><tr><th><code>cust_name</code></th></tr></thead><tbody><tr><td><code>andy</code></td></tr><tr><td><code>ben</code></td></tr><tr><td><code>tony</code></td></tr><tr><td><code>tom</code></td></tr><tr><td><code>an</code></td></tr><tr><td><code>lee</code></td></tr><tr><td><code>hex</code></td></tr></tbody></table><p>【问题】</p><p>编写 <code>SQL</code> 语句，组合 <code>Products</code> 表中的产品名称（<code>prod_name</code>）和 <code>Customers</code> 表中的顾客名称（<code>cust_name</code>）并返回，然后按产品名称对结果进行升序排序。</p><p>【示例结果】</p><table><thead><tr><th><code>prod_name</code></th></tr></thead><tbody><tr><td><code>an</code></td></tr><tr><td><code>andy</code></td></tr><tr><td><code>ben</code></td></tr><tr><td><code>flower</code></td></tr><tr><td><code>hex</code></td></tr><tr><td><code>lee</code></td></tr><tr><td><code>rice</code></td></tr><tr><td><code>ring</code></td></tr><tr><td><code>tom</code></td></tr><tr><td><code>tony</code></td></tr><tr><td><code>umbrella</code></td></tr></tbody></table><p>【示例解析】</p><p>拼接<code>cust_name</code>和<code>prod_name</code>并根据结果升序排序</p><h4 id="示例1-5"><a href="#示例1-5" class="headerlink" title="示例1"></a>示例1</h4><p>输入：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">DROP</span> <span class="keyword">TABLE</span> IF <span class="keyword">EXISTS</span> `Products`;</span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> IF <span class="keyword">NOT</span> <span class="keyword">EXISTS</span> `Products` (</span><br><span class="line">`prod_name` <span class="type">VARCHAR</span>(<span class="number">255</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;产品名称&#x27;</span></span><br><span class="line">);</span><br><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> `Products` <span class="keyword">VALUES</span> (<span class="string">&#x27;flower&#x27;</span>),</span><br><span class="line">(<span class="string">&#x27;rice&#x27;</span>),</span><br><span class="line">(<span class="string">&#x27;ring&#x27;</span>),</span><br><span class="line">(<span class="string">&#x27;umbrella&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">DROP</span> <span class="keyword">TABLE</span> IF <span class="keyword">EXISTS</span> `Customers`;</span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> IF <span class="keyword">NOT</span> <span class="keyword">EXISTS</span> `Customers`(</span><br><span class="line">cust_name <span class="type">VARCHAR</span>(<span class="number">255</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;客户姓名&#x27;</span></span><br><span class="line">);</span><br><span class="line"><span class="keyword">INSERT</span> `Customers` <span class="keyword">VALUES</span> (<span class="string">&#x27;andy&#x27;</span>),(<span class="string">&#x27;ben&#x27;</span>),(<span class="string">&#x27;tony&#x27;</span>),(<span class="string">&#x27;tom&#x27;</span>),(<span class="string">&#x27;an&#x27;</span>),(<span class="string">&#x27;lee&#x27;</span>),(<span class="string">&#x27;hex&#x27;</span>);</span><br></pre></td></tr></table></figure><p>输出：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">an</span><br><span class="line">andy</span><br><span class="line">ben</span><br><span class="line">flower</span><br><span class="line">hex</span><br><span class="line">lee</span><br><span class="line">rice</span><br><span class="line">ring</span><br><span class="line">tom</span><br><span class="line">tony</span><br><span class="line">umbrella</span><br></pre></td></tr></table></figure><h4 id="解法-2"><a href="#解法-2" class="headerlink" title="解法"></a>解法</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> prod_name</span><br><span class="line"><span class="keyword">from</span> Products</span><br><span class="line"><span class="keyword">union</span></span><br><span class="line"><span class="keyword">select</span> cust_name <span class="keyword">as</span> prod_name</span><br><span class="line"><span class="keyword">from</span> Customers</span><br><span class="line"><span class="keyword">order</span> <span class="keyword">by</span> prod_name</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;ul&gt;
&lt;li&gt;&lt;p&gt;牛客SQL&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;#1-sql91-%E8%BF%94%E5%9B%9E%E8%B4%AD%E4%B9%B0%E4%BB%B7%E6%A0%BC%E4%B8%BA-10-%E7%BE%8E%E5%85%83%E</summary>
      
    
    
    
    
    <category term="SQL基础" scheme="http://fyupeng.github.io/tags/SQL%E5%9F%BA%E7%A1%80/"/>
    
  </entry>
  
  <entry>
    <title>腾讯一面</title>
    <link href="http://fyupeng.github.io/2022/11/29/%E8%85%BE%E8%AE%AF%E4%B8%80%E9%9D%A2/"/>
    <id>http://fyupeng.github.io/2022/11/29/%E8%85%BE%E8%AE%AF%E4%B8%80%E9%9D%A2/</id>
    <published>2022-11-29T13:08:12.000Z</published>
    <updated>2022-11-29T13:10:20.156Z</updated>
    
    <content type="html"><![CDATA[<p>自我介绍这里一笔带过，给对面介绍自己内在 + 外在 + 校园经历 + 校园项目  + 意向岗位</p><p>腾讯子公司云智研发一面</p><h3 id="1-有了解过C-吗？接受转语言吗？"><a href="#1-有了解过C-吗？接受转语言吗？" class="headerlink" title="1. 有了解过C++吗？接受转语言吗？"></a>1. 有了解过C++吗？接受转语言吗？</h3><h3 id="2-有没有了解过一些框架的底层原理、底层优化、数据库的索引优化"><a href="#2-有没有了解过一些框架的底层原理、底层优化、数据库的索引优化" class="headerlink" title="2. 有没有了解过一些框架的底层原理、底层优化、数据库的索引优化"></a>2. 有没有了解过一些框架的底层原理、底层优化、数据库的索引优化</h3><h3 id="3-了解过哪些Map，可以从底层简单说下嘛？"><a href="#3-了解过哪些Map，可以从底层简单说下嘛？" class="headerlink" title="3. 了解过哪些Map，可以从底层简单说下嘛？"></a>3. 了解过哪些Map，可以从底层简单说下嘛？</h3><p>比如我最常使用的有<code>HashMap</code>、<code>HashTable</code>、<code>CurrentHashMap</code>这三种</p><p><code>HashMap</code>它的底层是采用了数组+链表的数据结构</p><p>从源码中分析，<code>HashMap</code>有一个静态内部类<code>Node</code>，<code>HashMap</code>通过成员变量<code>table</code>（<code>Node</code>数组类型）来采取<code>put</code>和<code>get</code>操作，该成员变量用关键字<code>transient</code>加以修饰，表明禁止序列化。</p><p>而<code>Node</code>是<code>table</code>数组的一个结点，内部成员数据有<code>hash</code>、<code>key</code>、<code>value</code>和指向<code>Node</code>类的<code>next</code></p><blockquote><p>– 初始化阶段 –</p></blockquote><p>在这里，<code>put</code>操作后，<code>table</code>会先调用<code>resize</code>方法进行扩展，扩展后<code>table</code>才从<code>null</code>变为<code>16</code>位的<code>Node</code>数组，初始化后的每一个下标依旧为<code>null</code>，并且初始化阈值<code>threshold</code>，默认为<code>0.75 * size</code>,作用是与成员变量<code>size</code>（成员变量<code>size</code>会在每次<code>put</code>而且要该<code>key</code>值为首次操作即不存在该<code>Map</code>中会自增）进行比较，如果<code>size</code>数即表明<code>put</code>的<code>key-value</code>对的数量超过阈值<code>threshold</code>，进行下一步操作，即<code>resize</code>方法另一作用。</p><blockquote><p>– 扩容阶段 –</p></blockquote><p>扩容阶段更简单，就是将<code>阈值*2</code>来扩容，然后遍历整个<code>table</code>即<code>Node</code>数组，如果发现有<code>Node</code>结点，那么这里就出现了两种情况，第一种为每个结点上是链表结构，另一种则是红黑树结构。</p><p>扩容操作是创建新<code>table</code>来扩容，没有在旧<code>table</code>上直接扩容，遍历旧<code>table</code>进行扩容时，将会对每个<code>table</code>槽位置空，多线程在扩容时会发生一些导致线程安全的问题。</p><p><code>ConcurrentHashMap</code>在<code>JDK7</code>扩容时使用到了<code>ReentrantLock</code>对段加锁，这样该段的旧<code>table</code>也无法操作，<code>JDK</code>则通过一种手段设置标志位，后面操作使用<code>CAS</code>乐观锁来操作，成功则退出，不成功发现扩容标志后则参与扩容。</p><p>扩容成功的结果就是发生链表转移或者是红黑树转移，都是为了将节点分散，以至于发挥出<code>Hash</code>一次命中取出和保存的高性能。</p><p>当然刚开始没有结点数据，<code>oldTable</code>为<code>null</code>，不需要转移结点，然后什么事都没干，接着通过<code>hash</code>与<code>table</code>的长度进行<code>&amp;</code>操作命中槽位，<code>put</code>操作会保存到数组对应下标处即槽位中的结点。</p><blockquote><p>– 结点初始化 –</p></blockquote><p>保存之前很简单，得判断对应<code>Node</code>数组下标结点是否为<code>null</code>，因为这里是懒加载嘛，刚开始没有初始化数组的每一个元素，<code>resize</code>初始化方法也只是<code>new</code>了个<code>Node</code>数组，而且这也一样是懒加载。</p><p>所以在这里会将<code>put</code>中<code>key</code>的<code>hash、key和value</code>值封装到<code>Node</code>结点中并实例化，然后赋值到对应的<code>Node</code>数组下标中。</p><p>如果结点已经实例化了，那么这里有两种方式，一是链表寻址，二是树寻址，然后就是找到就覆盖，找不到就追加。</p><blockquote><p>– 链表寻址 –</p></blockquote><p>寻址前会先判断结点类型，如果<code>instanceof</code>不是<code>TreeNode</code>，那么便是链表，然后就可以开始遍历链表了，如果找到了的话，<code>key-value</code>当然就是要覆盖了，但是这里对<code>key</code>的处理分为了两种：</p><p>分析之前，其实链表中所有结点都位于同一个数组下标，那么应该都必须满足每个结点的<code>hash</code>值相同，即槽位相同，所以会再一次判断<code>hash</code>值，才会用<code>key</code>去比较。</p><ul><li>如果<code>key</code>为基本数据类型，那么是直接通过运算符<code>==</code>来比较的，为<code>true</code>，那么就直接跳出链表遍历了，然后将刚刚在链表访问后命中的<code>Node</code>结点<code>value</code>覆盖</li><li>如果key为复杂类型，那么仅仅通过<code>==</code>运算符比较是错误的，这里就需要我们去主动重写<code>equals</code>方法，让<code>key</code>值怎么比较才能相等，因为默认<code>equals</code>是<code>Object</code>比较，这里涉及到内存指针了，所以创建的对象一般内存都不同，除非浅拷贝。</li><li>只是我们一般都用<code>String</code>类型，而<code>String</code>类型已经帮我们重写过了，虽然<code>String</code>类型也算是复杂类型，它重写了<code>Object</code>的<code>equals</code>方法，使得不同对象可以进行比较，比较的是对应字符串序列的内容是否相同。</li></ul><p>如果没找到，那么将实例化新的<code>Node</code>结点，然后追加到链表末尾，在<code>JDK7</code>中是追加到头结点，后来发现线程安全，即多线程情况下，有几率会导致链表出现死锁。</p><blockquote><p>– 树寻址 –</p></blockquote><p>首先了解下<code>TreeNode</code>，它继承了<code>LinkedHashMap.Entry</code>，而<code>Entry</code>又继承了<code>HashMap.Node</code>，归根结底还是跟链表的<code>Node</code>类似，只不过多了一些成员和方法。</p><p>树寻址是判断结点是否为树节点，一般在链表转变为红黑树之后才会进行，红黑树是一棵平衡的二叉有序树，即由哈希值排序的二叉树，从根节点依次比较哈希值并遍历到叶子节点依旧没有找到，通过哈希值比较的大小直接实例出一个新的树节点给原来叶子节点的左孩子节点或右孩子节点。</p><p>如果找到该节点，与链表一样的方法，则中断遍历并将该节点返回，在外部进行赋值操作。</p><blockquote><p>– 链表转移 –</p></blockquote><p>链表转移前会判断链表是否只有表头，即只有一个元素，那么通过寻找该结点<code>Node</code>的<code>Hash</code>值，与扩容后的<code>lenth</code>长度<code>&amp;</code>操作命中新的槽位</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">e : table即Node数组中的一个下标元素</span></span><br><span class="line"><span class="comment">newCap: 新table的容量</span></span><br><span class="line"><span class="comment">e.hash: 哈希值，常用于与table长度做&amp;操作来命中寻址</span></span><br><span class="line"><span class="comment">newTab: 新table</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">newTab[e.hash &amp; (newCap - <span class="number">1</span>)] = e;</span><br></pre></td></tr></table></figure><p>如果是链表结构，那么按照<code>hash</code>值对链表拆成高位和低位链表，这里的高低位不是字面意思，我是直译源码字面意思，从代码分析：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** </span></span><br><span class="line"><span class="comment">如果 hash 二进制数中 与 旧table的容量表示的二进制数 所在的 位为1 &amp; 为 真，则表示为高位链表</span></span><br><span class="line"><span class="comment">如果 hash 二进制数中 与 旧table的容量表示的二进制数 所在的 位为1 &amp; 为 假，则表示为低位链表</span></span><br><span class="line"><span class="comment">从随机性角度考虑，这两种散列均匀的话各占一半</span></span><br><span class="line"><span class="comment">hash: 4字节的 int 类型</span></span><br><span class="line"><span class="comment">oldCap: 4字节的 int 类型，默认初始化为 16，二进制表示为 10000</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">e.hash &amp; oldCap) == <span class="number">0</span></span><br></pre></td></tr></table></figure><p>由于新<code>table</code>是旧<code>table</code>的两倍，那么低位链表命中赋值给新<code>table</code>中与旧<code>table</code>相同的下标，而高位从字面上理解，就要比低位更靠右，所以它的下标是<code>原下标+oldCap</code>，<code>oldCap</code>大小也是新<code>table</code>大小的一半，看图</p><p><img src="https://yupeng-tuchuang.oss-cn-shenzhen.aliyuncs.com/image-20221116225644342.png" alt="HashMap扩容图"></p><blockquote><p>– 转化红黑树 –</p></blockquote><p>转化红黑树刚开始是不会触发的，在<code>put</code>操作的时候，在<code>table</code>中某个槽遍历链表时，如果链表长度达到扩展树阈值<code>8</code>，并且<code>table</code>长度要大于<code>64</code>。</p><p>链表满足长度<code>8</code>是树转移的必要条件，在<code>table</code>初始化后且发生转化红黑树前，会先去判断<code>table</code>长度是否达到<code>64</code>，没有则会扩容，扩容前面也详细说了，上面的图就很明确，达到<code>8</code>个节点的链表，扩容后链表拆成两部分，很明显没有必要再次去转化了。</p><p>如果长度满足，这时会先转变为双向链表，用于辅助树节点增加和删除以及维护树根节点。</p><p>转换红黑树是从遍历该链表开始，特殊处理第一个链表节点转成树节点，遍历这棵树，通过哈希值构造一颗排序树，每次构造新节点都是从叶子节点开始，不平衡就利用红黑标记来转为平衡树。</p><blockquote><p>– 树转移 –</p></blockquote><p>树转移发生在<code>resize</code>方法中，触发的事件是当前<code>put</code>成功后的所有元素个数大于<code>table</code>的阈值（容量 * 负载因子），接着会先扩容<code>table</code>的容量，树转移时，在<code>put</code>时<code>hash</code>命中的<code>table</code>中一个插槽发现是树节点，就开始转移，转移不是从树的逻辑转移，跟链表一样，只不过还有考虑前置节点，也就是跟树转移一样构造双向链表，拆成两条待转移成树的双向链表。</p><p>拆成的子双向链表，长度小于<code>6</code>就直接退化为链表，否则才将红黑树转移到新<code>table</code>的高低位上。</p><blockquote><p>– 特殊key –</p></blockquote><p>对于key为<code>null</code>值，<code>JDK7</code>与<code>JDK8</code>做法不同，但结果都是从<code>table</code>的<code>0</code>下标开始遍历。</p><p><code>JDK7</code>的做法是对<code>Null</code>做了单独的<code>put</code>处理，不与非空的<code>key</code>放一起，直接单独在一个方法里面遍历<code>table</code>的<code>0</code>下标。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> V <span class="title function_">put</span><span class="params">(K key, V value)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (key == <span class="literal">null</span>)</span><br><span class="line">        <span class="keyword">return</span> putForNullKey(value);</span><br><span class="line">    <span class="type">int</span> <span class="variable">hash</span> <span class="operator">=</span> hash(key);</span><br></pre></td></tr></table></figure><p>所以常常说<code>JDK7</code>版本可读性比<code>JDK8</code>版本要强，这是无须质疑的。</p><p><code>JDK8</code>的做法更绝了，是对<code>Null</code>做了单独的<code>hash</code>处理，因为前面的<code>JDK7</code>版本对<code>key</code>做单独处理，无外乎就是<code>null</code>没法调用本地<code>native</code>方法<code>hashCode</code>取的哈希值，所以干脆就在外面传入<code>hash</code>值，并且将为<code>null</code>的<code>key</code>哈希值处理为<code>0</code>。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">return</span> putVal(hash(key), key, value, <span class="literal">false</span>, <span class="literal">true</span>);</span><br></pre></td></tr></table></figure><blockquote><p>– 哈希定位 –</p></blockquote><p>在<code>HashMap</code>中其实哈希的影响还是蛮大的，所以版本迭代对哈希也做了一些改进。</p><p><code>JDK7</code>版本的做法其实也是将<code>32位</code>的<code>int</code>哈希类型在命中<code>table</code>中的下标是能够分布均衡，因为<code>table</code>的数组长度没有像哈希值一样每个位随机均匀，它高位字节一般为<code>0</code>，这就导致很多时候不同的哈希值高位虽然不同，但结果命中了同一个下标，<code>JDK</code>的做法通过无符号右移，让每<code>12</code>位无差别异或，即高低位能够很好的利用到了。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">h ^= k.hashCode();</span><br><span class="line"></span><br><span class="line"><span class="comment">// This function ensures that hashCodes that differ only by</span></span><br><span class="line"><span class="comment">// constant multiples at each bit position have a bounded</span></span><br><span class="line"><span class="comment">// number of collisions (approximately 8 at default load factor).</span></span><br><span class="line">h ^= (h &gt;&gt;&gt; <span class="number">20</span>) ^ (h &gt;&gt;&gt; <span class="number">12</span>);</span><br><span class="line"><span class="keyword">return</span> h ^ (h &gt;&gt;&gt; <span class="number">7</span>) ^ (h &gt;&gt;&gt; <span class="number">4</span>);</span><br></pre></td></tr></table></figure><p>但这种做法还不够极致，<code>JDK8</code>版本的做法，更加简单明了。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">return</span> (key == <span class="literal">null</span>) ? <span class="number">0</span> : (h = key.hashCode()) ^ (h &gt;&gt;&gt; <span class="number">16</span>);</span><br></pre></td></tr></table></figure><h3 id="4-你项目中是如何去实现幂等性的？"><a href="#4-你项目中是如何去实现幂等性的？" class="headerlink" title="4. 你项目中是如何去实现幂等性的？"></a>4. 你项目中是如何去实现幂等性的？</h3><p>先说下我对幂等性的理解，这样再去理解我项目对幂等性的处理。</p><blockquote><p>定义</p></blockquote><p>用户对于同一操作发起的一次请求或者多次请求的结果是一致的，不会因为多次点击而产生了副作用。</p><blockquote><p>实现</p></blockquote><p>幂等性在<code>RPC</code>上的实现，不仅要考虑结果一致性，还有考虑请求失败后负载到其他机器依旧保证幂等性，这就不能使用本地缓存来实现，而要用到分布式缓存，推荐的分布式缓存可以是<code>Redis</code>。</p><p>需要考虑到重试的时机来恰当设置缓存的失效时间，一般逻辑上只要保证请求次数达到阈值后重试请求成功即可失效，可从逻辑成功调用后去手动失效，或者是达到阈值后手动失效，不过这种做法增大了<code>Redis</code>的阻塞，因为<code>Redis</code>是单线程，没法做到并发，<code>Jedis</code>虽然可以并发，但会导致线程安全问题，而<code>Jedis</code>每次请求都需要建立连接， 所以可以考虑共享一个连接，不过请求速度将大大降低。</p><p>而考虑<code>Lettuce</code>则需要提前准备线程池，后面就可以直接从从线程池取连接，连接数更好管控，性能也相对高一些。</p><h3 id="5-RPC的协议讲一讲，怎么处理的？"><a href="#5-RPC的协议讲一讲，怎么处理的？" class="headerlink" title="5. RPC的协议讲一讲，怎么处理的？"></a>5. RPC的协议讲一讲，怎么处理的？</h3><p><code>RPC</code>的请求过程：</p><ul><li>服务端启动，并将服务注册到注册中心；</li><li>客户端启动通过代理调用所需的远程方法；</li><li>代理从注册中心拉取服务，并使用某种负载策略调用其中一个服务；</li><li>代理将客户端想要调用的方法等调用信息序列化，然后通过网络传输给服务端；</li><li>服务端接收到数据后，进行反序列化；</li><li>服务端得到反序列化的数据，包含需要调用的方法、参数数据，接着调用本地方法；</li><li>服务端调用完成将结果进行序列化，然后通过网络传输给代理；</li><li>代理拿到结果后，将结果反序列化，然后返回给客户端；</li><li>客户端接收到服务端远程方法调用的结果；</li></ul><h3 id="6-项目中是怎么序列化的？"><a href="#6-项目中是怎么序列化的？" class="headerlink" title="6. 项目中是怎么序列化的？"></a>6. 项目中是怎么序列化的？</h3><p>项目中使用到了两种序列化方式，一种是<code>Jackson</code>，另一种是<code>Kryo</code>序列化；</p><p>这两种序列化工具都提供对Java对象的序列化，即转成二进制，然后进行网络传输；</p><p>接收到网络传输的二进制，再反过来进行反序列化成对象；</p><p>不过项目序列化的是核心数据，对一些请求包头协议，是使用了自定义编解码的方式；</p><p>对特定的字节码进行编解码解析，从而可以避免出现粘包的问题；</p><p>拆完包之后，对包中的核心数据进行反序列化操作;</p><p>当然具体的操作还有考虑到包的其他安全问题，比如重发包和校验码失败包。</p><h3 id="7-说一说深拷贝和浅拷贝"><a href="#7-说一说深拷贝和浅拷贝" class="headerlink" title="7.说一说深拷贝和浅拷贝"></a>7.说一说深拷贝和浅拷贝</h3><p>浅拷贝主要拷贝了对象的指针，本身数据引用的是同一块内存；</p><p>深拷贝则拷贝了对象本身，引用的是不同的内存。</p><p>在<code>Java</code>中，深拷贝和浅拷贝有<code>Object</code>的<code>clone</code>拷贝方法（浅拷贝）；</p><p><code>Arrays.copy</code>（浅拷贝）和<code>System.arraycopy</code>（浅拷贝）</p><p>序列化则是深拷贝，比如Java内置的<code>ObjectOutputStream</code>的<code>writeObject</code>方法和<code>ObjectInputStream</code>的<code>readObject</code>方法；</p><p>更深入来讲，浅拷贝中，目标对象对内存的操作会影响到原对象，而深拷贝则受影响。</p><h3 id="8-TCP和UDP了解下？"><a href="#8-TCP和UDP了解下？" class="headerlink" title="8. TCP和UDP了解下？"></a>8. TCP和UDP了解下？</h3><p><code>TCP</code>与<code>UDP</code>都是传输层协议，<code>TCP</code>基于连接的，<code>UDP</code>基于非连接的；</p><p>从连接上看，TCP需要有三次握手才能完成连接，之后才能进行通信，是可靠的连接；</p><p>而<code>UDP</code>不需要连接，没有<code>TCP</code>严谨的三次握手、确认连接和四次挥手过程，是不可靠的连接；</p><p><code>TCP</code>可以保证服务的可靠，一般用于高可靠服务，<code>UDP</code>不能保证服务是否可靠。</p><blockquote><p>特点</p></blockquote><p><strong>TCP：</strong></p><ul><li>稳定可靠，适用于要求较高的场景，需要准确无误的传输给对方，比如文件传输、发送邮件和浏览网页等；</li></ul><p><strong>UDP：</strong></p><ul><li>速度快，性能损耗少、资源占用少，但是可能产生丢包，所以适用于对实时性要求较高，但是对少量丢包，并没有太大的要求的场景，比如域名查询、电话通信和视频直播等。</li></ul><h3 id="9-Http底层使用了什么："><a href="#9-Http底层使用了什么：" class="headerlink" title="9. Http底层使用了什么："></a>9. Http底层使用了什么：</h3><p>TCP协议，是基于连接的。</p><blockquote><p>http请求报文</p></blockquote><p>HTTP请求报文由请求行（<code>request line</code>）、请求头部（<code>header</code>）、空行和请求数据等4个部分组成。</p><p>请求行：（<code>get/post</code> 方法，<code>url</code>中的<code>path</code>路径，<code>http</code>版本）</p><p>请求头部（<code>header</code>）关键字/值对组成，每行一对，关键字和值用英文冒号</p><p>请求数据 (<code>body</code>)</p><blockquote><p>http响应报文</p></blockquote><p>HTTP响应报文由四个部分组成：</p><ul><li>状态码（<code>Status Code</code>）：描述了响应的状态。可以用来检查是否成功完成了请求。请求失败的情况下，状态码可用来找出失败的原因。如果<code>Servlet</code>没有返回状态码，默认会返回成功的状态码<code>HttpServletResponse SC OK</code>。</li><li>HTTP头部（<code>HTTP Header</code>）：它们包含了更多关于响应的信息。比如：头部可以指定任务响应过期的过期日期，或者是指定用来给用户安全的传输实体内容的编码格式。如何在<code>Servlet</code>中检索<code>HTTP</code>的头部看这里。</li><li>空行</li><li>主体（<code>Body</code>）：它包含了响应的内容。它可以包含HTML代码、图片等。主体是由传输在HTTP消息中紧跟在头部后面的数据字节组成的。</li></ul><blockquote><p>请求过程</p></blockquote><p><strong>客户端连接服务器：</strong></p><p>客户端一般为<code>Web</code>浏览器，通过<code>url</code>访问服务端，与服务端建立一个<code>TCP</code>的套接字。</p><p><strong>发送HTTP请求：</strong></p><p>通过<code>TCP</code>套接字，发送HTTP请求时，客户端向服务端发送了一个文本的请求报文，一个请求报文由请求行、请求头部、空行和请求数据四部分组成。</p><p><strong>返回HTTP响应：</strong></p><p>Web服务器解析请求，定位请求资源。服务器将资源副本写到TCP套接字，有客户端读取。一个响应由状态行、响应头部、空行和响应数据四部分组成。</p><p><strong>释放TCP连接：</strong></p><ul><li>当<code>connection</code>模式为<code>close</code>，主动关闭方为服务端，客户端被动关闭连接，释放<code>TCP</code>连接；</li><li>当<code>connection</code>模式为<code>keepalive</code>，则该连接会保持一段时间，在该时间可以继续接收其他请求，长连接复用，与2.0的多路复用目的都是为了重用连接，减少连接开销。</li></ul><h3 id="10-三次挥手说下？"><a href="#10-三次挥手说下？" class="headerlink" title="10. 三次挥手说下？"></a>10. 三次挥手说下？</h3><p>三次挥手与四次挥手很像，先说下四次挥手。</p><blockquote><p>四次挥手</p></blockquote><p>由主动关闭方<code>(Client)</code>发起中断连接请求，也就是<code>FIN</code>报文，被动关闭方<code>(Server)</code>接收到<code>FIN</code>报文后，从协议上来说是主动关闭方在等待收到<code>FIN + ACK</code>报文段，被动关闭方由于有数据要方法，第二次挥手只发了<code>ACK</code>确认号，接着主动关闭方进入<code>FIN_WAIT</code>状态，继续等待被动关闭方的FIN报文。</p><p>当被动关闭方数据发送完成后，将发送FIN报文给主动关闭方，主动关闭方收到FIN报文后，主动关闭方完成关闭，但被动关闭方并不知道FIN报文是否被接收成功，所以主动关闭方关闭前还需发送<code>ACK</code>给被动关闭方，表示已成功接收被动关闭方的<code>FIN</code>报文，这时被动关闭方就可以断开连接了。</p><p>如果没收到将再次发送<code>FIN</code>报文给主动关闭方，最后主动关闭方也不知道被动关闭方是否接收，需要等待<code>2MSL</code>时间，如果没收到重发包即成功了。</p><blockquote><p>三次挥手</p></blockquote><p>三次挥手不同于四次挥手，不同是第二次与第三次挥手合并了，如果服务端没有数据要发送，就可以将第二次与第三次挥手合并，而如果有数据要发，并且客户端允许延迟收到确认，那么服务端可以将结果封装到第二次与第三次合并的报文段中一起发送。</p><h3 id="11-进程和线程的关系？"><a href="#11-进程和线程的关系？" class="headerlink" title="11. 进程和线程的关系？"></a>11. 进程和线程的关系？</h3><blockquote><p>定义</p></blockquote><p>进程是资源分配的基本单位，线程是CPU调度和分派的基本单位。</p><blockquote><p>包含关系</p></blockquote><p>线程是进程的一部分，一个线程只能属于一个进行，一个进程可以有多个线程，但至少有一个线程。</p><blockquote><p>内存角度</p></blockquote><p>每个进程分配了不同的内存空间，每个进程都有独立的代码和数据空间，多个进程可以同时运行在内存中，而同一个进程的多个线程（简称同类线程）在其分配的指定内存中运行，需要CPU来调度这一块内存区域。</p><p>系统不会为线程分配资源，只能通过CPU来调度和分派资源，也就是说，进程分配内存资源，然后线程需要CPU来过渡使用这块内存。</p><p>进程切换开销大，线程切换开销小。</p><blockquote><p>共享角度</p></blockquote><p>同类线程之间可以共享堆空间和方法区和运行时常量池，每个线程都有自己独立的虚拟机栈和程序计数器。</p><blockquote><p>进行共享的方式</p></blockquote><p>线程共享环境：进程代码段、进程公有数据、进程打开的文件描述符、信号处理器、进程当前目录和进程用户ID与进程组ID</p><blockquote><p>进程通信的方式</p></blockquote><ul><li>匿名管道 (PIPE)</li></ul><p>半双工，即不能同时在两个方向上传输数据，有的系统可能支持全双工。</p><p>速度慢，容量有限，且只能在父子进程间。经典的形式就是管道由父进程创建，进程fork子进程之后，就可以在父子进程之间使用了。</p><ul><li>命名管道 (FIFO)</li></ul><p>不想关的进程也能够进行数据交换。</p><ul><li>消息队列</li></ul><p>消息队列，就是一个消息的链表，是一系列保存在内核中消息的列表。</p><ul><li>信号量</li></ul><p>信号量是互斥信号量，能够用来同步通信。</p><ul><li>共享内存</li></ul><p>通过共享内存机制来实现。</p><ul><li>套接字</li></ul><p>套接字通过Socket绑定套接字，服务端开启监听被动连接，客户端主动连接，向套接字读写数据实现通信。</p><ul><li>信号</li></ul><p>一种异步通信方式，可在用户态和内核态中交互，也可以在内核态中通知用户态事件的发生。</p><h3 id="12-了解过CAS嘛？"><a href="#12-了解过CAS嘛？" class="headerlink" title="12. 了解过CAS嘛？"></a>12. 了解过CAS嘛？</h3><p><code>CAS</code>是一种乐观锁的实现，<code>CAS</code>操作包含三个操作数——内存位置（<code>V</code>）、预期原值（A）期待值（<code>B</code>）。</p><p>如果内存位置的值与预期值向匹配，那么处理器会自动将该位置值更新为期待值，否则不做任何操作。</p><p>缺陷：无法解决<code>ABA</code>问题，在<code>Java</code>中解决该问题使用到了<code>AtomicStampReference</code>和<code>AtomicMarkableReference</code>来解决</p><p>数据库中MySQL的锁机制也是通过乐观锁来实现，更具体的实现则是<code>MVCC</code>多版本并发控制。</p><p>具体是两个数据结构，一个为<code>undo log</code>日志，一个为<code>readView</code>可读视图。</p><p>每张表中都会有隐藏的两列，一列是<code>undo log</code>指针，指向当前行数据操作指向的数据链表结构，另一列为事务版本号<code>id</code>，即每次事务开启后都会获取一个自增长的事务<code>id</code>，这样在<code>ReadView</code>可读视图中实现乐观操作，回到最先定义乐观锁三个操作数，实现方式差不多。</p><ul><li><code>undo log</code> 主要对所有并发修改结果保存了下来，并以链表的形式组织。</li><li><code>ReadView</code>主要处理事务<code>id</code>的可见性，包含当前活跃事务版本Id列表，活跃最小事务<code>id</code>（<code>up_limit_id</code>）即已提交最大事务<code>id + 1</code>，活跃最大事务<code>id</code>（<code>low_limit_id</code>）即最小不可见事务<code>id - 1</code>和<code>creator_trx_id</code>表示当前事务开启的版本号。</li><li>活跃事务即为同一时间并发下，所有发生了交叉的事务。</li></ul><h3 id="13-你的RPC项目怎么怎么划分模块的？"><a href="#13-你的RPC项目怎么怎么划分模块的？" class="headerlink" title="13. 你的RPC项目怎么怎么划分模块的？"></a>13. 你的RPC项目怎么怎么划分模块的？</h3><ul><li>服务发现（从注册中心获取服务及对应地址、使用负载均衡策略选择服务）</li><li>服务注册者（注册服务名和对应地址到注册中心、连接注册中心、清除服务）</li><li>服务提供者（存放服务实例、获取服务实例）</li><li>客户端代理（处理客户端请求、处理超时重试、负责分布式请求id生成）</li><li>服务端（启动服务时将服务所有类实例化并存放到服务提供者，将所有服务类对应服务名注册到服务注册者、处理客户端请求方法、处理超时包、处理重发包、处理异常包）</li><li>服务端处理器（从服务提供者获取服务实例、读取请求包代理执行服务对应方法）</li><li>异步请求池（客户端请求调用后立马返回future结果，由netty客户端正确读取结果后返回给异步请求池）</li><li>编码与解码器（对请求包和响应包的封包和拆包）</li><li>包检查器（对请求包和响应包的安全检验）</li><li>注册中心（暴露服务真实地址和端口号、提供服务注册与发现）</li><li>分布式缓存（缓存时间戳与序列号、重试请求号与请求结果、机器码）</li><li>服务宕机钩子（中心服务下线与本地服务清除、下线机器码、线程池关闭）</li></ul><h3 id="14-做一套算法题"><a href="#14-做一套算法题" class="headerlink" title="14. 做一套算法题"></a>14. 做一套算法题</h3><p>如有升序链表：list1 = [1,2,3]，list2 = [1,4,5]，则合并后的链表：list = [1,1,2,3,4,5]</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> List <span class="title function_">concat</span><span class="params">(List list1, List list2)</span> &#123;</span><br><span class="line"><span class="comment">// 头结点存储数据</span></span><br><span class="line"><span class="type">List</span> <span class="variable">p</span> <span class="operator">=</span> list1;</span><br><span class="line"><span class="type">List</span> <span class="variable">q</span> <span class="operator">=</span> list2;</span><br><span class="line"></span><br><span class="line"><span class="type">List</span> <span class="variable">newNode</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">List</span>();</span><br><span class="line"><span class="type">List</span> <span class="variable">head</span> <span class="operator">=</span> newNode;</span><br><span class="line"><span class="keyword">while</span>(p.next != <span class="literal">null</span> &amp;&amp; q.next != <span class="literal">null</span>) &#123;</span><br><span class="line"><span class="keyword">if</span> (p.data &lt; q.data) &#123;</span><br><span class="line">newNode.next = p;</span><br><span class="line">p = p.next;</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">newNode.next = q;</span><br><span class="line">q = q.next;</span><br><span class="line">&#125;</span><br><span class="line">newNode = newNode.next;</span><br><span class="line">&#125;</span><br><span class="line">    <span class="comment">// list2 仍存在节点</span></span><br><span class="line"><span class="keyword">if</span> (p.next == <span class="literal">null</span> &amp;&amp; q.next != <span class="literal">null</span>) &#123;</span><br><span class="line">newNode.next = q;</span><br><span class="line">&#125;</span><br><span class="line">    <span class="comment">// list1 仍存在节点</span></span><br><span class="line"><span class="keyword">if</span> (p.next != <span class="literal">null</span> &amp;&amp; q.next == <span class="literal">null</span>) &#123;</span><br><span class="line">newNode.next = p;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> head.next;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;自我介绍这里一笔带过，给对面介绍自己内在 + 外在 + 校园经历 + 校园项目  + 意向岗位&lt;/p&gt;
&lt;p&gt;腾讯子公司云智研发一面&lt;/p&gt;
&lt;h3 id=&quot;1-有了解过C-吗？接受转语言吗？&quot;&gt;&lt;a href=&quot;#1-有了解过C-吗？接受转语言吗？&quot; class=&quot;hea</summary>
      
    
    
    
    <category term="面经" scheme="http://fyupeng.github.io/categories/%E9%9D%A2%E7%BB%8F/"/>
    
    
    <category term="Java面经" scheme="http://fyupeng.github.io/tags/Java%E9%9D%A2%E7%BB%8F/"/>
    
  </entry>
  
  <entry>
    <title>SpringBoot整合rpc-netty-framework</title>
    <link href="http://fyupeng.github.io/2022/10/20/SpringBoot%E6%95%B4%E5%90%88rpc-netty-framework/"/>
    <id>http://fyupeng.github.io/2022/10/20/SpringBoot%E6%95%B4%E5%90%88rpc-netty-framework/</id>
    <published>2022-10-20T09:15:08.000Z</published>
    <updated>2022-10-20T09:29:20.128Z</updated>
    
    <content type="html"><![CDATA[<p>一个分布式微服务RPC框架 | <a href="README.CN.md">返回</a></p><h2 id="使用效果："><a href="#使用效果：" class="headerlink" title="使用效果："></a>使用效果：</h2><ol><li>用户访问客户端：GET <a href="http://localhost:8081/user/hello?name=&quot;%E5%BC%A0%E4%B8%89%E6%9D%A5%E8%AE%BF&quot;">http://localhost:8081/user/hello?name=&quot;张三来访&quot;</a></li></ol><p><img src="https://yupeng-tuchuang.oss-cn-shenzhen.aliyuncs.com/image-20221020170500139.png" alt="image-20221020170500139"></p><ol><li>浏览器访问客户端：</li></ol><p><img src="https://yupeng-tuchuang.oss-cn-shenzhen.aliyuncs.com/image-20221020170622580.png" alt="image-20221020170622580"></p><p>服务端接收情况：</p><p><img src="https://yupeng-tuchuang.oss-cn-shenzhen.aliyuncs.com/image-20221020170428236.png" alt="image-20221020170428236"></p><p>服务端负载注册服务：</p><p><img src="https://yupeng-tuchuang.oss-cn-shenzhen.aliyuncs.com/image-20221020170833644.png" alt="image-20221020170833644"></p><p>上面的实现就好比客户端只拿到服务端的api接口，加上配置中心地址即可调用远程服务！</p><h2 id="1-创建工程"><a href="#1-创建工程" class="headerlink" title="1. 创建工程"></a>1. 创建工程</h2><p>创建两个工程，一个作为服务端<code>SpringBoot</code>、一个作为客户端<code>SpringBoot</code>，同时作为后端接口服务</p><p>创建Maven工程的时候推荐使用父子工程依赖，而且要注意子模块之间的相互依赖关系，其中：</p><p>父模块（<code>root</code>项目）：负责管理<code>SpringBoot</code>版本、统一版本、<code>JDK</code>版本、日志依赖</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">profiles</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">profile</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">id</span>&gt;</span>jdk1.8<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">activation</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">activeByDefault</span>&gt;</span>true<span class="tag">&lt;/<span class="name">activeByDefault</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">jdk</span>&gt;</span>1.8<span class="tag">&lt;/<span class="name">jdk</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">activation</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">properties</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">maven-compiler-source</span>&gt;</span>1.8<span class="tag">&lt;/<span class="name">maven-compiler-source</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">maven-compiler-target</span>&gt;</span>1.8<span class="tag">&lt;/<span class="name">maven-compiler-target</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">maven-copiler-compilerVersion</span>&gt;</span>1.8<span class="tag">&lt;/<span class="name">maven-copiler-compilerVersion</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">properties</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">profile</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">profiles</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-parent<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">version</span>&gt;</span>2.1.2.RELEASE<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">relativePath</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- 与 logbakc 整合 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.projectlombok<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>lombok<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.18.10<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- 日志框架 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>ch.qos.logback<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>logback-classic<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.2.3<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure><p>主模块（启动类所在模块）应配置<code>maven</code>打包插件，<code>SpringBootStarterWeb</code>，<code>rpc-core</code>和依赖<code>Service</code>/<code>Controller</code>模块，客户端只有<code>Service</code>模块，服务端只有<code>Controller</code>模块</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>cn.fyupeng<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>springboot-rpc-service<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.0-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>cn.fyupeng<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>rpc-core<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.0.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-web<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">build</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">plugins</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-maven-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">plugins</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">build</span>&gt;</span></span><br></pre></td></tr></table></figure><p><code>Service</code>/<code>Controller</code>模块：依赖<code>api</code>模块和<code>common</code>模块，客户端请求服务端调用服务，当然没有<code>Service</code>模块，让主模块依赖<code>Controller</code>模块，<code>Controller</code>模块还要与主模块一样依赖<code>SpringBootStarterWeb</code>和<code>rpc-core</code></p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>cn.fyupeng<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>springboot-rpc-common<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.0-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>cn.fyupeng<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>springboot-rpc-api<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.0-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure><p><code>Common</code>模块，依赖<code>rpc-common</code>模块</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>cn.fyupeng<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>rpc-common<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.0.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br></pre></td></tr></table></figure><p>项目在准备测试服务端的自动发现服务功能时，要保证<code>cn.fyupeng.@Service</code>注解类能够被扫描，使用<code>cn.fyupeng.util.ReflectUtil</code>类即可。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Test</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        Set&lt;Class&lt;?&gt;&gt; classes = ReflectUtil.getClasses(<span class="string">&quot;cn.fyupeng&quot;</span>);</span><br><span class="line">        <span class="keyword">for</span> (Class&lt;?&gt; aClass : classes) &#123;</span><br><span class="line">            System.out.println(aClass);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="2-客户端"><a href="#2-客户端" class="headerlink" title="2. 客户端"></a>2. 客户端</h2><h3 id="2-1-编写启动器"><a href="#2-1-编写启动器" class="headerlink" title="2.1 编写启动器"></a>2.1 编写启动器</h3><p>新建<code>cn.fyupeng</code>包，包下新建启动器类</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@ComponentScan(basePackages = &#123;&quot;cn.fyupeng&quot;,&quot;org.utils&quot;&#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SpringBootClientStarter</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        SpringApplication.run(SpringBootClientStarter.class, args);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="2-2-编写配置文件"><a href="#2-2-编写配置文件" class="headerlink" title="2.2 编写配置文件"></a>2.2 编写配置文件</h3><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 单机模式</span></span><br><span class="line"><span class="attr">cn.fyupeng.nacos.register-addr</span>=<span class="string">192.168.10.1:8848</span></span><br><span class="line"><span class="comment"># 集群模式</span></span><br><span class="line"><span class="attr">cn.fyupeng.nacos.cluster.use</span>=<span class="string">true</span></span><br><span class="line"><span class="attr">cn.fyupeng.nacos.cluster.load-balancer</span>=<span class="string">round</span></span><br><span class="line"><span class="attr">cn.fyupeng.nacos.cluster.nodes</span>=<span class="string">192.168.10.1:8847|192.168.10.1:8848;192.168.10.1:8849</span></span><br></pre></td></tr></table></figure><h3 id="2-3-编写自定义配置类"><a href="#2-3-编写自定义配置类" class="headerlink" title="2.3 编写自定义配置类"></a>2.3 编写自定义配置类</h3><h3 id="2-4-编写api"><a href="#2-4-编写api" class="headerlink" title="2.4 编写api"></a>2.4 编写api</h3><p>注意与客户端包名完全相同</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> cn.fyupeng.service;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Auther</span>: fyp</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022/10/19</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: HelloWorld接口</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Package</span>: cn.fyupeng.cn.fyupeng.controller</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Version</span>: 1.0</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">HelloService</span> &#123;</span><br><span class="line">    String <span class="title function_">sayHello</span><span class="params">(String name)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="2-5-编写控制器"><a href="#2-5-编写控制器" class="headerlink" title="2.5 编写控制器"></a>2.5 编写控制器</h3><p><code>@PostConstruct</code>注解不要与<code>@Autowire</code>公用，因为<code>@Autowire</code>是本地依赖的，而我<code>@PostConstruct</code>会在该变量使用前调用，不过需要自行去实现，我的实现是远程依赖。</p><p>而<code>@Reference</code>没有依赖注入的功能，只有在超时重试才需要标记上！</p><p><code>@PostContruct</code>与<code>@Autowire</code>在<code>51cto</code>博客中有所讲解，请自行到我的<code>github</code>主页<code>get</code></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> cn.fyupeng.controller;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> cn.fyupeng.anotion.Reference;</span><br><span class="line"><span class="keyword">import</span> cn.fyupeng.loadbalancer.RandomLoadBalancer;</span><br><span class="line"><span class="keyword">import</span> cn.fyupeng.net.netty.client.NettyClient;</span><br><span class="line"><span class="keyword">import</span> cn.fyupeng.proxy.RpcClientProxy;</span><br><span class="line"><span class="keyword">import</span> cn.fyupeng.serializer.CommonSerializer;</span><br><span class="line"><span class="keyword">import</span> cn.fyupeng.service.HelloService;</span><br><span class="line"><span class="keyword">import</span> org.springframework.beans.factory.annotation.Autowired;</span><br><span class="line"><span class="keyword">import</span> org.springframework.web.bind.annotation.GetMapping;</span><br><span class="line"><span class="keyword">import</span> org.springframework.web.bind.annotation.RequestMapping;</span><br><span class="line"><span class="keyword">import</span> org.springframework.web.bind.annotation.RestController;</span><br><span class="line"><span class="keyword">import</span> org.utils.JSONResult;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> javax.annotation.PostConstruct;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Auther</span>: fyp</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022/10/19</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: HelloWorld控制器</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Package</span>: cn.fyupeng.cn.fyupeng.controller</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Version</span>: 1.0</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@RequestMapping(&quot;/user&quot;)</span></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HelloController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//@Autowired</span></span><br><span class="line">    <span class="comment">//private HelloService helloService;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">RandomLoadBalancer</span> <span class="variable">randomLoadBalancer</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">RandomLoadBalancer</span>();</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">NettyClient</span> <span class="variable">nettyClient</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NettyClient</span>(randomLoadBalancer, CommonSerializer.KRYO_SERIALIZER);</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">RpcClientProxy</span> <span class="variable">rpcClientProxy</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">RpcClientProxy</span>(nettyClient);</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Reference(retries = 5, timeout = 600, asyncTime = 3000)</span></span><br><span class="line">    <span class="comment">//@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> HelloService helloService;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@PostConstruct</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">init</span><span class="params">()</span> &#123;</span><br><span class="line">        helloService = rpcClientProxy.getProxy(HelloService.class, HelloController.class);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@GetMapping(&quot;/hello&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> JSONResult <span class="title function_">sayHello</span><span class="params">(String name)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> JSONResult.ok(helloService.sayHello(name));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="3-服务端"><a href="#3-服务端" class="headerlink" title="3. 服务端"></a>3. 服务端</h2><h3 id="3-1-编写配置类"><a href="#3-1-编写配置类" class="headerlink" title="3.1 编写配置类"></a>3.1 编写配置类</h3><p>新建<code>cn.fyupeng.config</code>包，在包下新建资源配置类，用于注入绑定端口</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@ConfigurationProperties(prefix=&quot;cn.fyupeng.config&quot;)</span></span><br><span class="line"><span class="comment">//不使用默认配置文件application.properties和application.yml</span></span><br><span class="line"><span class="meta">@PropertySource(&quot;classpath:resource.properties&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ResourceConfig</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> serverPort;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getServerPort</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> serverPort;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setServerPort</span><span class="params">(<span class="type">int</span> serverPort)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.serverPort = serverPort;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="3-2-编写启动器"><a href="#3-2-编写启动器" class="headerlink" title="3.2 编写启动器"></a>3.2 编写启动器</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@ServiceScan</span></span><br><span class="line"><span class="meta">@SpringBootApplication</span></span><br><span class="line"><span class="meta">@ComponentScan(basePackages = &#123;&quot;cn.fyupeng&quot;, &quot;org.utils&quot;&#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RpcServerStarter</span> <span class="keyword">implements</span> <span class="title class_">CommandLineRunner</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    <span class="keyword">private</span> ResourceConfig resourceConfig;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@PostConstruct</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">init</span><span class="params">()</span> &#123;</span><br><span class="line">        Map&lt;String, String&gt; resourceLoaders = ResourceLoadUtils.load(<span class="string">&quot;resource.properties&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span> (resourceLoaders != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">serverPort</span> <span class="operator">=</span> resourceLoaders.get(<span class="string">&quot;cn.fyupeng.config.serverPort&quot;</span>);</span><br><span class="line">            resourceConfig.setServerPort(Integer.parseInt(serverPort));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        SpringApplication.run(RpcServerStarter.class,args);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">(String... args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="comment">//这里也可以添加一些业务处理方法，比如一些初始化参数等</span></span><br><span class="line">        <span class="keyword">while</span>(<span class="literal">true</span>)&#123;</span><br><span class="line">            <span class="type">NettyServer</span> <span class="variable">nettyServer</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                nettyServer = <span class="keyword">new</span> <span class="title class_">NettyServer</span>(<span class="string">&quot;192.168.2.185&quot;</span>, resourceConfig.getServerPort(), SerializerCode.KRYO.getCode());</span><br><span class="line">            &#125; <span class="keyword">catch</span> (RpcException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">            log.info(<span class="string">&quot;Service bind in port with &quot;</span>+ resourceConfig.getServerPort() +<span class="string">&quot; and start successfully!&quot;</span>);</span><br><span class="line">            nettyServer.start();</span><br><span class="line">            log.error(<span class="string">&quot;RegisterAndLoginService is died，Service is restarting....&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="3-3-编写配置文件"><a href="#3-3-编写配置文件" class="headerlink" title="3.3 编写配置文件"></a>3.3 编写配置文件</h3><p>注意<code>config/resource.properties</code>与资源目录下的<code>resource.properties</code>不能同时公用，前者优先级高于后者</p><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 用于启动 jar 包端口</span></span><br><span class="line"><span class="attr">cn.fyupeng.config.serverPort</span>=<span class="string">8082</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment"># 用于配置中心单机</span></span><br><span class="line"><span class="attr">cn.fyupeng.nacos.register-addr</span>=<span class="string">192.168.2.185:8848</span></span><br><span class="line"><span class="comment"># 用于配置中心集群</span></span><br><span class="line"><span class="attr">cn.fyupeng.nacos.cluster.use</span>=<span class="string">true</span></span><br><span class="line"><span class="attr">cn.fyupeng.nacos.cluster.load-balancer</span>=<span class="string">round</span></span><br><span class="line"><span class="attr">cn.fyupeng.nacos.cluster.nodes</span>=<span class="string">192.168.2.185:8847|192.168.2.185:8848;192.168.2.185:8849</span></span><br></pre></td></tr></table></figure><h3 id="3-4-编写api"><a href="#3-4-编写api" class="headerlink" title="3.4 编写api"></a>3.4 编写api</h3><p>注意与客户端包名完全相同</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> cn.fyupeng.service;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Auther</span>: fyp</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022/10/19</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: HelloWorld接口</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Package</span>: cn.fyupeng.cn.fyupeng.controller</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Version</span>: 1.0</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">HelloService</span> &#123;</span><br><span class="line">    String <span class="title function_">sayHello</span><span class="params">(String name)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="3-5-编写业务"><a href="#3-5-编写业务" class="headerlink" title="3.5 编写业务"></a>3.5 编写业务</h3><p>注意<code>Service</code>注解为<code>cn.fyupeng.service.HelloService</code></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> cn.fyupeng.service.impl;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> cn.fyupeng.anotion.Service;</span><br><span class="line"><span class="keyword">import</span> cn.fyupeng.service.HelloService;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Auther</span>: fyp</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Date</span>: 2022/10/19</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Description</span>: HelloWorld实现类</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Package</span>: cn.fyupeng.cn.fyupeng.controller.impl</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@Version</span>: 1.0</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HelloServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">HelloService</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">sayHello</span><span class="params">(String name)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;hello, my name is &quot;</span> + name;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>为了使用配置文件注入来启动服务对应的端口</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;一个分布式微服务RPC框架 | &lt;a href=&quot;README.CN.md&quot;&gt;返回&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;使用效果：&quot;&gt;&lt;a href=&quot;#使用效果：&quot; class=&quot;headerlink&quot; title=&quot;使用效果：&quot;&gt;&lt;/a&gt;使用效果：&lt;/h2&gt;&lt;ol&gt;
&lt;li</summary>
      
    
    
    
    <category term="Java笔记" scheme="http://fyupeng.github.io/categories/Java%E7%AC%94%E8%AE%B0/"/>
    
    
    <category term="RPC" scheme="http://fyupeng.github.io/tags/RPC/"/>
    
  </entry>
  
  <entry>
    <title>小迈科技一面</title>
    <link href="http://fyupeng.github.io/2022/09/29/%E5%B0%8F%E8%BF%88%E7%A7%91%E6%8A%80%E4%B8%80%E9%9D%A2/"/>
    <id>http://fyupeng.github.io/2022/09/29/%E5%B0%8F%E8%BF%88%E7%A7%91%E6%8A%80%E4%B8%80%E9%9D%A2/</id>
    <published>2022-09-29T08:00:20.000Z</published>
    <updated>2022-09-29T08:02:24.370Z</updated>
    
    <content type="html"><![CDATA[<h2 id="简单三分钟自我介绍"><a href="#简单三分钟自我介绍" class="headerlink" title="简单三分钟自我介绍"></a>简单三分钟自我介绍</h2><p>自我介绍这里一笔带过，给对面介绍自己内在 + 外在 + 校园经历 + 校园项目  + 意向岗位</p><h2 id="一面"><a href="#一面" class="headerlink" title="一面"></a>一面</h2><h3 id="1-Java基础"><a href="#1-Java基础" class="headerlink" title="1. Java基础"></a>1. Java基础</h3><h3 id="1-1-序列化和反序列化"><a href="#1-1-序列化和反序列化" class="headerlink" title="1.1 序列化和反序列化"></a>1.1 序列化和反序列化</h3><p>首先了解一下序列化和反序列化的概念</p><p>序列化：将Java对象以二进制即字节码的形式保存在磁盘文件中，可以说是保存Java对象状态的过程，序列化可以实现对象保存的持久化；</p><p>反序列化：将保存在磁盘文件中的Java字节码重新转换为Java对象的过程；</p><p>其他特点：一般<code>RPC</code>框架底层协议通信就是通过序列化和反序列化在网络上传输Java对象。</p><p>序列化和反序列化的实现主要有两种，准确来说有三种方法：</p><p>采用默认的序列化方式，即通过<code>ObjectOutPutStream</code>类的<code>writeObject(OutputStream out)</code>方法来序列化到输出流中</p><p>输出流可以选择文件流、也可以选择管道流，甚至是二进制流</p><p>文件流是直接写到文件中再读取转成对象，管道流是通过管道缓存数据，然后再通过输入流连接管道读取转成对象</p><p>序列化的对象必须实现<code>Seriablized</code>接口，才能完成序列化和反序列化操作</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Student</span> <span class="keyword">implements</span> <span class="title class_">Serializable</span> &#123;</span><br><span class="line">   <span class="keyword">private</span> String name;</span><br><span class="line">   <span class="keyword">private</span> Integer age;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>第一种实现（采用默认）</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//FileOutputStream fos = new FileOutputStream(&quot;D:\\student.txt&quot;);</span></span><br><span class="line">      <span class="type">PipedOutputStream</span> <span class="variable">pos</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">PipedOutputStream</span>();</span><br><span class="line">      <span class="type">PipedInputStream</span> <span class="variable">pis</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">PipedInputStream</span>();</span><br><span class="line">      pis.connect(pos);</span><br><span class="line">      <span class="type">ObjectOutputStream</span> <span class="variable">oos</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ObjectOutputStream</span>(pos);</span><br><span class="line"></span><br><span class="line">      <span class="type">Student</span> <span class="variable">student</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Student</span>();</span><br><span class="line">      student.setAge(<span class="number">21</span>);</span><br><span class="line">      student.setName(<span class="string">&quot;zs&quot;</span>);</span><br><span class="line">      oos.writeObject(student);</span><br><span class="line"></span><br><span class="line">      <span class="comment">//FileInputStream fis = new FileInputStream(&quot;D:\\student.txt&quot;);</span></span><br><span class="line">      <span class="type">ObjectInputStream</span> <span class="variable">ois</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ObjectInputStream</span>(pis);</span><br><span class="line">      <span class="type">Student</span> <span class="variable">res</span> <span class="operator">=</span> (Student) ois.readObject();</span><br><span class="line">      System.out.println(res);</span><br></pre></td></tr></table></figure><ul><li>第二种实现（自定义）</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 自定义协议 比如我可以将加入对象头 cafe babe</span></span><br><span class="line">   <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">readObject</span><span class="params">(ObjectInputStream in)</span> <span class="keyword">throws</span> IOException, ClassNotFoundException &#123;</span><br><span class="line">      System.out.println(<span class="string">&quot;自定义反序列化&quot;</span>);</span><br><span class="line">      <span class="type">int</span> <span class="variable">magic</span> <span class="operator">=</span> in.readInt();</span><br><span class="line">      <span class="built_in">this</span>.name = (String) in.readObject();</span><br><span class="line">      <span class="built_in">this</span>.age = (Integer) in.readObject();</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="comment">// 自定义协议 比如我可以将加入对象头 cafe babe</span></span><br><span class="line">   <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">writeObject</span><span class="params">(ObjectOutputStream out)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">      System.out.println(<span class="string">&quot;自定义序列化&quot;</span>);</span><br><span class="line">      <span class="comment">// 4 字节 魔数</span></span><br><span class="line">      out.writeInt(<span class="number">0xCAFEBABE</span>);</span><br><span class="line">      out.writeObject(<span class="built_in">this</span>.name);</span><br><span class="line">      out.writeObject(<span class="built_in">this</span>.age);</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><ul><li>第三种（自定义）</li></ul><p>实现接口<code>Externalizable</code>并重写方法，其实跟第二种差别不大，只是第二种有默认的私有方法</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">writeExternal</span><span class="params">(ObjectOutput out)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">      System.out.println(<span class="string">&quot;实现 Externalizable 接口的自定义序列化&quot;</span>);</span><br><span class="line">      out.writeObject(<span class="built_in">this</span>.name);</span><br><span class="line">      out.writeObject(<span class="built_in">this</span>.age);</span><br><span class="line">   &#125;</span><br><span class="line"></span><br><span class="line">   <span class="meta">@Override</span></span><br><span class="line">   <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">readExternal</span><span class="params">(ObjectInput in)</span> <span class="keyword">throws</span> IOException, ClassNotFoundException &#123;</span><br><span class="line">      System.out.println(<span class="string">&quot;实现 Externalizable 接口的自定义反序列化&quot;</span>);</span><br><span class="line">      <span class="built_in">this</span>.name = (String) in.readObject();</span><br><span class="line">      <span class="built_in">this</span>.age = (Integer) in.readObject();</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><p>解决反序列化破坏单例</p><ul><li>在单例模式中，序列化可以破坏单例，这是需要重写<code>readResolve()</code> 方法，将之前的单例实例对象返回即可保证单例。</li></ul><p>解决反射破坏单例</p><p>反射创建对象根本也是要调用构造方法，而且可以无视构造方法的访问修饰符（<code>public</code>、<code>private</code>）</p><ul><li>Java规定反射不能破坏枚举类型，采用枚举构造单例</li><li>构造方法执行前，单例实例逻辑上不应该创建而且只执行一次，可判断是否已创建来避免反射创建多个实例，前提单例已先于反射</li></ul><h3 id="1-2-说一说数据类型有哪些"><a href="#1-2-说一说数据类型有哪些" class="headerlink" title="1.2 说一说数据类型有哪些"></a>1.2 说一说数据类型有哪些</h3><p>Java有八种基本数据类型 + String</p><p>1个位的<code>bit</code>，布尔类型的<code>boolean</code>，2个字节的<code>short</code>，4个字节的<code>int</code>、<code>char</code>，8个字节的<code>long</code>、<code>double</code>、<code>float</code></p><p>其中基本类型中的包装类型常用的有Integer、Long</p><p>其中<code>Integer</code>会自动进行拆封装处理，也就是可以直接跟<code>int</code>类型比较数值上的大小</p><p>底层在<code>-128-127</code>之间会做缓存，在这之间通过<code>Integer.valueOf(int)</code>创建的对象都是同一个,使用了享元设计模式</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">System.out.println(<span class="keyword">new</span> <span class="title class_">Integer</span>(<span class="number">1</span>) == <span class="number">1</span>);</span><br><span class="line">System.out.println(Integer.valueOf(<span class="number">1</span>) == Integer.valueOf(<span class="number">1</span>));</span><br><span class="line">System.out.println(Integer.valueOf(<span class="number">128</span>) == Integer.valueOf(<span class="number">128</span>));</span><br></pre></td></tr></table></figure><p>结果</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="literal">true</span></span><br><span class="line"><span class="literal">true</span></span><br><span class="line"><span class="literal">false</span></span><br></pre></td></tr></table></figure><h3 id="1-3-数据结构"><a href="#1-3-数据结构" class="headerlink" title="1.3 数据结构"></a>1.3 数据结构</h3><p>给你你个浏览器，要求设计前进和后退的数据结构，优先考虑性能</p><p>那么由于浏览器前进后退访问，是一种<code>FIFO</code>的结构，比如你连续点击前进几个页面，最先进的页面最后返回，即<code>LIFO</code></p><p>那么可以考虑栈的结构设计，栈结构设计要考虑性能，首先我们可以分析到，浏览器页面跳转没有涉及到页面的修改，即用于查询</p><p>那么优先考虑数组而不考虑链表</p><p>我们可以设计两个栈数组，一个用于入栈已前进的页面，一个用于入栈已后退的页面，取其中一个栈顶元素作为当前页面</p><h3 id="2-MySQL"><a href="#2-MySQL" class="headerlink" title="2. MySQL"></a>2. MySQL</h3><h4 id="2-1-索引"><a href="#2-1-索引" class="headerlink" title="2.1 索引"></a>2.1 索引</h4><p>在MySQL中，直接影响索引类型的是数据库的存储引擎</p><p>使用MyIsam存储引擎，数据文件和索引是分开的，索引会另外存储在另一个文件中</p><p>使用InnoDB存储引擎，数据和索引都存储在同一个文件中，而且是以B+数的数据结构存储，非叶子节点存储索引，叶子节点存储索引和索引对应的数据</p><p>就MyIsam引擎来说，索引中最主要的是聚簇索引，也就是主键的默认索引</p><p>单值索引和多值索引，这里是指组合索引，好的组合索引可以达到覆盖索引，可以做到避免回表，这里是因为叶子节点通过覆盖索引带了数据，因为B+树只有叶子节点带有数据，非叶子节点都是索引</p><p>而多个组合索引下，有效索引要做到左匹配，也就是必须从左顺序匹配到右查询，否则索引将失效</p><h4 id="2-2-口述-sql"><a href="#2-2-口述-sql" class="headerlink" title="2.2 口述 sql"></a>2.2 口述 sql</h4><p>给你一张表，有三个字段，id、产品id、备注信息，现在需要你查询相同产品id的记录，然后id值相同的记录数大于等于5的产品id</p><p>首先呢，我们可以先定义这张表为S</p><p>因为涉及相同字段的记录，可以考虑直接分组，使用聚合函数</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> pro_id <span class="keyword">from</span> S</span><br><span class="line"><span class="keyword">group</span> <span class="keyword">by</span> pro_id</span><br><span class="line"><span class="keyword">having</span> <span class="built_in">count</span>(pro_id) <span class="operator">&gt;=</span> <span class="number">5</span></span><br></pre></td></tr></table></figure><h3 id="3-JVM"><a href="#3-JVM" class="headerlink" title="3. JVM"></a>3. JVM</h3><h4 id="3-1-线程的死锁了解过吗？"><a href="#3-1-线程的死锁了解过吗？" class="headerlink" title="3.1 线程的死锁了解过吗？"></a>3.1 线程的死锁了解过吗？</h4><p>比如有两个线程t1和t2，t1线程有资源r1，t2有资源r2，t1线程执行代码中需要资源r2，不过这段代码需要t2线程把资源r2释放才能执行，此时t2线程也执行代码中，释放资源前需要获取资源r1，但t2又需要r2，此时处于相互等待的状态，就导致了死锁。</p><p>从Java角度来说，资源相当于Java的锁对象，是互斥的，一个线程获取锁对象后，另一个线程只能等待，t1线程获取锁对象r1，也就是占有了锁r1，这时线程t2也是占有了锁对象r2，而线程t1需要获取锁对象r2才能往下执行代码，使得线程t1阻塞，而线程t2处于阻塞等待锁对象r1释放，才能释放锁对象r2，这样就导致死锁。（互相等待）</p><h3 id="4-JUC"><a href="#4-JUC" class="headerlink" title="4. JUC"></a>4. JUC</h3><h4 id="4-1-线程池有了解过吗？"><a href="#4-1-线程池有了解过吗？" class="headerlink" title="4.1 线程池有了解过吗？"></a>4.1 线程池有了解过吗？</h4><p>线程池主要有核心线程数、救急线程和队列，队列分为阻塞队列和非阻塞队列。</p><p>目前线程池主要可以分为几类：</p><p>只有核心线程、无救急线程的线程池，此时等待队列中有要执行的任务，而核心线程在轮询地执行等待队列中的任务，如果队列满或者队列是无界队列，可能导致内存溢出问题。</p><p>一般这种线程池的做法就是使用了拒绝策略，拒绝策略可分为直接丢弃新任务、异常抛出（主动逻辑处理）、丢弃等待队列头结点、提交任务线程执行。</p><p>最后一个是推荐使用的</p><ul><li>不会造成数据丢失，也就不会出现业务损失；</li><li>提交任务的线程被占用，新的任务不可提交，减缓任务提交的速度，相当于负反馈，能给到线程池一定的缓冲期；</li></ul><p>只有救急线程的线程池，这种的话有线程池<code>newCachedThreadPool</code>，它能无限创建救急线程，队列采用<code>SynchronousQueue</code> ，是一个没有容量的队列，只有线程取任务时才能提交任务；</p><p>最后是一种核心线程数只有1的线程池，没有救急线程，任务队列无界，一般作为单线程任务，这样就不会有CPU的轮询切换，任务的执行效率最高，不过请求数太多的情况下，也是容易导致内存溢出。</p><p>非阻塞队列实现的有<code>CurrentLinkedQueue</code>，它是通过<code>CAS</code>无锁化机制的线程池队列，每个线程通过<code>for(;;)</code>执行，是一个单向且通过<code>GC</code>自动回收出队节点的，利用可达性算法分析，将<code>next</code>指向自己，即可触发延迟回收，利用元素的不可重用性，规避<code>ABA</code>问题</p><h3 id="5-Linux"><a href="#5-Linux" class="headerlink" title="5. Linux"></a>5. Linux</h3><p>给你一个日志文件，要求你查询最近一天内中，匹配到关键字的所有记录数据</p><p>一般日志实时查询可以使用该命令，它会实时更新，而且会处于fg模式，tail打印文件末尾记录，也就是最近</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tail -100f catalina.log |<span class="params"> grep &quot;关键字&quot; </span></span><br></pre></td></tr></table></figure><p>而使用cat是查询历史，只能打印日志到屏幕</p><p>比如按照<code>tail</code>或<code>head</code>来查询关键字为<code>20:</code>的后5条数据并显示行号</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cat -n log.log4j |<span class="params"> grep &quot;20:&quot; </span>| tail -n <span class="number">5</span></span><br></pre></td></tr></table></figure><p>时间段查询</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">grep <span class="string">&#x27;2022-08-21 20:1[1-9]&#x27;</span> log.log4j</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;简单三分钟自我介绍&quot;&gt;&lt;a href=&quot;#简单三分钟自我介绍&quot; class=&quot;headerlink&quot; title=&quot;简单三分钟自我介绍&quot;&gt;&lt;/a&gt;简单三分钟自我介绍&lt;/h2&gt;&lt;p&gt;自我介绍这里一笔带过，给对面介绍自己内在 + 外在 + 校园经历 + 校园项目  +</summary>
      
    
    
    
    <category term="面经" scheme="http://fyupeng.github.io/categories/%E9%9D%A2%E7%BB%8F/"/>
    
    
    <category term="Java面经" scheme="http://fyupeng.github.io/tags/Java%E9%9D%A2%E7%BB%8F/"/>
    
  </entry>
  
  <entry>
    <title>右岸物联面经</title>
    <link href="http://fyupeng.github.io/2022/09/25/%E5%8F%B3%E5%B2%B8%E7%89%A9%E8%81%94%E9%9D%A2%E7%BB%8F/"/>
    <id>http://fyupeng.github.io/2022/09/25/%E5%8F%B3%E5%B2%B8%E7%89%A9%E8%81%94%E9%9D%A2%E7%BB%8F/</id>
    <published>2022-09-25T10:28:50.000Z</published>
    <updated>2022-09-25T10:30:38.942Z</updated>
    
    <content type="html"><![CDATA[<h2 id="简单三分钟自我介绍"><a href="#简单三分钟自我介绍" class="headerlink" title="简单三分钟自我介绍"></a>简单三分钟自我介绍</h2><p>自我介绍这里一笔带过，给对面介绍自己内在 + 外在 + 校园经历 + 校园项目  + 意向岗位</p><h2 id="技术面"><a href="#技术面" class="headerlink" title="技术面"></a>技术面</h2><h3 id="1-Java基础篇"><a href="#1-Java基础篇" class="headerlink" title="1. Java基础篇"></a>1. Java基础篇</h3><h4 id="1-1-Object你知道哪些方法"><a href="#1-1-Object你知道哪些方法" class="headerlink" title="1.1 Object你知道哪些方法?"></a>1.1 <code>Object</code>你知道哪些方法?</h4><p>我就回答了自己常用的 <code>wait</code>、<code>notify</code>、<code>notifyAll</code> 这三个方法，其他自己不怎么使用，就跟技术官说抱歉，其他不怎么使用，就回答不上来，面试官也不停留在这个问题上，直接带到下一个话题。</p><p>所以这里总结下，<code>Object</code>其实还有一个 <code>clone()</code>方法，这个回答不上了其实真不应该，当前想不起来，还有一个获取类信息<code>getClass()</code>，可以做映射的东西。</p><h4 id="1-2-你了解过哪些list-类型吗"><a href="#1-2-你了解过哪些list-类型吗" class="headerlink" title="1.2 你了解过哪些list 类型吗"></a>1.2 你了解过哪些list 类型吗</h4><p>嗯，有两种，一种是 数组，一种是链表，即<code>ArrayList</code>和<code>LinkedList</code></p><h4 id="1-3-这两种怎么选择使用？"><a href="#1-3-这两种怎么选择使用？" class="headerlink" title="1.3 这两种怎么选择使用？"></a>1.3 这两种怎么选择使用？</h4><p>一般的话，如果读操作大于增删改的话，使用数组效率要比链表的好，而如果要频繁修改操作，那么使用链表更好一点</p><p>所谓层层递进，就在这里了</p><h4 id="1-4-那为什么这么说？"><a href="#1-4-那为什么这么说？" class="headerlink" title="1.4 那为什么这么说？"></a>1.4 那为什么这么说？</h4><p>呃，因为数组如果你要增加一个元素的话，比如举例，你有十个元素，那么你要在0号下标增加一个元素，那么你得把后面10个元素整体向后挪到一位，这里我还用手势比划了一下，面试官点头，然后示意我继续，我就说链表的话，只需要修改一下指针就可以了，不用对整体移动，其实这里应该回调内存的连续跟分散会更好一点，因为数组是开辟的连续内存空间，而链表不需要，它是零散的，所以链表读要使用指针，自然而然数组读效率更高。</p><h3 id="2-Spring-篇"><a href="#2-Spring-篇" class="headerlink" title="2. Spring 篇"></a>2. Spring 篇</h3><h4 id="2-1-Spring-是干什么用到？"><a href="#2-1-Spring-是干什么用到？" class="headerlink" title="2.1 Spring 是干什么用到？"></a>2.1 Spring 是干什么用到？</h4><p><code>Spring</code>可以方便地给我们管理创建、获取和<code>kill</code>对象，不需要我们去额外做这一份工作。</p><h4 id="2-2-了解过-IOC吗？"><a href="#2-2-了解过-IOC吗？" class="headerlink" title="2.2 了解过 IOC吗？"></a>2.2 了解过 <code>IOC</code>吗？</h4><p><code>IOC</code>是控制反转的意思，在<code>Spring</code> 容器中，创建对象的工作不再由我们来负责，而是交由 <code>Spring</code> 容器去管理各个对象的生命周期。</p><h4 id="2-3-说一说-Spring-容器管理对象生命周期有什么用？"><a href="#2-3-说一说-Spring-容器管理对象生命周期有什么用？" class="headerlink" title="2.3 说一说 Spring 容器管理对象生命周期有什么用？"></a>2.3 说一说 Spring 容器管理对象生命周期有什么用？</h4><p>由于 <code>Spring</code>容器默认使用的是 <code>Single</code>单例创建对象，这样各个线程对同一个对象是共享的，就可以做到多个用户请求访问是拿的是同一个实例，不用总去创建对象，导致频繁的 <code>GC</code>垃圾回收。</p><h4 id="2-4-说一说-Spring-中容器它的创建实例的作用域"><a href="#2-4-说一说-Spring-中容器它的创建实例的作用域" class="headerlink" title="2.4 说一说 Spring 中容器它的创建实例的作用域"></a>2.4 说一说 Spring 中容器它的创建实例的作用域</h4><p>在 <code>Spring</code>容器中，有一个属性 <code>scope</code>，就是一个作用域，它可以指明创建对象是采用单例还是其他方式</p><p>单例的话多个线程下是共享实例的，当时只想到 <code>Request</code>，但这个不是线程级别的，没回答上来，没想到 <code>Prototype</code>（原型），技术官帮我解围了，说知道我知道想说什么，就是说不出来哈哈。</p><p><code>Prototype</code> 它是一个原型，在多个线程中的创建的单例是互不干扰的，也就是生命周期的处于线程。</p><p>当然还有其他，因为不怎么使用，所以就没有怎么说。</p><h3 id="MySQL-篇"><a href="#MySQL-篇" class="headerlink" title="MySQL 篇"></a>MySQL 篇</h3><h4 id="MySQL-你了解过哪些引擎"><a href="#MySQL-你了解过哪些引擎" class="headerlink" title="MySQL 你了解过哪些引擎"></a>MySQL 你了解过哪些引擎</h4><p>嗯？这个我了解过两个，<code>INNODB</code>和<code>Myisam</code></p><h4 id="这两个是有什么区别？"><a href="#这两个是有什么区别？" class="headerlink" title="这两个是有什么区别？"></a>这两个是有什么区别？</h4><p>我就说了第一个默认会使用 行锁，<code>Myisam</code> 使用的是表锁</p><h4 id="面试官继续追问，为什么？怎么判断的？"><a href="#面试官继续追问，为什么？怎么判断的？" class="headerlink" title="面试官继续追问，为什么？怎么判断的？"></a>面试官继续追问，为什么？怎么判断的？</h4><p>这个我真不知道该怎么回答，我就瞎扯了锁升级，就说 <code>INNODB</code>因为默认使用行锁，但是如果因为没有索引优化或者语句优化等问题，可能会出现锁升级现象，这是 <code>Myisam</code> 没有的。</p><p>面试官一头雾水，但没有反驳，好，继续下个问题。</p><h4 id="事务有了解吗？"><a href="#事务有了解吗？" class="headerlink" title="事务有了解吗？"></a>事务有了解吗？</h4><p>我说有有，这就来劲了，就说<code>INODB</code> 有事务，但<code>Myisam</code> 没有事务这说法，现在才明白面试官说这两个引擎的区别原来在这里。</p><p>面试官没有为难，知道问的差不多了，就开始手撕 <code>SQL</code></p><h4 id="口述SQL"><a href="#口述SQL" class="headerlink" title="口述SQL"></a>口述SQL</h4><p>给你一个表，这个表有三个字段，一个是学生 <code>id</code>，一个是课程名 <code>cname</code>，一个是课程成绩 <code>cscore</code></p><p>要求：如果有三门课程，要求找出学生的总成绩大于270，并且返回学生<code>id</code>和总成绩</p><p>纳尼，第一个想法就是想着连表查询，当时觉得没戏了，就很挣扎</p><p>口述：就是通过三张表自然关联，关联三门成绩，然后关联条件是学生id，<code>where</code> 是三门成绩之和大于<code>270</code></p><p>面试官觉得有问题，这种做法不通用，要我再想想其他方法</p><p>想了两分钟，没有思绪，给我个提示：可以使用 group by</p><p>分组？那就简单了，我就直接口述了，说可以先分组，然后在每个分组里面使用聚合函数<code>sum</code>，将总成绩这个条件满足<code>270</code>的查询出来</p><p>当时很紧张，怕错了，面试官一脸微笑的说，是的，思路是对的，你可以用在纸上用笔写下来吗？</p><p>他知道我口述可以，但还是不相信我动手能力，瞧不起了，可能之前用那个关联的SQL 手写SQL，出现了一点问题，但在他提出问题的时候，我立马说我可以修改下<code>sql</code>吗，改了再给他看了下，可能就是因为这个原因，自己不仔细写，给自己埋坑，让面试官对我有了质疑</p><p>于是这次我就好好捋了捋思绪，面试官看我迟迟未动笔，说不用紧张，慢慢想，当然我肯定不这么想，当时已经有想法了，只是怕又写错了<code>SQL</code>，因为当时已经有一个月没刷 力扣 <code>SQL</code>题型了，想了两分钟左右，写了出来</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> sc.sid, <span class="built_in">sum</span>(sc.score) <span class="keyword">as</span> sum_score</span><br><span class="line"><span class="keyword">from</span> SC sc</span><br><span class="line"><span class="keyword">group</span> <span class="keyword">by</span> sc.sid</span><br><span class="line"><span class="keyword">having</span> <span class="keyword">on</span> <span class="built_in">sum</span>(sc.score) <span class="operator">&gt;</span> <span class="number">270</span></span><br></pre></td></tr></table></figure><p>就这样，面试官比较满意，就问我为什么当时没想出来，我就说当时他说给三科成绩，很容易就让人想到用联表</p><p>到这里 <code>SQL</code> 也没继续写了</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;简单三分钟自我介绍&quot;&gt;&lt;a href=&quot;#简单三分钟自我介绍&quot; class=&quot;headerlink&quot; title=&quot;简单三分钟自我介绍&quot;&gt;&lt;/a&gt;简单三分钟自我介绍&lt;/h2&gt;&lt;p&gt;自我介绍这里一笔带过，给对面介绍自己内在 + 外在 + 校园经历 + 校园项目  +</summary>
      
    
    
    
    <category term="面经" scheme="http://fyupeng.github.io/categories/%E9%9D%A2%E7%BB%8F/"/>
    
    
    <category term="Java面经" scheme="http://fyupeng.github.io/tags/Java%E9%9D%A2%E7%BB%8F/"/>
    
  </entry>
  
  <entry>
    <title>长城软件面经</title>
    <link href="http://fyupeng.github.io/2022/09/25/%E9%95%BF%E5%9F%8E%E8%BD%AF%E4%BB%B6%E9%9D%A2%E7%BB%8F/"/>
    <id>http://fyupeng.github.io/2022/09/25/%E9%95%BF%E5%9F%8E%E8%BD%AF%E4%BB%B6%E9%9D%A2%E7%BB%8F/</id>
    <published>2022-09-25T10:28:40.000Z</published>
    <updated>2022-09-25T15:23:04.149Z</updated>
    
    <content type="html"><![CDATA[<h2 id="简单三分钟自我介绍"><a href="#简单三分钟自我介绍" class="headerlink" title="简单三分钟自我介绍"></a>简单三分钟自我介绍</h2><p>自我介绍这里一笔带过，给对面介绍自己内在 + 外在 + 校园经历 + 校园项目  + 意向岗位</p><h3 id="技术面"><a href="#技术面" class="headerlink" title="技术面"></a>技术面</h3><h4 id="1-Java基础篇"><a href="#1-Java基础篇" class="headerlink" title="1. Java基础篇"></a>1. Java基础篇</h4><h5 id="1-1-说一说数据结构"><a href="#1-1-说一说数据结构" class="headerlink" title="1.1 说一说数据结构"></a>1.1 说一说数据结构</h5><p>说下链表的属性，指针属性</p><p>树结构了解过吗？</p><p>叶子节点怎么存储的？</p><h4 id="2-Spring-篇"><a href="#2-Spring-篇" class="headerlink" title="2. Spring 篇"></a>2. Spring 篇</h4><h5 id="2-1-Spring-MVC-的原理"><a href="#2-1-Spring-MVC-的原理" class="headerlink" title="2.1 Spring MVC 的原理"></a>2.1 Spring MVC 的原理</h5><p>主要有5个组件，前端控制器、映射器、处理器、处理器适配器、视图解析器</p><ul><li><p>前端控制器也就是中央处理器，它主要负责前端用户的请求和对其他组件的转发调用；</p></li><li><p>前端控制器接收到请求后，会把请求转发给处理器映射器，处理器会根据配置或注解（@Controller）找到最终要执行的Handler；</p></li><li><p>然后将Handler（自己的业务处理）对象以及其对应的拦截器，封装到HandlerExecutionChain对象中并以它的形式放回；</p></li><li><p>前端控制器根据获取到的Handler（Controller），去选择一个合适的HandlerAdapter（它还支持Servlet、HttpRequestHandler、ThrowawayController），成功获取到会先去执行拦截器；</p></li><li><p>这时将开始对提取Request中的模型数据，并且将Handler的入参进行数据填充，以至于我们写控制层方法时，入参是前端传来的值，在填充过程中，根据自己的配置，Spring还可以帮你做一些额外操作：</p><ul><li>Json/Xml 数据转换成对象</li><li>格式化请求数据</li><li>校验</li></ul></li><li><p>Handler执行完成后，返回一个ModelAndView（视图和视图需要填充的模型数据）</p></li><li><p>前端控制器根据返回的ModelAndView，选择合适的ViewResolver</p></li><li><p>ViewResolver 结合Model和View，来渲染视图</p></li><li><p>最终前端控制器将渲染结果返回给客户端</p></li></ul><h4 id="3-JVM-篇"><a href="#3-JVM-篇" class="headerlink" title="3. JVM 篇"></a>3. JVM 篇</h4><h5 id="3-1-说一说你对jvm内存结构的理解"><a href="#3-1-说一说你对jvm内存结构的理解" class="headerlink" title="3.1 说一说你对jvm内存结构的理解"></a>3.1 说一说你对jvm内存结构的理解</h5><p>jvm 内存模型由程序计数器、方法区、虚拟机栈、本地方法栈、堆和运行常量池组成。</p><p>方法区：主要是存储类变量，静态变量，常量以及<code>class</code>描述信息（会在类加载后加载到方法区中）</p><p>虚拟机栈：虚拟机栈跟线程是直接挂钩的，它的生命周期跟线程一样，而且虚拟机栈设置的大小（<code>-Xss1024k</code>，1024个字节1M单词缩写Stack Size），当设置过大时，在堆大小固定的情况下，总内存 = 堆内存 + 线程数 * 虚拟机栈大小，可线程数就会相应减少；</p><p>本地方法栈：跟虚拟机栈相似，不过不是用Java写的；</p><p>程序计数器：线程私有，每个线程在让出CPU会由它来记录当前位置，以便恢复CPU还在原位置执行；</p><p>堆：存放Java对象实例，在JDK7版本中，由新生代（Eden区、Survivor区）和 老年代组成，也是GC垃圾收集器处理的主要区域，俗称GC堆，是管理内存中最大的一块；</p><p>运行时常量池是放在方法区中的，作为方法区的一部分。</p><h5 id="3-2-对象创建和销毁的过程"><a href="#3-2-对象创建和销毁的过程" class="headerlink" title="3.2 对象创建和销毁的过程"></a>3.2 对象创建和销毁的过程</h5><p>对象创建的过程其实在 <code>jvm</code> 中包括了加载、链接（包含验证（虚拟机的安全性、对象头、父类与子类中<code>final</code>等声明的安全性校验等等）、准备、解析）、初始化；</p><p>准备阶段：申请内存空间并完成默认的初始化，比如int类型默认初始化为4个字节的内存空间，并初始化为0；</p><p>解析阶段：将常量池中的引用解析为直接引用，也就是将类中的常量池放到了运行时常量池中，这样我们在直接调用方法或者引用类的时候，是需要符号引用的，只不过<code>jvm</code>帮我们自动引用了常量中的符号，就成直接引用。</p><p>初始化阶段：用户真正的赋值操作，即调用相应的构造器构造对象，构造对象创建的时候，如果是比较复杂的，由于虚拟机优化机制，会出现指令重排序问题，什么问题？</p><p>在该场景中，构造过程分为几个步骤：申请内存 -&gt; 构造块构造 -&gt; 内存地址返回，这里面的指令为了性能上的提示，而不改变结果，jvm可能会将内存地址也就是构造函数创建的对象指向的内存地址放到构造块过程中返回，而没有等到最后。</p><p>引用：编译器可以自由地以优化的名义对指令的顺序进行随意处理。</p><p>那么怎么解决？</p><p>当然是保证原子性呀！</p><p>怎么保证？</p><p><code>final</code>声明不仅能保证类不可继承，方法不可重写，变量不可变，还有一个重要的特点就是能保证可见性，媲美<code>volatile</code></p><p>可见性保证就是对象如果已经可见（地址已经返回了，那么保证它构造的完整性，也就是指令不会发生重排）性</p><p>文章分享：<a href="https://qa.1r1g.com/sf/ask/451997661/">https://qa.1r1g.com/sf/ask/451997661/</a></p><p>内存模型官方讲解：<a href="http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html">http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html</a></p><p>推荐比较好的网站：<a href="https://qa.1r1g.com/">https://qa.1r1g.com/</a></p><blockquote><p> 补充</p></blockquote><p>如果类中的成员变量声明为final并且是基本类型，那么赋值阶段会在准备阶段就完成了，这些可实现一些并发安全性应用；</p><h5 id="3-2-GC-垃圾回收了解过吗"><a href="#3-2-GC-垃圾回收了解过吗" class="headerlink" title="3.2 GC 垃圾回收了解过吗"></a>3.2 GC 垃圾回收了解过吗</h5><p>了解过一点点，没了，他就不问了……</p><p>我就是专门去看了<code>gc</code>中的一些原理，比如复制算法、并发标记、并发清除、分代回收，还有<code>Serial</code>、<code>parNew</code>、<code>parallel Scavenge</code>和<code>g1</code>这些，看來不能太低调了。</p><h5 id="3-3-JVM-调优了解过吗"><a href="#3-3-JVM-调优了解过吗" class="headerlink" title="3.3 JVM 调优了解过吗"></a>3.3 JVM 调优了解过吗</h5><p>答案是没有，所以没有下文了，可惜这篇面经比较卑微，味道不够香。</p><p>因为就是简单使用过，没有在具体项目场景用到，不敢吹嘘。</p><h5 id="3-4-volatile-能保证原子性吗"><a href="#3-4-volatile-能保证原子性吗" class="headerlink" title="3.4 volatile 能保证原子性吗"></a>3.4 volatile 能保证原子性吗</h5><p>不能，volatile可以保证可见性，它是在<code>jvm</code>优化后，出现指令重排序后才有的，能够防止指令的重排序，也就是说指令重排序可能会先将对象在执行构造函数中，提前把对象所在地址给返回了，这就可能导致成员变量出现为null的情况，有了volatile就不会发生。</p><p><code>volatile</code>一般修饰于变量</p><p>当然除了这个，<code>volatile</code>保证可见性还体现在内存与高速缓存之间，如果一个变量通过for循环去执行，那么<code>jvm</code>将进行性能优化，将内存中的变量放到高速缓存中，这样容易出现内存与高速缓存的不一致性问题，<code>volatile</code>可以保证对内存中变量的修改及时同步到高速缓存中。</p><h4 id="4-SQL-篇"><a href="#4-SQL-篇" class="headerlink" title="4. SQL 篇"></a>4. SQL 篇</h4><h5 id="4-1-聊一聊你对SQL中的优化"><a href="#4-1-聊一聊你对SQL中的优化" class="headerlink" title="4.1 聊一聊你对SQL中的优化"></a>4.1 聊一聊你对SQL中的优化</h5><p>主要分为语句优化和索引优化</p><p>语句优化比如避免使用or，可能导致全盘遍历，我是说了join连接和子查询，前一个只需要查询一次，后一个会多次遍历全表，如果数据量大的表将很严重。</p><p>索引优化的话，面试官很耐心给我讲了组合索引、失效的问题</p><h5 id="4-2-SQL-中的函数的处理"><a href="#4-2-SQL-中的函数的处理" class="headerlink" title="4.2 SQL 中的函数的处理"></a>4.2 SQL 中的函数的处理</h5><p><code>SQL</code>中的函数比如时间的<code>yyyy-MM-dd hh-mm-ss</code>,通过这种设置可以来处理相应的时间</p><p>可以看看这篇文章：<a href="https://blog.csdn.net/chentaocba/article/details/7539409">https://blog.csdn.net/chentaocba/article/details/7539409</a></p><p>可以这样自定义去处理拼接时间</p><h4 id="5-Redis-篇"><a href="#5-Redis-篇" class="headerlink" title="5. Redis 篇"></a>5. Redis 篇</h4><h5 id="5-1-聊一聊哨兵模式"><a href="#5-1-聊一聊哨兵模式" class="headerlink" title="5.1 聊一聊哨兵模式"></a>5.1 聊一聊哨兵模式</h5><p>哨兵模式的应用其实是<code>Redis</code>集群中故障恢复和高可用的应用，在<code>Redis</code>读写分离也就是主从复制中，哨兵节点作为独立节点，不参与选举。</p><p>在主节点也就是<code>master</code>节点宕机后，多个哨兵节点一致发现主节点下线，通过投票选举哪一个从节点选举为主节点。</p><p>其实就是一种监控主节点是否故障的行为。</p><h4 id="5-分布式"><a href="#5-分布式" class="headerlink" title="5. 分布式"></a>5. 分布式</h4><h5 id="3-1-说一说服务降级"><a href="#3-1-说一说服务降级" class="headerlink" title="3.1 说一说服务降级"></a>3.1 说一说服务降级</h5><p>在请求超时、资源不足、<code>CPU</code>、数据库<code>IO</code>达到瓶颈时，采取的一种补救措施。</p><p>服务降级主要有<code>Dubbbo</code>的<code>Mock</code>和<code>Spring Cloud</code>的<code>Hystrix</code>，屏蔽对远程的调用，一般使用<code>SpringBoot</code>比较推荐整合<code>Hystrix</code>来实现服务降级容错。</p><h5 id="3-2-聊一聊你的项目分布式中怎么解决出现的问题"><a href="#3-2-聊一聊你的项目分布式中怎么解决出现的问题" class="headerlink" title="3.2 聊一聊你的项目分布式中怎么解决出现的问题"></a>3.2 聊一聊你的项目分布式中怎么解决出现的问题</h5><p>我做的是一个分布式的博客管理项目，在搭建并部署分布式项目时，调用远程服务的时候，注册中心<code>Nacos</code>很容易出现宕机，当时排查的原因是内存不够，当时使用了阿里的两台服务器、一台腾讯云服务器来搭建分布式项目，阿里一台作为前端和<code>Nginx</code>做反向代理，一台运行着博客的文章、评论、分类标签和用户的五个服务，一台作为代理服务器，处理前端的请求、远程调用腾讯云服务器的服务，但我把注册中心放到了腾讯云上，因为腾讯云上的服务需要进行服务注册，而且注册中心<code>Nacos</code>启动是带有Web后台管理页面的，所以就作为一个内部的服务器使用。</p><p>于是解决方案就是将多个服务进行横向拆分，将文章和用户的服务放到代理服务器上运行，解决了在远程调用服务的时候因为请求超时或资源不足导致<code>Nacos</code>注册中心出现宕机的情况。</p><h4 id="6-网络编程"><a href="#6-网络编程" class="headerlink" title="6. 网络编程"></a>6. 网络编程</h4><h5 id="6-1-Tomcat-与-Netty-有什么区别"><a href="#6-1-Tomcat-与-Netty-有什么区别" class="headerlink" title="6.1 Tomcat 与 Netty 有什么区别"></a>6.1 Tomcat 与 Netty 有什么区别</h5><p><code>Tomcat</code>是一种使用<code>http</code>协议的<code>web</code>容器，是用来启动<code>web</code>应用的，而Netty是一种自定义处理各种字节流数据做相应的编解码，也就是可以自定义通信协议，<code>Netty</code>是一款基于<code>NIO</code>的网络通信框架，而且Tomcat的话其实也支持<code>NIO</code>模式。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;简单三分钟自我介绍&quot;&gt;&lt;a href=&quot;#简单三分钟自我介绍&quot; class=&quot;headerlink&quot; title=&quot;简单三分钟自我介绍&quot;&gt;&lt;/a&gt;简单三分钟自我介绍&lt;/h2&gt;&lt;p&gt;自我介绍这里一笔带过，给对面介绍自己内在 + 外在 + 校园经历 + 校园项目  +</summary>
      
    
    
    
    <category term="面经" scheme="http://fyupeng.github.io/categories/%E9%9D%A2%E7%BB%8F/"/>
    
    
    <category term="Java面经" scheme="http://fyupeng.github.io/tags/Java%E9%9D%A2%E7%BB%8F/"/>
    
  </entry>
  
  <entry>
    <title>华资面经</title>
    <link href="http://fyupeng.github.io/2022/09/25/%E5%8D%8E%E8%B5%84%E9%9D%A2%E7%BB%8F/"/>
    <id>http://fyupeng.github.io/2022/09/25/%E5%8D%8E%E8%B5%84%E9%9D%A2%E7%BB%8F/</id>
    <published>2022-09-25T10:28:30.000Z</published>
    <updated>2022-09-25T10:30:09.032Z</updated>
    
    <content type="html"><![CDATA[<h2 id="华资一面"><a href="#华资一面" class="headerlink" title="华资一面"></a>华资一面</h2><p>—— 人力面（挂）</p><h3 id="1-Spring"><a href="#1-Spring" class="headerlink" title="1. Spring"></a>1. Spring</h3><h4 id="1-1-Spring-的生命周期"><a href="#1-1-Spring-的生命周期" class="headerlink" title="1.1 Spring 的生命周期"></a>1.1 Spring 的生命周期</h4><blockquote><p>推荐学习链接：<a href="https://blog.csdn.net/shijinjins/article/details/124071095">https://blog.csdn.net/shijinjins/article/details/124071095</a> </p></blockquote><p>Spring 生命周期会经历过四个阶段，分别是实例化、属性赋值、初始化、销毁</p><p>其他的一些阶段就是通过AOP面向切面扩展来的，能够让你在实例化、初始化和销毁前后做一些逻辑。</p><p>初始化阶段，有一个特别重要的接口<code>BeanPostProcessor</code></p><ul><li><code>postProcessorBeforeInitialization();</code> // 初始化前调用</li><li><code>postProcessorAfterInitialization();</code> // 初始化后调用</li></ul><p><strong>初始化</strong>方式有三个</p><ul><li><code>InitializingBean</code>类的<code>afterPropertiesSet</code>方法</li><li><code>@PostConstruct</code>注解标注的方法 // <code>Spring</code>支持javax包中的<code>@PostConstruct</code>，会在属性值注入成功后执行</li><li><code>Spring</code>配置文件<code>applicationContext.xml</code>的标签属性<code>init-method=&quot;initMethod&quot;</code></li></ul><p>这里提醒，<code>@PostConstruct</code>是通过<code>Spring</code>的<code>BeanPostProcessor#postProcessBeforeInitialization(Object, String)</code>来完成调用的</p><p>如果是使用到了<code>Spring</code>自动注入来使用创建的<code>Bean</code>，那么如果使用反射的方式创建对象执行构造方法，先后顺序是这样的：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">flowchart LR</span><br><span class="line"></span><br><span class="line">A[服务器加载Servlet]--&gt; B[Servlet构造函数]</span><br><span class="line">B --&gt; C[&quot;@Autowire&quot;]</span><br><span class="line">C --&gt; D[&quot;@PostConstruct&quot;]</span><br></pre></td></tr></table></figure><p>反射的方式会在执行构造器后返回，所以你要通过反射使用<code>Bean</code>，可在<code>@PostConstruct</code>注解的方法保存该<code>Bean</code>，再去调用使用。</p><p><strong>销毁</strong>方式有三个</p><ul><li><code>@PreDestroy</code>注解标注的方法 //  // <code>Spring</code>支持javax包中的<code>@preDestroy</code>，会在销毁前执行</li><li><code>DisposableBean</code>接口的<code>destroy</code>方法</li><li><code>Spring</code>配置文件<code>applicationContext.xml</code>的标签属性<code>destroy-method=;&quot;destroyMethod&quot;</code></li></ul><p>这里提醒，<code>@preDestroy</code>执行是通过<code>Spring</code>的<code>DestructionAwareBeanPostProcessor#postProcessBeforeDestruction(Object, String)</code>来完成调用的</p><blockquote><p> 简要步骤</p></blockquote><p>1.当程序加载运行时会根据spring中配置文件找到bean配置的属性和方法，并通过java反射机制创建实例化bean对象。</p><p>Bean实现了<code>BeanNameAware</code>接口，执行了<code>setBeanName</code>方法，实现注入对象。</p><p>2.实现了<code>BeanFactoryAware</code>工厂接口，执行了<code>setBeanFactory</code>方法。</p><p>3.实现了<code>ApplicationContext</code>接口类，执行了<code>setsetApplicationContest</code>方法。</p><p>4.实现了<code>BeanPostProcessor</code>接口类，执行<code>postProcessBeforeInitialization</code>方法</p><p>5.实现了<code>InitiliazingBean</code> 执行<code>afterPropertiesSet</code>方法，并加载配置文件定义了<code>init-method</code> 则执行对应初始化方法<code>BeanPostProcessor</code> 执行<code>postProcessorfterInitilization</code>方法，完成  <code>Bean</code>的初始化，使得<code>bean</code>可以使用。</p><p>6.实现了<code>DisposabileBean</code>接口执行<code>destroy</code>方法，加载配置文件中的<code>destroy-method</code>方法销毁<code>bean</code>对象实例。</p><h4 id="1-2-Spring-作用域"><a href="#1-2-Spring-作用域" class="headerlink" title="1.2 Spring 作用域"></a>1.2 Spring 作用域</h4><p>单例（默认<code>Single</code>）和多例（<code>Prototype</code>）</p><p>单例在一个容器里面只存在一个对象，多例的Bean生命周期在容器产生时并不创建，对象使用时才创建，最后由JVM垃圾回收期回收</p><p>生命周期出生和销毁还可以自己指定，比如</p><ul><li>init-method 对象出生之后立刻执行什么方法</li><li>destroy-method对象销毁之前执行什么方法</li></ul><h4 id="1-3-Spring-依赖注入方式"><a href="#1-3-Spring-依赖注入方式" class="headerlink" title="1.3 Spring 依赖注入方式"></a>1.3 Spring 依赖注入方式</h4><p>有三种：属性注入、构造函数注入、工厂方法（通过标签属性配置静态或实例工厂）注入</p><p>而基于这三种方式有多种实现：配置文件实现、注解实现、</p><h3 id="2-集合"><a href="#2-集合" class="headerlink" title="2. 集合"></a>2. 集合</h3><p>集合的性质: <strong>唯一性</strong>、<strong>无序性</strong>、<strong>确定性</strong></p><p>唯一性指的是集合元素是不重复的、无序性指的是集合插入元素的顺序性、确定性是指集合中元素是确定的，不是相对性的。</p><p>集合类型按类型划分可以划分为可重复集合不可重复集</p><ul><li>可重复集合：<code>List</code>实现类</li><li>不可重复集合：<code>Set</code>实现类、<code>Map</code>实现类（<code>key</code>）</li></ul><p>按照有序性（添加和访问）划分可以划分为有序集和无序集</p><ul><li>有序集：<code>List</code>实现类、<code>TreeSet</code>、<code>LinkedHashSet</code>、<code>LinkedHashMap</code>、<code>TreeMap</code></li><li>无序集：<code>HashSet</code>、<code>HashMap</code>、</li></ul><p>按照结构划分可以划分为列表<code>List</code>、<code>Set</code>、<code>Map</code>（<code>Stack</code> 和 <code>Queue</code>）</p><p>List：<code>ArrayList</code>、<code>LinkedList</code></p><p>Set：<code>HashSet</code>、<code>LinkedHashSet</code>、<code>TreeSet</code></p><p>Map：<code>HashMap</code>、<code>HashTable</code>、<code>TreeMap</code>、<code>LinkedHashMap</code>、<code>CurrentHashMap</code>、</p><p>按照存储连续性划分可以划分为连续集和散列集</p><ul><li>连续集：<code>ArrayList</code></li><li>散列集：<code>LinkedList</code>、<code>Map</code>实现类、<code>Set</code>实现类</li></ul><h3 id="3-线程安全"><a href="#3-线程安全" class="headerlink" title="3. 线程安全"></a>3. 线程安全</h3><h4 id="3-1-线程安全的集合有哪些？"><a href="#3-1-线程安全的集合有哪些？" class="headerlink" title="3.1 线程安全的集合有哪些？"></a>3.1 线程安全的集合有哪些？</h4><p>HashTable、CurrentHashMap、CurrentLinkedQueue、ArrayBlockingQueue、LinkedBlockingQueue</p><p>HashTable：它的方法都使用了重量级锁synchronized修饰来保证方法执行的线程安全。</p><p>CurrentHashMap：有JDK迭代版本；</p><ul><li>7使用了分段锁Segment（继承可重入锁ReentrantLock）实现线程安全，是真正意义上实现了并发</li><li>8使用了synchronized + CAS，因为在1.6版本之后，synchronized一步步得到优化<ul><li>包含了偏向锁、轻量级锁、重量级锁</li><li>处于偏向锁的对象，是指可以在获得该偏向锁的同一个线程中，多次执行同步代码块时相当于无锁</li><li>而如果处于偏向锁的对象被其他线程获取了，偏向锁将不再偏向，就升级为轻量级锁</li><li>升级为轻量级锁还是有机会继续偏向锁的，是怎么个原理？</li><li>在对象头前8个字节中，存储了偏向线程id，前提得是该线程未获取锁对象，获取锁对象后设置的是当前线程id</li><li>所以想让对象头上的偏向线程id重新偏向为当前线程id，得让该线程重复获取锁对象达到阈值20次才会偏向，不过只能偏向一次</li><li>这就是锁降级，如果有第三个线程参与了，同样执行第二个线程的操作，将不再偏向（对象头偏向线程id重置为0）</li><li>这是由再次升级为轻量级锁，前面是不发生竞争，如果多个线程发生竞争，竞争失败会自旋CAS再次尝试获取锁，如果还是失败</li><li>这时升级为重量级锁，重量级锁将不再通过乐观锁CAS操作，而是会发生阻塞，必须等获得锁的线程释放锁才能让给下一个线程</li></ul></li></ul><p>CurrentLinkedQueue：使用了非阻塞的方式实现了线程安全，是一个无锁化的单向队列，使用到了<code>CAS</code> + <code>for(;;)</code>，而且这种单向队列巧妙利用节点的不可重用 + CAS + next 指向本身 + GC垃圾回收，规避掉了ABA问题</p><ul><li>因为节点出队列并不会马上出队列，而是通过next指向自己让GC垃圾回收掉，这样有线程刻意去ABA操作时，发现还有旧值，没法做修改达到ABA的目的，这说专业点就是延迟出队。</li></ul><p>LinkedBlockingQueue：与CurrentLinkedQueue非常像，也是一个无界队列，这样如果入队快于出队的情况容易出现栈溢出</p><p>ArrayBlockingQueue：是有界队列</p><h3 id="4-谈谈自己的项目"><a href="#4-谈谈自己的项目" class="headerlink" title="4. 谈谈自己的项目"></a>4. 谈谈自己的项目</h3><p>略</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;华资一面&quot;&gt;&lt;a href=&quot;#华资一面&quot; class=&quot;headerlink&quot; title=&quot;华资一面&quot;&gt;&lt;/a&gt;华资一面&lt;/h2&gt;&lt;p&gt;—— 人力面（挂）&lt;/p&gt;
&lt;h3 id=&quot;1-Spring&quot;&gt;&lt;a href=&quot;#1-Spring&quot; class=&quot;hea</summary>
      
    
    
    
    <category term="面经" scheme="http://fyupeng.github.io/categories/%E9%9D%A2%E7%BB%8F/"/>
    
    
    <category term="Java面经" scheme="http://fyupeng.github.io/tags/Java%E9%9D%A2%E7%BB%8F/"/>
    
  </entry>
  
  <entry>
    <title>第一场面试</title>
    <link href="http://fyupeng.github.io/2022/09/25/%E7%AC%AC%E4%B8%80%E5%9C%BA%E9%9D%A2%E8%AF%95/"/>
    <id>http://fyupeng.github.io/2022/09/25/%E7%AC%AC%E4%B8%80%E5%9C%BA%E9%9D%A2%E8%AF%95/</id>
    <published>2022-09-25T10:26:54.000Z</published>
    <updated>2022-09-25T10:28:18.019Z</updated>
    
    <content type="html"><![CDATA[<h2 id="简单三分钟自我介绍"><a href="#简单三分钟自我介绍" class="headerlink" title="简单三分钟自我介绍"></a>简单三分钟自我介绍</h2><p>自我介绍这里一笔带过，给对面介绍自己内在 + 外在 + 校园经历 + 校园项目  + 意向岗位</p><h2 id="技术面"><a href="#技术面" class="headerlink" title="技术面"></a>技术面</h2><h3 id="1-Spring-原理篇"><a href="#1-Spring-原理篇" class="headerlink" title="1. Spring 原理篇"></a>1. Spring 原理篇</h3><h4 id="1-1-有使用过Spring吗，说一说它的-SpringMVC-原理"><a href="#1-1-有使用过Spring吗，说一说它的-SpringMVC-原理" class="headerlink" title="1.1 有使用过Spring吗，说一说它的 SpringMVC 原理"></a>1.1 有使用过Spring吗，说一说它的 SpringMVC 原理</h4><p>主要有5个组件，前端控制器、映射器、处理器、处理器适配器、视图解析器</p><ul><li><p>前端控制器也就是中央处理器，它主要负责前端用户的请求和对其他组件的转发调用；</p></li><li><p>前端控制器接收到请求后，会把请求转发给处理器映射器，处理器会根据配置或注解（@Controller）找到最终要执行的Handler；</p></li><li><p>然后将Handler（自己的业务处理）对象以及其对应的拦截器，封装到HandlerExecutionChain对象中并以它的形式放回；</p></li><li><p>前端控制器根据获取到的Handler（Controller），去选择一个合适的HandlerAdapter（它还支持Servlet、HttpRequestHandler、ThrowawayController），成功获取到会先去执行拦截器；</p></li><li><p>这时将开始对提取Request中的模型数据，并且将Handler的入参进行数据填充，以至于我们写控制层方法时，入参是前端传来的值，在填充过程中，根据自己的配置，Spring还可以帮你做一些额外操作：</p><ul><li>Json/Xml 数据转换成对象</li><li>格式化请求数据</li><li>校验</li></ul></li><li><p>Handler执行完成后，返回一个ModelAndView（视图和视图需要填充的模型数据）</p></li><li><p>前端控制器根据返回的ModelAndView，选择合适的ViewResolver</p></li><li><p>ViewResolver 结合Model和View，来渲染视图</p></li><li><p>最终前端控制器将渲染结果返回给客户端</p></li></ul><h3 id="1-2-springboot-有什么优点"><a href="#1-2-springboot-有什么优点" class="headerlink" title="1.2 springboot 有什么优点"></a>1.2 springboot 有什么优点</h3><p><code>springboot</code> 它可以帮我快速、使用最低代码最少时间开发出一个项目，不想 <code>SpringMVC</code>，需要繁琐写很多配置信息，我们只关注写业务代码即可。</p><p>通过配置文件解耦代码，在一些可能改动的配置信息，可以不通过修改代码再打包部署，而是让代码去外部读取该配置文件，再去运行。</p><p>还有<code>SpringBoot</code>是使用了<code>maven</code> 可以方便对版本的管理，能够让自己不需要考虑版本依赖问题和兼容版本的问题。</p><h3 id="Redis-篇"><a href="#Redis-篇" class="headerlink" title="Redis 篇"></a>Redis 篇</h3><h3 id="有使用过-Redis吗"><a href="#有使用过-Redis吗" class="headerlink" title="有使用过 Redis吗"></a>有使用过 Redis吗</h3><p>有，它是一个缓存数据库，主要存储的是<code>k-v</code>类型的数据。</p><h3 id="那说说它的一个缓存雪崩？"><a href="#那说说它的一个缓存雪崩？" class="headerlink" title="那说说它的一个缓存雪崩？"></a>那说说它的一个缓存雪崩？</h3><p>嗯？缓存雪崩的话，就是<code>key</code>集中失效，导致 <code>Redis</code> 相当于宕机，大量请求依旧打到<code>mysql</code>数据库上了。</p><p>这里总结一下，其实雪崩<code>Redis</code>服务基本不可用了，存储层的调用量会暴增，造成存储层也会挂掉的情况，而且雪崩会出现周期性的峰值，因为就算缓存失效了，请求到了存储层，获取到数据后，是会再做一次缓存的，所以会出现周期性峰值。</p><p>致命的原因是存储层在暴增的情况下扛不住调用后，导致数据库直接挂掉了。</p><h3 id="有什么解决措施？"><a href="#有什么解决措施？" class="headerlink" title="有什么解决措施？"></a>有什么解决措施？</h3><p>嗯？我们可以对那个 key 的缓存时间设置更长一些，缓存雪崩导致的原因是因为太过于集中失效，导致暴增，那么如果我们均摊一下时间，就能避免出现该情况，所以我们可以对缓存时效设置的更随机性一些，且对一些热点数据缓存时间设置永久性。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;简单三分钟自我介绍&quot;&gt;&lt;a href=&quot;#简单三分钟自我介绍&quot; class=&quot;headerlink&quot; title=&quot;简单三分钟自我介绍&quot;&gt;&lt;/a&gt;简单三分钟自我介绍&lt;/h2&gt;&lt;p&gt;自我介绍这里一笔带过，给对面介绍自己内在 + 外在 + 校园经历 + 校园项目  +</summary>
      
    
    
    
    <category term="面经" scheme="http://fyupeng.github.io/categories/%E9%9D%A2%E7%BB%8F/"/>
    
    
    <category term="Java面经" scheme="http://fyupeng.github.io/tags/Java%E9%9D%A2%E7%BB%8F/"/>
    
  </entry>
  
  <entry>
    <title>ConCurrentHashMap底层原理透析</title>
    <link href="http://fyupeng.github.io/2022/08/06/ConCurrentHashMap%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86%E9%80%8F%E6%9E%90/"/>
    <id>http://fyupeng.github.io/2022/08/06/ConCurrentHashMap%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86%E9%80%8F%E6%9E%90/</id>
    <published>2022-08-06T05:56:54.000Z</published>
    <updated>2023-01-04T03:15:58.326Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>创作宗旨：化繁为简，绝不冗余，点到为止</p></blockquote><p><code>ConcurrentHashMap&lt;K,V&gt;</code> 继承了<code>AbstractMap&lt;K,V&gt;</code>，实现了<code>ConcurrentMap&lt;K,V&gt;</code>和<code>Serializable</code>。<br><strong>说明：</strong><br><code>AbstractMap&lt;K,V&gt;</code>实现了基本的<code>Map</code>操作<br><code>ConcurrentMap&lt;K,V&gt;</code>规范了对<code>k-v</code>的并发操作的方法</p><h2 id="一、介绍"><a href="#一、介绍" class="headerlink" title="一、介绍"></a>一、介绍</h2><ol><li><h3 id="ConCurrentHashMap是如何做到线程安全的？"><a href="#ConCurrentHashMap是如何做到线程安全的？" class="headerlink" title="ConCurrentHashMap是如何做到线程安全的？"></a>ConCurrentHashMap是如何做到线程安全的？</h3>通俗来讲，<code>ConcurrentHashMap&lt;K,V&gt;</code>已经迭代了几个版本，我们先从<code>JDK7</code>说起,起初为了实现了对<code>HashMap</code>真正意义上的并发，在<code>ConcurrentHashMap&lt;K,V&gt;</code>引入了一个静态内部类<code>Segment</code>(段)，而且<code>ConcurrentHashMap&lt;K,V&gt;</code>也聚合了一个<code>Segment</code>的成员变量数组来维护，每一个<code>Segment</code>数组的下标元素相当于一个<code>HashMap</code>，也就是一个<code>HashEntry数组</code> + 每位下标元素构成一个<code>HashEntry</code>链的头结点（依旧保存值），每次存在线程安全的操作都会去使用该数组的其中一个下标，并锁住该下标（其他线程无法访问），而不同[下标之间的操作是不会相互影响的，也就不存在冲突的情况。<br><code>Segment</code>继承了<code>ReentrantLock</code>（<code>可重入锁</code>），用法其实用到加锁和解锁，保证每一个线程使用前加锁，使用完成后释放锁。<br><code>ConcurrentHashMap</code>中<code>Segment</code>与<code>Entries</code>的示意图<br><img src="https://yupeng-tuchuang.oss-cn-shenzhen.aliyuncs.com/dafc603d0b93fd22610455e48eaa3f5f.png" alt="image.png"><br>更专业来讲，<code>ConcurrentHashMap</code>使用了分段锁<code>Segment</code>来解决线程安全。<h3 id="2-JDK7-的-get-和-put-以及扩容"><a href="#2-JDK7-的-get-和-put-以及扩容" class="headerlink" title="2. JDK7 的 get 和 put 以及扩容"></a>2. JDK7 的 get 和 put 以及扩容</h3></li></ol><ul><li>get<br><code>get</code> 操作没有任何加锁，所以在<code>ConCurrent</code>是非常高效的。<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">HashEntry</span>&lt;K,V&gt; &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">int</span> hash;</span><br><span class="line">        <span class="keyword">final</span> K key;</span><br><span class="line">        <span class="keyword">volatile</span> V value;</span><br><span class="line">        <span class="keyword">volatile</span> HashEntry&lt;K,V&gt; next;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>就算获取<code>key</code>之前<code>value</code>被改变了，由于<code>volatile</code>修饰了<code>value</code>变量，所以对内容是及时更新的，<code>新增</code>和<code>删除</code>操作亦是如此。<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> V <span class="title function_">get</span><span class="params">(Object key)</span> &#123;</span><br><span class="line">    Segment&lt;K,V&gt; s; <span class="comment">// manually integrate access methods to reduce overhead</span></span><br><span class="line">    HashEntry&lt;K,V&gt;[] tab;</span><br><span class="line">    <span class="type">int</span> <span class="variable">h</span> <span class="operator">=</span> hash(key);</span><br><span class="line">    <span class="type">long</span> <span class="variable">u</span> <span class="operator">=</span> (((h &gt;&gt;&gt; segmentShift) &amp; segmentMask) &lt;&lt; SSHIFT) + SBASE;</span><br><span class="line">    <span class="comment">// Concurrent 初始化时会初始 Segment数组和 第 0 位下标即Segment[0]（包括 HashEntry数组 的初始化）</span></span><br><span class="line">    <span class="comment">// 如果 segment 对应的 HashEntry 数组不为空，则往下遍历</span></span><br><span class="line">    <span class="keyword">if</span> ((s = (Segment&lt;K,V&gt;)UNSAFE.getObjectVolatile(segments, u)) != <span class="literal">null</span> &amp;&amp;</span><br><span class="line">        (tab = s.table) != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="comment">// 遍历 HashEntry 数组 哈希命中的下标元素（链表）</span></span><br><span class="line">        <span class="keyword">for</span> (HashEntry&lt;K,V&gt; e = (HashEntry&lt;K,V&gt;) UNSAFE.getObjectVolatile</span><br><span class="line">             (tab, ((<span class="type">long</span>)(((tab.length - <span class="number">1</span>) &amp; h)) &lt;&lt; TSHIFT) + TBASE);</span><br><span class="line">             e != <span class="literal">null</span>; e = e.next) &#123;</span><br><span class="line">            K k;</span><br><span class="line">            <span class="comment">// 如果找到了 key就返回对应的value，否则往下继续遍历链表</span></span><br><span class="line">            <span class="comment">// 这里前面匹配的是 基本数据类型，</span></span><br><span class="line">            <span class="comment">//如果是 复杂对象，则使用的是先 hash筛选桶下标，相等再去 比较 “值”</span></span><br><span class="line">            <span class="keyword">if</span> ((k = e.key) == key || (e.hash == h &amp;&amp; key.equals(k)))</span><br><span class="line">                <span class="keyword">return</span> e.value;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>post<br>在<code>ConcurrentHashMap</code>中<code>post</code>操作是不允许设置<code>value</code>为<code>null</code>。<br><code>Concurrent</code>初始化时会初始<code>Segment</code>数组和 第<code>0</code>位下标即<code>Segment[0]</code>（其中包括<code>HashEntry</code>数组的初始化），所以这里会出现命中到另外<code>15</code>个未初始化的<code>Segment</code>锁段，即当前<code>Segment</code>为<code>null</code>的情况<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> V <span class="title function_">put</span><span class="params">(K key, V value)</span> &#123;</span><br><span class="line">    Segment&lt;K,V&gt; s;</span><br><span class="line">    <span class="comment">// 不允许对 value 设置为 null</span></span><br><span class="line">    <span class="keyword">if</span> (value == <span class="literal">null</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">NullPointerException</span>();</span><br><span class="line">    <span class="type">int</span> <span class="variable">hash</span> <span class="operator">=</span> hash(key);</span><br><span class="line">    <span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> (hash &gt;&gt;&gt; segmentShift) &amp; segmentMask;</span><br><span class="line">    <span class="comment">// 没有找到，即说明未初始化</span></span><br><span class="line">    <span class="keyword">if</span> ((s = (Segment&lt;K,V&gt;)UNSAFE.getObject          <span class="comment">// nonvolatile; recheck</span></span><br><span class="line">         (segments, (j &lt;&lt; SSHIFT) + SBASE)) == <span class="literal">null</span>) <span class="comment">//  in ensureSegment</span></span><br><span class="line">        <span class="comment">// 初始化当前 Segment 对应的 HashEntry 数组</span></span><br><span class="line">        s = ensureSegment(j);</span><br><span class="line">    <span class="keyword">return</span> s.put(key, hash, value, <span class="literal">false</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><code>post</code>细节<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">final</span> V <span class="title function_">put</span><span class="params">(K key, <span class="type">int</span> hash, V value, <span class="type">boolean</span> onlyIfAbsent)</span> &#123;</span><br><span class="line">    <span class="comment">// 尝试加锁</span></span><br><span class="line">    HashEntry&lt;K,V&gt; node = tryLock() ? <span class="literal">null</span> :</span><br><span class="line">    <span class="comment">// 如果不成功, 进入 scanAndLockForPut 流程</span></span><br><span class="line">    <span class="comment">// 如果是多核 cpu 最多 tryLock 64 次, 进入 lock 流程</span></span><br><span class="line">    <span class="comment">// 在尝试期间, 还可以顺便看该节点在链表中有没有, 如果没有顺便创建出来</span></span><br><span class="line">    scanAndLockForPut(key, hash, value);</span><br><span class="line">    <span class="comment">// 执行到这里 segment 已经被成功加锁, 可以安全执行</span></span><br><span class="line">    V oldValue;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        HashEntry&lt;K,V&gt;[] tab = table;</span><br><span class="line">        <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> (tab.length - <span class="number">1</span>) &amp; hash;</span><br><span class="line">        HashEntry&lt;K,V&gt; first = entryAt(tab, index);</span><br><span class="line">        <span class="keyword">for</span> (HashEntry&lt;K,V&gt; e = first;;) &#123;</span><br><span class="line">            <span class="keyword">if</span> (e != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="comment">// 更新</span></span><br><span class="line">                K k;</span><br><span class="line">                <span class="keyword">if</span> ((k = e.key) == key ||</span><br><span class="line">                    (e.hash == hash &amp;&amp; key.equals(k))) &#123;</span><br><span class="line">                    oldValue = e.value;</span><br><span class="line">                    <span class="keyword">if</span> (!onlyIfAbsent) &#123;</span><br><span class="line">                        e.value = value;</span><br><span class="line">                        ++modCount;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                e = e.next;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// 新增</span></span><br><span class="line">                <span class="comment">// 1) 之前等待锁时, node 已经被创建, next 指向链表头</span></span><br><span class="line">                <span class="keyword">if</span> (node != <span class="literal">null</span>)</span><br><span class="line">                    node.setNext(first);</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                    <span class="comment">// 2) 创建新 node</span></span><br><span class="line">                    node = <span class="keyword">new</span> <span class="title class_">HashEntry</span>&lt;K,V&gt;(hash, key, value, first);</span><br><span class="line">                <span class="type">int</span> <span class="variable">c</span> <span class="operator">=</span> count + <span class="number">1</span>;</span><br><span class="line">                <span class="comment">// 3) 扩容</span></span><br><span class="line">                <span class="comment">// 必须 是 达到阈值 并且 HashEntry 数组还没被扩容，也就是长度依旧是旧值</span></span><br><span class="line">                <span class="keyword">if</span> (c &gt; threshold &amp;&amp; tab.length &lt; MAXIMUM_CAPACITY)</span><br><span class="line">                    rehash(node);</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                    <span class="comment">// tab已更新，即扩容已经完毕，将 node 作为链表头</span></span><br><span class="line">                    setEntryAt(tab, index, node);</span><br><span class="line">                ++modCount;</span><br><span class="line">                count = c;</span><br><span class="line">                oldValue = <span class="literal">null</span>;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        unlock();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> oldValue;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>扩容<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">rehash</span><span class="params">(HashEntry&lt;K,V&gt; node)</span> &#123;</span><br><span class="line">    <span class="comment">// 每个 Segment 对应的一个 HashEntry&lt;K,V&gt;数组</span></span><br><span class="line">    HashEntry&lt;K,V&gt;[] oldTable = table;</span><br><span class="line">    <span class="comment">// 扩容前 HashEntry&lt;K,V&gt;数组的长度</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">oldCapacity</span> <span class="operator">=</span> oldTable.length;</span><br><span class="line">    <span class="comment">// 扩容后 HashEntry&lt;K,V&gt;数组的长度 = 扩容前 HashEntry&lt;K,V&gt;数组的长度 * 2</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">newCapacity</span> <span class="operator">=</span> oldCapacity &lt;&lt; <span class="number">1</span>;</span><br><span class="line">    <span class="comment">// 扩容阈值的元素数量，HashEntry&lt;K,V&gt;数组 中元素的个数到 某个数量（低于最大数）时的数量</span></span><br><span class="line">    threshold = (<span class="type">int</span>)(newCapacity * loadFactor);</span><br><span class="line">    <span class="comment">// 初始化一个新的 HashEntry 数组，每个下标表示的 HashEntry 都为 null，后面会用到这个 null 做头插法 put 元素</span></span><br><span class="line">    HashEntry&lt;K,V&gt;[] newTable =</span><br><span class="line">        (HashEntry&lt;K,V&gt;[]) <span class="keyword">new</span> <span class="title class_">HashEntry</span>[newCapacity];</span><br><span class="line">    <span class="comment">// hash 命中 &amp; 操作要用的掩码（计算hash结果的长度）</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">sizeMask</span> <span class="operator">=</span> newCapacity - <span class="number">1</span>;</span><br><span class="line">    <span class="comment">// 遍历 扩容前 HashEntry&lt;K,V&gt;数组</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; oldCapacity ; i++) &#123;</span><br><span class="line">        HashEntry&lt;K,V&gt; e = oldTable[i];</span><br><span class="line">        <span class="keyword">if</span> (e != <span class="literal">null</span>) &#123;</span><br><span class="line">            HashEntry&lt;K,V&gt; next = e.next;</span><br><span class="line">            <span class="type">int</span> <span class="variable">idx</span> <span class="operator">=</span> e.hash &amp; sizeMask;</span><br><span class="line">            <span class="comment">// 当前 HashEntry&lt;K,V&gt;数组 下标只有一个元素，则直接赋值给 新数组对应下标</span></span><br><span class="line">            <span class="comment">// 这里对应的新的下标跟 原下标已经不一样了，因为是按照 扩容后 的长度来计算得到的</span></span><br><span class="line">            <span class="keyword">if</span> (next == <span class="literal">null</span>)   <span class="comment">//  Single node on list</span></span><br><span class="line">                newTable[idx] = e;</span><br><span class="line">            <span class="comment">// 如果链表长度 大于等于两个元素以上</span></span><br><span class="line">            <span class="keyword">else</span> &#123; <span class="comment">// Reuse consecutive sequence at same slot</span></span><br><span class="line">                HashEntry&lt;K,V&gt; lastRun = e;</span><br><span class="line">                <span class="type">int</span> <span class="variable">lastIdx</span> <span class="operator">=</span> idx;</span><br><span class="line">                <span class="comment">// 重用 链表最后面 新命中值 相同的</span></span><br><span class="line">                <span class="keyword">for</span> (HashEntry&lt;K,V&gt; last = next;</span><br><span class="line">                     last != <span class="literal">null</span>;</span><br><span class="line">                     last = last.next) &#123;</span><br><span class="line">                    <span class="type">int</span> <span class="variable">k</span> <span class="operator">=</span> last.hash &amp; sizeMask;</span><br><span class="line">                    <span class="keyword">if</span> (k != lastIdx) &#123;</span><br><span class="line">                        lastIdx = k;</span><br><span class="line">                        lastRun = last;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="comment">// 将重用的链表头结点 转移到 新的 数组中</span></span><br><span class="line">                newTable[lastIdx] = lastRun;</span><br><span class="line">                <span class="comment">// Clone remaining nodes</span></span><br><span class="line">                <span class="comment">// 链表剩下的 部分，挨个按照头插法 命中到 新的数组中 </span></span><br><span class="line">                <span class="keyword">for</span> (HashEntry&lt;K,V&gt; p = e; p != lastRun; p = p.next) &#123;</span><br><span class="line">                    <span class="type">V</span> <span class="variable">v</span> <span class="operator">=</span> p.value;</span><br><span class="line">                    <span class="type">int</span> <span class="variable">h</span> <span class="operator">=</span> p.hash;</span><br><span class="line">                    <span class="type">int</span> <span class="variable">k</span> <span class="operator">=</span> h &amp; sizeMask;</span><br><span class="line">                    HashEntry&lt;K,V&gt; n = newTable[k];</span><br><span class="line">                    newTable[k] = <span class="keyword">new</span> <span class="title class_">HashEntry</span>&lt;K,V&gt;(h, p.key, v, n);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 最后再 把要 put 的值 头插法 加入到 新的数组中</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">nodeIndex</span> <span class="operator">=</span> node.hash &amp; sizeMask; <span class="comment">// add the new node</span></span><br><span class="line">    node.setNext(newTable[nodeIndex]);</span><br><span class="line">    newTable[nodeIndex] = node;</span><br><span class="line">    <span class="comment">// 这里通过 暂时 数组操作完毕再 赋值，能够避免操作中途 被其他 线程再次重复操作</span></span><br><span class="line">    <span class="comment">// 在这之前 其他 线程无法进入</span></span><br><span class="line">    <span class="comment">// 一赋值 其他线程 就算判断 达到阈值，但newTable 的大小改变</span></span><br><span class="line">    <span class="comment">// tab.length &lt; MAXIMUM_CAPACITY 也会让 其他线程直接去 新的数组 put 操作</span></span><br><span class="line">    table = newTable;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><code>JDK8</code>抛弃了<code>Segment</code>分段锁，转而使用<code>CAS</code>+<code>synchronized</code>来保证并发安全性。<br>并且，不再单纯使用链表，<code>JDK8</code>在<code>HashEntry</code>数组中的链表长度大于<code>8</code>的时候会去转化为红黑树结构。<br>在这里，链表长度大于<code>8</code>时，才会去进一步判断阈值是否达到。<br>与<code>JDK7</code>扩容的区别在上一篇 <a href="https://blog.51cto.com/fyphome/5520040">深入浅出~HashMap的底层原理透析</a> 详细讲过了，其实与<code>ConCurrentHashMap</code>大同小异，也就是多做了<code>CAS</code>的操作、链表转化红黑树和等待的<code>put</code>会帮忙扩容。<h2 id="二、附语"><a href="#二、附语" class="headerlink" title="二、附语"></a>二、附语</h2></li></ul><p>谢谢大家，我会继续努力，只为力争创作高质量的文章，分享给各位有需要的读者。<br>你们的阅读和评论是对作者最大的支持！<br>我的技术专栏：<a href="https://github.com/fyupeng">https://github.com/fyupeng</a></p><blockquote><p>专注品质，热爱生活。<br>交流技术，寻求同志。</p></blockquote>]]></content>
    
    
      
      
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;创作宗旨：化繁为简，绝不冗余，点到为止&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;ConcurrentHashMap&amp;lt;K,V&amp;gt;&lt;/code&gt; 继承了&lt;code&gt;AbstractMap&amp;lt;K,V&amp;gt;&lt;/code&gt;，实现</summary>
      
    
    
    
    <category term="Java笔记" scheme="http://fyupeng.github.io/categories/Java%E7%AC%94%E8%AE%B0/"/>
    
    
    <category term="Java原理" scheme="http://fyupeng.github.io/tags/Java%E5%8E%9F%E7%90%86/"/>
    
  </entry>
  
  <entry>
    <title>贪心算法</title>
    <link href="http://fyupeng.github.io/2022/08/06/%E8%B4%AA%E5%BF%83%E7%AE%97%E6%B3%95/"/>
    <id>http://fyupeng.github.io/2022/08/06/%E8%B4%AA%E5%BF%83%E7%AE%97%E6%B3%95/</id>
    <published>2022-08-06T03:57:51.000Z</published>
    <updated>2022-08-06T04:00:08.680Z</updated>
    
    <content type="html"><![CDATA[<h1 id="贪心算法"><a href="#贪心算法" class="headerlink" title="贪心算法"></a>贪心算法</h1><h2 id="1-455-分发饼干"><a href="#1-455-分发饼干" class="headerlink" title="1. 455. 分发饼干"></a>1. <a href="https://leetcode.cn/problems/assign-cookies/">455. 分发饼干</a></h2><h3 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h3><p>假设你是一位很棒的家长，想要给你的孩子们一些小饼干。但是，每个孩子最多只能给一块饼干。</p><p>对每个孩子 i，都有一个胃口值 g[i]，这是能让孩子们满足胃口的饼干的最小尺寸；并且每块饼干 j，都有一个尺寸 s[j] 。如果 s[j] &gt;= g[i]，我们可以将这个饼干 j 分配给孩子 i ，这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子，并输出这个最大数值。</p><p>示例 1:</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">输入: g = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>], s = [<span class="number">1</span>,<span class="number">1</span>]</span><br><span class="line">输出: <span class="number">1</span></span><br><span class="line">解释: </span><br><span class="line">你有三个孩子和两块小饼干，<span class="number">3</span>个孩子的胃口值分别是：<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>。</span><br><span class="line">虽然你有两块小饼干，由于他们的尺寸都是<span class="number">1</span>，你只能让胃口值是<span class="number">1</span>的孩子满足。</span><br><span class="line">所以你应该输出<span class="number">1</span>。</span><br></pre></td></tr></table></figure><p>示例 2:</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">输入: g = [<span class="number">1</span>,<span class="number">2</span>], s = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>]</span><br><span class="line">输出: <span class="number">2</span></span><br><span class="line">解释: </span><br><span class="line">你有两个孩子和三块小饼干，<span class="number">2</span>个孩子的胃口值分别是<span class="number">1</span>,<span class="number">2</span>。</span><br><span class="line">你拥有的饼干数量和尺寸都足以让所有孩子满足。</span><br><span class="line">所以你应该输出<span class="number">2</span>.</span><br></pre></td></tr></table></figure><p><strong>提示：</strong></p><ul><li><code>1 &lt;= g.length &lt;= 3 * 104</code></li><li><code>0 &lt;= s.length &lt;= 3 * 104</code></li><li><code>1 &lt;= g[i], s[j] &lt;= 231 - 1</code></li></ul><h3 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">findContentChildren</span><span class="params">(<span class="type">int</span>[] g, <span class="type">int</span>[] s)</span> &#123;</span><br><span class="line">        Arrays.sort(g);</span><br><span class="line">        Arrays.sort(s);</span><br><span class="line">        <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">result</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; g.length &amp;&amp; index &lt; s.length; i++)&#123;</span><br><span class="line">            <span class="keyword">if</span>(g[i] &lt;= s[index]) &#123;</span><br><span class="line">                <span class="comment">// 吃掉</span></span><br><span class="line">                result++;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                i--;</span><br><span class="line">            &#125;</span><br><span class="line">            index++;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="2-376-摆动序列"><a href="#2-376-摆动序列" class="headerlink" title="2. 376. 摆动序列"></a>2. <a href="https://leetcode.cn/problems/wiggle-subsequence/">376. 摆动序列</a></h2><h3 id="问题-1"><a href="#问题-1" class="headerlink" title="问题"></a>问题</h3><p>如果连续数字之间的差严格地在正数和负数之间交替，则数字序列称为 摆动序列 。第一个差（如果存在的话）可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。</p><p>例如， <code>[1, 7, 4, 9, 2, 5]</code> 是一个 摆动序列 ，因为差值 (6, -3, 5, -7, 3) 是正负交替出现的。</p><p>相反，<code>[1, 4, 7, 2, 5]</code> 和 <code>[1, 7, 4, 5, 5]</code> 不是摆动序列，第一个序列是因为它的前两个差值都是正数，第二个序列是因为它的最后一个差值为零。<br>子序列 可以通过从原始序列中删除一些（也可以不删除）元素来获得，剩下的元素保持其原始顺序。</p><p>给你一个整数数组 <code>nums</code> ，返回 <code>nums</code> 中作为 摆动序列 的 最长子序列的长度 。</p><p>示例 1：</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入：nums = [<span class="number">1</span>,<span class="number">7</span>,<span class="number">4</span>,<span class="number">9</span>,<span class="number">2</span>,<span class="number">5</span>]</span><br><span class="line">输出：<span class="number">6</span></span><br><span class="line">解释：整个序列均为摆动序列，各元素之间的差值为 (<span class="number">6</span>, -<span class="number">3</span>, <span class="number">5</span>, -<span class="number">7</span>, <span class="number">3</span>) 。</span><br></pre></td></tr></table></figure><p>示例 2：</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">输入：nums = [<span class="number">1</span>,<span class="number">17</span>,<span class="number">5</span>,<span class="number">10</span>,<span class="number">13</span>,<span class="number">15</span>,<span class="number">10</span>,<span class="number">5</span>,<span class="number">16</span>,<span class="number">8</span>]</span><br><span class="line">输出：<span class="number">7</span></span><br><span class="line">解释：这个序列包含几个长度为 <span class="number">7</span> 摆动序列。</span><br><span class="line">其中一个是 [<span class="number">1</span>, <span class="number">17</span>, <span class="number">10</span>, <span class="number">13</span>, <span class="number">10</span>, <span class="number">16</span>, <span class="number">8</span>] ，各元素之间的差值为 (<span class="number">16</span>, -<span class="number">7</span>, <span class="number">3</span>, -<span class="number">3</span>, <span class="number">6</span>, -<span class="number">8</span>) 。</span><br></pre></td></tr></table></figure><p>示例 3：</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：nums = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>,<span class="number">7</span>,<span class="number">8</span>,<span class="number">9</span>]</span><br><span class="line">输出：<span class="number">2</span></span><br></pre></td></tr></table></figure><p><strong>提示：</strong></p><ul><li><code>1 &lt;= nums.length &lt;= 1000</code></li><li><code>0 &lt;= nums[i] &lt;= 1000</code></li></ul><h3 id="代码-1"><a href="#代码-1" class="headerlink" title="代码"></a>代码</h3><ul><li>贪心法</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">wiggleMaxLength</span><span class="params">(<span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">curDiff</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">preDiff</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">result</span> <span class="operator">=</span> <span class="number">1</span>; <span class="comment">// 考虑 右 边界值 特殊情况</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; nums.length - <span class="number">1</span>; i++) &#123;</span><br><span class="line">            <span class="comment">// 考虑了 左边界值 特殊情况</span></span><br><span class="line">            curDiff = nums[i] - nums[i + <span class="number">1</span>];</span><br><span class="line">            <span class="keyword">if</span>(curDiff &lt; <span class="number">0</span> &amp;&amp; preDiff &gt;= <span class="number">0</span> || curDiff &gt; <span class="number">0</span> &amp;&amp; preDiff &lt;= <span class="number">0</span>) &#123;</span><br><span class="line">                result++;</span><br><span class="line">                preDiff = curDiff;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>动态规划</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">wiggleMaxLength</span><span class="params">(<span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> nums.length;</span><br><span class="line">        <span class="keyword">if</span>(n &lt; <span class="number">2</span>) <span class="keyword">return</span> n;</span><br><span class="line">        <span class="type">int</span> <span class="variable">up</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">down</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt; n; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span>(nums[i] &lt; nums[i - <span class="number">1</span>]) &#123;</span><br><span class="line">                down = up + <span class="number">1</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span>(nums[i] &gt; nums[i - <span class="number">1</span>]) &#123;</span><br><span class="line">                up = down + <span class="number">1</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> Math.max(up, down);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="3-53-最大子数组和"><a href="#3-53-最大子数组和" class="headerlink" title="3. 53. 最大子数组和"></a>3. <a href="https://leetcode.cn/problems/maximum-subarray/">53. 最大子数组和</a></h2><h3 id="问题-2"><a href="#问题-2" class="headerlink" title="问题"></a>问题</h3><p>给你一个整数数组 nums ，请你找出一个具有最大和的连续子数组（子数组最少包含一个元素），返回其最大和。</p><p>子数组 是数组中的一个连续部分。</p><p>示例 1：</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">输入：nums = [-<span class="number">2</span>,<span class="number">1</span>,-<span class="number">3</span>,<span class="number">4</span>,-<span class="number">1</span>,<span class="number">2</span>,<span class="number">1</span>,-<span class="number">5</span>,<span class="number">4</span>]</span><br><span class="line">输出：<span class="number">6</span></span><br><span class="line">解释：连续子数组 [<span class="number">4</span>,-<span class="number">1</span>,<span class="number">2</span>,<span class="number">1</span>] 的和最大，为 <span class="number">6</span> 。</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>示例 2：</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：nums = [<span class="number">1</span>]</span><br><span class="line">输出：<span class="number">1</span></span><br></pre></td></tr></table></figure><p>示例 3：</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：nums = [<span class="number">5</span>,<span class="number">4</span>,-<span class="number">1</span>,<span class="number">7</span>,<span class="number">8</span>]</span><br><span class="line">输出：<span class="number">23</span></span><br></pre></td></tr></table></figure><p><strong>提示：</strong></p><ul><li><code>1 &lt;= nums.length &lt;= 105</code></li><li><code>-104 &lt;= nums[i] &lt;= 104</code></li></ul><h3 id="代码-2"><a href="#代码-2" class="headerlink" title="代码"></a>代码</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">maxSubArray</span><span class="params">(<span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">res</span> <span class="operator">=</span> - <span class="number">10000</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">sum</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; nums.length; i++) &#123;</span><br><span class="line">            sum += nums[i];</span><br><span class="line">            <span class="keyword">if</span>(sum &gt; res) &#123;</span><br><span class="line">                res = sum;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span>(sum &lt; <span class="number">0</span>) &#123;</span><br><span class="line">                sum = <span class="number">0</span>;</span><br><span class="line">            &#125;      </span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="4-122-买卖股票的最佳时机-II"><a href="#4-122-买卖股票的最佳时机-II" class="headerlink" title="4. 122. 买卖股票的最佳时机 II"></a>4. <a href="https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/">122. 买卖股票的最佳时机 II</a></h2><h3 id="问题-3"><a href="#问题-3" class="headerlink" title="问题"></a>问题</h3><p>给你一个整数数组 <code>prices</code> ，其中 <code>prices[i]</code> 表示某支股票第 <code>i</code> 天的价格。</p><p>在每一天，你可以决定是否购买和/或出售股票。你在任何时候 <strong>最多</strong> 只能持有 一股 股票。你也可以先购买，然后在     <strong>同一天</strong> 出售。</p><p>返回 你能获得的 <strong>最大</strong> 利润 。</p><p>示例 1：</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">输入：prices = [<span class="number">7</span>,<span class="number">1</span>,<span class="number">5</span>,<span class="number">3</span>,<span class="number">6</span>,<span class="number">4</span>]</span><br><span class="line">输出：<span class="number">7</span></span><br><span class="line">解释：在第 <span class="number">2</span> 天（股票价格 = <span class="number">1</span>）的时候买入，在第 <span class="number">3</span> 天（股票价格 = <span class="number">5</span>）的时候卖出, 这笔交易所能获得利润 = <span class="number">5</span> - <span class="number">1</span> = <span class="number">4</span> 。</span><br><span class="line">     随后，在第 <span class="number">4</span> 天（股票价格 = <span class="number">3</span>）的时候买入，在第 <span class="number">5</span> 天（股票价格 = <span class="number">6</span>）的时候卖出, 这笔交易所能获得利润 = <span class="number">6</span> - <span class="number">3</span> = <span class="number">3</span> 。</span><br><span class="line">     总利润为 <span class="number">4</span> + <span class="number">3</span> = <span class="number">7</span> 。</span><br></pre></td></tr></table></figure><p>示例 2：</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">输入：prices = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>]</span><br><span class="line">输出：<span class="number">4</span></span><br><span class="line">解释：在第 <span class="number">1</span> 天（股票价格 = <span class="number">1</span>）的时候买入，在第 <span class="number">5</span> 天 （股票价格 = <span class="number">5</span>）的时候卖出, 这笔交易所能获得利润 = <span class="number">5</span> - <span class="number">1</span> = <span class="number">4</span> 。</span><br><span class="line">     总利润为 <span class="number">4</span> 。</span><br></pre></td></tr></table></figure><p>示例 3：</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入：prices = [<span class="number">7</span>,<span class="number">6</span>,<span class="number">4</span>,<span class="number">3</span>,<span class="number">1</span>]</span><br><span class="line">输出：<span class="number">0</span></span><br><span class="line">解释：在这种情况下, 交易无法获得正利润，所以不参与交易可以获得最大利润，最大利润为 <span class="number">0</span> 。</span><br></pre></td></tr></table></figure><p><strong>提示：</strong></p><ul><li><code>1 &lt;= prices.length &lt;= 3 * 104</code></li><li><code>0 &lt;= prices[i] &lt;= 104</code></li></ul><h3 id="代码-3"><a href="#代码-3" class="headerlink" title="代码"></a>代码</h3><ul><li>直接写法</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">maxProfit</span><span class="params">(<span class="type">int</span>[] prices)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(prices.length &lt; <span class="number">2</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> <span class="variable">res</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; prices.length - <span class="number">1</span>; i++) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">pricesDiff</span> <span class="operator">=</span> prices[i + <span class="number">1</span>] - prices[i];</span><br><span class="line">            <span class="keyword">if</span>(pricesDiff &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                res += pricesDiff;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>贪心写法</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">maxProfit</span><span class="params">(<span class="type">int</span>[] prices)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(prices.length &lt; <span class="number">2</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> <span class="variable">res</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; prices.length - <span class="number">1</span>; i++) &#123;</span><br><span class="line">            res += Math.max(prices[i + <span class="number">1</span>] - prices[i], <span class="number">0</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>动态规划</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">maxProfit</span><span class="params">(<span class="type">int</span>[] prices)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> prices.length;</span><br><span class="line">        <span class="keyword">if</span>(n &lt; <span class="number">2</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span>[][] dp = <span class="keyword">new</span> <span class="title class_">int</span>[n][<span class="number">2</span>];</span><br><span class="line">        <span class="comment">// 表示 买进第 0 天的股</span></span><br><span class="line">        dp[<span class="number">0</span>][<span class="number">0</span>] -= prices[<span class="number">0</span>];</span><br><span class="line">        <span class="comment">//默认初始：dp[0][1] = 0; 表示初始收益为 0 </span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt; n; i++) &#123;</span><br><span class="line">            <span class="comment">// 股票下跌，买进</span></span><br><span class="line">            dp[i][<span class="number">0</span>] = Math.max(dp[i - <span class="number">1</span>][<span class="number">0</span>], dp[i - <span class="number">1</span>][<span class="number">1</span>] - prices[i]);</span><br><span class="line">            <span class="comment">// 股票上涨，卖出</span></span><br><span class="line">            dp[i][<span class="number">1</span>] = Math.max(dp[i - <span class="number">1</span>][<span class="number">1</span>], dp[i - <span class="number">1</span>][<span class="number">0</span>] + prices[i]);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> Math.max(dp[n - <span class="number">1</span>][<span class="number">0</span>], dp[n - <span class="number">1</span>][<span class="number">1</span>]);</span><br><span class="line">    &#125;</span><br><span class="line">&#125; </span><br></pre></td></tr></table></figure><h2 id="5-55-跳跃游戏"><a href="#5-55-跳跃游戏" class="headerlink" title="5.  55. 跳跃游戏"></a>5.  <a href="https://leetcode.cn/problems/jump-game/">55. 跳跃游戏</a></h2><h3 id="问题-4"><a href="#问题-4" class="headerlink" title="问题"></a>问题</h3><p>给定一个非负整数数组 <code>nums</code> ，你最初位于数组的 <strong>第一个下标</strong> 。</p><p>数组中的每个元素代表你在该位置可以跳跃的最大长度。</p><p>判断你是否能够到达最后一个下标。</p><p>示例 1：</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入：nums = [<span class="number">2</span>,<span class="number">3</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">4</span>]</span><br><span class="line">输出：<span class="literal">true</span></span><br><span class="line">解释：可以先跳 <span class="number">1</span> 步，从下标 <span class="number">0</span> 到达下标 <span class="number">1</span>, 然后再从下标 <span class="number">1</span> 跳 <span class="number">3</span> 步到达最后一个下标。</span><br></pre></td></tr></table></figure><p>示例 2：</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入：nums = [<span class="number">3</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">0</span>,<span class="number">4</span>]</span><br><span class="line">输出：<span class="literal">false</span></span><br><span class="line">解释：无论怎样，总会到达下标为 <span class="number">3</span> 的位置。但该下标的最大跳跃长度是 <span class="number">0</span> ， 所以永远不可能到达最后一个下标。</span><br></pre></td></tr></table></figure><p><strong>提示：</strong></p><ul><li><code>1 &lt;= nums.length &lt;= 3 * 104</code></li><li><code>0 &lt;= nums[i] &lt;= 105</code></li></ul><h3 id="代码-4"><a href="#代码-4" class="headerlink" title="代码"></a>代码</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">canJump</span><span class="params">(<span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(nums.length == <span class="number">1</span>) <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        <span class="comment">// 可到达的 最长下标</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">cover</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt;= cover; i++) &#123;</span><br><span class="line">            cover = Math.max(cover, i + nums[i]);</span><br><span class="line">            <span class="keyword">if</span>(cover &gt;= nums.length - <span class="number">1</span>) <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="6-45-跳跃游戏-II"><a href="#6-45-跳跃游戏-II" class="headerlink" title="6. 45. 跳跃游戏 II"></a>6. <a href="https://leetcode.cn/problems/jump-game-ii/">45. 跳跃游戏 II</a></h2><h3 id="问题-5"><a href="#问题-5" class="headerlink" title="问题"></a>问题</h3><p>给你一个非负整数数组 <code>nums</code> ，你最初位于数组的第一个位置。</p><p>数组中的每个元素代表你在该位置可以跳跃的最大长度。</p><p>你的目标是使用最少的跳跃次数到达数组的最后一个位置。</p><p>假设你总是可以到达数组的最后一个位置。</p><p>示例 1:</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">输入: nums = [<span class="number">2</span>,<span class="number">3</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">4</span>]</span><br><span class="line">输出: <span class="number">2</span></span><br><span class="line">解释: 跳到最后一个位置的最小跳跃数是 <span class="number">2</span>。</span><br><span class="line">     从下标为 <span class="number">0</span> 跳到下标为 <span class="number">1</span> 的位置，跳 <span class="number">1</span> 步，然后跳 <span class="number">3</span> 步到达数组的最后一个位置。</span><br></pre></td></tr></table></figure><p>示例 2:</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入: nums = [<span class="number">2</span>,<span class="number">3</span>,<span class="number">0</span>,<span class="number">1</span>,<span class="number">4</span>]</span><br><span class="line">输出: <span class="number">2</span></span><br></pre></td></tr></table></figure><p><strong>提示:</strong></p><ul><li><code>1 &lt;= nums.length &lt;= 104</code></li><li><code>0 &lt;= nums[i] &lt;= 1000</code></li></ul><h3 id="代码-5"><a href="#代码-5" class="headerlink" title="代码"></a>代码</h3><ul><li>版本一</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">jump</span><span class="params">(<span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(nums.length == <span class="number">1</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> <span class="variable">curDistance</span> <span class="operator">=</span> nums[<span class="number">0</span>];</span><br><span class="line">        <span class="type">int</span> <span class="variable">nextDistance</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">ans</span> <span class="operator">=</span> <span class="number">0</span>;;</span><br><span class="line">        <span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> nums.length;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; n; i++) &#123;</span><br><span class="line">            nextDistance = Math.max(nextDistance, nums[i] + i);</span><br><span class="line">            <span class="comment">// 如果当前 最远步长 已经达到终点下标了，</span></span><br><span class="line">            <span class="keyword">if</span>(curDistance &gt;= n - <span class="number">1</span>) &#123;</span><br><span class="line">                ans++;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">//否则 看是否已经 满足 当前下标是 最远步长</span></span><br><span class="line">            <span class="comment">// 主要是为了更新 nextDistance 为 在当前下标到 最远步长中 找到 下一个最远步长下标最远的</span></span><br><span class="line">            <span class="keyword">if</span>(i == curDistance) &#123;</span><br><span class="line">                ans++;          </span><br><span class="line">                curDistance = nextDistance;            </span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>版本二</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">jump</span><span class="params">(<span class="type">int</span>[] nums)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(nums.length == <span class="number">1</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> <span class="variable">curDistance</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">nextDistance</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">ans</span> <span class="operator">=</span> <span class="number">0</span>;;</span><br><span class="line">        <span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> nums.length;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; n - <span class="number">1</span>; i++) &#123;</span><br><span class="line">            nextDistance = Math.max(nextDistance, nums[i] + i);</span><br><span class="line">            <span class="comment">//否则 看是否已经 满足 当前下标是 最远步长</span></span><br><span class="line">            <span class="comment">// 主要是为了更新 nextDistance 为 在当前下标到 最远步长中 找到 下一个最远步长下标最远的</span></span><br><span class="line">            <span class="keyword">if</span>(i == curDistance) &#123;</span><br><span class="line">                ans++;          </span><br><span class="line">                curDistance = nextDistance;            </span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="7-1005-K-次取反后最大化的数组和"><a href="#7-1005-K-次取反后最大化的数组和" class="headerlink" title="7. 1005. K 次取反后最大化的数组和"></a>7. <a href="https://leetcode.cn/problems/maximize-sum-of-array-after-k-negations/">1005. K 次取反后最大化的数组和</a></h2><h3 id="问题-6"><a href="#问题-6" class="headerlink" title="问题"></a>问题</h3><p>给你一个整数数组 <code>nums</code> 和一个整数 <code>k</code> ，按以下方法修改该数组：</p><p>选择某个下标 i 并将 <code>nums[i]</code> 替换为 <code>-nums[i]</code> 。<br>重复这个过程恰好 <code>k</code> 次。可以多次选择同一个下标 <code>i</code> 。</p><p>以这种方式修改数组后，返回数组 可能的最大和 。</p><p>示例 1：</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">输入：nums = [<span class="number">4</span>,<span class="number">2</span>,<span class="number">3</span>], k = <span class="number">1</span></span><br><span class="line">输出：<span class="number">5</span></span><br><span class="line">解释：选择下标 <span class="number">1</span> ，nums 变为 [<span class="number">4</span>,-<span class="number">2</span>,<span class="number">3</span>] 。</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>示例 2：</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入：nums = [<span class="number">3</span>,-<span class="number">1</span>,<span class="number">0</span>,<span class="number">2</span>], k = <span class="number">3</span></span><br><span class="line">输出：<span class="number">6</span></span><br><span class="line">解释：选择下标 (<span class="number">1</span>, <span class="number">2</span>, <span class="number">2</span>) ，nums 变为 [<span class="number">3</span>,<span class="number">1</span>,<span class="number">0</span>,<span class="number">2</span>] 。</span><br></pre></td></tr></table></figure><p>示例 3：</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入：nums = [<span class="number">2</span>,-<span class="number">3</span>,-<span class="number">1</span>,<span class="number">5</span>,-<span class="number">4</span>], k = <span class="number">2</span></span><br><span class="line">输出：<span class="number">13</span></span><br><span class="line">解释：选择下标 (<span class="number">1</span>, <span class="number">4</span>) ，nums 变为 [<span class="number">2</span>,<span class="number">3</span>,-<span class="number">1</span>,<span class="number">5</span>,<span class="number">4</span>] 。</span><br></pre></td></tr></table></figure><p><strong>提示：</strong></p><ul><li><code>1 &lt;= nums.length &lt;= 104</code></li><li><code>-100 &lt;= nums[i] &lt;= 100</code></li><li><code>1 &lt;= k &lt;= 104</code></li></ul><h3 id="代码-6"><a href="#代码-6" class="headerlink" title="代码"></a>代码</h3><ul><li>绝对值排序</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">largestSumAfterKNegations</span><span class="params">(<span class="type">int</span>[] nums, <span class="type">int</span> k)</span> &#123;</span><br><span class="line">        nums = IntStream.of(nums).boxed().sorted((t1, t2) -&gt; Math.abs(t2) - Math.abs(t1)).mapToInt(Integer::intValue).toArray();</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; nums.length; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span>(nums[i] &lt; <span class="number">0</span> &amp;&amp; k &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                nums[i] = - nums[i];</span><br><span class="line">                k--;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span>(k % <span class="number">2</span> == <span class="number">1</span>) &#123;</span><br><span class="line">            nums[nums.length - <span class="number">1</span>] = - nums[nums.length - <span class="number">1</span>];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> Arrays.stream(nums).sum();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>从小到大排序</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">largestSumAfterKNegations</span><span class="params">(<span class="type">int</span>[] nums, <span class="type">int</span> k)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> nums.length;</span><br><span class="line">        <span class="keyword">if</span>(n == <span class="number">1</span>) <span class="keyword">return</span> k % <span class="number">2</span> == <span class="number">0</span> ? nums[<span class="number">0</span>] : -nums[<span class="number">0</span>];</span><br><span class="line">        <span class="type">int</span> <span class="variable">sum</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        Arrays.sort(nums);</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; k; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span>(i &lt; nums.length - <span class="number">1</span> &amp;&amp; nums[i] &lt; <span class="number">0</span>) &#123;</span><br><span class="line">                nums[i] = -nums[i];</span><br><span class="line">                <span class="keyword">if</span>(nums[i] &gt;= Math.abs(nums[i + <span class="number">1</span>])) index++;</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            nums[index] = -nums[index];</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; nums.length; i++) &#123;</span><br><span class="line">            sum += nums[i];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> sum;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="8-134-加油站"><a href="#8-134-加油站" class="headerlink" title="8. 134. 加油站"></a>8. <a href="https://leetcode.cn/problems/gas-station/">134. 加油站</a></h2><h3 id="问题-7"><a href="#问题-7" class="headerlink" title="问题"></a>问题</h3><p>在一条环路上有 <code>n</code> 个加油站，其中第 <code>i</code> 个加油站有汽油 <code>gas[i]</code> 升。</p><p>你有一辆油箱容量无限的的汽车，从第 <code>i</code> 个加油站开往第 <code>i+1</code> 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发，开始时油箱为空。</p><p>给定两个整数数组 <code>gas</code> 和 <code>cost</code> ，如果你可以绕环路行驶一周，则返回出发时加油站的编号，否则返回 <code>-1</code> 。如果存在解，则 <code>保证</code> 它是 <code>唯一</code> 的。</p><p>示例 1:</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">输入: gas = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>], cost = [<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">1</span>,<span class="number">2</span>]</span><br><span class="line">输出: <span class="number">3</span></span><br><span class="line">解释:</span><br><span class="line">从 <span class="number">3</span> 号加油站(索引为 <span class="number">3</span> 处)出发，可获得 <span class="number">4</span> 升汽油。此时油箱有 = <span class="number">0</span> + <span class="number">4</span> = <span class="number">4</span> 升汽油</span><br><span class="line">开往 <span class="number">4</span> 号加油站，此时油箱有 <span class="number">4</span> - <span class="number">1</span> + <span class="number">5</span> = <span class="number">8</span> 升汽油</span><br><span class="line">开往 <span class="number">0</span> 号加油站，此时油箱有 <span class="number">8</span> - <span class="number">2</span> + <span class="number">1</span> = <span class="number">7</span> 升汽油</span><br><span class="line">开往 <span class="number">1</span> 号加油站，此时油箱有 <span class="number">7</span> - <span class="number">3</span> + <span class="number">2</span> = <span class="number">6</span> 升汽油</span><br><span class="line">开往 <span class="number">2</span> 号加油站，此时油箱有 <span class="number">6</span> - <span class="number">4</span> + <span class="number">3</span> = <span class="number">5</span> 升汽油</span><br><span class="line">开往 <span class="number">3</span> 号加油站，你需要消耗 <span class="number">5</span> 升汽油，正好足够你返回到 <span class="number">3</span> 号加油站。</span><br><span class="line">因此，<span class="number">3</span> 可为起始索引。</span><br></pre></td></tr></table></figure><p>示例 2:</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">输入: gas = [<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>], cost = [<span class="number">3</span>,<span class="number">4</span>,<span class="number">3</span>]</span><br><span class="line">输出: -<span class="number">1</span></span><br><span class="line">解释:</span><br><span class="line">你不能从 <span class="number">0</span> 号或 <span class="number">1</span> 号加油站出发，因为没有足够的汽油可以让你行驶到下一个加油站。</span><br><span class="line">我们从 <span class="number">2</span> 号加油站出发，可以获得 <span class="number">4</span> 升汽油。 此时油箱有 = <span class="number">0</span> + <span class="number">4</span> = <span class="number">4</span> 升汽油</span><br><span class="line">开往 <span class="number">0</span> 号加油站，此时油箱有 <span class="number">4</span> - <span class="number">3</span> + <span class="number">2</span> = <span class="number">3</span> 升汽油</span><br><span class="line">开往 <span class="number">1</span> 号加油站，此时油箱有 <span class="number">3</span> - <span class="number">3</span> + <span class="number">3</span> = <span class="number">3</span> 升汽油</span><br><span class="line">你无法返回 <span class="number">2</span> 号加油站，因为返程需要消耗 <span class="number">4</span> 升汽油，但是你的油箱只有 <span class="number">3</span> 升汽油。</span><br><span class="line">因此，无论怎样，你都不可能绕环路行驶一周。</span><br></pre></td></tr></table></figure><p><strong>提示:</strong></p><ul><li><code>gas.length == n</code></li><li><code>cost.length == n</code></li><li><code>1 &lt;= n &lt;= 105</code></li><li><code>0 &lt;= gas[i], cost[i] &lt;= 104</code></li></ul><h3 id="代码-7"><a href="#代码-7" class="headerlink" title="代码"></a>代码</h3><ul><li>暴力法</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">canCompleteCircuit</span><span class="params">(<span class="type">int</span>[] gas, <span class="type">int</span>[] cost)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">curGas</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; gas.length; i++) &#123;</span><br><span class="line">            curGas = gas[i] - cost[i];</span><br><span class="line">            <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> (i + <span class="number">1</span>) % gas.length;</span><br><span class="line">            <span class="keyword">while</span>(curGas &gt;= <span class="number">0</span> &amp;&amp; index != i) &#123;</span><br><span class="line">                curGas += gas[index] - cost[index];</span><br><span class="line">                index = (index + <span class="number">1</span>) % gas.length;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span>(curGas &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> i;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>贪心算法1</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">canCompleteCircuit</span><span class="params">(<span class="type">int</span>[] gas, <span class="type">int</span>[] cost)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">curSum</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">min</span> <span class="operator">=</span> Integer.MAX_VALUE;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; gas.length; i++) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">res</span> <span class="operator">=</span> gas[i] - cost[i];</span><br><span class="line">            curSum += res;</span><br><span class="line">            <span class="keyword">if</span>(curSum &lt; min) &#123;</span><br><span class="line">                min = curSum;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span>(curSum &lt; <span class="number">0</span>) <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span>(min &gt;= <span class="number">0</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> gas.length - <span class="number">1</span>; i &gt;= <span class="number">0</span>; i--) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">res</span> <span class="operator">=</span> gas[i] - cost[i];</span><br><span class="line">            min += res;</span><br><span class="line">            <span class="keyword">if</span>(min &gt;= <span class="number">0</span>) <span class="keyword">return</span> i;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>贪心算法2</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">canCompleteCircuit</span><span class="params">(<span class="type">int</span>[] gas, <span class="type">int</span>[] cost)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">curSum</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">totalSum</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">startIndex</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; gas.length; i++) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">res</span> <span class="operator">=</span> gas[i] - cost[i];</span><br><span class="line">            curSum += res;</span><br><span class="line">            totalSum += res;</span><br><span class="line">            <span class="keyword">if</span>(curSum &lt; <span class="number">0</span>) &#123;</span><br><span class="line">                startIndex = i + <span class="number">1</span>;</span><br><span class="line">                curSum = <span class="number">0</span>;</span><br><span class="line">            &#125;     </span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span>(totalSum &lt; <span class="number">0</span>) <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">        <span class="keyword">return</span> startIndex;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="9-135-分发糖果"><a href="#9-135-分发糖果" class="headerlink" title="9. 135. 分发糖果"></a>9. <a href="https://leetcode.cn/problems/candy/">135. 分发糖果</a></h2><h3 id="问题-8"><a href="#问题-8" class="headerlink" title="问题"></a>问题</h3><p><code>n</code> 个孩子站成一排。给你一个整数数组 <code>ratings</code> 表示每个孩子的评分。</p><p>你需要按照以下要求，给这些孩子分发糖果：</p><p>每个孩子至少分配到 <code>1</code> 个糖果。<br>相邻两个孩子评分更高的孩子会获得更多的糖果。<br>请你给每个孩子分发糖果，计算并返回需要准备的 <strong>最少糖果数目</strong> 。</p><p>示例 1：</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入：ratings = [<span class="number">1</span>,<span class="number">0</span>,<span class="number">2</span>]</span><br><span class="line">输出：<span class="number">5</span></span><br><span class="line">解释：你可以分别给第一个、第二个、第三个孩子分发 <span class="number">2</span>、<span class="number">1</span>、<span class="number">2</span> 颗糖果。</span><br></pre></td></tr></table></figure><p>示例 2：</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">输入：ratings = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">2</span>]</span><br><span class="line">输出：<span class="number">4</span></span><br><span class="line">解释：你可以分别给第一个、第二个、第三个孩子分发 <span class="number">1</span>、<span class="number">2</span>、<span class="number">1</span> 颗糖果。</span><br><span class="line">第三个孩子只得到 <span class="number">1</span> 颗糖果，这满足题面中的两个条件。</span><br></pre></td></tr></table></figure><p><strong>提示：</strong></p><ul><li><code>n == ratings.length</code></li><li><code>1 &lt;= n &lt;= 2 * 104</code></li><li><code>0 &lt;= ratings[i] &lt;= 2 * 104</code></li></ul><h3 id="代码-8"><a href="#代码-8" class="headerlink" title="代码"></a>代码</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">candy</span><span class="params">(<span class="type">int</span>[] ratings)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(ratings.length == <span class="number">1</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> ratings.length;</span><br><span class="line">        <span class="type">int</span>[] candyArg = <span class="keyword">new</span> <span class="title class_">int</span>[n];</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; n; i++) &#123;</span><br><span class="line">            candyArg[i] = <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; n - <span class="number">1</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span>(ratings[i + <span class="number">1</span>] &gt; ratings[i]) candyArg[i + <span class="number">1</span>] = candyArg[i] + <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> n - <span class="number">2</span>; i &gt;= <span class="number">0</span>; i--) &#123;</span><br><span class="line">            <span class="keyword">if</span>(ratings[i] &gt; ratings[i + <span class="number">1</span>]) candyArg[i] = Math.max(candyArg[i], candyArg[i + <span class="number">1</span>] + <span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> <span class="variable">sum</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>;  i &lt; n; i++) &#123;</span><br><span class="line">            sum += candyArg[i];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> sum;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="10-860-柠檬水找零"><a href="#10-860-柠檬水找零" class="headerlink" title="10. 860. 柠檬水找零"></a>10. <a href="https://leetcode.cn/problems/lemonade-change/">860. 柠檬水找零</a></h2><h3 id="问题-9"><a href="#问题-9" class="headerlink" title="问题"></a>问题</h3><p>在柠檬水摊上，每一杯柠檬水的售价为 5 美元。顾客排队购买你的产品，（按账单 bills 支付的顺序）一次购买一杯。</p><p>每位顾客只买一杯柠檬水，然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零，也就是说净交易是每位顾客向你支付 5 美元。</p><p>注意，一开始你手头没有任何零钱。</p><p>给你一个整数数组 bills ，其中 bills[i] 是第 i 位顾客付的账。如果你能给每位顾客正确找零，返回 true ，否则返回 false 。</p><p>示例1：</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">输入：bills = [<span class="number">5</span>,<span class="number">5</span>,<span class="number">5</span>,<span class="number">10</span>,<span class="number">20</span>]</span><br><span class="line">输出：<span class="literal">true</span></span><br><span class="line">解释：</span><br><span class="line">前 <span class="number">3</span> 位顾客那里，我们按顺序收取 <span class="number">3</span> 张 <span class="number">5</span> 美元的钞票。</span><br><span class="line">第 <span class="number">4</span> 位顾客那里，我们收取一张 <span class="number">10</span> 美元的钞票，并返还 <span class="number">5</span> 美元。</span><br><span class="line">第 <span class="number">5</span> 位顾客那里，我们找还一张 <span class="number">10</span> 美元的钞票和一张 <span class="number">5</span> 美元的钞票。</span><br><span class="line">由于所有客户都得到了正确的找零，所以我们输出 <span class="literal">true</span>。</span><br></pre></td></tr></table></figure><p>示例2：</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">输入：bills = [<span class="number">5</span>,<span class="number">5</span>,<span class="number">10</span>,<span class="number">10</span>,<span class="number">20</span>]</span><br><span class="line">输出：<span class="literal">false</span></span><br><span class="line">解释：</span><br><span class="line">前 <span class="number">2</span> 位顾客那里，我们按顺序收取 <span class="number">2</span> 张 <span class="number">5</span> 美元的钞票。</span><br><span class="line">对于接下来的 <span class="number">2</span> 位顾客，我们收取一张 <span class="number">10</span> 美元的钞票，然后返还 <span class="number">5</span> 美元。</span><br><span class="line">对于最后一位顾客，我们无法退回 <span class="number">15</span> 美元，因为我们现在只有两张 <span class="number">10</span> 美元的钞票。</span><br><span class="line">由于不是每位顾客都得到了正确的找零，所以答案是 <span class="literal">false</span>。</span><br></pre></td></tr></table></figure><p><strong>提示：</strong></p><ul><li><code>1 &lt;= bills.length &lt;= 105</code></li><li><code>bills[i]</code> 不是 <code>5</code> 就是 <code>10</code> 或是 <code>20</code> </li></ul><h3 id="代码-9"><a href="#代码-9" class="headerlink" title="代码"></a>代码</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">lemonadeChange</span><span class="params">(<span class="type">int</span>[] bills)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">five</span> <span class="operator">=</span> <span class="number">0</span>, ten = <span class="number">0</span>, twenty = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; bills.length; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span>(bills[i] == <span class="number">5</span>) &#123;</span><br><span class="line">                five++;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span>(bills[i] == <span class="number">10</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span>(five &lt;= <span class="number">0</span>) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">                ten++;</span><br><span class="line">                five--;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span>(bills[i] == <span class="number">20</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span>(five &gt; <span class="number">0</span> &amp;&amp; ten &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                    five--;</span><br><span class="line">                    ten--;</span><br><span class="line">                    twenty++;</span><br><span class="line">                &#125; <span class="keyword">else</span> <span class="keyword">if</span>(five &gt;= <span class="number">3</span>) &#123;</span><br><span class="line">                    five -= <span class="number">3</span>;</span><br><span class="line">                    twenty++;</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="11-406-根据身高重建队列"><a href="#11-406-根据身高重建队列" class="headerlink" title="11. 406. 根据身高重建队列"></a>11. <a href="https://leetcode.cn/problems/queue-reconstruction-by-height/">406. 根据身高重建队列</a></h2><h3 id="问题-10"><a href="#问题-10" class="headerlink" title="问题"></a>问题</h3><p>假设有打乱顺序的一群人站成一个队列，数组 <code>people</code> 表示队列中一些人的属性（不一定按顺序）。每个 <code>people[i] = [hi, ki]</code> 表示第 <code>i</code> 个人的身高为 <code>hi</code> ，前面 <strong>正好</strong> 有 <code>ki</code> 个身高大于或等于 <code>hi</code> 的人。</p><p>请你重新构造并返回输入数组 <code>people</code> 所表示的队列。返回的队列应该格式化为数组 <code>queue</code> ，其中 <code>queue[j] = [hj, kj]</code> 是队列中第 <code>j</code> 个人的属性（<code>queue[0]</code> 是排在队列前面的人）。</p><p>示例 1：</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">输入：people = [[<span class="number">7</span>,<span class="number">0</span>],[<span class="number">4</span>,<span class="number">4</span>],[<span class="number">7</span>,<span class="number">1</span>],[<span class="number">5</span>,<span class="number">0</span>],[<span class="number">6</span>,<span class="number">1</span>],[<span class="number">5</span>,<span class="number">2</span>]]</span><br><span class="line">输出：[[<span class="number">5</span>,<span class="number">0</span>],[<span class="number">7</span>,<span class="number">0</span>],[<span class="number">5</span>,<span class="number">2</span>],[<span class="number">6</span>,<span class="number">1</span>],[<span class="number">4</span>,<span class="number">4</span>],[<span class="number">7</span>,<span class="number">1</span>]]</span><br><span class="line">解释：</span><br><span class="line">编号为 <span class="number">0</span> 的人身高为 <span class="number">5</span> ，没有身高更高或者相同的人排在他前面。</span><br><span class="line">编号为 <span class="number">1</span> 的人身高为 <span class="number">7</span> ，没有身高更高或者相同的人排在他前面。</span><br><span class="line">编号为 <span class="number">2</span> 的人身高为 <span class="number">5</span> ，有 <span class="number">2</span> 个身高更高或者相同的人排在他前面，即编号为 <span class="number">0</span> 和 <span class="number">1</span> 的人。</span><br><span class="line">编号为 <span class="number">3</span> 的人身高为 <span class="number">6</span> ，有 <span class="number">1</span> 个身高更高或者相同的人排在他前面，即编号为 <span class="number">1</span> 的人。</span><br><span class="line">编号为 <span class="number">4</span> 的人身高为 <span class="number">4</span> ，有 <span class="number">4</span> 个身高更高或者相同的人排在他前面，即编号为 <span class="number">0</span>、<span class="number">1</span>、<span class="number">2</span>、<span class="number">3</span> 的人。</span><br><span class="line">编号为 <span class="number">5</span> 的人身高为 <span class="number">7</span> ，有 <span class="number">1</span> 个身高更高或者相同的人排在他前面，即编号为 <span class="number">1</span> 的人。</span><br><span class="line">因此 [[<span class="number">5</span>,<span class="number">0</span>],[<span class="number">7</span>,<span class="number">0</span>],[<span class="number">5</span>,<span class="number">2</span>],[<span class="number">6</span>,<span class="number">1</span>],[<span class="number">4</span>,<span class="number">4</span>],[<span class="number">7</span>,<span class="number">1</span>]] 是重新构造后的队列。</span><br></pre></td></tr></table></figure><p>示例 2：</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：people = [[<span class="number">6</span>,<span class="number">0</span>],[<span class="number">5</span>,<span class="number">0</span>],[<span class="number">4</span>,<span class="number">0</span>],[<span class="number">3</span>,<span class="number">2</span>],[<span class="number">2</span>,<span class="number">2</span>],[<span class="number">1</span>,<span class="number">4</span>]]</span><br><span class="line">输出：[[<span class="number">4</span>,<span class="number">0</span>],[<span class="number">5</span>,<span class="number">0</span>],[<span class="number">2</span>,<span class="number">2</span>],[<span class="number">3</span>,<span class="number">2</span>],[<span class="number">1</span>,<span class="number">4</span>],[<span class="number">6</span>,<span class="number">0</span>]]</span><br></pre></td></tr></table></figure><p><strong>提示：</strong></p><ul><li><code>1 &lt;= people.length &lt;= 2000</code></li><li><code>0 &lt;= hi &lt;= 106</code></li><li><code>0 &lt;= ki &lt; people.length</code></li><li>题目数据确保队列可以被重建</li></ul><h3 id="代码-10"><a href="#代码-10" class="headerlink" title="代码"></a>代码</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span>[][] reconstructQueue(<span class="type">int</span>[][] people) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">length</span> <span class="operator">=</span> people.length;</span><br><span class="line">        Arrays.sort(people, <span class="keyword">new</span> <span class="title class_">Comparator</span>&lt;<span class="type">int</span>[]&gt;()&#123;</span><br><span class="line">            <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">compare</span><span class="params">(<span class="type">int</span>[] people1, <span class="type">int</span>[] people2)</span> &#123;</span><br><span class="line">                <span class="comment">// 从大到小 排序</span></span><br><span class="line">                <span class="keyword">if</span>(people2[<span class="number">0</span>] == people1[<span class="number">0</span>]) &#123;</span><br><span class="line">                    <span class="keyword">return</span> people1[<span class="number">1</span>] - people2[<span class="number">1</span>];</span><br><span class="line">                &#125;<span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="keyword">return</span> people2[<span class="number">0</span>] - people1[<span class="number">0</span>];</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; length; i++) &#123;</span><br><span class="line">            inset(people, i, people[i][<span class="number">1</span>]);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> people;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">inset</span><span class="params">(<span class="type">int</span>[][] people, <span class="type">int</span> sourceIndex,  <span class="type">int</span> destIndex)</span> &#123;</span><br><span class="line">        <span class="type">int</span>[] temp = people[sourceIndex];</span><br><span class="line">        <span class="keyword">if</span>(sourceIndex &lt;= destIndex) &#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> sourceIndex; i &lt; destIndex; i++) &#123;</span><br><span class="line">                people[i] = people[i + <span class="number">1</span>];</span><br><span class="line">            &#125;</span><br><span class="line">            people[destIndex] = temp;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> sourceIndex; i &gt; destIndex; i--) &#123;</span><br><span class="line">                people[i] = people[i - <span class="number">1</span>];</span><br><span class="line">            &#125;</span><br><span class="line">            people[destIndex] = temp;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="12-452-用最少数量的箭引爆气球"><a href="#12-452-用最少数量的箭引爆气球" class="headerlink" title="12. 452. 用最少数量的箭引爆气球"></a>12. <a href="https://leetcode.cn/problems/minimum-number-of-arrows-to-burst-balloons/">452. 用最少数量的箭引爆气球</a></h2><h3 id="问题-11"><a href="#问题-11" class="headerlink" title="问题"></a>问题</h3><p>有一些球形气球贴在一堵用 <code>XY</code> 平面表示的墙面上。墙面上的气球记录在整数数组 <code>points</code> ，其中<code>points[i] = [xstart, xend]</code> 表示水平直径在 <code>xstart</code> 和 <code>xend</code>之间的气球。你不知道气球的确切 <code>y</code> 坐标。</p><p>一支弓箭可以沿着 <code>x</code> 轴从不同点 完全垂直 地射出。在坐标 <code>x</code> 处射出一支箭，若有一个气球的直径的开始和结束坐标为 <code>xstart</code>，<code>xend</code>， 且满足  <code>xstart ≤ x ≤ xend</code>，则该气球会被 引爆 。可以射出的弓箭的数量 没有限制 。 弓箭一旦被射出之后，可以无限地前进。</p><p>给你一个数组 <code>points</code> ，返回引爆所有气球所必须射出的 最小 弓箭数 。</p><p>示例 1：</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">输入：points = [[<span class="number">10</span>,<span class="number">16</span>],[<span class="number">2</span>,<span class="number">8</span>],[<span class="number">1</span>,<span class="number">6</span>],[<span class="number">7</span>,<span class="number">12</span>]]</span><br><span class="line">输出：<span class="number">2</span></span><br><span class="line">解释：气球可以用<span class="number">2</span>支箭来爆破:</span><br><span class="line">-在x = <span class="number">6</span>处射出箭，击破气球[<span class="number">2</span>,<span class="number">8</span>]和[<span class="number">1</span>,<span class="number">6</span>]。</span><br><span class="line">-在x = <span class="number">11</span>处发射箭，击破气球[<span class="number">10</span>,<span class="number">16</span>]和[<span class="number">7</span>,<span class="number">12</span>]。</span><br></pre></td></tr></table></figure><p>示例 2：</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入：points = [[<span class="number">1</span>,<span class="number">2</span>],[<span class="number">3</span>,<span class="number">4</span>],[<span class="number">5</span>,<span class="number">6</span>],[<span class="number">7</span>,<span class="number">8</span>]]</span><br><span class="line">输出：<span class="number">4</span></span><br><span class="line">解释：每个气球需要射出一支箭，总共需要<span class="number">4</span>支箭。</span><br></pre></td></tr></table></figure><p>示例 3：</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">输入：points = [[<span class="number">1</span>,<span class="number">2</span>],[<span class="number">2</span>,<span class="number">3</span>],[<span class="number">3</span>,<span class="number">4</span>],[<span class="number">4</span>,<span class="number">5</span>]]</span><br><span class="line">输出：<span class="number">2</span></span><br><span class="line">解释：气球可以用<span class="number">2</span>支箭来爆破:</span><br><span class="line">- 在x = <span class="number">2</span>处发射箭，击破气球[<span class="number">1</span>,<span class="number">2</span>]和[<span class="number">2</span>,<span class="number">3</span>]。</span><br><span class="line">- 在x = <span class="number">4</span>处射出箭，击破气球[<span class="number">3</span>,<span class="number">4</span>]和[<span class="number">4</span>,<span class="number">5</span>]。</span><br></pre></td></tr></table></figure><p><strong>提示:</strong></p><ul><li><code>1 &lt;= points.length &lt;= 105</code></li><li><code>points[i].length == 2</code></li><li><code>-231 &lt;= xstart &lt; xend &lt;= 231 - 1</code></li></ul><h3 id="代码-11"><a href="#代码-11" class="headerlink" title="代码"></a>代码</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">findMinArrowShots</span><span class="params">(<span class="type">int</span>[][] points)</span> &#123;</span><br><span class="line">        Arrays.sort(points, (a,b) -&gt; &#123;</span><br><span class="line">            <span class="keyword">if</span>(a[<span class="number">0</span>] != b[<span class="number">0</span>]) &#123;</span><br><span class="line">                <span class="keyword">return</span> (<span class="type">long</span>)a[<span class="number">0</span>] - (<span class="type">long</span>)b[<span class="number">0</span>] &gt; <span class="number">0</span> ? <span class="number">1</span> : -<span class="number">1</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span>  (<span class="type">long</span>)a[<span class="number">0</span>] - (<span class="type">long</span>)b[<span class="number">0</span>] &gt; <span class="number">0</span> ? <span class="number">1</span> : -<span class="number">1</span>;</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt; points.length; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span>(points[i][<span class="number">0</span>] &gt; points[i - <span class="number">1</span>][<span class="number">1</span>]) &#123;</span><br><span class="line">                count++;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                points[i][<span class="number">1</span>] = Math.min(points[i][<span class="number">1</span>], points[i - <span class="number">1</span>][<span class="number">1</span>]);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> count + <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>先排序（按元素<code>0</code>号下标升序）后贪左边，因为排完后，前面的肯定包含后面的元素，再去贪右边边界，贪最小边界。</p><h2 id="13-435-无重叠区间"><a href="#13-435-无重叠区间" class="headerlink" title="13. 435. 无重叠区间"></a>13. <a href="https://leetcode.cn/problems/non-overlapping-intervals/">435. 无重叠区间</a></h2><h3 id="问题-12"><a href="#问题-12" class="headerlink" title="问题"></a>问题</h3><p>给定一个区间的集合 intervals ，其中 <code>intervals[i] = [starti, endi]</code> 。返回 需要移除区间的最小数量，使剩余区间互不重叠 。</p><p>示例 1:</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入: intervals = [[<span class="number">1</span>,<span class="number">2</span>],[<span class="number">2</span>,<span class="number">3</span>],[<span class="number">3</span>,<span class="number">4</span>],[<span class="number">1</span>,<span class="number">3</span>]]</span><br><span class="line">输出: <span class="number">1</span></span><br><span class="line">解释: 移除 [<span class="number">1</span>,<span class="number">3</span>] 后，剩下的区间没有重叠。</span><br></pre></td></tr></table></figure><p>示例 2:</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入: intervals = [ [<span class="number">1</span>,<span class="number">2</span>], [<span class="number">1</span>,<span class="number">2</span>], [<span class="number">1</span>,<span class="number">2</span>] ]</span><br><span class="line">输出: <span class="number">2</span></span><br><span class="line">解释: 你需要移除两个 [<span class="number">1</span>,<span class="number">2</span>] 来使剩下的区间没有重叠。</span><br></pre></td></tr></table></figure><p>示例 3:</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入: intervals = [ [<span class="number">1</span>,<span class="number">2</span>], [<span class="number">2</span>,<span class="number">3</span>] ]</span><br><span class="line">输出: <span class="number">0</span></span><br><span class="line">解释: 你不需要移除任何区间，因为它们已经是无重叠的了。</span><br></pre></td></tr></table></figure><p><strong>提示:</strong></p><ul><li><code>1 &lt;= intervals.length &lt;= 105</code></li><li><code>intervals[i].length == 2</code></li><li><code>-5 * 104 &lt;= starti &lt; endi &lt;= 5 * 104</code></li></ul><h1 id="代码-12"><a href="#代码-12" class="headerlink" title="代码"></a>代码</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">eraseOverlapIntervals</span><span class="params">(<span class="type">int</span>[][] intervals)</span> &#123;</span><br><span class="line">        <span class="comment">// 对右区间升序，</span></span><br><span class="line">        Arrays.sort(intervals, (a,b) -&gt; &#123;</span><br><span class="line">            <span class="keyword">return</span> a[<span class="number">1</span>] - b[<span class="number">1</span>];</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="type">int</span> <span class="variable">removeCount</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt; intervals.length; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span>(intervals[i][<span class="number">0</span>] &lt; intervals[i - <span class="number">1</span>][<span class="number">1</span>]) &#123;</span><br><span class="line">                removeCount++;</span><br><span class="line">                <span class="comment">// 更新当前状态下不重叠右区间上限</span></span><br><span class="line">                <span class="comment">// 不用理会两个区间中间可能留下的空白区域，因为已经排序</span></span><br><span class="line">                <span class="comment">// 所以后面的区间不会重叠中间那块空白区域</span></span><br><span class="line">                intervals[i][<span class="number">1</span>] = intervals[i - <span class="number">1</span>][<span class="number">1</span>];</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> removeCount;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="14-714-买卖股票的最佳时机含手续费"><a href="#14-714-买卖股票的最佳时机含手续费" class="headerlink" title="14. 714. 买卖股票的最佳时机含手续费"></a>14. <a href="https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/">714. 买卖股票的最佳时机含手续费</a></h2><h3 id="问题-13"><a href="#问题-13" class="headerlink" title="问题"></a>问题</h3><p>给定一个整数数组 <code>prices</code>，其中 <code>prices[i]</code>表示第 <code>i</code> 天的股票价格 ；整数 <code>fee</code> 代表了交易股票的手续费用。</p><p>你可以无限次地完成交易，但是你每笔交易都需要付手续费。如果你已经购买了一个股票，在卖出它之前你就不能再继续购买股票了。</p><p>返回获得利润的最大值。</p><p>注意：这里的一笔交易指买入持有并卖出股票的整个过程，每笔交易你只需要为支付一次手续费。</p><p>示例 1：</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">输入：prices = [<span class="number">1</span>, <span class="number">3</span>, <span class="number">2</span>, <span class="number">8</span>, <span class="number">4</span>, <span class="number">9</span>], fee = <span class="number">2</span></span><br><span class="line">输出：<span class="number">8</span></span><br><span class="line">解释：能够达到的最大利润:  </span><br><span class="line">在此处买入 prices[<span class="number">0</span>] = <span class="number">1</span></span><br><span class="line">在此处卖出 prices[<span class="number">3</span>] = <span class="number">8</span></span><br><span class="line">在此处买入 prices[<span class="number">4</span>] = <span class="number">4</span></span><br><span class="line">在此处卖出 prices[<span class="number">5</span>] = <span class="number">9</span></span><br><span class="line">总利润: ((<span class="number">8</span> - <span class="number">1</span>) - <span class="number">2</span>) + ((<span class="number">9</span> - <span class="number">4</span>) - <span class="number">2</span>) = <span class="number">8</span></span><br></pre></td></tr></table></figure><p>示例 2：</p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入：prices = [<span class="number">1</span>,<span class="number">3</span>,<span class="number">7</span>,<span class="number">5</span>,<span class="number">10</span>,<span class="number">3</span>], fee = <span class="number">3</span></span><br><span class="line">输出：<span class="number">6</span></span><br></pre></td></tr></table></figure><p><strong>提示：</strong></p><ul><li><code>1 &lt;= prices.length &lt;= 5 * 104</code></li><li><code>1 &lt;= prices[i] &lt; 5 * 104</code></li><li><code>0 &lt;= fee &lt; 5 * 104</code></li></ul><h3 id="代码-13"><a href="#代码-13" class="headerlink" title="代码"></a>代码</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">maxProfit</span><span class="params">(<span class="type">int</span>[] prices, <span class="type">int</span> fee)</span> &#123;</span><br><span class="line">        <span class="comment">// 买入是先 算手续费，buy 是买入+手续费</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">buy</span> <span class="operator">=</span> prices[<span class="number">0</span>] + fee;</span><br><span class="line">        <span class="type">int</span> <span class="variable">result</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i &lt; prices.length; i++) &#123;</span><br><span class="line">            <span class="comment">// buy 已经更新为能否继续收益的更高水准</span></span><br><span class="line">            <span class="comment">// 如果不能继续收益了，也就是跌股了，那就卖掉上一股，重新买入新股</span></span><br><span class="line">            <span class="comment">// 因为相比 同一股 会有更大的收益</span></span><br><span class="line">            <span class="keyword">if</span>(prices[i] + fee &lt; buy) &#123;</span><br><span class="line">                buy = prices[i] + fee;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span>(prices[i] &gt; buy) &#123;</span><br><span class="line">                result += prices[i] - buy;</span><br><span class="line">                <span class="comment">// 连续涨股就持股</span></span><br><span class="line">                buy = prices[i];</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="15-968-监控二叉树"><a href="#15-968-监控二叉树" class="headerlink" title="15. 968. 监控二叉树"></a>15. <a href="https://leetcode.cn/problems/binary-tree-cameras/">968. 监控二叉树</a></h2><h3 id="问题-14"><a href="#问题-14" class="headerlink" title="问题"></a>问题</h3><p>给定一个二叉树，我们在树的节点上安装摄像头。</p><p>节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。</p><p>计算监控树的所有节点所需的最小摄像头数量。</p><p>示例 1：</p><p><img src="https://yupeng-tuchuang.oss-cn-shenzhen.aliyuncs.com/bst_cameras_01.png" alt="img"></p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入：[<span class="number">0</span>,<span class="number">0</span>,null,<span class="number">0</span>,<span class="number">0</span>]</span><br><span class="line">输出：<span class="number">1</span></span><br><span class="line">解释：如图所示，一台摄像头足以监控所有节点。</span><br></pre></td></tr></table></figure><p>示例 2：</p><p><img src="https://yupeng-tuchuang.oss-cn-shenzhen.aliyuncs.com/bst_cameras_02.png" alt="img"></p><figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">输入：[<span class="number">0</span>,<span class="number">0</span>,null,<span class="number">0</span>,null,<span class="number">0</span>,null,null,<span class="number">0</span>]</span><br><span class="line">输出：<span class="number">2</span></span><br><span class="line">解释：需要至少两个摄像头来监视树的所有节点。 上图显示了摄像头放置的有效位置之一。</span><br></pre></td></tr></table></figure><p><strong>提示：</strong></p><ul><li>给定树的节点数的范围是 <code>[1, 1000]</code>。</li><li>每个节点的值都是 0。</li></ul><h3 id="代码-14"><a href="#代码-14" class="headerlink" title="代码"></a>代码</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">res</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">minCameraCover</span><span class="params">(TreeNode root)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(traversal(root) == <span class="number">0</span>) <span class="keyword">return</span> ++res;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 0 - 无覆盖 1 - 有摄像头 2 - 有覆盖</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="title function_">traversal</span><span class="params">(TreeNode node)</span> &#123;</span><br><span class="line">        <span class="comment">// 终止条件</span></span><br><span class="line">        <span class="keyword">if</span>(node == <span class="literal">null</span>) <span class="keyword">return</span> <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 后序递归遍历</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">left</span> <span class="operator">=</span> traversal(node.left);</span><br><span class="line">        <span class="type">int</span> <span class="variable">right</span> <span class="operator">=</span> traversal(node.right);</span><br><span class="line">        <span class="comment">// 设置叶子节点为 未覆盖，或者是 两个子节点均已覆盖。那么设置该节点为未覆盖</span></span><br><span class="line">        <span class="keyword">if</span>(left == <span class="number">2</span> &amp;&amp; right == <span class="number">2</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 存在子节点还未覆盖，该节点设置摄像头</span></span><br><span class="line">        <span class="keyword">if</span>(left == <span class="number">0</span> || right == <span class="number">0</span>) &#123;</span><br><span class="line">            res++;</span><br><span class="line">            <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 子节点有摄像头，该节点设置为覆盖</span></span><br><span class="line">        <span class="keyword">if</span>(left == <span class="number">1</span> || right == <span class="number">1</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="number">2</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 因为逻辑返回需要，虽然不会到这一步但还是要写</span></span><br><span class="line">        <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;贪心算法&quot;&gt;&lt;a href=&quot;#贪心算法&quot; class=&quot;headerlink&quot; title=&quot;贪心算法&quot;&gt;&lt;/a&gt;贪心算法&lt;/h1&gt;&lt;h2 id=&quot;1-455-分发饼干&quot;&gt;&lt;a href=&quot;#1-455-分发饼干&quot; class=&quot;headerlink&quot; titl</summary>
      
    
    
    
    <category term="leetCode算法" scheme="http://fyupeng.github.io/categories/leetCode%E7%AE%97%E6%B3%95/"/>
    
    
    <category term="贪心算法" scheme="http://fyupeng.github.io/tags/%E8%B4%AA%E5%BF%83%E7%AE%97%E6%B3%95/"/>
    
  </entry>
  
  <entry>
    <title>HashMap底层原理透析</title>
    <link href="http://fyupeng.github.io/2022/07/31/HashMap%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86%E9%80%8F%E6%9E%90/"/>
    <id>http://fyupeng.github.io/2022/07/31/HashMap%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86%E9%80%8F%E6%9E%90/</id>
    <published>2022-07-31T05:50:18.000Z</published>
    <updated>2023-01-04T03:07:14.958Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>创作宗旨：化繁为简，绝不冗余，点到为止</p></blockquote><p>直接开门见山，就事论事！</p><blockquote><p>什么是 HashMap?</p></blockquote><p><code>HashMap</code>类继承了父类<code>AbstractMap&lt;K,V&gt;</code>，实现了接口<code>Map&lt;K,V&gt;, Cloneable, Serializable</code>,<code>AbstractMap&lt;K,V&gt;</code>实现了对<code>Map</code>的基本操作，<code>Serializable</code>是序列化接口，可实现序列化，<code>Map&lt;K,V&gt;</code>接口规范了<code>k-v</code>对操作的抽象方法，<code>Cloneable</code>规范了继承了<code>Object</code>后去使用<code>clone()</code>方法前必须得实现<code>Cloneable</code>接口，否则抛出<code>CloneNotSupportedException</code>错误。</p><h2 id="一、介绍"><a href="#一、介绍" class="headerlink" title="一、介绍"></a>一、介绍</h2><h3 id="1-HashMap-的线程不安全的场景"><a href="#1-HashMap-的线程不安全的场景" class="headerlink" title="1. HashMap 的线程不安全的场景"></a>1. HashMap 的线程不安全的场景</h3><ul><li>死锁</li></ul><p>扩容产生死锁的原因关键有两点：<br>第一点：链表的头插法转移结点，<code>JDK8</code>版本就改成了尾插法；<br>第二点：多线程环境下，<code>CPU</code>的让出使得头结点的指针指向发生变化，但原线程重新占有<code>CPU</code>时仍然采取原先的做法；<br><strong>解读：</strong><code>JDK8</code>下使用尾插法，就算头结点已经转移了，但还是在头结点位置，也就是转移前后链表顺序不改变，只是多了一次重复的操作，结果不影响。<br>我不会用篇幅很长而做这么点事情，没必要，很简单的事 5 幅图就可以啦。<br>由于<code>HashMap</code>是<code>数组</code>+<code>链表</code>的数据结构，旧桶是指数组下标所在位置。<br><code>线程 1</code> 扩容操作<br><img src="https://yupeng-tuchuang.oss-cn-shenzhen.aliyuncs.com/1658888533494624.png" alt="image.png"></p><p><code>线程 1</code> 转移结点<code>A</code><br><img src="https://yupeng-tuchuang.oss-cn-shenzhen.aliyuncs.com/1658888733985797.png" alt="image.png"><br><code>线程1</code>结点转移完毕<br><img src="https://yupeng-tuchuang.oss-cn-shenzhen.aliyuncs.com/1658888829949728.png" alt="image.png"><br><code>线程2</code>开始转移结点<code>A</code><br>因为是<code>线程2</code>首先要扩容的，不过<code>CPU</code>时间却让给了<code>线程2</code>,<code>线程2</code>也发觉需要扩容，等扩容完毕后再把<code>CPU</code>还给<code>线程1</code><br><img src="https://yupeng-tuchuang.oss-cn-shenzhen.aliyuncs.com/1658888880999037.png" alt="image.png"></p><p><code>线程2</code>转移结点<code>A</code>结束，环形链产生，也就是产生死锁。<br><img src="https://yupeng-tuchuang.oss-cn-shenzhen.aliyuncs.com/1658889083366975.png" alt="image.png"></p><p>所以原因的<code>头插法</code>得以佐证，<code>CPU</code>的轮询切换也演绎的很到位。<br>线程不安全讲完，下面就将原理，去潜移默化它。</p><h3 id="2-HashMap-实现哈希命中均匀"><a href="#2-HashMap-实现哈希命中均匀" class="headerlink" title="2. HashMap 实现哈希命中均匀"></a>2. HashMap 实现哈希命中均匀</h3><p>哈希也就是<code>k</code>的<code>hashcode</code>计算后得到的下标，而<code>hashcode</code>是一个<code>32</code>位的<code>int</code>类型，要让分布均匀，即让不同的k不能出现有相同的效果，也就是得有唯一性，就要完全去利用高<code>16</code>位和低<code>16</code>位，而如果直接用<code>32</code>位去取模运算，高<code>16</code>就可能会用不到，导致<code>hashcode</code>高<code>16</code>位不同的<code>k</code>依旧命中同一个桶位。<br>所以JDK8采取的做法是对高<code>16</code>位与低<code>16</code>位进行异或运算，这样就变相保留了高位和低位的所有特征了。<br>哈希均匀，<code>JDK8</code>之后</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">return</span> (key == <span class="literal">null</span>) ? h = key.hashCode() ^ (h &gt;&gt;&gt; <span class="number">16</span>); </span><br></pre></td></tr></table></figure><p>取模运算，<code>JDK7</code>之前</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">h &amp; (length - <span class="number">1</span>)</span><br></pre></td></tr></table></figure><h3 id="3-HashMap-参数以及扩容机制"><a href="#3-HashMap-参数以及扩容机制" class="headerlink" title="3. HashMap 参数以及扩容机制"></a>3. HashMap 参数以及扩容机制</h3><p>初始容量<code>16</code>，达到<code>阈值</code>扩容，<code>阈值</code>等于<code>最大容量</code> * <code>负载因子</code>，扩容每次<code>2</code>倍，总是<code>2</code>的<code>n</code>次方。</p><ul><li>扩容机制<br>使用一个容量更大的数组来代替已有的容量小的数组，<code>transfer()</code>方法将原有<code>Entry</code>数组元素拷贝到新的<code>Entry</code>数组里，<code>Java1.7</code>重新计算每个元素在数组中的位置。<br>而<code>Java1.8</code>它不需要重新计算每个元素，它会先比较元素的<code>hashcode &amp; 原来数组的大小</code>的结果，如果为<code>0</code>，就以元素的原下标加入到新数组中，如果为<code>1</code>，就在原下标加上原数组的大小加入到新数组中，这样这两种情况就命中到新数组的<code>前一半</code>和<code>后一半</code>了，也就是将原本全在原数组大小的所有元素又均匀分布一些元素到<code>扩容</code>未使用的下标中。<br>那么就解释一下，假设加入的元素是随机的，也就是所有情况的<code>hash</code>值是均等出现的，而<code>&amp;</code>原数组大小可以这样比如，有<code>64</code>个元素均等的从<code>0</code>到<code>63</code>，那么<code>0</code>到<code>15</code>分配给了扩容后数组大小为<code>32</code>的左半部分，<code>16-31</code>分配给了右半部分，<code>32-48</code>分配给了左半部分，<code>48-63</code>分配给了右半部分，就比较均匀分散在了两侧，而且机会均等，很巧妙！</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 分布到左侧</span></span><br><span class="line"><span class="keyword">if</span> ((e.hash &amp; oldCap) == <span class="number">0</span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> (loTail == <span class="literal">null</span>)</span><br><span class="line">        loHead = e;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        loTail.next = e;</span><br><span class="line">    loTail = e;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 分布到右侧</span></span><br><span class="line"><span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (hiTail == <span class="literal">null</span>)</span><br><span class="line"><span class="comment">// 初始化</span></span><br><span class="line">        hiHead = e;</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line"><span class="comment">// 尾插法</span></span><br><span class="line">        hiTail.next = e;</span><br><span class="line">    <span class="comment">// 自增，下次尾插法必要的变量</span></span><br><span class="line">    hiTail = e;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="4-HashMap-的-get-和-put-操作"><a href="#4-HashMap-的-get-和-put-操作" class="headerlink" title="4. HashMap 的 get 和 put 操作"></a>4. HashMap 的 get 和 put 操作</h3><ul><li>get</li></ul><blockquote><p>让我们见识下此时无图胜有图的效果吧！</p></blockquote><p><code>get</code>由源码解读是先获取<code>key</code>的<code>hashCode</code>，然后定位到对应的数组位置，再去遍历该元素处的链表<br>在这里才讲<code>equals</code>我觉得是最合适的，可以区分它与<code>hashCode</code>的区别，如果<code>hashCode</code>相同的两个元素在同一个数组下标，<code>equals</code>就开始发挥作用了，<br>不得不说，当然别说我是话痨，我觉得<code>HashMap</code>加入这两个的初衷是如果在一个庞大的<code>Map</code>中，要去<code>get</code>某一个<code>key</code>的<code>value</code>，有一种最直接的做法，就是直接整个<code>Map</code>去<code>equals</code>比较，这没错，而且还是有用的，但加入<code>hashCode</code>之后，<code>hashCode</code>能够给<code>equals</code>先筛选一番，因为不同的<code>key</code>，它的<code>hash</code>值一般是不同的，理解这里的一般，所以<code>hash</code>值不同的<code>equals</code>肯定就不同，就没必要再去对不同下标元素来<code>equals</code>比较了，好理解吧！<br>由于<code>HashMap</code>中新加入了红黑树，就多了一种<code>get</code>到<code>key</code>的方法，是一种二分法，对于大量数据的链表是不推荐的，所以采用红黑树（总是平衡的二叉树）更加地高效！</p><ul><li>put</li></ul><p><code>put</code>的实现主要针对两种<code>key</code>，一种是不存在的<code>key</code>，一种是已存在的<code>key</code><br><strong>局部源码</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> V <span class="title function_">put</span><span class="params">(K key, V value)</span> &#123;</span><br><span class="line">    <span class="comment">//采用hash(key)来计算key的hashCode值。</span></span><br><span class="line">    <span class="keyword">return</span> putVal(hash(key), key, value, <span class="literal">false</span>, <span class="literal">true</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="title function_">hash</span><span class="params">(Object key)</span> &#123;</span><br><span class="line">    <span class="type">int</span> h;</span><br><span class="line">    <span class="comment">//当key等于null的时候，不走hashCode()方法</span></span><br><span class="line">    <span class="keyword">return</span> (key == <span class="literal">null</span>) ? <span class="number">0</span> : (h = key.hashCode()) ^ (h &gt;&gt;&gt; <span class="number">16</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果为前者，而且<code>key</code>值为<code>null</code>，就在数组下标为<code>0</code>的链表中去寻找<code>key</code>为<code>null</code>的值，当然找不到，就创建<code>key</code>为<code>null</code>的结点。<br>如果为前者，而且<code>key</code>值不为<code>null</code>，就调用该元素hash计算具体的下标，在该下标的链表中寻找，当然也寻找不到，就尾插法加入该<code>k-v</code>结点。<br>如果为后者，即找到了，直接赋值就可以了，不需要考虑是否为<code>null</code>。</p><p>这里要分开来<code>put</code>是因为，在<code>HashTable</code>上，是不允许有<code>key</code>为<code>null</code>的情况出现，因为当<code>key</code>为<code>null</code>时，<code>key.hashCode()</code>会报空指针异常。<br>所以在<code>HashMap</code>做了特别的处理，对<code>key</code>为<code>null</code>做了单独处理，这样就解决了<code>key</code>为<code>null</code>的情况。<br>还有一点，<code>HashMap</code>的<code>value</code>如果设置为<code>null</code>，虽然你<code>put</code>了，但再去取<code>key</code>的时候，依旧是不存在！</p><h2 id="二、附语"><a href="#二、附语" class="headerlink" title="二、附语"></a>二、附语</h2><p>不足之处请大佬予以佐证，不希望哪个地方出现的错误误导同行，结果闹出笑话来，总之你们的阅读和评论是对作者最大的支持！<br>谢谢大家，我会继续努力，只为力争创作高质量的文章，分享给各位有需要的读者。<br>我的技术专栏：<a href="https://github.com/fyupeng">https://github.com/fyupeng</a></p><blockquote><p>专注品质，热爱生活。<br>交流技术，寻求同志。</p></blockquote>]]></content>
    
    
      
      
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;创作宗旨：化繁为简，绝不冗余，点到为止&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;直接开门见山，就事论事！&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;什么是 HashMap?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;HashMap&lt;/c</summary>
      
    
    
    
    <category term="Java笔记" scheme="http://fyupeng.github.io/categories/Java%E7%AC%94%E8%AE%B0/"/>
    
    
    <category term="Java原理" scheme="http://fyupeng.github.io/tags/Java%E5%8E%9F%E7%90%86/"/>
    
  </entry>
  
</feed>
