<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>根雷尚</title>
  
  <subtitle>一个程序员的点滴感悟</subtitle>
  <link href="https://www.genlei.cn/atom.xml" rel="self"/>
  
  <link href="https://www.genlei.cn/"/>
  <updated>2024-04-03T09:05:49.445Z</updated>
  <id>https://www.genlei.cn/</id>
  
  <author>
    <name>竣峰</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>MySQL with 语句的使用</title>
    <link href="https://www.genlei.cn/2024/03/clujl4u5g004x88ln3r658ksw/"/>
    <id>https://www.genlei.cn/2024/03/clujl4u5g004x88ln3r658ksw/</id>
    <published>2024-03-28T11:11:42.000Z</published>
    <updated>2024-04-03T09:05:49.445Z</updated>
    
    <content type="html"><![CDATA[<h2 id="插入"><a href="#插入" class="headerlink" title="插入"></a>插入</h2><p>需求：给年龄小于18岁的用户加上学生角色，实现SQL如下</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></pre></td><td class="code"><pre><span class="line">insert into s_user_role (user_id,role_id)</span><br><span class="line">with a as (select * from s_user u where u.age&lt;18 ),</span><br><span class="line">b as (select * from s_role sr where sr.role_name=&#x27;学生&#x27;)</span><br><span class="line">select user_id,role_id from a,b</span><br></pre></td></tr></table></figure><h2 id="更新"><a href="#更新" class="headerlink" title="更新"></a>更新</h2><p>需求：给管理员这个角色的用户年龄加1 </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">with a as (select u.user_id,u.username,u.age from s_user u,s_user_role sur ,s_role sr where</span><br><span class="line">u.user_id=sur.user_id and sur.role_id=sr.role_id and sr.role_name=&#x27;管理员&#x27;</span><br><span class="line">)</span><br><span class="line">update s_user set age = age+1 where user_id in (select user_id from a)</span><br><span class="line"></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;需求：给年龄小于18岁的用户加上学生角色，实现SQL如下&lt;/p&gt;
&lt;figure class=&quot;highlight plaintext&quot;&gt;&lt;</summary>
      
    
    
    
    <category term="MySQL" scheme="https://www.genlei.cn/categories/MySQL/"/>
    
    
    <category term="MySQL" scheme="https://www.genlei.cn/tags/MySQL/"/>
    
  </entry>
  
  <entry>
    <title>MySQL 统计记录补全</title>
    <link href="https://www.genlei.cn/2023/08/2023-mysql-stat-patch/"/>
    <id>https://www.genlei.cn/2023/08/2023-mysql-stat-patch/</id>
    <published>2023-08-06T03:19:33.000Z</published>
    <updated>2024-04-03T09:05:49.445Z</updated>
    
    <content type="html"><![CDATA[<h2 id="日期补全"><a href="#日期补全" class="headerlink" title="日期补全"></a>日期补全</h2><p>在数据统计时，有时候会出现某天没有数据的情况，比如某天某类产品销量为0，这样就没有真实订单。我们统计的时候就可能出现下面的数据：</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">    dt     | val</span><br><span class="line">----------------</span><br><span class="line">2023-05-06 | 12</span><br><span class="line">2023-05-08 | 80</span><br><span class="line">2023-05-10 | 110</span><br></pre></td></tr></table></figure><p>我们看到在上面的数据里，5月7日和5月9日的数据是缺失的。业务上往往要求，我们要把缺失的补0.也就是需要得到下面的结果：</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></pre></td><td class="code"><pre><span class="line">    dt     | val</span><br><span class="line">----------------</span><br><span class="line">2023-05-06 | 12</span><br><span class="line">2023-05-07 | 0</span><br><span class="line">2023-05-08 | 80</span><br><span class="line">2023-05-09 | 0</span><br><span class="line">2023-05-10 | 110</span><br></pre></td></tr></table></figure><p>把5月7日和5月9日的数据补上，那么怎么实现呢？先看完整的SQL：</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></pre></td><td class="code"><pre><span class="line">with a as (select &#x27;2023-05-06&#x27; as dt, 12 as val union select &#x27;2023-05-10&#x27;,110 union select &#x27;2023-05-08&#x27;,80),</span><br><span class="line">b as (select max(dt) max_dt,min(dt) min_dt from a),</span><br><span class="line">z as (select DATE(DATE_SUB(CURDATE(),INTERVAL rn day)) as dt from</span><br><span class="line">  (SELECT ROW_NUMBER() over() as rn</span><br><span class="line">    FROM </span><br><span class="line">    (SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) a</span><br><span class="line">    ,(SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) b</span><br><span class="line">    ,(SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) c</span><br><span class="line">  ) t </span><br><span class="line">),</span><br><span class="line">t as (select z.* from z,b where dt&gt;=b.min_dt and dt &lt;= b.max_dt ) </span><br><span class="line">select t.dt,ifnull(a.val,0) as val from t left join a on t.dt = a.dt order by t.dt;</span><br></pre></td></tr></table></figure><p>看到上面的SQL 是不是有一种要放弃的感觉。太长了，确实太长了。我们来解析一下 都是干嘛的。我们这里用的是MySQL with 的语法，这个语法的好处就是可以把SQL写的贼长。然后还能年的懂。</p><p>先看：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">with a as (select &#x27;2023-05-06&#x27; as dt, 12 as val union select &#x27;2023-05-10&#x27;,110 union select &#x27;2023-05-08&#x27;,80)</span><br></pre></td></tr></table></figure><p>这一段就是我们造三行数据，实际业务中应该用真实的业务表数据替换。接下来</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">b as (select max(dt) max_dt,min(dt) min_dt from a),</span><br></pre></td></tr></table></figure><p>这个就是找到业务里最大的日期，和最小的日期。以方便后面的补全。然后就是特别长的 z</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></pre></td><td class="code"><pre><span class="line">z as (select DATE(DATE_SUB(CURDATE(),INTERVAL rn day)) as dt from</span><br><span class="line">  (SELECT ROW_NUMBER() over() as rn</span><br><span class="line">    FROM </span><br><span class="line">    (SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) a</span><br><span class="line">    ,(SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) b</span><br><span class="line">    ,(SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) c</span><br><span class="line">  ) t </span><br><span class="line">),</span><br></pre></td></tr></table></figure><p>这一段就是 获取最近1000天的日期。 这里用三个 select 0 一直 union 到9的表，三个表每个表有10个数字。10<em>10</em>10 就等于1000. 然后通过 ROW_NUMBER() over() 获得行号，也就是1 至 1000 。再通过 DATE(DATE_SUB(CURDATE(),INTERVAL rn day)) as dt 转成日期。因为我们不需要这么的日期，所以需要限定一下日期的数据，也就是</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">t as (select z.* from z,b where dt&gt;=b.min_dt and dt &lt;= b.max_dt ) </span><br></pre></td></tr></table></figure><p>到了这里 就只会返回 最小日期和最大日期之间的日期了。然后 做一下表关联和排序。也就是</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">select t.dt,ifnull(a.val,0) as val from t left join a on t.dt = a.dt order by t.dt;</span><br></pre></td></tr></table></figure><p>这样我们就得到了想要的，日期补全后的数据了。</p><h3 id="长度优化"><a href="#长度优化" class="headerlink" title="长度优化"></a>长度优化</h3><p>看到这里，有的朋友要说了，这个SQL也太长了，能不能精简一点。我们看上面的 1 到 1000 这一步，确实太长了。我们可以用视图来优化一下。我们先创建一个视图：</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">create view vn as SELECT ROW_NUMBER() over() as rn</span><br><span class="line">    FROM </span><br><span class="line">    (SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) a</span><br><span class="line">    ,(SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) b</span><br><span class="line">    ,(SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) c</span><br></pre></td></tr></table></figure><p>这样一个 1 到 1000 的视图就创建好了。前面的SQL 也可以简化为：</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">with a as (select &#x27;2023-05-06&#x27; as dt, 12 as val union select &#x27;2023-05-10&#x27;,110 union select &#x27;2023-05-08&#x27;,80),</span><br><span class="line">b as (select max(dt) max_dt,min(dt) min_dt from a),</span><br><span class="line">z as (select DATE(DATE_SUB(CURDATE(),INTERVAL rn day)) as dt from vn),</span><br><span class="line">t as (select z.* from z,b where dt&gt;=b.min_dt and dt &lt;= b.max_dt ) </span><br><span class="line">select t.dt,ifnull(a.val,0) from t left join a on t.dt = a.dt order by t.dt;</span><br></pre></td></tr></table></figure><h2 id="月份数据补全"><a href="#月份数据补全" class="headerlink" title="月份数据补全"></a>月份数据补全</h2><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></pre></td><td class="code"><pre><span class="line">    dt     | val</span><br><span class="line">----------------</span><br><span class="line">2023-04 | 12</span><br><span class="line">2023-06 | 110</span><br><span class="line">2023-07 | 80</span><br></pre></td></tr></table></figure><p>我们可以通过下面的SQL来对期进行补全：</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">with a as (select &#x27;2023-04&#x27; as dt, 12 as val union select &#x27;2023-06&#x27;,110 union select &#x27;2023-07&#x27;,80),</span><br><span class="line">b as (select max(dt) max_dt,min(dt) min_dt from a),</span><br><span class="line">z as (select date_format(DATE_SUB(CURDATE(),INTERVAL rn month),&#x27;%Y-%m&#x27;) as dt from vn),</span><br><span class="line">t as (select z.* from z,b where dt&gt;=b.min_dt and dt &lt;= b.max_dt ) </span><br><span class="line">select t.dt,ifnull(a.val,0) from t left join a on t.dt = a.dt order by t.dt;</span><br></pre></td></tr></table></figure><p>运行上面的SQL，得到：</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></pre></td><td class="code"><pre><span class="line">    dt     | val</span><br><span class="line">----------------</span><br><span class="line">2023-04 | 12</span><br><span class="line">2023-05 | 0</span><br><span class="line">2023-06 | 110</span><br><span class="line">2023-07 | 80</span><br></pre></td></tr></table></figure><p>对于 月份的处理，我们用到了 date_format(DATE_SUB(CURDATE(),INTERVAL rn month),’%Y-%m’) 来把月份变成 2023-05 这样的一个格式。<br>这个方法，各位觉得怎么样？欢迎留言讨论</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;在数据统计时，有时候会出现某天没有数据的情况，比如某天某类产品销量为0，这样就没有真实订单。我们统计的时候就可能出现下面的数</summary>
      
    
    
    
    <category term="MySQL" scheme="https://www.genlei.cn/categories/MySQL/"/>
    
    
    <category term="MySQL" scheme="https://www.genlei.cn/tags/MySQL/"/>
    
    <category term="统计" scheme="https://www.genlei.cn/tags/%E7%BB%9F%E8%AE%A1/"/>
    
  </entry>
  
  <entry>
    <title>SQL Server 事务引起的表锁问题</title>
    <link href="https://www.genlei.cn/2023/07/sql-server-is_read_committed_snapshot_on/"/>
    <id>https://www.genlei.cn/2023/07/sql-server-is_read_committed_snapshot_on/</id>
    <published>2023-07-04T04:19:33.000Z</published>
    <updated>2024-04-03T09:05:49.445Z</updated>
    
    <content type="html"><![CDATA[<p>最近在查一个SQL Server 事务引起的表锁问题，折腾好久才发现要开启 快照读取模式。</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></pre></td><td class="code"><pre><span class="line">-- 查询数据库是否开启了 快照读取模式</span><br><span class="line">select name, is_read_committed_snapshot_on from sys.databases</span><br><span class="line"></span><br><span class="line">-- 打开 快照读取模式 </span><br><span class="line">ALTER DATABASE my_db SET READ_COMMITTED_SNAPSHOT ON</span><br><span class="line"></span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;最近在查一个SQL Server 事务引起的表锁问题，折腾好久才发现要开启 快照读取模式。&lt;/p&gt;
&lt;figure class=&quot;highlight plaintext&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;lin</summary>
      
    
    
    
    <category term="SQLServer" scheme="https://www.genlei.cn/categories/SQLServer/"/>
    
    
    <category term="SQLServer" scheme="https://www.genlei.cn/tags/SQLServer/"/>
    
    <category term="事务" scheme="https://www.genlei.cn/tags/%E4%BA%8B%E5%8A%A1/"/>
    
  </entry>
  
  <entry>
    <title>MySQL查询最近1000天的日期</title>
    <link href="https://www.genlei.cn/2023/06/mysql-latest-1000/"/>
    <id>https://www.genlei.cn/2023/06/mysql-latest-1000/</id>
    <published>2023-06-27T13:19:33.000Z</published>
    <updated>2024-04-03T09:05:49.445Z</updated>
    
    <content type="html"><![CDATA[<p>直接看SQL</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></pre></td><td class="code"><pre><span class="line">select DATE(DATE_SUB(CURDATE(),INTERVAL rn day)) as date from</span><br><span class="line">(SELECT ROW_NUMBER() over() as rn</span><br><span class="line"> FROM </span><br><span class="line">(SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) a</span><br><span class="line">,(SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) b</span><br><span class="line">,(SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) c</span><br><span class="line">) t</span><br><span class="line"></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">2023-06-26</span><br><span class="line">2023-06-25</span><br><span class="line">2023-06-24</span><br><span class="line">2023-06-23</span><br><span class="line">...省略N行</span><br><span class="line">2020-10-03</span><br><span class="line">2020-10-02</span><br><span class="line">2020-10-01</span><br><span class="line">2020-09-30</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;直接看SQL&lt;/p&gt;
&lt;figure class=&quot;highlight plaintext&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/sp</summary>
      
    
    
    
    <category term="MySQL" scheme="https://www.genlei.cn/categories/MySQL/"/>
    
    
  </entry>
  
  <entry>
    <title>Nutuml 测试</title>
    <link href="https://www.genlei.cn/2023/06/test/"/>
    <id>https://www.genlei.cn/2023/06/test/</id>
    <published>2023-06-22T13:19:33.000Z</published>
    <updated>2024-04-03T09:05:49.445Z</updated>
    
    <content type="html"><![CDATA[<p>Nutuml 测试</p><h2 id="时序图"><a href="#时序图" class="headerlink" title="时序图"></a>时序图</h2><pre class="nutuml">a -> b : hahahb --> a: ooo</pre><h2 id="思维导图"><a href="#思维导图" class="headerlink" title="思维导图"></a>思维导图</h2><pre class="nutuml"># root## first## second</pre>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Nutuml 测试&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;pre class=&quot;nutuml&quot;&gt;a -&gt; b : hahah
b --&gt; a: ooo&lt;/pre</summary>
      
    
    
    
    <category term="Java" scheme="https://www.genlei.cn/categories/Java/"/>
    
    
  </entry>
  
  <entry>
    <title>MySQL 8.0 利用窗口函数计算同比环比</title>
    <link href="https://www.genlei.cn/2023/06/mysql-window-function-yy-mm/"/>
    <id>https://www.genlei.cn/2023/06/mysql-window-function-yy-mm/</id>
    <published>2023-06-17T04:19:33.000Z</published>
    <updated>2024-04-03T09:05:49.445Z</updated>
    
    <content type="html"><![CDATA[<p>我们在业务中常常需要统计这个月销售量多少，同比增加多少，环比增加多少。这篇博文我们就看看如何利用窗口函数实现同比及环比的计算。<br>用到的关键字包括：lag, window</p><h2 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h2><p>首先我们得有MySQL 8.0及以上版本, 然后我们准备一张统计表。</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></pre></td><td class="code"><pre><span class="line">CREATE TABLE `my_stat` (</span><br><span class="line">  `month` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,</span><br><span class="line">  `profit` decimal(10,2) DEFAULT NULL</span><br><span class="line">) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;</span><br></pre></td></tr></table></figure><p>表里的内容如下：</p><table><thead><tr><th>month</th><th>profit</th></tr></thead><tbody><tr><td>2022-05</td><td>10.00</td></tr><tr><td>2022-06</td><td>12.00</td></tr><tr><td>2022-07</td><td>6.00</td></tr><tr><td>2022-08</td><td>30.00</td></tr><tr><td>2022-09</td><td>20.00</td></tr><tr><td>2022-10</td><td>8.00</td></tr><tr><td>2022-11</td><td>2.00</td></tr><tr><td>2022-12</td><td>4.00</td></tr><tr><td>2023-01</td><td>14.00</td></tr><tr><td>2023-02</td><td>10.00</td></tr><tr><td>2023-03</td><td>8.00</td></tr><tr><td>2023-04</td><td>9.00</td></tr><tr><td>2023-05</td><td>7.00</td></tr><tr><td>2023-06</td><td>10.00</td></tr><tr><td>2023-07</td><td>15.00</td></tr></tbody></table><p>数据 插入的SQL</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><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">INSERT INTO `my_stat` (`month`, `profit`)</span><br><span class="line">VALUES</span><br><span class="line">  (&#x27;2022-05&#x27;, 10.00),</span><br><span class="line">  (&#x27;2022-06&#x27;, 12.00),</span><br><span class="line">  (&#x27;2022-07&#x27;, 6.00),</span><br><span class="line">  (&#x27;2022-08&#x27;, 30.00),</span><br><span class="line">  (&#x27;2022-09&#x27;, 20.00),</span><br><span class="line">  (&#x27;2022-10&#x27;, 8.00),</span><br><span class="line">  (&#x27;2022-11&#x27;, 2.00),</span><br><span class="line">  (&#x27;2022-12&#x27;, 4.00),</span><br><span class="line">  (&#x27;2023-01&#x27;, 14.00),</span><br><span class="line">  (&#x27;2023-02&#x27;, 10.00),</span><br><span class="line">  (&#x27;2023-03&#x27;, 8.00),</span><br><span class="line">  (&#x27;2023-04&#x27;, 9.00),</span><br><span class="line">  (&#x27;2023-05&#x27;, 7.00),</span><br><span class="line">  (&#x27;2023-06&#x27;, 10.00),</span><br><span class="line">  (&#x27;2023-07&#x27;, 15.00);</span><br><span class="line"></span><br></pre></td></tr></table></figure><h2 id="环比计算"><a href="#环比计算" class="headerlink" title="环比计算"></a>环比计算</h2><p>先看最终SQL及输出结果</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></pre></td><td class="code"><pre><span class="line">select `month`,profit,</span><br><span class="line"> lag(profit) OVER w AS 上月,</span><br><span class="line"> (profit - lag(profit) OVER w) as 环比,</span><br><span class="line"> 100*(profit - lag(profit) OVER w)/lag(profit) OVER w as 环比比例</span><br><span class="line"> from my_stat</span><br><span class="line"> WINDOW w AS (ORDER BY `month`);</span><br></pre></td></tr></table></figure><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><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">month|profit|上月|环比|环比比例</span><br><span class="line">--|--|--|--|--</span><br><span class="line">2022-05|10.00|NULL|NULL|NULL</span><br><span class="line">2022-06|12.00|10.00|2.00|20.000000</span><br><span class="line">2022-07|6.00|12.00|-6.00|-50.000000</span><br><span class="line">2022-08|30.00|6.00|24.00|400.000000</span><br><span class="line">2022-09|20.00|30.00|-10.00|-33.333333</span><br><span class="line">2022-10|8.00|20.00|-12.00|-60.000000</span><br><span class="line">2022-11|2.00|8.00|-6.00|-75.000000</span><br><span class="line">2022-12|4.00|2.00|2.00|100.000000</span><br><span class="line">2023-01|14.00|4.00|10.00|250.000000</span><br><span class="line">2023-02|10.00|14.00|-4.00|-28.571429</span><br><span class="line">2023-03|8.00|10.00|-2.00|-20.000000</span><br><span class="line">2023-04|9.00|8.00|1.00|12.500000</span><br><span class="line">2023-05|7.00|9.00|-2.00|-22.222222</span><br><span class="line">2023-06|10.00|7.00|3.00|42.857143</span><br><span class="line">2023-07|15.00|10.00|5.00|50.000000</span><br></pre></td></tr></table></figure><h3 id="lag"><a href="#lag" class="headerlink" title="lag"></a>lag</h3><p>lag 函数的完整定义如下：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">LAG(expr [, N[, default]]) [null_treatment] over_clause</span><br></pre></td></tr></table></figure><ul><li><p>参数一 expr（表达式）是必须的，这里可以是字段名，如上面的示例SQL，也可以是其它运算表达式。</p></li><li><p>参数二 N 是可选的，必须是大于等于0的整数。默认1，1表示上一行，0表示当前行，12表示前12行，后面计算同比时会用到</p></li><li><p>参数三 是默认值，比如第一行的 上一行是不存在的，这个时候 返回什么值就可以通过参数三来控制，默认是 NULL</p></li><li><p>null_treatment 的定义是处理NULL的策略，但是因为MySQL只实现了 RESPECT NULLS 并且作为默认值，就感觉它只是提醒我们 计算结果需要考虑NULL</p></li></ul><p>over_clause 是必须的，它描述窗口的定义， 如上面的SQL示例最后一行：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">WINDOW w AS (ORDER BY `month`);</span><br></pre></td></tr></table></figure><p>这里定义了一个 窗口(window), 基于 month这个字段升序。</p><h3 id="window"><a href="#window" class="headerlink" title="window"></a>window</h3><p>上一节的 over_clause 定义如下：</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">over_clause:</span><br><span class="line">    &#123;OVER (window_spec) | OVER window_name&#125;</span><br></pre></td></tr></table></figure><p>我们看到，可以 over (窗口定义) 或者  over 窗口名称。 </p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">lag(profit) OVER w AS 上月</span><br></pre></td></tr></table></figure><p>这就是一个 over 窗口名称的示例。</p><p>接下来我们看 window_spec 的定义：</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">window_spec:</span><br><span class="line">    [window_name] [partition_clause] [order_clause] [frame_clause]</span><br></pre></td></tr></table></figure><ul><li><b><i>window_name</i></b> 就是给窗口起个名字，方便使用</li><li><b><i>partition_clause</i></b> 分区定义, 就是定义查询结果如何分组，有点类似group by 的意思。</li><li><b><i>order_clause</i></b> 排序定义,定义窗口内容排序字段及方式，如前面示例里的 order by `month` 。如果省略了order by , 那就按查询到结果的顺序来排序。</li><li><b><i>frame_clause</i></b> frame_clause(框架子句)指定如何定义子集</li></ul><h2 id="同比环比计算"><a href="#同比环比计算" class="headerlink" title="同比环比计算"></a>同比环比计算</h2><p>还是先看最终SQL</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></pre></td><td class="code"><pre><span class="line">select `month`,profit,</span><br><span class="line"> lag(profit) OVER w AS 上月,</span><br><span class="line"> (profit - lag(profit) OVER w) as 环比,</span><br><span class="line"> 100*(profit - lag(profit) OVER w)/lag(profit) OVER w as 环比比例,</span><br><span class="line"> </span><br><span class="line"> lag(profit,12) OVER w AS 上年同月,</span><br><span class="line"> (profit - lag(profit,12) OVER w) as 同比,</span><br><span class="line"> 100*(profit - lag(profit,12) OVER w)/lag(profit,12) OVER w as 同比比例</span><br><span class="line"> </span><br><span class="line"> from my_stat</span><br><span class="line"> WINDOW w AS (ORDER BY `month`);</span><br></pre></td></tr></table></figure><p>返回结果</p><table><thead><tr><th>month</th><th>profit</th><th>上月</th><th>环比</th><th>环比比例</th><th>上年同月</th><th>同比</th><th>同比比例</th></tr></thead><tbody><tr><td>2022-05</td><td>10.00</td><td>NULL</td><td>NULL</td><td>NULL</td><td>NULL</td><td>NULL</td><td>NULL</td></tr><tr><td>2022-06</td><td>12.00</td><td>10.00</td><td>2.00</td><td>20.000000</td><td>NULL</td><td>NULL</td><td>NULL</td></tr><tr><td>2022-07</td><td>6.00</td><td>12.00</td><td>-6.00</td><td>-50.000000</td><td>NULL</td><td>NULL</td><td>NULL</td></tr><tr><td>2022-08</td><td>30.00</td><td>6.00</td><td>24.00</td><td>400.000000</td><td>NULL</td><td>NULL</td><td>NULL</td></tr><tr><td>2022-09</td><td>20.00</td><td>30.00</td><td>-10.00</td><td>-33.333333</td><td>NULL</td><td>NULL</td><td>NULL</td></tr><tr><td>2022-10</td><td>8.00</td><td>20.00</td><td>-12.00</td><td>-60.000000</td><td>NULL</td><td>NULL</td><td>NULL</td></tr><tr><td>2022-11</td><td>2.00</td><td>8.00</td><td>-6.00</td><td>-75.000000</td><td>NULL</td><td>NULL</td><td>NULL</td></tr><tr><td>2022-12</td><td>4.00</td><td>2.00</td><td>2.00</td><td>100.000000</td><td>NULL</td><td>NULL</td><td>NULL</td></tr><tr><td>2023-01</td><td>14.00</td><td>4.00</td><td>10.00</td><td>250.000000</td><td>NULL</td><td>NULL</td><td>NULL</td></tr><tr><td>2023-02</td><td>10.00</td><td>14.00</td><td>-4.00</td><td>-28.571429</td><td>NULL</td><td>NULL</td><td>NULL</td></tr><tr><td>2023-03</td><td>8.00</td><td>10.00</td><td>-2.00</td><td>-20.000000</td><td>NULL</td><td>NULL</td><td>NULL</td></tr><tr><td>2023-04</td><td>9.00</td><td>8.00</td><td>1.00</td><td>12.500000</td><td>NULL</td><td>NULL</td><td>NULL</td></tr><tr><td>2023-05</td><td>7.00</td><td>9.00</td><td>-2.00</td><td>-22.222222</td><td>10.00</td><td>-3.00</td><td>-30.000000</td></tr><tr><td>2023-06</td><td>10.00</td><td>7.00</td><td>3.00</td><td>42.857143</td><td>12.00</td><td>-2.00</td><td>-16.666667</td></tr><tr><td>2023-07</td><td>15.00</td><td>10.00</td><td>5.00</td><td>50.000000</td><td>6.00</td><td>9.00</td><td>150.000000</td></tr></tbody></table><p>同比与环比的差别其实不大，只是 lag(profit)变成了 lag(profit,12)。 lag(profit,12)表示取前12行的数据。</p><blockquote><p>需要注意的是计算同比的时候，我们需要保证数据是连续的，不然数据会有偏差，因为这里的lag(profit,12)取的是前12行的数据，如果月份数据有缺失就可以取错数据。比如：如果少了一个月，那么前12行取的可能就是去年上个月的数据了。</p></blockquote><h2 id="partition-clause-聊聊分区"><a href="#partition-clause-聊聊分区" class="headerlink" title="partition_clause 聊聊分区"></a>partition_clause 聊聊分区</h2><p>我们先准备一下数据，先创建一张表 my_stat_food</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">CREATE TABLE `my_stat_food` (</span><br><span class="line">  `month` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,</span><br><span class="line">  `type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,</span><br><span class="line">  `profit` decimal(10,2) DEFAULT NULL</span><br><span class="line">) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">INSERT INTO `my_stat_food` (`month`, `type`, `profit`)</span><br><span class="line">VALUES</span><br><span class="line"> (&#x27;2023-05&#x27;, &#x27;蔬菜&#x27;, 1.00),</span><br><span class="line"> (&#x27;2023-05&#x27;, &#x27;水果&#x27;, 2.00),</span><br><span class="line"> (&#x27;2023-05&#x27;, &#x27;肉类&#x27;, 3.00),</span><br><span class="line"> (&#x27;2023-06&#x27;, &#x27;蔬菜&#x27;, 4.00),</span><br><span class="line"> (&#x27;2023-06&#x27;, &#x27;水果&#x27;, 5.00),</span><br><span class="line"> (&#x27;2023-06&#x27;, &#x27;肉类&#x27;, 6.00),</span><br><span class="line"> (&#x27;2023-07&#x27;, &#x27;蔬菜&#x27;, 7.00),</span><br><span class="line"> (&#x27;2023-07&#x27;, &#x27;水果&#x27;, 8.00),</span><br><span class="line"> (&#x27;2023-07&#x27;, &#x27;肉类&#x27;, 9.00);</span><br></pre></td></tr></table></figure><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">partition_clause:</span><br><span class="line">    PARTITION BY expr [, expr] ...</span><br></pre></td></tr></table></figure><p>然后我们试试pratition的效果</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">select *,lag(`profit`) over (partition by `type`) as lp from my_stat_food</span><br></pre></td></tr></table></figure><p>输出：</p><table><thead><tr><th>month</th><th>type</th><th>profit</th><th>lp</th></tr></thead><tbody><tr><td>2023-05</td><td>水果</td><td>2.00</td><td>NULL</td></tr><tr><td>2023-06</td><td>水果</td><td>5.00</td><td>2.00</td></tr><tr><td>2023-07</td><td>水果</td><td>8.00</td><td>5.00</td></tr><tr><td>2023-05</td><td>肉类</td><td>3.00</td><td>NULL</td></tr><tr><td>2023-06</td><td>肉类</td><td>6.00</td><td>3.00</td></tr><tr><td>2023-07</td><td>肉类</td><td>9.00</td><td>6.00</td></tr><tr><td>2023-05</td><td>蔬菜</td><td>1.00</td><td>NULL</td></tr><tr><td>2023-06</td><td>蔬菜</td><td>4.00</td><td>1.00</td></tr><tr><td>2023-07</td><td>蔬菜</td><td>7.00</td><td>4.00</td></tr></tbody></table><p>我们的数据是按月分排序的，但我们看到通过 partition进行分区后，就会分区来返回内容了。这样我们就可以通过分区来统计不同品类的环比。</p><h2 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h2><ol><li>上面的给数据都是按月统计好的数据。但原始数据往往是一笔笔订单，那么如何通过原始数据生成每个月的统计数据呢？</li><li>如何保证数据的连续性？在业务上每个月的数据往往是有保证的，毕竟一个月一单不成交的话，也没有统计的必要了。但是考虑到天呢？可以一些小店一天一个成交也没有，也是正常的。那么如何实现如果某天没有数据，统计的时候让那天的统计数据变成0呢？</li></ol><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ol><li><a href="https://dev.mysql.com/doc/refman/8.0/en/window-function-descriptions.html">https://dev.mysql.com/doc/refman/8.0/en/window-function-descriptions.html</a></li></ol>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;我们在业务中常常需要统计这个月销售量多少，同比增加多少，环比增加多少。这篇博文我们就看看如何利用窗口函数实现同比及环比的计算。&lt;br&gt;用到的关键字包括：lag, window&lt;/p&gt;
&lt;h2 id=&quot;准备工作&quot;&gt;&lt;a href=&quot;#准备工作&quot; class=&quot;headerli</summary>
      
    
    
    
    <category term="MySQL" scheme="https://www.genlei.cn/categories/MySQL/"/>
    
    
    <category term="MySQL" scheme="https://www.genlei.cn/tags/MySQL/"/>
    
    <category term="窗口函数" scheme="https://www.genlei.cn/tags/%E7%AA%97%E5%8F%A3%E5%87%BD%E6%95%B0/"/>
    
  </entry>
  
  <entry>
    <title>go语言日期时间处理</title>
    <link href="https://www.genlei.cn/2023/04/253/"/>
    <id>https://www.genlei.cn/2023/04/253/</id>
    <published>2023-04-23T04:33:50.000Z</published>
    <updated>2024-04-03T09:05:49.447Z</updated>
    
    <content type="html"><![CDATA[<p>在go语言的标准库里，可以通过time来处理日期和时间。我们需要引用标准库</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">import &quot;time&quot;</span><br></pre></td></tr></table></figure><h2 id="1-获取当前时间"><a href="#1-获取当前时间" class="headerlink" title="1. 获取当前时间"></a>1. 获取当前时间</h2><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></pre></td><td class="code"><pre><span class="line">now := time.Now()</span><br><span class="line">fmt.Println(now)</span><br><span class="line">// 当前时间戳,1970年1月1日到现在的秒数</span><br><span class="line">fmt.Println(&quot;  秒&quot;, now.Unix())</span><br><span class="line">fmt.Println(&quot;豪秒&quot;, now.UnixMilli())</span><br><span class="line">fmt.Println(&quot;微秒&quot;, now.UnixMicro())</span><br><span class="line">fmt.Println(&quot;纳秒&quot;, now.UnixNano())</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">2023-04-13 13:10:17.8577739 +0800 CST m=+0.004403301</span><br><span class="line">  秒 1681362617</span><br><span class="line">豪秒 1681362617857</span><br><span class="line">微秒 1681362617857773</span><br><span class="line">纳秒 1681362617857773900</span><br></pre></td></tr></table></figure><p>time.Now 返回的结构是 time.Time</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">type Time struct &#123;</span><br><span class="line">wall uint64</span><br><span class="line">ext  int64</span><br><span class="line">loc *Location</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>按go的约定，这个结构里的数据都是私有变量，对于我们使用来说没有价值。</p><h2 id="2-日期时间格式化"><a href="#2-日期时间格式化" class="headerlink" title="2. 日期时间格式化"></a>2. 日期时间格式化</h2><p>特别注意：go的格式化字符串不是常见的 yy-MM-dd HH:mm:ss，而是2006-01-02 15:04:05 Go的成立日期。</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">fmt.Println(now.Format(&quot;2006-01-02 15:04:05&quot;))</span><br><span class="line">fmt.Println(now.Format(&quot;06-1-2 3:4:5&quot;))</span><br></pre></td></tr></table></figure><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">2023-04-13 14:21:21</span><br><span class="line">23-4-13 2:21:21</span><br></pre></td></tr></table></figure><p>格式化字符说明：</p><table><thead><tr><th>字符</th><th>说明</th></tr></thead><tbody><tr><td>1</td><td>月份</td></tr><tr><td>01</td><td>月份，保证两位数字，1月会输出01</td></tr><tr><td>2</td><td>日期</td></tr><tr><td>02</td><td>日期，保证两位数字，2日会输出02</td></tr><tr><td>3</td><td>小时，12小时制</td></tr><tr><td>03</td><td>小时，保证两位数字，3时会输出03</td></tr><tr><td>15</td><td>小时，24小时制，保证两位数字，3时会输出03</td></tr><tr><td>4</td><td>分钟</td></tr><tr><td>04</td><td>分钟，保证两位数字，4分会输出04</td></tr><tr><td>5</td><td>秒</td></tr><tr><td>05</td><td>秒，保证两位数字，5秒会输出05</td></tr><tr><td>06</td><td>年，输出最后两位</td></tr><tr><td>2006</td><td>年，输出4位</td></tr><tr><td>.000</td><td>毫秒</td></tr></tbody></table><p>可以快速记忆： 1月2日3时4分5秒6年</p><h2 id="3-字符串日期时间转time"><a href="#3-字符串日期时间转time" class="headerlink" title="3. 字符串日期时间转time"></a>3. 字符串日期时间转time</h2><p>通过time.Parse来把字符串转为 Time 时间对象</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></pre></td><td class="code"><pre><span class="line">t, err := time.Parse(&quot;2006-01-02 15:04:05&quot;, &quot;2023-04-14 07:03:04&quot;)</span><br><span class="line">if err == nil &#123;</span><br><span class="line">    fmt.Print(t.Year())</span><br><span class="line">&#125; else &#123;</span><br><span class="line">    fmt.Print(err)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>输出： 2023</p><p>如果出错了，会在返回的err 里有说明。比如：</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></pre></td><td class="code"><pre><span class="line">t, err := time.Parse(&quot;2006-01-02 15:04:05&quot;, &quot;04-14 07:03:04&quot;)</span><br><span class="line">if err == nil &#123;</span><br><span class="line">    fmt.Print(t.Year())</span><br><span class="line">&#125; else &#123;</span><br><span class="line">    fmt.Print(err)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>就会输出 </p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">parsing time &quot;04-14 07:03:04&quot; as &quot;2006-01-02 15:04:05&quot;: cannot parse &quot;04-14 07:03:04&quot; as &quot;2006&quot;</span><br></pre></td></tr></table></figure><h2 id="4-获取年-月-周-日-时-分-秒"><a href="#4-获取年-月-周-日-时-分-秒" class="headerlink" title="4. 获取年&#x2F;月&#x2F;周&#x2F;日&#x2F;时&#x2F;分&#x2F;秒"></a>4. 获取年&#x2F;月&#x2F;周&#x2F;日&#x2F;时&#x2F;分&#x2F;秒</h2><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></pre></td><td class="code"><pre><span class="line">t, err := time.Parse(&quot;2006-01-02 15:04:05&quot;, &quot;04-14 07:03:04&quot;)</span><br><span class="line"></span><br><span class="line">fmt.Println(&quot;年&quot;, t.Year())</span><br><span class="line">fmt.Println(&quot;月&quot;, t.Month())</span><br><span class="line">fmt.Println(&quot;月&quot;, t.Month() == 4)</span><br><span class="line">fmt.Println(&quot;日&quot;, t.Day())</span><br><span class="line">fmt.Println(&quot;时&quot;, t.Hour())</span><br><span class="line">fmt.Println(&quot;分&quot;, t.Minute())</span><br><span class="line">fmt.Println(&quot;秒&quot;, t.Second())</span><br><span class="line">fmt.Println(&quot;星期几&quot;, t.Weekday())</span><br><span class="line">year, week := t.ISOWeek()</span><br><span class="line">fmt.Println(&quot;某年第几周&quot;, year, week)</span><br><span class="line">fmt.Println(&quot;当年第几天&quot;, t.YearDay())</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">年 2023</span><br><span class="line">月 April</span><br><span class="line">月 true</span><br><span class="line">日 14</span><br><span class="line">时 7</span><br><span class="line">分 3</span><br><span class="line">秒 4</span><br><span class="line">星期几 Friday</span><br><span class="line">某年第几周 2023 15</span><br><span class="line">当年第几天 104</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;在go语言的标准库里，可以通过time来处理日期和时间。我们需要引用标准库&lt;/p&gt;
&lt;figure class=&quot;highlight plaintext&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/sp</summary>
      
    
    
    
    <category term="go" scheme="https://www.genlei.cn/categories/go/"/>
    
    
  </entry>
  
  <entry>
    <title>go 编码</title>
    <link href="https://www.genlei.cn/2023/04/250/"/>
    <id>https://www.genlei.cn/2023/04/250/</id>
    <published>2023-04-14T04:17:20.000Z</published>
    <updated>2024-04-03T09:05:49.447Z</updated>
    
    <content type="html"><![CDATA[<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><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">package main</span><br><span class="line"></span><br><span class="line">import (</span><br><span class="line">    &quot;errors&quot;</span><br><span class="line">    &quot;fmt&quot;</span><br><span class="line">    &quot;time&quot;</span><br><span class="line"></span><br><span class="line">    &quot;gorm.io/driver/sqlite&quot;</span><br><span class="line">    &quot;gorm.io/gorm&quot;</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">type Code struct &#123;</span><br><span class="line">    ID        string `gorm:&quot;primaryKey&quot;`</span><br><span class="line">    CreatedAt time.Time</span><br><span class="line">    UpdatedAt time.Time</span><br><span class="line">    Batch     string</span><br><span class="line">    Code      int64</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">func main() &#123;</span><br><span class="line">    db, err := gorm.Open(sqlite.Open(&quot;test.db&quot;), &amp;gorm.Config&#123;&#125;)</span><br><span class="line">    if err != nil &#123;</span><br><span class="line">        panic(&quot;failed to connect database&quot;)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    // 迁移 schema</span><br><span class="line">    db.AutoMigrate(&amp;Code&#123;&#125;)</span><br><span class="line"></span><br><span class="line">    fmt.Println(IncAndGet(db, &quot;test&quot;, &quot;oo&quot;))</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">func IncAndGet(db *gorm.DB, idType string, batch string) (id int64, err error) &#123;</span><br><span class="line">    code := Code&#123;ID: idType, Batch: batch&#125;</span><br><span class="line">    var result *gorm.DB</span><br><span class="line">    for i := 0; i &lt;= 10; i++ &#123;</span><br><span class="line">        result = db.First(&amp;code)</span><br><span class="line">        if result.Error == nil &#123;</span><br><span class="line">            if batch == code.Batch &#123;</span><br><span class="line">                newCode := code.Code + 1</span><br><span class="line">                result = db.Debug().Model(&amp;code).Where(&quot;code = ?&quot;, code.Code).Updates(Code&#123;Code: newCode&#125;)</span><br><span class="line">                if result.RowsAffected &gt; 0 &#123;</span><br><span class="line">                    return newCode, nil</span><br><span class="line">                &#125; else &#123;</span><br><span class="line">                    continue</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; else &#123;</span><br><span class="line">                var newCode int64</span><br><span class="line">                newCode = 1</span><br><span class="line">                result = db.Debug().Model(&amp;code).Where(&quot;code = ? and batch = ?&quot;, code.Code, code.Batch).Updates(Code&#123;Code: newCode, Batch: batch&#125;)</span><br><span class="line">                if result.RowsAffected &gt; 0 &#123;</span><br><span class="line">                    return newCode, nil</span><br><span class="line">                &#125; else &#123;</span><br><span class="line">                    continue</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        if errors.Is(result.Error, gorm.ErrRecordNotFound) &#123;</span><br><span class="line">            code.Code = 1</span><br><span class="line">            result = db.Create(&amp;code)</span><br><span class="line">            if result.Error != nil &#123;</span><br><span class="line">                continue</span><br><span class="line">            &#125;</span><br><span class="line">            return code.Code, nil</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    return 0, result.Error</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;figure class=&quot;highlight plaintext&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span c</summary>
      
    
    
    
    <category term="go" scheme="https://www.genlei.cn/categories/go/"/>
    
    
  </entry>
  
  <entry>
    <title>go sha1</title>
    <link href="https://www.genlei.cn/2023/03/247/"/>
    <id>https://www.genlei.cn/2023/03/247/</id>
    <published>2023-03-31T04:28:54.000Z</published>
    <updated>2024-04-03T09:05:49.447Z</updated>
    
    <content type="html"><![CDATA[<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><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">package main</span><br><span class="line"></span><br><span class="line">import (</span><br><span class="line">    &quot;crypto/sha1&quot;</span><br><span class="line">    &quot;encoding/hex&quot;</span><br><span class="line">    &quot;fmt&quot;</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">func SHA1(s string) string &#123;</span><br><span class="line">    o := sha1.New()</span><br><span class="line">    o.Write([]byte(s))</span><br><span class="line">    return hex.EncodeToString(o.Sum(nil))</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">func main()&#123;</span><br><span class="line">    fmt.Println(SHA1(&quot;123456&quot;))</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;figure class=&quot;highlight plaintext&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span c</summary>
      
    
    
    
    <category term="go" scheme="https://www.genlei.cn/categories/go/"/>
    
    
  </entry>
  
  <entry>
    <title>01.Jackson简介及入门</title>
    <link href="https://www.genlei.cn/2022/12/240/"/>
    <id>https://www.genlei.cn/2022/12/240/</id>
    <published>2022-12-19T06:19:33.000Z</published>
    <updated>2024-04-03T09:05:49.444Z</updated>
    
    <content type="html"><![CDATA[<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>Jackson是一个解析json的java库。springboot默认引用了这个json库。Jackson基于Apache-2.0 license开源，在github上有超过3k的star。 Jackson 在 mvnrepository.com 网站上的json分类里排名第一。如此优秀的开源库，值得我们去学习它的使用方法。</p><h2 id="依赖说明"><a href="#依赖说明" class="headerlink" title="依赖说明"></a>依赖说明</h2><p>spring-boot-starter-web里已经通过 spring-boot-starter-josn 依赖了 jackson-databind包。 对于springboot的web项目，我们可以直接使用Jackson。 如果没有使用springboot可以在pom.xml中通过以下方式引入jar包。</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">&lt;dependency&gt;</span><br><span class="line">    &lt;groupId&gt;com.fasterxml.jackson.core&lt;/groupId&gt;</span><br><span class="line">    &lt;artifactId&gt;jackson-databind&lt;/artifactId&gt;</span><br><span class="line">    &lt;version&gt;2.14.1&lt;/version&gt;</span><br><span class="line">&lt;/dependency&gt;</span><br></pre></td></tr></table></figure><h2 id="把json文本解析为java对象"><a href="#把json文本解析为java对象" class="headerlink" title="把json文本解析为java对象"></a>把json文本解析为java对象</h2><p>先定义一个java类 Student.java</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></pre></td><td class="code"><pre><span class="line">import lombok.Data;</span><br><span class="line"></span><br><span class="line">@Data</span><br><span class="line">public class Student &#123;</span><br><span class="line">    String name;</span><br><span class="line">    Integer age;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后通过以下代码完成json到java对象的转换</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></pre></td><td class="code"><pre><span class="line">import com.fasterxml.jackson.core.JsonProcessingException;</span><br><span class="line">import com.fasterxml.jackson.databind.ObjectMapper;</span><br><span class="line">public class Simple &#123;</span><br><span class="line">    ObjectMapper objectMapper = new ObjectMapper();</span><br><span class="line">    public void testJson2Java() throws JsonProcessingException &#123;</span><br><span class="line">        String str = &quot;&#123;\&quot;name\&quot;:\&quot;tom\&quot;,\&quot;age\&quot;:16&#125;&quot;;</span><br><span class="line">        Student student = objectMapper.readValue(str,Student.class);</span><br><span class="line">        System.out.println(student);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="把java对象序列化为json文本"><a href="#把java对象序列化为json文本" class="headerlink" title="把java对象序列化为json文本"></a>把java对象序列化为json文本</h2><p>关键方法：ObjectMapper.writeValueAsString, 代码示例：</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></pre></td><td class="code"><pre><span class="line">Student student = new Student();</span><br><span class="line">student.setAge(11);</span><br><span class="line">student.setName(&quot;jack&quot;);</span><br><span class="line">String json = objectMapper.writeValueAsString(student);</span><br></pre></td></tr></table></figure><h2 id="把json文本解析为Jackson-JsonNode"><a href="#把json文本解析为Jackson-JsonNode" class="headerlink" title="把json文本解析为Jackson JsonNode"></a>把json文本解析为Jackson JsonNode</h2><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></pre></td><td class="code"><pre><span class="line">String str = &quot;&#123;\&quot;name\&quot;:\&quot;tom\&quot;,\&quot;age\&quot;:16&#125;&quot;;</span><br><span class="line">JsonNode jsonNode = objectMapper.readTree(str);</span><br><span class="line">String name = jsonNode.get(&quot;name&quot;).asText();</span><br></pre></td></tr></table></figure><h2 id="把json文本解析为Java-List"><a href="#把json文本解析为Java-List" class="headerlink" title="把json文本解析为Java List"></a>把json文本解析为Java List</h2><p>借助 TypeReference</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">String str = &quot;[&#123;\&quot;name\&quot;:\&quot;tom\&quot;,\&quot;age\&quot;:16&#125;,&#123;\&quot;name\&quot;:\&quot;lucy\&quot;,\&quot;age\&quot;:15&#125;]&quot;;</span><br><span class="line">List&lt;Student&gt; list = objectMapper.readValue(str, new TypeReference&lt;List&lt;Student&gt;&gt;()&#123;&#125;);</span><br></pre></td></tr></table></figure><h2 id="把json文件解析为Java-数组"><a href="#把json文件解析为Java-数组" class="headerlink" title="把json文件解析为Java 数组"></a>把json文件解析为Java 数组</h2><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></pre></td><td class="code"><pre><span class="line">String str = &quot;[&#123;\&quot;name\&quot;:\&quot;tom\&quot;,\&quot;age\&quot;:16&#125;,&#123;\&quot;name\&quot;:\&quot;lucy\&quot;,\&quot;age\&quot;:15&#125;]&quot;;</span><br><span class="line">ObjectMapper objectMapper = new ObjectMapper();</span><br><span class="line">objectMapper.configure(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true);</span><br><span class="line">Student[] students = objectMapper.readValue(str, Student[].class);</span><br></pre></td></tr></table></figure><h2 id="把json文本解析为Java-Map"><a href="#把json文本解析为Java-Map" class="headerlink" title="把json文本解析为Java Map"></a>把json文本解析为Java Map</h2><p>借助 TypeReference</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">String str = &quot;&#123;\&quot;name\&quot;:\&quot;tom\&quot;,\&quot;age\&quot;:16&#125;&quot;;</span><br><span class="line">Map&lt;String,Object&gt; map = objectMapper.readValue(str, new TypeReference&lt;Map&lt;String,Object&gt;&gt;()&#123;&#125;);</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;Jackson是一个解析json的java库。springboot默认引用了这个json库。Jackson基于Apache-2.0 lice</summary>
      
    
    
    
    <category term="Java" scheme="https://www.genlei.cn/categories/Java/"/>
    
    
  </entry>
  
  <entry>
    <title>国内优秀npm，yarn 镜像推荐及使用</title>
    <link href="https://www.genlei.cn/2022/11/236/"/>
    <id>https://www.genlei.cn/2022/11/236/</id>
    <published>2022-11-16T04:31:52.000Z</published>
    <updated>2024-04-03T09:05:49.453Z</updated>
    
    <content type="html"><![CDATA[<p>npm全称Node Package Manager，是node.js的模块依赖管理工具。由于npm的源在国外，所以国内用户使用起来各种不方便。下面整理出了一部分国内优秀的npm镜像资源，国内用户可以选择使用。</p><p>国内优秀npm镜像</p><ul><li><p>淘宝npm镜像 搜索地址：<a href="http://npm.taobao.org/">http://npm.taobao.org/</a> registry地址：<a href="https://registry.npmmirror.com/">https://registry.npmmirror.com</a></p></li><li><p>cnpmjs镜像 搜索地址：<a href="http://cnpmjs.org/">http://cnpmjs.org/</a> registry地址：<a href="http://r.cnpmjs.org/">http://r.cnpmjs.org/</a></p></li></ul><h2 id="如何使用"><a href="#如何使用" class="headerlink" title="如何使用"></a>如何使用</h2><p>有很多方法来配置npm的registry地址，下面根据不同情境列出几种比较常用的方法。以淘宝npm镜像举例：</p><p>1.临时使用</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm --registry https://registry.npmmirror.com install express</span><br></pre></td></tr></table></figure><p>2.持久使用</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm config set registry https://registry.npmmirror.com</span><br></pre></td></tr></table></figure><p>&#x2F;&#x2F; 配置后可通过下面方式来验证是否成功</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm config get registry</span><br></pre></td></tr></table></figure><p>&#x2F;&#x2F; 或</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm info express</span><br></pre></td></tr></table></figure><p>3.通过cnpm使用</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install -g cnpm --registry=https://registry.npmmirror.com</span><br></pre></td></tr></table></figure><p>&#x2F;&#x2F; 使用</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cnpm install express</span><br></pre></td></tr></table></figure><h2 id="Yarn"><a href="#Yarn" class="headerlink" title="Yarn"></a>Yarn</h2><p>临时修改</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn save 软件名 --registry https://registry.npmmirror.com</span><br></pre></td></tr></table></figure><p>全局修改</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn config set registry https://registry.npmmirror.com</span><br></pre></td></tr></table></figure><p>参考： <a href="https://www.jianshu.com/p/bd03c4c497c5">https://www.jianshu.com/p/bd03c4c497c5</a> <a href="https://www.cnblogs.com/zh719588366/p/6784848.html">https://www.cnblogs.com/zh719588366/p/6784848.html</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;npm全称Node Package Manager，是node.js的模块依赖管理工具。由于npm的源在国外，所以国内用户使用起来各种不方便。下面整理出了一部分国内优秀的npm镜像资源，国内用户可以选择使用。&lt;/p&gt;
&lt;p&gt;国内优秀npm镜像&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p</summary>
      
    
    
    
    <category term="javascript" scheme="https://www.genlei.cn/categories/javascript/"/>
    
    
  </entry>
  
  <entry>
    <title>使用Typescript编写和发布npm包</title>
    <link href="https://www.genlei.cn/2022/11/233/"/>
    <id>https://www.genlei.cn/2022/11/233/</id>
    <published>2022-11-13T13:51:27.000Z</published>
    <updated>2024-04-03T09:05:49.452Z</updated>
    
    <content type="html"><![CDATA[<p><a href="https://www.jianshu.com/p/8fa2c50720e4">https://www.jianshu.com/p/8fa2c50720e4</a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;&lt;a href=&quot;https://www.jianshu.com/p/8fa2c50720e4&quot;&gt;https://www.jianshu.com/p/8fa2c50720e4&lt;/a&gt;&lt;/p&gt;
</summary>
      
    
    
    
    <category term="uncategorized" scheme="https://www.genlei.cn/categories/uncategorized/"/>
    
    
  </entry>
  
  <entry>
    <title>设计与架构究竟是什么-《架构整洁之道》读书笔记</title>
    <link href="https://www.genlei.cn/2022/11/231/"/>
    <id>https://www.genlei.cn/2022/11/231/</id>
    <published>2022-11-13T06:21:50.000Z</published>
    <updated>2024-04-03T09:05:49.452Z</updated>
    
    <content type="html"><![CDATA[<h2 id="设计与架构究竟是什么？"><a href="#设计与架构究竟是什么？" class="headerlink" title="设计与架构究竟是什么？"></a>设计与架构究竟是什么？</h2><p>尽管标题是 设计与架构究竟是什么？然而书中似乎并没有给出答案。反而对于目标给出了清晰的定义。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">软件架构的终极目标是，用最小的人力成本来满足构建和维护该系统的需求。</span><br></pre></td></tr></table></figure><p>对于架构评估，书中给出了简单的说明。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">一个软件架构的优劣，可以用它满足用户需求所需要的成本来衡量。如果该成本很低，并且在系统的整个生命周期内一直都能维持这样的低成本，那么这个系统的设计就是优良的。如果该系统的每次发布都会提升下一次的变更的成本，那么这个设计就是不好的。</span><br></pre></td></tr></table></figure><h2 id="乱麻系统"><a href="#乱麻系统" class="headerlink" title="乱麻系统"></a>乱麻系统</h2><p>书中举了一个乱麻系统的例子，说明一个糟糕的架构有多令人难受。乱麻系统的特点如下：</p><ol><li>没有经过设计，匆匆忙忙被构建起来</li><li>为了加快发布速度，拼命地往团队里加入新人</li><li>决策层对代码质量提升和设计结构优化存在着持续的、长久的忽视</li></ol><p>导致的后果就是，对开发者来讲带来了很大的挫败感，团队没有人偷懒，拼命工作，然而不管他们投入了多少个人时间，救了多少次火，产出始终上不去。工程师的大部分时间都消耗在对现有系统的修修补补上，而不完成新功能。</p><p>对于公司管理层而且，就是成本不断的上升，功能产出确明显下降。</p><h2 id="解决之道"><a href="#解决之道" class="headerlink" title="解决之道"></a>解决之道</h2><p>书中举了龟兔赛跑的故事，归纳了以下的主题思想：</p><ol><li>慢但是稳，是成功的秘诀</li><li>该比赛并不是拼谁开始跑的快，也是不拼谁更有力气</li><li>心态越急，反而跑得慢</li></ol><p>工程师们普遍用一句话来欺骗自己：“我们可以未来再重构代码，产品上线最重要！”但是结果大家都知道，产品上线后重构工作就再没人提起了。</p><p>接下来就举例子说明 测试驱动开发（TDD） 的方法论，说明软件开发的一个核心特点：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">要想跑得快，先要跑得稳。</span><br></pre></td></tr></table></figure><p>接下来是另一个极端，某些工程师可能会认为挽救一个系统的唯一办法是抛弃现有系统，设计一个全新的系统来替代。然而</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">过度自信只会使得重构设计陷入和原项目一样的困局中。</span><br></pre></td></tr></table></figure><h2 id="个人观点"><a href="#个人观点" class="headerlink" title="个人观点"></a>个人观点</h2><ol><li><p>关于架构评估，书中只简单讲了评判标准，而且是结果型的标准。当然这样指明了大的方向，而具体的方法论并没有讲。当然架构评估本身就是一门复杂的学问。</p></li><li><p>关于乱麻系统一，有的项目就是开发完成就结束了，或者还没开发完成就结束了，对于这一类项目，似乎是什么样的架构并不重要，因为不存在维护的问题。所以一个项目在立项时，有没有考虑过它的生命周期呢？项目的生命周期越长，平摊成本就越低，而收益越大。</p></li><li><p>关于乱麻系统二，业务与技术往往是相反的，业务上的事件的发生往往是突发的，而为了应对突发事件产生的系统，往往没有足够的时间留给技术实现团队。这也变相的要求作为技术人，需要准备好架构预案，针对某类系统有提前想的架构模块，业务需求真的到来的时候，需要我们能花费较少的时间就能出来一个还不错的设计，从而满足业务的需要，所以没事多磨刀。</p></li><li><p>关于设计一个全新的系统，往往我们面临的是别人留下来一堆代码，而这一堆里，不知道哪些有用，哪些没用，而且出于新人接手的情况下，如果业务上不特别着急，又需要工程师表现时，就很容易出现通过重构以显示自己能力的想法。这个时候我们一定不要忘记架构的目标，就是前面说的:</p><blockquote><p>软件架构的终极目标是，用最小的人力成本来满足构建和维护该系统的需求。</p></blockquote></li></ol><p>我们一定要想想，重构是不是能用最小的人力成本来满足构建和维护该系统的需求。</p><ol start="5"><li>关于要快还是要稳，我想架构上是要稳，业务上可能是要快，这个时候要看我们如何取舍了。有时候要快才能占领先机。</li></ol>]]></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="https://www.genlei.cn/categories/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
    
    
  </entry>
  
  <entry>
    <title>java 获取https证书过期时间</title>
    <link href="https://www.genlei.cn/2022/11/228/"/>
    <id>https://www.genlei.cn/2022/11/228/</id>
    <published>2022-11-07T12:11:20.000Z</published>
    <updated>2024-04-03T09:05:49.447Z</updated>
    
    <content type="html"><![CDATA[<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><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><br><span class="line">import javax.net.ssl.HttpsURLConnection;</span><br><span class="line">import java.net.URL;</span><br><span class="line">import java.security.cert.Certificate;</span><br><span class="line">import java.security.cert.X509Certificate;</span><br><span class="line"></span><br><span class="line">public class Main &#123;</span><br><span class="line"></span><br><span class="line">    public static void main(String[] args) throws Exception &#123;</span><br><span class="line">        URL url = new URL(&quot;https://www.baidu.com&quot;);</span><br><span class="line">        HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();</span><br><span class="line">        connection.connect();</span><br><span class="line">        for (Certificate certificate : connection.getServerCertificates()) &#123;</span><br><span class="line">            //第一个就是服务器本身证书，后续的是证书链上的其他证书</span><br><span class="line">            X509Certificate x509Certificate = (X509Certificate) certificate;</span><br><span class="line">            System.out.println(x509Certificate.getSubjectDN());</span><br><span class="line">            System.out.println(x509Certificate.getNotBefore());//有效期开始时间</span><br><span class="line">            System.out.println(x509Certificate.getNotAfter());//有效期结束时间</span><br><span class="line">        &#125;</span><br><span class="line">        connection.disconnect();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;figure class=&quot;highlight plaintext&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span c</summary>
      
    
    
    
    <category term="Java" scheme="https://www.genlei.cn/categories/Java/"/>
    
    
  </entry>
  
  <entry>
    <title>CSV工具类</title>
    <link href="https://www.genlei.cn/2022/10/224/"/>
    <id>https://www.genlei.cn/2022/10/224/</id>
    <published>2022-10-31T06:55:18.000Z</published>
    <updated>2024-04-03T09:05:49.447Z</updated>
    
    <content type="html"><![CDATA[<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><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></pre></td><td class="code"><pre><span class="line">import java.util.Arrays;</span><br><span class="line">import java.util.List;</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line"> * @author junfeng</span><br><span class="line"> */</span><br><span class="line">public class CsvLine &#123;</span><br><span class="line">    private final String[] items;</span><br><span class="line"></span><br><span class="line">    CsvLine(List&lt;String&gt; items)&#123;</span><br><span class="line">        if(items==null)&#123;</span><br><span class="line">            this.items = new String[0];</span><br><span class="line">        &#125;else&#123;</span><br><span class="line">            this.items = items.toArray(new String[0]);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    public int length()&#123;</span><br><span class="line">        return items.length;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * 根据下标获取值，从0开始</span><br><span class="line">     * @param off 下标</span><br><span class="line">     * @return 下标对应的文本</span><br><span class="line">     */</span><br><span class="line">    public String get(int off)&#123;</span><br><span class="line">        if(off&lt;0)&#123;</span><br><span class="line">            throw new IllegalArgumentException(&quot;off should &gt;= 0&quot;);</span><br><span class="line">        &#125;</span><br><span class="line">        if(off&gt;=this.items.length)&#123;</span><br><span class="line">            return null;</span><br><span class="line">        &#125;</span><br><span class="line">        return this.items[off];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * 获取所有项的内容列表</span><br><span class="line">     * @return 所有项的内容列表</span><br><span class="line">     */</span><br><span class="line">    public List&lt;String&gt; getItemList()&#123;</span><br><span class="line">        return Arrays.asList(this.items);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public String toString()&#123;</span><br><span class="line">        return  &quot;[&quot; +  String.join(&quot;,&quot;,items) + &quot;]&quot;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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><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><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br></pre></td><td class="code"><pre><span class="line">import java.io.*;</span><br><span class="line">import java.nio.charset.Charset;</span><br><span class="line">import java.nio.charset.StandardCharsets;</span><br><span class="line">import java.util.ArrayList;</span><br><span class="line">import java.util.List;</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line"> * CSV内容读取类封装</span><br><span class="line"> * 格式约定：</span><br><span class="line"> * 列分隔符：默认是逗号”，“</span><br><span class="line"> * 封闭符：指定源文件中使用的结束字符，默认为双引号。其中包括：双引号、单引号。</span><br><span class="line"> *</span><br><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"> *</span><br><span class="line"> * 分隔符 和 封闭符 是单一字符。</span><br><span class="line"> * @author junfeng</span><br><span class="line"> */</span><br><span class="line">public class CsvReader implements Closeable &#123;</span><br><span class="line">    char splitChar = &#x27;,&#x27;;</span><br><span class="line">    char limitChar = &#x27;&quot;&#x27;;</span><br><span class="line">    BufferedReader br;</span><br><span class="line">    File file;</span><br><span class="line">    Charset charset = StandardCharsets.UTF_8;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * CSV解析类构造函数</span><br><span class="line">     * @param file 要读取的文件</span><br><span class="line">     */</span><br><span class="line">    public CsvReader(File file)&#123;</span><br><span class="line">        this.file = file;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * CSV解析类构造函数</span><br><span class="line">     * @param file 要读取的文件</span><br><span class="line">     * @param charset 文件编码</span><br><span class="line">     */</span><br><span class="line">    public CsvReader(File file,Charset charset)&#123;</span><br><span class="line">        this.file = file;</span><br><span class="line">        this.charset = charset;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * CSV解析类构造函数</span><br><span class="line">     * @param file 要读取的文件</span><br><span class="line">     * @param charset 文件编码</span><br><span class="line">     * @param splitChar 分隔符</span><br><span class="line">     * @param limitChar 限定符</span><br><span class="line">     */</span><br><span class="line">    public CsvReader(File file,Charset charset,char splitChar,char limitChar)&#123;</span><br><span class="line">        this.file = file;</span><br><span class="line">        this.charset = charset;</span><br><span class="line">        this.splitChar = splitChar;</span><br><span class="line">        this.limitChar = limitChar;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * 读取一行 CSV 内容，正常返回 CsvLine 对象，读到文件结束，则返回null</span><br><span class="line">     * @return CsvLine 封装的内容</span><br><span class="line">     * @throws IOException 文件读取异常</span><br><span class="line">     */</span><br><span class="line">    public CsvLine readLine() throws IOException&#123;</span><br><span class="line">        if(br==null)&#123;</span><br><span class="line">            br = new BufferedReader(new InputStreamReader(new FileInputStream(this.file),charset));</span><br><span class="line">        &#125;</span><br><span class="line">        // 标记当前是否位于 限定符内</span><br><span class="line">        boolean inLimit = false;</span><br><span class="line">        List&lt;String&gt; list = new ArrayList&lt;&gt;();</span><br><span class="line">        StringBuilder sbItem = new StringBuilder();</span><br><span class="line">        // 标记当前项是否由限定符 包括，如果包括 加入项列表时 不会清空前后的空格</span><br><span class="line">        boolean itemInLimit = false;</span><br><span class="line">        int nextChar = -1;</span><br><span class="line">        while (true)&#123;</span><br><span class="line">            int c = nextChar;</span><br><span class="line">            nextChar = -1;</span><br><span class="line">            if(c==-1)&#123;</span><br><span class="line">                c = br.read();</span><br><span class="line">            &#125;</span><br><span class="line">            if(inLimit)&#123;</span><br><span class="line">                if(c==limitChar)&#123;</span><br><span class="line">                    nextChar =  br.read();</span><br><span class="line">                    if(nextChar==limitChar)&#123;</span><br><span class="line">                        sbItem.append((char)c);</span><br><span class="line">                        nextChar = -1;</span><br><span class="line">                    &#125;else&#123;</span><br><span class="line">                        inLimit = false;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;else&#123;</span><br><span class="line">                    sbItem.append((char)c);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;else&#123;</span><br><span class="line">                if(c==limitChar)&#123;</span><br><span class="line">                    inLimit = true;</span><br><span class="line">                    sbItem = new StringBuilder();</span><br><span class="line">                    itemInLimit = true;</span><br><span class="line">                &#125;else if(c==splitChar)&#123;</span><br><span class="line">                    if(itemInLimit)&#123;</span><br><span class="line">                        list.add(sbItem.toString());</span><br><span class="line">                    &#125;else&#123;</span><br><span class="line">                        list.add(sbItem.toString().trim());</span><br><span class="line">                    &#125;</span><br><span class="line">                    itemInLimit = false;</span><br><span class="line">                    sbItem = new StringBuilder();</span><br><span class="line">                &#125;else if(c==&#x27;\r&#x27;)&#123;</span><br><span class="line">                    // 啥也不干</span><br><span class="line">                &#125;else if(c==&#x27;\n&#x27;)&#123;</span><br><span class="line">                    if(sbItem.length()&gt;0)&#123;</span><br><span class="line">                        if(itemInLimit)&#123;</span><br><span class="line">                            list.add(sbItem.toString());</span><br><span class="line">                        &#125;else&#123;</span><br><span class="line">                            list.add(sbItem.toString().trim());</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                    break;</span><br><span class="line">                &#125;else&#123;</span><br><span class="line">                    sbItem.append((char)c);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            if(c==-1)&#123;</span><br><span class="line">                if(list.size()==0)&#123;</span><br><span class="line">                    return null;</span><br><span class="line">                &#125;</span><br><span class="line">                break;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        return new CsvLine(list);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public void close() throws IOException &#123;</span><br><span class="line">        if(br!=null)&#123;</span><br><span class="line">            br.close();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public static void main(String[] args) throws IOException &#123;</span><br><span class="line">        CsvReader reader = new CsvReader(new File(&quot;c:/tmp/31.csv&quot;),Charset.forName(&quot;gbk&quot;));</span><br><span class="line">        CsvLine line;</span><br><span class="line">        while ((line=reader.readLine())!=null)&#123;</span><br><span class="line">            System.out.println(line);</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>]]></content>
    
    
      
      
    <summary type="html">&lt;figure class=&quot;highlight plaintext&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span c</summary>
      
    
    
    
    <category term="uncategorized" scheme="https://www.genlei.cn/categories/uncategorized/"/>
    
    
  </entry>
  
  <entry>
    <title>从java 8 到 java 17 , 多个新特性整理</title>
    <link href="https://www.genlei.cn/2022/10/221/"/>
    <id>https://www.genlei.cn/2022/10/221/</id>
    <published>2022-10-28T08:59:43.000Z</published>
    <updated>2024-04-03T09:05:49.452Z</updated>
    
    <content type="html"><![CDATA[<h2 id="1-集合工厂方法"><a href="#1-集合工厂方法" class="headerlink" title="1.集合工厂方法"></a>1.集合工厂方法</h2><p>从Java 9开始， List，Set 和 Map 接口中，新的静态工厂方法可以创建这些集合的不可变实例。方便创建简单列表</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><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">public static void main(String[] args)&#123;</span><br><span class="line">    List&lt;String&gt; list = List.of(&quot;a&quot;,&quot;b&quot;,&quot;c&quot;);</span><br><span class="line">    System.out.println(list);</span><br><span class="line">    Set&lt;String&gt; set = Set.of(&quot;a&quot;, &quot;b&quot;, &quot;c&quot;);</span><br><span class="line">    System.out.println(set);</span><br><span class="line">    Map&lt;String, String&gt; map = Map.of(&quot;k1&quot;,&quot;v1&quot;,&quot;k2&quot;,&quot;v2&quot;,&quot;k3&quot;,&quot;v3&quot;);</span><br><span class="line">    System.out.println(map);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">[a, b, c]</span><br><span class="line">[a, b, c]</span><br><span class="line">&#123;k1=v1, k2=v2, k3=v3&#125;</span><br></pre></td></tr></table></figure><h2 id="2-私有接口方法"><a href="#2-私有接口方法" class="headerlink" title="2.私有接口方法"></a>2.私有接口方法</h2><p>在Java 8，接口可以有常量变量和抽象方法。我们不能在接口中提供私有方法的实现。现在接口越来越象抽象类了，意味着java通过这种方式变相的实现多继承。</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><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">public interface MyInterface &#123;</span><br><span class="line">    private void  test()&#123;</span><br><span class="line">        System.out.println(&quot;private test method&quot;);</span><br><span class="line">    &#125;</span><br><span class="line">    default void doTest()&#123;</span><br><span class="line">        test();</span><br><span class="line">    &#125;</span><br><span class="line">    public static void main(String[] args)&#123;</span><br><span class="line">        MyInterface myinterface = new MyInterface() &#123;</span><br><span class="line"></span><br><span class="line">        &#125;;</span><br><span class="line">        myinterface.doTest();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>关键点：可以在 接口中使用 private 来定义私有接口</p><h2 id="3-改进的进程-API"><a href="#3-改进的进程-API" class="headerlink" title="3.改进的进程 API"></a>3.改进的进程 API</h2><p>在 Java 9 之前，Process API 仍然缺乏对使用本地进程的基本支持，例如获取进程的 PID 和所有者，进程的开始时间，进程使用了多少 CPU 时间，多少本地进程正在运行等。</p><p>Java 9 向 Process API 添加了一个名为 ProcessHandle 的接口来增强 java.lang.Process 类。</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><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">public static void main(String[] args) throws IOException &#123;</span><br><span class="line">    ProcessBuilder pb = new ProcessBuilder(&quot;cmd.exe&quot;);</span><br><span class="line">    String Null = &quot;Null&quot;;</span><br><span class="line">    Process p = pb.start();</span><br><span class="line">    ProcessHandle.Info info = p.info();</span><br><span class="line">    System.out.println(&quot;进程ID &quot; + p.pid());</span><br><span class="line">    System.out.println(&quot;可执行文件 &quot; + info.command().orElse(Null));</span><br><span class="line">    System.out.println(&quot;启动时间 &quot; + info.startInstant().map(i -&gt; i.atZone(ZoneId.systemDefault())</span><br><span class="line">            .toLocalDateTime().toString()).orElse(Null));</span><br><span class="line">    System.out.println(&quot;参数 &quot; + info.arguments().map(a -&gt; Stream.of(a).collect(</span><br><span class="line">                    Collectors.joining(&quot; &quot;))).orElse(Null));</span><br><span class="line">    System.out.println(&quot;用户 &quot; + info.user().orElse(Null));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">进程ID 25420</span><br><span class="line">可执行文件 C:\Windows\System32\cmd.exe</span><br><span class="line">启动时间 2022-10-27T10:35:42.124</span><br><span class="line">参数 Null</span><br><span class="line">用户 admin</span><br></pre></td></tr></table></figure><h2 id="4-改进的-Stream-API"><a href="#4-改进的-Stream-API" class="headerlink" title="4. 改进的 Stream API"></a>4. 改进的 Stream API</h2><p>Java 9 为 Stream 新增了：dropWhile、takeWhile、ofNullable 方法，iterate 方法新增了一个重载方法。</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><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">public static void main(String[] args)&#123;</span><br><span class="line">    Stream.of(&quot;a&quot;,&quot;b&quot;,&quot;c&quot;,&quot;&quot;,&quot;e&quot;,&quot;f&quot;).takeWhile(s-&gt;!s.isEmpty())</span><br><span class="line">            .forEach(System.out::print);</span><br><span class="line">    System.out.println(&quot;\n===&quot;);</span><br><span class="line">    Stream.of(&quot;a&quot;,&quot;b&quot;,&quot;c&quot;,&quot;&quot;,&quot;e&quot;,&quot;f&quot;).dropWhile(s-&gt; !s.isEmpty())</span><br><span class="line">            .forEach(System.out::print);</span><br><span class="line">    System.out.println(&quot;\n===&quot;);</span><br><span class="line">    IntStream.iterate(3, x -&gt; x &lt; 10, x -&gt; x+ 3).forEach(System.out::println);</span><br><span class="line">    System.out.println(&quot;===&quot;);</span><br><span class="line">    List list = Stream.ofNullable(null).collect(Collectors.toList());</span><br><span class="line">    System.out.println(list.size());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">abc</span><br><span class="line">===</span><br><span class="line">ef</span><br><span class="line">===</span><br><span class="line">3</span><br><span class="line">6</span><br><span class="line">9</span><br><span class="line">===</span><br><span class="line">0</span><br></pre></td></tr></table></figure><p>说明：</p><ul><li>takeWhile ： 满足条件才执行，不满足则退出循环</li><li>dropWhile ： 第一次不满足条件才执行</li><li>iterate ： 方便创建流，可用于测试</li><li>ofNullable： 防止空指针</li></ul><h2 id="5-改进的-try-with-resources"><a href="#5-改进的-try-with-resources" class="headerlink" title="5. 改进的 try-with-resources"></a>5. 改进的 try-with-resources</h2><p>从java7开始，支持了try-with-resources， 但在java 7 的时候，变量必须定义在try后的括号里。现在可以放出来了，使用更简单 示例代码</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></pre></td><td class="code"><pre><span class="line"> public static void main(String[] args) throws IOException &#123;</span><br><span class="line">    BufferedReader br = new BufferedReader(new FileReader(&quot;c:/tmp/b.txt&quot;));</span><br><span class="line">    BufferedWriter bw = new BufferedWriter(new FileWriter(&quot;c:/tmp/c.txt&quot;));</span><br><span class="line">    try(br;bw)&#123;</span><br><span class="line">        String line = br.readLine();</span><br><span class="line">        bw.write(line);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="6-改进的-Optional-类"><a href="#6-改进的-Optional-类" class="headerlink" title="6. 改进的 Optional 类"></a>6. 改进的 Optional 类</h2><p>在 Java 9 中, Optional 添加了以下方法：</p><ul><li><p>stream()</p></li><li><p>ifPresentOrElse()</p></li><li><p>orElse()</p><h3 id="stream"><a href="#stream" class="headerlink" title="stream()"></a>stream()</h3><p>从Optional 中获取一个stream, 如果有值则返回该值的stream, 否则返回一个空的流，Stream.empty()。</p></li></ul><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></pre></td><td class="code"><pre><span class="line">public static void main(String[] args)&#123;</span><br><span class="line">    Optional op = Optional.empty();</span><br><span class="line">    System.out.println(op.stream().toList().size());</span><br><span class="line">    Optional op2 = Optional.of(&quot;1&quot;);</span><br><span class="line">    System.out.println(op2.stream().toList().size());</span><br><span class="line"></span><br><span class="line">    System.out.println(&quot;===&quot;);</span><br><span class="line">    Optional&lt;List&lt;String&gt;&gt; op3 = Optional.of(List.of(&quot;1&quot;,&quot;2&quot;));</span><br><span class="line">    op3.stream().forEach(t-&gt; System.out.println(t));</span><br><span class="line">    System.out.println(op3.stream().toList().size());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">0</span><br><span class="line">1</span><br><span class="line">===</span><br><span class="line">[1, 2]</span><br><span class="line">1</span><br></pre></td></tr></table></figure><h3 id="ifPresentOrElse"><a href="#ifPresentOrElse" class="headerlink" title="ifPresentOrElse"></a>ifPresentOrElse</h3><p>定义</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">public void ifPresentOrElse(Consumer&lt;? super T&gt; action, Runnable emptyAction) </span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">public static void main(String[] args)&#123;</span><br><span class="line">    Optional&lt;String&gt; op = Optional.of(&quot;A&quot;);</span><br><span class="line">    op.ifPresentOrElse(t-&gt;System.out.println(t),()-&gt;System.out.println(&quot;Empty&quot;));</span><br><span class="line"></span><br><span class="line">    Optional&lt;String&gt; op1 = Optional.empty();</span><br><span class="line">    op1.ifPresentOrElse(t-&gt;System.out.println(t),()-&gt;System.out.println(&quot;Empty&quot;));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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">A</span><br><span class="line">Empty</span><br></pre></td></tr></table></figure><h3 id="orElse"><a href="#orElse" class="headerlink" title="orElse"></a>orElse</h3><p>如果optional有值，返回对应的值此，否则返回orElse里的内容 定义：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">public T orElse(T other)</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">public static void main(String[] args)&#123;</span><br><span class="line">    Optional&lt;String&gt; op = Optional.of(&quot;A&quot;);</span><br><span class="line">    System.out.println(op.orElse(&quot;B&quot;));</span><br><span class="line"></span><br><span class="line">    Optional&lt;String&gt; op1 = Optional.empty();</span><br><span class="line">    System.out.println(op1.orElse(&quot;B&quot;));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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">A</span><br><span class="line">B</span><br></pre></td></tr></table></figure><h2 id="7-改进的-CompletableFuture-API"><a href="#7-改进的-CompletableFuture-API" class="headerlink" title="7. 改进的 CompletableFuture API"></a>7. 改进的 CompletableFuture API</h2><p>新增了</p><ul><li>newIncompleteFuture</li><li>defaultExecutor</li><li>copy</li><li>minimalCompletionStage</li><li>completeAsync</li><li>orTimeout</li><li>completeOnTimeout</li><li>delayedExecutor</li><li>completedStage</li><li>failedFuture</li><li>failedStage</li></ul><h2 id="8-支持使用var"><a href="#8-支持使用var" class="headerlink" title="8. 支持使用var"></a>8. 支持使用var</h2><p>从 java 10开始，引入了var 来声明局部变量 示例代码：</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></pre></td><td class="code"><pre><span class="line">var str = &quot;Hi&quot;;</span><br><span class="line">var v = 0;</span><br><span class="line">for(var i=0;i&lt;10;i++)&#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line">var list = new ArrayList&lt;String&gt;();</span><br></pre></td></tr></table></figure><h3 id="9-删除的内容"><a href="#9-删除的内容" class="headerlink" title="9. 删除的内容"></a>9. 删除的内容</h3><p>Java 10 删除了很多工具：</p><ul><li>命令行工具 javah，可以使用 javac -h 代替。</li><li>命令行选项 -X：prof，尽管可以使用 jmap 工具来访问分析信息。</li></ul><p>一些从 Java1.2 开始标记的为已弃用的 API 也被永久删除了。包括 java.lang.SecurityManager.inCheck 字段和以下方法：</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></pre></td><td class="code"><pre><span class="line">java.lang.SecurityManager.classDepth(java.lang.String)</span><br><span class="line">java.lang.SecurityManager.classLoaderDepth()</span><br><span class="line">java.lang.SecurityManager.currentClassLoader()</span><br><span class="line">java.lang.SecurityManager.currentLoadedClass()</span><br><span class="line">java.lang.SecurityManager.getInCheck()</span><br><span class="line">java.lang.SecurityManager.inClass(java.lang.String)</span><br><span class="line">java.lang.SecurityManager.inClassLoader()</span><br><span class="line">java.lang.Runtime.getLocalizedInputStream(java.io.InputStream)</span><br><span class="line">java.lang.Runtime.getLocalizedOutputStream(java.io.OutputStream)</span><br><span class="line">弃用</span><br></pre></td></tr></table></figure><p>整个 SecurityManager 类在 java 17 被标识为 弃用。在将来可能会删除。</p><p>JDK 10 也弃用了一些 API。</p><ul><li>java.security.acl 包已标记为已弃用，也包括 java.security 包中包含各种相关的类（Certificate，Identity，IdentityScope，Singer，auth.Policy）。</li><li>javax.management.remote.rmi.RMIConnectorServer 类中的 CREDENTIAL_TYPES 被标记为不建议使用。<ul><li>java.io.FileInputStream 和 java.io.FileOutputStream 中的 finalize（）方法已被标记为已弃用。所以在 java.util.zip.Deflater &#x2F; Inflater &#x2F; ZipFileclasses 中的 finalize（）方法也被弃用。</li></ul></li></ul><h2 id="10-字符串API加强"><a href="#10-字符串API加强" class="headerlink" title="10. 字符串API加强"></a>10. 字符串API加强</h2><h3 id="isBlank"><a href="#isBlank" class="headerlink" title="isBlank()"></a>isBlank()</h3><p>从java 11 起开始支持,用来判断字符串里是否只包含空字符</p><p>定义：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">public boolean isBlank()</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">public static void main(String[] args)&#123;</span><br><span class="line">    String s = &quot;  &quot;;</span><br><span class="line">    System.out.println(s.isBlank());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>输出结果</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">true</span><br></pre></td></tr></table></figure><p>常见的空格，回车，换行，制表符 都会被识别为空白字符。</p><h3 id="lines"><a href="#lines" class="headerlink" title="lines()"></a>lines()</h3><p>定义</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">public Stream&lt;String&gt; lines()</span><br></pre></td></tr></table></figure><p>从java 11 起开始支持，按 \r \n 把符串转成stream.</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">String str = &quot;a\n b\r\rc&quot;;</span><br><span class="line">System.out.println(str.lines().count());</span><br></pre></td></tr></table></figure><p>输出结果</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">4</span><br></pre></td></tr></table></figure><p>\r\n会只作为一个换行符。连续的 \r\n\r\n 就会识别为两行</p><h3 id="strip"><a href="#strip" class="headerlink" title="strip()"></a>strip()</h3><p>定义</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">public String strip()</span><br></pre></td></tr></table></figure><p>从java 11 起开始支持，去掉字符串前后的半角和全角空格，trim()只能去掉半角空格。stripLeading()是去掉左边的空格，stripTrailing()是去掉右边的空格</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></pre></td><td class="code"><pre><span class="line">String str = &quot; Hello\u3000&quot;;</span><br><span class="line">// str = 7</span><br><span class="line">System.out.println(&quot;str = &quot; + str.length());</span><br><span class="line">// trim = 6</span><br><span class="line">System.out.println(&quot;trim = &quot; + str.trim().length());</span><br><span class="line">// strip = 5</span><br><span class="line">System.out.println(&quot;strip = &quot; + str.strip().length());</span><br><span class="line">System.out.println(&quot;stripLeading[&quot; + str.stripLeading() + &quot;]&quot;);</span><br><span class="line">System.out.println(&quot;stripTrailing[&quot; + str.stripTrailing() + &quot;]&quot;);</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">str = 7</span><br><span class="line">trim = 6</span><br><span class="line">strip = 5</span><br><span class="line">stripLeading[HELLO　]</span><br><span class="line">stripTrailing[ HELLO]</span><br></pre></td></tr></table></figure><h3 id="repeat"><a href="#repeat" class="headerlink" title="repeat"></a>repeat</h3><p>定义</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">public String repeat(int count)</span><br></pre></td></tr></table></figure><p>从java 11开始支持，重复当前字符串多次。可以用于日志输出时，输出多个分隔符，比如：&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;&#x3D;</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></pre></td><td class="code"><pre><span class="line">String s = &quot;=&quot;;</span><br><span class="line">// output ===</span><br><span class="line">System.out.println(s.repeat(3)); </span><br></pre></td></tr></table></figure><h3 id="indent"><a href="#indent" class="headerlink" title="indent"></a>indent</h3><p>定义</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">public String indent(int n) </span><br></pre></td></tr></table></figure><p>从java12开始支持，对字符串的每一行前面加n个空格。当n小于0时，就是去掉n个空格。并且在字符串的最后，会加上换行符。</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><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">String a = &quot;hi&quot;;</span><br><span class="line">System.out.println(&quot;[&quot; + a.indent(2) + &quot;]&quot;);</span><br><span class="line">System.out.println(&quot;[&quot; + a.indent(-2) + &quot;]&quot;);</span><br><span class="line">System.out.println(&quot;-&quot;.repeat(10));</span><br><span class="line"></span><br><span class="line">String b = &quot; ha&quot;;</span><br><span class="line">System.out.println(&quot;[&quot; + b.indent(2) + &quot;]&quot;);</span><br><span class="line">System.out.println(&quot;[&quot; + b.indent(-2) + &quot;]&quot;);</span><br><span class="line">System.out.println(&quot;-&quot;.repeat(10));</span><br><span class="line"></span><br><span class="line">String c = &quot;   ho&quot;;</span><br><span class="line">System.out.println(&quot;[&quot; + c.indent(2) + &quot;]&quot;);</span><br><span class="line">System.out.println(&quot;[&quot; + c.indent(-2) + &quot;]&quot;);</span><br><span class="line">System.out.println(&quot;-&quot;.repeat(10));</span><br><span class="line"></span><br><span class="line">String t = &quot;ta\ntb&quot;;</span><br><span class="line">System.out.println(&quot;[&quot; + t.indent(2) + &quot;]&quot;);</span><br><span class="line">System.out.println(&quot;[&quot; + t.indent(-2) + &quot;]&quot;);</span><br></pre></td></tr></table></figure><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><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">[  hi</span><br><span class="line">]</span><br><span class="line">[hi</span><br><span class="line">]</span><br><span class="line">----------</span><br><span class="line">[   ha</span><br><span class="line">]</span><br><span class="line">[ha</span><br><span class="line">]</span><br><span class="line">----------</span><br><span class="line">[     ho</span><br><span class="line">]</span><br><span class="line">[ ho</span><br><span class="line">]</span><br><span class="line">----------</span><br><span class="line">[  ta</span><br><span class="line">  tb</span><br><span class="line">]</span><br><span class="line">[ta</span><br><span class="line">tb</span><br><span class="line">]</span><br></pre></td></tr></table></figure><h3 id="formatted"><a href="#formatted" class="headerlink" title="formatted"></a>formatted</h3><p>定义：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">public String formatted(Object... args)</span><br></pre></td></tr></table></figure><p>从java 15开始支持。便于字符串本身就是格式化串时的使用，等价于String.format(this, args)</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">String str = &quot;hi %s&quot;;</span><br><span class="line">System.out.println(str.formatted(&quot;小明&quot;));</span><br></pre></td></tr></table></figure><p>输出结果</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hi 小明</span><br></pre></td></tr></table></figure><h2 id="11-多行字符串"><a href="#11-多行字符串" class="headerlink" title="11. 多行字符串"></a>11. 多行字符串</h2><p>从java 15开始支持多行字符串，String类也增加了配套的方法。</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></pre></td><td class="code"><pre><span class="line">public static void main(String[] args)&#123;</span><br><span class="line">    String str = &quot;&quot;&quot;</span><br><span class="line">    &lt;div&gt;</span><br><span class="line">        &lt;h1&gt;多行文本&lt;/h1&gt;</span><br><span class="line">        &lt;p&gt;java 支持多行文本语法&lt;/p&gt;</span><br><span class="line">    &lt;/div&gt;</span><br><span class="line">    &quot;&quot;&quot;;</span><br><span class="line">    System.out.println(str);</span><br><span class="line">    System.out.println(&quot;_&quot;.repeat(30));</span><br><span class="line">    System.out.println(str.stripIndent());</span><br><span class="line">    System.out.println(&quot;_&quot;.repeat(30));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">&lt;div&gt;</span><br><span class="line">    &lt;h1&gt;多行文本&lt;/h1&gt;</span><br><span class="line">    &lt;p&gt;java 支持多行文本语法&lt;/p&gt;</span><br><span class="line">&lt;/div&gt;</span><br><span class="line"></span><br><span class="line">______________________________</span><br><span class="line">&lt;div&gt;</span><br><span class="line">    &lt;h1&gt;多行文本&lt;/h1&gt;</span><br><span class="line">    &lt;p&gt;java 支持多行文本语法&lt;/p&gt;</span><br><span class="line">&lt;/div&gt;</span><br><span class="line"></span><br><span class="line">______________________________</span><br></pre></td></tr></table></figure><p>java17会自动把多行文本前面的缩进空格去掉。没有必要再用stripIndent()方法重复处理。</p><h2 id="12-文本转义"><a href="#12-文本转义" class="headerlink" title="12. 文本转义"></a>12. 文本转义</h2><p>定义</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">public String translateEscapes()</span><br></pre></td></tr></table></figure><p>从java 15开始支持，把字符串里的\n变成换行符，其它的\t \r 也同样支持。</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><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">String str = &quot;ha\\noo\\tkk&quot;;</span><br><span class="line">System.out.println(str);</span><br><span class="line">System.out.println(str.translateEscapes());</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">ha\noo\tkk</span><br><span class="line">ha</span><br><span class="line">oo  kk</span><br></pre></td></tr></table></figure><h2 id="13-文件读写更方便"><a href="#13-文件读写更方便" class="headerlink" title="13. 文件读写更方便"></a>13. 文件读写更方便</h2><p>java从1.7开始提供了Files工具类，java 11 增加了 writeString 和 readString，可以更方便的读写文本文件。</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><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">public static String readString(Path path) throws IOException</span><br><span class="line">public static String readString(Path path, Charset cs) throws IOException </span><br><span class="line">public static Path writeString(Path path, CharSequence csq, OpenOption... options)throws IOException</span><br><span class="line">public static Path writeString(Path path, CharSequence csq, Charset cs, OpenOption... options)throws IOException</span><br></pre></td></tr></table></figure><p>默认使用的是UTF-8的编码，示例代码如下</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></pre></td><td class="code"><pre><span class="line">String dir= &quot;C://tmp/hello.txt&quot;;</span><br><span class="line">Path path = Files.writeString(Path.of(dir), &quot;java 学习&quot;);</span><br><span class="line">String str = Files.readString(path);</span><br></pre></td></tr></table></figure><p>java 12 还增加了 mismatch 方法。对比两个文件，如果内容一致，会返回 -1 ，如果内容不同，会返回不同的字节开始位置。</p><h2 id="14-HttpClient"><a href="#14-HttpClient" class="headerlink" title="14. HttpClient"></a>14. HttpClient</h2><p>java 11 支持了HttpClient, Http请求可以不用再引第三方包了。</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></pre></td><td class="code"><pre><span class="line"> public static void main(String[] args) throws IOException, InterruptedException &#123;</span><br><span class="line">    var request = HttpRequest.newBuilder()</span><br><span class="line">            .uri(URI.create(&quot;https://baidu.com&quot;))</span><br><span class="line">            .GET()</span><br><span class="line">            .build();</span><br><span class="line">    var client = HttpClient.newHttpClient();</span><br><span class="line">    HttpResponse&lt;String&gt; response = client.send(request, HttpResponse.BodyHandlers.ofString());</span><br><span class="line">    System.out.println(response.body());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="15-switch表达式改进"><a href="#15-switch表达式改进" class="headerlink" title="15. switch表达式改进"></a>15. switch表达式改进</h2><p>可以把多个case写在一行了，通过 -&gt; 来执行相关动作。</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></pre></td><td class="code"><pre><span class="line">String month = &quot;june&quot;;</span><br><span class="line">switch (month) &#123;</span><br><span class="line">    case &quot;march&quot;, &quot;april&quot;, &quot;may&quot;            -&gt; System.out.println(&quot;春天&quot;);</span><br><span class="line">    case &quot;june&quot;, &quot;july&quot;, &quot;august&quot;           -&gt; System.out.println(&quot;夏天&quot;);</span><br><span class="line">    case &quot;september&quot;, &quot;october&quot;, &quot;november&quot; -&gt; System.out.println(&quot;秋天&quot;);</span><br><span class="line">    case &quot;december&quot;, &quot;january&quot;, &quot;february&quot;  -&gt; System.out.println(&quot;冬天&quot;);</span><br><span class="line">    default -&gt; System.out.println(&quot;不知道什么季节&quot;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="16-NumberFormat"><a href="#16-NumberFormat" class="headerlink" title="16. NumberFormat"></a>16. NumberFormat</h2><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><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">Locale locale = new Locale(&quot;zh&quot;, &quot;CN&quot;);</span><br><span class="line">NumberFormat numberFormat = NumberFormat.getCompactNumberInstance(locale, NumberFormat.Style.SHORT);</span><br><span class="line"></span><br><span class="line">System.out.println(numberFormat.format(100));</span><br><span class="line">System.out.println(numberFormat.format(1000));</span><br><span class="line">System.out.println(numberFormat.format(10000));</span><br><span class="line">System.out.println(numberFormat.format(100000));</span><br><span class="line">System.out.println(numberFormat.format(1000000));</span><br><span class="line"></span><br><span class="line">// 设置小数位数</span><br><span class="line">numberFormat.setMaximumFractionDigits(2);</span><br><span class="line">System.out.println(numberFormat.format(1234));</span><br><span class="line">System.out.println(numberFormat.format(123456));</span><br><span class="line">System.out.println(numberFormat.format(12345678));</span><br><span class="line">System.out.println(numberFormat.format(1234567890));</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">100</span><br><span class="line">1,000</span><br><span class="line">1万</span><br><span class="line">10万</span><br><span class="line">100万</span><br><span class="line">1,234</span><br><span class="line">12.35万</span><br><span class="line">1234.57万</span><br><span class="line">12.35亿</span><br></pre></td></tr></table></figure><p>如果把第一行改成美国的Locale</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Locale locale = new Locale(&quot;en&quot;, &quot;US&quot;);</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">100</span><br><span class="line">1K</span><br><span class="line">10K</span><br><span class="line">100K</span><br><span class="line">1M</span><br><span class="line">1.23K</span><br><span class="line">123.46K</span><br><span class="line">12.35M</span><br><span class="line">1.23B</span><br></pre></td></tr></table></figure><h2 id="17-instanceof"><a href="#17-instanceof" class="headerlink" title="17. instanceof"></a>17. instanceof</h2><p>java 14 简化了 instanceof 关键字的用法，现在你可以这样写</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></pre></td><td class="code"><pre><span class="line">Object obj = &quot;abc&quot;;</span><br><span class="line">if(obj instanceof String str)&#123;</span><br><span class="line">    System.out.println(str);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="18-record"><a href="#18-record" class="headerlink" title="18. record"></a>18. record</h2><p>record关键字，在java 17转正。它是和class, interface, enum 同类的关键字。 我们可以创建一个Point.java文件，里面就写：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">public record Point(int x,int y) &#123;&#125;</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">public class RecordTest &#123;</span><br><span class="line">    public static void main(String[] args)&#123;</span><br><span class="line">        Point p = new Point(1,2);</span><br><span class="line">        System.out.println(p.x());</span><br><span class="line">        System.out.println(p.y());</span><br><span class="line">        System.out.println(p.toString());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>它适用于，简单不可变对象的封装。在某些场景下，可以替代lombok</p><h2 id="19-sealed-class-密封类"><a href="#19-sealed-class-密封类" class="headerlink" title="19. sealed class 密封类"></a>19. sealed class 密封类</h2><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></pre></td><td class="code"><pre><span class="line">public sealed class Person permits Female, Male &#123;</span><br><span class="line">&#125;</span><br><span class="line">public final class Male extends Person&#123;</span><br><span class="line">&#125;</span><br><span class="line">public final class Female extends Person &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面我们定义了三个类，Person, Male 和 Female，即 人，男人，女人。 密封类就是增加了一种可能，明确定义某个类只能被指定的类继承。其它类无法对其进行继承。</p><p>从上面的例子我们看到 Person类前面使用了 sealed class 对其进行声明，在后面通过 permits 关键字指定了它只能被Female, Male这两个类进行继承。</p><p>Female, Male 这两个类在声明的时候必须指定它是final,还是 sealed ，还是non-sealed。如果指定为non-sealed,那么它就以可以被随意继承了。</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><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">public sealed class Person permits Female, Male &#123;</span><br><span class="line">&#125;</span><br><span class="line">public final class Female extends Person &#123;</span><br><span class="line">&#125;</span><br><span class="line">public sealed class Male extends Person permits OldMale &#123;</span><br><span class="line">&#125;</span><br><span class="line">public non-sealed class OldMale extends Male&#123;</span><br><span class="line">&#125;</span><br><span class="line">public class CityOldMale extends OldMale&#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里我们把Male改为sealed修饰的类，指定其唯一子类 OldMale。但在声明 OldMale 指定为 non-sealed, 这么一来 OldMale 就又可以被随意的继承了。比如上面的 CityOldMale。</p><p>注意 上面的代码是5个类，我自己测试时是用了5个文件，大家可以自己试试以加深印象。</p><h2 id="其它更新"><a href="#其它更新" class="headerlink" title="其它更新"></a>其它更新</h2><p>在 Java 11 支持了 Unicode 10 之后， Java 12 支持了 Unicode 11，支持操作更多的表情、符号。</p><p>垃圾回收优化，引入 ZGC回收</p><p>java 14 移除了CMS（Concurrent Mark Sweep）垃圾收集器</p><h2 id="官方参考"><a href="#官方参考" class="headerlink" title="官方参考"></a>官方参考</h2><ul><li><a href="https://openjdk.org/projects/jdk/17/">https://openjdk.org/projects/jdk/17/</a></li><li><a href="https://openjdk.org/projects/jdk/16/">https://openjdk.org/projects/jdk/16/</a></li><li><a href="https://openjdk.org/projects/jdk/15/">https://openjdk.org/projects/jdk/15/</a></li><li><a href="https://openjdk.org/projects/jdk/14/">https://openjdk.org/projects/jdk/14/</a></li><li><a href="https://openjdk.org/projects/jdk/13/">https://openjdk.org/projects/jdk/13/</a></li><li><a href="https://openjdk.org/projects/jdk/12/">https://openjdk.org/projects/jdk/12/</a></li><li><a href="https://openjdk.org/projects/jdk/11/">https://openjdk.org/projects/jdk/11/</a></li><li><a href="https://openjdk.org/projects/jdk/10/">https://openjdk.org/projects/jdk/10/</a></li><li><a href="https://openjdk.org/projects/jdk9/">https://openjdk.org/projects/jdk9/</a></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;1-集合工厂方法&quot;&gt;&lt;a href=&quot;#1-集合工厂方法&quot; class=&quot;headerlink&quot; title=&quot;1.集合工厂方法&quot;&gt;&lt;/a&gt;1.集合工厂方法&lt;/h2&gt;&lt;p&gt;从Java 9开始， List，Set 和 Map 接口中，新的静态工厂方法可以创建这些集合</summary>
      
    
    
    
    <category term="Java" scheme="https://www.genlei.cn/categories/Java/"/>
    
    
  </entry>
  
  <entry>
    <title>request 封装</title>
    <link href="https://www.genlei.cn/2022/10/219/"/>
    <id>https://www.genlei.cn/2022/10/219/</id>
    <published>2022-10-21T05:13:11.000Z</published>
    <updated>2024-04-03T09:05:49.451Z</updated>
    
    <content type="html"><![CDATA[<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><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><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">function messageError(message,title)&#123;</span><br><span class="line">    console.log(message,title)</span><br><span class="line">&#125;</span><br><span class="line">/**</span><br><span class="line"> * 状态检查</span><br><span class="line"> */</span><br><span class="line">export function checkStatus(response) &#123;</span><br><span class="line">  if (response &amp;&amp; response.status &gt;= 200 &amp;&amp; response.status &lt; 400) &#123;</span><br><span class="line">    return response;</span><br><span class="line">  &#125;</span><br><span class="line">  if (response &amp;&amp; response.status === 401) &#123;</span><br><span class="line">    messageError(&#x27;会话超时,请重新登录&#x27;,&#x27;会话超时,请重新登录&#x27;)</span><br><span class="line">    if(typeof window !== &quot;undefined&quot;) &#123;</span><br><span class="line">        sessionStorage.clear();</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  if (!response) &#123;</span><br><span class="line">    messageError(&#x27;您的网络发生异常，无法连接服务器&#x27;,&#x27;网络异常&#x27;)</span><br><span class="line">  &#125; else &#123;</span><br><span class="line">    const &#123; status, url, statusText &#125; = response;</span><br><span class="line">    messageError(statusText,`请求错误 $&#123;status&#125;: $&#123;url&#125;`)</span><br><span class="line">  &#125; </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">var access_token = &#x27;&#x27;;</span><br><span class="line">if(typeof window !== &quot;undefined&quot;) &#123;</span><br><span class="line">    access_token = sessionStorage.getItem(&#x27;token&#x27;)  &#x27;&#x27;</span><br><span class="line">&#125;    </span><br><span class="line"></span><br><span class="line">const headerParams = &#123;</span><br><span class="line">  token: access_token,</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line"> * 请求接口封装</span><br><span class="line"> */</span><br><span class="line">export async function request(url,headers) &#123;</span><br><span class="line">  try&#123;</span><br><span class="line">    const fetchResponse = await fetch(url, headers);</span><br><span class="line">    const response = checkStatus(fetchResponse);</span><br><span class="line">    if(response)&#123;</span><br><span class="line">      return response.json();</span><br><span class="line">    &#125;  </span><br><span class="line">  &#125;catch(err)&#123;</span><br><span class="line">    messageError(url,&#x27;请求失败&#x27;)</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"> * post</span><br><span class="line"> * @param url</span><br><span class="line"> * @param params</span><br><span class="line"> * @param headers</span><br><span class="line"> */</span><br><span class="line">export function post(url,body,headers)&#123;</span><br><span class="line">  return request(url, &#123;</span><br><span class="line">    method: &#x27;POST&#x27;,</span><br><span class="line">    headers: &#123;</span><br><span class="line">      Accept: &#x27;application/json&#x27;,</span><br><span class="line">      &#x27;Content-Type&#x27;: &#x27;application/json&#x27;,</span><br><span class="line">      ...headerParams,</span><br><span class="line">      ...headers,</span><br><span class="line">    &#125;,</span><br><span class="line">    body: JSON.stringify(body),</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"> * get</span><br><span class="line"> * @param url 请求URL</span><br><span class="line"> * @param params 请求参数</span><br><span class="line"> * @param headers 额外HTTP头</span><br><span class="line"> */</span><br><span class="line">export function get(url, params, headers)&#123;</span><br><span class="line">  if (params) &#123;</span><br><span class="line">    const paramsArray = [];</span><br><span class="line">    //拼接参数</span><br><span class="line">    Object.keys(params).forEach(key =&gt;</span><br><span class="line">      paramsArray.push(key + &#x27;=&#x27; + params[key]),</span><br><span class="line">    );</span><br><span class="line">    if (url.search(/\?/) === -1) &#123;</span><br><span class="line">      url += &#x27;?&#x27; + paramsArray.join(&#x27;&amp;&#x27;);</span><br><span class="line">    &#125; else &#123;</span><br><span class="line">      url += &#x27;&amp;&#x27; + paramsArray.join(&#x27;&amp;&#x27;);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  return request(url, &#123;</span><br><span class="line">    method: &#x27;GET&#x27;,</span><br><span class="line">    headers: &#123;</span><br><span class="line">      Accept: &#x27;application/json&#x27;,</span><br><span class="line">      &#x27;Content-Type&#x27;: &#x27;application/json&#x27;,</span><br><span class="line">      ...headerParams,</span><br><span class="line">      ...headers,</span><br><span class="line">    &#125;,</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"> * put</span><br><span class="line"> * @param url 请求URL</span><br><span class="line"> * @param body 正文内容</span><br><span class="line"> * @param headers 额外请求头</span><br><span class="line"> */</span><br><span class="line">export function put(url,body,headers)&#123;</span><br><span class="line">  return request(url, &#123;</span><br><span class="line">    method: &#x27;PUT&#x27;,</span><br><span class="line">    headers: &#123;</span><br><span class="line">      Accept: &#x27;application/json&#x27;,</span><br><span class="line">      &#x27;Content-Type&#x27;: &#x27;application/json&#x27;,</span><br><span class="line">      ...headerParams,</span><br><span class="line">      ...headers,</span><br><span class="line">    &#125;,</span><br><span class="line">    body: JSON.stringify(body),</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;figure class=&quot;highlight plaintext&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span c</summary>
      
    
    
    
    <category term="uncategorized" scheme="https://www.genlei.cn/categories/uncategorized/"/>
    
    
  </entry>
  
  <entry>
    <title>汉东省健康码系统设计说明书（撰写中）</title>
    <link href="https://www.genlei.cn/2022/09/209/"/>
    <id>https://www.genlei.cn/2022/09/209/</id>
    <published>2022-09-27T22:55:37.000Z</published>
    <updated>2024-04-03T09:05:49.452Z</updated>
    
    <content type="html"><![CDATA[<p><strong>声明：本人从未参与任何健康码系统的开发，此文是我基于自己对健康码的使用，相关新闻的阅读，按我自己的理解，整理出来的文章。如果跟某些系统有技术上的相似，纯属巧合。</strong></p><p><strong>摘要：</strong> 本文尝试从一个旁观者的身份，设计一个省的健康码系统。讲述一个健康码系统包含的主要功能，主要流程。技术上讲述 技术要求，技术架构，流程设计，安全处理，并发处理。 希望能通过一个相对完整的技术文档，给自己一个纸上谈兵的机会，给各位一个参考。</p><p><strong>正文：</strong> 随着新冠疫情的发展，各省市都建设了配套的健康码系统和APP。一开始只是能查看健康码（绿码，黄码，红码），后来再支持了核酸码，同时健康码上能查看核酸信息。 尽管亮码看起来很简单，但还是发生过不少事故；2022年9月1日开始，成都开始进入“静默模式”，市民按要求开始有序做核酸检测，目标是全市2000万人每天核酸检测一次。但9月2日、3日，成都核酸检测系统出现崩溃的情况，导致一天一检并不顺利。2021年12月20日，西安“一码通”崩溃；2021年12月25日，天津健康码出现异常。2022年1月粤康码公告显示，10日上午8：31平台检测到粤康码流量异常增大，最高达每分钟140万次，超出承载极限，触发系统保护机制，导致部分用户访问粤康码缓慢或异常，运行保障团队紧急处置，于9：04部分缓解，9：56完全恢复顺畅运行。 上面是基于部分新闻报道的整理，说明健康码系统容易出故障，而且一旦出现故障对民众的生活都会造成较大影响，很容易就民怨沸腾。坦白讲又有多少人真的处理过1分钟140万次请求呢，按秒来算就是2.3万次每秒。</p><p>昨天晚上，沙瑞金书记托梦给我，通知我已经中标汉东省健康码系统的项目，所以我赶紧开始写文档。</p><h2 id="一、主要功能"><a href="#一、主要功能" class="headerlink" title="一、主要功能"></a>一、主要功能</h2><p>健康码系统使用人分为五类：民众用户，采样员，检测员，客服，系统管理员。</p><ol><li>民众用户指普通民众，即生活在汉东省的所有人，需要通过健康码系统完成查看健康码，查看核酸码。</li><li>采样员指为普通民众进行核酸采样的工作人员，需要通过扫描 普通用户出示的健康码，登记录采样信息。</li><li>检测员指对核酸样本进行检查的工作人员，需要通过扫描采样管上的二维码登记核酸检测结果。</li><li>客服指对健康码异常情况进行处理的工作人员，负责接听民众的申诉，核实信息，按需要调整健康码状态。</li><li>系统管理员指能创建各种工作人员账号，分配想着权限的工作人员。</li></ol><p>接入终端包括：</p><ol><li>健康码APP，给民众用户使用, 民众可以使用健康码APP查看自己的健康码，核酸码，核酸记录。</li><li>健康码微信小程序，给民众用户使用，功能同健康码APP。</li><li>健康码支付宝小程序，给民众用户使用，功能同健康码APP。</li><li>采样APP，给采样员使用，采样员通过采样APP完成扫码登记采样。</li><li>健康码web管理系统，给检测员，客服，系统管理员使用。</li></ol><h3 id="民众用户"><a href="#民众用户" class="headerlink" title="民众用户"></a>民众用户</h3><ol><li>民众用户能通过手机号验证码或用户名密码登录健康码APP。</li><li>民众用户能通过健康码APP查看健康码</li><li>民众用户能通过健康码APP查看核酸码，提供给采样员扫码完成核酸采样登记</li><li>民众用户能通过健康码APP查看核酸检测记录，检测结果</li></ol><h3 id="采样员"><a href="#采样员" class="headerlink" title="采样员"></a>采样员</h3><ol><li><p>采样员能通过手机号验证码或用户名密码登录采样APP。</p></li><li><p>采样员能通过采样APP完成检测试管条形码扫码登录，完成民众用户的核酸码扫描登记</p><h3 id="检测员"><a href="#检测员" class="headerlink" title="检测员"></a>检测员</h3></li><li><p>检测员能通过用户名密码登录健康码web管理系统</p></li><li><p>检测员能通过扫描检测试管条形码登记检测结果</p></li><li><p>检测员能批量登记检测结果</p></li></ol><h3 id="客服"><a href="#客服" class="headerlink" title="客服"></a>客服</h3><ol><li>客服能通过用户名密码登录健康码web管理系统</li><li>客服能通过姓名，身份证号码查询民众用户，查看其核酸记录，异常记录</li><li>客服能给特定民众用户设定标记，标记为红码或黄码，需要记录异常原因</li><li>客服能给特定民众用户解除标记，标记为绿码，需要记录异常解除原因</li></ol><h3 id="系统管理员"><a href="#系统管理员" class="headerlink" title="系统管理员"></a>系统管理员</h3><ol><li>客服能通过用户名密码登录健康码web管理系统</li><li>系统管理员能添加 采样员， 检测员，客服 和系统管理员</li></ol><p>更多详细功能描述请参考《汉东省健康码系统需求规格说明书》。</p><h2 id="二、技术要求"><a href="#二、技术要求" class="headerlink" title="二、技术要求"></a>二、技术要求</h2><ol><li>高可用性要求，时间可用性需要达到99.95%, 即年累积不可用时间不得超过285分钟，月累积不可用时间不得超过24分钟。</li><li>高并发要求，对于民众用户的查看健康码和核酸码的请求，需要支持 4.6万次&#x2F;秒的高峰请求，1分钟280万次。按已知的峰值的2倍设计。</li><li>低延迟要求，对于民众用户的查看健康码和核酸码的请求，99.9%的请求都应当于2秒内响应。即通常情况下用户点击查看健康码，应当在2秒内能正常显示健康码。</li><li>数据防篡改要求，核酸记录一旦录入，不能被随意更改。</li><li>数据备份要求，应当建立完备的数据备份机制，保证数据得到完整快速的备份，防止数据丢失。</li><li>审计要求，系统应该详细记录核酸采样过程，包括但不限于哪位采样员，在什么时间，什么地点对哪位民众进行了采样；哪位检测员，在什么时间，会么地点对哪些样本进行了检测，检测结果是什么。</li><li>审计要求，系统应该详细记录健康码变化过程，包括但不限于哪位客服，在什么时间，对哪位民众进行了健康码变更；记录变更结果及变更原因。</li><li>实名认证要求，系统的服务要求对姓名和身份证号码做实名认证。因为认证工作量巨大，需要支持人脸识别，完成自动实名认证。</li><li>离线支持，由于采样时可能遇到的网络问题或者并发问题，核酸码需要支持离线截图使用。保证采样过程顺畅。</li><li>离线支持，出于保证采样顺畅的保证，允许采样APP短暂离线，即采样APP能够在无网络的情况下完成扫码登记，联网后能自动上传采样记录。保证在网络不稳定的情况下也能正常采样。界面上未同步的数据应当有明显的标识提示。</li></ol><h2 id="三、技术架构"><a href="#三、技术架构" class="headerlink" title="三、技术架构"></a>三、技术架构</h2><h3 id="分层架构"><a href="#分层架构" class="headerlink" title="分层架构"></a>分层架构</h3><p><img src="http://www.genlei.cn/wp-content/uploads/2022/09/%E5%88%86%E5%B1%82%E6%9E%B6%E6%9E%84%E5%9B%BE-1.png"> <strong>终端层</strong> 终端层的说明请参考本文 一、主要功能中的 接入终端 <strong>接入层</strong> 接入层是指网络及接入中间件，终端通过接入层访问服务层。</p><ol><li>CDN，CDN主要用于静态文件的缓存，缓解源站压力，同时提高终端的访问速度</li><li>K8S容器，K8S容器用于装载服务层的微服务，使得维护与扩容变得更可靠便利。</li><li>VPN，通过VPN建立虚拟专用网络，防止外部人员非法访问</li><li>堡垒机，堡垒机保证系统维护过程可追踪，可审计</li></ol><p><strong>服务层</strong> 服务层是指业务的代码实现层，主要业务逻辑代码均在服务层实现。服务层通过微服务技术实现。</p><ol><li>CodeQueryService 提供查询核酸码，健康码，核酸记录功能</li><li>SampleService 提供采样登记功能</li><li>UserService 提供用户登录，实名认证功能</li><li>ManageService 提供审计，用户管理，异常申诉功能</li></ol><p><strong>存储层</strong> 存储层是指数据存储实现层，主要通过使用开源中间件，实现数据的持久化存储，及缓存实现。</p><ol><li>Redis 用于缓存用户健康码及核酸码内容</li><li>MySQL 用于存储用户信息，健康码状态</li><li>Clickhouse 用于存储核酸记录，健康码变更记录</li><li>Elastic Search 用于存储系统日志信息</li><li>OSS 用于存储用户上传的图片或文件。</li></ol><p><strong>外部服务层</strong> 外部服务层指系统依赖的第三方服务</p><ol><li>支付宝实名认证，支付宝提供的实名认证服务</li><li>微信实名认证，微信提供的实名认证服务</li></ol><h3 id="数据流图"><a href="#数据流图" class="headerlink" title="数据流图"></a>数据流图</h3><h3 id="逻辑架构"><a href="#逻辑架构" class="headerlink" title="逻辑架构"></a>逻辑架构</h3><p>未完待续</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;&lt;strong&gt;声明：本人从未参与任何健康码系统的开发，此文是我基于自己对健康码的使用，相关新闻的阅读，按我自己的理解，整理出来的文章。如果跟某些系统有技术上的相似，纯属巧合。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;摘要：&lt;/strong&gt; 本文尝试从一个旁观者的</summary>
      
    
    
    
    <category term="文档" scheme="https://www.genlei.cn/categories/%E6%96%87%E6%A1%A3/"/>
    
    
  </entry>
  
  <entry>
    <title>sql server 读取表结构</title>
    <link href="https://www.genlei.cn/2022/09/207/"/>
    <id>https://www.genlei.cn/2022/09/207/</id>
    <published>2022-09-27T05:26:18.000Z</published>
    <updated>2024-04-03T09:05:49.451Z</updated>
    
    <content type="html"><![CDATA[<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><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></pre></td><td class="code"><pre><span class="line">SELECT col.name                        AS name,</span><br><span class="line">       t.name                          AS type,</span><br><span class="line">       Cast(col.length AS VARCHAR(80)) AS length,</span><br><span class="line">       Cast(col.xprec AS VARCHAR(80))  AS precision,</span><br><span class="line">       CASE</span><br><span class="line">         WHEN EXISTS (SELECT 1</span><br><span class="line">          FROM   dbo.sysindexes si</span><br><span class="line">                 INNER JOIN dbo.sysindexkeys sik</span><br><span class="line">                         ON si.id = sik.id</span><br><span class="line">                            AND si.indid = sik.indid</span><br><span class="line">                 INNER JOIN dbo.syscolumns sc</span><br><span class="line">                         ON sc.id = sik.id</span><br><span class="line">                            AND sc.colid = sik.colid</span><br><span class="line">                 INNER JOIN dbo.sysobjects so</span><br><span class="line">                         ON so.name = si.name</span><br><span class="line">                            AND so.xtype = &#x27;PK&#x27;</span><br><span class="line">          WHERE  sc.id = col.id</span><br><span class="line">                 AND sc.colid = col.colid) THEN &#x27;YES&#x27;</span><br><span class="line">         ELSE &#x27;NO&#x27;</span><br><span class="line">       END                             AS &#x27;key&#x27;,</span><br><span class="line">       CASE</span><br><span class="line">         WHEN col.isnullable = 1 THEN &#x27;NO&#x27;</span><br><span class="line">         ELSE &#x27;YES&#x27;</span><br><span class="line">       END                             AS isnullable,</span><br><span class="line">       Isnull(ep.[value], NULL)        AS comment</span><br><span class="line">FROM   dbo.syscolumns col</span><br><span class="line">       LEFT JOIN dbo.systypes t</span><br><span class="line">              ON col.xtype = t.xusertype</span><br><span class="line">       LEFT JOIN sys.extended_properties ep</span><br><span class="line">              ON col.id = ep.major_id</span><br><span class="line">                 AND col.colid = ep.minor_id</span><br><span class="line">       INNER JOIN dbo.sysobjects obj</span><br><span class="line">               ON col.id = obj.id</span><br><span class="line">                  AND obj.status &gt;= 0</span><br><span class="line">       LEFT JOIN sys.schemas s</span><br><span class="line">              ON obj.uid = s.schema_id</span><br><span class="line">WHERE  s.name = &#x27;schema_name&#x27;</span><br><span class="line">       AND obj.name = ( &#x27;table_name&#x27; );</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;figure class=&quot;highlight plaintext&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;line&quot;&gt;2&lt;/span&gt;&lt;br&gt;&lt;span c</summary>
      
    
    
    
    <category term="uncategorized" scheme="https://www.genlei.cn/categories/uncategorized/"/>
    
    
  </entry>
  
  <entry>
    <title>JD 岗位要求</title>
    <link href="https://www.genlei.cn/2022/09/204/"/>
    <id>https://www.genlei.cn/2022/09/204/</id>
    <published>2022-09-01T02:30:24.000Z</published>
    <updated>2024-04-03T09:05:49.448Z</updated>
    
    <content type="html"><![CDATA[<p>JAVA开发工程师 任职要求： 1、本科及以上学历，计算机相关专业，3年以上工作经验 2、具有扎实的java技术功底，熟悉SpringBoot、MyBatis等开源框架的应用了解其运行基本原理 3、熟悉数据库设计，如MySQL,能熟练的编写SQL及调优； 熟悉常见的NoSQL,如：Redis 4、熟悉微服务架构，对Spring Cloud 有一定了解优先，了解K8S优先 5、热爱技术研发、注重代码质量，有良好的软件工程和编码规范意识 6、有较好的沟通能力，逻辑清晰，有独立分析和解决问题的能力；有强烈的责任心和团队合作精神，能快速主动融入团队</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;JAVA开发工程师 任职要求： 1、本科及以上学历，计算机相关专业，3年以上工作经验 2、具有扎实的java技术功底，熟悉SpringBoot、MyBatis等开源框架的应用了解其运行基本原理 3、熟悉数据库设计，如MySQL,能熟练的编写SQL及调优； 熟悉常见的NoSQ</summary>
      
    
    
    
    <category term="uncategorized" scheme="https://www.genlei.cn/categories/uncategorized/"/>
    
    
  </entry>
  
</feed>
